/* ==========================================================================
   Inovbi — mobile presentation layer

   Everything a phone-width viewport needs that the desktop console shell
   (shell.css/components.css/base.css) doesn't already provide: card lists
   instead of tables, bottom sheets instead of dropdowns/modals, collapsible
   sections instead of dense forms, one visible primary action with the
   rest tucked behind an overflow menu.

   Same views, same templates, same data — this file only ever adapts
   PRESENTATION below the breakpoint. Nothing here changes what renders on
   a desktop-width screen; every rule is scoped to a max-width query (or
   targets a class that a desktop-hidden element wears). See mobile.js for
   the handful of progressive-enhancement behaviours this styling needs
   (responsive-table labels, qty steppers, sheet/menu toggling).

   Breakpoint convention (matches what shell.css/components.css already
   use — not a new scheme):
     <=720px   "mobile"  — cards, sheets, single column
     721-900px "tablet"  — drawer nav (shell.css), desktop tables/forms
                            single-column via existing .two-col/.form-grid
     >900px    "desktop" — unchanged
   ========================================================================== */


/* ==========================================================================
   Generic card list (erp/_data_table_cards.html) — the mobile replacement
   for erp/_data_table.html's <table>, built from the exact same `table`
   context dict (no view changes). Same visual language as the workflow
   inbox's hand-built .erp-approval-card (components.css), generalised so
   any page using the shared list partial gets it for free.
   ========================================================================== */
.m-list { display: none; }
@media (max-width: 720px) {
  .m-list { display: flex; flex-direction: column; gap: var(--space-3); }
  .m-list--panel { padding: var(--space-4); }
  .m-hide-mobile { display: none; }
}
.m-list__group-label {
  font-size: var(--text-2xs); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-subtle); margin: var(--space-2) 0 -var(--space-1);
}
.m-list-item {
  position: relative;
  display: block; border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: var(--space-4); background: var(--surface); box-shadow: var(--elev-1);
  text-decoration: none; color: inherit;
}
.m-list-item__cover { position: absolute; inset: 0; z-index: 0; }
.m-list-item__top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-2); }
.m-list-item__title { font-weight: 600; font-size: var(--text-md); color: var(--brand); word-break: break-word; }
.m-list-item:not(:has(.m-list-item__cover)) .m-list-item__title { color: var(--text); }
.m-list-item__badge { flex-shrink: 0; position: relative; z-index: 1; }
.m-list-item__meta { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); font-size: var(--text-sm); color: var(--text-muted); }
.m-list-item__meta-item { display: flex; gap: 4px; min-width: 0; }
.m-list-item__meta-label { color: var(--text-subtle); }
.m-list-item__meta-label::after { content: ":"; }
/* anything meant to stay tappable on its own (a secondary link/button inside
   the card) sits above the full-card cover link */
.m-list-item > :not(.m-list-item__cover) { position: relative; z-index: 1; pointer-events: none; }
.m-list-item__top, .m-list-item__meta { pointer-events: none; }
.m-list-item a, .m-list-item button { pointer-events: auto; }

/* ==========================================================================
   Responsive fallback for hand-written tables (Purchasing, Manufacturing,
   Warehouse, and every in-page sub-table that doesn't go through the
   shared _data_table.html partial). mobile.js sets a `data-label` on each
   <td> from its column's <th> text at load time; this just lays the
   result out as stacked label/value rows instead of a scrolling grid.
   Deliberately a safety net, not a curated design — it guarantees nothing
   overflows horizontally anywhere in the app, even on pages nobody has
   hand-tuned for mobile yet.
   ========================================================================== */
@media (max-width: 720px) {
  table.data-table.m-stacked, table.c360-table.m-stacked {
    display: block; width: 100%;
  }
  /* kv-table is already just a 2-column label/value table (see
     components.css) — no stacking needed, just let long values wrap
     instead of forcing the table wider than the viewport. */
  .kv-table td, .kv-table th { word-break: break-word; }
  .m-stacked thead { display: none; }
  .m-stacked tbody, .m-stacked tr { display: block; width: 100%; }
  .m-stacked tr {
    display: grid; gap: 2px var(--space-3); padding: var(--space-3) 0;
    border-bottom: 1px solid var(--line);
  }
  .m-stacked tr:last-child { border-bottom: none; }
  .m-stacked td {
    display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3);
    padding: 3px 0; border: none; font-size: var(--text-sm); text-align: start;
  }
  .m-stacked td.num { text-align: start; }
  .m-stacked td[data-label]::before {
    content: attr(data-label); flex-shrink: 0; font-size: var(--text-xs); font-weight: 600; color: var(--text-subtle);
  }
  .m-stacked td:empty, .m-stacked td[data-label=""] { display: none; }
}

