/* ═══════════════════════════════════════════════
   Aarth Accounting — Tabler Mobile UI
   app.css  |  OpenCart 4 STOREFRONT copy of the admin component vocabulary
   ═══════════════════════════════════════════════

   STOREFRONT NOTE (Twisti, 2026-09-03). This file is the admin
   admin/view/javascript/native/css/app.css copied verbatim so that the
   storefront (catalog/view/native/) shares ONE class vocabulary with the admin
   re-skin: tokens (--app-*), .card-android, .badge-android, .chip, .list-item,
   .fi-group form fields, .bottom-sheet / .sheet-overlay, .toast-android, etc.
   Page authors may use any of those classes on storefront screens.

   Deliberate divergences from the admin file (the ONLY edits made here):
     1. The admin-shell rules that target the bare ids #content, #column-left
        and #column-right are scoped to `.app-shell` (the admin root). The
        storefront root is `.store-shell` (see store.css), whose #content is an
        ordinary Bootstrap grid column and whose #column-left / #column-right
        are real module sidebars — the admin rules (fixed app-bar padding,
        100vh min-height, hiding #column-right, display:contents on
        #column-left) would break the storefront grid.
   Everything else is byte-identical to admin. The storefront SHELL itself
   (top bar, drawer, category nav, footer, cookie bar) lives in store.css,
   loaded after this file by catalog/view/native/common/header.twig.
   ═══════════════════════════════════════════════ */

/* ── Variables ── */
:root {
  --app-primary:      #1a56db;
  --app-purple:       #7c3aed;
  --app-grad:         linear-gradient(135deg, #1a56db 0%, #2563eb 60%, #7c3aed 100%);
  --app-bg:           #f1f5f9;
  --app-card:         #ffffff;
  --app-border:       #e2e8f0;
  --app-text:         #0f172a;
  --app-muted:        #64748b;
  /* One native codebase for all devices (owner rule, 2026-08-14): every
     fixed/centered shell element sizes off this token instead of a hard
     430px. Stepped up per plan breakpoints in the media block at EOF. */
  --shell-w:          430px;
  /* D14 (plan §8.11.3): pinned-sidebar width. 0 = no pinned sidebar (mobile +
     tablet keep the overlay). Becomes 280px only at >=1200px — every centered
     shell rule below uses the auto-margin inset pattern
     (left: var(--nav-w); right: 0; margin-inline: auto) so with --nav-w:0 the
     geometry is byte-identical to the old translateX(-50%) centering. */
  --nav-w:            0px;
  --app-bar-h:        60px;
  --app-bottom-nav-h: 64px;
  --app-radius:       14px;
  --app-shadow:       0 1px 6px rgba(0,0,0,0.08);
}

/* ── Reset & Base ── */
*, *::before, *::after { box-sizing: border-box; }
html, body {
  height: 100%; margin: 0; padding: 0;
  background: var(--app-bg);
  font-family: -apple-system, 'Inter', Roboto, 'Segoe UI', sans-serif;
  font-size: 14px; color: var(--app-text);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}
a { text-decoration: none; color: inherit; }
/* uix.min.css (vendored Tabler) ships `a:hover { text-decoration: underline }`,
   which surfaces as an underline flash on click/tap of link-styled buttons and
   menu items. The pseudo-class outranks the bare `a` rule above, so it must be
   overridden per-state. The native UI has no underlined inline text links by
   design (links are buttons, menu rows, chips), so a global kill is safe. */
a:hover, a:focus, a:active { text-decoration: none; }
button { font-family: inherit; }

/* suppress OC default layout elements */
/* storefront: scoped to .app-shell (see header note) */
.app-shell #column-right, .app-shell .breadcrumb-wrapper { display: none !important; }

/* ── App Shell ── */
.app-shell {
  position: relative;
  max-width: var(--shell-w);
  margin: 0 auto;
  min-height: 100vh;
  background: var(--app-bg);
  overflow-x: hidden;
}

/* ── App Bar (base — shared by the global bar and form .ord-app-bar) ── */
.app-bar {
  /* D14 §8.11.3 auto-margin inset pattern: centers in the band between the
     pinned sidebar (--nav-w, 0 below 1200px) and the viewport right edge. */
  position: fixed; top: 0; left: var(--nav-w); right: 0;
  margin-inline: auto;
  width: auto; max-width: var(--shell-w);
  height: var(--app-bar-h);
  padding: 0 8px;
  padding-top: env(safe-area-inset-top, 0px);
  background: var(--app-grad);
  display: flex; align-items: center; gap: 2px;
  z-index: 30;
  box-shadow: 0 2px 16px rgba(26,86,219,0.28);
}
.app-bar-icon {
  width: 40px; height: 40px; border-radius: 50%;
  background: rgba(255,255,255,0.15); border: none;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 20px; cursor: pointer;
  flex-shrink: 0; text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.app-bar-icon:hover, .app-bar-icon:active {
  background: rgba(255,255,255,0.25); color: #fff;
}
.app-bar-body { flex: 1; padding: 0 4px; min-width: 0; }
.app-bar-title {
  font-size: 16px; font-weight: 700; color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.app-bar-subtitle { font-size: 11px; color: rgba(255,255,255,0.8); margin-top: 1px; }
.avatar-android {
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(255,255,255,0.25);
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; color: #fff;
  flex-shrink: 0; border: 2px solid rgba(255,255,255,0.35);
  text-decoration: none; cursor: pointer;
}
.notif-dot {
  position: absolute; top: 8px; right: 8px;
  width: 8px; height: 8px; background: #f03e3e;
  border-radius: 50%; border: 2px solid #1a56db;
}
.notif-badge {
  position: absolute; top: 3px; right: 3px;
  min-width: 16px; height: 16px; padding: 0 4px;
  background: #f03e3e; color: #fff; font-size: 10px; font-weight: 800;
  line-height: 1; border-radius: 9px; border: 2px solid #fff;
  display: flex; align-items: center; justify-content: center;
}

/* ── Global app bar → white (single row, title beside menu). Forms keep the gradient base above. ── */
#appBar.app-bar {
  background: #fff;
  border-bottom: 1px solid var(--app-border);
  box-shadow: 0 2px 12px rgba(15,23,42,0.06);
}
#appBar .app-bar-icon {
  border-radius: 11px; background: transparent; color: #475569;
}
#appBar .app-bar-icon:hover, #appBar .app-bar-icon:active {
  background: #f1f5f9; color: #0f172a;
}
#appBar .app-bar-title { color: #0f172a; }
#appBar .app-bar-subtitle { color: var(--app-muted); }
#appBar .avatar-android { background: var(--app-grad); border: none; }
/* Avatar photo (oc_user.image, resized 45x45 by common/header.php) fills the circle. */
.avatar-android img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }
#appBar .notif-dot { border-color: #fff; }

/* ── AI Assistance corner slot (admin/view/native/common/header.twig) ──
   Scoped by data-testid so ONLY the AI link gets the treatment: the Profile
   fallback in the same slot and every other .avatar-android (e.g. the profile
   menu in column_left.twig) keep the plain --app-grad circle above.
   Visual language is the shell's own: a 135deg gradient plus a coloured glow,
   the same recipe as .btn-auth / the FAB — only shifted violet→cyan so it reads
   as "AI" rather than as another primary-blue action. The white ring lifts the
   circle off the white app bar; the glow marks it as tappable. */
#appBar .avatar-android[data-testid="app-bar-ai-assistant"] {
  background: linear-gradient(135deg, #7c3aed 0%, #4f46e5 45%, #06b6d4 100%);
  box-shadow: 0 0 0 2px #fff, 0 3px 12px rgba(124,58,237,0.45);
  transition: box-shadow .18s ease, transform .18s ease;
}
#appBar .avatar-android[data-testid="app-bar-ai-assistant"] i {
  font-size: 18px;              /* sparkles reads thin at the inherited 13px */
  line-height: 1;
  filter: drop-shadow(0 1px 2px rgba(15,23,42,0.28));
}
#appBar .avatar-android[data-testid="app-bar-ai-assistant"]:hover,
#appBar .avatar-android[data-testid="app-bar-ai-assistant"]:focus-visible {
  box-shadow: 0 0 0 2px #fff, 0 4px 16px rgba(124,58,237,0.60);
}
#appBar .avatar-android[data-testid="app-bar-ai-assistant"]:active {
  transform: scale(0.94);       /* matches the press feedback used elsewhere in the shell */
}

