/* FieldTrack colour tokens — single source of truth for both portals.
 *
 * Loaded before index.css / edit.css so those files (and inline styles) can
 * reference the variables. Every LIGHT value below is byte-identical to the
 * literal it replaced, so tokenising is a visual no-op until the dark block at
 * the bottom is allowed to apply.
 *
 * Why tokens are property-aware rather than one-per-hex: the same literal wore
 * two hats. #1a2b45 was both body text and the navy header background, and #fff
 * was both card surfaces and text sitting on a coloured button. Mapping either
 * to a single token would invert the header or bleach the buttons in dark mode,
 * so the replacement is driven by the CSS property, not the colour.
 */
:root {
  color-scheme: light;

  /* ── Restyle, 2026-08-17 (Option A) ────────────────────────────────────────
     Neutrals move from blue-grey to the near-white lilac family Zoho Expense
     uses: the page is almost white, cards sit on it with a hairline rather than
     a tone step, sidebar and inset panels are #f7f7fe, lines are #ececf9. Text
     darkens slightly to a cool charcoal. The accent stays FieldTrack's own blue.
     Radii come down from 14/8 to 8/6 — Zoho draws cards at 4px; 8 keeps a
     little of FieldTrack's softness. */

  /* Surfaces */
  --surface:        #fff;      /* cards, modals, menus */
  --surface-2:      #f7f7fe;   /* subtle raised rows, hovers, the sidebar */
  --surface-3:      #eef0f8;   /* chips, inset panels, thumbnails */
  --bg-page:        #fafaff;   /* one step off white so a white card still lifts */
  --bg-page-hover:  #f0f1fa;
  --navy:           #1e2a44;   /* top band — stays dark in BOTH themes */

  /* Text */
  --text:           #202432;
  --text-muted:     #58627f;
  --text-faint:     #8b93a8;
  --text-on-dark:   #fff;      /* on navy or on an accent fill */

  /* Lines */
  --line:           #ececf9;
  --line-strong:    #dedff3;

  /* Accent. Split in two because a colour that reads well as text on a page is
     not the same colour that white text can sit on. --accent-fill stays the
     deeper #007ab8: FieldTrack's brand #0096D6 under 13px white text is 3.2:1,
     which fails AA, and Zoho's own #408DFB has the same problem. */
  --accent:         #0b6fa4;   /* links, icons, accent text  (5.5:1 on white) */
  --accent-hover:   #085a86;
  --accent-fill:    #007ab8;   /* button background under white text */
  --accent-fill-hover: #006699;
  --accent-soft:    #e6f5fc;   /* tinted background for selected/active states */
  --accent-ink:     #0c447c;   /* text on --accent-soft */

  /* Brand blue for GRAPHICS only — map pins, flight arcs, SVG icon strokes.
     These are drawn by Leaflet/Chart/SVG attributes that cannot resolve var(),
     so this exists for documentation and CSS use, never for canvas. */
  --brand-graphic:  #0096D6;

  /* Status. Kept legible against their own tinted backgrounds in both themes. */
  --danger:         #dc2626;
  --danger-bg:      #fef2f2;
  --danger-border:  #fecaca;
  --danger-ink:     #991b1b;
  --success:        #16a34a;
  --success-bg:     #f0fdf4;
  --success-ink:    #166534;
  --warn-bg:        #fef3c7;
  --warn-ink:       #92400e;

  /* Badge palette. Previously duplicated in both page stylesheets with no dark
     variants, which is what left the active tab as light text on a light peach
     chip the first time dark mode was switched on. One definition now. */
  --badge-blue-bg:  #e0f4fb;  --badge-blue-text:  #0c447c;
  --badge-green-bg: #eaf3de;  --badge-green-text: #27500a;
  --badge-coral-bg: #fdeee4;  --badge-coral-text: #993c1d;
  --badge-amber-bg: #faeeda;  --badge-amber-text: #854f0b;
  --badge-gray-bg:  #eef0f8;  --badge-gray-text:  #58627f;


  /* Tinted chips and soft status fills. These were one-off literals paired with
     themed text, which is how the map popup buttons ended up light-on-light. */
  --chip-blue-bg:     #e0f0fb;  --chip-blue-border:   #baddee;
  --chip-purple-bg:   #f0ebf8;  --chip-purple-border: #c4b0e0;
  --chip-orange-bg:   #fff7ed;  --chip-orange-border: #f0c090;
  --danger-soft:      #fee2e2;
  --success-soft:     #dcfce7;
  --warn-border:      #fde68a;
  --info-soft:        #eef2ff;  --info-border:        #c7d2fe;  --info-ink:        #3730a3;

  /* Kept for compatibility with existing rules that reference them. */
  --blue:           #0096D6;
  --radius-sm: 6px; --radius-md: 8px; --radius-pill: 20px;
}