/* Same treatment for any document's line-items table — both the editable
   formset variant (Sales Order / Quotation create-edit, Purchasing PO/
   reception/RFQ/requisition forms, Manufacturing order form — all built
   on erp/_doc_items_table.html or a per-module copy of it) and the plain
   read-only variant (every document detail page's line-items tab). The
   <input>/<select> controls in the editable case stay exactly as they
   are — same names, same formset — only the layout wrapping them
   changes, so "+ Add item" (shell.js) keeps working completely
   unmodified and there is only ever one copy of each field (no
   duplicate-name inputs to keep in sync). */
@media (max-width: 720px) {
  table.line-items-table { display: block; }
  table.line-items-table thead { display: none; }
  table.line-items-table tbody, table.line-items-table tr { display: block; width: 100%; }
  table.line-items-table tr {
    display: flex; flex-direction: column; gap: var(--space-2);
    padding: var(--space-4); margin-bottom: var(--space-3);
    border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface);
  }
  table.line-items-table td {
    display: flex; align-items: center; gap: var(--space-3); padding: 0; border: none;
  }
  table.line-items-table td[data-label]::before {
    content: attr(data-label); flex: 0 0 88px; font-size: var(--text-xs); font-weight: 600; color: var(--text-subtle);
  }
  table.line-items-table td[data-formset-index] { display: none; }
  table.line-items-table td input, table.line-items-table td select { flex: 1; min-height: 44px; }
}

/* qty stepper — mobile.js wraps a formset row's quantity <input type=number>
   with these +/- buttons on every viewport (one DOM, no duplicate inputs),
   but the buttons themselves are mobile-only: desktop keeps the plain
   number input it always had, the +/- just hidden below (min-width:721px
   rule). Deliberately NOT `display: contents` — WebKit/Safari has a long-
   standing bug where a form control descended from a `display: contents`
   ancestor can drop out of the form's submission entirely, or block
   native required-field validation with no visible error ("Save" doing
   nothing). A plain inline-flex wrapper carries none of that risk and
   looks identical once the buttons are hidden. */
.m-stepper { display: inline-flex; width: 100%; }
@media (max-width: 720px) {
  .m-stepper {
    display: inline-flex; align-items: stretch; border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm); overflow: hidden; flex: 1;
  }
  .m-stepper__btn {
    flex: 0 0 40px; display: flex; align-items: center; justify-content: center;
    border: none; background: var(--surface-sunken); color: var(--text); cursor: pointer; font-size: var(--text-lg);
  }
  .m-stepper__btn:active { background: var(--surface-hover); }
  .m-stepper input { border: none !important; border-radius: 0 !important; text-align: center; flex: 1; min-width: 0; }
}
@media (min-width: 721px) {
  .m-stepper__btn { display: none; }
}

/* ==========================================================================
   Searchable select (mobile.js wireSearchableSelect) — filter-as-you-type
   replacement for a plain <select data-searchable> with many options (the
   Sales Order line-item product picker: forms.py's ProductUomSelect).
   Desktop/tablet keep the native <select> exactly as it always was;
   <=720px swaps to the text input + listbox mobile.js builds around it.
   Same "wrap, don't replace" approach as the qty stepper above — one real
   form control either way, just a different way to pick its value.
   ========================================================================== */