/* ── Page Content ── */
/* storefront: #content scoped to .app-shell (see header note) */
.app-shell #content,
.app-content {
  padding-top: calc(var(--app-bar-h) + 10px);
  padding-bottom: calc(var(--app-bottom-nav-h) + 16px);
  padding-left: 0; padding-right: 0;
  min-height: 100vh;
}
.app-shell #content > .container-fluid { padding: 0 12px; }

/* ── Bottom Navigation ── */
.bottom-nav {
  /* D14 auto-margin inset pattern (see .app-bar). Retired at >=1200px. */
  position: fixed; bottom: 0; left: var(--nav-w); right: 0;
  margin-inline: auto;
  width: auto; max-width: var(--shell-w);
  height: var(--app-bottom-nav-h);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: #fff;
  border-top: 1px solid var(--app-border);
  display: flex; align-items: stretch;
  z-index: 30;
  box-shadow: 0 -2px 12px rgba(0,0,0,0.06);
}
.bottom-nav-item {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 3px; color: var(--app-muted);
  font-size: 10px; font-weight: 600;
  cursor: pointer; text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.15s;
}
.bottom-nav-item .bn-ic {
  width: 38px; height: 38px; display: flex;
  align-items: center; justify-content: center;
  border-radius: 12px; font-size: 21px;
  color: var(--c, var(--app-muted)); transition: 0.2s;
}
.bottom-nav-item.active { color: var(--c, var(--app-primary)); }
.bottom-nav-item.active .bn-ic {
  background: var(--c, var(--app-primary)); color: #fff;
  box-shadow: 0 4px 12px rgba(0,0,0,0.18);
}

