/* gps.html: the tracker app, laid out the way the app lays itself out.
 *
 * 10 GPS/sinotrack_map.py serves one screen — a map filling the window
 * with a 290px panel floating over its top-left corner — and this is
 * that screen under the site's header. The geometry is the app's: same
 * corner, same width, same translucent card, same two-column read-out
 * with the reading named on the left and the figure on the right.
 *
 * THE COLOURS ARE THE SITE'S. The app hardcodes rgba(255,255,255,.94)
 * and a prefers-color-scheme block under it, because it is one page
 * served by itself and has no palette to belong to. This one does, and
 * a panel in another design system's white on a page in this one reads
 * as a screenshot pasted onto the site. So the shapes are copied and
 * the tokens are ours — which is the same call live.css made, and the
 * two panels are deliberately siblings.
 *
 * Tabular numerals throughout the read-out. It repaints on every poll,
 * and proportional digits make the whole column jump every time a 1
 * becomes a 7.
 */

/* ---- the page is the chart, edge to edge ---------------------------
   Every other page on this site opens with a band of sand and the
   wordmark across it. This one is an instrument: the chart runs to all
   four edges of the window and the header floats on top of it, which is
   how the app this came from looks and the only way the map is as big
   as the screen it is on.

   So <main> is pulled back up under the header rather than starting
   below it. build.py puts the page's name on <body>, which is what lets
   a stylesheet loaded for one page reach a header drawn before <main>
   ever starts. */
.page-gps main { margin-top: calc(-1 * var(--header-h, 96px)) }

.gps-page {
  position: relative;
  /* dvh rather than vh: on a phone the browser's chrome slides away as
     you scroll and vh keeps promising the taller number, which leaves
     the map's bottom edge under the toolbar for the whole visit. */
  height: 100dvh;
  min-height: 460px;
}

/* THE SCRIM GOES. .site-header::before lays the page's own sand behind
   the header and fades it out below -- right everywhere else, and here
   it is a band of beach across the top of a chart. */
.page-gps .site-header::before { display: none !important }

/* AND THE HEADER STOPS EATING CLICKS.
   Everywhere else it sits above the page and nothing needs to be
   reached through it. Here the chart runs underneath it, so its full
   width by 126px tall box was swallowing every press and drag in the
   top strip of the map -- Leaflet's own zoom buttons among them, which
   sit in exactly that corner and could not be clicked at all.
   The bar is transparent to the pointer and its actual controls take it
   back. Only the things somebody presses, not their containers, or the
   dead strip comes straight back at a different width. */
.page-gps .site-header,
.page-gps .site-header .wrap { pointer-events: none }
.page-gps .site-header .brand,
.page-gps .site-header .sea-slot,
.page-gps .site-header .account-ic,
.page-gps .site-header .menu-toggle,
.page-gps .site-header .nav { pointer-events: auto }

/* Leaflet puts zoom in the top-right corner, which on this page is
   where the sea chip and the drawer live. Down below them. */
.page-gps .leaflet-top.leaflet-right { top: calc(var(--header-h, 96px) + 4px) }

/* THE BADGE, NOT THE WORD, and it is magik.css that draws it.
   site.js shuts the lockup down to the disc once a page has scrolled;
   this page has nothing to scroll, and the 250px wordmark over a map is
   the largest thing on the screen and the least useful.
   The rules used to be copied here, which is how the badge ended up a
   different size from every other page's and still rippling when they
   had gone still -- three of the five rules had been copied and two had
   not. They are not copied any more: the mark rules in magik.css name
   `.page-gps` beside `.wm-shut`, so there is one description of what a
   closed badge looks like and this page cannot drift from it.
   The rules about the BAR are deliberately not shared -- the sea chip
   collapses to a glyph when the page is docked, and this page is not
   docked, it is simply short. */

/* The header's own children keep their ground -- the sea chip, the
   account glyph and the drawer are all pills with a fill of their own,
   so they read against photography as well as against sand. Nothing to
   do here but let them float. */

/* EXCEPT THAT THEY WERE NOT AT THE EDGES.
   .wrap is the site's measure -- max-width and centred -- which is
   right for a page of prose and wrong for one that is a chart from
   edge to edge: on a wide screen it left the badge stranded a quarter
   of the way in from the left and the sea chip, the account and the
   drawer stranded a quarter of the way in from the right, both of them
   floating over open water with nothing to sit against. A full-bleed
   page wants a full-bleed bar. The gutter is kept, so nothing is
   jammed into the corner, and the safe-area insets with it. */
