/* ==========================================================================
   AI Accounting — shared design system
   Loaded last on every page, so it standardises what the per-page inline
   styles and Tailwind utilities left inconsistent.

   1. Tokens          2. Base / reset      3. Typography
   4. Layout          5. Sidebar           6. Buttons
   7. Tables          8. Forms             9. Cards & headers
  10. Utilities      11. Print
   ========================================================================== */

/* ---------- 1. Tokens ---------------------------------------------------- */
:root {
  /* Brand — kept from the existing indigo/violet identity */
  --aia-primary:        #4f46e5;
  --aia-primary-hover:  #4338ca;
  --aia-primary-soft:   #eef2ff;
  --aia-accent:         #7c3aed;

  /* Semantic */
  --aia-success:        #059669;
  --aia-success-soft:   #ecfdf5;
  --aia-warning:        #d97706;
  --aia-warning-soft:   #fffbeb;
  --aia-danger:         #dc2626;
  --aia-danger-soft:    #fef2f2;
  --aia-info:           #0284c7;
  --aia-info-soft:      #f0f9ff;

  /* Neutrals */
  --aia-bg:             #f6f7fb;
  --aia-surface:        #ffffff;
  --aia-border:         #e4e7ec;
  --aia-border-strong:  #cbd2dc;
  --aia-text:           #101828;
  --aia-text-muted:     #667085;
  --aia-text-subtle:    #98a2b3;

  /* Type scale */
  --aia-fs-xs:   0.75rem;    /* 12 */
  --aia-fs-sm:   0.8125rem;  /* 13 */
  --aia-fs-base: 0.9375rem;  /* 15 */
  --aia-fs-md:   1rem;       /* 16 */
  --aia-fs-lg:   1.125rem;   /* 18 */
  --aia-fs-xl:   1.25rem;    /* 20 */

  /* Spacing */
  --aia-sp-1: 0.25rem;
  --aia-sp-2: 0.5rem;
  --aia-sp-3: 0.75rem;
  --aia-sp-4: 1rem;
  --aia-sp-5: 1.5rem;
  --aia-sp-6: 2rem;

  /* Shape */
  --aia-radius-sm: 6px;
  --aia-radius:    10px;
  --aia-radius-lg: 14px;

  --aia-shadow-sm: 0 1px 2px rgba(16, 24, 40, .05);
  --aia-shadow:    0 1px 3px rgba(16, 24, 40, .08), 0 1px 2px rgba(16, 24, 40, .04);
  --aia-shadow-lg: 0 8px 24px -6px rgba(16, 24, 40, .12), 0 2px 6px rgba(16, 24, 40, .05);

  /* Controls */
  --aia-control-h:    2.375rem;  /* 38px — the one button/input height   */
  --aia-control-h-sm: 2rem;      /* 32px — compact, for in-table actions */

  --aia-header-h: 56px;          /* mobile top bar */
}

/* ---------- 2. Base / reset --------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-size: var(--aia-fs-base);
  line-height: 1.55;
  color: var(--aia-text);
  /* Nothing may scroll the page sideways. Wide content scrolls inside itself. */
  overflow-x: hidden;
}

/* Media never overflows its column. */
img, svg, video, canvas, iframe { max-width: 100%; height: auto; }

/* Long unbroken strings (account codes, URLs, emails) wrap instead of pushing.
   Scoped to text containers — applying this broadly made button labels break
   one character per line inside narrow columns. */
body :is(td, th, p, li, dd, dt) { overflow-wrap: break-word; }

/* Controls and their labels never break mid-word. */
body :is(button, .btn, label, th, a[class*="px-"]) ,
body :is(button, .btn, a[class*="px-"]) * {
  overflow-wrap: normal;
  word-break: normal;
  hyphens: none;
}

:focus-visible {
  outline: 2px solid var(--aia-primary);
  outline-offset: 2px;
  border-radius: var(--aia-radius-sm);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-delay: 0s !important;          /* staggered entrances otherwise
                                                sit off-screen for their delay */
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
}

/* Entrance animations must never leave content parked outside the viewport:
   if the animation never runs (reduced motion, print, a JS error), the element
   must still be in its final, visible position. */
