/* ==========================================================================
   PRV — Design V2 shared application layer
   ==========================================================================

   Two jobs:

   1. Owns the shared `--dv2-*` token set. These used to live at the top of
      design-v2-onboarding.css, where they were scoped to `.dv2` alone. Now
      that the dashboard and grocery also draw from them, the definitions live
      here and the host selector lists every surface that needs them. Moving
      them changed no value — the onboarding blocks were the only definitions
      of these properties anywhere in the codebase, so there is nothing for the
      cascade to reorder.

   2. Owns the Design V2 app chrome (tab bar, and later the dashboard), gated
      behind `body.dv2-app`.

   GATE A — `body.dv2-app`
   app.js `show()` sets this class on the dashboard and nowhere else, the same
   way `body.dv2-landing` works for onboarding. Every rule in this file below
   the token block is written as `body.dv2-app …`, so Profile, Vault, Pending
   and every other view match nothing here and render exactly as they do today.

   Namespacing follows the onboarding sheet: custom properties `--dv2-*`,
   classes `dv2-*`. style.css owns generic names like `--accent` and carries
   `.mesh, .blob { display: none !important }`, hence the prefixes.
   ========================================================================== */


/* ── Shared tokens ─────────────────────────────────────────────────────────
   `.dv2`                    onboarding / auth (unchanged)
   `body.dv2-app`            dashboard chrome            — Gate A
   `#view-list.dv2-grocery`  grocery list only           — Gate B
   Tokens inherit, so declaring them on the host element covers its subtree. */

html[data-theme="dark"] .dv2,
html[data-theme="dark"] body.dv2-app,
html[data-theme="dark"] #view-list.dv2-grocery {
  --dv2-bg-grad: radial-gradient(ellipse 90% 70% at 20% 10%, #1a0845 0%, #06001a 45%, #000d18 100%);
  --dv2-blob1: hsla(270,70%,45%,0.55); --dv2-blob2: hsla(185,80%,35%,0.45);
  --dv2-blob3: hsla(330,65%,40%,0.35); --dv2-blob4: hsla(240,70%,38%,0.3);

  --dv2-text-primary: rgba(255,255,255,0.95);
  --dv2-text-sub:     rgba(255,255,255,0.45);
  --dv2-text-muted:   rgba(255,255,255,0.32);
  --dv2-text-caption: rgba(255,255,255,0.4);

  /* T.g(opacity, blur, border) at each call site used by onboarding */
  --dv2-g-07-bg: rgba(255,255,255,0.07); --dv2-g-07-bd: rgba(255,255,255,0.12);
  --dv2-g-08-bg: rgba(255,255,255,0.08); --dv2-g-08-bd: rgba(255,255,255,0.15);
  --dv2-g-09-bg: rgba(255,255,255,0.09); --dv2-g-09-bd: rgba(255,255,255,0.18);
  --dv2-g-16-bd: rgba(255,255,255,0.16);
  --dv2-g-10-bg: rgba(255,255,255,0.10); --dv2-g-10-bd: rgba(255,255,255,0.20);
  --dv2-g-shadow: inset 0 1.5px 0 rgba(255,255,255,0.28),
                  inset 0 -1px 0 rgba(0,0,0,0.08),
                  0 8px 32px rgba(0,0,0,0.28);
  --dv2-g-sat: 180%;

  --dv2-input-bg: rgba(255,255,255,0.07); --dv2-input-bd: rgba(255,255,255,0.15);
  --dv2-input-text: rgba(255,255,255,0.9); --dv2-input-ph: rgba(255,255,255,0.35);

  --dv2-key-bg: rgba(255,255,255,0.1);
  --dv2-dot-empty-bd: rgba(255,255,255,0.22);
  --dv2-divider: rgba(255,255,255,0.08);

  --dv2-toggle-bg: rgba(255,255,255,0.1); --dv2-toggle-bd: rgba(255,255,255,0.2);
  --dv2-toggle-ic: rgba(255,255,255,0.7);

  --dv2-btn-primary: linear-gradient(135deg,hsla(270,70%,55%,1) 0%,hsla(290,65%,50%,1) 50%,hsla(250,75%,60%,1) 100%);
  --dv2-btn-shadow: 0 8px 32px hsla(270,70%,40%,0.5), 0 2px 8px rgba(0,0,0,0.3);
  /* S1 fix — the source hardcodes rgba(255,255,255,.15) + #fff text in both
     themes, which is white-on-white when disabled in light mode. */
  --dv2-btn-disabled-bg: rgba(255,255,255,0.15);
  --dv2-btn-disabled-tx: rgba(255,255,255,0.55);

  --dv2-progress-bg: rgba(255,255,255,0.1);
  --dv2-progress-fill: linear-gradient(90deg,hsla(270,70%,65%,1),hsla(200,80%,65%,1));

  --dv2-badge-core: rgba(10,4,28,0.6); --dv2-badge-core-signin: rgba(10,4,28,0.55);
  --dv2-ring-core: rgba(6,0,26,0.9);
  --dv2-link: hsla(270,70%,72%,1);
  --dv2-accent: hsla(270,70%,65%,1);
  --dv2-danger: hsla(355,85%,72%,1);
  --dv2-danger-bg: hsla(355,70%,50%,0.14);
  --dv2-danger-bd: hsla(355,70%,60%,0.32);

  --dv2-svg-stroke: rgba(255,255,255,0.85); --dv2-svg-fill: rgba(255,255,255,0.06);
  --dv2-house-stroke: rgba(255,255,255,0.8); --dv2-house-fill: rgba(255,255,255,0.05);
  --dv2-house-door: rgba(255,255,255,0.08);

  --dv2-cel-stroke: rgba(255,255,255,0.75); --dv2-cel-fill: rgba(255,255,255,0.04);
  --dv2-cel-door: rgba(255,255,255,0.07);   --dv2-cel-plate: rgba(255,255,255,0.06);
  --dv2-cel-text: rgba(255,255,255,0.85);
  --dv2-cel-balloon-l: 65%; --dv2-cel-string-l: 60%; --dv2-cel-confetti-l: 65%;
  --dv2-disc-bg: rgba(255,255,255,0.1); --dv2-disc-bd-l: 65%;

  /* hue-driven tints */
  --dv2-tile-bg-l: 60%; --dv2-tile-bd-l: 65%; --dv2-tile-ic-l: 72%; --dv2-tile-ck-l: 65%;
  --dv2-choice-bg-l: 55%; --dv2-choice-bd-l: 65%; --dv2-choice-ic-l: 72%; --dv2-choice-edge-l: 65%;
  --dv2-pill-bg-l: 25%; --dv2-pill-bd-l: 65%; --dv2-pill-tx-l: 82%;
  --dv2-check-l: 65%;

  /* tab bar */
  --dv2-tab-active-bg: rgba(255,255,255,0.12);
  --dv2-tab-active-bd: rgba(255,255,255,0.2);
  --dv2-tab-ic-active: rgba(255,255,255,0.95);
  --dv2-tab-ic-idle:   rgba(255,255,255,0.3);
  --dv2-tab-tx-active: rgba(255,255,255,0.9);
  --dv2-tab-tx-idle:   rgba(255,255,255,0.3);
  --dv2-tab-ai-core:   rgba(10,4,28,0.55);
  --dv2-tab-ai-icon:   rgba(255,255,255,0.9);

  /* ── dashboard ──────────────────────────────────────────────────────────
     Values transcribed from DARK in the Make source: satIdle / satActive /
     iconIdle / labelIdle / dialBg / spokeIdle / pillBg / cardAccent / bellDot
     / weatherIcon.

     The hue-driven ones are stored as a bare `S%,L%,A` triple and composed at
     the use site: `hsla(var(--h,270), var(--dv2-c-badge-c))`.

     They CANNOT carry `var(--h)` inside the token. A custom property resolves
     its own `var()` references on the element where it is DECLARED, not where
     it is used — and these are declared on <html>, which has no `--h`. Written
     that way every one of them silently collapsed to the 270 fallback, so the
     dashboard came out purple whatever hue a list was set to. Splitting the hue
     out is also the convention the rest of this file already uses for tinted
     surfaces (see --dv2-tile-*-l above). */
  --dv2-c-node-bg: rgba(255,255,255,0.08);
  --dv2-c-node-bd: rgba(255,255,255,0.16);
  --dv2-c-node-ic: rgba(255,255,255,0.82);
  --dv2-c-node-ic-sel-c: 70%,84%,1;
  --dv2-c-sel-bg-c: 70%,65%,0.22;
  --dv2-c-sel-bd-c: 80%,75%,0.55;
  --dv2-c-sel-glow-c: 80%,55%,0.35;
  --dv2-c-label: rgba(255,255,255,0.55);
  --dv2-c-label-sel-c: 65%,82%,1;
  --dv2-c-spoke: rgba(255,255,255,0.07);
  --dv2-c-spoke-lit-c: 70%,70%,0.65;
  --dv2-c-dial-bg: rgba(255,255,255,0.12);
  --dv2-c-dial-bd: rgba(255,255,255,0.5);
  --dv2-c-halo: hsla(270,70%,55%,0.4);
  --dv2-c-badge-c: 70%,58%,1;
  --dv2-c-badge-glow-c: 70%,55%,0.6;
  --dv2-c-pill-bg-c: 50%,25%,0.7;
  --dv2-c-pill-bd-c: 60%,65%,0.3;
  --dv2-c-pill-tx-c: 70%,82%,1;
  --dv2-c-pill-glow-c: 70%,55%,0.25;
  --dv2-c-pill-inset: rgba(255,255,255,0.2);

  --dv2-c-accent-a-c: 70%,55%,0.9;  --dv2-c-accent-b-c: 70%,40%,0.4;
  --dv2-c-done-ic-c: 70%,80%,1;
  --dv2-c-tile-bg-c: 60%,55%,0.18;
  --dv2-c-tile-bd-c: 65%,60%,0.25;
  --dv2-c-tile-ic-c: 65%,65%,1;
  --dv2-c-chev-bg: rgba(255,255,255,0.07);
  --dv2-c-chev-bd: rgba(255,255,255,0.12);

  --dv2-c-bell: hsla(350,80%,65%,1);
  --dv2-c-weather-ic: rgba(120,200,255,0.9);
}

