/* ============================================================================================================
   PHONE LAYER — the app on a handheld (a Galaxy S25 is 384 css px wide; the field range is 360-430).

   Everything in this file lives inside `@media (max-width: 47.99rem)` (below the tablet step at 48rem that
   the rest of the app already uses), so the tablet and desktop layouts are not touched by a single rule here.
   Linked AFTER the head's scoped-CSS bundle (TradeYoke.Web.styles.css) so these overrides win on order; where
   a component's scoped rule is more specific, the selector below is deliberately a notch heavier and says so.

   Components reference only --ty-* tokens (never raw hex) — see /design/ui. The chrome geometry tokens
   (--ty-bottomnav-h, --ty-trialstrip-h, --ty-fab-*) are re-published here for the phone so every consumer
   of the corner rail (the + button, messages, the page's own FAB, the content's bottom padding) moves together.

   Behavioural classes on <html> come from mobile-shell.js:  ty-kbd (keyboard up) · ty-chrome-away (scrolling down).
   ============================================================================================================ */

@media (max-width: 47.99rem) {

    /* ── Chrome geometry ──────────────────────────────────────────────────────────────────────────────
       The nav bar is measured (item + padding + hairline), not guessed: the old 60px token was 4px short of the
       64px bar, which is why the last card sat under it. A slimmer 56px bar, a one-line 46px trial strip, and
       48px floating buttons on a tighter rail take the fixed stack from ~25% of an 832px screen to ~15%. */
    :root {
        --ty-bottomnav-h: 56px;
        /* The phone's top bar is 3rem of content (below); --ty-topbar-offset (app.css) adds the status-bar inset to it. */
        --ty-topbar-h: 3rem;
        --ty-trialstrip-h: 2.875rem;
        --ty-fab-size: 3rem;
        --ty-fab-gap: .5rem;
        --ty-fab-inset: .875rem;
        --ty-fab-bottom: calc(var(--ty-bottomnav-h, 56px) + var(--ty-trialstrip-clearance, 0px) + .625rem + var(--ty-safe-bottom, env(safe-area-inset-bottom, 0px)));
        --ty-fab-slot-1: var(--ty-fab-inset);
        --ty-fab-slot-2: calc(var(--ty-fab-inset) + (var(--ty-fab-size) + var(--ty-fab-gap)));
        --ty-fab-slot-3: calc(var(--ty-fab-inset) + 2 * (var(--ty-fab-size) + var(--ty-fab-gap)));
        --ty-fab-slot-4: calc(var(--ty-fab-inset) + 3 * (var(--ty-fab-size) + var(--ty-fab-gap)));
        /* How much the content must clear below its last line: the bar, the strip, the home indicator AND the
           floating-button row, so "Showing 1 of 1", a price or a card's last line is never underneath a button. */
        --ty-content-clear: calc(var(--ty-bottomnav-h, 56px) + var(--ty-trialstrip-clearance, 0px) + var(--ty-safe-bottom, env(safe-area-inset-bottom, 0px)) + var(--ty-fab-size) + 1.25rem);
    }

    /* No 300 ms tap delay, no grey flash, no stray text selection on controls, momentum scrolling, and no
       horizontal pan of the whole page. */
    html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; overflow-x: clip; }
    body { overflow-x: clip; -webkit-tap-highlight-color: transparent; overscroll-behavior-y: none; }
    a, button, [role="button"], summary, label, select, input[type="checkbox"], input[type="radio"] { touch-action: manipulation; }
    button, [role="button"], .btn, .ty-btn, .cl-chip, .nav-link, .ty-bn-item, .ty-seg-item {
        -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
    }

    /* Pressed state: native controls answer the finger immediately. */
    button:not(:disabled):active, .btn:not(.disabled):active, .ty-btn:not(:disabled):active,
    :root:root .cl-chip:active, :root:root .ty-bn-item:active, :root:root .nav-link:active, :root:root .qa-item:active {
        transform: scale(.97);
        filter: brightness(.94);
        transition: transform 60ms ease, filter 60ms ease;
    }

    /* ── Page gutters and bottom clearance ────────────────────────────────────────────────────────── */
    :root:root .page main article.content.px-4 { padding-left: .875rem !important; padding-right: .875rem !important; padding-top: .25rem; }
    :root:root .page main article.content { padding-bottom: var(--ty-content-clear); }
    :root:root .page main .top-row.px-4 { padding-left: .5rem !important; padding-right: .5rem !important; }

    /* ── Top bar: smaller, the language reduced to its flag, the account to a short chip ──────────── */
    :root:root .page main > .top-row { min-height: calc(3rem + var(--ty-safe-top, 0px)); flex-wrap: nowrap; }
    :root:root .ty-lang-flag__code { display: none; }
    :root:root .ty-account__name { max-width: 7.5rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* ── The documents sticky ────────────────────────────────────────────────────────────────────────
       (2026-10-14, owner request; thumb pass 2026-10-15; calmer handle 2026-10-16; one coloured folder 2026-10-19.)
       The edge tab on the right: a solid tab in the colour of whose documents it opens, ONE folder glyph on its side
       (owner: "a little wider, a little deeper, a single icon"), 48 x 76 and flush with the screen edge, so the tab
       is itself a 48px-wide thumb target; ::before adds a little forgiving margin around it. It is allowed to float
       over the list it sits on (owner, 2026-10-16: "Documents sticky can overlap content") — no page reserves a
       gutter for it. It is anchored to the floating-button rail, not to a percentage of the screen: its foot sits a
       clear --dl-tab-lift above the + / messages row AND the "Guide me" pill a wizard stacks on that rail, so on any
       phone height it lands in the lower-right thumb arc without ever touching them. It tucks away with the
       floating buttons while scrolling down and leaves the screen to the keyboard. */
    :root:root .dl-pull {
        --dl-tab-lift: calc(2 * var(--ty-fab-size, 3rem) + 2 * var(--ty-fab-gap, .5rem) + 1rem);
        --dl-w: 2.3rem; --dl-h: 3.75rem;
        top: auto;
        bottom: calc(var(--ty-fab-bottom) + var(--dl-tab-lift));
        transform: none;
        min-width: 2.75rem; min-height: 0;
    }
    :root:root .dl-pull::before {
        content: ""; position: absolute;
        left: -.5rem; top: -.25rem; width: calc(100% + .5rem); height: calc(100% + .5rem);   /* a little margin round the tab, flush with the screen edge */
    }
    :root:root .dl-pull .dl-pull__status { top: .3rem; width: .6rem; height: .6rem; }
    :root:root .dl-pull .dl-pull__count { top: .2rem; }
    :root:root .dl-pull:hover, :root:root .dl-pull:focus-visible { transform: translateX(-3px); }
    :root:root .dl-pull:active { transform: translateX(-2px) scale(.95); }
    html.ty-chrome-away .dl-pull { transform: translateX(calc(100% + 4px)); opacity: 0; pointer-events: none; }
    html.ty-kbd .dl-pull { display: none !important; }

    /* ── Floating buttons ─────────────────────────────────────────────────────────────────────────── */
    :root:root .qa-fab, :root:root .comms-trigger, :root:root .ty-assist-trigger, :root:root .cl-fab-btn, :root:root .cl-docfab {
        transition: transform .22s cubic-bezier(.2, 0, 0, 1), opacity .18s ease, background-color .12s;
        font-size: 1.2rem;
    }
    :root:root .qa-fab, :root:root .comms-trigger, :root:root .ty-assist-trigger { width: var(--ty-fab-size); height: var(--ty-fab-size); border-radius: 14px; }

    /* Scrolling down through content: tuck the floating buttons and the trial strip away; the first
       upward scroll brings them back. */
    html.ty-chrome-away .qa-fab:not(.on), html.ty-chrome-away .comms-trigger, html.ty-chrome-away .ty-assist-trigger,
    html.ty-chrome-away .cl-fab, html.ty-chrome-away .cl-recfabs {
        transform: translateY(calc(var(--ty-fab-size) + var(--ty-fab-bottom) + 1rem));
        opacity: 0; pointer-events: none;
    }
    :root:root .ty-trialstrip { transition: transform .22s cubic-bezier(.2, 0, 0, 1), opacity .18s ease; }
    html.ty-chrome-away .ty-trialstrip { transform: translateY(calc(100% + var(--ty-bottomnav-h, 56px))); opacity: 0; pointer-events: none; }

    /* The on-screen keyboard is up: nothing fixed may share the screen with it. */
    html.ty-kbd .ty-bottomnav, html.ty-kbd .ty-trialstrip, html.ty-kbd .qa-fab, html.ty-kbd .comms-trigger, html.ty-kbd .ty-assist-trigger,
    html.ty-kbd .cl-fab, html.ty-kbd .cl-recfabs, html.ty-kbd .wz-guideme {
        display: none !important;
    }
    :root:root.ty-kbd .page main article.content { padding-bottom: 1rem; }
    /* ...and with the bar and the strip gone, a toast sits just above the keyboard rather than a bar's height over it. */
    :root.ty-kbd { --ty-toast-bottom: calc(.5rem + var(--ty-safe-bottom, env(safe-area-inset-bottom, 0px))); }

    /* ── Trial strip: one line, slim, still dismissible ───────────────────────────────────────────── */
    :root:root .ty-trialstrip { padding: 0 .25rem 0 .75rem; gap: .5rem; font-size: .8125rem; border-top-width: 1px; }
    :root:root .ty-trialstrip__dot { display: none; }
    :root:root .ty-trialstrip__text { flex-wrap: nowrap; overflow: hidden; }
    :root:root .ty-trialstrip__text strong { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    :root:root .ty-trialstrip__cta { padding: .3rem .65rem; font-size: .8125rem; white-space: nowrap; }

    /* ── Bottom nav ───────────────────────────────────────────────────────────────────────────────── */
    :root:root .page main .ty-bottomnav { padding: 3px 4px calc(3px + var(--ty-safe-bottom, env(safe-area-inset-bottom, 0px))); }
    :root:root .page main .ty-bottomnav .ty-bn-item { padding: 4px 2px 3px; gap: 1px; min-height: 48px; justify-content: center; }
    :root:root .page main .ty-bottomnav .ty-bn-item span { font-size: .6875rem; }
    :root:root .page main .ty-bottomnav .ty-bn-ico { font-size: 1.25rem; }

    /* ── The + "Create" menu becomes a bottom sheet ───────────────────────────────────────────────── */
    :root:root .qa-fab.on { opacity: 0; pointer-events: none; }
    :root:root .qa-backdrop { background: rgba(var(--ty-color-overlay-rgb, 12, 18, 28), .5); animation: ty-m-fade .18s ease; }
    :root:root .qa-menu {
        left: 0; right: 0; bottom: 0; min-width: 0; max-width: none; width: auto;
        border-radius: 20px 20px 0 0; border-width: 1px 0 0;
        padding: .25rem .75rem calc(.75rem + var(--ty-safe-bottom, env(safe-area-inset-bottom, 0px)));
        transform-origin: bottom center;
        animation: ty-m-sheet .24s cubic-bezier(.2, 0, 0, 1);
    }
    :root:root .qa-menu::before { content: ""; display: block; width: 2.25rem; height: .25rem; margin: .4rem auto .25rem; border-radius: 999px; background: var(--ty-color-border-strong, #c9d1da); }
    :root:root .qa-menu-hd { padding: .35rem .5rem .35rem; }
    :root:root .qa-item { min-height: 3rem; padding: .35rem .5rem; font-size: 1rem; }
    :root:root .qa-item-ico { width: 2.25rem; height: 2.25rem; }

    /* ── Nav drawer: a real scrim, no scroll chaining ─────────────────────────────────────────────── */
    html:has(.page.nav-open) { overflow: hidden; }
    :root:root .sidebar { overscroll-behavior: contain; }

    /* ── Dashboard: the greeting first, then the work ─────────────────────────────────────────────── */
    :root:root .ty-dash__identity { display: none; }          /* name + email are already in the top bar's account chip */
    :root:root .ty-dash__title { font-size: 1.5rem; line-height: 1.2; }
    :root:root .ty-dash__subtitle { margin-bottom: .5rem; }
    /* "Finish setting up": a swipeable strip of compact tiles instead of a tall two-column grid. */
    :root:root .setup-strip { margin-bottom: .75rem; }
    :root:root .setup-strip__row {
        display: flex; gap: .5rem; overflow-x: auto; scroll-snap-type: x proximity;
        margin-inline: -.875rem; padding: .125rem .875rem .5rem; scrollbar-width: none; -webkit-overflow-scrolling: touch;
    }
    :root:root .setup-strip__row::-webkit-scrollbar { display: none; }
    :root:root .setup-strip__tile { flex: 0 0 8.5rem; scroll-snap-align: start; padding: .55rem .5rem; gap: .2rem; }
    /* KPI cards: two across, value beside its label, no tall whitespace. */
    :root:root .ty-kpi__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
    :root:root .ty-kpi__face { padding: .6rem .7rem; }
    :root:root .ty-kpi__icon { width: 30px; height: 30px; margin-bottom: 2px; font-size: .95rem; }
    :root:root .ty-kpi__value { font-size: 1.5rem; }
    :root:root .ty-kpi__label, :root:root .ty-kpi__caption { font-size: .75rem; }

    /* ── Tap targets: 48px, not 40 ─────────────────────────────────────────────────────────────────────
       app.css already floors the shared icon / chip / button vocabulary at 40px below 600px with !important
       (a scoped bundle loads after it, so nothing less forceful could win). A thumb wants 44 (WCAG 2.5.5
       target size; Material asks for 48 including spacing), so this raises that same list by the same
       means, with a selector heavy enough to beat the 40px rule. Icon-only controls keep their glyphs
       centred; the glyph size does not change, only the area around it. */
    :root:root .ty-iconbtn, :root:root .ty-pager__btn, :root:root .ty-chip, :root:root .cl-chip, :root:root .jl-chip,
    :root:root .inv-chip, :root:root .ci-btn, :root:root .cl-act, :root:root .cl-job, :root:root .p2-iconbtn,
    :root:root a.ty-dir, :root:root .ty-lang-flag, :root:root .ty-tour-replay-btn, :root:root .coach-btn,
    :root:root .coach-skip, :root:root .ty-pin, :root:root .ty-order-btn, :root:root .ty-sortsel__dir,
    :root:root .ty-account__trigger, :root:root .pl-map-toggle, :root:root .ty-dl-segbtn, :root:root .hb {
        min-width: 48px !important; min-height: 48px !important;
    }
    :root:root .btn, :root:root .ty-btn, :root:root .ty-btn-primary, :root:root .ty-btn-ghost, :root:root .ty-btn-quiet,
    :root:root .p2-btn, :root:root .p2-cta, :root:root .pl-act, :root:root .nc-back, :root:root .ad-toggle,
    :root:root .ty-sortsel__select { min-height: 48px !important; }
    :root:root .ty-pin, :root:root .ty-order-btn, :root:root .ty-sortsel__dir, :root:root .ty-account__trigger, :root:root .hb {
        display: inline-flex; align-items: center; justify-content: center;
    }
    /* The little "i" beside a label: a 48px hit area that does not push the label's line apart. */
    :root:root .fh-btn { min-width: 48px !important; min-height: 48px !important; margin-block: -.65rem; }
    /* Destructive: pushed to the far end of the actions line, with a gap no thumb lands in by accident
       (the delete is also a two-step arm-then-confirm, so one stray tap never deletes). */
    :root:root .cl-row > .cl-act--danger { margin-left: auto; }
    :root:root .cl-row > .cl-act--danger + .cl-act { margin-left: .25rem; }
}

@media (max-width: 47.99rem) {

    /* ── Page roots: their own side/top/bottom padding stacked on the shell's gutter (34px a side) and the
       shell already clears the bottom chrome, so a 4rem tail was dead space ───────────────────────────── */
    :root:root .cl, :root:root .jl { padding: .5rem 0 .5rem; }

    /* ── Dashboard ───────────────────────────────────────────────────────────────────────────────── */
    :root:root .ty-dash { gap: .875rem; padding-bottom: .5rem; }
    :root:root .ty-dash__titlerow { flex-wrap: nowrap; gap: .5rem; }
    :root:root .ty-dash__title { flex: 1 1 auto; min-width: 0; }
    :root:root .ty-dash__tools { flex: none; }
    :root:root .ty-dash__subtitle { margin-top: .125rem; }

    /* ── Customers: one card = who + where on top, a single action line below ──────────────────────── */
    :root:root .cl-head { gap: .5rem; margin-bottom: .5rem; }
    :root:root .cl-title { font-size: 1.25rem; }
    :root:root .cl-searchrow { margin-bottom: .5rem; }
    :root:root .cl-search { min-width: 0; padding: .35rem .55rem .35rem .85rem; }
    :root:root .cl-tools { gap: .375rem; margin-bottom: .5rem; }
    :root:root .cl-sortbar { flex: 1 1 100%; margin-left: 0; }
    :root:root .cl-list { gap: .5rem; }
    :root:root .cl-row { gap: .375rem .375rem; padding: .625rem .625rem .5rem; align-items: flex-start; }
    :root:root .cl-main { flex: 1 1 calc(100% - 7rem); min-width: 8rem; }
    :root:root .cl-sub { white-space: normal; overflow: visible; }
    :root:root .cl-row > .cl-job { margin-left: 0; }
    :root:root .cl-row > .cl-act, :root:root .cl-row > .cl-job { align-self: center; }
    :root:root .cl-act { border-radius: 10px; }

    /* ── Jobs (owner, 2026-10-15: "the list item for the first job should appear towards the top") ───────
       One slim line of chrome, then the jobs: the title, the in-view total as plain text (count · value · profit)
       and a ⋯ overflow holding Import / export (Material's top-app-bar overflow menu). The search, the when-chips
       and the Filters sheet are the SHELL's (FB-192: the header search + PageListBar, the same on every list page),
       so nothing here draws them. "New job" gives way to the shell's +, whose menu leads with it here. */
    /* Density pass (owner, 2026-10-16: "Job search and job page should follow suit and maximise real estate on
       mobile"): ONE line of chrome — a smaller title, the summary as one line of text ("17 jobs · £615 · £615
       profit"), and the ⋮ menu, which now also holds the walkthrough (its round ▶ button is hidden here; the same
       slide-out opens from the menu). The 48px floor is the ⋮ button's, not padding. */
    :root:root .jl { padding-top: .25rem; }
    :root:root .jl-head { flex-wrap: nowrap; gap: .5rem; margin-bottom: .25rem; min-height: var(--ty-tap, 48px); }
    :root:root .jl-title { flex: none; font-size: 1.125rem; line-height: 1.2; }
    :root:root .jl-head > .jl-domlabel, :root:root .jl-head > .jl-new, :root:root .jl-head > .jl-csv > .btn, :root:root .jl-head > .flex-grow-1,
    :root:root .jl-head > .hb { display: none; }
    :root:root .jl-sums { display: flex; flex-direction: row; align-items: baseline; gap: .3rem; flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; line-height: 1.2; text-overflow: ellipsis; }
    :root:root .jl-sums .jl-sum { flex: none; gap: .3rem; padding: 0; border: 0; border-radius: 0; background: none; font-size: .8125rem; align-items: baseline; }
    :root:root .jl-sums .jl-sum i { display: none; }
    :root:root .jl-sums .jl-sum-v { font-size: .8125rem; }
    :root:root .jl-sums .jl-sum-v--long { display: none; }
    :root:root .jl-sums .jl-sum-v--short { display: inline; }
    /* The separators are drawn, not written, so no translation carries a stray "·". */
    :root:root .jl-sums .jl-sum--count .jl-sum-v--short::before,
    :root:root .jl-sums .jl-sum--profit::before { content: "·"; margin-right: .3rem; color: var(--ty-color-text-muted, #57606a); font-weight: 400; }
    :root:root .jl-sums .jl-sum--profit::before { margin-right: 0; }   /* a flex item: the sum's own gap spaces it */

    /* The job cards (JobList — the jobs page AND the schedule's List view): ~50px instead of 69, the mainstream
       two-line list row (Gmail / Material "two-line list item"). A 28px trade mark; the name, its LATE / TODAY badge
       and the price on the first line; the date and address across the whole width below, where the price used to
       take a column. Pure layout — the markup is the tablet's and the desktop's, so neither changes: .jl-main steps
       out of the box (display: contents) and its two lines join the card's grid. The status chip (shown from 560px
       up) gets a column beside the price. Secondary text keeps its .78rem. */
    :root:root .jl-list { gap: .375rem; }
    :root:root .jl-list .jl-row {
        display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto;
        grid-template-areas: "mark name status price" "mark sub sub sub";
        column-gap: .625rem; row-gap: 0; align-items: center;
        padding: .375rem .75rem .375rem .625rem; border-radius: 10px; min-height: 44px;
    }
    :root:root .jl-list .jl-row > .wm { grid-area: mark; width: 28px; height: 28px; }
    :root:root .jl-list .jl-row > .wm .wm-coin { width: 28px; height: 28px; font-size: 13px; border-radius: 8px; }
    :root:root .jl-list .jl-row > .wm.wm--pair .wm-coin--owner { width: 24px; height: 24px; font-size: 12px; border-radius: 7px; }
    :root:root .jl-list .jl-row > .wm.wm--pair .wm-coin--trade { width: 16px; height: 16px; font-size: 8.5px; border-radius: 5px; border-width: 1.5px; }
    :root:root .jl-list .jl-row .jl-main { display: contents; }
    :root:root .jl-list .jl-row .jl-name { grid-area: name; line-height: 1.3; min-width: 0; }
    :root:root .jl-list .jl-row .jl-sub { grid-area: sub; line-height: 1.35; min-width: 0; }
    :root:root .jl-list .jl-row > .jl-status { grid-area: status; }
    :root:root .jl-list .jl-row > .jl-price { grid-area: price; min-width: 2.5rem; font-size: .95rem; line-height: 1.3; }
    :root:root .jl-list .jl-row .jl-late, :root:root .jl-list .jl-row .jl-today { margin-left: .3rem; padding: 0 .4rem; line-height: 1.5; }
    :root:root .jl-overflow { display: block; position: relative; flex: none; margin-left: auto; }
    :root:root .jl-overflow__btn {
        display: inline-flex; align-items: center; justify-content: center; width: var(--ty-tap, 48px); height: var(--ty-tap, 48px);
        margin-right: -.5rem; border: 0; border-radius: 50%; background: transparent;
        color: var(--ty-color-text, #1c2128); font-size: 1.25rem;
    }
    :root:root .jl-overflow__btn[aria-expanded="true"] { background: var(--ty-color-surface-2, #f6f8fa); }
    :root:root .jl-overflow__backdrop { position: fixed; inset: 0; z-index: 1060; }
    :root:root .jl-overflow__menu {
        position: absolute; right: -.25rem; top: calc(100% + .25rem); z-index: 1061; min-width: 13rem; padding: .375rem;
        background: var(--ty-color-surface, #fff); border: 1px solid var(--ty-color-border, #e7ebf0); border-radius: 14px;
        box-shadow: var(--ty-shadow-lg, 0 18px 40px -18px rgba(0, 0, 0, .45));
    }
    :root:root .jl-overflow__item {
        display: flex; align-items: center; gap: .75rem; width: 100%; min-height: var(--ty-tap, 48px); padding: 0 .75rem;
        border: 0; border-radius: 10px; background: transparent; text-align: left;
        color: var(--ty-color-text, #1c2128); font-weight: 600; font-size: .95rem;
    }
    :root:root .jl-overflow__item:active { background: var(--ty-color-surface-2, #f6f8fa); }
    /* Where the status chip is hidden (below 560px, JobList's own rule), the card has no status column at all, so the
       name line does not carry an empty track and its gap. */
    @media (max-width: 35rem) {
        :root:root .jl-list .jl-row { grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "mark name price" "mark sub sub"; }
    }


    /* ── Planning: nothing may be wider than the screen ─────────────────────────────────────────────── */
    :root:root .p2 { padding-inline: 0; }
    :root:root .p2-hero { grid-template-columns: minmax(0, 1fr); padding: .75rem .875rem; }
    :root:root .p2-hero-main, :root:root .p2-hero-side { min-width: 0; max-width: 100%; }
    :root:root .p2-steps { flex-wrap: wrap; row-gap: .25rem; }
    :root:root .p2-step + .p2-step::before { width: 14px; margin: 0 5px; }
    :root:root .p2-viewrow { flex-wrap: wrap; row-gap: .375rem; }
    :root:root .p2-days-row { gap: 3px; }
}

@media (max-width: 47.99rem) {

    /* ── A record's panel (job / customer) ─────────────────────────────────────────────────────────────
       The site map's phone height and card cap live with the map now (FB-189: LiveMapView's compact preview,
       sized by JobDetailPage.razor.css); one finger scrolls the panel and two pan the map (tyMap.js
       "cooperative-touch"). */
    :root:root .jd-head { padding: .75rem 1rem 1rem; }
    :root:root .jd-rows, :root:root .jd-profit { padding-left: 1rem; padding-right: 1rem; }
    :root:root .jd-row-lbl { width: 6.5rem; }
}

@media (max-width: 47.99rem) {

    /* ── Schedule: title + New on one line, the view switch, the date, the filters — four short rows
       rather than a 270px stack of wrapped controls ──────────────────────────────────────────────────── */
    /* Data-priority pass (2026-10-16, PhoneDataPriorityAuditTests: the first job sat at 64% of an 844px phone behind a
       242px header). TWO rows of controls, then the day: [Day Week Month List] + pin; then the date, Today and the
       trade chips. The title is the bottom bar's lit "Schedule" (kept for screen readers, visually hidden), and "New"
       gives way to the shell's +, whose menu leads with New job — the Jobs and Quotes pattern. */
    :root:root .ty-sched { padding-top: .25rem !important; padding-inline: 0; }
    :root:root .ty-sched-head { gap: .5rem; margin-bottom: .375rem; flex-wrap: wrap; }
    :root:root .ty-sched-head > .ty-sched-title {
        position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
    }
    :root:root .ty-sched-head > .ty-btn-primary { display: none; }
    /* The switch takes the first line with the pin (a basis, not 0: with every item at basis 0 a 412px phone fitted
       the date and Today on the same line and squashed the switch to "Day Wee" — seen on the Android device). */
    :root:root .ty-sched-head > .ty-seg--views { order: 1; flex: 1 1 calc(100% - 7.75rem); min-width: 0; }
    :root:root .ty-sched-head > .ty-seg--views .ty-seg-item { flex: 1 1 auto; padding-inline: .375rem; font-size: .875rem; }
    :root:root .ty-sched-head > .ty-pin { order: 2; flex: none; }
    :root:root .ty-sched-head > .ty-monthspan { order: 4; flex: 1 1 100%; }
    :root:root .ty-sched-head > .ty-datenav { order: 5; flex: 0 0 auto; min-width: 0; justify-content: space-between; }
    /* Today is its calendar glyph here (the word stays its accessible name), so the date, Today and the trade chips fit
       one line at 390px; with the word, the chips took a third line (data at 40%). */
    :root:root .ty-sched-head > .ty-today-btn { order: 6; font-size: 0; padding: 0; width: 48px; min-width: 48px; min-height: 48px; flex: none; display: inline-flex; align-items: center; justify-content: center; }
    :root:root .ty-sched-head > .ty-today-btn .ty-today-ico { display: inline-block; font-size: 1.2rem; line-height: 1; }
    :root:root .ty-sched-head .ty-datenav-label { font-size: 1rem; padding-inline: .125rem; white-space: nowrap; }
    /* The stepper box is its two 48px arrows, not 60px of padded frame around them. */
    :root:root .ty-sched-head > .ty-datenav { padding-block: 0; min-height: 48px; }
    :root:root .ty-sched-head > .ty-sched-spacer { display: none; }
    :root:root .ty-sched-head > .d-inline-flex { order: 7; flex: 1 1 0; min-width: 48px; flex-wrap: nowrap !important; /* beats Bootstrap .flex-wrap */ overflow-x: auto; scrollbar-width: none; }
    :root:root .ty-sched-head > .d-inline-flex::-webkit-scrollbar { display: none; }
    :root:root .ty-sched-head > .d-inline-flex > * { flex: none; }
    /* "Take a tour" as an icon beside the pin. Its glyph is 1em, so the font-size 0 that hides the words also needs the
       glyph sized back up — without it this was an invisible 48px button on a line of its own (seen on the device). */
    :root:root .ty-sched-head > .ty-tour-replay-btn { order: 3; font-size: 0; gap: 0; padding: 0; flex: none; min-width: 48px; min-height: 48px; justify-content: center; }
    :root:root .ty-sched-head > .ty-tour-replay-btn .ty-glyph { width: 1.15rem; height: 1.15rem; }
    /* The Watch-the-walkthrough icon (HelpLink, compact) has no room on a phone: with it the header needs a THIRD row
       (switch+pin+tour / date+Today+watch / chips - measured at 390px), which pushed the first agenda row to 39% of the
       screen (PhoneDataPriorityAuditTests). The tour replay beside the switch stays; the narrated walkthrough is still
       one tap away from the wider layouts and the Help centre. */
    :root:root .ty-sched-head > .hb--compact { display: none; }
    :root:root .ty-sched-head .ty-iconbtn { width: 48px; height: 48px; }
    :root:root .ty-sched-head .ty-chip { min-height: 48px; min-width: 48px; justify-content: center; }
    :root:root .ty-seg-item { min-height: 48px; }
    /* The period line: one line ("Friday 16 October  TODAY · 2 jobs"), not a two-line 26px headline under a date
       switch that already says the day. */
    :root:root .ty-period { margin-bottom: .375rem; padding-bottom: .25rem; gap: .5rem; flex-wrap: nowrap; align-items: center; }
    :root:root .ty-period-name { font-size: 1rem; line-height: 1.3; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    :root:root .ty-period-sub { font-size: .875rem; }
    :root:root .ty-period .ty-month-badge { flex: none; padding: 2px 8px; font-size: 10.5px; }
    :root:root .ty-period .ty-month-count { flex: none; white-space: nowrap; }
    /* The day's weather: one slim line, not a 75px banner. */
    :root:root .ty-day-weather { flex-wrap: nowrap; gap: .5rem; padding: .25rem .625rem; margin-bottom: .375rem; }
    :root:root .ty-day-weather .ty-wx { font-size: 1.25rem; }
    :root:root .ty-day-weather-cond, :root:root .ty-day-weather-temp { font-size: .8125rem; }
    :root:root .ty-day-weather-precip { font-size: .75rem; }

    /* Week: each day is a slim row — the date on the left, its jobs below — not a 220px box of nothing. */
    :root:root .ty-week { gap: .5rem; }
    :root:root .ty-week-colhead { display: flex; align-items: center; gap: .5rem; text-align: left; padding: .125rem 0 .25rem; }
    :root:root .ty-week-colhead .dow { display: inline; }
    :root:root .ty-week-colhead .dom { margin-top: 0; width: 48px; height: 48px; }
    :root:root .ty-week-colhead .ty-week-weather { margin-left: auto; }
    :root:root .ty-week-cells { min-height: 3.25rem; padding: .375rem; }
    :root:root .ty-month-cell { min-height: 4.5rem; }
}

@media (max-width: 47.99rem) {

    /* ── Documents drawer: the heading, not a paragraph, leads ───────────────────────────────────────── */
    :root:root .dl-drawer-lede { margin-bottom: .5rem; }
    :root:root .dl-drawer-lede h2 { font-size: 1.2rem; }
    :root:root .dl-drawer-lede p { display: none; }

    /* ── Sheets and drawers respect the home-indicator / gesture strip ───────────────────────────────── */
    :root:root .dl-drawer, :root:root .comms-drawer { padding-bottom: var(--ty-safe-bottom, env(safe-area-inset-bottom, 0px)); }
}

@keyframes ty-m-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes ty-m-sheet { from { transform: translateY(100%); } to { transform: none; } }

@media (max-width: 47.99rem) and (prefers-reduced-motion: reduce) {
    .qa-menu, .qa-backdrop { animation: none; }
    .qa-fab, .comms-trigger, .ty-trialstrip { transition: none; }
}

/* Touch phones only: the schedule's drag grip is dead weight there (a card cannot be dragged on touch; the up /
   down nudges beside it do the job), and it took a fifth of a card's width. A narrow desktop window with a mouse
   keeps its grip. Either feature (tablet-layer rule): Android WebView on the emulator reports pointer: fine with
   hover: none, so a "coarse" gate alone left the grip on the device. */
@media (max-width: 47.99rem) and (pointer: coarse), (max-width: 47.99rem) and (hover: none) {
    :root:root .ty-drag-grip { display: none; }
    :root:root .ty-jobcard-body { padding: .6rem .7rem; }
}

@media (max-width: 47.99rem) {

    /* ── Nothing may be wider than the screen ────────────────────────────────────────────────────────────
       Measured at 360 / 384 / 430 across the menu's pages: these were the ones that ran past the edge. */
    /* A page's title row (h1 + its actions) wraps instead of pushing the actions off the right edge. */
    :root:root article.content .d-flex:has(> h1), :root:root article.content .d-flex:has(> h2.h3) { flex-wrap: wrap; row-gap: .5rem; }
    /* A segmented control wider than the screen scrolls inside itself. */
    :root:root .ty-seg { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
    :root:root .ty-seg::-webkit-scrollbar { display: none; }
    :root:root .ty-seg > * { flex: none; }
    /* Wide fixed-column tables scroll inside their own box. */
    :root:root table.ph-hours { display: block; max-width: 100%; overflow-x: auto; }
    /* A from-to date pair wraps. */
    :root:root .inv-range { flex-wrap: wrap; }
    :root:root .inv-range input { min-width: 0; max-width: 100%; }
    :root:root .au-seq li { flex-wrap: wrap; }
    :root:root .au-seq-ch { white-space: normal; }
}

/* The schedule board's fallback panel (ScheduleDetailPanel): the tap-target floor below grows its floated close
   button from 28 to 48px, so Directions (placed for the 28px desktop button) ran 10px under it. Close's 48px + 8px
   spacing from its 16px inset. */
@media (max-width: 47.99rem) {
    :root:root .ty-panel-dir { right: calc(16px + 48px + 8px); }
}

/* Six bottom-nav destinations on a 360px screen: the longest label ("Customers") has to fit its slot. */
@media (max-width: 23.75rem) {
    :root:root .page main .ty-bottomnav .ty-bn-item span { font-size: .625rem; letter-spacing: -.01em; }
}

/* ============================================================================================================
   TAP TARGETS — 48 x 48 css px of hit area, 8px between neighbours (Material's bar; Apple asks 44).
   Verified on every route by TradeYoke.E2E/Phone/PhoneAuditTests (the probe in Phone/phone-audit.js measures the
   hit area including a label that belongs to a checkbox/radio and an absolutely positioned ::before/::after).
   Below, the floor is stated ONCE for the whole vocabulary; component families that need more than a floor
   (a gap, a hit-area extension where the visual must stay small) follow it.
   ============================================================================================================ */
@media (max-width: 47.99rem) {
    :root { --ty-tap: 48px; --ty-tap-gap: .5rem; }

    :root:root button, :root:root [role="button"], :root:root a.btn, :root:root a.ty-btn, :root:root summary,
    :root:root select, :root:root textarea,
    :root:root input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="hidden"]):not([type="color"]) {
        min-height: var(--ty-tap);
    }
    /* Typing never zooms the page: iOS Safari zooms in on a focused field whose text is under 16px (Bootstrap's -sm
       controls are 14px, the pager's page size 13.5px), and the page then stays zoomed and scrolls sideways. The
       native app pins the zoom in its viewport tag, so this is the web on an iPhone. Responsive sweep, 2026-11-01. */
    /* Raised only where nothing sized the field: the floor is one type selector (it beats Bootstrap's reboot
       "font-size: inherit" by coming later, and the browser's 13px control font), so it never shrinks a field a
       component deliberately drew larger with a class (a registration plate, a price). The app's standard field
       classes are then held at 16px whatever a page's own (scoped, three-class-deep) rule says: they are form
       fields, never display type, and every one the first sweep found was 13-14.4px. -lg stays large. */
    select, textarea, input:where(:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="hidden"]):not([type="color"])) {
        font-size: max(16px, 1em);
    }
    :root:root:root:root :is(.form-control, .form-select, .ty-field__input, .ty-select, .ty-input, .ty-textarea, .nc-fld, .ad-fld, .tycb-fld):not(.form-control-lg):not(.form-select-lg) {
        font-size: 16px;
    }
    :root:root button, :root:root [role="button"] { min-width: var(--ty-tap); }
    :root:root summary { display: flex; align-items: center; }
    /* ...except inside Google's own map DOM (.gm-style: its map-type bar, the Street View panorama's arrows, the
       keyboard-shortcuts link). Those are third-party controls sized by Google (the phone audit exempts them), and
       the floor above inflated them — the "Map | Satellite" bar grew to 48px buttons and crowded the app's own
       control column on a phone-width map (15 Oct 2026 recording). The app's TyMapControls keep the floor. */
    :root:root .gm-style button, :root:root .gm-style [role="button"] { min-height: 0; min-width: 0; }

    /* A checkbox / radio row: the whole label is the target, the box sits at its start. */
    :root:root .form-check { display: flex; align-items: center; gap: .625rem; min-height: var(--ty-tap); padding-left: 0; margin-bottom: 0; }
    :root:root .form-check .form-check-input { float: none; margin: 0; flex: none; }
    :root:root .form-check .form-check-label { flex: 1 1 auto; display: flex; align-items: center; min-height: var(--ty-tap); }
}

@media (max-width: 47.99rem) {

    /* Label + box rows wherever the markup wraps the control in its own label (switches, "show tips", notify
       toggles): the label is the target, so it gets the 48px row. */
    :root:root label:has(> input[type="checkbox"]), :root:root label:has(> input[type="radio"]) {
        display: flex; align-items: center; gap: .625rem; min-height: var(--ty-tap);
    }
    /* The customer card's select box: 17px of box inside a 48px label. */
    :root:root .cl-check-hit { display: flex; align-items: center; justify-content: center; min-width: var(--ty-tap); min-height: var(--ty-tap); margin: -.375rem 0 -.375rem -.5rem; cursor: pointer; }

    /* Tabs (settings, sub-navigation). */
    :root:root .ty-tab { display: inline-flex; align-items: center; min-height: var(--ty-tap); }
    :root:root a.ow-link, :root:root a.cpm-card__link { display: inline-flex; align-items: center; min-height: var(--ty-tap); }
    /* Links dressed as buttons (the trade pages' action links, the canvassing buttons, a supplier's website, "View
       plans", a record's phone and email, a live check-in's phone number) were 17-40px tall: the responsive sweep
       (2026-11-01) found them on 20 views. They get the button floor; their look does not change. */
    :root:root :is(a.gdr-link, a.eld-link, a.elcp-link, a.btr-link, a.pld-link, a.plcp-link, a.plr-link, a.plfg-link, a.sor-link,
                   a.sodr-link, a.cv-btn, a.ty-weblink, a.cv-link, a.ty-locked__link, a.el-tile__title, .swp-facts > a[href^="tel:"]) {
        display: inline-flex; align-items: center; min-height: var(--ty-tap);
    }
    /* The sign-in and sign-up screens' inline links ("Already have an account? Sign in", the plan link, the
       consent links) stay text links in their sentence, but their hit area is padded to the tap floor: padding grows the
       target, the equal negative margin keeps the line exactly where it was (owner, 2026-11-01). The provider buttons
       (Google, Apple, Microsoft) are links too but already full-size buttons: the negative margin stacked them on top of
       each other on a phone, so they are left out (owner, 2026-11-02). */
    :root:root .ty-auth a:not(.ty-auth__submit):not(.ty-auth__back):not(.ty-auth__provider):not(.btn):not(.ty-btn) {
        display: inline-block; padding: 1rem .375rem; margin: -1rem -.375rem;
    }
    /* The drawer's brand mark is a link home: a 48px row, like every other row in the drawer. */
    :root:root .sidebar .navbar-brand { display: inline-flex; align-items: center; min-height: var(--ty-tap); }

    /* The little "i" by a label: the label line grows to hold its 48px, so it never lies over the field below. */
    :root:root .fh { display: inline-flex; align-items: center; vertical-align: middle; }
    :root:root .fh-btn { margin-block: 0; }

    /* Eight pixels between neighbours — the rows of chips and icon buttons that sat at 4-7px. */
    :root:root :is(.cl-row, .tycd-chipset, .cb-colour, .jl-when, .jl-trades, .jl-tools, .pl-card-acts, .lm-legend, .ty-order-ctl,
                   .ty-weather__strip, .cl-tools, .ty-sortsel, .p2-days-row, .p2-row-acts, .ty-round-add, .pl-map-modes, .who-search,
                   .cl-searchrow, .cl-chips, .ty-activity) { gap: var(--ty-tap-gap); }
    /* The top bar: every item 8px from the next (search, weather chip, trade pills, flag, account). The weather chip's
       margin-right:auto hands the spare width to the account, but it resolves to 0 when the bar is full, so the gap
       is the row's own (the same rule tablet.css applies), not a margin on one item. */
    :root:root .page main > .top-row { column-gap: var(--ty-tap-gap); }
}

@media (max-width: 47.99rem) {
    :root:root :is(.ty-jobcard, .ty-agenda-item, .lm-stack, .p2-days, .inv-range, .wz-head, .ty-dl-seg.tycd-seg) { gap: var(--ty-tap-gap); }
    :root:root .wz-head, :root:root .ty-dl-seg.tycd-seg { margin-bottom: var(--ty-tap-gap); }
    :root:root .fh { margin-bottom: .25rem; }
    :root:root input[type="color"] { min-height: var(--ty-tap); min-width: var(--ty-tap); }
    :root:root table.ph-hours td { padding-block: .25rem; }
    /* A link that stands alone in a list item ("Choose how you message customers") is a row, not a word in a sentence. */
    :root:root article.content li > a:only-child:not([class]) { display: flex; align-items: center; gap: .5rem; min-height: var(--ty-tap); }
}

@media (max-width: 47.99rem) {
    :root:root label:has(> input[type="checkbox"]) { min-width: var(--ty-tap); }
}

/* ============================================================================================================
   SPACE — what the 0.3.26 pass left on the table (audit: Phone/PhoneAuditTests + the space survey in the report).
   ============================================================================================================ */
@media (max-width: 47.99rem) {

    /* Bootstrap's container gutter (12px a side) was stacked on the shell's 14px gutter: 26px of margin before a
       card began, and a card-in-a-card made it 45. The shell's gutter is the one margin; rows give back the
       negative margin they used to cancel the container's. */
    :root:root article.content :is(.container-fluid, .container-xxl, .container) { padding-inline: 0; }
    :root:root article.content :is(.container-fluid, .container-xxl, .container) .row { margin-inline: 0; }
    :root:root article.content :is(.container-fluid, .container-xxl, .container).py-3 { padding-block: .25rem !important; }

    /* The explanatory line under a page title: two lines, not a paragraph, so the work starts above the fold. */
    :root:root article.content .container-fluid > p.text-muted, :root:root .inv-intro, :root:root .rh-intro, :root:root .plan__lede {
        display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
    }

    /* A trade filter that would show nothing (a trade the business does not run) is not offered. */
    :root:root .cl-chip--none { display: none; }

    /* Chip rows that wrapped over three or four lines are one swipeable line. */
    :root:root :is(.lb-chips, .lb-colnav, .ql-colnav, .tycd-chipset, .jl-trades) {
        flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
        margin-inline: -.875rem; padding-inline: .875rem;
    }
    :root:root :is(.lb-chips, .lb-colnav, .ql-colnav, .tycd-chipset, .jl-trades)::-webkit-scrollbar { display: none; }
    :root:root :is(.lb-chips, .lb-colnav, .ql-colnav, .tycd-chipset, .jl-trades) > * { flex: none; }

    /* Settings: the module card's left rail, its heading, its sections and the section's own padding were each
       16px; a phone gets 12. */
    :root:root .settings-page { gap: .75rem; }
    :root:root .settings-group__title { padding: .5rem .75rem; }
    :root:root .settings-group__sections { padding: .75rem; gap: .75rem; }
    :root:root .settings-section + .settings-section { padding-top: .75rem; }
}

@media (max-width: 47.99rem) {
    /* The customer card's icon buttons carried a 5.6px margin on top of the row gap: Delete wrapped to a line of its own. */
    :root:root .cl-row > .cl-act:not(.cl-act--danger) { margin-left: 0; }

    /* Day view: the hour sits above its jobs instead of in a 58px column beside them, so a card is the full width. */
    :root:root .ty-agenda-row { flex-direction: column; gap: .125rem; }
    :root:root .ty-agenda-time { width: auto; padding-top: 0; }
    :root:root .ty-agenda { gap: .625rem; }
}

@media (max-width: 47.99rem) {
    /* A day-view card: its up / down / skip nudges sit in a row under it, so three stacked 48px buttons no longer
       set the card's height (160px for a two-line job). */
    :root:root .ty-agenda-item { flex-direction: column; align-items: stretch; }
    :root:root .ty-agenda-item > .ty-order-ctl { flex-direction: row; justify-content: flex-end; width: auto; height: auto; }
}

/* ============================================================================================================
   TAP TARGETS, second pass - what the phone audit still found on the long tail of pages.
   ============================================================================================================ */
@media (max-width: 47.99rem) {
    /* Reports: the period chips and the links in the header. */
    :root:root .rp-chip { min-height: var(--ty-tap); }
    :root:root .rp-periods, :root:root .rp-head { gap: var(--ty-tap-gap); }
    :root:root .rp-custom input { min-height: var(--ty-tap); }
    :root:root a.rp-link { min-height: var(--ty-tap); }

    /* Access: Users / Roles switch. */
    :root:root a.ty-access__segbtn { min-height: var(--ty-tap); display: inline-flex; align-items: center; }

    /* Setup hub footnote link. */
    :root:root a.setup-hub__note-link { display: inline-flex; align-items: center; min-height: var(--ty-tap); }

    /* Window cleaning round: its date stepper, its action links. */
    :root:root .wcr-datenav, :root:root .wcr-links { gap: var(--ty-tap-gap); }
    :root:root .wcr-iconbtn, :root:root .wcr-date, :root:root .wcr-link { min-height: var(--ty-tap); }
    :root:root .wcr-link { display: inline-flex; align-items: center; }

    /* Squeegee Rush's pickers (a game, but the same thumbs): 8px between the pills. */
    :root:root :is(.sqThemePicker, .sqLevelPicker, .sqTierPicker) { gap: var(--ty-tap-gap); }

    /* Branding mark-style pills, supplier actions, the dashboard KPI cards, the map's "Navigate here". */
    :root:root .branding-pill { min-height: var(--ty-tap); }
    :root:root .branding-pill + .branding-pill { margin-top: var(--ty-tap-gap); }
    :root:root .sd-actions { gap: var(--ty-tap-gap); }
    :root:root a.sd-act { min-height: var(--ty-tap); display: inline-flex; align-items: center; }
    :root:root a.sd-fact-value { display: flex; align-items: center; min-height: var(--ty-tap); }
    :root:root .ty-kpi__grid { gap: var(--ty-tap-gap); }
    /* Knowledge result rows: the open button and its pin / tag / remove actions sat 2-4px apart. */
    :root:root .kr-row { gap: var(--ty-tap-gap); }
    :root:root .kr-acts { gap: var(--ty-tap-gap); }
    :root:root a.ty-mapctl-btn { min-width: var(--ty-tap); min-height: var(--ty-tap); }

    /* The version line at the foot of the nav drawer is a link (to the release notes): a full row. */
    :root:root a.ty-nav-version { min-height: var(--ty-tap); }

    /* The global search's results: every row a full target (the panel is a list, so rows are not spaced apart). */
    :root:root .gs-hit { min-height: var(--ty-tap); }

    /* The AI Mode chip in the search field draws a compact pill and keeps its 44px+ hit area in an ::after (GlobalSearch.razor.css),
       so the button floor above does not blow it back up into a 48px disc. */
    :root:root .gs-mode { min-height: 0; min-width: 2.4rem; }
    /* Close is an icon-only 44px target (GlobalSearch.razor.css), not the 48px button floor. */
    :root:root .gs-close { min-width: 2.75rem; }
    /* While the phone search is open the glass, the clear and the microphone are 36px wide (full 48px height) so the input keeps the
       room (GlobalSearch.razor.css); the button floor must not stretch them back. */
    :root:root .gs--open .gs-ico, :root:root .gs--open .gs-clear, :root:root .gs--open .gs-mic { min-width: 0; }

    /* A wizard header that wraps keeps 8px between its lines. */
    :root:root .wz-head { row-gap: var(--ty-tap-gap); }
}

/* ── Top bar: an open search takes the whole bar ────────────────────────────────────────────────────────────────
   Tapping the search disc expands it across the full width and every other top-bar item (weather chip, trade pills,
   account) yields until it is closed, so there is room to type and to read what was found. The Close button, Esc and
   a tap on the backdrop all put it away (GlobalSearch). Driven by :focus-within as well as .gs--open because the tap
   focuses the input in the same touch, a server round-trip before .gs--open is set. */
@media (max-width: 47.99rem) {
    :root:root .top-row:has(.gs--open) > :not(.gs),
    :root:root .top-row:has(.gs:focus-within) > :not(.gs) { display: none !important; }
    :root:root .top-row .gs--open,
    :root:root .top-row .gs:focus-within { flex: 1 1 100%; }

    /* The finance tile leads the dashboard here: its "Anything to chase today?" line is the one that goes, so the
       owed figures and who to chase sit higher on the first screen. */
    :root:root .fin-tile__q { display: none; }
}

/* The sign-in / sign-up "Back" to the welcome screen (FB-187) is a text link, so the floor above (buttons, .btn)
   missed it: 49 x 21 on a phone. A full tap height and a padded hit area, pulled back by the same amount so the
   chevron still lines up with the column's left edge. */
@media (max-width: 47.99rem) {
    :root:root .ty-auth .ty-auth__back--top {
        min-height: var(--ty-tap);
        padding: 0 .75rem 0 .5rem;
        margin: 0 0 var(--ty-space-xs, 4px) -.5rem;
    }
}

/* ============================================================================================================
   DATA FIRST — the phone data-priority pass (owner, 2026-10-16: "check all of the mobile screens for space issues
   … to ensure that data gets priority on mobile"). Measured and held by TradeYoke.E2E/Phone/
   PhoneDataPriorityAuditTests: a screen's first data item starts in the top third of the screen, and a list with
   five or more items shows at least five whole rows above the bottom bar. Rules of the pass (Dev Help /design/ui
   "Phone data priority"): one compact title row; New / Add gives way to the shell's + where it has the action;
   secondary page actions become icon buttons (their words stay their accessible names); a page's explanatory
   paragraph is for the desktop and the help, not a phone's first screen; denser rows with the key facts first.
   ============================================================================================================ */
@media (max-width: 47.99rem) {

    /* ── Utilities a page opts into ───────────────────────────────────────────────────────────────────────
       .ty-phone-icon: an icon + words button that shows only its icon on a phone (a 48px square). The words stay
       in the DOM at font-size 0, so they are still the button's accessible name; the glyph keeps its size.
       .ty-phone-hide: not drawn on a phone (an intro paragraph whose words the help already carries).
       h1.ty-phone-title: the page title at the compact phone size, on one line, without its decorative glyph. */
    :root:root .ty-phone-icon {
        font-size: 0 !important; gap: 0 !important; padding: 0 !important;
        width: var(--ty-tap, 48px); min-width: var(--ty-tap, 48px) !important; height: var(--ty-tap, 48px); min-height: var(--ty-tap, 48px) !important;
        display: inline-flex !important; align-items: center; justify-content: center; flex: none;
    }
    :root:root .ty-phone-icon > i, :root:root .ty-phone-icon > .bi { font-size: 1.15rem; margin: 0 !important; line-height: 1; }
    :root:root .ty-phone-hide { display: none !important; }

    :root:root article.content .d-flex:has(> h1.ty-phone-title), :root:root article.content .ty-phone-titlerow { flex-wrap: nowrap; row-gap: 0; min-height: var(--ty-tap, 48px); }
    :root:root article.content h1.ty-phone-title { font-size: 1.125rem !important; line-height: 1.2; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    :root:root article.content h1.ty-phone-title > i.bi { display: none; }
    :root:root article.content .container-fluid.py-3:has(h1.ty-phone-title) { padding-top: .25rem !important; }
    :root:root article.content .d-flex.mb-3:has(> h1.ty-phone-title), :root:root article.content .d-flex.mb-2:has(> h1.ty-phone-title) { margin-bottom: var(--ty-tap-gap, .5rem) !important; }

    /* ── Customers: a two-line card + one action line (~108px, was ~214px; 3 → 5+ above the fold) ──────────
       Line 1: the select box, the avatar (its colour is the trade), the name over the address (one line each), and
       what they owe. Line 2: + Job, SMS, email, notes — and Directions, pinned to the card's right edge. The trade
       icons and the distance give way here (the avatar's colour says the trade; both are on the record), and Delete
       moves to the record panel (opened by tapping the card), where it already lives. "Add customer" is the shell's
       + and the page's own menu button. */
    :root:root .cl-head > .cl-addbtn { display: none; }
    :root:root .cl-list { gap: .5rem; }
    :root:root .cl-row { position: relative; flex-wrap: wrap; align-items: center; column-gap: .5rem; row-gap: 0; padding: .5rem .625rem .5rem .375rem; }
    /* The 48px label hit area overhangs upward (14px) more than downward (2px), so it stays 8px clear of the + Job
       line below and of the card above, while the row itself is only as tall as the name and address. */
    :root:root .cl-row > .cl-check-hit { order: 1; align-self: flex-start; margin: -14px -.375rem -2px -.25rem; }
    :root:root .cl-row > .cl-ava { order: 2; width: 34px; height: 34px; font-size: .75rem; }
    :root:root .cl-row > .cl-main { order: 3; flex: 1 1 0; min-width: 0; }
    :root:root .cl-row > .ow { order: 4; flex: none; }
    :root:root .cl-row::after { content: ""; order: 5; flex: 0 0 100%; height: 0; }
    /* The action line starts at the card's edge (+ Job, SMS, email, notes = 248px), leaving the right end clear for
       Directions, so all five fit a 360px phone with 8px between them. */
    :root:root .cl-row > .cl-job, :root:root .cl-row > .cl-act { order: 6; margin-top: .375rem; }
    :root:root .cl-row > .cl-job { margin-left: .25rem; }
    :root:root .cl-row > .cl-act--danger, :root:root .cl-row > .cl-act--danger + .cl-act { display: none; }
    :root:root .cl-name { flex-wrap: nowrap; line-height: 1.25; }
    /* The "i" keeps its 48px hit area but overhangs the 20px name line instead of making it 48px tall. */
    :root:root .cl-name .ci-btn { margin-block: -14px; }
    :root:root .cl-name .cl-nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; overflow-wrap: normal; min-width: 0; }
    :root:root .cl-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.3; }
    :root:root .cl-meta { position: absolute; right: .625rem; bottom: .5rem; margin: 0; gap: 0; }
    :root:root .cl-meta > .ts, :root:root .cl-meta > .cl-dist { display: none; }

    /* ── Planning (classic): the plan, not the controls, on the first screen ──────────────────────────────
       [Day Week Month] + Today, then the date, then the state pills, then the people chips on ONE sideways line.
       The title is the bottom bar's lit "Planning" (kept for screen readers). */
    :root:root .pl-head { gap: .5rem; margin-bottom: .375rem; flex-wrap: wrap; }
    :root:root .pl-head > .pl-title { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
    :root:root .pl-head > .ty-seg { flex: 1 1 0; min-width: 0; }
    :root:root .pl-head > .ty-seg .ty-seg-item { flex: 1 1 0; }
    :root:root .pl-head > .ty-today-btn { flex: none; min-height: 48px; padding: .6rem .75rem; }
    :root:root .pl-head > .ty-datenav { order: 3; flex: 1 1 100%; justify-content: space-between; }
    :root:root .pl-head > .pl-spacer { display: none; }
    :root:root .pl-head > :not(.pl-title, .ty-seg, .ty-today-btn, .ty-datenav, .pl-spacer) { order: 4; }
    :root:root .pl-filters { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; margin: 0 -.875rem .5rem; padding-inline: .875rem; }
    :root:root .pl-filters::-webkit-scrollbar { display: none; }
    :root:root .pl-filters > *, :root:root .pl-filters .d-inline-flex { flex: none; flex-wrap: nowrap; }

    /* The state pills ("1 missed · 2 to plan", "14 booked later") are words, not targets: a slim line. */
    :root:root .pl-head .pl-state { padding: .2rem .55rem; font-size: .8125rem; min-height: 0; }

    /* Missed visits (planning, dashboard): the decision rows, not the paragraph explaining them; a one-line header; the
       four decisions on one line (Open job shows its icon — its words stay its accessible name). */
    :root:root .mv-hint { display: none; }
    :root:root .mv-hd { padding: .375rem .625rem; gap: .5rem; align-items: center; }
    :root:root .mv-hd-ic { width: 28px; height: 28px; font-size: .9rem; }
    :root:root .mv-title { font-size: 1rem; margin: 0; }
    :root:root .mv-acts { display: flex; flex-wrap: nowrap; gap: .5rem; }
    :root:root .mv-loc { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    :root:root .mv-acts .mv-btn { flex: 1 1 auto; min-width: 0; padding-inline: .5rem; white-space: nowrap; }
    :root:root .mv-acts .mv-btn--open { flex: none; width: var(--ty-tap, 48px); font-size: 0; padding: 0; justify-content: center; }
    :root:root .mv-acts .mv-btn--open i { font-size: 1.1rem; margin: 0; }

    /* ── The window cleaner's round: the stops on the first screen, not 800px below it ─────────────────────
       The title on one line (the trade's eyebrow goes — the droplet and the bottom bar say it), the date stepper,
       then the page's actions as one line of icons (New job, Map, Text today's round, View schedule, Jobs: their
       words are their accessible names); the sync line slim; the day's totals one sideways strip; the rain-day
       explanation and the section hints are the help's. */
    :root:root .wcr-head { gap: .5rem; margin-bottom: .375rem; flex-wrap: wrap; }
    :root:root .wcr-titlewrap { flex: 1 1 100%; min-width: 0; }
    :root:root .wcr-trade { display: none; }
    :root:root .wcr-title { font-size: 1.125rem; line-height: 1.25; margin: 0; }
    :root:root .wcr-head > .wcr-spacer { display: none; }
    :root:root .wcr-links { flex: 1 1 100%; flex-wrap: nowrap; gap: .5rem; overflow-x: auto; scrollbar-width: none; }
    :root:root .wcr-links .wcr-link { flex: none; font-size: 0; gap: 0; padding: 0; width: var(--ty-tap, 48px); justify-content: center; }
    :root:root .wcr-links .wcr-link i { font-size: 1.15rem; margin: 0; }
    :root:root .wcr-addr { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
    :root:root .wcr-totals {
        display: flex; flex-wrap: nowrap; gap: .5rem; overflow-x: auto; scrollbar-width: none;
        margin: 0 -.875rem .375rem; padding-inline: .875rem;
    }
    :root:root .wcr-totals::-webkit-scrollbar, :root:root .wcr-links::-webkit-scrollbar { display: none; }
    :root:root .wcr-total { flex: none; padding: .3rem .625rem; min-width: 0; }
    :root:root .wcr-total b { font-size: 1rem; }
    :root:root .wcr-total span { font-size: .6875rem; }
    :root:root .wcr-rain { margin-bottom: .375rem; gap: .5rem; }
    :root:root .wcr-rainbtn + .wcr-rainhint { display: none; }
    :root:root .wcr-sect-sub { display: none; }
    :root:root .wcr-sect-title { margin-bottom: .375rem; font-size: 1rem; }
}

/* The narrowest phones (360-380px): a page's one labelled primary action shows only its icon too, so the title row
   still fits on one line. */
@media (max-width: 23.75rem) {
    :root:root .ty-phone-icon-narrow {
        font-size: 0 !important; gap: 0 !important; padding: 0 !important;
        width: var(--ty-tap, 48px); min-width: var(--ty-tap, 48px) !important; height: var(--ty-tap, 48px);
        display: inline-flex !important; align-items: center; justify-content: center; flex: none;
    }
    :root:root .ty-phone-icon-narrow > i { font-size: 1.15rem; margin: 0 !important; }
}

@media (max-width: 47.99rem) {
    /* Documents (the page, the drawer and every record's Documents tab): the actions are one line of icons plus the
       labelled Upload, and the folder line keeps its view switch beside it (the stats shorten rather than push the
       switch onto a line of its own). */
    :root:root .ty-dl-actions { flex-wrap: nowrap; margin-bottom: .5rem !important; }
    :root:root .ty-dl-crumbrow { flex-wrap: nowrap; gap: .5rem; }
    :root:root .ty-dl-crumbrow > .ty-dl-crumb { flex: 0 1 auto; min-width: 0; }
    :root:root .ty-dl-crumbrow > .ty-dl-stats { flex: 0 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: .75rem; }
    :root:root .ty-dl-crumbrow > .ty-dl-seg { flex: none; }

    /* The price book's summary pills: one sideways line, not two wrapped rows. */
    :root:root .cat-summary { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; margin-inline: -.875rem; padding-inline: .875rem; }
    :root:root .cat-summary::-webkit-scrollbar { display: none; }
    :root:root .cat-summary > * { flex: none; }
}

/* Touch phones: an hour of the day view with nothing in it is a drop zone for a mouse drag, which a finger cannot
   make (the drag grip is already gone here) — so it is not drawn, and the day's jobs start at the top. */
@media (max-width: 47.99rem) and (pointer: coarse), (max-width: 47.99rem) and (hover: none) {
    :root:root .ty-agenda-row--slot.ty-agenda-row--empty { display: none; }
    /* Two jobs in one hour: the first one's nudges sit just above the next card, so the hour's jobs are a clear 10px
       apart (8px measured 7 after rounding). */
    :root:root .ty-agenda-slot { gap: .625rem; }
}

/* ============================================================================================================
   TABLES ON A PHONE — the Vehicles treatment for every list that is still a <table> (responsive sweep, 2026-11-01).
   The fleet stopped being a table on a phone by becoming a list of cards (TyCardDirectory). A page that keeps a
   table adopts the same outcome with one class and no second markup:

     <table class="table ty-stack-table">                 every row becomes a card; each cell a "label  value" line
       <td class="ty-stack-title">Ford Transit</td>        the row's name: the card's heading, no label, full width
       <td data-label="@L["Cost"]">£1,250</td>             the label is the column's own header text
       <td class="ty-stack-actions">…</td>                 the row's buttons: one line at the foot of the card
       <td class="ty-phone-hide">…</td>                    a column a phone does not need (priority columns)

   Tablet and desktop are untouched (everything here is inside the phone query), the header row stays in the
   accessibility tree (visually hidden, not display:none), and a clickable row (role="button") keeps working because
   the row is still the row. Guarded by tests/TradeYoke.E2E/Phone/ResponsiveSweepTests. See /design/ui#s10-3.
   ============================================================================================================ */
@media (max-width: 47.99rem) {
    :root:root table.ty-stack-table,
    :root:root table.ty-stack-table > tbody,
    :root:root table.ty-stack-table > tfoot { display: block; width: 100%; min-width: 0; }
    /* position: relative keeps the visually hidden header inside the table (an absolute child of an unpositioned box
       escapes its clip and widens the document: tablet-audit trap 2). */
    :root:root table.ty-stack-table { position: relative; border: 0; background: none; }
    :root:root table.ty-stack-table > thead {
        position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
    }
    :root:root table.ty-stack-table > tbody > tr,
    :root:root table.ty-stack-table > tfoot > tr {
        display: flex; flex-wrap: wrap; align-items: baseline; column-gap: var(--ty-space-md, 12px); row-gap: var(--ty-space-xs, 4px);
        padding: var(--ty-space-md, 12px);
        border: 1px solid var(--ty-color-border, #e7ebf0); border-radius: var(--ty-radius-card, 12px);
        background: var(--ty-color-surface, #fff);
    }
    :root:root table.ty-stack-table > tbody > tr + tr,
    :root:root table.ty-stack-table > tfoot > tr { margin-top: var(--ty-space-sm, 8px); }
    :root:root table.ty-stack-table > tbody > tr > td,
    :root:root table.ty-stack-table > tfoot > tr > td {
        display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--ty-space-xs, 4px) var(--ty-space-sm, 8px);
        flex: 1 1 100%; min-width: 0; padding: 0; border: 0; background: none;
        text-align: end; white-space: normal; overflow-wrap: anywhere;
    }
    :root:root table.ty-stack-table > tbody > tr > td[data-label]::before,
    :root:root table.ty-stack-table > tfoot > tr > td[data-label]::before {
        content: attr(data-label); flex: none; max-width: 45%; margin-inline-end: auto; text-align: start;
        font-size: var(--ty-font-xs, 12px); font-weight: var(--ty-weight-semibold, 600);
        color: var(--ty-color-text-subtle, #8a95a1);
    }
    :root:root table.ty-stack-table > tbody > tr > td[data-label=""]::before { content: none; }
    :root:root table.ty-stack-table > tbody > tr > td.ty-stack-title {
        order: -1; display: block; text-align: start; font-weight: var(--ty-weight-semibold, 600); font-size: 1rem;
    }
    :root:root table.ty-stack-table > tbody > tr > td.ty-stack-actions {
        justify-content: flex-end; flex-wrap: wrap; padding-top: var(--ty-space-xs, 4px);
    }
    :root:root table.ty-stack-table > tbody > tr > td:empty { display: none; }
    /* A full-width row of its own (an inline editor, a "no rows" line) stays a block. */
    :root:root table.ty-stack-table > tbody > tr > td[colspan] { display: block; text-align: start; }
    /* .ty-stack-table--flow: for a table whose cells come from a host that cannot label them (TyCardDirectory's list
       face draws each host's own <td>s). The first cell is the card's heading and the rest flow after it as one
       wrapping line of values - the contacts-row reading of the same facts, no label needed. */
    :root:root table.ty-stack-table--flow > tbody > tr > td {
        flex: 0 1 auto; justify-content: flex-start; text-align: start; color: var(--ty-color-text-muted, #57606a);
    }
    :root:root table.ty-stack-table--flow > tbody > tr > td:first-child {
        flex: 1 1 100%; font-weight: var(--ty-weight-semibold, 600); color: var(--ty-color-text, #1c2128);
    }
    /* td.ty-stack-chip: a small value that belongs in a row of its peers (a timesheet's seven days): four to a line,
       the label above the value. A host tints one with --stack-chip-bg (today's column). */
    :root:root table.ty-stack-table > tbody > tr > td.ty-stack-chip {
        flex: 0 0 calc((100% - 3 * var(--ty-space-md, 12px)) / 4); flex-direction: column; flex-wrap: nowrap; align-items: center; justify-content: center;
        gap: 0; padding: var(--ty-space-xs, 4px) 2px; text-align: center; border-radius: var(--ty-radius-control, 8px);
        background: var(--stack-chip-bg, var(--ty-color-surface-2, #f6f8fa));
    }
    :root:root table.ty-stack-table > tbody > tr > td.ty-stack-chip[data-label]::before { margin: 0; max-width: none; text-align: center; }
    /* The scroller around it no longer scrolls: the cards fit. */
    :root:root :is(.table-responsive, .ty-table-wrap):has(> table.ty-stack-table) { overflow: visible; border: 0; background: none; }
}