/* ── Dark theme ────────────────────────────────────────────────────────────
 * Follows the OS by default; the data-theme attribute (set from Settings) wins
 * over it in both directions so an explicit choice always sticks.
 *
 * Not a naive inversion: surfaces lift rather than flip, muted text is lightened
 * only as far as it stays clearly secondary, and the accent moves to a lighter
 * blue for text while the button fill stays deep enough for white to sit on.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --surface:        #161c2b;
    --surface-2:      #1a2133;
    --surface-3:      #1e2638;
    --bg-page:        #0f1420;
    --bg-page-hover:  #1a2133;
    --navy:           #0b0f19;

    --text:           #e6e9f0;
    --text-muted:     #9aa3b8;
    --text-faint:     #6f788c;
    --text-on-dark:   #fff;

    --line:           #252d3f;
    --line-strong:    #2f394f;

    --accent:         #6cc4f0;   /* text/links on a dark ground */
    --accent-hover:   #93d6f6;
    --accent-fill:    #0a6fa8;   /* keeps white label text legible */
    --accent-fill-hover: #0d84c6;
    --accent-soft:    #0e3350;
    --accent-ink:     #9fd4f0;

    --danger:         #f87171;
    --danger-bg:      #3a1a1a;
    --danger-border:  #5c2b2b;
    --danger-ink:     #fca5a5;
    --success:        #4ade80;
    --success-bg:     #12301f;
    --success-ink:    #86efac;
    --warn-bg:        #3a2f12;
    --warn-ink:       #fcd34d;

    --badge-blue-bg:  #12354f;  --badge-blue-text:  #9fd4f0;
    --badge-green-bg: #1d3016;  --badge-green-text: #b3d68f;
    --badge-coral-bg: #3a2318;  --badge-coral-text: #f0b394;
    --badge-amber-bg: #372a12;  --badge-amber-text: #e8c07a;
    --badge-gray-bg:  #1e2638;  --badge-gray-text:  #9aa3b8;

    --chip-blue-bg:     #12354f;  --chip-blue-border:   #1d5378;
    --chip-purple-bg:   #2a2140;  --chip-purple-border: #453563;
    --chip-orange-bg:   #3a2a17;  --chip-orange-border: #5e4426;
    --danger-soft:      #3a1a1a;
    --success-soft:     #12301f;
    --warn-border:      #5c4a1c;
  --info-soft:        #1c2340;  --info-border:        #33406b;  --info-ink:        #b9c6f5;
    --info-soft:        #1c2340;  --info-border:        #33406b;  --info-ink:        #b9c6f5;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --surface:        #161c2b;
  --surface-2:      #1a2133;
  --surface-3:      #1e2638;
  --bg-page:        #0f1420;
  --bg-page-hover:  #1a2133;
  --navy:           #0b0f19;

  --text:           #e6e9f0;
  --text-muted:     #9aa3b8;
  --text-faint:     #6f788c;
  --text-on-dark:   #fff;

  --line:           #252d3f;
  --line-strong:    #2f394f;

  --accent:         #6cc4f0;
  --accent-hover:   #93d6f6;
  --accent-fill:    #0a6fa8;
  --accent-fill-hover: #0d84c6;
  --accent-soft:    #0e3350;
  --accent-ink:     #9fd4f0;

  --danger:         #f87171;
  --danger-bg:      #3a1a1a;
  --danger-border:  #5c2b2b;
  --danger-ink:     #fca5a5;
  --success:        #4ade80;
  --success-bg:     #12301f;
  --success-ink:    #86efac;
  --warn-bg:        #3a2f12;
  --warn-ink:       #fcd34d;

  --badge-blue-bg:  #12354f;  --badge-blue-text:  #9fd4f0;
  --badge-green-bg: #1d3016;  --badge-green-text: #b3d68f;
  --badge-coral-bg: #3a2318;  --badge-coral-text: #f0b394;
  --badge-amber-bg: #372a12;  --badge-amber-text: #e8c07a;
  --badge-gray-bg:  #1e2638;  --badge-gray-text:  #9aa3b8;

  --chip-blue-bg:     #12354f;  --chip-blue-border:   #1d5378;
  --chip-purple-bg:   #2a2140;  --chip-purple-border: #453563;
  --chip-orange-bg:   #3a2a17;  --chip-orange-border: #5e4426;
  --danger-soft:      #3a1a1a;
  --success-soft:     #12301f;
  --warn-border:      #5c4a1c;
  --info-soft:        #1c2340;  --info-border:        #33406b;  --info-ink:        #b9c6f5;
}