.m-searchable-select { position: relative; }
.m-searchable-select__input, .m-searchable-select__list { display: none; }
@media (max-width: 720px) {
  .m-searchable-select > select { display: none; }
  .m-searchable-select__input {
    display: block; width: 100%; min-height: 44px; padding: 0 var(--space-3);
    border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
    background: var(--surface); color: var(--text); font: inherit; font-size: var(--text-sm);
  }
  .m-searchable-select.is-open .m-searchable-select__input {
    border-color: var(--brand); border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  }
  .m-searchable-select__list {
    position: absolute; inset-inline-start: 0; inset-inline-end: 0; top: 100%; z-index: var(--z-popover);
    max-height: 240px; overflow-y: auto; margin-top: -1px;
    background: var(--surface); border: 1px solid var(--brand); border-top: none;
    border-radius: 0 0 var(--radius-sm) var(--radius-sm); box-shadow: var(--elev-2);
  }
  .m-searchable-select__opt { padding: var(--space-3); font-size: var(--text-sm); cursor: pointer; border-bottom: 1px solid var(--line); }
  .m-searchable-select__opt:last-child { border-bottom: none; }
  .m-searchable-select__opt.is-selected { background: var(--brand-tint); font-weight: 600; }
  .m-searchable-select__opt:active { background: var(--surface-hover); }
  .m-searchable-select__empty { padding: var(--space-3); font-size: var(--text-sm); color: var(--text-subtle); }
}

/* ==========================================================================
   Header / overflow menu (object header + context bar page actions).
   Desktop keeps showing every action inline exactly as today (unchanged
   markup, just hidden <=720px); a second, mobile-only block shows the
   primary action plus a "more" trigger for the rest, reusing the existing
   <details class="erp-menu"> disclosure — shell.js already handles its
   outside-click/Escape dismissal for any such element on the page, no JS
   changes needed here.
   ========================================================================== */
.obj-header__actions-mobile, .erp-crumbs__actions-mobile { display: none; }
@media (max-width: 720px) {
  .obj-header__actions { display: none; }
  .obj-header__actions-mobile, .erp-crumbs__actions-mobile {
    display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0;
  }
  .erp-crumbs__actions { display: none; }
  .erp-crumbs__trail { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; }
}
.m-more-menu > summary { list-style: none; }
.m-more-menu > summary::-webkit-details-marker { display: none; }

/* ==========================================================================
   Bottom sheet — the existing generic AJAX modal (#erpModal, shell.js) and
   global search dialog, repositioned to slide up from the bottom instead
   of centering, on a phone-width viewport. Same fetch/open/close JS, same
   markup — CSS-only. The desktop drag/resize grip is pointless once the
   sheet is pinned to the bottom edge, so it's hidden here.
   ========================================================================== */
