/* ==========================================================================
   Page chrome: header, navigation, containers, app shell grid
   ========================================================================== */

.app-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.container {
  width: 100%;
  max-width: var(--container-max);
  margin: 0 auto;
  /* Phones get a tight, near-edge-to-edge gutter on purpose — the app's
     information density is already compact, so padding sized for "breathing
     room" mostly just steals width from content that could use it. */
  padding: 0 var(--space-2);
}

@media (min-width: 480px) {
  .container { padding: 0 var(--space-3); }
}

@media (min-width: 640px) {
  .container { padding: 0 var(--space-6); }
}

.page-main {
  flex: 1;
  padding: var(--space-4) 0 calc(var(--bottom-nav-clearance) + var(--space-4));
}

@media (min-width: 1024px) {
  .page-main { padding: var(--space-7) 0 var(--space-9); }
}

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
  flex-wrap: wrap;
  animation: rise-in var(--duration-slow) var(--ease-standard) both;
}

@media (min-width: 1024px) {
  .page-head { margin-bottom: var(--space-7); }
}

@keyframes rise-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.page-head h1 { margin-bottom: var(--space-1); }
.page-head-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

.breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--space-3);
}
.breadcrumb a { color: var(--color-text-muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--color-text); }

/* ---------- Top header ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  height: var(--header-height);
  display: flex;
  align-items: center;
  background: var(--glass-bg-strong);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border-bottom: 1px solid var(--glass-border);
  box-shadow: 0 1px 0 var(--glass-highlight) inset;
}

/* A hairline of accent light along the header's bottom edge, brightest in
   the middle — gives the sticky chrome a defined edge against scrolling
   content without resorting to a heavier border or drop shadow. */
.site-header::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: linear-gradient(to right,
    transparent,
    color-mix(in srgb, var(--color-accent) 55%, transparent) 25%,
    color-mix(in srgb, var(--color-accent) 70%, transparent) 50%,
    color-mix(in srgb, var(--color-accent) 55%, transparent) 75%,
    transparent);
  pointer-events: none;
}

.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  letter-spacing: -0.01em;
  color: var(--color-text);
  text-decoration: none;
}

.brand .icon { width: 28px; height: 28px; color: var(--color-accent); }
.brand img.icon {
  border-radius: var(--radius-sm);
  object-fit: cover;
  display: block;
  box-shadow: 0 2px 8px rgba(21, 24, 43, 0.16);
  transition: transform var(--duration-base) var(--ease-spring);
}
.brand:hover img.icon { transform: rotate(-4deg) scale(1.05); }

.header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  border: none;
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  position: relative;
  transition: background var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-spring);
}
.icon-btn:hover { background: var(--color-bg-subtle); }
.icon-btn:active { background: var(--color-bg-inset); transform: scale(0.9); }
.icon-btn .icon { width: 22px; height: 22px; }

.notif-dot {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-danger);
  border: 2px solid var(--color-bg-elevated);
}

/* ---------- Primary navigation (desktop sidebar / mobile bottom bar) ---------- */
.primary-nav {
  display: flex;
}

.nav-list {
  display: flex;
  width: 100%;
}

/* Mobile: floating glass dock, not a hard full-bleed bar */
.bottom-nav {
  position: fixed;
  left: var(--space-3);
  right: var(--space-3);
  bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0));
  z-index: var(--z-bottom-nav);
  background: var(--glass-bg-strong);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-xl);
  box-shadow: var(--glass-shadow-lg);
  transition: transform var(--duration-base) var(--ease-standard), opacity var(--duration-base) var(--ease-standard);
}

.bottom-nav .nav-list {
  align-items: stretch;
}

/* The <li> is the actual flex item of .nav-list — sizing .nav-link (the <a>
   inside it) instead left every tab shrink-to-fit its own label width, so
   tabs with longer labels crowded out shorter ones instead of splitting the
   bar evenly. */
.bottom-nav .nav-item {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
}

.bottom-nav .nav-link {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: var(--space-2) var(--space-1);
  min-height: var(--bottom-nav-height);
  color: var(--color-text-muted);
  text-decoration: none;
  font-size: var(--text-xs);
  position: relative;
  transition: color var(--duration-fast) var(--ease-standard),
              transform var(--duration-fast) var(--ease-spring);
}

/* Short glowing bar above the current tab. Sits inside the dock's rounded
   frame rather than on its edge, so it doesn't fight the border radius. */
