/* ======================================================================
   MobileTracking — component kit
   Every surface, control and indicator this product is allowed to draw.

   If a component is not in here, it should not exist. The fastest way to
   make a 25-screen panel feel cheap is to let each page invent its own
   buttons.

   Load order:  tokens.css → base.css → kit.css
   ====================================================================== */

/* ==========================================================================
   0. THE PHONE TYPE SCALE
   ========================================================================== */

/**
 * One scale, re-tuned for a 390px screen.
 *
 * The desktop ramp runs 11px → 56px. Both ends are wrong on a phone, in
 * opposite directions, and the panel had both problems on the same screen:
 *
 *   - the big end overflows. `--t-display-sm` at 35px turned "One step decides
 *     whether this works" into four lines and half a viewport, so the checklist
 *     it introduces was below the fold on the page whose entire job is getting
 *     the checklist finished.
 *
 *   - the small end is unreadable. 11px labels and 12.5px captions are a
 *     comfortable caption size at arm's length on a 27" monitor and squinting
 *     material at 30cm on glass, held one-handed, often outdoors. They are also
 *     where the product puts the numbers: stat labels, chip words, the eyebrow
 *     over every card.
 *
 * Overriding the six custom properties is the whole fix, because every
 * component in this file sizes from them — nothing sets a px font-size of its
 * own. So one block re-scales all seventeen screens, and a component added
 * tomorrow is correct on a phone without anyone thinking about it.
 *
 * The ramp is compressed rather than shifted: the gaps between steps narrow so
 * the hierarchy survives in a smaller total range.
 */
@media (max-width: 700px) {
  :root {
    --t-display-lg-size: 38px;  --t-display-lg-line: 43px;
    --t-display-md-size: 32px;  --t-display-md-line: 38px;
    --t-display-sm-size: 27px;  --t-display-sm-line: 33px;

    --t-headline-lg-size: 24px; --t-headline-lg-line: 31px;
    --t-headline-md-size: 21px; --t-headline-md-line: 28px;
    --t-headline-sm-size: 19px; --t-headline-sm-line: 26px;

    --t-title-lg-size: 17px;    --t-title-lg-line: 24px;
    --t-title-md-size: 16px;    --t-title-md-line: 22px;
    --t-title-sm-size: 15px;    --t-title-sm-line: 21px;

    --t-body-lg-size: 16px;     --t-body-lg-line: 25px;
    --t-body-md-size: 15px;     --t-body-md-line: 23px;
    --t-body-sm-size: 13.5px;   --t-body-sm-line: 19px;

    --t-label-lg-size: 15px;    --t-label-lg-line: 21px;
    --t-label-md-size: 13px;    --t-label-md-line: 18px;
    --t-label-sm-size: 12px;    --t-label-sm-line: 16px;

    --s-gutter: 16px;
  }
}

/* ==========================================================================
   1. APP SHELL
   ========================================================================== */

.shell {
  display: grid;
  grid-template-columns: var(--s-rail) minmax(0, 1fr);
  min-height: 100dvh;
}

@media (max-width: 1000px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------------------------ rail */

.rail {
  position: sticky;
  top: 0;
  height: 100dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: var(--s-hair);
  padding: var(--s-lg) var(--s-md) var(--s-md);
  background: var(--c-surface-container-low);
  border-right: 1px solid var(--c-outline-variant);
}

/**
 * On a phone the rail becomes a drawer rather than disappearing.
 *
 * It used to be `display: none`, and the five-item bottom bar was the whole of
 * mobile navigation — so twelve of the panel's seventeen destinations had no
 * route at all on a phone. Sliding the same element keeps the mobile menu and
 * the desktop sidebar as ONE thing: a section added to `NAV` appears in both,
 * and there is no second list to forget to update.
 */
@media (max-width: 1000px) {
  .rail {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(304px, 86vw);
    height: 100dvh;
    z-index: 70;
    box-shadow: var(--shadow-4);
    padding-bottom: calc(var(--s-lg) + env(safe-area-inset-bottom));

    /* `visibility`, not just `transform`. A drawer parked off-screen with a
       transform is still in the tab order, so a keyboard or screen-reader user
       tabs out of the page into seventeen invisible links.

       It is switched with a DELAY rather than interpolated, and the two
       directions are deliberately different:

         closing — hold `visible` for the length of the slide, then hide, so the
                   drawer is not deleted mid-animation
         opening — flip to `visible` at once (`0s`, no delay), so the very next
                   line of script can move focus into it

       Putting `visibility` in the transition list to be interpolated instead
       looks equivalent and is not: at t=0 the computed value is still `hidden`,
       `focus()` on a hidden element is ignored, and the drawer opens with the
       keyboard still stranded on the page behind it. */
    visibility: hidden;
    transform: translateX(-100%);
    transition: transform var(--m-base) var(--m-easeOut),
                visibility 0s linear var(--m-base);
  }

  .rail.is-open {
    visibility: visible;
    transform: none;
    transition: transform var(--m-base) var(--m-easeOut), visibility 0s;
  }
}

.rail-scrim {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 60;
  background: color-mix(in srgb, var(--c-scrim) 52%, transparent);
  opacity: 0;
  transition: opacity var(--m-base) var(--m-easeOut);
}

@media (max-width: 1000px) {
  .rail-scrim.is-open { display: block; opacity: 1; }
}

/* Scroll lock while the drawer is open. Scoped to the open state so the rest of
   the panel keeps pull-to-refresh and momentum scrolling. */
body.drawer-open { overflow: hidden; }

.rail-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-sm); }
.rail-head .brand { flex: 1 1 auto; min-width: 0; }

/* The close button is drawer-only: on desktop the rail is furniture, not a
   dismissible surface, and an X on it would be a control that does nothing. */
.rail-close {
  display: none;
  flex: none;
  width: 40px;
  height: 40px;
  margin-top: var(--s-xs);
  border: 1px solid var(--c-outline-variant);
  border-radius: var(--r-sm);
  background: var(--c-surface-container);
  color: var(--c-on-surface-variant);
  place-items: center;
  cursor: pointer;
}

