/* =============================================================
   Aurelia — Mobile Refinements
   Loaded LAST so it wins the cascade over the per-feature sheets.
   Scope: phones & small tablets only. Desktop is untouched.
   Goals (per ui-ux-pro-max audit):
     • ≥44px touch targets in the global bar / toolbars
     • Schedule landing screen: compact toolbar + usable grid
     • Nav drawer shows navigation first (mini-calendar hidden)
     • Modals scroll internally, forms collapse to one column
     • Filter / segment / period rows scroll instead of wrapping ugly
   ============================================================= */

/* ============================================================
   TABLET / PHONE  (≤768px)
   ============================================================ */
@media (max-width: 768px) {

  /* ---- Global bar: comfortable, evenly spaced ---- */
  .globalbar {
    height: 56px;
    padding: 0 12px;
    gap: 6px;
  }

  /* Hamburger — primary nav affordance, full 44px target */
  .hamburger#mobileNavToggle,
  .hamburger {
    width: 44px;
    height: 44px;
    border-radius: var(--radius-md);
  }
  .hamburger .lucide { width: 20px; height: 20px; }

  /* Language pill toggle */
  .lang-toggle button {
    min-height: 40px;
    padding: 8px 14px;
    font-size: 12px;
  }

  /* Bell — square 40px tap target */
  .globalbar .icon-btn {
    width: 40px;
    height: 40px;
  }
  .user-av {
    width: 40px;
    height: 40px;
    font-size: 14px;
  }

  /* Notifications panel: near-full-width sheet under the bar */
  .notif-panel {
    top: 60px;
    right: 8px;
    left: 8px;
    width: auto;
    max-height: calc(100dvh - 80px);
  }
  .notif-mark-all { min-height: 36px; }

  /* ---- Sidebar drawer: navigation first ---- */
  .sidebar {
    width: min(84vw, 300px);
  }
  /* Mini-calendar eats the top half of the drawer and buries the
     nav links — hide it on mobile so menu items are reachable. */
  .sidebar .sb-cal { display: none; }
  /* Make sure the brand name isn't clipped at the wider drawer */
  .sb-workspace .ws-name { font-size: 18px; }
  .nav-item { padding: 12px 12px; }      /* roomier rows for touch */
  .nav-sub-item { padding: 10px 10px; }

  /* ============================================================
     SCHEDULE (the landing screen) — compact, usable
     ============================================================ */
  #scheduleTopbar {
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px 12px;
    align-items: center;
  }
  /* Drop desktop-only zoom control on phones */
  #scheduleTopbar .zoom-pill { display: none; }
  /* The flex spacer forces awkward wrapping on small screens */
  #scheduleTopbar .topbar-spacer { display: none; }
  #scheduleTopbar .icon-btn.sm { width: 40px; height: 40px; }
  #scheduleTopbar .icon-btn { width: 40px; height: 40px; }
  /* The New Appointment CTA is a floating action button on phones — see the
     "SCHEDULE — MOBILE-FIRST TOOLBAR" section at the end of this file. It is
     deliberately NOT a full-width toolbar row any more: that cost ~54px of
     vertical space on every screen. */

  /* Schedule grid: kill the hard 900px min-width that left a big
     dead area; let the day grid fit and scroll horizontally only
     when there are enough staff columns to need it. */
  .schedule-wrapper { overflow: auto; -webkit-overflow-scrolling: touch; }
  #scheduleInner { min-width: 0; }

  /* ============================================================
     SECTION HEADINGS & ACTIONS
     ============================================================ */
  .sec-header { gap: 12px; }
  .sec-title { font-size: 22px; }
  /* Primary action keeps prominence; secondary (Export/Categories/
     Scan) shrinks so the CTA wins visual weight. */
  .sec-header-actions { width: 100%; gap: 8px; }
  .sec-header-actions .btn { min-height: 42px; }
  .sec-header-actions .btn-primary { flex: 1 1 auto; justify-content: center; }
  .sec-header-actions .btn-secondary { flex: 0 1 auto; }

  /* ============================================================
     FILTER / SEGMENT / PERIOD ROWS — scroll, don't pile up
     ============================================================ */
  /* Client segment tabs */
  .seg-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .seg-tabs::-webkit-scrollbar { display: none; }
  .seg-tab { flex: 0 0 auto; white-space: nowrap; }

  /* Transaction / expense / period button groups: keep on one row
     and let them scroll. Uses :has() to catch the inline-styled
     wrappers (style="display:flex;gap:0") that hold .tx-period-btn. */
  .tx-period-group,
  .filter-bar :has(> .tx-period-btn),
  .tx-filters :has(> .tx-period-btn) {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
    scrollbar-width: none;
  }
  .tx-period-group::-webkit-scrollbar,
  .filter-bar :has(> .tx-period-btn)::-webkit-scrollbar { display: none; }
  .tx-period-btn { flex: 0 0 auto; white-space: nowrap; min-height: 40px; }

  /* Date-range inputs carry inline width:140/148px — let them flex */
  .filter-bar input[type="date"],
  .emp-filter-bar input[type="date"] {
    width: auto !important;
    min-width: 120px;
    flex: 1 1 130px;
  }

  /* Comfortable controls everywhere on touch */
  .filter-sel,
  .form-input,
  .form-select,
  .search-input { min-height: 42px; }

  /* ============================================================
     TABLES — already wrapped; just give a touch-friendly scroll
     and a hint of inset so first/last cell isn't glued to the edge
     ============================================================ */
  .table-wrap,
  .table-card-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* ============================================================
     MODALS — internal scroll, reachable header & footer
     ============================================================ */
  .modal-overlay { align-items: flex-end; padding: 0; }
  .modal, .modal-box {
    width: 100%;
    max-width: 100%;
    max-height: 92dvh;
    border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
    display: flex;
    flex-direction: column;
  }
  .modal-head, .modal-header { flex-shrink: 0; }
  .modal-foot, .modal-footer { flex-shrink: 0; }
  .modal-body {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    flex: 1 1 auto;
    min-height: 0;
  }

  /* ============================================================
     SETTINGS — header action bar wraps instead of clipping.
     "‹ All Settings" + Discard + Save Changes totalled ~356px next
     to the pane title and pushed Save Changes off a 390px screen.
     ============================================================ */
  .settings-head { flex-wrap: wrap; padding: 16px 16px 12px; }
  .settings-head-actions {
    width: 100%;
    flex-wrap: wrap;
    gap: 8px;
  }
  .settings-head-actions .btn { min-height: 44px; justify-content: center; }
  .settings-head-actions #settingsBtnBack { flex: 1 1 100%; }
  .settings-head-actions .btn-ghost { flex: 1 1 auto; }
  .settings-head-actions .btn-primary { flex: 2 1 auto; }

  /* Settings tables (Team & Access, Employees, Positions, Specialities)
     sit in .form-card-body without a .table-wrap — give them the same
     scroll + floor-width treatment so headers stop truncating to "NA…". */
  .form-card-body:has(> .crm-table) {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .form-card-body > .crm-table { min-width: 560px; }

  /* Working Hours (and any other) time inputs were 22px tall */
  input[type="time"] { min-height: 42px; }

  /* Any stray date input keeps its inline fixed width otherwise */
  .crm-section input[type="date"] {
    width: auto !important;
    max-width: 100%;
    min-width: 110px;
    flex: 1 1 130px;
  }

  /* Table row actions (Clients/Discounts Edit, settings Edit/Delete):
     28px icons are half the touch guideline */
  .crm-table .icon-btn.sm { width: 40px; height: 40px; }

  /* Segment tabs / status chips: comfortable touch heights */
  .seg-tab { min-height: 38px; }
  .status-chip, .records-pill { min-height: 32px; align-items: center; }

  /* Toasts: modals are bottom sheets on phones, and bottom-right toasts
     landed exactly on their Save/Cancel footers. Anchor top-center
     instead (below the 56px globalbar). supplement.css positions with
     !important, so these must out-cascade it (mobile.css loads last). */
  .toast-msg {
    top: 64px !important;
    bottom: auto !important;
    left: 50% !important;
    right: auto !important;
    transform: translateX(-50%) translateY(-12px) !important;
    max-width: calc(100vw - 24px) !important;
  }
  .toast-msg.show { transform: translateX(-50%) translateY(0) !important; }

  /* Horizontally scrollable chip/filter rows: fade the right edge so
     clipped content reads as "scrollable", not "broken". */
  .seg-tabs,
  .tx-period-group,
  .filter-bar :has(> .tx-period-btn),
  .tx-filters :has(> .tx-period-btn) {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), rgba(0,0,0,0.25));
    mask-image: linear-gradient(to right, #000 calc(100% - 24px), rgba(0,0,0,0.25));
  }

  /* Modal bodies laid out with inline two-column grids (Add/Edit Client,
     product & expense modals) collapse to one column — inline styles need
     the !important to lose. Children carrying inline grid-column:span 2
     would otherwise force an implicit second column right back, so they
     are neutralized too. */
  .modal-body[style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
  }
  .modal-body[style*="grid-template-columns"] > [style*="grid-column"] {
    grid-column: auto !important;
  }

  /* Appointment modal (supplement.css) — let the sheet grow to its
     natural content height and have the overlay be the scroller,
     instead of pinning the modal to 100dvh with overflow:hidden
     (which hard-clipped content past the viewport with no way to
     reach it — .appt-modal keeps overflow:hidden from supplement.css
     otherwise). align-items:flex-start (not stretch) so the overlay
     doesn't force the modal to viewport height. */
  .appt-overlay {
    align-items: flex-start;
    padding: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  .appt-modal {
    width: 100%;
    max-width: 100%;
    min-height: auto;
    border-radius: 0;
    margin: 0;
    overflow: visible;
  }
  /* Collapse the 3-column appointment form to one column */
  .appt-3col { grid-template-columns: 1fr !important; }
}

