/* Type: 14px across the portal, Today at 15px (decided 2026-08-18) — the same
   scale as the admin app, one notch larger on the page read at arm's length. */
html { font-size: 14px; }
#tab-today { zoom: 1.0714; }


      /* ── Design tokens ── */
      /* Colour tokens now live in theme.css — one source of truth for both apps. */

      /* ── Business card ──
         Two surfaces. The chooser is app UI. The presented card is not: it is
         the only thing a client ever sees, so it carries no app chrome and no
         trace of the choice that produced it. */

      /* Choosing. Full-width rows rather than a segmented control, because the
         organisation names are long and getting this wrong is the one failure
         that matters. */
      .card-choose{display:grid;gap:.55rem}
      .card-choose-lead{font-size:.86rem;color:var(--text-muted);margin-bottom:.15rem}
      .card-choose-btn{
        display:block;width:100%;text-align:left;cursor:pointer;
        padding:.85rem 1rem;border:1.5px solid var(--line);border-radius:12px;
        background:var(--surface);transition:border-color .15s,background .15s
      }
      .card-choose-btn:hover{border-color:var(--accent);background:var(--surface-2)}
      .card-choose-name{display:block;font-weight:700;font-size:.95rem;color:var(--text)}
      .card-choose-sub{display:block;font-size:.78rem;color:var(--text-muted);margin-top:.1rem}
      .card-views{font-size:.76rem;color:var(--text-faint);text-align:center;margin-top:.9rem;min-height:1.1em}

      /* ── The presented card ──
         Deliberately does not use the app's theme tokens. This surface belongs
         to the brand on it, not to FieldTrack, and it must look identical
         whichever theme the consultant happens to run. Colours arrive from the
         server as --cp-stock / --cp-ink / --cp-accent / --cp-muted so one
         brand's card never inherits the other's.

         The stock is light, and that is not a style preference. Both real marks
         are dark-on-transparent — Gallagher Bassett's wordmark is #053145 and
         Focus's is #4D4D4D grey — so the dark ground this surface used to have
         rendered both of them invisible. The ink now lives in a band along the
         card's foot, which is where a printed card puts it anyway.

         It fills the screen edge to edge: the phone is being handed across a
         table, so the card should be the whole object, not a panel inside an
         app. Mirrors the public page at /card/:token/:brand. */
      #card-present{
        position:fixed;inset:0;z-index:10000;
        background:var(--cp-stock,#fbfcfd);color:var(--cp-ink,#4d4d4d);
        overscroll-behavior:contain;
        /* The strip the controls live in. Tall enough for the pill, its offset
           and the home indicator beneath it. */
        --cp-gutter:calc(3.4rem + max(env(safe-area-inset-bottom,0px),0px))
      }
      /* Rotated for the person opposite. Only the card — the controls stay the
         consultant's way up.

         The card stops short of the gutter rather than running under it, and
         that is not a nicety. Rotating the face 180deg puts its TOP edge — the
         logo — at the physical bottom of the screen, which is exactly where the
         controls sit. Overlapping them is the default outcome; this is the fix.
         The strip left over is the stock colour, so in the rotated view it
         reads as a continuation of the card rather than a letterbox. */
      .cp-face{
        position:absolute;left:0;right:0;
        top:max(env(safe-area-inset-top,0px),0px);
        bottom:var(--cp-gutter);
        display:flex;flex-direction:column;
        transform:rotate(180deg);transition:transform .28s ease
      }
      .cp-face.upright{transform:rotate(0deg)}
      @media (prefers-reduced-motion:reduce){.cp-face{transition:none}}

      /* The engraved rule — a letterpress card is blind-debossed at the border,
         and that reads as print in a way a drop shadow never does. */
      .cp-face::before{
        content:'';position:absolute;inset:.7rem;border:1px solid rgba(0,0,0,.08);
        border-radius:10px;pointer-events:none
      }
      .cp-inner{
        flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;
        justify-content:center;text-align:center;
        /* Symmetric: the safe areas are handled on .cp-face, which is the part
           that knows nothing about which way up it currently is. */
        padding:1.25rem 1.75rem
      }
      /* Fit-within-box, so proportion decides the size rather than a per-brand
         rule. The two marks are nothing alike — Gallagher Bassett's is 3145x261
         (about 12:1), Focus's is 317x39 (about 8:1) — and an uploaded
         replacement could be any shape at all. Capping both axes lets a wide
         wordmark run full width and a square or stacked mark stop at the
         height, with no special-casing. */
      .cp-logo{
        max-width:min(19rem,100%);max-height:2.75rem;margin-bottom:1.75rem;
        display:flex;align-items:center;justify-content:center
      }
      .cp-logo svg,.cp-logo img{max-width:100%;max-height:2.75rem;width:auto;height:auto;display:block}
      .cp-photo{
        width:92px;height:92px;border-radius:50%;object-fit:cover;margin-bottom:1rem;
        box-shadow:0 0 0 1px rgba(0,0,0,.08),0 6px 20px rgba(0,0,0,.16)
      }
      /* A serif, and the only one on the surface. This is the line that has to
         read as engraved from across a desk, and Georgia is on every device the
         PWA installs to — no font file in the precached shell. */
      .cp-name{
        font:600 1.95rem/1.12 Georgia,'Times New Roman',serif;letter-spacing:-.012em;
        color:var(--cp-ink,#4d4d4d);text-wrap:balance
      }
      /* accent-ink, not accent. Focus's brand cyan is a 3.3:1 on their stock —
         fine as a 2px rule, not fine as small bold type. */
      .cp-title{
        margin-top:.45rem;font-size:.7rem;font-weight:700;letter-spacing:.15em;
        text-transform:uppercase;color:var(--cp-accent-ink,#00719f);text-wrap:balance
      }
      /* The organisation's own words, set as the beats they were written in. */
      .cp-tag{margin-top:1.4rem;padding-top:1.15rem;border-top:1px solid rgba(0,0,0,.09);width:min(20rem,100%)}
      .cp-tag i{display:block;font:italic 400 .86rem/1.75 Georgia,'Times New Roman',serif;color:var(--cp-muted,#6f7a82)}
      /* A pure-white panel, always. The stock is near-white but not white, and a
         scanner reading a code off a tinted ground is the commonest way these
         fail in the field. */
      .cp-qr{
        background:#fff;padding:.8rem;border-radius:12px;margin-top:1.6rem;line-height:0;
        box-shadow:0 0 0 1px rgba(0,0,0,.07),0 8px 28px rgba(0,0,0,.10)
      }
      .cp-qr svg{width:min(15rem,56vw);height:auto;display:block}
      .cp-scan{
        margin-top:.85rem;font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;
        color:var(--cp-muted,#6f7a82)
      }
      /* The card's foot, capped by a hairline of the brand's accent — a foil
         rule, and the one flourish on the surface. */
      .cp-band{
        background:var(--cp-ink,#4d4d4d);color:rgba(255,255,255,.78);
        border-top:2px solid var(--cp-accent,#0096d6);
        padding:1rem 1.5rem 1.05rem;
        text-align:center;font-size:.76rem;line-height:1.55
      }
      .cp-band strong{display:block;color:#fff;font-weight:600;font-size:.83rem;margin-bottom:.2rem}

      /* Along the consultant's own edge and upright. A floating pill rather than
         bare buttons, because it has to stay legible over both the stock and the
         ink band — whichever way round the card currently is. */
      /* Centred in the gutter the card leaves for it, never over the card. */
      .cp-bar{
        position:absolute;left:0;right:0;z-index:1;
        bottom:calc(.55rem + max(env(safe-area-inset-bottom,0px),0px));
        display:flex;justify-content:center
      }
      .cp-bar-pill{
        display:flex;gap:.15rem;padding:.2rem;border-radius:999px;
        background:rgba(255,255,255,.92);border:1px solid rgba(0,0,0,.10);
        box-shadow:0 4px 18px rgba(0,0,0,.20);backdrop-filter:blur(6px)
      }
      .cp-btn{
        width:38px;height:38px;border-radius:50%;cursor:pointer;
        background:transparent;border:0;color:#4a555e;font-size:1rem;
        display:flex;align-items:center;justify-content:center
      }
      .cp-btn:hover{background:rgba(0,0,0,.06)}
      .cp-btn:focus-visible{outline:2px solid var(--cp-accent-ink,#00719f);outline-offset:1px}
      .cp-warn{
        position:absolute;top:max(env(safe-area-inset-top,0px),.5rem);left:1rem;right:1rem;z-index:1;
        text-align:center;font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;
        color:#8a3a12;background:#fff4ec;border:1px dashed #e8b48f;
        border-radius:6px;padding:.3rem .5rem
      }
      .card-foot{margin-top:1rem;padding-top:.85rem;border-top:1px solid var(--line);text-align:center}
      .card-warn{
        margin-top:.6rem;font-size:.72rem;letter-spacing:.04em;color:#b04a1a;background:#fff4ec;
        border:1px dashed #e8b48f;border-radius:6px;padding:.35rem .5rem;text-align:center
      }
      /* No wrapping. The native file input is hidden and the label carries the
         hit target, so the row is preview + one button + one text action and
         fits a 320px screen without stacking. */
      .card-photo-row{display:flex;align-items:center;gap:.6rem}
      .card-photo-pick{
        display:inline-flex;align-items:center;justify-content:center;
        cursor:pointer;white-space:nowrap;flex:0 0 auto
      }
      /* Kept out of the layout but reachable by keyboard and screen readers,
         because the label is what is visible and it needs a real input behind
         it. display:none would take it out of the tab order. */
      /* .visually-hidden is defined in theme.css — both portals need it. */
      /* Keyboard focus lands on the hidden input, so the ring is drawn on the
         label sitting immediately after it. Without this, tabbing through the
         form appears to skip a control. */
      #card-photo-in:focus-visible + .card-photo-pick{outline:2px solid var(--accent);outline-offset:2px}

      /* .btn-link (the text action) lives in theme.css — shared with the admin app. */

      /* Save and Back on one row, always. Right-aligned, and neither grows to
         fill — the previous markup let .btn-primary's display:flex turn Save
         into a block, which is what stacked them on a phone. */
      .card-actions{
        display:flex;justify-content:flex-end;align-items:center;gap:.5rem;margin-top:1.1rem
      }
      .card-actions .btn-primary,.card-actions .btn-sm{flex:0 0 auto;width:auto}

      /* The rare, destructive one. Its own section, below the primary actions,
         with the consequence spelled out before the control rather than only in
         the confirm dialog. */
      /* The label and its copy shortcut share a line, so the shortcut reads as
         belonging to the Gallagher Bassett field rather than floating above it. */
      /* ── Receipts ──
         Two moments, two shapes. Capture happens one-handed at a pump in bad
         light and must cost one tap. Review happens later, sitting down, and
         can afford detail. Nothing here asks a question at capture time. */

      /* The capture control. Deliberately the largest thing in its card — this
         is the only button in the app someone presses while holding a fuel
         nozzle. */
      .rc-capture{
        display:flex;align-items:center;justify-content:center;gap:.5rem;width:100%;
        min-height:52px;border-radius:12px;cursor:pointer;
        background:var(--accent-fill);border:1.5px solid var(--accent);color:var(--text-on-dark);
        font-size:.95rem;font-weight:700
      }
      .rc-capture:hover{background:var(--accent-fill-hover)}
      .rc-capture:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
      .rc-capture[disabled]{opacity:.6;cursor:default}
      .rc-count{font-size:.8rem;color:var(--text-muted);text-align:center;margin-top:.55rem;min-height:1.1em}
      /* Camera and file picker side by side (2026-08-18); the picker is the
         quieter of the two — the camera is still the one-handed default. */
            .rc-capture-2{padding:.35rem .8rem;line-height:1.25}
      .rc-cap-txt{display:flex;flex-direction:column;align-items:flex-start;min-width:0}
      .rc-cap-sub{font-size:.72rem;font-weight:500;opacity:.85;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rc-capture-row{display:flex;gap:.6rem}
      .rc-capture-row .rc-capture{flex:1;min-width:0}
      .rc-capture.rc-capture-alt{background:var(--surface);color:var(--accent-ink);border-color:var(--line-strong)}
      .rc-capture.rc-capture-alt:hover{background:var(--surface-2)}
      /* One tap to assign a row that has no trip */
      .rc-suggest{display:inline-flex;align-items:center;gap:.3rem;margin-top:.35rem;padding:.2rem .6rem;border-radius:999px;border:1px solid var(--accent);background:var(--accent-soft);color:var(--accent-ink);font:600 .74rem/1.4 inherit;cursor:pointer}
      .rc-suggest:hover{background:var(--accent-fill);color:var(--text-on-dark)}
      /* Where forwarded receipts land, for the next little while. Sits inside
         a section about something else, so it takes one line and no colour
         until it is actually running. */
      .fw-live{background:var(--accent-soft);border-radius:8px;padding:.4rem .55rem}
      .fw-live span{color:var(--accent-ink);font-weight:600}
      .fw-body{padding:.2rem .1rem .4rem}
      .fw-what{margin:0 0 .5rem;font-size:.9rem;color:var(--text)}
      .fw-how{margin:0 0 .8rem;font-size:.8rem;line-height:1.45;color:var(--text-muted)}
      .fw-how b{color:var(--text);word-break:break-all}
      .fw-mins{display:flex;gap:.4rem;flex-wrap:wrap}
      .fw-min{flex:1;min-width:4.5rem;padding:.45rem .3rem;border-radius:8px;border:1.5px solid var(--line);
        background:var(--surface);color:var(--text);font:600 .82rem/1 inherit;cursor:pointer}
      .fw-min.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-ink)}
      .fw-msg{margin-top:.6rem;font-size:.8rem;color:var(--danger,#b91c1c);min-height:1em}
      /* The address, on the half of the modal that exists to hand it over.
         Wraps rather than scrolls: it is long, and a phone should show all of
         it so it can be read back against what was pasted. */
      .fw-addr-row{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
      .fw-addr{
        flex:1;min-width:12rem;padding:.45rem .55rem;border-radius:8px;
        background:var(--surface-2);border:1px solid var(--line);
        font:600 .82rem/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;
        color:var(--text);word-break:break-all;user-select:all
      }
      #fw-done-msg{color:var(--text-muted)}
      /* The review page: a receipt dated outside its trip is said in colour */
      .rc-date-warn{margin-top:.75rem;padding:.55rem .75rem;border-radius:8px;background:var(--warn-bg,#fff7e6);border:1px solid var(--warn-border,#f0c36d);color:var(--warn-ink,#7a4d00);font-size:.85rem;display:flex;gap:.45rem;align-items:flex-start}
      .rc-help{display:inline-flex;align-items:center;justify-content:center;width:1.1rem;height:1.1rem;border-radius:50%;background:var(--surface-3);color:var(--text-muted);font-size:.68rem;font-weight:700;margin-left:.3rem;cursor:help}
      .rc-help-text{font-size:.78rem;color:var(--text-muted);margin:.15rem 0 .4rem}
      .rc-card-hint{display:block;font-size:.72rem;font-weight:600;color:var(--accent-ink);margin-top:.2rem;min-height:1em}
      .rc-pdf{display:flex;align-items:center;gap:.5rem;justify-content:center;padding:1.2rem;border:1px dashed var(--line-strong);border-radius:10px;font-size:.88rem;color:var(--text-muted)}
      .rc-forward{font-size:.8rem;color:var(--text-muted);text-align:center;margin-top:.5rem}
      .rc-extras{font-size:.8rem;color:var(--text-muted);margin-top:.5rem}
      .rc-extras a{color:var(--accent);text-decoration:none;font-weight:600}
      .rc-extras-l{font-weight:600}
      /* TripWire (2026-08-18): row cards in an enclosed list; status and
         specialty as chips; matches indented under their wire. */
      .row-list{border:1px solid var(--line);border-radius:var(--radius-md);overflow:hidden;background:var(--surface)}
      .row-list .row-card:last-child{border-bottom:0}
      .tw-row .row-right.tw-actions{display:flex;gap:.35rem;align-items:flex-start;flex-shrink:0}
      .tw-row.past{opacity:.75}
      .tw-status{display:inline-block;font-size:.66rem;font-weight:700;text-transform:capitalize;padding:.05rem .45rem;border-radius:999px;background:var(--surface-3);color:var(--text-muted);vertical-align:middle;margin-left:.25rem}
      .tw-status.active{background:var(--success-bg);color:var(--success-ink)}
      .tw-status.triggered{background:var(--accent-soft);color:var(--accent-ink)}
      .tw-tag{display:inline-flex;align-items:center;gap:.25rem;font-size:.74rem;font-weight:600;color:var(--accent-ink);background:var(--accent-soft);border-radius:999px;padding:.02rem .5rem}
      .tw-events{margin-top:.45rem;display:flex;flex-direction:column;gap:.3rem}
      .tw-event{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.4rem .6rem;background:var(--surface-2);border-radius:7px;font-size:.8rem}
      .tw-event-main{flex:1;min-width:0}
      .tw-outcome{color:var(--accent);font-size:.76rem}
      .tw-history-h{font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--text-faint);margin-bottom:.4rem}
      .rc-forward code{font-size:.8rem;background:var(--surface-2);padding:.1rem .35rem;border-radius:4px;user-select:all}

      /* Filter chips above the list. */
      .rc-filters{display:flex;gap:.4rem;margin-bottom:.85rem;flex-wrap:wrap}
      .rc-chip{
        padding:.35rem .8rem;border-radius:var(--radius-pill);cursor:pointer;font-size:.8rem;font-weight:600;
        border:1.5px solid var(--line);background:var(--surface);color:var(--text-muted);min-height:36px
      }
      .rc-chip.on{background:var(--accent-fill);border-color:var(--accent);color:var(--text-on-dark)}

      /* One receipt in the list. Thumbnail left so the eye finds the document
         before the numbers — a consultant recognises their own receipts by
         sight faster than by merchant name. */
      .rc-item{
        display:flex;gap:.85rem;align-items:flex-start;width:100%;text-align:left;cursor:pointer;
        background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-md);
        padding:.75rem;margin-bottom:.6rem
      }
      .rc-item:hover{border-color:var(--accent)}
      .rc-item:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
      /* contain, not cover: a cropped receipt is a tall strip, and cover would
         throw away everything but a band across its middle. Letterboxed on
         paper-white in both themes, so the strip reads as a receipt on a page
         rather than as a hole in the row. */
      .rc-thumb{
        width:54px;height:54px;flex:0 0 54px;border-radius:8px;object-fit:contain;
        background:#f3f5f8;border:1px solid var(--line)
      }
      /* ── The expense report ─────────────────────────────────────────────
         Full screen, because the Focus form is eighteen columns wide and a
         modal on a phone is not. */
      #er-view{
        position:fixed;inset:0;z-index:10000;background:var(--surface-3);
        display:flex;flex-direction:column
      }
      .er-bar{
        display:flex;align-items:center;gap:.6rem;
        padding:calc(.7rem + max(env(safe-area-inset-top,0px),0px)) 1rem .7rem;
        background:var(--surface);border-bottom:1px solid var(--line);flex:0 0 auto
      }
      .er-bar-title{font-weight:700;font-size:.95rem;color:var(--text);flex:1;min-width:0;
        overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
      .er-scroll{flex:1;overflow-y:auto;
        padding-bottom:calc(1.5rem + max(env(safe-area-inset-bottom,0px),0px))}
      .er-sheet{max-width:56rem;margin:0 auto;padding:1rem}
      .er-head{display:grid;grid-template-columns:repeat(2,1fr);gap:.7rem;margin-bottom:1rem}
      @media (max-width:30rem){ .er-head{grid-template-columns:1fr} }

      /* Status carries a word as well as a colour — the whole point of the
         revision cycle is knowing whose desk it is on. */
      .er-status{
        font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;
        padding:.2rem .5rem;border-radius:999px;white-space:nowrap;
        background:var(--surface-2);color:var(--text-muted);border:1px solid var(--line)
      }
      .er-status.draft{background:#eef2f7;color:#40506a;border-color:#d5deea}
      .er-status.submitted{background:#e7f2fb;color:#15527d;border-color:#c2ddf2}
      .er-status.returned{background:#fdf0e3;color:#8a4b06;border-color:#f3d7b4}
      .er-status.approved,.er-status.paid{background:#e6f5ec;color:#14622f;border-color:#bfe4cd}
      /* Dark mode: the light pastels stay legible on a dark ground but read as
         stickers pasted onto the page. Same hues, inverted — declared under
         both the media query and the explicit toggle, so an OS preference and a
         chosen theme land in the same place. */
      @media (prefers-color-scheme: dark) {
        :root:not([data-theme="light"]) .er-status.draft{background:#232e3f;color:#b7c6db;border-color:#33415a}
        :root:not([data-theme="light"]) .er-status.submitted{background:#12314b;color:#9ecdf2;border-color:#1d4f78}
        :root:not([data-theme="light"]) .er-status.returned{background:#3d2a12;color:#f0c286;border-color:#5e421d}
        :root:not([data-theme="light"]) .er-status.approved,
        :root:not([data-theme="light"]) .er-status.paid{background:#12331f;color:#8fd7a8;border-color:#1e5232}
      }
      :root[data-theme="dark"] .er-status.draft{background:#232e3f;color:#b7c6db;border-color:#33415a}
      :root[data-theme="dark"] .er-status.submitted{background:#12314b;color:#9ecdf2;border-color:#1d4f78}
      :root[data-theme="dark"] .er-status.returned{background:#3d2a12;color:#f0c286;border-color:#5e421d}
      :root[data-theme="dark"] .er-status.approved,
      :root[data-theme="dark"] .er-status.paid{background:#12331f;color:#8fd7a8;border-color:#1e5232}

      /* The grid scrolls sideways inside its own box; the page never does. */
      .er-gridwrap{
        overflow-x:auto;-webkit-overflow-scrolling:touch;
        border:1px solid var(--line);border-radius:10px;background:var(--surface)
      }
      .er-grid{border-collapse:collapse;font-size:.8rem;min-width:100%}
      .er-grid th,.er-grid td{
        padding:.45rem .55rem;border-bottom:1px solid var(--line);
        text-align:left;white-space:nowrap;vertical-align:top
      }
      .er-grid th{
        background:var(--surface-2);font-size:.68rem;font-weight:700;color:var(--text-muted);
        text-transform:uppercase;letter-spacing:.03em;position:sticky;top:0;z-index:1;
        /* Wraps, because "Ground Transportation (Gas, Rental Car, Taxi, Uber)"
           is a column heading and cannot be shortened without leaving the form. */
        white-space:normal;min-width:5.5rem;max-width:9rem
      }
      /* Money lines up, so a wrong figure is visible as a wrong shape. */
      .er-grid td.num,.er-grid th.num{text-align:right;font-variant-numeric:tabular-nums}
      .er-grid td.zero{color:var(--text-faint)}
      /* An unanswered Yes/No, or a column that does not apply to this row. Kept
         visibly different from "No" — the form says "Must Select Yes/No", so a
         blank is an outstanding question, not an answer. */
      .er-grid-un{color:var(--text-faint);font-style:italic}
      /* A booked cost nobody has said who paid for. Marked on the row because
         its money is in Total Expenses but in neither paid-by column, so the
         columns stop adding up until somebody answers — and a consultant
         reading the grid should be able to see which row is doing that. Not
         colour alone: the Company Card? cell already reads as an italic dash. */
      .er-grid tr.er-unanswered td{background:var(--chip-orange-bg,#fdf0e3)}
      .er-grid tr.er-unanswered td:first-child{box-shadow:inset 3px 0 0 var(--chip-orange-text,#8a4b06)}
      /* A row with an open question on it — a suspected duplicate, or a card
         that disagrees with who paid. Lighter than er-unanswered, because
         these warn and do not block. */
      .er-grid tr.er-queried td{background:var(--chip-orange-bg,#fdf0e3)}
      /* The losing half of a settled duplicate. Struck through and dimmed, not
         removed: its money is out of the totals and the reader can still see
         what was decided. */
      .er-grid tr.er-superseded td{opacity:.55;text-decoration:line-through}
      .er-grid tr.er-superseded td.desc .er-note{text-decoration:none;opacity:1}
      /* Why a row is queried, in the description cell where it is read. */
      .er-note{
        display:block;margin-top:.2rem;font-size:.74rem;color:var(--text-muted);
        font-style:italic;white-space:normal
      }
      .er-note.warn{color:var(--chip-orange-text,#8a4b06);font-style:normal}
      .er-grid tr.total td{font-weight:700;background:var(--surface-2);border-top:2px solid var(--line)}
      .er-grid td.desc{white-space:normal;min-width:9rem;max-width:14rem}
      /* Mileage rows are marked with a real <i class=ic-car> in the markup
         rather than a CSS ::before. The icons are codepoints in a subset font,
         not ligatures, so content:'car' would have printed the word. */
      .er-grid td.desc .ic-car{color:var(--text-muted);margin-right:.35rem}
      .er-empty{padding:1.5rem 1rem;text-align:center;color:var(--text-muted);font-size:.88rem}

      .er-settle{
        margin-top:1rem;background:var(--surface);border:1px solid var(--line);
        border-radius:10px;padding:.9rem 1rem
      }
      .er-settle-row{display:flex;justify-content:space-between;gap:1rem;padding:.25rem 0;font-size:.88rem}
      .er-settle-row.due{
        font-weight:700;font-size:1rem;border-top:1px solid var(--line);
        margin-top:.35rem;padding-top:.6rem
      }
      .er-settle-row .amt{font-variant-numeric:tabular-nums}
      /* Money owed back to Focus is not a negative reimbursement, it is a
         different thing, and it reads as one. */
      .er-settle-row.owing .amt{color:var(--danger,#b42318)}

      /* Lisa's verdict. An alert rather than a note: the report is back because
         of what this says, so it outranks everything else on the screen. */
      .er-returned{
        margin-top:1rem;padding:.8rem 1rem;border-radius:10px;
        background:var(--chip-orange-bg,#fdf0e3);color:var(--chip-orange-text,#8a4b06);
        border:1px solid var(--chip-orange-border,#f3d7b4);font-size:.88rem
      }
      .er-returned div{margin-top:.35rem;white-space:pre-wrap}
      .er-cycle{
        display:flex;flex-wrap:wrap;gap:.6rem;align-items:center;margin-top:1rem;
        padding-top:1rem;border-top:1px solid var(--line)
      }
      .er-hint{font-size:.82rem;color:var(--text-muted)}
      /* The two figures, side by side, with the gap between them named. */
      .er-dupe-pair{
        display:grid;grid-template-columns:1fr 1fr;gap:.6rem;margin-bottom:.9rem
      }
      .er-dupe-side{
        background:var(--surface-2);border:1px solid var(--line);
        border-radius:8px;padding:.7rem .8rem
      }
      .er-dupe-lbl{font-size:.74rem;text-transform:uppercase;letter-spacing:.04em;color:var(--text-muted)}
      .er-dupe-amt{font-size:1.25rem;font-weight:700;font-variant-numeric:tabular-nums;margin-top:.15rem}
      .er-dupe-meta{font-size:.78rem;color:var(--text-muted);margin-top:.2rem;white-space:normal}
      .er-dupe-gap{
        grid-column:1/-1;text-align:center;font-size:.82rem;
        color:var(--chip-orange-text,#8a4b06);font-weight:600
      }
      /* Stacked, because the three answers are not equally weighted and a row
         of equal buttons would suggest they are. */
      .er-dupe-acts{display:flex;flex-direction:column;gap:.5rem}

      /* Which booking is being answered for, above the question. */
      .er-book-what{
        padding:.7rem .9rem;margin-bottom:.9rem;border-radius:8px;
        background:var(--surface-2);border:1px solid var(--line);font-size:.86rem
      }
      /* Sits under a field, explaining when to leave it alone. */
      .rc-hint{display:block;margin-top:.25rem;font-size:.76rem;color:var(--text-muted)}
      /* Claiming part of a line (2026-08-27). Indented under the checkbox that
         reveals it, so it reads as a consequence of unticking rather than as
         another field on the line. Narrow boxes with the sign beside them
         rather than wide ones with "$" or "%" typed in: each holds four or
         five characters, and a full-width input invites a sentence. */
      .rc-claim{margin:.4rem 0 0 1.6rem}
      .rc-claim-row{display:flex;align-items:center;gap:.35rem;margin-top:.2rem}
      .rc-claim-row .rc-claim-in{width:5rem;text-align:right}
      .rc-claim-row > span{color:var(--text-muted);font-size:.85rem}

      /* ── Stacking, once, in one place ────────────────────────────────────
         Several surfaces can be open together, each opened from the one below,
         and .modal-backdrop's 1000 is beneath all of them:

           10000  #er-view                                      the report, full screen
           10010  #er-pick / #er-mile / #er-book / #rc-modal    opened from the report
           10010  #fw-modal                                     opened from the trip OR the report
           10020  #rc-crop                                      opened from the receipt sheet

         Set as a block rather than bumped one at a time. Every ad-hoc increment
         so far was a bug found by opening something and watching the screen dim
         with nothing reachable underneath it. */
      #er-pick,#er-mile,#er-book,#er-dupe{z-index:10010}
      /* The forwarding window is reached from two places — the trip's Expenses
         section and a draft report — and the report is already at 10000, so
         the default 1000 put it behind the screen that opened it. Reported the
         first time it was pressed. */
      #fw-modal{z-index:10010}
      /* The receipt sheet is reachable from a report line now, so it has to
         clear the report. Unaffected when opened from the receipts list, where
         nothing else is up this high. */
      #rc-modal{z-index:10010}

      .er-actions{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;margin-top:1rem}
      .er-actions .btn-link{margin-left:auto}

      .er-item{
        display:flex;gap:.6rem;align-items:center;padding:.5rem 0;
        border-bottom:1px solid var(--line)
      }
      /* rows are enclosed: a line above the first as well as below the last */
      .er-item:first-child{border-top:1px solid var(--line)}
      .er-item input[type=checkbox]{width:20px;height:20px;flex:0 0 20px}
      .er-item-main{flex:1;min-width:0}
      .er-item-t{font-weight:600;font-size:.88rem;color:var(--text);
        overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
      .er-item-s{font-size:.76rem;color:var(--text-muted)}
      .er-item-amt{font-weight:700;font-size:.88rem;font-variant-numeric:tabular-nums}

      /* Expenses on a trip that no report has claimed. Deliberately an alert
         rather than a quiet note: a report snapshots what existed when it was
         made, so this is the only thing standing between a late receipt and
         nobody ever noticing it. */
      .td-loose{
        margin-top:.5rem;padding:.5rem .65rem;border-radius:8px;font-size:.82rem;
        background:var(--chip-orange-bg,#fdf0e3);color:var(--chip-orange-text,#8a4b06);
        border:1px solid var(--chip-orange-border,#f3d7b4)
      }
      .er-row{
        display:flex;align-items:center;gap:.6rem;padding:.55rem 0;
        border-bottom:1px solid var(--line);cursor:pointer;width:100%;
        background:none;border-left:0;border-right:0;border-top:0;text-align:left
      }
      .er-row:first-child{border-top:1px solid var(--line)}
      .er-row-main{flex:1;min-width:0}
      .er-row-t{font-weight:600;font-size:.9rem;color:var(--text)}
      .er-row-s{font-size:.76rem;color:var(--text-muted)}
      .er-row-amt{font-weight:700;font-variant-numeric:tabular-nums;color:var(--text)}

      .rc-body{flex:1;min-width:0}
      .rc-line1{display:flex;align-items:baseline;gap:.6rem}
      .rc-merchant{font-weight:700;color:var(--text);font-size:.95rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
      /* Amounts line up down the list, so they are tabular and right-aligned. */
      .rc-amount{margin-left:auto;font-weight:700;font-size:.98rem;color:var(--text);font-variant-numeric:tabular-nums;white-space:nowrap}
      .rc-meta{font-size:.79rem;color:var(--text-muted);margin-top:.2rem;display:flex;gap:.45rem;flex-wrap:wrap;align-items:center}
      .rc-tag{
        display:inline-flex;align-items:center;gap:.25rem;font-size:.7rem;font-weight:700;
        padding:.13rem .45rem;border-radius:var(--radius-pill);
        background:var(--badge-gray-bg);color:var(--badge-gray-text)
      }
      .rc-tag.cat{background:var(--badge-blue-bg);color:var(--badge-blue-text)}
      .rc-tag.warn{background:var(--badge-amber-bg);color:var(--badge-amber-text)}
      .rc-tag.busy{background:var(--surface-2);color:var(--text-muted)}
      .rc-tag.er{background:var(--badge-green-bg);color:var(--badge-green-text);font-variant-numeric:tabular-nums}
      /* The trip it is assigned to. Green like the report pill, because both are
         the settled state — every grey tag on that row is about something not
         being done yet. Long trip names truncate rather than wrapping the row. */
      .rc-tag.trip{background:var(--badge-green-bg);color:var(--badge-green-text);
        max-width:15rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}
      /* Still to do: "Not assigned", "Not filed on a report". These were grey,
         deliberately, so as not to be read as amber-means-something-is-wrong
         on a row that also carries "Does not add up". Asked for 2026-08-26 to
         carry more weight than that — an expense on no report is a loose end,
         and grey let it sit unnoticed among settled ones.
         So: warm, but OUTLINED rather than filled. Solid amber stays the mark
         of a problem; an outline reads as unfinished. The ring is an inset
         shadow, not a border, so the pill keeps the exact height of the filled
         ones beside it and the row does not shift. Same warm ink the trip page
         already uses for a loose expense.
         Unfilled, not merely a paler fill: measured against the tokens, a warm
         fill lands 22 RGB units from .warn in light and SIX in dark, which is
         no distinction at all on the screen half the readers use. Outline
         against the card versus a solid block survives both, and the text
         clears 7:1 on white and 11:1 on the dark card. */
      .rc-tag.todo{
        background:transparent;color:var(--warn-ink);
        box-shadow:inset 0 0 0 1px var(--chip-orange-border)
      }
      .rc-empty{text-align:center;color:var(--text-muted);font-size:.86rem;padding:2rem 1rem;line-height:1.6}

      /* ── The review sheet ── */
      .rc-image{
        width:100%;max-height:38vh;object-fit:contain;border-radius:10px;cursor:zoom-in;
        background:var(--surface-2);border:1px solid var(--line);display:block
      }
      /* The receipt is behind a button now (2026-08-27). The image took most
         of a phone screen to show something the consultant had just
         photographed and rarely needed to re-read; the zoomed windows below
         are the part worth checking, and they are what stayed. */
      /* Both under the zoomed windows, side by side where there is room and
         stacked where there is not. They answer a question the windows raise,
         so they follow it. */
      .rc-actions-row{ display:flex; gap:.5rem; flex-wrap:wrap; margin:.75rem 0 0 }
      .rc-actions-row > div{ flex:1 1 12rem; min-width:0 }
      .rc-view-row .btn-sm,
      #rc-crop-offer .btn-sm{ width:100%; justify-content:center; min-height:44px }
      /* ── What the receipt itself says, zoomed ──────────────────────────
         The windows Operations has read reports by since the review screen
         existed, on the consultant's own receipt (2026-08-25). Each is a
         window onto the photograph — the image is scaled up and shifted so
         the measured box sits in the frame, which is sharper than a
         re-encoded cut-out and needs no second copy of anything. Placed
         between the picture and the fields, so a figure and the print it
         was read from are next to each other.
         Two per row here, not one: a phone has less height to spend than
         the review screen's side panel, and four stacked windows would push
         the fields off the bottom. */
      .rc-crops{display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin-top:.75rem}
      @media(max-width:380px){.rc-crops{grid-template-columns:1fr}}
      .rc-crop{border:1px solid var(--line);border-radius:8px;overflow:hidden;background:var(--surface-2)}
      .rc-crop-lbl{
        font-size:.64rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
        color:var(--text-muted);padding:.28rem .45rem;border-bottom:1px solid var(--line);
        display:flex;justify-content:space-between;gap:.4rem;align-items:baseline
      }
      /* The figure this sheet records, beside the print it came from. Truncated
         rather than wrapped: a merchant name is often longer than the frame,
         and a label that grows to two lines drags the window down with it. */
      .rc-crop-lbl b{
        color:var(--text);font-weight:700;letter-spacing:0;text-transform:none;font-size:.76rem;
        overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0
      }
      .rc-crop-win{position:relative;overflow:hidden;height:58px}
      .rc-crop-win img{position:absolute;transform-origin:0 0;max-width:none}
      /* A fixed height already, so filling it costs no layout — the strip is
         its final size from the first tick and nothing jumps when the reading
         lands. */
      .rc-crop-skel{
        position:absolute;inset:0;
        background:linear-gradient(90deg,var(--surface-3) 25%,var(--line) 50%,var(--surface-3) 75%);
        background-size:600px 100%;animation:rc-skel 1.4s infinite
      }
      @keyframes rc-skel{0%{background-position:-600px 0}100%{background-position:600px 0}}
      @media(prefers-reduced-motion:reduce){.rc-crop-skel{animation:none}}
      /* Said plainly rather than cropped at a guess: a consultant told "not
         found" looks at the photograph, one shown the wrong crop does not. */
      .rc-crop-none{
        position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
        font-size:.68rem;color:var(--text-muted);font-style:italic;text-align:center;padding:0 .3rem
      }
      .rc-crops-note{
        grid-column:1 / -1;font-size:.72rem;line-height:1.35;color:var(--text-muted);
        background:var(--surface-2);border:1px dashed var(--line);border-radius:8px;padding:.4rem .55rem
      }
      /* A photograph worth taking again. Amber rather than red: nothing is
         wrong with the expense, only with the picture of it, and the receipt is
         still perfectly usable if the consultant would rather not bother. */
      .rc-retake{
        display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;
        font-size:.78rem;line-height:1.4;color:var(--text);
        background:var(--warn-bg,#fff7ed);border:1px solid var(--warn-border,#f9ab6b);
        border-radius:8px;padding:.5rem .6rem;margin-bottom:.6rem
      }
      .rc-retake #rc-retake-text{flex:1;min-width:10rem}
      .rc-retake .btn-sm{white-space:nowrap}

      /* The tip reconciliation is an ANSWER, not a warning: the claim is right
         and the receipt is right, and the difference between them has a name.
         Solid rather than dashed, and in the ink of a normal note — the dashed
         box beside it means "something is missing", which this is not. */
      .rc-crops-note.rc-tip-note{
        border:1px solid var(--line);border-style:solid;color:var(--text);
        background:var(--accent-soft)
      }
      .rc-crops-note.rc-tip-note b{color:var(--accent-ink)}
      /* And the window above it says why there is nothing to show, in the same
         quiet voice as "not located" but without implying a failure. */
      .rc-crop-none.rc-crop-tip{font-style:normal}

      .rc-grid{display:grid;grid-template-columns:1fr 1fr;gap:.6rem;margin-top:.9rem}
      .rc-grid .rc-wide{grid-column:1 / -1}
      /* The report's name spans the header grid: it is the whole report, not
         one of its four details. */
      .er-head .er-wide{grid-column:1 / -1}
      .er-head .er-wide input{font-size:1rem;font-weight:600}
      /* The add buttons, tucked under the grid they add to. */
      .er-addrow{display:flex;flex-wrap:wrap;gap:.5rem;margin:.6rem 0 1rem}
      /* A heading in the add-to-report picker, naming the trip its rows are
         already assigned to. Sticky so the trip stays readable while scrolling
         a long list of receipts under it. */
      .er-pick-group{
        display:flex;align-items:center;gap:.4rem;position:sticky;top:0;z-index:1;
        background:var(--surface);padding:.5rem 0 .3rem;margin-top:.5rem;
        font-size:.75rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
        color:var(--text-muted);border-bottom:1px solid var(--line)
      }
      .er-pick-group .btn-link{margin-left:auto;font-size:.75rem;text-transform:none;letter-spacing:0}

      /* Capture, above the list rather than in it: it is the one way onto the
         report that works when the list is empty, which is exactly when a
         consultant needs it. */
      .er-pick-snap{
        display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;
        padding-bottom:.7rem;border-bottom:1px solid var(--line)
      }
      .er-pick-snap-sub{font-size:.75rem;color:var(--text-muted)}

      /* Moving between the receipts on a report without going back to it. */
      .rc-nav{display:flex;align-items:center;gap:.3rem;margin-left:auto}
      .rc-nav .btn-sm{padding:.3rem .5rem;min-height:34px}
      .rc-nav-n{font-size:.75rem;color:var(--text-muted);font-variant-numeric:tabular-nums;min-width:4.5em;text-align:center}

      /* A receipt opened from a report reads before it edits. The fields look
         like what they are — settled — rather than like empty boxes. */
      .rc-reading .rc-field input,.rc-reading .rc-field select,
      .rc-reading .rc-line input,.rc-reading .rc-line select{
        background:var(--surface-2);border-color:transparent;color:var(--text)
      }
      .rc-reading .rc-help,.rc-reading .rc-help-text{display:none}

      /* Dollars or percent. A two-button toggle rather than a select: there
         are two answers and both fit in the space a select would take. */
      .rc-split-mode{display:inline-flex;border:1.5px solid var(--line);border-radius:var(--radius-pill);overflow:hidden}
      .rc-split-mode button{
        border:0;background:var(--surface);color:var(--text-muted);
        font-size:.8rem;font-weight:700;padding:.25rem .7rem;cursor:pointer;min-height:32px
      }
      .rc-split-mode button.on{background:var(--accent-fill);color:var(--text-on-dark)}

      /* The member box on a split line, with its typeahead under it. */
      .rc-ent{position:relative}
      .rc-ent-drop{
        position:absolute;top:100%;left:0;right:0;z-index:60;max-height:210px;overflow-y:auto;
        background:var(--surface);border:1.5px solid var(--accent);border-radius:0 0 8px 8px;
        box-shadow:0 4px 12px rgba(0,0,0,.12)
      }
      .rc-ent-opt{padding:.5rem .6rem;cursor:pointer;font-size:.85rem;border-bottom:1px solid var(--line)}
      .rc-ent-opt:last-child{border-bottom:0}
      .rc-ent-opt:hover,.rc-ent-opt.sel{background:var(--accent-soft)}
      .rc-ent-opt b{display:block;font-weight:600}
      .rc-ent-opt span{font-size:.75rem;color:var(--text-muted)}
      /* The people already on this trip, offered first. */
      .rc-ent-opt.near b:after{content:' · on this trip';font-weight:400;font-size:.72rem;color:var(--text-muted)}

      /* The photograph in a duplicate comparison. Two receipts side by side is
         a comparison of two rows of text until you can see them; the image is
         what tells the till receipt from the vendor's emailed copy. */
      .er-dupe-shot{
        height:120px;margin:.4rem 0;border-radius:8px;overflow:hidden;
        background:var(--surface-2);border:1px solid var(--line);
        display:flex;align-items:center;justify-content:center
      }
      .er-dupe-shot img{width:100%;height:100%;object-fit:cover;cursor:zoom-in;display:block}
      .rcd-shot-wait{font-size:.75rem;color:var(--text-muted);display:flex;align-items:center;gap:.3rem}

      /* Two receipts that look like one expense, flagged above the list. */
      .rc-dupes{
        display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;margin-bottom:.85rem;
        padding:.65rem .85rem;border-radius:10px;font-size:.85rem;
        background:var(--warn-bg);border:1px solid var(--warn-border);color:var(--warn-ink)
      }
      .rc-dupes .btn-sm{margin-left:auto}

      /* The offer to start a report from an expense that is on nothing. */
      .rc-startreport{
        display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;margin-top:.85rem;
        padding:.65rem .85rem;border-radius:10px;font-size:.85rem;
        background:var(--surface-2);border:1px solid var(--line);color:var(--text-muted)
      }
      /* Where this expense belongs, and the way there. Deliberately the same
         strip as rc-startreport, which is the same sentence for the opposite
         case — that one says it belongs nowhere, this one says where. */
      .rc-links{
        display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-top:.85rem;
        padding:.6rem .85rem;border-radius:10px;
        background:var(--surface-2);border:1px solid var(--line)
      }
      .rc-links-l{font-size:.72rem;font-weight:700;color:var(--text-muted);
        text-transform:uppercase;letter-spacing:.06em}
      .rc-link-btn{
        display:inline-flex;align-items:center;gap:.35rem;
        padding:.35rem .7rem;border-radius:var(--radius-pill);
        border:1px solid var(--line-strong);background:var(--surface);
        font-family:inherit;font-size:.82rem;font-weight:600;color:var(--accent-ink);
        cursor:pointer;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap
      }
      .rc-link-btn:hover{border-color:var(--accent);background:var(--accent-soft)}
      /* Autosave says so quietly. Loud enough to find when you look for it,
         quiet enough not to compete with the submit button. */
      .er-autosave{font-size:.75rem;color:var(--text-muted);margin-left:auto}
      .er-autosave.saving{font-style:italic}
      /* A report sent back is the one thing on this screen that is waiting on
         the consultant, so it gets the one red mark in the navigation. */
      .nav-badge{
        position:absolute;top:2px;right:2px;min-width:16px;height:16px;padding:0 4px;
        border-radius:8px;background:var(--danger);color:#fff;
        font-size:.62rem;font-weight:700;line-height:16px;text-align:center;
        font-variant-numeric:tabular-nums
      }
      @media(min-width:721px){ .nav-badge{position:static;margin-left:auto} }

      /* The same weight on the filter that holds them. A neutral count here
         reads as information; this is a queue. */
      .rc-chip[data-erf="returned"] .chip-n:not(:empty){
        background:var(--danger);color:#fff;padding:0 .35rem;border-radius:8px;opacity:1
      }

      /* A count riding on a filter chip. Inherits the chip's colour when the
         chip is on, so it never fights the selected state. */
      .rc-chip .chip-n{
        display:inline-block;margin-left:.35rem;font-size:.72rem;font-weight:700;
        opacity:.75;font-variant-numeric:tabular-nums
      }
      .rc-field label{display:block;font-size:.72rem;font-weight:600;color:var(--text-muted);
        text-transform:uppercase;letter-spacing:.06em;margin-bottom:.2rem}
      .rc-field input,.rc-field select,.rc-field textarea{width:100%;font-size:16px;padding:.5rem}

      /* One line of the split. Grid rather than flex so the columns align down
         the stack — a split you cannot read down is a split nobody checks. */
      .rc-lines{margin-top:.5rem;display:grid;gap:.5rem}
      /* minmax(0,1fr) rather than 1fr: a grid track defaults to a min-content
         floor, so a long select label pushed the whole row wider than the card
         and the billable checkbox spilled out past the right edge on a phone.
         min-width:0 on the children is the other half of the same fix. */
      .rc-line{
        border:1px solid var(--line);border-radius:10px;padding:.6rem;background:var(--surface-2);
        display:grid;grid-template-columns:minmax(0,1fr) 7.5rem;gap:.45rem;align-items:end
      }
      .rc-line > *{min-width:0}
      .rc-line .rc-full{grid-column:1 / -1}
      /* Checkbox and its words on one line, wrapping inside the card rather
         than running off it. */
      .rc-check{display:flex;align-items:center;gap:.45rem;font-size:.82rem;cursor:pointer;min-width:0;flex-wrap:wrap}
      .rc-check input{flex:0 0 auto;margin:0}
      .rc-check span{min-width:0}
      .rc-line input,.rc-line select{font-size:16px;padding:.45rem;width:100%}
      .rc-line-amt{text-align:right;font-variant-numeric:tabular-nums}
      .rc-line-head{display:flex;align-items:center;justify-content:space-between;gap:.5rem;grid-column:1 / -1}
      .rc-line-n{font-size:.7rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-muted)}

      /* The reconciliation, live while typing. Green is not decoration here —
         it is the difference between a report Lisa accepts and one she returns. */
      .rc-recon{
        display:flex;align-items:center;justify-content:space-between;gap:.75rem;margin-top:.7rem;
        padding:.6rem .75rem;border-radius:8px;font-size:.83rem;font-weight:600;
        background:var(--surface-2);border:1px solid var(--line);color:var(--text-muted)
      }
      .rc-recon.ok{background:var(--badge-green-bg);color:var(--badge-green-text);border-color:transparent}
      .rc-recon.off{background:var(--badge-amber-bg);color:var(--badge-amber-text);border-color:transparent}
      .rc-recon b{font-variant-numeric:tabular-nums}

      /* ── Adjusting the crop ──
         Shown only where automatic detection declined, so a photo that already
         cropped itself costs nobody a tap. Corners arrive pre-placed from the
         texture detector, which is usually close enough that this is a nudge
         rather than four drags. */
      #rc-crop{
        /* Above the receipt sheet it opens from — which is itself now above the
           report. See the stacking block near .rc-hint. */
        position:fixed;inset:0;z-index:10020;background:#0d1116;
        display:flex;flex-direction:column;
        padding-top:max(env(safe-area-inset-top,0px),0px)
      }
      .rc-crop-stage{flex:1;position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center}
      /* The photo and the overlay share one positioned box so image pixels and
         handle coordinates cannot drift apart. */
      .rc-crop-box{position:relative;touch-action:none}
      .rc-crop-box img{display:block;max-width:100%;max-height:100%;user-select:none;-webkit-user-drag:none}
      .rc-crop-svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
      /* Big enough to hit with a thumb; the visible dot is smaller than the
         target so the finger does not cover the corner it is placing. */
      .rc-handle{
        position:absolute;width:44px;height:44px;margin:-22px 0 0 -22px;
        border-radius:50%;cursor:grab;touch-action:none;
        display:flex;align-items:center;justify-content:center
      }
      .rc-handle::after{
        content:'';width:18px;height:18px;border-radius:50%;
        background:var(--accent-fill);border:3px solid #fff;
        box-shadow:0 2px 8px rgba(0,0,0,.5)
      }
      .rc-handle:active::after{transform:scale(1.25)}
      /* The loupe. A finger covers the corner it is placing, so the patch of
         photograph underneath it is shown at 3x, pinned opposite the drag.
         Crosshairs mark the exact pixel the corner is on. */
      .rc-loupe{
        position:absolute;top:12px;width:132px;height:132px;
        border-radius:50%;border:3px solid #fff;pointer-events:none;
        background-repeat:no-repeat;background-color:#0d1116;
        box-shadow:0 6px 20px rgba(0,0,0,.55);z-index:2
      }
      .rc-loupe::before,.rc-loupe::after{
        content:'';position:absolute;background:#4db3e8;opacity:.9
      }
      .rc-loupe::before{left:50%;top:0;bottom:0;width:1px;margin-left:-.5px}
      .rc-loupe::after{top:50%;left:0;right:0;height:1px;margin-top:-.5px}
      .rc-crop-bar{
        display:flex;gap:.5rem;align-items:center;justify-content:space-between;
        padding:.85rem 1rem calc(.85rem + max(env(safe-area-inset-bottom,0px),0px));
        background:#141a21
      }
      .rc-crop-hint{color:#9fb0c0;font-size:.8rem;line-height:1.4;flex:1;min-width:0}
      .rc-crop-bar .btn-primary,.rc-crop-bar .btn-sm{flex:0 0 auto}

      .card-title-gb-head{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem}
      .card-title-gb-head .btn-link{font-size:.72rem;white-space:nowrap}

      .card-danger{margin-top:1.4rem;padding-top:.95rem;border-top:1px solid var(--line)}
      .card-danger-head{
        font-size:.72rem;font-weight:600;color:var(--text-muted);
        text-transform:uppercase;letter-spacing:.06em
      }
      .card-danger-note{font-size:.78rem;line-height:1.5;color:var(--text-muted);margin:.35rem 0 .1rem}
      .card-photo-preview{width:52px;height:52px;border-radius:50%;object-fit:cover;border:2px solid var(--line)}
      .card-msg{font-size:.78rem;margin-top:.6rem;min-height:1.1em;color:var(--text-muted)}
      .card-msg.ok{color:var(--success-ink,#166534)}
      .card-msg.err{color:var(--danger)}

      /* ── Device safe areas ──
         index.html sets viewport-fit=cover, so the page now paints edge to
         edge and these report the real hardware insets: the notch/status bar
         at the top, the home-indicator strip at the bottom.

         The bottom one carries a floor of 10px. On a device with no home
         indicator the inset is 0, and a nav flush against the screen edge is
         uncomfortable to tap even when nothing system-level sits there. max()
         gives the hardware value where there is one and a small buffer where
         there is not. Everything that has to clear the nav derives from
         --ft-nav-h so the offset is stated once. */
      :root{
        --ft-safe-top: env(safe-area-inset-top, 0px);
        --ft-safe-bottom: max(env(safe-area-inset-bottom, 0px), 10px);
        --ft-nav-h: 0px;               /* no bottom nav above 720px */
      }

      /* ── Offline banner ──
         Sits directly on top of the bottom nav rather than at the top of the
         page. On a phone the thumb and the eye are already down there, and the
         old top placement pushed the header — the one thing that tells you
         which screen you are on — off the top of a small display. Above 720px
         --ft-nav-h is 0 and it simply sits on the bottom edge. */
      #ft-offline-banner{
        position:fixed;left:0;right:0;bottom:var(--ft-nav-h);z-index:899;
        background:#854f0b;color:var(--text-on-dark);text-align:center;
        padding:.4rem .8rem;font:600 12.5px/1.4 'Segoe UI',system-ui,sans-serif
      }

      /* ── Reset & Base ── */
      *{box-sizing:border-box;margin:0;padding:0}
      body{font-family:'Segoe UI',system-ui,-apple-system,sans-serif;background:var(--bg-page);color:var(--text);min-height:100vh}

      /* ── Header ── */
      /* Padded down off the notch, and the navy fills that strip rather than
         leaving a white band above it. */
      header{background:var(--navy);padding:calc(1rem + var(--ft-safe-top)) 2rem 1rem;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem}
      .header-brand{display:flex;align-items:center;gap:.75rem}
      .brand-text{color:var(--text-on-dark);font-size:1.15rem;font-weight:700;letter-spacing:.03em}
      .brand-sub{color:#6ea8cc;font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;margin-top:1px}
      .readonly-badge{background:#f59e0b;color:var(--text-on-dark);font-size:.7rem;font-weight:700;padding:.2rem .6rem;border-radius:4px;letter-spacing:.06em;text-transform:uppercase}

      /* ── Buttons ── */
      .btn-primary{background:var(--accent-fill);color:var(--text-on-dark);border:none;border-radius:8px;padding:.55rem 1.1rem;font-size:.85rem;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:.4rem;transition:background .15s}
      .btn-primary:hover{background:var(--accent-fill-hover)}
      .btn-ghost{background:transparent;color:#6ea8cc;border:1px solid #2e4a6a;border-radius:8px;padding:.5rem 1rem;font-size:.82rem;font-weight:600;cursor:pointer;transition:all .15s}
      .btn-ghost:hover{background:#243d5a;color:var(--text-on-dark)}
      .btn-danger{background:var(--danger-soft);color:var(--danger-ink);border:1px solid var(--danger-border);border-radius:6px;padding:.3rem .75rem;font-size:.78rem;font-weight:600;cursor:pointer}
      .btn-danger:hover{background:#fecaca}
      .btn-sm{background:var(--chip-blue-bg);color:var(--accent-ink);border:1px solid var(--chip-blue-border);border-radius:6px;padding:.3rem .75rem;font-size:.78rem;font-weight:600;cursor:pointer}
      .btn-sm:hover{background:#c7d8ec}

      /* ── Nav ── */
      nav{background:var(--surface);border-bottom:1px solid var(--line);padding:.6rem 2rem;display:flex;gap:.3rem;overflow-x:auto}
      .tab{padding:.55rem 1rem;font-size:.85rem;font-weight:600;color:var(--text-muted);cursor:pointer;border-radius:var(--radius-sm);white-space:nowrap;transition:all .15s}
      .tab:hover{color:var(--text);background:var(--bg-page-hover)}
      .tab.active{color:var(--text);background:var(--badge-coral-bg)}

      /* ── Layout ── */
      main{max-width:1100px;margin:2rem auto;padding:0 1.5rem 4rem}

      /* ── Cards ── */
      .card{background:var(--surface);border-radius:var(--radius-md);border:1px solid var(--line);padding:1.75rem 2rem;margin-bottom:1.5rem}
      .card-title{font-size:1.05rem;font-weight:700;color:var(--text);margin-bottom:1.15rem;display:flex;align-items:center;gap:.5rem}

      /* ── KPI tiles ── */
      .kpi-row{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:1rem;margin-bottom:1.75rem}
      .kpi{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-md);padding:1.25rem 1.5rem}
      .kpi-val{font-size:2rem;font-weight:700;color:var(--accent);line-height:1}
      .kpi-label{font-size:.78rem;color:var(--text-muted);margin-top:.5rem;font-weight:500}
      .kpi.alert .kpi-val{color:var(--danger)}

      /* ── Filters ── */
      .filters{display:flex;gap:1rem;flex-wrap:wrap;align-items:flex-end;margin-bottom:1.25rem}
      .field-group{display:flex;flex-direction:column;gap:.3rem}
      .field-label{font-size:.72rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em}
      /* Required, said before the button is pressed rather than after. The
         mark sits on the label so the form can be read for what it will ask
         for; teMissingForSubmit names anything still missing in words. */
      .field-label.req::after{content:" *";color:var(--danger);font-weight:700}
      input,select,textarea{border:1px solid var(--line-strong);border-radius:8px;padding:.5rem .75rem;font-size:.85rem;color:var(--text);background:var(--surface);font-family:inherit;outline:none;transition:border .15s}
      input:focus,select:focus,textarea:focus{border-color:#0096D6;box-shadow:0 0 0 3px #e0f4fb}
      textarea{resize:vertical;min-height:60px}
      select{min-width:150px}

      /* ── Search bar ── */
      .search-wrap{position:relative;margin-bottom:1.25rem}
      .search-wrap input{width:100%;padding:.6rem 1rem .6rem 2.5rem;font-size:.9rem;border-radius:10px}
      .search-icon{position:absolute;left:.8rem;top:50%;transform:translateY(-50%);color:var(--text-muted);font-size:1rem;pointer-events:none}

      /* ── Map ── */
      .map-wrap{border-radius:10px;overflow:hidden;border:1px solid var(--line);margin-bottom:1.25rem;height:480px}
      #conf-side-panel{position:absolute;top:0;right:-320px;width:300px;height:100%;background:var(--surface);box-shadow:-4px 0 16px rgba(0,0,0,.12);z-index:1000;transition:right .25s ease;display:flex;flex-direction:column;border-left:1px solid var(--line)}
      #conf-side-panel.open{right:0}
      .conf-panel-header{padding:.75rem 1rem;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
      .conf-panel-body{flex:1;overflow-y:auto;padding:.75rem 1rem;font-size:.82rem;color:var(--text)}
      @media(max-width:540px){#conf-side-panel{width:100%;right:-100%}}
      #leaflet-map{height:100%;width:100%}
      .leaflet-popup-content-wrapper{border-radius:10px;box-shadow:0 4px 16px rgba(0,0,0,.12);padding:0;overflow:hidden}
      .leaflet-popup-content{margin:0;min-width:220px}
      .map-popup{padding:.85rem 1rem}
      .map-popup-dest{font-size:.9rem;font-weight:700;color:var(--text);margin-bottom:.2rem}
      .map-popup-consultant{font-size:.78rem;color:var(--text-muted);margin-bottom:.5rem}
      .map-popup-meta{font-size:.78rem;color:var(--text-muted);display:flex;flex-direction:column;gap:.2rem}
      .map-popup-badge{display:inline-block;font-size:.68rem;font-weight:600;padding:.15rem .5rem;border-radius:var(--radius-pill);background:var(--chip-blue-bg);color:var(--accent-ink);margin-top:.35rem}
      .map-popup-badge.badge-blue{background:var(--badge-blue-bg);color:var(--badge-blue-text)}
      .map-popup-badge.badge-green{background:var(--badge-green-bg);color:var(--badge-green-text)}
      .map-popup-badge.badge-coral{background:var(--badge-coral-bg);color:var(--badge-coral-text)}
      .map-popup-badge.badge-amber{background:var(--badge-amber-bg);color:var(--badge-amber-text)}
      .map-popup-badge.badge-gray{background:var(--badge-gray-bg);color:var(--badge-gray-text)}

      /* ── Trip cards ── */
      .trip-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-md);padding:1.25rem 1.5rem;margin-bottom:.85rem;transition:border-color .15s}
      .trip-card:hover{border-color:#a9bccf}
      .trip-header{display:flex;align-items:center;gap:.85rem;margin-bottom:.65rem}
      .trip-header-info{flex:1;min-width:0}
      .trip-icon-chip{width:38px;height:38px;border-radius:11px;display:flex;align-items:center;justify-content:center;font-size:1.1rem;flex-shrink:0;background:var(--badge-blue-bg)}
      .trip-dest{font-size:1.02rem;font-weight:700;color:var(--text)}
      .trip-consultant{font-size:.8rem;color:var(--text-muted);margin-top:.15rem}
      .trip-meta{display:flex;gap:1rem;flex-wrap:wrap;margin-bottom:.4rem}
      .meta-chip{font-size:.78rem;color:var(--text-muted);display:flex;align-items:center;gap:.3rem}
      .purpose-badge{display:inline-block;font-size:.72rem;font-weight:600;padding:.2rem .6rem;border-radius:var(--radius-pill);background:var(--chip-blue-bg);color:var(--accent-ink);flex-shrink:0}
      .purpose-badge.badge-blue{background:var(--badge-blue-bg);color:var(--badge-blue-text)}
      .purpose-badge.badge-green{background:var(--badge-green-bg);color:var(--badge-green-text)}
      .purpose-badge.badge-coral{background:var(--badge-coral-bg);color:var(--badge-coral-text)}
      .purpose-badge.badge-amber{background:var(--badge-amber-bg);color:var(--badge-amber-text)}
      .purpose-badge.badge-gray{background:var(--badge-gray-bg);color:var(--badge-gray-text)}
      .trip-icon-chip.badge-blue{background:var(--badge-blue-bg)}
      .trip-icon-chip.badge-green{background:var(--badge-green-bg)}
      .trip-icon-chip.badge-coral{background:var(--badge-coral-bg)}
      .trip-icon-chip.badge-amber{background:var(--badge-amber-bg)}
      .trip-icon-chip.badge-gray{background:var(--badge-gray-bg)}
      .trip-notes{font-size:.8rem;color:var(--text-muted);font-style:italic;margin-top:.4rem}
      .trip-actions{display:flex;gap:.5rem;margin-top:.6rem}
      .overlap-alert{background:var(--danger-bg);border:1px solid var(--danger-border);border-radius:var(--radius-md);padding:.75rem 1rem;margin-bottom:.75rem;font-size:.82rem;color:var(--danger-ink);display:flex;align-items:flex-start;gap:.5rem}

      /* ── Dashboard cards ── */
      .dash-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-md);padding:1.25rem 1.5rem;margin-bottom:.85rem;display:flex;gap:1rem;align-items:flex-start;transition:border-color .15s;cursor:pointer}
      .dash-card:hover{border-color:#a9bccf}
      .dash-card.active{border-color:#0096D6;background:var(--accent-soft)}
      .dash-card-main{flex:1;min-width:0}
      .dash-card-top{display:flex;align-items:center;gap:.45rem;flex-wrap:wrap;margin-bottom:.2rem}
      /* enclosed rows: a line above every row and one below the last */
      .my-req{padding:.7rem 0;border-top:1px solid var(--line);cursor:pointer}
      .my-req:last-of-type{border-bottom:1px solid var(--line)}
      .my-req:hover .my-req-dest{color:var(--accent)}
      .my-req-top{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
      .my-req-dest{font-weight:700;color:var(--text)}
      .my-req-dates{font-size:.85rem;color:var(--text-muted);margin-top:.1rem}
      .my-req-next{font-size:.85rem;color:var(--text);margin-top:.25rem}
      .dash-card-consultant{font-weight:700;font-size:.98rem;color:var(--text)}
      .dash-card-dest{font-size:.88rem;color:var(--text-muted);margin-bottom:.55rem}
      .dash-card-badges{display:flex;flex-direction:column;align-items:flex-end;gap:.4rem;flex-shrink:0}
      .now-pill{font-size:.68rem;font-weight:700;color:var(--text-on-dark);background:var(--accent-fill);padding:.2rem .55rem;border-radius:var(--radius-pill);letter-spacing:.03em;white-space:nowrap}
      .dash-card-notes{font-size:.82rem;color:var(--text-muted);font-style:italic;margin-top:.55rem}

      /* ── Table ── */
      table{width:100%;border-collapse:collapse;font-size:.83rem}
      th{text-align:left;padding:.65rem .9rem;color:var(--text-muted);font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid var(--line)}
      td{padding:.7rem .9rem;border-bottom:1px solid #f0f3f7;color:var(--text)}
      tr:hover td{background:var(--surface-2)}

      /* ── Modal ── */
      .modal-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;z-index:1000;padding:1rem}
      .modal{z-index:1001;position:relative;background:var(--surface);border-radius:var(--radius-md);padding:2rem;width:100%;max-width:520px;max-height:92vh;overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,.2)}
      .modal-title{font-size:1.05rem;font-weight:700;color:var(--text);margin-bottom:1.5rem}
      /* Title-plus-close header. Both portals had modals whose markup already
         referenced these two classes while neither stylesheet defined them, so
         the heading fell back to browser defaults and the close button rendered
         as a raw grey button. */
      .modal-header{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin-bottom:1.25rem}
      .modal-header .modal-title{margin-bottom:0}
      .modal-close{background:none;border:none;cursor:pointer;color:var(--text-muted);
        font-size:1.15rem;line-height:1;padding:.25rem;margin:-.25rem -.25rem 0 0;
        min-width:38px;min-height:38px;display:flex;align-items:center;justify-content:center;border-radius:8px}
      .modal-close:hover{background:var(--surface-2);color:var(--text)}
      /* A modal that can be opened from inside another one. Every .modal-backdrop
         shares z-index 1000, so without this the winner is whichever sits later
         in the document — which is not a property the markup should depend on. */
      .modal-backdrop.modal-over-modal{z-index:1100}
      .modal-backdrop.modal-over-modal .modal{z-index:1101}
      .form-grid{display:grid;grid-template-columns:1fr 1fr;gap:.85rem}
      .form-grid .full{grid-column:1/-1}
      .modal-actions{display:flex;gap:.75rem;margin-top:1.5rem}

      /* ── Consultant roster ── */
      .consultant-row{display:flex;align-items:center;justify-content:space-between;padding:.6rem 0;border-bottom:1px solid #f0f3f7}
      .consultant-row:first-child{border-top:1px solid #f0f3f7}
      .consultant-name{font-weight:600;font-size:.88rem}
      .consultant-home{font-size:.78rem;color:var(--text-muted);margin-top:.1rem}

      /* ── Loading / Empty / Error ── */
      .loading{text-align:center;padding:3rem 1rem;color:var(--text-muted);font-size:.9rem}
      .spinner{width:28px;height:28px;border:3px solid var(--line);border-top-color:#0096D6;border-radius:50%;animation:spin .7s linear infinite;margin:0 auto 1rem}
      @keyframes spin{to{transform:rotate(360deg)}}
      @keyframes spin-cluster{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
      .empty{text-align:center;padding:3rem 1rem;color:var(--text-muted)}
      .empty-icon{font-size:2.5rem;margin-bottom:.75rem}
      .empty-text{font-size:.9rem}
      .error-msg{background:var(--danger-bg);border:1px solid var(--danger-border);border-radius:var(--radius-md);padding:1rem 1.25rem;color:var(--danger-ink);font-size:.85rem;margin-bottom:1rem}

      /* ── Flight tracking ── */
      .flight-status-wrap{margin-top:.5rem;display:flex;flex-direction:column;gap:.3rem}
      .flight-leg{display:flex;align-items:center;gap:.5rem;font-size:.78rem;color:var(--text-muted);padding:.3rem .5rem;border-radius:6px;background:var(--surface-2)}
      .plane-icon{font-size:1rem}

      /* ── Custom calendar widget ── */
      .cal-wrap{position:relative;display:inline-block}
      .cal-display{display:flex;align-items:center;gap:.4rem;border:1px solid var(--line-strong);border-radius:8px;padding:.5rem .75rem;font-size:.85rem;color:var(--text);background:var(--surface);cursor:pointer;min-width:130px;transition:border .15s;user-select:none}
      .cal-display:hover{border-color:#0096D6}
      .cal-display.open{border-color:#0096D6;box-shadow:0 0 0 3px #e0f4fb}
      .cal-dropdown{position:absolute;top:calc(100% + 6px);left:0;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-md);box-shadow:0 4px 20px rgba(0,0,0,.12);padding:.75rem;z-index:500;width:260px;display:none}
      .cal-dropdown.open{display:block}
      .cal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:.6rem}
      .cal-month-label{font-size:.85rem;font-weight:700;color:var(--text)}
      .cal-nav{background:none;border:none;cursor:pointer;color:var(--text-muted);font-size:1rem;padding:.2rem .4rem;border-radius:4px;line-height:1;transition:all .15s}
      .cal-nav:hover{background:var(--surface-3);color:var(--text)}
      .cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
      .cal-dow{text-align:center;font-size:.68rem;font-weight:700;color:var(--text-muted);padding:.2rem 0;text-transform:uppercase;letter-spacing:.05em}
      .cal-day{text-align:center;font-size:.78rem;padding:.3rem .1rem;border-radius:6px;cursor:pointer;color:var(--text);transition:all .1s;position:relative}
      .cal-day:hover{background:var(--surface-3)}
      .cal-day.empty{cursor:default}
      .cal-day.today{font-weight:700}
      .cal-day.today::after{content:'';position:absolute;bottom:2px;left:50%;transform:translateX(-50%);width:4px;height:4px;border-radius:50%;background:var(--navy)}
      .cal-day.selected{background:var(--accent-fill);color:var(--text-on-dark);font-weight:700}
      .cal-day.selected::after{background:var(--surface)}
      .cal-day.has-trips{background:var(--accent-soft);color:var(--accent-ink)}
      .cal-day.has-trips:hover{background:#c7e8f5}
      .cal-day.selected.has-trips{background:var(--accent-fill);color:var(--text-on-dark)}
      .cal-legend{display:flex;gap:.75rem;margin-top:.6rem;padding-top:.6rem;border-top:1px solid #f0f3f7}
      .cal-legend-item{display:flex;align-items:center;gap:.3rem;font-size:.7rem;color:var(--text-muted)}
      .cal-legend-dot{width:10px;height:10px;border-radius:3px}
      .date-arrow{background:var(--surface-3);border:1px solid var(--line-strong);border-radius:6px;width:28px;height:34px;cursor:pointer;font-size:.95rem;color:var(--text);display:flex;align-items:center;justify-content:center;transition:all .15s;flex-shrink:0}
      .date-arrow:hover{background:var(--chip-blue-bg);border-color:#0096D6;color:var(--accent)}
      .btn-today{background:var(--surface-3);border:1px solid var(--line-strong);border-radius:6px;height:34px;padding:0 .65rem;cursor:pointer;font-size:.75rem;font-weight:700;color:var(--text);letter-spacing:.03em;transition:all .15s;flex-shrink:0;white-space:nowrap;font-family:inherit}
      .btn-today:hover{background:var(--chip-blue-bg);border-color:#0096D6;color:var(--accent)}
      .last-updated-bar{display:flex;align-items:center;gap:.75rem;font-size:.75rem;color:var(--text-muted);padding:.4rem 0 .75rem;margin-bottom:.25rem}
      .last-updated-bar button{background:none;border:none;color:var(--accent);font-size:.75rem;font-weight:600;cursor:pointer;padding:0;font-family:inherit}
      .last-updated-bar button:hover{text-decoration:underline}

      /* ── Collapsible alerts ── */
      .alert-section{margin-bottom:1.25rem}
      .alert-header{display:flex;align-items:center;justify-content:space-between;cursor:pointer;user-select:none;padding:.1rem 0}
      .alert-header:hover .alert-toggle{color:var(--accent)}
      .alert-toggle{font-size:.8rem;color:var(--text-muted);transition:transform .2s;display:inline-block}
      .alert-toggle.open{transform:rotate(180deg)}
      .alert-body{overflow:hidden;transition:max-height .25s ease}
      .alert-body.collapsed{max-height:0!important}

      /* ── Responsive ── */
      /* ── Today view ──────────────────────────────────────────────────────
         The landing screen for a travelling consultant. A card per thing they
         need in the next few hours, ordered by when they need it. */
      .today-hero{background:var(--navy);color:var(--text-on-dark);border-radius:var(--radius-md);padding:1.25rem 1.4rem;margin-bottom:1rem}
      .today-hero-eyebrow{font-size:.7rem;letter-spacing:.12em;text-transform:uppercase;color:#8fc3e3;font-weight:700}
      .today-hero-dest{font-size:1.5rem;font-weight:800;letter-spacing:-.01em;margin:.25rem 0 .1rem;text-wrap:balance}
      .today-hero-sub{font-size:.85rem;color:#c6d6e6}
      .today-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-md);padding:1rem 1.15rem;margin-bottom:.75rem}
      .today-card-head{display:flex;align-items:center;gap:.5rem;font-size:.72rem;font-weight:700;letter-spacing:.07em;
        text-transform:uppercase;color:var(--text-muted);margin-bottom:.6rem}
      .today-row{display:flex;align-items:baseline;gap:.6rem;flex-wrap:wrap}
      .today-primary{font-size:1.05rem;font-weight:700;color:var(--text)}
      .today-secondary{font-size:.85rem;color:var(--text-muted)}
      .today-status{display:inline-flex;align-items:center;gap:.3rem;font-size:.72rem;font-weight:700;
        padding:.2rem .55rem;border-radius:var(--radius-pill);background:var(--badge-gray-bg);color:var(--badge-gray-text)}
      .today-status.live{background:var(--badge-blue-bg);color:var(--badge-blue-text)}
      .today-status.warn{background:var(--badge-amber-bg);color:var(--badge-amber-text)}
      .today-status.bad{background:var(--danger-soft);color:var(--danger-ink)}
      .today-status.ok{background:var(--badge-green-bg);color:var(--badge-green-text)}
      /* Big, obvious tap targets — this is used one-handed in an airport. */
      .today-action{display:flex;align-items:center;justify-content:center;gap:.4rem;min-height:44px;
        border-radius:10px;font-size:.88rem;font-weight:700;text-decoration:none;cursor:pointer;border:1.5px solid var(--line);
        background:var(--surface);color:var(--text);padding:.55rem 1rem}
      .today-action:hover{border-color:var(--accent);color:var(--accent)}
      .today-action.primary{background:var(--accent-fill);border-color:var(--accent);color:var(--text-on-dark)}
      .today-action.primary:hover{background:var(--accent-fill-hover);border-color:#006699;color:var(--text-on-dark)}
      .today-action.urgent{background:var(--danger-bg);border-color:var(--danger-border);color:var(--danger-ink)}
      .today-actions{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:.75rem}
      /* Gate, delay and record locator are scanned at a glance in bad light while
         walking, so they get weight and a box rather than sitting in body copy. */
      /* Several contacts share one card, so they need a divider between them
         rather than each becoming its own box competing for attention. */
      .today-contact + .today-contact{margin-top:.85rem;padding-top:.85rem;border-top:1px solid var(--line)}
      .today-ahead{opacity:.85}
      .today-ahead .today-card-head{color:var(--text-muted)}
      .today-gates{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:.45rem}
      .today-gate{display:inline-flex;align-items:baseline;gap:.35rem;background:var(--surface-2);
        border:1px solid var(--line);border-radius:6px;padding:.25rem .5rem;
        font-size:.82rem;font-weight:600;color:var(--text)}
      .today-gate-lbl{font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;
        font-weight:700;color:var(--text-muted)}
      .today-delay{display:inline-block;margin-left:.35rem;padding:.1rem .4rem;border-radius:var(--radius-pill);
        font-size:.72rem;font-weight:700}
      .today-delay.warn{background:var(--badge-amber-bg);color:var(--badge-amber-text)}
      .today-delay.bad{background:var(--danger-soft);color:var(--danger-ink)}
      .today-delay.ok{background:var(--badge-green-bg);color:var(--badge-green-text)}
      .today-pnr{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700;
        letter-spacing:.04em;color:var(--text)}
      /* The connection card carries its risk in the border, not only in the
         wording — a tight layover has to register before it is read. */
      .today-conn{border-left:4px solid var(--line)}
      .today-conn.ok{border-left-color:var(--success)}
      .today-conn.warn{border-left-color:var(--badge-amber-text)}
      .today-conn.bad{border-left-color:var(--danger)}
      .today-conn-note{margin-top:.3rem;font-size:.82rem;font-weight:700}
      .today-conn.warn .today-conn-note{color:var(--badge-amber-text)}
      .today-conn.bad .today-conn-note{color:var(--danger-ink)}
      .today-empty{text-align:center;padding:2.5rem 1rem;color:var(--text-muted)}

      /* ── Mobile bottom nav ───────────────────────────────────────────────
         Thumb-reachable primary navigation. Replaces the top tab strip under
         720px; the strip stays for wider screens. */
      #bottom-nav{display:none}
      @media(max-width:720px){
        nav{display:none}
        /* The fallback until ftSyncNavHeight measures the real thing: 56px of
           content, 1px of border-top, and the safe area. The border is the bit
           the old sum forgot. */
        :root{--ft-nav-h:calc(57px + var(--ft-safe-bottom))}
        #bottom-nav{
          display:flex;position:fixed;left:0;right:0;bottom:0;z-index:900;
          background:var(--surface);border-top:1px solid var(--line);
          padding-bottom:var(--ft-safe-bottom);
          box-shadow:0 -2px 12px rgba(0,0,0,.06)
        }
        #bottom-nav button{
          flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.15rem;
          min-height:56px;background:none;border:none;cursor:pointer;
          font:600 .68rem/1 'Segoe UI',system-ui,sans-serif;color:var(--text-muted);padding:.4rem 0
        }
        #bottom-nav button i{font-size:1.15rem}
        #bottom-nav button.active{color:var(--accent)}
        /* Clears the nav plus the offline banner that sits on top of it, so the
           last card is never left stranded underneath either. */
        main{padding-bottom:calc(var(--ft-nav-h) + 2.5rem)}
        body{padding-bottom:0}
      }

      @media(max-width:600px){
        header{padding:calc(.85rem + var(--ft-safe-top)) 1rem .85rem}
        /* Longhand for the bottom: the shorthand here used to land after the
           720px rule above and silently reset the nav clearance back to a flat
           5.5rem, undoing the safe-area allowance. */
        main{padding-left:1rem;padding-right:1rem;padding-top:0}
        .form-grid{grid-template-columns:1fr}
        nav{padding:0 1rem}
        .kpi-val{font-size:1.4rem}
        /* One column, roomier rows, and controls big enough to hit reliably. */
        .kpi-row{grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:.6rem}
        .card{padding:1.1rem 1rem}
        .trip-card{padding:1rem}
        .trip-meta{gap:.5rem}
        .filters{flex-direction:column;align-items:stretch}
        .filters .field-group{width:100%}
        select,input,textarea{font-size:16px}      /* 16px stops iOS zooming on focus */
        /* .card-photo-pick is a <label>, not a button, so it needs naming here
           to get the same touch target as everything beside it. */
        button,.btn-primary,.btn-ghost,.btn-sm,.card-photo-pick{min-height:40px}
        .today-hero-dest{font-size:1.3rem}
        table{font-size:.78rem}
      }

      /* Modals become bottom sheets on small screens — the same treatment the
         admin app already uses, ported here. */
      @media(max-width:540px){
        .modal-backdrop{padding:.5rem;align-items:flex-end}
        .modal{padding:1.25rem;max-height:92vh;border-radius:12px 12px 0 0}
      }


    
      
      /* Account dropdown. The header is a wrapping flex row, so on a narrow
         screen the account button is NOT at the right edge — anchoring the menu
         to the button with right:0 pushed a 277px menu off the left of a 375px
         viewport. Below 600px the wrapper stops being the containing block so the
         menu resolves against the header instead and spans it with even gutters. */
      #acct-wrap{position:relative}
      .acct-menu{
        position:absolute; right:0; top:calc(100% + 6px);
        background:var(--surface); border-radius:10px; box-shadow:0 4px 20px rgba(0,0,0,.15);
        min-width:220px; z-index:9999; overflow:hidden
      }
      @media(max-width:600px){
        header{position:relative}
        #acct-wrap{position:static}
        .acct-menu{left:1rem; right:1rem; min-width:0}
      }

      /* Icon font (replaces emoji) */
      @font-face{font-family:"tabler-icons";font-style:normal;font-weight:400;font-display:swap;src:url("vendor/icons/tabler-icons-subset.woff2") format("woff2")}
      .ic{font-family:"tabler-icons";font-style:normal;speak:none;font-variant:normal;text-transform:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}
      .ic-plane{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-plane:before{content:"\eb6f"}
      .ic-arrow-right{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-arrow-right:before{content:"\ea1f"}
      .ic-link{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-link:before{content:"\eade"}
      .ic-credit-card{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-credit-card:before{content:"\ea84"}
      .ic-alert-triangle{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-alert-triangle:before{content:"\ea06"}
      .ic-check{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-check:before{content:"\ea5e"}
      .ic-refresh{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-refresh:before{content:"\eb13"}
      .ic-clock{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-clock:before{content:"\ea70"}
      .ic-x{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-x:before{content:"\eb55"}
      .ic-clipboard{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-clipboard:before{content:"\ea6f"}
      .ic-arrows-left-right{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-arrows-left-right:before{content:"\edb0"}
      .ic-building{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-building:before{content:"\ea4f"}
      .ic-car{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-car:before{content:"\ebbb"}
      .ic-bus{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-bus:before{content:"\ebe4"}
      .ic-shield{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-shield:before{content:"\eb24"}
      .ic-target{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-target:before{content:"\eb35"}
      .ic-phone{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-phone:before{content:"\eb09"}
      .ic-map-pin{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-map-pin:before{content:"\eae8"}
      .ic-lock{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-lock:before{content:"\eae2"}
      .ic-users{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-users:before{content:"\ebf2"}
      .ic-bell{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-bell:before{content:"\ea35"}
      .ic-key{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-key:before{content:"\eac7"}
      .ic-eye{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-eye:before{content:"\ea9a"}
      .ic-corner-up-left{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-corner-up-left:before{content:"\ea82"}
      .ic-search{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-search:before{content:"\eb1c"}
      .ic-school{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-school:before{content:"\ecf7"}
      .ic-mail{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-mail:before{content:"\eae5"}
      .ic-home{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-home:before{content:"\eac1"}
      .ic-circle-check{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-circle-check:before{content:"\ea67"}
      .ic-paperclip{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-paperclip:before{content:"\eb02"}
      .ic-pencil{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-pencil:before{content:"\eb04"}
      .ic-trash{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-trash:before{content:"\eb41"}
      .ic-calendar{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-calendar:before{content:"\ea53"}
      .ic-file-text{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-file-text:before{content:"\eaa2"}
      .ic-arrows-shuffle{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-arrows-shuffle:before{content:"\f000"}
      .ic-tag{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-tag:before{content:"\10096"}
      .ic-building-factory-2{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-building-factory-2:before{content:"\f082"}
      .ic-bed{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-bed:before{content:"\eb5c"}
      .ic-plane-departure{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-plane-departure:before{content:"\eb9a"}
      .ic-plane-arrival{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-plane-arrival:before{content:"\eb99"}
      .ic-arrow-left{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-arrow-left:before{content:"\ea19"}
      .ic-user{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-user:before{content:"\eb4d"}
      .ic-download{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-download:before{content:"\ea96"}
      .ic-chart-bar{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-chart-bar:before{content:"\ea59"}
      .ic-map{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-map:before{content:"\eae9"}
      .ic-upload{font-family:"tabler-icons";font-style:normal;speak:none;line-height:1;display:inline-block;-webkit-font-smoothing:antialiased}.ic-upload:before{content:"\eb47"}
      .ic-plane:before{content:"\eb6f"}
      .ic-arrow-right:before{content:"\ea1f"}
      .ic-link:before{content:"\eade"}
      .ic-credit-card:before{content:"\ea84"}
      .ic-alert-triangle:before{content:"\ea06"}
      .ic-check:before{content:"\ea5e"}
      .ic-refresh:before{content:"\eb13"}
      .ic-clock:before{content:"\ea70"}
      .ic-x:before{content:"\eb55"}
      .ic-clipboard:before{content:"\ea6f"}
      .ic-arrows-left-right:before{content:"\edb0"}
      .ic-bed:before{content:"\eb5c"}
      .ic-car:before{content:"\ebbb"}
      .ic-shield:before{content:"\eb24"}
      .ic-target:before{content:"\eb35"}
      .ic-phone:before{content:"\eb09"}
      .ic-map-pin:before{content:"\eae8"}
      .ic-lock:before{content:"\eae2"}
      .ic-building:before{content:"\ea4f"}
      .ic-users:before{content:"\ebf2"}
      .ic-building-factory-2:before{content:"\f082"}
      .ic-bell:before{content:"\ea35"}
      .ic-key:before{content:"\eac7"}
      .ic-eye:before{content:"\ea9a"}
      .ic-corner-up-left:before{content:"\ea82"}
      .ic-search:before{content:"\eb1c"}
      .ic-school:before{content:"\ecf7"}
      .ic-mail:before{content:"\eae5"}
      .ic-home:before{content:"\eac1"}
      .ic-circle-check:before{content:"\ea67"}
      .ic-paperclip:before{content:"\eb02"}
      .ic-pencil:before{content:"\eb04"}
      .ic-trash:before{content:"\eb41"}
      .ic-calendar:before{content:"\ea53"}
      .ic-file-text:before{content:"\eaa2"}
      .ic-arrows-shuffle:before{content:"\f000"}
      .ic-tag:before{content:"\10096"}
      .ic-plane-departure:before{content:"\eb9a"}
      .ic-plane-arrival:before{content:"\eb99"}

/* Scrolling that reaches the end of a dialog must stop there rather than
   handing the gesture to the page behind it. Pairs with the body scroll lock in
   map-common.js: the lock stops the page moving, this stops the chain starting. */
.modal-backdrop, .disambig-backdrop { overscroll-behavior: contain; }
.modal, .disambig-box {
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;   /* momentum inside the dialog on iOS */
}

/* ── Mobile "More" sheet ───────────────────────────────────────────────────
   Overflow for tabs without a slot in the bottom bar. Sits just above the bar
   (z-index 950: over the nav at 900, under modals at 1000) and slides up from
   the bottom, where a thumb already is. */
#more-sheet {
  position: fixed; inset: 0; z-index: 950;
  background: rgba(15, 23, 42, .5);
  display: flex; align-items: flex-end;
}
#more-sheet-panel {
  width: 100%;
  background: var(--surface);
  border-radius: 14px 14px 0 0;
  padding: .5rem .75rem calc(.75rem + 56px + var(--ft-safe-bottom));
  box-shadow: 0 -8px 32px rgba(0, 0, 0, .3);
}
#more-sheet-grip {
  width: 38px; height: 4px; border-radius: 2px;
  background: var(--line); margin: .35rem auto .6rem;
}
.more-item {
  display: flex; align-items: center; gap: .7rem;
  width: 100%; min-height: 48px;
  background: none; border: none; cursor: pointer;
  padding: .6rem .5rem; border-radius: 10px;
  font: 600 .95rem 'Segoe UI', system-ui, sans-serif;
  color: var(--text); text-align: left;
}
.more-item:hover, .more-item:focus-visible { background: var(--surface-2); }
.more-item i { font-size: 1.15rem; color: var(--accent); }

/* ── Pull to refresh ──────────────────────────────────────────────────────
   The browser's own pull-to-refresh would fire alongside ours on Android,
   reloading the whole document underneath the gesture. Containing overscroll
   on the scrolling element is what suppresses it, and it also stops the
   rubber-band on iOS fighting the indicator. */
html, body { overscroll-behavior-y: contain; }

#ft-ptr {
  position: fixed; top: 0; left: 50%;
  transform: translate(-50%, 0);
  z-index: 950;                      /* under modals (1000), over the page */
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; margin-top: 8px;
  border-radius: 50%;
  background: var(--surface); color: var(--accent);
  border: 1px solid var(--line);
  box-shadow: 0 2px 10px rgba(0,0,0,.14);
  opacity: 0; pointer-events: none;
}
#ft-ptr i { font-size: 1.05rem; line-height: 1; display: block; }
/* Past the trigger point the icon commits to a full turn, so the pull has a
   clear "let go now" moment rather than an ambiguous halfway state. */
#ft-ptr.ready i { transform: rotate(180deg); transition: transform .18s ease; }
#ft-ptr.spinning i { animation: ft-ptr-spin .8s linear infinite; }
@keyframes ft-ptr-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  #ft-ptr.ready i, #ft-ptr.spinning i { animation: none; transition: none; transform: none; }
  #ft-ptr.spinning { opacity: 1; }
}

/* The Refresh button is traded for the pull gesture only where the gesture
   exists. On a mouse or trackpad there is nothing to pull, so the button
   stays — losing it there would leave no way to refresh at all. */
@media (pointer: coarse) {
  #refresh-btn, #map-refresh-btn { display: none; }
}

      /* A correction Operations made to the consultant's own figures. Neutral
         rather than alarming — it is information, not a fault — but never
         hidden: an unseen correction is a discrepancy they argue about later. */
      .er-changed{
        margin-top:.75rem;padding:.7rem .9rem;border-radius:10px;font-size:.84rem;
        background:var(--surface-2);border:1px solid var(--line);color:var(--text)
      }
      .er-changed div{margin-top:.3rem;color:var(--text-muted);font-variant-numeric:tabular-nums}

      /* A report line that can be opened. The row is the target and the two
         buttons stop the click from reaching it, so a tap on Remove never
         opens the editor on the way past. */
      .er-grid tr.er-editable{cursor:pointer}
      .er-grid tr.er-editable:hover{background:var(--surface-2)}
      .er-grid td.er-rowacts{white-space:nowrap}
      .er-grid td.er-rowacts .btn-link{margin-right:.5rem}

/* ── Trip request form (2026-08-18) ────────────────────────────────────────── */
.te-days{display:grid;grid-template-columns:repeat(auto-fill,minmax(52px,1fr));gap:.35rem}
.te-day{border:1px solid var(--line-strong);border-radius:8px;padding:.35rem .2rem;text-align:center;font-size:.68rem;color:var(--text-muted);cursor:pointer;user-select:none;background:var(--surface)}
.te-day b{display:block;font-size:.95rem;color:var(--text);font-weight:600}
.te-day small{display:block;font-size:.62rem;font-weight:700;margin-top:.1rem;text-transform:capitalize}
.te-day.travel{background:var(--surface-3)} .te-day.travel small{color:var(--text-muted)}
.te-day.service{background:var(--accent-soft);border-color:var(--accent)} .te-day.service small{color:var(--accent-ink)}
.te-day.personal{background:var(--warn-bg);border-color:var(--warn-border)} .te-day.personal small{color:var(--warn-ink)}
/* Unmarked: a dashed outline reads as a blank waiting to be filled, where a
   solid one reads as a thing already in a state. Nothing is pre-selected —
   see teDefaultDayTypes' epitaph in index-app.js (2026-08-28). */
.te-day.unset{border-style:dashed;border-color:var(--line-strong);background:var(--surface-2)}
.te-day.unset small{color:var(--text-muted);font-weight:600}
/* Only after a submit was refused. Colouring every unmarked day red on first
   sight would shout at somebody who has not been given a chance to answer yet. */
.te-day.unset.needs{border-color:var(--danger);border-style:solid;background:var(--danger-bg)}
.te-day.unset.needs small{color:var(--danger-ink)}
.te-days-left{color:var(--warn-ink);font-weight:700}
/* A locked trip's chips state what is known; they are not controls. Cursor and
   dashes matter: an unmarked day on a finished trip is a fact about the past,
   not a job (2026-08-28). */
.te-day-ro{cursor:default}
.te-day-ro.unset{border-style:dashed;opacity:.7}
.te-bk{border:1px solid var(--line);border-radius:10px;padding:.6rem .8rem}
.te-bk-t{display:flex;align-items:center;gap:.5rem;font-weight:600;font-size:.9rem;cursor:pointer}
.te-bk-t input{width:18px;height:18px;accent-color:#0096D6}
.te-bk-body{margin-top:.6rem;padding-top:.6rem;border-top:1px dashed var(--line)}
.te-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:.5rem}
.te-check{display:flex;align-items:center;gap:.45rem;font-size:.85rem;margin-top:.5rem;cursor:pointer}
.te-check input{width:16px;height:16px;accent-color:#0096D6}
/* Which direction the fields beneath belong to. Only drawn for a round trip,
   where there are two of everything and the sheet was previously ambiguous
   about which date owned which times. */
.te-leg-h{font-size:.72rem;font-weight:700;color:var(--accent-ink);text-transform:uppercase;letter-spacing:.06em;margin:.75rem 0 .1rem;padding-bottom:.25rem;border-bottom:1px solid var(--line)}
.te-leg-h:first-of-type{margin-top:.4rem}

/* The must-meet times. Boxed and inset so it reads as a rule attached to the
   leg above it rather than two more preference fields. */
.te-cons{border:1px solid var(--line);border-radius:8px;padding:.5rem .6rem .35rem;margin-top:.5rem;background:var(--surface-2)}
.te-cons-h{display:flex;align-items:baseline;justify-content:space-between;gap:.5rem;margin-bottom:.4rem}
.te-opt{font-weight:400;text-transform:none;letter-spacing:0;color:var(--text-faint)}
.te-cons-clear{border:0;background:none;padding:.1rem .2rem;margin:-.1rem -.2rem;font:600 .74rem/1.2 inherit;color:var(--accent-ink);cursor:pointer;border-radius:4px}
.te-cons-clear:hover{text-decoration:underline}
.te-cons-clear:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
/* --text-faint measures under AA at this size; --text-muted is 6.72:1. */
.te-cons-help{margin:.35rem 0 .15rem;font-size:.72rem;line-height:1.35;color:var(--text-muted)}

.te-hotel{border:1px dashed var(--line-strong);border-radius:8px;padding:.5rem .6rem;margin-bottom:.5rem}
.te-hotel .te-hotel-h{display:flex;justify-content:space-between;align-items:center;font-size:.78rem;color:var(--text-muted);margin-bottom:.35rem}

/* ── The trip request page (round three, 2026-08-18) ─────────────────── */
.te-page { max-width: 720px; margin: 0 auto; padding-bottom: 4.5rem; }
.te-head { display: flex; align-items: center; gap: .6rem; padding: .25rem 0 .9rem; }
.te-back { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text); display: grid; place-items: center; cursor: pointer; font-size: 1rem; }
.te-page-title { font-size: 1.15rem; font-weight: 700; }
.te-saved { margin-left: auto; font-size: .76rem; color: var(--text-faint); white-space: nowrap; }
.te-saved.busy { color: var(--text-muted); }
.te-saved.err { color: var(--danger); }
.te-bar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; background: var(--surface); border-top: 1px solid var(--line); padding: .6rem 1rem calc(.6rem + env(safe-area-inset-bottom)); display: flex; align-items: center; gap: .5rem; }
.te-bar .te-saved { flex: 1; text-align: left; }
.te-bar .btn-primary { width: auto; padding: .55rem 1rem; }
@media (min-width: 721px) { .te-bar { position: sticky; margin: 1rem -1rem -1rem; border-radius: 0 0 var(--radius-md) var(--radius-md); } }
/* Same anchoring as every other bottom bar since 2026-08-25: fixed to the
   bottom EDGE and held clear of the nav by padding, rather than offset upward
   by a measured nav height. The offset form is what drifted 4px away from the
   nav on the Today button, and it was here too. */
@media (max-width: 720px) {
  .te-bar {
    bottom: 0; z-index: 899;
    padding-bottom: calc(.6rem + var(--ft-nav-h));
  }
}
.te-add { font-size: .84rem; padding: .35rem .7rem; }

/* The row follows the LIST's width, not the viewport's — the same fix the
   purpose badge needed on Travel's trips page. Without this the container
   queries below never match and the row keeps its desktop shape on a phone. */
#te-selected-members-pills:empty { display: none; }
#search-list { container-type: inline-size; }
/* ── The trip page as six sections (2026-08-24) ───────────────────────────
   It was one card holding sixteen stacked things. Each block is now a section
   in the markup, so the order is fixed rather than whichever request finished
   first. Same chrome as the booking cards and the trips row. */
.tv-stack { display: flex; flex-direction: column; gap: .75rem; }
.tv-sec { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); }
.tv-sec-h { display: flex; align-items: baseline; gap: .5rem; padding: .7rem .9rem;
            font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
            color: var(--text-muted); border-bottom: 1px solid var(--line); }
.tv-sec-h .n { margin-left: auto; font-weight: 400; text-transform: none; letter-spacing: 0;
               font-size: .8rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.tv-sec-b { padding: .9rem; }
.tv-sec-b.flush { padding: 0 .9rem; }
.tv-empty { font-size: .82rem; color: var(--text-muted); padding: .8rem 0; }
.tv-sub { font-size: .66rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase;
          letter-spacing: .06em; padding: .8rem 0 .1rem; border-top: 1px solid var(--line); }

/* The identity block: the same facts, each behind its own label. */
.tv-facts { display: flex; flex-wrap: wrap; gap: .6rem 1.6rem; }
.tv-facts:empty { display: none; }
.tv-f { min-width: 0; }
.tv-fl { display: block; font-size: .66rem; font-weight: 600; color: var(--text-muted);
         text-transform: uppercase; letter-spacing: .06em; }
.tv-fv { display: block; font-size: .85rem; color: var(--text); margin-top: .1rem; overflow-wrap: anywhere; }
.tv-dim { color: var(--text-muted); font-weight: 400; }
.tv-note { font-size: .84rem; color: var(--text-muted); margin-top: .8rem; padding-top: .7rem;
           border-top: 1px solid var(--line); line-height: 1.5; white-space: pre-wrap; }
#td-flightlines { margin-top: .8rem; }

/* One row shape for a person, a place, a report or a receipt. */
.tv-gr { display: flex; align-items: flex-start; gap: .65rem; width: 100%; box-sizing: border-box;
         padding: .7rem 0; border-top: 1px solid var(--line); text-align: left; }
.tv-sec-b > .tv-gr:first-child, .tv-sec-b > .tv-gr-btn:first-child { border-top: 0; }
.tv-gr-btn { background: none; border-left: 0; border-right: 0; border-bottom: 0; font: inherit;
             color: inherit; cursor: pointer; }
.tv-gr-btn:hover { background: var(--surface-2); }
.tv-gr-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tv-gr-i { width: 30px; height: 30px; border-radius: 8px; background: var(--surface-3); color: var(--text-muted);
           display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.tv-gr-m { flex: 1; min-width: 0; }
.tv-gr-n { display: block; font-size: .86rem; font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
.tv-gr-s { display: block; font-size: .78rem; color: var(--text-muted); margin-top: .1rem; overflow-wrap: anywhere; }
.tv-gr-a { display: flex; gap: .4rem; margin-top: .45rem; flex-wrap: wrap; }
.tv-gr-k { flex: 0 0 auto; font-size: .68rem; color: var(--text-muted); text-transform: uppercase;
           letter-spacing: .05em; white-space: nowrap; padding-top: .15rem; }
.tv-gr-add { display: flex; gap: .5rem; flex-wrap: wrap; padding: .75rem 0; border-top: 1px solid var(--line); }

/* An expense: what it cost, and whether it is on a report yet. */
.tv-x { flex: 0 0 auto; text-align: right; }
.tv-x-a { display: block; font-size: .88rem; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.tv-x-s { display: block; font-size: .68rem; color: var(--text-muted); margin-top: .1rem; white-space: nowrap; }
.tv-x-s.loose { color: var(--warn-ink, #b45309); font-weight: 600; }
.tv-more { display: block; width: 100%; padding: .7rem 0; border: 0; border-top: 1px solid var(--line);
           background: none; color: var(--accent); font: 600 .8rem/1.3 inherit; cursor: pointer; text-align: left; }
.tv-loose { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; padding: .75rem 0;
            border-top: 1px solid var(--line); font-size: .82rem; color: var(--text-muted); }
.tv-loose .btn-sm { margin-left: auto; }

@media (max-width: 560px) {
  .tv-facts { gap: .55rem 1.1rem; }
  .tv-gr-k { display: none; }   /* the glyph says which it is; the label is the first thing to give */
}

/* ── A booking, as a card (2026-08-24) ────────────────────────────────────
   Replaces four stacks of grey boxes on the trip page. Same shell for every
   mode, which is how Zoho's trip page reads and therefore what a consultant
   already knows. Card chrome matches the itinerary cards and the trips row:
   1px --line, --radius-md. */
.bk-sec { padding: .9rem 0; border-top: 1px solid var(--line); }
.tv-sec-b > .bk-sec:first-child { border-top: 0; }
.bk-sec-h { display: flex; align-items: baseline; gap: .4rem; font-size: .72rem; font-weight: 700;
            text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); margin-bottom: .5rem; }
.bk-sec-t { margin-left: auto; font-weight: 600; text-transform: none; letter-spacing: 0; font-size: .8rem;
            color: var(--text); font-variant-numeric: tabular-nums; }
.bk-card { border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface);
           padding: .75rem .85rem; margin-bottom: .5rem; }
.bk-head { display: flex; align-items: flex-start; gap: .6rem; flex-wrap: wrap; }
.bk-ico { width: 30px; height: 30px; border-radius: 8px; background: var(--surface-3); color: var(--text-muted);
          display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.bk-h-main { flex: 1 1 10rem; min-width: 0; display: block; }
.bk-title { display: block; font-size: .92rem; font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
.bk-sub { display: block; font-size: .78rem; color: var(--text-muted); margin-top: .1rem; }
.bk-badge { flex: 0 0 auto; align-self: center; font-size: .66rem; font-weight: 700; text-transform: uppercase;
            letter-spacing: .04em; padding: .2rem .5rem; border-radius: 999px;
            background: var(--success-bg); color: var(--success-ink, #166534); white-space: nowrap; }

/* Reference and amount, the line Zoho puts directly under the title. */
.bk-ref { display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap;
          margin-top: .6rem; padding-top: .55rem; border-top: 1px solid var(--line); }
.bk-code { display: block; font-size: .84rem; font-weight: 600; color: var(--accent-ink);
           font-variant-numeric: tabular-nums; margin-top: .1rem; overflow-wrap: anywhere; }
.bk-amt { margin-left: auto; font-size: .95rem; font-weight: 700; color: var(--text);
          font-variant-numeric: tabular-nums; white-space: nowrap; }

.bk-facts { display: flex; flex-wrap: wrap; gap: .55rem 1.5rem; margin-top: .6rem; }
.bk-f { min-width: 0; }
.bk-fl { display: block; font-size: .66rem; font-weight: 600; color: var(--text-muted);
         text-transform: uppercase; letter-spacing: .06em; }
.bk-fv { display: block; font-size: .82rem; color: var(--text); margin-top: .1rem; overflow-wrap: anywhere; }

/* A flight leg: the two ends and the time between them. */
.bk-legs { margin-top: .6rem; display: flex; flex-direction: column; gap: .55rem; }
.bk-leg { border: 1px solid var(--line); border-radius: var(--radius-md); padding: .55rem .65rem; background: var(--surface-2); }
.bk-leg-top { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; font-size: .76rem; color: var(--text-muted); }
.bk-leg-no { font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.bk-leg-day { margin-left: auto; white-space: nowrap; }
.bk-leg-row { display: flex; align-items: center; gap: .5rem; margin-top: .35rem; }
.bk-end { min-width: 0; flex: 0 1 auto; }
.bk-end-r { text-align: right; }
.bk-end-t { display: block; font-size: .9rem; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.bk-end-c { display: block; font-size: .74rem; font-weight: 700; color: var(--text-muted); letter-spacing: .04em; }
.bk-end-p { display: block; font-size: .72rem; color: var(--text-muted); overflow-wrap: anywhere; }
/* The connector, with a ceiling.
   It was flex:1 with no max, so it swallowed every spare pixel of the card:
   on a desktop the two ends were shoved to opposite edges of a wide row and
   you could not read the departure and the arrival without moving your eyes
   across the whole screen — which is the one comparison this row exists to
   make. Capped, the pair stays together and the row packs to the left at
   whatever width the card happens to be. Reported 2026-08-25. */
.bk-mid { flex: 1 1 auto; max-width: 5rem; display: flex; flex-direction: column; align-items: center; gap: .15rem; min-width: 3rem; }
.bk-line { display: block; width: 100%; height: 1px; background: var(--line-strong); }
.bk-leg-x { font-size: .74rem; color: var(--text-muted); margin-top: .3rem; }

.bk-foot { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
           margin-top: .6rem; padding-top: .55rem; border-top: 1px solid var(--line); }
.bk-foot-i { font-size: .78rem; color: var(--text-muted); }
.bk-zoho { font-size: .7rem; color: var(--text-muted); }

@media (max-width: 560px) {
  .bk-amt { margin-left: 0; }
  .bk-facts { gap: .5rem 1rem; }
  .bk-end-p { display: none; }   /* the code carries it; the city is the first thing to give */
}

/* ── The Trips row (2026-08-24) ───────────────────────────────────────────
   The same facts the row always carried, each behind its own label, plus the
   Booking Status block. Only .rc-full rows get this; Today's Upcoming card
   uses the plain .row-card and is unchanged. */
.row-card.rc-full .row-main { grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; padding: .85rem .9rem; }
.rc-body { min-width: 0; }
.rc-grid { display: flex; flex-wrap: wrap; gap: .3rem 1.4rem; margin-top: .45rem; }
.rc-f { min-width: 0; }
.rc-fl { display: block; font-size: .66rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .06em; }
.rc-fv { display: block; font-size: .82rem; color: var(--text); margin-top: .1rem; overflow-wrap: anywhere; }
.rc-f:first-child .rc-fv { font-variant-numeric: tabular-nums; white-space: nowrap; }
.rc-book { display: flex; flex-direction: column; align-items: center; gap: .4rem; padding-left: 1rem;
           border-left: 1px dashed var(--line); align-self: stretch; justify-content: center; }
.rc-rings { display: flex; align-items: center; gap: .25rem; }
.rc-ring { width: 35px; height: 35px; border-radius: 50%; border: 2px solid var(--line-strong);
           display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
           color: var(--text-muted); font-size: .95rem; }
.rc-ring.booked { border-color: #22b378; }
.rc-ring.pending { border-color: #f9ab6b; }
.rc-none { font-size: .76rem; color: var(--text-muted); white-space: nowrap; }

/* Narrow: the booking block drops below the facts and the dashed rule turns
   horizontal, which is the phone board. Kept as a container query so it
   follows the LIST's width — #search-list is already a container. */
@container (max-width: 620px) {
  .row-card.rc-full .row-main { grid-template-columns: auto minmax(0, 1fr); }
  .rc-book { grid-column: 1 / -1; flex-direction: row; align-items: center; justify-content: flex-start;
             gap: .6rem; padding: .6rem 0 0; margin-top: .55rem;
             border-left: 0; border-top: 1px dashed var(--line); }
  .rc-book .rc-fl { flex: 1; }
  .rc-grid { gap: .3rem 1rem; }
}
@container (max-width: 400px) {
  .rc-ring { width: 30px; height: 30px; font-size: .85rem; }
}

/* The request page as sections (Option A, 2026-08-23). Same fields, same order,
   given headings so the page reads as three things to do. */
.te-form { display: flex; flex-direction: column; gap: .85rem; }
.te-sec { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); }
.te-sec-h { margin: 0; padding: .7rem .9rem; font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); border-bottom: 1px solid var(--line); }
.te-sec-b { padding: .9rem; display: flex; flex-direction: column; gap: .85rem; }
.te-more > summary { cursor: pointer; display: flex; align-items: center; gap: .5rem; list-style: none; }
.te-more > summary::-webkit-details-marker { display: none; }
.te-more > summary::after { content: ""; width: .45rem; height: .45rem; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(-45deg); margin-left: auto; transition: transform .15s; }
.te-more[open] > summary::after { transform: rotate(45deg); }
.te-more:not([open]) > summary { border-bottom: 0; }
.te-more-sum { font-weight: 400; text-transform: none; letter-spacing: 0; font-size: .78rem; color: var(--text-faint); }
/* Typed add tiles — the same three teAddItem calls, given a target worth
   tapping on a phone. */
.te-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; }
.te-tile { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .3rem; min-height: 66px; padding: .6rem .35rem; border: 1px dashed var(--line-strong); border-radius: var(--radius-md); background: var(--surface); color: var(--accent-ink); font: 600 .8rem/1.2 inherit; cursor: pointer; text-align: center; }
.te-tile i { font-size: 1.1rem; color: var(--accent-fill); }
.te-tile:hover { border-style: solid; border-color: var(--accent); background: var(--accent-soft); }
.te-sheet { max-width: 520px; }
.te-sheet .field-label { font-size: .7rem; }
.te-choice { display: flex; gap: .4rem; flex-wrap: wrap; }
.te-choice button { font: inherit; font-size: .8rem; padding: .3rem .7rem; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--text-muted); cursor: pointer; }
.te-choice button.on { background: var(--accent-soft); color: var(--accent-ink); border-color: transparent; font-weight: 700; }

/* ── Today, round three: the agenda while on the trip; row cards below ── */
.today-card-head .today-more { margin-left: auto; font-size: .8rem; font-weight: 600; color: var(--accent); text-decoration: none; }
.today-card.ag { padding-top: .8rem; }
.ag-day { padding: .5rem 0 .3rem; border-top: 1px solid var(--line); }
.ag-day:last-of-type { border-bottom: 1px solid var(--line); }
.ag-day.past { opacity: .6; }
.ag-day.today { background: var(--accent-soft); margin: 0 -1.15rem; padding-left: 1.15rem; padding-right: 1.15rem; border-radius: 6px; }
.ag-h { display: flex; align-items: center; gap: .5rem; font-size: .8rem; font-weight: 700; color: var(--text-muted); margin-bottom: .2rem; }
.ag-h .now-pill { font-size: .6rem; padding: .1rem .45rem; }
.ag-kind { margin-left: auto; font-size: .68rem; font-weight: 700; text-transform: capitalize; padding: .1rem .5rem; border-radius: 999px; background: var(--surface-3); color: var(--text-muted); }
.ag-kind.service { background: var(--accent-soft); color: var(--accent-ink); }
.ag-kind.personal { background: #fdf1d8; color: #7a4d00; }
.ag-ev { display: flex; gap: .6rem; padding: .25rem 0; font-size: .9rem; }
.ag-ev .ag-t { flex: 0 0 4.2rem; font-size: .78rem; color: var(--text-muted); font-variant-numeric: tabular-nums; padding-top: .1rem; }
.ag-ev b { font-weight: 600; }
.ag-ev .ag-d { font-size: .8rem; color: var(--text-muted); }
/* the layover between two legs: same row shape, quieter, indented under the plane */
.ag-ev.ag-lay { padding-left: 1rem; }
.ag-ev.ag-lay b { font-weight: 500; color: var(--text-muted); }
#tab-today .row-card { border-bottom: 1px solid var(--line); }
#tab-today .row-card:first-child { border-top: 1px solid var(--line); }

/* ── Trip detail as a page ── */
.tv-head { display: flex; align-items: center; gap: .6rem; margin: .1rem 0 .8rem; }
.tv-back { width: 38px; height: 38px; border-radius: 9px; border: 1px solid var(--line); background: var(--surface); color: var(--text); display: grid; place-items: center; font-size: 1rem; cursor: pointer; flex: 0 0 38px; }
.tv-back:hover { background: var(--surface-2); }
.tv-title { font-size: 1.15rem; font-weight: 700; color: var(--text); flex: 1; min-width: 0; line-height: 1.25; }
.tv-card { padding: 1rem 1.1rem; }
.tv-req { border: 1px solid var(--line); border-radius: var(--radius-md); padding: .75rem .9rem; margin-top: .9rem; background: var(--surface-2); }
.tv-req .lab { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); }
.tv-req .head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.tv-req .meta { font-size: .8rem; color: var(--text-muted); margin-top: .3rem; }
.tv-req .bp { font-size: .9rem; margin-top: .4rem; }
.tv-req .itin-wrap { margin-top: .5rem; overflow-x: auto; }

      /* Receipts as row cards (2026-08-18): the row card's shell wins over the
         old bordered tile, and the thumb keeps letterboxing the receipt. */
      .row-card.rc-item{display:block;gap:0;background:none;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:0;margin:0}
      .row-card.rc-item:hover{border-color:var(--line);background:var(--surface-2)}
      .row-card .row-thumb .rc-thumb{width:100%;height:100%;flex:none;border:0;border-radius:6px;object-fit:contain;background:#f3f5f8}
      .row-card .rc-amount{margin-left:0}
      #rc-list,#er-list,#search-list{border:1px solid var(--line);border-radius:var(--radius-md);overflow:hidden;background:var(--surface)}
      #rc-list .row-card:last-child,#er-list .row-card:last-child,#search-list .row-card:last-child{border-bottom:0}

      /* ── Desktop shell: band + sidebar, like the admin app (2026-08-18) ──
         Above the phone breakpoint the tab strip becomes a left column; the
         band stays across the top; main takes the rest. Phones keep the
         bottom nav. Everything positioned fixed (overlays, sheets, toasts) is
         out of the grid and unaffected. */
      @media(min-width:721px){
        body{display:grid;grid-template-columns:200px minmax(0,1fr);grid-template-rows:auto 1fr;grid-template-areas:"header header" "nav main";min-height:100vh}
        header{grid-area:header}
        nav{grid-area:nav;flex-direction:column;align-items:stretch;gap:.15rem;padding:.9rem .6rem;border-bottom:0;border-right:1px solid var(--line);position:sticky;top:0;align-self:start;max-height:100vh;overflow:auto;box-sizing:border-box}
        nav .tab{display:flex;align-items:center;gap:.55rem;padding:.5rem .7rem;font-size:.86rem;border-radius:var(--radius-sm)}
        nav .tab i{width:1.1rem;text-align:center;color:var(--text-faint)}
        nav .tab.active{background:var(--accent-soft);color:var(--accent-ink)}
        nav .tab.active i{color:var(--accent-ink)}
        main{grid-area:main;margin:1.5rem auto;width:100%;max-width:1100px}
      }

      /* Flight sheet: one way · round trip · multi-city */
      .te-modes{display:flex;gap:.3rem;margin-bottom:.6rem;background:var(--surface-2);border-radius:999px;padding:.2rem}
      .te-mode{flex:1;border:0;background:none;border-radius:999px;padding:.35rem .5rem;font:600 .8rem/1.2 inherit;color:var(--text-muted);cursor:pointer}
      .te-mode.on{background:var(--surface);color:var(--accent-ink);box-shadow:0 1px 2px rgba(0,0,0,.08)}

      /* Today: the Spending card folds (collapsed by default); its head is the
         toggle. On phones the receipt button is fixed directly on top of the
         bottom nav — no gap — and stays whether the card is open or not; the
         page scrolls behind it. Wider screens keep it inline, inside the fold. */
      .today-card-toggle{display:flex;align-items:center;gap:.4rem;width:100%;background:none;border:0;padding:0;cursor:pointer;text-align:left;font:inherit;color:inherit}
      .today-card-toggle .today-toggle-count{margin-left:.35rem;font-weight:600;font-size:.72rem;color:var(--accent-ink);background:var(--accent-soft);border-radius:999px;padding:.05rem .45rem}
      .today-card-toggle .today-toggle-count:empty{display:none}
      .today-card-toggle .today-chev{margin-left:auto;transition:transform .15s ease;color:var(--text-muted)}
      .today-snap:not(.collapsed) .today-card-toggle .today-chev{transform:rotate(180deg)}
      .today-snap.collapsed .today-collapse{display:none}
      .today-snap.collapsed .today-card-toggle{margin-bottom:0}
      @media(max-width:720px){
        /* Anchored to the bottom edge, the same edge the nav uses, and held clear
           of it by padding rather than by an offset. Two things fixed to the
           same origin cannot drift apart; an offset against a measured height
           can, and did — by 4px. */
        .today-snap-bar{position:fixed;left:0;right:0;bottom:0;z-index:890;display:flex;flex-direction:column;justify-content:flex-end;padding:.5rem .9rem 0;padding-bottom:var(--ft-nav-h);background:linear-gradient(to bottom, transparent, var(--bg-page) 35%)}
        .today-snap.collapsed .today-collapse{display:block}
        .today-snap.collapsed .today-collapse > .today-secondary{display:none}
        .today-snap-bar .rc-capture{margin:0;flex:1 1 auto;box-shadow:0 6px 18px rgba(0,0,0,.14);border-radius:12px 12px 0 0;border-bottom:0}
        /* room at the end of Today so the last card clears the fixed button */
        #tab-today{padding-bottom:4.5rem}

        /* ── Save, Cancel and Delete on the bottom edge (2026-08-25) ──
           The same treatment as Snap a receipt above, for the same reason:
           that is where a thumb already is, and on a long sheet the primary
           action otherwise scrolls off the end — on the receipt page you had
           to scroll past four crop windows, six fields and every split line
           to reach Save.

           Anchored to bottom:0 and held clear of the nav by PADDING, never by
           an offset against a measured nav height. Two things fixed to the
           same origin cannot drift apart; an offset can, and did, by 4px.

           Page action rows only. The modals on this portal are already bottom
           sheets on a phone, so their own buttons are thumb-reachable without
           this — and pinning a row inside a sheet to the viewport would tear
           it out of the sheet it belongs to. Adding another page here is one
           more selector on this line. */
        #tab-receipt > .card > .card-actions{
          position:fixed;left:0;right:0;bottom:0;z-index:899;
          margin:0;padding:.55rem .9rem;
          padding-bottom:calc(.55rem + var(--ft-nav-h));
          background:var(--surface);border-top:1px solid var(--line);
          box-shadow:0 -6px 18px rgba(0,0,0,.10)
        }
        /* Room at the end so the last field clears the bar rather than hiding
           under it. */
        #tab-receipt{padding-bottom:4rem}
      }

      /* ── The last modals become bottom sheets on phones (2026-08-18) ──
         Report picker, mileage, booking line, duplicate, contact/location,
         TripWire: they slide up from the bottom, full width, thumb-reachable,
         with the top corners rounded — the same shape as the itinerary sheet.
         Desktop keeps them centred. */
      @media(max-width:720px){
        .modal-backdrop{align-items:flex-end;padding:0}
        .modal-backdrop .modal{max-width:none;border-radius:var(--radius-md) var(--radius-md) 0 0;padding:1.1rem 1rem calc(1rem + var(--ft-safe-bottom));max-height:88vh;box-shadow:0 -12px 40px rgba(0,0,0,.25);animation:ft-sheet-up .18s ease-out}
        .modal-backdrop .modal::before{content:"";display:block;width:36px;height:4px;border-radius:2px;background:var(--line-strong);margin:0 auto .8rem}
      }
      @keyframes ft-sheet-up{from{transform:translateY(24px);opacity:.6}to{transform:none;opacity:1}}
      @media(prefers-reduced-motion:reduce){.modal-backdrop .modal{animation:none}}

      /* Desktop drop zone on Receipts: a quiet dashed strip that lights up while
         a file is over the tab. Phones have the camera; the strip hides there. */
      .ft-dropzone{display:none;border:1.5px dashed var(--line-strong);border-radius:var(--radius-md);padding:.8rem 1rem;text-align:center;font-size:.85rem;color:var(--text-muted);margin-bottom:.6rem}
      @media(min-width:721px){.ft-dropzone{display:block}}
      #tab-receipts.ft-dropping .ft-dropzone{border-color:var(--accent);background:var(--accent-soft);color:var(--accent-ink)}
      #tab-receipts.ft-dropping{outline:2px dashed var(--accent);outline-offset:-4px;border-radius:var(--radius-md)}

/* ── Dialogs against the visible viewport, not the layout one ──────────────
   See ftSyncViewportVars in map-common.js. --ft-vvh is the height actually on
   screen; with a keyboard up that is a fraction of the layout viewport, which
   is what vh keeps measuring. Without the custom properties every value below
   falls back to exactly what this file said before.

   --ft-cap reproduces this portal's own caps (92vh, 88vh once the sheet takes
   over at 720px) so nothing moves when there is no keyboard. */
.modal-backdrop { --ft-cap: 92vh; top: var(--ft-vvtop, 0px); height: var(--ft-vvh, 100%); bottom: auto }
@media (max-width: 720px) { .modal-backdrop { --ft-cap: 88vh } }
.modal-backdrop > .modal { max-height: min(var(--ft-cap), calc(var(--ft-vvh, 100vh) - 1rem)) }

/* Bars pinned to the bottom edge ride above the keyboard rather than behind
   it. Both of these sit on pages that are mostly form: the receipt page's
   Save/Cancel/Delete, and Save as draft / Send to the Travel Team on a trip
   request. Tapping a field to correct a total is exactly when the button to
   keep the correction disappeared. --ft-kb is 0 unless a keyboard is actually
   up (see ftSyncViewportVars), so nothing moves otherwise. */
#tab-receipt > .card > .card-actions, .te-bar { bottom: var(--ft-kb, 0px) }
/* The bottom nav is behind the keyboard too, so a bar that has risen above the
   keys must stop reserving room for a nav nobody can see. */
.ft-kb-open #tab-receipt > .card > .card-actions { padding-bottom: .55rem }
.ft-kb-open .te-bar { padding-bottom: .6rem }

/* ── An expense that is not ready to file ──────────────────────────────────
   The row carries one pill saying how many answers are outstanding; this is
   how the sheet says WHICH. A ring rather than an overlay: the field has to
   stay readable and typeable while it is being complained about, and the ring
   sits exactly where the focus ring sits, so it reads as a state of the field
   rather than as something laid on top of the form.

   Inset box-shadow, not a border, so a ringed field is the same size as the
   ones beside it and nothing shifts as marks come and go while typing. */
.rc-need{
  box-shadow:inset 0 0 0 1.5px var(--danger);
  border-color:var(--danger)
}
/* Focus still wins: while the reader is IN the field, the ring should say
   "you are here", not "this is wrong" — they are already fixing it. */
.rc-need:focus{
  box-shadow:0 0 0 3px var(--accent-soft);
  border-color:var(--accent)
}
.rc-needs{
  margin-top:.85rem;padding:.6rem .85rem;border-radius:10px;
  font-size:.82rem;line-height:1.45;
  background:var(--danger-bg);border:1px solid var(--danger-border);color:var(--danger-ink)
}

      /* The reader sometimes measures a box across most of the receipt's
         width. Fitted whole — which is the point, so nothing is cut mid-word —
         that scales to a few pixels of unreadable grey. Say so, the way
         "not located" already does, rather than leave a smudge that looks like
         a rendering fault. The picture stays behind it: where on the receipt
         the value came from is still worth seeing. Set by ftFitCrop. */
      .rc-crop.rc-crop-tiny .rc-crop-win{ position:relative }
      .rc-crop.rc-crop-tiny .rc-crop-win img{ opacity:.35 }
      .rc-crop.rc-crop-tiny .rc-crop-win::after{
        content:'Too wide to show — check it on the receipt';
        position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
        text-align:center; padding:0 .5rem; font-size:.7rem; line-height:1.3;
        color:var(--text-muted); background:color-mix(in srgb, var(--surface) 72%, transparent);
      }
