:root{
  --navy:#0b3d5c;
  --navy-2:#0a2f47;
  --sky:#1a7fb5;        /* brand blue: fills, dots, bars - never text on a light ground */
  --sky-ink:#0a4d75;    /* the SAME blue as text. --sky on white is 4.42:1 and fails AA at
                           14px; this is 9.01:1 on white and 8.2:1 on paper. Two tokens
                           because one colour cannot be both a dot and a sentence. */
  --sun:#f4b23e;
  --ink:#0d1b24;
  --paper:#f7f4ec;
  --card:#ffffff;
  --muted:#3a4a52;      /* was #5c6b74 — mid-greys are the first thing polarized lenses eat */
  --good:#2e8b57;
  --bad:#c0483a;        /* borders, bars, fills */
  --bad-ink:#8a2b1b;    /* the same red as TEXT: 8.60:1 on white, 7.62:1 on the urgent
                           panel. --bad was 4.97:1 - legal at 13px, but this is the
                           EMERGENCY heading in a card read on open water. */
  --shadow:0 6px 24px rgba(0,0,0,.28);
  --r:16px;
  --tap:52px;
  --safe-t:env(safe-area-inset-top,0px);
  --safe-b:env(safe-area-inset-bottom,0px);
  --caveat-h:30px;           /* the permanent "not a nav app" strip along the bottom */
  --above-caveat:calc(var(--safe-b) + var(--caveat-h) + 8px);
}
*{box-sizing:border-box}
html,body{height:100%;margin:0}
body{
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  color:var(--ink);background:var(--navy);overscroll-behavior:none;
  -webkit-tap-highlight-color:transparent;
}
#map{position:fixed;inset:0;touch-action:none}

/* keep MapLibre attribution readable but small */
.maplibregl-ctrl-bottom-right{margin-bottom:calc(var(--safe-b) + var(--caveat-h) + 4px)}
.maplibregl-ctrl-attrib{font-size:10px;opacity:.85}

/* ---------- top bar ---------- */
#topbar{
  position:fixed;top:0;left:0;right:0;z-index:20;
  padding:calc(var(--safe-t) + 6px) 10px 10px;
  display:flex;align-items:center;justify-content:center;
  background:linear-gradient(180deg,rgba(11,61,92,.92),rgba(11,61,92,0));
  pointer-events:none;   /* the strip itself stays see-through... */
  justify-content:space-between;
}
.brand{display:flex;align-items:center;gap:7px;color:#fff;justify-content:center}
.brand-logo{display:flex;align-items:center}
.brand-logo svg{display:block;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.35)}
.brand-name{font-weight:800;letter-spacing:.3px;text-shadow:0 1px 3px rgba(0,0,0,.55);font-size:18px}
.modal-logo{text-align:center;margin-bottom:8px}
.modal-logo svg{border-radius:14px;box-shadow:var(--shadow)}
.pill{font-size:11px;font-weight:700;border-radius:999px;padding:3px 9px;text-transform:uppercase;letter-spacing:.5px}
.pill-off{background:#7a1f1f;color:#ffdede}
.pill-on{background:#124a2b;color:#c9f5d8}
.icon-btn{
  width:var(--tap);height:var(--tap);min-width:var(--tap);border:none;border-radius:14px;
  background:rgba(255,255,255,.94);color:var(--navy);font-size:22px;font-weight:700;
  box-shadow:var(--shadow);cursor:pointer;display:flex;align-items:center;justify-content:center;
}
.icon-btn:active{transform:scale(.95)}
/* ---------- north badge ----------
   Under the menu button, clear of the thumb zone. It is chrome you read, not chrome
   you press: pointer-events:none at rest is not a detail, because the .30 audit found
   51% of the screen unreachable by MapLibre thanks to transparent containers with
   pointer-events:auto, and this corner is one people pinch from. It earns a tap
   target only in the .off-north state, when there is actually something to fix.
   z-index 17 sits one below #route-bar (18), the lowest of the map chrome, so on a
   narrow screen the route bar always wins the overlap. */
#north-badge{
  position:fixed;
  top:calc(var(--safe-t) + 84px);
  right:calc(env(safe-area-inset-right,0px) + 10px);
  width:46px;height:46px;
  border-radius:50%;
  background:rgba(11,61,92,.92);
  border:2px solid #f7f4ec;
  box-shadow:0 2px 8px rgba(0,0,0,.35);
  display:flex;align-items:center;justify-content:center;
  pointer-events:none;
  z-index:17;
}
#north-badge svg{width:34px;height:34px;display:block}
#north-needle{transition:transform .18s linear}
/* Only when the map is genuinely off north does this become tappable. The N glyph
   is font-size:0 until then — at 46px the needle reads faster than a letter, and
   the letter is the extra signal you want once something is wrong. It rides inside
   the rotating group on purpose: a tilted N is exactly the alarm. */
#north-badge.off-north{
  pointer-events:auto;
  border-color:var(--sun);
  box-shadow:0 0 0 3px rgba(244,178,62,.45),0 2px 8px rgba(0,0,0,.35);
}
#north-badge.off-north text{font-size:26px;font-weight:700}
/* Gone while you are placing a pin, like the rest of the map chrome. */
body.placing #north-badge{display:none}
/* Under heading-up the badge is the documented way out -- both new strings say 'tap
   the N badge'. But it is pointer-events:none until the map is off north, so in .56
   turning heading-up on at the dock and immediately changing your mind did nothing at
   all: bearing still 0, badge still inert. If we promise the tap, it has to be live
   the whole time the mode is on, and look live. */
body.course-up #north-badge{
  pointer-events:auto;border-color:var(--sun);
  box-shadow:0 0 0 3px rgba(244,178,62,.45),0 2px 8px rgba(0,0,0,.35);
}
body.course-up #north-badge text{font-size:26px;font-weight:700}

/* FIELD TEST 1: the gold ring was explained nowhere except inside a card you had
   already tapped. Bill commissioned the feature and could not identify it on the
   water. This is the cheapest place to say it: in the list, where you browse. */
.soft-legend{
  display:flex;align-items:center;gap:8px;padding:8px 14px;
  font-size:13px;line-height:1.35;color:var(--muted);background:#fffdf7;
  border-top:1px solid #eee6d4;
}
/* It has to LOOK like the mark it explains, or it explains nothing. This is .pm-q, at
   the size it draws on the map, and it replaced a teardrop-with-a-gold-ring swatch when
   the ring itself came off in .60. */
.soft-swatch{
  flex:none;min-width:17px;height:17px;border-radius:999px;
  background:var(--sun);color:#3a2a06;border:2px solid #fff;
  font-size:12px;font-weight:900;line-height:13px;text-align:center;
  box-shadow:0 1px 3px rgba(0,0,0,.4);margin:0 7px 0 5px;
}
.soft-swatch::after{content:"?"}
.chip-soft{
  background:#f0d9a8;color:#6b4c05;border-radius:999px;padding:1px 7px;
  font-size:11px;font-weight:800;letter-spacing:.03em;text-transform:uppercase;
}
/* ---------- story card ---------- */
/* THERE IS NO SUN MODE ANY MORE - the app IS the sun version. It was off by default,
   buried behind a menu button in the regrip zone, it changed nothing outside this card,
   and what it did change it made SMALLER: 28px to 25px on the heading, 20px to 18.5px on
   the body, under a control labelled "Bigger text". Everything it got right is now
   simply how the app looks, and the sizes go up instead of down. Nobody is reading this
   in the shade. */
.card{
  position:fixed;left:8px;right:8px;bottom:var(--above-caveat);z-index:25;
  background:var(--card);border:2px solid var(--navy);border-radius:var(--r);box-shadow:var(--shadow);
  padding:16px 16px 14px;max-height:70vh;overflow:auto;
  animation:rise .22s ease;
}
@keyframes rise{from{transform:translateY(30px);opacity:0}to{transform:none;opacity:1}}
/* The ✕ used to be absolutely positioned inside a card that scrolls. On a long list —
   Manage Lake Scouts, with every account in it — it scrolled off the top and there was
   no way out of the popup at all. Sticky keeps it pinned to the top of the card no
   matter how far down you are. Tapping the dark area outside also closes now. */