/* ============================================================
   SMALL PHONES (≤480px) — single-column forms, tighter chrome
   ============================================================ */
@media (max-width: 480px) {
  /* Any two-up form/info grid becomes one column so inputs are
     full-width and tappable rather than 150px stubs. */
  .form-grid,
  .info-grid,
  .modal-body .form-grid,
  .modal-card > div[style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
  }

  .sec-title { font-size: 20px; }
  .crm-section { padding: 14px 14px; }

  /* Exchange-rate editor: two columns are 140px stubs at this width */
  .rates-grid { grid-template-columns: 1fr; }

  /* Salary: trim toolbar chrome so content surfaces above the fold */
  #sec-salary .filter-bar { padding: 8px 10px; gap: 8px; }
}

/* =============================================================
   2026-07-17 — iPhone screenshot pass (430x932 audit)
   Drawer label guard, appointment-modal hierarchy, toolbar
   compaction, scrollable-table affordances, KPI + range polish,
   landscape-phone layer. Appended last: wins the cascade.
   ============================================================= */

/* ---- Drawer guard + shared phone/landscape rules ---- */
@media (max-width: 768px), (max-height: 500px) and (orientation: landscape) and (max-width: 1024px) {

  /* Open drawer width: supplement.css:1062 pins .sidebar to
     var(--sidebar-w) (220px) with !important, which truncated the brand
     ("Aurelia Be…") and "Salary Calculati…". Override with higher
     specificity + !important so labels get room. */
  .sidebar.open { width: min(86vw, 300px) !important; }

  /* The open drawer must NEVER render icon-only. If the desktop rail
     class .collapsed is still present (leaked from desktop use), undo
     everything layout.css:34-49 hides. Belt-and-braces with the JS
     guard in toggleSidebar(). */
  .sidebar.open.collapsed { width: min(86vw, 300px) !important; }
  .sidebar.open.collapsed .nav-item .label { display: block; }
  .sidebar.open.collapsed .nav-item .badge { display: inline-block; }
  .sidebar.open.collapsed .nav-item .caret { display: block; }
  .sidebar.open.collapsed .nav-sub.open { display: flex; }
  .sidebar.open.collapsed .sb-footer .label { display: block; }
  .sidebar.open.collapsed .sb-workspace .ws-info { display: flex; }
  .sidebar.open.collapsed .sb-workspace .ws-disc { display: flex; }
  .sidebar.open.collapsed .sb-workspace {
    justify-content: flex-start;
    padding: 14px 16px;
  }

  /* Date chip must never wrap into a circle (seen in landscape) */
  .date-pill, .date-pill span { white-space: nowrap; }
}