html[data-theme="light"] .dv2,
html[data-theme="light"] body.dv2-app,
html[data-theme="light"] #view-list.dv2-grocery {
  --dv2-bg-grad: radial-gradient(ellipse 90% 70% at 20% 10%, #DDD5FF 0%, #EEF2FF 50%, #DFF4F0 100%);
  --dv2-blob1: hsla(270,70%,75%,0.35); --dv2-blob2: hsla(185,80%,65%,0.28);
  --dv2-blob3: hsla(330,65%,75%,0.22); --dv2-blob4: hsla(240,60%,75%,0.2);

  --dv2-text-primary: rgba(14,10,38,0.92);
  --dv2-text-sub:     rgba(14,10,38,0.45);
  --dv2-text-muted:   rgba(14,10,38,0.32);
  --dv2-text-caption: rgba(14,10,38,0.38);

  --dv2-g-07-bg: rgba(255,255,255,0.50); --dv2-g-07-bd: rgba(255,255,255,0.75);
  --dv2-g-08-bg: rgba(255,255,255,0.55); --dv2-g-08-bd: rgba(255,255,255,0.80);
  --dv2-g-09-bg: rgba(255,255,255,0.62); --dv2-g-09-bd: rgba(255,255,255,0.85);
  --dv2-g-16-bd: rgba(255,255,255,0.85);
  --dv2-g-10-bg: rgba(255,255,255,0.65); --dv2-g-10-bd: rgba(255,255,255,0.90);
  --dv2-g-shadow: inset 0 1.5px 0 rgba(255,255,255,0.95),
                  inset 0 -1px 0 rgba(0,0,0,0.04),
                  0 8px 32px rgba(80,60,160,0.1);
  --dv2-g-sat: 200%;

  --dv2-input-bg: rgba(255,255,255,0.75); --dv2-input-bd: rgba(255,255,255,0.95);
  --dv2-input-text: rgba(14,10,38,0.9); --dv2-input-ph: rgba(14,10,38,0.3);

  --dv2-key-bg: rgba(255,255,255,0.75);
  --dv2-dot-empty-bd: rgba(14,10,38,0.2);
  --dv2-divider: rgba(14,10,38,0.06);

  --dv2-toggle-bg: rgba(255,255,255,0.65); --dv2-toggle-bd: rgba(255,255,255,0.9);
  --dv2-toggle-ic: rgba(40,28,80,0.6);

  --dv2-btn-primary: linear-gradient(135deg,hsla(270,65%,50%,1) 0%,hsla(290,60%,48%,1) 50%,hsla(250,70%,56%,1) 100%);
  --dv2-btn-shadow: 0 8px 28px hsla(270,65%,45%,0.4), 0 2px 6px rgba(0,0,0,0.12);
  --dv2-btn-disabled-bg: rgba(40,28,80,0.10);
  --dv2-btn-disabled-tx: rgba(14,10,38,0.42);

  --dv2-progress-bg: rgba(80,60,160,0.12);
  --dv2-progress-fill: linear-gradient(90deg,hsla(270,65%,55%,1),hsla(200,75%,55%,1));

  --dv2-badge-core: rgba(255,255,255,0.65); --dv2-badge-core-signin: rgba(255,255,255,0.6);
  --dv2-ring-core: rgba(238,242,255,0.9);
  --dv2-link: hsla(270,70%,52%,1);
  --dv2-accent: hsla(270,70%,52%,1);
  --dv2-danger: hsla(355,72%,44%,1);
  --dv2-danger-bg: hsla(355,85%,96%,1);
  --dv2-danger-bd: hsla(355,70%,80%,1);

  --dv2-svg-stroke: rgba(80,50,160,0.8); --dv2-svg-fill: rgba(120,80,200,0.06);
  --dv2-house-stroke: rgba(60,30,130,0.75); --dv2-house-fill: rgba(120,80,200,0.05);
  --dv2-house-door: rgba(120,80,200,0.1);

  --dv2-cel-stroke: rgba(60,30,130,0.7);  --dv2-cel-fill: rgba(120,80,200,0.05);
  --dv2-cel-door: rgba(120,80,200,0.08);  --dv2-cel-plate: rgba(120,80,200,0.08);
  --dv2-cel-text: rgba(60,30,130,0.8);
  --dv2-cel-balloon-l: 58%; --dv2-cel-string-l: 50%; --dv2-cel-confetti-l: 55%;
  --dv2-disc-bg: rgba(255,255,255,0.72); --dv2-disc-bd-l: 52%;

  --dv2-tile-bg-l: 50%; --dv2-tile-bd-l: 55%; --dv2-tile-ic-l: 45%; --dv2-tile-ck-l: 48%;
  --dv2-choice-bg-l: 50%; --dv2-choice-bd-l: 55%; --dv2-choice-ic-l: 48%; --dv2-choice-edge-l: 50%;
  --dv2-pill-bg-l: 96%; --dv2-pill-bd-l: 70%; --dv2-pill-tx-l: 38%;
  --dv2-check-l: 48%;

  /* tab bar */
  --dv2-tab-active-bg: rgba(255,255,255,0.75);
  --dv2-tab-active-bd: rgba(255,255,255,0.95);
  --dv2-tab-ic-active: rgba(14,10,38,0.88);
  --dv2-tab-ic-idle:   rgba(14,10,38,0.28);
  --dv2-tab-tx-active: rgba(14,10,38,0.85);
  --dv2-tab-tx-idle:   rgba(14,10,38,0.28);
  --dv2-tab-ai-core:   rgba(255,255,255,0.55);
  /* Same class of bug as the S1 fix in onboarding: the Make source hardcodes a
     white sparkle in both themes, which is white-on-white over the light core. */
  --dv2-tab-ai-icon:   hsla(270,70%,45%,1);

  /* dashboard — LIGHT in the Make source */
  --dv2-c-node-bg: rgba(255,255,255,0.58);
  --dv2-c-node-bd: rgba(255,255,255,0.88);
  --dv2-c-node-ic: rgba(40,28,80,0.72);
  --dv2-c-node-ic-sel-c: 60%,42%,1;
  --dv2-c-sel-bg-c: 70%,96%,0.82;
  --dv2-c-sel-bd-c: 60%,70%,0.55;
  --dv2-c-sel-glow-c: 70%,65%,0.25;
  --dv2-c-label: rgba(40,28,80,0.5);
  --dv2-c-label-sel-c: 60%,40%,1;
  --dv2-c-spoke: rgba(60,40,140,0.1);
  --dv2-c-spoke-lit-c: 70%,50%,0.65;
  --dv2-c-dial-bg: rgba(255,255,255,0.55);
  --dv2-c-dial-bd: rgba(255,255,255,0.9);
  --dv2-c-halo: hsla(270,70%,75%,0.5);
  --dv2-c-badge-c: 70%,52%,1;
  --dv2-c-badge-glow-c: 70%,55%,0.6;
  --dv2-c-pill-bg-c: 60%,96%,0.85;
  --dv2-c-pill-bd-c: 50%,70%,0.4;
  --dv2-c-pill-tx-c: 60%,38%,1;
  --dv2-c-pill-glow-c: 70%,55%,0.15;
  --dv2-c-pill-inset: rgba(255,255,255,0.8);

  --dv2-c-accent-a-c: 65%,55%,0.85;  --dv2-c-accent-b-c: 65%,45%,0.35;
  --dv2-c-done-ic-c: 60%,42%,1;
  --dv2-c-tile-bg-c: 60%,55%,0.12;
  --dv2-c-tile-bd-c: 65%,60%,0.3;
  --dv2-c-tile-ic-c: 65%,45%,1;
  --dv2-c-chev-bg: rgba(60,40,140,0.07);
  --dv2-c-chev-bd: rgba(60,40,140,0.12);

  --dv2-c-bell: hsla(350,75%,58%,1);
  --dv2-c-weather-ic: hsla(200,70%,48%,1);
}