.rail-close:hover { background: var(--c-surface-container-high); color: var(--c-on-surface); }

@media (max-width: 1000px) {
  .rail-close { display: grid; }
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--s-md);
  padding: var(--s-xs) var(--s-sm) var(--s-xl);
  text-decoration: none;
  color: inherit;
}

.brand-mark {
  width: 38px;
  height: 38px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  color: var(--c-on-primary);
  background: linear-gradient(140deg, var(--c-ring-start), var(--c-ring-end));
}

.brand-mark svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 2; }

.brand-name {
  font-size: var(--t-title-lg-size);
  font-weight: 700;
  letter-spacing: -0.02em;
}

.rail-group {
  padding: var(--s-lg) var(--s-md) var(--s-xs);
  font-size: var(--t-label-sm-size);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--c-outline);
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--s-md);
  padding: 10px var(--s-md);
  border-radius: var(--r-sm);
  color: var(--c-on-surface-variant);
  text-decoration: none;
  font-size: var(--t-title-sm-size);
  font-weight: 500;
  transition: background var(--m-fast) var(--m-easeOut),
              color var(--m-fast) var(--m-easeOut);
}

.nav:hover { background: var(--c-surface-container-high); color: var(--c-on-surface); }

/**
 * The current page.
 *
 * Filled container plus a weight change, not just a colour change. A parent
 * scanning a 14-item sidebar for "where am I" should not have to compare two
 * shades of the same hue, and on a colour-managed external monitor those two
 * shades can land closer together than they do here.
 */
.nav[aria-current='page'] {
  background: var(--c-primary-container);
  color: var(--c-on-primary-container);
  font-weight: 600;
}

.nav .i { color: inherit; }

.nav-badge {
  margin-left: auto;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  display: grid;
  place-items: center;
  background: var(--c-risk);
  color: var(--c-on-risk);
  font-size: var(--t-label-sm-size);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.rail-spacer { flex: 1 1 auto; min-height: var(--s-lg); }

/* ---------------------------------------------------------------- topbar */

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-lg);
  padding: var(--s-lg) var(--s-gutter);
  background: color-mix(in srgb, var(--c-background) 86%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--c-outline-variant);
}

/* color-mix + backdrop-filter is a progressive enhancement; without it the bar
   is simply opaque, which is fine. This keeps it opaque on the browsers that
   would otherwise render a transparent bar with no blur — unreadable text over
   scrolling content is worse than no effect at all. */
@supports not (backdrop-filter: blur(1px)) {
  .topbar { background: var(--c-background); }
}

/*
 * `flex-basis: 0`, not `auto`.
 *
 * With `auto` the title's hypothetical main size is its max-content width, and
 * a flex container decides where to WRAP from hypothetical sizes before it
 * shrinks anything. So a long page title — "Calls & messages" — pushed the
 * avatar onto a row of its own, on a bar that had plenty of room for it once
 * the title was allowed to shrink. A zero basis lets the title take what is
 * left rather than claim what it wants.
 */
.topbar-title { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 0; }
.topbar h1 { font-size: var(--t-headline-sm-size); line-height: var(--t-headline-sm-line); }

/* One line, clipped. The subtitle is a caption — "Sun, 23 Aug · 2 children" —
   and wrapping it turns a 20px strip into a paragraph. It was wrapping one word
   per line on a phone, because the child switcher beside it would not shrink. */
.topbar-sub {
  font-size: var(--t-body-sm-size);
  color: var(--c-on-surface-variant);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar-actions { display: flex; align-items: center; gap: var(--s-sm); flex: none; }

/* The hamburger. Desktop has the rail pinned, so it has nothing to toggle. */
.topbar-burger {
  display: none;
  flex: none;
  width: 44px;
  height: 44px;
  margin-left: calc(var(--s-sm) * -1);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--c-on-surface);
  place-items: center;
  cursor: pointer;
}

.topbar-burger:hover { background: var(--c-surface-container-high); }
.topbar-burger .i { width: 24px; height: 24px; }

.main {
  padding: var(--s-xl) var(--s-gutter) var(--s-3xl);
  display: flex;
  flex-direction: column;
  gap: var(--s-xl);
}

/**
 * Phone chrome.
 *
 * The switcher moves to its own full-width row and scrolls sideways, instead of
 * competing with the title for a 390px line. Everything on the row keeps its
 * real size — the previous layout squeezed rather than wrapped, which is how a
 * 390px viewport ended up with 417px of content and a page that slid about
 * under the thumb.
 */
@media (max-width: 1000px) {
  .topbar { flex-wrap: wrap; row-gap: var(--s-sm); column-gap: var(--s-md); }
  .topbar-burger { display: grid; order: 1; }
  .topbar-title { order: 2; }
  .topbar-actions { order: 3; }

  .topbar .who {
    order: 4;
    flex: 1 0 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;

    /* Bleed to the screen edges so the strip reads as scrollable and the last
       child is not cut off mid-name by the gutter. */
    margin: 0 calc(var(--s-lg) * -1);
    padding: 2px var(--s-lg);
    scroll-padding: 0 var(--s-lg);
  }

  .topbar .who::-webkit-scrollbar { display: none; }
  .topbar .who-btn { flex: none; scroll-snap-align: start; }
}

@media (max-width: 700px) {
  .main { padding: var(--s-lg) var(--s-lg) 96px; gap: var(--s-lg); }
  .topbar { padding: var(--s-md) var(--s-lg); }
}

/* ------------------------------------------------------------ bottom nav
   Phone only. The same destinations as the rail, capped at five, because a
   sixth item makes every target too narrow to hit reliably.                  */

.bottom-nav {
  display: none;
  position: fixed;
  inset: auto 0 0 0;
  z-index: 40;
  padding: var(--s-sm) var(--s-sm) calc(var(--s-sm) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--c-surface-container-low) 92%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--c-outline-variant);
}