/* The two dark blocks above must stay in step. A mismatch is invisible until
   someone picks a theme explicitly instead of inheriting the OS one, so any new
   token added to one belongs in the other. */

/* ── Third-party map chrome ────────────────────────────────────────────────
 * Leaflet paints its own popup, tooltip and controls white from its stylesheet
 * (".leaflet-popup-content-wrapper, .leaflet-popup-tip { background: white }").
 * Our popup CONTENT is tokenised, so in dark mode that left light text on a
 * white card — unreadable, and exactly what a pin click showed.
 *
 * Lives here rather than in the page stylesheets so both apps get it once.
 * theme.css loads after leaflet.css, so equal-specificity selectors win without
 * needing !important; the close button is matched as a.leaflet-… because
 * Leaflet's own rule for it is element-qualified.
 *
 * The map TILES are deliberately left alone — they are imagery, and inverting
 * them makes roads and labels read wrongly. A dark tile layer is a separate
 * decision, not a CSS override.
 */
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
  background: var(--surface);
  color: var(--text);
}
a.leaflet-popup-close-button        { color: var(--text-muted); }
a.leaflet-popup-close-button:hover  { color: var(--text); background: transparent; }

.leaflet-tooltip {
  background-color: var(--surface);
  border-color: var(--line);
  color: var(--text);
}

/* Member name labels. Deliberately lighter than a default tooltip: a dozen of
   these on screen at once must read as annotation, not as a dozen popups
   competing with the map. No border, no arrow, tight padding — the halo does
   the work of separating text from whatever tile is underneath. */
.leaflet-tooltip.member-label {
  background: transparent;
  border: none;
  box-shadow: none;
  padding: 0 2px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  /* Fixed dark-on-white, NOT theme tokens. These sit on map tiles, and the tiles
     stay light in both themes by design — inverting imagery makes roads and
     labels read wrongly. Using var(--text) here flipped the label to near-white
     in dark mode, leaving light text on a light map held together only by its
     halo. Same reasoning as the map legend on the share page. */
  color: #1b2431;
  text-shadow:
     0 0 3px #fff, 0 0 3px #fff,
     1px 1px 0 #fff, -1px -1px 0 #fff,
     1px -1px 0 #fff, -1px 1px 0 #fff;
}
.leaflet-tooltip.member-label::before { display: none; }   /* no callout arrow */
.leaflet-tooltip-top::before    { border-top-color: var(--surface); }
.leaflet-tooltip-bottom::before { border-bottom-color: var(--surface); }
.leaflet-tooltip-left::before   { border-left-color: var(--surface); }
.leaflet-tooltip-right::before  { border-right-color: var(--surface); }

.leaflet-bar a,
.leaflet-bar a:link,
.leaflet-bar a:visited {
  background-color: var(--surface);
  color: var(--text);
  border-bottom-color: var(--line);
}
.leaflet-bar a:hover { background-color: var(--surface-2); color: var(--text); }
.leaflet-control-layers,
.leaflet-control-layers-expanded { background: var(--surface); color: var(--text); }
.leaflet-container .leaflet-control-attribution {
  background: var(--surface);
  color: var(--text-muted);
}
.leaflet-container .leaflet-control-attribution a { color: var(--accent); }

/* ── Layer order ───────────────────────────────────────────────────────────
 * Leaflet ships its control container at z-index 1000, which put the
 * "Leaflet | OpenStreetMap" attribution above the fixed bottom nav while
 * scrolling the map tab on a phone. Controls only need to clear Leaflet's own
 * panes — the popup pane is the highest at 700 — so 800 keeps the zoom buttons
 * clickable over popups while sitting below app chrome.
 *
 * The intended stack, low to high:
 *   400-700  Leaflet panes (tiles, markers, tooltips, popups)
 *   800      Leaflet controls
 *   900      #bottom-nav
 *   1000-2   modal backdrop / modal / disambiguation
 *   100000+  impersonation banner, settings, toasts, confirm, command palette
 */
