# Changelog

All notable changes to Binnacle are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project aims to follow
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

<a id="v0210"></a>

## [0.21.0] - 2026-08-13

The watchkeeping release: the helm learns to hand off a watch, rotate the chart, plan a passage,
and say plainly what it trusts.

### Added

- Watch handoff: a timestamped review-status snapshot for the change of watch, capturing the fix
  and its age, the course with cross-track error and a basis-qualified time to go, raised alarms
  and the collision mute expiry, the top contact, the depth watch, radar health, weather and tide
  ages, whether the active route's offline coverage was checked, and a short operator note. Facts
  are rendered to plain text at snapshot time, shared between stations through Signal K
  applicationData, queued on the device when the store is unreachable, and every record states
  shared, waiting to sync, or device-only. The surface never says a watch is safe to take.
- Chart orientation: north-up, course-up, and heading-up, as explicit profile-owned choices with
  north-up the default. A rotating mode needs a fresh reference (course-up also needs way on) and
  falls back to north immediately when it goes stale, naming why; a status-strip chip keeps the
  live orientation visible with a one-tap return to north, and Follow adds a bounded look-ahead
  while rotated so the water ahead gets the pixels. Rotation gestures stay disabled: the mode is
  the only author of chart bearing.
- The route plan is a real passage plan: a plan speed and an editable departure time produce a
  named leg table with cumulative elapsed time and a local-clock arrival at every endpoint, dated
  when it lands past midnight, plus the whole-route duration. Deliberately simple arithmetic,
  never weather-, current-, or polar-aware, and labeled as such.
- An advisory route offline-coverage check: sample a chosen corridor around a route against ready
  saved areas, their included charts, and a requested detail level, report Complete, Partial, or
  Unknown, and highlight uncovered or insufficient-detail stretches on the chart, read-only. The
  forecast mini-map now draws the active route for weather context, and saved-area cards lead
  with at-a-glance readiness facts. The check never certifies safety.
- The chart-trust badge: an ambient corner control grading the current view (Chart, Chart
  overzoomed, Reference map only, Outside chart coverage, Chart source failed, or Base map
  unavailable) that opens Layers and charts. A reference base map is never called a chart.
- An emergency rail with deterministic alarm priority: one audio authority ranks every alarm
  channel, so MOB and an escalating collision interleave at the top, lower alarms rotate with
  bounded reminders, courtesy tones yield entirely, and blocked audio is reported honestly beside
  every alarm surface with one enable action.
- A recent-source trace on watch-critical paths: the instrument detail calls out a source change
  with the prior label, or several recently alternating sources, within a ten-minute window, so a
  quiet sensor failover never goes unnoticed. Trend charts carry an honest data-coverage line
  (percent present, longest gap, newest sample age, Partial or Stale marks).
- First-run orientation and a permanent Help panel: the advisory framing, the reference-map
  versus nautical-chart distinction, Signal K access and alarm-sound setup with direct actions, a
  marine glossary, and operating-context checklists for a coastal day, a night passage, and lying
  at anchor. The banner offers once and never nags again.
- The starter profiles carry their presentation half (Coastal day explicit north-up, Night
  passage course-up with radar raised, At anchor with tides raised), and the top-bar profile
  control is a real switcher: one menu item per profile with the active row marked, plus a Manage
  profiles row.
- Server-declared staleness is honored end to end. When the Signal K server's meta.timeout
  enforcement declares a path timed out, every surface that grades freshness reacts at once: the
  status strip relabels the retained fix as Last fix with its age, chart orientation falls back
  and names why, follow pauses, the collision and shallow-water watches stand down honestly, and
  the instrument tile reads Stale with its last good value kept visible. The instrument detail
  distinguishes Stale (server declared) from a client-side window, ages the value from its own
  receipt rather than the declaration, and names the source that went quiet. Declarations are
  honored per source, so with two GPS units the surviving unit keeps the position live while the
  dead unit's declaration is set aside. On servers without the enforcement nothing changes.
  One consequence to know: when a sensor dies under an active server zone alarm, the server itself
  clears that alarm as it retires the reading; Binnacle's watch reports the stale reading and
  pauses in its place.
- The instrument detail shows what each sensor says: when two or more sources fed the shown path
  within the last ten minutes, a Recent sources list names each source with its own formatted
  value and age, without judging disagreement. A watch handoff snapshot now also names any
  watch-critical path with multiple recent sources.
- A path's explicitly declared meta.timeout replaces the ten-second client staleness window on
  its instrument tile, so a legitimately slow sensor is not flashed stale; a declared timeout of
  zero means never stale.
- A Get set up checklist at the top of Help, with a live state and one action per row: turn on a
  nautical chart, see a GPS position, get read and write access, enable alarm sound, and enable
  saved data on the server. Every row routes to a surface that already exists, and the section
  retires itself once the durable rows pass.
- In US waters, a dismissible offer to turn on the official NOAA electronic navigation charts
  when no nautical chart is on. It grades the boat's own fix against NOAA's published regional
  coverage, so it never appears where the charts do not reach, and it stays quiet while a panel
  is open or an emergency is live.
- The bottom bar can carry the menu itself: Menu can now be pinned there and ships in the
  default toolbar set alongside Center, Follow, and AIS, so a phone journey no longer starts with
  a reach to the opposite corner. Pinning it hides the top-bar hamburger, leaving one opener.
- Saved routes gain a read-only passage plan: the same leg table, planned arrivals, plan speed,
  and departure the edit session shows, reviewable from the card without entering chart edit mode,
  with a link to check offline chart coverage for the route.
- Save and navigate in the waypoint drop dialog, for the mark-that-spot-and-go moment. It saves
  the mark and arms the same navigation confirmation naming the destination; plain Save stays the
  primary action.
- Rename on a saved route card, so a route drawn on the chart and quick-saved under a dated name
  can be named afterward without re-entering the editor.
- Degraded status-strip chips explain themselves on touch: tapping the connection dot, the AIS
  chip, the depth chip, or a radar-trouble chip shows the explanation that used to live only in a
  hover tooltip. Waiting for GPS carries a Help action, and the anchor chip opens Anchor watch.
- Help covers the states it previously left to tooltips: what each connection state means, what
  Stale means and why the strip dashes a value while the dock keeps it with its age, what an
  unassessed AIS target is, and glossary entries for the Keel, Surface, and Xducer depth datums
  and for Signal K itself.
- The nearby-vessels menu item carries a live count of danger-grade collision contacts, so a
  closed panel says something is waiting.

### Changed

- A retained stale GPS fix now places a large question badge over the own-vessel symbol without
  rotating the badge, so the chart itself cannot make an old position look current. The stream also
  requests the own-vessel name, MMSI, and VHF call sign leaf paths supported by Signal K 2.31 while
  remaining compatible with older servers.
- Alarm acknowledgments now retain and display the server's local acknowledgment time when Signal K
  supplies the `acknowledgedAt` field added in server-api 2.31.
- Routes editing is an exclusive mode with a persistent strip that keeps the mode, point count,
  and exit actions visible even when another panel replaces the Routes panel, and the navigation
  strip names the provenance of every course figure (server estimate or locally computed) per
  field instead of as one blanket label.
- AIS assessment is honest about what it cannot grade: contacts missing course or motion data are
  counted as unassessed rather than silently skipped, per-field freshness windows drop expired
  positions, approaches, and motion instead of freezing them, and helm health surfaces (radar,
  stream, audio) report their real states.
- The heavy dock panels load on demand, so the main bundle stays inside its budget and first
  paint on a Pi-class helm gets its headroom back.
- MapLibre GL moves to 6.2.0 and the toolchain to its current releases across the board.
- A helm-visibility pass across the chrome. Instrument tiles lead with their loud abbreviation
  (SOG, HDG, AWS) over the quiet long name, states render as tinted chips where Alarm outranks
  Stale and Stale outranks a warning computed from an untrusted value, a stale tile keeps its
  retained number with its age beside it, and the wind tile's angle freshness folds into the same
  chip line. The collision strip's CPA and TCPA numbers and its grade word take the large readout
  treatment. The MOB button is a solid red key in day and dusk, the one solid alarm fill in the
  chrome, with a dimmed resting state at night so the brightest night pixel is only ever a live
  emergency. The map scale is a classic open-bracket bar with a mono label instead of a labeled
  box, and the zoom glyphs now follow the theme, so they stay visible at dusk and night. The
  Orientation tile reads Orientation with its mode on a quiet second line, the instrument
  dashboard tile no longer truncates, menu groups separate by heading alone in day and dusk, the
  menu keeps a scroll shadow when tiles continue past the fold, the layers headers count with the
  shared chip, and the alarm mute rows say On or Off at a glance.
- Menu naming and grouping follow chartplotter habits: the Map group is now Chart and absorbs
  Layers and charts along with Offline charts, Time travel is now Playback and sits beside Tracks
  under Navigate, Tides is now Tides and currents, the Points of interest layer row is now
  Places, and the Profiles tile names what it holds (units, sync, and privacy). Saved profiles,
  pinned toolbars, and layer settings are unaffected: they key on ids, not titles.
- Write-blocked copy speaks the same language everywhere: read and write access, approved by the
  boat's Signal K admin, instead of tokens. The outcome of a request (declined, unanswered, or
  unreachable) now lands in the panel that asked, not only in the app-wide banner, and while a
  request is out the panel says where the approval happens.
- Routes and Waypoints name a missing Resources Provider instead of blaming the connection: on a
  stock server the first open of either panel now explains the exact admin step and offers Check
  again, matching what Tracks already did.
- The Charts tab explains a reference-only view where the fix is made, including that depth
  shading never counts as a chart and what to do outside US waters, and the first-run banner
  offers Set up charts beside the safety orientation.
- The instrument detail says Stale plainly and explains the server's declaration in a sentence
  instead of labeling the badge "server declared". Radar and history hints name a next step, and
  the Chart Locker access hint no longer calls an HTTP endpoint a route.
- The not-encrypted warning can be dismissed per device, with the durable explanation kept in
  Help, so a stock plain-HTTP install does not spend every first impression on a warning the
  navigator cannot act on from here.
- While the Signal K link itself is down, the strip subordinates the readouts that pause with it
  (GPS, speed, course, heading, and the depth watch) so one failure with one action reads as one
  failure. Radar health is deliberately excluded: it rides its own stream.

### Fixed

- Pull request CI accepts the active version's explicit Unreleased changelog heading, while the
  publication gate continues to require a dated heading for that same version.
- The status strip no longer doubles in height when a chip carries an action. A button set beside
  inline text is a block-level box, so the label was pushed onto its own line with a full-size
  target stacked beneath it, and every such chip cost the chart an extra row. Label and action now
  share one row, which also stops the first-run and chart-offer banners from stacking one button
  per line.
- Alarm audio left the status strip. It was shown whenever the Signal K stream was merely
  connected, so a boat with no depth sensor, no AIS traffic, and no anchor watch carried a standing
  warning about a silence that could not happen, and at tablet widths it pushed the readouts onto a
  second row. The Alarms and Anchor watch panels now state the grade (blocked, failed to start, or
  unavailable on this display), the Help checklist carries it as a setup row, and any tap or key
  still primes the audio. Previously the strip was the only surface that reported a failed or
  unsupported audio device at all.
- The alarm-audio chips state their condition instead of carrying a button. Any tap or key anywhere
  already primes or retries the shared audio context, so Sound off and Sound unavailable are now
  readouts that explain themselves on tap, the way the connection, AIS, depth, and radar chips do.
  Alarm audio also primes on pointer release, not only on press, so the first tap on a touchscreen
  reliably turns sound on.
- Starting navigation from a phone no longer leaves the sheet covering the chart and the new
  guidance strip: confirming a route activation, a waypoint destination, or a track retrace
  collapses the sheet the way Locate already did.
- The route-edit strip's Save no longer claims a naming flow it never had. It quick-saves under
  the working or dated name, which the new card Rename can change, and saving from the panel now
  offers to start navigation on the route just drawn.
- A named contact on the collision strip is tappable, opening that vessel's AIS detail instead of
  costing four taps from the far corner of the screen mid-incident.
- Status strip text can no longer slide under the centered toolbar pills on desktop widths: the
  readouts wrap instead, a paused depth watch drops its dashed value in favor of its label, and
  between 900 and 1200 pixels the strip now keeps the vessel position visible, dropping the clock
  first. On phones, the collision strip no longer covers the chart badge, the scale, or the
  instrument dock's last row: chart chrome and the dock reserve the strip's measured height, and
  the strip gives the contact name its own line instead of truncating it. Dusk no longer leaves
  pure-white highway shields as the brightest pixels on the chart, night-red recolors park
  outlines, POI dots, and low-zoom landcover that previously kept their source colors, and the
  own-vessel and AIS markers at night are pure red-family tones rather than pink.
- Enabling the NOAA ENC chart layer now flips the chart badge to Chart: the badge counts the ENC
  as a real nautical chart within its actual regional coverage (US coasts, Great Lakes, Alaska,
  Hawaii, and island territories), and mid-ocean or foreign waters still read Outside chart
  coverage. Bathymetry reference layers still never count as charts, and the ENC row's own
  description no longer contradicts the badge. The badge also recognizes charts correctly after
  panning across the antimeridian.
- A Signal K source identified only by its $source reference no longer renders as "Unknown" in
  the instrument detail, and its handoffs now feed the source-changed cue.
- The pinned always-on-top guarantee (own vessel, active alarms) is enforced at every visibility
  door, so a corrupted or hand-edited profile document applied, imported, or synced from another
  station can never hide the boat.
- Man-overboard raises and clears lost to a closed socket are replayed on reconnect, anchor watch
  treats a lost fix as an explicit episode with an audible escalation and a healthy hold before it
  ends, and the sound-off chip cannot report audio as enabled when the browser still blocks it.
- Radar control values reconcile from the live stream for every radar rather than only the
  selected one, and stream reconnection has a single authority, so a mid-outage reopen cannot
  race a stale connection back to life.
- Stream and authentication recovery survives server reboots, network outages, and worker faults:
  the subscription registry replays everything on reopen, a refreshed token takes effect on the
  next reconnect, and a dead worker is rebuilt rather than left silent.
- Offline caches enforce their lifetimes with an atomic prune, every cache name is registered
  with the privacy erase so a device-data wipe misses nothing, and the offline landing page
  explains a service-worker registration blocked by an untrusted certificate instead of failing
  quietly.
- The Measure tile stays disabled until the chart tap handler is ready, so an early tap cannot
  arm a measurement that no tap can extend.
- The NOAA ENC chart offer renders. Its guard compared a nullable panel id against `undefined`,
  so it was always true and the offer had never appeared once since it shipped: a boat inside US
  ENC waters with only a reference base map was never told a real chart was available.
- The chart routes its raster overlays through Chart Locker on a secured server. The tile-proxy
  probe used a credential captured before authentication resolved, so the server answered "no
  access" and the session ran on direct upstream tile URLs for its whole life, quietly skipping
  the boat's shared offline cache.
- The tides picker no longer claims no stations are nearby when the station search failed or has
  not run. An empty list was read as an answer about the water, so the panel showed that false
  claim beside the accurate connection error.
- Closing a panel now clears what that panel armed or drew: a dismissed "Save and navigate"
  confirm cannot come back armed on reopen, and a highlighted point of interest cannot strand its
  ring on the chart when the full-screen instrument dock closes the panel that owned it.
- A saved offline area with no width reports no span instead of a coverage figure about half the
  globe wide.
- Server-declared path metadata (alarm zones, a declared staleness window) is refetched on
  reconnect instead of being trusted for the rest of the session, so an alarm threshold changed on
  the server reaches an already-open station. A reconnect that lands on a different vessel context
  now adopts it rather than keeping the first one seen.
- The access-request poll validates the server-supplied address as a same-origin path before
  using it, so a malformed response cannot send the poll, or the approved token it carries, to
  another host.
- The mirrored notification set is bounded on both sides. Raised notifications were already
  capped, but the per-path cells behind them grew without limit, and each raise mints a new id, so
  a hazard alarming repeatedly through a passage accumulated one cell per raise.
- The app menu's arrow keys no longer skip the first tile when focus sits on the menu itself, now
  that all three menus share one definition of the roving-focus arithmetic.

<a id="v0191"></a>

## [0.19.1] - 2026-08-04

### Added

- Tapping a waypoint on the chart opens the Waypoints panel with that mark's card current, the way
  a tapped note opens its details. A hidden or fully faded waypoint layer is not a tap target, and
  chart editing tools keep the tap.
- A profile updated on another station now lists which setting categories changed before you choose
  Apply update or Keep current setup, and the profile switcher in the top bar carries an update
  indicator, so an update arriving mid-passage is discoverable without opening the panel.
- Panels that cannot save because Signal K write access is missing (waypoints, tracks, notes,
  layers, anchor, and profiles) now offer the request-access button in place, one tap from the
  block it explains.
- The man-overboard announcement for screen readers carries the bearing and range to the mark, the
  same guidance the strip shows visually.
- The app manifest ships true maskable icons, so Android adaptive launchers no longer clip the
  compass badge.

### Changed

- Follow mode survives a GPS outage: it pauses recentering while the fix is stale and resumes on
  the next fresh fix, instead of silently disarming during a docking approach. The menu tile stays
  enabled while armed, so follow can still be switched off under a bridge without panning.
- One launcher order at every width: the phone-only group reorder is gone, the launcher groups no
  longer repeat their name as a tile (the dock tile is Instrument dock, the one-tile group is
  Offline), Waypoints precedes Tracks, and the dock leads its group. Below 480 px the pinned
  toolbar pills go icon-only, so all four defaults keep a 44 px target on the narrowest phones.
- Time travel renames Now to Latest, since it moves to the newest loaded sample, and names its
  speeds Slow, Normal, and Fast.
- Radar copy is honest about its sources: the arming button reads Save sector, angle fields say
  they measure from heading, the static "Latest spoke received now" line is gone, and a stock
  server with no radar provider keeps the plain install hint instead of an HTTP status.
- GPX import and Reverse pan the chart to the route start the way a manual show does, and a
  trk-only GPX file explains that Binnacle imports routes rather than reporting none found.

### Fixed

- The chart context menu is no longer dismissed by a follow recenter, so Go to here and Start a
  route here can be completed while following with live GPS.
- Arming the toolbar Reset confirm no longer closes the whole menu, blocked-tile feedback cannot
  shift the menu layout or scroll out of view, and resetting the toolbar now asks before
  discarding, as does resetting the alarm thresholds.
- A delete can no longer race an in-flight chart replacement and resurrect the removed chart or
  overwrite an unrelated one, in the store and in the panel controls.
- Turning a parent overlay off and back on restores its children's visibility choices, and a
  parent with several children stacks them in registration order.
- Alarm tones no longer accumulate while a tab is suspended, which used to fire one distorted
  blast when the tab returned.
- A cached empty weather-warnings list reads as unavailable when a refresh fails, not as stale
  hidden data, and the forecast list names the provider and shows wind-wave height.
- A dropped Signal K stream is labeled on the populated AIS list and in the target detail, not
  only in the empty state.
- Alarm announcements use helm voice and fire only for grades that also sound or render, so a lone
  warn cannot interrupt a screen reader with nothing to show for it.
- Assorted accessibility associations across the panels: slider value text and labels, the panel
  minimize button's target, layer-toggle descriptions for touch and keyboard users, live regions
  for sync states and chart draw mode, and keyboard focus returning to the chart after a context
  menu action.
- The service worker precache lists each icon exactly once.

<a id="v0190"></a>

## [0.19.0] - 2026-08-03

### Added

- Eleven more chart overlays. GEBCO bathymetry can now be read as flat color bands, or restricted to
  where the seabed was actually surveyed rather than interpolated. EMODnet adds depth contour lines.
  The jurisdiction set gains the 24 nm contiguous zone, the high seas, and the IHO named sea areas,
  and the protected-area set gains UNESCO marine World Heritage sites, which is the first
  protected-area layer here that covers the whole world rather than one region.
- Seabed infrastructure overlays: submarine power cables, telecom cables, pipelines, and offshore
  wind farms. These answer an anchoring question the panel could not before, since dragging across a
  submarine cable can part it and anchoring on one is both a hazard and, in most places, an offense.
  All four default hidden and draw with the other safety overlays.

### Changed

- Seascape draws one zoom level deeper: depth shading and hillshade now reach zoom 18, and contours,
  soundings, and drying areas reach zoom 15, following the tiles the service actually publishes.
  Two consequences at those zooms. A saved area that includes Seascape contours at Harbor detail is
  roughly three times the download it used to be (about 592 MB rather than 178 MB for that layer on
  a San Francisco Bay box), so an area that used to fit may now report insufficient space. And
  browsing past zoom 17 fetches genuine depth-shading tiles instead of stretching the last
  generation, which is sharper but caches about four times the data.
- The Seascape credit shown on the chart now reads "Open Waters", the provider's current name.

### Fixed

- No-transmit radar sectors can be edited again, and their safety warning now describes both angle
  fields to assistive technology.
- Chart tools now apply their cursor as soon as MapLibre creates the canvas, even when the base style
  is still loading.
- Data trends keeps one stable panel instance when a phone crosses its focus-trap breakpoint, so
  orientation and viewport changes preserve panel scroll, focus, and child-local state.
- Incremental MapLibre GeoJSON updates now use the library's exported diff contract, including its
  keyed property-update payload.

### Development

- The chart-source catalog moved to 0.7.1. The seamark overlay and the base map style URL now read
  their upstream facts from it rather than keeping private copies, which is what let the old Seascape
  zoom ceilings sit in the tests until a correct upstream release broke the build. A zero-area
  area-download rectangle is now answered as
  covering nothing instead of reaching an enumerator that rejects it, so finishing a draw with a tap
  rather than a drag no longer raises. The catalog's time-dynamic weather and ocean sources are
  recognized from their declared maximum age and stay excluded from offline pre-warming, because a
  stored weather frame is wrong before anyone sails into it.
- GitHub Actions workflows are now checked by zizmor in their own CI job. It found that every
  checkout left the repository token in `.git/config`, where an uploaded artifact can carry it off;
  no workflow pushes or uses that token, so none of them persists it any more. A full dependency
  audit joins the runtime audit in the release gate, kept as two separate gates because the
  community registry scores only the runtime one.
- Runtime and build dependencies moved to their latest compatible releases, including Lucide 1.28,
  MapLibre GL JS 6.1, Biome 2.5.6, Playwright 1.62.1, Size Limit 13.0.3, Knip 6.31, and Vite 8.2.
  Every direct dependency is now at its latest release except the `typescript` package, which stays
  on 6.x deliberately: TypeScript 7 moved the compiler API behind an unstable entry point, so the
  tools that read types cap below it. Type checking already runs on the TypeScript 7 compiler
  regardless, through `tsgo`. Development and release workflows now use npm 11.19.0, the newest
  release compatible with the Node 22.18 runtime floor.
- Dependabot now holds each new release for a week before proposing it, so this repository is not
  the first consumer of a package version that turns out to be compromised. Security updates are
  exempt.

