/* ==========================================================================
   App shell
   ========================================================================== */

#app {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  height: 100vh;
  height: 100dvh; /* mobile browsers: accounts for the collapsing address/tab bar so content never ends up under it */
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-standard),
    grid-template-columns var(--dur-slow) var(--ease-standard);
}

#app.is-ready {
  opacity: 1;
}

#app.sidebar-collapsed {
  grid-template-columns: var(--sidebar-width-collapsed) 1fr;
}

/* ---- Sidebar ---- */

.sidebar {
  grid-column: 1;
  height: 100vh;
  height: 100dvh;
  padding: var(--space-6) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  background: var(--color-bg-elevated);
  border-right: 1px solid var(--color-border);
  z-index: var(--z-sidebar);
}

/* Appearance controls (light/dark, colour scheme) plus the collapse
   handle. One row, same icon-button treatment, so they read as one group. */
.sidebar__footer {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}

/* The collapse handle is the odd one out - it steers layout, not
   appearance - so it sits apart at the far end. */
.sidebar__collapse {
  margin-left: auto;
}

#app.sidebar-collapsed .sidebar__footer {
  flex-direction: column;
  gap: var(--space-1);
}

#app.sidebar-collapsed .sidebar__collapse {
  margin-left: 0;
}

#app.sidebar-collapsed .sidebar__toggle-icon {
  transform: rotate(180deg);
}

#app.sidebar-collapsed .sidebar__brand {
  justify-content: center;
}

#app.sidebar-collapsed .sidebar__brand-name,
#app.sidebar-collapsed .account-switcher,
#app.sidebar-collapsed .nav-item__label {
  display: none;
}

#app.sidebar-collapsed .nav-item {
  justify-content: center;
}

.sidebar__brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
}

.sidebar__brand-mark {
  width: 30px;
  height: 30px;
  border-radius: var(--radius-md);
  background: linear-gradient(155deg, var(--color-accent), #6ac1ff 120%);
  box-shadow: 0 4px 14px rgba(var(--color-accent-rgb), 0.4);
  flex-shrink: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}

.sidebar__brand-mark img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.sidebar__brand-name {
  font-size: var(--fs-callout);
  font-weight: var(--fw-semibold);
  letter-spacing: -0.01em;
}

.brand-name__accent {
  color: var(--color-accent);
}

/* ---- Account switcher ---- */

.account-switcher {
  position: relative;
  margin: 0 var(--space-2);
}

.account-switcher__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  color: var(--color-text-primary);
  font-size: var(--fs-footnote);
  font-weight: var(--fw-medium);
  transition: background-color var(--dur-fast) var(--ease-standard),
    border-color var(--dur-fast) var(--ease-standard);
}

.account-switcher__trigger:hover {
  background: var(--color-surface-hover);
  border-color: var(--color-border-strong);
}

.account-switcher__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-switcher__chevron {
  flex-shrink: 0;
  color: var(--color-text-tertiary);
}

.account-switcher__panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: var(--z-sidebar);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.account-switcher__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--fs-footnote);
  color: var(--color-text-secondary);
  text-align: left;
  transition: background-color var(--dur-fast) var(--ease-standard),
    color var(--dur-fast) var(--ease-standard);
}

.account-switcher__option:hover {
  background: var(--color-surface-hover);
  color: var(--color-text-primary);
}

.account-switcher__option.is-active {
  color: var(--color-accent);
  font-weight: var(--fw-medium);
}

.account-switcher__divider {
  height: 1px;
  background: var(--color-border);
  margin: var(--space-1) 0;
}

.account-switcher__add {
  color: var(--color-text-secondary);
}

.sidebar__nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  position: relative;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-text-secondary);
  font-size: var(--fs-footnote);
  font-weight: var(--fw-medium);
  position: relative;
  transition: color var(--dur-fast) var(--ease-standard),
    background-color var(--dur-fast) var(--ease-standard);
}

.nav-item__icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  opacity: 0.85;
}

.nav-item:hover {
  background: var(--color-surface-hover);
  color: var(--color-text-primary);
}

.nav-item.is-active {
  color: var(--color-text-primary);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-sm);
}

.nav-item.is-active .nav-item__icon {
  opacity: 1;
  color: var(--color-accent);
}

/* ---- Main column ---- */

.main {
  grid-column: 2;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.topbar {
  height: var(--topbar-height);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-8);
  background: var(--glass-bg);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-bottom: 1px solid var(--color-border);
  z-index: var(--z-topbar);
  gap: var(--space-3);
}

.topbar__left {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}

.topbar__title {
  font-size: var(--fs-title3);
  font-weight: var(--fw-semibold);
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}

/* .btn (components.css) also sets display:inline-flex at equal specificity
   and loads after layout.css, so this needs the extra .btn qualifier to
   reliably win regardless of stylesheet order. */
.btn.topbar__menu-toggle {
  display: none;
  flex-shrink: 0;
}

