/* ============================================================
   Print Ledgers — "App UI" theme (opt-in, additive)
   Touch-first, native-app design system modeled on iOS/app
   conventions. Every rule here is scoped under html.theme-appui
   so this file is a complete no-op when that class is absent.
   Load order: after style.css / style-fixes.css.
   ============================================================ */

/* ── 1. Design tokens ── */
html.theme-appui {
  --accent: #0070EB;
  --accent-on-tint: #0060C9;
  --accent-soft: rgba(0,122,255,0.12);
  --bg: #F2F2F7;
  --card: rgba(255,255,255,0.82);
  --card-solid: #FFFFFF;
  /* Frosted liquid-glass bar material: translucent enough that content
     scrolling underneath shows through as a soft blurred wash (the
     saturate+blur backdrop-filter does the "glass"), opaque enough to
     keep bar controls readable. The earlier 0.94 near-opaque value was a
     workaround for a bright blob that turned out to be a leaked button
     box-shadow (fixed at the source in the shell button reset), so the
     genuine glass look is back. */
  --bar-bg: rgba(249,249,251,0.78);
  --glass-edge: inset 0 1px 0 rgba(255,255,255,0.5);
  --border: rgba(60,60,67,0.12);
  --text: #1C1C1E;
  --text-2: #6C6C70;
  --radius: 20px;
  --radius-sm: 14px;
  --blur: saturate(180%) blur(20px);
  --tabbar-h: 56px;
  --appbar-h: 56px;
  --shadow: 0 1px 2px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.06);
  --shadow-lift: 0 2px 6px rgba(0,0,0,0.06), 0 16px 40px rgba(0,0,0,0.12);
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);

  /* status colors — soft tints, not solid fills */
  --status-green-bg: rgba(48,209,88,0.14);
  --status-green-text: #157732;
  --status-red-bg: rgba(255,69,58,0.14);
  --status-red-text: #BD2217;
  --status-amber-bg: rgba(255,159,10,0.16);
  --status-amber-text: #945900;
  --status-gray-bg: rgba(142,142,147,0.16);
  --status-gray-text: #646469;
  --status-violet-bg: rgba(191,90,242,0.15);
  --status-violet-text: #9527CC;
  --status-indigo-bg: rgba(88,86,214,0.15);
  --status-indigo-text: #504ED4;

  --border-w: 1px;
}

html.theme-appui.dark {
  --accent: #0072E5;
  --accent-on-tint: #51A8FF;
  --accent-soft: rgba(10,132,255,0.18);
  --bg: #000000;
  --card: rgba(28,28,30,0.86);
  --card-solid: #1C1C1E;
  --bar-bg: rgba(22,22,24,0.78);
  --glass-edge: inset 0 1px 0 rgba(255,255,255,0.07);
  --border: rgba(84,84,88,0.42);
  --text: #F2F2F7;
  --text-2: #98989F;

  --status-green-text: #30D158;
  --status-red-text: #FF756D;
  --status-amber-text: #FF9F0A;
  --status-gray-text: #A5A5AB;
  --status-violet-bg: rgba(191,90,242,0.2);
  --status-violet-text: #D38EF6;
  --status-indigo-bg: rgba(94,92,230,0.2);
  --status-indigo-text: #9B9AEF;
}

@media (prefers-contrast: more) {
  html.theme-appui {
    --border-w: 1.5px;
    --border: rgba(60,60,67,0.32);
  }
  html.theme-appui.dark {
    --border: rgba(140,140,148,0.55);
  }
}

/* ── 2. Base / reset (scoped) ── */
html.theme-appui body {
  background: var(--bg);
  color: var(--text);
  /* Uniform-width digits everywhere - money in tables/tiles, quantities,
     dates, badge counts - so numbers in adjacent rows/columns line up
     instead of jittering with each digit's natural (proportional) width. */
  font-variant-numeric: tabular-nums;
}
html.theme-appui * {
  -webkit-tap-highlight-color: transparent;
}
/* iOS's long-press "touch callout" (a dark rounded card previewing the
   pressed element, meant for links/images) fires on plain buttons too
   when nothing suppresses it — it renders as its own OS-level overlay,
   NOT constrained by this app's layout, which is why it can overhang
   the tab bar and run off the screen edge. That overlay is the actual
   "black box on tap" users hit on the tab bar's More button and other
   controls: no amount of :focus/:active/background CSS on the button
   itself touches it, because it isn't the button repainting — it's a
   separate native preview card stacked on top of it. */
html.theme-appui button,
html.theme-appui a,
html.theme-appui .appui-tab,
html.theme-appui .appui-appbar-btn,
html.theme-appui .appui-chip,
html.theme-appui .appui-list-row,
html.theme-appui .appui-sidebar-link,
html.theme-appui .appui-tile {
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
}
html.theme-appui .appui-hidden { display: none !important; }
.appui-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* Neutralize BOTH sources of stray button chrome inside the App UI shell:
   1. iOS's native <button> face (appearance) — background:transparent
      alone doesn't stop it.
   2. The classic theme's global `button{...}` rule (style.css), whose
      box-shadow painted halo discs around the appbar sun/menu buttons
      and a white rounded blob behind the "More" tab, and whose
      .5rem/.85rem padding squeezed the 24px icons off-center inside
      their 40px boxes — the actual "icons misaligned" bug.
   :where() keeps this at element-level specificity (0,0,1): it beats the
   classic `button` rule purely by load order, while every component
   class (.appui-fab-main's shadow, .appui-tab's min-height, inline
   styles on form buttons) still wins over it. A plain scoped selector
   would out-rank those and strip intentional styling. */
:where(html.theme-appui #appui-shell) button {
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  /* Sensible default for TEXT buttons: horizontal breathing room and a
     centered label. A bare padding:0 here glued full-width button labels
     to the left edge on every form that didn't re-specify padding
     inline. Icon-box buttons (appbar/tabs/FABs) opt back into padding:0
     via their component classes below, which out-rank this reset. */
  padding: 0 14px;
  margin: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  font: inherit;
  color: inherit;
  white-space: normal;
}

/* Icon wrappers are inline by default, so the SVG inside rides the text
   baseline and every icon sits a few px low in its container. Flex-center
   the wrapper and block the SVG to remove the baseline slop everywhere. */
html.theme-appui [data-lucide] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
}
html.theme-appui [data-lucide] svg {
  display: block;
}

