/* Design tokens, taken from DispatchApp's Geofence*.xaml. Scoped to .gf-root rather than :root
   so this stylesheet can never restyle the host app around it -- both hosts have their own
   established look, and this feature is a dark panel inside them. */
.gf-root {
  /* Three independent numbers, all in one place:
     --gf-panel-size is the WHOLE panel's height, identical across all three tabs (Activity,
     Adjust pin/radius, Change history) -- it's a plain min/max-height on .gf-body below, which is
     the one container element shared by every tab, so setting it here is enough on its own; no JS
     measurement needed.
     --gf-map-size caps the map's square width/height on Activity; --gf-list-size caps the
     check-in list's height on Activity -- both independent of the panel and of each other. If
     either pushes Activity's own content taller than --gf-panel-size, that column just scrolls
     inside the fixed-height panel (.gf-body's overflow:auto) rather than growing the panel.
     No rebuild needed to preview -- editing this file and refreshing the browser is enough in dev. */
  --gf-panel-size: 830px;
  --gf-map-size: 700px;
  --gf-list-size: 640px;

  --gf-page: #15100F;
  --gf-panel: #1D1817;
  --gf-field: #241E1C;
  --gf-raised: #2A2422;
  --gf-track: #332A28;
  --gf-border: #3A312E;
  --gf-border-strong: #4A403D;

  --gf-text: #F2EDEB;
  --gf-text-high: #E8E0DE;
  --gf-text-2: #C0B5B3;
  --gf-muted: #8D8280;
  --gf-dim: #6B6260;

  /* Indigo is the pin/primary colour; amber is radius; green is on/allowed AND the no-location
     mode (explicit choice -- reused rather than given its own hue). */
  --gf-indigo: #5D6EE1;
  --gf-indigo-light: #A7B2F5;
  --gf-indigo-tint: rgba(93,110,225,0.10);
  --gf-amber: #ECB469;
  --gf-amber-tint: rgba(236,180,105,0.16);
  /* A DISTINCT amber from --gf-amber (radius-change color) -- the no-location "Flag for review"
     icon uses this specific shade on desktop; conflating the two ambers was flagged as a real
     divergence during the web-parity gap analysis. */
  --gf-flag-amber: #E8A33D;
  --gf-flag-amber-tint: rgba(232,163,61,0.16);
  --gf-amber-tint-strong: rgba(236,180,105,0.30);
  --gf-green: #3ED598;
  --gf-green-tint: rgba(62,213,152,0.14);
  --gf-allowed: #6EE2A0;
  --gf-green-track: #1C3A2E;
  /* Matches the desktop's own "🕐 Change history" button color exactly. */
  --gf-teal: #2DC5D0;
  --gf-teal-tint: rgba(45,197,208,0.10);
  --gf-red: #FF8A91;
  --gf-red-hover: #FF9AA0;
  /* A separate, deeper red from the pair above -- the desktop uses this specific shade only for
     the "current radius" map circle and the Settings Save button, never for chips/dots, which
     stay on the lighter --gf-red/--gf-red-hover pair. Two visually-close but distinct reds by
     deliberate design, not an inconsistency to merge. */
  --gf-red-deep: #F0424E;
  --gf-red-deep-tint: rgba(240,66,78,0.10);

  color: var(--gf-text);
  background: var(--gf-page);

  /* These are back-office screens, not driver screens. Below this the map stops being usable, so
     the page scrolls horizontally rather than collapsing into an unusable layout. Phone widths
     are explicitly not a target -- see the spec's Out of scope. */
  min-width: 980px;

  font-size: 13px;
  display: flex;
  flex-direction: column;
  min-height: 100%;

  /* Sharp corners on the whole panel read as cheap/unfinished. overflow:hidden so the tab bar's
     own background (flush against the top edge) actually gets clipped to match, rather than
     leaving square corners peeking out from behind a rounded outline. */
  border-radius: 12px;
  overflow: hidden;
}

/* Light theme -- DispatchWebApp defaults to this (Radzen ThemeService's "material" theme,
   reflected as data-theme="material" on <html>); the block above stays the effective look under
   data-theme="material-dark". tdLoopSysAdmin shares this same data-theme convention and will
   follow along automatically -- confirmed acceptable, not gated behind a per-host flag (unlike
   ShowGpsAccuracyToggle/UseEnhancedDriverPill, which exist for unrelated reasons).
   "Crisp Light + Bold Colors" -- round 2 of live brightness/palette review (near-white, then
   Dimmed Blue-Gray, then Warm Paper were all tried and rejected). Different hypothesis this time:
   the problem wasn't brightness alone, it was soft/washed-out pastel tints reading as murky.
   Background stays light, but every accent hue is bold/saturated, and selection states below get
   an explicit solid border instead of relying on a translucent tint fill. */
html[data-theme='material'] .gf-root {
  --gf-page: #f2f4f7;
  --gf-panel: #ffffff;
  --gf-field: #eef1f5;
  --gf-raised: #eef1f5;
  --gf-track: #e3e7ed;
  --gf-border: #c7cedb;
  --gf-border-strong: #a7b1c2;

  --gf-text: #10151c;
  --gf-text-high: #05080d;
  --gf-text-2: #2f3742;
  --gf-muted: #4b5563;
  --gf-dim: #7c8794;

  --gf-indigo: #3730d6;
  --gf-indigo-light: #3730d6;
  --gf-indigo-tint: rgba(55,48,214,0.10);
  --gf-amber: #b45f06;
  --gf-amber-tint: rgba(180,95,6,0.14);
  /* Restores the two ambers' original distinctness (see the dark-mode comment on --gf-flag-amber
     above) -- collapsing them to one shade in the light theme read as "brown" rather than
     "orange" for the flag icon specifically. */
  --gf-flag-amber: #e8790d;
  --gf-flag-amber-tint: rgba(232,121,13,0.18);
  --gf-amber-tint-strong: rgba(180,95,6,0.28);
  --gf-green: #0f8a5f;
  --gf-green-tint: rgba(15,138,95,0.14);
  --gf-allowed: #1b8f3f;
  --gf-green-track: #d7f0e3;
  --gf-teal: #0a7ea4;
  --gf-teal-tint: rgba(10,126,164,0.14);
  --gf-red: #d21f1f;
  --gf-red-hover: #d21f1f;
  --gf-red-deep: #d21f1f;
  --gf-red-deep-tint: rgba(210,31,31,0.12);
}