html body :is([class*="slide-in"], [class*="fade-in"], [class*="stagger"]) {
  /* `forwards`, not `both`: with `both` the element holds its *start* position
     for the whole stagger delay, which parks it off-screen. */
  animation-fill-mode: forwards;
  /* And the resting state must be the visible one. These cards animate in from
     translateX(-100%); with a stagger delay that left them off the left edge
     of the viewport until their turn came. Keyframes still win while the
     animation is actually running, so the entrance is unchanged. */
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce), print {
  body :is([class*="slide-in"], [class*="fade-in"], [class*="stagger"]) {
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
}

/* ---------- 3. Typography ------------------------------------------------ */
/* Page titles were text-3xl/4xl/5xl on different pages. One responsive scale. */
body :is(h1, .page-title, .text-4xl, .text-5xl) { font-size: clamp(1.35rem, 1.05rem + 1.2vw, 1.75rem); line-height: 1.25; }
body :is(h2, .text-3xl)                          { font-size: clamp(1.2rem,  1rem   + 0.8vw, 1.5rem);  line-height: 1.3;  }
body :is(h3, .text-2xl)                          { font-size: clamp(1.05rem, 0.95rem + 0.5vw, 1.25rem); line-height: 1.35; }
body :is(h4, .text-xl)                           { font-size: 1.0625rem; line-height: 1.4; }
body .text-lg                                    { font-size: var(--aia-fs-lg); }
body .text-base                                  { font-size: var(--aia-fs-md); }
body .text-sm                                    { font-size: var(--aia-fs-sm); }
body .text-xs                                    { font-size: var(--aia-fs-xs); }

body :is(h1, h2, h3, h4, h5, h6) {
  text-wrap: balance;
  font-weight: 600;
  letter-spacing: -0.011em;
}

/* Figures line up in columns. */
body :is(td, th, .tabular, .amount, .num) { font-variant-numeric: tabular-nums; }

/* ---------- 4. Layout ---------------------------------------------------- */
/* sidebar.css carries a "FINAL LOCK" block:
       body .content-area { margin-left:280px !important; width:calc(100vw - 280px) !important }
   Its own mobile override is written as `.content-area` — lower specificity than
   `body .content-area`, so it never won and the content stayed pushed 280px to
   the right on every phone and tablet. These rules use `html body` to outrank it.
   Using 100vw was also wrong: it includes the scrollbar, so the row overflowed. */
html body :is(.content-area, .main-content) {
  margin-left: var(--sidebar-width, 280px) !important;
  width: auto !important;
  max-width: none !important;
  padding: clamp(0.875rem, 0.4rem + 1.6vw, 1.75rem) !important;
}

/* Clear the sidebar exactly once. sidebar.css also offsets <body>, so a page
   that has both the body class and a content wrapper was indented twice and
   ran 280px off the right edge. Where a wrapper exists, it owns the offset;
   pages without one keep the offset on <body>. */
html body.has-app-sidebar:has(:is(.content-area, .main-content)) {
  margin-left: 0 !important;
}

/* Pages that never adopted .content-area still get sane gutters. */
body.has-app-sidebar > :is(main, .container, .wrapper, .page) {
  padding-inline: clamp(0.875rem, 0.4rem + 1.6vw, 1.75rem);
}

/* Keep dense report pages readable on very wide monitors. */
@media (min-width: 1800px) {
  html body :is(.content-area, .main-content) > * { max-width: 1600px; }
}

/* Standard block rhythm for the common Tailwind paddings, scaled down on small screens. */
@media (max-width: 640px) {
  body :is(.p-8, .p-6)  { padding: var(--aia-sp-4) !important; }
  body :is(.p-5, .p-4)  { padding: var(--aia-sp-3) !important; }
  body :is(.px-8, .px-6){ padding-inline: var(--aia-sp-4) !important; }
  body :is(.py-8, .py-6){ padding-block:  var(--aia-sp-4) !important; }
  body .gap-6           { gap: var(--aia-sp-3) !important; }
  body :is(.mb-8, .mb-6){ margin-bottom: var(--aia-sp-4) !important; }
}

/* Grids collapse rather than squeeze. */
@media (max-width: 900px) {
  body :is(.grid-cols-3, .grid-cols-4, .grid-cols-5, .grid-cols-6) { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
}
@media (max-width: 600px) {
  body :is(.grid-cols-2, .grid-cols-3, .grid-cols-4, .grid-cols-5, .grid-cols-6) { grid-template-columns: 1fr !important; }
}

/* ---------- 5. Sidebar --------------------------------------------------- */
/* The mobile rules already existed but were defeated by inline styles from
   sidebar.php and by an !important margin on body. Both are corrected here. */

.aia-topbar { display: none; }

@media (max-width: 1024px) {
  .aia-topbar {
    display: flex;
    align-items: center;
    gap: var(--aia-sp-3);
    position: fixed;
    inset: 0 0 auto 0;
    height: var(--aia-header-h);
    padding-inline: var(--aia-sp-3);
    background: #0f172a;
    color: #fff;
    z-index: 1002;
    box-shadow: var(--aia-shadow);
  }

  .aia-topbar__title { font-size: var(--aia-fs-md); font-weight: 600; letter-spacing: -0.01em; }

  .aia-burger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px; height: 40px;
    flex: 0 0 40px;
    border: 1px solid rgba(255,255,255,.18);
    border-radius: var(--aia-radius-sm);
    background: rgba(255,255,255,.08);
    color: #fff;
    font-size: 1.05rem;
    cursor: pointer;
  }
  .aia-burger:hover { background: rgba(255,255,255,.16); }

  /* Off-canvas, and beat the inline styles sidebar.php used to write. */
  body .modern-sidebar,
  body aside.modern-sidebar {
    transform: translateX(-100%) !important;
    transition: transform .24s ease !important;
    width: min(300px, 86vw) !important;
    min-width: 0 !important;
    max-width: 86vw !important;
    z-index: 1003 !important;
  }
  body .modern-sidebar.mobile-open,
  body aside.modern-sidebar.mobile-open { transform: translateX(0) !important; }

  /* The base rules set these with !important, so the resets need to outrank them. */
  html body.has-app-sidebar { margin-left: 0 !important; }

  html body :is(.content-area, .main-content) {
    margin-left: 0 !important;
    width: 100% !important;
    max-width: 100% !important;
    /* Clear the fixed top bar. */
    padding: calc(var(--aia-header-h) + var(--aia-sp-3)) var(--aia-sp-3) var(--aia-sp-5) !important;
  }

  html body.has-app-sidebar > main { padding-top: calc(var(--aia-header-h) + var(--aia-sp-3)); }

  .aia-scrim {
    position: fixed; inset: 0;
    background: rgba(15, 23, 42, .5);
    backdrop-filter: blur(2px);
    z-index: 1002;
    opacity: 0; pointer-events: none;
    transition: opacity .2s ease;
  }
  .aia-scrim.active { opacity: 1; pointer-events: auto; }

  body.aia-nav-open { overflow: hidden; }

  /* The floating back-to-top button collides with the burger. */
  .back-to-top-btn { display: none !important; }
}

/* Smoother nav interaction on pointer devices. */
@media (min-width: 1025px) and (prefers-reduced-motion: no-preference) {
  .modern-sidebar .nav-item { transition: background-color .15s ease, transform .15s ease; }
  .modern-sidebar .nav-item:hover { transform: translateX(2px); }
}

.modern-sidebar .nav-item:focus-visible { outline: 2px solid #fbbf24; outline-offset: -2px; }

/* ---------- 6. Buttons --------------------------------------------------- */
/* One height, one radius, one weight. Only elements that already carry a
   px-/py- utility are touched, so icon-only square buttons keep their sizing. */
body :is(button, a, input[type="submit"], input[type="button"])[class*="px-"][class*="py-"]:not(:has(p)):not(:has(h1, h2, h3, h4, h5, h6)) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--aia-sp-2);
  min-height: var(--aia-control-h);
  padding-block: 0 !important;
  padding-inline: var(--aia-sp-4) !important;
  font-size: var(--aia-fs-sm) !important;
  font-weight: 500;
  line-height: 1.2;
  border-radius: var(--aia-radius) !important;
  white-space: nowrap;
  /* A button is never narrower than its own label. Without this, a button
     dropped into a tight grid column collapsed into a vertical letter stack. */
  min-width: fit-content;
  flex-shrink: 0;
  cursor: pointer;
  transition: filter .15s ease, box-shadow .15s ease, transform .15s ease;
}

/* Compact variants keep a smaller footprint. */
body :is(button, a)[class*="px-2"][class*="py-1"],
body :is(button, a)[class*="px-3"][class*="py-1"] {
  min-height: var(--aia-control-h-sm);
  padding-inline: var(--aia-sp-3) !important;
  font-size: var(--aia-fs-xs) !important;
}

body :is(button, a)[class*="px-"][class*="py-"]:hover:not(:disabled) { filter: brightness(.94); }
body :is(button, a)[class*="px-"][class*="py-"]:active:not(:disabled) { transform: translateY(1px); }
body button:disabled,
body button[disabled] { opacity: .55; cursor: not-allowed; filter: none !important; }

/* Icon-only buttons: perfectly centred, consistent radius. */
body :is(button, a)[class*="w-8"][class*="h-8"],
body :is(button, a)[class*="w-9"][class*="h-9"],
body :is(button, a)[class*="w-10"][class*="h-10"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  border-radius: var(--aia-radius-sm);
}

/* Several pages use their own .modern-btn / .btn classes rather than utilities.
   They get the same height, radius and label protection. */
body :is(.modern-btn, .btn, .action-btn) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--aia-sp-2);
  min-height: var(--aia-control-h);
  padding-inline: var(--aia-sp-4);
  font-size: var(--aia-fs-sm);
  font-weight: 500;
  border-radius: var(--aia-radius);
  white-space: nowrap;
  min-width: fit-content;
  text-overflow: clip;
}

/* A grid cell holding buttons must not be squeezed below its own content, but
   it must never widen the track either — forcing max-content here blew whole
   `xl:col-span-*` layouts past the viewport. min-content is the safe floor:
   the buttons stack instead of the grid growing. */
body [class*="col-span-"]:has(:is(.modern-btn, .btn, button, a[class*="px-"])) {
  min-width: 0;
}

/* Button rows wrap instead of overflowing on narrow screens. */
body :is(.flex, .inline-flex):has(> :is(button, a[class*="px-"], .modern-btn, .btn)) { flex-wrap: wrap; }

/* Grid and flex children default to min-width:auto, which refuses to shrink
   below their content and is the usual cause of a layout pushing sideways. */
body :is([class*="grid-cols-"], .grid) > * { min-width: 0; }
body :is(.flex, .inline-flex) > :not(:is(button, a[class*="px-"], .modern-btn, .btn)) { min-width: 0; }

/* Pagination and other deep button groups wrap too, rather than running off
   both edges of a phone. */
@media (max-width: 900px) {
  html body .content-area :is(.flex, .inline-flex):has(:is(button, a[class*="px-"], .modern-btn, .btn)) {
    flex-wrap: wrap;
    row-gap: var(--aia-sp-2);
  }
}

@media (max-width: 640px) {
  /* Full-width actions read better on a phone than a row of stubs. */
  body .content-area :is(button, a)[class*="px-"][class*="py-"]:not([class*="w-"]) { flex: 1 1 auto; }
}

/* ---------- 7. Tables ---------------------------------------------------- */
/* aia.js wraps every bare table in this, so wide reports scroll inside
   themselves and never push the page sideways. */