/* Safety net: the classic theme's global a:hover{text-decoration:underline}
   (style.css) outranks a plain class selector on specificity and isn't
   scoped to html.theme-appui, so every appui <a>-based interactive
   component needs an explicit, unconditional override — not just inside
   @media(hover:hover), since that never matches on touch/coarse-pointer
   devices in the first place. */
html.theme-appui #appui-shell a:hover,
html.theme-appui #appui-shell a:visited:hover {
  text-decoration: none;
}

/* Shell visibility toggle: mutually exclusive with the classic app shell */
#appui-shell { display: none; }
html.theme-appui #appui-shell { display: flex; }
html.theme-appui #app-shell { display: none; }

/* ── 3. Shell layout ── */
#appui-shell {
  position: fixed;
  inset: 0;
  flex-direction: row;
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
}

.appui-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  height: 100%;
}

.appui-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  padding: calc(var(--appbar-h) + env(safe-area-inset-top) + 12px) 16px calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 24px);
}

/* ── 4. Top app bar (mobile / tablet) ──
   iOS/WebKit gotcha: backdrop-filter directly on a position:fixed box
   can fail to composite in PWA standalone mode (renders fully
   transparent). Fix: filter lives on an absolutely-positioned ::before
   that fills the box from behind; the fixed box itself carries no
   filter and can't hit the bug. */
.appui-appbar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 40;
  /* +border-w: box-sizing is border-box, so without it the 1px bottom
     border shrinks the content box to 55px and the centered buttons/title
     sit 0.5px high relative to the bar's visual center. */
  height: calc(var(--appbar-h) + env(safe-area-inset-top) + var(--border-w));
  padding-top: env(safe-area-inset-top);
  display: flex;
  align-items: center;
  gap: 8px;
  padding-left: 12px;
  padding-right: 12px;
  border-bottom: var(--border-w) solid var(--border);
  background: transparent;
}
.appui-appbar::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--bar-bg);
  box-shadow: var(--glass-edge);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  z-index: -1;
}
.appui-appbar > * { position: relative; z-index: 1; }

.appui-appbar-btn {
  width: 40px; height: 40px;
  min-width: 40px;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: none;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  transition: background .15s var(--ease-out);
}
.appui-appbar-btn:active { background: var(--accent-soft); transform: scale(0.94); }
.appui-appbar-btn:focus { outline: none; }
.appui-appbar-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.appui-appbar-title {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.appui-appbar-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* ── 5. Bottom tab bar (mobile only) ── */
.appui-tabbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  /* No fixed height: with box-sizing:border-box the 1px border-top ate
     into it and the 56px row overflowed the bar by 1px. Content-sized,
     the bar is exactly row + safe-area padding + border. */
  padding-bottom: env(safe-area-inset-bottom);
  border-top: var(--border-w) solid var(--border);
  background: transparent;
  display: none;
}
.appui-tabbar::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--bar-bg);
  box-shadow: var(--glass-edge);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  z-index: -1;
}
.appui-tabbar-row {
  position: relative;
  height: var(--tabbar-h);
  display: grid;
  grid-template-columns: repeat(var(--tab-count, 5), 1fr);
}
.appui-tabbar-bubble {
  position: absolute;
  top: 6px;
  left: 0;
  width: calc(100% / var(--tab-count, 5));
  height: calc(100% - 12px);
  transform: translateX(calc(var(--tab-index, 0) * 100%));
  transition: transform .32s var(--ease-spring), opacity .2s var(--ease-out);
  opacity: 1;
  pointer-events: none;
  z-index: 0;
}
/* The visible pill is inset inside the slot-sized bubble so the active
   tab's highlight never touches the screen edges (the bubble element
   itself must stay exactly one slot wide — translateX(n*100%) is
   relative to its own width, so shrinking it would break the slide). */
.appui-tabbar-bubble::before {
  content: "";
  position: absolute;
  inset: 0 5px;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
}
.appui-tabbar-row[data-bubble-hidden="1"] .appui-tabbar-bubble { opacity: 0; }

.appui-tab {
  position: relative;
  z-index: 1;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border: none;
  background: transparent;
  color: var(--text-2);
  text-decoration: none;
  cursor: pointer;
  min-height: 44px;
  transition: color .15s var(--ease-out), transform .08s var(--ease-out);
}
.appui-tab:active { transform: scale(0.92); }
.appui-tab.is-active { color: var(--accent); }
/* Only <button data-appui-open-more> among the tabs is a real <button>
   (the rest are <a>) — buttons pick up the browser's own default
   :focus treatment on tap in a way links don't, and since nothing here
   overrode :focus, that UA default (often a solid filled box, worst on
   Android WebViews) could win: a bare pseudo-class selector like
   `button:focus` outranks the plain class selector above it. Force it
   back to transparent for a tap (:focus without :focus-visible), while
   still giving keyboard users a visible ring. */