.card-close{
  position:sticky;top:0;float:right;z-index:3;
  margin:-10px -10px 0 10px;
  border:none;background:rgba(255,255,255,.94);border-radius:50%;
  font-size:20px;color:var(--muted);width:44px;height:44px;cursor:pointer;
  box-shadow:0 1px 6px rgba(0,0,0,.14);
}
.card-close:active{background:#e9eef1}
.card-head{display:flex;align-items:center;flex-wrap:wrap;gap:4px 10px;margin:0 30px 6px 0}
/* THE CAPTAIN'S LINE. "Off your starboard bow" is the sentence that makes four people
   look the same way, and it is why this app is not a notification. It used to be written
   to #nearby (z22) and then covered by the card (z25), so it only ever existed as speech.
   Gold is the app's "this one" colour, but #f4b23e on white is 1.86:1 - unreadable, and
   unthinkable on a screen used in glare. This is the darkest amber that still reads gold
   and clears the 7:1 outdoor bar: #6b4c05 on white is 7.89:1, on paper 7.18:1. It is the
   same amber .needscheck already uses, so it is not a new colour in the app. */
.lead{color:#6b4c05;font-size:20px;font-weight:800;line-height:1.15;letter-spacing:-.01em}
.chip{background:var(--navy);color:#fff;border-radius:999px;padding:4px 11px;font-size:14px;font-weight:700}
.dist{color:var(--sky-ink);font-weight:800;font-size:15px}
.card h2{margin:2px 0 8px;font-size:28px;line-height:1.12;letter-spacing:-.01em}
.card p{margin:0 0 11px;font-size:20px;line-height:1.5;color:#0a1620}
/* Weight, not size, is what carries body copy through glare - 20px at 600 stays inside
   the measure while reading harder than 20px at 400. */
#card-short{font-weight:600}
#card-long{color:#152a35}
.card-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:6px}

/* ---------- the approach card (peek) ----------
   A card you tapped is a thing you chose to read. A card that fired on approach is an
   announcement, and it arrives while the boat is moving and four people are waiting for
   you to point at something. The full card measured 243-824 on a 390x844 screen: 79% of
   the display, with the Do bar, Find, Add a point, Locate and the list handle all
   removed. That is the captain losing his map and every control at the exact moment he
   is using them.
   Peek keeps the lead line, the name, the distance and the gold Listen button, and
   nothing else. Tapping it anywhere expands to the full card - the class comes off and
   every rule below stops applying, so there is no second layout to keep in step. */
#card-expand{display:none}
body.card-peek #card-short,
body.card-peek #card-needscheck,
body.card-peek #card-long-wrap,
body.card-peek .card-actions,
body.card-peek #card-sources-list,
body.card-peek #card-confidence{display:none}
/* Keeping the controls on screen is not the same as keeping them USABLE. The band is
   z25 and #thumb-zone is z19, so a bottom-anchored peek sat straight on top of the Do
   bar: displayed, and covered. --peek-bottom is written by JS on open: the distance
   from the bottom of the viewport to the TOP of the thumb zone, plus a little air. It
   has to be that and not the thumb zone's height, because the caveat bar sits below the
   thumb zone - measuring the height alone leaves the band short by exactly the caveat,
   and it clips the top of Find and Add a point. Falls back to --above-caveat and
   behaves like the old card if the measurement never happens. */
body.card-peek #story-card{
  cursor:pointer;bottom:var(--peek-bottom, var(--above-caveat));max-height:42vh;
}
/* 28px was sized for a card that owned the screen. In a band the name shares the space
   with the line that matters more. */
body.card-peek .card h2{font-size:22px;line-height:1.15;margin:2px 0 6px}
body.card-peek #card-expand{
  display:block;width:100%;margin-top:8px;padding:7px 10px;min-height:34px;
  border:none;background:none;font:inherit;font-size:15px;font-weight:800;
  color:var(--sky-ink);text-align:center;cursor:pointer;
}
.sources{margin-top:10px;font-size:14px;border-top:1px solid #eee;padding-top:10px}
.sources a{color:var(--sky-ink);display:block;margin:4px 0;word-break:break-word}
.confidence{margin-top:8px;font-size:14px;color:var(--muted)}

.needscheck{background:#fff8ea;border:1.5px solid #f0d9a8;border-left:4px solid var(--sun);
  border-radius:9px;padding:9px 11px;font-size:14px !important;line-height:1.4 !important;color:#6b4c05;margin:0 0 10px !important}
.needscheck b{color:#7a5300}
/* FIELD TEST 1 (2026-08-20): the dock->eats join is the nicest thing in the app and it
   was reachable ONLY by a user who already knew to press the Docks icon. Bill sat at the
   Center Harbor dock, saw a hotel and a steamer, and concluded there was no ice cream --
   Dewey's was in the data the whole time. Green matches .dock-marker.has-stuff on
   purpose: the badge on the map and the prompt on the card are one feature. */
.dockwalk{
  display:block;width:100%;text-align:left;font-family:inherit;cursor:pointer;
  background:#eef7f2;border:2px solid #0e7c66;border-left:4px solid #0e7c66;
  border-radius:9px;padding:11px 12px;margin:0 0 10px;
  font-size:15px;font-weight:700;color:#083b31;min-height:48px;
}
.dockwalk:active{background:#dcece4}
/* Mountain sightlines. The label rides on the line between you and the summit; you
   trace it with your eye and look up. MARKER RULE: .mtn-label is a bare wrapper --
   MapLibre writes transform on it inline and a stylesheet rule here would silently
   lose. Everything visual is on .mtn-text. */
.mtn-label{cursor:pointer}
/* "We need more prominent peaks." Bigger type, a heavier ring and a real shadow, and
   the box grows to 40px to match the bh the placement maths assumes -- those two numbers
   have to move together or the pill overhangs the edge it was measured to sit inside. */
.mtn-text{
  white-space:nowrap;font-size:14px;font-weight:800;letter-spacing:.01em;
  color:#101a1f;background:#fff;border:2.5px solid #26343c;
  border-radius:999px;padding:9px 14px;box-shadow:0 2px 8px rgba(0,0,0,.38);
  min-height:40px;display:flex;align-items:center;
}
/* The gold ring lived here until .60. Removed, not restyled: it marked a strict subset
   of what the "?" marks, so keeping it meant two unsure signals over two different
   populations, and it was the one that could not be identified on the water. */

/* Markers are <button>s now so a keyboard can reach them. Strip the UA chrome HERE,
   above the individual marker rules, so each one still wins its own border, background
   and colour further down. Nothing in this rule may set position, transform or
   animation - MapLibre writes those inline and a stylesheet fight silently drops pins. */
.poi-marker,.prop-marker,.fish-marker,.route-marker,.svc-marker{
  -webkit-appearance:none;appearance:none;border:0;padding:0;margin:0;
  background:transparent;color:inherit;font:inherit;
}

/* ---------- buttons ---------- */
.btn{
  border:2px solid var(--navy);background:#fff;color:var(--navy);
  border-radius:12px;padding:12px 15px;font-size:17px;font-weight:700;cursor:pointer;min-height:56px;
}
.btn:active{transform:scale(.97)}
.btn-primary{background:var(--navy);color:#fff}
.btn-ghost{border-color:#9fb3bd;color:var(--navy)}
/* Confirming is the thing an editor does most, so it stops looking like a ghost. Green
   rather than navy: it is an affirmation, not navigation, and it must not be mistaken
   for the primary Read-it-to-us action two rows up. */
#card-confirm{background:var(--good,#1f7a52);border-color:var(--good,#1f7a52);color:#fff;
  font-size:18px;min-height:60px;flex:1 1 100%}
#card-confirm:active{transform:scale(.98)}
.btn.full{width:100%}
.btn[aria-pressed="true"]{background:var(--sun);border-color:var(--sun);color:#3a2a06}

/* ---------- nearby ticker ---------- */
.nearby{
  position:fixed;left:8px;right:8px;bottom:var(--above-caveat);z-index:22;
  background:#0a2f47;color:#fff;border-radius:12px;padding:10px 12px;
  font-size:15px;box-shadow:var(--shadow);display:flex;align-items:center;gap:8px;
}
.nearby b{color:var(--sun)}

/* ---------- drawer & sheet ---------- */
.drawer,.sheet{
  position:fixed;z-index:30;background:var(--paper);box-shadow:var(--shadow);
  display:flex;flex-direction:column;
}
.drawer{top:0;bottom:0;left:0;width:min(420px,90vw);border-radius:0 18px 18px 0;animation:slideL .2s ease}
.sheet{top:0;bottom:0;right:0;width:min(420px,92vw);border-radius:18px 0 0 18px;animation:slideR .2s ease}
@keyframes slideL{from{transform:translateX(-100%)}to{transform:none}}
@keyframes slideR{from{transform:translateX(100%)}to{transform:none}}
.drawer-head,.sheet-head{
  display:flex;align-items:center;justify-content:space-between;
  padding:calc(var(--safe-t) + 12px) 14px 10px;background:var(--navy);color:#fff;
}
.drawer-head h2,.sheet-head h2{margin:0;font-size:18px}
.drawer-tools{padding:10px 12px;background:#fff;border-bottom:1px solid #e7e2d6}
#search{width:100%;padding:12px;border:1.5px solid #d6dde1;border-radius:12px;font-size:16px}
/* ONE ROW THAT SCROLLS, not a wrapping block. There were four chips here; as of .60
   there are twelve, and wrapping them cost SEVEN ROWS of the drawer -- which is where
   the stories are. A legend that pushes the thing it describes off the screen is not
   doing its job. Same swipe as the service bar, so the gesture is already learned. */
.filters{
  display:flex;gap:6px;flex-wrap:nowrap;margin-top:10px;
  overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;
  scrollbar-width:none;padding-bottom:2px;
}
.filters::-webkit-scrollbar{display:none}
.filters .filt{flex:none;white-space:nowrap}
/* 38px was under the 44px floor on eight targets that sit inside a scrolling list, and
   13px is small for a control that doubles as the map legend. */
.filt{border:1.5px solid #cdd6db;background:#fff;border-radius:999px;padding:7px 14px;font-size:14px;font-weight:600;cursor:pointer;color:var(--navy);
  display:inline-flex;align-items:center;gap:7px;min-height:44px}
/* the chip row doubles as the map legend — same colour as the pin it filters */
.filt-dot{width:12px;height:12px;border-radius:50%;flex:none;box-shadow:0 0 0 1.5px rgba(255,255,255,.9),0 1px 2px rgba(0,0,0,.35)}
.filt.active{background:var(--navy);color:#fff;border-color:var(--navy)}
/* Each chip is its own switch as of .60, so "on" cannot be the filled navy pill any
   more -- with eight of eleven on, eight solid navy pills is a wall. ON is the normal
   chip with a FILLED colour dot; OFF fades the chip and hollows the dot to a ring of
   the same colour, so you can still see WHICH group you switched off. */
.filt-tog.off{opacity:.5;border-style:dashed;color:var(--muted)}
.filt-tog.on{border-color:#9fb0b8}
.poi-list{flex:1;overflow:auto;padding:6px 8px}
/* A <button>, not a div - these are the story list, the main way in after the map.
   display:flex keeps it block-level so it still fills the drawer. */
.poi-item{background:#fff;border-radius:12px;padding:14px 12px;margin:8px 4px;min-height:72px;box-shadow:0 1px 4px rgba(0,0,0,.06);cursor:pointer;display:flex;gap:10px;align-items:flex-start;
  -webkit-appearance:none;appearance:none;border:0;text-align:left;font:inherit;color:inherit}
.poi-item:active{background:#f0ece1}

/* ---------- About the lake ----------
   One door at the top of the stories list for the four stories that are about the whole
   lake. Navy, because it is a section header and not a story; collapsed by default,
   because the nearest real thing has to stay the first thing you see. Big enough to hit
   and read at arm's length on a moving boat -- 58 px tall, 18 px label. */
/* The Points list buckets. One row, wraps -- there are five and they are short. The
   count is set apart because it is the number you are actually reading. */
.ap-chips{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 10px}
.ap-chip{
  -webkit-appearance:none;appearance:none;font:inherit;cursor:pointer;
  min-height:40px;padding:7px 12px;border-radius:999px;
  background:#fff;border:1.5px solid #c2ccd3;color:var(--ink);
  font-size:15px;font-weight:700;display:inline-flex;align-items:center;gap:7px;
}
.ap-chip i{font-style:normal;font-weight:800;font-size:13px;opacity:.72;
  font-variant-numeric:tabular-nums}
.ap-chip.on{background:var(--navy);border-color:var(--navy);color:#fff}
.ap-chip.on i{opacity:.85}

.about-lake{margin:8px 4px 12px}
.about-head{
  width:100%;min-height:58px;display:flex;align-items:center;gap:10px;
  background:var(--navy);color:#fff;border:0;border-radius:12px;
  padding:12px 14px;font:inherit;text-align:left;cursor:pointer;
  -webkit-appearance:none;appearance:none;
}
.about-head:active{background:var(--navy-2)}
.about-head.open{border-radius:12px 12px 0 0}
.about-ic{font-size:20px;line-height:1}
.about-t{flex:1;font-size:18px;font-weight:800;letter-spacing:.2px}
.about-n{
  min-width:26px;height:26px;padding:0 7px;border-radius:999px;
  background:rgba(255,255,255,.20);
  font-size:14px;font-weight:800;line-height:26px;text-align:center;
}
.about-chev{font-size:22px;font-weight:800;opacity:.9;transition:transform .18s ease}
.about-head.open .about-chev{transform:rotate(90deg)}
.about-body{background:rgba(11,61,92,.06);border-radius:0 0 12px 12px;padding:2px 6px 8px}
.about-body .poi-item{margin:8px 2px;min-height:64px}
/* Bill asked for safety BELOW A LINE. The line is the point: everything above it is a
   story, and the thing below it is not. */
.about-rule{height:1px;background:#c3ced6;margin:12px 10px 4px}
.about-safety h3{color:var(--navy)}
.poi-dot{width:12px;height:12px;border-radius:50%;margin-top:5px;flex:none}
/* The story list is the main way into the app after the map, and sun mode never touched
   a pixel of it. 15px body was below the 16px floor on the surface people actually read. */
.poi-item h3{margin:0 0 3px;font-size:19px}
.poi-item p{margin:0;font-size:16px;color:var(--muted);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.poi-meta{font-size:13px;color:var(--sky-ink);font-weight:800;margin-top:5px}
.drawer-foot{padding:10px 14px;background:#fff;border-top:1px solid #e7e2d6;font-size:13px;color:var(--muted)}

.sheet-body{flex:1;overflow:auto;padding:14px}
.row{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:12px 0}
.row .muted{font-size:14px}
.small{font-size:14px}
.muted{color:var(--muted)}
.center{text-align:center}
hr{border:none;border-top:1px solid #e2ddd0;margin:14px 0}
.auth-box{background:#fff;border-radius:12px;padding:12px;margin:6px 0}
.auth-box label{font-weight:700;font-size:14px;display:block;margin-bottom:6px}
.auth-box input{width:100%;padding:12px;border:1.5px solid #d6dde1;border-radius:10px;font-size:16px;margin-bottom:8px}
.or{text-align:center;color:var(--muted);font-size:13px;margin:8px 0}
.code-row{display:flex;gap:8px}
.code-row input{flex:1}
.seg{display:flex;border:1.5px solid var(--navy);border-radius:10px;overflow:hidden}
.seg-btn{border:none;background:#fff;color:var(--navy);padding:9px 14px;font-weight:700;cursor:pointer}
.seg-btn.active{background:var(--navy);color:#fff}
.progress{height:12px;background:#e2ddd0;border-radius:999px;position:relative;margin:4px 0 8px;overflow:hidden}
#offline-bar{height:100%;width:0;background:var(--sky);transition:width .2s}
#offline-pct{position:absolute;right:8px;top:-3px;font-size:11px;color:var(--ink)}

/* ---------- modal ---------- */
.modal{position:fixed;inset:0;z-index:50;background:rgba(6,22,33,.72);display:flex;align-items:center;justify-content:center;padding:16px}
.modal-card{background:#fff;border-radius:18px;padding:20px;max-width:440px;max-height:90vh;overflow:auto;box-shadow:var(--shadow)}
.modal-card h2{margin:0 0 10px;font-size:22px;color:var(--navy)}
.modal-card p{font-size:15.5px;line-height:1.5;margin:0 0 12px}
.ack{display:flex;gap:10px;align-items:flex-start;font-size:15px;margin:6px 0 14px;line-height:1.4}
.ack input{width:22px;height:22px;margin-top:1px;flex:none}

/* ---------- the mailing list ask ----------
   Shown once, only after three stories, only to someone who's clearly enjoying it.
   Dismissible, never blocks the map, never asked twice. */
#signup-bar{
  position:fixed;left:8px;right:8px;bottom:var(--above-caveat);z-index:26;
  background:#fff;border-radius:16px;box-shadow:var(--shadow);
  padding:16px 16px 12px;animation:rise .22s ease;
}
#signup-x{position:absolute;top:6px;right:6px;border:none;background:transparent;
  font-size:20px;color:var(--muted);width:44px;height:44px;cursor:pointer}
.su-copy{font-size:16px;line-height:1.45;color:#152a35;margin:0 34px 12px 0}
.su-copy b{display:block;font-size:20px;color:var(--navy);margin-bottom:3px}
.su-row{display:flex;gap:8px}
#signup-email{flex:1;min-width:0;padding:13px;border:2px solid #9fb3bd;border-radius:12px;font-size:17px}
.su-row .btn{flex:none;padding:12px 18px}
.su-msg{margin:8px 0 0;font-size:14px;color:var(--muted);min-height:1px}

/* ---------- toast ---------- */
/* 🔴 A TOAST MAY NEVER EAT A TAP.
   This was position:fixed at z-index 60 -- above every marker -- up to 460px wide, with
   cursor:pointer and a click handler that hid it. So it was a transparent lid over a band
   of the map. Bill, from the boat: tap Property, get the "zoom in" hint, then tap a pin
   and NOTHING HAPPENS. The tap was landing on the toast, which quietly dismissed itself
   and swallowed the event. Reproduced in the harness: aimed at Sandy Island, the toast
   received the tap, the card never opened.
   pointer-events:none means it is now what it always claimed to be -- a message, not a
   surface. Only the STICKY variant takes pointer events back, because that one carries a
   real ✕ and is meant to be dismissed by hand.
   THE GENERAL RULE: anything that floats over the map and is not a control gets
   pointer-events:none. The map is the primary surface; nothing decorative outranks it. */
.toast{
  position:fixed;left:50%;transform:translateX(-50%);bottom:calc(var(--above-caveat) + 76px);z-index:60;
  background:var(--ink);color:#fff;padding:11px 16px;border-radius:999px;font-size:14px;
  box-shadow:var(--shadow);max-width:90vw;text-align:center;
  pointer-events:none;
  /* 16px, not 14: it is on screen for 3.2 seconds and it is competing with sun. */
  font-size:16px;font-weight:600;padding:13px 18px;
}
#toast.sticky{pointer-events:auto}
.hidden{display:none !important}

/* top bar: fit four controls, let brand shrink */
#topbar{gap:6px}
.brand{min-width:0}
.brand-name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#btn-account{position:relative}
.acct-dot{position:absolute;top:9px;right:9px;width:11px;height:11px;border-radius:50%;background:var(--good);border:2px solid #fff}

/* account modal */
.auth-seg{width:100%;margin:12px 0 10px}
.auth-seg .seg-btn{flex:1;font-size:14px}
#account-modal input{width:100%;padding:13px;border:1.5px solid #d6dde1;border-radius:10px;font-size:16px;margin:6px 0}
.btn-row{display:flex;gap:8px;margin-top:8px}
.btn-row .btn{flex:1}
#acct-who{font-size:16px;margin:2px 0 14px}
/* sits over the dark surround, and lets taps through to it so it never blocks the exit */
.modal-hint{
  position:absolute;left:0;right:0;bottom:calc(var(--safe-b) + 12px);
  text-align:center;color:#cfe0ea;font-size:12.5px;opacity:.85;pointer-events:none;
}
/* Was --sky at 4.42:1 - the only WCAG AA failure in the visible UI, and it carried
   "Continue as a guest for now", the escape hatch a first-run user needs most. */
.linkbtn{background:none;border:none;color:var(--sky-ink);font-size:15px;font-weight:600;cursor:pointer;padding:8px 2px}
.center-block{display:block;width:100%;text-align:center;margin-top:12px;border-top:1px solid #eee;padding-top:12px}
.back-link{display:inline-block;margin:0 0 6px}
#account-modal .btn.full{margin-top:2px}
#view-login .linkbtn,#view-create .linkbtn{display:block}

/* ---------- the thumb zone ----------
   Everything you press lives in the bottom third. The top 200px of a 390x844 screen
   is the regrip zone, and regripping on a moving boat means briefly not holding the
   phone — or taking a hand off the wheel. So nothing tappable goes up there. */
#thumb-zone{
  position:fixed;left:0;right:0;bottom:calc(var(--safe-b) + var(--caveat-h));
  z-index:19;padding:0 10px 8px;pointer-events:none;
  display:flex;flex-direction:column;gap:8px;
  touch-action:none; /* a stray pinch on the chrome must never zoom the PAGE */
}
/* Only real controls swallow touches. The container rows are see-through.
   This matters more than it looks: .tz-row is a full-width flex row and used to
   eat every touch across the bottom 64px of the screen, most of it empty space.
   Half the screen was dead to the map, which is why a two-finger pinch down there
   turned into a one-finger drag and the map felt stuck. */
#thumb-zone>*{pointer-events:none}
#thumb-zone>button,#do-bar,.round-btn,.do{pointer-events:auto}
.tz-row{display:flex;align-items:flex-end;gap:8px;pointer-events:none}
.tz-row>*{pointer-events:auto}
.tz-spacer{flex:1;pointer-events:none}
.tz-stack{display:flex;flex-direction:column;gap:8px;align-items:flex-end;pointer-events:none}
.tz-stack>*{pointer-events:auto}
.round-btn{
  width:64px;height:64px;min-width:64px;border:none;border-radius:18px;
  background:rgba(255,255,255,.97);color:var(--navy);
  font-size:26px;font-weight:800;box-shadow:var(--shadow);cursor:pointer;
  display:flex;align-items:center;justify-content:center;
}
.round-btn.sm{width:52px;height:52px;min-width:52px;font-size:26px;border-radius:15px}
.round-btn:active{transform:scale(.94)}
#btn-locate.tracking{background:var(--navy);color:#fff}

/* #layer-bar is GONE. It had no element in index.html and had not for a long time, but
   .layer-seg was still live - the three map buttons in the Do bar carry that class to
   pick up .do.layer-seg.active. So the orphan's flex:1 and min-height:56px landed on
   them, and only them: History, Property and Fishing rendered 50x56 at y691 while
   Raft-ups, Docks and Gas rendered 50x50 at y694. Six icons, two heights, two baselines,
   one thumb, on a moving boat. Everything .do needs it sets itself; the active state
   moved down to .do.layer-seg.active. */

#btn-nearby{
  /* was width:100% — a full-width button is a full-width wall your fingers hit.
     Content width leaves live map on both flanks. */
  align-self:center;max-width:100%;padding:0 22px;
  min-height:52px;border:none;border-radius:14px;
  background:rgba(10,47,71,.95);color:#fff;font-family:inherit;
  font-size:16px;font-weight:800;cursor:pointer;box-shadow:var(--shadow);
}
#btn-nearby:active{transform:scale(.98)}
/* one thing at a time: when a story card is up it owns the bottom of the screen */
/* :not(.card-peek) is the whole point of the peek state. A card you OPENED may take the
   screen; a card that opened ITSELF while you were driving may not take the controls
   with it. */
body.placing #thumb-zone,body.sheet-open #thumb-zone,body.card-open:not(.card-peek) #thumb-zone{display:none}

/* the lakes list is now only reachable from the menu — it's a rare action */
#lakes-panel{
  position:fixed;left:50%;transform:translateX(-50%);top:calc(var(--safe-t) + 70px);
  z-index:31;background:#fff;border-radius:14px;box-shadow:var(--shadow);padding:10px;
  width:min(320px,90vw);max-height:min(62vh,520px);overflow:auto;
  animation:drop .16s ease;
}
@keyframes drop{from{transform:translate(-50%,-8px);opacity:0}to{transform:translateX(-50%);opacity:1}}

/* read-aloud — the whole point of the app is the people in the boat, not the screen */
.btn-listen{
  background:var(--sun);border-color:var(--sun);color:#3a2a06;
  font-size:19px;font-weight:800;margin:2px 0 10px;min-height:60px;
}
.btn-listen.speaking{background:var(--navy);border-color:var(--navy);color:#fff}
.lakes-title{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);padding:2px 6px 6px}
.lake-item{display:block;width:100%;text-align:left;border:none;background:#eef1ee;color:var(--navy);border-radius:9px;padding:10px 11px;font-size:14px;font-weight:700;margin-top:4px;cursor:pointer;line-height:1.15}
.lake-item:first-child{margin-top:0}
.lake-item.active{background:var(--navy);color:#fff}
.lake-item:active{transform:scale(.98)}
.lp-sep{margin-top:10px}
.layer-item{display:block;width:100%;text-align:left;border:2px solid transparent;background:#eef1ee;color:var(--navy);border-radius:9px;padding:11px 12px;font-size:15px;font-weight:800;margin-top:5px;cursor:pointer;line-height:1.1}
.layer-item:first-child{margin-top:0}
.layer-item.active{background:var(--sun);color:#3a2a06;border-color:var(--sun)}
.layer-item:active{transform:scale(.98)}
.tour-item{display:block;width:100%;text-align:left;border:2px solid transparent;background:#eef1ee;color:var(--navy);border-radius:9px;padding:10px 12px;font-size:14px;font-weight:700;margin-top:5px;cursor:pointer;line-height:1.15}
.tour-item:first-child{margin-top:0}
.tour-item.active{background:var(--navy);color:#fff;border-color:var(--navy)}
.tour-item:active{transform:scale(.98)}
.build-badge{font-weight:800;color:var(--navy);background:#eef1ee;border-radius:7px;padding:3px 9px;font-variant-numeric:tabular-nums}
.name-row{display:flex;gap:8px}
.name-row input{flex:1;min-width:0}
#acct-name-box{margin:4px 0 12px;padding-bottom:12px;border-bottom:1px solid #eef1ee}
.fb-sec-title{font-weight:800;color:var(--navy);font-size:13px;text-transform:uppercase;letter-spacing:.04em;margin:0 0 6px}
.fb-item{background:#f6f8f7;border-radius:10px;padding:10px 12px;margin-bottom:8px}
.fb-item p{margin:0;font-size:15px;line-height:1.35;color:var(--navy)}
.fb-item .fb-resp{margin-top:6px;font-size:14px;color:#3a5563;border-left:3px solid var(--sun);padding-left:8px}
.fb-pill{display:inline-block;font-size:11px;font-weight:800;padding:2px 8px;border-radius:20px;margin-bottom:5px;text-transform:uppercase;letter-spacing:.03em}
.fb-pill.new{background:#e7edf1;color:#4a6472}
.fb-pill.planned{background:#dfe9f5;color:#1a5fa0}
.fb-pill.progress{background:#fff0d6;color:#8a5a00}
.fb-pill.done{background:#dff0e4;color:#1c7a3f}
.fb-pill.declined{background:#efe3e1;color:#8a4b40}
/* Property pins draw with anchor:"center", so the shape is symmetric and needs no
   rotation. The old transform:rotate(0deg) did nothing but imply it did. Press
   feedback is brightness, not scale — a transform here would fight MapLibre. */
/* The way back into Admin. z-index 19: under #topbar (20) so the menu still wins, over
   the north badge (17) and the route bar (18). It is a CONTROL and it lives OUTSIDE
   #thumb-zone, so it needs no pointer-events exception -- and it must stay outside,
   because anything inside that wrapper needs adding to its pointer-events rule. */
#admin-back{
  position:fixed;left:50%;transform:translateX(-50%);
  top:calc(var(--safe-t) + 76px);z-index:19;
  -webkit-appearance:none;appearance:none;font:inherit;cursor:pointer;
  background:var(--navy);color:#fff;border:2px solid #fff;border-radius:999px;
  padding:11px 18px;min-height:48px;font-size:16px;font-weight:800;
  box-shadow:0 3px 14px rgba(0,0,0,.42);white-space:nowrap;
}
#admin-back:active{background:var(--navy-2);transform:translateX(-50%) scale(.97)}

/* Every bottom button carries its word as of .72. The glyph shrinks a little to pay
   for it and the button grows a little; the row is still one line and still inside the
   thumb zone. An icon nobody can name is a control nobody uses. */
.do-g{font-size:19px;line-height:1;display:block}
.do-w{font-size:9.5px;font-weight:800;line-height:1.15;display:block;margin-top:1px;
  letter-spacing:.1px;white-space:nowrap}
#do-bar .do{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0}

/* \u{1F534} THE PROPERTY PIN HAD ITS OWN COLOUR, AND NOBODY EVER MEASURED IT.
   This background was #7a5c3e, hard-coded, and it is the reason the house layer looked
   like the hotel layer: 16.6 dE from hotel_estate #a8845f, below the 28.2 floor the
   eleven group colours are held to. It was also 47.1 dE from #c98fb8 -- the colour the
   property chip, its dot and the legend have always shown -- so the legend and the map
   disagreed about the layer.
   The Lab check only ever ran over the GROUPS table. This colour was not in it, exactly
   like the fallback colour that .63 had to fix for the same reason. THIRD TIME.
   markerEl() now sets the background from groupColor(); this value is only the fallback
   for the split second before JS runs, and it must stay equal to GROUPS.property.color. */
.prop-marker{width:30px;height:30px;border-radius:50%;border:2.5px solid #fff;background:#c98fb8;box-shadow:0 2px 6px rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;font-size:15px;cursor:pointer}
.prop-marker:active{filter:brightness(.85)}
.prop-grid{margin:6px 0 4px}
.prop-row{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px solid #eef1ee;font-size:15px}
.prop-row span{color:#5a6b74}
.prop-row b{color:var(--navy);text-align:right;font-variant-numeric:tabular-nums}
.fish-marker{width:28px;height:28px;border-radius:50%;border:2.5px solid #fff;box-shadow:0 2px 5px rgba(0,0,0,.45);color:#fff;font-size:15px;display:flex;align-items:center;justify-content:center;cursor:pointer}
.fish-marker.sm{width:20px;height:20px;font-size:11px}

/* install pop-up steps */
.modal-card ol.steps{margin:8px 0 10px;padding-left:22px}
.modal-card ol.steps li{font-size:15px;line-height:1.5;margin:7px 0}
#install-body p{font-size:15px;line-height:1.5;margin:0 0 8px}

/* feedback lightbulb + form */
#feedback-modal textarea{width:100%;padding:12px;border:1.5px solid #d6dde1;border-radius:10px;font-size:16px;margin:10px 0;font-family:inherit;resize:vertical}
#feedback-modal input{width:100%;padding:12px;border:1.5px solid #d6dde1;border-radius:10px;font-size:16px;margin-bottom:8px}
#fb-kind{margin:12px 0}
#fb-kind .seg-btn{flex:1}
#team-modal input{width:100%;padding:12px;border:1.5px solid #d6dde1;border-radius:10px;font-size:16px;margin:8px 0}
.team-row{display:flex;align-items:center;justify-content:space-between;gap:10px;background:#f6f8f7;border-radius:9px;padding:9px 11px;margin-bottom:6px;font-size:14.5px;color:var(--navy)}
.team-tag{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);flex:none}
.team-tag.scout{background:var(--sun);color:#3a2a06;border-radius:20px;padding:2px 9px}
.team-pick{display:block;width:100%;text-align:left;border:1.5px solid #d6dde1;background:#fff;color:var(--navy);
  border-radius:9px;padding:11px 12px;margin-bottom:6px;font-size:14.5px;font-weight:600;cursor:pointer;min-height:46px}
.team-pick:active{background:#eef4f8}

/* make "Wrong spot?" impossible to miss on the story card */
#card-wrongspot{border-color:var(--sun);background:#fff8ea;color:#7a5300;font-weight:800}
#card-fixloc{border-color:var(--navy);background:var(--navy);color:#fff;font-weight:800}

/* guided tours: numbered markers, bar, picker */
.route-marker{width:27px;height:27px;border-radius:50%;border:3.5px solid #fff;box-shadow:0 2px 5px rgba(0,0,0,.45);color:#fff;font-weight:800;font-size:14px;display:flex;align-items:center;justify-content:center;cursor:pointer}
#route-bar{position:fixed;left:50%;transform:translateX(-50%);top:calc(var(--safe-t) + 114px);z-index:18;background:var(--navy);color:#fff;border-radius:999px;padding:7px 8px 7px 14px;display:flex;align-items:center;gap:9px;box-shadow:var(--shadow);max-width:92vw}
#route-bar .route-tag{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;opacity:.85}
#route-name{font-weight:700;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:52vw}
#route-exit{border:none;background:rgba(255,255,255,.2);color:#fff;border-radius:999px;padding:6px 13px;font-weight:700;font-size:13px;cursor:pointer}
.route-item{display:block;width:100%;text-align:left;background:#fff;border:1.5px solid #e2ddd0;border-radius:12px;padding:12px;margin:8px 0;cursor:pointer}
.route-item:active{background:#f0ece1}
.route-item h3{margin:0 0 4px;font-size:16px;color:var(--navy)}
.route-item p{margin:0 0 4px;font-size:13px;color:var(--muted)}

/* sun mode: high-contrast + larger text for glare */

/* user location marker */
/* YOU-ARE-HERE — split into wrapper + core (2026-08-07), for the same reason the
   story pin was split. MapLibre writes transform inline on the element you hand it,
   and a transformed element is a stacking context, so a child can never paint BEHIND
   it — a z-index:-1 child lands on top of the parent's own background. The heading
   cone has to sit under the disc. So .me-dot is now a bare box that owns nothing but
   size, .me-core is the disc, and .me-cone stacks beneath it. Footprint is unchanged:
   box-sizing is border-box globally, so the old .me-dot was 18x18 overall, and the
   marker's default anchor "center" still lands the disc's centre on the fix. */
.me-dot{width:18px;height:18px}
.me-core{
  width:18px;height:18px;border-radius:50%;background:var(--sky);border:3px solid #fff;
  box-shadow:0 0 0 4px rgba(26,127,181,.35),0 1px 4px rgba(0,0,0,.4);
  position:relative;z-index:1;
}
/* HEADING CONE — apex on the dot, pointing where the boat is going. The map holds
   still and you turn on it, which is the whole point: the stories are written
   geographically ("across the Broads") and stop resolving on a map that spins.
   farthest-side, NOT closest-side: the gradient origin sits ON the bottom edge, so
   the closest side is 0px away, and a zero-radius radial gradient renders as its
   LAST colour stop — which here is fully transparent. farthest-side gives the
   48 x 104 ellipse that was actually wanted. */
.me-cone{
  position:absolute;
  left:50%;top:50%;
  width:96px;height:104px;
  transform-origin:50% 100%;                    /* apex, sitting on the dot */
  transform:translate(-50%,-100%) rotate(0deg);
  background:radial-gradient(farthest-side at 50% 100%,
              rgba(26,127,181,.55) 0%,
              rgba(26,127,181,.30) 45%,
              rgba(26,127,181,0)  100%);
  clip-path:polygon(50% 100%, 8% 6%, 92% 6%);   /* ~45 deg wedge */
  pointer-events:none;
  opacity:0;transition:opacity .25s ease;
  z-index:0;
}
.me-cone.on{opacity:1}
/* \u{1F534} THE POSITION SAYS WHEN IT HAS GONE STALE.
   Before .74 the blue dot sat wherever the last fix put it, for ever, looking exactly
   as confident as a live one. Hollow centre, no cone, and a slow pulse: it still shows
   the last place we actually knew, because that is genuinely useful, but it stops
   claiming to be where you are now. Same doctrine as the heading cone hiding below
   1.5 kt -- a mark that lies is worse than no mark. */
.me-dot.stale .me-core{
  background:transparent;border-color:#fff;
  box-shadow:0 0 0 2px rgba(11,61,92,.55), 0 1px 4px rgba(0,0,0,.4);
  animation:otl-stale 2.2s ease-in-out infinite;
}
.me-dot.stale .me-cone{opacity:0 !important}
@keyframes otl-stale{0%,100%{opacity:.45}50%{opacity:1}}
/* STORY PIN — the teardrop, rebuilt to the marker rule (2026-08-02).
   The old one put border-radius AND transform:rotate(-45deg) on .poi-marker itself.
   MapLibre writes transform inline, so the rotation never applied and every story pin
   drew as a blob with its square corner pointing bottom-LEFT instead of down.

   Now .poi-marker is a bare box that owns nothing but size, and .pm-shape does the work.

   The numbers are not eyeballed. Rotating a square by 45 deg puts its corner
   0.70711 x S directly below the shape's centre, so the tip sits 1.20711 x S below the
   wrapper's top and the rotated diamond is 1.41421 x S wide. Markers use anchor:"bottom",
   which lands the wrapper's bottom-centre on the coordinate — so sizing the wrapper to
   exactly those two numbers puts the visible tip on the place it marks, to a
   hundredth of a pixel. S=24 -> 33.94 x 28.97. S=15 -> 21.21 x 18.11. */
/* 🔴 THE WRAPPER MUST BE SQUARE, AND THE OLD ARITHMETIC ASSUMED IT WAS NOT.
   Bill: "keep the pins in the right places, that's a priority, we have a real issue
   with that." He does. Measured at z12.4: the visible teardrop TIP sat 2.47 px BELOW the
   coordinate it claims -- about 60 m -- on every normal pin. Growing the pin for
   legibility made it 3.61 px before this rule was corrected.
   WHY. .poi-marker is a <button>, and a button VERTICALLY CENTRES its content. The note
   in CLAUDE.md ("the tip is 1.20711 x S below the wrapper top") is only true if the shape
   is TOP-ALIGNED. It is not. Centred, the shape centre sits at H/2 and the rotated
   corner reaches H/2 + 0.70711 x S. Setting that equal to H -- because the marker is
   anchored "bottom", so the tip must land exactly on the wrapper bottom -- gives
                     H = 1.41421 x S,  NOT 1.20711 x S.
   The wrapper is therefore SQUARE: the same 1.41421 x S that was already correct for the
   width was needed for the height all along. S=29 -> 41.01, so 41 x 41, residual 0.01 px.
   ⚠ AND THIS IS WHY `quiet` NO LONGER CHANGES SIZE. Shrinking only the shape inside a
   fixed wrapper re-centres it and lifts the tip off the anchor the other way -- measured
   0.86 px high on the hotel pins. Quiet is now colour and opacity only. A modifier that
   changes a marker's size has to change its wrapper too, or it moves the pin. */
.poi-marker{width:41px;height:41px;cursor:pointer}
.pm-shape{
  width:29px;height:29px;margin:0 auto;box-sizing:border-box;
  border-radius:50% 50% 50% 0;transform:rotate(-45deg);
  border:4px solid #fff;box-shadow:0 2px 8px rgba(0,0,0,.62);
}
/* The type glyph. A SIBLING of .pm-shape, never a child: the shape is rotated -45deg
   and a child would rotate with it. Positioned against MapLibre's own position:absolute
   on the marker element -- .poi-marker itself must stay free of position. */
/* 10px type in a 17px disc was decorative, not legible. Bill: "everybody's squinting to
   see anything -- make things bigger and more obvious, or not do them at all."
   top is the shape CENTRE, which is H/2 = 20.5 now the wrapper is square. */
.pm-icon{
  position:absolute;left:50%;top:20.5px;transform:translate(-50%,-50%);
  width:23px;height:23px;border-radius:50%;background:#fff;
  display:flex;align-items:center;justify-content:center;
  font-size:14px;line-height:1;pointer-events:none;
  box-shadow:0 0 0 1px rgba(0,0,0,.2);
}
/* "We are not sure where this is." Group colour is kept so you still know WHAT it is. */
.pm-q{
  position:absolute;top:2px;right:0;
  min-width:22px;height:22px;border-radius:999px;
  background:var(--sun);color:#3a2a06;border:2.5px solid #fff;
  font-size:15px;font-weight:900;line-height:17px;text-align:center;
  box-shadow:0 1px 4px rgba(0,0,0,.45);pointer-events:none;
}
.fish-marker .pm-q,.prop-marker .pm-q{top:-6px;right:-6px}
/* THE GROUP RAIL. Chrome, not a marker -- it may safely own position and transform.
   z-index sits with the map chrome, below #route-bar(18) and the north badge(17) so it
   can never cover a control that has to be reachable. */
/* The compass is a real switch as of .64, so it is always tappable. It was
   pointer-events:none except when off north -- inert in exactly the state where a lost
   person reaches for it. .bow-up is the mode; .off-north is only "the map is rotated". */
#north-badge{pointer-events:auto;cursor:pointer}
#north-badge.bow-up{box-shadow:0 0 0 2.5px var(--sun),0 2px 8px rgba(0,0,0,.35)}
#group-rail{
  position:absolute;left:6px;top:calc(env(safe-area-inset-top,0px) + 84px);
  z-index:16;display:flex;flex-direction:column;gap:3px;
  /* max() floors it: vh resolves to ZERO in a backgrounded tab, which collapsed the
     whole rail to 10px and made every toggle unreachable. Nobody is looking at a
     background tab, but a unit that can degenerate to nothing has no business being the
     only thing standing between the user and a control. */
  /* visible, not auto: the sliding names have to escape the rail horizontally. The
     vertical scroll that overflow-y:auto provided is kept by capping the height and
     letting the rail scroll itself. */
  /* 12 rows overflowed a 56vh cap and left a half-row sliced off the bottom, which
     reads as broken rather than as scrollable. A taller cap fits the current set
     whole; if the taxonomy work cuts the count this stops mattering. */
  max-height:max(180px, 66vh);overflow-y:auto;overflow-x:visible;scrollbar-width:none;
  padding:5px;border-radius:16px;
  background:rgba(255,255,255,.82);backdrop-filter:blur(6px);
  box-shadow:0 2px 10px rgba(0,0,0,.22);
}
#group-rail::-webkit-scrollbar{display:none}
#group-rail.hidden{display:none}
.grp-btn{
  -webkit-appearance:none;appearance:none;border:0;padding:0;margin:0;background:transparent;
  position:relative;width:36px;height:36px;flex:none;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
}
/* 36px is under the 44px floor and that is deliberate: the TAP TARGET is padded out to
   46px by the gap and the rail padding, while the visible dot stays small enough that
   eleven of them fit above the thumb zone on a 844px screen. Measured, not guessed. */
.grp-btn::after{content:"";position:absolute;inset:-5px}
.grp-dot{
  width:30px;height:30px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;font-size:15px;line-height:1;
  box-shadow:0 1px 3px rgba(0,0,0,.3);
}
/* An OFF row still has to be findable, or you cannot turn it back on. .42 opacity with
   full greyscale looked right on a desk and would vanish on a wet screen in the sun --
   which is the only screen this app runs on. Enough colour survives to tell the rows
   apart, and the missing check is what says "off". */
/* The name slides out of the icon and retracts. position:absolute against the button so
   the rail never changes width; pointer-events:none so it can never eat the next tap --
   the same rule the toast had to learn in .65. */
.grp-name{
  position:absolute;left:34px;top:50%;transform:translateY(-50%) translateX(-6px);
  white-space:nowrap;pointer-events:none;opacity:0;
  background:var(--navy);color:#fff;font-size:13.5px;font-weight:700;
  padding:7px 12px;border-radius:0 999px 999px 0;
  box-shadow:0 2px 8px rgba(0,0,0,.35);
  transition:opacity .16s ease,transform .16s ease;
}
.grp-btn.saying .grp-name{opacity:1;transform:translateY(-50%) translateX(0)}
@media (prefers-reduced-motion:reduce){.grp-name{transition:none}}
.grp-btn.off{opacity:.62}
.grp-btn.off .grp-dot{filter:grayscale(.55)}
.grp-ck{
  position:absolute;right:-1px;bottom:-1px;
  width:15px;height:15px;border-radius:50%;
  background:var(--navy);color:#fff;border:2px solid #fff;
  font-size:9px;font-weight:900;line-height:11px;text-align:center;
}
.grp-btn.off .grp-ck{display:none}
/* A quiet group recedes: a size down and a little transparent, still a real pin. */
/* Quiet is COLOUR AND OPACITY ONLY as of .66. It used to shrink the shape inside an
   unchanged wrapper, which re-centred it and lifted the tip 0.86 px off the anchor on
   all sixteen hotel pins. Recede by tone, never by geometry. */
.poi-marker.quiet .pm-shape{opacity:.8}
.poi-marker.quiet .pm-icon{opacity:.9}
/* Square here too: S=19 -> 19 x 1.41421 = 26.87, so 27 x 27, residual 0.13 px. */
.poi-marker.sm{width:27px;height:27px}
.poi-marker.sm .pm-shape{width:19px;height:19px;opacity:.9;border-width:2.5px}
/* Zoomed out the pins are 15 px and there is no room for a glyph or a badge. Colour
   alone carries it there, which is why the colours had to stop colliding first. */
.poi-marker.sm .pm-icon,.poi-marker.sm .pm-q{display:none}

/* ---------- permanent caveat strip: always there, never dismissible ---------- */
#caveat-bar{
  position:fixed;left:0;right:0;bottom:0;z-index:23;touch-action:none;
  height:calc(var(--caveat-h) + var(--safe-b));
  padding:0 6px calc(var(--safe-b)) 6px;
  display:flex;align-items:stretch;
  background:rgba(10,25,35,.94);
  box-shadow:0 -2px 10px rgba(0,0,0,.3);
}
#caveat-safety{
  flex:1;min-width:0;display:flex;align-items:center;justify-content:center;gap:6px;
  border:none;background:none;cursor:pointer;text-align:center;padding:0 4px;
  color:#fff;font-family:inherit;font-size:12.5px;line-height:1.25;
}
#caveat-safety:active{opacity:.7}
.caveat-warn{color:var(--sun);font-size:13px;flex:none}
/* IT LOOKED CUT OFF BECAUSE IT WAS. Ellipsis on the one line that is never allowed to
   be dismissed reads as a broken layout, and a truncated safety notice is worse than a
   small one. Two short lines, always whole. */
.caveat-text{white-space:normal;line-height:1.22;font-size:12.5px;min-width:0}
.caveat-more{color:var(--sun);font-weight:800;flex:none;text-decoration:underline}

/* Feedback lived in the menu, two taps deep, and nobody found it. It now sits on
   the one strip that is always on screen, next to the version — which is exactly
   the pair of things you want when something looks wrong. */
/* A speech bubble on its own is not a word. Bill: "I am not 100% sure it is clear that
   button is what you use to send feedback -- it probably needs to say Feedback." */
#caveat-fb{
  flex:none;display:flex;align-items:center;justify-content:center;gap:5px;border:none;cursor:pointer;
  background:rgba(255,255,255,.16);color:#eaf3f8;font-weight:700;
  border-radius:7px;margin:4px 6px 4px 0;padding:0 11px;font-size:13px;line-height:1;
}
.fb-word{font-size:12.5px;letter-spacing:.01em}
#caveat-fb:active{background:rgba(255,255,255,.2)}
/* THE LOCATE SPINNER. The glyph is the button's own text, so it is hidden and a ring
   is spun in its place -- no extra element, nothing to position, and the 56px tap
   target is untouched. Under prefers-reduced-motion it pulses instead of spinning:
   still obviously working, no rotation. */
#btn-locate.seeking{color:transparent;position:relative}
#btn-locate.seeking::after{
  content:"";position:absolute;left:50%;top:50%;width:22px;height:22px;margin:-11px 0 0 -11px;
  border-radius:50%;border:3px solid rgba(11,61,92,.25);border-top-color:var(--navy);
  animation:otl-spin .8s linear infinite;
}
@keyframes otl-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
  #btn-locate.seeking::after{animation:otl-pulse 1.1s ease-in-out infinite;border-top-color:rgba(11,61,92,.25)}
  @keyframes otl-pulse{0%,100%{opacity:.3}50%{opacity:1}}
}
#caveat-fb.nudge{background:var(--sun);color:#3a2a06}

/* the version, always on screen. tap it to check for an update. */
#caveat-ver{
  flex:none;display:flex;align-items:center;border:none;cursor:pointer;
  background:rgba(255,255,255,.10);color:#cfe0ea;
  border-radius:7px;margin:4px 0;padding:0 9px;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:11.5px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap;
}
#caveat-ver:active{background:rgba(255,255,255,.2)}
#caveat-ver.checking{color:var(--sun)}
#caveat-ver.stale{background:var(--sun);color:#3a2a06;animation:verpulse 1.4s ease-in-out infinite}
#caveat-ver.fresh{background:#1c5c38;color:#c9f5d8}
@keyframes verpulse{0%,100%{opacity:1}50%{opacity:.55}}

/* narrow phones: let the caveat wrap, and give the strip room to do it */
@media (max-width:430px){
  :root{--caveat-h:48px}
  /* 10.8px was the smallest type in the app, on the one line that is never dismissible
     and is the safety disclaimer. --caveat-h grows to carry it. */
  .caveat-text{white-space:normal;font-size:12px;line-height:1.3}
  .caveat-more{display:none}
  #caveat-ver{font-size:11px;padding:0 7px}
  #caveat-fb{font-size:12px;padding:0 8px;margin-right:5px}
  /* The word STAYS at phone width -- hiding it here would have hidden it on exactly
     the device the complaint came from. It shrinks instead, and the version button
     gives up the room. */
  .fb-word{font-size:11px}
  #caveat-ver{font-size:10px;padding:0 6px}
}

/* ---------- safety card ---------- */
.sf-block{border-top:1px solid #e7ecef;padding-top:12px;margin-top:14px}
.sf-block:first-of-type{border-top:none}
.sf-h{font-weight:800;color:var(--navy);font-size:13px;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px}
.sf-urgent .sf-h{color:var(--bad-ink)}
.sf-call{
  display:flex;align-items:baseline;gap:10px;text-decoration:none;
  background:#eef4f8;border:1.5px solid #cfe0ea;border-radius:11px;
  padding:11px 13px;margin-bottom:7px;min-height:48px;
}
.sf-call b{color:var(--navy);font-size:18px;font-variant-numeric:tabular-nums}
.sf-call span{color:var(--muted);font-size:12.5px}
.sf-urgent .sf-call{background:#fdeeec;border-color:#f0c4bd}
.sf-urgent .sf-call b{color:var(--bad-ink);font-size:22px}
.sf-list{margin:0 0 8px;padding-left:20px}
.sf-list li{font-size:15px;line-height:1.45;margin:6px 0}
#safety-modal p{font-size:15px;line-height:1.5;margin:0 0 9px}
#safety-modal .modal-card{max-width:460px}

/* ---------- Lake Scout: badge, suggest flow, review queue ---------- */
.scout-card{background:linear-gradient(135deg,#0b3d5c,#14608c);color:#fff;border-radius:12px;padding:12px 14px;margin:0 0 14px}
.scout-card-top{display:flex;align-items:center;gap:8px;font-size:16px}
.scout-anchor{font-size:19px}
.scout-card p{color:#d6e7f1 !important;margin:5px 0 0;font-size:13.5px;line-height:1.45}
.scout-badge{display:inline-block;background:var(--sun);color:#3a2a06;border-radius:999px;padding:1px 8px;font-size:12px;font-weight:800;margin-left:5px}

#suggest-modal textarea,#suggest-modal input{width:100%;padding:12px;border:1.5px solid #d6dde1;border-radius:10px;font-size:16px;margin:10px 0;font-family:inherit;resize:vertical}
.sug-picked{background:#eef6ec;border:1.5px solid #bcd9b6;border-radius:10px;padding:10px 12px;font-size:14.5px;color:#1d4a24;line-height:1.4}
.sug-picked b{display:block;font-size:15px;margin-bottom:2px}
.sug-choices{margin-top:12px}

/* ---------- placing a pin from a boat ----------
   The crosshair is nailed to the centre of the screen and you pan the map under it.
   Panning is a big forgiving gesture; dragging a 30px marker with a wet hand on a
   rocking boat is not. Nothing is saved until you tap the confirm button. */
#place-cross{
  position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);
  z-index:24;pointer-events:none;width:96px;height:96px;
}
.pc-ring{position:absolute;inset:14px;border-radius:50%;border:3px solid var(--sun);
  box-shadow:0 0 0 2px rgba(0,0,0,.35),inset 0 0 0 2px rgba(0,0,0,.25);opacity:.95}
.pc-dot{position:absolute;left:50%;top:50%;width:9px;height:9px;margin:-4.5px 0 0 -4.5px;
  border-radius:50%;background:var(--sun);box-shadow:0 0 0 2px rgba(0,0,0,.45)}
.pc-v,.pc-h{position:absolute;background:var(--sun);box-shadow:0 0 0 1px rgba(0,0,0,.35)}
.pc-v{left:50%;top:0;bottom:0;width:2px;margin-left:-1px}
.pc-h{top:50%;left:0;right:0;height:2px;margin-top:-1px}
#place-cross.locked .pc-ring{border-color:#5fe08a}
#place-cross.locked .pc-dot,#place-cross.locked .pc-v,#place-cross.locked .pc-h{background:#5fe08a}

/* which pin you're placing, so you never lose track mid-pan */
#place-name{
  position:fixed;left:50%;transform:translateX(-50%);top:calc(var(--safe-t) + 66px);z-index:24;
  background:var(--navy);color:#fff;border-radius:999px;padding:8px 15px;font-size:14px;font-weight:800;
  box-shadow:var(--shadow);max-width:86vw;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}

/* where it is right now, so you can see what you're about to change */
.pin-ghost{width:26px;height:26px;border-radius:50%;background:rgba(11,61,92,.30);
  border:3px dashed rgba(255,255,255,.95);box-shadow:0 1px 5px rgba(0,0,0,.35)}
/* Same rebuild, same arithmetic: S=34 -> 48.08 x 41.04, anchor "bottom". */
.pin-flag{width:48px;height:41px}
.pf-shape{width:34px;height:34px;margin:0 auto;box-sizing:border-box;
  border-radius:50% 50% 50% 2px;transform:rotate(-45deg);
  background:var(--sun);border:3px solid #fff;box-shadow:0 3px 10px rgba(0,0,0,.5)}

#pin-bar{
  position:fixed;left:8px;right:8px;bottom:var(--above-caveat);z-index:40;
  background:rgba(10,47,71,.97);color:#fff;border-radius:16px;padding:12px 12px 12px;box-shadow:var(--shadow);
}
.pin-readout{display:flex;gap:8px;margin-bottom:9px}
.pr-cell{flex:1;background:rgba(255,255,255,.09);border-radius:10px;padding:7px 10px;text-align:center}
.pr-cell b{display:block;font-size:19px;font-weight:800;color:var(--sun);font-variant-numeric:tabular-nums;line-height:1.1}
.pr-cell span{font-size:11px;opacity:.8}
#pin-bar-text{font-size:14px;line-height:1.35;margin-bottom:10px;opacity:.92}
#pin-bar-text b{color:var(--sun)}
.pin-bar-btns{display:flex;gap:8px;margin-top:8px}
.pin-bar-btns:first-of-type{margin-top:0}
/* big targets — this gets used with one wet thumb */
.pin-bar-btns .btn{flex:1;border-color:rgba(255,255,255,.65);min-height:52px;font-size:15px;padding:10px 8px}
.pin-bar-btns .btn:not(.btn-primary){background:transparent;color:#fff}
.pin-bar-btns .btn-primary{background:var(--sun);border-color:var(--sun);color:#3a2a06;font-size:16px}
#pin-follow[aria-pressed="true"]{background:#1c5c38;border-color:#5fe08a;color:#c9f5d8}
body.placing #route-bar{display:none}

.sc-item{background:#f6f8f7;border-radius:12px;padding:12px;margin-bottom:10px}
.sc-item h3{margin:0 0 3px;font-size:16px;color:var(--navy)}
.sc-meta{font-size:12.5px;color:var(--muted);margin-bottom:6px}
.sc-note{font-size:14.5px;color:#2b4351;border-left:3px solid var(--sun);padding-left:8px;margin:6px 0}
.sc-btns{display:flex;gap:7px;margin-top:9px;flex-wrap:wrap}
.sc-btns .btn{flex:1;min-width:96px;padding:9px 10px;font-size:14px;min-height:40px}
.sc-done{font-size:13px;font-weight:800;color:var(--good)}
.sc-declined{font-size:13px;font-weight:800;color:var(--bad)}

@media (min-width:820px){
  .card{left:auto;right:16px;width:400px}
  .nearby{left:auto;right:16px;width:400px}
  #pin-bar{left:auto;right:16px;width:400px}
}

/* ---------- spots: services, agree, comments, admin ---------- */
/* Services are tabs inside "What's around me", not layers on the map. Six map layers
   would mean five wrong ones on screen at all times. */
.find-tabs{display:flex;gap:6px;overflow-x:auto;padding:8px 10px 4px;-webkit-overflow-scrolling:touch}
.find-tab{
  flex:none;border:2px solid #d7e0e6;background:#fff;color:var(--navy);
  border-radius:999px;padding:9px 14px;font-family:inherit;font-size:15px;font-weight:700;
  cursor:pointer;white-space:nowrap;min-height:44px;
}
.find-tab.active{background:var(--navy);color:#fff;border-color:var(--navy)}
.find-tab:active{transform:scale(.97)}

.place-row{
  display:block;width:100%;text-align:left;border:none;border-bottom:1px solid #eef2f4;
  background:none;font-family:inherit;padding:14px 14px;cursor:pointer;min-height:60px;
}
.place-row:active{background:#f2f6f8}
.pr-main{font-size:17px;color:#111;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.agree-pill{background:#e6f2ea;color:#14603a;border-radius:999px;padding:2px 9px;font-size:12.5px;font-weight:800}
.agree-pill.you{background:#dceaf5;color:#0a5c86}
.nomap-pill{background:#fdf0d8;color:#7a5310;border-radius:999px;padding:2px 9px;font-size:12px;font-weight:700}

.place-caveat{background:#fdf6e8;border-left:4px solid var(--sun);padding:9px 11px;font-size:14px;color:#5a4415;border-radius:0 8px 8px 0}
.place-src{display:inline-block;color:var(--sky-ink);font-size:14px;font-weight:700;margin-top:4px}
.locate-box{background:#f4f8fa;border:1px solid #e0e8ec;border-radius:12px;padding:12px;margin-top:12px}
.locate-box .btn-row{margin-top:8px}

.cm-sec{border-top:1px solid #e7ecef;margin-top:16px;padding-top:12px}
.cm{border-bottom:1px solid #f0f4f6;padding:9px 0;font-size:15px}
.cm b{color:var(--navy)}
#cm-body,#as-note{width:100%;font-family:inherit;font-size:16px;padding:10px;border:2px solid #d7e0e6;border-radius:10px;margin:8px 0}
#cm-name,#as-name,#as-who{width:100%;font-family:inherit;font-size:16px;padding:11px;border:2px solid #d7e0e6;border-radius:10px;margin:6px 0}

.adm-row{border-bottom:1px solid #eef2f4;padding:12px 0}
/* Next is the run's own control and sits beside Confirm. Same 60px as Confirm so the
   pair reads as one bar of thumb targets, not a primary with an afterthought. */
#card-next{background:var(--navy);border-color:var(--navy);color:#fff;
  font-size:17px;min-height:60px;flex:1 1 100%}
.adm-row .btn-row{margin-top:8px}
.adm-hidden{opacity:.55}

/* Sponsor. One line, at the foot of a list you already opened, clearly labelled.
   Never on the map, never in the way, never pretending to be a spot. */
.sponsor{
  display:flex;align-items:center;gap:10px;text-decoration:none;
  margin:8px 10px;padding:11px 12px;border:1px solid #e0e8ec;border-radius:12px;
  background:#fafcfd;color:#111;
}
.sponsor:active{background:#eff5f8}
/* 10.5px at 4.95:1 was the worst size-and-contrast pairing in the app, on the one
   element that has a commercial obligation to be legible. Now 11.5px at 9.31:1. */
.sponsor-tag{
  flex:none;background:#eef3f6;color:#33424a;border-radius:6px;padding:3px 7px;
  font-size:11.5px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;
}
.sponsor-body{flex:1;min-width:0;display:flex;flex-direction:column}
.sponsor-body b{font-size:15px;color:var(--navy)}
.sponsor-body span{font-size:13px;color:var(--muted)}
.sponsor-go{flex:none;color:var(--sky-ink);font-size:20px;font-weight:800}

/* Never hold someone's screen awake invisibly. */
#wake-pill{
  position:fixed;left:50%;transform:translateX(-50%);top:calc(var(--safe-t) + 40px);z-index:21;
  background:#0a1923;color:#fff;border-radius:999px;
  padding:5px 12px;font-size:12.5px;font-weight:700;pointer-events:none;
  box-shadow:0 2px 8px rgba(0,0,0,.3);
}

/* Reviewing a suggested spot on the map — the only thing on screen is the thing
   you are judging, and the decision travels with it. */
#review-bar{
  position:fixed;left:8px;right:8px;bottom:var(--above-caveat);z-index:27;
  background:#fff;border-radius:16px;padding:14px;box-shadow:var(--shadow);
}
.rv-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:6px}
.rv-head b{font-size:19px;color:var(--navy)}
#rv-note{margin-bottom:4px}
#review-bar .pin-bar-btns{display:flex;gap:8px;margin-top:12px}
#review-bar .btn{flex:1;min-height:52px;font-size:15px;padding:10px 8px}
/* REVIEW PIN — this was the worst of the family. A CSS animation outranks even an
   inline style, so `animation:rvpulse` on the marker element was overwriting the
   transform MapLibre uses to POSITION it — parking the pin near the map's top-left
   while the map flew to the spot being judged. Every "See it on the map" approval
   was potentially made looking at nothing. The pulse now lives on the inner div. */
.review-marker{width:46px;height:46px}
.rv-inner{
  width:100%;height:100%;box-sizing:border-box;
  border-radius:50%;background:var(--sun);color:#3a2a06;
  border:3px solid #fff;box-shadow:0 3px 12px rgba(0,0,0,.5);
  display:flex;align-items:center;justify-content:center;font-size:22px;
  animation:rvpulse 1.6s ease-in-out infinite;
}
@keyframes rvpulse{0%,100%{transform:scale(1)}50%{transform:scale(1.12)}}
body.reviewing-spot #thumb-zone{display:none}

/* Service shortcuts: icons only, riding with the layer bar so the map never gains a
   second permanent strip. Four icons fit where two words would not. */
#svc-bar{display:flex;gap:6px;align-self:center;background:rgba(255,255,255,.97);
  border-radius:14px;padding:4px;box-shadow:var(--shadow)}
.svc{width:48px;height:44px;border:none;border-radius:11px;background:none;
  font-size:22px;cursor:pointer;display:flex;align-items:center;justify-content:center}
.svc:active{background:#e9eef1;transform:scale(.94)}

/* Eats grouped by the dock you tie up at — pick the dock, then the meal. */
.dock-head{
  position:sticky;top:0;z-index:1;background:var(--navy);color:#fff;
  padding:9px 12px;font-size:13px;font-weight:800;letter-spacing:.02em;
  display:flex;justify-content:space-between;align-items:center;
  width:100%;border:none;font-family:inherit;cursor:pointer;text-align:left;min-height:44px;
}
.dock-head:active{background:var(--navy-2)}
/* A group header that is NOT a dock: same look, no tap target, no ⛴. */
.dock-head.is-plain{cursor:default;min-height:38px;background:var(--navy-2)}
.dock-head span{background:rgba(255,255,255,.22);border-radius:999px;padding:1px 8px;font-size:12px}
.place-wrap{position:relative}
.place-it{
  position:absolute;right:10px;top:50%;transform:translateY(-50%);
  background:var(--sun);color:#3a2a06;border-radius:9px;padding:7px 10px;
  font-size:12.5px;font-weight:800;cursor:pointer;white-space:nowrap;
}
.place-it:active{opacity:.75}

#pe-note,#pe-hours,#pe-address,#pe-url,#po-email{
  width:100%;font-family:inherit;font-size:16px;padding:10px;
  border:2px solid #d7e0e6;border-radius:10px;margin:6px 0;
}

/* Public docks on the map — where you tie up and walk. Distinct from story pins
   because they are a reference point, not a thing to read about. */
.dock-marker{
  width:30px;height:30px;border-radius:9px;background:#00354d;color:#fff;
  border:3.5px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.45);
  display:flex;align-items:center;justify-content:center;font-size:15px;cursor:pointer;
}

#dock-walk .place-row{border-bottom:1px solid #eef2f4;padding:12px 4px}
#dock-walk .fb-sec-title{margin-top:14px}

.dock-marker.has-stuff{background:#0e7c66}
.dock-badge{
  position:absolute;top:-6px;right:-6px;background:var(--sun);color:#3a2a06;
  border-radius:999px;min-width:18px;height:18px;font-size:11px;font-weight:800;
  display:flex;align-items:center;justify-content:center;padding:0 4px;border:2px solid #fff;
}
/* MUST be absolute. maplibre-gl.css sets .maplibregl-marker{position:absolute};
   styles.css loads after it at equal specificity, so position:relative here won't
   just style the badge -- it drops every dock pin back into normal document flow.
   The transform maplibre writes is then measured from the pin's flow position
   instead of the map origin, so pin N sits 30*N px too low: a fixed screen-pixel
   error, which looks like drift when you zoom (2026-08-02, build .44).
   absolute is still a positioned ancestor, so .dock-badge is unaffected. */
.dock-marker{position:absolute}

/* ---------- the Do bar: one row, six things ----------
   Grouped by what you are doing, not by what the code calls them.
   Left three change the map and show a filled state; right three open a list.
   The divider is the entire explanation. */
#do-bar{
  display:flex;align-items:center;gap:2px;align-self:center;
  background:rgba(255,255,255,.97);border-radius:16px;padding:5px;
  box-shadow:var(--shadow);max-width:100%;
}
.do{
  width:50px;height:50px;min-width:50px;border:none;border-radius:12px;
  background:none;font-size:23px;cursor:pointer;position:relative;
  display:flex;align-items:center;justify-content:center;
}
.do:active{transform:scale(.93);background:#e9eef1}
/* only the map layers get a "you are here" state — the list icons never look chosen */
/* color:#fff came from the deleted .layer-seg.active; it lives here now. */
.do.layer-seg.active{background:var(--navy);color:#fff}
.do.layer-seg.active::after{
  content:"";position:absolute;left:12px;right:12px;bottom:5px;height:3px;
  border-radius:2px;background:var(--sun);
}
.do-sep{width:1px;align-self:stretch;margin:6px 5px;background:#d7e0e6;flex:none}
@media (max-width:380px){ .do{width:45px;height:46px;min-width:45px;font-size:21px} }

/* The browse sheet is a handle now, not a wall. Same job, a fraction of the weight. */
#btn-nearby{
  align-self:center;min-width:120px;min-height:34px;padding:0 14px;gap:8px;
  background:#fff;border:none;border-radius:999px;
  box-shadow:0 2px 8px rgba(0,0,0,.22);cursor:pointer;
  display:flex;align-items:center;justify-content:center;
}
#btn-nearby:active{transform:scale(.95)}
.grabber{display:block;width:44px;height:5px;border-radius:3px;background:#8fa3ad}

/* A service pin: same shape language as a dock, different colour so you can tell
   "somewhere to tie up" from "the thing you came for" at a glance. */
.svc-marker{
  width:30px;height:30px;border-radius:50%;background:#b5651d;color:#fff;
  border:2.5px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.45);
  display:flex;align-items:center;justify-content:center;font-size:15px;cursor:pointer;
}

.adm-row.sev-hi{border-left:4px solid var(--bad);padding-left:10px}
.adm-row.sev-mid{border-left:4px solid var(--sun);padding-left:10px}
.adm-row.sev-lo{border-left:4px solid #cdd6db;padding-left:10px}

.grab-label{font-size:13px;font-weight:800;color:var(--navy);letter-spacing:.02em}
/* a service icon stays lit while its pins are on the map */
.do.svc.on{background:#0e7c66}
.do.svc.on::after{content:"";position:absolute;left:12px;right:12px;bottom:5px;height:3px;border-radius:2px;background:var(--sun)}

/* toasts you can actually read on the water, and dismiss when you want */
#toast{max-width:min(92vw,460px);line-height:1.35;cursor:pointer}
#toast.sticky{background:var(--navy);border:2px solid var(--sun);padding-right:34px;position:relative}
.toast-x{position:absolute;top:6px;right:8px;font-size:15px;font-weight:800;color:var(--sun)}

/* ...but the menu now lives up here, at Bill's request. It is the one control that
   is never urgent, so the regrip cost is acceptable for it and it buys back a whole
   button of space in the thumb zone. */
#topbar #btn-menu{pointer-events:auto;width:48px;height:48px;min-width:48px;font-size:23px;border-radius:14px;flex:none}
#topbar .brand{flex:1;justify-content:flex-start;padding-left:2px}

/* ---------- splash ---------- */
#splash{position:fixed;inset:0;z-index:80;background:var(--navy);color:#fff;
  display:flex;align-items:center;justify-content:center;text-align:center;
  padding:24px;transition:opacity .45s ease}
#splash.gone{opacity:0;pointer-events:none}
.sp-inner{max-width:340px}
.sp-logo svg{border-radius:20px;box-shadow:0 8px 30px rgba(0,0,0,.45)}
#splash h1{font-size:30px;margin:16px 0 2px;letter-spacing:-.02em}
.sp-sub{margin:0 0 20px;color:var(--sun);font-weight:700;font-size:15px;letter-spacing:.04em;text-transform:uppercase}
#sp-stats{list-style:none;padding:0;margin:0 0 20px;display:flex;flex-direction:column;gap:9px}
#sp-stats li{display:flex;align-items:center;gap:11px;font-size:16px;color:#dceaf3;text-align:left}
#sp-stats b{color:#fff;font-size:19px;min-width:34px;text-align:right;font-variant-numeric:tabular-nums}
.sp-fun{font-size:17px;font-weight:800;color:var(--sun);margin:0 0 18px}
/* Safety copy, so it does not get to be the dimmest thing on the splash: 5.41 -> 8.45:1.
   Tinted from the navy it sits on rather than desaturated toward grey - a grey at this
   lightness reads as washed out on a colour, which is the opposite of the point. */
.sp-caveat{font-size:12px;color:#c2e2f7;margin:0;line-height:1.4}

/* ---------- Add a point + your pending submissions (2026-08-03) ---------- */

/* The one button that makes contributing obvious. Gold, so it reads as "do
   something" next to the navy Locate control, and big enough for a wet thumb. */
#btn-addpoint.rb-add{
  background:var(--sun);color:#0b3d5c;border-color:#0b3d5c;
  font-size:30px;font-weight:800;line-height:1;
}
#btn-addpoint.rb-add:active{filter:brightness(.92)}
body.card-open:not(.card-peek) #btn-addpoint,body.placing #btn-addpoint,body.reviewing-spot #btn-addpoint{display:none}

/* YOUR pending point, on YOUR map only. Faded and dashed so it reads as
   "not real yet" at a glance without needing a label.
   MARKER RULE: the element MapLibre gets owns position and nothing else.
   Shape, dashes and the pulse all live on .sub-dot, the inner child. A
   transform or an animation on .sub-marker would be silently overridden
   (inline styles) or would hijack positioning entirely (animations beat
   inline styles). This cost us a whole build once. */
.sub-marker{width:30px;height:30px;cursor:pointer}
.sub-dot{
  width:100%;height:100%;border-radius:50%;
  background:rgba(11,61,92,.28);
  border:3px dashed rgba(255,255,255,.9);
  box-shadow:0 2px 6px rgba(0,0,0,.35);
  animation:subpulse 2.4s ease-in-out infinite;
}
.sub-dot.is-move{border-style:dotted;background:rgba(244,178,62,.30)}
@keyframes subpulse{0%,100%{opacity:.55}50%{opacity:.95}}

/* Add-a-point form */
.ap-lab{display:block;margin:14px 0 4px;font-weight:800;font-size:15px;color:var(--navy)}
.ap-lab .muted{font-weight:600}
#ap-where{margin-top:10px}
#ap-where.ap-nowhere{background:#fff6e5;border-color:#e6bd77;color:#5a4212}

/* The payoff card. Deliberately celebratory — this is the moment somebody
   finds out they made the map better, and it is the entire reward loop. */
.news-card{text-align:center}
.news-hero{font-size:54px;line-height:1;margin:6px 0 4px}
.news-card h2{margin:2px 0 8px}
.news-sec{text-align:left;margin-top:14px;background:#f2f7fa;border-radius:12px;padding:10px 12px}
.news-sec>b{display:block;font-size:13px;text-transform:uppercase;letter-spacing:.04em;color:#456;margin-bottom:6px}
.news-row{padding:4px 0;font-weight:700}
.news-why{font-weight:600;color:#456;font-size:14px;margin-top:2px}

/* Admin rows for the points list */
.adm-tight{padding-top:8px;padding-bottom:8px}
.adm-remove{border-left:4px solid #b4432f}

/* ---------- find a point (2026-08-02) ---------- */
#btn-find.rb-find{background:#fff;color:var(--navy);font-size:23px;font-weight:700}
body.card-open:not(.card-peek) #btn-find,body.placing #btn-find,body.reviewing-spot #btn-find{display:none}

/* Says out loud that a search ignores the layer you were on — which is the answer to
   "why couldn't I find it before." */
.search-note{
  padding:8px 12px;margin:6px 0;border-radius:10px;
  background:#eef4f8;color:#3d5866;font-size:13.5px;font-weight:700;
}

/* The splash now waits for a tap, so it has to say so. */
.sp-go{
  margin-top:16px;padding:12px 18px;border-radius:999px;
  background:var(--sun);color:#0b3d5c;font-weight:800;font-size:16px;
  display:inline-block;animation:spgo 2.2s ease-in-out infinite;
}
@keyframes spgo{0%,100%{opacity:.75}50%{opacity:1}}
#splash{cursor:pointer}

/* ---------- Lake Scout ---------- */
/* The permanent mark rides the header that already occupies its height, so it costs no
   map. A separate colour bar would spend back the fix that took bottom chrome from 51%
   of the screen to 27%. Gold only, and only when is_editor is true. */
#topbar.is-scout{border-bottom:2px solid #f4b23e;box-shadow:0 1px 0 rgba(244,178,62,.25)}
.scout-chip{
  display:inline-flex;align-items:center;gap:4px;
  background:linear-gradient(135deg,#f4b23e,#e09a1f);color:#3a2600;
  font-weight:800;font-size:11px;letter-spacing:.2px;
  padding:2px 7px;border-radius:999px;white-space:nowrap;
  box-shadow:0 1px 3px rgba(0,0,0,.35);pointer-events:none;
}
/* The moment itself. Gold takeover, fires once, server-enforced by seen_at. */
#news-modal.scout-moment .modal-card{
  background:linear-gradient(160deg,#1b1206,#3a2600 55%,#5a3c05);
  border:1px solid rgba(244,178,62,.55);
}
#news-modal.scout-moment .news-hero{
  font-size:76px;filter:drop-shadow(0 3px 10px rgba(244,178,62,.55));
}
#news-modal.scout-moment h2{color:#f4b23e;font-size:26px;letter-spacing:.2px}
#news-modal.scout-moment p{color:#f0e2c4}
.news-brag{
  font-size:16px;line-height:1.4;margin:8px 0 2px;
  color:#fff !important;font-weight:700;
}

/* ---------- keyboard focus ----------
   Nothing in this file ever removed the outline, and nothing ever drew one either, so
   the whole app ran on the UA default: a 1px ring that vanishes over a map. This is the
   ring. Gold, because gold already means "this one" everywhere else in the app. */
:focus-visible{outline:3px solid var(--sun);outline-offset:2px}
.poi-marker:focus-visible,.prop-marker:focus-visible,.fish-marker:focus-visible,
.route-marker:focus-visible,.svc-marker:focus-visible,.dock-marker:focus-visible{
  outline:3px solid var(--sun);outline-offset:3px;
}
/* The row already has its own shadow and radius; an outside ring would fight the card. */
.poi-item:focus-visible{outline-offset:-3px}

/* Four things pulse forever in this file and two more come from MapLibre. On a boat that
   is motion nobody asked for, and for a vestibular user it is worse than noise. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;scroll-behavior:auto !important;
  }
}