@media (max-width: 720px) {
  .erp-modal, .erp-modal--wide, .erp-search-dialog {
    position: fixed; inset: auto 0 0 0; margin: 0;
    width: 100%; max-width: 100%; min-width: 0;
    max-height: 88dvh; border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-bottom: none;
    animation: mSheetUp var(--dur) var(--ease);
  }
  .erp-modal__resize-grip { display: none; }
  .erp-modal__titlebar { cursor: default; justify-content: center; position: relative; }
  .erp-modal__titlebar::before {
    content: ""; position: absolute; left: 50%; top: 6px; transform: translateX(-50%);
    width: 36px; height: 4px; border-radius: var(--radius-pill); background: var(--line-strong);
  }
  .erp-modal__grip, .erp-modal__close { display: none; }
  .erp-search-dialog__form input { font-size: 16px; }
  /* safe-area: iOS home-indicator clearance under the sheet's own padding */
  .erp-modal__body:last-child, .erp-search-dialog__results:last-child {
    padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom));
  }
}
@media (prefers-reduced-motion: reduce) {
  .erp-modal, .erp-search-dialog { animation: none; }
}
@keyframes mSheetUp {
  from { transform: translateY(16px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

/* ==========================================================================
   Filter sheet (erp/_filter_bar.html). Search stays inline and always
   visible (high-frequency, per the mobile search convention below); the
   filter <select> fields — one <form>, never duplicated — become a bottom
   sheet opened by a small trigger button, closed by its own close button,
   the backdrop, or Escape.
   ========================================================================== */
.erp-filter-bar__trigger { display: none; }
.m-sheet-backdrop { display: none; }
/* Above the breakpoint, .erp-filter-bar__fields mirrors its parent's own
   flex row (same align-items/gap/wrap) so the <select>s + Apply button it
   wraps still read as one inline group next to the search box — NOT
   `display: contents`, which has a long-standing WebKit/Safari bug where
   a form control descended from a `display: contents` ancestor can drop
   out of the form's submission, or silently block native required-field
   validation ("Apply"/"Save" doing nothing, no visible error). */
.erp-filter-bar__fields { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3); }
.m-filter-sheet__head { display: none; }
@media (max-width: 720px) {
  .erp-filter-bar { flex-wrap: wrap; }
  .erp-filter-bar__search { flex: 1; }
  .erp-filter-bar__trigger { display: inline-flex; }
  .erp-filter-bar__fields {
    display: none;
  }
  .erp-filter-bar-wrap.is-open .m-sheet-backdrop {
    display: block; position: fixed; inset: 0; z-index: var(--z-backdrop);
    background: rgba(13, 33, 29, 0.45);
  }
  .erp-filter-bar-wrap.is-open .erp-filter-bar__fields {
    display: flex; flex-direction: column; gap: var(--space-4);
    position: fixed; inset: auto 0 0 0; z-index: var(--z-drawer);
    max-height: 80dvh; overflow-y: auto;
    padding: var(--space-5) var(--space-5) calc(var(--space-6) + env(safe-area-inset-bottom));
    background: var(--surface); border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: var(--elev-3); animation: mSheetUp var(--dur) var(--ease);
  }
  .m-filter-sheet__head { display: flex; align-items: center; justify-content: space-between; }
  .m-filter-sheet__head strong { font-family: var(--font-display); font-size: var(--text-md); }
  .erp-filter-bar__fields .erp-filter-bar__select { width: 100%; }
  .erp-filter-bar__fields .erp-filter-bar__select select { width: 100%; min-height: 44px; }
  .erp-filter-bar__fields .erp-filter-bar__go { width: 100%; min-height: 44px; }
}

/* ==========================================================================
   Checkbox list (sales/_partials/_form_field.html) — a genuine
   CheckboxSelectMultiple field (e.g. RFQHeaderForm.suppliers), styled as
   a scrollable, comfortably-tappable list instead of Django's bare <ul>.
   Not breakpoint-gated: this fixes an actual rendering bug (the field
   used to fall into the single-boolean-checkbox branch, squeezing the
   whole option list into one flex row next to the field's own label) at
   every width, not just mobile — only the per-row min-height bump below
   is mobile-only.
   ========================================================================== */
.m-checkbox-list {
  max-height: 260px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: var(--space-2);
}
.m-checkbox-list ul { list-style: none; margin: 0; padding: 0; }
.m-checkbox-list li { margin: 0; }
.m-checkbox-list label {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2); border-radius: var(--radius-xs);
  cursor: pointer; font-size: var(--text-sm); font-weight: 400;
}
.m-checkbox-list label:hover { background: var(--surface-hover); }
@media (max-width: 720px) {
  .m-checkbox-list label { min-height: 44px; }
}

/* ==========================================================================
   Collapsible form section (MobileFormSection) — a plain <details>, styled
   to match .panel so it drops into any create/edit form. Used to keep a
   long form to one visible field group at a time on mobile without a
   second copy of the fields; wide screens can render it open by default
   via the `open` attribute (still just a normal panel, no JS required).
   ========================================================================== */
.m-section { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); margin-bottom: var(--space-4); }
.m-section > summary {
  list-style: none; cursor: pointer; padding: var(--space-4) var(--space-5);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  font-family: var(--font-display); font-weight: 600; font-size: var(--text-md);
}
.m-section > summary::-webkit-details-marker { display: none; }
.m-section > summary::after {
  content: ""; flex-shrink: 0; width: 9px; height: 9px;
  border-right: 2px solid var(--text-subtle); border-bottom: 2px solid var(--text-subtle); /* rotated chevron: physical on purpose */
  transform: rotate(45deg); transition: transform var(--dur-fast) var(--ease);
}
.m-section[open] > summary::after { transform: rotate(-135deg); }
.m-section__body { padding: 0 var(--space-5) var(--space-5); }

/* ==========================================================================
   "More fields" — progressive disclosure inside an existing .form-grid
   (sales/_partials/form_fields.html, shared by ~75 create/edit forms
   across every domain). Required fields render normally, above; this
   wraps only the optional ones, as its own full-width row spanning both
   .form-grid columns, laid out as a matching 2-column sub-grid — NOT
   `display: contents` (which would have made it byte-for-byte invisible
   to the parent grid). WebKit/Safari has a long-standing bug where a
   form control descended from a `display: contents` ancestor can drop
   out of the form's submission entirely, or silently block native
   required-field validation — "Save" doing nothing, no visible error,
   on exactly the phones this whole redesign is for. A real sub-grid
   costs a hairline visual seam between required and optional fields on
   desktop; that's a fair trade for every field actually being able to
   submit. Below the breakpoint the toggle appears and the body
   collapses, so a long form (e.g. the 18-field Employee form) opens on
   the handful of fields that actually need filling in, not a wall of
   scroll.
   ========================================================================== */