/* ---- Phone portrait refinements ---- */
@media (max-width: 768px) {

  /* Drawer typography: room for "Aurelia Beauty" + "Salary Calculation" */
  .sidebar .sb-workspace .ws-name { font-size: 16px; }
  .sidebar.open .nav-item { padding: 12px 10px; gap: 10px; }

  /* ============================================================
     APPOINTMENT MODAL — correct hierarchy + fitted inputs
     DOM order is status-bar -> tabs -> header; on phones show the
     title + close FIRST, then status, then tabs (flex order).
     ============================================================ */
  .appt-modal {
    display: flex;
    flex-direction: column;
    padding-top: max(env(safe-area-inset-top), 6px);
  }
  .appt-modal > .modal-header {
    order: -2;
    padding: 10px 16px 10px !important; /* inline style carries desktop padding */
  }
  .appt-modal > .appt-status-bar { order: -1; }
  .appt-status-btn { min-height: 42px; font-size: 12px; }
  .appt-tab { padding: 12px 14px; font-size: 12px; }
  .appt-tabs {
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), rgba(0,0,0,0.3));
    mask-image: linear-gradient(to right, #000 calc(100% - 24px), rgba(0,0,0,0.3));
  }
  .appt-tabs::-webkit-scrollbar { display: none; }

  /* iOS date inputs keep an intrinsic width and overflowed the sheet
     (right border offscreen). Hard-cap every date field in a modal. */
  .appt-modal input[type="date"],
  .modal input[type="date"],
  .modal-box input[type="date"] {
    width: 100% !important;
    min-width: 0;
    max-width: 100%;
    box-sizing: border-box;
    -webkit-appearance: none;
    appearance: none;
    min-height: 44px;
  }
  /* Belt and braces app-wide: a date field may never exceed its box */
  input[type="date"] { max-width: 100%; box-sizing: border-box; }

  /* Sticky sheet footers: home-indicator clearance; last form field
     must never hide beneath the footer */
  .modal-foot, .modal-footer, .appt-modal-footer {
    padding-bottom: max(14px, env(safe-area-inset-bottom));
  }
  .modal-body { padding-bottom: 24px; }

  /* ============================================================
     SCHEDULE TOOLBAR — reclaim vertical space for the grid
     ============================================================ */
  #scheduleTopbar { padding: 6px 10px 8px; gap: 6px; }
  #scheduleTopbar .btn.btn-sm,
  #scheduleTopbar .seg button { padding-top: 8px; padding-bottom: 8px; }
  #scheduleTopbar .pill { min-height: 40px; }

  /* ============================================================
     SCROLLABLE TABLES — empty message kept in the VISIBLE area

     These containers used to carry a right-edge `mask-image` fade as a
     scroll affordance. A mask multiplies the element's ALPHA, so the last
     28px of every table was permanently rendered at 35% opacity —
     regardless of whether it could actually scroll. Over the cream page
     background that read as a pale vertical rectangle pinned to the right
     edge of nearly every screen (tables appear on almost every section),
     with the row text under it washed out. Removing the mask leaves a
     clean, fully opaque card edge; the horizontal scroll below is still
     discoverable by dragging, and no content is dimmed.
     Do NOT reintroduce an alpha mask here — if a fade is ever wanted it
     must be an opaque overlay in the container's own background colour,
     applied only while the element really overflows.
     ============================================================ */
  td.table-empty { padding: 28px 12px !important; }
  .table-empty-msg {
    position: sticky;
    left: 12px;
    right: 12px;
    display: inline-block;
    max-width: min(78vw, 400px);
  }

  /* Employee list: three real columns that FIT the phone viewport.

     The previous rule forced `min-width: 560px` and scrolled the table
     sideways. At a 402px-wide card that pushed ~160px off-screen: ACCESS was
     never reachable without a drag, SERVICES was sliced mid-word, and the
     clipped edge sitting next to the section's 14px gutter read as a blank
     rectangle laid over the list. The original reason for the min-width was
     that percentage/auto columns COLLAPSED (the ACCESS header rendered as
     "AC") — that is fixed here by giving every column an explicit basis plus
     `min-width: 0` and ellipsis, so they shrink gracefully instead of
     colliding. Total basis 165 + 92 = 257px leaves SERVICES ~105px at 390px
     wide, the narrowest handset we target.
     `overflow-x: auto` stays as a safety valve for very long single words. */
  .emp-table { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .emp-table-head, .emp-table .dt-row { min-width: 0; }
  .emp-table-head > div,
  .emp-table .dt-cell { padding-left: 10px; padding-right: 10px; }
  .emp-table-head > div:nth-child(1),
  .emp-table .dt-cell:nth-child(1) { flex: 0 0 165px; min-width: 0; }
  .emp-table-head > div:nth-child(2),
  .emp-table .dt-cell:nth-child(2) { flex: 1 1 auto; min-width: 0; }
  .emp-table-head > div:nth-child(3),
  .emp-table .dt-cell:nth-child(3) { flex: 0 0 92px; min-width: 0; }
  /* Name/role stack: ellipsis needs min-width:0 on the flex child that wraps
     the two lines, or the text sets the column's floor and nothing shrinks. */
  .emp-table .dt-cell:nth-child(1) > div:last-child { min-width: 0; }
  .emp-table .dt-cell:nth-child(1) > div:last-child > div {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Cells 2 and 3 hold bare text nodes. `.dt-cell` is display:flex, where a
     bare text node becomes an anonymous flex item that ignores both ellipsis
     and line-clamp — so these two switch off flex and centre themselves
     instead (the row is align-items:stretch).
     Both use single-line ellipsis rather than -webkit-line-clamp: clamp draws
     the ellipsis but does not reliably shrink the BOX, so a clipped third line
     kept painting under the row border, and pinning `max-height` instead
     fought `.dt-cell`'s 12px vertical padding (border-box) and sliced line two
     in half. One clean line is the honest summary — the profile lists them all. */
  .emp-table .dt-cell:nth-child(2),
  .emp-table .dt-cell:nth-child(3) {
    display: block;
    align-self: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Employee profile sub-tabs: scroll with a fade, not a hard clip */
  .subtabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    flex-wrap: nowrap;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), rgba(0,0,0,0.3));
    mask-image: linear-gradient(to right, #000 calc(100% - 24px), rgba(0,0,0,0.3));
  }
  .subtabs::-webkit-scrollbar { display: none; }

  /* ============================================================
     KPI CARDS — consistent display numerals, tighter chrome
     ============================================================ */
  .kpi-grid { gap: 10px; }
  .kpi-card { padding: 16px; gap: 6px; }
  .kpi-value { font-size: clamp(22px, 6.5vw, 28px); }

  /* ============================================================
     RANGE PAIRS — tidy 2-up From/To fields on phones
     ============================================================ */
  .range-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    width: 100%;
    max-width: 430px;
  }
  .range-fld { display: flex; }
  .range-lbl { font-size: 10.5px; }
  .range-fld > input[type="date"] {
    width: 100% !important;
    height: auto !important;
    min-height: 42px;
    flex: none;
  }
}