.leaflet-top,
.leaflet-bottom { z-index: 800; }

/* ── Picker search boxes ────────────────────────────────────────────────────
 * ftEnhanceSelect's search box is a one-row <textarea>, not an <input> — see
 * map-common.js for why (Chrome's password manager). Its look is set inline
 * there; this only stops the two apps' broad `textarea` rules (resize handle,
 * 60px floor) from reaching it should the inline styles ever be trimmed.
 */
textarea.ft-picker-input { resize: none; min-height: 0; overflow: hidden; }

/* ── Text action ────────────────────────────────────────────────────────────
 * A button that reads as a link: secondary next to a real button, and does not
 * take a 40px chip on mobile for something used once a year. Lived in index.css
 * only, so the admin app's first use of it (the card portal) rendered as a
 * default browser button. One definition, both apps.
 */
.btn-link {
  background: none; border: 0; padding: .35rem 0; cursor: pointer;
  font-family: inherit; font-size: .8rem; font-weight: 600;
  color: var(--accent-ink); text-decoration: underline; text-underline-offset: 2px;
}
.btn-link.danger { color: var(--danger); }
.btn-link:hover { text-decoration-thickness: 2px; }

/* ── Comments & History thread (both portals) ─────────────────────────────── */
.thread { display: flex; flex-direction: column; gap: .5rem; }
.thread-empty { font-size: .82rem; color: var(--text-faint); padding: .3rem 0; }
.thread-event { font-size: .8rem; color: var(--text); padding: .15rem 0 .15rem .75rem; border-left: 2px solid var(--line-strong); }
.thread-event b { font-weight: 600; }
.thread-when { color: var(--text-muted); font-size: .74rem; }
.thread-note { color: var(--text-muted); font-size: .8rem; margin-top: .15rem; white-space: pre-wrap; }
.thread-comment { background: var(--surface-2); border: 1px solid var(--line); border-radius: 10px; padding: .55rem .75rem; }
.thread-who { font-size: .78rem; font-weight: 600; color: var(--text); }
.thread-text { font-size: .85rem; color: var(--text); margin-top: .2rem; white-space: pre-wrap; }
.thread-compose { display: flex; gap: .5rem; align-items: flex-end; margin-top: .6rem; }
.thread-compose textarea { flex: 1; resize: vertical; min-height: 2.4rem; }

