/* ==========================================================================
   Budget Bot Mini WebApp — app shell, top app bar, navigation bar / rail, FAB
   Depends on: tokens.css
   Window size classes: compact <600px (bottom bar) · medium 600–839px (rail)
                        expanded 840px+ (rail + capped measure)
   ========================================================================== */

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

html {
  height: 100%;
  background: var(--md-sys-color-surface);
  -webkit-text-size-adjust: 100%;
}

body {
  height: 100%;
  margin: 0;
  background: var(--md-sys-color-surface);
  color: var(--md-sys-color-on-surface);
  font: var(--md-sys-typescale-body-large);
  letter-spacing: var(--md-sys-typescale-body-large-tracking);
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: none;
  overflow: hidden; /* the shell owns scrolling — see .app-content */
}

[x-cloak] { display: none !important; }

/* Components below set an explicit `display`, which would beat the UA rule for
   the `hidden` attribute. Pages toggle visibility with `hidden` everywhere
   (snackbar, tab panels, inline errors), so make the attribute authoritative. */
[hidden] { display: none !important; }

/* ---------- Shell grid ----------
   The shell is exactly one viewport tall and .app-content is the single
   scroll container. This deviates from SPEC-layout §1.2's `position: sticky`
   top bar / nav bar on a body-scrolled page: a sticky grid item is clamped
   to its own grid AREA (64px / 80px tall), so it never actually pins and
   both bars scroll away on a long list. Making the content region the
   scroller keeps the bars genuinely fixed with no fixed-position hacks,
   keeps exactly one scrolling region, and plays well with Telegram's
   collapsing viewport (height driven by --tg-viewport-stable).           */
.app-shell {
  display: grid;
  /* Three declarations, weakest first: ancient webviews without dvh keep
     100vh; modern ones take 100dvh; inside Telegram the var wins and tracks
     viewportStableHeight (updated only on isStateStable, so the bars never
     jump mid keyboard animation). */
  height: 100vh;
  height: 100dvh;
  height: var(--tg-viewport-stable, 100dvh);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "topbar"
    "content"
    "navbar";
  overflow: hidden;
  background: var(--md-sys-color-surface);
}

.app-topbar { grid-area: topbar; }
.app-content { grid-area: content; }
.app-navbar { grid-area: navbar; }

/* Which of the two navigation surfaces is on is a pure function of the
   window size class, so both `display` values live HERE, above every
   component rule below. A `display` declared further down the file — in
   the .app-navbar component block, say — would beat the @media override
   at line ~118 on order alone (a media query adds no specificity) and
   the bottom bar would stay visible next to the rail. */
.app-navbar { display: flex; }
.app-rail { display: none; }