.page-gps .site-header .wrap { max-width: none }

.gps-map {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ac) 8%, var(--bg));
  /* Leaflet numbers its panes from 400 and its controls to 1000, which
     is more than anything else on this site. Contained here, so those
     numbers mean something inside this box and nothing outside it. */
  z-index: 0;
  isolation: isolate;
}

/* Leaflet's own furniture, in the site's type and colour rather than
   two design systems on one screen. */
.gps-map .leaflet-container {
  font-family: "Lexend", system-ui, sans-serif;
  background: color-mix(in srgb, var(--ac) 8%, var(--bg));
}
.gps-map .leaflet-control-attribution {
  font-size: 10.5px;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
}
.gps-map .leaflet-bar a {
  color: var(--ink);
  background: var(--bg);
  border-bottom-color: color-mix(in srgb, var(--ink) 12%, transparent);
}
.gps-map .leaflet-popup-content-wrapper,
.gps-map .leaflet-popup-tip {
  background: var(--bg);
  color: var(--ink);
  border-radius: 12px;
}
.gps-map .leaflet-popup-content { font-size: 13px; line-height: 1.5 }

/* The trail, and it is the app's BLUE rather than the site's coral.
   Coral was the palette argument and it lost to the map underneath it:
   OSM draws trunk roads in orange, this unit spends its life on them,
   and a coral line at 75% over an orange road is a line nobody can
   find. The whole point of the trace is telling where she has been from
   where the roads are. #2563eb is the app's own stroke and the one
   colour on the chart that no OSM feature is already using. */