/* ============================================================
   iOS INPUT ZOOM GUARD
   ------------------------------------------------------------
   Safari on iOS/iPadOS zooms the whole page when a focused field
   computes below 16px, which throws the layout off-centre and
   leaves it that way after the keyboard closes. 16px is the exact
   threshold, so this is a hard platform floor rather than a style
   choice — hence !important, which is needed to beat the more
   specific per-component rules (.phone-input-wrap input is 13px,
   .form-input is 13px, .text-input input is 13px).

   Scoped to touch pointers, NOT to a width breakpoint: iPadOS sits
   well above 768px and zooms just the same, while a narrow desktop
   window has a mouse and must keep the 13px design scale.

   Controls that never receive text and never trigger the zoom
   (checkbox, radio, color, range, file, hidden, buttons) are left
   alone so their box sizing is untouched.
   ============================================================ */
@media (hover: none) and (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="color"]):not([type="range"]):not([type="file"]):not([type="hidden"]):not([type="button"]):not([type="submit"]):not([type="reset"]),
  select,
  textarea {
    font-size: 16px !important;
  }
}

/* ---- Small phones ---- */
@media (max-width: 480px) {
  /* Appointment status: 2-col grid (3 rows) instead of six crushed buttons */
  .appt-status-bar { display: grid; grid-template-columns: 1fr 1fr; }
  .appt-status-btn { min-height: 44px; font-size: 11px; }
  .appt-status-btn:nth-child(odd) { border-right: 1px solid var(--border); }
  .appt-status-btn:not(:nth-last-child(-n+2)) { border-bottom: 1px solid var(--border); }
}

/* ============================================================
   LANDSCAPE PHONES (short viewports) — the width lands in the
   tablet band, but it is still a phone: mobile nav drawer, one
   scrollable toolbar row, no desktop rail.
   ============================================================ */
@media (max-height: 500px) and (orientation: landscape) and (max-width: 1024px) {
  .app, .app:has(.sidebar.collapsed) { grid-template-columns: 1fr; }
  .hamburger { display: inline-flex; }
  .sidebar {
    position: fixed;
    top: 0; left: 0;
    height: 100%;
    width: min(50vw, 320px);
    z-index: 300;
    transform: translateX(-100%);
    transition: transform var(--dur-base) var(--ease-out);
    box-shadow: var(--shadow-lg);
  }
  .sidebar.open { transform: translateX(0); }
  .sidebar.collapsed { width: min(50vw, 320px); }
  .sidebar .sb-cal { display: none; }
  .sidebar-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(8, 10, 14, 0.55);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    z-index: 299;
  }

  /* Toolbar: one row that scrolls; nothing gets clipped offscreen */
  #scheduleTopbar {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: 6px 10px;
    gap: 6px;
  }
  #scheduleTopbar::-webkit-scrollbar { display: none; }
  #scheduleTopbar > * { flex-shrink: 0; }
  #scheduleTopbar .topbar-spacer,
  #scheduleTopbar .zoom-pill { display: none; }
  #scheduleTopbar .btn.btn-primary { white-space: nowrap; }

  /* Modals use the full short viewport */
  .modal, .modal-box { max-height: 100dvh; border-radius: 0; }
  .modal-overlay { align-items: stretch; }
}

/* =============================================================
   2026-07-26 — SCHEDULE: MOBILE-FIRST TOOLBAR
   -------------------------------------------------------------
   Before this pass the global bar (56px) plus a four-row wrapped
   schedule toolbar (~150px) consumed ~45% of an iPhone viewport
   before a single appointment was visible.

   The toolbar is now ONE row of primary controls:

       [ Today, 26 Jul ]  [ Day | Week ]  [ ⚙ ]
        └ unified date nav                  └ filters sheet

   Everything secondary (worker filter, status legend, zoom, Sell,
   revenue, add-staff) is MOVED — not copied — into a bottom sheet
   by mobile-layout.js, the language toggle moves into the nav
   drawer, and the New Appointment CTA becomes a floating action
   button. Net chrome: ~56px, down from ~150px.
   ============================================================= */