/* "Real borders instead of soft tints": the two selection/CTA elements that most read as murky
   in earlier rounds get an explicit 1.5-2px solid accent border, rather than depending on
   --gf-indigo-tint alone to read as "selected"/"primary". Both the unselected AND selected
   no-location cards also get an explicit darker-gray/indigo-gray background here (not the
   near-white --gf-field/--gf-panel the base rule uses) -- on their own, against a light page,
   both states read as "just white" with no visual cue that they're an option at all. */
html[data-theme='material'] .gf-card {
  background: #d8dee6;
}
html[data-theme='material'] .gf-card:hover {
  background: #cfd6e0;
}
html[data-theme='material'] .gf-card.is-on {
  background: #dfe3f5; border-width: 2px;
}
html[data-theme='material'] .gf-back-pill {
  background: var(--gf-panel); border: 1.5px solid var(--gf-indigo); color: var(--gf-indigo);
}
html[data-theme='material'] .gf-jump-pin {
  background: var(--gf-panel); border: 1.5px solid var(--gf-indigo); color: var(--gf-indigo);
}

/* History change-type icons -- "Solid Filled Badge" (light mode only), same treatment as the
   Activity row icon and picked in the same live review: bold solid accent fill, white glyph,
   instead of a tinted circle. .gf-icon-circle--radius/--pin/--noloc/--other are on the SAME
   markup used for both a solo icon and a segment of the combined pill (IconGroup renders one set
   of spans; .gf-icons--combined only reshapes their corner-radius/dividers), so this rule covers
   both automatically -- no separate "combined" styling needed. !important on background because
   each span still carries a literal tint inline (dark mode's own look, left unchanged). */
html[data-theme='material'] .gf-icon-circle {
  border-width: 0;
}
html[data-theme='material'] .gf-icon-circle--radius { background: var(--gf-amber) !important; }
html[data-theme='material'] .gf-icon-circle--pin { background: var(--gf-indigo) !important; }
html[data-theme='material'] .gf-icon-circle--noloc { background: var(--gf-green) !important; }
html[data-theme='material'] .gf-icon-circle--other { background: var(--gf-muted) !important; }
html[data-theme='material'] .gf-icon-circle svg {
  stroke: #fff;
  stroke-width: 2.4;
}

/* "Show" filter checkboxes (History sidebar) -- same Solid Filled Badge treatment as the icon
   circles above. Previously these had their own separate hardcoded dark-mode-only stroke hex
   (#A7B2F5/#ECB469/#3ED598) and pastel tint backgrounds, missed by the earlier icon-circle pass
   since they're a distinct markup block, not IconGroup. No !important needed: the inline
   background style was removed from the markup, so the class wins outright now. */