.m-form-more { display: block; grid-column: 1 / -1; }
.m-form-more__toggle { display: none; }
.m-form-more__body { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--space-4); }
@media (max-width: 720px) {
  .m-form-more__toggle {
    width: 100%; display: flex; align-items: center; gap: var(--space-2);
    margin: var(--space-2) 0 var(--space-4); padding: var(--space-3) var(--space-4);
    border: 1px dashed var(--line-strong); border-radius: var(--radius-sm); background: none;
    font: inherit; font-weight: 600; font-size: var(--text-sm); color: var(--text-muted); cursor: pointer;
  }
  .m-form-more__toggle span:first-child { flex: 1; text-align: start; }
  .m-form-more__count {
    min-width: 20px; padding: 1px 6px; border-radius: var(--radius-pill);
    background: var(--surface-sunken); color: var(--text-muted); font-size: var(--text-xs); text-align: center;
  }
  .m-form-more__chevron { flex-shrink: 0; transition: transform var(--dur-fast) var(--ease); }
  .m-form-more.is-open .m-form-more__toggle { color: var(--text); border-style: solid; border-color: var(--line); }
  .m-form-more.is-open .m-form-more__chevron { transform: rotate(180deg); }
  .m-form-more__body { display: none; }
  .m-form-more.is-open .m-form-more__body { display: block; }
}

/* ==========================================================================
   KPI card (dashboards) — one metric per full-width card on mobile,
   instead of the 4-across .erp-kpis grid (erp/dashboard/_kpis.html renders
   both from the same dashboard.kpis data; CSS picks which one shows).
   ========================================================================== */
/* .erp-kpis carries .m-hide-mobile (erp/dashboard/_kpis.html) so it's
   already hidden <=720px by the generic rule above — this just shows its
   mobile replacement in the gap that leaves. */
.m-kpi-list { display: none; }
@media (max-width: 720px) {
  .m-kpi-list { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-5); }
}
.m-kpi-card {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);
  padding: var(--space-4); box-shadow: var(--elev-1); text-decoration: none; color: inherit;
}
.m-kpi-card--status-warning { border-inline-start: 3px solid var(--warn); }
.m-kpi-card--status-negative { border-inline-start: 3px solid var(--crit); }
.m-kpi-card__label { display: flex; align-items: center; gap: 5px; font-size: var(--text-sm); color: var(--text-muted); font-weight: 600; }
.m-kpi-card__foot { margin-top: var(--space-1); font-size: var(--text-xs); color: var(--text-subtle); display: flex; gap: var(--space-2); }
.m-kpi-card__value { flex-shrink: 0; font-family: var(--font-display); font-size: var(--text-2xl); font-weight: 700; line-height: 1; }
.m-kpi-card__value--warn { color: var(--warn-fg); }
.m-kpi-card__value--crit { color: var(--crit-fg); }

/* Home's "at a glance" strip (tenants/dashboard.html) — reuses .m-kpi-card
   for the card look, but as a row that wraps rather than a mobile-only
   stacked list: this is the first screen after login on every device, not
   a desktop table's mobile fallback, so it's shown at every breakpoint. */
.home-glance { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-5); }
.home-glance .m-kpi-card { flex: 1 1 200px; }

/* ==========================================================================
   Wizard step progress (Sales Order create, mobile only — desktop keeps
   today's tab row). Dots reflect which [data-tab-target] is active;
   Next/Back just click the neighbouring existing tab button, so the whole
   thing rides on shell.js's initTabs() with no parallel state machine.
   ========================================================================== */