/* ── ASK AI tab — base ─────────────────────────────────────────────────────
   NOT gated, deliberately. `.bn-ai` and its two wrapper spans are new elements
   that exist nowhere else in the app, so these rules cannot reach anything that
   already renders. They give the new tab a sane appearance on Vault, Profile
   and Pending, where the Design V2 tab-bar restyle below does not apply.

   `display: contents` dissolves the wrappers, so the icon becomes a direct
   child of `.bn-item` and inherits its existing column layout — the button
   renders as an ordinary tab. Only `body.dv2-app` turns them back into boxes
   and builds the orb. */

.bn-ai .bn-ai-ring,
.bn-ai .bn-ai-core { display: contents; }


/* ── Design V2 tab bar ─────────────────────────────────────────────────────
   GATE A. Floats the nav into a rounded glass pill, per the Make source
   (mx-4 mb-8, radius 28, glass, justify-around). Dashboard only. */

body.dv2-app #bottomnav {
  left: max(16px, env(safe-area-inset-left));
  right: max(16px, env(safe-area-inset-right));
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  border-radius: 28px;
  border: 1px solid var(--dv2-g-10-bd);
  border-top: 1px solid var(--dv2-g-10-bd);
  background: var(--dv2-g-10-bg);
  box-shadow: var(--dv2-g-shadow);
  padding: 12px 8px;
  overflow: visible;   /* the AI orb overhangs the top edge */
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  body.dv2-app #bottomnav {
    -webkit-backdrop-filter: blur(40px) saturate(var(--dv2-g-sat));
    backdrop-filter: blur(40px) saturate(var(--dv2-g-sat));
  }
}