@media (max-width: 1000px) {
  /* `minmax(0, 1fr)`, not `1fr`. A bare `1fr` track is `minmax(auto, 1fr)`, so
     its floor is the widest tab's MIN-CONTENT width — and once the labels stop
     wrapping, that floor is a whole word. Five of those made a 390px screen
     into a 565px bar hanging off the right edge, with a fixed-position element
     no scroll could reach. The zero floor lets the ellipsis do its job. */
  .bottom-nav { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
}

.bottom-nav a {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: var(--s-sm) var(--s-xs);
  border-radius: var(--r-sm);
  text-decoration: none;
  color: var(--c-on-surface-variant);
  font-size: var(--t-label-sm-size);
  font-weight: 500;
  min-height: 48px;
  justify-content: center;
}

/* One line each. "Screen time" wrapped to two, which made that one tab taller
   than its four neighbours and pushed the whole row out of alignment. The bar
   uses short labels for this reason; the drawer carries the full ones. */
.bottom-nav a > span:last-child {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.bottom-nav a[aria-current='page'] { color: var(--c-on-primary-container); font-weight: 600; }

.bottom-nav a[aria-current='page'] .bottom-nav-pill {
  background: var(--c-primary-container);
}

.bottom-nav-pill {
  padding: 3px 16px;
  border-radius: var(--r-pill);
  display: grid;
  place-items: center;
  transition: background var(--m-fast) var(--m-easeOut);
}

/* ==========================================================================
   2. SURFACES
   ========================================================================== */

.card {
  background: var(--c-card);
  border: 1px solid var(--c-outline-variant);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}

/* `--c-card` rather than a literal tone: in the dark theme a "lowest" surface is
   darker than the page and a card painted with it reads as a hole rather than as
   a raised object, so each theme points the alias at whichever of its own tones
   is genuinely raised. See the note in tokens.json under `derived`. */

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-lg);
  padding: var(--s-lg) var(--s-xl);
  border-bottom: 1px solid var(--c-outline-variant);
}

.card-head h2,
.card-head h3 { font-size: var(--t-title-lg-size); line-height: var(--t-title-lg-line); }

/* A card head is a title and its actions on one line, and on a phone that line
   is 326px. It does not wrap by default, so a title beside a legend or a
   Save button simply ran off the card and was clipped. */
@media (max-width: 700px) {
  .card-head { flex-wrap: wrap; row-gap: var(--s-sm); column-gap: var(--s-md); }
  .card-head > * { min-width: 0; max-width: 100%; }
}

.card-body { padding: var(--s-xl); }
.card-body-tight { padding: var(--s-lg); }

.card-foot {
  padding: var(--s-md) var(--s-xl);
  border-top: 1px solid var(--c-outline-variant);
  background: var(--c-sunken);
  font-size: var(--t-body-sm-size);
  color: var(--c-on-surface-variant);
}

/**
 * The explanatory line under a chart or a table.
 *
 * This product does a lot of work that is invisible unless it is explained —
 * a bar is red because the child went over *that day's* budget, not over some
 * fixed number — and this is where that gets said. Kept as a component so the
 * explanation always looks the same and never gets styled as body copy.
 */
.note {
  padding: var(--s-md) var(--s-xl) var(--s-lg);
  font-size: var(--t-body-sm-size);
  line-height: 1.55;
  color: var(--c-on-surface-variant);
}

/* ----------------------------------------------------------------- hero
   The panel at the top of the dashboard. Rounded harder than any other
   surface — 28px against 20px — which does most of the work of saying
   "this one is not a card like the others".                                  */

.hero {
  position: relative;
  border-radius: var(--r-hero);
  border: 1px solid var(--c-outline-variant);
  background:
    linear-gradient(160deg, var(--c-hero-wash-top), var(--c-hero-wash-bottom)),
    var(--c-card);
  box-shadow: var(--shadow-2);
  padding: var(--s-xl);
  overflow: hidden;
}

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s-xl);
  align-items: center;
}

@media (max-width: 700px) {
  .hero { padding: var(--s-lg); }
  .hero-grid { grid-template-columns: minmax(0, 1fr); }
  .hero-ring { justify-self: center; }
}

/* ==========================================================================
   3. BUTTONS
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-sm);
  padding: 10px 18px;
  min-height: 42px;
  border-radius: var(--r-sm);
  border: 1px solid var(--c-outline-variant);
  background: var(--c-raised);
  color: var(--c-on-surface);
  font-size: var(--t-label-lg-size);
  font-weight: 600;
  letter-spacing: var(--t-label-lg-track);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--m-fast) var(--m-easeOut),
              border-color var(--m-fast) var(--m-easeOut),
              box-shadow var(--m-fast) var(--m-easeOut),
              transform var(--m-instant) var(--m-easeOut);
}

.btn:hover { background: var(--c-surface-container-high); border-color: var(--c-outline); }
.btn:active { transform: translateY(1px); }

.btn[disabled],
.btn[aria-disabled='true'] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

.btn .i { width: 17px; height: 17px; }

/* Primary. One per view, ideally — if a screen has three of these, none of
   them is the thing the parent should do next. */
.btn-primary {
  background: var(--c-primary);
  border-color: transparent;
  color: var(--c-on-primary);
  box-shadow: var(--shadow-1);
}

.btn-primary:hover {
  background: color-mix(in srgb, var(--c-primary) 88%, var(--c-on-surface));
  border-color: transparent;
  box-shadow: var(--shadow-2);
}

.btn-tonal {
  background: var(--c-primary-container);
  border-color: transparent;
  color: var(--c-on-primary-container);
}

.btn-tonal:hover {
  background: color-mix(in srgb, var(--c-primary-container) 88%, var(--c-on-primary-container));
  border-color: transparent;
}

.btn-ghost { background: transparent; border-color: transparent; color: var(--c-on-surface-variant); }
.btn-ghost:hover { background: var(--c-surface-container-high); border-color: transparent; color: var(--c-on-surface); }

/**
 * Destructive.
 *
 * Uses `risk`, not `error`. Deleting a child's location history is a finding-
 * level consequence, not a failed form submission, and the two reds mean
 * different things everywhere else in this product.
 */
.btn-danger { background: var(--c-risk); border-color: transparent; color: var(--c-on-risk); }
.btn-danger:hover { background: color-mix(in srgb, var(--c-risk) 88%, var(--c-on-surface)); border-color: transparent; }