.m-wizard { display: none; }
@media (max-width: 720px) {
  .m-wizard { display: block; }
  .m-wizard__steps { display: flex; align-items: center; justify-content: center; gap: var(--space-2); margin-bottom: var(--space-4); }
  .m-wizard__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--line-strong); }
  .m-wizard__dot.is-active { background: var(--brand); width: 20px; border-radius: var(--radius-pill); }
  .m-wizard__dot.is-done { background: var(--brand-tint-strong); }
  .m-wizard__nav { display: flex; gap: var(--space-3); margin-top: var(--space-5); position: sticky; bottom: 0; padding: var(--space-3) 0; background: var(--canvas); }
  .m-wizard__nav .btn { flex: 1; }
  /* base.css's .btn sets display:inline-flex unscoped, which (author
     normal-importance beats UA normal-importance regardless of
     specificity — the same class of bug already documented for
     .erp-modal[open] in shell.css) wins over the UA [hidden] rule, so
     Back/Next/Save would all show at once without this. */
  .m-wizard__nav .btn[hidden] { display: none; }
}

/* ==========================================================================
   Floating action button — used sparingly (per-page opt-in), e.g. the
   Sales Order list's "New order".
   ========================================================================== */
.m-fab { display: none; }
@media (max-width: 720px) {
  .m-fab {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
    position: fixed; inset-inline-end: var(--space-5); bottom: calc(var(--bottom-nav-h) + var(--space-4) + env(safe-area-inset-bottom));
    z-index: var(--z-popover); height: 52px; padding: 0 var(--space-5);
    border-radius: var(--radius-pill); border: none;
    background: var(--brand); color: var(--on-brand); box-shadow: var(--elev-3);
    font-weight: 600; font-size: var(--text-base); text-decoration: none;
  }
  .m-fab:active { background: var(--brand-strong); }
}

/* ==========================================================================
   Bottom navigation (erp/_bottom_nav.html) — the handful of destinations
   reached constantly (Home, Search, Approvals, Alerts, the drawer for
   everything else), so opening the full module-tree drawer isn't the only
   way to get anywhere on a touch-width screen. Same breakpoint as the
   sidebar-becomes-a-drawer cutover in shell.css (<=900px) — wherever the
   persistent sidebar is gone, this replaces it as the fast path. Search
   and Menu reuse shell.js's existing [data-search-open]/[data-drawer-toggle]
   delegated listeners (they bind every matching element at load, header
   button or this one) — no new JS.
   ========================================================================== */
:root { --bottom-nav-h: 58px; }
.m-bottom-nav { display: none; }
@media (max-width: 900px) {
  .m-bottom-nav {
    display: flex; position: fixed; inset-inline-start: 0; inset-inline-end: 0; bottom: 0; z-index: var(--z-header);
    height: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    background: var(--surface); border-top: 1px solid var(--line); box-shadow: var(--elev-2);
  }
  .m-bottom-nav__item {
    flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    border: none; background: none; padding: 0; margin: 0;
    font: inherit; font-size: var(--text-2xs); font-weight: 600; color: var(--text-muted);
    text-decoration: none; cursor: pointer;
  }
  /* The Sign out item is a <button> inside a <form> (it's a POST, not a
     link) — display:contents drops the form from the box tree so the
     button underneath becomes the actual flex child and lines up with
     every other item instead of sitting in an oversized wrapper. */
  .m-bottom-nav__form { display: contents; }
  .m-bottom-nav__item.is-active { color: var(--brand); }
  .m-bottom-nav__item:active { background: var(--surface-sunken); }
  .m-bottom-nav__icon-wrap { position: relative; display: inline-flex; }
  .m-bottom-nav__count {
    position: absolute; top: -4px; inset-inline-end: -8px; min-width: 15px; height: 15px; padding: 0 3px;
    border-radius: var(--radius-pill); background: var(--crit); color: #fff;
    font-size: 9px; font-weight: 700; line-height: 15px; text-align: center;
  }
  /* Reserve room at the bottom of every scrollable page so content and
     the sticky wizard nav (.m-wizard__nav) never end up underneath the
     bar. Supersedes the plainer safe-area-only padding mobile.css sets
     elsewhere below 720px. */
  .erp-content { padding-bottom: calc(var(--bottom-nav-h) + var(--space-5) + env(safe-area-inset-bottom)); }
  .m-wizard__nav { bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom)); }
}

/* ==========================================================================
   Sticky action bar — for a long single-page form (Quality's inspection
   execute page is the first user: a dense multi-characteristic/multi-
   sample entry grid where "Save" can otherwise be a long scroll away).
   Same markup at every breakpoint; only the position/background changes
   <=720px, same "restyle, don't duplicate" approach as everything else
   in this file. Reusable — any long form's action row can opt in.
   ========================================================================== */