/* The floating bar no longer reserves its own space at the bottom of the page. */
body.dv2-app #view-dashboard { padding-bottom: calc(112px + env(safe-area-inset-bottom, 0px)); }

body.dv2-app .bn-item {
  position: relative;
  gap: 4px;
  padding: 4px 14px;
  border-radius: 16px;
  color: var(--dv2-tab-tx-idle);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  transition: color .2s ease, transform .2s ease;
}
body.dv2-app .bn-item:active { transform: scale(0.9); }
body.dv2-app .bn-item .material-symbols-outlined {
  font-size: 20px;
  color: var(--dv2-tab-ic-idle);
  font-variation-settings: 'FILL' 0, 'wght' 300;
  transition: color .2s ease;
}

/* Active pill. ::before rather than a background on the button itself so the
   label and icon stay above it without a stacking-context fight. */
body.dv2-app .bn-item.active {
  color: var(--dv2-tab-tx-active);
  font-weight: 700;
}
body.dv2-app .bn-item.active::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: 16px;
  background: var(--dv2-tab-active-bg);
  border: 1px solid var(--dv2-tab-active-bd);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.5);
}
body.dv2-app .bn-item.active .material-symbols-outlined {
  color: var(--dv2-tab-ic-active);
  font-variation-settings: 'FILL' 0, 'wght' 500;
}
body.dv2-app .bn-item > * { position: relative; }

/* The AI orb — a conic-gradient ring with a frosted core, raised out of the bar. */
body.dv2-app .bn-ai { margin-top: -18px; padding-left: 4px; padding-right: 4px; }
body.dv2-app .bn-ai .bn-ai-ring {
  display: flex; align-items: center; justify-content: center;
  position: relative;
  width: 52px; height: 52px; border-radius: 50%;
  background: conic-gradient(from 0deg,
    hsla(270,60%,55%,0.85), hsla(200,70%,55%,0.85),
    hsla(330,65%,55%,0.85), hsla(270,60%,55%,0.85));
  box-shadow: 0 0 12px hsla(270,70%,55%,0.4);
}
body.dv2-app .bn-ai .bn-ai-core {
  display: flex; align-items: center; justify-content: center;
  position: absolute; inset: 3px;
  border-radius: 50%;
  background: var(--dv2-tab-ai-core);
  box-shadow: inset 0 1.5px 0 rgba(255,255,255,0.4);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  body.dv2-app .bn-ai .bn-ai-core {
    -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  }
}
body.dv2-app .bn-ai .material-symbols-outlined {
  font-size: 20px;
  color: var(--dv2-tab-ai-icon);
}
body.dv2-app .bn-ai > span:last-child { margin-top: 2px; font-weight: 700; }
/* No active pill behind the orb — the ring is its own affordance. */
body.dv2-app .bn-ai.active::before { content: none; }


/* ==========================================================================
   DASHBOARD                                                          GATE A
   ==========================================================================
   Everything below is `body.dv2-app …`. show() sets that class for
   v === 'dashboard' only and hides every other view in the same pass, so
   these rules — including the one that reaches `body` — cannot affect any
   other screen. The legacy #view-home is a different view and is untouched.

   Geometry and colour are transcribed from the Make source. Three places
   where production and the mock necessarily differ, each noted at the rule:

     · the mock draws a fixed 9-spoke ring in a 390x432 box; production keeps
       its parametric SVG (viewBox 1000x1080, N nodes) because the spoke
       count follows the household's visible lists;
     · the mock puts a line icon AND its label inside the 80px circle;
       production nodes carry the per-list 3D PNG / Lottie icon, which fills
       the circle, so labels stay below the node as they do today;
     · the mock has no "complete the whole list" button; production does, and
       it is kept rather than deleted for a visual change.
   ========================================================================== */

/* style.css puts 20px of horizontal padding on <body> for every view. The
   Design V2 dashboard manages its own gutters (24px header, 16px cards, per
   the source's px-6 / px-4) and wants the constellation near full-bleed, so
   the shared padding stands down — as body.dv2-landing already does. */
body.dv2-app {
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

body.dv2-app #view-dashboard {
  font-family: 'Plus Jakarta Sans', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  -webkit-font-smoothing: antialiased;
  /* Room for the floating tab bar, which no longer reserves its own space. */
  padding-bottom: calc(112px + env(safe-area-inset-bottom, 0px));
}

/* .dv2-aurora is `position: fixed; z-index: 0` (onboarding sheet), so the
   content only has to sit above it. #bottomnav is z-index 60 and a sibling of
   the view, so it stays clear either way. */
body.dv2-app .dv2-dash { position: relative; z-index: 1; }
body.dv2-app .dv2-dash *,
body.dv2-app .dv2-dash *::before,
body.dv2-app .dv2-dash *::after { box-sizing: border-box; }


/* ── Header ────────────────────────────────────────────────────────────── */

body.dv2-app .dv2-dash-head {
  padding: max(env(safe-area-inset-top), 16px) 24px 8px;
  animation: dv2FadeUp .5s ease both;
}
body.dv2-app .dv2-dash-head-row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
}
body.dv2-app .dv2-dash-head-txt { min-width: 0; }