/* Raised center action (Purchase) */
.bottom-nav-center {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 0;
  color: var(--app-muted); font-size: 10px; font-weight: 600;
  cursor: pointer; text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.bottom-nav-center .bn-center-btn {
  width: 52px; height: 52px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 25px; color: #fff;
  background: linear-gradient(135deg, #fb923c, #ea580c);
  box-shadow: 0 7px 18px rgba(234,88,12,0.5);
  border: 4px solid #fff; transform: translateY(-16px);
}
.bottom-nav-center .bn-center-lbl { transform: translateY(-12px); }

/* ── FAB ── */
.fab {
  position: fixed; bottom: calc(var(--app-bottom-nav-h) + 16px);
  right: 16px; width: 56px; height: 56px;
  background: var(--app-grad); color: #fff;
  border: none; border-radius: 18px;
  display: flex; align-items: center; justify-content: center;
  font-size: 26px; cursor: pointer;
  box-shadow: 0 4px 18px rgba(26,86,219,0.4);
  z-index: 28; -webkit-tap-highlight-color: transparent;
}
.fab:active { transform: scale(0.94); }

/* ── Summary Banner ── */
.summary-banner {
  background: var(--app-grad);
  border-radius: var(--app-radius);
  padding: 20px; margin: 0 12px 14px; color: #fff;
}
.summary-banner .label  { font-size: 11px; opacity: 0.82; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
.summary-banner .amount { font-size: 32px; font-weight: 800; margin: 6px 0 4px; letter-spacing: -1px; }
.summary-banner .sub    { font-size: 11px; opacity: 0.72; margin-bottom: 16px; }
.summary-banner .row    { display: flex; gap: 0; }
.summary-banner .col    { flex: 1; text-align: center; }
.summary-banner .col + .col { border-left: 1px solid rgba(255,255,255,0.2); }
.summary-banner .val    { font-size: 16px; font-weight: 800; }
.summary-banner .lbl    { font-size: 10px; opacity: 0.78; margin-top: 2px; }

/* ── Stat Grid ── */
.stat-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 10px; margin: 0 12px 16px;
}
.stat-card {
  background: #fff; border-radius: var(--app-radius);
  padding: 14px; box-shadow: var(--app-shadow);
}
.stat-card-icon {
  width: 38px; height: 38px; border-radius: 11px;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; margin-bottom: 10px;
}
.stat-card-value  { font-size: 20px; font-weight: 800; color: var(--app-text); }
.stat-card-label  { font-size: 11px; color: var(--app-muted); font-weight: 500; margin-top: 2px; }
.stat-card-trend  { font-size: 11px; font-weight: 600; margin-top: 6px; }
.text-income { color: #16a34a; }
.text-expense{ color: #dc2626; }
.mini-progress { height: 3px; background: #f1f5f9; border-radius: 4px; margin-top: 8px; }
.mini-progress-bar { height: 3px; border-radius: 4px; }
.bg-blue-soft   { background: #dbeafe; color: #1d4ed8; }
.bg-yellow-soft { background: #fef9c3; color: #ca8a04; }
.bg-red-soft    { background: #fee2e2; color: #dc2626; }
.bg-green-soft  { background: #dcfce7; color: #16a34a; }

/* ── Section Heading ── */
.section-heading {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 14px; margin-bottom: 8px; margin-top: 4px;
}
.section-heading h3 { font-size: 13px; font-weight: 700; color: var(--app-text); margin: 0; }
.section-heading a  { font-size: 12px; color: var(--app-primary); font-weight: 600; }

/* ── Cards ── */
.card-android {
  background: #fff; border-radius: var(--app-radius);
  box-shadow: var(--app-shadow); margin: 0 12px 12px; overflow: hidden;
}

/* ── List Items ── */
.list-item {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px; border-bottom: 1px solid #f8fafc;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.list-item:last-child { border-bottom: none; }
.list-item:active { background: #f8fafc; }
.list-item-avatar {
  width: 40px; height: 40px; border-radius: 13px;
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 700; color: #fff; flex-shrink: 0;
}
.list-item-body { flex: 1; min-width: 0; }
.list-item-title { font-size: 14px; font-weight: 600; color: var(--app-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-item-sub   { font-size: 11px; color: var(--app-muted); margin-top: 2px; }
.list-item-right { text-align: right; flex-shrink: 0; }
.list-item-amount{ font-size: 15px; font-weight: 700; }
.list-item-status{ margin-top: 3px; }

/* ── Badges ── */
.badge-android {
  display: inline-block; padding: 2px 9px;
  border-radius: 20px; font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.3px;
}
.badge-paid    { background: #dcfce7; color: #16a34a; }
.badge-pending { background: #fef9c3; color: #ca8a04; }
.badge-overdue { background: #fee2e2; color: #dc2626; }
.badge-draft   { background: #f1f5f9; color: #64748b; }
.badge-active  { background: #dbeafe; color: #1a56db; }
.badge-inactive{ background: #f1f5f9; color: #64748b; }

/* ── Chip Row ── */
.chip-row { display: flex; gap: 8px; padding: 4px 12px 12px; overflow-x: auto; scrollbar-width: none; }
.chip-row::-webkit-scrollbar { display: none; }
.chip {
  padding: 6px 14px; border-radius: 20px; font-size: 12px;
  font-weight: 600; white-space: nowrap; cursor: pointer; border: none;
  background: #fff; color: var(--app-muted);
  box-shadow: 0 1px 4px rgba(0,0,0,0.08);
  -webkit-tap-highlight-color: transparent;
}
.chip.active { background: var(--app-primary); color: #fff; box-shadow: 0 2px 8px rgba(26,86,219,0.3); }

/* ── Search Bar ── */
.search-bar {
  display: flex; align-items: center; gap: 10px;
  background: #fff; border-radius: 12px;
  padding: 10px 14px; margin: 0 12px 12px;
  box-shadow: var(--app-shadow);
}
.search-bar input {
  flex: 1; border: none; outline: none;
  font-size: 14px; color: var(--app-text); background: transparent;
}
.search-bar input::placeholder { color: #94a3b8; }
.search-bar i { color: #94a3b8; font-size: 18px; }

/* ── Form Styles (floating inputs) ── */
.form-area { padding: 8px 14px 0; }
.section-divider {
  display: flex; align-items: center; gap: 10px;
  margin: 16px 0 12px;
}
.section-divider-icon {
  width: 32px; height: 32px; border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  font-size: 16px; flex-shrink: 0;
}
.section-divider-line { flex: 1; height: 1px; background: var(--app-border); }

.fi-group { position: relative; margin-bottom: 14px; }
.fi-group .input-group-text {
  background: #f8fafc; border-color: var(--app-border); border-right: none;
  color: var(--app-muted); font-size: 18px; border-radius: 12px 0 0 12px;
}
.fi-group .form-floating { flex: 1; }
.fi-group .form-floating .form-control,
.fi-group .form-floating .form-select {
  border-left: none; border-color: var(--app-border);
  border-radius: 0 12px 12px 0;
  background: #fff; font-size: 14px; color: var(--app-text);
  box-shadow: none; padding-left: 14px;
}
.fi-group .form-floating .form-control:focus,
.fi-group .form-floating .form-select:focus {
  border-color: var(--app-primary);
  box-shadow: 0 0 0 3px rgba(26,86,219,0.10);
}
.fi-group:focus-within .input-group-text {
  border-color: var(--app-primary); color: var(--app-primary);
}
.fi-group .form-floating label { padding-left: 14px; color: #94a3b8; font-size: 13px; }
.form-floating .form-control,
.form-floating .form-select {
  border-radius: 12px; border-color: var(--app-border);
  background: #fff; font-size: 14px; color: var(--app-text); box-shadow: none;
}
.form-floating .form-control:focus,
.form-floating .form-select:focus {
  border-color: var(--app-primary);
  box-shadow: 0 0 0 3px rgba(26,86,219,0.10);
}
textarea.form-control { min-height: 90px; resize: none; }
select.form-control, select.form-select { appearance: none; -webkit-appearance: none; }
.select-chevron { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); color: #94a3b8; pointer-events: none; font-size: 16px; }

/* ── Balance Pill ── */
.balance-pill {
  display: none; align-items: center; justify-content: space-between;
  margin: -8px 0 14px; padding: 8px 14px 8px 50px;
  background: linear-gradient(135deg, #f0fdf4, #dcfce7);
  border: 1px solid #bbf7d0; border-top: none;
  border-radius: 0 0 12px 12px; animation: bpIn 0.22s;
}
.balance-pill.show { display: flex; }
.balance-pill.low  { background: linear-gradient(135deg, #fef2f2, #fee2e2); border-color: #fca5a5; }
@keyframes bpIn { from { opacity:0; transform:translateY(-6px); } to { opacity:1; transform:translateY(0); } }
.bp-label  { font-size: 11px; color: #16a34a; font-weight: 600; display: flex; align-items: center; gap: 4px; }
.bp-label.low { color: #dc2626; }
.bp-amount { font-size: 14px; font-weight: 800; color: #16a34a; }
.bp-amount.low { color: #dc2626; }

/* ── Hide bottom-nav on form pages that have a save-bar ── */
.app-shell:has(.save-bar) .bottom-nav { display: none; }
/* Must ALSO target #content: the base rule at the top of this file is
   "#content, .app-content" and the ID selector (1-0-0) outranks any
   class-only rule here — without the #content variant this padding never
   applies and the save-bar covers the last form fields (visible at
   >=1200px where --app-bottom-nav-h collapses to 0). 68px bar + 24px
   breathing room. */
.app-shell:has(.save-bar) #content,
.app-shell:has(.save-bar) .app-content {
  padding-bottom: calc(68px + env(safe-area-inset-bottom, 0px) + 24px);
}

/* Sub-pages (forms & views — any .method route) are immersive: no bottom menu.
   Pages with a .save-bar keep their larger padding via the higher-specificity rule above. */
.app-subpage .bottom-nav { display: none !important; }
.app-subpage .app-content { padding-bottom: 24px; }

/* ── Save / Action Bar ── */
.save-bar {
  /* D14 auto-margin inset pattern (see .app-bar) — aligns with the form it saves. */
  position: fixed; bottom: 0; left: var(--nav-w); right: 0; margin-inline: auto;
  width: auto; max-width: var(--shell-w);
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
  background: rgba(255,255,255,0.96); backdrop-filter: blur(10px);
  border-top: 1px solid var(--app-border);
  display: flex; gap: 10px; z-index: 31;
}
.btn-save {
  flex: 1; height: 48px; background: var(--app-grad); color: #fff;
  border: none; border-radius: 12px; font-size: 15px; font-weight: 700;
  cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px;
  box-shadow: 0 4px 14px rgba(26,86,219,0.35);
}
.btn-save:active { opacity: 0.88; }
.btn-cancel-bar {
  padding: 0 18px; height: 48px; background: #f1f5f9; color: var(--app-muted);
  border: none; border-radius: 12px; font-size: 14px; font-weight: 600;
  cursor: pointer; flex-shrink: 0;
}
.btn-cancel-bar:active { background: var(--app-border); }

/* ── Bottom Sheet ── */
.sheet-overlay {
  display: none; position: fixed; inset: 0;
  background: rgba(0,0,0,0.46); z-index: 40;
}
.sheet-overlay.show { display: block; }
.bottom-sheet {
  /* D14 auto-margin inset pattern (see .app-bar). Also frees transform for the
     slide animation — translateY only, no more coupled -50% centering. */
  position: fixed; bottom: 0; left: var(--nav-w); right: 0; margin-inline: auto;
  transform: translateY(100%);
  width: auto; max-width: var(--shell-w); background: #fff;
  border-radius: 20px 20px 0 0; z-index: 50;
  transition: transform 0.3s cubic-bezier(0.4,0,0.2,1);
  max-height: 92dvh; display: flex; flex-direction: column;
}
.bottom-sheet.show { transform: translateY(0); }
.sheet-handle {
  width: 40px; height: 4px; background: var(--app-border);
  border-radius: 4px; margin: 12px auto 0; flex-shrink: 0;
}
.sheet-hdr {
  display: flex; align-items: center; padding: 10px 18px 12px;
  border-bottom: 1px solid #f1f5f9; flex-shrink: 0; gap: 8px;
}
.sheet-hdr-title { flex: 1; font-size: 16px; font-weight: 700; }
.sheet-scroll { overflow-y: auto; flex: 1; -webkit-overflow-scrolling: touch; }
.sheet-footer {
  padding: 10px 18px calc(10px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid #f1f5f9; display: flex; gap: 10px; flex-shrink: 0;
}
.btn-sheet-primary {
  flex: 1; padding: 13px; background: var(--app-grad); color: #fff;
  border: none; border-radius: 10px; font-size: 15px; font-weight: 600;
  cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 6px;
}
.btn-sheet-secondary {
  padding: 13px 18px; background: #f1f5f9; color: #475569;
  border: none; border-radius: 10px; font-size: 15px; font-weight: 600; cursor: pointer;
}

/* ── More Menu Panel ── */
.more-panel {
  position: fixed; inset: 0; z-index: 35;
  background: var(--app-bg);
  /* off-canvas: 50vw+50% clears the right edge even when the viewport is wider
     than the shell (translateX(100%) left a strip visible on desktop);
     on phones shell width == viewport so this equals the old 100%. */
  transform: translateX(calc(50vw + 50%));
  transition: transform 0.3s cubic-bezier(0.4,0,0.2,1);
  display: flex; flex-direction: column;
  max-width: var(--shell-w); left: 50%; margin-left: calc(var(--shell-w) / -2);
}
@media (max-width: 430px) {
  .more-panel { left: 0; margin-left: 0; }
}
.more-panel.show { transform: translateX(0); }
.more-panel-bar {
  height: var(--app-bar-h); padding: 0 8px;
  padding-top: env(safe-area-inset-top, 0px);
  background: #fff; display: flex; align-items: center; gap: 2px;
  flex-shrink: 0; border-bottom: 1px solid var(--app-border);
  box-shadow: 0 2px 12px rgba(15,23,42,0.06);
}
.more-panel-bar .app-bar-icon { background: transparent; color: #475569; border-radius: 11px; }
.more-panel-bar .app-bar-icon:hover, .more-panel-bar .app-bar-icon:active { background: #f1f5f9; color: #0f172a; }
.more-panel-title { font-size: 16px; font-weight: 700; color: #0f172a; flex: 1; padding: 0 6px; }
.more-panel-scroll {
  flex: 1; overflow-y: auto; padding: 12px 12px calc(var(--app-bottom-nav-h) + 16px);
  -webkit-overflow-scrolling: touch;
}

/* ── Menu Section ── */
.menu-section { margin-bottom: 18px; }
.menu-section-title {
  font-size: 11px; font-weight: 800; color: var(--app-muted);
  text-transform: uppercase; letter-spacing: 0.7px;
  padding: 0 4px 9px; display: flex; align-items: center; gap: 7px;
}
.menu-section-title::after { content: ''; flex: 1; height: 1px; background: #e2e8f0; }
.menu-grid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 4px;
}
/* vertical card — icon on top, name below (no card background) */
.menu-card {
  position: relative; background: transparent; border-radius: 14px;
  padding: 9px 1px; display: flex; flex-direction: column;
  align-items: center; justify-content: flex-start; gap: 6px;
  text-decoration: none; cursor: pointer;
  -webkit-tap-highlight-color: transparent; min-height: 74px;
  transition: transform 0.12s;
}
.menu-card:active { transform: scale(0.92); }
.menu-card-name {
  font-size: 10px; font-weight: 600; color: var(--app-text);
  line-height: 1.2; text-align: center; width: 100%;
  white-space: normal; word-break: break-word; padding: 0 2px;
}
.menu-card-icon {
  width: 38px; height: 38px; border-radius: 11px;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; flex-shrink: 0;
}
.menu-card-badge {
  position: absolute; top: 7px; right: 9px;
  background: #f03e3e; color: #fff;
  font-size: 9px; font-weight: 700; padding: 1px 5px;
  border-radius: 8px; min-width: 16px; text-align: center;
}
.menu-card-wrap { position: relative; }

/* ── Sidebar "Quick actions" (D14 §8.11.5): the five bottom-nav shortcuts,
   rehomed at the top of the pinned sidebar. Hidden below 1200px — there the
   .bottom-nav carries the same five actions (exactly one visible per tier). ── */
.sidebar-quick {
  display: none;               /* → grid at >=1200px (media block at EOF) */
  grid-template-columns: repeat(5, 1fr);
  gap: 2px; margin-bottom: 14px; padding: 8px 4px;
  background: #fff; border-radius: 12px;
  box-shadow: 0 1px 3px rgba(15,23,42,0.08);
}
.sidebar-quick-item {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 6px 2px; border-radius: 10px; text-decoration: none;
  color: var(--app-muted); font-size: 9px; font-weight: 600; text-align: center;
  cursor: pointer;
}
.sidebar-quick-item:hover { background: #f1f5f9; }
.sidebar-quick-item .sq-ic {
  width: 30px; height: 30px; border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  font-size: 17px; background: color-mix(in srgb, var(--c, var(--app-muted)) 12%, #fff);
  color: var(--c, var(--app-muted));
}
.sidebar-quick-item.active { color: var(--c, var(--app-primary)); }
.sidebar-quick-item.active .sq-ic { background: var(--c, var(--app-primary)); color: #fff; }

/* ── Notifications Panel (slide-in, mirrors .more-panel) ── */
.notif-panel {
  position: fixed; inset: 0; z-index: 38;
  background: var(--app-bg);
  /* off-canvas: 50vw+50% clears the right edge even when the viewport is wider
     than the shell (translateX(100%) left a strip visible on desktop);
     on phones shell width == viewport so this equals the old 100%. */
  transform: translateX(calc(50vw + 50%));
  transition: transform 0.3s cubic-bezier(0.4,0,0.2,1);
  display: flex; flex-direction: column;
  max-width: var(--shell-w); left: 50%; margin-left: calc(var(--shell-w) / -2);
}
@media (max-width: 430px) {
  .notif-panel { left: 0; margin-left: 0; }
}
.notif-panel.show { transform: translateX(0); }
.notif-bar {
  height: var(--app-bar-h); padding: 0 8px;
  padding-top: env(safe-area-inset-top, 0px);
  background: #fff; display: flex; align-items: center; gap: 2px;
  flex-shrink: 0; border-bottom: 1px solid var(--app-border);
  box-shadow: 0 2px 12px rgba(15,23,42,0.06);
}
.notif-bar .app-bar-icon { background: transparent; color: #475569; border-radius: 11px; }
.notif-bar .app-bar-icon:hover, .notif-bar .app-bar-icon:active { background: #f1f5f9; color: #0f172a; }
.notif-title { font-size: 16px; font-weight: 700; color: #0f172a; flex: 1; padding: 0 6px; }
.notif-scroll {
  flex: 1; overflow-y: auto; padding: 8px 0 calc(var(--app-bottom-nav-h) + 16px);
  -webkit-overflow-scrolling: touch;
}
.notif-item {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px 16px; text-decoration: none;
  border-bottom: 1px solid #eef2f6; background: #fff;
  transition: background 0.12s; -webkit-tap-highlight-color: transparent;
}
.notif-item:active { background: #f8fafc; }
.notif-item.unread { background: #f5f8ff; }
.notif-ic {
  width: 40px; height: 40px; border-radius: 12px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; background: #e0ecff; color: #1a56db;
}
.notif-body { flex: 1; min-width: 0; }
.notif-item-title { font-size: 14px; font-weight: 600; color: #0f172a; line-height: 1.35; }
.notif-item.unread .notif-item-title { font-weight: 700; }
.notif-time { font-size: 11px; color: #94a3b8; margin-top: 3px; }
.notif-unread-dot { width: 8px; height: 8px; border-radius: 50%; background: #1a56db; flex-shrink: 0; margin-top: 6px; }
.notif-all {
  display: block; text-align: center; padding: 14px;
  font-size: 13px; font-weight: 700; color: #1a56db; text-decoration: none;
  background: #fff; border-top: 1px solid var(--app-border); flex-shrink: 0;
}
.notif-all:active { background: #f8fafc; }
.notif-empty { text-align: center; padding: 60px 20px; color: #94a3b8; font-size: 13px; }
.notif-empty i { font-size: 44px; display: block; margin-bottom: 12px; color: #cbd5e1; }

/* ── Toast ── */
.toast-android {
  /* D14 auto-margin inset pattern: intrinsic width via fit-content so auto
     margins center the pill in the content band (not the viewport). */
  position: fixed; bottom: calc(var(--app-bottom-nav-h) + 12px);
  left: var(--nav-w); right: 0; margin-inline: auto;
  width: fit-content; transform: translateY(20px);
  background: #0f172a; color: #fff; padding: 11px 20px;
  border-radius: 24px; font-size: 13px; font-weight: 500;
  opacity: 0; transition: all 0.25s; z-index: 60;
  /* Long messages (e.g. save-error text) must wrap, not clip: normal
     white-space + overflow-wrap; fit-content keeps short toasts pill-sized. */
  white-space: normal; overflow-wrap: break-word; word-break: break-word;
  text-align: center; line-height: 1.45; pointer-events: none;
  max-width: min(calc(100vw - 32px), 480px);
}
.toast-android.show { opacity: 1; transform: translateY(0); }

/* ── Print ── */
.print-page { display: none; }
@media print {
  .app-shell, .fab, .sheet-overlay, .bottom-sheet,
  .more-panel, .notif-panel, .toast-android, .save-bar, .bottom-nav { display: none !important; }
  .print-page { display: block !important; }
}

/* ── Voucher / Ledger ── */
.voucher { padding: 18px; }
.v-header { text-align: center; padding-bottom: 14px; margin-bottom: 14px; border-bottom: 1.5px dashed var(--app-border); }
.v-logo { width: 48px; height: 48px; background: var(--app-grad); border-radius: 13px; display: flex; align-items: center; justify-content: center; font-size: 22px; font-weight: 800; color: #fff; margin: 0 auto 8px; }
.v-store { font-size: 15px; font-weight: 700; color: var(--app-text); }
.v-addr  { font-size: 11px; color: var(--app-muted); margin-top: 2px; }
.v-contact { font-size: 11px; color: var(--app-muted); margin-top: 4px; }
.v-badge { display: inline-block; padding: 3px 14px; background: #dbeafe; color: var(--app-primary); border-radius: 20px; font-size: 11px; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase; margin-top: 10px; }
.v-meta { display: flex; justify-content: space-between; margin-bottom: 14px; }
.v-meta-label { font-size: 10px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.5px; }
.v-meta-val { font-size: 14px; font-weight: 700; color: var(--app-text); margin-top: 3px; }
.ledger { width: 100%; border-collapse: collapse; font-size: 12px; margin-bottom: 12px; }
.ledger th { font-size: 10px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.5px; padding: 6px 8px; border-bottom: 1.5px solid var(--app-border); text-align: left; }
.ledger th:nth-child(2), .ledger th:nth-child(3) { text-align: right; }
.ledger td { padding: 3px 8px; }
.ledger td:nth-child(2), .ledger td:nth-child(3) { text-align: right; font-weight: 600; color: var(--app-text); }
.l-group td { font-size: 10px; font-weight: 700; color: var(--app-muted); text-transform: uppercase; letter-spacing: 0.4px; padding-top: 10px; padding-bottom: 1px; }
.l-acc td:first-child { padding-left: 18px; color: var(--app-text); font-weight: 500; }
.l-total { border-top: 2px solid var(--app-text); }
.l-total td { font-size: 13px; font-weight: 800; color: var(--app-text); padding-top: 8px; padding-bottom: 4px; }
.v-words { background: #f8fafc; border: 1px solid var(--app-border); border-radius: 8px; padding: 10px 12px; font-size: 12px; color: #475569; margin-bottom: 12px; line-height: 1.5; }
.v-words strong { color: var(--app-text); }
.v-cmt-label { font-size: 10px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
.v-cmt-body  { font-size: 13px; color: #475569; line-height: 1.5; white-space: pre-wrap; }

/* ── Contextual Selection Bar ── */
.select-bar {
  /* D14 auto-margin inset pattern — must occupy exactly the .app-bar band. */
  position: fixed; top: 0; left: var(--nav-w); right: 0; margin-inline: auto;
  width: auto; max-width: var(--shell-w); height: var(--app-bar-h);
  background: #0f172a; display: none; align-items: center;
  padding: 0 12px; gap: 10px; z-index: 32;
}
.select-bar.show { display: flex; }
.select-bar-count { font-size: 15px; font-weight: 700; color: #fff; flex: 1; }
.select-bar-icon {
  width: 36px; height: 36px; border-radius: 50%;
  background: rgba(255,255,255,0.12); border: none;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 19px; cursor: pointer;
}

/* ── Bulk Action Bar ── */
.bulk-bar {
  /* D14 auto-margin inset pattern. bottom uses --app-bottom-nav-h, which is 0
     at >=1200px (§8.11.5), so it falls to the viewport bottom automatically. */
  display: none; position: fixed;
  bottom: var(--app-bottom-nav-h); left: var(--nav-w); right: 0; margin-inline: auto;
  width: auto; max-width: var(--shell-w);
  background: #1e293b; padding: 10px 14px; gap: 8px;
  z-index: 29;
}
.bulk-bar.show { display: flex; }
.bulk-bar-btn {
  flex: 1; padding: 11px; border: none; border-radius: 10px;
  font-size: 13px; font-weight: 600; cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  background: rgba(255,255,255,0.1); color: #fff;
}
.bulk-bar-btn.danger { background: rgba(220,38,38,0.85); }

/* ── Auth Pages ── */
.auth-shell { min-height:100vh; display:flex; flex-direction:column; max-width:var(--shell-w); margin:0 auto; background:var(--app-bg); }
.auth-hero { background:var(--app-grad); padding:52px 24px 36px; display:flex; flex-direction:column; align-items:center; text-align:center; }
.auth-logo { width:64px; height:64px; border-radius:20px; background:rgba(255,255,255,0.2); border:2px solid rgba(255,255,255,0.3); display:flex; align-items:center; justify-content:center; font-size:30px; font-weight:800; color:#fff; margin-bottom:12px; }
.auth-app-name { font-size:21px; font-weight:800; color:#fff; letter-spacing:-0.3px; }
.auth-app-sub  { font-size:12px; color:rgba(255,255,255,0.75); margin-top:3px; }
.auth-body { flex:1; padding:20px 16px 32px; }
.auth-card { background:#fff; border-radius:18px; padding:24px 20px; box-shadow:0 2px 16px rgba(0,0,0,0.08); margin-bottom:14px; }
.auth-card-title    { font-size:17px; font-weight:700; color:var(--app-text); margin-bottom:3px; }
.auth-card-subtitle { font-size:13px; color:var(--app-muted); margin-bottom:20px; line-height:1.5; }
.btn-auth { width:100%; height:50px; background:var(--app-grad); color:#fff; border:none; border-radius:13px; font-size:16px; font-weight:700; cursor:pointer; display:flex; align-items:center; justify-content:center; gap:8px; box-shadow:0 4px 14px rgba(26,86,219,0.30); margin-top:8px; }
.btn-auth:active { opacity:0.88; }
.auth-links { display:flex; justify-content:center; flex-wrap:wrap; gap:4px 20px; padding:6px 0; }
.auth-links a { font-size:13px; color:var(--app-primary); font-weight:600; text-decoration:none; }
.auth-alert { display:flex; align-items:flex-start; gap:10px; padding:12px 14px; border-radius:10px; margin-bottom:16px; font-size:13px; border:1px solid; line-height:1.5; }
.auth-alert-error   { background:#fef2f2; border-color:#fca5a5; color:#dc2626; }
.auth-alert-success { background:#f0fdf4; border-color:#86efac; color:#16a34a; }

/* OTP boxes */
.otp-group { display:flex; gap:10px; justify-content:center; margin:16px 0 20px; }
.otp-input { width:44px; height:54px; border:2px solid var(--app-border); border-radius:12px; text-align:center; font-size:22px; font-weight:700; color:var(--app-text); background:#fff; outline:none; caret-color:var(--app-primary); }
.otp-input:focus { border-color:var(--app-primary); box-shadow:0 0 0 3px rgba(26,86,219,0.12); }

/* Language selector */
.lang-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; padding:4px 0; }
.lang-card { background:#fff; border-radius:12px; padding:12px 14px; display:flex; align-items:center; gap:10px; box-shadow:var(--app-shadow); cursor:pointer; border:2px solid transparent; text-decoration:none; }
.lang-card.active { border-color:var(--app-primary); background:#f0f6ff; }
.lang-card:active { background:#f8fafc; }
.lang-flag { width:28px; height:20px; border-radius:3px; object-fit:cover; flex-shrink:0; }
.lang-name { font-size:13px; font-weight:600; color:var(--app-text); }
.lang-code { font-size:10px; color:var(--app-muted); font-weight:500; text-transform:uppercase; }

/* Pagination */
.oc-pagination-wrap { padding:8px 12px 4px; display:flex; flex-direction:column; gap:8px; }
.oc-page-results { font-size:12px; color:var(--app-muted); font-weight:500; text-align:center; }
.oc-page-nav { display:flex; justify-content:center; }
.oc-page-nav .pagination { flex-wrap:wrap; gap:6px; margin:0; justify-content:center; }
.oc-page-nav .page-item .page-link { border-radius:10px !important; border:1px solid var(--app-border); font-size:13px; font-weight:600; color:var(--app-muted); padding:7px 13px; min-width:38px; text-align:center; }
.oc-page-nav .page-item.active .page-link { background:var(--app-primary); border-color:var(--app-primary); color:#fff; }
.oc-page-nav .page-item.disabled .page-link { opacity:0.4; }

/* Dashboard extras */
.dash-date    { font-size:12px; color:rgba(255,255,255,0.75); margin-bottom:4px; font-weight:500; }
.dash-welcome { font-size:18px; font-weight:800; color:#fff; margin-bottom:2px; }
.quick-actions-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:10px; padding:0 12px; margin-bottom:16px; }
.quick-action { display:flex; flex-direction:column; align-items:center; gap:6px; cursor:pointer; -webkit-tap-highlight-color:transparent; }
.quick-action-icon { width:52px; height:52px; border-radius:16px; display:flex; align-items:center; justify-content:center; font-size:22px; }
.quick-action-label { font-size:11px; font-weight:600; color:var(--app-muted); text-align:center; }

/* Dashboard widget grid (Stage 4) — replaces the stock Bootstrap row/col-lg-N
   loop. Each widget seeds a width in 12-column units (defaultWidth()); the
   template translates that straight into `grid-column: span N` on the cell, so
   any future widget seeding any width keeps working with no template change.
   Below 768px every cell spans the full grid (a 4-unit tile is unreadable on a
   phone); 768-1199px tiles get a half-width floor so three 4-unit tiles do not
   shrink to slivers. `align-items:stretch` is what lets `.acc-tile{height:100%}`
   equalise tile heights within a row. */
.dash-widget-grid { display:grid; grid-template-columns:repeat(12,1fr); gap:12px; align-items:stretch; padding:0 12px 8px; }
.dash-widget-cell { min-width:0; }
@media (max-width: 767.98px) {
  .dash-widget-grid { grid-template-columns:1fr; }
  .dash-widget-cell { grid-column:1 / -1 !important; }
}
@media (min-width: 768px) and (max-width: 1199.98px) {
  .dash-widget-grid { grid-template-columns:repeat(2,1fr); }
  .dash-widget-cell { grid-column:span 1 !important; }
  .dash-widget-cell--wide { grid-column:1 / -1 !important; }
}

/* ── Utilities ── */
.text-truncate { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

/* ═══════════════════════════════════════════════
   Responsive shell — tablet & desktop breakpoints
   (plan §5: mobile <768, tablet 768, desktop 1440;
   one native codebase serves all devices)
   ═══════════════════════════════════════════════ */

/* Tablet (>=768px): widen the shell; keep the app-bar/bottom-nav pattern. */
@media (min-width: 768px) {
  :root { --shell-w: 720px; }
  /* Modal sheets should not stretch to full shell width on big screens */
  .bottom-sheet { max-width: min(var(--shell-w), 560px); }
  /* Menu cards: let the grid fill the wider panel instead of fixed 5-up */
  .menu-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
  .menu-card-name { font-size: 11px; }
}

/* ═══════════════════════════════════════════════
   Desktop (>=1200px) — D14 pinned sidebar (plan §8.11)
   The nav stops being an overlay and becomes permanent chrome on the left;
   content fills the remainder, capped at --shell-w and centered in the band.
   Below 1200px NOTHING in this block applies — mobile/tablet are unchanged.
   ═══════════════════════════════════════════════ */
@media (min-width: 1200px) {
  :root {
    --shell-w: 1140px;
    --nav-w: 280px;
    /* §8.11.5: bottom nav retires; token → 0 so all dependent paddings/offsets
       (content padding, toast, bulk-bar, fab, panel scroll paddings) collapse. */
    --app-bottom-nav-h: 0px;
  }

  /* ── Shell becomes a flex row: [sidebar 280px][content 1fr] ── */
  .app-shell {
    max-width: none;          /* the app is now the full viewport… */
    margin: 0;
    display: flex;
    align-items: flex-start;
  }
  /* #column-left is a zero-size wrapper around .more-panel; display:contents
     promotes .more-panel to a direct flex child of .app-shell. */
  .app-shell #column-left { display: contents; }   /* storefront: scoped, see header note */
  .app-shell #content, .app-content {    /* …and the CONTENT is what gets capped/centered */
    flex: 1 1 auto;
    min-width: 0;             /* MANDATORY (§8.11.3): without it a wide ledger
                                 table blows the flex item out and pushes the
                                 sidebar off-screen. Do not remove. */
    max-width: var(--shell-w);
    margin-inline: auto;
    padding-top: calc(var(--app-bar-h) + 10px);
  }

  /* ── The sidebar: fixed overlay → sticky flex child ── */
  .more-panel {
    position: sticky; top: 0; inset-inline-start: auto; margin-inline-start: 0;
    left: auto; margin-left: 0;                  /* undo overlay centering */
    flex: 0 0 var(--nav-w); width: var(--nav-w); max-width: var(--nav-w);
    height: 100vh;
    transform: none !important;  /* neutralises .show / swipe-close (§8.11.3):
                                    stray open/closeMoreMenu() must never slide
                                    permanent chrome off-screen. */
    border-inline-end: 1px solid var(--app-border);
    z-index: 1;                  /* was 35 — it no longer overlays anything */
  }
  .more-panel.show { transform: none; }          /* .show is a no-op, not a bug */

  /* Panel header: brand block, not a "Menu ← back" bar (§8.11.6b).
     Close button hidden (kept in DOM for mobile + testid contract). */
  .more-panel [data-testid="sidebar-close"] { display: none; }
  .more-panel-bar { padding-left: 16px; }
  .more-panel-title { font-size: 17px; font-weight: 800; letter-spacing: -0.2px; }
  .more-panel-scroll { padding-bottom: 24px; }

  /* ── Menu: tile grid → vertical list (§8.11.6a, CSS-only) ── */
  .menu-grid { grid-template-columns: 1fr; gap: 2px; }
  .menu-card { flex-direction: row; align-items: center; gap: 10px;
               padding: 8px 10px; border-radius: 8px; text-align: start;
               min-height: 0; }
  .menu-card:active { transform: none; }
  .menu-card:hover { background: #e8eef7; }
  .menu-card-icon { width: 28px; height: 28px; font-size: 16px; }
  .menu-card-name { font-size: 13px; text-align: start; white-space: nowrap;
                    overflow: hidden; text-overflow: ellipsis;
                    word-break: normal; }
  /* Nesting levels become indentation instead of grouped grids (§5.6) */
  .menu-sub-section { padding-inline-start: 10px; }
  .menu-subsub { padding-inline-start: 10px; }

  /* Active-route highlight (§8.11.6c) — class set by markActiveNav() in app.js.
     Styled at desktop only: overlay tiers are dismissed on navigation, and
     styling them would break the §8.11.9 pixel-identity guarantee. */
  .menu-card.active { background: #e0ecff; }
  .menu-card.active .menu-card-name { color: var(--app-primary); font-weight: 700; }

  /* ── Hamburger disappears (kept in DOM — Stage 0.5 contract) ── */
  #btnMenu { display: none; }

  /* ── Bottom nav retires (token already 0 above; both halves required) ── */
  .bottom-nav { display: none; }

  /* ── Sidebar "Quick actions" block (§8.11.5 rehoming) — desktop only ── */
  .sidebar-quick { display: grid; }

  /* ── Notification panel: VIEWPORT verdict (§8.11.4 row 7) — dock to the
     viewport right edge, mirroring .fm-panel's >=768px behaviour. Keeps
     z-index 38 (> sidebar's 1) but never covers the sidebar band itself. ── */
  .notif-panel {
    left: auto; right: 0; margin-left: 0;
    max-width: 430px;
    transform: translateX(100%);               /* deliberate viewport anchor */
    box-shadow: -8px 0 32px rgba(15,23,42,0.18);
  }
  .notif-panel.show { transform: translateX(0); }

  /* ── FAB: bottom-right of the CONTENT region, not the viewport (§8.11.7).
     Fixed-position margins resolve against the scrollbar-corrected containing
     block (unlike 100vw, which §8.11.3 bans), so this stays aligned with the
     content column. Applied at desktop only — below 1200px the FAB keeps its
     current viewport-right anchor (pixel-identity, §8.11.9). ── */
  .fab {
    left: var(--nav-w); right: 0;
    margin-left: auto;
    margin-right: max(16px, calc((100% - var(--nav-w) - var(--shell-w)) / 2 + 16px));
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   REPORT SHELL — shared by all report/* fragments (core + reports_accounts)
   ══════════════════════════════════════════════════════════════════════════
   One canonical skin for every report page that core's
   admin/view/native/report/report.twig injects into `.rp-pane`. It replaces
   four near-duplicate scoped blocks that previously lived inline in the
   fragments themselves (`.ocr-*` CC-415, `.rpx-*` CC-416, `.srp-*` CC-417 and
   the extension's `.rpt-*`). They were structurally the same shell — header
   strip, inline filter panel, scrolling table, footer — with renamed selectors
   and a few per-family extras, so they are unified here under `.rp-shell-*`.

   WHY IT LIVES HERE AND NOT IN THE FRAGMENTS. Several report shells pair with a
   *_list.twig fragment that is re-fetched standalone over AJAX (a bare table
   with no styles of its own) and swapped in by jQuery .load(). Rules declared
   in the stylesheet survive that swap unconditionally, which is exactly the
   property the old inline blocks were duplicated to obtain.

   RECONCILED DIFFERENCES (the four blocks were NOT identical; where values
   disagreed the majority value won, noted so nobody "fixes" them back):
     - .rp-shell-filter-col flex-basis: was 170/170/160/150px -> 160px
     - .rp-shell-num font-weight: was 700 in the ocr family -> 600 (majority)
     - .rp-shell-act-btn: was 30px/radius 9 (ocr) -> 32px/radius 10
     - .rp-shell-btn-ghost padding: was 0 14px/13px (srp reset) -> 9px 13px/12.5px
     - pagination pills: was 30px/radius 9/1px border (ocr) -> 32px/10/1.5px
     - .rp-shell-name min-width: was 150/160px -> 150px
     - .rp-shell-muted font-weight unified to 500
     - alert variants unified to the four-state set (ok/bad/warn/info)
     - position:relative on .rp-shell-filter-col is now applied for every family
       (previously ocr/rpx only); purely additive where no dropdown is anchored.

   Core tokens are read through var() with literal fallbacks so a fragment still
   renders recognisably if this stylesheet is ever absent. */

/* ── Header strip: soft icon chip + title, the native card-head idiom ── */
.rp-shell-head { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.rp-shell-head-chip { width: 38px; height: 38px; border-radius: 11px; display: flex; align-items: center; justify-content: center; font-size: 19px; flex-shrink: 0; background: #dbeafe; color: var(--app-primary, #1a56db); }
.rp-shell-head-title { font-size: 17px; font-weight: 800; color: var(--app-text, #0f172a); line-height: 1.2; flex: 1; min-width: 0; }

/* ── Filter panel ──
   The filter fields live in a BOTTOM SHEET (see the block right below), not in
   an always-open inline panel and not in stock's separate right-hand card. That
   stock card was `d-none d-lg-block` — i.e. the filters were completely
   unreachable, and the report therefore unfilterable, on every screen narrower
   than 992px. The native admin is used on phones, so that was not survivable.
   Controls are core's .filter-* classes (ui-list.css) so they match every other
   native filter.

   `.rp-shell-filter` is still the form's class, but inside the sheet it carries
   no surface of its own: the sheet already IS the surface, and a grey card
   inside a white sheet reads as a second, nested panel. The standalone
   (non-sheet) look is kept as the base so the class degrades sanely if a
   fragment ever renders the form outside a sheet. */
.rp-shell-filter { background: #f8fafc; border-radius: 12px; padding: 12px 14px; margin-bottom: 14px; }
.rp-shell-filter-title { font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: .4px; margin-bottom: 10px; display: flex; align-items: center; gap: 6px; }
.rp-shell-filter-row { display: flex; gap: 10px; flex-wrap: wrap; }
/* position:relative anchors the autocomplete dropdown common.js toggles open. */
.rp-shell-filter-col { flex: 1 1 160px; min-width: 0; position: relative; }
.rp-shell-filter-col--wide { flex: 1 1 100%; }
.rp-shell-filter-col .dropdown-menu { position: absolute; top: 100%; left: 0; right: 0; z-index: 70; max-height: 230px; overflow-y: auto; margin-top: 2px; }
.rp-shell-filter-actions { margin-top: 12px; display: flex; gap: 8px; align-items: center; }
.rp-shell-filter-actions .btn-sp, .rp-shell-filter-actions .btn-ss { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }

/* Secondary/reset button sitting next to the primary filter action. */
.rp-shell-btn-ghost { display: inline-flex; align-items: center; justify-content: center; gap: 6px; background: #fff; border: 1.5px solid var(--app-border, #e2e8f0); border-radius: 12px; padding: 9px 13px; font-size: 12.5px; font-weight: 700; color: #475569; cursor: pointer; }
.rp-shell-btn-ghost i { font-size: 16px; }
.rp-shell-btn-ghost:active { border-color: var(--app-primary, #1a56db); color: var(--app-primary, #1a56db); }

/* ══ Filter bottom sheet ══════════════════════════════════════════════════
   ONE shared block for all sixteen filterable report shells. Each fragment
   contributes only markup (its own fields, verbatim) — no per-report rules.

   The sheet chrome itself (`.bottom-sheet`, `.sheet-handle`, `.sheet-title`,
   `.sheet-body`, `.sheet-footer`) and the `#sheetOverlay` backdrop are NOT
   redeclared here: they are the app-wide sheet idiom already used by
   admin/view/native/payment/invoice_form.twig's invoice picker and the
   Verify/Reverse confirmations, driven by app.js's openSheet()/closeAllSheets().
   Only the report-specific deltas live below.

   WHY A SHEET AND NOT THE OLD INLINE PANEL. A report page is read by scanning a
   money column; the filter panel is touched once and then only re-read to
   answer "what am I looking at?". Permanently spending 150-200px of a phone
   viewport on controls that are idle 95% of the time pushed the first data row
   below the fold on every report. The trigger + sheet gives that space back and
   keeps the answer to "what am I looking at?" on the trigger's dot. */

/* Trigger row: a single small centred button where the panel used to be. */
.rp-shell-filter-bar { display: flex; justify-content: center; margin-bottom: 14px; }
/* Extends ui-form.css's .os-act-btn (same trigger idiom as "Select Invoice").
   position:relative is what the active dot is absolutely positioned against. */
.rp-shell-filter-open { position: relative; display: inline-flex; align-items: center; gap: 6px; }
.rp-shell-filter-open i { font-size: 16px; }

/* Active-filter indicator. Deliberately the SAME red-dot-on-a-blue-chip idiom
   as the app bar's unread-notification `.notif-dot` (see the APP BAR block
   above) rather than a new invention — same size, same colour, same 2px ring in
   the host's own background so it reads as a badge and not as a stray pixel.
   Hidden by default; app.js's rpShellFilterSync() adds .is-filtered to the
   trigger when any control differs from the value the page loaded with. */
.rp-shell-filter-dot { display: none; position: absolute; top: -3px; right: -3px; width: 9px; height: 9px; border-radius: 50%; background: #f03e3e; border: 2px solid #fff; }
.rp-shell-filter-open.is-filtered .rp-shell-filter-dot { display: block; }

/* Inside the sheet the form is bare: the sheet is the surface. */
.rp-shell-filter-sheet .rp-shell-filter { background: none; border-radius: 0; padding: 14px 18px 16px; margin-bottom: 0; }
/* The sheet-scoped title would duplicate .sheet-title directly above it. */
.rp-shell-filter-sheet .rp-shell-filter-title { display: none; }
/* Field pairs stack to full width in the sheet: the sheet is phone-width, and
   two 160px columns side by side left every date input too narrow to read. */
.rp-shell-filter-sheet .rp-shell-filter-col { flex: 1 1 210px; }
/* The in-form action row moved to .sheet-footer; hide it if a fragment still
   emits one, so no report can end up with two Filter buttons. */
.rp-shell-filter-sheet .rp-shell-filter-actions { display: none; }

/* ── overflow: visible, deliberately ──
   `.sheet-body` is `overflow-y: auto` for the long pick-lists it was written
   for. Five of these reports carry a customer autocomplete whose dropdown is an
   `position: absolute` <ul> anchored to `.rp-shell-filter-col`; inside a
   scrolling body that dropdown is CLIPPED at the body edge and the suggestions
   become unreachable. Every filter set here is at most five short controls, so
   the sheet never needs to scroll and the clip can simply be removed. If a
   report ever grows a genuinely long filter set, re-introduce scrolling on a
   modifier class for THAT report only — do not put it back here. */
.rp-shell-filter-sheet .sheet-body { overflow: visible; }
/* Room under the last field so an opened dropdown has somewhere to land before
   it reaches the footer. */
.rp-shell-filter-sheet .rp-shell-filter-row:last-child { margin-bottom: 4px; }
.rp-shell-filter-sheet .sheet-footer { align-items: center; }
/* Footer order is Reset (secondary, left) then Filter (primary, right, filling
   the remaining width) — the same weighting as every other sheet footer. */
.rp-shell-filter-sheet .sheet-footer .btn-sp { flex: 1; }

/* Dim the outgoing figures while an AJAX refresh is in flight instead of
   blanking them — the operator keeps context and nothing reflows twice. */
.rp-shell-busy { opacity: .45; pointer-events: none; transition: opacity .15s; }

/* ── Status / verdict banners ──
   Four states, four colours: a self-verifying balanced/unbalanced verdict must
   never be mistakable for a plain informational note, and "no gateway" must
   never be mistakable for "the books are wrong". Each carries a distinct glyph
   too, for colour-blind readers. */
.rp-shell-alert { display: flex; align-items: flex-start; gap: 8px; border-radius: 12px; padding: 11px 13px; font-size: 12.5px; line-height: 1.5; font-weight: 600; margin-bottom: 12px; }
.rp-shell-alert i { font-size: 17px; flex-shrink: 0; margin-top: 1px; }
.rp-shell-alert--ok   { background: #dcfce7; color: #15803d; }
.rp-shell-alert--bad  { background: #fee2e2; color: #b91c1c; }
.rp-shell-alert--warn { background: #fffbeb; color: #92400e; }
.rp-shell-alert--info { background: #eff6ff; color: #1d4ed8; }

/* ── Table ──
   Kept as a real table, NOT re-cut into per-row cards: these reports are read
   by scanning a money column down the page and every column carries a figure
   that must not be dropped. Width is allowed to exceed the shell; this wrapper
   plus the host's `.rp-pane { overflow-x: auto }` scroll it. */
.rp-shell-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--app-border, #e2e8f0); border-radius: 12px; }
.rp-shell-table { width: 100%; border-collapse: collapse; font-size: 12.5px; white-space: nowrap; }
/* Base alignment is set here, not with Bootstrap's .text-start/.text-end
   utilities: the native shell does not load bootstrap.css and those utilities
   only happen to exist via the vendored uix build. The per-cell alignment
   classes below are all scoped `.rp-shell-table .x` so they outrank this. */
.rp-shell-table th, .rp-shell-table td { padding: 9px 11px; border-bottom: 1px solid #f1f5f9; text-align: left; vertical-align: middle; }
.rp-shell-table thead th { background: #f8fafc; color: #94a3b8; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; border-bottom: 1px solid var(--app-border, #e2e8f0); }
.rp-shell-table thead th.rp-shell-group { text-align: center; border-left: 1px solid var(--app-border, #e2e8f0); }
.rp-shell-table tbody tr:last-child td { border-bottom: none; }
.rp-shell-table tbody tr:nth-child(even) td { background: #fcfdfe; }

/* Cell roles. */
.rp-shell-table .rp-shell-num { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; color: var(--app-text, #0f172a); }
.rp-shell-table .rp-shell-code { font-weight: 700; color: var(--app-primary, #1a56db); font-variant-numeric: tabular-nums; }
.rp-shell-table .rp-shell-date { color: #475569; font-weight: 600; font-variant-numeric: tabular-nums; }
.rp-shell-table .rp-shell-txt { color: var(--app-text, #0f172a); font-weight: 600; }
.rp-shell-table .rp-shell-name { color: var(--app-text, #0f172a); font-weight: 600; white-space: normal; min-width: 150px; }
.rp-shell-table .rp-shell-muted { color: var(--app-muted, #64748b); font-weight: 500; }
/* Free-text columns (comment, keyword, product list) must wrap instead of
   widening the pane forever. */
.rp-shell-table .rp-shell-wrap { white-space: normal; min-width: 190px; font-weight: 500; color: #475569; line-height: 1.45; }
.rp-shell-table .rp-shell-act { text-align: right; width: 1%; }
/* Header cells reuse the alignment classes, so restate the header look at equal
   specificity — otherwise the two-class cell roles above outrank
   `.rp-shell-table thead th` and repaint the header row as body text. */
.rp-shell-table thead th.rp-shell-num, .rp-shell-table thead th.rp-shell-date, .rp-shell-table thead th.rp-shell-name, .rp-shell-table thead th.rp-shell-code, .rp-shell-table thead th.rp-shell-txt { color: #94a3b8; font-weight: 700; font-size: 10.5px; }

/* Row action button (open the underlying record). */
.rp-shell-act-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 10px; background: var(--app-primary, #1a56db); color: #fff; text-decoration: none; font-size: 15px; }
.rp-shell-act-btn:active { opacity: .85; }

/* Totals row — the figures a balanced/unbalanced verdict is computed from. */
.rp-shell-table tfoot td { background: #f1f5f9; font-weight: 800; color: var(--app-text, #0f172a); border-top: 1px solid var(--app-border, #e2e8f0); border-bottom: none; }
.rp-shell-table tfoot .rp-shell-total-label { text-align: right; text-transform: uppercase; font-size: 10.5px; letter-spacing: .5px; color: #64748b; }
/* Brought-forward / carried-forward running-balance rows (general ledger). */
.rp-shell-table tr.rp-shell-carry td { background: #eff6ff; font-weight: 700; color: #1e3a8a; font-style: normal; }

/* Empty state, rendered inside a full-width cell so the column headers survive. */
.rp-shell-empty { padding: 30px 18px; text-align: center; }
.rp-shell-empty-circle { width: 60px; height: 60px; border-radius: 50%; background: #f1f5f9; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px; }
.rp-shell-empty-circle i { font-size: 26px; color: #94a3b8; }
.rp-shell-empty-title { font-size: 13px; font-weight: 700; color: #64748b; }

/* ── Footer: server-rendered pagination + result counter ──
   The pagination markup is emitted either by core's Pagination class (a bare
   ul.pagination of li>a) or by admin/view/native/common/pagination.twig
   (.oc-page-nav > ul.pagination). Neither can be changed from a template, so
   both are restyled in place here; the margin/padding resets cancel the
   standalone spacing the native partial carries for full-page list screens. */
.rp-shell-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.rp-shell-foot .oc-page-nav { margin: 0; }
.rp-shell-foot .pag-wrap { padding: 0; }
.rp-shell-foot .pagination { display: flex; gap: 4px; list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.rp-shell-foot .pagination a, .rp-shell-foot .pagination span { display: inline-flex; align-items: center; justify-content: center; min-width: 32px; height: 32px; padding: 0 8px; border: 1.5px solid var(--app-border, #e2e8f0); border-radius: 10px; background: #fff; font-size: 12.5px; font-weight: 700; color: #475569; text-decoration: none; }
.rp-shell-foot .pagination .active a, .rp-shell-foot .pagination .active span { background: var(--app-primary, #1a56db); border-color: var(--app-primary, #1a56db); color: #fff; }
.rp-shell-results { font-size: 11.5px; font-weight: 600; color: var(--app-muted, #64748b); }

/* ── Prev/next pager (reports that page a cursor rather than emit page links) ── */
.rp-shell-pager { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 12px; }
.rp-shell-pager-btn { display: inline-flex; align-items: center; gap: 4px; background: #fff; border: 1.5px solid var(--app-border, #e2e8f0); border-radius: 12px; padding: 8px 13px; font-size: 12.5px; font-weight: 700; color: #475569; cursor: pointer; }
.rp-shell-pager-btn i { font-size: 16px; }
.rp-shell-pager-btn:active { border-color: var(--app-primary, #1a56db); color: var(--app-primary, #1a56db); }
.rp-shell-pager-info { font-size: 11.5px; font-weight: 600; color: var(--app-muted, #64748b); text-align: center; flex: 1; }
.rp-shell-pager-slot { min-width: 40px; display: flex; }
.rp-shell-pager-slot:last-child { justify-content: flex-end; }

/* Back action on the report "form" fragments, rendered as an <a> so it
   navigates without JS. .btn-cancel-bar is authored for a <button>, so restore
   the parts an anchor does not inherit. (Was a fifth duplicated inline block,
   repeated identically in all five customer-report *_form.twig fragments.) */
.rp-shell-back { display: inline-flex; align-items: center; justify-content: center; gap: 6px; text-decoration: none; }

/* ── Account identity + summary tiles (general ledger) ── */
.rp-shell-acct { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.rp-shell-acct-name { font-size: 15px; font-weight: 800; color: var(--app-text, #0f172a); }
.rp-shell-acct-no { font-size: 12px; font-weight: 700; color: var(--app-primary, #1a56db); }
.rp-shell-sum { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px; margin-bottom: 12px; }
.rp-shell-sum-cell { background: #f8fafc; border-radius: 12px; padding: 10px 12px; }
.rp-shell-sum-label { font-size: 10px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: .4px; }
.rp-shell-sum-value { font-size: 15px; font-weight: 800; color: var(--app-text, #0f172a); margin-top: 5px; word-break: break-word; }
.rp-shell-sum-side { font-size: 11px; font-weight: 700; color: #64748b; margin-left: 3px; }