.bottom-nav .nav-link[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 6px;
  left: 50%;
  width: 26px;
  height: 3px;
  margin-left: -13px;
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  box-shadow: 0 0 10px color-mix(in srgb, var(--color-accent) 80%, transparent);
  animation: nav-indicator-in var(--duration-base) var(--ease-spring) both;
}

@keyframes nav-indicator-in {
  from { opacity: 0; transform: scaleX(0.3); }
  to { opacity: 1; transform: scaleX(1); }
}

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

.bottom-nav .nav-link .icon { width: 22px; height: 22px; transition: filter var(--duration-base) var(--ease-standard); }
.bottom-nav .nav-link[aria-current="page"] { color: var(--color-accent-text); }
.bottom-nav .nav-link[aria-current="page"] .icon {
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-accent) 60%, transparent));
}

.bottom-nav .nav-label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Anchors the unread dot to the icon itself. Scoped here (rather than
   changing the shared .notif-dot rule, which is also used — correctly
   positioned relative to .icon-btn — for the header bell icon) since the
   nav-link's tall column layout needs a different offset than that square
   button. */
.nav-icon-wrap {
  position: relative;
  display: inline-flex;
}
.nav-icon-wrap .notif-dot {
  top: -2px;
  right: -2px;
}

@media (min-width: 1024px) {
  .bottom-nav { display: none; }
}

/* While a popup is open (see initNavGuard() in ui.js), the bottom tab bar
   sits directly beneath the popup's bottom edge — a tap that closes the
   popup can otherwise land on the nav link now underneath it. Desktop uses
   the side rail instead, so this only ever needs to apply below the same
   breakpoint the bar itself is limited to. */
@media (max-width: 1023px) {
  .bottom-nav.is-nav-disabled {
    pointer-events: none;
  }
}

/* Desktop: side rail */
.side-nav {
  display: none;
}

@media (min-width: 1024px) {
  .side-nav {
    display: block;
    width: var(--sidebar-width);
    flex-shrink: 0;
    padding: var(--space-4);
    position: sticky;
    top: calc(var(--header-height) + var(--space-4));
    height: calc(100vh - var(--header-height) - var(--space-7));
    overflow-y: auto;
    margin: var(--space-4) 0 var(--space-4) var(--space-2);
    background: var(--glass-bg);
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--glass-shadow);
  }

  .side-nav .nav-list {
    flex-direction: column;
    gap: var(--space-1);
  }

  .side-nav .nav-link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    color: var(--color-text-muted);
    text-decoration: none;
    font-weight: var(--weight-medium);
    font-size: var(--text-sm);
    transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard), transform var(--duration-fast) var(--ease-spring);
  }

  .side-nav .nav-link { position: relative; }
  .side-nav .nav-link:hover { background: var(--color-bg-subtle); color: var(--color-text); transform: translateX(2px); }
  .side-nav .nav-link[aria-current="page"] {
    background: var(--color-accent-subtle);
    color: var(--color-accent-text);
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4) inset, var(--glow-accent-sm);
  }

  /* Vertical counterpart to the bottom dock's indicator bar — same idea,
     rotated to suit a stacked rail. */
  .side-nav .nav-link[aria-current="page"]::before {
    content: "";
    position: absolute;
    left: 0;
    top: 22%;
    bottom: 22%;
    width: 3px;
    border-radius: var(--radius-pill);
    background: var(--color-accent);
    box-shadow: 0 0 10px color-mix(in srgb, var(--color-accent) 80%, transparent);
  }

  .side-nav .nav-link[aria-current="page"] .icon {
    filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-accent) 55%, transparent));
  }

  .side-nav .nav-link .icon { width: 20px; height: 20px; flex-shrink: 0; }
}

.app-body {
  display: flex;
  flex: 1;
  width: 100%;
  max-width: var(--container-max);
  margin: 0 auto;
}

.app-body > .page-main {
  flex: 1;
  min-width: 0;
}

/* Below 1024px .page-main has no horizontal padding of its own — the
   .container it wraps (see above) already supplies the gutter, and stacking
   both here doubled up the margin on phones for no reason. Desktop gets an
   extra layer back, to keep clear air next to the floating side rail. */
@media (min-width: 1024px) {
  .app-body > .page-main { padding-left: var(--space-7); padding-right: var(--space-6); }
}

/* ---------- Grids ---------- */
.grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: 1fr;
}

@media (min-width: 640px) {
  .grid-2 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 900px) {
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
}

.dashboard-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
}

@media (min-width: 900px) {
  .dashboard-grid {
    grid-template-columns: 1.6fr 1fr;
    align-items: start;
  }
}

.section {
  margin-bottom: var(--space-6);
}