.aia-table-scroll {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: var(--aia-radius);
  scrollbar-width: thin;
}
.aia-table-scroll > table { min-width: max-content; }
.aia-table-scroll::-webkit-scrollbar { height: 8px; }
.aia-table-scroll::-webkit-scrollbar-thumb { background: var(--aia-border-strong); border-radius: 99px; }

body table { border-collapse: collapse; width: 100%; font-size: var(--aia-fs-sm); }
body :is(th, td) { padding: 0.625rem 0.75rem; vertical-align: middle; }
body th { font-weight: 600; text-align: left; white-space: nowrap; }
body tbody tr { border-bottom: 1px solid var(--aia-border); }
body tbody tr:last-child { border-bottom: 0; }

/* Keep sticky headers usable inside the scroll container. */
body thead th { position: sticky; top: 0; z-index: 1; background: inherit; }

/* ---------- 8. Forms ----------------------------------------------------- */
body :is(input:not([type="color"]):not([type="checkbox"]):not([type="radio"]):not([type="color"]):not([type="submit"]):not([type="button"]),
         select, textarea) {
  min-height: var(--aia-control-h);
  /* Vertical padding only. Setting it horizontally here outranked the pl-*
     that icon fields use to reserve room, dropping the icon onto the text. */
  padding-block: 0.4375rem;
  font-size: var(--aia-fs-base);
  font-family: inherit;
  color: var(--aia-text);
  background: var(--aia-surface);
  border: 1px solid var(--aia-border-strong);
  border-radius: var(--aia-radius-sm);
  width: 100%;
  max-width: 100%;
  transition: border-color .15s ease, box-shadow .15s ease;
}
body textarea { min-height: 5rem; padding-block: 0.5rem; }

body :is(input:not([type="color"]), select, textarea):focus {
  outline: none;
  border-color: var(--aia-primary);
  box-shadow: 0 0 0 3px rgba(79, 70, 229, .14);
}
body :is(input:not([type="color"]), select, textarea):disabled { background: #f2f4f7; color: var(--aia-text-subtle); }
body ::placeholder { color: var(--aia-text-subtle); }

body :is(input[type="checkbox"], input[type="radio"]) {
  width: 1rem; height: 1rem; accent-color: var(--aia-primary); cursor: pointer;
}
body label { font-size: var(--aia-fs-sm); font-weight: 500; color: var(--aia-text); }

/* iOS zooms the page when a field under 16px is focused. */
@media (max-width: 640px) {
  body :is(input:not([type="color"]), select, textarea) { font-size: 16px; }
}

/* ---------- 9. Cards & page headers -------------------------------------- */
body :is(.card, .glass-card, .bg-white[class*="rounded"][class*="shadow"]) {
  background: var(--aia-surface);
  border: 1px solid var(--aia-border);
  border-radius: var(--aia-radius-lg);
  box-shadow: var(--aia-shadow);
}

/* The gradient banner at the top of most pages. */
body :is(.page-header, [class*="bg-gradient-to-r"][class*="rounded"]) {
  border-radius: var(--aia-radius-lg);
}
@media (max-width: 640px) {
  body [class*="bg-gradient-to-r"][class*="rounded"] { padding: var(--aia-sp-4) !important; }
}

/* ---------- 9b. Modals & popups ------------------------------------------ */
/* The overlays are `fixed inset-0 flex items-center justify-center p-4`, which
   is a sound pattern, but almost none of them capped their height. A tall form
   on a short screen pushed its own Save button off-screen with no way to reach
   it. Every overlay now scrolls, and its panel is capped to the viewport. */
/* Every modal in the app was left at z-index:auto while the sidebar sits at
   1000, so each one opened *behind* the navigation and lost its left edge.
   One stacking rule for all of them; the confirm dialog stays above at 4000. */
html body :is(.modal, .modal-backdrop, .modal-overlay, .image-modal-overlay),
html body [id*="modal" i][class*="fixed"][class*="inset-0"],
html body [id*="Modal"][class*="fixed"][class*="inset-0"] {
  /* The overlays carry Tailwind's z-50, which loses to the sidebar's 1000.
     !important so no utility class can put a dialog back under the nav. */
  z-index: 3000 !important;
}

/* One scrim, so a dialog does not dim the page a different amount per page. */
html body :is(.modal, .modal-backdrop, .modal-overlay),
html body [id*="modal" i][class*="fixed"][class*="inset-0"]:not(.image-modal-overlay) {
  background-color: rgba(16, 24, 40, .55);
}

body :is(.modal-backdrop, .modal-overlay, .image-modal-overlay),
body [class*="fixed"][class*="inset-0"][class*="flex"] {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--aia-sp-4);
  align-items: flex-start;
}

/* Centre it again once there is room to centre it in. */
@media (min-height: 720px) {
  body :is(.modal-backdrop, .modal-overlay, .image-modal-overlay),
  body [class*="fixed"][class*="inset-0"][class*="items-center"] { align-items: center; }
}

body :is(.modal-container, .modal-content, .image-modal-content),
body :is(.modal-backdrop, .modal-overlay) > :is(div, form, section) {
  max-height: calc(100vh - 2 * var(--aia-sp-4));
  max-height: calc(100dvh - 2 * var(--aia-sp-4));
  overflow-y: auto;
  overscroll-behavior: contain;
  width: 100%;
}

/* A modal's own header and footer stay put while the body scrolls. */
body :is(.modal-container, .modal-content) > :is(.modal-header, .modal-footer) {
  position: sticky;
  z-index: 2;
  /* No background here: these headers carry their own gradient with white
     text on it, and painting the surface colour over it hid the title. */
}
body :is(.modal-container, .modal-content) > .modal-header { top: 0; }
body :is(.modal-container, .modal-content) > .modal-footer { bottom: 0; }

@media (max-width: 640px) {
  /* Full-bleed sheets read better than a cramped centred card on a phone. */
  body :is(.modal-container, .modal-content) { border-radius: var(--aia-radius-lg); }
  body :is(.modal-backdrop, .modal-overlay) { padding: var(--aia-sp-2); }
}

/* Dropdown menus must not run off the right edge. */
body :is(.dropdown-menu, [id$="-menu"], [id$="-dropdown"]) {
  max-width: calc(100vw - var(--aia-sp-4));
  max-height: min(70vh, 32rem);
  overflow-y: auto;
}

/* Toasts and notification bubbles sit inside the viewport on every screen. */
body :is(#notification-box, .toast, .notification) {
  max-width: calc(100vw - var(--aia-sp-4));
  word-break: break-word;
}

body.aia-modal-open { overflow: hidden; }

/* Status pills read at a glance. */
.aia-pill {
  display: inline-flex; align-items: center; gap: 0.375rem;
  padding: 0.125rem 0.5rem;
  font-size: var(--aia-fs-xs); font-weight: 600;
  border-radius: 99px; white-space: nowrap;
}
.aia-pill--ok      { background: var(--aia-success-soft); color: var(--aia-success); }
.aia-pill--warn    { background: var(--aia-warning-soft); color: var(--aia-warning); }
.aia-pill--danger  { background: var(--aia-danger-soft);  color: var(--aia-danger);  }
.aia-pill--info    { background: var(--aia-info-soft);    color: var(--aia-info);    }

/* ---------- 10. Utilities ------------------------------------------------ */
.aia-only-mobile  { display: none; }
.aia-only-desktop { display: initial; }
@media (max-width: 1024px) {
  .aia-only-mobile  { display: initial; }
  .aia-only-desktop { display: none; }
}