@media (max-width: 768px) {

  /* ---- Global bar: hamburger + bell only ----
     The language toggle now lives in the drawer (mobile-layout.js). */
  .globalbar .lang-toggle { display: none; }

  /* Drawer footer language toggle: full-width segmented pair, 44px tall. */
  .sb-lang {
    display: block;
    padding: 10px 12px 4px;
    border-top: 1px solid var(--border-soft);
  }
  .sb-lang .lang-toggle { display: flex; width: 100%; }
  .sb-lang .lang-toggle button {
    flex: 1 1 50%;
    min-height: 44px;
    justify-content: center;
  }

  /* ============================================================
     TOOLBAR — one self-tuning row
     ============================================================ */
  #scheduleTopbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    height: auto;
    min-height: 0;
  }

  /* Secondary controls live in the sheet. The child combinator means these
     rules stop applying the moment mobile-layout.js reparents them, so the
     same nodes render normally inside the sheet. If the script never runs,
     they simply stay hidden and the toolbar is still usable. */
  #scheduleTopbar > #posBtn,
  #scheduleTopbar > #scheduleZoomPill,
  #scheduleTopbar > #statusLegend,
  #scheduleTopbar > #sellMenuTrigger,
  #scheduleTopbar > #revenueBadge,
  #scheduleTopbar > #addStaffBtn,
  #scheduleTopbar > .topbar-spacer { display: none; }

  /* ---- Unified date navigator ----
     The flex-basis is the whole responsive story: while 170px of date nav
     fits next to the view toggle and the filters button the toolbar is one
     row, and it absorbs all the leftover width when it does. Measured against
     the real controls (view toggle 109px + filters 44px + 12px of gaps +
     20px padding), 170px keeps a single row down to 360px; only a 320px
     screen wraps, and it wraps to two clean 44px rows. It can never overflow
     horizontally at any width. */
  #scheduleTopbar .date-nav {
    flex: 1 1 170px;
    min-width: 0;
    gap: 0;
    padding: 2px;
  }
  /* Redundant on a phone: the label itself jumps to today. */
  #scheduleTopbar .date-nav-today { display: none; }

  #scheduleTopbar .date-nav-arrow {
    width: 44px;
    height: 44px;
    flex: 0 0 44px;
  }
  #scheduleTopbar .date-nav-arrow .lucide { width: 20px; height: 20px; }

  #scheduleTopbar .date-nav-label {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 44px;
    justify-content: center;
    padding: 0 6px;
    font-size: 13px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  /* "Today, 26 Jul" — the prefix appears only when the shown date IS today
     (updateScheduleDateLabel() owns the .is-today class). */
  #scheduleTopbar .date-nav-label.is-today .dn-today,
  #scheduleTopbar .date-nav-label.is-today .dn-comma { display: inline; }
  #scheduleTopbar .date-nav-label .dn-text { flex: 0 1 auto; }
  /* The year is redundant on a phone — and dropping it is what keeps the
     toolbar to a single row down to ~360px. */
  #scheduleTopbar #scheduleDateYear { display: none; }

  /* ---- View toggle: the one filter that stays in the bar ---- */
  #scheduleTopbar .seg { flex: 0 0 auto; }
  #scheduleTopbar .seg button {
    min-height: 44px;
    padding: 0 14px;
    font-size: 13px;
  }

  /* ---- Filters trigger ---- */
  #scheduleTopbar .schedule-filters-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: relative;
    flex: 0 0 44px;
    width: 44px;
    height: 44px;
  }
  .schedule-filters-btn .lucide { width: 20px; height: 20px; }
  /* Dot badge when a non-default worker filter is active, so a filtered
     schedule can never look like the full one. schedule.js toggles the class
     from updateWorkerFilterLabel(). */
  .schedule-filters-btn.has-active-filter::after {
    content: "";
    position: absolute;
    top: 7px; right: 7px;
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--bg-base);
  }

  /* ============================================================
     FILTERS BOTTOM SHEET
     ============================================================ */
  .filter-sheet-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-modal) - 1);
    background: rgba(8, 10, 14, 0.5);
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
    opacity: 0;
    transition: opacity var(--dur-base) var(--ease-out);
    /* Only the OPEN backdrop takes taps. While it fades out it is still
       painted, and without this it would keep intercepting every tap aimed at
       the toolbar beneath it. */
    pointer-events: none;
  }
  .filter-sheet-backdrop.open { opacity: 1; pointer-events: auto; }

  .filter-sheet {
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: var(--z-modal);
    max-height: min(82dvh, 620px);
    background: var(--bg-elev-2);
    border-top: 1px solid var(--border);
    border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
    box-shadow: var(--shadow-lg);
    transform: translateY(100%);
    transition: transform var(--dur-base) var(--ease-out);
    /* Home indicator / gesture bar clearance on notched devices. */
    padding-bottom: max(10px, env(safe-area-inset-bottom));
  }
  .filter-sheet.open { transform: translateY(0); }
  /* `hidden` is the resting state — it must beat display:flex above. */
  .filter-sheet[hidden],
  .filter-sheet-backdrop[hidden] { display: none; }

  .filter-sheet-grip {
    width: 38px; height: 4px;
    margin: 8px auto 2px;
    border-radius: 999px;
    background: var(--border-strong);
    flex-shrink: 0;
  }
  .filter-sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 6px 12px 8px 16px;
    border-bottom: 1px solid var(--border-soft);
    flex-shrink: 0;
  }
  .filter-sheet-title {
    font-family: var(--font-display);
    font-size: 17px;
    font-weight: 600;
    color: var(--fg);
  }
  .filter-sheet-head .icon-btn { width: 44px; height: 44px; }

  .filter-sheet-body {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding: 4px 16px 8px;
    flex: 1 1 auto;
    min-height: 0;
  }
  .fs-group { padding: 12px 0; border-bottom: 1px solid var(--border-soft); }
  .fs-group:last-child { border-bottom: none; }
  .fs-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--fg-dim);
    margin-bottom: 8px;
  }
  /* An empty group would otherwise show a stray label and divider — e.g. a
     staff-role user who never gets the revenue or Sell controls. */
  .fs-group:has(> .fs-slot:empty) { display: none; }

  .fs-slot { display: flex; flex-direction: column; gap: 8px; }
  .fs-slot > .btn,
  .fs-slot > .icon-btn,
  .fs-slot > .pill {
    min-height: 44px;
    width: 100%;
    justify-content: flex-start;
    gap: 10px;
  }
  .fs-slot-actions { display: grid; grid-template-columns: 1fr; gap: 8px; }
  /* The add-staff control is icon-only in the toolbar; in the sheet it needs
     a readable width and its title as the visible affordance. */
  .fs-slot > #addStaffBtn { width: 100%; padding: 0 14px; }

  /* Portals rendered INSIDE the sheet lose their fixed positioning and
     become inline blocks — a fixed dropdown anchored to a control sitting at
     the bottom of the screen would have opened off the bottom edge. The
     inline `top`/`left` that toggle*() writes is ignored under position:static. */
  .filter-sheet .pos-drop,
  .filter-sheet .sell-menu,
  .filter-sheet .rev-menu {
    position: static;
    width: 100%;
    min-width: 0;
    max-width: 100%;
    box-shadow: none;
    background: var(--bg-elev-1);
    max-height: none;
  }
  .filter-sheet .pos-drop { padding: 8px 0; }
  .filter-sheet .pos-worker-opt,
  .filter-sheet .sell-menu-item { min-height: 44px; }
  .filter-sheet .rev-metric-list { overflow: visible; }

  /* Status legend is hidden in the topbar below 1024px (supplement.css); in
     the sheet there is room to show it properly, labels and all. */
  .filter-sheet .status-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }
  .filter-sheet .status-legend-item {
    min-height: 32px;
    padding: 4px 10px;
    font-size: 12px;
  }
  .filter-sheet .status-legend-item .sl-label { display: inline; }

  /* Zoom only means something in WEEK view: mobile day view renders as a
     chronological agenda list (renderDayAgenda), which ignores the --slot-h
     sizing the slider drives. The group is hidden until setView('week') puts
     .view-week on the sheet, so the sheet never offers a dead control.
     supplement.css blanket-hides .zoom-pill below 900px, hence the explicit
     display here. */
  .filter-sheet .fs-group:has(> #fsSlotZoom) { display: none; }
  .filter-sheet.view-week .fs-group:has(> #fsSlotZoom) { display: block; }
  .filter-sheet .zoom-pill {
    display: inline-flex;
    width: 100%;
    min-height: 44px;
  }
  .filter-sheet .zoom-pill .track { flex: 1 1 auto; }
  /* The group heading already reads "Zoom" — drop the pill's own label. */
  .filter-sheet .zoom-pill > [data-i18n="zoom"] { display: none; }

  /* ============================================================
     CTA — floating action button
     ============================================================ */
  #scheduleTopbar .btn.btn-primary.schedule-cta {
    position: fixed;
    right: 16px;
    /* Clears the home indicator and Safari's collapsing bottom bar. */
    bottom: calc(16px + env(safe-area-inset-bottom));
    z-index: var(--z-sticky);
    width: 56px;
    height: 56px;
    min-height: 56px;
    padding: 0;
    border-radius: 50%;
    justify-content: center;
    box-shadow: var(--shadow-lg);
  }
  .schedule-cta .lucide { width: 24px; height: 24px; }
  /* Icon-only: the button keeps its aria-label for screen readers. */
  .schedule-cta .cta-label {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }
  /* Don't float the CTA over the sheet the user just opened. */
  body.sheet-open .schedule-cta { opacity: 0; pointer-events: none; }

  /* The FAB overlays the grid — keep the last rows reachable. */
  .schedule-wrapper { padding-bottom: calc(76px + env(safe-area-inset-bottom)); }

  /* ============================================================
     OVERFLOW GUARD
     The page may scroll vertically; it must never scroll sideways.
     ============================================================ */
  .app, .main, .content { max-width: 100%; overflow-x: hidden; }
  #sec-timetable { overflow-x: hidden; }
}