@media (max-width: 720px) {
  .m-sticky-actions {
    position: sticky; bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom));
    z-index: 5; margin-top: var(--space-4); padding: var(--space-3) 0;
    background: var(--canvas); border-top: 1px solid var(--line);
  }
  .m-sticky-actions .btn { flex: 1; min-width: 0; }
}

/* ==========================================================================
   Tab row — the same .tab-row used for both the client-side [data-tabs]
   form sections and the server-rendered ?tab= detail-page sections. Turn
   it into a comfortably-tappable, snapping horizontal chip strip below
   the breakpoint instead of a cramped desktop underline-tab row.
   ========================================================================== */
@media (max-width: 720px) {
  .tab-row { gap: var(--space-2); border-bottom: none; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; margin-bottom: var(--space-4); }
  .tab-row a, .tab-row button {
    scroll-snap-align: start; flex-shrink: 0;
    padding: var(--space-2) var(--space-4); border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
    margin-bottom: 0; min-height: 38px;
  }
  .tab-row a.is-active, .tab-row button.is-active { background: var(--brand); color: var(--on-brand); border-color: var(--brand); }
}

/* ==========================================================================
   Misc small-viewport polish
   ========================================================================== */
@media (max-width: 720px) {
  /* comfortable minimum tap target everywhere a real <button>/<a class=btn> is used */
  .btn { min-height: 44px; }
  .btn-sm { min-height: 40px; }
  .field input, .field textarea, .field select { min-height: 44px; }
  /* .erp-content's bottom clearance is set by the bottom-nav block above
     (<=900px, so it already covers this <=720px range) — no separate
     declaration here, so there's only ever one value to reconcile. */
  .erp-header { padding-inline-start: max(var(--space-4), env(safe-area-inset-left)); padding-inline-end: max(var(--space-4), env(safe-area-inset-right)); }
  /* chart captions/legends (components.css keeps them on one line) wrap
     under the title instead of running past the panel edge */
  .chart-panel__head { flex-wrap: wrap; }
  .chart-panel__head span { white-space: normal; }
  .auth-page__body { padding-bottom: max(var(--space-8), env(safe-area-inset-bottom)); }
}

/* ==========================================================================
   Phone policy — consult, don't enter (erp/phone.py, ESM_MOBILE_ANALYSIS.md)

   <html class="is-phone"> is set by the head script in erp/shell.html from
   `(max-width: 720px), (pointer: coarse) and (max-height: 500px)` — a
   phone in either orientation, never a tablet. On a phone ESM shows
   indicators, alerts and upcoming events; creating or editing anything is
   tablet/desktop only. Approving or refusing is the one action kept.
   body.phone-exempt (e-commerce portal, messaging, own profile) opts out.

   1. Gated pages (body.has-phone-gate — forms, settings, statements):
      the page content is replaced by the T7 notice (erp/_phone_gate.html).
   2. Everywhere else: create/edit entry points and POST actions are
      hidden, except approval forms (action URL containing approve/reject/
      decide — a quotation "reject" is a sales action, not an approval)
      and any form explicitly marked [data-phone-allowed] (sign-out).
   ========================================================================== */
.m-phone-gate { display: none; }
html.is-phone body.has-phone-gate:not(.phone-exempt) .erp-content > :not(.m-phone-gate) { display: none !important; }
html.is-phone body.has-phone-gate:not(.phone-exempt) .erp-crumbs__actions,
html.is-phone body.has-phone-gate:not(.phone-exempt) .erp-crumbs__actions-mobile { display: none !important; }
html.is-phone body.has-phone-gate:not(.phone-exempt) .m-phone-gate {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
  max-width: 340px; margin: var(--space-8) auto 0; text-align: center;
}
.m-phone-gate__icon {
  width: 64px; height: 64px; border-radius: var(--radius-lg);
  display: flex; align-items: center; justify-content: center;
  background: var(--brand-tint); color: var(--brand);
}
.m-phone-gate__title { margin: 0; font-family: var(--font-display); font-size: var(--text-xl); line-height: var(--leading-tight); }
.m-phone-gate__text { margin: 0; color: var(--text-muted); font-size: var(--text-base); line-height: var(--leading-normal); }
.m-phone-gate__actions { display: flex; flex-direction: column; gap: var(--space-3); width: 100%; margin-top: var(--space-2); }
.m-phone-gate__actions .btn { width: 100%; justify-content: center; }