.aia-sr {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- 11. Print ---------------------------------------------------- */
@media print {
  .modern-sidebar, .aia-topbar, .aia-scrim, .back-to-top-btn,
  .no-print, [class*="sidebar"] { display: none !important; }
  body.has-app-sidebar { margin-left: 0 !important; }
  .content-area { padding: 0 !important; max-width: none !important; }
  .aia-table-scroll { overflow: visible !important; }
  body { font-size: 11pt; }
  a[href]::after { content: ""; }
}

/* ---------- 11b. Full-height app shells ----------------------------------
   The AI Assistant lays its three columns out as
       <div class="flex h-[calc(100vh-120px)]">
   with each column capped at calc(100vh - 240px) / calc(100vh - 320px).

   Three things go wrong with that:
     1. 120px is a guess at the header height. The real gradient header is
        taller, so the row runs past the bottom of the window and the right
        column's last cards are cut off.
     2. 100vh ignores mobile browser chrome and our own 56px top bar.
     3. A scroll container that centres its content with `items-center` pushes
        any overflow *upwards*, out of reach — which is why the welcome text
        was clipped mid-sentence at the top.

   aia.js measures the real offset into --aia-shell-offset; these rules use it,
   switch to dvh, and use `safe` centring so overflow always stays reachable. */

body [class*="h-[calc(100vh"] {
  height: calc(100vh - var(--aia-shell-offset, 120px)) !important;
  height: calc(100dvh - var(--aia-shell-offset, 120px)) !important;
  min-height: 320px;
}

/* Let the flex parent govern the columns rather than a second 100vh guess. */
body [class*="h-[calc(100vh"] [style*="max-height: calc(100vh"],
body [class*="h-[calc(100vh"] [style*="max-height:calc(100vh"] {
  max-height: none !important;
  min-height: 0 !important;
}

/* Safe centring: centre when it fits, align to the start when it does not, so
   the top of the content is never scrolled out of reach. */
body :is(.overflow-y-auto, [class*="overflow-y-auto"]).flex.items-center { align-items: safe center; }
body :is(.overflow-y-auto, [class*="overflow-y-auto"]).flex.justify-center { justify-content: safe center; }
body :is(.modal-container, .modal-content).flex.items-center { align-items: safe center; }

@media (max-width: 1024px) {
  /* Three side-by-side columns do not fit a phone or a tablet — stack them. */
  body [class*="h-[calc(100vh"] {
    height: auto !important;
    min-height: 0;
    flex-direction: column;
  }
  body [class*="h-[calc(100vh"] > * {
    width: 100% !important;
    max-width: 100% !important;
    flex: 0 0 auto !important;
  }
  body [class*="h-[calc(100vh"] [style*="max-height: calc(100vh"],
  body [class*="h-[calc(100vh"] [style*="max-height:calc(100vh"] {
    max-height: 65vh !important;
    max-height: 65dvh !important;
  }
}

/* ---------- 12. Mobile layout reset (last word) --------------------------
   Appended deliberately at the end of the file: the earlier mobile rule uses
   the same selector as the desktop one, and on at least one page the desktop
   declaration was still winning. Higher specificity plus last position makes
   this unambiguous. */
@media (max-width: 1024px) {
  html body.has-app-sidebar .content-area,
  html body.has-app-sidebar .main-content,
  html body .content-area,
  html body .main-content {
    margin-left: 0 !important;
    margin-right: 0 !important;
    width: auto !important;
    max-width: 100% !important;
    padding: calc(var(--aia-header-h) + var(--aia-sp-3)) var(--aia-sp-3) var(--aia-sp-5) !important;
  }
  html body.has-app-sidebar { margin-left: 0 !important; }
}

/* ---------- 13. Very narrow screens --------------------------------------
   Buttons hold `min-width: fit-content` so a tight grid cell cannot collapse
   their label into a letter stack. Below 480px a long label can still be wider
   than the whole screen, so there the label wraps instead. */
@media (max-width: 480px) {
  body :is(button, a, input[type="submit"], input[type="button"], .modern-btn, .btn)[class*="px-"] ,
  body :is(.modern-btn, .btn) {
    min-width: 0 !important;
    max-width: 100%;
    white-space: normal;
    text-align: center;
    height: auto;
    line-height: 1.3;
    padding-block: var(--aia-sp-2) !important;
  }
  body :is([class*="col-span-"], .grid > *, .flex > *) { min-width: 0 !important; }
}

/* ==========================================================================
   14. CONTROL NORMALISATION (final word)
   Measured across all 53 pages, the app was rendering 18 different button
   heights, 4 button font sizes, 5 button radii, 9 input heights, 4 card radii
   and two different body font sizes. The earlier rules only matched controls
   that happened to carry a px-/py- utility; everything using .modern-btn, a
   bare py-4, or its own inline padding slipped through. These selectors cover
   every control and are last in the file so they settle the cascade.
   ========================================================================== */

/* --- one base font size, everywhere --- */
html body { font-size: var(--aia-fs-base); }

/* --- one control height, radius and label size --- */
html body :is(
  button, .btn, .modern-btn, .action-btn,
  input[type="submit"], input[type="button"], input[type="reset"],
  a[class*="px-"], a[class*="btn"]
):not(.aia-burger):not(.back-to-top-btn):not(.nav-item):not([class*="rounded-full"]):not(:has(p)):not(:has(h1, h2, h3, h4, h5, h6)) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--aia-sp-2);
  min-height: var(--aia-control-h);
  height: auto;
  padding-block: 0 !important;
  padding-inline: var(--aia-sp-4) !important;
  font-size: var(--aia-fs-sm) !important;
  font-weight: 500;
  line-height: 1.2;
  border-radius: var(--aia-radius) !important;
  cursor: pointer;
}

/* Square icon buttons stay square; round ones stay round. */
html body :is(button, a)[class*="rounded-full"] { border-radius: 9999px !important; }
html body :is(button, a):is([class*="w-8"], [class*="w-9"], [class*="w-10"], [class*="w-11"]):is([class*="h-8"], [class*="h-9"], [class*="h-10"], [class*="h-11"]) {
  padding-inline: 0 !important;
  min-height: 0;
  aspect-ratio: 1;
}

/* Compact in-table actions keep a smaller footprint. */
html body :is(button, a)[class*="text-xs"]:not([class*="rounded-full"]) {
  min-height: var(--aia-control-h-sm);
  padding-inline: var(--aia-sp-3) !important;
  font-size: var(--aia-fs-xs) !important;
}

/* Sidebar navigation is its own component — leave it alone. */
html body .modern-sidebar :is(a, button) {
  min-height: 0;
  padding-inline: revert !important;
  font-size: revert !important;
  border-radius: revert !important;
}

/* --- one field height and radius --- */
/* Shared field sizing. Padding is handled separately below, because many
   fields reserve space for a leading icon with their own pl-* class and
   overriding it puts the icon on top of the placeholder. */
html body :is(input:not([type="color"]):not([type="checkbox"]):not([type="radio"]):not([type="color"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]), select) {
  min-height: var(--aia-control-h) !important;
  padding-block: 0 !important;
  font-size: var(--aia-fs-base);
  border-radius: var(--aia-radius-sm) !important;
}

/* Only fields that set no horizontal padding of their own get ours. */
html body :is(input:not([type="color"]):not([type="checkbox"]):not([type="radio"]):not([type="color"]):not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]), select):not([class*="pl-"]):not([class*="pr-"]):not([class*="px-"]):not(.table-input) {
  padding-inline: var(--aia-sp-3) !important;
}
html body textarea {
  min-height: 5rem;
  padding: var(--aia-sp-2) var(--aia-sp-3) !important;
  font-size: var(--aia-fs-base);
  border-radius: var(--aia-radius-sm) !important;
}
@media (max-width: 640px) {
  /* iOS zooms on focus below 16px. */
  html body :is(input:not([type="color"]), select, textarea) { font-size: 16px; }
}

/* --- one card radius --- */
html body :is(.card, .glass-card, [class*="rounded-xl"], [class*="rounded-2xl"], [class*="rounded-3xl"]):not(button):not(a):not(input):not([class*="w-8"]):not([class*="w-10"]):not([class*="w-12"]):not([class*="w-14"]):not([class*="w-16"]) {
  border-radius: var(--aia-radius-lg);
}

/* --- one table type scale --- */
/* Page-level rules set table type at 12, 12.8, 13 and 13.6px. One scale wins. */
html body :is(th, td) { font-size: var(--aia-fs-sm) !important; }
html body th { font-weight: 600; }