@media (min-width: 1024px) {
  .section { margin-bottom: var(--space-8); }
}

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
  gap: var(--space-3);
}

/* A small glowing tick before each section title. Purely decorative, so
   it's a pseudo-element rather than markup — screen readers still just
   hear the heading text. */
.section-head h2 {
  margin-bottom: 0;
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.section-head h2::before {
  content: "";
  width: 3px;
  height: 1em;
  border-radius: var(--radius-pill);
  background: var(--gradient-accent-solid);
  box-shadow: 0 0 10px color-mix(in srgb, var(--color-accent) 60%, transparent);
  flex-shrink: 0;
}

.card-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* Gentle staggered entrance for repeating card/grid content — one
   orchestrated reveal rather than scattered micro-animations. Capped at 6
   explicit steps; anything past that shares the last delay so a long list
   doesn't take a full second to finish appearing. */
.card-list > *,
.grid > * {
  animation: rise-in var(--duration-slow) var(--ease-standard) both;
}
.card-list > *:nth-child(1), .grid > *:nth-child(1) { animation-delay: 0ms; }
.card-list > *:nth-child(2), .grid > *:nth-child(2) { animation-delay: 40ms; }
.card-list > *:nth-child(3), .grid > *:nth-child(3) { animation-delay: 80ms; }
.card-list > *:nth-child(4), .grid > *:nth-child(4) { animation-delay: 120ms; }
.card-list > *:nth-child(5), .grid > *:nth-child(5) { animation-delay: 160ms; }
.card-list > *:nth-child(n+6), .grid > *:nth-child(n+6) { animation-delay: 200ms; }

/* ---------- Auth pages (login / invite) ---------- */
/* Column rather than the default row, so the authorship line can sit under
   the card as a sibling instead of beside it. With a single child the
   result is identical to before — align-items still centres it
   horizontally, justify-content still centres the stack vertically. */
.auth-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-5);
  padding: var(--space-6) var(--space-4);
}

.auth-card {
  position: relative;
  width: 100%;
  max-width: 440px;
  background: var(--glass-bg-strong);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-xl);
  padding: var(--space-7) var(--space-6);
  box-shadow: var(--glass-shadow-lg);
  animation: rise-in var(--duration-slow) var(--ease-standard) both;
}

/* Slow rotating light traces the card's edge — the entry-point card gets a
   touch more ceremony than the glass treatment used everywhere else. */
.auth-card::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1.5px;
  background: conic-gradient(from 0deg,
    transparent 0%,
    color-mix(in srgb, var(--color-accent) 80%, transparent) 14%,
    transparent 32%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  animation: ring-rotate 9s linear infinite;
  pointer-events: none;
}

/* Cursor-tracked spotlight, same mechanism as .card-interactive/.stat-tile
   (see initSpotlight() in ui.js) — sits above the rotating ring, below the
   form content. */
.auth-card::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(var(--spotlight-size) circle at var(--mx, 50%) var(--my, 20%),
              color-mix(in srgb, var(--color-accent) 14%, transparent), transparent 70%);
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-standard);
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
  .auth-card:hover::after { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .auth-card::before { animation: none; }
}

.auth-card .brand {
  justify-content: center;
  margin-bottom: var(--space-6);
  font-size: var(--text-xl);
}

/* Wordmark gets a subtle accent-to-info gradient rather than flat text —
   both colours already exist in the palette, just recombined. Guarded so
   an unsupporting browser keeps a readable solid wordmark instead of a
   transparent one (see .stat-tile .num for the same reasoning). */
@supports ((background-clip: text) or (-webkit-background-clip: text)) {
  .auth-card .brand span {
    background: var(--gradient-accent);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

.auth-card .brand img.icon {
  box-shadow: 0 2px 8px rgba(21, 24, 43, 0.16), var(--glow-accent-sm);
  animation: icon-glow-pulse 3.2s ease-in-out infinite;
}

@keyframes icon-glow-pulse {
  0%, 100% { filter: drop-shadow(0 0 0 transparent); }
  50% { filter: drop-shadow(0 0 8px color-mix(in srgb, var(--color-accent) 55%, transparent)); }
}

@media (prefers-reduced-motion: reduce) {
  .auth-card .brand img.icon { animation: none; }
}

.auth-footer {
  text-align: center;
  margin-top: var(--space-5);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* ---------- Utility layout helpers ---------- */
.flex-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.mt-2 { margin-top: var(--space-2); }
.mt-4 { margin-top: var(--space-4); }
.mt-6 { margin-top: var(--space-6); }
.mb-0 { margin-bottom: 0; }