/* 2a. entry points into create/edit pages */
html.is-phone body:not(.phone-exempt) :is(.erp-content, .erp-crumbs) :is(
  a[href*="/new/"], a[href*="/edit/"], a[href*="/create/"], a[href*="/add/"], a[href*="/import/"],
  a[href*="/email/"], a[href*="/send/"], a[href$="/new"], [data-modal-form]
),
html.is-phone body:not(.phone-exempt) .m-fab,
html.is-phone body:not(.phone-exempt) :is(.erp-crumbs__actions-mobile, .obj-header__actions-mobile) .m-more-menu { display: none !important; }

/* 2b. POST actions — whole small action forms go; a POST form that wraps a
   listing (bulk-selection tables) keeps its content and only loses its
   submit controls. */
html.is-phone body:not(.phone-exempt) :is(.erp-content, .erp-crumbs)
  form[method="post" i]:not([data-phone-allowed]):not([action*="approve"]):not([action*="reject"]):not([action*="decide"]):not(:has(table, .m-list)),
html.is-phone body:not(.phone-exempt) :is(.erp-content, .erp-crumbs)
  form[method="post" i][action*="/quotations/"][action*="reject"] { display: none !important; }
html.is-phone body:not(.phone-exempt) .erp-content
  form[method="post" i]:has(table, .m-list):not([data-phone-allowed]):not([action*="approve"]):not([action*="reject"]):not([action*="decide"])
  :is(button[type="submit"], button:not([type]), input[type="submit"]) { display: none !important; }

/* Sign-out lives in the drawer on phones and tablets (the header that
   carries it on desktop is hidden <=900px). */
.erp-sidebar__account { display: none; }
/* So does the modules launcher: the drawer lists the other modules. */
.erp-sidebar__modules-drawer { display: none; }
@media (max-width: 900px) { .erp-sidebar__modules-drawer { display: block; } }
@media (max-width: 900px) {
  .erp-sidebar__account {
    display: flex; align-items: center; gap: var(--space-3);
    margin-top: auto; padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom));
    border-top: 1px solid var(--on-dark-line);
    color: var(--on-dark);
  }
  .erp-sidebar__account-name { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .erp-sidebar__account form { margin: 0; }
  .erp-sidebar__logout {
    display: inline-flex; align-items: center; gap: var(--space-2); min-height: 40px; padding: 0 var(--space-3);
    border: 1px solid var(--on-dark-line); border-radius: var(--radius-sm); background: var(--on-dark-fill);
    color: var(--on-dark); font: inherit; font-size: var(--text-sm); font-weight: 600; cursor: pointer;
  }
  .erp-sidebar__logout:active { background: var(--on-dark-fill-strong); }
}

/* ==========================================================================
   Phone list rows (T5) — hand-written tables (the ~160 templates that
   don't use the shared card list). mobile.js marks each data row's title
   cell, its empty cells and its other cells; on a phone the row reads
   « TITLE / a · b · c » instead of one « label: value » line per column.
   (Shared card lists drop empty meta fields in erp/_data_table_card.html.)
   ========================================================================== */
html.is-phone table.m-stacked:not(.line-items-table) tr:has(> .m-cell-title) {
  display: flex; flex-wrap: wrap; align-items: baseline; row-gap: 2px; column-gap: 0;
  padding: var(--space-3) 0;
}
html.is-phone table.m-stacked:not(.line-items-table) td.m-cell-title,
html.is-phone table.m-stacked:not(.line-items-table) td.m-cell-meta {
  display: inline; padding: 0; border: 0; text-align: start;
}
html.is-phone table.m-stacked td.m-cell-title {
  flex-basis: 100%; font-size: var(--text-base); font-weight: 600; color: var(--text);
}
html.is-phone table.m-stacked td.m-cell-meta { font-size: var(--text-xs); color: var(--text-muted); }
html.is-phone table.m-stacked td.m-cell-title::before,
html.is-phone table.m-stacked td.m-cell-meta::before { content: none; }
html.is-phone table.m-stacked td.m-cell-meta ~ td.m-cell-meta::before { content: "·"; margin: 0 6px; color: var(--text-subtle); }
html.is-phone table.m-stacked td.m-cell-empty { display: none; }
