/* ========================================================================
 *  KX-PAY V2 dashboard  ·  03-sidebar.css
 * -----------------------------------------------------------------------
 *  The left navigation rail. Replaces the top tab bar, which was carrying
 *  fifteen items in a row that also had to hold the logo, the wallet badge
 *  and the user menu.
 *
 *  WIDTH IS ONE VARIABLE, SET ON <html>
 *
 *    --sb-w  is read by #sidebar and nothing else needs to know about it.
 *    html[data-sidebar="collapsed"] changes it, and that attribute is
 *    written in TWO places:
 *      · the inline script at the top of index.html, before first paint
 *      · core/sidebar.js, when the toggle is clicked
 *    Same attribute, same rules, so there is no expanded-then-collapsed
 *    flicker on refresh. This mirrors how data-initial-tab and data-role
 *    already work -- see 00-anti-flash.css.
 *
 *  WHY NOT A CLASS ON #app
 *    Because #app does not exist until the browser has parsed <body>, and
 *    the point is to be correct in the first frame.
 * =======================================================================
 */

  :root {
    --sb-w: 236px;          /* expanded */
    --sb-w-collapsed: 68px;
  }
  html[data-sidebar="collapsed"] { --sb-w: var(--sb-w-collapsed); }

  /* ------------------------------------------------------------- LAYOUT */
  /* #app is the two-column frame. It is display:none until login, and
     00-anti-flash.css re-shows it as FLEX for users with a live session --
     if that override still said `block`, a returning user would get the
     sidebar stacked on top of the content and only on refresh, which is a
     miserable bug to reproduce. */
  #app {
    display: flex;
    align-items: flex-start;
    min-height: 100vh;
  }

  /* A soft blue panel, a step deeper than the page behind it, with each
     link a small white card on top.
     Flat and OPAQUE rather than a tint over the page gradient: the page
     backdrop is a gradient anchored to the viewport (background-attachment:
     fixed, 01-base.css), so a translucent fill would leave the rail a
     different shade at the top than at the bottom, and the shade would
     change again as the window resized. One colour cannot do that. */
  #sidebar {
    flex: 0 0 var(--sb-w);
    width: var(--sb-w);
    position: sticky; top: 0;
    /* 100dvh, not 100vh.
       On iOS Safari 100vh is the height the page WOULD have if the browser
       toolbars were hidden -- it counts the strip behind the address bar and
       the bottom bar. A full-height rail therefore extends below the visible
       screen, and anything pinned to its bottom edge goes with it. That is
       what put Logout underneath Safari's own toolbar, cropped and
       untappable, with no way to scroll to it: the rail is fixed, so the
       page scroll does not move it.
       dvh is the DYNAMIC viewport height -- what is actually on screen right
       now, toolbars included. The 100vh line above stays as the fallback for
       anything too old to know dvh. */
    height: 100vh;
    height: 100dvh;
    /* GRID, not column-flex, and the middle row is minmax(0, 1fr).
       Three rows: wordmark, menu, account block. The menu is the one that
       gives way when there is not enough height, and minmax(0, 1fr) is the
       explicit way to say "this row may be shorter than its contents".

       It was `display:flex; flex-direction:column` with the menu as
       `flex: 1 1 auto; overflow-y: auto`. That is correct by the spec -- an
       item with a non-visible overflow has an automatic minimum size of zero,
       so it shrinks and scrolls -- and it behaves in Chrome. iOS Safari has a
       long-standing bug where such an item refuses to shrink below its
       content anyway. The menu is about 930px of cards; on a ~660px screen it
       stayed 930px tall and shoved the account block off the bottom of the
       drawer, where nothing could scroll to it because #sidebar does not
       clip. That is the "list ends at Callbacks" report, and it is invisible
       in Chrome, which is why two rounds of testing here missed it.

       Grid has no equivalent argument to lose: the rows are sized from the
       container's height, and minmax(0, 1fr) says so in as many words. */
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    background: #dbeafe;
    /* The one line the rail keeps. With no panel behind it, this is what
       tells the eye where the menu ends and the page begins -- so it is a
       border on the RAIL, running the full height of the viewport, rather
       than a rule under any particular thing. Quiet on purpose: the change
       of colour across it already does most of the work. */
    border-right: 1px solid rgba(148,163,184,0.35);
    z-index: 120;
    transition: flex-basis .18s ease, width .18s ease;
  }

  /* min-width:0 is load-bearing. Without it a flex item refuses to shrink
     below its content's intrinsic width, and the Transactions table -- which
     is wider than the viewport -- would push the sidebar off-screen instead
     of scrolling inside its own .table-wrap. */
  #app-body {
    flex: 1 1 auto;
    min-width: 0;
  }

  /* --------------------------------------------------------- BRAND ROW */
  /* No bottom rule. A hairline across the full width used to be the top
     edge of a panel; with the rail transparent there is no panel for it to
     be the edge of, and it reads as a stray line. Spacing separates the
     wordmark from the links instead. */
  .sb-brand {
    display: flex; align-items: center; gap: 8px;
    padding: 18px 16px 14px;
    flex-shrink: 0;
  }
  .sb-brand .logo { font-size: 24px; flex: 1; min-width: 0; }

  /* BLUE, on purpose. This and the floating button on mobile are the two
     controls that open and close the menu, and everything else in the rail
     is a white card -- so a white toggle disappeared into the crowd. Blue is
     the only filled colour in the sidebar, which makes it the thing your eye
     lands on. Both buttons share it so they read as the same control in two
     places. */
  .sb-toggle {
    flex-shrink: 0;
    width: 32px; height: 32px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg, #2563eb, #1d4ed8);
    border: 0; border-radius: 9px;
    box-shadow: 0 2px 6px rgba(37,99,235,0.35);
    color: #ffffff; font-size: 14px; line-height: 1;
    transition: filter .15s, box-shadow .15s;
  }
  .sb-toggle:hover  { filter: brightness(1.12); box-shadow: 0 3px 10px rgba(37,99,235,0.45); }
  .sb-toggle:active { filter: brightness(0.95); }

  html[data-sidebar="collapsed"] .sb-brand {
    justify-content: center;
    padding: 18px 8px 14px;
  }
  html[data-sidebar="collapsed"] .sb-brand .logo { display: none; }

  /* ---------------------------------------------------------------- NAV */
  /* Scrolls independently: fifteen items can outgrow a short laptop screen,
     and the brand row and the account block must stay put when they do. */
  #main-nav {
    /* min-height:0 belt-and-braces. The grid row above already allows this to
       be shorter than its content; this says the same thing a second way, for
       any engine that still treats the child as a flex item. */
    min-height: 0;
    overflow-y: auto; overflow-x: hidden;
    overscroll-behavior: contain;   /* do not scroll the page behind the drawer */
    padding: 10px 10px 20px;
    display: flex; flex-direction: column; gap: 2px;
    scrollbar-width: thin;
  }
  #main-nav::-webkit-scrollbar { width: 6px; }
  #main-nav::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }

  .nav-group { display: flex; flex-direction: column; gap: 6px; }
  /* Separated by space rather than by a rule. The links are discrete white
     cards on the page backdrop now, so a hairline drawn across the gap has
     nothing to divide -- the gap itself is already a visible break, and the
     KX-Staff Area heading names what follows it. */
  .nav-group + .nav-group { margin-top: 20px; }

  .nav-group-title {
    padding: 4px 10px; margin-bottom: 2px;
    font-size: 10px; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: #94a3b8;
    white-space: nowrap; overflow: hidden;
  }

  /* A group whose every link is hidden for this role must hide its heading
     too, or a brand user gets a "KX-STAFF AREA" label with nothing under it.
     Set by syncNavGroups() in core/sidebar.js, and pre-paint by the role
     rules in 00-anti-flash.css. */
  .nav-group.nav-group-empty { display: none; }

  /* Each link is its own small white card. Opaque white, not a translucent
     tint: the backdrop is a gradient, so a semi-transparent fill would make
     the cards at the top of the rail a different shade from the ones at the
     bottom. */
  #main-nav a {
    display: flex; align-items: center; gap: 11px;
    padding: 8px 10px; border-radius: 10px;
    background: #ffffff;
    border: 1px solid rgba(148,163,184,0.16);
    box-shadow: 0 1px 2px rgba(15,23,42,0.05);
    font-size: 14px; font-weight: 500; color: #475569;
    cursor: pointer; white-space: nowrap;
    transition: background .15s, color .15s, border-color .15s, box-shadow .15s;
  }
  #main-nav a:hover {
    background: #eff6ff; color: #1d4ed8;
    border-color: #bfdbfe;
    box-shadow: 0 2px 6px rgba(37,99,235,0.12);
  }
  #main-nav a.active {
    background: linear-gradient(135deg, #2563eb, #1d4ed8);
    border-color: transparent;
    color: white; font-weight: 600;
    box-shadow: 0 2px 8px rgba(37,99,235,0.3);
  }

  .nav-ico {
    flex-shrink: 0;
    width: 22px; text-align: center;
    font-size: 15px; line-height: 1;
    /* Emoji ignore `color`, so an active row's white text does not apply to
       them. They stay legible on the blue gradient regardless. */
  }
  .nav-label { overflow: hidden; text-overflow: ellipsis; }

  /* ----------------------------------------------------------- COLLAPSED */
  html[data-sidebar="collapsed"] .nav-label,
  html[data-sidebar="collapsed"] .nav-group-title { display: none; }
  html[data-sidebar="collapsed"] #main-nav { padding: 10px 8px 20px; }
  html[data-sidebar="collapsed"] #main-nav a {
    justify-content: center;
    padding: 10px 0;
  }
  html[data-sidebar="collapsed"] .nav-ico { font-size: 17px; }
  /* The heading is hidden here, so the gap is the only thing marking the
     boundary -- give it more room than the 6px between cards so it reads as
     a break rather than as uneven spacing. */
  html[data-sidebar="collapsed"] .nav-group + .nav-group { margin-top: 18px; }

  /* Account block collapses to the avatar alone. The name, role, caret and
     wallet badge have nowhere to go in 68px — the badge in particular is a
     number plus a brand <select>, which cannot usefully shrink. Expand the
     rail to read a balance. */
  /* Keeps 03-account.css's deep bottom padding. This rule narrows the SIDES
     for the 68px rail; writing it as the `padding` shorthand also reset the
     bottom to 10px, which put the avatar back under a horizontal scrollbar --
     fixed expanded, still cropped collapsed. */
  html[data-sidebar="collapsed"] .sb-foot { padding-left: 8px; padding-right: 8px; }
  html[data-sidebar="collapsed"] .user-info-stack,
  html[data-sidebar="collapsed"] .user-menu-caret,
  html[data-sidebar="collapsed"] #wallet-badge { display: none !important; }
  html[data-sidebar="collapsed"] .user-menu-btn {
    justify-content: center; gap: 0; padding: 4px;
  }
  /* The menu keeps its real width and hangs out over the content rather than
     being squeezed into 48px. Nothing clips it: #sidebar sets no overflow,
     and #main-nav — which does — is not an ancestor of this. */
  html[data-sidebar="collapsed"] .user-menu {
    left: 8px; right: auto; min-width: 210px;
  }

  /* ------------------------------------------------------ MOBILE DRAWER */
  /* Off-canvas below 900px: the sidebar slides OVER the content instead of
     squeezing it, because at that width there is not enough room for both.
     #sb-mobile-toggle floats over the content and opens it; the backdrop,
     Escape, or picking a page closes it. */
  .sb-mobile-toggle { display: none; }
  #sb-backdrop { display: none; }

  @media (max-width: 900px) {
    #sidebar {
      /* top AND bottom, with height:auto -- NOT a viewport unit.
         The base rule asks for 100dvh with 100vh as the fallback, and on
         iOS Safari that was still leaving the account block below the fold:
         the drawer ran under the browser's bottom toolbar and the menu ended
         at Callbacks with nothing after it. Either dvh is unsupported there
         and it fell back to 100vh -- which is measured as though the
         toolbars were hidden -- or dvh was reporting the taller value.
         Pinning both edges sidesteps the question entirely. A fixed element
         with top:0 and bottom:0 is exactly as tall as the area the browser
         is actually showing, on every version, with nothing to get wrong. */
      position: fixed; top: 0; left: 0; bottom: 0;
      height: auto;
      /* Always the full width when it slides in -- an icon rail you have to
         open a drawer to reach is the worst of both. */
      flex-basis: 260px; width: 260px;
      transform: translateX(-100%);
      transition: transform .2s ease;
      box-shadow: 8px 0 32px rgba(15,23,42,0.18);
      /* No background override. The drawer slides OVER the page, so it has
         to be opaque -- and the base rule is opaque now, so it already is.
         This used to force #f0f9ff because the desktop rail was transparent
         and would have shown the transactions table through the menu. */
    }
    html[data-sidebar-open="true"] #sidebar { transform: translateX(0); }

    #app-body { width: 100%; }

    /* FIXED, not in flow. #app is a flex row, and this button is one of its
       children — in normal flow it would lay out as a second column beside
       the content. Fixed takes it out of the flex line entirely. The
       padding-top on #app-body below reserves the strip it floats in, so it
       never covers a banner or a heading. */
    /* The one button that gets you to every other page, floating over the
       content with nothing around it -- so it is filled blue rather than
       another pale outline, and 44px, which is the size Apple asks for a
       touch target. */
    .sb-mobile-toggle {
      display: inline-flex; align-items: center; justify-content: center;
      position: fixed; top: 10px; left: 10px; z-index: 105;
      width: 44px; height: 44px;
      background: linear-gradient(135deg, #2563eb, #1d4ed8);
      border: 0; border-radius: 12px;
      color: #ffffff; font-size: 19px; line-height: 1;
      box-shadow: 0 3px 12px rgba(37,99,235,0.40);
    }
    .sb-mobile-toggle:active { filter: brightness(0.95); }
    #app-body { padding-top: 60px; }

    html[data-sidebar-open="true"] #sb-backdrop {
      display: block;
      position: fixed; inset: 0; z-index: 110;
      background: rgba(15,23,42,0.45);
    }

    /* The collapse toggle is meaningless in drawer mode -- it would collapse
       a panel that is already overlaying the page. It closes the drawer
       instead; see sidebar.js. */
    .sb-brand .logo { display: block !important; }
    .sb-brand { justify-content: flex-start !important; padding: 18px 16px 14px !important; }
    html[data-sidebar="collapsed"] .nav-label,
    html[data-sidebar="collapsed"] .nav-group-title { display: block; }
    html[data-sidebar="collapsed"] #main-nav a { justify-content: flex-start; padding: 8px 10px; }
    html[data-sidebar="collapsed"] .nav-group + .nav-group {
      margin-top: 14px; padding-top: 0; border-top: 0;
    }
  }