.app-content {
  min-width: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.page {
  display: flex;
  flex-direction: column;
  gap: var(--md-sys-spacing-4);
  padding: var(--md-sys-spacing-4) var(--app-margin)
           calc(var(--md-sys-spacing-6) + var(--safe-bottom));
  min-width: 0;
}

/* When a nav bar is present it already pads the home indicator, so the page
   does not need to. Progressive enhancement: browsers without :has() simply
   keep a little extra bottom space. */
.app-shell:has(.app-navbar) .page { padding-bottom: var(--md-sys-spacing-6); }

/* Nav-less (pushed) screens end in a sticky .m3-form-actions bar. A sticky
   box is constrained by its containing block's CONTENT edge, so any
   padding-bottom on .page parks the bar that far above the bottom of the
   scrollport at full scroll. The bar pads --safe-bottom itself. */
.app-shell--no-nav .page { padding-bottom: 0; }

/* Medium & expanded: rail on the left, no bottom bar */
@media (min-width: 600px) {
  .app-shell {
    grid-template-columns: var(--app-rail-width) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas:
      "rail topbar"
      "rail content";
  }
  /* Pushed screens (expense-form) ship neither nav bar nor rail. An explicit
     grid track does not collapse when nothing is placed in it, so without
     this the 80px rail column would stay reserved and empty and push the
     whole screen to the right. Same specificity as the rule above — it wins
     by order, so keep it after it. */
  .app-shell--no-nav {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "topbar"
      "content";
  }
  .app-navbar { display: none; }
  .app-rail { display: flex; grid-area: rail; }
  .page { padding-block-start: var(--md-sys-spacing-6); gap: var(--md-sys-spacing-6); }
}

/* Expanded: centred, capped measure — never full-bleed */
@media (min-width: 840px) {
  .app-content > .page,
  .app-topbar > .m3-tabs,
  .app-topbar > .topbar-inner {
    width: 100%;
    max-width: var(--app-content-max);
    margin-inline: auto;
  }
}

/* ---------- Top app bar ----------
   Two equivalent markups are supported:
     <header class="app-topbar"><div class="topbar-inner">…</div></header>
     <header class="m3-top-bar">…</header>            (SPEC-components §11)
   Variants: default = small (start-aligned title)
             .topbar-inner--center / .m3-top-bar--center = center-aligned  */
.app-topbar,
.m3-top-bar {
  background: var(--md-sys-color-surface);
  color: var(--md-sys-color-on-surface);
  padding-top: var(--safe-top);
  transition: background-color var(--md-sys-motion-duration-short4) var(--md-sys-motion-easing-standard);
  z-index: 9;
}

.app-topbar--scrolled,
.m3-top-bar--scrolled {
  background: var(--md-sys-color-surface-container);
}

.topbar-inner,
.m3-top-bar {
  display: flex;
  align-items: center;
  gap: var(--md-sys-spacing-1);
  min-height: var(--app-top-bar-height);
  padding-inline: var(--md-sys-spacing-1);
}

.topbar-inner {
  padding-inline: var(--md-sys-spacing-1);
}

/* Leading / trailing slots keep the title optically centred */
.topbar-leading,
.topbar-actions,
.m3-top-bar__actions {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  min-width: var(--md-sys-spacing-12);
}
.topbar-actions,
.m3-top-bar__actions { justify-content: flex-end; }
.m3-top-bar > .m3-icon-btn:first-child { min-width: var(--md-sys-spacing-12); }

.topbar-title,
.m3-top-bar__title {
  flex: 1;
  min-width: 0;
  margin: 0;
  padding-inline: var(--md-sys-spacing-1);
  font: var(--md-sys-typescale-title-large);
  letter-spacing: var(--md-sys-typescale-title-large-tracking);
  color: var(--md-sys-color-on-surface);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-inner--center .topbar-title,
.m3-top-bar--center .m3-top-bar__title { text-align: center; }

/* ---------- Navigation bar (compact) ----------
   No `display` here on purpose — see the note at the top of the file. */
.app-navbar {
  align-items: stretch;
  height: calc(var(--app-nav-bar-height) + var(--safe-bottom));
  padding-bottom: var(--safe-bottom);
  padding-inline: var(--safe-left) var(--safe-right);
  background: var(--md-sys-color-surface-container);
  z-index: 10;
}

.nav-item,
.m3-nav__item {
  flex: 1;
  min-width: 0;
  max-width: 120px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--md-sys-spacing-1);
  min-height: 48px;
  padding: var(--md-sys-spacing-3) 0 var(--md-sys-spacing-4);
  text-decoration: none;
  color: var(--md-sys-color-on-surface-variant);
  -webkit-tap-highlight-color: transparent;
}

.nav-item__icon,
.m3-nav__indicator {
  display: grid;
  place-items: center;
  width: 64px;
  height: 32px;
  flex-shrink: 0;
  border-radius: var(--md-sys-shape-corner-full);
  transition: background-color var(--md-sys-motion-duration-short4) var(--md-sys-motion-easing-standard);
}

.nav-item__icon svg,
.m3-nav__indicator svg {
  width: 24px;
  height: 24px;
  display: block;
}

.nav-item__label,
.m3-nav__label {
  font: var(--md-sys-typescale-label-medium);
  letter-spacing: var(--md-sys-typescale-label-medium-tracking);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav-item[aria-current="page"],
.m3-nav__item[aria-current="page"] { color: var(--md-sys-color-on-surface); }

.nav-item[aria-current="page"] .nav-item__icon,
.m3-nav__item[aria-current="page"] .m3-nav__indicator {
  background: var(--md-sys-color-secondary-container);
  color: var(--md-sys-color-on-secondary-container);
}

.nav-item[aria-current="page"] .nav-item__label,
.m3-nav__item[aria-current="page"] .m3-nav__label { font-weight: 700; }

/* Focus ring — R10: 3px secondary, offset 2px, :focus-visible only */
.nav-item:focus-visible,
.m3-nav__item:focus-visible { outline: none; }
.nav-item:focus-visible .nav-item__icon,
.m3-nav__item:focus-visible .m3-nav__indicator {
  outline: 3px solid var(--md-sys-color-secondary);
  outline-offset: 2px;
}

/* ---------- Navigation rail (medium & expanded) ---------- */
.app-rail {
  flex-direction: column;
  align-items: center;
  gap: var(--md-sys-spacing-3);
  width: var(--app-rail-width);
  padding: calc(var(--md-sys-spacing-6) + var(--safe-top)) 0 var(--md-sys-spacing-4);
  padding-left: var(--safe-left);
  background: var(--md-sys-color-surface);
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 10;
}

/* The FAB occupies the top of the rail; destinations follow below it. */
.app-rail .nav-item,
.app-rail .m3-nav__item {
  flex: none;
  width: 100%;
  max-width: none;
  min-height: 56px;
  padding: var(--md-sys-spacing-1) 0;
}

.app-rail .nav-item__icon,
.app-rail .m3-nav__indicator { width: 56px; }

/* ---------- FAB placement ----------
   One element, repositioned by CSS (PLAN R11 / §A4):
   compact  → fixed bottom-right, clear of the navigation bar
   ≥600px   → parked at the top of the rail                               */
.app-fab {
  position: fixed;
  right: calc(var(--md-sys-spacing-4) + var(--safe-right));
  bottom: calc(var(--app-nav-bar-height) + var(--safe-bottom) + var(--md-sys-spacing-4));
  z-index: 11;
}

/* Pushed screens (expense-form) carry no nav bar — drop the reserved space. */
.app-shell--no-nav .app-fab {
  bottom: calc(var(--safe-bottom) + var(--md-sys-spacing-4));
}

@media (min-width: 600px) {
  .app-fab {
    top: calc(var(--safe-top) + var(--md-sys-spacing-3));
    left: calc(var(--safe-left) + var(--md-sys-spacing-3));
    right: auto;
    bottom: auto;
  }
  /* Only the two pages that carry a FAB reserve the slot it parks in
     (PLAN R11: index.html and transactions.html). */
  .app-shell:has(.app-fab) .app-rail {
    padding-top: calc(var(--safe-top) + var(--md-sys-spacing-3) + 56px + var(--md-sys-spacing-4));
  }
}

/* Without :has() we cannot tell the two cases apart — reserve the slot
   everywhere rather than let the FAB land on the first destination. */
@supports not selector(:has(*)) {
  @media (min-width: 600px) {
    .app-rail { padding-top: calc(var(--safe-top) + var(--md-sys-spacing-3) + 56px + var(--md-sys-spacing-4)); }
  }
}

/* ---------- Horizontally wide content ----------
   Charts, tables and chip rows scroll inside their own box; the shell and
   the body never scroll sideways. */
.app-scroll-x {
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.app-scroll-x::-webkit-scrollbar { display: none; }
