/* ============================================================================
   FB ATLAS — Live Dashboard (Super Admins)
   "Frosted glass on warm canvas" — F. BINSABBAR brand book + Apple widget bento.
   Tokens mirror https://fb-brand.web.app/guidelines/brand-book 1:1.
   ========================================================================== */

/* Futura PT (Adobe kit vvg4egg) is loaded via <link> in index.html <head> — not @import (render-blocking). */

:root {
  /* ── Brand colors ── */
  --premium-yellow: #fee5a5;
  --signature-red:  #f74827;
  --vert:           #b5af8d;
  --royal-lavender: #a77fb2;
  --champagne:      #b19b7d;

  --canvas:         #EBE9E5;   /* warm body canvas (beige) */
  --beige-2:        #DEDAD5;   /* wells, tracks, chips */
  --ink:            #0e0e0e;
  --surface-dark:   #0a0a0a;

  /* ── Text ── */
  --text:        #0e0e0e;
  --text-muted:  #3a3a3a;
  --text-faint:  #776F6D;      /* taupe */
  --text-inverse:#f6f4ef;

  /* ── Semantic ── */
  --success: #2d8a4f;
  --warning: #edb845;
  --error:   #f74827;
  --lavender:#A77FB2;

  /* ── Glass ── */
  --surface:         var(--glass-bg);
  --glass-bg:        rgba(250,249,247,0.54);    /* lighter, warmer frost — the backdrop-filter still supplies the see-through */
  --glass-bg-strong: rgba(251,250,248,0.74);
  --glass-bg-dark:   rgba(14,14,14,0.82);
  --glass-blur:      blur(16px) saturate(118%); /* calmer: less over-juiced, crisper, lighter on the kiosk GPU */
  --glass-border:    1px solid rgba(255,255,255,0.42);
  /* one shared elevation — a soft, Apple-grade two-layer "float": a tight contact
     line for crispness + a large, low-opacity ambient blur biased slightly down.
     Same warm hue (40,33,22) so it never greys out on the beige canvas. */
  --drop: 0 1px 2px rgba(40,33,22,0.05), 0 12px 28px -16px rgba(40,33,22,0.20);
  --glass-shadow: inset 0 1px 0 rgba(255,255,255,0.38), var(--drop);
  /* hover/press "lifted" — same language, a larger/softer ambient layer so a card eases UP */
  --glass-shadow-lift: inset 0 1px 0 rgba(255,255,255,0.42), 0 2px 4px rgba(40,33,22,0.06), 0 22px 44px -20px rgba(40,33,22,0.26);
  /* ONE recipe for any glass chip over the dark master sky / a photo (LIVE pill, sign-out, loc chips) */
  --glass-bg-onsky:     rgba(255,255,255,0.20);
  --glass-border-onsky: rgba(255,255,255,0.24);
  --glass-noise:        0.05;   /* whisper of tooth on the sky gradient — was 0.22 (read as visual debris) */
  /* compat shadow scale (kept defined; harmless) */
  --shadow-sm: 0 1px 3px rgba(40,33,22,0.10);
  --shadow-md: 0 3px 6px rgba(40,33,22,0.12);
  --shadow-lg: 0 12px 30px -18px rgba(40,33,22,0.30);

  /* ── Radius (evenly-stepped family; concentric rule: inner = outer − padding) ── */
  --radius-pill:    999px;
  --radius-glass:   28px;
  --radius-card:    20px;
  --radius-control: 14px;
  --radius-sm:      9px;
  --r-card:     clamp(15px, 2.2vmin, var(--radius-card));   /* fluid card radius */
  --r-card-pad: clamp(13px, 2.4vmin, 20px);                 /* fluid card padding */
  --m-radius:   clamp(16px, 2.4vmin, var(--radius-glass));  /* master hero radius */

  /* ── Lines · fills · press washes (one each, translucent so frost shows through) ── */
  --hair:      rgba(14,14,14,0.06);    /* one hairline divider */
  --well:      rgba(14,14,14,0.055);   /* one track/chip well */
  --row-press: rgba(14,14,14,0.04);    /* row press wash */

  /* ── Type rhythm ── */
  --tracking-caps:      0.08em;   /* calm all-caps labels */
  --tracking-caps-wide: 0.12em;   /* tiny status pills */
  --tracking-num:      -0.025em;  /* tight big numerals */
  --lh-body:  1.5;
  --lh-tight: 1.15;

  /* ── Motion — interaction-only; every transition references these. Honor reduced-motion. ── */
  --ease-standard:   cubic-bezier(.4,0,.2,1);
  --ease-emphasized: cubic-bezier(.2,0,0,1);
  --ease-spring:     cubic-bezier(.32,.72,0,1);
  --ease-out:        cubic-bezier(.22,.61,.36,1);
  --dur-1: 120ms; --dur-2: 200ms; --dur-3: 320ms; --dur-value: 720ms;
  --press-scale: 0.97; --press-bright: 0.96;

  /* ── Spacing (8pt) ── */
  --s-1: 6px;  --s-2: 10px; --s-3: 14px; --s-4: 18px; --s-5: 22px;
  --s-6: 28px; --s-7: 36px; --s-8: 44px; --s-9: 56px;

  --font: "futura-pt", "Futura PT", "Futura", "Trebuchet MS", "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "SFMono-Regular", Menlo, monospace;
}

/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font);
  color: var(--text);
  background: var(--canvas);
  min-height: 100vh;
  font-weight: 400;
  letter-spacing: 0.005em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  position: relative;
  overflow-x: hidden;
  touch-action: manipulation;   /* disable double-tap / pinch zoom on touch devices */
  -webkit-user-select: none; user-select: none;   /* no text selection (except inputs/note) */
}
input, textarea { -webkit-user-select: text; user-select: text; }
/* Warm aurora wash behind the glass — gives the frost something to refract. */
body::before {
  content: "";
  position: fixed; inset: -20vmax;
  z-index: -1;
  background:
    radial-gradient(42vmax 42vmax at 12% -6%,  rgba(254,229,165,0.85), transparent 60%),
    radial-gradient(40vmax 40vmax at 100% 8%,  rgba(167,127,178,0.40), transparent 58%),
    radial-gradient(46vmax 46vmax at 82% 104%, rgba(247,72,39,0.20),  transparent 60%),
    radial-gradient(50vmax 50vmax at 0% 100%,  rgba(181,175,141,0.45), transparent 62%),
    var(--canvas);
  filter: saturate(108%);
}
button, input { font-family: inherit; color: inherit; border: 0; outline: 0; -webkit-appearance: none; appearance: none; background: none; }
button { cursor: pointer; }
svg { display: block; }

/* eyebrow / label style — brand "Label" type (11px / 0.14em tracking) */
.eyebrow {
  font-size: 11px; font-weight: 400;
  text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--text-faint);
}

/* ============================================================================
   AUTH — loader + sign-in
   Markup + styling now come from the canonical shared components
   (.atlas-loader / .atlas-login in atlas-components.css). Only the show/hide
   glue the JS drives lives here.
   ========================================================================== */
/* the sign-in page is hidden until the auth gate says "signin" (JS adds .show) */
#signin-screen { display: none; }
#signin-screen.show { display: grid; }

/* ============================================================================
   APP SHELL
   ========================================================================== */
#app-shell { display: none; }
#app-shell.visible { display: block; }

.wrap {
  max-width: 1320px; margin: 0 auto;
  padding:
    max(var(--s-6), calc(env(safe-area-inset-top) + var(--s-5)))
    max(var(--s-6), env(safe-area-inset-right))
    calc(var(--s-7) + env(safe-area-inset-bottom))
    max(var(--s-6), env(safe-area-inset-left));
}

/* Live dot + pulse (master hero LIVE pill) and the avatar chip (carousel people,
   members list) — the surrounding top-bar / user-pill / floating-dock chrome was
   removed along with the global header; the paged shell uses none of it. */
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--success); box-shadow: 0 0 0 0 rgba(45,138,79,0.5); animation: pulse 2s infinite; }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(45,138,79,0.5); } 70% { box-shadow: 0 0 0 7px rgba(45,138,79,0); } 100% { box-shadow: 0 0 0 0 rgba(45,138,79,0); } }
.avatar { width: 32px; height: 32px; border-radius: 50%; background: var(--ink); color: var(--text-inverse); display: grid; place-items: center; font-size: 12px; font-weight: 400; flex-shrink: 0; }