/* `scrollbar-gutter: stable` is load-bearing, not polish.
   .view-scroll is the one surface that scrolls in this shell, and its width
   is what every card on every page is measured against. Without a reserved
   gutter, any switch that changes content height also toggles the scrollbar
   and therefore resizes the whole content column: switching the calendar
   from Week (short, no scrollbar) to Month (tall, scrollbar) moved the
   container from 1368px to 1358px, so *every* card on the page re-laid out
   by 10px. That is the "the whole page changes proportions when I switch
   views" symptom, and it applies to every tab/mode switch in the app, not
   just the calendar. Reserving the gutter permanently makes the content
   width independent of content height. */
.view-scroll {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-gutter: stable;
}

/* Background scroll lock while any modal is open (see js/components/modal.js
   lockBackgroundScroll/unlockBackgroundScroll) - .view-scroll, not body, is
   the surface that actually scrolls in this app's fixed-shell layout.
   `drawer-open` is the Trade Detail panel's own flag, deliberately separate:
   a modal opened from inside the drawer would otherwise unlock the
   background on its way out while the drawer is still up. */
html.modal-open .view-scroll,
html.drawer-open .view-scroll {
  overflow: hidden;
}

.view-container {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: var(--section-gap);
  padding-bottom: calc(var(--section-gap) + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: var(--section-gap);
}

@media (max-width: 560px) {
  .view-container {
    padding: var(--space-4);
    padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom));
    gap: var(--stack-gap);
  }
}

/* ---- Generic grid helpers ---- */

.grid {
  display: grid;
  gap: var(--space-5);
}

.grid-cols-2 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.grid-cols-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.grid-cols-4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* 12-column grid for view-level layout (Dashboard). minmax(0, 1fr) for the
   same reason .grid-cols-N uses it: a track must be allowed to shrink below
   its content's min-content width, or long numbers force overflow. */
.grid-12 {
  grid-template-columns: repeat(12, minmax(0, 1fr));
}

.col-span-3 { grid-column: span 3; }
.col-span-4 { grid-column: span 4; }
.col-span-5 { grid-column: span 5; }
.col-span-6 { grid-column: span 6; }
.col-span-7 { grid-column: span 7; }
.col-span-8 { grid-column: span 8; }
.col-span-12 { grid-column: span 12; }

@media (max-width: 1080px) {
  .grid-cols-4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .grid-cols-3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .col-span-3,
  .col-span-4,
  .col-span-5 {
    grid-column: span 6;
  }

  .col-span-7,
  .col-span-8 {
    grid-column: span 12;
  }
}

@media (max-width: 560px) {
  .grid-cols-2,
  .grid-cols-3,
  .grid-cols-4 {
    grid-template-columns: 1fr;
  }

  .col-span-3,
  .col-span-4,
  .col-span-5,
  .col-span-6,
  .col-span-7,
  .col-span-8 {
    grid-column: span 12;
  }
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--stack-gap);
}

.row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.row-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.section-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: var(--space-1);
}

.section-header h2 {
  font-size: var(--fs-title3);
}

/* ==========================================================================
   Mobile shell: bottom tab bar + FAB, off-canvas drawer for the rest
   ========================================================================== */

/* Desktop keeps the sidebar; the bar only exists below the mobile
   breakpoint (see the media query further down). */
.bottom-nav {
  display: none;
}

.sidebar-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 99;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-standard);
}