body.dv2-app .dv2-dash-greet {
  margin: 0 0 3px;
  font-size: 13px; font-weight: 400; letter-spacing: .03em;
  color: var(--dv2-text-sub);
}
body.dv2-app .dv2-dash-home {
  margin: 0;
  font-size: 30px; font-weight: 800; line-height: 1.1; letter-spacing: -.03em;
  color: var(--dv2-text-primary);
  overflow-wrap: anywhere;
}
body.dv2-app .dv2-dash-eyebrow {
  display: flex; align-items: center; gap: 8px;
  margin: 6px 0 0;
}
body.dv2-app .dv2-dash-eyebrow-k {
  font-size: 11px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--dv2-text-muted);
}
body.dv2-app .dv2-dash-sep {
  flex: none; width: 3px; height: 3px; border-radius: 50%;
  background: var(--dv2-text-muted); opacity: .5;
}
body.dv2-app .dv2-dash-eyebrow-d {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 11px; font-weight: 400;
  color: var(--dv2-text-caption);
}

body.dv2-app .dv2-dash-head-acts {
  flex: none;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
}
body.dv2-app .dv2-dash-head-btns { display: flex; align-items: center; gap: 8px; }

/* .dv2-toggle already ships at 36px from the onboarding sheet; the source
   draws both header buttons at 40 (w-10 h-10). */
body.dv2-app .dv2-dash-icbtn {
  position: relative;
  width: 40px; height: 40px; border-radius: 9999px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--dv2-g-10-bg); border: 1px solid var(--dv2-g-10-bd);
  box-shadow: var(--dv2-g-shadow);
  color: var(--dv2-text-primary); cursor: pointer;
  transition: transform .2s ease;
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  body.dv2-app .dv2-dash-icbtn {
    -webkit-backdrop-filter: blur(24px) saturate(var(--dv2-g-sat));
    backdrop-filter: blur(24px) saturate(var(--dv2-g-sat));
  }
}
body.dv2-app .dv2-dash-icbtn:active { transform: scale(.9); }
body.dv2-app .dv2-dash-icbtn:focus-visible { outline: 2px solid var(--dv2-link); outline-offset: 2px; }
body.dv2-app .dv2-dash-bell .material-symbols-outlined {
  font-size: 19px; font-variation-settings: 'FILL' 0, 'wght' 350;
}

/* The source draws a bare 7px dot. refreshBellBadge() writes a real unread
   COUNT into this element, so it is sized as a small pill that reads as a dot
   at a single digit — same footprint, more information. */
body.dv2-app .dv2-dash-belldot {
  position: absolute; top: 7px; right: 6px;
  min-width: 7px; height: 15px; padding: 0 4px;
  border-radius: 9999px;
  display: flex; align-items: center; justify-content: center;
  background: var(--dv2-c-bell);
  box-shadow: 0 0 6px var(--dv2-c-bell);
  color: #fff;
  font-family: 'Inter', -apple-system, sans-serif;
  font-size: 9px; font-weight: 700; line-height: 1;
}
body.dv2-app .dv2-dash-belldot[hidden] { display: none; }


/* ── Weather pill (D4 — visual placeholder) ────────────────────────────── */

body.dv2-app .dv2-weather {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 9999px;
  background: var(--dv2-g-08-bg); border: 1px solid var(--dv2-g-08-bd);
  box-shadow: var(--dv2-g-shadow);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  body.dv2-app .dv2-weather {
    -webkit-backdrop-filter: blur(20px) saturate(var(--dv2-g-sat));
    backdrop-filter: blur(20px) saturate(var(--dv2-g-sat));
  }
}
body.dv2-app .dv2-weather-ic {
  font-size: 13px; color: var(--dv2-c-weather-ic);
  font-variation-settings: 'FILL' 1, 'wght' 400;
}
body.dv2-app .dv2-weather-t {
  font-family: 'Inter', -apple-system, sans-serif;
  font-size: 12px; font-weight: 500; color: var(--dv2-text-sub);
}
body.dv2-app .dv2-weather-wind {
  font-size: 12px; color: var(--dv2-text-muted);
  font-variation-settings: 'FILL' 0, 'wght' 300;
}


/* ── Constellation ─────────────────────────────────────────────────────── */

body.dv2-app .dv2-dash-const { margin-top: 6px; padding: 0 8px; }
body.dv2-app .dv2-const-wrap { position: relative; }
body.dv2-app .dv2-const { position: relative; z-index: 1; width: 100%; }

/* Dial halo. SVG 1.1 has no conic-gradient, so the source's two glow layers
   are HTML positioned against the wrapper: the viewBox is 1000x1080 with the
   hub centred at (500, 512) — 50% across, 47.4% down — and the dial is
   2*118/1000 = 23.6% of the width. */
body.dv2-app .dv2-const-halo {
  position: absolute; z-index: 0;
  left: 50%; top: 47.4%;
  width: 23.6%; aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    hsla(270,70%,70%,.6), hsla(200,80%,70%,.6), hsla(150,70%,65%,.6),
    hsla(45,80%,70%,.6),  hsla(330,70%,70%,.6), hsla(270,70%,70%,.6));
  filter: blur(4px);
  pointer-events: none;
}
body.dv2-app .dv2-const-halo::before {
  content: '';
  position: absolute; inset: -22%;
  border-radius: 50%;
  background: radial-gradient(circle, var(--dv2-c-halo) 0%, transparent 70%);
  filter: blur(14px);
}

/* D2 selection pill. The source parks this at the bottom of a fixed-height
   constellation box; production's box height follows the node count, so it
   sits just under the dial where it is unambiguous at any N. Its own keyframe
   because dv2ScaleIn animates `transform` and would drop the centring. */
body.dv2-app .dv2-const-pill {
  position: absolute; z-index: 2;
  left: 50%; top: 61%;
  transform: translateX(-50%);
  max-width: 74%;
  padding: 8px 20px; border-radius: 9999px;
  background: hsla(var(--h,270),var(--dv2-c-pill-bg-c));
  outline: 1px solid hsla(var(--h,270),var(--dv2-c-pill-bd-c));
  box-shadow: 0 0 20px hsla(var(--h,270),var(--dv2-c-pill-glow-c)), inset 0 1px 0 var(--dv2-c-pill-inset);
  color: hsla(var(--h,270),var(--dv2-c-pill-tx-c));
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  pointer-events: none;
  animation: dv2PillIn .22s ease both;
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  body.dv2-app .dv2-const-pill {
    -webkit-backdrop-filter: blur(24px); backdrop-filter: blur(24px);
  }
}
body.dv2-app .dv2-const-pill[hidden] { display: none; }
@keyframes dv2PillIn {
  from { opacity: 0; transform: translateX(-50%) scale(.92) }
  to   { opacity: 1; transform: translateX(-50%) scale(1) }
}