/* ============================================================================
   MASTER widget — greeting · time · live weather · announcements (animated sky)
   ========================================================================== */
.master { position: relative; overflow: hidden; border-radius: var(--atlas-glass-radius, var(--m-radius)); min-height: 0;
  margin-bottom: var(--s-5); color: var(--m-fg);
  container-type: inline-size; container-name: w;   /* hero adapts to its own rendered width */
  border: var(--atlas-glass-border, 1px solid var(--m-border));
  box-shadow: var(--atlas-glass-shadow, var(--m-inset), var(--drop));
  --m-fg: #fff; --m-fg2: rgba(255,255,255,0.82); --m-fg3: rgba(255,255,255,0.64);
  --m-shadow: 0 1px 10px rgba(0,0,0,0.35); --m-chip: rgba(255,255,255,0.16); --m-chip-line: rgba(255,255,255,0.18);
  --m-border: rgba(255,255,255,0.16); --m-inset: inset 0 1px 0 rgba(255,255,255,0.22); }
.master.text-dark {
  --m-fg: #1c1a17; --m-fg2: rgba(28,26,23,0.72); --m-fg3: rgba(28,26,23,0.55);
  --m-shadow: none; --m-chip: rgba(28,26,23,0.10); --m-chip-line: rgba(28,26,23,0.12);
  --m-border: rgba(28,26,23,0.08); --m-inset: inset 0 1px 0 rgba(255,255,255,0.4); }
/* ease the light/dark text flip when the time-of-day sky bucket changes (no 1-frame jump) */
.m-clock, .m-name, .m-date, .m-hijri, .m-eyebrow { transition: color var(--dur-3) var(--ease-out), text-shadow var(--dur-3) var(--ease-out); }
.master-sky { position: absolute; inset: 0; z-index: 0; transition: background-image .6s ease;
  background-image: linear-gradient(180deg,#e7f4fd 0%,#ebf3f5 25%,#f3f1e6 50%,#faeed7 75%,#ffedce 100%); }
.master-grain { position: absolute; inset: 0; z-index: 1; opacity: var(--glass-noise); mix-blend-mode: soft-light; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }
.master-inner { position: relative; z-index: 3; display: flex; flex-direction: column; min-height: 0; height: 100%; padding: clamp(16px, 6cqi, 36px); }
.master-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; }
.master-tr { display: flex; flex-direction: column; align-items: flex-end; gap: 12px; }
.master-live { display: inline-flex; align-items: center; gap: 7px; padding: 5px 12px; border-radius: var(--radius-pill);
  background: var(--m-chip); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--m-fg); }
.master-live .live-dot { background: #2fb86a; }
.master-live.stale { background: rgba(237,184,69,0.30); }
.master-live.stale .live-dot { background: var(--warning); animation: none; }
.m-eyebrow { font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--m-fg2); text-shadow: var(--m-shadow); }
.m-name { font-size: clamp(19px, 6cqi, 38px); font-weight: 400; line-height: 1.02; margin-top: 6px; letter-spacing: -0.01em; color: var(--m-fg); text-shadow: var(--m-shadow); }
.master-wx { display: flex; align-items: center; gap: 12px; color: var(--m-fg); }
.master-wx .wx-r { text-align: right; }
.master-wx .wx-temp { font-size: clamp(20px, 7cqi, 32px); font-weight: 300; line-height: 1; font-variant-numeric: tabular-nums; }
.master-wx .wx-cond { font-size: 12px; color: var(--m-fg2); }
.master-wx .wx-place { font-size: 11px; color: var(--m-fg3); margin-top: 2px; }
.master-wx .wx-ico { width: 46px; height: 46px; }
.master-wx .wx-ico svg { width: 46px; height: 46px; stroke-width: 1.4; }
.master-mid { margin-top: auto; }
.m-clock { font-size: clamp(38px, 17cqi, 96px); font-weight: 300; line-height: 0.94; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; color: var(--m-fg); text-shadow: var(--m-shadow); }
.m-clock .ampm { font-size: 0.3em; letter-spacing: 0.08em; margin-left: 8px; color: var(--m-fg3); }
.m-date { font-size: 15px; color: var(--m-fg2); margin-top: 8px; }
.m-hijri { font-size: 12px; color: var(--m-fg3); margin-top: 3px; }
.master-ann { margin-top: 20px; display: flex; align-items: center; gap: 12px;
  background: var(--m-chip); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border: 1px solid var(--m-chip-line); border-radius: var(--radius-pill); padding: 10px 16px; min-height: 46px; color: var(--m-fg); }
.master-ann[hidden] { display: none; }
.master-ann .ma-ico { width: 26px; height: 26px; border-radius: var(--radius-sm); flex-shrink: 0; display: grid; place-items: center; background: var(--m-chip); }
.master-ann .ma-ico svg { width: 14px; height: 14px; }
.master-ann .ma-body { flex: 1; min-width: 0; overflow: hidden; }
.master-ann .ma-line { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.master-ann .ma-line b { font-weight: 400; }
.master-ann .ma-tag { font-size: 9px; letter-spacing: 0.10em; text-transform: uppercase; color: var(--m-fg2); }
.master-ann .ma-dots { display: flex; gap: 5px; flex-shrink: 0; }
.master-ann .ma-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--m-fg3); transition: all .2s; }
.master-ann .ma-dot.on { background: var(--m-fg); width: 15px; border-radius: 99px; }
/* ============================================================================
   CARD BASE
   ========================================================================== */
.card {
  position: relative;
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: var(--glass-border);
  /* Fluid chrome — radius/padding are the card's own properties, so they can't be
     driven by the card's OWN @container query (a container can't query itself); a
     gentle viewport clamp keeps them in proportion. Everything INSIDE the card
     (type, gaps, which elements show) is driven by `@container w` on the card's
     real rendered width — see the FLUID WIDGET SYSTEM section. */
  border-radius: var(--r-card);
  box-shadow: var(--glass-shadow);
  padding: var(--r-card-pad);
  display: flex; flex-direction: column;
  min-height: 0;
  /* one motion language: lift eases its shadow + filter too */
  transition: transform var(--dur-2) var(--ease-spring),
              box-shadow var(--dur-2) var(--ease-out),
              filter var(--dur-1) var(--ease-out);
  /* Each widget is its own sizing context: its internals respond to THIS width. */
  container-type: inline-size;
  container-name: w;
}
.card.clickable { cursor: pointer; }
.card.clickable:hover { transform: translateY(-3px); box-shadow: var(--glass-shadow-lift); }
.card.clickable:active { transform: scale(var(--press-scale)); filter: brightness(var(--press-bright));
  transition: transform var(--dur-1) var(--ease-out), filter var(--dur-1) var(--ease-out); }
/* swipe-safe long-press LIFT (toggled by the Pager only after a hold with no drag) */
.card.lifted { transform: scale(1.03); box-shadow: var(--glass-shadow-lift); z-index: 2;
  transition: transform var(--dur-3) var(--ease-emphasized), box-shadow var(--dur-3) var(--ease-out); }
.col-2 { grid-column: span 2; }
.col-4 { grid-column: span 4; }
.row-2 { grid-row: span 2; }