/* Phones (as opposed to small tablets): trim the two controls that share the
   row with the date navigator, so the date label keeps the width it needs.
   420px covers every current handset in portrait. */
@media (max-width: 420px) {
  #scheduleTopbar .date-nav-label > .lucide { display: none; }
  #scheduleTopbar .date-nav-label { font-size: 12.5px; }
  #scheduleTopbar .seg button { padding: 0 11px; font-size: 12px; }
}

/* 360-374px (common Android portrait): the row fits, but only just — the
   label loses the "Today, " prefix rather than ellipsing the date, which is
   the part you cannot infer. Tapping it still jumps to today. */
@media (min-width: 360px) and (max-width: 374px) {
  #scheduleTopbar .date-nav-label.is-today .dn-today,
  #scheduleTopbar .date-nav-label.is-today .dn-comma { display: none; }
}

/* <=359px (iPhone SE and friends): three 44px controls cannot share a row
   without breaking the touch-target floor, so wrap deliberately — date
   navigator across the top, view toggle + filters beneath. Two clean rows
   beat one row with a stranded button, and the full-width nav has room for
   the "Today, " prefix again. */
@media (max-width: 359px) {
  #scheduleTopbar .date-nav { flex: 1 1 100%; }
}

/* =============================================================
   2026-07-30 — DATA PAGES: fold reclamation + card tables
   Transactions · Expenses · Records · Employees (+ Upcoming)

   Measured at 430x932 before this block, the filter chrome pushed the first
   row of data to:
       Records 647px (69% of the viewport)   Expenses 565px (61%)
       Transactions 467px (50%)              Employees 307px (33%)
   and every table was a 680px-wide sideways scroller inside a 402px column,
   so Amount/Method/Status sat off-screen with no affordance.

   The structure: filter-sheet.js MOVES the secondary controls into a bottom
   sheet, leaving one compact line of "search + Filters (n)"; the period pills
   stay on the page as a scrollable strip; and the tables become stacked cards
   so nothing needs sideways scrolling at all.
   ============================================================= */