.btn-sm { padding: 6px 12px; min-height: 34px; font-size: var(--t-label-md-size); border-radius: var(--r-xs); }
.btn-lg { padding: 14px 26px; min-height: 52px; font-size: var(--t-title-md-size); border-radius: var(--r-sm); }
.btn-block { display: flex; width: 100%; }
.btn-pill { border-radius: var(--r-pill); }

/**
 * Icon-only.
 *
 * Always needs a visible-to-assistive-tech label. `aria-label` on the element,
 * or a `.sr-only` span inside — the kit cannot enforce that, but every use of
 * this class in this product has one and a review should check for it.
 */
.btn-icon { padding: 0; width: 42px; min-height: 42px; border-radius: var(--r-sm); }
.btn-icon.btn-sm { width: 34px; min-height: 34px; }

/* Segmented control. For 2–4 mutually exclusive view options. */
.segment {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  border-radius: var(--r-pill);
  background: var(--c-surface-container-high);
  border: 1px solid var(--c-outline-variant);
}

.segment button {
  padding: 7px 16px;
  border-radius: var(--r-pill);
  font-size: var(--t-label-md-size);
  font-weight: 600;
  color: var(--c-on-surface-variant);
  transition: background var(--m-fast) var(--m-easeOut), color var(--m-fast) var(--m-easeOut);
}

.segment button:hover { color: var(--c-on-surface); }

.segment button[aria-pressed='true'],
.segment button[aria-selected='true'] {
  background: var(--c-surface-container-lowest);
  color: var(--c-on-surface);
  box-shadow: var(--shadow-1);
}

/* ==========================================================================
   4. STATUS
   ========================================================================== */

/**
 * A status chip.
 *
 * THE WORD IS NOT OPTIONAL. Every chip in this product contains text, because
 * the four status colours sit as close as 1.14:1 apart under simulated
 * protanopia and no palette fixes that. The colour is a fast path for people
 * who can use it; the word is what makes the chip correct for everyone else.
 *
 * The leading dot is a second redundancy: it differs in SHAPE between states
 * (see `.chip-mark` below), not only in fill.
 */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px 5px 10px;
  border-radius: var(--r-pill);
  font-size: var(--t-label-md-size);
  font-weight: 600;
  white-space: nowrap;
  background: var(--c-surface-container-high);
  color: var(--c-on-surface-variant);
  border: 1px solid transparent;
}

.chip-mark { width: 9px; height: 9px; flex: none; }

.chip-safe     { background: var(--c-safe-container);     color: var(--c-on-safe-container); }
.chip-caution  { background: var(--c-caution-container);  color: var(--c-on-caution-container); }
.chip-risk     { background: var(--c-risk-container);     color: var(--c-on-risk-container); }
.chip-critical { background: var(--c-critical-container); color: var(--c-on-critical-container); }

/* Shape, not just fill: circle / triangle / square / diamond. Under greyscale
   or any colour-vision deficiency, the silhouette still separates them. */
.chip-safe     .chip-mark { border-radius: 50%; background: var(--c-safe); }
.chip-caution  .chip-mark { background: var(--c-caution); clip-path: polygon(50% 0, 100% 100%, 0 100%); }
.chip-risk     .chip-mark { border-radius: 2px; background: var(--c-risk); }
.chip-critical .chip-mark { background: var(--c-critical); clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); }
.chip-neutral  .chip-mark { border-radius: 50%; background: var(--c-outline); }

/* Filled variant, for the one place a status needs to shout. */
.chip-solid.chip-safe     { background: var(--c-safe);     color: var(--c-on-safe); }
.chip-solid.chip-caution  { background: var(--c-caution);  color: var(--c-on-caution); }
.chip-solid.chip-risk     { background: var(--c-risk);     color: var(--c-on-risk); }
.chip-solid.chip-critical { background: var(--c-critical); color: var(--c-on-critical); }
.chip-solid .chip-mark    { background: currentColor; }

.chip-sm { padding: 3px 9px 3px 8px; font-size: var(--t-label-sm-size); gap: 6px; }
.chip-sm .chip-mark { width: 7px; height: 7px; }

/**
 * A live indicator — a phone reporting right now, a location fix seconds old.
 *
 * The pulse is suppressed under prefers-reduced-motion by the global override
 * in base.css, and the dot stays visible either way, so "live" never depends
 * on the animation running.
 */
.live {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--t-label-md-size);
  font-weight: 600;
  color: var(--c-secondary);
}

.live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  position: relative;
}

.live-dot::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 2px solid currentColor;
  opacity: 0;
  animation: pw-pulse 2s var(--m-easeOut) infinite;
}