<a id="v0181"></a>

## [0.18.1] - 2026-07-30

### Fixed

- Tapping a visible tide or current station with a mouse or touchscreen now opens its controls from
  either the marker or its prediction label, prioritizes that label over overlapping touch targets,
  tolerates briefly stale rendered features, preserves automatic signalk-tides behavior during
  source refreshes, deduplicates repeated NOAA station records, and repopulates a recreated chart
  source. Tide selection also waits for dirty Radar controls to be confirmed instead of opening
  underneath them.
- Clock-expired AIS markers and motion vectors now leave the chart even when no new AIS delta changes
  the store version. Stale Measure vertices and working-route waypoint hits are also rejected against
  current state, so a delayed rendered feature cannot swallow a chart tap or select an invalid index.
  Tide, AIS, and note hit surfaces no longer compete with active chart-editing gestures or cursors.
  Short touch taps now use the same drag-safe path as mouse clicks across map markers, chart tools,
  and weather point readouts, and an overlapping gesture goes only to the highest visible overlay.
- Lazy optional panels now time out with Retry, Back, Close, and Escape recovery instead of leaving
  the chart covered indefinitely, and a successfully imported panel that later fails to render gets
  the same recovery surface. Tide and AIS controls are warmed when their chart markers become
  available, and Instruments, routes, trends, dialogs, and chart tools use the same recovery
  contract. One-shot supporting overlays wait for slow imports instead of expiring their only
  registration attempt.
- Full-screen Instruments now yields to an existing chart panel when a tablet crosses the 900 px
  breakpoint, and opening Instruments closes other full-screen chart surfaces. A dirty Radar area
  draft retains its confirmation through this transition: Cancel keeps the editor and Discard
  completes the requested navigation.
- Duplicate or conflicting identities from chart management, Chart Locker regions and cache
  statistics, Signal K History columns, radar discovery and controls, weather forecasts and warnings,
  notes, symbols, and tide catalogs are normalized or rejected before they reach keyed interface or
  cache state. Radar enum controls preserve numeric and string wire values separately, and
  whitespace-bearing provider control ids no longer become invalid document label references.
- Persisted tide catalogs, predictions, plugin readings, and weather point conditions are validated
  and bounded before reuse. Corrupt or older cache records fall back to providers instead of wedging
  a loader, queued tide requests continue after an unexpected failure, and rejected weather loads
  clear their loading state and offer Retry.
- Hidden, fully transparent, detached, or chart-tool-blocked AIS, Tide, note, and anchor interaction
  surfaces now cancel armed touches, pending cluster actions, drag ownership, and stale cursors.
  Genuine mouse input immediately following a touch remains usable on hybrid devices.

<a id="v0180"></a>

## [0.18.0] - 2026-07-29

### Added

- Personal chart notes can now be created from the chart actions menu with bounded name, text,
  category, symbol, and position fields, then edited, moved, or deleted from their detail panel.
  Binnacle writes standard Signal K v2 note resources with a strict namespaced ownership marker, so
  third-party notes remain read-only. Missing providers, v1-only providers, and read-only access are
  explained without hiding the editor, failed saves retain every field, and confirmed writes stay
  synchronized with the chart and Find places through a failed refresh.
- Measure points can now be selected through a 44 px chart target or the strip, deliberately moved
  by drag, chart tap, or chart-center keyboard workflow, and deleted with operation-based Undo.
  Selected points show both adjacent rhumb-leg distances and true bearings, while antimeridian-safe
  distance labels use collision placement above a bounded zoom. Move mode cancels before Measure on
  Escape, Clear requires confirmation, and route editing is excluded in both directions without
  silently clearing either tool.
- Complete native Radar API zones, no-transmit sectors, and rectangles now have explicit form and chart
  editors with degree and server-length display conversion, separate enabled state, atomic Save,
  Cancel, dirty-discard confirmation, live provider conflict detection, exact rollback, and
  provider-safe bulk writes. Radar chart placement is cancelable and mutually exclusive with Measure,
  route editing, Offline charts, and delegated marker actions. Placement reveals hidden radar areas,
  keeps its step and Stop action visible on phones, preserves each zone bearing with its tapped
  distance, and stops with a clear error if position or heading freshness is lost. Areas follow the
  live spoke-frame heading and range, reject oversized provider angle domains, cap polygon
  tessellation, and stay pure red in night-red. Sector writes require a separate emission-envelope
  confirmation, while unsupported compound controls remain honestly read-only.
- Time travel now offers bounded 1-hour, 6-hour, 24-hour, and 7-day history ranges with adaptive
  resolution, play and pause, 0.5x, 1x, and 2x speeds, full local dates, provider attribution, and a
  range-owned track synchronized with the marker and metric readouts. It keeps one provider's
  accepted data visible during refresh or failure, pauses when the page is hidden, disables
  automatic playback for reduced motion, and never persists its temporary vessel dimming or track.
- User-added PMTiles charts can now stage and review a replacement URL, refresh metadata from the
  current URL, and switch between device-only and Signal K sharing. Replacements preserve the chart
  id, name, visibility, opacity, and stacking position, restore the accepted overlay on failure, and
  keep query values redacted from readouts and errors.
- Nearby vessels now searches reported names and Maritime Mobile Service Identity numbers before the
  500-row display cap, filters collision risks and getting-close targets, opens the same live
  in-panel detail from the list or a generous chart target, highlights the selected target, and
  names supported AIS ship types while retaining their numeric ids.
- A generic alarm surface for the boat's own equipment: any inbound Signal K alarm or emergency
  grade notification outside the dedicated hazards (man overboard, anchor drag, collision danger,
  and shallow water) now sounds its own tone and raises an Alarm strip, with worst-first ordering,
  a quieted-dim state, Silence, Acknowledge, a device-local Mute here fallback for when the server
  cannot silence it, and an Open Alarms action. The Alarms menu entry now carries a live count of
  raised alarms, and a notification's method field is honored, with sound as the safe default when
  a producer names none.
- Waypoints now search name and description, sort by name, distance, or bearing with fresh-fix-only
  metrics, and follow GPS availability until a sort is chosen, capping the rendered list and saying
  how many matches are hidden.
- Instrument tiles and their detail view now show the server's meta display name for a path when
  the boat has renamed it, falling back to the catalog label otherwise.
- Data trends can now show zero to eight profile-owned instrument readings in saved order.
  Customize groups static and discovered readings by category, supports touch and keyboard
  reordering, preserves temporarily unavailable selections, and keeps a ninth option visible with
  an explanation. Eligible instrument details add a focused recent-trend action that leaves the
  saved overview unchanged and returns to the same detail and control.
- Tracks now detect a server with no track storage (tracks are not a standard Signal K resource
  type) and say so directly, naming the one administrator step to enable it, with a Check again
  action, instead of only reporting the collection as reachable and empty.
- The offline charts page and guide now explain the HTTPS boundary directly: which cache layer
  needs a secure context, why a plain-HTTP server still caches through Chart Locker and the
  browser's own IndexedDB, and how to add and trust a certificate.

### Changed

- Radar control writes now enforce both capability read-only state and the live allowed flag at the
  action boundary, serialize requests per radar and control, coalesce unsent updates, protect active
  writes from polling, and parse the standard rectangle coordinate fields.
- The status strip's Depth readout, the anchor panel, and the depth instrument tile now resolve
  depth per purpose from whichever of keel, transducer, and surface references the boat publishes,
  in that safety priority, and label the readout with its source (Keel, Xducer, Surface) so two
  depths on screen at once never look contradictory.
- The shallow-water alarm now follows the server's depth zones when they publish an alarm band,
  merging conservatively with the locally configured threshold so the deeper of the two always
  governs; the Alarms panel explains which one is in force and says when no depth source is
  publishing.
- Starting navigation from a saved waypoint now sends the server the waypoint's resource reference
  instead of a bare position, so the destination name reaches the navigation strip and other
  stations; a rejected reference retries once with the position alone.
- Stop navigation, on the navigation strip and in the routes panel, now requires a confirming
  second tap instead of firing immediately. The strip's Stop control's accessible name is now its
  own visible text, so it reads "Confirm stop?" while armed instead of a fixed "Stop navigation"
  label throughout.
- Alarm audio now shares one AudioContext for the whole app instead of one per alarm, primed from
  both a pointer gesture and a keydown so a keyboard-only operator gets audible alarms too, and a
  second alarm raised while one is already sounding is now heard rather than absorbed into the
  running tone.
- Data trends now resolves each chart from one ordered Signal K path and one registered history
  provider instead of combining fallback sources. Nonempty 24-hour history wins per chart, while an
  empty or unavailable provider falls back to bounded in-memory session samples. Charts identify
  their source and provider, retain accepted data during retries, and add touch and keyboard timeline
  scrubbing plus textual latest, minimum, maximum, start, and end values.

### Fixed

- Signal K style-document chart sources no longer register as checked, opacity-capable blank layers.
  They remain visible with details available for inspection in Charts, but are disabled, forced off,
  and explain that style-document rendering is unsupported. Runtime provider loss now hides affected
  overlays while retaining the user's visibility preference for recovery. Other chart sources
  continue loading normally.

<a id="v0171"></a>

## [0.17.1] - 2026-07-27

### Fixed

- The README no longer contains relative file links. The Signal K App Store README view renders
  link targets unmodified (only image paths are rewritten to the package CDN), so links to the
  shipped guides and repository files could never resolve there. Guide references are plain text
  now, and the security policy link is an absolute URL.

<a id="v0170"></a>

## [0.17.0] - 2026-07-27

### Changed

- Offline chart areas now use the shared chart catalog's disjoint NOAA ENC coverage regions, so
  availability and download estimates exclude waters without NOAA chart cells.
- The map engine is MapLibre GL JS 6 and now requires WebGL2. The shipped ES2023 bundle requires
  Safari 16.4 or later. A device without WebGL2 sees a clear notice in the affected map surface
  while instruments, alarms, and panels keep working. Provided symbol anchors stay pinned under
  v6's changed icon-offset scaling, and bundled builds explicitly emit and configure the map worker
  so vector maps start reliably.
- Collision warnings now publish over the delta stream only, so a warning stays visual instead of
  sounding boat-wide through the server's REST raise, which hardcodes an audible method. Danger
  alarms still raise through the Notifications API, and a successful raise or update now retracts
  any outstanding delta so the shown grade cannot stick at an older state after a clear.
- A write-access upgrade that ends without a grant now says whether it was declined, went
  unanswered (it may still be waiting in Access Requests), or never reached the server, each with a
  retry, instead of silently reverting to the generic read-only banner.
- Menus share one keyboard focus machine: the toolbar More and overflow menus redirect Tab and
  restore focus identically, grayed rows are skipped by arrow keys in every menu, every anchored
  menu closes when focus leaves it, and the layer opacity dialog no longer stays open when Tab
  moves past its controls.
- Line geometry that crosses the antimeridian now splits into canonical date-line segments across
  every overlay (trails, vectors, rings, tracks, routes, and measurements).
- The radar sweep and wind particle renderers reuse their per-frame center and viewport values
  instead of allocating new ones on every frame.

### Fixed

- Cross-platform webapp CI now installs the Chromium revision required by the
  Vitest browser project before running tests. The no-storage PWA reload test
  also models unavailable storage explicitly on Node 26, where
  `sessionStorage` is globally available.
- Patched transitive development dependencies remove the current
  `brace-expansion` and `js-yaml` advisories, and the PWA build now uses the
  maintained off-main-thread plugin line without the vulnerable EJS toolchain.
- WebGL2 startup now treats optional context cleanup as best effort and recovers from MapLibre's
  partial GPU-initialization result, so a valid renderer is not rejected and an actual failure
  leaves instruments, alarms, and panels usable. A late companion base-style failure or silent
  stall now starts a fresh bounded watchdog for the direct OpenFreeMap retry before the offline
  fallback takes over.
- A chart's declared native zoom cap now remains authoritative while MapLibre applies source options
  asynchronously, instead of being replaced by the runtime source's temporary default of 22.
- A locally cached profile applies at startup again. The startup apply ran before the map command
  bindings it pushes settings into existed, so any saved profile made boot fail its apply with an
  initialization error whenever profiles were stored locally.
- The automatic reload after a service-worker update is now single-shot per 30 second window. A
  pathological environment that keeps changing the controlling worker (a mutating proxy or
  extension, or repeated external activations) previously drove an endless reload storm that
  canceled its own navigation attempts and left the page unable to settle; the guard reloads once, then
  logs and defers to the Update control. Clicking Update always applies: an explicit click bypasses
  the guard, and a click that arrives after a suppressed reload (the new worker already controls
  the page) reloads directly instead of doing nothing.
- With an active collision contact, AIS vectors and icons no longer rebuild once per second: the
  collision assessment re-runs only when a staleness flag actually flips, steady-state AIS churn is
  throttled to about one repaint per second while new targets and severity changes still paint
  immediately, and radar range rings are reused when only the heading changes.
- Time-travel scrubbing finds its samples by binary search instead of a full scan, and the scrub
  position clamps into range when a reload returns no samples.
- A stale notification could linger on the server when a collision cleared during a Notifications
  API outage; the orphaned alert is now resolved once the API answers again.
- Concurrent note loads for different viewports no longer share one in-flight slot, so the notes
  overlay cannot report ready or failed while a fetch is still running.
- Fractional measured tile averages from Chart Locker are rounded up at the estimate boundary, so
  valid cache statistics no longer prevent an offline-area download estimate.
- Instrument zone banding drops malformed server metadata zones, so a zone with a non-numeric
  bound can no longer mis-band a reading.
- A theme or opacity change arriving while an overlay's layers were still being added could throw;
  overlays now apply the change once the add completes.
- Panel loading and error placeholders render on the panel surface instead of as bare text over
  the moving chart, load errors show in the alarm color, and their Retry buttons no longer
  reference an undefined button style.
- The AIS target list rejects a malformed own-position cell key instead of reading an empty
  coordinate as zero.

### Development

- The build tooling moved to rolldown code splitting, per-icon Lucide 1.27 imports, MapLibre GL JS
  6.0.0 with an explicitly emitted worker bundle, PBF 5.1.2, Terra Draw 1.32.2, Svelte 5.56.8,
  Playwright 1.62, the Svelte Vite plugin 7.2, dependency-cruiser 18.1, markdownlint-cli2 0.23.2,
  @types/node 26.1.2, and refreshed lint and test dependencies (eslint 10.8, knip 6.29, publint
  0.3.22, and size-limit 13).
- TypeScript 7.0.2 remains deferred because the latest typescript-eslint 8.65.0 supports TypeScript
  versions below 6.1. TypeScript 6.0.3 is the newest compatible compiler for the current lint
  toolchain. npm 12.0.1 also remains deferred because its Node 22.22.2 minimum is above Binnacle's
  supported Node 22.18 runtime floor; npm 11.18.0 is the newest compatible package manager.
- Overlay tests build their map context through one shared helper, and SKFrame test factories can
  carry AIS vessels.
- Type-checking runs on the TypeScript native preview (tsgo, via svelte-check 4.7.4) for the app,
  the tooling config, and now the repository scripts, while the compiler dependency stays on
  TypeScript 6.0.3 for the lint toolchain above.
- The typed promise lint rules cover test files, with every async overlay call in tests awaited,
  and the prose gate flags standalone ampersands in prose and two-word chartplotter spellings.

<a id="v0160"></a>

## [0.16.0] - 2026-07-19

### Added

- Persistent warnings now explain when Binnacle is connected to a non-local Signal K server over
  insecure HTTP.
- Functional WebKit smoke coverage now complements the full Chromium browser suite, and direct alarm
  tests cover anchor, MOB, shallow-water, and waypoint-arrival behavior.

### Changed

- Chart discovery now uses `signalk-chart-sources` 0.4.0 and its current provider contract.
- Every query-bearing chart URL stays on this device by default, all query values are redacted in
  displays and errors, and sharing the full URL with Signal K requires an explicit reviewed choice.
  Signed and cache-busted PMTiles URLs retain their query strings when requested.
- Signal K deltas, history results, radar messages, weather data, chart metadata, symbols, profiles,
  and companion responses now use explicit structural, count, text, and byte limits.
- Map and weather renderers now bound device-pixel scaling and wind-particle buffers, while overlay
  loading and optional panel imports can recover after transient failures.
- Development and release checks now use npm 11.18.0, current compatible Svelte and Vite tooling,
  metadata-derived package assertions, stronger coverage floors, CodeQL analysis, grouped Dependabot
  security updates, and the exact Node 22.18 compatibility floor.
- Network privacy documentation now identifies every external data provider and explains when each
  request occurs.

### Fixed

- Alarm actions now distinguish servers that delegate notification management from transport or
  write failures, and Silence and Acknowledge are offered only when the server explicitly advertises
  those capabilities.
- Course guidance now clears immediately when another station removes the active server course.
- IndexedDB writes now wait for transaction completion, and a connection that succeeds after a
  blocked open was abandoned closes immediately instead of becoming an untracked upgrade blocker.
- Restored active tracks reject duplicate and regressed stored timestamps but preserve a future clock
  epoch. After an RTC or NTP rollback, the first new fix starts a segment break on a monotonic logical
  timeline, including when live fixes race the initial IndexedDB restore, and track history retains a
  fixed capacity.
- Route and waypoint identifiers now require strictly valid values, GPX imports bound names and count
  all encountered records, and course arrival state includes route identity and point index. GPX
  structure now uses a bounded one-pass scanner, preventing repeated unclosed tags from causing
  structural regular-expression backtracking.
- Profile storage survives blocked localStorage access, and remote profile merges retain local data
  instead of silently truncating an over-capacity union.
- RainViewer metadata is fully validated, optional RainViewer failures no longer discard a valid
  atmospheric or marine forecast, and optional weather and panel loaders retry cleanly after failures.
- Signal K point-weather calls now time out, inverted warning intervals fail validation, and missing
  or oversized optional warning text receives bounded fallback labels. Open-Meteo marine fields are
  omitted when sea-snapped coordinates exceed a grid-scaled alignment tolerance.
- Signal K history values now require a valid ordered range, columns, and a data array. A valid empty
  data array remains distinct from a transport or schema failure.
- Layer, chart, and weather teardown now prevents late asynchronous work from reinstalling overlays,
  mutating destroyed maps, or leaking partially initialized resources. Canceled or superseded user
  chart imports and same-id replacements wait for stale cleanup, and per-chart server PUT and DELETE
  intents are serialized so the final local action wins.
- Radar discovery, controls, streams, spokes, frames, and worker traffic now reject malformed or
  oversized input and adapt update frequency to bounded copy throughput. Radar and control
  identifiers preserve bounded provider punctuation while rejecting control characters and unsafe
  object keys, dotted control ids reconcile from deltas, and disposal clears control polling.
- PMTiles recognition now uses the URL pathname so signed query URLs work. Range requests combine
  caller cancellation with finite timeouts, avoid retrying aborted reads, cancel rejected response
  bodies, reject authority credentials, redact query values, and discard URL fragments before
  storage or use.
- NOAA CO-OPS station identifiers now reject query injection, and malformed or oversized station and
  prediction containers fail validation before iteration.
- Anchor, MOB, overflow-menu, and night-mode controls now expose complete accessible descriptions and
  predictable keyboard behavior. Forward Tab from the last overflow-menu item exits naturally instead
  of looping back to the trigger.

<a id="v0156"></a>

## [0.15.6] - 2026-07-17

### Changed

- Anchored menus can now opt into one shared viewport-fixed positioning path that aligns with its
  trigger, flips above or below based on available room, clamps to every screen edge, and follows
  viewport resize and panel scrolling.

### Fixed

- Profile and route card overflow menus remain fully visible on narrow displays, including the first
  card in a panel and cards reached by scrolling. Final-size measurements now ignore the opening scale
  transition so a menu cannot grow a few pixels beyond an edge.
- Layer opacity controls flip away from a clipped panel edge, and long bottom-toolbar More menus stay
  within short landscape displays with their own scrolling region.
- The mobile app-menu sheet now reserves the existing Android and Samsung system-bar clearance and
  follows landscape safe-area insets, keeping its final controls out from under device chrome.

<a id="v0155"></a>

## [0.15.5] - 2026-07-16

### Added

- A typed persistence-scope registry now classifies every Binnacle localStorage setting as portable
  profile data, device state, a server resource, safety state, or credentials. The profile bundle now
  includes the preferred radius for the next anchor drop without carrying an active watch.
- A profiles guide documents autosave, cross-device sync, conflict handling, privacy, migration, and
  which settings intentionally remain specific to a browser or Signal K server.

### Changed

- The active profile now saves automatically after settings change. Profile selection remains local
  to each browser, while named profiles and the default sync through the authenticated Signal K
  account. Remote changes to the profile currently in use require explicit application before they
  alter the live chart.
- Profile sync now uses a revisioned version 2 applicationData document, a durable local mutation
  journal, tombstones, and per-setting logical clocks. Independent edits from multiple stations merge
  without replacing unrelated settings, and version 1 documents migrate without being overwritten.
- The Profiles panel now loads on demand, keeping its management and privacy tools out of the initial
  chart bundle until they are needed.

### Fixed

- Startup now hydrates server profiles before creating starters or applying a default, preventing a
  fresh browser from replacing an existing setup. Stable starter identifiers no longer receive
  current-time timestamps that could outrank a customized server copy.
- Offline edits survive reload and retry after reconnect, autosave writes are serialized with refresh,
  remote defaults apply on first startup, and a profile is always active after initialization or
  deletion.
- Equal-clock edits now converge on the server copy, unresolved remote updates survive reload, and a
  remotely deleted active profile is replaced without changing the live helm setup.
- Profile erasure now suspends queued and in-flight persistence, profile HTTP requests are bounded,
  version 2 initialization reads back the authoritative document, generic server failures are no
  longer reported as revision conflicts, and unsafe or oversized profile data is rejected.
- Offline edits and deletions now rebase their logical clocks above newer server state, version 2
  creation is conditional, untouched offline fallback profiles yield to the synchronized default,
  and forward-compatible field clocks remain readable.
- Device-local profile selection and last-applied state now use a separate storage record from the
  portable profile library, including during local privacy erasure. Corrupt device-only state no
  longer discards an otherwise valid profile library.
- Failed initial server hydration now creates a replaceable offline fallback instead of permanent
  starter profiles, and forward-compatible values remain paired with their winning field clocks.

<a id="v0154"></a>

## [0.15.4] - 2026-07-16

### Fixed

- Saved-area creation remains compatible with Chart Locker 0.5.0 responses that omit the initial
  cache-derived byte total, while creation and re-download now support recovery responses from newer
  Chart Locker builds when the server loses a warm job identifier. Binnacle continues polling by
  region identifier until Chart Locker reconciles the background download.
- Chart Locker mutation payloads must match their documented HTTP statuses. Starting a new poll clears
  stale progress, repeated status failures offer an explicit retry, removed chart sources are
  identified before a re-download can fail, and bounded server rejection reasons replace opaque
  HTTP-number errors.