/* ---------- 15. Confirmation dialog --------------------------------------
   Destructive actions used the browser's native grey confirm() box on five
   pages and a styled modal on one. This is the single styled dialog; aia.js
   routes the inline handlers through it. */
.aia-confirm-backdrop {
  position: fixed;
  inset: 0;
  z-index: 4000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--aia-sp-4);
  background: rgba(16, 24, 40, .55);
  backdrop-filter: blur(3px);
  overflow-y: auto;
}
.aia-confirm {
  background: var(--aia-surface);
  border-radius: var(--aia-radius-lg);
  box-shadow: var(--aia-shadow-lg);
  width: 100%;
  max-width: 26rem;
  padding: var(--aia-sp-5);
  text-align: center;
  animation: aia-pop .16s ease-out;
}
@keyframes aia-pop { from { transform: scale(.96); opacity: 0 } to { transform: none; opacity: 1 } }
@media (prefers-reduced-motion: reduce) { .aia-confirm { animation: none } }

.aia-confirm__icon {
  width: 3rem; height: 3rem;
  margin: 0 auto var(--aia-sp-3);
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--aia-danger-soft);
  color: var(--aia-danger);
  font-size: 1.25rem;
}
.aia-confirm__msg {
  margin: 0 0 var(--aia-sp-5);
  font-size: var(--aia-fs-md);
  line-height: 1.5;
  color: var(--aia-text);
  white-space: pre-line;
}
.aia-confirm__actions { display: flex; gap: var(--aia-sp-3); }
.aia-confirm__actions > button {
  flex: 1 1 0;
  min-height: var(--aia-control-h);
  border: 0;
  border-radius: var(--aia-radius);
  font-size: var(--aia-fs-sm);
  font-weight: 600;
  cursor: pointer;
}
.aia-confirm__cancel { background: #eef0f4; color: var(--aia-text); }
.aia-confirm__cancel:hover { background: #e2e6ec; }
.aia-confirm__ok { background: var(--aia-danger); color: #fff; }
.aia-confirm__ok:hover { filter: brightness(.94); }

/* The back-to-top-btn keeps its circle; the control normalisation above
   excluded it from sizing but a radius was still leaking in. */
html body .back-to-top-btn {
  border-radius: 999px !important;
  width: 44px !important;
  height: 44px !important;
  min-height: 0 !important;
  padding: 0 !important;
  font-size: var(--aia-fs-md) !important;
}

/* ==========================================================================
   16. ONE GUTTER
   Measured across every page: the gap between the sidebar and the first
   content card was 44px on 15 pages, 28px on 4, 0 on one, and anywhere from
   37 to 498px on the pages that never adopted a content wrapper. This makes
   it a single token applied the same way everywhere.
   ========================================================================== */
:root { --aia-gutter: clamp(1rem, 0.55rem + 1.25vw, 1.75rem); }

/* The three wrappers in use all get the same padding. */
html body :is(.content-area, .main-content, .content-wrapper) {
  padding: var(--aia-gutter) !important;
}

/* Pages with a sidebar but no wrapper at all: their top-level blocks become
   the content region, so the gutter goes there instead. */
html body.has-app-sidebar:not(:has(.content-area)):not(:has(.main-content)):not(:has(.content-wrapper))
  > *:not(aside):not(.modern-sidebar):not(.aia-topbar):not(.aia-scrim):not(script):not(style):not(link):not(template):not(.aia-confirm-backdrop) {
  padding-left: var(--aia-gutter);
  padding-right: var(--aia-gutter);
}

/* Nothing stacks a second gutter on top of the wrapper's own padding.
   Centred forms (mx-auto) keep their auto margins. */
html body :is(.content-area, .main-content, .content-wrapper)
  > *:not([class*="mx-auto"]):not(.aia-topbar):not(.aia-scrim) {
  margin-left: 0 !important;
  margin-right: 0 !important;
}

@media (max-width: 1024px) {
  /* The mobile rule already zeroes the sidebar offset; keep the side gutter. */
  html body :is(.content-area, .main-content, .content-wrapper) {
    padding: calc(var(--aia-header-h) + var(--aia-sp-3)) var(--aia-gutter) var(--aia-sp-5) !important;
  }
}

/* Many pages nest a `max-w-[1900px] mx-auto p-4` container directly inside the
   wrapper, which added a second 16px gutter on top of the wrapper's own — so
   those pages sat 43px from the sidebar while the rest sat at 27px. Keep the
   centring and the max-width; drop the duplicated side padding. */
html body :is(.content-area, .main-content, .content-wrapper)
  > [class*="max-w-"] {
  padding-left: 0 !important;
  padding-right: 0 !important;
  width: 100%;
}

/* Same again one level deeper, which is how a few of the older pages nest. */
html body :is(.content-area, .main-content, .content-wrapper)
  > * > [class*="max-w-"][class*="mx-auto"] {
  padding-left: 0 !important;
  padding-right: 0 !important;
}

/* A few pages put the sidebar inside a `flex min-h-screen` row rather than
   using a content wrapper. Padding the row indents the sidebar as well, so the
   gutter goes on the content column instead. */
html body :has(> .modern-sidebar) {
  padding-left: 0 !important;
  padding-right: 0 !important;
}
html body :has(> .modern-sidebar) > *:not(.modern-sidebar):not(.aia-topbar):not(.aia-scrim):not(script):not(style) {
  padding-left: var(--aia-gutter);
  padding-right: var(--aia-gutter);
  flex: 1 1 auto;
  min-width: 0;
}

/* ==========================================================================
   17. FIT
   An element-level pass across all 53 pages (desktop and phone, with modals
   opened) found 82 places where content was wider than its box with no way to
   reach it. They came down to four causes, fixed here rather than per page.
   ========================================================================== */

/* (a) Flex and grid children default to min-width:auto, which refuses to
       shrink below their content. This is the single biggest cause of a row
       pushing past its container. */
html body :is(.flex, .inline-flex, .grid, [class*="grid-cols"]) > * { min-width: 0; }

/* .table-input is the ledger's inline-edit field and sets its own tighter
   padding; excluded above so the two rules are not fighting on specificity. */

/* (b) A text input carries an intrinsic width from its `size` attribute
       (~20 characters), so it will not shrink into a narrow column however
       much width:100% it is given. */
html body :is(input:not([type="color"]), select, textarea) { min-width: 0; }

/* (c) Big display figures and long headings could not wrap, so they ran past
       the edge on a phone. Headings balance; figures break if they must. */
html body :is(h1, h2, h3, .text-3xl, .text-4xl, .text-5xl) { overflow-wrap: break-word; }
html body :is([class*="text-3xl"], [class*="text-4xl"], [class*="text-5xl"]) {
  overflow-wrap: break-word;
  min-width: 0;
}

/* (d) A card given a fixed height clipped its own content. Let cards grow. */
html body :is(.card, .glass-card, [class*="rounded-xl"], [class*="rounded-2xl"])[class*="h-"]:not([class*="w-"]) {
  min-height: 0;
  height: auto;
}

/* Tables always keep a scroll container, including ones the page already
   wrapped in a div that does not actually scroll. */
html body .aia-table-scroll { overflow-x: auto !important; }
html body :is(.data-table, .table-wrap, .table-container) { overflow-x: auto; }

/* ==========================================================================
   18. FRACTIONAL COLUMNS
   Several pages split the screen with `h-screen flex` and two `w-1/2`
   children. A fractional width is not responsive — it stays at 50% on a
   phone, which squeezed the Company Settings form into 180px and broke its
   labels mid-word. Below the tablet breakpoint these stack full width.
   ========================================================================== */
@media (max-width: 900px) {
  html body :is([class*="w-1/2"], [class*="w-1/3"], [class*="w-2/3"],
                [class*="w-1/4"], [class*="w-3/4"], [class*="w-2/5"], [class*="w-3/5"]) {
    width: 100% !important;
    max-width: 100% !important;
  }

  /* The row itself stacks, and stops being pinned to the viewport height so
     the page scrolls normally instead of trapping two tiny scroll panes. */
  html body .flex:has(> [class*="w-1/"]),
  html body :is(.h-screen, [class*="h-screen"]).flex {
    flex-direction: column;
    height: auto !important;
    min-height: 0 !important;
  }

  /* Those columns often carry their own scroll; let them size to content. */
  html body .flex > [class*="w-1/"][class*="overflow-y-auto"] {
    overflow-y: visible !important;
    height: auto !important;
    max-height: none !important;
  }
}

/* ==========================================================================
   19. TABLES INSIDE SCROLL CONTAINERS
   A table carrying `w-full` inside a horizontally scrolling container shrinks
   to the container instead of overflowing it, so the container never scrolls
   and the cells are crushed — on a phone the Chart of Accounts rendered its
   dates one character per line. Inside a scroller a table must be allowed to
   exceed the box; that is what makes the scroll work.
   ========================================================================== */
html body :is(.aia-table-scroll, .data-table, .table-wrap, .table-container,
               [class*="overflow-x-auto"], [class*="overflow-auto"]) table {
  /* max(container, content): a wide table overflows and the box scrolls, a
     narrow one still fills the width it was given. `width: auto` used to be
     here and shrank the P&L, Balance Sheet and Cash Flow tables to about a
     fifth of the page. */
  min-width: max-content !important;
  width: 100% !important;
}

/* Cells keep their content on one line so the column widths mean something. */
html body :is(.aia-table-scroll, .data-table, [class*="overflow-x-auto"]) :is(th, td) {
  white-space: nowrap;
}
/* …except genuinely long free text, which may wrap within a sensible width. */
html body :is(.aia-table-scroll, .data-table, [class*="overflow-x-auto"])
  :is(td, th)[class*="description"],
html body :is(.aia-table-scroll, .data-table, [class*="overflow-x-auto"])
  :is(td, th):has(> textarea) {
  white-space: normal;
  min-width: 14rem;
}

/* ==========================================================================
   20. ONE PAGE BACKGROUND
   Measured across the app: 26 pages sat on slate-50, 8 on gray-100, one on
   gray-50 and four on white — four different "page" greys. App pages now
   share one. Public marketing/auth pages keep white, the print views keep
   white for paper, and the log viewer keeps its dark console.
   ========================================================================== */
html body.has-app-sidebar,
html body:has(.modern-sidebar) {
  background-color: var(--aia-bg) !important;
}
html body:has(.modern-sidebar) :is(.content-area, .main-content, .content-wrapper) {
  background-color: transparent;
}

/* Cards sit on that ground as one surface colour, not five translucent ones. */
html body:has(.modern-sidebar) :is(.card, .glass-card) {
  background-color: var(--aia-surface);
}

@media print {
  html body { background: #fff !important; }
}

/* Any horizontal scroller gets the same visible thumb the table helper has,
   so a table wider than its box reads as scrollable instead of cut off. */
html body :is([class*="overflow-x-auto"], [class*="overflow-auto"], .table-wrap, .table-container) {
  scrollbar-width: thin;
  scrollbar-color: var(--aia-border-strong) transparent;
  overscroll-behavior-x: contain;
}
html body :is([class*="overflow-x-auto"], [class*="overflow-auto"])::-webkit-scrollbar { height: 9px; }
html body :is([class*="overflow-x-auto"], [class*="overflow-auto"])::-webkit-scrollbar-track {
  background: var(--aia-bg); border-radius: 99px;
}
html body :is([class*="overflow-x-auto"], [class*="overflow-auto"])::-webkit-scrollbar-thumb {
  background: var(--aia-border-strong); border-radius: 99px;
}
html body :is([class*="overflow-x-auto"], [class*="overflow-auto"])::-webkit-scrollbar-thumb:hover {
  background: var(--aia-text-subtle);
}

/* E. The sidebar's own nav was clipping its longest labels ("Cash Flow
   Statement") against a fixed 280px rail. Let the label wrap instead. */
html body .modern-sidebar :is(.sidebar-nav, .nav-item, .nav-link, a) { min-width: 0; }
html body .modern-sidebar :is(.nav-link, .nav-item) > span,
html body .modern-sidebar a > span { overflow-wrap: anywhere; }

/* A button used as a card carries its own heading and a paragraph of its own
   prose. Forcing it into the inline-flex control shape laid the label and the
   description out side by side, which on the AI assistant page squeezed
   "Financial Overview" into 66px and stacked it two lines deep. These keep
   block layout and their own padding. */
html body button:is(:has(p), :has(h1, h2, h3, h4, h5, h6)) {
  display: block;
  text-align: left;
  white-space: normal;
  height: auto;
  cursor: pointer;
}

/* A headline figure must not break across lines. Trial Balance showed
   "$12,920.00" as "$12,920.0" over "0" once six stat cards shared a row. */
html body :is(p, div, span, h2, h3)[class*="text-2xl"][class*="font-bold"],
html body :is(p, div, span, h2, h3)[class*="text-3xl"][class*="font-bold"] {
  white-space: nowrap;
}

/* The editable General Ledger grid.
   Ten columns of text inputs want ~1490px. On anything narrower than a very
   wide monitor that pushed Debit, Credit and Actions off the right edge -
   the debit and credit columns, on an accounting page, with only a
   scrollbar to hint they existed.
   So the grid earns its columns. Date, Journal, Description, Account, Debit,
   Credit and Actions are the ledger and always show. Buyer, Seller and Note
   appear only when there is genuinely room for them; below that they stay in
   the DOM (so they still submit and nothing is lost on save) and are edited
   through the row's edit action. */
html body :is([class*="overflow-x-auto"], .aia-table-scroll) table.ledger-grid {
  min-width: 1040px !important;
  width: 100% !important;
  table-layout: fixed;
}
html body table.ledger-grid :is(th, td) {
  padding-inline: var(--aia-sp-2) !important;
  vertical-align: top;
}
html body table.ledger-grid :is(input, select):not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([readonly]):not([disabled]) {
  width: 100%;
  min-width: 0;
  padding-inline: 0.5rem !important;
}
/* Money reads right-aligned. */
html body table.ledger-grid :is(th, td):nth-child(7) input,
html body table.ledger-grid :is(th, td):nth-child(8) input { text-align: right; }

/* The seven that always show. Description takes what is left over. */
html body table.ledger-grid :is(th, td):nth-child(1)  { width: 130px; }
html body table.ledger-grid :is(th, td):nth-child(2)  { width: 132px; }
html body table.ledger-grid :is(th, td):nth-child(3)  { width: auto;  }
html body table.ledger-grid :is(th, td):nth-child(6)  { width: 252px; }
html body table.ledger-grid :is(th, td):nth-child(7),
html body table.ledger-grid :is(th, td):nth-child(8)  { width: 110px; }
html body table.ledger-grid :is(th, td):nth-child(10) { width: 124px; }
/* Three 32px icon buttons plus their gaps need 112px; at 96px the delete
   dropped onto a second line and the "Actions" header lost its last letter. */

/* Buyer, Seller and Note: present, not shown, until the width is there. */
html body table.ledger-grid :is(th, td):nth-child(4),
html body table.ledger-grid :is(th, td):nth-child(5),
html body table.ledger-grid :is(th, td):nth-child(9) { display: none; }

@media (min-width: 1850px) {
  html body :is([class*="overflow-x-auto"], .aia-table-scroll) table.ledger-grid {
    min-width: 1490px !important;
  }
  html body table.ledger-grid :is(th, td):nth-child(4),
  html body table.ledger-grid :is(th, td):nth-child(5),
  html body table.ledger-grid :is(th, td):nth-child(9) { display: table-cell; }
  html body table.ledger-grid :is(th, td):nth-child(3) { width: 214px; }
  html body table.ledger-grid :is(th, td):nth-child(4) { width: 158px; }
  html body table.ledger-grid :is(th, td):nth-child(5) { width: 210px; }
  html body table.ledger-grid :is(th, td):nth-child(6) { width: 264px; }
  html body table.ledger-grid :is(th, td):nth-child(9) { width: 72px; }
}

/* The new-entry account lines had the same problem as the ledger: Account
   442px, Note 332px and the table 1326px inside a 1158px box, so the Note
   column ran off the card. Debit, Credit and the row button have known
   widths; Account and Note share the rest. */
html body table.entry-lines {
  table-layout: fixed;
  width: 100% !important;
  min-width: 0 !important;
}
html body table.entry-lines :is(th, td):nth-child(2),
html body table.entry-lines :is(th, td):nth-child(3) { width: 130px; }
html body table.entry-lines :is(th, td):nth-child(5) { width: 56px; }
html body table.entry-lines :is(input, select) { width: 100%; min-width: 0; }

/* New Journal Entry left only 6px between the page subtitle and the AI
   Auto-fill row, where every other page gives its heading 24-40px before the
   first panel. Something on that page zeroes the Tailwind mb-4, so the
   separation is set here instead. */
html body .aia-page-head { margin-bottom: var(--aia-sp-6) !important; }
html body .aia-page-head + * { margin-top: 0; }

/* .btn-primary / .btn-secondary carry no px-/py- utility, so the control
   normalisation above never reached them and their labels were free to wrap:
   "Run Report" broke across two lines on Profit & Loss and Cash Flow. A
   button label is a label, not a paragraph. */
html body :is(.btn-primary, .btn-secondary, .btn-success, .btn-danger, .btn-warning) {
  white-space: nowrap;
  min-width: fit-content;
}

/* ==========================================================================
   21. CONTRAST
   A contrast pass over all 53 pages found these four utilities failing WCAG
   AA for normal text. Each is nudged one step darker, which keeps the brand
   hue and clears 4.5:1 on a white or near-white surface.
     text-green-600  #16a34a  3.3:1 -> green-700  #15803d  4.6:1
     text-slate-400  #94a3b8  2.6:1 -> slate-500  #64748b  4.8:1
     text-yellow-600 #ca8a04  2.9:1 -> yellow-700 #a16207  4.6:1
     white on bg-green-600    3.3:1 -> on green-700        4.6:1
   ========================================================================== */
html body .text-green-600  { color: #15803d; }
html body .text-slate-400  { color: #64748b; }
html body .text-yellow-600 { color: #a16207; }
html body .bg-green-600    { background-color: #15803d; }
html body .hover\:bg-green-700:hover { background-color: #126a33; }

/* Second pass: these each missed 4.5:1 on white or on the page ground. */
html body .text-gray-500   { color: #4b5563; }  /* 4.4 -> 6.4 */
html body .text-slate-500  { color: #475569; }  /* 4.4 -> 7.0 */
html body .text-teal-600   { color: #0f766e; }  /* 3.7 -> 4.8 */
html body .text-orange-600 { color: #c2410c; }  /* 3.6 -> 4.7 */
html body .text-cyan-600   { color: #0e7490; }  /* 3.4 -> 4.6 */
html body .text-green-400  { color: #15803d; }  /* 1.7 -> 4.6 on a light card */

/* A colour picker keeps the square footprint its own utilities give it; the
   field normalisation above was stretching the Company Settings swatch from
   48x48 to 144x38, so it read as an empty text box. */
html body input[type="color"] {
  width: 3rem !important;
  height: 3rem !important;
  min-height: 0 !important;
  padding: 2px !important;
  flex: 0 0 auto;
  cursor: pointer;
}

/* ---------------------------------------------------------------------------
   24. DOUBLE SIDEBAR OFFSET
   Pages laid out as `.flex.min-h-screen > main.flex-grow` were indented twice:
   once by the body margin the sidebar adds (aia.css §855, applied because these
   pages have no .content-area), and again by sidebar.css's own
   `main.flex-grow { margin-left: 280px; width: calc(100vw - 280px) }`.
   Content ended at 1792px in a 1512px viewport and body{overflow-x:hidden}
   silently clipped the right 280px — no scrollbar, so it read as "fits".
   Only the pages that lack .content-area/.main-content/.content-wrapper take the
   body margin (see §855), so the override is scoped to exactly that condition —
   the other 8 flex-grow pages still need sidebar.css's 280px.
   Affected: adjust_stock, edit_product, financial_analysis, help_center.
   --------------------------------------------------------------------------- */
html body.has-app-sidebar:not(:has(.content-area)):not(:has(.main-content)):not(:has(.content-wrapper))
  :is(main.flex-grow, div.flex-grow) {
  margin-left: 0 !important;
  width: 100% !important;
  max-width: 100% !important;
}

/* ---------------------------------------------------------------------------
   25. BUTTON CONTRAST (WCAG AA)
   White 13px text needs 4.5:1. Tailwind's -500/-600 steps land around 3:1,
   and the per-page .btn-secondary / .action-btn styles sit lower still. Each
   family below keeps its hue and drops to the -700/-800 step, which clears
   4.5:1 on white text. Scoped to controls so cards, headers and badges keep
   the lighter palette.
   --------------------------------------------------------------------------- */
:root {
  --aia-ok:      #15803d;  /* green-700   white 4.9:1 */
  --aia-ok-dk:   #166534;  /* green-800               */
  --aia-info:    #1d4ed8;  /* blue-700    white 6.3:1 */
  --aia-info-dk: #1e40af;  /* blue-800                */
  --aia-warn:    #b45309;  /* amber-700   white 4.9:1 */
  --aia-bad:     #b91c1c;  /* red-700     white 5.9:1 */
  --aia-teal:    #0f766e;  /* teal-700    white 5.3:1 */
  --aia-violet:  #6d28d9;  /* violet-700  white 6.5:1 */
}

/* --- solid Tailwind backgrounds on controls --- */
html body :is(button, a, input[type="submit"], label).bg-green-600:not(:disabled),
html body :is(button, a, input[type="submit"], label).bg-green-500:not(:disabled) {
  background-color: var(--aia-ok) !important;
}
html body :is(button, a, input[type="submit"], label).bg-orange-600:not(:disabled),
html body :is(button, a, input[type="submit"], label).bg-orange-500:not(:disabled),
html body :is(button, a, input[type="submit"], label).bg-amber-500:not(:disabled) {
  background-color: var(--aia-warn) !important;
}
html body :is(button, a, input[type="submit"], label).bg-blue-500:not(:disabled),
html body :is(button, a, input[type="submit"], label).bg-blue-600:not(:disabled) {
  background-color: var(--aia-info) !important;
}
html body :is(button, a, input[type="submit"], label).bg-red-500:not(:disabled) {
  background-color: var(--aia-bad) !important;
}
html body :is(button, a, input[type="submit"], label).bg-purple-500:not(:disabled),
html body :is(button, a, input[type="submit"], label).bg-violet-500:not(:disabled) {
  background-color: var(--aia-violet) !important;
}

/* --- gradient stops on controls: redefine the two custom properties Tailwind
       composes --tw-gradient-stops from, so the gradient survives, darker. --- */
html body :is(button, a, input[type="submit"]):is(.from-green-500, .from-green-600, .from-emerald-500):not(:disabled) {
  --tw-gradient-from: var(--aia-ok) var(--tw-gradient-from-position) !important;
}
html body :is(button, a, input[type="submit"]):is(.to-green-600, .to-green-700, .to-emerald-600):not(:disabled) {
  --tw-gradient-to: var(--aia-ok-dk) var(--tw-gradient-to-position) !important;
}
html body :is(button, a, input[type="submit"]):is(.from-blue-500, .from-blue-600, .from-indigo-500):not(:disabled) {
  --tw-gradient-from: var(--aia-info) var(--tw-gradient-from-position) !important;
}
html body :is(button, a, input[type="submit"]):is(.to-blue-600, .to-cyan-600, .to-indigo-600):not(:disabled) {
  --tw-gradient-to: var(--aia-info-dk) var(--tw-gradient-to-position) !important;
}
html body :is(button, a, input[type="submit"]):is(.from-red-500, .from-red-600):not(:disabled) {
  --tw-gradient-from: var(--aia-bad) var(--tw-gradient-from-position) !important;
}
html body :is(button, a, input[type="submit"]):is(.to-red-600, .to-red-700, .to-pink-500, .to-pink-600):not(:disabled) {
  --tw-gradient-to: #991b1b var(--tw-gradient-to-position) !important;
}
html body :is(button, a, input[type="submit"]):is(.from-teal-500, .from-cyan-500):not(:disabled) {
  --tw-gradient-from: var(--aia-teal) var(--tw-gradient-from-position) !important;
}
html body :is(button, a, input[type="submit"]):is(.to-teal-600, .to-teal-700):not(:disabled) {
  --tw-gradient-to: #115e59 var(--tw-gradient-to-position) !important;
}
html body :is(button, a, input[type="submit"]):is(.from-purple-500, .from-purple-600, .from-violet-500):not(:disabled) {
  --tw-gradient-from: var(--aia-violet) var(--tw-gradient-from-position) !important;
}
html body :is(button, a, input[type="submit"]):is(.to-purple-600, .to-purple-700, .to-violet-600):not(:disabled) {
  --tw-gradient-to: #5b21b6 var(--tw-gradient-to-position) !important;
}

/* --- the per-page semantic button classes, which each page restyles --- */
html body :is(button, a, input[type="submit"]).btn-secondary:not(:disabled),
html body :is(button, a, input[type="submit"]).btn-success:not(:disabled) {
  background-image: none !important;
  background-color: var(--aia-ok) !important;
  color: #fff !important;
}
html body :is(button, a, input[type="submit"]).btn-danger:not(:disabled),
html body :is(button, a, input[type="submit"]).action-btn.btn-danger:not(:disabled) {
  background-image: none !important;
  background-color: var(--aia-bad) !important;
  color: #fff !important;
}
html body :is(button, a, input[type="submit"]).btn-warning:not(:disabled) {
  background-image: none !important;
  background-color: var(--aia-warn) !important;
  color: #fff !important;
}

/* --- muted text that fell under AA on its own ground --- */
html body :is(.text-gray-400, .text-slate-400):not(.modern-sidebar *) { color: #64748b !important; }
html body :is(button, a) .text-slate-500,
html body :is(button, a).text-slate-500 { color: #475569 !important; }

/* --- the per-page gradient button classes -------------------------------
   .btn-primary, .btn-delete and .demo-btn are redefined in each page's own
   <style> block, with a different hue per page and all of them under 4.5:1
   on white text. One accessible definition each, which also makes the three
   read the same everywhere instead of drifting page to page. */
html body :is(button, a, input[type="submit"]).btn-primary:not(:disabled) {
  background: linear-gradient(135deg, #1d4ed8 0%, #1e40af 100%) !important;
  color: #fff !important;
}
html body :is(button, a, input[type="submit"]):is(.btn-delete, .btn-danger):not(:disabled) {
  background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%) !important;
  color: #fff !important;
}
html body :is(button, a, input[type="submit"]).demo-btn:not(:disabled) {
  background: linear-gradient(135deg, #5b21b6 0%, #6d28d9 50%, #7c3aed 100%) !important;
  color: #fff !important;
}
/* inactive report tab: slate-500 on gray-100 reads 4.32:1 */
html body .tab-inactive { color: #475569 !important; }
/* section header bands carrying white text */
html body .bg-gradient-to-r.from-emerald-500.to-teal-600 {
  --tw-gradient-from: #047857 var(--tw-gradient-from-position) !important;
  --tw-gradient-to:   #115e59 var(--tw-gradient-to-position) !important;
}
/* muted grey needed one more step to clear AA on tinted grounds */
html body :is(.text-gray-400, .text-slate-400):not(.modern-sidebar *) { color: #475569 !important; }

/* .success-btn and .modal-header share one emerald gradient, redefined per
   page, that reads 3.07:1 under white text. */
html body :is(button, a, input[type="submit"]).success-btn:not(:disabled),
html body .modal-header {
  background: linear-gradient(135deg, #047857 0%, #065f46 100%) !important;
  color: #fff !important;
}

/* Tailwind green gradients used as table headings carry white 13px text at
   4.04:1; the control rules above did not reach <tr>/<thead>. */
html body :is(tr, thead, th).from-green-600,
html body :is(tr, thead, th).from-green-500 {
  --tw-gradient-from: #15803d var(--tw-gradient-from-position) !important;
}
html body :is(tr, thead, th).to-green-700,
html body :is(tr, thead, th).to-green-600 {
  --tw-gradient-to: #14532d var(--tw-gradient-to-position) !important;
}

/* ---------------------------------------------------------------------------
   26. AN ESCAPE HATCH FOR THE BUTTON-ROW WRAP RULE
   §373 gives flex-wrap:wrap to any flex row holding a button, which keeps
   button groups from overflowing in content areas. Applied to a page toolbar
   it does the opposite: the Business Intelligence header wrapped its user menu
   onto a second line under Quick Add. Tailwind's own flex-nowrap now wins, so
   a row that must stay on one line can say so.
   --------------------------------------------------------------------------- */
/* Only from tablet up. On a 320px phone the top bar genuinely cannot fit on
   one line -- forcing it there pushed the search field 23px off screen -- so
   below 768px the default wrapping from section 373 is the right behaviour. */
@media (min-width: 768px) {
  html body :is(.flex, .inline-flex).flex-nowrap { flex-wrap: nowrap !important; }
}

/* ---------------------------------------------------------------------------
   27. TOUCH TARGETS IN TABLE ROWS
   The row actions (print, reverse, delete, remove line) are 32px icon buttons.
   That is comfortable with a mouse and fiddly with a thumb, so on tablet and
   phone widths they grow to 40px. Scoped to cells, which is where row actions
   live -- page toolbars keep their existing proportions.
   --------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  /* !important because section 255 pins min-width:0 and the control-height
     variables pin min-height:38px, both with !important. */
  html body :is(td, th) :is(button, a[class*="btn"], a[class*="px-"]):not(.select2-hidden-accessible) {
    min-width: 40px !important;
    min-height: 40px !important;
  }
}

/* ---------------------------------------------------------------------------
   28. Empty ledger: hide the column header
   The ledger table is table-layout:fixed with an auto-width Description
   column. When journalEntries is empty the only body row is a single
   <td colspan="10">, which gives that auto column nothing to size against --
   it collapsed to 67px and clipped its own "DESCRIPTION" header to "DESC",
   and the header row ended short of the card. renderMainTable() adds
   .is-empty in that state.
   --------------------------------------------------------------------------- */
html body table.ledger-grid.is-empty thead { display: none; }
html body table.ledger-grid.is-empty { min-width: 0 !important; }

/* ==========================================================================
   17. ONE PAGE BANNER
   The four reports each coloured .page-header differently (violet, purple,
   teal). Give them the same banner as every other page: purple-600 to
   blue-600, the same radius, padding and title size.
   ========================================================================== */
html body .content-area .page-header {
  background: linear-gradient(to right, #9333ea, #2563eb) !important;
  background-size: auto !important;
  animation: none !important;
  border-radius: 0.75rem !important;
  padding: 1.5rem !important;
  box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}
html body .content-area .page-header h1 { font-size: 1.875rem !important; line-height: 2.25rem; }

/* Counter cards (.stats-card) on Chart of Accounts, Roles and Team were
   translucent white-on-white with a white border, so they read as faint
   floating text. Give them the same white card + hairline border as the rest. */
html body .content-area .stats-card {
  background: #fff !important;
  border: 1px solid #e5e7eb !important;
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.05) !important;
  backdrop-filter: none !important;
}