@keyframes pw-pulse {
  0%   { transform: scale(0.6); opacity: 0.7; }
  70%  { transform: scale(1.3); opacity: 0; }
  100% { transform: scale(1.3); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .live-dot::after { animation: none; }
}

.badge {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: var(--c-surface-container-highest);
  color: var(--c-on-surface-variant);
  font-size: var(--t-label-sm-size);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.badge-risk { background: var(--c-risk); color: var(--c-on-risk); }
.badge-primary { background: var(--c-primary); color: var(--c-on-primary); }

/* ==========================================================================
   5. INDICATORS — rings, meters, bars
   ========================================================================== */

.ring { position: relative; display: inline-grid; place-items: center; flex: none; }
.ring svg { transform: rotate(-90deg); overflow: visible; }
.ring-track { stroke: var(--c-meter-track); fill: none; stroke-linecap: round; }
.ring-fill { fill: none; stroke-linecap: round; transition: stroke-dashoffset var(--m-slow) var(--m-easeOut); }

.ring-label {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  text-align: center;
}

.ring-value {
  font-size: var(--t-display-sm-size);
  line-height: 1;
  font-weight: 700;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.ring-lg .ring-value { font-size: var(--t-display-lg-size); }
.ring-sm .ring-value { font-size: var(--t-headline-md-size); }

.ring-caption {
  font-size: var(--t-label-sm-size);
  font-weight: 600;
  color: var(--c-on-surface-variant);
}

/**
 * A horizontal meter.
 *
 * The number always renders beside it. A bar on its own encodes one value in
 * one dimension and is unreadable the moment it is short — "is that 4% or 0%?"
 * — and this product's bars are frequently short.
 */
.meter {
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--c-meter-track);
  overflow: hidden;
}

.meter-fill {
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--c-primary);
  transition: width var(--m-base) var(--m-easeOut);
}

.meter-lg { height: 12px; }
.meter-safe .meter-fill     { background: var(--c-safe); }
.meter-caution .meter-fill  { background: var(--c-caution); }
.meter-risk .meter-fill     { background: var(--c-risk); }
.meter-critical .meter-fill { background: var(--c-critical); }

/* A vertical bar chart built from divs. Enough for "screen time this week",
   which is the only chart in the product that appears on every screen. */
.bars {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: var(--s-sm);
  align-items: end;
  min-height: 140px;
}

.bar-col { display: flex; flex-direction: column; align-items: center; gap: 6px; height: 100%; justify-content: flex-end; }

.bar {
  width: 100%;
  max-width: 34px;
  min-height: 4px;
  border-radius: var(--r-xs) var(--r-xs) 3px 3px;
  background: var(--c-primary);
  transition: height var(--m-base) var(--m-easeOut);
}

.bar-over { background: var(--c-risk); }
.bar-label { font-size: var(--t-label-sm-size); color: var(--c-on-surface-variant); font-weight: 500; }

/* A stat tile. Number first, label under — the number is what is being
   looked up, and putting the label first makes every tile start the same way. */
.stat {
  padding: var(--s-lg);
  border-radius: var(--r-sm);
  background: var(--c-surface-container);
  border: 1px solid transparent;
}

.stat-value {
  font-size: var(--t-headline-md-size);
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.stat-unit { font-size: 0.55em; font-weight: 600; color: var(--c-on-surface-variant); margin-left: 3px; }
.stat-label { margin-top: 5px; font-size: var(--t-body-sm-size); color: var(--c-on-surface-variant); }

.stat-safe    { background: var(--c-safe-container);    color: var(--c-on-safe-container); }
.stat-caution { background: var(--c-caution-container); color: var(--c-on-caution-container); }
.stat-risk    { background: var(--c-risk-container);    color: var(--c-on-risk-container); }
.stat-safe .stat-label,
.stat-caution .stat-label,
.stat-risk .stat-label { color: inherit; opacity: 0.78; }

/* ==========================================================================
   6. LISTS AND ROWS
   ========================================================================== */

.list { display: flex; flex-direction: column; }

.item {
  display: flex;
  align-items: center;
  gap: var(--s-md);
  padding: var(--s-md) var(--s-xl);
  border-bottom: 1px solid var(--c-outline-variant);
  transition: background var(--m-fast) var(--m-easeOut);
}

.item:last-child { border-bottom: 0; }
.item-hover:hover { background: var(--c-hover); }
.item-icon {
  width: 38px;
  height: 38px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--r-xs);
  background: var(--c-surface-container-high);
  color: var(--c-on-surface-variant);
}

.item-icon-safe     { background: var(--c-safe-container);     color: var(--c-on-safe-container); }
.item-icon-caution  { background: var(--c-caution-container);  color: var(--c-on-caution-container); }
.item-icon-risk     { background: var(--c-risk-container);     color: var(--c-on-risk-container); }
.item-icon-critical { background: var(--c-critical-container); color: var(--c-on-critical-container); }
.item-icon-primary  { background: var(--c-primary-container);  color: var(--c-on-primary-container); }

.item-text { flex: 1 1 auto; min-width: 0; }
.item-title { font-size: var(--t-title-sm-size); font-weight: 600; }
.item-sub { margin-top: 2px; font-size: var(--t-body-sm-size); color: var(--c-on-surface-variant); }
.item-actions { display: flex; align-items: center; gap: var(--s-sm); flex: none; }

/**
 * List rows on a phone.
 *
 * `.item` is one flex line: icon, text, actions — and `.item-actions` was
 * `flex: none`, so on a 358px card the actions kept their full desktop width
 * and were simply CLIPPED by the card's `overflow: hidden`. Not squashed,
 * not scrollable: gone. The allow/limit/block segment on Apps, the enter/leave
 * toggles on Places, the edit and delete buttons on Tasks — all invisible and
 * unreachable on a phone, on screens whose entire purpose is those controls.
 * That is most of what "full features on desktop, missing on mobile" was.
 *
 * The row is allowed to wrap instead. `55%` is the basis that decides it: a
 * compact action set — a chip, one small button — still sits beside the text,
 * and anything wider takes its own line at full width rather than pushing
 * itself off the edge. `max-width: 100%` is what then lets the `.wrap` rows
 * INSIDE the actions do their own wrapping, which they could not do while
 * their parent was sized to max-content.
 */
@media (max-width: 700px) {
  .item { flex-wrap: wrap; row-gap: var(--s-sm); padding: var(--s-md) var(--s-lg); }
  .item-text { flex: 1 1 55%; }
  .item-actions { flex: 0 1 auto; max-width: 100%; flex-wrap: wrap; }

  /* A segment control is a set of equal choices; letting it wrap mid-set reads
     as three separate buttons. It stays one line and scrolls if it must. */
  .segment { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
  .segment::-webkit-scrollbar { display: none; }

  /* Card padding matches the row padding, or the head sits 8px off the rows. */
  .card-head, .card-body, .card-foot, .note { padding-left: var(--s-lg); padding-right: var(--s-lg); }
}

/**
 * An attention row — the "you should look at this" band.
 *
 * A left border rather than a tinted background, because a page with four
 * tinted bands on it has no hierarchy left. The border plus the icon shape
 * carries the severity; the row itself stays on the normal surface.
 */
.attention {
  display: flex;
  align-items: center;
  gap: var(--s-md);
  padding: var(--s-md) var(--s-lg);
  border-radius: var(--r-sm);
  border: 1px solid var(--c-outline-variant);
  border-left: 4px solid var(--c-outline);
  background: var(--c-surface-container-lowest);
}

.attention-safe     { border-left-color: var(--c-safe); }
.attention-caution  { border-left-color: var(--c-caution); }
.attention-risk     { border-left-color: var(--c-risk); }
.attention-critical { border-left-color: var(--c-critical); }
.attention-primary  { border-left-color: var(--c-primary); }

/* ==========================================================================
   7. AVATARS AND PEOPLE
   ========================================================================== */

/**
 * A child's avatar.
 *
 * Initials on a tinted ground rather than a photo, by default. Uploading a
 * photograph of a child to a monitoring service is a decision a parent should
 * make deliberately, not one the empty state nudges them into.
 */
.avatar {
  width: 36px;
  height: 36px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--r-xs);
  background: var(--c-tertiary-container);
  color: var(--c-on-tertiary-container);
  font-size: var(--t-label-md-size);
  font-weight: 700;
  letter-spacing: 0.02em;
  overflow: hidden;
}

.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar-sm { width: 26px; height: 26px; font-size: var(--t-label-sm-size); border-radius: 7px; }
.avatar-lg { width: 52px; height: 52px; font-size: var(--t-title-md-size); border-radius: var(--r-sm); }
.avatar-round { border-radius: 50%; }

/* Alternate tints, so three children in a list are visually separable. These
   are tertiary/secondary/primary containers rather than new colours — no new
   hue enters the product just to differentiate three avatars. */
.avatar-1 { background: var(--c-tertiary-container);  color: var(--c-on-tertiary-container); }
.avatar-2 { background: var(--c-secondary-container); color: var(--c-on-secondary-container); }
.avatar-3 { background: var(--c-primary-container);   color: var(--c-on-primary-container); }
.avatar-4 { background: var(--c-safe-container);      color: var(--c-on-safe-container); }

/* The child switcher in the topbar. */
.who { display: flex; gap: var(--s-sm); flex-wrap: wrap; }

.who-btn {
  display: flex;
  align-items: center;
  gap: var(--s-sm);
  padding: 5px 14px 5px 6px;
  border-radius: var(--r-pill);
  border: 1px solid var(--c-outline-variant);
  background: var(--c-raised);
  color: var(--c-on-surface);
  font-size: var(--t-title-sm-size);
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: border-color var(--m-fast) var(--m-easeOut), background var(--m-fast) var(--m-easeOut);
}

.who-btn:hover { border-color: var(--c-outline); }

.who-btn[aria-pressed='true'] {
  border-color: var(--c-primary);
  background: var(--c-primary-container);
  color: var(--c-on-primary-container);
  font-weight: 600;
}

.who-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--c-safe); flex: none; }
.who-dot-off { background: var(--c-outline); }