<a id="v0153"></a>

## [0.15.3] - 2026-07-15

### Added

- Profiles now includes device-privacy actions to forget the local Signal K device token or erase
  Binnacle settings, browser caches, IndexedDB data, profiles, and credentials. Erasure is blocked
  during active MOB, anchor, navigation, route-edit, measurement, or unsaved-track work, is limited
  to explicitly owned Binnacle storage, and reports partial failures.
- Signal K resource requests now have an injectable transport with live credential lookup, bounded
  timeouts, and instance-scoped write outcomes while existing clients retain their compatibility API.

### Changed

- The development toolchain now uses one shared verification hierarchy across local hooks, CI, and
  release publication. It adds Svelte and typed async linting, tooling type checks, Markdown and
  spelling checks, coverage floors, bundle budgets, package-content validation, exact local tool
  versions, and pinned GitHub Actions. Browser tests reuse the verified production build, and npm
  publication uses the exact tarball that passed the release gate.
- Offline-chart discovery distinguishes an absent Chart Locker installation, refused access, and an
  unreachable service. Saved-area orchestration now lives in a focused controller, and optional map
  overlays register independently so a slow or broken server chart catalog cannot delay safety layers.
  Chart Locker readiness checks use only the browser administrator session, preserving a refused
  plugin base for recovery without allowing a device bearer token to mask a valid administrator.
- Persisted settings now use bounded codecs with migration and repair reporting. Invalid stored
  navigation, alarm, chart, instrument, layer, unit, profile-support, and authentication values are
  replaced safely before they enter reactive state.

### Fixed

- GitHub workflows now disable package-manager cache detection until the supported npm version is
  installed outside the checkout, so the Node 22 matrix can bootstrap before `devEngines` is checked.
  The commit gate prevents this ordering from regressing, and package validation invokes npm through
  its JavaScript entry point so it runs consistently on Windows. Publication uses an explicit local
  tarball path so npm cannot interpret the artifact as GitHub shorthand, and artifact actions use
  their Node 24 releases.
- Dynamic instrument labels now identify both the reading and its source across engines, tanks,
  solar controllers, and cabin sensors. Customize also disambiguates any future repeated catalog
  label in visible text, accessible checkbox names, and reorder announcements.
- Async overlay, MapLibre event, and file-picker paths now declare or handle their promises
  explicitly, forecast risk cues use stable keys, and Svelte menu children use the native snippet
  path expected by the component API.
- Stream reconnect generations now prevent values from a prior connection from becoming current.
  AIS freshness is tracked per path, paired CPA and TCPA expire together, course calculations expire,
  MOB alarms re-arm, anchor watch reports lost fixes, and radar rendering rejects stale center or
  heading data.
- Route arrival advancement now reconciles with the current server snapshot before writing an
  absolute point index, preventing duplicate waypoint skips when the server has already advanced.
- PMTiles range reads now verify status, `Content-Range`, length, and archive identity before caching.
  Concurrent tide requests use the latest result, and track simplification preserves short
  antimeridian crossings.
- Server-provided chart symbols now require same-origin SVG paths, bounded metadata and bodies,
  correct media types, and passive SVG content. The application entry point also applies a content
  security policy and no longer needs an inline theme script.

<a id="v0151"></a>

## [0.15.1] - 2026-07-14

### Added

- Instrument discovery merges the live Signal K model with the preceding year of paths from
  registered history providers within a bounded scan, including QuestDB, so seasonal engines and
  sensors remain available to configure while stopped. Previously seen readings are labeled as not
  reporting live and never replace current values with stored samples.

### Fixed

- Battery instruments include voltage, state of charge, time remaining, or current in their visible
  labels, so multiple values from the same battery remain distinguishable in Customize and on tiles.
- Instrument scans validate provider ownership and own-vessel population, bound provider, path, and
  identifier input, retain accepted results through partial failures, cancel superseded history
  queries, and reject malformed or incomplete provider responses.
- Radar controls offer overlay settings only after the layer panel is ready, preventing an action
  that could not open its destination during startup.

<a id="v0150"></a>

## [0.15.0] - 2026-07-14

### Added

- A reusable latest-write-wins persistence queue serializes whole-document settings updates, exposes
  saving and failure states, and supports retry without allowing an older response to overwrite a
  newer choice.
- Dead-code validation now runs through `npm run deadcode` and checks unused files, dependencies, and
  public exports in local, CI, and release gates.
- The chart now has 44 px zoom controls plus a nautical scale. Chart actions also open from the
  keyboard Context Menu key or Shift+F10 at the chart center.
- Phone chart workflows can collapse Routes, Tracks, Waypoints, Find places, Tides, Layers and
  charts, and Anchor watch to their headers. A one-time touch hint teaches the chart action gesture.
- Browser coverage now includes desktop and phone WebKit, reduced motion, night-red restoration,
  touch target sizing, keyboard focus, and automated accessibility checks.
- Every chartplotter menu action now has one operational reference covering availability, loading,
  recovery, write access, stale sensors, provider fallback, and safety behavior.
- Layers and charts, Forecast, Tides, Data trends, and Time travel now expose explicit recovery for
  failed provider or module loads while preserving accepted data where available.
- Waypoints now reports loading, retained-data refresh, real empty results, and failures. Navigation
  requires a destination-specific confirmation, and browser coverage verifies HTTP-only loading,
  confirmation, and narrow-screen layout.
- Measure now guides each next chart tap, shows a crosshair while active, announces changing metrics,
  reports its point limit, and has browser coverage for drawing, active-menu retention, Undo, Done,
  cursor restoration, and narrow-screen layout.
- Tracks now reports saved-resource loading and refresh failures, memory-only recording, point counts,
  GPS-gap route behavior, and pending operations. Retrace requires an explicit safety confirmation,
  and browser coverage verifies HTTP-only loading and narrow-screen layout.
- Find places now reports loading, chart zoom limit, real empty results, cached-offline data, and
  provider failures separately. Provider-backed and narrow-screen browser coverage exercises the
  complete menu flow.
- Marine radar now reports provider availability, radar identity, separate spoke-stream and renderer
  health, pending control writes, rejected controls, and stale-picture state in the controls panel.
- Radar capability parsing now preserves string, button, sector, zone, rectangle, compound, category,
  ordering, enabled, allowed, and read-only metadata instead of silently dropping unfamiliar controls.
- An apple-touch-icon and larger PNG app icons, for a proper icon on an iOS home screen install
  alongside the existing SVG favicon.
- The instrument catalog now discovers batteries, engines, tanks, chargers, inverters, shore power,
  solar controllers, and environmental sensor instances from the live Signal K tree. Instrument
  detail views show the path, source, update age, units, and recent values, and the weather display
  adds richer observed, forecast, warning, wave, current, visibility, and provenance data.
- Route cards can import and export standard GPX routes, show the entire route on the chart, reverse
  a saved route, and present a leg-by-leg passage plan with a bounded planning speed.
- Saved offline areas now expose their bounds, included charts, detail range, size, update date, and
  visible tile and byte progress. They can be shown on the chart or reused as the safe starting point
  for an adjusted copy without deleting known-good coverage first.

### Changed

- App Store screenshots now use a controlled Boston-area demo view with no own-vessel coordinates or
  embedded location metadata.
- Stream lifecycle, reconnect behavior, and notification orchestration now live in focused app
  controllers. Plotter dependencies are grouped by services, controllers, entity stores, and actions,
  and optional offline-chart, installed-chart, radar, and weather panels load only when opened.
- Offline-chart storage and automatic-caching screens are separate view components, while the parent
  retains area selection, download orchestration, and sub-view navigation.
- Updated `signalk-chart-sources` to 0.3.1, adopted its readonly catalog metadata for NOAA ENC
  rendering, and describe offline download size as a planning estimate.
- The phone shell keeps full-size header controls, condenses secondary status readouts and actions,
  places Safety near the top of the menu, and keeps emergency strips pinned above secondary strips.
- Route and profile cards keep their primary action visible and move secondary actions into a labeled
  keyboard-accessible overflow menu. Progress copy consistently uses an ellipsis, and actionable
  failures use the shared alert treatment.
- Starter profiles now configure distinct helm actions and instruments for coastal day, night
  passage, and anchor use. Profile sync reports Local, Syncing, Synced, and Failed states with Retry.
- Release automation now waits for an intentionally published GitHub release and rejects a release
  tag whose version disagrees with the package or whose commit is outside `main`. A maintainer
  checklist separates preparation from the owner-approved tag and publication steps.
- Center, Follow, Nearby vessels, and Anchor watch no longer derive actions, distances, or bearings
  from stale GPS. AIS and alarm panels now identify disconnected stream state instead of presenting
  cached state as current.
- Routes, chart sources, AIS targets, alarms, weather and tide providers, instrument discovery,
  history rows, profile documents, names, ids, numeric settings, imports, and persisted collections
  now enforce explicit validation and size bounds before entering UI state.
- Profiles now confirm before replacing unsaved active settings, report completed imports, and explain
  local-only deletion when server write access is unavailable. Time travel's Now action moves to the
  newest loaded sample without issuing a hidden refresh.
- Waypoint reads load when access resolves without waiting for the WebSocket, reject stale refreshes,
  serialize writes, disable conflicting controls, and update accepted adds, edits, and deletes before
  the follow-up refresh. Failed saves retain the dialog and entered values.
- Waypoint resources now bound ids, names, descriptions, icons, and collection size, reject invalid
  coordinates and control characters, and keep the v1 collection as a read-only fallback.
- Measure preserves active work when its menu item is selected again, limits measurements to 1,000
  validated points, ignores duplicate consecutive points, and draws date-line crossings by the short
  leg. Measure from here still starts fresh.
- Saved-track writes and deletes update the panel and overlay immediately, serialize conflicting
  mutations, retain confirmed writes through refresh failures, and ignore stale refresh results.
  Saved tracks load when access resolves even if the Signal K WebSocket cannot connect.
- Track-to-route and retrace use only the latest continuous segment. Stored track settings are bounded,
  incoming resources and filenames are hardened, and shared profile validation uses the same settings
  guard.
- Find places now searches names, categories, sources, and attribution without case or accent
  sensitivity; exposes category and source on each row; uses stable sort tie-breakers; limits the
  rendered list to 250 results; and turns on its matching chart overlay when opened.
- Find places uses only a fresh GPS fix for distance, true bearing, and its nearest-first default. A
  selected result stays visually identified, and returning to the menu clears selection and preview.
- Marine radar now hydrates controls from the standard `/controls` endpoint and reconciles live values
  from `radars.*.controls.*` Signal K deltas. Discovery is refreshable, including token changes,
  provider removal, malformed providers, access refusal, and transport failure.
- The radar spoke worker runs only while the overlay is visible, the page is visible, and the selected
  radar is transmitting. Radar range and angle controls remain SI internally and convert only at the
  display edge according to the server unit preference.
- MapLibre, Terra Draw, and pmtiles now build into their own cacheable vendor chunks, and
  production builds carry hidden sourcemaps for future error-monitoring symbolication.
- Menu polish: an unavailable item (Radar, Time travel) keeps its own icon with a small add-on
  badge instead of a warning triangle, and tapping or clicking it now shows why it is grayed
  instead of doing nothing. The floating menu, side panels, and bottom strip are fully opaque in
  every theme instead of letting the map show through. The access-request banner leads with one
  short line and a clear "Approve in Signal K" button. "Trends" and "Replay" are now "Data trends"
  and "Time travel" everywhere they appear, instead of three different names for one feature.
- SOG and COG in the status strip now carry a tooltip explaining what they mean, and the AIS list
  and the collision alarm's sensitivity fields now show CPA and TCPA alongside their existing
  plain-English labels.
- Charts and overlays now have separate, purpose-specific views. Charts selects and inspects chart
  sources, while Overlays owns visibility, opacity, and stacking. The toolbar editor and instrument
  customizer use the same compact editing pattern, and the status-strip clock uses local time.
- Route lists keep their current data when a refresh fails, route writes update the list immediately,
  active-route edits refresh through the Signal K Course API, and route mutations are serialized to
  prevent duplicate saves or conflicting actions.
- Offline charts now has one discoverable menu entry and landing page for saved areas, automatic
  caching, installed charts, and storage. The area builder follows a select, customize, and review
  sequence; explains every disabled download; gives Overview, Coastal, and Harbor plain-language
  guidance; and collapses to its header while a phone user draws on the chart. Installed charts adds
  refresh, autosave guidance, scale details, and server-folder remediation for invalid files.
- The header's offline control now shows the cache amount or visible access and service state instead
  of presenting a provider-health check as passage readiness. Saved-area status remains the
  authoritative readiness check.

### Fixed

- Chart Locker cache statistics accept the fractional per-source averages returned by the live
  service instead of misclassifying a healthy response as unavailable. Detection now tries the
  administrator session before a device token, treats the plugin's not-ready response as installed,
  and prevents an older probe from overwriting a newer successful result.
- The npm package now includes the feature and operational guides linked from the README, so those
  links work in the registry and Signal K Webapps view instead of pointing outside the tarball.
- Failed Signal K streams can be retried without reloading the app, including a fresh worker and
  replayed instrument subscriptions. Stream startup failures are no longer left as passive status.
- Rapid offline-chart setting changes and overlapping installed-chart name or description edits can
  no longer arrive out of order and restore stale values. Save and initial-load failures are visible
  and retryable.
- Installed-chart responses are runtime-validated and bounded before entering UI state, including
  identifiers, zooms, bounds, formats, invalid entries, and collection size.
- Installed touch PWAs now keep the bottom status strip above Android and Samsung system bars, even
  when the browser reports no safe-area inset, and landscape chrome clears side insets.
- Offline chart planning now validates cache statistics, reports retryable estimate failures, uses
  source-specific byte estimates, and keeps actual storage enforcement in the downloader.
- Offline areas now preserve antimeridian-crossing rectangles when drawing, naming, fitting, and
  reusing saved settings, and the area tool now accepts the drag gesture described in the UI.
- NOAA ENC remains available across its service envelope but is selected automatically only for
  conservative United States coverage areas, including Alaska across the antimeridian.
- Depth, heading, course, and speed freshness are evaluated independently. Stale depth can no longer
  trigger or suppress the shallow-water alarm, and the status strip identifies the unavailable feed.
- MOB, collision, anchor, and shallow-water announcements no longer repeat changing metrics on every
  update. Generic Signal K alarms now have a deduplicated assistive alert channel.
- Routes, Tracks, and Waypoints expose real Retry actions without raising background-load toasts.
  Find places has a direct chart-layer toggle, and Radar links directly to overlay settings.
- Radar transmit now requires an inline confirmation. Instrument tiles expose their value, unit,
  freshness, and alarm state to assistive technology, with visible Warning, Alarm, and Stale labels.
- Saved night-red restores before the first paint and updates browser theme color. Icon pickers flip
  and clamp to available viewport space, and full-screen instruments contain and restore focus.
- KIP and history discovery distinguish checking, available, absent, failed, and retrying states.
- The Instruments dock keeps its title on one line, and the enlarged attribution target renders one
  full-size information glyph instead of a tiled group.
- The chart scale stacks its label and distance inside its measured box, bottom controls clear the
  helm toolbar, and the inert north-reset icon is removed by locking chart rotation north-up.
- Chart Locker management requests use the signed-in Signal K administrator session instead of a
  Binnacle device token that could mask valid access. Binnacle checks Signal K's live login status,
  distinguishes signed-out, non-administrator, and rejected-administrator sessions, and never asks an
  authenticated administrator to sign in again without evidence.
- Chart Locker sign-in stays in the installed PWA window and redirects back to the current Binnacle
  route. Management requests include the browser session and bypass cached authentication failures.
- The Offline charts guide documents administrator-session setup, same-origin cookie requirements,
  access recovery, passage downloads, automatic caching, storage, and status meanings.
- URL-backed charts now sync on unsecured Signal K servers, restored charts resync after access
  changes, and sync failures are visible. A malformed server chart can no longer abort chart startup.
- Anchor actions are serialized, browser-only fallback remains usable without server writes, alarm
  server actions cannot double-submit, and weather or time-travel loader exceptions no longer leave
  their UI stuck loading.
- Measure points now replace the store array instead of mutating it in place, so the overlay's
  identity-based dirty check redraws every accepted tap instead of stopping after the first render.
- Find places skips provider requests when offline without cache, refreshes immediately on reconnect,
  invalidates cache and pending work when credentials change, discards malformed persisted notes, and
  keeps live results independent of IndexedDB failures. Phone detail now returns to the result list.
- Track recording now rejects stale, malformed, out-of-order, and out-of-range fixes, normalizes bad
  speed values, splits implausible GPS jumps, prevents a late restore from undoing Discard, and orders
  persistent appends and clears. Fixes captured during a server save remain in the active recording.
  Route conversion no longer draws a guidance leg across a GPS gap.
- Find places no longer presents provider failure, a hidden layer, low chart zoom, and a real empty
  response as the same empty state. Out-of-range note coordinates and blank provider strings are now
  rejected or normalized before they reach the chart. A slow IndexedDB cache write no longer holds a
  successful provider response in the Loading state.
- Marine radar no longer keeps repainting and transferring a full polar buffer after spoke data stops.
  A five-second freshness guard clears the old echo and rings, and reports the picture as stale.
- Radar discovery now rejects unsafe or unbounded frame geometry, validates legend colors, resolves
  relative spoke-stream URLs safely, keeps tokens off cross-origin streams, catches worker-open failures,
  and ignores late discovery, selection, stream, and control-write results.
- Marine radar uses the vessel's true heading when a spoke omits bearing data and suppresses the echo
  when no safe heading is available. WebGL failures no longer overwrite spoke-stream health.
- Every radar legend pixel and sweep pixel has zero green and blue in night-red, including Doppler and
  history accents, which remain distinguishable through red intensity.
- A fresh install with no saved chart view now flies to the vessel once its first GPS fix lands,
  instead of leaving the map at the whole-world default forever.
- Night-red no longer leaks the base map's shaded-relief terrain colors at low zoom; the chart
  stays pure red on black at every zoom level now, not just where the vector layers dominate.
- The active waypoint's number no longer disappears under its own highlight ring while drawing a
  route.
- Starting a new route no longer throws a console exception on every click.
- The Chart Locker companion probe, run once at page load before authentication resolves, now
  retries once credentials arrive and again on reconnect, so an auth-gated companion is no longer
  stuck undetected until a reload.
- The chart's proximity highlight for a hovered search result or selected note no longer runs from
  two places at once, and the profiles panel no longer renders a redundant empty slot alongside its
  real one.
- The Signal K stream and marine radar worker connections release their callback proxies on
  reconnect, so a dropped and restored connection cannot leak a browser MessagePort.
- The marine radar's recycle-failure warning now surfaces in production too, not only during
  development.
- Wind particle WebGL textures are reused across forecast updates instead of recreated, and a
  removed PMTiles chart archive is dropped defensively rather than silently leaking if the
  library's internal shape ever changes.
- Instrument labels no longer repeat their unit or terminal path segment, battery labels include a
  useful instance name and measurement, and the Update button keeps readable contrast on hover.
- Route editing now emits its final geometry as soon as drawing finishes, ignores deferred editor
  work after cancellation, confirms before discarding an in-progress route, preserves an existing
  route name while editing, and disables waypoint skipping when the server does not provide a valid
  route extent.
- Route and Course API input validation now rejects malformed coordinates, route extents, calculated
  values, and active-route links. GPX import is bounded and strict about coordinates, unnamed GPX
  points remain unnamed, exported filenames are portable, stale vessel positions no longer draw an
  active course line, and route geometry remains finite at the poles.
- Offline charts no longer disappears when Chart Locker is missing or temporarily unreachable. It
  remains visible and explains how to install, start, or sign in to Signal K as an administrator.
  Long saved-area names no longer compete with status text, download progress includes a visible
  percentage and counts, and storage-cap failures now offer a direct path to Storage.

<a id="v0141"></a>

## [0.14.1] - 2026-07-08

### Added

- **Seascape bathymetry.** A new layer group in the Layers panel: depth shading and hillshade from
  Seascape's globally merged elevation model, plus contours, soundings, and drying areas from its
  vector tiles. Contour and sounding labels follow the server's unit preference, in meters or feet.
  All four rows start hidden until enabled for your area, and none of the depths are reduced to a
  chart datum, so they are for reference only, not for navigation.

### Fixed

- The map attribution box no longer pops open full width by default. MapLibre auto-expands it the
  first time any attribution text appears, which can happen well after the chart loads as overlays
  like Seascape and NOAA ENC register their own credits; the control now stays collapsed to its
  small icon until tapped, whenever that first happens.

<a id="v0140"></a>

## [0.14.0] - 2026-07-06

Adds the instrument dock: a customizable column of live gauges beside the chart. Also brings
provider-absence and read-write access handling in line across every write-gated panel and the
Replay and track history layers.

### Added