.appui-tab:focus { background: transparent; outline: none; }
.appui-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.appui-tab svg, .appui-tab i { width: 23px; height: 23px; }
.appui-tab-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* Raised "+" CTA in the middle tab slot */
.appui-tab-fab {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
/* Sits fully inside the bar, aligned with the other tabs — the earlier
   raised/floating variant read as a misplaced element rather than a
   deliberate CTA. */
.appui-tab-fab-btn {
  width: 46px; height: 46px;
  padding: 0;
  border-radius: 999px;
  border: none;
  background: var(--accent);
  color: #fff;
  display: grid;
  place-items: center;
  box-shadow: var(--shadow);
  cursor: pointer;
  transition: transform .15s var(--ease-spring);
}
.appui-tab-fab-btn:active { transform: scale(0.92); }
.appui-tab-fab-btn:focus { outline: none; }
.appui-tab-fab-btn:focus-visible { outline: 2px solid #fff; outline-offset: -3px; }
.appui-tab-fab-btn svg { width: 24px; height: 24px; }

/* ── 6. Desktop sidebar (≥1024px) ── */
.appui-sidebar {
  display: none;
  flex-direction: column;
  width: 248px;
  flex-shrink: 0;
  height: 100%;
  border-right: var(--border-w) solid var(--border);
  background: var(--card-solid);
  overflow: hidden;
}
.appui-sidebar.is-collapsed { width: 72px; }

.appui-sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 12px 12px 16px;
  font-weight: 800;
  font-size: 14.5px;
  letter-spacing: -0.01em;
}
.appui-sidebar-brand img { width: 28px; height: 28px; border-radius: 7px; flex-shrink: 0; }
.appui-sidebar.is-collapsed .appui-sidebar-brand { padding: 14px 0 12px; justify-content: center; }
.appui-sidebar.is-collapsed .appui-sidebar-brand span,
.appui-sidebar.is-collapsed .appui-sidebar-brand img { display: none; }

/* Collapse chevron lives in the brand row, like most desktop products */
.appui-sidebar-collapse {
  margin-left: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 7px;
  border: none;
  background: transparent;
  color: var(--text-2);
  cursor: pointer;
  box-shadow: none;
  padding: 0;
  transition: background .15s var(--ease-out), transform .2s var(--ease-out);
}
.appui-sidebar-collapse:hover { background: var(--status-gray-bg); color: var(--text); }
.appui-sidebar-collapse svg, .appui-sidebar-collapse i { width: 16px; height: 16px; }
.appui-sidebar.is-collapsed .appui-sidebar-collapse { margin: 0 auto; transform: rotate(180deg); }

/* Current business / signed-in user identity */
.appui-sidebar-biz {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin: 0 12px 6px;
  padding: 9px 10px;
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  min-width: 0;
}
.appui-sidebar-biz-name {
  font-size: 12.5px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.appui-sidebar-biz-user {
  font-size: 11px;
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.appui-sidebar.is-collapsed .appui-sidebar-biz { display: none; }

/* Live-filter search box above the nav list */
.appui-sidebar-search-wrap {
  position: relative;
  margin: 0 12px 8px;
}
.appui-sidebar-search-icon {
  position: absolute;
  left: 9px;
  top: 50%;
  transform: translateY(-50%);
  width: 14px;
  height: 14px;
  color: var(--text-2);
  opacity: .7;
  pointer-events: none;
}
.appui-sidebar-search {
  width: 100%;
  height: 30px;
  padding: 0 8px 0 28px;
  border-radius: 8px;
  border: var(--border-w) solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: 12.5px;
  font-family: inherit;
}
.appui-sidebar-search:focus { outline: none; border-color: var(--accent); }
.appui-sidebar-search::placeholder { color: var(--text-2); }
.appui-sidebar.is-collapsed .appui-sidebar-search-wrap { display: none; }

/* Scrollable nav: thin, unobtrusive scrollbar that only shows on hover */
.appui-sidebar-nav {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding-bottom: 8px;
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
}
.appui-sidebar-nav:hover { scrollbar-color: var(--border) transparent; }
.appui-sidebar-nav::-webkit-scrollbar { width: 5px; }
.appui-sidebar-nav::-webkit-scrollbar-track { background: transparent; }
.appui-sidebar-nav::-webkit-scrollbar-thumb { background: transparent; border-radius: 3px; }
.appui-sidebar-nav:hover::-webkit-scrollbar-thumb { background: var(--border); }

/* ── Collapsible groups: <details>/<summary>, single-group-open accordion ── */
.appui-sidebar-group {
  padding: 0 12px;
}
.appui-sidebar-group + .appui-sidebar-group { margin-top: 2px; }
.appui-sidebar-group[data-search-hidden] { display: none; }

.appui-sidebar-group summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 8px;
  border-radius: 8px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.appui-sidebar-group summary::-webkit-details-marker { display: none; }
.appui-sidebar-group summary:hover { background: var(--status-gray-bg); }

.appui-sidebar-group-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 8px;
  flex-shrink: 0;
  background: var(--status-gray-bg);
  color: var(--text-2);
}
.appui-sidebar-group-icon svg, .appui-sidebar-group-icon i { width: 14px; height: 14px; }
.appui-sidebar-group[data-tint="blue"]   .appui-sidebar-group-icon { background: var(--accent-soft); color: var(--accent); }
.appui-sidebar-group[data-tint="green"]  .appui-sidebar-group-icon { background: var(--status-green-bg); color: var(--status-green-text); }
.appui-sidebar-group[data-tint="amber"]  .appui-sidebar-group-icon { background: var(--status-amber-bg); color: var(--status-amber-text); }
.appui-sidebar-group[data-tint="red"]    .appui-sidebar-group-icon { background: var(--status-red-bg); color: var(--status-red-text); }
.appui-sidebar-group[data-tint="violet"] .appui-sidebar-group-icon { background: var(--status-violet-bg); color: var(--status-violet-text); }
.appui-sidebar-group[data-tint="indigo"] .appui-sidebar-group-icon { background: var(--status-indigo-bg); color: var(--status-indigo-text); }
.appui-sidebar-group[data-tint="gray"]   .appui-sidebar-group-icon { background: var(--status-gray-bg); color: var(--text-2); }

/* Wraps label + preview so the chevron/icon can sit beside a stacked
   two-line block instead of a single line. Desktop never shows the
   preview line (see .appui-sidebar-group-preview below) so this is a
   no-op there beyond the wrapper itself. */
.appui-sidebar-group-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.appui-sidebar-group-label {
  flex: none;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Content preview ("Vendors · Bills · Expenses · Banking") for a
   collapsed group — glanceable without opening it. Desktop's sidebar
   is compact/mouse-driven and doesn't need it; only the touch-first
   More sheet shows it (enabled again below under .appui-sheet). Text
   is filled in by JS (initNavAccordion in theme-appui.js) from each
   group's own visible links, so it can never drift out of sync with
   what's actually rendered (permission-gated links included/excluded
   automatically). */
.appui-sidebar-group-preview { display: none; }
.appui-sidebar-group-chevron {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  color: var(--text-2);
  opacity: .6;
  transition: transform .2s var(--ease-out);
}
.appui-sidebar-group[open] > summary .appui-sidebar-group-chevron { transform: rotate(180deg); }

.appui-sidebar-group-body {
  display: flex;
  flex-direction: column;
  padding: 2px 0 4px 4px;
}

.appui-sidebar.is-collapsed .appui-sidebar-group-label,
.appui-sidebar.is-collapsed .appui-sidebar-group-chevron { display: none; }
.appui-sidebar.is-collapsed .appui-sidebar-group summary { justify-content: center; padding: 7px; }
/* Icon rail: no room for an accordion, so force every group's link list
   visible regardless of the <details> open state (CSS can override the
   UA's display:none on non-summary children of a closed <details>). */
.appui-sidebar.is-collapsed .appui-sidebar-group-body { display: flex !important; padding-left: 0; }
.appui-sidebar.is-collapsed .appui-sidebar-group + .appui-sidebar-group {
  margin-top: 8px;
  border-top: var(--border-w) solid var(--border);
  padding-top: 8px;
}

/* ── More sheet: the same accordion groups, re-tuned for touch.
   The mobile sheet reuses .appui-sidebar-group markup/JS so the two
   navs can't drift apart; these overrides only resize it for thumbs
   (48px summaries, larger chips/labels) and restyle the search box. */
.appui-sheet .appui-sidebar-group { padding: 0; }
.appui-sheet .appui-sidebar-group + .appui-sidebar-group { margin-top: 6px; }
.appui-sheet .appui-sidebar-group summary {
  min-height: 60px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  gap: 12px;
}
/* Bigger chip than the desktop rail (24px) — on a touch sheet the icon
   is the primary visual anchor for the row, not a small decoration.
   Deliberately smaller than the open-tile-grid's icon chips below
   (36px vs 44px) — the section header is a compact identity marker,
   the tiles inside are the actual tap targets and get the emphasis. */
.appui-sheet .appui-sidebar-group-icon { width: 36px; height: 36px; border-radius: 10px; }
.appui-sheet .appui-sidebar-group-icon svg, .appui-sheet .appui-sidebar-group-icon i { width: 18px; height: 18px; }

/* Dusty sheet palette. The status tokens are full-saturation iOS
   system colors — right for small badges, but loud on 36-44px chips.
   The reference design this sheet follows uses powdery washes with
   muted mid-tone glyphs (dusty steel blue, sage, clay — not pure
   #007AFF/#30D158). Hand-tuned equivalents here, scoped to the sheet
   so badges/desktop-sidebar chips elsewhere keep the vivid tokens. */
.appui-sheet .appui-sidebar-group[data-tint="blue"]   .appui-sidebar-group-icon { background: rgba(74,108,158,0.18);  color: #4A6C9E; }
.appui-sheet .appui-sidebar-group[data-tint="green"]  .appui-sidebar-group-icon { background: rgba(86,123,86,0.19);   color: #567B56; }
.appui-sheet .appui-sidebar-group[data-tint="amber"]  .appui-sidebar-group-icon { background: rgba(160,106,54,0.19);  color: #A06A36; }
.appui-sheet .appui-sidebar-group[data-tint="red"]    .appui-sidebar-group-icon { background: rgba(176,85,77,0.18);   color: #B0554D; }
.appui-sheet .appui-sidebar-group[data-tint="violet"] .appui-sidebar-group-icon { background: rgba(118,99,179,0.18);  color: #7663B3; }
.appui-sheet .appui-sidebar-group[data-tint="indigo"] .appui-sidebar-group-icon { background: rgba(99,99,174,0.18);   color: #6363AE; }
.appui-sheet .appui-sidebar-group[data-tint="gray"]   .appui-sidebar-group-icon { background: rgba(93,104,117,0.16);  color: #5D6875; }
/* Tile chips + utility rows (Help/Dark Mode/Logout icons): same dusty
   steel blue as the reference's tiles, instead of vivid accent blue. */
.appui-sheet .appui-list-row-icon { background: rgba(91,127,185,0.14); color: #5B7FB9; }
html.theme-appui.dark .appui-sheet .appui-sidebar-group[data-tint="blue"]   .appui-sidebar-group-icon { background: rgba(143,174,220,0.18); color: #8FAEDC; }
html.theme-appui.dark .appui-sheet .appui-sidebar-group[data-tint="green"]  .appui-sidebar-group-icon { background: rgba(147,184,147,0.18); color: #93B893; }
html.theme-appui.dark .appui-sheet .appui-sidebar-group[data-tint="amber"]  .appui-sidebar-group-icon { background: rgba(217,168,107,0.18); color: #D9A86B; }
html.theme-appui.dark .appui-sheet .appui-sidebar-group[data-tint="red"]    .appui-sidebar-group-icon { background: rgba(217,138,131,0.18); color: #D98A83; }
html.theme-appui.dark .appui-sheet .appui-sidebar-group[data-tint="violet"] .appui-sidebar-group-icon { background: rgba(169,150,219,0.18); color: #A996DB; }
html.theme-appui.dark .appui-sheet .appui-sidebar-group[data-tint="indigo"] .appui-sidebar-group-icon { background: rgba(149,149,217,0.18); color: #9595D9; }
html.theme-appui.dark .appui-sheet .appui-sidebar-group[data-tint="gray"]   .appui-sidebar-group-icon { background: rgba(150,160,175,0.16); color: #9AA5B5; }
html.theme-appui.dark .appui-sheet .appui-list-row-icon { background: rgba(143,174,220,0.16); color: #8FAEDC; }
.appui-sheet .appui-sidebar-group-label { font-size: 15px; color: var(--text); }
.appui-sheet .appui-sidebar-group-preview {
  display: block;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 1px;
}
/* Once open, the grid below already shows exactly what's in the
   group — the one-line summary would just be a redundant echo. */
.appui-sheet .appui-sidebar-group[open] > summary .appui-sidebar-group-preview { display: none; }
.appui-sheet .appui-sidebar-group-chevron { width: 17px; height: 17px; }
.appui-sheet .appui-sidebar-group-body { padding: 4px 0 2px; }
.appui-sheet-search-wrap { margin: 0 0 10px; }

/* Open group's children: a 2-column grid of icon-over-label cards
   instead of a vertical row list — the row list is denser and reads
   fine on desktop, but on a touch sheet each destination becomes a
   proper tappable tile, matching how the rest of the App UI theme
   presents destinations (.appui-tile) rather than a plain menu. */
.appui-sheet .appui-sidebar-group[open] .appui-list-group {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  padding: 0 4px 4px;
  background: transparent;
  border: none;
  overflow: visible;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
/* Plain cards with a soft shadow, no border — every tile in every
   section uses the same neutral card face regardless of the section's
   own identity color (that color lives on the section header only);
   the icon chip inside stays the same dusty blue for the same reason.
   Background is the TRANSLUCENT card token, not --card-solid: an
   opaque white tile on the frosted sheet reads as a stark foreign
   layer (and completely defeats the Liquid Glass material, which
   theme-glass.css applies on top of this rule). */
.appui-sheet .appui-sidebar-group[open] .appui-list-row {
  flex-direction: column;
  justify-content: center;
  text-align: center;
  gap: 6px;
  min-height: 84px;
  padding: 12px 10px;
  border: none;
  border-radius: 16px;
  /* Quieter than the standard card token — a half-strength wash so the
     tile reads as a gentle raised area of the sheet, not a bright card. */
  background: rgba(255,255,255,0.5);
  box-shadow: 0 1px 2px rgba(0,0,0,0.04);
  transition: transform .1s var(--ease-out), background .1s var(--ease-out);
}
html.theme-appui.dark .appui-sheet .appui-sidebar-group[open] .appui-list-row {
  background: rgba(255,255,255,0.055);
}
.appui-sheet .appui-sidebar-group[open] .appui-list-row-icon { width: 44px; height: 44px; border-radius: 16px; margin: 0 auto; }
.appui-sheet .appui-sidebar-group[open] .appui-list-row-icon svg,
.appui-sheet .appui-sidebar-group[open] .appui-list-row-icon i { width: 20px; height: 20px; }
.appui-sheet .appui-sidebar-group[open] .appui-list-row-body { flex: none; }
.appui-sheet .appui-sidebar-group[open] .appui-list-row-label { white-space: normal; font-size: 13px; }
.appui-sheet .appui-sidebar-group[open] .appui-list-row-chevron { display: none; }
/* active:scale-95 tap feedback */
.appui-sheet .appui-sidebar-group[open] .appui-list-row:active { transform: scale(0.95); }
/* Help: a single-destination "group" with nothing to expand, same
   reasoning as the desktop sidebar's pinned Help link. */
.appui-sheet-pinned-link { margin-top: 6px; padding-top: 6px; border-top: var(--border-w) solid var(--border); }
.appui-sheet .appui-sidebar-search {
  height: 40px;
  font-size: 14px;
  border-radius: 10px;
  padding-left: 32px;
}

.appui-sidebar-link {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 10px;
  padding: 6px 10px;
  border-radius: 8px;
  color: var(--text);
  text-decoration: none;
  font-size: 13.5px;
  font-weight: 600;
  min-height: 34px;
  margin-bottom: 1px;
  transition: background .15s var(--ease-out), color .15s var(--ease-out);
  /* Also used on bare <button> elements (toggle/logout) — the classic
     theme's global, unscoped button{background:var(--primary)}
     (style.css) would otherwise paint them navy. */
  background: transparent;
  border: none;
  box-shadow: none;
  font-family: inherit;
  cursor: pointer;
}
.appui-sidebar-link:hover { background: var(--status-gray-bg); text-decoration: none; }
.appui-sidebar-link.is-active { background: var(--accent-soft); color: var(--accent-on-tint); }
.appui-sidebar-link.is-active::before {
  content: "";
  position: absolute;
  left: -12px;
  top: 6px;
  bottom: 6px;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--accent);
}
.appui-sidebar-link svg, .appui-sidebar-link i { width: 17px; height: 17px; flex-shrink: 0; opacity: .72; }
.appui-sidebar-link.is-active svg, .appui-sidebar-link.is-active i,
.appui-sidebar-link:hover svg, .appui-sidebar-link:hover i { opacity: 1; }
.appui-sidebar.is-collapsed .appui-sidebar-link { justify-content: center; }
.appui-sidebar.is-collapsed .appui-sidebar-link span { display: none; }
.appui-sidebar.is-collapsed .appui-sidebar-link.is-active::before { left: -8px; }

/* A single-link "group" with nothing to expand — Help doesn't need an
   accordion for one destination. Sits below the scrollable group list
   (so it's always visible, doesn't scroll away) but above the footer. */
.appui-sidebar-pinned {
  padding: 6px 12px 4px;
  border-top: var(--border-w) solid var(--border);
}

.appui-sidebar-footer {
  margin-top: auto;
  padding: 8px 12px 10px;
  border-top: var(--border-w) solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.appui-sidebar-footer .appui-sidebar-link { color: var(--text-2); font-weight: 600; }
.appui-sidebar-footer .appui-sidebar-link:hover { color: var(--text); }
.appui-sidebar-version {
  text-align: center;
  font-size: 10px;
  color: var(--text-2);
  margin: 6px 0 0;
  letter-spacing: .02em;
}
.appui-sidebar.is-collapsed .appui-sidebar-version { display: none; }

/* Desktop top bar (breadcrumb / page title, replaces mobile appbar) */
.appui-topbar {
  display: none;
  align-items: center;
  height: var(--appbar-h);
  padding: 0 24px;
  border-bottom: var(--border-w) solid var(--border);
  background: var(--card-solid);
  flex-shrink: 0;
}
.appui-topbar-title {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.appui-topbar-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ── 7. Responsive tiers ── */
/* Mobile (<768px): appbar + tabbar, no sidebar/topbar */
@media (max-width: 767.98px) {
  .appui-appbar { display: flex; }
  .appui-tabbar { display: block; }
  .appui-scroll { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 24px); }
}

/* Tablet (768–1024px): keep appbar, hide tab bar, sidebar collapses to icon rail */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .appui-appbar { display: flex; }
  .appui-tabbar { display: none; }
  .appui-sidebar { display: flex; width: 76px; }
  .appui-sidebar .appui-sidebar-brand span,
  .appui-sidebar .appui-sidebar-search-wrap,
  .appui-sidebar .appui-sidebar-group-label,
  .appui-sidebar .appui-sidebar-group-chevron,
  .appui-sidebar-link span { display: none; }
  .appui-sidebar-link { justify-content: center; }
  .appui-sidebar-group summary { justify-content: center; padding: 7px; }
  /* Icon rail: same "force the group body visible" override as .is-collapsed
     (this breakpoint is a fixed rail, not a manual collapse, but needs the
     identical treatment since there's no room for an accordion either). */
  .appui-sidebar-group-body { display: flex !important; padding-left: 0; }
  .appui-sidebar-group + .appui-sidebar-group {
    margin-top: 8px;
    border-top: var(--border-w) solid var(--border);
    padding-top: 8px;
  }
  .appui-scroll { padding-top: calc(var(--appbar-h) + env(safe-area-inset-top) + 12px); padding-bottom: 24px; }
}

/* Desktop (≥1024px): sidebar + topbar, no appbar/tabbar */
@media (min-width: 1024px) {
  .appui-appbar { display: none; }
  .appui-tabbar { display: none; }
  .appui-sidebar { display: flex; }
  .appui-topbar { display: flex; }
  .appui-scroll { padding: 24px 32px 32px; }
}

/* ── 8. Tile (primary nav card — hub/dashboard grids) ── */
.appui-tile-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
}
@media (min-width: 640px) { .appui-tile-grid { grid-template-columns: repeat(2, 1fr); gap: 16px; } }
@media (min-width: 1024px) { .appui-tile-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1536px) { .appui-tile-grid { grid-template-columns: repeat(4, 1fr); } }

.appui-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 96px;
  padding: 18px;
  border-radius: var(--radius);
  background: var(--card);
  border: var(--border-w) solid var(--border);
  box-shadow: var(--shadow);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  color: var(--text);
  text-decoration: none;
  overflow: hidden;
  cursor: pointer;
  transform: translateZ(0) scale(1);
  transition: transform .22s var(--ease-spring), box-shadow .22s var(--ease-out);
  animation: appui-tile-in .38s var(--ease-out) both;
  animation-delay: calc(var(--tile-index, 0) * 0.025s);
}
.appui-tile::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(255,255,255,0.35), rgba(255,255,255,0) 55%);
  opacity: 0;
  transition: opacity .25s var(--ease-out);
  pointer-events: none;
}
html.theme-appui.dark .appui-tile::after {
  background: linear-gradient(135deg, rgba(255,255,255,0.08), rgba(255,255,255,0) 55%);
}
/* Neutralize the classic theme's global a:hover{text-decoration:underline}
   unconditionally — it outranks our base .appui-tile{text-decoration:none}
   on specificity, and on touch/coarse-pointer devices the (hover:hover)
   media query below never matches, so it must not be the only guard. */
.appui-tile:hover, .appui-tile:visited:hover { text-decoration: none; }
@media (hover: hover) {
  .appui-tile:hover { transform: translateZ(0) scale(1.008); box-shadow: var(--shadow-lift); }
  .appui-tile:hover::after { opacity: 1; }
}
.appui-tile:active { transform: translateZ(0) scale(0.975); transition-duration: .08s; }

.appui-tile-icon {
  width: 46px; height: 46px;
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  margin-bottom: 12px;
  background: var(--accent-soft);
  color: var(--accent);
  flex-shrink: 0;
}
.appui-tile-icon svg, .appui-tile-icon i { width: 24px; height: 24px; }

.appui-tile-title {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0 0 4px;
}
.appui-tile-desc {
  font-size: 12.5px;
  color: var(--text-2);
  line-height: 1.4;
  margin: 0;
}
.appui-tile-stat {
  margin-top: auto;
  padding-top: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
}
.appui-tile-chevron { color: var(--text-2); width: 16px; height: 16px; flex-shrink: 0; }

/* Compact "sm" variant for secondary grids */
.appui-tile.appui-tile-sm {
  min-height: 76px;
  padding: 14px;
  flex-direction: row;
  align-items: center;
  gap: 12px;
}
.appui-tile.appui-tile-sm .appui-tile-icon { width: 38px; height: 38px; margin-bottom: 0; }
.appui-tile.appui-tile-sm .appui-tile-icon svg, .appui-tile.appui-tile-sm .appui-tile-icon i { width: 19px; height: 19px; }
.appui-tile.appui-tile-sm .appui-tile-title { font-size: 14px; margin-bottom: 1px; }
.appui-tile.appui-tile-sm .appui-tile-desc { font-size: 11.5px; }

@keyframes appui-tile-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

/* ── 9. List row (settings-style grouped rows) ── */
.appui-list-section {
  margin-bottom: 20px;
}
.appui-list-section-label {
  font-size: 12.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-2);
  padding: 0 4px 6px;
}
.appui-list-group {
  border-radius: var(--radius-sm);
  background: var(--card);
  border: var(--border-w) solid var(--border);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  overflow: hidden;
}
.appui-list-row {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
  width: 100%;
  min-height: 46px;
  padding: 11px 14px;
  border: none;
  border-bottom: var(--border-w) solid var(--border);
  border-radius: 0;
  color: var(--text);
  text-decoration: none;
  text-align: left;
  background: transparent;
  box-shadow: none;
  font: inherit;
  cursor: pointer;
  transition: background .1s var(--ease-out);
}
.appui-list-row:last-child { border-bottom: none; }
.appui-list-row:active { background: var(--status-gray-bg); text-decoration: none; }
.appui-list-row-icon {
  width: 30px; height: 30px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  background: var(--accent-soft);
  color: var(--accent-on-tint);
}
.appui-list-row-icon svg, .appui-list-row-icon i { width: 17px; height: 17px; }
.appui-list-row-body { flex: 1; min-width: 0; }
/* display:block: these are spans, and inline they run together on one
   line ("PhoneTap to call") whenever both are present. */
.appui-list-row-label { display: block; font-size: 14.5px; font-weight: 600; color: var(--text); }
.appui-list-row-sub { display: block; font-size: 12px; color: var(--text-2); margin-top: 1px; }
.appui-list-row-value { font-size: 14px; color: var(--text-2); white-space: nowrap; }
.appui-list-row-chevron { color: var(--text-2); opacity: .55; width: 16px; height: 16px; flex-shrink: 0; }

/* ── 9b. Bulk selection (order lists) ──
   Checkbox sits as a sibling before the row's <a>, not nested inside
   it — an <a> containing a form control is invalid HTML and risky
   across browsers, and it would also need to fight the global SPA-nav
   click delegate for whose handler wins. */
.appui-bulk-row { display: flex; align-items: stretch; }
.appui-bulk-row .appui-list-row { flex: 1; min-width: 0; }
.appui-bulk-checkbox {
  display: none;
  align-items: center;
  padding: 0 2px 0 14px;
  flex-shrink: 0;
}
.appui-bulk-list.is-selecting .appui-bulk-checkbox { display: flex; }
.appui-bulk-checkbox input[type="checkbox"] { width: 20px; height: 20px; accent-color: var(--accent); }
.appui-bulk-bar { display: none; }
.appui-bulk-bar.is-active { display: block; }

/* ── 10. Bottom sheet ── */
.appui-sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0,0,0,0.32);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s var(--ease-out);
}
.appui-sheet-backdrop.is-open { opacity: 1; pointer-events: auto; }

.appui-sheet {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 61;
  max-height: 82vh;
  display: flex;
  flex-direction: column;
  background: var(--card-solid);
  border-radius: 20px 20px 0 0;
  box-shadow: var(--shadow-lift);
  padding-bottom: calc(env(safe-area-inset-bottom) + 8px);
  transform: translateY(100%);
  transition: transform .32s var(--ease-spring);
}
.appui-sheet-backdrop.is-open .appui-sheet { transform: translateY(0); }

.appui-sheet-grip {
  width: 40px; height: 5px;
  border-radius: 999px;
  background: var(--border);
  margin: 10px auto 4px;
  flex-shrink: 0;
}
.appui-sheet-title {
  font-size: 15px;
  font-weight: 700;
  text-align: center;
  padding: 6px 16px 10px;
  flex-shrink: 0;
}
.appui-sheet-body {
  overflow-y: auto;
  padding: 0 16px 16px;
}

/* ── 11. FAB with sub-actions ──
   Mobile (<768px) already has a raised "+" built into the tab bar
   (see .appui-tab-fab) as the primary create action — a second
   floating FAB there is redundant and, worse, its top edge sits
   ~50px above .appui-scroll's bottom padding, permanently covering
   the last row of content even when fully scrolled (reads as "can't
   scroll" / "frozen"). So: hidden on mobile, shown only at tablet/
   desktop widths where there's no tab bar to duplicate. */
.appui-fab-wrap {
  display: none;
  position: fixed;
  right: 20px;
  bottom: 28px;
  z-index: 45;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}
@media (min-width: 768px) {
  .appui-fab-wrap { display: flex; }
}
.appui-fab-sub {
  display: flex;
  align-items: center;
  gap: 10px;
  opacity: 0;
  transform: translateY(8px) scale(0.9);
  pointer-events: none;
  transition: opacity .18s var(--ease-out), transform .18s var(--ease-spring);
}
.appui-fab-wrap.is-open .appui-fab-sub {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}
.appui-fab-sub-label {
  font-size: 12.5px;
  font-weight: 700;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--card-solid);
  color: var(--text);
  box-shadow: var(--shadow);
  white-space: nowrap;
}
.appui-fab-sub-btn {
  width: 40px; height: 40px;
  padding: 0;
  border-radius: 999px;
  border: none;
  background: var(--card-solid);
  color: var(--accent);
  box-shadow: var(--shadow);
  display: grid;
  place-items: center;
  cursor: pointer;
  flex-shrink: 0;
}
.appui-fab-sub-btn svg, .appui-fab-sub-btn i { width: 19px; height: 19px; }

.appui-fab-main {
  width: 56px; height: 56px;
  padding: 0;
  border-radius: 999px;
  border: none;
  background: var(--accent);
  color: #fff;
  box-shadow: var(--shadow-lift);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: transform .25s var(--ease-spring);
}
.appui-fab-main svg, .appui-fab-main i {
  width: 24px; height: 24px;
  transition: transform .25s var(--ease-spring);
}
.appui-fab-wrap.is-open .appui-fab-main { transform: rotate(45deg); }
.appui-fab-main:active { transform: scale(0.92); }
.appui-fab-wrap.is-open .appui-fab-main:active { transform: rotate(45deg) scale(0.92); }

/* Stagger sub-action entrance */
.appui-fab-sub:nth-child(1) { transition-delay: 0s; }
.appui-fab-sub:nth-child(2) { transition-delay: .03s; }
.appui-fab-sub:nth-child(3) { transition-delay: .06s; }
.appui-fab-sub:nth-child(4) { transition-delay: .09s; }

/* ── 12. Badges / status pills ── */
.appui-badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.appui-badge-green { background: var(--status-green-bg); color: var(--status-green-text); }
.appui-badge-red { background: var(--status-red-bg); color: var(--status-red-text); }
.appui-badge-amber { background: var(--status-amber-bg); color: var(--status-amber-text); }
.appui-badge-gray { background: var(--status-gray-bg); color: var(--status-gray-text); }
.appui-badge-violet { background: var(--status-violet-bg); color: var(--status-violet-text); }
.appui-badge-indigo { background: var(--status-indigo-bg); color: var(--status-indigo-text); }

/* ── 12b. Filter chip row (horizontal scroll) + "New" pill button ──
   Shared by every list-style page (Orders, Invoices, Bills, ...). */
.appui-chip-row {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 2px 2px 10px;
  margin-bottom: 8px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.appui-chip-row::-webkit-scrollbar { display: none; }
.appui-chip {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  padding: 7px 14px;
  min-height: 34px;
  border-radius: 999px;
  background: var(--card);
  border: var(--border-w) solid var(--border);
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}
.appui-chip.is-active { background: var(--accent); color: #fff; border-color: var(--accent); }

.appui-page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
.appui-page-head h1 {
  margin: 0;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -.02em;
}
.appui-new-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 8px 16px;
  min-height: 36px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-weight: 700;
  font-size: 13px;
  text-decoration: none;
  border: none;
  cursor: pointer;
}
.appui-new-btn svg, .appui-new-btn i {
  width: 15px;
  height: 15px;
  display: inline-block;
  line-height: 0;
  vertical-align: middle;
}

/* Chart loading skeleton: a shimmer fills the canvas's wrapper the moment
   the page paints (before chart.umd.min.js has even loaded, let alone run),
   so a slow connection shows motion instead of a blank box. The chart's own
   inline <script> adds .is-loaded to the same wrapper right after
   `new Chart(...)` constructs, at which point the shimmer is replaced by
   the chart itself. */
.chart-wrap, .cp-chart-wrap {
  background: linear-gradient(90deg, var(--border) 25%, var(--card-solid) 37%, var(--border) 63%);
  background-size: 400% 100%;
  animation: appui-chart-skeleton 1.4s ease infinite;
  border-radius: 12px;
}
.chart-wrap.is-loaded, .cp-chart-wrap.is-loaded {
  background: none;
  animation: none;
}
@keyframes appui-chart-skeleton {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .chart-wrap, .cp-chart-wrap { animation: none; background: var(--border); }
}

.appui-empty-state {
  text-align: center;
  padding: 40px 16px;
  color: var(--text-2);
}
/* A single generic "nothing here yet" glyph (open box / inbox outline),
   masked through --text-2 so it stays theme-correct in light/dark without
   a second raster asset. Applies to every .appui-empty-state with zero
   per-page markup - a page can still opt out with an explicit
   data-appui-empty-icon="false" attribute if a glyph would ever be wrong
   there (e.g. a state that's actually an error, not "empty"). */
.appui-empty-state::before {
  content: "";
  display: block;
  width: 52px;
  height: 52px;
  margin: 0 auto 14px;
  background-color: var(--text-2);
  opacity: .38;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 12h-4l-2 3h-4l-2-3H4'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11Z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 12h-4l-2 3h-4l-2-3H4'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11Z'/%3E%3C/svg%3E");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.appui-empty-state[data-appui-empty-icon="false"]::before { display: none; }
.appui-empty-state p { margin: 0 0 12px; font-size: 14px; }

/* ── 12c. Busy-state feedback ──
   Any form submitted inside the shell marks its submit button busy
   (spinner appended, input blocked) so every action visibly "does
   something" the instant it's tapped. */
@keyframes appui-spin { to { transform: rotate(360deg); } }
html.theme-appui .appui-btn-busy {
  pointer-events: none;
  opacity: .75;
}
html.theme-appui .appui-btn-busy::after {
  content: "";
  display: inline-block;
  width: 14px; height: 14px;
  margin-left: 8px;
  vertical-align: -2px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 999px;
  animation: appui-spin .7s linear infinite;
}
/* Pulsing live-status dot (e.g. Update Center progress strip) */
@keyframes appui-pulse { 0%,100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.75); } }
html.theme-appui .appui-live-dot {
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: 999px;
  background: var(--accent);
  animation: appui-pulse 1.1s ease-in-out infinite;
  flex-shrink: 0;
}

/* ── 13. Skeleton loaders ── */
.appui-skeleton {
  background: linear-gradient(90deg, var(--status-gray-bg) 25%, rgba(142,142,147,0.28) 37%, var(--status-gray-bg) 63%);
  background-size: 400% 100%;
  animation: appui-shimmer 1.4s ease infinite;
  border-radius: var(--radius-sm);
}
@keyframes appui-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
.appui-skeleton-stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin-bottom: 16px;
}
@media (min-width: 640px) { .appui-skeleton-stats { grid-template-columns: repeat(4, 1fr); } }
.appui-skeleton-stats .appui-skeleton { height: 76px; }
.appui-skeleton-chart { height: 180px; margin-bottom: 16px; }
.appui-skeleton-line { height: 14px; margin-bottom: 10px; }
.appui-skeleton-line.w-60 { width: 60%; }
.appui-skeleton-line.w-80 { width: 80%; }
.appui-skeleton-line.w-40 { width: 40%; }

/* ── 14. Content transition (SPA-style nav) ──
   IMPORTANT: no standing will-change/transform on .appui-content-region.
   will-change:transform makes the region a containing block for
   position:fixed descendants (per spec), which re-anchored every
   bottom sheet defined inside page content (add/edit customer, vendor,
   payments, …) to the region instead of the viewport — sheets floated
   mid-screen with a partial backdrop. will-change is applied only via
   the transient animation classes, when no sheet can be open. */
.appui-content-region.appui-nav-out-fwd { animation: appui-slide-out-left .22s var(--ease-out) both; will-change: transform, opacity; }
.appui-content-region.appui-nav-in-fwd { animation: appui-slide-in-right .26s var(--ease-out) both; will-change: transform, opacity; }
.appui-content-region.appui-nav-out-back { animation: appui-slide-out-right .22s var(--ease-out) both; will-change: transform, opacity; }
.appui-content-region.appui-nav-in-back { animation: appui-slide-in-left .26s var(--ease-out) both; will-change: transform, opacity; }

@keyframes appui-slide-out-left { to { opacity: 0; transform: translateX(-16px); } }
@keyframes appui-slide-in-right { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: translateX(0); } }
@keyframes appui-slide-out-right { to { opacity: 0; transform: translateX(16px); } }
@keyframes appui-slide-in-left { from { opacity: 0; transform: translateX(-16px); } to { opacity: 1; transform: translateX(0); } }

/* ── Touch target floor ── */
.appui-appbar-btn, .appui-tab, .appui-fab-sub-btn, .appui-sidebar-link, .appui-list-row {
  min-height: 40px;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
  html.theme-appui *, html.theme-appui *::before, html.theme-appui *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

/* ── Command palette (Cmd/Ctrl+K) ── */
.appui-palette-overlay {
  position: fixed; inset: 0; z-index: 9999;
  background: rgba(0,0,0,.45);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 12vh 16px 16px;
}
.appui-palette-overlay[hidden] { display: none; }
.appui-palette {
  width: 100%; max-width: 560px;
  background: var(--card-solid); border: 1px solid var(--border); border-radius: 16px;
  box-shadow: 0 20px 60px rgba(0,0,0,.35);
  overflow: hidden;
  display: flex; flex-direction: column;
  max-height: 70vh;
}
.appui-palette-input-wrap {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px; border-bottom: 1px solid var(--border);
}
.appui-palette-input-wrap i { width: 18px; height: 18px; color: var(--text-2); flex-shrink: 0; }
.appui-palette-input {
  flex: 1; border: none; background: none; outline: none;
  font-size: 16px; color: var(--text);
}
.appui-palette-esc {
  font-size: 11px; color: var(--text-2); border: 1px solid var(--border);
  border-radius: 6px; padding: 2px 6px; font-family: inherit;
}
.appui-palette-results { overflow-y: auto; padding: 8px; }
.appui-palette-empty { padding: 20px; text-align: center; color: var(--text-2); font-size: 13.5px; }
.appui-palette-group-label {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-2); padding: 8px 10px 4px;
}
.appui-palette-item {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 10px 12px; border-radius: 10px; text-decoration: none; color: var(--text);
}
.appui-palette-item:hover, .appui-palette-item.is-active { background: var(--hover-bg, rgba(127,127,127,.12)); }
.appui-palette-item-title { font-weight: 600; font-size: 14px; }
.appui-palette-item-sub { font-size: 12.5px; color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── Plain-language term tooltip (i) ── uses the native title attribute
   for the popup text itself: several call sites live inside
   overflow:hidden tile cards, and a custom CSS-positioned popup gets
   clipped there, while the browser's native tooltip always escapes it. */
.appui-term-tip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-left: 4px; border-radius: 50%;
  background: var(--border); color: #5F5F63;
  font-size: 10px; font-weight: 700; font-style: normal; line-height: 1;
  cursor: help; vertical-align: middle;
}
html.theme-appui.dark .appui-term-tip { color: #A3A3A9; }

/* Contextual help link: a small "?" next to a page's <h1> that jumps
   straight to that page's section in /guide, instead of making someone
   open the Guide from the sidebar and hunt for the right heading. */
.appui-help-link {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; margin-left: 6px; border-radius: 50%;
  background: var(--border); color: var(--text-2);
  font-size: 12px; font-weight: 800; font-style: normal; line-height: 1;
  text-decoration: none; vertical-align: middle; flex-shrink: 0;
}
.appui-help-link:hover, .appui-help-link:focus-visible {
  background: var(--accent-soft); color: var(--accent-on-tint);
}