.card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); margin-bottom: var(--s-3); }
.card-head .ch-title { display: flex; align-items: center; gap: 9px; font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-faint); font-weight: 400; }
.card-head .ch-ico { width: 26px; height: 26px; border-radius: var(--radius-sm); display: grid; place-items: center; background: var(--well); color: var(--text-muted); }
.card-head .ch-ico svg { width: 15px; height: 15px; }
/* Entity accent palette (FF types_color_palette) — tints per related widget */
.ch-ico.e-project  { background: rgba(96,9,26,0.10);    color: #60091A; }
.ch-ico.e-reminder { background: rgba(185,225,108,0.28); color: #5c7a1c; }
.ch-ico.e-client   { background: rgba(0,28,113,0.10);   color: #001C71; }
.ch-ico.e-member   { background: rgba(0,120,63,0.13);   color: #00783F; }
.ch-ico.e-location { background: rgba(203,172,15,0.22); color: #8a7400; }
.ch-ico.e-entry    { background: rgba(167,127,178,0.16); color: #6e4a79; }
.ch-ico.e-brand    { background: rgba(214,129,55,0.16);  color: #b3651f; }
.ch-ico.e-invoice  { background: rgba(161,78,221,0.14);  color: #8a39c4; }
.chip { font-size: 10px; letter-spacing: var(--tracking-caps); text-transform: uppercase; padding: 4px 10px; border-radius: var(--radius-pill); background: var(--well); color: var(--text-muted); white-space: nowrap;
  transition: transform var(--dur-1) var(--ease-out), background var(--dur-2) var(--ease-out), filter var(--dur-1) var(--ease-out); }
.chip:active { transform: scale(var(--press-scale)); filter: brightness(var(--press-bright)); }
.chip.red    { background: rgba(247,72,39,0.14);  color: #b5371f; }
.chip.green  { background: rgba(45,138,79,0.14);  color: #1f6b3c; }
.chip.amber  { background: rgba(237,184,69,0.20); color: #8a6300; }
.chip.violet { background: rgba(167,127,178,0.18);color: #6e4a79; }

/* KPI tile */
.kpi { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.kpi .big { font-size: clamp(25px, 18cqi, 52px); font-weight: 300; line-height: 0.92; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.kpi .sub { font-size: 12px; color: var(--text-faint); margin-top: var(--s-2); }
.kpi .meta-row { display: flex; gap: var(--s-3); margin-top: var(--s-3); flex-wrap: wrap; }
.kpi .meta-row:empty { display: none; margin: 0; }
.kpi .meta-row .m { font-size: 11px; color: var(--text-muted); display: inline-flex; align-items: center; gap: 5px; }
.dotmark { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }

/* Revenue card */
.rev-net { font-size: clamp(26px, 15cqi, 50px); font-weight: 300; line-height: 1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.rev-net .cur { font-size: clamp(12px, 4.2cqi, 18px); color: var(--text-faint); margin-right: 6px; font-weight: 400; }
.rev-net.neg { color: var(--signature-red); }
.rev-sub { font-size: 12px; color: var(--text-faint); margin-top: 4px; }
.rev-bars { display: flex; flex-direction: column; gap: var(--s-3); margin-top: auto; }
.rev-bars .rb-row { }
.rev-bars .rb-top { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 6px; }
.rev-bars .rb-top .rb-amt { font-variant-numeric: tabular-nums; font-weight: 400; }
.rev-track { height: 9px; border-radius: var(--radius-pill); background: var(--well); overflow: hidden; }
.rev-fill { height: 100%; border-radius: var(--radius-pill); transition: width .8s cubic-bezier(.4,0,.2,1); }
.rev-fill.in  { background: var(--royal-lavender); }   /* income — purple */
.rev-fill.out { background: #9d9790; }                 /* expense — warm gray */

/* Segmented bar (project / invoice status) */
.seg-bar { display: flex; height: 14px; border-radius: var(--radius-pill); overflow: hidden; background: var(--well); margin: var(--s-2) 0 var(--s-4); box-shadow: inset 0 0 0 1px rgba(14,14,14,0.04); }
.seg-bar .seg { height: 100%; transition: width .8s cubic-bezier(.4,0,.2,1); min-width: 0; }
.seg-legend { display: flex; flex-direction: column; gap: 9px; }
.seg-legend .sl-row { display: flex; align-items: center; gap: 9px; font-size: 12px; }
.seg-legend .sl-row .sl-name { flex: 1; color: var(--text-muted); }
.seg-legend .sl-row .sl-val { font-variant-numeric: tabular-nums; font-weight: 400; }

/* Horizontal bar list (brands) */
.hbars { display: flex; flex-direction: column; gap: var(--s-4); flex: 1; justify-content: space-between; }
.hbar-row .hb-top { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 5px; }
.hbar-row .hb-name { color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbar-row .hb-val { font-variant-numeric: tabular-nums; font-weight: 400; color: var(--text); }
.hbar-track { height: 8px; border-radius: var(--radius-pill); background: var(--well); overflow: hidden; }
.hbar-fill { height: 100%; border-radius: var(--radius-pill); background: #D68137; transition: width .8s cubic-bezier(.4,0,.2,1); }
.hbar-fill.exp { background: #9d9790; }

/* Cash-flow trend column chart */
.col-chart { display: flex; align-items: flex-end; gap: clamp(3px, 2.4cqi, 14px); flex: 1; min-height: 0; padding-top: var(--s-2); }
.cc-month { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 6px; height: 100%; justify-content: flex-end; }
.cc-bars { display: flex; align-items: flex-end; gap: clamp(2px, 1cqi, 4px); flex: 1; min-height: 0; width: 100%; justify-content: center; }
.cc-bar { width: clamp(5px, 2.6cqi, 11px); border-radius: 5px 5px 0 0; transition: height .8s cubic-bezier(.4,0,.2,1); }
.cc-bar.in { background: var(--royal-lavender); }
.cc-bar.out { background: #9d9790; }
.cc-label { font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-faint); }
.legend-row { display: flex; gap: var(--s-4); margin-top: var(--s-3); }
.legend-row .lg { font-size: 11px; color: var(--text-muted); display: inline-flex; align-items: center; gap: 6px; }

/* List card (approvals / activity) */
.list { display: flex; flex-direction: column; gap: 2px; flex: 1; overflow-y: auto; min-height: 0; }
.list-row { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-2) 0; border-bottom: 1px solid var(--hair);
  border-radius: var(--radius-sm); margin-inline: calc(-1 * var(--s-1)); padding-inline: var(--s-1);
  transition: background var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out), filter var(--dur-1) var(--ease-out); }
.list-row:last-child { border-bottom: 0; }
.list-row:active { background: var(--row-press); transform: scale(0.992); filter: brightness(var(--press-bright)); }
.lr-ico { width: 30px; height: 30px; border-radius: var(--radius-sm); flex-shrink: 0; display: grid; place-items: center; background: var(--well); color: var(--text-muted); }
.lr-ico.round { border-radius: 50%; }
.lr-ico svg { width: 15px; height: 15px; }
.lr-ico.green  { background: rgba(45,138,79,0.14);  color: #1f6b3c; }
.lr-ico.amber  { background: rgba(237,184,69,0.20); color: #8a6300; }
.lr-ico.red    { background: rgba(247,72,39,0.14);  color: #b5371f; }
.lr-ico.violet { background: rgba(167,127,178,0.18);color: #6e4a79; }
.lr-body { flex: 1; min-width: 0; }
.lr-title { font-size: 13px; font-weight: 400; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lr-meta { font-size: 11px; color: var(--text-faint); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.act-who { font-weight: 400; }
#activity-list .lr-title { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.35; }

/* Email Summary digest (mirrors the FlutterFlow app's inbox briefing) — flat inbox list */
.mail-card { height: auto; }
.mail-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(310px, 1fr)); column-gap: var(--s-7); row-gap: 0; max-height: 224px; overflow-y: auto; }
.mail-loading { color: var(--text-faint); font-size: 12px; padding: 12px 2px; }
.mail-item { display: flex; flex-direction: column; gap: 2px; padding: 10px 0; border-bottom: 1px solid var(--hair); min-width: 0; }
.mail-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.mail-subj { font-size: 13px; font-weight: 400; color: var(--text); line-height: var(--lh-tight); display: flex; align-items: center; gap: 7px; min-width: 0; }
.mail-subj-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.mail-flag { font-size: 8.5px; letter-spacing: var(--tracking-caps-wide); text-transform: uppercase; font-weight: 400; color: #fff; background: var(--signature-red); padding: 2px 7px; border-radius: 99px; flex-shrink: 0; line-height: 1.5; }
.mail-date { font-variant-numeric: tabular-nums; }
.mail-date { font-size: 10.5px; color: var(--text-faint); flex-shrink: 0; white-space: nowrap; }
.mail-from { font-size: 11px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-body { font-size: 11.5px; color: var(--text-faint); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
#activity-list .lr-title strong { font-weight: 400; }
.lr-time { font-size: 11px; color: var(--text-faint); white-space: nowrap; font-variant-numeric: tabular-nums; flex-shrink: 0; }

/* Members widget */
.mem-ava { width: 32px; height: 32px; font-size: 11px; background: var(--champagne); color: #fff; flex-shrink: 0; overflow: hidden; padding: 0; border-radius: 100%; }
.mem-ava img { width: 100%; height: 100%; object-fit: cover; border-radius: 0; display: block; }
.mem-on { width: 8px; height: 8px; border-radius: 50%; background: var(--success); flex-shrink: 0; margin-right: 5px; box-shadow: 0 0 0 3px rgba(45,138,79,0.15); }

/* Butler typewriter caret */
.bm.bm-stream::after { content: "▍"; opacity: .5; animation: bcar 1s steps(1) infinite; }
@keyframes bcar { 50% { opacity: 0; } }

/* Sticky note widget (writable, device-local) */
.note-card { background: rgba(254,229,165,0.42); }
.note-card .ch-ico { background: rgba(176,122,0,0.14); color: #8a6300; }
.note-card #note-status { background: rgba(45,138,79,0.16); color: #1f6b3c; opacity: 0; transition: opacity .25s ease; }
.note-card #note-status.show { opacity: 1; }
.note-text { flex: 1; width: 100%; font-weight: 300; border: 0; background: transparent; resize: none; outline: none;
  font-family: inherit; font-size: 13px; line-height: 1.55; color: var(--text); margin-top: var(--s-2); min-height: 0; }
.note-text::placeholder { color: var(--text-faint); }

/* System status widget */
.sys-main { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-4); }
.sys-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--text-faint); flex-shrink: 0; box-shadow: 0 0 0 4px rgba(0,0,0,0.04); }
.sys-dot.ok  { background: var(--success); box-shadow: 0 0 0 4px rgba(45,138,79,0.14); }
.sys-dot.warn{ background: var(--warning); box-shadow: 0 0 0 4px rgba(237,184,69,0.18); }
.sys-dot.err { background: var(--error);   box-shadow: 0 0 0 4px rgba(247,72,39,0.16); }
.sys-label { font-size: clamp(15px, 5cqi, 22px); font-weight: 300; }
.sys-sub { font-size: 11px; color: var(--text-faint); margin-top: 2px; }
.sys-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px var(--s-4); flex: 1; align-content: space-evenly; margin-top: var(--s-3); }
.sys-grid .sg-item { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-muted); }
.sys-grid .sg-item .dotmark { flex-shrink: 0; }
.sys-grid .sg-item .sg-n { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--text-faint); }

.empty { flex: 1; display: grid; place-items: center; text-align: center; color: var(--text-faint); font-size: 12px; padding: var(--s-5) 0; }

/* Skeleton shimmer */
.skeleton, .skeleton-text {
  background: linear-gradient(100deg, rgba(14,14,14,0.05) 30%, rgba(14,14,14,0.10) 50%, rgba(14,14,14,0.05) 70%);
  background-size: 200% 100%; animation: shimmer 1.4s infinite; border-radius: 8px; color: transparent !important;
}
.skeleton-text { display: inline-block; min-width: 2ch; }
@keyframes shimmer { to { background-position: -200% 0; } }

/* ── Full-bleed card + generic cross-fade carousel ── */
.bleed-card { padding: 0; overflow: hidden; }
.bleed-card.tall { min-height: 300px; }
.cz { position: absolute; inset: 0; border-radius: inherit; overflow: hidden; isolation: isolate; cursor: grab; }
/* horizontal scroll-snap track — slides slide (drag-follow + momentum), same feel as the pager */
.cz-track { display: flex; height: 100%; width: 100%; overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.cz-track::-webkit-scrollbar { display: none; }
.cz-track.cz-dragging { scroll-snap-type: none; cursor: grabbing; }
.cz-slide { position: relative; flex: 0 0 100%; width: 100%; height: 100%;
  scroll-snap-align: start; scroll-snap-stop: always; display: flex; flex-direction: column; padding: var(--s-5); overflow: hidden; }
.cz-bg { position: absolute; inset: 0; z-index: 1; background-size: cover; background-position: center; }
.cz-bg.placeholder { background: linear-gradient(150deg, var(--champagne), var(--royal-lavender)); }
.cz-noise { position: absolute; inset: 0; z-index: 2; opacity: 0.20; pointer-events: none; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }
.cz-overlay { position: absolute; inset: 0; z-index: 3; background: linear-gradient(180deg, rgba(10,10,10,0.14) 0%, rgba(10,10,10,0.40) 50%, rgba(10,10,10,0.82) 100%); }
.cz-content { position: relative; z-index: 4; display: flex; flex-direction: column; flex: 1; min-height: 0; }
.cz-dots { position: absolute; bottom: 20px; right: 25px; z-index: 6; display: flex; gap: 6px; }
.cz-dots .cz-dot { width: 7px; height: 7px; border-radius: 50%; background: rgba(255,255,255,0.45); cursor: pointer; transition: all .2s ease; }
.cz-dots .cz-dot.on { background: #fff; width: 18px; border-radius: var(--radius-pill); }

/* On-site slide */
.loc-top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-2); }
.loc-eyebrow { font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(255,255,255,0.8); text-shadow: 0 1px 6px rgba(0,0,0,0.5); }
.loc-title { color: #fff; font-size: clamp(15px, 5.5cqi, 23px); font-weight: 300; margin-top: 3px; text-shadow: 0 1px 8px rgba(0,0,0,0.45); }
.loc-addr { color: rgba(255,255,255,0.78); font-size: 11px; margin-top: 2px; text-shadow: 0 1px 6px rgba(0,0,0,0.5); }
.loc-count { display: inline-flex; align-items: center; gap: 6px; background: rgba(255,255,255,0.20); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); color: #fff; padding: 6px 12px; border-radius: var(--radius-pill); font-size: 13px; font-weight: 400; white-space: nowrap; }
.loc-count .live-dot { background: #fff; }
.loc-people { display: flex; flex-wrap: wrap; gap: 7px; margin-top: auto; padding-top: var(--s-4); }
.loc-chip { display: inline-flex; align-items: center; gap: 8px; background: rgba(255,255,255,0.18); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); border: 1px solid rgba(255,255,255,0.22); padding: 4px 13px 4px 4px; border-radius: var(--radius-pill); }
.loc-chip .avatar { width: 23px; height: 23px; font-size: 9px; background: rgba(255,255,255,0.92); color: var(--ink); }
.loc-chip .lc-name { color: #fff; font-size: 12px; font-weight: 400; max-width: 170px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.loc-empty { color: rgba(255,255,255,0.85); font-size: 13px; margin-top: auto; text-shadow: 0 1px 6px rgba(0,0,0,0.5); }

/* Announcement slide */
.ann-card { color: #fff; }
.ann-slide .cz-bg.placeholder { background: linear-gradient(145deg, #2b2722, #4a4036 58%, var(--champagne)); }
.ann-slide .cz-content { justify-content: flex-end; }
.ann-eyebrow { display: inline-flex; align-items: center; gap: 6px; font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(255,255,255,0.82); }
.ann-eyebrow svg { width: 13px; height: 13px; }
.ann-eyebrow.important { color: var(--premium-yellow); }
.ann-title { color: #fff; font-size: clamp(14px, 5cqi, 21px); font-weight: 300; margin-top: 8px; text-shadow: 0 1px 8px rgba(0,0,0,0.45); }
.ann-text { color: rgba(255,255,255,0.86); font-size: 12px; margin-top: 5px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.ann-until { color: rgba(255,255,255,0.72); font-size: 11px; margin-top: 8px; }

/* Project comments — grouped (3 per project) */
.pc-proj { padding: var(--s-3) 0; border-bottom: 1px solid var(--hair); }
.pc-proj:last-child { border-bottom: 0; }
.pc-proj-name { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 400; }
.pc-comment { display: flex; gap: 8px; font-size: 12px; color: var(--text-muted); margin-top: 7px; padding-left: 16px; }
.pc-comment .pc-bullet { color: var(--royal-lavender); flex-shrink: 0; }
.pc-comment .pc-body { flex: 1; min-width: 0; overflow: hidden; }
.pc-comment .pc-meta { color: var(--text-faint); font-size: 11px; white-space: nowrap; flex-shrink: 0; font-variant-numeric: tabular-nums; }
.pc-comment .pc-by { color: var(--text-faint); font-weight: 400; opacity: 0.85; }

/* ── Status pill (leave / invoices) ── */
.spill { font-size: 9px; letter-spacing: var(--tracking-caps-wide); text-transform: uppercase; padding: 3px 8px; border-radius: var(--radius-pill); font-weight: 400; white-space: nowrap;
  transition: transform var(--dur-1) var(--ease-out), filter var(--dur-1) var(--ease-out); }
.spill:active { transform: scale(var(--press-scale)); filter: brightness(var(--press-bright)); }
.spill.pending  { background: rgba(237,184,69,0.22); color: #8a6300; }
.spill.approved { background: rgba(45,138,79,0.16);  color: #1f6b3c; }
.spill.rejected { background: rgba(247,72,39,0.14);  color: #b5371f; }
.spill.draft    { background: rgba(14,14,14,0.08);   color: var(--text-faint); }
.spill.amount   { background: rgba(14,14,14,0.05);   color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* sub-header inside a stacked card */
.card-sub { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-faint); margin: var(--s-3) 0 var(--s-2); }
.card-sub:first-child { margin-top: 0; }

.foot { margin-top: var(--s-7); display: flex; flex-direction: column; align-items: center; gap: var(--s-2); text-align: center; color: var(--text-faint); font-size: 11px; letter-spacing: 0.04em; }
.foot .brand-name { letter-spacing: 0.18em; font-size: 12px; }
.foot-sub { font-size: 11px; color: var(--text-faint); }
.foot-logout { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 14px; margin-top: var(--s-1); border-radius: var(--radius-pill);
  background: var(--glass-bg); border: var(--glass-border); -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  color: var(--text-muted); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; cursor: pointer; transition: background .18s ease, color .18s ease; }
.foot-logout:hover { background: var(--ink); color: var(--text-inverse); }
.foot-logout svg { width: 14px; height: 14px; }
.foot-disclaimer { max-width: 56ch; margin: var(--s-4) auto 0; font-size: 10px; line-height: 1.6; color: var(--text-faint); opacity: 0.85; }

/* ============================================================================
   FB BUTLER — AI concierge (in-grid bento card)
   ========================================================================== */
.butler-ava { width: 26px; height: 26px; border-radius: 50%; overflow: hidden; flex-shrink: 0; background: #F6E4A8; }
.butler-ava img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; }
.butler-card { min-height: 300px; background: var(--premium-yellow); border-color: rgba(28,26,23,0.06); }
.butler-card .ch-title { color: rgba(28,26,23,0.7); }
.butler-card .chip { background: rgba(28,26,23,0.10); color: rgba(28,26,23,0.78); }
.butler-msgs { flex: 1; min-height: 120px; overflow-y: auto; display: flex; flex-direction: column; gap: 9px; margin: var(--s-2) 0 var(--s-3); }
.butler-intro { text-align: center; color: rgba(28,26,23,0.6); font-size: 12px; line-height: 1.6; padding: var(--s-3); margin: auto 0; }
.butler-intro b { color: rgba(28,26,23,0.85); }
.bm { max-width: 86%; padding: 10px 14px; border-radius: var(--radius-control); font-size: 13px; line-height: 1.5; word-wrap: break-word; }
.bm a { color: #ffd27a; text-decoration: underline; }
.bm strong { font-weight: 400; }
.bm.user { align-self: flex-end; background: rgba(20,18,15,0.14); color: var(--text); border-bottom-right-radius: 5px; }
.bm.bot  { align-self: flex-start; background: #141210; color: #f4f1ea; border: 0; border-bottom-left-radius: 5px; }
.bm.err  { align-self: stretch; max-width: 100%; background: rgba(247,72,39,0.14); border: 1px solid rgba(247,72,39,0.35); color: #7a2414; }
.bm-typing { align-self: flex-start; display: flex; gap: 4px; padding: 14px; border-radius: 16px; background: #141210; }
.bm-typing span { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,0.7); animation: bdot 1s infinite; }
.bm-typing span:nth-child(2) { animation-delay: .15s; }
.bm-typing span:nth-child(3) { animation-delay: .3s; }
@keyframes bdot { 0%,60%,100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }

.butler-input { display: flex; gap: 8px; margin-top: auto; }
.butler-input input { flex: 1; height: 42px; padding: 0 16px; border-radius: var(--radius-pill); color: var(--text); font-size: 13px;
  background: rgba(255,255,255,0.28); border: 1px solid rgba(255,255,255,0.5);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.butler-input input::placeholder { color: rgba(28,26,23,0.5); }
.butler-input input:focus { border-color: rgba(28,26,23,0.4); background: rgba(255,255,255,0.5); }
.butler-send { width: 42px; height: 42px; border-radius: 50%; background: #141210; color: #fff; display: grid; place-items: center; flex-shrink: 0; cursor: pointer; border: 0; transition: opacity .18s ease; }
.butler-send:hover { opacity: 0.85; }
.butler-send:disabled { opacity: .5; cursor: default; }
.butler-send svg { width: 18px; height: 18px; }

/* ── Toast ── */
.toast { position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom)); transform: translateX(-50%) translateY(20px); opacity: 0; pointer-events: none; transition: opacity .26s var(--ease-out), transform var(--dur-3) var(--ease-spring); z-index: 300; background: var(--ink); color: var(--text-inverse); padding: 12px 20px; border-radius: var(--radius-pill); font-size: 13px; box-shadow: 0 14px 38px -14px rgba(0,0,0,0.5), var(--drop); }
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.error { background: var(--signature-red); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ============================================================================
   NATIVE iPad PAGED SHELL  —  horizontal swipe pages · dots (no global header)
   Everything is scoped to #app-shell.visible so the loader / sign-in screen are
   never affected. The shell stays identical; only the PAGE COMPOSITION below is
   new — pages are fluid grids and every widget is a size-aware @container, so
   layout follows rendered size, not device breakpoints.
   ========================================================================== */
:root{
  --dots-h: 40px;       /* dots strip height */
  --gut: clamp(12px, 2.0vmin, 20px);   /* fluid grid gutter + page padding */
  --safe-t: env(safe-area-inset-top);
  --safe-b: env(safe-area-inset-bottom);
  --safe-l: env(safe-area-inset-left);
  --safe-r: env(safe-area-inset-right);
}

/* App shell becomes the fixed full-screen frame ONLY when authed + visible. */
#app-shell.visible{
  position: fixed; inset: 0;
  display: flex; flex-direction: column;
  height: 100dvh;
  width: 100vw; overflow: hidden;
  overscroll-behavior: none;
}
#app-shell.visible .wrap{ max-width:none; margin:0; padding:0; flex:1 1 auto;
  min-height:0; display:flex; flex-direction:column; }

/* ── Canonical topbar (.atlas-topbar from atlas-components.css) sits above the
   pager as a fixed-height flex row — shell glue only (safe areas + gutters).
   LIVE pill stays top-right of the master hero; sign-out stays pinned to the
   hero's BOTTOM-RIGHT corner. Both adapt to the sky via the --m-* vars. ── */
#app-shell.visible .atlas-topbar {
  flex: 0 0 auto; position: static; margin: 0;
  padding: max(10px, calc(var(--safe-t) + 6px))
           max(var(--gut), var(--safe-r))
           6px
           max(var(--gut), var(--safe-l));
}
.master-signout{ position:absolute; right:var(--s-5); bottom:var(--s-5); z-index:4;
  width:36px; height:36px; border-radius:var(--radius-pill);
  background:var(--m-chip); border:1px solid var(--m-chip-line);
  -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
  display:grid; place-items:center; color:var(--m-fg); cursor:pointer; flex-shrink:0;
  transition:opacity .18s ease; }
.master-signout:hover{ opacity:0.78; }
.master-signout svg{ width:16px; height:16px; }

/* ── Pager: the only scroller, X only. Swipeable by touch (native) AND mouse
   (JS pointer-drag — see Pager controller). ── */
.pager{
  flex:1 1 auto; min-height:0;
  display:flex; flex-wrap:nowrap;
  overflow-x:auto; overflow-y:hidden;
  scroll-snap-type:x mandatory;
  overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
  cursor:grab;
  user-select:none; -webkit-user-select:none;
}
.pager::-webkit-scrollbar{ display:none; }
.pager.dragging{ cursor:grabbing; scroll-snap-type:none; }
.pager img{ -webkit-user-drag:none; user-drag:none; }
.pager input, .pager textarea{ -webkit-user-select:text; user-select:text; }

/* ── Page = exactly one viewport, AND the page-level sizing context.
   `container-type:inline-size` makes the page a query container so the COMPOSITION
   (how many columns, which widget is the hero) follows the page's own rendered
   width — there are no per-device viewport breakpoints anywhere below. ── */
.page{
  flex:0 0 100%; width:100%; height:100%;
  scroll-snap-align:start; scroll-snap-stop:always;
  padding: max(var(--s-4), calc(var(--safe-t) + var(--s-2)))   /* clear iOS status-bar glyphs + a small floor on desktop */
           max(var(--gut),var(--safe-r))
           max(var(--s-2), var(--safe-b))
           max(var(--gut),var(--safe-l));
  overflow:hidden;
  container-type: inline-size;
  container-name: page;
}
.page-fit{ height:100%; display:grid; gap:var(--gut); min-height:0; }

/* ── Overflow linchpin: every grid descendant fills its cell and never grows by
   content — the inner .list/.mail-list/.butler-msgs scrollers are the only valve.
   Descendant selector so cards inside wrappers (carousels, ops-cards) obey too. ── */
.page-fit .card,
.page-fit .master{ height:100%; min-height:0; margin:0; }
.page-fit .card{ overflow:hidden; }
.page-fit .list,
.page-fit .mail-list,
.page-fit .butler-msgs{ overflow-y:auto; min-height:0; }
.page-fit .bleed-card{ min-height:0; height:100%; }
.master-inner{ min-height:0; }
.page-fit .col-chart{ min-height:0; }
/* sub-grid wrappers fill their page cell and never overflow it */
.today-carousels, .today-bottom, .kpi-band, .ops-cards{ min-height:0; min-width:0; }

/* ════════════════════════════════════════════════════════════════════════════
   PAGE COMPOSITION — fluid, container-query driven
   Tiers are queried on the PAGE'S own inline-size (≈ viewport minus padding):
     base   < 640   → phone portrait (1 column, widgets go to their micro tiers)
     wide   ≥ 640   → tablet portrait, landscape, desktop (multi-column bento)
     xwide  ≥ 1560  → ultra-wide desktop (extra breathing room)
   Only 2 structural thresholds exist, and they're justified: a few widgets have a
   genuine "hero vs companion" relationship (master, Butler) that a single auto-fit
   track can't express. Everything else is plain fluid grid; widgets self-adapt to
   whatever cell size they're handed (see FLUID WIDGET SYSTEM).
   ════════════════════════════════════════════════════════════════════════════ */

/* ── PAGE 1 · TODAY ── hero + two carousels + (note · comments) ── */
.today-carousels, .today-bottom{ display:grid; gap:var(--gut); }
/* base (phone): hero, then a 2-up carousels row, then a 2-up note+comments row.
   The cells go narrow → each widget drops to its micro/compact tier (no clipping). */
#page-today .page-fit{ grid-template-columns:1fr; grid-template-rows:1.15fr 1fr 1.05fr; }
#page-today .today-carousels{ grid-template-columns:1fr 1fr; }
#page-today .today-bottom{ grid-template-columns:1fr 1fr; }

/* wide (landscape / desktop only): master is a half-width hero (top-left);
   carousels stack at top-right; note + comments fill the bottom row. This is the
   kiosk (iPad-11 landscape) view. Threshold is 1040 so the TALL iPad-portrait
   screen keeps the stacked layout above — which fills its height instead of
   leaving a short, half-empty bottom row. */
@container page (min-width: 1040px){
  #page-today .page-fit{ grid-template-columns:repeat(4,1fr); grid-template-rows:1.05fr 1fr; }
  #page-today .master{ grid-column:1 / span 2; grid-row:1; }
  #page-today .today-carousels{ grid-column:3 / span 2; grid-row:1; grid-template-columns:1fr; grid-template-rows:1fr 1fr; }
  #page-today .today-bottom{ grid-column:1 / -1; grid-row:2; grid-template-columns:1fr 1fr; }
}
/* xwide: give the hero a touch less width so the carousels breathe on big walls. */
@container page (min-width: 1560px){
  #page-today .page-fit{ grid-template-columns:repeat(5,1fr); }
  #page-today .master{ grid-column:1 / span 2; }
  #page-today .today-carousels{ grid-column:3 / span 3; grid-template-columns:1fr 1fr; grid-template-rows:1fr; }
}

/* ── PAGE 2 · FINANCE ── stable 2×2 quad at every size; cards self-adapt ── */
#page-fin .page-fit{ grid-template-columns:1fr 1fr; grid-template-rows:1fr 1fr; }

/* ── PAGE 3 · OPERATIONS ── KPI strip (always 4-up) + fluid card zone ── */
#page-ops .page-fit{ grid-template-columns:1fr; grid-template-rows:clamp(84px, 15vh, 156px) minmax(0,1fr); }
#page-ops .kpi-band{ display:grid; grid-template-columns:repeat(4,1fr); gap:var(--gut); }
/* the 3 cards tile 1-up (phone) → 2-up + full-width members (portrait) → 3-up
   (landscape/desktop). The portrait 2-up step exists so three short cards don't
   each stretch to ~1000px of dead height on a tall screen. */
#page-ops .ops-cards{ display:grid; gap:var(--gut); grid-template-columns:1fr; grid-auto-rows:minmax(0,1fr); }
@container page (min-width: 660px){
  #page-ops .ops-cards{ grid-template-columns:1fr 1fr; }
  #page-ops .ops-cards > :last-child{ grid-column:1 / -1; }   /* members spans the full width */
}
@container page (min-width: 1040px){
  #page-ops .ops-cards{ grid-template-columns:repeat(3,1fr); }
  #page-ops .ops-cards > :last-child{ grid-column:auto; }
}

/* ── PAGE 4 · LIVE ── Butler (hero) + Activity + System Status ── */
/* base (phone): stacked, Butler tallest. */
#page-live .page-fit{ grid-template-columns:1fr; grid-template-rows:1.5fr 1fr 0.92fr; }
/* wide: Butler is a full-height companion on the left; the two info cards stack right. */
@container page (min-width: 640px){
  #page-live .page-fit{ grid-template-columns:1.12fr 1fr; grid-template-rows:1fr 1fr; }
  #page-live .butler-card{ grid-column:1; grid-row:1 / span 2; }
}

/* ── PAGE 5 · EMAIL SUMMARY ── single card; inner list is its own fluid grid ── */
#page-mail .page-fit{ grid-template-columns:1fr; grid-template-rows:1fr; }
#page-mail .mail-list{ max-height:none; height:100%; overflow-y:auto; }

/* ── iOS Home-Screen dots ── */
.pager-dots{
  flex:0 0 auto;
  height: calc(var(--dots-h) + var(--safe-b)); padding-bottom:var(--safe-b);
  display:flex; align-items:center; justify-content:center; gap:9px; z-index:20;
}
.pager-dots .dot{ width:7px; height:7px; border-radius:50%;
  background:rgba(14,14,14,0.22); cursor:pointer; transition:width .25s ease, background .25s ease; border:0; padding:0; }
.pager-dots .dot.on{ width:22px; border-radius:99px; background:var(--ink); }
/* sky-aware inversion: on page 1 over a dark sky, flip dots to light */
body.sky-dark.on-home .pager-dots .dot{ background:rgba(255,255,255,0.40); }
body.sky-dark.on-home .pager-dots .dot.on{ background:#fff; }

/* hidden but alive: original foot (keeps #signout-btn + #foot-updated bound) */
#app-shell.visible .foot{ display:none; }

/* ── Scroll-model safety valve (the ONLY layout media query that remains).
   It does not define any grid — it just lets a page scroll vertically when the
   window is genuinely too SHORT to fit (rotated phone, tiny desktop window), so
   nothing clips. The priority viewports all fit without it. ── */
@media (max-height: 620px){
  #app-shell.visible .page{ overflow-y:auto; -webkit-overflow-scrolling:touch; }
  #app-shell.visible .page-fit{ height:auto; min-height:100%; }
  #app-shell.visible .page-fit .card,
  #app-shell.visible .page-fit .master{ min-height:clamp(120px, 30vh, 220px); }
}

/* ════════════════════════════════════════════════════════════════════════════
   FLUID WIDGET SYSTEM
   Every .card and .master is its own `w` query container (see CARD BASE / .master).
   These rules re-tier a widget's INTERNALS on its OWN rendered width — `cqi`
   units and `@container w (...)` queries, never the viewport. So one widget reads
   correctly as a tiny iPhone tile, a portrait panel, or a wall-display hero with
   NO per-device special-casing.

   Tiers (by the widget's own width):
     micro    < 150px   — essential element only (KPI number, chart, net figure…)
     compact  150–250   — + primary label / secondary line
     standard 250–360   — + metadata (amounts, timestamps, sub-labels)
     expanded ≥ 360     — generous type/spacing; content spreads to fill the cell

   Continuous type/spacing already scale via cqi clamp()s on the base rules above.
   The queries here are the progressive SHOW/HIDE so a shrinking widget drops its
   least-important parts instead of truncating, and a growing one fills the space.
   Disclosure order is the brief's: essential → label → sub/meta → chrome.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── Generic card chrome — header tightens, status chip drops first ── */
.card-head{ margin-bottom: clamp(8px, 2.4cqi, 16px); gap: var(--s-3); }
.card-head .ch-title{ min-width: 0; overflow: hidden; }
@container w (max-width: 230px){
  .card-head .chip{ display: none; }            /* the status pill is secondary chrome */
}
@container w (max-width: 150px){
  .card-head .ch-ico{ width: 22px; height: 22px; border-radius: 7px; }
  .card-head .ch-ico svg{ width: 13px; height: 13px; }
}

/* ── Lists (members · activity · leave · invoices) ──
   title (always) → secondary meta line → trailing time → leading icon. ── */
.list-row{ gap: clamp(8px, 2.6cqi, 14px); padding-block: clamp(6px, 1.8cqi, 11px); }
.lr-title{ font-size: clamp(12.5px, 2.6cqi, 15px); }
.lr-meta{ font-size: clamp(10.5px, 2.1cqi, 12.5px); }
@container w (max-width: 215px){
  .list-row .lr-meta{ display: none; }          /* drop the secondary line */
  .list-row .lr-time{ display: none; }
}
@container w (max-width: 150px){
  .list-row .lr-ico{ display: none; }           /* then the leading icon */
  .mem-ava{ width: 24px; height: 24px; font-size: 10px; }
}

/* ── Master hero — clock (essential) → name → date/hijri → weather detail ──
   The weather block trims horizontally before it can ever crowd the name. ── */
.master-top{ gap: clamp(8px, 3cqi, 18px); }
@container w (max-width: 520px){ .master .wx-place{ display: none; } }   /* drop place · hi/lo */
@container w (max-width: 410px){ .master .wx-cond{ display: none; } }    /* keep just the temperature */
@container w (max-width: 330px){ .master .m-hijri{ display: none; } }
.m-date{ font-size: clamp(12px, 3.4cqi, 16px); }

/* ── KPI tiles — number (essential) → icon → label → sub-label ──
   (thresholds are on the container's CONTENT box, i.e. width minus the card's
   own padding, so they fire a bit earlier than the tile's layout width.) ── */
.kpi-band .kpi{ gap: clamp(2px, 1.5cqi, 8px); }
@container w (max-width: 228px){
  .kpi-band .kpi .sub, .kpi-band .kpi .meta-row{ display: none; }   /* no sub-label yet */
}
@container w (max-width: 132px){
  .kpi-band .card-head{ justify-content: center; margin-bottom: 2px; }
  .kpi-band .card-head .ch-title{ font-size: 0; gap: 0; }          /* icon only — never a clipped label */
  .kpi-band .kpi{ align-items: center; text-align: center; }
}

/* ── Cash Flow · month — net figure (essential) → in/out bars → labels/amounts ──
   Center the net+bars cluster in the cell (auto margins top & bottom) so a tall
   card doesn't show a big empty band between the headline and the footer bars. ── */
.rev-net{ font-size: clamp(23px, 15cqi, 50px); margin-top: auto; }
.rev-bars{ gap: clamp(8px, 2.6cqi, 14px); margin-top: clamp(12px, 4cqi, 26px); margin-bottom: auto; }
@container w (max-width: 210px){
  .rev-sub{ display: none; }
  .rev-bars .rb-top .rb-amt{ display: none; }   /* the bar carries the value */
}

/* ── Cash Flow · 6-mo + Top Expenses — chart/bars fill height; legend drops first ── */
.hbars{ gap: clamp(8px, 2.4cqi, 18px); }
@container w (max-width: 230px){ .legend-row{ display: none; } }
@container w (max-width: 200px){ .hbar-row .hb-val{ display: none; } }

/* ── Segmented bars + legends (Pipeline · Invoices) ── */
.seg-bar{ height: clamp(11px, 3cqi, 20px); margin: clamp(6px,2cqi,10px) 0 clamp(10px,3cqi,18px); }
.seg-legend .sl-row{ font-size: clamp(11.5px, 2.5cqi, 15px); }
/* Pipeline has no list below its legend → let the legend spread to fill the tall
   card instead of leaving dead space under a short list of rows. */
@container w (min-width: 260px){ #proj-legend{ flex: 1; justify-content: space-evenly; } }
@container w (max-width: 215px){ .seg-legend .sl-row .sl-val{ font-weight: 400; } }

/* ── System Status — headline (essential) → per-area grid (fills, no dead gap) ── */
.sys-main{ gap: clamp(8px, 2.6cqi, 14px); }
.sys-grid .sg-item{ font-size: clamp(11px, 2.4cqi, 14px); }
@container w (max-width: 200px){ .sys-grid{ grid-template-columns: 1fr; } .sys-sub{ display: none; } }

/* ── FB Butler — messages + input always usable; trim header chrome when short ── */
.butler-msgs{ gap: clamp(7px, 2cqi, 10px); }
@container w (max-width: 230px){ .butler-input input{ padding: 0 12px; } }

/* ── On-site carousel — title (essential) → count → address → people ──
   At narrow the people chips collapse to an avatar cluster (names hidden) so they
   never wrap past the slide and clip the title above them. ── */
@container w (max-width: 250px){
  .loc-addr{ display: none; }
  .loc-people{ gap: 5px; padding-top: var(--s-2); max-height: 52%; overflow: hidden; }
  .loc-chip{ padding: 3px; }
  .loc-chip .lc-name{ display: none; }          /* avatars only */
}
@container w (max-width: 250px){ .loc-count{ padding: 4px 9px; font-size: 12px; } }

/* ── Announcements carousel — eyebrow + title (essential) → body text ── */
@container w (max-width: 250px){ .ann-text{ -webkit-line-clamp: 2; } }
@container w (max-width: 200px){ .ann-text{ display: none; } .ann-until{ font-size: 10px; } }

/* ── Latest Project Comments — comment (essential) → author byline → timestamp ── */
@container w (max-width: 230px){
  .pc-comment{ padding-left: 9px; }
  .pc-comment .pc-meta{ display: none; }
  .pc-comment .pc-by{ display: none; }
}

/* ── Email Summary — inbox grid reflows fluidly; rows trim on a narrow card ── */
.mail-list{ grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
@container w (max-width: 230px){ .mail-date{ display: none; } .mail-body{ -webkit-line-clamp: 2; } }

/* ════════════════════════════════════════════════════════════════════════════
   SMOOTH · FLUID · CLEAN — motion + polish layer
   Apple-grade CRAFT on OUR brand kit (Futura PT, warm frosted glass, brand palette).
   Everything here is interaction-driven and references the motion tokens; it is
   fully neutralized by prefers-reduced-motion at the bottom. Appended last so its
   transition/timing overrides win over the base rules' ad-hoc curves.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── LIVE-DATA enter — renderers rebuild nodes via innerHTML on each Firestore
   snapshot, so freshly-rendered rows/bars fade-RISE in instead of popping (free). ── */
@keyframes rowIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes barIn { from { opacity: 0; } to { opacity: 1; } }
/* `backwards` fill (not `both`): pre-applies the start to kill a first-frame flash
   but does NOT persist the end — so this filling animation can't out-rank the
   :active press transform on .list-row once the one-shot enter has finished. */
.list-row, .mail-item, .pc-proj { animation: rowIn var(--dur-2) var(--ease-out) backwards; }
.hbar-row, .cc-month, .sl-row   { animation: barIn var(--dur-2) var(--ease-out) backwards; }
@keyframes valBump { 0% { opacity: .5; } 100% { opacity: 1; } }
.big.val-bump { animation: valBump var(--dur-3) var(--ease-out) backwards; }

/* ── Value-change sweeps — ONE duration + emphasized curve across every bar/fill ── */
.rev-fill, .seg-bar .seg, .hbar-fill, .cc-bar {
  transition-duration: var(--dur-value); transition-timing-function: var(--ease-emphasized);
}

/* ── Page dots + carousel dots — spring the capsule growth, press pop, contact shadow ── */
.pager-dots .dot {
  transition: width var(--dur-2) var(--ease-spring), background-color var(--dur-2) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.pager-dots .dot.on { box-shadow: 0 1px 2px rgba(40,33,22,0.18); }
.pager-dots .dot:active { transform: scale(1.25); }
.cz-dots .cz-dot {
  transition: width var(--dur-2) var(--ease-spring), background-color var(--dur-2) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.cz-dots .cz-dot:active { transform: scale(1.3); }

/* ── Carousel slide press — brightness only (a transform would reveal a seam on full-bleed) ── */
.cz-slide { transition: filter var(--dur-2) var(--ease-out); }
.cz:active .cz-slide { filter: brightness(0.97); }

/* ── On-sky / on-photo glass chips — ONE recipe; legible over the night/dawn skies ── */
.loc-count, .loc-chip {
  background: var(--glass-bg-onsky); border: 1px solid var(--glass-border-onsky);
  -webkit-backdrop-filter: blur(8px) saturate(120%); backdrop-filter: blur(8px) saturate(120%);
}
.loc-count { text-shadow: 0 1px 6px rgba(0,0,0,0.45); }
.master-live, .master-ann, .master-signout {
  -webkit-backdrop-filter: blur(10px) saturate(140%); backdrop-filter: blur(10px) saturate(140%);
}
.master-signout { -webkit-tap-highlight-color: transparent; }

/* ── Chrome controls — tactile depress + brighter-glass hover + focus rings ── */
.master-signout, .butler-send {
  transition: transform var(--dur-1) var(--ease-spring), opacity var(--dur-2) var(--ease-out), background-color var(--dur-2) var(--ease-out);
}
.master-signout:hover { opacity: 1; background: color-mix(in srgb, var(--m-chip) 70%, #fff 30%); }
.master-signout:active, .butler-send:not(:disabled):active { transform: scale(var(--press-scale)); filter: brightness(var(--press-bright)); }
.master-signout:focus-visible, .master-live:focus-visible { outline: 2px solid var(--m-fg); outline-offset: 2px; }

/* ── Type rhythm — calm caps tracking, tight big numerals, tabular figures, open body ── */
.eyebrow, .card-head .ch-title, .card-sub, .m-eyebrow, .loc-eyebrow, .ann-eyebrow, .ma-tag { letter-spacing: var(--tracking-caps); }
.m-clock, .kpi .big, .rev-net { letter-spacing: var(--tracking-num); }
.lr-meta, .mail-date, .kpi .sub { font-variant-numeric: tabular-nums; }
.lr-title, .card-head .ch-title, .sys-label { line-height: var(--lh-tight); }
.mail-body, .ann-text, #activity-list .lr-title { line-height: var(--lh-body); }

/* ── Hairline inset to the text edge on the avatar/icon lists (Members, Leave) ── */
:where(#members-list, #vac-list) .list-row { position: relative; border-bottom: 0; }
:where(#members-list, #vac-list) .list-row::after { content: ''; position: absolute; left: 42px; right: 0; bottom: 0; height: 1px; background: var(--hair); }
:where(#members-list, #vac-list) .list-row:last-child::after { display: none; }
@container w (max-width: 150px) { :where(#members-list, #vac-list) .list-row::after { left: 0; } }

/* ── Optional: tactile micro-press on small glass controls (wired in dashboard.js) ── */
.pressed { transform: scale(0.94); transition: transform var(--dur-1) var(--ease-spring); }
.pager-dots .dot.pressed { transform: scale(0.9); }

/* ── Optional: iOS-grade continuous corners where supported (OFF until on-device review;
   gated behind BOTH @supports and a body class so non-supporting engines fall straight
   back to the rounded radii with zero JS). ── */
@supports (corner-shape: superellipse) {
  body.smooth-corners .card, body.smooth-corners .master { corner-shape: superellipse(1.8); }
}

/* ── Reduced-motion: neutralize every interaction/enter animation (functionality stays) ── */
@media (prefers-reduced-motion: reduce) {
  .list-row, .mail-item, .pc-proj, .hbar-row, .cc-month, .sl-row, .big.val-bump { animation: none; }
  .card.clickable:active, .list-row:active, .chip:active, .spill:active,
  .pager-dots .dot:active, .cz-dots .cz-dot:active, .cz:active .cz-slide,
  .master-signout:active, .butler-send:active, .pressed { transform: none; filter: none; }
}

/* ── Pager swipe physics — the drag sets a depth/rubber transform (JS); on release
   it springs back. No transition WHILE dragging (1:1 finger-follow), spring after. ── */
.pager { transition: transform var(--dur-3) var(--ease-spring); }
.pager.dragging { transition: none; }
.pager .page { transition: transform var(--dur-2) var(--ease-out); will-change: transform; }
.pager.dragging .page { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .pager, .pager .page { transition: none; }
}

/* ── Hidden scrollbars + a soft edge-fade that SIGNALS "more to scroll" ──
   The inner vertical scrollers hide their bars (clean) and mask their top/bottom
   edges. A tiny JS helper (wireScrollFades) grows the fade only on the side that
   still has hidden content — so the fade itself is the scroll affordance, and it
   eases in/out because --fade-top/-bottom are registered @property lengths.
   (The pager + carousels already hide their bars and must NOT be masked.) ── */
.list, .mail-list, .butler-msgs, .note-text { scrollbar-width: none; }
.list::-webkit-scrollbar, .mail-list::-webkit-scrollbar,
.butler-msgs::-webkit-scrollbar, .note-text::-webkit-scrollbar { width: 0; height: 0; display: none; }

@property --fade-top    { syntax: "<length>"; inherits: false; initial-value: 0px; }
@property --fade-bottom { syntax: "<length>"; inherits: false; initial-value: 0px; }
.list, .mail-list, .butler-msgs, .note-text {
  --fade-top: 0px; --fade-bottom: 0px;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade-top), #000 calc(100% - var(--fade-bottom)), transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade-top), #000 calc(100% - var(--fade-bottom)), transparent 100%);
  transition: --fade-top var(--dur-2) var(--ease-out), --fade-bottom var(--dur-2) var(--ease-out);
}
@media (prefers-reduced-motion: reduce) { .list, .mail-list, .butler-msgs, .note-text { transition: none; } }