- **An instrument dock.** Tap Instruments (pinned to the bottom bar on fresh installs, and in the
  menu's Conditions group) and the chart slides left beside a column of live gauges: SOG, heading
  (with a magnetic or COG fallback), depth, and apparent wind by default, with STW, true wind,
  pressure, and position in the catalog. Customize picks and reorders tiles, the selection rides
  profiles, and on a phone the tiles take the full screen. A tile without its sensor says so
  ("No depth sensor") instead of showing dashes, values gray when their feed goes stale, and tiles
  color by the zone bands (meta.zones) configured on your server, with raised notifications taking
  the alarm tint.
- **Open KIP.** When the KIP instrument webapp is installed on the server, a menu item opens it in
  a new tab.
- **Course, battery, and smarter wind tiles.** A Waypoint tile shows distance and bearing to the
  next waypoint while a course is active; battery voltage tiles appear in Customize for every
  battery the server reports; and the wind tile falls back to ground-referenced wind (labeled GND)
  when there is no masthead anemometer, so virtual weather stations still show wind. Depth gets
  built-in shallow-water coloring (alarm under 2 m, caution under 5 m) whenever the server has no
  zones of its own.
- **A friendlier, denser dock.** Tiles lead with plain words (Speed, Heading, Wind, Water speed,
  Barometer) with the marine abbreviation as a quiet tag and pack a two-column grid with bigger
  numbers. The grid fills the whole dock: tiles share the available height instead of leaving
  empty space below, gaps in the grid are packed closed, and a tile without its sensor drops to
  half width so it never burns a full row. Every tile keeps the same height: when a sensor is
  absent, a plain note ("No depth sensor") sits centered where the number would be instead of
  shrinking the tile.
  The dock and the toolbar editor share one quiet customize control that names what it edits
  ("Customize instruments", "Customize toolbar") and opens with a short how-to line in edit mode.
  Bearings read as three digits ("004°") everywhere.
- **More instruments, with small visualizations.** The customize catalog gains battery health
  tiles for every battery the server reports (state of charge with a battery-bar glyph, time
  remaining at the present load, and charge or discharge current), water and air temperature,
  GNSS satellites in the fix, and rate of turn with a small needle dial. Continuous readouts
  (speed, depth, temperatures, pressure, battery voltage and current) draw a quiet sparkline of
  the last few minutes under the number. Tiles now center their text like gauge faces, the wind
  angle steps up a size, and the bottom strip reads at one size with COG dashing while the boat
  is stationary. The Customize view now splits into Shown (drag to reorder) and Available, so
  dragging visibly moves an instrument, including by touch on a helm display.
- Chart Locker joins the "Works well with" plugin recommendations.
- When Binnacle is installed as a desktop PWA and the browser supports Window Controls Overlay,
  the header bar extends into the native window title bar so the MOB button, navigation controls,
  and profile pill sit flush with the window chrome.
- Cloud and precipitation layers describe what they show in the Layers panel, matching the other
  weather layers.

### Fixed

- Routes, waypoints, tracks, anchor watch, alarms, and profiles now show a note explaining that a
  read-write token is needed when a save is blocked by read-only access, matching the note the
  charts and offline-areas panels already show, instead of only a generic error after the fact.
  Profiles keep saving on the device either way: only syncing them to other stations needs the
  token.
- Track history's layer row now grays out on a server with no history provider instead of
  appearing available and rendering nothing.
- The Replay tile now grays with an explanation when no history provider is present, matching the
  radar tile, instead of opening into an empty mode.
- Profile sync to the server now recovers after an access upgrade: approving a read-write token
  mid-session resumes syncing instead of silently staying local until the next reload, and the
  applicationData adapter always sends the current token.
- Adopting a sign-in from another tab no longer flashes the auth state back to "requesting" when a
  pending access poll resolves at the same moment.
- The Chart Locker management calls (saved regions, cache settings, region delete and redownload)
  now surface a failed response as an error instead of parsing an error body as data or silently
  swallowing it.
- Honor a reduced-motion preference change for the wind layer the next time it is turned on,
  instead of only after a full reload.

### Changed

- The chartplotter's panel layer moved into a dedicated view component behind the app's views
  layer, and the composition root slimmed to construction, controllers, and shell chrome. No
  behavior change.
- Waypoint, profile, and chart locker region deletes use the same one-at-a-time armed confirm as
  routes and tracks, so arming one row disarms the rest.
- The tides panel readouts use the shared stat-grid layout, and its close button is labeled "Close
  tides panel" (matching "Close tracks panel"), so the panels read consistently.
- The place-details panel lays its body out with the shared panel column, so the "Show on chart"
  action sizes to its content instead of stretching full width.
- Live track statistics are accumulated per fix instead of rescanned from the full point history on
  every update, so the tracks panel stays light on long passages.
- Faster nearby tide and current station lookup: stations far outside the search radius are skipped
  before any distance math runs.
- Repeated place-icon lookups and radar legend theming do less work per call, and the track
  simplifier allocates less on long recordings.
- The place-details cache keeps the most recently viewed places instead of evicting the
  oldest-loaded first.
- Radar spoke frames recycle their transfer buffers between the app and the radar worker, so
  steady-state streaming stops allocating a fresh multi-megabyte buffer many times a second.
- Nearby tide and current stations resolve through a latitude-sorted index, so only stations that
  can possibly be in range are measured.
- Profile lookups by id go through an index instead of scanning the list per call.
- The point-of-interest note shape is owned by the entities layer, so the notes overlay and the
  POI search share one canonical type.
- The notes overlay's fetch, cache, and retry state moved into a dedicated source module, with the
  map hit handlers and the highlight ring extracted alongside.
- The Signal K store's AIS target and notification maps are read-only outside the store, so
  nothing can bypass the write paths that keep its change counters honest.
- IndexedDB transaction lifetime is managed inside the storage slice through one shared helper.
- The waypoint editor and MOB confirm use native HTML dialog elements, so the browser provides
  scroll-lock, focus trapping, and modal semantics directly without custom scaffolding.
- The MOB confirm carries `role="alertdialog"` so assistive technology announces it as an
  emergency requiring immediate response, not a routine dialog.
- The visibility toggles (a layer, a route, a track) share one prop contract, so showing or hiding
  an item runs through the same code path everywhere.
- Duplicate CSS for the docked-panel slot, muted notes, and numeral styling were folded into their
  shared classes across several panels.
- Unused type and value exports were trimmed from several feature and shared module public APIs.

<a id="v0130"></a>

## [0.13.0] - 2026-07-01

The first published release since 0.10.6. It brings the whole offline-charts and Chart Locker system
built across the intervening development versions, and adds a Chart Locker status pill to the header.

### Added

- **Offline charts you download before you lose coverage.** With the Chart Locker plugin installed,
  draw a box on the chart and download every chart source that covers it into the boat-wide tile
  cache. The area is saved and pinned, so it keeps rendering at anchor and at sea. A storage estimate,
  gated against the cache budget, shows the size before the download starts.
- **A saved-areas list you manage.** Every downloaded area is listed with its cached size and last
  download date, with re-download and delete on each, and a progress bar tracks an area while it fills.
- **The base map and marks saved by default.** A downloaded area saves the chart that covers it, the
  navigation marks, and the vector base map with no setup, so an offline area is never a blank canvas.
  "Customize what's included" opens the full layer list when you want the specialist overlays.
- **Detail as plain presets.** Pick Overview, Coastal, or Harbor instead of raw zoom numbers, and the
  area downloads to the right level of detail for how you will use it.
- **Auto-cache around the boat.** An optional background fill keeps a small area cached around the
  vessel as it moves. Pick the charts it caches, and set its radius and refresh interval, so you stay
  covered even away from a saved area.
- **Local chart management.** A panel lists every local PMTiles chart the Chart Locker plugin has
  found and registered, each with its name and description, so your full set of offline charts is
  visible in one place.
- **One shared, boat-wide offline cache.** The remote chart overlays and the vector base map with its
  labels are fetched and cached through the Signal K server, so the whole boat shares one cache, the
  same tile is not refetched per device, and they keep working offline at sea.
- **The Offline charts panel.** All of the above lives under one plain-language "Offline charts" menu
  group: a landing with a single "Download an area" action, the saved-areas list, and rows into
  Storage and Auto-cache, in place of the old scroll of source checkboxes, zoom numbers, and cache
  internals.
- **Chart Locker status in the header.** With the plugin installed, the header's right controls show a
  Chart Locker pill beside the profile pill whose glyph reads its state at a glance: a check when it is
  online, a pulled plug when it is offline, and a warning triangle on a server fault. It opens the
  Offline charts panel on tap. It reads online whether or not you have the access to read the cache
  size, and shows the size in its hover tooltip when you do. The tooltip and the screen-reader label
  always name the state in words, and the pill is absent on a standalone install without Chart
  Locker.

### Fixed

- **The vector base map and its labels render again.** The base map tiles and glyphs are fetched in a
  web worker, which could not resolve a path-absolute `/plugins/...` URL and failed every vector tile
  and glyph request, leaving only the shaded-relief raster visible. Binnacle now hands MapLibre
  absolute URLs, so the vector base map, its labels, and a read-only server all work again.
- **Writes use the read-write token the moment access is approved.** Routes, waypoints, tracks, course
  changes, and alarm writes switch to the read-write token as soon as access is granted, instead of
  failing on the original read-only token until the page reloads.
- **Auto-cache settings are kept.** Turning on auto-cache around the boat, and its chart picks, radius,
  and interval, now persist and reload. The settings were saved all along, but the panel read them back
  in the wrong shape and reopened on its defaults, so the toggle looked like it never saved.
- **The man-overboard confirm dialog times out exactly once.** The 15 second self-dismiss countdown
  could fire more than once if the dialog lingered, so the alarm handoff now runs a single time.
- **Theme switch during a chart load.** Switching the day, dusk, or night theme no longer errors when a
  chart overlay layer is still loading; the paint update skips a layer that is not on the map yet.
- **A downloading area survives a token change.** If the access token rotates while an area download is
  in progress, the status poll picks up the new token on its next tick instead of running on the stale
  one.
- **The offline-area storage check no longer hangs.** The storage check bounds its request with a
  timeout, so an unreachable tile cache fails and reports rather than leaving the panel on "Checking
  storage".

### Changed

- **Companion plugin renamed to Chart Locker.** Binnacle points its tile cache, base map, chart, and
  area requests at the renamed `signalk-chart-locker` plugin, previously the Binnacle Companion plugin.
- **The man-overboard alert range follows your distance units.** The MOB alert's announced range now
  matches the on-screen Range readout and honors your distance-unit preference, instead of always
  announcing meters.
- **Dependencies and internals.** Refreshed @lucide/svelte, declared the Biome dev dependency the lint
  scripts rely on, dropped an unused dev dependency, tightened the US and EU chart bounds so a layer
  only appears where it has data, and removed dead export surface across the app.

<a id="v0122"></a>

## [0.12.2] - 2026-07-01

### Changed

- **The man-overboard alert range follows your distance units.** The MOB alert's announced range now
  matches the on-screen Range readout and honors your distance-unit preference, instead of always
  announcing meters.
- **The offline-area storage check no longer hangs.** The check now bounds its request with a timeout,
  so an unreachable tile cache fails and reports rather than leaving the panel on "Checking storage".
- **Offline charts, redesigned for a plain-language flow.** The old "Tile cache" panel, a single long
  scroll of chart-source checkboxes, zoom numbers, and cache internals, is now "Offline charts" under
  its own menu group. A landing offers one "Download an area" action, the list of saved areas, and
  rows to Storage and Auto-cache. The builder hides the chart layers behind "Customize what's
  included" and shows a plain summary instead, the base map is saved by default so an offline area is
  never a blank canvas, detail is an Overview, Coastal, or Harbor preset rather than raw zoom levels,
  and storage and the around-the-boat auto-cache each move to their own sub-view. Smart defaults save
  the chart that covers the area, the navigation marks, and the base map, leaving specialist layers
  off. The US and EU chart bounds were tightened so a layer only appears where it has data.

### Fixed

- **The man-overboard confirm dialog timed out cleanly.** The 15 second self-dismiss countdown could
  fire its timeout more than once if the dialog lingered, so the alarm handoff now runs exactly once.
- **A downloading region kept polling on the current credentials.** If the access token rotated while
  an offline-area download was in progress, the status poll now picks up the new token on its next
  tick instead of running on the stale one until it gave up.
- **Vector basemap and labels rendered blank.** The OpenFreeMap base map tiles and glyphs are
  fetched in a web worker, which cannot resolve a path-absolute `/plugins/...` URL and failed every
  vector tile and glyph request, leaving only the shaded-relief raster (which loads on the main
  thread) visible. The map now hands MapLibre absolute URLs for companion and Signal K requests, so
  the vector base map, labels, and a read-only server all work again.

<a id="v0121"></a>

## [0.12.1] - 2026-06-29

### Added

- **Regions panel.** Draw a box on the chart and download every covering raster source into the
  boat-wide tile cache before leaving internet coverage. The panel enumerates the raster chart sources
  that cover the box, shows a regions-free byte estimate gated against the cache budget so a download
  cannot overrun the space reserved for saved regions, and lets you edit a geocoded name for the region
  before saving it. Saved regions are listed with their cached size and last download date, each with
  re-download and delete, and a progress bar tracks a region while it warms. Requires the Chart Locker
  plugin.

### Changed

- **Companion plugin renamed to Chart Locker.** Binnacle now points its tile cache, basemap, chart,
  and region requests at the renamed `signalk-chart-locker` plugin, previously the Binnacle Companion
  plugin, and the prewarm panel and its client were renamed to the regions naming throughout.

### Fixed

- **Route creation from the chart.** "Start a route here" in the chart context menu now reliably
  drops the first point. The synthetic pointer press was missing the held-button flag the draw tool
  checks, so on some setups the first tap did nothing.
- **Writes after an access upgrade.** Routes, waypoints, tracks, course changes, and alarm writes now
  use the read-write token the moment access is approved, instead of failing with the original
  read-only token until the page reloads.
- **Man-overboard re-trigger.** A mark dropped right after cancelling another is no longer silenced
  by the cancelled mark's clear.
- **Measure tool.** A long-press or right-click during an active measurement no longer opens the
  chart context menu.
- **Stalled links.** Chart-management requests now time out instead of hanging when the server
  accepts the connection but never answers.
- **Collision and chart robustness.** A zero warning threshold now warns that it disables the warning
  alarm, and malformed chart bounds are dropped before they can reach the map.
- **Accessibility.** The icon picker keeps Tab focus within the open list, and a profile import
  failure announces as an alert.

<a id="v0120"></a>

## [0.12.0] - 2026-06-29

### Removed

- **AI route-draft and optimize controls.** The "Draft a route with AI" button and the "Optimize
  route" button are removed from the Routes panel. All manual routing is retained: drawing a
  new route, editing an existing one, importing and exporting GPX files, reversing a route,
  activating via the Course API, and stopping navigation all work exactly as before.

<a id="v0110"></a>

## [0.11.0] - 2026-06-28

### Added

- **Tile cache prewarm panel.** Draw a cruising box on the chart and fill the shared boat-wide tile
  cache before leaving internet coverage. A live byte estimate, gated against the cache capacity,
  shows how much storage the selected area and zoom range will use before the fill begins. The
  prewarmed box is pinned and never evicted; writes are bounded for microSD longevity. Requires the
  Binnacle Companion plugin.
- **Off-plan position-warm.** An optional, throttled background fill keeps a small tile radius warm
  around the vessel when it travels outside the prewarmed box, using an LRU-bounded eviction policy
  so the fill is always storage-bounded and never displaces the pinned prewarm.
- **Chart-management panel.** Lists every local `.pmtiles` archive the Binnacle Companion has
  discovered and registered, with a per-chart name and description, so the full set of offline charts
  is visible in one panel. Requires the Binnacle Companion plugin.
- **Shared offline tile cache for the remote charts and the basemap.** When the Binnacle Companion
  plugin is installed, the depth, boundary, protected-area, and seamark raster overlays, and the
  vector basemap with its glyphs and tiles, are fetched and cached through the Signal K server, so
  the whole boat shares one cache, they work offline at sea, and the same tile is not refetched per
  device. When the companion is absent, every source keeps its direct upstream URL, so a standalone
  install is unchanged. The NASA GIBS ocean fields still fetch directly for now. Companion detection
  is bounded by a short timeout so a wedged server cannot stall the map, and if the proxied basemap
  style fails while the device is online the direct style is used before the blank offline fallback.

### Changed

- **PMTiles charts served by the companion use the browser cache correctly.** When the Binnacle
  Companion provides a `.pmtiles` archive, Binnacle issues conditional requests against the strong
  ETag the companion sets, retiring the `no-store` workaround that had been applied to
  companion-provided chart paths. The archive is served with HTTP Range support so partial fetches
  work and previously cached ranges are reused.

<a id="v0106"></a>

## [0.10.6] - 2026-06-27

### Fixed

- **Course readouts stay live during navigation.** Cross-track error, velocity made good, distance and
  bearing to the waypoint, and ETA had frozen at the value read when the page first loaded. The course
  calculations stream one field at a time, and Binnacle had been listening for them as a single bundle
  that never arrives, so the numbers never refreshed. Each field is now followed as it streams, so the
  active-leg readouts update continuously while you navigate.
- **Anchor controls report a read-only token.** Dropping the anchor, raising it, or changing the swing
  radius on a read-only grant now raises the read-and-write request banner, the same as routes,
  waypoints, tracks, course, and alarms, instead of appearing to do nothing.
- **A tide forecast survives a current-station error.** When the station serving tidal currents fails,
  Binnacle keeps the tide forecast it already fetched rather than discarding both.
- **Weather readouts no longer show NaN.** The weather grid is guarded against an empty or single-point
  data axis that previously produced not-a-number readings at the cursor.
- **A mid-session data drop shows as a disconnect.** If the background Signal K connection crashes while
  running, the status now reads as a closed connection instead of going silently stale.

<a id="v0105"></a>

## [0.10.5] - 2026-06-27

### Added

- **Transmit and standby control.** Key the radar between transmit and standby from the radar panel,
  with a live status that reads transmitting, standby, warming up, or off.
- **Show echo on chart toggle** in the radar panel, synced with the Layers eye so the two never
  disagree. The echo also turns on automatically the first time a radar is detected, so a radar boat
  sees its picture without hunting for a layer switch.
- **Read and write access prompt.** When the server grants a read-only token, a banner offers a
  one-click request for read and write, so saving routes, waypoints, tracks, course, alarms, and radar
  controls works. The chart keeps updating from the existing read access while the new grant is approved.

### Fixed

- **The radar echo renders.** With the stream live the picture had been blank for three reasons, all
  fixed: the echo layer shipped off and nothing turned it on, the fallback color ramp only colored the
  lowest sample values so strong returns were transparent, and the spoke image was drawn transposed and
  swept counter-clockwise. The echo now draws in the correct orientation and sweeps clockwise from dead
  ahead, matching the radar's rotation.
- **The radar spoke stream reconnects** automatically after a drop (a provider restart, a power cycle, or
  a network blip), and the panel reconciles control values and the operational status from the radar's
  live state.
- **The spoke stream carries the auth token**, so a token-secured stream connects rather than failing
  silently, and the device token is never sent to a cross-origin provider.

### Changed

- Radar controls parse both the array and object-keyed capability shapes, so the gain, sea clutter,
  rain, mode, and power controls render whichever shape a provider serves.

<a id="v0104"></a>

## [0.10.4] - 2026-06-26

### Fixed

- **Radar controls render again.** A provider's capabilities are read as the object-keyed map the
  Signal K radar API actually returns, so the gain, sea clutter, rain, and mode controls appear. The
  panel had shown nothing because the response shape was misread. When a provider serves no
  capabilities, the controls the radar reports at discovery are shown instead.
- **Vector charts served as `{z}/{x}/{y}` tile templates load.** Such a chart was requested with the
  placeholders unfilled and returned a 404; the template is now used as a tile source so the z, x, and
  y coordinates are filled in per tile.
- **Older recorded tracks draw correctly.** A track point saved by an earlier build without a speed
  value is normalized on load, so the speed-colored track line and the track statistics both read a
  number rather than breaking on a missing value.

<a id="v0103"></a>

## [0.10.3] - 2026-06-25

### Added

- Radar gain, sea clutter, and rain clutter can be switched to Auto, handing the level to the radar;
  moving the slider returns the control to manual. Each control that reports an auto capability shows
  an Auto toggle on its row.

### Changed

- The Radar menu tile now stays visible and grays out, with a hover tooltip, when no radar is
  detected, rather than disappearing from the menu. This matches the radar layer row and the other
  detect-and-degrade overlays (track history and AIS trails), so a capability never silently vanishes,
  and the tooltip points to installing a Signal K radar provider plugin.

### Fixed

- Binnacle now asks the Signal K server for read and write access, so the access-request approval
  defaults to read/write. A read-only grant silently blocked saving routes, waypoints, and tracks,
  starting and clearing a course, acknowledging alarms, and adjusting radar controls.
- Signal K stream subscriptions now send only the policies the server supports (instant and fixed)
  and no longer attach a fixed-rate period to an instant subscription, so the server stops logging
  policy warnings for Binnacle's stream.
- Chart, note, and symbol resources served by Signal K providers are validated before they render: a
  chart needs a name and type, a note needs finite coordinates, and a symbol needs a finite scale and
  anchor. A malformed entry or an error response is skipped instead of becoming a broken layer or a
  stray marker.

<a id="v0102"></a>

## [0.10.2] - 2026-06-25

### Added

- A Radar menu tile that opens the radar controls, shown only when a radar is detected, so the radar
  is reachable from the menu and not only from its layer row in Layers and charts.
- **The radar is easier to read.** A sweep wedge marks where the radar is currently scanning, with a
  short afterglow trail, so the picture reads as actively sweeping. Faint or small returns are now
  drawn at a minimum on-screen size, so a sparse echo stays visible whether you are zoomed in or out.
- The radar range rings are labeled with their range in nautical miles, so the rings read as a
  distance scale.
- Region tags on the regional chart sources in the Layers panel, so it is clear at a glance which
  area each survey-quality source covers.

### Changed

- AIS targets now uses a ship icon, and the marine radar shows the radar sweep icon on its controls
  panel, so each reads true. AIS previously borrowed the radar glyph while the radar had none.
- The Layers and charts panel is redesigned: flat rows, the opacity slider moved into a per-row
  popover, and the sources grouped into clearer categories.
- The radar controls panel is rebuilt to match the other side panels: one consistent field layout for
  its sliders and selects, with the range offered as a list of standard nautical-mile steps.
- The range rings are drawn bolder so they stand out over the chart, and the side-panel section
  spacing is unified so every panel shares the same rhythm.

### Fixed

- Several reactivity correctness issues found in a codebase sweep.
- The "Go to here" point no longer fires twice after a base-map style change: the long-press handler
  is now detached on teardown so a re-install cannot stack a second one.
- A disabled danger button (for example a delete control) no longer changes color on hover.
- Accessibility and consistency fixes across the menu system, the radar controls back arrow is scoped
  to its own entry, the radar range labels no longer read brighter than their ring, and the POI search
  close button has a descriptive label.

<a id="v0101"></a>

## [0.10.1] - 2026-06-24

### Fixed

- **Marine radar now works against the Signal K v2 radar API.** The 0.10.0 radar read a
  provider-specific shape and never detected a radar on a real server. It is rewritten to consume the
  standard Signal K radar API at `/signalk/v2/api/vessels/self/radars`, with the controls and the
  protobuf spoke stream the spec defines, and the whole path (discovery, controls, and the live picture)
  is verified end to end against the mayara radar emulator. It still has not been tried on real radar
  hardware, so feedback through GitHub issues is welcome.

### Added

- **Read-write access note for radar controls.** The radar picture works with read-only access; adjusting
  the radar's controls needs read-write. The controls panel now says so when a change is refused, so you
  know to approve Binnacle for read and write on the Signal K server.

<a id="v0100"></a>

## [0.10.0] - 2026-06-23

### Added

- **Marine radar overlay.** Binnacle draws the live radar picture from a Signal K radar provider
  (mayara, with the older Radar SK as a fallback) on the chart: a polar sweep rendered with WebGL,
  range rings, and a heading line, in the "Traffic and live data" layers. It discovers the radar
  automatically and degrades cleanly on a server without one. Built to the Signal K radar spec and
  verified against a synthetic radar; it has not yet been tested on real radar hardware, so feedback
  through GitHub issues is welcome.
- **Radar controls.** A panel opened from the radar's row in the Layers panel exposes the radar's own
  controls (gain, sea clutter, rain clutter, range, transmit, and standby), built from whatever
  controls the radar reports.
- **Grayed-out unavailable layers.** A layer that needs a provider you do not have (the radar, AIS
  trails, or track history) now shows grayed out in the Layers panel with a hover note explaining what
  to install, instead of a switch that does nothing.

### Changed

- Updated Svelte, Vite, svelte-check, and Playwright to their latest releases.

<a id="v090"></a>

## [0.9.0] - 2026-06-22

### Added

- **Time travel.** Scrub the last 24 hours from a bottom strip: a marker walks the recorded track, a
  four-metric readout (depth, wind, barometer, and speed over ground) snaps to the scrubbed time, and
  the live vessel dims while you review. It reads the server history API and says plainly when no
  history provider is installed.
- **POI search.** A themed panel lists the points of interest in the current map view as a two-line
  list, sortable by name, type, distance, or bearing: each row leads with its category icon and name,
  then shows its distance and bearing below. Hovering or focusing a row rings that point on the chart
  without moving the map, and tapping a row rings it and opens its detail in the note popup, exactly as
  tapping the marker on the chart does, so you can click through results while the list stays open.
- **Configurable bottom bar.** Choose which actions ride the bottom bar. A "Customize bottom toolbar" mode
  in the menu pins or unpins any action (Center, Follow, Charts, Forecast, Tides, Anchor, and the rest);
  Center, Follow, and Charts are pinned by default. The choice is saved with your profile and follows
  you across devices. Pin more than fit and the extras collapse into a "More" button.

### Changed

- The AIS targets list and the POI search now share one row and sort style, so the two panels read
  and behave the same: a full-width sort control above a column of two-line rows.

### Removed

- The plotter-extensions integration is gone: Binnacle no longer surfaces third-party Signal K
  plugins' own buttons, panels, widgets, or iframes on the chart. Plugin data still renders through
  Binnacle's own overlays (points of interest and notes, routes, waypoints, charts, and the symbols
  from signalk-symbol-manager); this only drops the plugins' injected UI, which did not match the
  app's design and duplicated built-in features such as the POI search.