/* ── The itinerary table (ftItineraryTable): consultant builds it, Travel reads it ── */
.itin { width: 100%; border-collapse: collapse; font-size: .84rem; }
.itin th { text-align: left; font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-faint); padding: .3rem .4rem; border-bottom: 1px solid var(--line); background: none; position: static; }
.itin td { padding: .45rem .4rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.itin tr:last-child td { border-bottom: 0; }
.itin .itin-g { width: 1.4rem; color: var(--text-muted); }
.itin .itin-when { white-space: nowrap; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.itin .itin-what { font-weight: 600; color: var(--text); }
.itin .itin-pref { color: var(--text-muted); }
.itin .itin-edit { width: 1.4rem; color: var(--text-faint); text-align: right; }
.itin tr[role="button"] { cursor: pointer; }
.itin tr[role="button"]:hover td { background: var(--surface-2); }
/* Card mode (ftItineraryTable opts.cards) — the request editor, where a row is
   something still being asked for and its state matters more than alignment. */
.itin-cards { display: flex; flex-direction: column; gap: .5rem; }
.itin-card { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .35rem .6rem; padding: .6rem .7rem; border: 1px solid var(--line-strong); border-radius: var(--radius-md); background: var(--surface); }
.itin-card[role="button"] { cursor: pointer; }
.itin-card[role="button"]:hover { border-color: var(--accent); background: var(--surface-2); }
.itin-card-g { flex: 0 0 auto; color: var(--text-muted); padding-top: .15rem; }
.itin-card-main { flex: 1 1 11rem; min-width: 0; }
.itin-card-what { font-weight: 600; font-size: .9rem; color: var(--text); }
.itin-card-when { font-size: .78rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.itin-card-pref { font-size: .78rem; color: var(--text-muted); margin-top: .15rem; }
.itin-card-e { flex: 0 0 auto; color: var(--text-faint); align-self: center; }
.itin-chip { flex: 0 0 auto; margin-left: auto; align-self: center; font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; padding: .15rem .45rem; border-radius: 999px; white-space: nowrap; }
.itin-chip.ok { background: var(--success-bg); color: var(--success-ink, #1c6b3d); }
.itin-chip.wait { background: var(--surface-3); color: var(--text-muted); }
.itin-empty { font-size: .84rem; color: var(--text-muted); padding: .4rem 0; }
.itin-notes { font-size: .82rem; color: var(--text-muted); margin-top: .4rem; padding-top: .35rem; border-top: 1px dashed var(--line); }
@media (max-width: 560px) {
  .itin thead { display: none; }
  .itin tr { display: grid; grid-template-columns: 1.4rem 1fr auto; padding: .4rem 0; border-bottom: 1px solid var(--line); }
  .itin td { border: 0; padding: 0 .3rem; }
  .itin .itin-when { grid-column: 2; grid-row: 1; font-size: .74rem; }
  .itin .itin-what { grid-column: 2; grid-row: 2; }
  .itin .itin-pref { grid-column: 2; grid-row: 3; font-size: .78rem; }
  .itin .itin-g { grid-row: 1 / span 3; padding-top: .15rem; }
  .itin .itin-edit { grid-column: 3; grid-row: 1 / span 3; align-self: center; }
}

/* ── The row card: the one list row every screen in both portals uses ── */
/* The row. Grid: [thumb] [identity / detail / meta] [right column]. A problem
   goes in .row-inline under the row, never in a hover. */
/* Shared, because map-common.js renders markup that uses it and edit.html
   never loads index.css. Kept out of the tab order only visually — the element
   still has to be reachable and readable. */
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
                   clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; }
.row-card { display: block; width: 100%; text-align: left; background: none; border: 0; border-bottom: 1px solid var(--line); padding: 0; cursor: pointer; font: inherit; color: var(--text); }
.row-card:hover { background: var(--surface-2); }
.row-card.sel { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
.row-card:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.row-main { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: .75rem; align-items: start; padding: .75rem .9rem; }
.row-thumb { width: 38px; height: 38px; border-radius: 6px; background: var(--surface-3); color: var(--text-faint); display: grid; place-items: center; font-size: .72rem; font-weight: 700; letter-spacing: .04em; overflow: hidden; }
.row-thumb img { width: 100%; height: 100%; object-fit: cover; }
.row-thumb.stripe { width: 6px; border-radius: 3px; height: 38px; }
.row-id { font-size: .88rem; font-weight: 600; color: var(--text); }
.row-id .sep { display: inline-block; width: 1px; height: 10px; background: var(--line-strong); margin: 0 .45rem; vertical-align: -1px; }
.row-id .light { font-weight: 400; color: var(--text-muted); }
.row-detail { font-size: .8rem; color: var(--accent); font-weight: 500; margin-top: .1rem; }
.row-desc { font-size: .78rem; color: var(--text-muted); font-style: italic; margin-top: .05rem; }
.row-meta { font-size: .74rem; color: var(--text-muted); margin-top: .35rem; display: flex; gap: .8rem; flex-wrap: wrap; }
.row-meta b { font-weight: 500; color: var(--text); }
.row-right { display: flex; flex-direction: column; align-items: flex-end; gap: .35rem; }
.row-amt { font-size: .88rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.row-amt small { display: block; font-size: .72rem; color: var(--text-muted); text-align: right; }
.row-inline { display: flex; gap: .45rem; align-items: center; padding: .4rem .9rem .55rem 3.6rem; font-size: .78rem; color: var(--text); border-top: 1px dashed var(--line); }
.row-inline i { color: var(--warn-ink); }
.row-inline .fix { color: var(--accent); font-style: italic; margin-left: auto; white-space: nowrap; }
.row-inline.bad i { color: var(--danger); }
.row-empty { padding: 1.2rem .9rem; color: var(--text-muted); font-size: .86rem; }
.row-sub { padding: .55rem .9rem .3rem; font-size: .68rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint); }

/* Airport typeahead (both portals): a list under the input, a hint beside it. */
.ft-air-list { position: absolute; left: 0; right: 0; top: 100%; z-index: 30; background: var(--surface); border: 1px solid var(--line-strong); border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,.12); max-height: 14rem; overflow: auto; margin-top: 2px; }
.ft-air-opt { padding: .4rem .6rem; font-size: .84rem; cursor: pointer; display: flex; gap: .5rem; align-items: baseline; }
.ft-air-opt b { font-variant-numeric: tabular-nums; min-width: 2.6em; }
.ft-air-opt.on, .ft-air-opt:hover { background: var(--accent-soft); color: var(--accent-ink); }
.ft-air-hint { font-size: .72rem; color: var(--text-muted); min-height: 1em; margin-top: .15rem; }

/* City typeahead: the same list, but the city is the thing being read, so the
   state sits quiet and right-aligned rather than leading in bold like a code. */
.ft-city-opt { justify-content: space-between; }
.ft-city-st { color: var(--text-muted); font-size: .78rem; letter-spacing: .02em; }
.ft-city-opt.on .ft-city-st, .ft-city-opt:hover .ft-city-st { color: inherit; opacity: .75; }

/* ── Stopping the phone zooming in and staying there ───────────────────────
   Two separate causes, both ours, both reported from a phone on 2026-08-26:
   an action zooms the screen and it never zooms back, leaving the edges of
   the app off-screen until the reader pinches out again.

   1. iOS Safari zooms whenever a field with a computed font-size UNDER 16px
      takes focus, and deliberately does not undo it on blur. index.css has
      known this since the restyle — "16px stops iOS zooming on focus" — but
      only inside @media(max-width:600px) on the consultant portal. The admin
      portal had no such rule at any width, the shared Settings sheet is
      .9rem, and login and register are .95rem: 15.2px, missing it by eight
      tenths of a pixel, so the very first tap anyone makes on a phone zooms
      them in before they are signed in.

      !important, which is otherwise not this codebase's habit, because the
      sizes being overridden are INLINE — a dozen of them across three JS
      files and two HTML files, several inside strings built at render time.
      A stylesheet cannot outrank an inline style any other way, and chasing
      them individually would leave the next one to be written broken again.

      pointer:coarse rather than a width, so a phone in landscape and an iPad
      are covered too — both of which the 600px rule misses.

   2. Double-tap zoom, which is what fires when nobody was typing at all.
      touch-action:manipulation keeps panning and pinch-zoom and drops only
      the double-tap gesture. [onclick] is deliberate: a tappable div with an
      inline handler is this codebase's dominant pattern, and naming every
      row class would date immediately. No !important, so the crop editor's
      own touch-action:none still wins — it is declared later, in index.css. */
@media (pointer: coarse) {
  input, select, textarea { font-size: 16px !important; }
}
button, [role="button"], [onclick], a, label, summary, select,
input[type="checkbox"], input[type="radio"], input[type="submit"], input[type="button"] {
  touch-action: manipulation;
}

/* ── Touch targets ─────────────────────────────────────────────────────────
   This codebase already commits to 44px, but only inside the Settings modal
   (ftEnsureSettingsStyle), and nothing enforced it anywhere else. Driving the
   app in a real browser at 390px found the gap: a 13x13 checkbox for "paid with
   the company card", 15x15 map layer toggles and roster row-pickers, and 27x34
   arrows for stepping between receipts — which is the single most repeated
   action a consultant has.

   Only on a coarse pointer. A mouse hits 13px fine, and growing every control
   on the desktop screens would change layouts nobody asked to change. */
@media (pointer: coarse) {
  /* Small square controls: give them the whole 44, they sit in flex rows with
     room to spare. */
  .date-arrow,
  #map-refresh-btn,
  #rc-prev, #rc-next,
  .rc-split-mode > button,
  .btn-today {
    min-width: 44px;
    min-height: 44px;
  }

  /* A checkbox cannot carry a pseudo-element to grow its hit area — it is a
     replaced element — so the box itself grows and the row that owns it gets a
     real height. 22px is the largest that still reads as a checkbox rather than
     a panel. */
  input[type="checkbox"], input[type="radio"], .ft-check {
    width: 22px;
    height: 22px;
  }
  /* The label IS the tap target for a checkbox; enlarging a 22px box alone
     still leaves most of the row dead. */
  label:has(> input[type="checkbox"]),
  label:has(> input[type="radio"]) {
    min-height: 44px;
    align-items: center;
  }
  /* The roster and trip-list row pickers, which are a bare label around a
     checkbox inside a row card. */
  .row-pick { min-width: 44px; min-height: 44px; }
}