.gps-map .gps-trail {
  stroke: #2563eb;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---- the panel ------------------------------------------------------ */

/* THE READ-OUT LIVES IN THE BOAT'S POPUP NOW.
   It was a card standing open in the corner for the whole visit, under
   the header and over the chart -- a lot of furniture on a page whose
   subject is the chart, answering a question nobody had asked. Leaflet
   moves this element into its popup, so it needs no position of its own
   any more; what it needs is to stop looking like Leaflet's popup and
   start looking like the panel it was. */
/* HIDDEN UNTIL LEAFLET TAKES IT. The element sits in the markup where
   it belongs -- it is a document, and a document belongs in the HTML --
   but Leaflet only moves it into the popup the first time the popup is
   opened. Until then it was simply drawn in the page, stacked under the
   chart, which is the old always-open panel back again by accident. So
   it is not painted anywhere except inside a popup. */
.gps-panel { display: none }
.leaflet-popup-content > .gps-panel {
  display: block;
  padding: 2px 2px 0;
  min-width: 250px;
}

/* Leaflet's own popup chrome, made into the site's card: the same
   translucent ground, blur and hairline the notice below uses, so the
   two things that float over this chart are plainly one family. */
.gps-map .gps-pop .leaflet-popup-content-wrapper {
  background: color-mix(in srgb, var(--bg) 93%, transparent);
  backdrop-filter: blur(10px);
  border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  border-radius: 14px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, .18);
  color: var(--ink);
}
.gps-map .gps-pop .leaflet-popup-content { margin: 13px 15px; width: auto !important }
.gps-map .gps-pop .leaflet-popup-tip {
  background: color-mix(in srgb, var(--bg) 93%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  box-shadow: none;
}
/* The close control is Leaflet's serif × in the corner. Put it in the
   page's own ink and give it room, so it reads as part of the card. */
.gps-map .gps-pop .leaflet-popup-close-button {
  color: var(--muted); font: 400 20px/1 "Lexend", system-ui, sans-serif;
  padding: 8px 10px 0 0; width: auto; height: auto;
}
.gps-map .gps-pop .leaflet-popup-close-button:hover { color: var(--ink) }

/* ---- what is wrong, if anything ------------------------------------
   Its own card, because most of what it has to say is true when there
   is no boat on the chart to hang a popup on. Drawn only when it has
   something to say. */
.gps-note {
  position: absolute;
  top: calc(var(--header-h, 96px) + 12px);
  left: 16px;
  z-index: 5;                   /* over the map, which is its own context */
  width: min(320px, calc(100% - 32px));
  max-height: calc(100% - var(--header-h, 96px) - 28px);
  overflow-y: auto;
  padding: 14px 16px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(10px);
  border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  box-shadow: 0 10px 34px rgba(0, 0, 0, .14);
}
.gps-note .gps-msg { margin-top: 0 }

/* Everything in this panel that comes and goes does it with the hidden
   attribute, so the attribute has to win here. It did not: the browser
   hides [hidden] with a `display: none` in its own stylesheet, an
   author rule beats that whatever it says, and .gps-gm sets a display
   of its own -- so the Google Maps link stayed on screen, pointing at
   "#", for a unit with no position to open. */
.gps-panel [hidden] { display: none !important }

.gps-panel h1 {
  display: flex; align-items: center; gap: 7px;
  margin: 0 0 2px; font-size: 15px; font-weight: 600; letter-spacing: -.01em;
}
.gps-sub {
  font-size: 12px; color: var(--muted); margin-bottom: 10px;
  font-variant-numeric: tabular-nums;
}
.gps-sub:empty { display: none }

/* ---- THE THREE STATES, IN THE THREE COLOURS EVERYONE ALREADY KNOWS
   Green is going, amber is idling, red is not answering. The page used
   to draw a reporting boat in the site's coral -- Living Coral, a red
   -- so a unit working perfectly showed the colour every other
   instrument on earth uses for a fault.
   The palette has no green and never needed one: nothing else on this
   site is a status light. These three live here, on the page that has
   them, rather than in magik.css where they would be a fourth accent
   nobody asked for. Amber is the site's own --wa, because it already
   is that amber. */
/* THE HIDDEN ATTRIBUTE HAS TO WIN, ANYWHERE ON THIS PAGE.
   The browser hides [hidden] with a `display: none` in its own
   stylesheet, and any author rule beats that. Everything here that
   comes and goes -- the scale, the notice, the read-out's own sections
   -- is toggled with the attribute, and every one of them sets a
   display of its own, so each was staying on screen. It caught the
   Google Maps link first, then the speed scale; a rule per element was
   plainly the wrong shape for it.
   The panel needs its own copy of this, further down: Leaflet moves it
   out of .gps-page into the popup pane, where this selector cannot
   reach it. */
.gps-page [hidden] { display: none !important }

.gps-page {
  --go:   #1FA971;   /* under way */
  --idle: var(--wa); /* reporting, not making way */
  --gone: #D2402F;   /* not reporting: quiet, off, or off the shelf */
}

.gps-dot { width: 9px; height: 9px; border-radius: 50%; flex: none }
.gps-dot.is-under { background: var(--go) }
.gps-dot.is-idle  { background: var(--idle) }
.gps-dot.is-off   { background: var(--gone) }
/* The popup is moved out of .gps-page by Leaflet, so the tokens have to
   reach it there too. Same three values, named once. */
.leaflet-popup-content .gps-dot.is-under { background: #1FA971 }
.leaflet-popup-content .gps-dot.is-idle  { background: #E2A33C }
.leaflet-popup-content .gps-dot.is-off   { background: #D2402F }

.gps-row {
  display: flex; justify-content: space-between; align-items: center;
  gap: 10px; padding: 2.5px 0;
  font-size: 12.5px;
}
.gps-row .k { color: var(--muted) }
.gps-row .v { text-align: right; font-variant-numeric: tabular-nums }

/* The one control on the page, and the one thing on it that acts
   rather than reports — so it is set apart by a rule from everything
   above it, which only reports. */
.gps-cmd {
  margin-top: 10px; padding-top: 9px;
  border-top: 1px dashed color-mix(in srgb, var(--ink) 16%, transparent);
}
.gps-cmd .v { display: flex; gap: 5px }
.gps-cmd select, .gps-cmd button, .gps-setup button {
  font: inherit; font-family: "Lexend", system-ui, sans-serif;
  font-size: 11.5px;
  color: var(--ink); background: var(--bg);
  border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
  border-radius: 8px; padding: 3px 7px;
}
.gps-cmd button, .gps-setup button { cursor: pointer; font-weight: 550 }
.gps-cmd button:hover, .gps-setup button:hover {
  background: color-mix(in srgb, var(--ink) 8%, var(--bg));
}
.gps-cmdmsg {
  margin: 5px 0 0;
  font-size: 11.5px; line-height: 1.45; color: var(--muted);
}
.gps-cmdmsg:empty { display: none }

/* The app's amber note, and it carries every state the panel cannot
   put in a row: no fix yet, gone quiet, never configured, or the feed
   itself not answering. */
.gps-msg {
  margin-top: 10px; padding: 9px 11px;
  border-radius: 10px; font-size: 12px; line-height: 1.5;
  background: color-mix(in srgb, var(--wa) 20%, var(--bg));
  color: var(--ink);
}
.gps-msg b { font-weight: 600 }

/* The frames for a unit that has never reported. Monospaced and
   breaking anywhere, because they are strings to be copied exactly and
   a frame wrapped mid-token is one somebody retypes wrong. */
.gps-setup { margin-top: 10px; font-size: 12px }
.gps-setup > b { font-weight: 600 }
.gps-setup ol { margin: 6px 0 0; padding-left: 17px }
.gps-setup li { margin: 3px 0 }
.gps-setup code {
  font: 11px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-all;
}
.gps-setup button { margin-top: 8px }

.gps-gm {
  display: inline-block; margin-top: 10px;
  font-size: 12px; color: var(--su);
}


/* ---- the two chart controls ----------------------------------------
   Bottom right: away from the read-out top-left and out of the corner
   Leaflet's zoom already owns. Both are about the CHART rather than
   about the unit, which is why neither is in the panel with the
   readings. */
.gps-controls {
  position: absolute;
  right: 14px; bottom: 34px;
  z-index: 5;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
}

.gps-seg {
  display: flex;
  border-radius: 10px; overflow: hidden;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(10px);
  border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .16);
}
.gps-seg button {
  font: 550 11.5px/1 "Lexend", system-ui, sans-serif;
  color: var(--muted); background: transparent;
  border: 0; padding: 7px 11px; cursor: pointer;
}
.gps-seg button + button {
  border-left: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
}
.gps-seg button:hover { color: var(--ink) }
.gps-seg button.on { color: var(--bg); background: var(--ink) }

.gps-toggle {
  display: flex; align-items: center; gap: 6px;
  font: 550 11.5px/1 "Lexend", system-ui, sans-serif;
  color: var(--muted);
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(10px);
  border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  border-radius: 10px; padding: 7px 11px; cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .16);
}
.gps-toggle svg { width: 15px; height: 15px; flex: none }
.gps-toggle:hover { color: var(--ink) }
/* On is the trace's own blue rather than the ink, so the control and
   the line it governs are plainly the same thing. */
.gps-toggle.on { color: #2563eb; border-color: color-mix(in srgb, #2563eb 45%, transparent) }

/* ---- the battery ----------------------------------------------------
   The phone's cell: a rounded body, a nub on the right, and a fill that
   is the whole of the reading. Colour only where colour means
   something -- amber at a quarter, red at a tenth -- because a green
   battery is just a battery and does not need saying twice. */
.gps-batwrap { display: flex; align-items: center; gap: 7px }

.gps-bat { display: inline-flex; align-items: center; gap: 1.5px }
.gps-bat-cell {
  position: relative;
  width: 24px; height: 12px;
  border: 1.5px solid color-mix(in srgb, var(--ink) 45%, transparent);
  border-radius: 3.5px;
  padding: 1.5px;
  display: block;
}
.gps-bat-cell i {
  display: block; height: 100%; width: 0;
  border-radius: 1.5px;
  background: var(--ink);
  transition: width .35s ease, background .35s ease;
}
.gps-bat-cap {
  width: 2px; height: 5px; border-radius: 0 1.5px 1.5px 0;
  background: color-mix(in srgb, var(--ink) 45%, transparent);
}
.gps-bat.ok   i { background: var(--su) }
.gps-bat.warn i { background: var(--wa) }
.gps-bat.low  i { background: #dc2626 }
.gps-bat.low .gps-bat-cell { border-color: #dc2626 }
/* NOTHING REPORTED IS NOT A FLAT BATTERY, and they were drawing the
   same picture: an outline with no fill. One means "she is about to go
   dark", the other means "this box has never said" -- opposite facts.
   So the no-reading state is struck through, which is how a gauge with
   no sender is drawn on any dashboard, and the words beside it say so
   in full rather than with a dash. */
.gps-bat.none { opacity: .5 }
/* A level carried forward from an earlier fix rather than measured on
   this one. Drawn, because it is the best answer there is, but at half
   strength so it does not read as a live reading. */
.gps-bat.held { opacity: .62 }
.gps-bat.none .gps-bat-cell { position: relative }
.gps-bat.none .gps-bat-cell::after {
  content: "";
  position: absolute; left: -2px; right: -2px; top: 50%;
  height: 1.5px;
  background: color-mix(in srgb, var(--ink) 60%, transparent);
  transform: rotate(-20deg);
}
/* "not reported" is words, not a figure, so it should not sit in the
   tabular-numeral column pretending to be one. */
.gps-bat.none + .num { font-variant-numeric: normal; font-size: 11.5px }

/* ---- the boat -------------------------------------------------------
   A hull from above, turned to her course. White edge and a shadow for
   the same reason a chart mark always has them: a 34px shape crosses
   roads, blocks, labels and water in the course of one charter, and
   without a hard edge of its own it disappears into whichever of those
   it is over. On satellite photography that is not a nicety at all.

   THE THREE STATES ARE THE THREE COLOURS, and they are not decoration:
   green is a boat this page is hearing from and making way, amber is
   one it is hearing from at rest, red is one it is not hearing from at
   all. The last of those is the one that matters, because what is on
   the chart then is where she WAS. */

/* Leaflet puts the icon element in its own pane; the tokens on
   .gps-page do not reach it, so the three are named here. */
.gps-boat {
  --go: #1FA971;
  --idle: #E2A33C;
  --gone: #D2402F;
  position: relative;
  cursor: pointer;             /* the readings are behind it */
}

.gps-boat span {
  position: relative; z-index: 2;
  display: block; width: 34px; height: 34px;
  transition: transform .6s cubic-bezier(.2, .9, .3, 1);
}
.gps-boat svg { width: 34px; height: 34px; overflow: visible }

.gps-boat .hull {
  stroke: #fff; stroke-width: 2.2; stroke-linejoin: round;
  paint-order: stroke;
}
.gps-boat .deck { fill: #fff }

.gps-boat.is-under .hull { fill: var(--go) }
.gps-boat.is-idle  .hull { fill: var(--idle) }
.gps-boat.is-off   .hull { fill: var(--gone) }

/* THE GLOW SAYS "NOW".
   A drop-shadow in her own colour, sitting under the black one that
   keeps her legible. It is on only while the page is actually hearing
   from her -- switch it off and the mark goes visibly cold, which is
   the entire signal. */
.gps-boat svg { filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .5)) }
.gps-boat.is-under svg {
  filter: drop-shadow(0 0 7px color-mix(in srgb, #1FA971 85%, transparent))
          drop-shadow(0 1px 3px rgba(0, 0, 0, .5));
}
.gps-boat.is-idle svg {
  filter: drop-shadow(0 0 6px color-mix(in srgb, #E2A33C 75%, transparent))
          drop-shadow(0 1px 3px rgba(0, 0, 0, .5));
}

/* ---- the ripple -----------------------------------------------------
   Two rings rather than one, staggered half a cycle apart, which is
   what makes it read as a repeating signal going out rather than as a
   shape that grows and snaps back. They are rings, not discs: a filled
   pulse under a 34px mark just makes the mark look bigger and blurrier,
   while an expanding outline is the thing every radar screen and every
   "live" badge has used for the same reason.

   Off, they are not painted at all -- the element stays in the markup
   so nothing has to be rebuilt, and the animation simply stops. */
.gps-ping {
  position: absolute; left: 50%; top: 50%;
  width: 30px; height: 30px; margin: -15px 0 0 -15px;
  border-radius: 50%;
  border: 2px solid currentColor;
  color: transparent;
  opacity: 0;
  z-index: 1;
  pointer-events: none;
}
.gps-boat.is-under .gps-ping { color: var(--go) }
.gps-boat.is-idle  .gps-ping { color: var(--idle) }

.gps-boat.is-under .gps-ping,
.gps-boat.is-idle  .gps-ping {
  animation: gps-ripple 2.6s cubic-bezier(.15, .6, .3, 1) infinite;
}
.gps-boat .gps-ping.b { animation-delay: 1.3s }

/* Idling is a quieter version of the same thing rather than a second
   idea: she is still being heard from, just not going anywhere. */
.gps-boat.is-idle .gps-ping { animation-duration: 3.6s; opacity: 0 }
.gps-boat.is-idle .gps-ping.b { animation-delay: 1.8s }

@keyframes gps-ripple {
  0%   { transform: scale(.5);  opacity: .8 }
  75%  { transform: scale(2.4); opacity: 0 }
  100% { transform: scale(2.4); opacity: 0 }
}

/* Somebody who has asked their machine to stop animating has asked for
   this too. The state still has to be readable, so one ring is drawn
   and held rather than removed, and the glow stays. */
@media (prefers-reduced-motion: reduce) {
  .gps-boat .gps-ping { animation: none !important }
  .gps-boat.is-under .gps-ping.a,
  .gps-boat.is-idle  .gps-ping.a { opacity: .45; transform: scale(1.6) }
  .gps-boat .gps-ping.b { opacity: 0 }
}


/* ---- the trace, and its scale ---------------------------------------
   The line is drawn once per speed band, over one casing. The casing is
   not decoration: the ramp runs to a light yellow at the top, and the
   light end of any ramp disappears on a pale street tile. With a dark
   line under it the colour can be chosen for the data rather than for
   whatever the map happens to be doing underneath. */
.gps-map .gps-trail-case {
  stroke: rgba(20, 16, 14, .9);
  stroke-linecap: round;
  stroke-linejoin: round;
}

.gps-scale {
  position: absolute;
  left: 16px; bottom: 34px;
  z-index: 5;
  display: grid; gap: 5px;
  padding: 9px 11px 8px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(10px);
  border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .16);
}

.gps-scale-bar {
  display: block;
  width: 128px; height: 9px;
  border-radius: 3px;
  /* The same hairline the marks get, so the bar reads as a sample of
     the line rather than as a coloured box. */
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 18%, transparent);
}

/* The two ends of the scale, under the bar they belong to. Only the
   ends: a tick every ten would be a chart axis, and this is a key. */
/* One phrase under the bar -- "0 - 51 kn" -- rather than a number at
   each end. It is a key, not an axis, and the reference reads the same
   way. */
.gps-scale-ends {
  font: 500 11px/1 "Lexend", system-ui, sans-serif;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  text-align: center;
}
.gps-scale-ends i { font-style: normal }


/* ---- IS THE CHART STILL BEING FED ----------------------------------
   The Status row says how old the FIX is, which is a fact about the
   boat. This is a fact about the PAGE: a phone that has lost the feed
   goes on showing the last position it had, and until this existed the
   only tell was a number that quietly stopped going up.

   One dot, bottom left beside the scale. Green and breathing while the
   feed is answering; amber and still when it is not. Deliberately
   small: it is reassurance, and reassurance that takes up room becomes
   an alarm. */
.gps-live {
  position: absolute;
  left: 14px; bottom: 14px;
  z-index: 5;
  display: flex; align-items: center; gap: 6px;
  font: 500 11px/1 "Lexend", system-ui, sans-serif;
  color: var(--muted);
  pointer-events: none;
}
.gps-live::before {
  content: "";
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--go, #16a34a);
  animation: gps-breathe 2.4s ease-in-out infinite;
}
.gps-live.off::before { background: var(--wa, #d97706); animation: none }
.gps-live.off b::after { content: "reconnecting" }
.gps-live b { font-weight: 500 }
.gps-live.off b { color: var(--wa, #d97706) }
@keyframes gps-breathe { 0%,100% { opacity: 1 } 50% { opacity: .35 } }
@media (prefers-reduced-motion: reduce) {
  .gps-live::before { animation: none }
}

/* ---- THE ARCHIVE DRAWER --------------------------------------------
   A day out of the permanent record, drawn on the same chart with the
   same speed ramp.

   IT IS THE TOP OF THE BOTTOM-RIGHT STACK, and it did not used to be.
   It floated in the opposite corner and shoved the chart controls
   sideways to make room -- which left Street, Satellite, Trace and
   History adrift in the middle of the map, at neither the edge they
   belong to nor anywhere else meaningful. Now the panel and the button
   that opens it are one column growing up from one corner: the control
   never moves, and the thing it opens appears directly above it where
   the eye already is.

   On a phone it becomes a sheet across the bottom instead. A 330px
   column on a 390px screen is a page with a map behind it, and the map
   is the point. */
.gps-hist {
  width: 330px;
  max-width: 100%;
  /* Up to the header and no further. The stack is anchored at the
     bottom, so this is what stops a long list of days from growing off
     the top of the window. */
  max-height: calc(100dvh - var(--header-h, 96px) - 190px);
  display: flex; flex-direction: column;
  overflow: hidden;
  border-radius: 14px;
  background: color-mix(in srgb, var(--bg) 94%, transparent);
  backdrop-filter: blur(12px);
  border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  box-shadow: 0 10px 34px rgba(0, 0, 0, .2);
  text-align: left;
}

.gps-hist-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 12px 12px 8px;
}
.gps-hist-top h2 {
  margin: 0;
  font: 600 14px/1.2 "Bricolage Grotesque", "Lexend", system-ui, sans-serif;
  color: var(--ink);
}
.gps-hist-x {
  border: 0; background: transparent; cursor: pointer;
  color: var(--muted); font: 400 22px/1 "Lexend", system-ui, sans-serif;
  padding: 0 4px;
}
.gps-hist-x:hover { color: var(--ink) }

.gps-hist-say {
  margin: 0; padding: 0 12px 10px;
  font: 400 11.5px/1.45 "Lexend", system-ui, sans-serif;
  color: var(--muted);
}
.gps-hist-say:empty { display: none }
.gps-hist-say.bad { color: var(--wa, #d97706) }

.gps-hist-body {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 0 12px 12px;
}

.gps-hist-range {
  display: grid; grid-template-columns: 1fr 1fr auto; gap: 6px;
  align-items: end;
  padding-bottom: 10px;
  border-bottom: 1px dashed color-mix(in srgb, var(--ink) 14%, transparent);
}
.gps-hist-range label {
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
  font: 500 10.5px/1 "Lexend", system-ui, sans-serif; color: var(--muted);
}
.gps-hist-range input {
  min-width: 0; width: 100%;
  font: 400 11.5px/1 "Lexend", system-ui, sans-serif;
  color: var(--ink); background: var(--bg);
  border: 1px solid color-mix(in srgb, var(--ink) 18%, transparent);
  border-radius: 8px; padding: 7px 6px;
}

/* ---- THE DAYS THAT EXIST -------------------------------------------
   Not a month grid. A month grid offers thirty days that mostly do not
   exist and hides which of them do; this lists the ones with something
   in them and says how much, so the shape of the fleet's month is read
   off the panel without a single click. */
.gps-hist-days { display: flex; flex-direction: column; gap: 4px; padding: 10px 0 }
.gps-hist-day {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 8px;
  width: 100%; text-align: left; cursor: pointer;
  font: 400 11.5px/1.35 "Lexend", system-ui, sans-serif;
  color: var(--ink); background: transparent;
  border: 1px solid transparent; border-radius: 9px;
  padding: 7px 8px;
}
.gps-hist-day:hover { background: color-mix(in srgb, var(--ink) 5%, transparent) }
.gps-hist-day.on {
  background: color-mix(in srgb, #2563eb 10%, transparent);
  border-color: color-mix(in srgb, #2563eb 40%, transparent);
}
.gps-hist-day b { font-weight: 550 }
.gps-hist-day i {
  font-style: normal; color: var(--muted);
  font-variant-numeric: tabular-nums; font-size: 11px;
}
.gps-hist-day .sub { grid-column: 1 / -1; color: var(--muted); font-size: 10.5px }

.gps-hist-sum {
  border-top: 1px dashed color-mix(in srgb, var(--ink) 14%, transparent);
  padding-top: 10px;
}
.gps-hist-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px;
}
.gps-hist-grid div { display: flex; flex-direction: column; gap: 1px; min-width: 0 }
.gps-hist-grid b {
  font: 550 13px/1.2 "Lexend", system-ui, sans-serif;
  color: var(--ink); font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.gps-hist-grid span {
  font: 400 10px/1 "Lexend", system-ui, sans-serif;
  color: var(--muted); text-transform: uppercase; letter-spacing: .04em;
}

.gps-hist-scrub { padding: 12px 0 2px }
.gps-hist-scrub input { width: 100%; accent-color: #2563eb }
.gps-hist-at {
  font: 500 11.5px/1.3 "Lexend", system-ui, sans-serif;
  color: var(--muted); font-variant-numeric: tabular-nums;
  min-height: 15px;
}

.gps-hist-out { display: flex; flex-wrap: wrap; gap: 6px; padding-top: 12px }
.gps-hist-out button,
.gps-hist-range button {
  font: 550 11px/1 "Lexend", system-ui, sans-serif;
  color: var(--ink); background: var(--bg);
  border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
  border-radius: 8px; padding: 8px 10px; cursor: pointer;
}
.gps-hist-out button:hover,
.gps-hist-range button:hover { background: color-mix(in srgb, var(--ink) 8%, var(--bg)) }
#gps-hist-live { margin-left: auto; color: #2563eb;
                 border-color: color-mix(in srgb, #2563eb 45%, transparent) }

/* The two ends of the passage, and the mark the scrubber walks. Drawn
   as divIcons so they scale with nothing and stay legible on a dark
   satellite tile. */
.gps-hist-pin {
  width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .5);
}
.gps-hist-pin.start { background: #16a34a }
.gps-hist-pin.end   { background: #dc2626 }
/* THE BOAT AT THE HEAD OF A PAST PASSAGE.
   The same hull the live chart draws, so the two screens are plainly
   about the same object -- but a size down and with the glow taken
   off. The glow on the live mark means "we are hearing from her right
   now", and a fix from March is the one thing it must never say. A
   passage being replayed is a drawing, not a signal. */
.gps-hist-boat span { width: 30px; height: 30px }
.gps-hist-boat svg { width: 30px; height: 30px }
.gps-hist-boat.is-under svg,
.gps-hist-boat.is-idle svg,
.gps-hist-boat svg { filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .55)) }
/* Nor does it turn slowly into place: the live mark eases because she
   really is turning, and a scrubber dragged across an afternoon would
   leave the hull permanently a second behind the hand. */
.gps-hist-boat span { transition: none }

/* A sheet on a phone. The chart is what somebody is here for, so the
   panel takes the bottom half and no more, and the controls come up
   above it rather than under it. */
@media (max-width: 640px) {
  .gps-page.has-hist .gps-controls { left: 10px; right: 10px; bottom: 10px }
  .gps-hist {
    width: 100%;
    max-height: 58dvh;
    border-radius: 16px;
  }
  /* The stack still owns the corner; the sheet is the whole width of
     it, and the scale and the live dot step out of the way rather than
     being covered by it. */
  .gps-page.has-hist .gps-scale,
  .gps-page.has-hist .gps-live { display: none }
}

/* See WHAT A FINGER CAN ACTUALLY HIT at the foot of magik.css.
   This page is the one on the list explicitly meant to be opened on a
   phone standing on a pontoon, and it is the one whose controls were
   smallest: Leaflet ships a 30px zoom, and Street/Satellite and Trace
   are 26 and 31 tall. Missing any of them on a chart pans it.
   `.gps-map` on the front of the Leaflet selectors because Leaflet's own
   sheet is injected at runtime by leaflet-load.js and therefore lands
   after this file -- a bare `.leaflet-bar a` here would lose to it. */
@media (hover:none) and (pointer:coarse){
  /* `.gps-map.leaflet-touch`, both on the same element and both needed.
     Leaflet's own rule is `.leaflet-touch .leaflet-bar a` -- two classes
     and an element, the same weight as `.gps-map .leaflet-bar a`, and its
     sheet arrives after this one, so a tie goes to Leaflet and the button
     stays 30px. Naming the second class on the container breaks the tie
     on specificity rather than on load order, which is the only one of
     the two this file controls. */
  .gps-map.leaflet-touch .leaflet-bar a{width:44px; height:44px; line-height:44px}
  .gps-seg button{min-height:44px; padding:7px 14px}
  .gps-toggle{min-height:44px}
  /* The archive drawer is opened standing on a pontoon like everything
     else on this page, and a day in a list is the smallest target on
     it. */
  .gps-hist-day{min-height:44px}
  .gps-hist-out button,
  .gps-hist-range button{min-height:44px}
  .gps-hist-x{min-width:44px; min-height:44px}
  .gps-hist-scrub input{height:32px}
}

/* ---- THE FLEET, NAMED ON THE CHART ---------------------------------
   One boat needed no label: there was nothing to tell her apart from,
   and her name was in the panel behind her mark. A fleet does. Seven
   hulls in one bay, each one the same 34px shape, and finding the one
   you came for means hovering every mark in turn -- which is the whole
   job the chart was supposed to do.

   A label, not a tooltip: permanent, beside the hull, and clickable,
   because it is a bigger target than the boat and it is what somebody
   aims at once they already know which boat they want. Leaflet builds
   it in its own pane, so the page's tokens do not reach it and the
   colours are named here. */
.gps-tag {
  background: rgba(255, 255, 255, .92);
  color: #241E1B;
  border: none;
  border-radius: 999px;
  box-shadow: 0 1px 3px -1px rgba(0, 0, 0, .35), 0 6px 14px -8px rgba(0, 0, 0, .5);
  padding: 2px 9px;
  font: 500 12px/1.35 "Lexend", system-ui, sans-serif;
  white-space: nowrap;
  cursor: pointer;
  backdrop-filter: blur(3px);
}
.gps-tag::before { display: none }          /* Leaflet's little arrow */
.gps-tag:hover { background: #fff }

/* WHICH ONE THE PANEL IS ABOUT. The read-out is a single card behind a
   single mark, so on a chart of seven the selected hull has to be
   findable without opening anything -- otherwise the numbers in the
   panel belong to a boat the reader cannot point at. A ring, not a
   colour: the colour already means whether she is reporting, and that
   is the more important of the two. */
.gps-boat.is-sel::after {
  content: "";
  position: absolute; inset: -6px;
  border-radius: 50%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1.5px rgba(0, 0, 0, .45), inset 0 0 0 1.5px rgba(0, 0, 0, .25);
  pointer-events: none;
  z-index: 1;
}
.leaflet-marker-icon.gps-boat.is-sel { z-index: 600 !important }