/* The SVG. `.fhc-*` is emitted only by renderHomeConstellation, which paints
   nothing but this screen, so these overrides have nowhere else to land. CSS
   beats SVG presentation attributes, so the inline stroke/fill values in
   app.js are superseded without changing the emission. */
body.dv2-app .fhc-spoke {
  stroke: var(--dv2-c-spoke); stroke-width: 1.9;
  transition: stroke .22s ease, stroke-width .22s ease;
}
body.dv2-app .fhc-spoke.dv2-lit { stroke: hsla(var(--h,270),var(--dv2-c-spoke-lit-c)); stroke-width: 3.8; }

body.dv2-app .fhc-node-bg {
  fill: var(--dv2-c-node-bg);
  stroke: var(--dv2-c-node-bd); stroke-width: 1.5;
  transition: fill .2s ease, stroke .2s ease;
}
/* GP-1 · the line-icon glyph. app.js emits it on a 24x24 grid and scales the
   group, so stroke-width is specified in grid units and scales with it — one
   value holds at every node radius. `vector-effect` is deliberately NOT used:
   the stroke is meant to thin as the ring gets denser.
   The amber/blue/grey state ring is gone from the markup entirely; the count
   badge is what marks a list as needing attention. */
body.dv2-app .fhc-node-ic {
  fill: none;
  stroke: var(--dv2-c-node-ic);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke .2s ease;
}
body.dv2-app .fhc-node.dv2-sel .fhc-node-ic { stroke: hsla(var(--h,270),var(--dv2-c-node-ic-sel-c)); }
/* The hub glyph reads at dial scale, so it carries a touch more weight. */
body.dv2-app .fhc-hub .fhc-node-ic { stroke: var(--dv2-text-primary); stroke-width: 1.5; }

body.dv2-app .fhc-node:focus-visible .fhc-node-bg { stroke: var(--dv2-link); stroke-width: 4; }

body.dv2-app .fhc-node.dv2-sel .fhc-node-bg {
  fill: hsla(var(--h,270),var(--dv2-c-sel-bg-c));
  stroke: hsla(var(--h,270),var(--dv2-c-sel-bd-c));
  filter: drop-shadow(0 0 10px hsla(var(--h,270),var(--dv2-c-sel-glow-c)));
}

/* style.css animates `transform` on .fhc-node with fill-mode `both`, and an
   animation outranks a plain declaration — the entry animation would pin each
   node at its end transform and the selection scale would never apply. Swap in
   an opacity-only entrance so transform stays free for .dv2-sel. */
body.dv2-app .fhc-node {
  transform-box: fill-box; transform-origin: center;
  transition: transform .2s ease;
  animation: dv2ConstIn .45s ease both;
  animation-delay: calc(var(--i, 0) * .05s);
}
body.dv2-app .fhc-node.dv2-sel { transform: scale(1.1); }
@keyframes dv2ConstIn { from { opacity: 0 } to { opacity: 1 } }

body.dv2-app .fhc-hub-bg {
  fill: var(--dv2-c-dial-bg);
  stroke: var(--dv2-c-dial-bd); stroke-width: 2;
}
body.dv2-app .fhc-hub-tx {
  fill: var(--dv2-text-primary);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 26px; font-weight: 700;
  letter-spacing: 2px; text-transform: uppercase;
}
/* The base rule paints a 7px white halo behind every label so it survives on
   photography. On glass that reads as a smear, so the halo comes off. */
body.dv2-app .fhc-label {
  fill: var(--dv2-c-label); stroke: none;
  font-family: 'Inter', -apple-system, sans-serif;
  /* D6 — the label is a caption under the node, not a peer of it. The mock
     runs it two steps lighter and smaller than the old 27px/600, which was
     competing with the glyph for the eye. */
  font-size: 24px; font-weight: 500; letter-spacing: .02em;
  transition: fill .2s ease;
}
body.dv2-app .fhc-label.dv2-lit { fill: hsla(var(--h,270),var(--dv2-c-label-sel-c)); font-weight: 600; }

body.dv2-app .fhc-badge-bg {
  fill: hsla(var(--h,270),var(--dv2-c-badge-c));
  stroke: rgba(255,255,255,.5); stroke-width: 4;
  filter: drop-shadow(0 0 8px hsla(var(--h,270),var(--dv2-c-badge-glow-c)));
}
body.dv2-app .fhc-badge-tx {
  fill: #fff;
  font-family: 'Inter', -apple-system, sans-serif;
  font-size: 28px; font-weight: 700;
}


/* ── Activity cards ────────────────────────────────────────────────────── */

body.dv2-app .dv2-dash-cards {
  margin-top: 4px; padding: 0 16px;
  display: flex; flex-direction: column; gap: 10px;
}

body.dv2-app .dv2-actcard {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 12px;
  padding: 16px;
  border-radius: 22px;
  background: var(--dv2-g-10-bg); border: 1px solid var(--dv2-g-10-bd);
  box-shadow: var(--dv2-g-shadow);
  animation: dv2FadeUp .45s ease both;
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  body.dv2-app .dv2-actcard {
    -webkit-backdrop-filter: blur(32px) saturate(var(--dv2-g-sat));
    backdrop-filter: blur(32px) saturate(var(--dv2-g-sat));
  }
}
body.dv2-app .dv2-actcard-accent {
  position: absolute; left: 0; top: 16px; bottom: 16px; width: 3px;
  border-radius: 0 9999px 9999px 0;
  background: linear-gradient(180deg,hsla(var(--h,270),var(--dv2-c-accent-a-c)),hsla(var(--h,270),var(--dv2-c-accent-b-c)));
}
body.dv2-app .dv2-actcard-main {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 12px;
  padding: 0; background: none; border: 0; cursor: pointer; text-align: left;
  transition: transform .15s ease;
}
body.dv2-app .dv2-actcard-main:active { transform: scale(.98); }
body.dv2-app .dv2-actcard-main:focus-visible { outline: 2px solid var(--dv2-link); outline-offset: 4px; border-radius: 14px; }
body.dv2-app .dv2-actcard-ic {
  flex: none;
  width: 44px; height: 44px; border-radius: 16px;
  display: grid; place-items: center; overflow: hidden;
  background: hsla(var(--h,270),var(--dv2-c-tile-bg-c));
  border: 1px solid hsla(var(--h,270),var(--dv2-c-tile-bd-c));
}
/* iconImgTag() emits either a bare <img class="dv2-actcard-img"> or a
   .lottie-wrap carrying "<cls>-wrap" with the poster <img> inside it. */
