/* App shell: sidebar, main canvas, KPI row and the bento grids. */
/* =========================================================
   Redesign shell: persistent sidebar (nav + filters) + a
   dense bento-style main canvas, replacing the original's
   stacked hero / top nav / sticky filter-bar / blind teaser
   cards. Tokens, fonts, and every chart/table/dialog style
   above this point are unchanged from the shipped dashboard.
========================================================= */

html,body{ height:100%; }
/* No overflow-x:hidden here on purpose. It used to clip horizontal overflow
   rather than prevent it, which is how a phone layout that never fit went
   unnoticed. If something overflows now, it shows. */

#shell{ display:flex; align-items:flex-start; min-height:100vh; }

/* ---------- sidebar ---------- */
.side{
  width:288px; flex:none;
  min-height:calc(100vh - var(--app-header-h,0px)); max-height:calc(100vh - var(--app-header-h,0px));
  position:sticky; top:var(--app-header-h,0px); align-self:flex-start;
  display:flex; flex-direction:column;
  background:var(--surface); border-right:1px solid var(--border);
  overflow-y:auto;
}
.side-top{ padding:22px 22px 18px; border-bottom:1px solid var(--border); display:flex; flex-direction:column; gap:14px; }
.side-brand{ display:flex; align-items:center; gap:12px; }
.side-brand svg{ width:30px; height:30px; color:var(--text-primary); flex:none; }
.side-brand-text{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.side-brand-text .org{ font-family:var(--sans); font-weight:300; font-size:11.5px; color:var(--text-secondary); white-space:nowrap; }
.side-brand-text .product{ font-family:var(--sans); font-weight:700; font-size:15px; letter-spacing:-0.006em; color:var(--text-primary); white-space:nowrap; }
.side-top .pill-tag{ align-self:flex-start; }
.pill-tag{ font-family:var(--mono); font-size:10px; letter-spacing:.08em; text-transform:uppercase; padding:5px 10px; border-radius:var(--radius-pill); border:1px solid var(--amber-border); color:var(--amber-text); background:var(--amber-wash); }

.side-nav{ display:flex; flex-direction:column; padding:14px 12px; border-bottom:1px solid var(--border); gap:2px; }
.side-nav a{
  display:flex; align-items:center; gap:11px; height:38px; padding:0 10px 0 12px;
  border-left:2px solid transparent; border-radius:6px;
  font-family:var(--sans); font-weight:300; font-size:13.5px; color:var(--text-secondary);
  text-decoration:none; transition:background-color .15s, color .15s, border-color .15s;
}
.side-nav a svg{ width:17px; height:17px; flex:none; color:currentColor; }
.side-nav a:hover{ background:var(--surface-sunken); color:var(--text-primary); }
.side-nav a.on{ border-left-color:var(--tag); color:var(--text-primary); font-weight:400; background:var(--surface-sunken); }

.side-filters{ padding:18px 22px 20px; border-bottom:1px solid var(--border); display:flex; flex-direction:column; gap:14px; }
.side-filters-head{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
.side-filters-head .filter-count{ font-family:var(--mono); font-size:10.5px; color:var(--text-secondary); white-space:nowrap; }
.side-filters-body{ display:flex; flex-direction:column; gap:14px; }
.filters-collapse-toggle{
  width:26px; height:26px; flex:none; border:0; border-radius:var(--radius-sm);
  background:transparent; color:var(--text-secondary); cursor:pointer;
  display:flex; align-items:center; justify-content:center;
}
.filters-collapse-toggle:hover{ background:var(--surface-sunken); color:var(--text-primary); }
.filters-collapse-toggle svg{ width:16px; height:16px; transition:transform .15s; }
.filters-collapse-toggle.collapsed svg{ transform:rotate(-90deg); }
.side-label{ display:flex; flex-direction:column; gap:6px; }
.side-label > span{ font-family:var(--mono); font-size:10px; letter-spacing:.05em; text-transform:uppercase; color:var(--text-secondary); }
/* The shared .search-field carries flex:1 1 220px for the old horizontal filter
   bar; in the vertical sidebar that made it grow and shove the filters down. */
.side .search-field{ display:flex; flex:none; align-items:center; gap:8px; border-bottom:2px solid var(--text-primary); padding:6px 2px; max-width:none; }
.side .search-field svg{ width:17px; height:17px; color:var(--text-primary); flex:none; }
.side .search-field input{ border:0; outline:0; background:transparent; font-family:var(--sans); font-weight:300; font-size:14.5px; color:var(--text-primary); width:100%; min-width:0; }
.side .search-field input::placeholder{ color:var(--text-secondary); }
.side .sel{ width:100%; height:38px; }
.seg-group.vertical{ display:grid; grid-template-columns:1fr 1fr; gap:7px; }
.seg-group.vertical .seg-btn{ width:100%; height:36px; padding:0 8px; font-size:12.5px; }
.side-chips{ display:flex; flex-direction:column; gap:8px; }
.side-chips .chip{ width:100%; text-align:left; }
.chip-reset{ align-self:flex-start; }

.side-foot{ margin-top:auto; padding:18px 22px 22px; display:flex; flex-direction:column; gap:12px; }
.appbar-status{ display:flex; align-items:center; gap:7px; font-family:var(--mono); font-size:11px; color:var(--text-secondary); white-space:nowrap; }
.appbar-status .dot{ width:7px; height:7px; border-radius:50%; background:var(--warn-dot); flex:none; }
/* .user-block and its contents (auth.js's renderUserBlock) are styled in
   header.css: dashboard.html places it here in the sidebar footer, but
   admin.html - which has no sidebar - places the same component elsewhere. */

/* ---------- main canvas ---------- */
.main{ flex:1; min-width:0; }
.main-inner{ padding:26px clamp(20px,3vw,44px) 60px; max-width:1600px; }
.main-hdr{ padding-bottom:22px; margin-bottom:20px; border-bottom:1px solid var(--border); }
.content-title{ font-family:var(--serif); font-weight:400; font-size:clamp(24px,2.4vw,32px); letter-spacing:-0.01em; color:var(--text-primary); line-height:1.14; max-width:680px; text-wrap:balance; margin-top:8px; }
.content-sub{ font-family:var(--mono); font-size:11.5px; color:var(--text-secondary); margin-top:10px; }

/* ---------- kpi row ---------- */
.kpi-grid{ display:grid; grid-template-columns:repeat(6,1fr); gap:14px; margin-bottom:32px; }
.kpi{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); box-shadow:var(--shadow-card); padding:16px 16px 14px; display:flex; flex-direction:column; gap:6px; min-width:0; }
.kpi .eyebrow{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.kpi-value{ font-family:var(--sans); font-weight:300; font-size:clamp(20px,1.8vw,28px); letter-spacing:-0.02em; line-height:1; color:var(--text-primary); font-variant-numeric:tabular-nums; }
.kpi-sub{ font-size:11.5px; color:var(--text-secondary); line-height:1.35; }
.kpi-sub.crit{ color:var(--critical); font-weight:700; }
@media (max-width:1500px){ .kpi-grid{ grid-template-columns:repeat(3,1fr); } }
@media (max-width:760px){ .kpi-grid{ grid-template-columns:repeat(2,1fr); } }

/* ---------- sections & bento grids ---------- */
.sec{ padding:30px 0 34px; border-bottom:1px solid var(--border); scroll-margin-top:20px; }
.sec:last-of-type{ border-bottom:none; padding-bottom:0; }
.sec-head{ display:flex; align-items:baseline; justify-content:space-between; gap:20px; margin-bottom:18px; flex-wrap:wrap; }
.sec-head p.desc{ font-size:13px; color:var(--text-secondary); max-width:520px; margin-top:4px; }
/* Step 6 considered `repeat(auto-fit,minmax(320px,1fr))` here instead of the
   fixed templates below. Kept the fixed templates: auto-fit only starts
   wrapping once a column drops under 320px, which for a 2-column bento
   means each column must individually clear 320px BEFORE either wraps - at
   the width where these actually got tight (~1000-1100px, 2 columns of a
   1600px-max canvas minus the 288px sidebar) that collapses asymmetric
   pairs straight to one column anyway, just at an unpredictable width that
   moves with viewport and sidebar state instead of the one clean, testable
   1100px breakpoint below - and it would flatten .wide-left's deliberate
   1.25fr/.75fr map+donut ratio to equal columns the moment either got
   close to the edge. The overflow this step actually needed to fix lived
   inside the cards (BarList's fixed columns, the donut's fixed diameter),
   not in the grid template placing the cards - fixing those left no
   remaining case for auto-fit to earn its unpredictability. Verified at
   1440px the map+donut pairing (.wide-left, 1.25fr/.75fr) still reads
   correctly. */
.bento{ display:grid; gap:20px; }
.bento-2{ grid-template-columns:1.1fr .9fr; }
.bento-3{ grid-template-columns:repeat(3,1fr); }
.bento + .bento{ margin-top:20px; }
@media (max-width:1100px){ .bento-2,.bento-3{ grid-template-columns:1fr; } }

/* Every viz-card is built to the same shape now (see ChartCard in
   primitives.js): a head block carrying an eyebrow, a title and a REQUIRED
   description, optional actions beside it, then the body. Padding used to
   differ between this rule (22px 24px) and a #snapshotBlock override
   (18px 20px) that agreed with nothing; both now read --card-padding. */
/* Step 6: the nearest container-type ancestor for every @container query in
   charts.css/cards.css (BarList's label/value layout, DonutChart's ring
   size and legend placement). .viz-card already had min-width:0, which is
   what makes container-type:inline-size give a meaningful (non-huge, non-
   collapsing) query width immediately - a card that could not shrink below
   its content could never report a small container size either. */
.viz-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); box-shadow:var(--shadow-card); padding:var(--card-padding); min-width:0; container-type:inline-size; }
.viz-card-head{ display:flex; justify-content:space-between; align-items:flex-start; gap:var(--space-4); margin-bottom:var(--space-4); }
.viz-card-head-text{ flex:1; min-width:0; }
.viz-card-head-text .eyebrow{ margin-bottom:var(--space-1); }
.viz-card-actions{ flex:none; }
/* #trendToggle/#advocacyToggle: a flex:none 3-button toggle-mini beside the
   title works down to a normal card width, but on the narrowest phone cards
   (a ~271px trend card at 320px viewport) ~190px of buttons left the title
   only a sliver - readable but visually crushed. Below a 400px CONTAINER
   (the card itself), stack the actions under the title instead of beside
   it, so both get the full width. */
@container (max-width:400px){
  .viz-card-head{ flex-direction:column; align-items:stretch; }
  .viz-card-actions{ align-self:flex-start; }
}
.viz-card h3{ font-family:var(--sans); font-weight:700; font-size:var(--font-size-h3); letter-spacing:-0.006em; color:var(--text-primary); margin-bottom:var(--space-1); }
.viz-card .hint{ font-size:var(--font-size-caption); color:var(--text-secondary); margin:0; }
/* A secondary heading inside a card that holds two stacked visuals (the
   consent donut + the lifecycle funnel) - not a second ChartCard, just a
   plain sub-header, so it carries no eyebrow of its own. */
.viz-subhead{ font-family:var(--sans); font-weight:700; font-size:var(--font-size-h3); letter-spacing:-0.006em; color:var(--text-primary); margin:var(--space-6) 0 var(--space-1); }

/* ---------- section nav icon strokes ---------- */
.side-nav a svg{ stroke:currentColor; fill:none; stroke-width:1.5; }