/* ==========================================================================
   8. FORMS
   ========================================================================== */

.field { display: flex; flex-direction: column; gap: 6px; }

.label { font-size: var(--t-label-lg-size); font-weight: 600; }

/**
 * Help text sits ABOVE the input, not below it.
 *
 * Below is the conventional place and it is the wrong one: someone using a
 * screen magnifier, or filling the form on a phone with the keyboard up, reads
 * the label, types, and never sees the hint at all. Above means it is read
 * before it is needed rather than after.
 */
.hint { font-size: var(--t-body-sm-size); color: var(--c-on-surface-variant); }

.input,
.select,
.textarea {
  width: 100%;
  padding: 11px 14px;
  min-height: 44px;
  border-radius: var(--r-sm);
  border: 1px solid var(--c-outline);
  background: var(--c-surface-container-lowest);
  color: var(--c-on-surface);
  font-size: var(--t-body-lg-size);
  transition: border-color var(--m-fast) var(--m-easeOut), box-shadow var(--m-fast) var(--m-easeOut);
}

.input::placeholder, .textarea::placeholder { color: var(--c-outline); }

.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--c-primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-primary) 22%, transparent);
}

.textarea { min-height: 108px; resize: vertical; line-height: 1.55; }

.select {
  appearance: none;
  padding-right: 40px;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 19px) calc(50% + 1px), calc(100% - 14px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* Invalid uses `error`, not `risk`. See the note on .btn-danger. */
.input[aria-invalid='true'],
.select[aria-invalid='true'],
.textarea[aria-invalid='true'] { border-color: var(--c-error); }

.input[aria-invalid='true']:focus,
.textarea[aria-invalid='true']:focus {
  border-color: var(--c-error);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-error) 22%, transparent);
}

.error-text {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--t-body-sm-size);
  font-weight: 500;
  color: var(--c-error);
}

/* ------------------------------------------------------------------ switch */

.switch { display: inline-flex; align-items: center; gap: var(--s-md); cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }

.switch-track {
  width: 50px;
  height: 30px;
  flex: none;
  border-radius: var(--r-pill);
  background: var(--c-surface-container-highest);
  border: 2px solid var(--c-outline);
  position: relative;
  transition: background var(--m-fast) var(--m-easeOut), border-color var(--m-fast) var(--m-easeOut);
}

.switch-thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--c-outline);
  transition: transform var(--m-base) var(--m-spring), background var(--m-fast) var(--m-easeOut);
}

.switch input:checked + .switch-track { background: var(--c-primary); border-color: var(--c-primary); }
.switch input:checked + .switch-track .switch-thumb { transform: translateX(20px); background: var(--c-on-primary); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--c-primary); outline-offset: 2px; }
.switch input:disabled + .switch-track { opacity: 0.45; }

/* A switch inside a labelled row — the shape most settings take. */
.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-lg);
  padding: var(--s-md) var(--s-xl);
  border-bottom: 1px solid var(--c-outline-variant);
}

.switch-row:last-child { border-bottom: 0; }

/* -------------------------------------------------------------- checkbox */

.check { display: inline-flex; align-items: flex-start; gap: var(--s-md); cursor: pointer; }
.check input { width: 20px; height: 20px; margin-top: 1px; flex: none; accent-color: var(--c-primary); cursor: pointer; }

/* ------------------------------------------------------------ theme picker
   Three options, because "system" is a real preference and not a fallback:
   somebody whose OS switches on a schedule they did not choose needs to be
   able to pin this, and somebody who wants dark at all times should not have
   to change their whole machine to get it.                                   */