html[data-theme='material'] .gf-check-icon--pin { background: var(--gf-indigo); color: #fff; }
html[data-theme='material'] .gf-check-icon--radius { background: var(--gf-amber); color: #fff; }
html[data-theme='material'] .gf-check-icon--noloc { background: var(--gf-green); color: #fff; }
/* No blanket dimming for the off state -- full opacity in both states, same "no opacity, use a
   real border/background instead" fix already applied to the desktop's chips and day-range
   buttons after the same "too faint when off" complaint. is-on gets a light indigo highlight
   instead so the toggle state is still obvious. */
html[data-theme='material'] .gf-check {
  opacity: 1;
  border-radius: 6px;
  padding: 5px 8px;
}
html[data-theme='material'] .gf-check.is-on {
  background: var(--gf-field);
  font-weight: 600;
}

/* Activity row icon -- "Solid Filled Badge" (light mode only): a bold solid accent fill with a
   white glyph, picked after the tinted-circle-plus-thin-ring version was reported as looking
   cheap. !important on background because the row itself still sets a literal per-outcome tint
   inline (IconBgCss, an inline style -- higher specificity than a class -- left unchanged so dark
   mode keeps its original tinted-circle look untouched). */
html[data-theme='material'] .gf-row-icon {
  border-width: 0;
}
html[data-theme='material'] .gf-row-icon--allowed { background: var(--gf-allowed) !important; }
html[data-theme='material'] .gf-row-icon--flagged { background: var(--gf-flag-amber) !important; }
html[data-theme='material'] .gf-row-icon--toofar { background: var(--gf-red) !important; }
html[data-theme='material'] .gf-row-icon svg {
  stroke: #fff;
  stroke-width: 2.6;
}

/* --- tabs --- */
.gf-tabs { display: flex; align-items: center; gap: 4px; border-bottom: 1px solid var(--gf-border);
  padding: 0 16px; background: var(--gf-panel); }
.gf-tab { display: inline-flex; align-items: center; gap: 6px; background: none; border: 0;
  border-bottom: 2px solid transparent; color: var(--gf-muted);
  font-size: 13px; padding: 12px 14px; cursor: pointer; }
.gf-tab:hover { color: var(--gf-text-2); }
.gf-tab.is-active { font-weight: 600; }

/* Each tab lights up in its own color when active, rather than all three converging on one
   generic indigo underline. */
.gf-tab--activity.is-active { color: var(--gf-green); border-bottom-color: var(--gf-green); }
.gf-tab--settings.is-active { color: var(--gf-indigo-light); border-bottom-color: var(--gf-indigo); }
.gf-tab--history.is-active { color: var(--gf-teal); border-bottom-color: var(--gf-teal); }

.gf-tabs-spacer { flex: 1; }
/* One merged pill (warehouse name + back arrow + "Back") instead of a separate label and link --
   picked over a plain breadcrumb and an icon-badge-plus-circular-button treatment. Same
   glass-chip family as .gf-jump-pin so the header and map top bar read as one visual language. */
/* No background/border at all, not even a square box -- plain white text reads as a readout, not
   a control, which is the whole point: Back (a pill, with its own indigo glass background) is the
   only thing here that looks clickable. */
.gf-wh-badge { display: inline-flex; align-items: center; gap: 6px;
  color: #fff; font-size: 12px; font-weight: 600; padding: 6px 4px; margin: 8px 0; }
.gf-back-pill { display: inline-flex; align-items: center; gap: 6px; text-decoration: none;
  background: var(--gf-indigo-tint); border: 1px solid rgba(167,178,245,0.45); border-radius: 999px;
  color: #fff; font-size: 12px; font-weight: 600; padding: 6px 12px; margin: 8px 0; }
.gf-back-pill:hover { background: rgba(93,110,225,0.18); }

.gf-body { flex: 1; padding: 16px; overflow: auto; min-height: var(--gf-panel-size); max-height: var(--gf-panel-size); }

/* --- map ---
   Flush, square, borderless, matching the desktop. Kept for consistency, not necessity: the WPF
   limitation that forced it (a WebView2 HWND cannot be clipped) has no browser equivalent. */
/* width:100% is load-bearing, not decorative: with width left auto, the aspect-ratio + max-height
   combo on .gf-map--wide makes browsers shrink the WIDTH to match the capped height instead of
   filling the container (a real CSS interaction, not a browser bug) -- leaving a gap on one side
   that the earlier ResizeObserver fix (a JS-side issue) didn't touch, because the box itself
   really was narrower than its container this time. */
/* Rounded despite being an iframe: overflow:hidden already clips it to this box, so a radius
   clips the map tiles along with it -- the WPF/WebView2 limitation that forced the desktop map
   to stay a hard rectangle (a native window handle WPF can't clip) has no browser equivalent. */
.gf-map { position: relative; width: 100%; background: var(--gf-field); overflow: hidden;
  border-radius: 20px; }
.gf-map > iframe { display: block; width: 100%; height: 100%; border: 0; }
/* Capped (per the panel-height tuner artifact) rather than left to grow with the column's full
   width -- uncapped, it could get large enough on a wide monitor to make the whole panel (all
   three tabs, once pinned to match it) awkwardly tall. Centered since it no longer always fills
   the column's width. */
.gf-map--square { aspect-ratio: 1 / 1; max-width: var(--gf-map-size); margin: 0 auto; }

/* The Settings ("Adjust pin/radius") map, unlike Activity's, is wide and short on the desktop --
   forcing it square here was making the whole screen far taller than intended, since a wide
   window makes a 1:1 map enormous. */
.gf-map--wide { aspect-ratio: 2.8 / 1; max-height: 480px; }

/* Top/bottom bars framing the map -- matching the desktop's actual layout (GeofenceActivity.xaml:
   a 3-row Grid, top bar / map / bottom bar), not an overlay floating on top of map tiles. The
   WebView2 the desktop uses cannot be overlaid at all (a native HWND, called out in its own XAML
   comments) -- everything there that LOOKS like it floats on the map is really a row directly
   above or below it, and this reproduces that same structure rather than a browser-only overlay
   trick the desktop never had. */
.gf-map-topbar { display: flex; align-items: center; justify-content: space-between;
  margin: 0 0 10px; }
.gf-map-bottombar { display: flex; align-items: center; gap: 10px; margin: 10px 0 0;
  flex-wrap: wrap; }
.gf-toggle-label { color: var(--gf-muted); font-size: 10px; font-weight: 600;
  letter-spacing: .06em; }

.gf-fence-readout { display: flex; align-items: center; gap: 8px; }
.gf-fence-label { color: var(--gf-muted); font-size: 10px; }
.gf-fence-value { color: var(--gf-text); font-size: 16px; font-weight: 700; }

/* Buttons here brighten on hover/press via a white sheen laid under the content, rather than
   swapping to a flat hover color -- matching the desktop's DarkPanelButton mechanic, so a button
   keeps its own base color (dark neutral, or a tinted one like Settings' Save) instead of every
   button converging on one generic "hovered" look. */
/* Glass chip -- translucent indigo tint + blur instead of the old flat dark panel, with an
   indigo outline instead of the generic --gf-track border everything else uses. Picked over a
   solid indigo pill or an icon-only FAB as the more modern option that still reads as "part of
   this panel" rather than a floating app-chrome control. */
.gf-jump-pin { position: relative; display: inline-flex; align-items: center; gap: 7px;
  cursor: pointer; overflow: hidden; background: var(--gf-indigo-tint);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(167,178,245,0.45); color: var(--gf-indigo-light);
  border-radius: 8px; padding: 6px 12px; font-size: 13px; font-weight: 600; }
.gf-jump-pin::after { content: ''; position: absolute; inset: 0; background: #fff; opacity: 0;
  transition: opacity .1s ease; pointer-events: none; }
.gf-jump-pin:hover::after { opacity: .10; }
.gf-jump-pin:active::after { opacity: .18; }

/* --- status / errors --- */
.gf-error { color: var(--gf-red); font-size: 12.5px; }
.gf-badge { display: inline-block; border-radius: 999px; padding: 2px 8px; font-size: 10px;
  font-weight: 600; letter-spacing: .04em; }
.gf-badge--active { background: var(--gf-green-track); color: var(--gf-green); }
.gf-badge--off { background: var(--gf-field); color: var(--gf-muted); }

/* --- buttons ---
   Revert is transparent/borderless (a plain muted-text action), Save is red -- matching the
   "current radius" circle's own red, not the indigo used for navigation/selection elsewhere.
   Both brighten via a white sheen on hover/press rather than swapping to a flat hover color,
   same DarkPanelButton mechanic as .gf-jump-pin. */
.gf-btn { position: relative; overflow: hidden; background: transparent; border: 1px solid transparent;
  border-radius: 5px; color: var(--gf-muted); font-size: 12.5px; padding: 7px 14px; cursor: pointer; }
.gf-btn::after { content: ''; position: absolute; inset: 0; background: #fff; opacity: 0;
  transition: opacity .1s ease; pointer-events: none; }
.gf-btn:hover:not(:disabled)::after { opacity: .10; }
.gf-btn:active:not(:disabled)::after { opacity: .18; }
.gf-btn:disabled { opacity: .45; cursor: default; }
/* The one affirmative action on the Settings screen -- translucent WHITE, not a solid opaque
   fill (tried and rejected: it broke the translucent-pill language every other footer button
   uses) and not red, to stand apart from Revert/Back's "leave" red. */
.gf-btn--save { background: rgba(255,255,255,0.10); border-color: #fff;
  color: #fff; font-weight: 700; padding: 6px 12px; }
/* Revert and Back both "leave the current state behind" (undo edits / leave the screen) -- same
   translucent-red treatment for both. */
.gf-btn--leave { background: var(--gf-red-deep-tint); border-color: var(--gf-red-deep);
  color: var(--gf-red-hover); font-weight: 700; padding: 6px 12px; }

/* Light mode: the dark palette's translucent-white Save pill (white-on-near-black) becomes
   white-on-near-white and disappears entirely against a light panel. Solid indigo instead --
   this app's own primary accent -- keeps it as the one clearly affirmative action. Back's pale
   indigo-tint pill also needs its text color inverted for the same reason: white text on a
   pale-lavender-on-white background reads as nearly blank. */
html[data-theme='material'] .gf-btn--save {
  background: var(--gf-indigo); border-color: var(--gf-indigo); color: #fff;
}
html[data-theme='material'] .gf-back-pill {
  color: var(--gf-indigo);
}
/* Plain readout text, not a control (see its own comment above) -- neutral dark, not indigo,
   so it doesn't start looking clickable like the Back pill next to it. */
html[data-theme='material'] .gf-wh-badge {
  color: var(--gf-text);
}

/* --- the thin rounded scrollbar ---
   On the desktop this needed a full ScrollBar ControlTemplate. Here it is six lines. */
.gf-scroll { scrollbar-width: thin; scrollbar-color: var(--gf-border-strong) transparent; }
.gf-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
.gf-scroll::-webkit-scrollbar-track { background: transparent; }
.gf-scroll::-webkit-scrollbar-thumb { background: var(--gf-border-strong); border-radius: 4px; }
.gf-scroll::-webkit-scrollbar-thumb:hover { background: var(--gf-muted); }
.gf-scroll::-webkit-scrollbar-button { display: none; }

/* --- Adjust pin/radius ---
   Map on top (full width) / settings body below / footer bar at the bottom -- matching the
   desktop's actual 3-row layout exactly (GeofenceSettings.xaml). An earlier pass put the map and
   fields side by side instead, which squeezed the map into roughly half the available width and
   made the whole screen read as oversized relative to how little map was actually visible. */
.gf-settings { display: flex; flex-direction: column; gap: 20px; }
.gf-settings-map { width: 100%; }

.gf-settings-body { display: flex; flex-direction: column; gap: 16px; }
.gf-settings-caption { color: var(--gf-dim); font-size: 12px; max-width: 320px; margin: 16px 0 0; }

/* Badge (row's left edge), radius group (centered on the 25% mark), and no-location group
   (centered on the 75% mark) all share the same top edge, one row -- from the user's own mockup.
   Earlier passes gave the badge its own row above everything (pure vertical air once the other
   two groups moved up to meet it), and used a fixed/growing gutter between just the two groups
   before that; neither used the row's middle or its top edge the way this does. min-height keeps
   the row's own height stable regardless of which absolutely-positioned child is tallest. */
.gf-settings-row { position: relative; min-height: 230px; padding-top: 4px; }
.gf-settings-badge { position: absolute; top: 4px; left: 0; }
.gf-settings-radius { position: absolute; top: 0; left: 25%; transform: translateX(-50%); }
.gf-settings-noloc { position: absolute; top: 0; left: 75%; transform: translateX(-50%); }

.gf-settings-footer { display: flex; align-items: center; justify-content: space-between;
  background: var(--gf-panel); border-radius: 8px; padding: 10px 14px; }

/* Font size deliberately not set here -- the desktop uses two different sizes for its two
   section headers (GeofenceSettings.xaml: 10px for "CHECK-IN RADIUS", 11.5px for "IF THE PHONE
   GIVES NO LOCATION"). --sm/--md below supply the size per header. */
.gf-section-label { color: var(--gf-muted); font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; margin-bottom: 10px; }
.gf-section-label--sm { font-size: 10px; }
.gf-section-label--md { font-size: 11.5px; }

/* radius hero -- bumped up a step (30px->34px value, 220px->240px slider) per the user's own
   mockup ("make it a little bigger"), now that it has real room of its own at the 25% mark
   instead of competing for width with the no-location group beside it. */
.gf-hero { display: flex; align-items: baseline; gap: 11px; }
.gf-hero-value { font-size: 34px; font-weight: 600; color: var(--gf-text); line-height: 1; }
.gf-hero-unit { font-size: 17px; color: var(--gf-muted); }
.gf-hero-feet { font-size: 13px; color: var(--gf-muted); margin-top: 7px; }
.gf-hero-row { display: flex; align-items: center; gap: 15px; margin-top: 14px; }

.gf-slider { -webkit-appearance: none; appearance: none; width: 240px; height: 4px;
  border-radius: 2px; background: var(--gf-track); outline: none; }
.gf-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 17px; height: 17px;
  border-radius: 50%; background: var(--gf-indigo); border: 1.5px solid var(--gf-indigo-light);
  cursor: pointer; }
.gf-slider::-moz-range-thumb { width: 17px; height: 17px; border-radius: 50%;
  background: var(--gf-indigo); border: 1.5px solid var(--gf-indigo-light); cursor: pointer; }

/* Session on/off switch. Bound to IsEnabled, which is DERIVED from RadiusMi > 0 -- there is no
   stored flag, so dragging the slider up from 0 flips this on by itself. */
.gf-switch { width: 42px; height: 24px; border-radius: 12px; background: var(--gf-field);
  border: 1px solid var(--gf-border); position: relative; cursor: pointer; padding: 0;
  flex: 0 0 auto; }
.gf-switch-knob { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
  border-radius: 9px; background: var(--gf-muted); transition: left .12s ease, background .12s ease; }
.gf-switch.is-on { background: var(--gf-green-track); border-color: var(--gf-green); }
.gf-switch.is-on .gf-switch-knob { left: 22px; background: var(--gf-green); }

/* no-location radio cards -- enlarged for older users, per the desktop */
.gf-cards { display: flex; gap: 12px; }
.gf-card { width: 190px; text-align: left; background: var(--gf-field);
  border: 1px solid var(--gf-border); border-radius: 8px; padding: 12px 10px; cursor: pointer; }
.gf-card:hover { border-color: var(--gf-border-strong); }
.gf-card.is-on { border-color: var(--gf-indigo); background: var(--gf-indigo-tint); }
.gf-card-icon { width: 36px; height: 36px; border-radius: 18px; display: flex;
  align-items: center; justify-content: center; margin-bottom: 8px; }
.gf-card-title { font-size: 15px; color: var(--gf-text); margin-bottom: 4px; }
.gf-card-desc { font-size: 12.5px; color: var(--gf-muted); line-height: 1.35; }

.gf-coords { font-family: ui-monospace, Consolas, monospace; font-size: 11px; color: var(--gf-text-2); }
.gf-actions { display: flex; gap: 8px; justify-content: flex-end; }

/* --- Activity --- */
.gf-activity { display: grid; grid-template-columns: minmax(420px, 1fr) minmax(460px, 1fr);
  gap: 16px; align-items: start; }

/* day range header */
.gf-range-eyebrow { color: var(--gf-muted); font-size: 9px; font-weight: 600;
  letter-spacing: .10em; }
.gf-range-row { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.gf-range-text { font-size: 17px; color: var(--gf-text); }
.gf-range-count { font-size: 11px; color: var(--gf-muted); margin-top: 2px; }
.gf-step { background: none; border: 0; color: var(--gf-muted); cursor: pointer;
  font-size: 16px; padding: 0 4px; }
.gf-step:hover:not(:disabled) { color: var(--gf-text); }
.gf-step:disabled { opacity: .3; cursor: default; }

/* All quick-range buttons stay visible with the active one highlighted, so the current view is
   always readable rather than hidden behind a dropdown. */
.gf-quick { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
.gf-quick-btn { background: var(--gf-field); border: 1px solid var(--gf-border);
  border-radius: 999px; color: var(--gf-muted); font-size: 11px; padding: 4px 11px;
  cursor: pointer; }
.gf-quick-btn:hover { color: var(--gf-text-2); }
.gf-quick-btn.is-active { background: var(--gf-indigo-tint); border-color: var(--gf-indigo);
  color: var(--gf-text); font-weight: 600; }

/* month grid -- hand-built, matching the desktop. Six week-rows always, so the grid never
   changes height as you page between months. Opens INLINE, not as a popup: at a readable cell
   size it does not fit in a floating box, and pushing the list down beats covering it. */
.gf-cal { background: var(--gf-panel); border: 1px solid var(--gf-border); border-radius: 8px;
  padding: 10px; margin-top: 10px; width: 266px; }
.gf-cal-head { display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 8px; }
.gf-cal-month { font-size: 12px; color: var(--gf-text); }
.gf-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.gf-cal-dow { font-size: 9px; color: var(--gf-dim); text-align: center; padding-bottom: 4px; }
.gf-cal-day { aspect-ratio: 1; background: none; border: 1px solid transparent;
  border-radius: 5px; color: var(--gf-text-2); font-size: 11px; cursor: pointer; }
.gf-cal-day:hover:not(:disabled) { background: var(--gf-raised); }
.gf-cal-day.is-out { color: var(--gf-dim); }
.gf-cal-day.is-today { border-color: var(--gf-border-strong); }
.gf-cal-day.is-selected { background: var(--gf-indigo); color: #fff; font-weight: 600; }
.gf-cal-day:disabled { opacity: .25; cursor: default; }

/* Pill chips (visibility toggles + the list's filter/count chips) -- matches the desktop's
   FilterChipStyle exactly: off is dim and thin-bordered, on gets a 2px border IN THE CHIP'S OWN
   COLOR plus a soft glow in that same color, off+hover lifts partway there without becoming "on".
   Differing on four axes (border width, border color, glow, opacity) because two near-identical
   treatments were unreadable as on/off in an earlier pass. Each chip supplies its own accent via
   the --chip-color custom property (inline style), which drives both the border and the glow. */
.gf-chips { display: flex; gap: 6px; margin: 12px 0; flex-wrap: wrap; }
.gf-chip { --chip-color: var(--gf-text); display: inline-flex; align-items: center; gap: 6px;
  background: var(--gf-panel); border: 1px solid var(--gf-border-strong); border-radius: 999px;
  font-size: 12px; padding: 4px 10px; cursor: pointer; color: var(--gf-text-2); }
.gf-chip:not(.is-on):hover { color: var(--gf-text); border-color: var(--chip-color); }
.gf-chip.is-on { background: var(--gf-raised); border: 2px solid var(--chip-color);
  padding: 3px 9px; opacity: 1; font-weight: 700; color: var(--gf-text);
  box-shadow: 0 0 10px 0 color-mix(in srgb, var(--chip-color) 55%, transparent); }
.gf-chip-dot { width: 8px; height: 8px; border-radius: 4px; }

/* rows -- bordered cards with a colored left accent bar (the check-in's own outcome color),
   matching the desktop's card treatment (GeofenceActivity.xaml) rather than a flat list row. */
/* max-height is fixed to the locked --gf-list-size token (640px); do not reintroduce JS-driven
   height measurement here. */
.gf-list { overflow-y: auto; max-height: var(--gf-list-size); }
.gf-row { display: flex; align-items: stretch; gap: 12px; padding: 0;
  border-radius: 10px; border: 1px solid var(--gf-border); background: var(--gf-panel);
  margin-bottom: 8px; cursor: pointer; }
.gf-row:hover { border-color: var(--gf-border-strong); }
.gf-row.is-selected { border-color: var(--gf-indigo); background: #221E28;
  box-shadow: 0 0 12px 0 rgba(93,110,225,0.5); }
/* Rounded to match .gf-row's own corners now that .gf-row no longer clips via overflow:hidden --
   overflow:hidden was removed because it also clipped the pin-moved thumbnail popup below. */
.gf-row-accent { width: 4px; flex: 0 0 auto; border-radius: 10px 0 0 10px; }
.gf-row-icon { width: 42px; height: 42px; border-radius: 21px; display: flex; flex: 0 0 auto;
  align-items: center; justify-content: center; align-self: center; margin: 0 0 0 10px;
  border: 0 solid var(--icon-color); box-sizing: border-box; }
.gf-row-main { flex: 1; min-width: 0; padding: 11px 12px 11px 0; }
.gf-row-when { font-size: 15px; color: var(--gf-text); font-weight: 600; }
.gf-row-line2 { display: flex; align-items: center; gap: 8px; margin-top: 3px; }
/* Bolder + higher-contrast than the old muted gray, which read as barely-there next to the
   driver pill and note pills sitting right beside it on the same line. */
.gf-row-meta { font-size: 11.5px; color: var(--gf-text-2); font-weight: 600; }
.gf-row-driver { font-size: 11px; color: var(--gf-indigo-light); background: none; border: 0;
  padding: 0; cursor: pointer; text-align: left; }
.gf-row-driver:disabled { color: var(--gf-dim); cursor: default; }
.gf-row-driver--noid { display: inline-flex; align-items: center; background: var(--gf-field);
  border-radius: 999px; padding: 2px 9px; font-weight: 600; color: var(--gf-text); }
.gf-row-driver--noid:disabled { color: var(--gf-text-2); cursor: default; }
/* 25% bigger again on top of the previous ~14% bump -- driver identity is meant to be the most
   apparent thing on the row. Must come after .gf-row-driver--noid: same specificity, so source
   order decides. */
.gf-row-driver--lg { font-size: 15.6px; padding: 2.9px 12.5px; }
.gf-row-driver--lg svg { width: 18px; height: 18px; }
.gf-row-driver-wrap { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
/* Same pill treatment as .gf-row-driver--noid (white/field-colored pill), scaled down a notch --
   the phone is secondary to the name, not equal to it. */
.gf-row-driver-phone { display: inline-flex; align-items: center; font-size: 11px; font-weight: 600;
  color: var(--gf-text); background: var(--gf-field); border: 0; border-radius: 999px; padding: 2px 9px;
  font-family: ui-monospace, Consolas, monospace; cursor: pointer; }
/* Enhanced driver pill (DispatchWebApp only, gated behind UseEnhancedDriverPill) -- three states
   per the desktop snapshot: identified (clickable, arrow, brightens on hover), unresolved
   (non-interactive, person+X icon, no arrow), and a phone pill a visual step quieter than either. */
.gf-driver-pill { display: inline-flex; align-items: center; gap: 6px; border-radius: 14px;
  border: 1.5px solid var(--gf-text-2); padding: 4px 10px; font-size: 13.75px; font-weight: 700;
  color: var(--gf-text-2); background: transparent;
  box-shadow: 0 0 8px rgba(242,237,235,0.18); }
.gf-driver-pill--id { cursor: pointer; }
.gf-driver-pill--id:hover { color: var(--gf-text); border-color: var(--gf-text);
  background: rgba(255,255,255,0.08); }
.gf-driver-pill--noid { cursor: default; }
.gf-driver-pill-arrow { flex: 0 0 auto; }
/* The person-silhouette icon's corner "X" cutout: a small circle painted in the PAGE background
   color (not transparent) so it reads as a notch cut out of the icon, not an overlapping shape. */
.gf-driver-pill-iconwrap { position: relative; display: inline-flex; flex: 0 0 auto; }
.gf-driver-pill-x { position: absolute; right: -3px; bottom: -3px; width: 9px; height: 9px;
  border-radius: 50%; background: var(--gf-page); display: flex; align-items: center;
  justify-content: center; }
/* A visual step down from .gf-driver-pill: bordered/transparent rather than filled, and the
   dimmer --gf-dim rather than --gf-text-2 -- phone is secondary to the name, not equal to it. */
.gf-driver-pill-phone { display: inline-flex; align-items: center; gap: 4px; font-size: 11px;
  font-weight: 600; color: var(--gf-text-2); border: 1px solid var(--gf-dim);
  border-radius: 12px; padding: 2px 9px; background: transparent;
  font-family: ui-monospace, Consolas, monospace; cursor: pointer; }
.gf-note { display: inline-block; font-size: 9.5px; border-radius: 999px; padding: 1px 7px;
  margin-top: 4px; margin-right: 4px; }
/* Same icon (dashed circle + radius line) as the tooltip's own fence badge (.gf-tip-fence-badge)
   and History's radius-change icon -- one consistent "radius changed" glyph everywhere, not a
   one-off invented for this standalone pill. */
.gf-note--fence { display: inline-flex; align-items: center; background: var(--gf-amber-tint);
  color: var(--gf-amber); }
/* 30% bigger than the base pill -- "No location" and GPS-dist both sit in the row's main
   distance/status slot now, not a secondary corner note, so they read at that weight. */
.gf-note--lg { font-size: 12.4px; padding: 1.3px 9px; }
.gf-note--accuracy { display: inline-flex; align-items: center; background: var(--gf-green-tint);
  color: var(--gf-green); font-weight: 600; white-space: nowrap; margin-top: 0; }
/* Dashed border marks this pill as hoverable (opens the pin-moved/radius-changed tooltip). Covers
   both a pin move and a radius change now -- same color/border either way, only the label differs. */
/* 30% bigger than the base pill, same scale-up as .gf-note--lg -- this is a clickable button
   (opens the pin-moved/radius-changed tooltip and deep-links to history), not a passive status
   note, so it reads at a size that looks tappable. */
.gf-note--pin { background: var(--gf-indigo-tint); color: var(--gf-indigo-light);
  border: 1px dashed var(--gf-indigo); cursor: pointer; font-size: 12.4px; padding: 1.3px 9px; }
/* Same amber as .gf-note--fence, restored per explicit request -- the earlier attempt to give
   "no location" its own distinct yellow read as a bad mustard color, not a helpful distinction. */
/* margin-top: 0, matching .gf-note--accuracy -- without this override it kept the base .gf-note's
   4px margin-top on top of .gf-row-line2's own 3px, so a no-location row sat noticeably farther
   from its date/time line than a GPS-distance row did. */
.gf-note--noloc { background: var(--gf-amber-tint); color: var(--gf-amber); font-weight: 600; margin-top: 0; }

/* thumbnail hover -- FIXED-SIZE slot so swapping loading text for an image can never resize the
   popup while it is on screen (which on the desktop was an actual crash) */
.gf-tip { position: relative; display: inline-block; }
/* Column flex, not the default block flow -- a flex container blockifies every child onto its own
   row regardless of the child's own inline/inline-flex display, so the fence badge can never sit
   beside the coordinate text and stretch the box wider than the 260px image slot below it. */
.gf-tip-pop { position: absolute; bottom: 100%; left: 0; z-index: 200; background: var(--gf-panel);
  border: 1px solid var(--gf-border); border-radius: 8px; padding: 8px; margin-bottom: 6px;
  display: flex; flex-direction: column; align-items: flex-start; }
.gf-tip-slot { width: 260px; height: 140px; background: var(--gf-field); border-radius: 4px;
  display: flex; align-items: center; justify-content: center; overflow: hidden; }
.gf-tip-slot img { width: 260px; height: 140px; object-fit: cover; display: block; }
.gf-tip-status { font-size: 11px; color: var(--gf-muted); }
.gf-tip-text { font-size: 10.5px; color: var(--gf-muted); margin-top: 6px;
  font-family: ui-monospace, Consolas, monospace; }
/* Overrides the margin-top above only when this text follows the fence badge -- matches the
   from/to coordinate lines INSIDE this same span, which sit at plain line-spacing with nothing
   added between them. When there's no fence badge (a pin-only change), the default 6px above
   still separates this text from the map image. */
.gf-tip-fence-badge + .gf-tip-text { margin-top: 0; }
/* 25% bigger than the base .gf-note pill -- this badge is the whole point of the tooltip on a
   radius-only change, so it needed more presence than a corner note. */
/* No margin-bottom -- matches the from/to coordinate lines below, which have no gap of their own
   beyond ordinary line spacing (they're one span split by a <br>). */
.gf-tip-fence-badge { font-size: 11.9px; padding: 1.3px 8.8px; margin-bottom: 0; }

/* Marks plain text as the hover target for a ThumbnailTooltip (e.g. "Pin moved" in Change
   history) -- without this a bare word gives no visual sign that hovering does anything. Colored
   (not muted-gray) on purpose: a first pass used --gf-dim and it read as barely-there, easy to
   miss entirely. Indigo matches the pin icon's own hue, tying the two together. */
/* line-height: 1 so the dotted border sits right under the glyphs -- without it, the inline
   box's normal line-height leaves the border hanging well below the text, crowding whatever
   diff line comes next. */
.gf-hover-hint { cursor: help; color: var(--gf-indigo-light); line-height: 1;
  border-bottom: 1px dotted var(--gf-indigo-light); padding-bottom: 1px; }
.gf-hover-hint:hover { color: var(--gf-text-high); border-bottom-color: var(--gf-text-high); }

/* --- Change History --- */

/* A sidebar, not a filter row: two filter groups with headings read better stacked than crammed
   into a horizontal strip. */
.gf-history { display: grid; grid-template-columns: 190px 1fr; gap: 20px; align-items: start; }
.gf-side-group { margin-bottom: 22px; }
.gf-side-btns { display: flex; flex-direction: column; gap: 4px; }
.gf-side-btn { background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--gf-muted); font-size: 12px; padding: 6px 10px; cursor: pointer; text-align: left; }
.gf-side-btn:hover { color: var(--gf-text-2); background: var(--gf-panel); }
.gf-side-btn.is-active { background: var(--gf-indigo-tint); border-color: var(--gf-indigo);
  color: var(--gf-text); font-weight: 600; }

/* Colour-coded icon-in-circle checkboxes, one per change type. */
.gf-check { display: flex; align-items: center; gap: 8px; background: none; border: 0;
  color: var(--gf-muted); font-size: 12px; padding: 5px 0; cursor: pointer; text-align: left;
  opacity: .5; }
.gf-check.is-on { opacity: 1; color: var(--gf-text); }
.gf-check-icon { width: 22px; height: 22px; border-radius: 11px; display: flex; flex: 0 0 auto;
  align-items: center; justify-content: center; }

/* The sticky footer's own height needs to be reserved as bottom padding on the scrolling list --
   otherwise, since sticky only reserves its OWN box in flow (at the very end of the list) and then
   visually floats pinned to the bottom of whatever ancestor is actually scrolling, the last entry
   or two scroll fully underneath it before that natural end-of-flow position is reached. This
   showed up as the "Back" button covering part of the last visible card. */
.gf-history-list { padding-bottom: 64px; }
.gf-history-footer { display: flex; justify-content: flex-end; margin-top: 12px;
  position: sticky; bottom: -16px; background: var(--gf-page); padding-top: 8px; padding-bottom: 16px; }

.gf-summary { font-size: 12px; color: var(--gf-muted); margin-bottom: 12px; }
.gf-day-header { font-size: 9px; font-weight: 600; letter-spacing: .10em; color: var(--gf-dim);
  padding: 14px 2px 6px; }

/* Icon cards. Date/time is the boldest top-left element; the account name is a pill vertically
   centred on the right rather than pinned to the top. */
/* All sizes here are 1.25x the original values -- the whole History entry (card, text, icon
   badge) was made 25% larger on request. Not theme-gated: light and dark mode share this same
   sizing, always have, so this scale-up applies identically to both. */
.gf-card-row { display: flex; align-items: center; gap: 15px; background: var(--gf-panel);
  border: 1px solid var(--gf-border); border-radius: 10px; padding: 14px 15px; margin-bottom: 7.5px; }
.gf-card-row.is-highlighted { border-color: var(--gf-indigo);
  box-shadow: 0 0 0 2px var(--gf-indigo-tint); }
.gf-card-when { font-size: 16.25px; font-weight: 600; color: var(--gf-text); }
.gf-card-diffs { font-size: 14.4px; color: var(--gf-text-2); margin-top: 3px; }
/* Matches each diff line to its own icon color above (amber radius circle, green no-location
   flag) rather than the shared muted default -- the pin-moved line stays muted here because it
   already gets its own color via .gf-hover-hint. */
.gf-diff--radius { color: var(--gf-amber); }
.gf-diff--flag { color: var(--gf-green); }
.gf-card-main { flex: 1; min-width: 0; }
.gf-who { background: var(--gf-field); border-radius: 999px; padding: 3.75px 12.5px; font-size: 13.75px;
  font-weight: 600; color: var(--gf-text-2); flex: 0 0 auto; }
.gf-src { font-size: 11.9px; color: var(--gf-dim); margin-top: 3px; text-align: right; }

/* A radius change and a pin move in the SAME edit join into one connected pill, not two stacked
   icons -- two icons imply two separate edits. */
/* Fused-pill technique for a combined change (2+ of radius/pin/no-location in the same edit):
   half-round end caps + a square middle, not separate full circles in a bordered box -- that
   alternative was tried on desktop and explicitly rejected as a visual regression. Fixed order
   when combined: Radius (leftmost) -> Pin (middle) -> No-location (rightmost); a solo change
   (IsRadiusOnly/IsPinOnly/etc.) renders the same .gf-icon-circle alone with its own natural
   full-circle radius, so only the COMBINED case needs the segment corner overrides below. */
.gf-icons { display: flex; align-items: center; flex: 0 0 auto; }
.gf-icons--combined { border: 1px solid rgba(255,255,255,0.08); border-radius: 23.75px;
  overflow: hidden; }
.gf-icon-circle { width: 32.5px; height: 32.5px; border-radius: 16.25px; display: flex;
  align-items: center; justify-content: center; border: 0 solid transparent; box-sizing: border-box; }
.gf-icons--combined .gf-icon-circle { border-radius: 0; position: relative; }
/* Thin divider between segments -- skipped on the first segment via :not(:first-child), so there
   is no stray line on the leading edge of the pill. */
.gf-icons--combined .gf-icon-circle:not(:first-child)::before { content: ''; position: absolute;
  left: 0; top: 5px; bottom: 5px; width: 1px; background: rgba(255,255,255,0.12); }
.gf-icons--combined .gf-icon-circle:first-child { border-radius: 23.75px 0 0 23.75px; }
.gf-icons--combined .gf-icon-circle:last-child { border-radius: 0 23.75px 23.75px 0; }
/* Only Pin can land in the middle (radius is always leftmost, no-location always rightmost, per
   IconGroup's fixed render order below) -- a lone Pin segment between two others gets no radius
   of its own, which is correct: it's a square middle by design. */

/* --- Check-in History tab (ported from DispatchWebApp's standalone /checkin-history page) --- */
.gf-cih-intro { color: var(--gf-muted); font-size: 13px; margin: 0 0 14px; }
.gf-cih-search { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 16px; }
.gf-cih-input { background: var(--gf-field); border: 1px solid var(--gf-border); border-radius: 8px;
  color: var(--gf-text); font-size: 13px; padding: 8px 12px; min-width: 160px; }
.gf-cih-input::placeholder { color: var(--gf-dim); }
.gf-cih-input:focus { outline: none; border-color: var(--gf-indigo-light); }
.gf-cih-list { max-height: var(--gf-list-size); }
.gf-cih-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.gf-cih-table th { text-align: left; color: var(--gf-muted); font-weight: 600; font-size: 11px;
  text-transform: uppercase; letter-spacing: .02em; padding: 8px 10px; border-bottom: 1px solid var(--gf-border); }
.gf-cih-table td { color: var(--gf-text); padding: 8px 10px; border-bottom: 1px solid var(--gf-border); }
.gf-cih-row { cursor: pointer; }

/* --- Suspicious badge (SuspiciousBadge.razor) ---
   Ported verbatim from the approved mockup (.superpowers/brainstorm/4152-1789489676/content/
   flag-icon-variant-a-v3.html, "Variant A, distance on the badge"): same colors, same flash
   timing, same tooltip shell. Deliberately NOT built on this file's --gf-* custom properties --
   those are scoped to .gf-root (see the top of this file), but this badge also renders on
   DispatchDashboard's reg list, which sits entirely outside .gf-root. Hardcoded values keep the
   badge looking identical in both hosts instead of silently losing its color/background wherever
   .gf-root isn't an ancestor. */
@keyframes gf-susp-flash {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
.susp-tip-anchor { position: relative; display: inline-flex; vertical-align: middle; }
.susp-pill {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px 2px 6px;
  border-radius: 999px; background: rgba(255,70,70,0.14); color: #ff4646;
  font-size: 11.5px; font-weight: 700; cursor: default; white-space: nowrap;
  animation: gf-susp-flash 1.6s ease-in-out infinite;
}
.susp-dot {
  width: 14px; height: 14px; border-radius: 50%; background: #ff4646; color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 10px;
  font-weight: 800; flex-shrink: 0;
}
.susp-sep { opacity: 0.55; font-weight: 400; }
.susp-tip-pop {
  display: none; position: absolute; bottom: calc(100% + 10px); left: 50%; transform: translateX(-50%);
  width: 260px; background: #1e1e1e; color: #eee;
  border-radius: 10px; padding: 12px 14px; font-size: 12px; line-height: 1.5;
  box-shadow: 0 8px 24px rgba(0,0,0,0.35); z-index: 200; text-align: left;
}
.susp-tip-anchor:hover .susp-tip-pop { display: block; }
.susp-verdict { font-weight: 700; color: #ff6b6b; margin-bottom: 6px; }
.susp-loc-row { display: flex; gap: 8px; margin-bottom: 4px; align-items: flex-start; }
.susp-loc-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 4px; flex-shrink: 0; }
.susp-loc-dot.susp-start { background: #6ee2a0; }
.susp-loc-dot.susp-verify { background: #ff8a91; }
.susp-loc-label { opacity: 0.65; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; }
.susp-loc-coord { font-size: 12px; }
.susp-dist {
  margin-top: 8px; padding-top: 8px; border-top: 1px solid rgba(255,255,255,0.12);
  font-weight: 600;
}
.gf-cih-row:hover td { background: var(--gf-raised); }