@media (max-width: 880px) {
  /* Both selectors, not just `#app`.
     `#app.sidebar-collapsed` (one id + one class) outranks a bare `#app`,
     and a media query adds NO specificity - so the desktop collapsed rule
     kept winning here and the shell stayed a two-column grid on a phone:
     `72px 303px`, with .main forced into the 72px track by the rule below.
     That is the "everything squeezed left, right side cut off" report. It
     survived browser restarts because sidebarCollapsed is persisted (and,
     since the D1 sync layer, mirrored between devices), so collapsing the
     sidebar once on desktop reproduced it on mobile from then on.
     Every other collapsed-mode effect was already neutralised below; only
     the grid itself was missed. */
  #app,
  #app.sidebar-collapsed {
    grid-template-columns: 1fr;
  }

  /* .main is explicitly grid-column:2 for the desktop 2-column shell;
     #app only has one explicit track here, so without this .main gets
     auto-placed into an implicit second column, leaving a near-empty
     phantom first column that shifts/narrows all mobile content. */
  .main {
    grid-column: 1;
  }

  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: var(--sidebar-width);
    max-width: 82vw;
    transform: translateX(-100%);
    transition: transform var(--dur-slow) var(--ease-standard);
    box-shadow: var(--shadow-xl);
  }

  #app.sidebar-mobile-open .sidebar {
    transform: translateX(0);
  }

  /* The desktop icon-only collapse is a permanent-column space-saving
     mode; the drawer already overlays instead of taking column space,
     so it stays fully expanded here regardless of the persisted
     sidebarCollapsed preference. */
  #app.sidebar-collapsed .sidebar__brand-name,
  #app.sidebar-collapsed .nav-item__label {
    display: inline;
  }

  #app.sidebar-collapsed .account-switcher {
    display: block;
  }

  #app.sidebar-collapsed .sidebar__brand,
  #app.sidebar-collapsed .nav-item {
    justify-content: flex-start;
  }

  /* The footer stays visible in the drawer - it is where light/dark and
     the colour scheme now live, and the drawer is the only way to reach
     them on mobile. Only the collapse handle goes, since the drawer
     overlays rather than taking column space.

     Needs the .btn qualifier: .btn sets display:inline-flex at equal
     specificity and components.css loads after this file, so an unqualified
     .sidebar__collapse loses. Same reason .btn.topbar__menu-toggle above
     carries it. */
  .btn.sidebar__collapse {
    display: none;
  }

  #app.sidebar-mobile-open .sidebar-backdrop {
    opacity: 1;
    pointer-events: auto;
  }

  /* The hamburger is replaced by the bottom bar's "More" tab, and New Trade
     by the FAB - both topbar controls would otherwise duplicate them. */
  .btn.topbar__menu-toggle,
  #quick-add-trade {
    display: none;
  }

  .topbar {
    padding: 0 var(--space-4);
  }

  .nav-item {
    padding: var(--space-4) var(--space-3);
  }

  /* ---- Bottom tab bar ----
     Floating translucent pill rather than a full-width edge-anchored bar:
     it overlays content, so .view-scroll gets bottom padding instead of the
     bar taking a layout row. The active item is marked by a filled pill
     behind icon+label, both tinted with the accent. */

  .bottom-nav {
    position: fixed;
    left: var(--space-4);
    right: var(--space-4);
    bottom: calc(var(--space-3) + env(safe-area-inset-bottom));
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    padding: var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--glass-bg);
    backdrop-filter: blur(24px) saturate(180%);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    border: 1px solid var(--glass-border);
    box-shadow: var(--shadow-lg);
    z-index: var(--z-topbar);
    transform-origin: bottom center;
    transition: transform var(--dur-base) var(--ease-spring),
      opacity var(--dur-base) var(--ease-standard),
      left var(--dur-base) var(--ease-spring),
      right var(--dur-base) var(--ease-spring),
      padding var(--dur-base) var(--ease-spring);
  }

  /* Shrinks while scrolling down, restores on scroll up (see initBottomNav
     in js/app.js).

     Most of the shrink is real layout - labels dropped, padding halved, the
     pill pulled in from both edges - rather than a big transform scale. A
     scale strong enough to read as "much smaller" on its own would take the
     tap targets down with it (0.72 leaves ~29px of actual touch area, since
     a scaled element's hit box scales too). This way the bar loses ~18% of
     its height and ~16% of its width while every item still paints at 41px
     against a 44px layout box. */
  .bottom-nav.is-shrunk {
    left: var(--space-10);
    right: var(--space-10);
    transform: scale(0.94) translateY(8px);
    opacity: 0.9;
    padding: var(--space-1);
  }

  .bottom-nav.is-shrunk .bottom-nav__label {
    display: none;
  }

  .bottom-nav.is-shrunk .bottom-nav__item {
    padding-top: 0;
    padding-bottom: 0;
  }

  .bottom-nav.is-shrunk .bottom-nav__fab {
    width: 40px;
    height: 40px;
  }

  .bottom-nav.is-shrunk .bottom-nav__fab svg {
    width: 20px;
    height: 20px;
  }

  /* The bar floats above content, so the scroll area needs to clear it. */
  .view-scroll {
    padding-bottom: 84px;
  }

  .bottom-nav__item {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    /* Density never shrinks tap targets - 44px stays 44px. */
    min-height: 44px;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-pill);
    background: none;
    color: var(--color-text-tertiary);
    font-size: 10px;
    font-weight: var(--fw-medium);
    transition: color var(--dur-fast) var(--ease-standard),
      background-color var(--dur-base) var(--ease-standard);
  }

  .bottom-nav__item.is-active {
    color: var(--color-accent);
    background: var(--color-surface-hover);
  }

  .bottom-nav__icon {
    width: 20px;
    height: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .bottom-nav__label {
    line-height: 1;
  }

  .bottom-nav__fab {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    margin: 0 var(--space-1);
    border-radius: var(--radius-pill);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-accent);
    color: var(--color-on-accent);
    box-shadow: 0 4px 14px rgba(var(--color-accent-rgb), 0.4);
    transition: transform var(--dur-fast) var(--ease-spring-snappy);
  }

  /* Minimal palette: the FAB stops being the one filled, saturated element
     and reads as another icon in the row - same weight and colour as its
     neighbours, distinguished only by its outline. */
  :root[data-palette="minimal"] .bottom-nav__fab {
    background: none;
    color: var(--color-text-tertiary);
    border: 1px solid var(--color-border-strong);
    box-shadow: none;
  }

  :root[data-palette="minimal"] .bottom-nav__fab:hover {
    color: var(--color-text-secondary);
  }

  .bottom-nav__fab svg {
    width: 22px;
    height: 22px;
  }

  .bottom-nav__fab:active {
    transform: scale(0.94);
  }
}