/* GP-1 · the tile glyph. Same 24x24 grid and stroke language as the
   constellation, sized to the mock's tile. */
body.dv2-app .dv2-actcard-lineic {
  width: 22px; height: 22px;
  fill: none; stroke: hsla(var(--h,270),var(--dv2-c-tile-ic-c));
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
body.dv2-app .dv2-actcard-img { width: 30px; height: 30px; object-fit: contain; }
body.dv2-app .dv2-actcard-img-wrap { width: 30px; height: 30px; }
body.dv2-app .dv2-actcard-gic { font-size: 19px; color: hsla(var(--h,270),var(--dv2-c-tile-ic-c)); }
body.dv2-app .dv2-actcard-txt { display: flex; flex-direction: column; min-width: 0; }
body.dv2-app .dv2-actcard-name {
  font-size: 14px; font-weight: 700; line-height: 1.2;
  color: var(--dv2-text-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.dv2-app .dv2-actcard-sub {
  margin-top: 2px;
  font-family: 'Inter', -apple-system, sans-serif;
  font-size: 11px; font-weight: 400;
  color: var(--dv2-text-caption);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* D5 / GP-2 · not in the mock. This is the existing one-tap "complete the
   whole list" operation, kept rather than dropped (GOVERNING_PRINCIPLE §3.1)
   and restaged as a disc that pairs with the chevron instead of the gradient
   pill, which read louder than the card's own title. The label survives on
   aria-label/title — icon-only is a visual change, not an accessibility one. */
body.dv2-app .dv2-actcard-done {
  flex: none;
  width: 32px; height: 32px; padding: 0;
  border-radius: 9999px;
  display: grid; place-items: center;
  border: 1px solid hsla(var(--h,270),60%,60%,.34);
  background: hsla(var(--h,270),70%,55%,.14);
  color: hsla(var(--h,270),var(--dv2-c-done-ic-c));
  cursor: pointer;
  transition: transform .15s ease, opacity .15s ease, background .15s ease;
}
body.dv2-app .dv2-actcard-done .material-symbols-outlined {
  font-size: 18px;
  font-variation-settings: 'FILL' 0, 'wght' 400;
}
body.dv2-app .dv2-actcard-done:hover { background: hsla(var(--h,270),70%,55%,.24); }
body.dv2-app .dv2-actcard-done:active { transform: scale(.9); }
body.dv2-app .dv2-actcard-done:disabled { opacity: .45; }
body.dv2-app .dv2-actcard-done:focus-visible { outline: 2px solid var(--dv2-link); outline-offset: 3px; }

body.dv2-app .dv2-actcard-chev {
  flex: none;
  width: 32px; height: 32px; border-radius: 9999px;
  display: grid; place-items: center;
  background: var(--dv2-c-chev-bg); border: 1px solid var(--dv2-c-chev-bd);
}
body.dv2-app .dv2-actcard-chev .material-symbols-outlined {
  font-size: 17px; color: var(--dv2-text-sub);
  font-variation-settings: 'FILL' 0, 'wght' 400;
}


/* ── Motion opt-outs ───────────────────────────────────────────────────── */
/* Mirrors the onboarding sheet, which already covers .dv2-blob for both. */

@media (prefers-reduced-motion: reduce) {
  body.dv2-app .dv2-dash-head,
  body.dv2-app .fhc-node,
  body.dv2-app .dv2-actcard,
  body.dv2-app .dv2-const-pill { animation: none; opacity: 1; }
}
html[data-dv2-static="1"] body.dv2-app .dv2-dash-head,
html[data-dv2-static="1"] body.dv2-app .fhc-node,
html[data-dv2-static="1"] body.dv2-app .dv2-actcard,
html[data-dv2-static="1"] body.dv2-app .dv2-const-pill { animation: none; opacity: 1; }


/* ==========================================================================
   SHELL GEOMETRY — mobile-first at every width                       GATE A
   ==========================================================================
   Dashboard corrections D1 / D2 / D7 / D10.

   Design V2 is a mobile-first composition: one column, a floating tab pill,
   and a constellation sized against that column. Nothing in the approved
   screens re-composes for a wide viewport — the layout is centred, not
   stretched. Two pre-Design-V2 rules pulled against that:

     · style.css:4975  `@media (min-width:1024px)` turns #bottomnav into a
       248px left sidebar with a "PRV" wordmark, and pads <body> left to
       clear it. The Design V2 pill rule (above) sets left/right/bottom but
       never `top`, `width` or `flex-direction`, so the column geometry leaked
       through at equal specificity.
     · `.dv2-dash` had no max-width, so `.dv2-const{width:100%}` let the ring
       grow to the full viewport and push the activity cards below the fold.

   Both are corrected here. Everything is scoped to `body.dv2-app`, so Vault,
   Profile, Pending and every other view keep the desktop sidebar they were
   built for — this is a dashboard correction, not a removal of the rail.

   At 390px the numbers are unchanged: min(390 - 32, 460) = 358px, which is
   exactly what the pill and the column already measured. */

body.dv2-app {
  /* One column width for the whole shell. The tab pill matches it so the two
     read as a single object rather than a narrow card on a wide bar. */
  --dv2-shell-max: 460px;
  --dv2-shell-gut: max(16px, env(safe-area-inset-left), env(safe-area-inset-right));
}

/* D2 · cap and centre the dashboard column. This also settles D7: the header
   does not need a wide-viewport variant because there is no wide viewport —
   it recomposes by being centred at its design width. */
body.dv2-app .dv2-dash {
  max-width: var(--dv2-shell-max);
  margin-inline: auto;
}

/* D1 · the tab pill tracks the same column at every width. `left/right` are
   replaced by a centred fixed width so the ≥1024px `left:0; width:248px`
   cannot half-apply. `.bottomnav` is (0,1,0) and `#bottomnav.bottomnav` is
   (1,1,0); this is (1,2,0), so it wins on specificity rather than on source
   order — no !important needed. */
body.dv2-app #bottomnav.bottomnav {
  top: auto;
  left: 50%;
  right: auto;
  transform: translateX(-50%);
  width: min(calc(100% - 2 * var(--dv2-shell-gut)), var(--dv2-shell-max));
  flex-direction: row;
  justify-content: space-around;
  align-items: center;
  gap: 0;
  padding: 12px 8px;
  border-right: none;
  border-top: 1px solid var(--dv2-g-10-bd);
  overflow: visible;
  overscroll-behavior: auto;
}

/* D10 · the "PRV" wordmark is a sidebar artefact; there is no sidebar here. */
body.dv2-app #bottomnav.bottomnav::before { content: none; display: none; }

/* The ≥1024px block re-lays the tab buttons as sidebar rows (row direction,
   left-aligned, 48px tall, 15px text) and gives them their own hover/active
   pills. Restore the Design V2 tab treatment. */
body.dv2-app #bottomnav .bn-item {
  flex: 1;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 4px;
  min-height: 0;
  padding: 4px 14px;
  border-radius: 16px;
  font-size: 9.5px;
}
body.dv2-app #bottomnav .bn-item .material-symbols-outlined { font-size: 20px; }
body.dv2-app #bottomnav .bn-item:hover,
body.dv2-app #bottomnav .bn-item.active { background: none; }
body.dv2-app #bottomnav .bn-item .bn-dot { position: absolute; margin-left: 0; }
body.dv2-app #bottomnav .bn-ai { flex: 0 0 auto; }

/* The sidebar shifted the whole document right to clear itself. `body:has(
   #bottomnav:not([hidden]))` is (1,1,1); adding .dv2-app makes this (1,1,2). */
body.dv2-app:has(#bottomnav:not([hidden])) { padding-left: env(safe-area-inset-left); }


/* ══════════════════════════════════════════════════════════════════════════
   GP-1 · ONE ICON LANGUAGE
   ══════════════════════════════════════════════════════════════════════════

   Design V2 review finding: screens were mixing three icon systems — line
   icons, emoji, and Material Symbols ligatures. The rule is that a screen must
   never show two of them at once.

   The Design V2 surfaces are line-icon only. Legacy surfaces (Tasks, Trips,
   Health, School, Pets, Finance, Vault, Profile …) are NOT touched: they keep
   the exact glyphs they have today until their own phase begins.

   Two mechanisms, chosen per element:

   a) DIRECT SWAP — the markup exists only on a Design V2 surface (dashboard
      header, activity cards, grocery empty state, AI review sheet). The old
      glyph is simply gone.

   b) TWIN + CSS SWAP — the markup is SHARED with Tasks/Trips (the filter
      sheet, the list-options panel, the list toolbar, the bottom nav). Both
      glyphs sit in the DOM; `.lp-legacy-ic` / `.bn-legacy-ic` shows by default
      and `.lp-dv2-ic` / `.bn-dv2-ic` shows only under a Design V2 gate. That
      is what keeps "do not modify shared list styling globally" true — a Tasks
      list renders byte-identically to before.

   `.dv2-lineic` is the base class every one of these glyphs carries. It is
   deliberately UNGATED: no pre-existing element has this class, so it cannot
   reach legacy markup, and gating it would mean re-declaring it under each of
   Gate A / Gate B / body.dv2-groc.
   ────────────────────────────────────────────────────────────────────────── */

.dv2-lineic {
  width: 1em; height: 1em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  overflow: visible;
}

/* GP-2 · the LIST glyph. Same drawing language as .dv2-lineic but WITHOUT
   width/height: every caller (.hc-img, .c-ic-img, .hv-order-img, .hub-pick-img,
   .dv2-actcard-img) already declares its own size in px, and a competing
   `width: 1em` here would win or lose depending on stylesheet order — which is
   exactly the kind of silent, load-order-dependent bug the icons are being
   unified to avoid. Ungated for the same reason .dv2-lineic is: no
   pre-existing element carries this class. */
.dv2-listic {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  display: block;
  overflow: visible;
}

/* (b) the twin pair. Default = legacy, exactly as today. */
.lp-dv2-ic, .bn-dv2-ic { display: none; }

/* Design V2 gates flip the pair. body.dv2-groc covers the Grocery list AND its
   sheets (they are siblings of #view-list, not children); body.dv2-app covers
   the dashboard, which is where the bottom nav's Design V2 form belongs. */
body.dv2-groc .lp-legacy-ic { display: none; }
body.dv2-groc .lp-dv2-ic { display: inline-block; }
body.dv2-app .bn-legacy-ic,
body.dv2-groc .bn-legacy-ic { display: none; }
body.dv2-app .bn-dv2-ic,
body.dv2-groc .bn-dv2-ic { display: inline-block; }

/* Bottom nav — matches the 20px the ligature was set to at design-v2-app.css
   line ~896, so the tab metrics do not move. */
body.dv2-app #bottomnav .bn-dv2-ic,
body.dv2-groc #bottomnav .bn-dv2-ic { width: 20px; height: 20px; stroke-width: 1.7; }
body.dv2-app #bottomnav .bn-item.active .bn-dv2-ic { stroke-width: 2; }
body.dv2-app #bottomnav .bn-ai .bn-dv2-ic { width: 22px; height: 22px; }

/* Dashboard header — the bell and the weather pill were the last two Material
   Symbols on the Design V2 dashboard chrome. Sizes match the ligature they
   replace so the header does not reflow. */
body.dv2-app .dv2-dash-bellic { width: 19px; height: 19px; }
body.dv2-app .dv2-weather-ic { width: 14px; height: 14px; color: var(--dv2-c-weather-ic); }
body.dv2-app .dv2-weather-wind { width: 14px; height: 14px; color: var(--dv2-text-muted); }

/* Activity cards — the Done All disc (GP-2: the operation stays, the control
   is restyled) and the chevron. */
body.dv2-app .dv2-actcard-doneic { width: 18px; height: 18px; }
body.dv2-app .dv2-actcard-chevic { width: 16px; height: 16px; color: var(--dv2-text-sub); }