### Fixed

- On a secured Signal K server, the points-of-interest, AIS-trail, and history-track overlays now
  fetch with a live auth token. They captured the token at map load, before sign-in finished, so they
  stayed unauthenticated for the whole session and never loaded their data; the token is now read at
  fetch time, the way the rest of the app already reads it.
- Offline caching sweeps stale precache entries from earlier builds, so a returning visit no longer
  serves an outdated asset alongside the current one.
- The point-conditions valid-time line renders with its space restored before the middot ("Observed ·
  14:30" rather than "Observed· 14:30").
- A collision alarm now goes silent the instant the danger clears, instead of playing out the rest of
  the beep burst it was partway through. Scheduled beeps are stopped, not just the repeating interval.
- A failed file read while importing a route GPX or a profile JSON file now shows "Could not read that
  file." instead of doing nothing, so a flaky USB read is not mistaken for a cancel.
- A restored man-overboard mark is rebuilt from its known fields, so an unknown persisted field cannot
  carry into the live mark.
- The anchor controls use a freshly approved auth token at once: a token that arrives or changes
  mid-session is now read live, so an anchor action no longer needs a page reload to authenticate.
- A plugin enabled while the data stream was down (history, notifications, or anchor) is detected when
  the stream reconnects, instead of staying dark until the page is reloaded.
- The "Here" conditions panel keeps reading the latest sample as a live observation during a long open
  with no weather layer loaded, rather than drifting into treating it as a forecast step.
- A display token is corrected: the default waypoint marker and the disabled course-skip control hold
  their colors under the night-red theme.

### Internal improvements

- Accessibility and packaging: the active saved item (the active profile or route) is now announced to
  assistive technology with aria-current, matching its visual accent, and the browser tab and the
  Signal K admin webapp view now show the Binnacle favicon.
- Internal: a reuse pass routed the remaining AIS, anchor, collision, MOB, notes, route, tides, track,
  and weather overlays through the shared map source helpers, with no behavior change.
- Internal: a large modularization pass broke the biggest files into cohesive modules, components, and
  controllers without changing behavior. The App.svelte composition root, the WeatherMap and ChartCanvas
  widgets, the routes, layers, and weather panels, the notes overlay, and the pmtiles, themed-map,
  wind-gl, and route-draft modules were split; the global stylesheet was split
  one concern per module; and a whole-codebase reuse pass consolidated shared helpers (object guards,
  GeoJSON source updates, the icon-offset expression, the JSON-or-default fetch helper, and the
  provided-symbol overlay resolver). No user-facing behavior change.
- Internal: a whole-codebase consistency pass unified the four modal dialogs behind one shared frame,
  the two collision surfaces behind one severity-color definition, and the safety-band overlays behind
  one helper; named the remaining magic values; removed dead exports; tightened a few reactive and
  hot-path operations; and held the design system to its tokens. No user-facing behavior change.

<a id="v080"></a>

## [0.8.0] - 2026-06-20

### Added

- Plotter extensions. Binnacle now hosts third-party plotter add-ons that other Signal K plugins ship,
  implementing the Plotter Extensions API. An extension can place action buttons in the footer toolbar
  (capped at three, with a More menu for the rest), slide-in side panels, on-chart instrument widgets,
  and live display filters that hide non-matching markers and show a clearable chip. Widgets are placed
  with a long press on the chart, which opens the context menu's Add widget item, and packed into the
  chart corners. Try it with signalk-instrument-widgets and signalk-poi-search. With no extension
  plugin installed nothing renders and the chart is untouched.
- Custom waypoint icons. When you drop a waypoint you can choose its icon from Binnacle's built-in
  markers plus any symbols the signalk-symbol-manager plugin provides for the waypoint role, and each
  waypoint renders with its chosen symbol. Icons resolve through namespaces: `binnacle:` is Binnacle's
  built-in library (the default for a plain icon id), `custom:` is your own symbols from
  signalk-symbol-manager, and an icon another app stored in its own namespace still renders so nothing
  placed elsewhere disappears. The symbol rendering here was contributed by
  [Joel Kozikowski](https://github.com/joelkoz) in
  [#6](https://github.com/NearlCrews/signalk-binnacle/pull/6).

### Fixed

- Saved tracks stay on screen when a periodic refresh hits a transient network failure, instead of
  briefly blanking the list. This matches how routes and waypoints already behave.
- The severe-weather banner no longer ranks a thunderstorm watch, a storm-surge advisory, or a
  tropical-storm watch above a gale warning.
- Chart map instances are fully released on teardown, closing a pointer-listener leak that kept a
  map alive after the chart was destroyed.

### Changed

- Internal consolidation and performance work across the app: shared modal, button, and numeric
  readout styling unified into the global utilities, and several render and per-tick paths made
  cheaper (the map layer ordering, the resources filter regex, and the overlay draws). No change to
  behavior beyond the fixes above.

<a id="v071"></a>

## [0.7.1] - 2026-06-17

### Fixed

- The App Store listing no longer shows broken image placeholders in its README view. The README
  carried a screenshots section whose image paths were not part of the published package, so they
  could not load there. The screenshots shown in the App Store carousel were always present and are
  unaffected.

<a id="v070"></a>

## [0.7.0] - 2026-06-17

### Added

- AI route drafting. Describe a passage in plain language, for example "from here to Avalon, stay 3 nm
  off the coast", and signalk-crows-nest drafts a route you review and save. The draft opens as an
  editable working route with a not-chart-verified banner, the read-as destination, the model's note,
  a fuel estimate in your units, any land, shallow, hazard, or fuel flags above the leg table, and a
  note when the model marks the draft low confidence.
  Charted point hazards are grouped per leg, so a hazard-dense river or harbor passage reads as one
  count plus a short breakdown rather than dozens of lines. Each leg is checked against charted and
  modeled marine data that varies by region, and every flag states its source and datum; the absence
  of a flag is not proof of clear water. It cannot be minimized while a draft is up, and it saves only
  behind an armed "I checked every leg" confirm. The control appears only when signalk-crows-nest is
  installed at a version that ships the route-draft endpoint; on a stock server it stays hidden, with
  no error.
- Optimize a drawn route. While editing a route, tap Optimize and signalk-crows-nest returns an
  improved route, with safer and more detailed turning points and a more efficient track, opened in
  the same review-before-save draft panel with its per-leg flags. It is one tap, with an optional
  one-line constraint such as "stay 3 nm off", and it is non-destructive: Cancel restores your
  drawing, an unchanged result says so, and hand-editing the result accepts it as a normal route you
  can save or optimize again. Like drafting, it appears only when signalk-crows-nest is installed at a
  version that ships the route-draft endpoint.
- AIS course vectors. Each moving AIS target draws a short predictor line projecting its position
  about ten minutes ahead along its course at its speed, red for a danger contact and amber for a
  warning, so a crowded screen shows at a glance which targets are moving and which way. A
  stationary target shows no vector. (#3)
- Start a route from the chart. The right-click and long-press menu adds "Start a route here" under
  "Go to here": it opens the routes panel, begins a new editable route, and drops the first waypoint
  at the spot you picked, so you start a route from the chart in one step and then tap the rest.
- Route editing shows the route's waypoints as dots, the way a saved route looks, the moment you
  draft, optimize, or edit one, rather than only after you tap the line. Tap a leg in the panel's leg
  list and its segment and both end dots light up on the chart, with the chart easing to the leg when
  it is off-screen; tap a dot on the chart and the legs it joins light up in the list. Drag a dot to
  move it, tap a midpoint to insert one, or delete a waypoint, with the dots visible throughout.

### Changed

- The main menu now opens as a dropdown anchored under the menu button in the top corner and grows
  from that corner, instead of appearing as a panel centered on the screen. It is a compact
  three-column grid of labeled tiles grouped by section, dismisses on an outside tap or Escape, and on
  a phone-width screen docks to the bottom edge as a sheet. The main menu and the weather layer menu
  now share one anchored-dropdown primitive, so they open, position, and close the same way.
- Adding a chart by URL now reads its header and metadata through the same cached, retrying source
  the map tiles use, so re-adding the same chart, and its first render once added, hit the
  IndexedDB block cache instead of refetching over the network.
- Internal consolidation with no behavior change: the map overlay lifecycle, the rhumb-line
  geometry, the weather and tides display helpers, the GPX coordinate guards, the bounding-box and
  number-format helpers, the symbol registry, the map layer ordering, and the panel, icon-button, and
  numeric-readout styles now route through the existing shared primitives, design tokens, and utility
  classes.
- Hardening across the data and caching layers: the coordinate and Signal K delta guards reject
  non-finite and malformed values, the tide client drops bad readings and a no-data error response,
  the recorded-track in-memory fallback is bounded, the PMTiles and IndexedDB stores survive a
  concurrent write and another tab's upgrade, and the Escape-dismiss stack and arrow-key focus are
  made robust. The course readout uses a server-supplied ETA for a single-mark destination, and a
  route saved from a draft with no name falls back to a dated name. The layer manager now owns
  invalidating each overlay's change-detection cache on a base-style swap, so a cached overlay does
  not stay blank after a style reload, rather than each overlay carrying that duty itself. The AIS
  list keeps a non-finite range or CPA from scrambling its sort order.
- Further internal consolidation and small efficiency wins, no behavior change: more overlays and
  panels route through the shared feature-collection, layer-visibility, coordinate, and numeric
  guards and through the shared confirm-actions style and a new warning-tint token, so a caution
  control tints amber rather than red; the all-vessels context and the notification-path prefix have
  one home; and the weather-overlay registration, the route name reconcile, the collision severity
  map, and the AIS duration parse drop per-event allocations. The tidal current set is stored in SI
  radians like every other angle, converted to degrees only in the readout.
- Further internal consolidation, no behavior change: the resource and notification clients share one
  JSON-request helper; the map layer-ordering rank, the chart overlay's layer-id list, and the
  base-map theme pass are each computed once and reused; the AIS trails client reuses the shared
  bounding-box type; the working-route overlay builds each source its own empty collection; the notes
  overlay registers its marker icons concurrently; and the entity stores publish the remaining
  cross-feature types (collision source, measure leg, track stats, tides status, and anchor default
  radius) through their public index.

### Fixed

- The weather mini-map's tap readout no longer writes to a torn-down component if a provider answer
  arrives after the panel is closed.
- Offline and runtime caching works again in a secure context. The service worker's chart-tile,
  overlay, weather, and radar cache rules referenced module constants that did not survive into the
  generated worker, so the first matched fetch threw "CHART_TILE_PATH is not defined" and broke
  caching wherever the worker is active (HTTPS). The cache matchers are now self-contained.
- The route panel's Waypoints and Time readouts line up in their columns again. Their rows lacked the
  empty unit cell the three-column stat grid needs, so every value below shifted out of its column.
  The Time readout also splits its value and unit, so a minutes reading lines its "min" up in the
  unit column under the distance's "nm".
- Charted notes sit on their charted point again, and a console error on every map hover is gone. A
  provided symbol's pixel offset was read from an array feature property, which MapLibre coerces to a
  string; the offset now rides on the layer as a per-icon match.
- Drawing a route on the chart no longer disappears on the second tap, and the waypoint count
  reflects the points you place rather than the cursor. The chart editor mistook the draw library's
  cursor point, which carries the same tag as the route line, for the route itself, so the second tap
  read zero waypoints and cleared the route; it now selects the route by geometry, drops the trailing
  cursor point from the count and a save, and defers its working-line cleanup so the draw completes. (#1)
- "Go to here" now draws the course line from the vessel to the destination, and a destination
  marker, on the chart, not just the destination readout in the nav strip. The same line shows the
  current leg of a route under way. (#2)
- AIS collision alerts no longer cry wolf in a busy marina or at anchor. A moored or swinging boat
  (under one knot) is no longer flagged as a danger to a vessel that is itself anchored or near
  stationary, and the audible alarm is silenced while anchored, with the danger strip still visible
  and a genuinely close, imminent contact still sounding. (#4)
- A Signal K data worker that fails to load (a bundling or chunk error) now surfaces a connection
  error instead of leaving the app stuck on "connecting" with no signal.
- Weather precipitation, wave-height, and tide-station overlays redraw after the base map style
  reloads (the offline fallback) instead of staying blank until their underlying data next changes.
- A route or track that crosses the antimeridian (180 degrees) fits the chart the short way across
  the dateline instead of framing nearly the whole globe.

<a id="v062"></a>

## [0.6.2] - 2026-06-13

A full-codebase reliability, correctness, and coherence pass, plus a weather-panel layer menu. It
hardens the weather overlays, the caching and history layers, the course and anchor logic, makes a
set of previously silent failures visible, and moves the weather layer toggles into a floating menu.

### Changed

- The weather panel's layer toggles move from the header pill row, which ran out of room and
  truncated, into a single layers menu opened from a floating button at the upper left of the
  mini-map. The button lights and shows a count whenever layers are on, the menu groups the area
  fill and the overlays and carries the source line, and it docks as a bottom sheet on a narrow
  panel so it never covers the small map from the top. The one-tap Here conditions control stays
  in the header.

### Fixed

- Pressure isobars no longer stay blank after the base map style swaps (the offline fallback or a
  style reload): the overlay rebuilds its recreated sources instead of seeing an unchanged grid.
- The in-memory cache no longer evicts a just-refreshed weather grid or tide entry before older
  ones, so a refreshed view is not dropped early and refetched.
- A corrupted or legacy profile can no longer render a chart layer transparent or broken: a
  restored opacity is clamped to a valid range, matching first-registration.
- A malformed history timestamp no longer disables gap-splitting for the rest of a 24 hour track.
- The Trends panel can tell a present-but-empty history provider from an unreachable one.
- An active course's route, next, and arrival geometry survives a cross-station activation even
  when the continuously-updating calc values stream in before the one-time hydration completes.
- A dead data link (the worker failing to load, a rejected connect) shows a "Data link failed,
  reload" indicator instead of sitting forever on a connecting state.
- One chunk-load failure no longer kills route editing for the rest of the session.
- A failed track save or delete, a refused anchor drop on a server that advertises the standard
  Anchor API, a failed user chart registration, an empty-and-failed route fetch, and a chart that
  did not sync to the server all surface an error or a log breadcrumb now instead of going silent.
- The arrival alarm is stopped on teardown, profile sync retries after a transient first failure
  instead of staying local-only for the session, and a unit preset from a previous server is
  cleared when reconnecting to a different one.
- The layer opacity slider is a full-size touch target again, panel error lines use the shared
  alarm framing, and the chart action menu supports arrow-key navigation.

<a id="v061"></a>

## [0.6.1] - 2026-06-12

Quick access from community feedback: the chart actions a navigator reaches for stay within one
or two taps, and the weather panel's layer row scrolls honestly instead of clipping its last pill.

### Added

- Measure from the chart: the long-press and right-click menu gains "Measure from here", arming
  the measure tool with its first point at the pressed position, so measuring starts where you
  are looking instead of via the app menu. Re-arming mid-measurement deliberately starts fresh;
  extending an in-progress measurement is a plain chart tap.
- A Charts pill on the bottom status strip opens Layers and charts in one tap, beside Center,
  Follow, and Forecast, so switching charts no longer goes through the app menu.

### Fixed

- The weather panel's layer pills no longer render the last label clipped: the edge fade shows
  only while there is actually more to scroll, lifts at the end of the scroll, and the pills
  keep their natural width instead of compressing when the panel narrows, so the row genuinely
  scrolls.
- The chart context menu sizes itself to its longest label, so its edge-clamp math matches the
  rendered box.
- Voice control can activate the Charts pill by its visible word, its expanded state is not
  announced while it is still disabled during chart load, and its tooltip says the chart is
  loading while it is.

<a id="v060"></a>

## [0.6.0] - 2026-06-12

A reliability and correctness pass across the whole app: course following, the collision and anchor
watches, weather, charts, tides, and profiles, with the safety alarms now holding up in a
backgrounded browser tab. Plus: the app menu is a new tile launcher, every readout follows the
server's imperial-or-metric unit preference, and route editing loads on demand.

### Added

- Imperial and metric display units across the whole app, following the Signal K server's unit
  preferences (Server Config, Unit Preferences) with a per-profile local fallback on older
  servers. Depth, anchor distances and radius, MOB range, measured legs, tide heights and station
  range, temperatures, pressure, precipitation, wave heights, and visibility all convert; knots,
  nautical miles, bearings, and the hPa isobar convention stay nautical.
- The app menu is now a launcher: large icon tiles grouped Navigate, Conditions, Safety, and
  Settings over a dimming scrim, bottom-anchored on phones for one-handed reach, with Forecast
  now findable in the menu. Both alarm mutes moved into a new Alarms panel beside the collision
  thresholds.
- The measure layer supports opacity like every other overlay, starting Measure re-shows a hidden
  measure layer, the Tides panel cross-links its stations layer with a show-on-chart toggle, and
  layer opacity sliders have a floor so a checked safety layer can never be dimmed invisible.
- The Terra Draw route editor loads on first use instead of at startup, trimming the initial
  bundle by about 137 kB for faster cold loads on Pi-class displays.
- Standard waypoints: drop one from a long press on the chart, see them as named markers, and
  locate, go to, rename, or delete them from the new Waypoints panel. They live in the server's
  own waypoint resources, so they interoperate with Freeboard-SK and every other client.
- An Active alerts list in the Alarms panel: every notification on the boat (engine, NMEA2000,
  autopilot, or any plugin) surfaces with severity, time, and one-tap Silence and Acknowledge
  that propagate to every station on a 2.28 server.
- Collision and MOB alerts ride the server's v2 Notifications API when available (server-managed
  ids; muting locally silences the boat-wide alert), with the v1 delta publish kept for older
  servers. Server capabilities are detected once from the features endpoint.
- The anchor watch speaks the standard Anchor API the moment a server ships it (the proposal's
  drop, raise, radius, and reposition routes, feature-detected), ahead of the existing
  anchoralarm-plugin path and the client-local watch.
- Custom chart symbols from the signalk-symbol-manager plugin: a note whose icon reference
  resolves to a managed symbol renders that symbol (scale and anchor honored), and a provided
  "waypoint" symbol replaces the built-in waypoint marker. At night-red, user artwork is remapped
  into the red band so the theme's no-color rule holds. Without the plugin, every icon stays
  built-in.

- Worldwide tides through the signalk-tides plugin when the server runs it (NOAA, Neaps,
  WorldTides, or StormGlass per its configuration), with the NOAA CO-OPS path unchanged as the
  fallback; the Tides panel says which source served.
- AIS target trails from the tracks plugin: faded wakes behind moving targets, themed for all
  three themes, fetched only when the plugin is present and the layer is visible.
- Offline charts that actually work: PMTiles archives are cached as blocks in browser storage at
  the protocol layer, so previously viewed chart areas render offline in every context, including
  the plain-http default where no service worker can run (the old service-worker chart cache
  provably never stored anything: range responses cannot enter the Cache API). Plugin-served
  raster chart tiles, the seamark, bathymetry, boundary, and ice overlays, the base-map style,
  and CO-OPS predictions gain service-worker caching over https, with per-cache bounds and quota
  protection; opaque cross-origin responses are no longer cached (each one padded several MB of
  quota).
- Tide stations and predictions, chart notes, and the vessel conditions panel now persist in
  browser storage, so a reload with no signal replays the last data for the area, each item
  declaring its own age, over plain http as well as https.
- When the base map style itself is unreachable (plain http at sea with no internet), the map
  starts on a minimal water-colored fallback instead of staying blank, so cached charts and
  every overlay still load. The real base map returns on the next load with connectivity.
- A Trends panel: depth, apparent wind, barometric pressure, and speed over the last 24 hours
  as themed graphs, served by the server's v2 History API when a history provider runs
  (signalk-questdb, signalk-to-influxdb2, or signalk-parquet), with provider fallback when the
  default provider has no data. Without one, the graphs show the current session, sampled live.
- A "Track history (24 h)" chart layer: the vessel's server-recorded last day as a dashed line
  under the live track, gap-split across stops, opt-in from the Layers panel and only queried
  while shown.

### Removed

- The browser-local PMTiles file upload. Chart files belong on the server: install the
  signalk-pmtiles-plugin and drop .pmtiles files in its charts folder, and they appear in
  Binnacle on every device automatically. Adding a chart by URL is unchanged and still syncs to
  the server. Previously uploaded browser-local charts are dropped cleanly at upgrade.

### Changed

- Delta batching in the stream worker now runs on a timer instead of requestAnimationFrame, and
  AIS staleness pruning runs on a wall clock instead of the render loop, so live data keeps
  flowing and the collision and anchor alarms keep evaluating while the tab is hidden.
- An AIS target that stops reporting is now dropped after seven minutes, so anchored traffic with
  a slow AIS refresh no longer flickers in and out of the target list.
- Track recording no longer accumulates points while the boat sits at anchor: session gaps are
  detected from fix continuity, not motion.
- Cancelling MOB and raising the anchor are now two-tap confirms, and deleting a profile or a
  saved track asks first, matching the route delete.
- Escape handling is one shared topmost stack across the panels, the menu, and the measure tool,
  so Escape always closes the surface on top and never one underneath.
- Layer drag-to-reorder now stays within the layer's own category instead of crossing into the
  next section.
- Opening the Tides panel on a cold start is faster: the tide predictions and the current-station
  lookup now fetch concurrently instead of back to back.

### Fixed

- Editing a route no longer strips its waypoint names.
- The nav strip no longer shows the next waypoint's arrival time as the whole-route ETA.
- An active course now survives a page reload: the course state hydrates on first connect, and
  the route's Active badge tracks the server, including courses started or cleared from another
  station.
- Arrival no longer re-alarms from GPS jitter at the arrival circle; the alarm latches until the
  boat clearly leaves the circle.
- A failed waypoint skip and a partially failed GPX import are now reported instead of passing
  silently.
- At night-red the own vessel, the AIS targets, and the note icons are no longer hidden along
  with the base map's sprite icons.
- An acknowledged collision alert re-arms once the situation clears, and a contact's severity
  downgrade has hysteresis, so the alarm can neither stay silently dismissed nor flap between
  danger and warning.
- A target reporting speed without a course is no longer modeled as steaming due north, and
  contacts with provider-supplied CPA keep classifying during an own-fix dropout.
- The anchor watch announces a degraded state when GPS is lost, and an anchor-marker drag the
  system cancels no longer silently relocates the anchor.
- The MOB strip dashes out bearing and range on a stale fix instead of presenting frozen numbers
  as live.
- Wind particle colors now match the legend's absolute scale.
- Radar frames refetch on schedule: the cache no longer extends its own expiry on every read.
- A partial forecast no longer stretches stale wave pixels over a new viewport, and overlapping
  forecast loads can no longer finish out of order.
- The conditions panel's forecast section falls back to the free grid when a provider returns an
  empty series, and the weather panel's clock notes stay live during a long open.
- Deleting a user chart no longer leaves it in the persisted layer state, and renaming one
  updates its Layers row and its server resource.
- Tide times are correct when the browser's time zone differs from the station's (predictions
  are now requested in GMT), tide data refetches after midnight at anchor, and the on-chart tide
  label no longer shows past events.
- Tide fetches are skipped entirely while nothing displays them.
- Note markers recover after a failed or superseded fetch instead of freezing until reload.
- A transient network failure at startup no longer wipes the stored auth token, and a failed
  access request retries instead of hanging at "Requesting access".
- Profiles no longer show "unsaved changes" on every launch, deleting all profiles no longer
  resurrects the starter profiles, and a synced device no longer marks a profile active without
  applying it. A failed profile import shows an error.
- A refused alarm Silence or Acknowledge now shows an error in the Alarms panel instead of the
  alarm just continuing to sound, and a collision alert whose server notification was cleared
  from another station is re-raised instead of going silent.
- Cleared notifications no longer linger in the Active alerts list, and an unchanged
  notification broadcast no longer re-renders the panel.
- AIS wakes now clear after a few minutes of failed refreshes instead of freezing in place, and
  waypoint markers have their own color in each theme.
- The weather panel's layer pills stay on one scrollable row at every window width instead of
  wrapping into a second header row.
- A trend history load that resolves out of order can no longer overwrite a newer result, and a
  failed load shows its failure note instead of loading forever.
- A trend metric requested twice on one path with different aggregates now maps to its own
  column instead of mirroring the first.
- A tide reading replayed from the offline cache remeasures the station distance from the
  current position, so a reading cached a few kilometers away cannot misjudge the coverage
  radius or misstate the range.
- Muting the collision alarm from the danger strip now reports a refused boat-wide silence in
  the Alarms panel, matching the panel's own Silence and Acknowledge.
- Losing authorization mid-session no longer makes the collision notifier abandon its server
  notification id; the v1 delta fallback carries the change until the server accepts again.
- A unit preset changed on the server while the link was down is picked up on reconnect.
- The offline cache's third-party host matchers accept only the real weather and radar domains
  and their subdomains, not lookalike hostnames that merely end in the same letters.

<a id="v050"></a>

## [0.5.0] - 2026-06-11

A safety-focused redesign of the man-overboard confirm, a broad weather-panel upgrade (more
decision data, honest provenance, and accessibility), and a new app icon.

### Added

- **Gusts without a provider.** The free forecast grid now carries wind gusts, so the reefing
  number shows in the tap readout and the vessel conditions panel even with no weather provider
  configured.
- **Barometric tendency.** The conditions panel shows the trend a sailor decides by ("falling
  1.2 hPa/3 h"): the provider's own tendency when it reports one, otherwise computed from the
  trailing three hours of the forecast grid.
- **More conditions data.** Wave and swell direction (labeled "from"), visibility, and water
  temperature appear when the source carries them, and the current block is tagged Observed or
  Forecast with its valid time and zone.
- **Forecast provenance.** A footer states the source and fetch time, the stale note says how old
  the shown forecast is, and a grid missing its requested wave fields is qualified rather than
  passed off as complete.
- **Radar honesty.** The legend names the frame the loop is painting (for example "frame
  -40 min"), extrapolated nowcast frames are labeled as such, cached radar is flagged when
  offline, and the radar hides while the time slider is away from now instead of painting live
  rain over a three-day-out wind field.
- **Slider orientation.** A tick marks now on the forecast slider, the label carries Past or
  Forecast plus the time zone, and a one-shot note explains the zoom cap the first time you
  pinch into it.
- **New app icon**, aligned with the rest of the plugin family: a compass rose on a white
  compass-card badge over the shared ocean-wave mark.

### Changed

- **Man overboard confirm.** Pressing MOB now opens a centered dialog. The position is captured
  at the press, so the seconds spent confirming can no longer carry the mark away from the
  person; the confirm only gates the alarm. One full-width Mark man overboard button sits in the
  one-handed thumb zone with a quiet Cancel stacked above it, the dialog self-dismisses after 15
  seconds with a visible countdown (a re-press shortly after reuses the earlier press-time fix),
  and without a GPS fix the boat-wide alarm still raises, position-less, with a clear warning.
  The recovery strip adds the wall-clock Marked time for the log and the VHF relay.
- **Weather opens at now.** The time slider seeds to the forecast step nearest now instead of
  the start of the series, which begins up to a day in the past.
- **Conditions track the slider.** With a weather provider configured, the Here panel re-picks
  the forecast step as the slider moves, and it falls back to the free grid when the provider
  fails instead of freezing a one-shot sample.
- **Warnings.** Sorted most severe first, with the issuing source and the validity window, at a
  readable size; free mode now says warnings are unavailable instead of showing a silently empty
  list.
- **Legends.** Wind in whole 10-knot bands, cloud in whole percent, and weather readouts in
  whole knots.
- **Night-red.** The map attribution control follows the theme on both maps instead of rendering
  as a bright white bar.

### Fixed

- The tapped readout blends the two forecast steps exactly as the drawn fields do, so the number
  can no longer disagree with the picture under the finger by a full step.
- Weather provider detection re-runs when the auth token arrives or the stream reconnects; one
  failed probe no longer locks the whole session onto the free sources.
- The latest observation is picked by date rather than response order, and a provider's last
  forecast step no longer answers for a time days past its horizon.
- A rate-limited marine (waves) endpoint no longer blocks the healthy atmospheric fetch, so
  turning waves off recovers immediately.
- Provider precipitation is labeled as the accumulation it is (mm), not a rate.
- The course strip no longer covers the weather panel's slider and legend while a route is
  active; the strips lift to the panel's top edge instead.
- Accessibility: the forecast slider announces real times instead of epoch milliseconds, manual
  time changes are announced, the floating map notes are reliable live regions that stack
  instead of overlapping, scrubbing stops playback so the thumb is not yanked back mid-drag,
  Enter on the focused mini-map samples the center, and the tap readout can be pinned (hover or
  focus) and dismissed.

<a id="v040"></a>

## [0.4.0] - 2026-06-10

Four new at-sea features (an anchor watch, a man-overboard button, a measure tool, and an AIS
target list) plus shell refinements from helm feedback.

### Added

- **Anchor watch.** Drop the anchor at the boat, set the swing radius by hand or capture it from
  the live distance plus a margin, and get a drag alarm after three consecutive fixes outside the
  circle. The alarm latches until acknowledged, so a boat that swings back inside cannot silently
  clear an alarm you never saw, and the watch survives a reload. When the signalk-anchoralarm-plugin
  is installed, Binnacle drives it instead, so the alarm keeps running with the browser closed; the
  panel says which mode is watching. On the chart: the swing circle, a rode line, and a drop-point
  marker you can drag to where the hook actually lies.
- **Man overboard.** An always-visible MOB button centered in the top bar. One tap pops out a
  large confirm (so a stray tap can never raise the alarm, and the window self-dismisses); the
  confirm marks the spot, publishes the boat-wide `notifications.mob` alarm so every station sees
  it, flies the chart to the mark, and raises a recovery strip with live bearing, range, and
  elapsed time. Steering to the mark stays a deliberate second tap (Steer to MOB) through the
  course system, never automatic, since a coupled autopilot may follow the course. An MOB raised
  by another station shows here too, and the mark survives a reload.
- **Measure tool.** Arm it from the menu, tap points on the chart, and read each leg's range and
  bearing plus the running total, with the total labeled at the last point. Undo, Clear, Done, or
  Escape.
- **AIS target list.** Every tracked target as a tappable card with name or MMSI, live range and
  bearing, SOG, and CPA and TCPA when available, sortable by range, CPA, or name, with the
  lookout's severity coloring risky contacts and a tap flying the chart to the target.
- A depth readout in the anchor panel when a sounder publishes `environment.depth.belowTransducer`.

### Changed

- The footer's "Connected" text is now a compact status dot: green by day and dusk, a calm dim red
  in night-red (which forbids green), and the caution color while the stream is down. The label
  remains for screen readers and the hover title, and the dot stays on phones where the word used
  to be hidden.
- The trailing position cluster's numerals now take the same instrument-readout size as the
  leading AIS, SOG, and COG readouts, so the footer reads as one instrument row.
- The four feature alarms now share one edge-triggered core (`GatedAlarm`), with the collision
  alarm keeping its escalation-overrides-mute policy on top; tones are unchanged, and each alarm
  remains audibly distinct (MOB above the collision two-beep, anchor between collision and
  arrival).

### Fixed

- The MOB trigger's boat-wide notification never actually left the browser: the position object
  read from the live store is a reactive proxy, which cannot be structured-cloned into the stream
  worker, so the publish threw and was lost while the local strip looked fine. The mark is now
  snapshotted into a plain object, with a regression test, and the round trip is verified against
  a live server.

<a id="v031"></a>

## [0.3.1] - 2026-06-10

A pass over the existing features for safety, honesty under failure, performance on modest hardware,
and accessibility, plus a few navigation additions.

### Added

- Honest data-staleness signals. When the position feed stops, the footer shows a calm "No GPS fix"
  note and dashes SOG and COG instead of presenting a frozen speed and course as if they were live,
  and the collision watch and the course guidance stop computing against the stale fix. The connection
  badge turns to a caution color and reads "Reconnecting" or "Not connected" during an outage instead
  of staying "Connected".
- A cross-track deviation needle (a CDI) on the nav strip, so steering to track is a glance rather than
  a number read, with a caution color when it pegs at full scale.
- An on-screen arrival banner paired with the arrival tone, for a helm with the volume low.
- A footer "AIS" chip showing how many targets the collision watch is tracking, so an empty danger
  strip reads as all-clear rather than as a possible failure.
- A status note in the weather panel (loading, offline, or showing the last forecast) instead of a
  blank or silently outdated map.

### Changed

- The collision-alarm mute is now session-only and auto-expires after ten minutes, then re-arms; it is
  no longer persisted across a reload or carried by a profile, so a mute set in a crowded anchorage
  cannot silently follow you into the next passage. A close, imminent contact (inside about 0.1 nm and
  two minutes) overrides both mute and acknowledge, so a real emergency always sounds. Acknowledging a
  danger now keeps the strip on screen, dimmed, with its CPA and TCPA, while the target is still
  closing, instead of hiding it.
- Deleting a route now asks to confirm, with distinct wording when the delete will also stop active
  navigation. The nav strip disables waypoint-skip at the first and last points and keeps a gutter
  before Stop, so a mis-tap cannot end navigation.
- The footer SOG and COG step up to the full instrument-readout size, the numbers a helmsman glances
  at most.
- Performance on the Raspberry Pi: the chart's overlays no longer sync at full frame rate while idle
  (they update on real map repaints plus a low-frequency tick and pause when the tab is hidden), the
  weather fields and the wind particle field stop forcing continuous GPU work, and a long track
  simplifies incrementally rather than re-processing the whole track on every fix.
- Signal K conformance: TCPA accepts the spec's ISO-8601 duration form, the nav strip prefers the
  server's estimated time of arrival when a provider supplies it, the client-side course fallback uses
  consistent rhumb-line geometry, and a target that stops reporting is dropped after three minutes
  rather than six.
- Resilience: in-app requests time out instead of hanging on a half-open link, the local chart and
  track stores recover after a transient IndexedDB failure instead of dropping to memory for the
  session, the stream reconnects immediately when the network returns, and persisted charts and
  profiles are validated on load so a drifted entry is dropped rather than trusted.
- Accessibility: opening a panel moves focus into it, the Forecast and Here toggles use aria-expanded,
  in-place confirm and review steps move focus to their new control, a failed chart import is
  announced, and the app menu stays open when a mute toggle is flipped.

### Fixed

- The wind particle field no longer freezes after switching away from and back to the browser tab.

<a id="v030"></a>

## [0.3.0] - 2026-06-09

### Added

- Profiles: named bundles of your settings (theme, which layers are on, their opacity and order, the
  weather layers, the collision thresholds, the track and planning settings, and the alarm mutes) that
  you save, switch between, rename, delete, and set a default for. A switcher pill in the top bar shows
  the active profile and opens a Profiles panel; applying a profile updates the chart live, and tweaking
  a setting marks the profile as edited so you can save the change or discard it by switching away.
  Three starter profiles (Coastal day, Night passage, and At anchor) seed on first run. Profiles are
  stored locally, and when you are logged in to a secured SignalK server they also sync through the
  server's applicationData store so they follow you across devices. The sync degrades cleanly: an
  unsecured server, or a login that cannot use the applicationData store, keeps profiles local, and a
  login that can read the store but not write it stops after one rejected write rather than retrying on
  every edit, so it never floods the console. You can also export a profile to a JSON file and import
  profiles from one, to back them up or share them between boats.

- Course planning on the chart. Long-press (touch) or right-click (desktop) a point and choose "Go to
  here" to navigate straight to it via the Course API, with the destination shown on the nav strip.
- Route interchange via GPX. Export any saved route to a GPX file other plotters, MFDs, and
  Freeboard-SK read, and import routes from a GPX file back into Binnacle, closing the round trip.
- Passage planning in the route editor. A persisted plan speed turns the leg table into a passage
  plan, showing the cumulative time to reach each waypoint and a whole-route Time alongside the
  distance, plus a per-leg distance and bearing table that updates live as waypoints are dragged.
- Track-to-route workflows. Save the current track as a reusable route, reverse a saved route for the
  return leg, navigate home by retracing the current track, and skip the active route's waypoint
  forward or back from the nav strip. The nav strip also shows the whole-route distance and arrival
  time when a multi-leg route is active.
- A minimize control on the Routes panel. On a phone the panel is a bottom sheet that covers the chart,
  so a chevron in the header collapses it to just the header bar while it stays open, freeing the chart
  to tap waypoints into a route. The control only appears at phone widths.

### Changed

- The Layers panel now leads with "My routes and tracks" above "Traffic and live data". The panel
  order is kept aligned with the map stack so drag-to-reorder lands coherently, so this also raises
  the routes and track layers above AIS and the reference overlays on the chart; the own vessel and
  the collision rings stay pinned on top.
- The Tracks panel now renders saved tracks as the same elevated cards as the Routes panel, each
  showing the track's distance and duration, and the current-track stats line was tightened to a
  label, value, and unit grid that removes the trailing whitespace and aligns the values in a column.
- At night-red, the base map's pre-colored sprite icons (road and transit shields, aerodrome marks)
  are now hidden along with the POI dots, so the chart stays pure red on black with no stray blue,
  green, or white icons. The text labels stay visible.
- The route line, the note selection ring, and the AIS target triangles gained a dark casing or halo,
  so they keep their bright color but no longer sit low-contrast against the light day water. The
  casing is invisible on the dark dusk and night-red maps, where the bright shape reads on its own.

### Fixed

- Importing a GPX route no longer aborts on a malformed numeric character entity: a code point outside
  the Unicode range now stays literal instead of throwing an uncaught error that ended the whole import.
- The Routes opacity slider now dims the waypoint labels along with the route line and markers, the way
  the tides and notes overlays already did.
- The active-route strip's previous and next waypoint buttons are now a full 44px touch target, so they
  are usable underway.
- The precipitation legend now reads up to 40 mm/h, matching the range the precipitation field paints.

<a id="v021"></a>

## [0.2.1] - 2026-06-09

### Fixed

- Points of interest (Crow's Nest and ActiveCaptain) no longer flicker. A slow or rate-limited
  provider response made the markers vanish and reappear, because a failed fetch was treated as
  "no POIs" and cleared them. A failed fetch now keeps the markers on screen, and the overlay
  caches fetched sets by area, so panning back, panning a little, or zooming in reuses a recent
  fetch instead of re-hitting the network.
- Active marine warnings (gale, storm, and small-craft advisories) no longer flicker off the
  conditions panel when a weather-provider request transiently fails: the last warnings are kept
  until a real update replaces them.
- An active route's guidance (the nav strip, arrival circle, and auto-advance) no longer freezes on
  stale values after a stream reconnect. The v2 course data is re-hydrated on reconnect, since
  resubscribing cannot redeliver it under subscribe=none, and the route list is refreshed too.
- Server charts no longer blank on a transient failure at map load: the fetch now distinguishes a
  failed request from a reachable server with no charts, matching routes and points of interest.
- Imported-chart storage is reclaimed at startup: a PMTiles blob left behind by a failed save or a
  delete that ran while storage was degraded is now swept once its descriptor is gone, so orphaned
  blobs cannot accumulate on disk.
- The Points-of-interest layer no longer fetches from the provider or re-clusters while it is toggled
  off: a hidden layer now does no network or rendering work until it is shown again.
- Silenced a stream of "styleimagemissing" console warnings: the base map style references a few
  sprite icons and landuse patterns its published sprite does not contain, so a transparent
  placeholder is supplied for each. The map is unaffected; the console stays clean.

<a id="v020"></a>

## [0.2.0] - 2026-06-08

### Added

- A Tides panel (US waters), opened from the app menu, showing the nearest NOAA tide station's next
  high and low with heights in meters and feet, a 48-hour tide curve with a "now" marker, and the
  nearest tidal-current station's next flood or ebb with its rate and set. The nearest tide and
  current stations are also markable on the chart from the Layers panel. It degrades to a clear
  message outside US coverage, and the data is cached for the session.
- New built-in chart overlays, all free, key-free, and verified against the live services. Each
  starts hidden, toggles from the Layers panel, and carries its source attribution:
  - OpenSeaMap seamarks: a global overlay of navigation aids (buoys, beacons, lights, and harbors).
  - Marine protected areas: EMODnet protected areas with Natura 2000 nested under them (EU), and the
    NOAA MPA Inventory (US).
  - Maritime boundaries: the inter-country jurisdiction lines and the territorial sea (12 nm), so you
    can see when a passage crosses into another country's waters.
  - Ocean conditions: NASA GIBS sea-surface temperature and sea ice concentration (global, daily),
    which appear in a new Ocean conditions section of the Layers panel and default to translucent.
- A review step when importing a chart: after the file or URL is read, you can rename it and check
  its type, zoom range, and size before saving, instead of it saving immediately.
- URL-based imported charts now register on the Signal K server as a chart resource, so other
  devices on the same server discover them, and deleting the chart removes the server resource too.
  The chart stays manageable locally (a synced chart is shown once, not twice). File-based imports
  stay on the importing device, since a stock server cannot host the file bytes.

### Changed

- The Layers panel is reorganized into collapsible categories (Traffic and live data, Navigation
  aids, Areas and boundaries, My routes and tracks, Ocean conditions, and Charts and depth), each
  with a row count, so a long flat list reads as a few sections. The two most-used categories open by
  default and the rest collapse to cut clutter; each category remembers whether you left it open or
  closed. Per-layer toggles, opacity, the facet-group cards, and drag-to-reorder are unchanged.
- A better default chart order: the US NOAA ENC nautical chart leads, then US BlueTopo bathymetry,
  then EMODnet (Europe), then GEBCO (global), so the most detailed free coverage is on top. You can
  still drag any layer to reorder it.
- Cleaner, consistent layer names: sentence case throughout, plural for collection layers (Track is
  now Tracks), and a unified "source, type, region" format for the single-layer bathymetry overlays
  (for example "GEBCO bathymetry (global)"). The weather "Cloud cover" layer is now "Cloud" to match
  the other single-word weather layers.
- A denser, more consistent app menu and Layers panel: list rows use a compact row size while action
  buttons keep the larger touch target, the opacity sliders sit in shorter rows, and the gaps are
  snapped to one spacing scale, so more layers and menu items fit without scrolling.
- Three depth sources now present as labeled groups in the Layers panel, each with a base facet and a
  nested survey-quality facet: "NOAA ENC (US)" (Base chart, Data quality (ZOC)), "EMODnet (Europe)"
  (Bathymetry, Quality index), and "BlueTopo (US)" (Bathymetry, Uncertainty). The quality facets show
  how reliable each cell is: ZOC zones for the ENC, EMODnet's combined quality index, and BlueTopo's
  per-cell vertical uncertainty. Within a group the facet toggles are aligned in one column, a single
  drag handle and opacity slider serve the whole group, and the quality facet only enables while the
  base facet is on (turning the base off hides it). A generic sub-layer grouping mechanism backs this,
  so any future multi-facet chart can group its facets the same way.

### Fixed

- Deleting a user-imported chart now actually removes it. The layer row, the map overlay, and the
  stored descriptor were all left in place because the delete handler read the chart id after the
  panel had already cleared its selection, so the removal threw and never ran.
- A chart imported while a dark theme (dusk or night-red) is active now takes the theme immediately,
  instead of staying in day colors until the next theme change. Overlays registered after the first
  recolor are now themed at registration.
- The nearest tide and tidal-current readings no longer briefly go stale around local midnight: the
  session cache now rolls over on the same local day the NOAA forecast window uses, rather than on
  the UTC day.

<a id="v013"></a>

## [0.1.3] - 2026-06-08

### Added

- A fully keyboard-navigable app menu: arrow keys move between items, Home and End jump to the ends,
  and it follows the WAI-ARIA menu pattern with grouped sections (Navigation and Alarms), roving
  focus, and announced toggle states.
- Reduced-motion support: when the system prefers reduced motion, the chart's center-on-boat,
  fly-to, and fit-to-bounds camera moves jump instantly instead of animating.
- An opt-in NOAA ENC data-quality overlay. The NOAA ENC chart is now one clean chart layer plus a
  separate, default-hidden "data quality" layer carrying the Zones of Confidence (CATZOC) ratings,
  so the chart no longer always paints the data-quality triangles and overscale patterns on top.
- One-tap alarm muting from the danger strip, a muted-alarm badge in the top bar, and a spoken
  collision summary written to a live region for assistive technology.
- Motion and depth across the interface: the slide-over panels, the app menu, and the weather panel
  now reveal with a short reduced-motion-aware transition, the theme toggle animates its icon on each
  cycle, and the panels and menu carry a layered shadow. A more confident day palette and a larger
  instrument-readout type tier make the hero numbers (SOG, the nav metrics, the conditions) dominate
  their labels.

### Changed

- Bearings are now labeled true (123 degrees T) on the COG, BTW, and wind readouts, time-to-go shows
  hours and minutes past an hour (2h 05m) instead of a bare minute count, and the collision strip and
  its spoken summary are graded danger versus caution by the worst contact rather than always
  sounding full danger.
- The design system was consolidated: one shared lit-toggle, input, slider,
  and button-row vocabulary replaces the per-component copies, the danger and nav strips now stack
  instead of overlapping so course guidance survives a close-quarters contact, and the danger strip
  stays above the weather panel so Mute and Acknowledge are always reachable.

- The app menu is redesigned. Tracks, Routes, and Layers are now edge-docked slide-over panels
  promoted from inline accordions, each with a back-to-menu button so you can move between panels
  without reopening the menu, and the menu groups its items under section headers.
- Center, Follow, and Forecast now sit together in the bottom status strip as three matching labeled
  pill buttons, in that order. Follow and Forecast show a clear lit on-state, kept dim enough for
  night-red.
- A whole-codebase cleanup pass with no change to behavior beyond the fixes below: the Signal K
  frame pipeline hands the per-frame value map straight to the store instead of rebuilding it each
  frame, the active-route readouts compute each leg's geometry once per change rather than several
  times per render, the Layers drag measures row positions once at drag start instead of on every
  pointer move, and duplicated formatting, geometry, WMS, and map-image helpers were consolidated.

### Fixed

- The bottom status strip no longer overlaps or wraps unevenly on a phone. It stacks into a clean
  layout: the live readouts above, and the Center, Follow, and Forecast controls on one row below.
- The Tracks panel's statistics now align in a single value column.
- The collision danger strip no longer double-announces to screen readers. The app keeps a single
  concise spoken summary of the danger, and the on-screen contact list is now a silent visual
  landmark, so assistive technology reads the danger once instead of twice.
- The animated wind field now honors the system reduced-motion preference, falling back to the static
  wind arrows instead of running a continuous particle animation.
- The active-route strip no longer re-reads its whole readout line to a screen reader every second;
  only the destination name announces, when a waypoint advances.
- On a phone the note detail and a leading panel no longer overlap as stacked bottom sheets (they are
  mutually exclusive at narrow widths), the brand drops its version string so the top-bar controls
  keep room, and the weather "Here" conditions open as a full-width sheet rather than covering the
  small map.
- Form inputs theme their placeholder text, the day caution color is darker for contrast and is
  clearly distinct from the alarm red, and the Forecast control exposes its dialog to assistive tech.

<a id="v012"></a>

## [0.1.2] - 2026-06-05

### Fixed

- Importing a chart now flies the map to the chart's bounds, so a PMTiles archive (by file or URL)
  is immediately visible instead of staying off-screen when it covers a different area than the
  current view. Previously the chart was added to the Layers panel but the map did not move, so it
  looked like nothing happened. (A new fitBounds map command, fired only on a user import, never on
  the charts restored at startup.)

### Changed

- The layer-toggle checkbox no longer shrinks when a layer name is long: it stays square (the name
  ellipsizes instead).

<a id="v011"></a>

## [0.1.1] - 2026-06-05

### Changed

- App Store polish, reviewed against the Signal K AppStore publishing doc. The appIcon is now
  256x256 (the previous 72x72 was below the documented 128x128 minimum). The README is scannable,
  since the server's Webapps view renders it: the screenshots gallery that showed as raw HTML there
  is removed (the screenshots stay in `signalk.screenshots` for the App Store detail page), and the
  duplicate feature inventory is collapsed into one concise list. The title is now "WebGL chart
  plotter for Signal K" across the README, the PWA manifest, and the repository description.

<a id="v010"></a>

## [0.1.0] - 2026-06-05

### Added

- Routes: plan a passage and follow it. Open Routes from the menu, draw a route on the chart (tap to
  add waypoints, drag a point to move it, tap a midpoint to insert one), and watch the leg count and
  total distance update live as you draw. Save it to the Signal K server (`/resources/routes` as a
  GeoJSON LineString), and it syncs to every device and lists with show or hide, edit, activate, and
  delete. Activating a route hands it to the Signal K v2 Course API, and a nav strip shows the active
  waypoint, cross-track error with a steer-left or steer-right side, distance and bearing to the
  waypoint, velocity made good, and time to go, with an arrival alarm at the arrival circle. The
  guidance prefers the server's course calculations and computes them on the client when the
  course-provider plugin is absent (a "computing locally" badge says when), the same graceful
  degrade as the collision CPA. A failed save keeps the route under edit so nothing is lost, and the
  on-chart editing line is themed for day, dusk, and night-red. Route editing uses Terra Draw.

- The weather forecast is cached in IndexedDB, so it survives a reload and a return to a recent view
  reuses it instead of re-fetching. Unlike the service-worker cache, which browsers expose only in a
  secure context, IndexedDB works over plain HTTP, so this is the offline-leaning weather cache for
  the many users without SSL. Each grid is stored with a one-hour expiry, expiries are kept apart from
  the grids so pruning never loads them, and the store degrades to memory and never throws when
  IndexedDB is unavailable. Verified over https: after a reload, opening the forecast served the grid
  from IndexedDB with zero Open-Meteo requests.

- Weather. A dedicated weather mini-map, opened by the Forecast button centered in the status strip,
  keeps the navigation chart clean and the weather within its data resolution. The mini-map is capped
  at zoom 7 (RainViewer's real radar resolution) and panned independently of the chart, so weather can
  never be zoomed past what the data supports: no "zoom not supported" tiles, no pretending a coarse
  grid has street-level detail. In the panel you toggle Wind, Pressure, Waves, Precipitation, Cloud
  cover, or Rain radar, scrub the coming days with a time slider, read a per-layer color-ramp legend,
  and tap anywhere for the wind, pressure, sea state, and rain at that point and time. Wind draws as
  speed-colored arrows, mean-sea-level pressure as labeled isobar contours (marching squares, 4 hPa),
  significant wave height, precipitation, and cloud cover as smooth color fields, and precipitation
  radar as an animated RainViewer loop. The four area fills (waves, precipitation, cloud, and radar)
  are mutually exclusive, one at a time, so they never stack into mud; wind arrows and pressure
  isobars stay freely combinable on top. A "Here" panel shows the current conditions, a short
  forecast, and any gale or storm warnings for the vessel's own position.
- Weather data prefers a configured Signal K weather provider (for example AccuWeather) for point
  data: the tap readout and the "Here" conditions and warnings come from the provider when one is
  set, and fall back automatically to the free, browser-only sources when none is configured. Area
  data is always free, because no provider exposes gridded fields through Signal K: the atmospheric
  grid and the marine wave field come from Open-Meteo, and radar from RainViewer, with no key and no
  server plugin. Results are cached by viewport in memory so panning reuses a recent fetch, and the
  Open-Meteo responses, the RainViewer frame index, and the radar tiles are cached by the service
  worker for offline use. Layers beyond wind and waves are off on first open, themed for day, dusk,
  and night-red (a deep, low-brightness red on black at night, no blue; the radar raster is
  desaturated and dimmed).
- Wind draws as an animated WebGL particle field, the glanceable signature layer: thousands of
  particles stream through the forecast wind with fading trails, colored by speed (the day ramp, and
  a pure red-on-black ramp at night). It is a custom MapLibre layer running a GPU particle simulation
  over the forecast u/v, projected so pan and zoom only reproject the particles and the trails reset
  cleanly on a move. It falls back to the speed-colored arrow layer when WebGL is unavailable, and
  the animation runs only while the Wind layer is on.

- Approving Binnacle's Signal K access is now self-explanatory and recognizable. The request uses a
  named client id (`binnacle-<short>`) instead of a bare UUID, so it is easy to spot in the Signal K
  access-requests list, and the "Requesting access" banner shows that id plus a one-click "Approve in
  Signal K" shortcut that opens the admin access-requests page. A legacy bare-UUID client id is
  upgraded to the named form on load, keeping any existing token.

- Follow boat: a "Follow boat" item in the menu locks the chart to the vessel, recentering on each
  new position fix at your current zoom. It centers immediately when turned on, and a manual pan of
  the chart releases the lock (a scroll-zoom keeps it). It is off by default and does not persist
  across reloads. The one-shot "Center on boat" remains for a quick recenter that also zooms in when
  you are zoomed far out.

- A Layers panel and drag-to-reorder for every layer. The old inline "Layers" submenu is now a
  "Layers and charts" launcher that opens a left-docked slide-over listing every layer top of the map
  first, grouped into "Charts and Depth" and "Overlays" with the own vessel and active alarms pinned on
  top. Each row toggles, sets opacity, and drags (by pointer or keyboard) to restack the z-order, and
  the order persists across visits. The panel docks opposite the note detail so both can be open at
  once, and it themes for day, dusk, and night-red.

- Streaming depth charts. Four free hosted bathymetry and chart sources toggle on from the Charts
  and Depth section, off by default and cached as you pan: GEBCO global bathymetry, EMODnet (Europe), the
  NOAA ENC chart (US), and NOAA BlueTopo (US). They are reference overlays, not certified for
  navigation. A raster source cannot be recolored, so at night it is desaturated and dimmed (no blue,
  low brightness) rather than left full-color.

- Import your own charts. Add a PMTiles archive by URL or by dropping a file into a themed drop zone;
  Binnacle reads its name, bounds, zoom, and whether it is vector or raster, lists it under Charts
  and Depth as a normal reorderable layer, and stores an uploaded file in the browser for offline use. A
  per-chart detail view renames it, shows its metadata, and deletes it (stating the storage freed).
  Both vector and raster PMTiles render; full S-52 styling of converted ENC depth features is a later
  spec.

- Note detail panel: tapping a point of interest now opens a slide-in side panel with native,
  structured detail instead of a plain-text popup that bounced you to an external viewer. Binnacle
  consumes Crow's Nest's presentation-neutral `properties.crowsNest` sections from
  `/resources/notes/{id}`, rendering each item by kind (measures with units, availability badges,
  rating stars, flag toggles, links, and notes), and falls back cleanly to the plain-text
  description for any other notes provider or schema version. The marker icon now uses the
  explicit POI type when present, the structured values render as text with scheme-checked links
  (no HTML injection), and the panel is themed for day, dusk, and night-red and becomes a bottom
  sheet on a narrow screen.

- Tracks: Binnacle now records and shows where you have been. The active track is drawn behind the
  boat as you move, colored by speed (dark for slow, bright for fast) or a single solid color, and a
  break in the line marks a GPS dropout or a gap between sessions. The whole voyage is kept in the
  browser (IndexedDB) and reappears after a refresh. A "Tracks" submenu in the menu pauses and
  resumes recording, shows live voyage stats (distance, duration, and average and maximum speed),
  saves the current track to the Signal K server (`/resources/tracks` as GeoJSON), clears it, and
  toggles the color mode. Saved tracks list with show or hide on the chart, delete, and a GeoJSON
  export you can download. Track speeds are stored in SI (m/s) and converted to knots only at the
  display edge; the track layer is a normal layer, so it toggles and fades from the Layers submenu.

- Lookout collision thresholds are now editable (differentiator step 6). A "Collision thresholds"
  submenu in the menu sets the danger and warning CPA (nautical miles) and TCPA (minutes); changes
  apply live to the assessment and persist across visits, with a reset to defaults. Values are stored
  in SI and edited at the display edge in nm and minutes.

- Lookout publishes its collision alert to Signal K (differentiator step 5). When the assessment
  crosses a threshold, Binnacle writes `notifications.navigation.collision` over the streaming API
  (state alarm for danger, warn for warning, with the appropriate method) so other Signal K clients
  and devices share the alarm; it clears to normal when the risk passes. It is published only when
  the state or worst contact changes, not on every per-second tick.

- Lookout now sounds an audible collision alarm (differentiator step 4). When an AIS contact
  crosses the danger CPA/TCPA threshold, a repeating two-beep tone plays, synthesized with the Web
  Audio API so nothing is downloaded. Acknowledging the contact on the danger strip silences it, and
  a new or more severe contact re-arms it; a "Mute alarm" toggle in the menu turns sound off entirely
  and persists. The audio primes on your first interaction with the page (browsers block sound until
  a gesture). Warnings stay visual only.

- An app menu in the top bar gives app-wide options a home. It stays a single button until
  opened, then drops a themed popout, and closes on selection, Escape, or a click outside; the
  trigger is a labeled disclosure (aria-haspopup, aria-expanded, aria-controls). The menu is generic:
  it renders whatever action items it is given plus optional collapsible submenus, so adding an
  option is one `MenuItem` (or one `MenuSubmenu`) in the app shell, never a change to the menu
  itself. It hosts a "Center on boat" action that flies the map to the vessel and a "Layers"
  submenu.

- Binnacle now remembers your session across a page refresh. The map reopens at the last center and
  zoom, and each layer's visibility and opacity are restored, alongside the theme that was already
  persisted. The view is saved to local storage after panning settles (one write per gesture, not
  per frame), layer changes are saved as they happen, and a corrupt or out-of-range saved view is
  ignored in favor of the default world view.

- Points-of-interest markers now use per-category icons and a rich detail popup. Each note is sorted
  into a category (anchorage, marina, fuel, services, inlet, boat ramp, bridge, hazard, navaid,
  structure, or a generic point of interest), matched from the provider's skIcon against the live
  Crow's Nest / ActiveCaptain vocabulary with a keyword fallback for unfamiliar variants, so
  navigation lights and channel buoys read as navaids, creek inlets as inlets, and boat ramps and
  bridges as themselves instead of plain pins. Each category draws as a themed disc with a glyph:
  Lucide glyphs (anchor, sailboat, fuel pump, wrench, waves, landmark, triangle-alert, map-pin) per
  the spec's chosen app icon family, plus custom slipway and bridge marks. Hazards take the alarm hue,
  navaids the caution hue, the rest the POI hue; all recolor with the theme (night-red stays in the
  red band).
  Clicking a marker opens a themed popup with the name, category, any description and source
  attribution, and an http(s)-only link to the provider's detail page, and the selected marker gets a
  highlight ring.

- Navaids now render type-specific symbols instead of one generic marker. The note name is parsed
  into a kind (lighthouse, light, buoy, daybeacon, or generic) and, for buoys and daybeacons, a
  lateral side from the aid's number using the US IALA-B convention (even = red, starboard hand;
  odd = green, port hand). Lights draw as a magenta flare, lighthouses as a lantern-topped tower,
  starboard marks as a red cone or triangle, port marks as a green cylinder or square, so a channel
  reads at a glance. The side is carried by shape as well as color, so it survives night-red (where
  red and green collapse to two red shades). This infers symbols from the note text; full S-52
  symbology keyed off S-57 ENC attributes (shape, color, category) remains the later vector-chart
  spec, since notes carry no such attributes.

- Points-of-interest markers cluster at lower zoom and split apart as you zoom in, so a busy harbor
  shows a single counted disc instead of a stack of overlapping markers; clicking a cluster zooms to
  expand it. Marker size scales gently with zoom.

- Points-of-interest overlay: Binnacle now renders Signal K `notes` resources on the map, so POI
  providers like signalk-crows-nest (Active Captain, OpenSeaMap, NOAA, USCG light list) show up.
  The overlay fetches notes scoped to the current viewport (`?bbox=...`, no `provider` so every
  notes provider merges, which is how Freeboard-SK retrieves them), refetched as the map moves and
  gated below zoom 9. POIs draw as themed dots with names at zoom 12 and up, and toggle from the
  layers panel. Earlier nothing showed because Binnacle had no consumer for `notes` resources; the
  data was being served correctly all along.

- Lookout collision chart highlight (differentiator step 3): dangerous AIS contacts now get a graded
  ring on the chart in the safety z-band, danger and warning colored from the theme (day, dusk, and
  night-red, which keeps both in the red band with danger brighter). The overlay is dirty-checked
  against the assessment so it only rebuilds when a contact's id, severity, or position changes, is
  theme-aware through the layer manager's applyTheme broadcast, and toggles from the layers panel.

- Lookout danger strip: collision danger now surfaces on screen. A strip floats at the bottom of
  the chart listing the most dangerous AIS contacts with their closest point of approach in nautical
  miles and time to closest approach in minutes, color-graded by severity, with an acknowledge
  control and a "computing locally" note when the values are the client-side fallback rather than a
  Signal K provider. The strip is absent when nothing is dangerous, so a calm night watch stays dark,
  and it updates as traffic moves. This is the first on-screen slice of the active-safety Lookout
  feature; the chart highlight, audible alarm, notifications, and thresholds panel follow.
- Offline and PWA caching: Binnacle is now an installable progressive web app. A service worker
  precaches the app shell, runtime-caches the OpenFreeMap base map and the Signal K PMTiles charts
  cache-first (range-request aware) as they are viewed, and never caches the live Signal K stream or
  REST API, so anywhere the navigator has looked renders offline while live data stays fresh. The
  top bar offers an update when a new build is published, and the status strip shows an offline
  indicator. Service workers require a secure context, so this activates when the Signal K server is
  served over HTTPS; over plain HTTP the app degrades cleanly to online-only with no errors.
- The status strip shows the map's center latitude and longitude and the zoom level, updating as
  the chart is panned and zoomed, formatted at the display edge with hemisphere suffixes.
- Lookout (active-safety, first slice): the headless collision data layer behind the upcoming
  danger strip. A pure, test-first closest-point-of-approach module computes CPA and TCPA from the
  own vessel and a target's position and velocity, a persisted-settings helper holds
  user-configurable danger and warning thresholds with sensible defaults, and a collision
  assessment ranks AIS contacts by severity, preferring the server's `navigation.closestApproach`
  when a provider supplies it and falling back to the computed values otherwise. The danger strip,
  chart highlight, audible alarm, and Signal K notification publishing follow in later slices.
- Theming: a design-token system with day, dusk, and night-red palettes, switched by a single
  theme controller that sets `data-theme` on the document and persists the choice. Every surface
  recolors from CSS custom properties, a top-bar toggle cycles the themes, and the map base
  recolors via `setPaintProperty` (keeping tiles and overlays). Night-red is pure red on true
  black with no blue, and a dedicated alarm token stays distinguishable in every palette.
- Identity: self-hosted Inter (UI) and JetBrains Mono (tabular numeric readouts) typography
  bundled for offline use, Lucide icons for the theme toggle and the layers panel, the own-ship
  and AIS symbols recolored per theme so the chart shows no blue on the night-red theme (the own
  ship turns red and AIS a night-safe amber), and the build version shown in the top bar.
- AIS targets: the worker learns the self vessel from the `hello` handshake and routes other
  vessels' deltas into a per-context AIS stream, the store accumulates each target and prunes
  ones that go silent past a six-minute window, an `AisTargets` entity interprets each target
  into display units, and an AIS overlay renders them as GPU symbols in the traffic band that
  rotate with course and skip rebuilding when nothing changed. The app subscribes `vessels.*` at
  a controlled rate, and CPA and TCPA are read from `navigation.closestApproach` when a provider
  supplies them.
- Charts: a generic chart-source adapter turns any Signal K chart resource into MapLibre source
  and layer specs, branching on the chart type (raster tilelayer, WMS, WMTS, and S-57, plus
  vector tileJSON with PMTiles resolved to the `pmtiles://` protocol) and honoring bounds and
  zoom limits. Each chart wraps as a basemap-band overlay on the existing layer manager, the
  charts client discovers them from `/resources/charts` (v2, falling back to v1, degrading to an
  empty list offline), and a layers panel gives each chart a visibility toggle and an opacity
  slider.
- Verify-before-push git hooks (`.githooks/`, installed via `npm run hooks`): a fast format,
  lint, and boundary check before each commit, and the full type-check, test, and build gate
  before each push, so a broken tree cannot be committed or pushed.

### Changed

- A final whole-codebase cleanup before the 0.1.0 release, no feature change. The
  panel, button, icon, label, and instrument-strip styling moved into shared `app.css` utilities
  (`.icon-btn` with accent and danger modifiers, `.btn-ghost`, `.btn-pill`, a shared bottom-strip
  metrics row, a `.caps-label` for the uppercase section labels, and a 4px-based `--space-*` spacing
  scale for the common padding, gap, and margin values): the Routes panel now renders the same
  slide-over shell as the Layers and note panels instead of having the app shell hand-roll its dock
  chrome, every panel header reads the shared `.panel-title`, and the row-action icon buttons and
  ghost buttons stop being re-declared per component. The Signal K
  resource clients (routes, charts, tracks, and course) now share one `fetchKeyedResource` plus
  `putResource` and `deleteResource` instead of three copies of the v2-then-v1 fetch and five copies
  of the PUT and DELETE wrapper; the three IndexedDB stores share one `openIdbDatabase` opener and one
  `degradeToMemory` policy; the weather grid blends through the shared `lerp`; user-chart ids and the
  save-name prompt use shared helpers; the store iterates own keys with `Object.entries`; the
  longitude-delta normalize is total over any input; and the unused `routeLegs` was removed.

- The Signal K webapp manifest is complete for the App Store and the 0.1.0 release: five screenshots
  (the chart with AIS, route planning, charts and depth, an anchorage point-of-interest detail, and
  the weather mini-map) and a "Works well with" list (Crow's Nest for the points of interest Binnacle
  renders, signalk-ssl for the HTTPS its offline cache needs, and signalk-virtual-weather-sensors as a
  weather provider it reads). A cross-platform webapp CI builds, tests, and packs on Linux, macOS, and
  Windows on Node 22 and 24, and a release publishes to npm with a provenance attestation.

- A UI consistency pass covering design tokens, layout, typography,
  accessibility, and marine HMI conventions brought the whole interface to one standard. New tokens defined for all three themes (a large
  radius, a shared hover and press timing, a caution-tier warning color, and an alarm tint) replace
  the values components used to hand-code. Panel titles are consistent headings, the numeric readouts
  share the mono instrument font, the bottom-strip titles match the panel title scale, and the
  night-red border is deepened so panels stay separated where the shadow is dropped. The on-chart
  route editing color now reads the one map-theme source instead of a duplicated table. The four
  slide-over and overlay panels share one dismiss behavior (Escape closes the topmost, and focus
  returns to the control that opened it).

- Routing cleanup pass covering geodesy and the route domain, course guidance and the resource
  clients, the overlay, editor, and chart wiring, and the routing UI and app wiring, no feature
  change. The Earth-radius constant and the antimeridian longitude-delta normalize are now
  shared by the rhumb-line geometry and the collision CPA projection instead of duplicated, a
  `steerSide` helper centralizes the port-versus-starboard cross-track convention, a `clientId` helper
  folds the two copies of the secure-context id fallback, and the route distance no longer allocates a
  leg array just to sum it. Stopping an active course now clears every course cell, where before it
  left the previous point, the active route, and the arrival circle stale, and the seeding and
  clearing of those cells moved onto the course entity that owns them. Dead route-editor methods and a
  redundant overlay visibility flag were removed. Tests went from 412 to 415.

- The weather mini-map opens centered on the navigation chart's current view, so the forecast is for
  the area you are looking at, rather than reopening at its own last position. The zoom is still capped
  to the mini-map's maximum so weather never zooms past its data resolution.

- Second whole-repo cleanup pass covering weather, the Signal K data layer, map and charts, notes
  and tracks, safety and chrome, and app and build infrastructure, no feature change.
  The wind particle field caches its GPU uniform and attribute locations once at setup instead of
  re-querying them every frame, the layer manager applies the stacking order once per batch when the
  chart and overlays first load rather than restacking after each of a dozen-plus registrations, and
  shared helpers fold repeated logic: a `DEG_TO_RAD` constant for hot numeric loops, an `HOUR_MS`
  constant, an `applyRasterTheme` for the night-red raster treatment shared by the chart, depth, and
  radar layers, an `asKeyedObject` guard shared by the chart, note, track, and weather resource
  clients, a `toLonLat` mapper for the track coordinate builders, and one `RAIN_VISIBLE_MM_H`
  threshold. Dead Signal K path constants were removed.

- A new app build now surfaces an Update control instead of silently reloading. The progressive web
  app uses prompt registration rather than auto-update, so a fresh build never reloads the chart out
  from under you mid-passage; the Update control lets the navigator choose when to apply it.

- Whole-repo cleanup pass, weather-weighted, no behavior change. One shared
  `emptyFeatureCollection` in `$shared/map` replaces the per-overlay copies (vessel, track, ais,
  notes, and weather), a shared `headingDegrees` helper folds the vessel and AIS heading fallback,
  the weather mini-map's viewport cache is now bounded, the wind overlay reports both its candidate
  layer ids so a rare WebGL fallback still restacks, the notes cluster click no longer double-fires,
  the radar opacity is one constant, and dead surface was removed (the unused `cellIndex` export, the
  unused weather `type` field and `weatherCacheKey` export, and a pass-through wrapper).

- Points of interest cluster later and say what they hold. Markers now uncluster from zoom 12 (up
  from 14), so the zoom you usually navigate at shows individual POIs instead of group circles, while
  the wider view (zoom 9 to 11) still clusters so it does not turn into a mash of overlapping pins. A
  cluster no longer reads as a generic purple circle: it shows the colored icon of its most important
  member (a red hazard disc if it holds any hazard, the amber navaid disc otherwise the point-of-
  interest disc), inside a ring that marks it as a group, with a count badge. Clicking a cluster still
  zooms it apart.

- Tidied the Signal K auth flow internals, with no behavior change. The focus and cross-tab
  storage listeners now live inside `AuthController` (like `OnlineStatus` owns its own listeners)
  instead of the app shell parsing the stored auth JSON itself, a single in-flight guard stops a
  duplicate access-request poll when a tab return fires focus and visibilitychange together, and
  the own-vessel and AIS subscriptions are issued in one call.

- Cleanup pass over the depth-charts work. The two IndexedDB stores now
  share one open-and-transaction helper; the unused PMTiles store list and total-size methods were
  dropped; byte-size formatting moved to a shared `formatBytes`; and a few small dead guards, a
  redundant array copy, and duplicated layer-id lists were tidied.

- Whole-repo cleanup pass. The collision assessment is memoized with
  `$derived`, so the O(targets) CPA loop runs once per real change instead of several times per
  frame (it was recomputed on every animation frame by the overlay and twice per alarm tick). CPA
  and TCPA display formatting is centralized in `shared/lib` (`formatCpaNm`, `formatTcpaMin`). The
  Signal K socket gates every handler on still being the current socket, so a superseded socket
  cannot inject a delta or schedule a second reconnect. POI classification is case-insensitive, the
  notes overlay skips per-frame work when the map is idle, the layers view updates one item in place
  instead of rebuilding the list on every slider tick, the menu submenu is tied to its content with
  `aria-controls`, and the empty-spec chart overlay no longer installs a dangling listener. Renamed
  `radiansToDegrees` to `radiansToBearing` (it normalizes to 0..360), exported `SELF_CONTEXT`, and
  added `nauticalMilesToMeters`. No behavior change beyond the perf and robustness fixes.

- Second whole-repo cleanup pass. The vessel, AIS, and collision
  entities now expose speed and course in SI (m/s and radians); knots and compass-bearing
  conversion moved to the display edge (the status strip, the vessel and AIS overlays, and a shared
  `formatKnots`), removing a knots-to-m/s and degrees-to-radians round-trip in the collision math.
  The worker hands the AIS batch to the main thread as a nested `Map` across the Comlink boundary,
  dropping a per-frame object rebuild on each side. The collision overlay dirty-checks the
  assessment by reference instead of building a per-frame signature string. Gap-splitting for track
  simplification and export is one shared `splitAtGaps` helper, the theme and settings localStorage
  writes are guarded against quota and private-mode failures, the menu-icon, connection-phase, and
  theme label maps are typed to their unions, and dead code (an unused `metersToNauticalMiles`
  export, a `LatLon` re-export, the entry-module default export, and a redundant callback wrapper)
  was removed.

- The layers controls (per-layer visibility and opacity) moved off the chart into the app menu.
  They were a panel floating over the top-left of the map; they now live in a collapsible "Layers"
  submenu inside the menu, so the chart is unobstructed and the controls share one place with other
  app options. Each layer's visibility and opacity still persist across refreshes.

- The own-vessel marker is now a boat hull instead of a flat triangle. It is drawn with the 2D
  canvas (filled hull, darker outline, sharp bow, flat transom) at 2x for retina crispness, rotates
  to `headingTrue` (falling back to `courseOverGroundTrue`), and recolors with the theme (blue by
  day, red at night). The pointed bow makes the heading unambiguous. The symbol-overlay factory
  gained an optional `pixelRatio` so an icon can be drawn at 2x.

- A tiled chart now hands off to the base map when you zoom past its native detail. Each chart's
  draw layers are capped one zoom level beyond the source's native maximum (read from the loaded
  tile metadata, so it is archive-agnostic), so zooming in past the chart's scale reveals the sharp
  base map instead of a blocky overzoomed chart. The chart stays authoritative within its own zoom
  range and aligned with the base beyond it.

- The collision danger strip's Acknowledge control now works: acknowledging suppresses the current
  worst contact, and a new or more severe contact automatically re-arms the alert. The full
  mute/alarm lifecycle remains a later Lookout step.

- Whole-repo cleanup pass: a chart now themes its own draw layers through an `applyTheme` broadcast
  from the layer manager, so the widget no longer reaches into chart layers by id (the source-layer
  to color mapping lives in one place); the vessel and AIS overlays are built from a shared
  `createSymbolOverlay` factory instead of duplicated scaffolding; the vector draw order and per
  source-layer styling are a single ordered list; a `mapstyleJSON` chart is a clean no-op pending
  the style pipeline rather than a broken source; AIS target views are memoized by version so
  own-vessel motion no longer rebuilds the list; the AIS staleness scan is throttled off the
  per-frame path; the subscription registry gained a refcounted `remove` and the worker routes
  unsubscribe through it so a dropped path is not resurrected on reconnect; `PersistedValue` reports
  `fromStorage` by key presence rather than a value compare; coordinate formatting and the
  AIS-target field extractors were de-duplicated; the connecting state is a shared
  `INITIAL_CONNECTION_STATE`; dead code was removed (`PathCell.receivedAt`, the unused `worst`
  getter, the `kelvinToCelsius` and `metersToFeet` helpers, identity arithmetic in the icons, and
  several unreachable null-guards); and the danger strip shows a "+N more" cue instead of silently
  truncating the contact list.

- Whole-repo cleanup pass: the chart source and layer ids derive from a single `chartSourceId`
  helper, the own-vessel overlay skips its per-frame `setData` when position and heading are
  unchanged, the vessel icon is built once and cached, the connection clears its reconnect timer
  on disconnect, malformed delta frames and chart fetch errors now warn instead of failing
  silently, MapLibre source and layer specs are properly typed (no `as never` casts), the layers
  panel hides the opacity slider for layers that do not support it, the unit converters accept
  `null`, the connection wakes a single shared own-vessel instead of two, and the shared test
  fakes (`FakeWebSocket`, `createFakeMap`) live in `src/shared/testing/` rather than being
  redefined per test. The dependency-cruiser ruleset now covers every Feature-Sliced Design
  layer direction, including the cross-feature public-API boundary.

- The map: a MapLibre GL map with a vector base, rendered in the chart area. A framework-free
  `LayerManager` gives every overlay an independent toggle, opacity, and deterministic z-order
  via sentinel layers and `beforeId`, so a new overlay later is a new file plus one
  registration. The own vessel renders as a GPU symbol layer that rotates with heading (falling
  back to course over ground), updated from the Signal K store each animation frame. Includes
  the PMTiles protocol registration for future offline tiles.

- Real-time data layer: a Web Worker hosts the Signal K WebSocket client, bridged to the main
  thread with Comlink, delivering one batched frame per animation frame. A path-keyed runes
  store of independently reactive cells lets a component bound to one Signal K path avoid
  re-running when an unrelated path changes. Includes delta reconciliation, a per-frame
  last-write-wins batcher, a refcounted subscription registry, full-jitter reconnection with
  resubscribe on open, and an own-vessel entity that converts SI values to display units at the
  edge. The shell shows live connection state and own-vessel SOG and COG.

- Project floor (Phase 1 of the foundation): a Svelte 5, Vite, and TypeScript application
  that builds as a Signal K webapp, serving static files from `public/` at `/binnacle/`.
- Feature-Sliced Design layout (`app`, `views`, `widgets`, `features`, `entities`, and
  `shared`) with module boundaries enforced by dependency-cruiser.
- SI unit-conversion module in `shared`, built test-first, covering meters per second to
  knots, radians to a normalized degree bearing, Kelvin to Celsius, meters to feet, and
  meters to nautical miles.
- Verification toolchain: Biome for lint and format, svelte-check for type-checking, Vitest
  for unit tests, Playwright for an end-to-end smoke test, and a CI workflow running the
  full gate on Node 24.
- Foundation design spec and Phase 1 implementation plan under `docs/superpowers`.
- README, an Apache-2.0 LICENSE, and a Buy Me a Coffee funding link (README badge,
  `.github/FUNDING.yml`, and the `package.json` funding field).

### Fixed

- The active-route Stop button and the collision-alarm Acknowledge button on the bottom strips were
  inert. When both strips moved to the shared `.bottom-strip` class, the app shell's `pointer-events`
  override still targeted their old `.nav-strip` and `.danger-strip` selectors, so each slot's
  `pointer-events: none` reached the button. The override now targets `.bottom-strip`, restoring both
  safety-critical actions.

- Night-red contract violations are corrected: the AIS target was orange (it is
  now in the red band, with a test guarding it), and the rain-radar legend showed literal blue and
  green chips at night (now a red intensity ramp). The collision warning severity and an empty track's
  stats are no longer misleading (a distinct warning color in the danger strip and the thresholds, and
  a placeholder instead of a zero), and the weather panel no longer renders with square corners
  because a referenced radius token was undefined.

- Accessibility fixes: the slide-over panels close on Escape and restore focus, the
  layer visibility checkboxes and the threshold inputs have explicit names, the note-detail and
  weather conditions async states announce as status or alert, the add-chart URL field is labeled, the
  chart rename commits on Enter, the layer-reorder handle advertises its arrow-key shortcut, and the
  menu popout uses dvh so a long menu stays reachable on a phone.

- Saving a route now works. The Signal K server validates the standard route resource, and two
  things failed that validation silently: an unnamed waypoint wrote an empty metadata entry the
  schema rejects (every entry must carry a name), and over plain HTTP the route id fell back to a
  non-UUID string that the resources API refuses for standard types. Routes now omit per-waypoint
  metadata when no waypoint is named (and name the gaps by position otherwise), and route and track
  ids are always real v4 UUIDs, generated from `crypto.getRandomValues` where `crypto.randomUUID` is
  unavailable. Verified end to end against the server: a drawn, unnamed route now saves, lists, and
  survives a reload.

- The on-chart route editing line is easier to see and the Routes controls read as actions. The
  editing line was a blue that blended into the water; it now uses the bright selection accent (amber
  by day, a light red at night) and a heavier stroke, and the New route and Save buttons are filled
  with the accent instead of flat gray. Each saved route now sits in its own elevated card: the name
  is a title on its own line, a mono distance and waypoint-count readout sits beneath it in the same
  instrument style as the navigation strip, the actions form a clean cluster with the delete pushed to
  the trailing edge, and the active route is marked with an accent edge bar, an accent tint, and an
  "Active" pill so the live route is obvious at a glance in every theme.

- In the menu, Routes now sits above Layers and charts.

- A vector chart that declares a coverage extent now honors it. The raster chart paths already passed
  the declared `bounds` to MapLibre, but the vector path dropped it, so a regional vector chart
  requested and 404'd tiles across the whole world instead of only within its coverage. The vector
  source now carries `bounds` the same way the raster sources do.

- A provider-supplied collision contact is now gated the same as a locally computed one at the exact
  instant of closest approach. The provider branch treated a TCPA of zero (closest approach right now)
  as a live danger while the computed branch treated the same geometry as no longer closing; both now
  require a positive TCPA, so the two CPA sources agree and a just-passed contact cannot flicker as a
  danger from one source but not the other.

- Weather values now read consistently to one decimal. The legend low and high labels and the wave
  period readout previously mixed bare integers ("0", "9") with decimals ("0.0", "0.5"); wind, waves,
  precipitation, and cloud now all show one decimal place ("X.X"). Bearing, pressure, and temperature
  stay whole numbers, as those units are conventionally integers.

- Weather caching now fits the data. Forecasts are cached for an hour rather than 30 minutes (Open-Meteo
  model runs are hours apart, and the time slider already shows the right hour from the cached 5-day
  window), roughly halving request volume. When only the marine (waves) endpoint fails, commonly an
  Open-Meteo 429 on its separate host, the forecast grid (wind and pressure) is still shown but is not
  cached and the loader backs off, so panning no longer re-hits the rate-limited endpoint on every move.

- The weather mini-map no longer freezes blank when Rain radar is on. The RainViewer raster source
  starts with no frame URL (real frames arrive later), and MapLibre loads tiles for a layer in the
  style regardless of its visibility, so an empty tiles array crashed its tile-URL builder and then
  the raster render program, freezing the panel. The source now seeds a transparent placeholder tile
  so tile loading always succeeds, and the layer stays hidden until a real frame is applied so it is
  never drawn empty. Real frames replace the placeholder as before.

- Weather is gentler on the free data sources and correct over fresh water. The atmospheric forecast
  no longer forces Open-Meteo's sea cell selection, which picked wrong or missing cells over inland
  and freshwater areas such as the Great Lakes; sea selection now applies only to the marine wave
  request. A failed grid fetch backs off for a minute instead of retrying on every pan, the per-load
  grid is sampled to fewer points so a load fits a single request, and the viewport cache is capped.
  The "Here" conditions panel keys its lookups on a position rounded to about 110 meters, so GPS
  jitter no longer refetches (and no longer spams a Signal K weather provider with point requests) on
  every fix.

- The Forecast button is no longer clipped at the bottom of the status strip, and wind readouts show
  one decimal place (matching waves), even at zero.

- Signal K access approval now connects on its own. Previously, after you approved Binnacle in the
  Signal K UI and returned to the tab, it kept polling a stale request and only a second tab would
  connect. Binnacle now rechecks the pending request the moment the tab regains focus (background
  tabs throttle the poll timer), re-requests a fresh one if the old request expired, connects the
  stream reactively the instant access is granted (no reload), and adopts a token approved in another
  tab via the storage event. The old one-shot blocking connect that required a reload or a second tab
  is gone.

- A raster chart layer now follows the night-red theme (desaturated and dimmed) instead of staying
  full-saturation and full-brightness, matching the streaming depth layers.

- The track and PMTiles stores no longer lose records when IndexedDB degrades mid-session: every
  write is mirrored to the in-memory fallback, so data written before the failure survives.

- Switching the theme from night-red or dusk back to day no longer leaves the base map broken. The
  day restore brings the water and land fills back to their real colors and clears the dark label
  halo from street names. `fill-pattern` is a paint property in MapLibre, not a layout one, so the
  day-restore snapshot had silently dropped every fill layer, and the label halo was reset only when
  the source style already defined one.

- In the day theme, an unchecked layer checkbox no longer renders as a solid black square. The day
  theme declared a dark `color-scheme` despite being a light theme, so native controls rendered in
  dark mode; day now uses a light `color-scheme`, and checkboxes follow the theme accent (no blue at
  night).

- The base map now recolors fully for the theme. Previously only the background and water were
  themed, so over land at higher zoom the OpenFreeMap roads stayed white and the parks and landcover
  green even in night-red, breaking the pure-red-on-black contract. Every base layer is now recolored
  from its source layer (water, landcover, landuse, transportation, building, boundary, and text
  labels) per theme, fill patterns are cleared so the flat themed color shows, and label text gets a
  background-colored halo. Day and dusk gain a calmer palette consistent with the app; night-red is
  red-on-black across the whole map.

- A vector (PMTiles) chart could drop tiles to blank gaps at low-to-mid zoom (around z9) on some
  GPUs, even though the tiles fetched fine (HTTP 206) and decoded correctly. The cause was render
  load: the archive ships `landuse` un-simplified from low zoom, so a single z9 tile can carry
  roughly 1700 polygons that are invisible at that scale but heavy to draw over the full base map,
  which a weaker or high-DPI GPU silently fails to render. The chart now holds `landuse` until z12,
  where it is actually legible, cutting the low-zoom chart draw load sharply. Each chart layer's
  minzoom is preserved through the max-zoom cap.

- A vector (PMTiles) chart could show blank gaps over a real network. The archive is read
  uncached (`cache: 'no-store'`, to dodge a Chrome disk-cache write failure), so each chart tile
  depends on a live HTTP range read; a transient drop or a server hiccup under a burst of reads (a
  zoom that pulls in new tiles) blanked that tile until a later zoom re-requested it. The PMTiles
  source now retries a failed or 5xx range read (short backoff, up to two tries) while still
  honoring a caller abort, so a transient failure no longer leaves a hole.

- AIS targets disappeared from the chart after about six minutes of page uptime. The worker stamped
  each frame's epoch with 0 (`requestAnimationFrame` is absent in the worker, so the batcher's
  fallback passed 0) while the overlay pruned staleness against `performance.now()`, so pruning
  tracked uptime, not real staleness. The worker now stamps a wall clock (`Date.now`) and the
  overlay prunes with the same clock.
- The AIS change counter bumped on every worker frame, not only on AIS changes, because the worker
  always emits an `ais` object (empty when only the own vessel moved). It now bumps only when a
  context actually updates, so the AIS overlay and the collision assessment no longer rebuild every
  frame.
- The stored-token auth probe now omits credentials, so a live session cookie cannot mask a stale
  token and leave the WebSocket streaming nothing.

- Vector charts (MVT/PMTiles) rendered nothing on the map. A vector tile source paints nothing on
  its own: MapLibre needs a draw layer per source-layer, and the chart adapter both routed these
  charts to a raster source and, on the vector path, emitted no draw layers. The adapter now routes
  any chart marked `mvt`/`pbf`, typed `tileJSON`/`mapstyleJSON`, or ending in `.pmtiles` to a vector
  source and generates themed fill and line draw layers per source-layer. It covers the two dominant
  vector base-map schemas (Protomaps and OpenMapTiles) and, because Signal K's charts API often
  returns an empty layer list for an archive, falls back to the full known set when none are
  declared; MapLibre silently ignores a draw layer whose source-layer is absent. The chart layers
  recolor with the day, dusk, and night-red themes, and per-layer opacity now uses the correct paint
  property for fill, line, and raster layers.

- PMTiles vector charts failed to render with `ERR_CACHE_WRITE_FAILURE`: a large archive served
  with a weak ETag over range requests makes Chrome fail the HTTP disk-cache write, which rejects
  the whole fetch and blanks the chart. Binnacle now registers each PMTiles archive with a source
  that fetches ranges with `cache: 'no-store'`, bypassing the browser cache for these reads. Durable
  offline caching of these archives is a later spec.

- Collision assessment no longer raises a false alarm on an opening or already-passed AIS target:
  the provider closest-approach path now drops a contact whose time to closest approach is negative
  (the closest approach is in the past), matching the computed path's behavior.
- Closest-point-of-approach math now normalizes the longitude difference, so a vessel pair
  straddling the antimeridian computes a real short range instead of a bogus near-360-degree offset.
- The AIS change counter is now reactive state, so a future reactive consumer is notified rather
  than only the per-frame poll, removing a latent reactivity trap.

- Own-vessel readouts (SOG and COG) stayed blank while live data flowed. The store creates a
  path cell lazily on first access; the first access was the shell's reactive readout, so a
  brand-new `$state` source was created during the effect's tracking pass and never subscribed,
  and later updates did not re-render. `OwnVessel` now creates its cells at construction, before
  the readout runs, so the reactive read tracks an existing cell.
- The auth probe could mistake a secured server for an unsecured one. It checked a REST path that
  a browser session cookie satisfies, so it concluded "unsecured" and connected the WebSocket
  stream without a token, which the cookie does not authenticate, leaving no live data. The probe
  now checks a stored token first and runs the anonymous check with credentials omitted, so a
  cookie cannot mask the need for a token.
- The Signal K worker crashed at load with "Class extends value undefined" because the worker
  graph imported the server-side `@signalk/server-api` package, whose entry re-exports a
  `FullSignalK` class extending Node's `EventEmitter`; bundled into the browser worker with
  `events` externalized, that base class resolved to `undefined`. The worker now mirrors the few
  Signal K wire types it needs locally and no longer imports the package, dropping the worker
  bundle from about 164 KB to about 7 KB and removing the dependency entirely.
- The chart area rendered all blue offshore because the base map was fetched from a CDN
  (`tiles.openfreemap.org`), which is unreachable on a boat with no internet, leaving an empty
  map that showed the page background through it. Binnacle now ships a bundled, offline base
  style that the theme recolors, with Signal K charts layered on top. Bundled vector base tiles
  are a later spec; this removes the CDN dependency in line with the offline-first rule.

### Security

- The points-of-interest popup's "View details" link now follows only `http:` and `https:` URLs.
  A note's link comes from a resource provider Binnacle does not control, so a `javascript:` or
  `data:` URL would otherwise execute when clicked; non-http schemes and unparseable URLs are now
  dropped.