@media (max-width: 768px) {

  /* ---- Compact filter bar: full-width search + Filters (n) ------------- */
  .mfilter-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 14px 0;
    max-width: 100%;
  }
  /* The search wrapper is MOVED in here from the page's filter bar, so it
     arrives carrying desktop geometry (flex-basis, max-width, fixed widths).
     Override it to own the row. */
  .mfilter-search { flex: 1 1 auto; min-width: 0; display: flex; }
  .mfilter-search > * {
    flex: 1 1 auto;
    min-width: 0;
    width: 100%;
    max-width: none;
  }
  .mfilter-search .search-input,
  .mfilter-search .form-input,
  .mfilter-search .tx-search-input { width: 100%; min-width: 0; }

  .mfilter-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    min-height: 44px;
    padding: 0 14px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--bg-elev-2);
    color: var(--fg);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
  }
  .mfilter-btn i,
  .mfilter-btn svg { width: 16px; height: 16px; flex: 0 0 auto; }
  /* Active filters are the state worth shouting about — the button goes gold
     so "this list is narrowed" is legible without opening the sheet. */
  .mfilter-btn.has-filters {
    border-color: var(--accent);
    color: var(--accent);
  }
  .mfilter-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
  }
  .mfilter-count[hidden] { display: none; }

  /* ---- What is left on the page once the sheet has taken the rest ------ */
  /* These bars keep only their period pills / result counts, so they lose the
     desktop padding that assumed a full row of controls. */
  #sec-finance-transactions .tx-filters,
  #sec-finance-expenses .filter-bar,
  #sec-records .filter-bar,
  #sec-staff .emp-filter-bar {
    padding: 10px 14px;
    gap: 8px;
    border-bottom: none;
  }
  /* Rows whose entire contents moved into a sheet would otherwise render as a
     stray caption or an empty flex spacer taking a full line. */
  #recordsFilterRow2,
  #recordsStatusRow { display: none; }

  /* ---- Period pills: one scrollable strip, never a wrapped block ------- */
  .period-scroller {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    max-width: 100%;
    scrollbar-width: none;
    /* Fade the trailing edge so it reads as "more to the right" rather than
       as a clipped button. */
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), rgba(0,0,0,0.25));
    mask-image: linear-gradient(to right, #000 calc(100% - 24px), rgba(0,0,0,0.25));
  }
  .period-scroller::-webkit-scrollbar { display: none; }
  .period-scroller > * { flex: 0 0 auto; white-space: nowrap; }
  .period-scroller.pill-row > .records-pill { min-height: 34px; }

  /* ---- Filter sheet: footer with the page's clear/export actions ------- */
  .filter-sheet-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--border-soft);
    flex-shrink: 0;
  }
  .fs-foot-slot { display: flex; gap: 8px; flex: 1 1 auto; min-width: 0; }
  .fs-foot-slot > .btn,
  .fs-foot-slot > .tx-clear-btn {
    flex: 1 1 auto;
    min-height: 44px;
    justify-content: center;
    white-space: nowrap;
  }
  .fs-done { flex: 0 0 auto; min-height: 44px; padding: 0 22px; }
  .filter-sheet[hidden] .filter-sheet-foot { display: none; }

  /* Controls arrive from a desktop bar carrying inline widths (width:140px on
     the date fields, 90px on Min/Max) and flex bases sized for a toolbar. In
     the sheet they get a full row each. Inline styles need the !important.

     The flex reset is the important one: .filter-sel carries `flex: 1 1 140px`
     (responsive.css) to size its WIDTH in a horizontal filter bar. .fs-slot
     stacks its children in a COLUMN, where flex-basis resolves against the
     main axis — height — so every select rendered 140px tall. Scoped to the
     page sheets so the schedule sheet's own controls are untouched. */
  .page-filters .fs-slot > * { flex: 0 0 auto; }
  .fs-slot > * { max-width: 100%; min-width: 0; }
  .fs-slot .form-select,
  .fs-slot .filter-sel,
  .fs-slot > select { width: 100%; max-width: none; min-height: 44px; }
  .fs-slot input[type="date"],
  .fs-slot input[type="number"],
  .fs-slot input[type="text"] {
    width: 100% !important;
    min-width: 0;
    min-height: 44px;
  }
  .fs-slot .range-pair,
  .fs-slot .amount-range,
  .fs-slot #recordsCustomRange { display: flex !important; gap: 8px; width: 100%; }
  .fs-slot .range-fld { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
  .fs-slot .amount-range > input { flex: 1 1 0; }
  /* The date-range widget is a single bordered pill holding two text inputs;
     on desktop it is 34px tall with fixed-width children, which is what
     clipped "07/30/2026" down to "07/30/202". */
  .fs-slot .tx-date-range { width: 100%; height: auto; min-height: 44px; padding: 4px 10px; }
  .fs-slot .tx-date-range .tx-date-input { flex: 1 1 0; min-width: 0; width: auto; text-align: center; }
  /* Status chips keep wrapping inside the sheet — they are a multi-select
     grid, not a scrolling strip. */
  .fs-slot-chips > * { display: flex; flex-wrap: wrap; gap: 6px; }
  .fs-slot-chips .status-chip { min-height: 34px; }

  /* ============================================================
     TABLES AS CARDS
     A 7-to-9 column table cannot be made legible at 402px by scrolling it —
     the columns that matter (Amount, Method, Status) are the ones that end
     up off-screen. Each row becomes a card and each cell a labelled line, so
     every field is readable with no horizontal gesture and status badges sit
     on their own row where they cannot clip.
     `data-label` is stamped onto every <td> by filter-sheet.js from the
     table's own <thead>, so this stays correct as columns change.
     ============================================================ */
  .table-wrap:has(> .crm-table.as-cards),
  .table-card-wrap:has(> .crm-table.as-cards) {
    overflow-x: visible;
    padding-left: 0;
    padding-right: 0;
    border: none;
    background: none;
  }
  .crm-table.as-cards {
    min-width: 0;
    width: 100%;
    table-layout: auto;
    border-collapse: separate;
    border-spacing: 0;
  }
  /* Visually hidden, not display:none — the header cells stay in the
     accessibility tree so the table keeps its column semantics. */
  .crm-table.as-cards thead {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
  .crm-table.as-cards tbody tr {
    display: block;
    margin: 0 0 10px;
    padding: 4px 0;
    background: var(--bg-elev-2);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
  }
  .crm-table.as-cards tbody tr:nth-child(even) td { background: transparent; }
  .crm-table.as-cards tbody td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: auto;
    max-width: none;
    padding: 8px 14px;
    border: none;
    text-align: left !important;
    /* The base rule clips every cell with ellipsis to protect fixed column
       widths; a card row has no fixed width, so let the value wrap instead. */
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
    word-break: break-word;
  }
  .crm-table.as-cards tbody td::before {
    content: attr(data-label);
    flex: 0 0 38%;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--fg-dim);
  }
  /* Unlabelled columns are the row-action cells — no caption, and the control
     sits at the end of its own line. */
  .crm-table.as-cards tbody td[data-label=""]::before { content: none; }
  .crm-table.as-cards tbody td[data-label=""] { justify-content: flex-end; }
  /* The "#" column is a row counter for a desktop grid; in a list of cards it
     is a bare number with nothing to count against. */
  .crm-table.as-cards tbody td[data-label="#"] { display: none; }
  .crm-table.as-cards tbody td[data-label=""]:empty { display: none; }
  /* A "no results" row spans the whole table — keep it as one centred line
     rather than a labelled card field. */
  .crm-table.as-cards tbody td[colspan] {
    display: block;
    text-align: center !important;
    padding: 22px 14px;
  }
  .crm-table.as-cards tbody td[colspan]::before { content: none; }
  /* Badges are the thing most likely to be clipped in the scrolling layout —
     give them room to sit whole at the end of their line. */
  .crm-table.as-cards tbody td .pill,
  .crm-table.as-cards tbody td .badge,
  .crm-table.as-cards tbody td .status-chip,
  .crm-table.as-cards tbody td .category-chip {
    white-space: nowrap;
    flex: 0 0 auto;
  }

  /* ---- Employees: the "table" is a flex grid, not a <table> ------------ */
  /* Access and Services were both truncating to an ellipsis at 402px. Stack
     the three columns instead so each row shows its full content. */
  #sec-staff .emp-table { overflow-x: visible; }
  #sec-staff .emp-table-head { display: none; }
  #sec-staff .emp-table .dt-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 10px;
    padding: 10px 12px;
  }
  #sec-staff .emp-table .dt-cell:nth-child(1) { flex: 1 1 100%; min-width: 0; }
  #sec-staff .emp-table .dt-cell:nth-child(2),
  #sec-staff .emp-table .dt-cell:nth-child(3) {
    flex: 1 1 auto;
    min-width: 0;
    padding-left: 0;
    /* Let the services list wrap onto a second line instead of clipping. */
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    font-size: 12px;
  }
  #sec-staff .emp-table .dt-cell:nth-child(3) { flex: 0 0 auto; }

  /* ---- Stat grids: a clean 2-up that never squishes a currency ---------- */
  /* auto-fit + minmax(160px) silently collapses to ONE column under ~350px,
     which turns the KPI strip into a tall stack. Pin it to two and let the
     value type scale instead. */
  .kpi-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }
  .kpi-grid > * { min-width: 0; }
  .kpi-value {
    min-width: 0;
    overflow-wrap: anywhere;
    font-size: clamp(18px, 5.2vw, 26px);
  }
  .kpi-label { min-width: 0; overflow-wrap: anywhere; }

  /* ---- Back button ----------------------------------------------------- */
  /* The label would push the globalbar's controls apart on a narrow screen —
     the arrow alone is the affordance, the accessible name carries the
     destination. */
  #globalBackBtn .back-label { display: none; }

  /* ...but dropping the label leaves layout.css's `width: auto` (correct on
     desktop, where this is icon + text) sizing against a bare 16px icon, while
     `.globalbar .icon-btn` still forces height: 40px and components.css gives
     .icon-btn a pill radius. The result was an 18x40 vertical lozenge sitting
     next to the 44px hamburger — and 18px of touch target against a 44px
     minimum. Size it as a square and match the hamburger exactly (same box,
     radius and icon size) so the two read as one pair of nav controls rather
     than two unrelated shapes. ID selector to outrank #globalBackBtn in
     layout.css; equal specificity resolves here because this file loads last. */
  #globalBackBtn {
    width: 44px;
    height: 44px;
    padding: 0;
    gap: 0;
    max-width: none;
    border-radius: var(--radius-md);
  }
  #globalBackBtn .lucide { width: 20px; height: 20px; }
}

/* A history-only back target is a phone affordance: on desktop the sidebar is
   permanently on screen, so a button that merely retraces steps is noise.
   Parent-based back (an inner page returning to its owner) still shows. */
@media (min-width: 769px) {
  #globalBackBtn[data-back-mode="history"] { display: none !important; }
}

/* Belt-and-braces horizontal containment. `.crm-section` sets overflow-y:auto,
   and CSS computes the other axis of a scroll container to `auto` too — so
   every section is silently its own sideways scroller. One over-wide child
   (a table, a wide grid) then drags the whole column right, which reads as a
   stray strip down the edge of the screen with the content shifted under it.
   Pin the axis explicitly at every width; anything genuinely wide scrolls
   inside its own wrapper instead. */
.crm-section { overflow-x: hidden; }