.theme-pick { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-md); }

.theme-opt {
  display: flex;
  flex-direction: column;
  gap: var(--s-sm);
  padding: var(--s-md);
  border-radius: var(--r-sm);
  border: 2px solid var(--c-outline-variant);
  background: var(--c-raised);
  color: var(--c-on-surface-variant);
  font-size: var(--t-label-md-size);
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--m-fast) var(--m-easeOut);
}

.theme-opt:hover { border-color: var(--c-outline); }
.theme-opt[aria-pressed='true'] { border-color: var(--c-primary); color: var(--c-on-surface); }

.theme-swatch {
  height: 40px;
  border-radius: var(--r-xs);
  border: 1px solid var(--c-outline-variant);
  display: flex;
  overflow: hidden;
}

.theme-swatch i { flex: 1; display: block; }
.theme-swatch .sw-light { background: #F6F2EA; }
.theme-swatch .sw-dark { background: #1F1B18; }
.theme-swatch .sw-accent { flex: 0 0 26%; background: linear-gradient(150deg, #B34E22, #8FA98C); }

/* ==========================================================================
   9. TABLES
   ========================================================================== */

/**
 * `position: relative` is not decoration — it is what makes the scroll actually
 * contain what it scrolls.
 *
 * `overflow` never clips an absolutely positioned descendant whose containing
 * block is OUTSIDE the scroller. The notification table has a visually-hidden
 * `<label>` in every checkbox cell, each 1px wide and each `position:absolute`,
 * sitting at the static position of its cell — up to x=564 in a 584px-wide
 * table. Those 1px boxes escaped the wrapper entirely and stretched the
 * document to 565px on a 390px phone, which then dragged the fixed bottom bar
 * out to 565px with it: the last tab was permanently off-screen and could not
 * be scrolled to, on every page, because of a label nobody can see.
 */
.table-wrap { position: relative; overflow-x: auto; -webkit-overflow-scrolling: touch; }

.table { width: 100%; font-size: var(--t-body-md-size); }

.table th {
  text-align: left;
  padding: var(--s-md) var(--s-lg);
  font-size: var(--t-label-md-size);
  font-weight: 600;
  color: var(--c-on-surface-variant);
  border-bottom: 1px solid var(--c-outline-variant);
  white-space: nowrap;
  background: var(--c-sunken);
  position: sticky;
  top: 0;
}

.table td { padding: var(--s-md) var(--s-lg); border-bottom: 1px solid var(--c-outline-variant); }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--c-hover); }
.table .num-cell { text-align: right; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   10. FEEDBACK — dialogs, toasts, empty states, loading
   ========================================================================== */

dialog.modal {
  padding: 0;

  /* `margin: auto` is what centres an open <dialog>, and the UA stylesheet
     normally supplies it. base.css's `* { margin: 0 }` reset removes it, so
     without this line every modal in the product renders jammed into the
     top-left corner — which looks like a broken layout rather than a dialog.
     Restated here rather than exempting <dialog> from the reset, so the reset
     stays one unconditional rule. */
  margin: auto;
  border: 1px solid var(--c-outline-variant);
  border-radius: var(--r-lg);
  background: var(--c-card);
  color: var(--c-on-surface);
  box-shadow: var(--shadow-4);
  max-width: min(560px, calc(100vw - 32px));
  width: 100%;
}

dialog.modal::backdrop { background: color-mix(in srgb, var(--c-scrim) 55%, transparent); backdrop-filter: blur(3px); }

.modal-head { padding: var(--s-xl) var(--s-xl) var(--s-md); }
.modal-head h2 { font-size: var(--t-headline-sm-size); }
.modal-body { padding: 0 var(--s-xl) var(--s-lg); color: var(--c-on-surface-variant); line-height: 1.6; }
.modal-foot { display: flex; justify-content: flex-end; gap: var(--s-md); padding: var(--s-lg) var(--s-xl); border-top: 1px solid var(--c-outline-variant); }

/**
 * The "nothing happened" dialog.
 *
 * Narrower than a normal modal and centred, because it carries one sentence and
 * one button. It exists because a toast is the wrong shape for a refusal: it
 * appears in a corner the eye is not on, it leaves on a timer whether or not it
 * was read, and on a phone it can sit under the bottom bar. A failed action has
 * to be acknowledged, so this one waits.
 */
.modal-notice { max-width: min(420px, calc(100vw - 32px)); text-align: center; }

.notice-body { padding: var(--s-2xl) var(--s-xl) var(--s-lg); }
.notice-body h2 { font-size: var(--t-title-lg-size); margin-bottom: var(--s-sm); }
.notice-body p { color: var(--c-on-surface-variant); line-height: 1.6; }

.notice-icon {
  width: 56px;
  height: 56px;
  margin: 0 auto var(--s-lg);
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
}

.notice-risk { background: var(--c-risk-container); color: var(--c-on-risk-container); }
.notice-info { background: var(--c-primary-container); color: var(--c-on-primary-container); }

/* The code is for a support conversation, not for the parent. Small, muted, and
   last — but present, because "something went wrong" with nothing to quote is a
   report nobody can act on. */
.notice-code {
  margin-top: var(--s-md);
  font-family: var(--font-mono);
  font-size: var(--t-label-sm-size);
  color: var(--c-outline);
}

.modal-notice .modal-foot { justify-content: center; }
.modal-notice .modal-foot .btn { min-width: 120px; }

/* Toasts live in a fixed region that is `aria-live=polite`, so an
   announcement does not interrupt whatever a screen reader is mid-sentence on. */
.toasts {
  position: fixed;
  z-index: 90;
  right: var(--s-lg);
  bottom: var(--s-lg);
  display: flex;
  flex-direction: column;
  gap: var(--s-sm);
  max-width: min(420px, calc(100vw - 32px));
  pointer-events: none;
}

@media (max-width: 1000px) { .toasts { bottom: 88px; } }

.toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: var(--s-md);
  padding: var(--s-md) var(--s-lg);
  border-radius: var(--r-sm);
  background: var(--c-inverse-surface);
  color: var(--c-inverse-on-surface);
  box-shadow: var(--shadow-3);
  font-size: var(--t-body-md-size);
  animation: pw-toast-in var(--m-base) var(--m-easeOut);
}

@keyframes pw-toast-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

.toast-safe { background: var(--c-safe); color: var(--c-on-safe); }
.toast-risk { background: var(--c-risk); color: var(--c-on-risk); }

/**
 * Empty state.
 *
 * Always has an action. "No devices yet" with nothing to press is a dead end,
 * and the empty state is the screen a new customer sees first — it is the
 * onboarding, whether it was designed as onboarding or not.
 */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-md);
  padding: var(--s-3xl) var(--s-xl);
  text-align: center;
}

.empty-icon {
  width: 60px;
  height: 60px;
  display: grid;
  place-items: center;
  border-radius: var(--r-md);
  background: var(--c-surface-container-high);
  color: var(--c-on-surface-variant);
  margin-bottom: var(--s-xs);
}

.empty h3 { font-size: var(--t-title-lg-size); }
.empty p { max-width: 42ch; color: var(--c-on-surface-variant); line-height: 1.6; }

/* Skeletons. Sized by the caller to match the content they stand in for —
   a skeleton that is the wrong shape causes a visible jump on load, which is
   worse than a spinner. */
.skeleton {
  border-radius: var(--r-xs);
  background: linear-gradient(
    90deg,
    var(--c-surface-container) 25%,
    var(--c-surface-container-high) 37%,
    var(--c-surface-container) 63%
  );
  background-size: 400% 100%;
  animation: pw-shimmer 1.4s ease-in-out infinite;
}

@keyframes pw-shimmer {
  from { background-position: 100% 50%; }
  to   { background-position: 0 50%; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; background: var(--c-surface-container-high); }
}

.skeleton-text { height: 12px; margin: 6px 0; }
.skeleton-title { height: 20px; width: 40%; margin: 8px 0; }

/* A banner for page-level state — an expiring trial, a phone that stopped
   reporting. Distinct from `.attention` because it spans the content width and
   is about the account rather than about a child. */
.banner {
  display: flex;
  align-items: center;
  gap: var(--s-md);
  padding: var(--s-md) var(--s-lg);
  border-radius: var(--r-sm);
  background: var(--c-primary-container);
  color: var(--c-on-primary-container);
  font-size: var(--t-body-md-size);
}

.banner-caution { background: var(--c-caution-container); color: var(--c-on-caution-container); }
.banner-risk { background: var(--c-risk-container); color: var(--c-on-risk-container); }

/* ==========================================================================
   11. MISC
   ========================================================================== */

.tabs { display: flex; gap: var(--s-xs); border-bottom: 1px solid var(--c-outline-variant); overflow-x: auto; }

.tab {
  padding: var(--s-md) var(--s-lg);
  border-bottom: 2px solid transparent;
  color: var(--c-on-surface-variant);
  font-size: var(--t-title-sm-size);
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
  transition: color var(--m-fast) var(--m-easeOut), border-color var(--m-fast) var(--m-easeOut);
}

.tab:hover { color: var(--c-on-surface); }
.tab[aria-selected='true'] { color: var(--c-primary-text); border-bottom-color: var(--c-primary); }

.map {
  width: 100%;
  min-height: 420px;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--c-surface-container-high);
}

/* Dark-mode map tiles — THE ONE THEME-SCOPED SELECTOR IN THIS FILE.
   
   Everything else here resolves its per-theme value through an intent-named
   token (--c-card, --c-raised) precisely so no rule has to be scoped by theme;
   see the note in tokens.json under `derived` for the specificity bug that
   caused. This rule is the exception and cannot use that mechanism: it is not
   choosing a colour, it is applying a FILTER to third-party raster images we
   do not control and cannot recolour. A custom property has nothing to hold.
   
   It is also safe in a way a surface rule is not — there is no `.map-tiles`
   variant for it to outrank, so the specificity trap has nothing to catch.

   Inverting and re-rotating the hue is a well-worn trick and it is not perfect,
   but it beats a blazing white rectangle in the middle of a dark page at 11pm,
   which is exactly when this screen gets used.

   Applied to the tile layer ONLY, never to .map itself: markers, the trail and
   the accuracy circle are drawn by us in tokens that are already correct for
   the theme, and inverting them would turn the child's pin into its opposite
   colour. */
[data-theme='dark'] .map-tiles {
  filter: invert(1) hue-rotate(180deg) brightness(0.92) contrast(0.9) saturate(0.75);
}

.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: thin; }

/* Keeps a horizontally scrolling strip from looking like it just ends. */
.fade-edge {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12px, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 12px, #000 calc(100% - 24px), transparent);
}

/* ==========================================================================
   99. PHONE ADJUSTMENTS
   ==========================================================================
   LAST IN THE FILE ON PURPOSE.

   These are overrides of components defined above, at the same specificity, so
   they only win by coming later. Written at the top with the type scale — which
   is where they belong conceptually, since they are the same "make this usable
   at 30cm" decision — they lost to every component rule beneath them and
   silently did nothing.

   Custom properties do not have this problem, which is why the token block
   above can stay where it reads best: a `--t-body-md-size` set anywhere is
   inherited by everything that reads it. A `min-height` is not.
   ========================================================================== */

@media (max-width: 700px) {
  /* 44px is the Apple guideline and 48px the Material one. The panel had 34px
     `.btn-sm` on every table row and card head, and a 38px child switcher —
     small enough that a thumb misses, on the controls used most often. */
  .btn     { min-height: 46px; }
  .btn-sm  { min-height: 42px; padding: 9px 14px; }
  .btn-lg  { min-height: 54px; }
  .btn-icon { min-width: 46px; }
  .who-btn { min-height: 42px; padding: 6px 16px 6px 7px; }

  .input, .select { min-height: 46px; }

  /* A checkbox or radio a thumb can actually land on. The visual box stays the
     size it was; the hit area around it grows. */
  .switch-track { flex: none; }
}
