/* ════════════════════════════════════════════════════════════════════════
   Padoori — shared app shell
   ────────────────────────────────────────────────────────────────────────
   The cohesive "logged-in product" surface: the styles every customer-facing
   screen shares so they look like one app — the topbar, card, badges, tags,
   status pills, buttons, the dedicated-number block, plus the status colour
   tokens and the focus / reduced-motion a11y rules.

   These were refined on the dashboard (Brief A) and lived inline there; they
   now live here once so dashboard.html, onboarding.html, login.html, and
   billing_success.html stop redefining them.

   LOAD ORDER MATTERS. Every page links it as:
       <link rel="stylesheet" href="/static/styles.css">   (global palette + marketing base)
       <link rel="stylesheet" href="/static/app.css">      (this file — the app shell)
       <style> … page-specific rules … </style>            (per-page layout)
   i.e. AFTER styles.css (so the shell wins over the marketing .btn etc.) and
   BEFORE any page's inline <style> (so a page can still tweak its own layout).
   Colour/spacing tokens it relies on (--bg-*, --text-*, --accent-*, --radius-*,
   --transition-fast …) are defined in static/styles.css :root.
   ════════════════════════════════════════════════════════════════════════ */

/* Brand match: app surfaces use the marketing site's charcoal ink, never blue.
   This :root override lives in app.css ON PURPOSE — marketing pages (index/pricing/
   get-started/legal) don't load app.css, so their decorative blue (styles.css) is
   untouched. Green/amber/rose/violet status tokens are defined elsewhere and are
   intentionally NOT remapped. Every app blue routes through these four tokens +
   --focus-ring, so this one block recolours the whole logged-in product to charcoal. */
:root {
    --accent-blue:       var(--text-primary);  /* #1d1d1f charcoal — was #0066cc */
    --accent-blue-hover: #2d2d2f;              /* matches .btn.dark hover — was #00529e */
    --accent-blue-soft:  var(--bg-muted);      /* #f0f0f2 neutral chip/badge fill — was #e8f1fb */
    --accent-blue-glow:  rgba(0,0,0,0.06);     /* was rgba(0,102,204,0.06) */
    --focus-ring:        #1d1d1f;              /* charcoal focus outline — was #0066cc */
}

/* Display face — ported from the public site's Apple-light headline recipe (home-light.css
   .hv2-h1: Plus Jakarta Sans 600, -0.02em, 1.12 leading). App-scoped on purpose: styles.css
   :root (--font-sans) is shared with marketing pages and stays Inter-only. Body copy keeps
   --font-sans throughout the app; only headline-class elements opt into --font-display. */
:root {
    --font-display: "Plus Jakarta Sans", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* ── Status / semantic colour tokens ──────────────────────────────────────
   Semantic names mapped onto the global palette. Status text shades are tuned
   for AA contrast on their tint (the raw --accent-emerald/-rose/-amber are too
   light to use as text), so all status colour lives here, named, in one place
   rather than scattered as one-off hex. */
:root {
    --d-success:    #197a3a;                    /* emerald family, AA on light (4.9:1 on tint) */
    --d-success-bg: var(--accent-emerald-soft); /* #e7f8ed */
    --d-error:      #c0322b;                    /* rose family, AA on light  */
    --d-error-bg:   #fde2e1;
    --d-amber:      #92610b;                    /* amber family, AA on light */
    --d-amber-bg:   #fff3d6;
    --d-violet:     #6b3fd1;                    /* "building" state          */
    --d-violet-bg:  #ede4ff;
    --d-code-bg:    #0c0e12;                    /* agent-script code surface */
    --d-code-fg:    #d6dae2;
}

/* ── Reset + base ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* Atmosphere wash: a very subtle blue-tinted gradient (NOT the marketing site's saturated
   #e2edfb/#e9f1fc pair — this is a long-session-friendly whisper of blue so white .card
   surfaces still visibly float on it). Fixed attachment keeps it steady on scroll without
   jank (a plain gradient, no blur/animation, so this costs nothing to paint). */
body { font-family: var(--font-sans); background: linear-gradient(180deg, #f6f9fd 0%, #f2f6fb 100%) fixed; color: var(--text-primary); min-height: 100vh; letter-spacing: -0.01em; -webkit-font-smoothing: antialiased; }

/* ── Brand lockup (centered logo for the auth / transition screens) ── */
.brand-lockup { display: flex; align-items: center; justify-content: center; gap: 0.6rem; margin-bottom: 1.75rem; text-decoration: none; color: var(--text-primary); }
.brand-lockup img { height: 30px; width: auto; }
.brand-lockup span { font-weight: 700; font-size: 1.3rem; letter-spacing: -0.03em; }

/* ── Top bar ── */
.topbar { display: flex; align-items: center; justify-content: space-between; padding: 1.1rem 1.5rem; background: var(--bg-surface); border-bottom: 1px solid var(--border-strong); position: sticky; top: 0; z-index: 10; }
.topbar .logo { display: flex; align-items: center; gap: 0.55rem; text-decoration: none; color: var(--text-primary); font-weight: 700; font-size: 1.1rem; letter-spacing: -0.03em; }
.topbar .logo img { height: 24px; }
.topbar .brand-tag { font-weight: 500; color: var(--text-tertiary); }
.topbar .who { display: flex; align-items: center; gap: 1rem; font-size: 0.85rem; color: var(--text-secondary); }
.topbar .who a { color: var(--accent-blue); text-decoration: none; font-weight: 600; }

/* ── Card ──
   min-width: 0 is defense-in-depth: a .card is a flex/grid child on every route that grids its
   cards (.overview-grid etc.) and on any future Phase-2 layout — without it, a .card can be asked
   to force its own grid/flex TRACK wide instead of shrinking (the shell-level half of the 360px
   defect, fixed below). overflow-wrap: break-word is the OTHER half: min-width:0 only governs how
   a flex/grid ITEM negotiates space with siblings — it does nothing about a descendant's own
   unbreakable text (a long id/number/token with no space or hyphen) still laying out past the
   card's own box and bleeding into the document's scrollable width. Confirmed via a stress-inject
   test: min-width:0 alone left scrollWidth 403px/564px vs a 360px viewport; adding this line is
   what actually clips the text back inside the card. Matches the existing narrow precedents
   (.sidebar-email / .manage-info dd already use overflow-wrap: anywhere) generalized to every
   .card so no future page-local class has to remember to add it itself. */
.card { min-width: 0; overflow-wrap: break-word; background: var(--bg-surface); border: 1px solid var(--border-subtle); border-radius: var(--radius-xl); box-shadow: var(--shadow-sm); padding: 1.5rem; margin-bottom: 1.5rem; transition: box-shadow var(--transition-fast, 0.15s ease), border-color var(--transition-fast, 0.15s ease), transform var(--transition-fast, 0.15s ease); }
.card h2 { font-size: 0.78rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 1rem; }
/* Hover-lift is opt-in ONLY (.card--interactive) — a resting informational card (e.g. a
   static stat block) must not look clickable. Additive modifier; plain .card never lifts. */
.card--interactive:hover { box-shadow: var(--shadow-md); border-color: var(--border-hover); transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) {
    .card { transition: none; }
    .card--interactive:hover { transform: none; }
}

/* ── Status badges + tags ── */
.badge { display: inline-block; font-size: 0.74rem; font-weight: 700; padding: 0.2rem 0.6rem; border-radius: var(--radius-pill); }
.b-invited { background: var(--bg-muted); color: var(--text-secondary); }
.b-onboarding { background: var(--d-amber-bg); color: var(--d-amber); }
.b-submitted { background: var(--accent-blue-soft); color: var(--accent-blue); }
.b-building { background: var(--d-violet-bg); color: var(--d-violet); }
.b-live { background: var(--d-success-bg); color: var(--d-success); }
.b-error { background: var(--d-error-bg); color: var(--d-error); }
.tag { display: inline-block; font-size: 0.74rem; font-weight: 600; padding: 0.18rem 0.55rem; border-radius: 6px; background: var(--bg-muted); color: var(--text-secondary); }

/* ── Status pills (non-number agent states) ── */
.pill { display: inline-flex; align-items: center; gap: 0.45rem; font-size: 0.86rem; font-weight: 600; padding: 0.55rem 0.85rem; border-radius: var(--radius-md); border: 1px solid transparent; }
.pill.info { background: var(--accent-blue-soft); color: var(--accent-blue); border-color: rgba(0,0,0,0.12); }
.pill.success { background: var(--d-success-bg); color: var(--d-success); border-color: rgba(25,122,58,0.18); }
.pill.warn { background: var(--d-error-bg); color: var(--d-error); border-color: rgba(192,50,43,0.18); }
.pill .dot { width: 9px; height: 9px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.pill .dot.pulse { animation: pulse 1.4s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }

/* ── Dedicated-number block ── */
.num-block { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; background: var(--bg-base); border: 1px solid var(--border-strong); border-radius: var(--radius-lg); padding: 1.1rem 1.25rem; }
/* White chip, hairline border, charcoal glyph — crisp against the --bg-base block it sits on
   (was a blue-tinted fill; the token re-map alone would sink it into the grey box). */
.num-icon { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; flex-shrink: 0; border-radius: 12px; background: var(--bg-surface); border: 1px solid var(--border-strong); color: var(--text-primary); }
.num-icon svg { width: 22px; height: 22px; }
.num-main { flex: 1 1 12rem; min-width: 0; }
.num-label { display: block; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-tertiary); }
.num-value { display: block; margin-top: 0.2rem; font-family: var(--font-sans); font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; font-size: 1.7rem; font-weight: 800; color: var(--text-primary); letter-spacing: -0.01em; white-space: nowrap; }
.num-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* ── Buttons ── */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; font-family: inherit; font-size: 0.9rem; font-weight: 600; padding: 0.62rem 1.1rem; border-radius: var(--radius-md); border: 1px solid transparent; background: var(--accent-blue); color: #fff; cursor: pointer; text-decoration: none; white-space: nowrap; transition: box-shadow var(--transition-fast, 0.15s ease), background var(--transition-fast, 0.15s ease), border-color var(--transition-fast, 0.15s ease); }
.btn:hover { background: var(--accent-blue-hover); box-shadow: 0 8px 22px rgba(0,0,0,0.10); }
.btn svg { width: 16px; height: 16px; flex-shrink: 0; }
.btn.dark { background: var(--text-primary); }
.btn.dark:hover { background: #2d2d2f; box-shadow: 0 8px 22px rgba(29,29,31,0.18); }
.btn.btn-ghost { background: transparent; color: var(--text-secondary); border-color: var(--border-strong); }
.btn.btn-ghost:hover { background: var(--bg-muted); border-color: var(--border-hover); box-shadow: none; }
.btn.btn-sm { font-size: 0.82rem; padding: 0.46rem 0.8rem; }
.btn.btn-ghost.is-copied { color: var(--d-success); border-color: rgba(25,122,58,0.4); background: var(--d-success-bg); }

/* ── Accessibility ── */
a:focus-visible, button:focus-visible, summary:focus-visible, [tabindex]:focus-visible {
    outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: var(--radius-sm);
}
@media (prefers-reduced-motion: reduce) {
    .pill .dot.pulse { animation: none; }
    .btn { transition: none; }
}

/* ════════════════════════════════════════════════════════════════════════
   Dashboard SaaS shell — the 4-route authenticated app frame (Overview /
   Integrations / Leads / Settings). Used ONLY by the templates that extend
   _app_shell.html. Purely additive: every selector here is new, so the other
   app.css consumers (onboarding/login/billing_success, which use .topbar /
   .card / .btn / .num-block) are untouched. Token-based throughout.
   ════════════════════════════════════════════════════════════════════════ */
/* Rail + contextual second column widths. --sidebar-w kept as an alias so any older selector that
   still references it stays valid; --rail-w is the live token the grid uses. */
:root { --rail-w: 232px; --subnav-w: 248px; --sidebar-w: var(--rail-w); }

/* ── Shell grid: primary rail + (optional contextual column) + scrolling content ──
   Default is 2-col [rail | main]. When a route fills {% block subnav %} the shell gets
   .has-subnav and becomes 3-col [rail | subnav | main]; otherwise main is full-width. */
.app-shell { display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); min-height: 100vh; }
.app-shell.has-subnav { grid-template-columns: var(--rail-w) var(--subnav-w) minmax(0, 1fr); }
.app-main { min-width: 0; padding: 2.25rem 2rem 4rem; }
/* Was clamped to 880px + centered. Dropped so main fills the freed space (the "fills the empty
   space" win); a comfortable max keeps line lengths sane on very wide displays. */
.page { max-width: var(--container-width); margin: 0; }

/* Mobile-only top bar (brand + hamburger). Hidden on desktop; shown < 768px. */
.app-topbar { display: none; }
.nav-backdrop { display: none; }
.nav-burger { display: inline-flex; align-items: center; justify-content: center; width: 42px; height: 42px; border: 1px solid var(--border-strong); border-radius: var(--radius-md); background: var(--bg-surface); color: var(--text-primary); cursor: pointer; padding: 0; }
.nav-burger svg { width: 22px; height: 22px; }

/* ── Sidebar ──
   Frosted glass, ported from the public site's .main-header recipe (rgba white +
   backdrop-filter blur+saturate). @supports gates it: browsers without backdrop-filter
   get the old solid var(--bg-surface) fill instead of a translucent panel over nothing. */
.sidebar { position: sticky; top: 0; align-self: start; height: 100vh; display: flex; flex-direction: column; background: var(--bg-surface); border-right: 1px solid var(--border-strong); padding: 1.25rem 0.9rem; }
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .sidebar {
        background: rgba(255,255,255,0.78);
        backdrop-filter: blur(12px) saturate(160%);
        -webkit-backdrop-filter: blur(12px) saturate(160%);
    }
}
.sidebar-brand { display: flex; align-items: center; gap: 0.55rem; text-decoration: none; color: var(--text-primary); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.03em; padding: 0.3rem 0.6rem 1.1rem; }
.sidebar-brand img { height: 26px; width: auto; }
.sidebar-nav { display: flex; flex-direction: column; gap: 0.25rem; }
.nav-item { display: flex; align-items: center; gap: 0.7rem; padding: 0.62rem 0.7rem; border-radius: var(--radius-md); text-decoration: none; color: var(--text-secondary); font-weight: 600; font-size: 0.92rem; transition: background var(--transition-fast, 0.15s ease), color var(--transition-fast, 0.15s ease); }
.nav-item svg { width: 19px; height: 19px; flex-shrink: 0; opacity: 0.75; }
.nav-item:hover { background: var(--bg-muted); color: var(--text-primary); }
/* "You are here" must read distinctly from :hover (which is ALSO --bg-muted + charcoal): bolder
   weight + a charcoal left rail. Matches the marketing site's charcoal selection (--border-active).
   The inset box-shadow rail is also the forced-colors-mode fallback: Windows High Contrast strips
   background/box-shadow color but keeps `border`/`outline`, so an explicit border backs it up. */
.nav-item.is-active { background: var(--bg-muted); color: var(--text-primary); font-weight: 700; box-shadow: inset 3px 0 0 var(--text-primary); }
.nav-item.is-active svg { opacity: 1; }
@media (forced-colors: active) {
    .nav-item.is-active { border-left: 3px solid CanvasText; }
}
.sidebar-foot { margin-top: auto; padding: 0.9rem 0.7rem 0.2rem; border-top: 1px solid var(--border-strong); display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.82rem; }
.sidebar-email { color: var(--text-tertiary); overflow-wrap: anywhere; }
.sidebar-logout { color: var(--accent-blue); text-decoration: none; font-weight: 600; align-self: flex-start; }
.sidebar-logout:hover { text-decoration: underline; }

/* ── "Connected" integrations section (sidebar) ───────────────────────────────
   Renders on all four routes via the client-scoped connected_integrations context
   processor; hidden entirely when nothing is connected. Inherits the liked sidebar
   vibe (white surface, neutral ink, hairline divider like .sidebar-foot). Colour
   discipline: the ONLY colour is the green status dot (amber when a connection
   needs re-auth, via .provider-status-dot.is-warn). No blue, by design. The whole
   block is one link to /dashboard/integrations. */
.sidebar-section { display: block; margin-top: 1.1rem; padding-top: 0.95rem; border-top: 1px solid var(--border-strong); text-decoration: none; color: inherit; }
.sidebar-section-label { display: block; padding: 0 0.7rem; margin-bottom: 0.45rem; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); }
.connected-list { list-style: none; display: flex; flex-direction: column; gap: 0.1rem; }
.connected-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.4rem 0.7rem; border-radius: var(--radius-md); transition: background var(--transition-fast, 0.15s ease); }
.sidebar-section:hover .connected-item { background: var(--bg-muted); }
.connected-name { flex: 1 1 auto; min-width: 0; font-size: 0.84rem; font-weight: 600; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── "Agents" sidebar section — the customer's named voice agents (one today, scales to many).
   Each agent is its own link row (vs Connected's single block link). Neutral avatar; the only
   colour is the status dot. ── */
.agent-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.42rem 0.7rem; border-radius: var(--radius-md); text-decoration: none; color: var(--text-secondary); transition: background var(--transition-fast, 0.15s ease), color var(--transition-fast, 0.15s ease); }
.agent-row:hover { background: var(--bg-muted); color: var(--text-primary); }
.agent-avatar { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; flex-shrink: 0; border-radius: 7px; background: var(--bg-surface); border: 1px solid var(--border-strong); color: var(--text-primary); font-size: 0.74rem; font-weight: 800; }
@media (prefers-reduced-motion: reduce) { .connected-item, .agent-row { transition: none; } }

/* ── Shared page header (all four routes) ── */
.page-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.4rem; }
.page-head h1 { font-size: 1.7rem; font-weight: 800; letter-spacing: -0.03em; }
.page-head h1 .it { font-family: "Georgia", serif; font-style: italic; font-weight: 500; color: var(--accent-blue); }
.page-lead { color: var(--text-secondary); margin: 0.35rem 0 1.75rem; font-size: 0.98rem; line-height: 1.55; }
.app-main .card:last-child { margin-bottom: 0; }

/* ── Provider grid (Integrations) ── */
.provider-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.1rem; }
.provider-card { display: flex; flex-direction: column; gap: 0.8rem; background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--radius-lg); box-shadow: 0 12px 40px rgba(0,0,0,0.03); padding: 1.25rem; }
.provider-card .ph { display: flex; align-items: center; gap: 0.7rem; }
.provider-icon { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; flex-shrink: 0; border-radius: 11px; background: var(--bg-surface); border: 1px solid var(--border-strong); color: var(--text-primary); }
.provider-icon svg { width: 22px; height: 22px; }
.provider-card .pname { font-weight: 700; font-size: 1.02rem; letter-spacing: -0.01em; }
.provider-desc { font-size: 0.88rem; color: var(--text-secondary); line-height: 1.5; flex: 1 1 auto; }
.provider-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.conn-status { display: inline-flex; align-items: center; gap: 0.45rem; font-size: 0.82rem; font-weight: 600; color: var(--text-tertiary); }

/* ── Status dot (green = active, amber = needs reauth, neutral = not connected) ──
   Scoped name on purpose: styles.css already ships a bare global `.status-dot` (a 6px emerald
   marketing pulse dot), so this dashboard dot uses `.provider-status-dot` to avoid the collision
   rather than depend on stylesheet load order. */
.provider-status-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--text-tertiary); flex-shrink: 0; display: inline-block; }
.provider-status-dot.is-active { background: var(--d-success); box-shadow: 0 0 0 3px rgba(25,122,58,0.15); }
.provider-status-dot.is-warn { background: var(--d-amber); box-shadow: 0 0 0 3px rgba(146,97,11,0.15); }
/* A7 (dashboard sprint A): an errored agent build needs its own dot — reusing --d-error, the same
   token Overview's "We're on it" badge (.b-error) already uses, so the two pages agree visually. */
.provider-status-dot.is-error { background: var(--d-error); box-shadow: 0 0 0 3px rgba(192,50,43,0.15); }

/* ════════════════════════════════════════════════════════════════════════
   Rail restyle (Phase 0) — Apple/Linear light pass over the existing .sidebar.
   ALL markup contracts are preserved (the four .nav-item anchors + hrefs +
   aria-current order, .is-active, .provider-status-dot/.is-warn, .sidebar-foot);
   this is value-level styling + ADDITIVE grouping selectors only. Token-based;
   hairline borders + one restrained active language (subtle --bg-muted fill +
   inset charcoal left rail), shared by the rail and the second column below.
   ════════════════════════════════════════════════════════════════════════ */

/* Optional uppercase group label inside the rail nav ("Main"/"Connect"/…) — additive markup;
   no smoke asserts on it. Mirrors .sidebar-section-label's tracking for a consistent rail voice. */
.rail-group-label { display: block; padding: 0.85rem 0.7rem 0.3rem; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-tertiary); }
.sidebar-nav .rail-group-label:first-child { padding-top: 0.2rem; }

/* ════════════════════════════════════════════════════════════════════════
   Contextual second column (.subnav) — the column that renders ONLY when a
   route fills {% block subnav %} (the shell adds .has-subnav then). Phase 0
   ships the styling so Phase-1 routes can fill it; the active language matches
   the rail's (subtle --bg-muted fill). White surface, right hairline border.
   ════════════════════════════════════════════════════════════════════════ */
.subnav { position: sticky; top: 0; align-self: start; height: 100vh; overflow-y: auto; min-width: 0; background: var(--bg-surface); border-right: 1px solid var(--border-strong); padding: 1.5rem 0.9rem; display: flex; flex-direction: column; gap: 0.2rem; }
.subnav-head { padding: 0 0.5rem 0.4rem; font-size: 0.8125rem; font-weight: 500; color: var(--text-secondary); }
.subnav-search { position: relative; display: flex; align-items: center; margin-bottom: 0.6rem; }
.subnav-search svg { position: absolute; left: 0.65rem; width: 15px; height: 15px; color: var(--text-tertiary); pointer-events: none; }
.subnav-search input { width: 100%; font: inherit; font-size: 0.86rem; color: var(--text-primary); background: var(--bg-base); border: 1px solid var(--border-strong); border-radius: var(--radius-md); padding: 0.42rem 0.7rem 0.42rem 1.9rem; }
.subnav-search input::placeholder { color: var(--text-tertiary); }
.subnav-search input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; border-color: var(--accent-blue); }
.subnav-list { list-style: none; display: flex; flex-direction: column; gap: 0.1rem; }
.subnav-item { display: flex; align-items: center; gap: 0.55rem; padding: 0.5rem 0.6rem; border-radius: var(--radius-md); text-decoration: none; color: var(--text-secondary); font-size: 0.88rem; font-weight: 500; transition: background var(--transition-fast, 0.15s ease), color var(--transition-fast, 0.15s ease); }
.subnav-item:hover { background: var(--bg-muted); color: var(--text-primary); }
.subnav-item.is-active { background: var(--bg-muted); color: var(--text-primary); font-weight: 600; box-shadow: inset 3px 0 0 var(--text-primary); }
.subnav-section { margin-top: 1rem; padding-top: 0.85rem; border-top: 1px solid var(--border-subtle); }
.subnav-section-label { display: block; padding: 0 0.6rem 0.4rem; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-tertiary); }
@media (prefers-reduced-motion: reduce) { .subnav-item { transition: none; } }

/* ════════════════════════════════════════════════════════════════════════
   Integrations directory (Brief 1b) — full provider catalog as a filterable,
   searchable card grid + a per-integration Manage modal. Extends the base
   .provider-grid / .provider-card / .provider-status-dot above; everything here
   is new and token-based, so the other app.css consumers are untouched.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Filter tabs + live search ── */
.directory-controls { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.4rem; }
.cat-filter { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.cat-tab { appearance: none; font: inherit; font-size: 0.84rem; font-weight: 600; color: var(--text-secondary); background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--radius-pill); padding: 0.4rem 0.85rem; cursor: pointer; white-space: nowrap; transition: background var(--transition-fast, 0.15s ease), color var(--transition-fast, 0.15s ease), border-color var(--transition-fast, 0.15s ease); }
.cat-tab:hover { color: var(--text-primary); border-color: var(--border-hover); }
.cat-tab.is-active { background: var(--accent-blue); color: #fff; border-color: var(--accent-blue); }
.provider-search { position: relative; display: inline-flex; align-items: center; flex: 1 1 220px; max-width: 320px; }
.provider-search svg { position: absolute; left: 0.72rem; width: 16px; height: 16px; color: var(--text-tertiary); pointer-events: none; }
.provider-search input { width: 100%; font: inherit; font-size: 0.88rem; color: var(--text-primary); background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--radius-md); padding: 0.5rem 0.8rem 0.5rem 2.1rem; }
.provider-search input::placeholder { color: var(--text-tertiary); }
.provider-search input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; border-color: var(--accent-blue); }

/* ── Consistent logo tile (cards + Manage header); vendored SVG or lettermark ── */
.provider-logo { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; border-radius: 11px; background: var(--bg-surface); border: 1px solid var(--border-subtle); overflow: hidden; }
.provider-logo--sm { width: 24px; height: 24px; border-radius: 7px; }
.provider-logo--md { width: 42px; height: 42px; }
.provider-logo--lg { width: 52px; height: 52px; border-radius: 13px; }
.provider-logo img { width: 62%; height: 62%; object-fit: contain; }
.provider-lettermark { font-weight: 800; font-size: 1.05rem; color: var(--text-secondary); letter-spacing: -0.02em; }
.provider-logo--sm .provider-lettermark { font-size: 0.78rem; }
.provider-logo--lg .provider-lettermark { font-size: 1.3rem; }

/* ── Card head + meta ── */
.pc-head { display: flex; align-items: center; gap: 0.75rem; }
.pc-id { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.cat-chip { align-self: flex-start; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; color: var(--text-tertiary); background: var(--bg-muted); border-radius: var(--radius-pill); padding: 0.12rem 0.5rem; }
.provider-card { transition: box-shadow var(--transition-fast, 0.15s ease), border-color var(--transition-fast, 0.15s ease), transform var(--transition-fast, 0.15s ease); }
.provider-card:hover { box-shadow: var(--shadow-md); border-color: var(--border-hover); transform: translateY(-2px); }
/* Filter/search hides non-matching cards via the [hidden] attribute. The base .provider-card sets
   display:flex (author origin), which beats the UA [hidden]{display:none} rule — so we MUST restate
   it here or filtering would hide nothing (mirrors .manage-overlay[hidden] / .nav-backdrop[hidden]). */
.provider-card[hidden] { display: none; }

/* ── Coming-soon: visibly de-emphasized, no functional actions ── */
.provider-card.is-coming { background: var(--bg-base); box-shadow: none; }
.provider-card.is-coming:hover { box-shadow: none; border-color: var(--border-strong); transform: none; }
.provider-card.is-coming .provider-logo { filter: grayscale(1); opacity: 0.65; }
.provider-card.is-coming .pname { color: var(--text-secondary); }
.provider-card.is-coming .provider-desc { color: var(--text-tertiary); }
.chip-coming { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.02em; color: var(--text-tertiary); background: var(--bg-muted); border-radius: var(--radius-pill); padding: 0.22rem 0.6rem; }
.provider-notify { font-size: 0.8rem; font-weight: 600; color: var(--accent-blue); text-decoration: none; }
.provider-notify:hover { text-decoration: underline; }
.provider-empty { margin-top: 1.5rem; text-align: center; color: var(--text-tertiary); font-size: 0.9rem; }

/* ── Danger button (Disconnect) ── */
.btn.btn-danger { background: var(--d-error-bg); color: var(--d-error); border-color: rgba(192,50,43,0.22); }
.btn.btn-danger:hover { background: #fbd3d1; border-color: rgba(192,50,43,0.4); box-shadow: none; }

/* ── Per-integration Manage modal ── */
.manage-overlay { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; padding: 1.25rem; background: rgba(0,0,0,0.45); }
.manage-overlay[hidden] { display: none; }
body.modal-open { overflow: hidden; }
.manage-panel { position: relative; width: min(440px, 100%); max-height: calc(100vh - 2.5rem); overflow-y: auto; background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--radius-xl); box-shadow: var(--shadow-xl); padding: 1.5rem; }
.manage-panel:focus { outline: none; }
.manage-close { position: absolute; top: 0.9rem; right: 0.9rem; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; border-radius: var(--radius-md); background: transparent; color: var(--text-tertiary); cursor: pointer; }
.manage-close:hover { background: var(--bg-muted); color: var(--text-primary); }
.manage-close svg { width: 18px; height: 18px; }
.manage-head { display: flex; align-items: center; gap: 0.85rem; margin-bottom: 0.5rem; padding-right: 2rem; }
.manage-title { font-size: 1.2rem; font-weight: 800; letter-spacing: -0.02em; }
.manage-status { display: inline-flex; align-items: center; gap: 0.4rem; margin-top: 0.25rem; font-size: 0.82rem; font-weight: 600; color: var(--text-secondary); }
.manage-section { padding-top: 1rem; margin-top: 1rem; border-top: 1px solid var(--border-subtle); }
.manage-label { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-tertiary); margin-bottom: 0.6rem; }
.manage-info { display: flex; flex-direction: column; gap: 0.6rem; }
.manage-info > div { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.manage-info dt { font-size: 0.85rem; color: var(--text-secondary); }
.manage-info dd { font-size: 0.85rem; font-weight: 600; color: var(--text-primary); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; text-align: right; }
.manage-muted { font-size: 0.85rem; color: var(--text-tertiary); line-height: 1.55; }
.manage-actions { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin-top: 1.5rem; }
.manage-disconnect { margin: 0; }

@media (max-width: 768px) {
    .directory-controls { flex-direction: column; align-items: stretch; }
    .cat-filter { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 0.25rem; }
    /* Reset the row-layout flex basis: in this column stack a 220px basis would set the search box's
       HEIGHT (vertical main axis), not its width — make it auto-height, full-width instead. */
    .provider-search { flex: none; width: 100%; max-width: none; }
    .manage-panel { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
    .provider-card, .cat-tab { transition: none; }
    .provider-card:hover { transform: none; }
}

/* ── Shared notice banner (.app-notice) ────────────────────────────────────────
   Used by the Housecall Pro connect-action feedback (success/error) and any other
   route that needs a one-line status banner inside the app shell. Mirrors the
   inline .notice/.notice.success/.notice.error recipe in dashboard_settings.html
   so both surfaces look identical. Class names differ (.app-notice vs .notice) so
   there is no cross-page collision; both route through the same --d-* tokens.
   Modifier: .app-notice--success / .app-notice--error (match hcp_notice[0] values). */
.app-notice { display: flex; align-items: flex-start; gap: 0.5rem; font-size: 0.88rem; font-weight: 600; padding: 0.7rem 0.9rem; border-radius: var(--radius-md); margin-bottom: 1.25rem; border: 1px solid transparent; border-left-width: 4px; }
.app-notice svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 0.05rem; }
.app-notice--success { background: var(--d-success-bg); color: var(--d-success); border-color: rgba(25,122,58,0.18); border-left-color: var(--d-success); }
.app-notice--error   { background: var(--d-error-bg);   color: var(--d-error);   border-color: rgba(192,50,43,0.18); border-left-color: var(--d-error); }
.app-notice--warn    { background: var(--d-amber-bg);   color: var(--d-amber);   border-color: rgba(146,97,11,0.18); border-left-color: var(--d-amber); }

/* ── Inline connect-error text (.connect-error-msg) ────────────────────────────
   Small error line injected by the OAuth connect scripts (ghl/jobber/hubspot/pipedrive/
   close/monday/acuity-connect.js), replacing the old window.alert() feedback. Inserted as a
   full-width block row AFTER the button's flex container (.manage-actions / .provider-foot) —
   never as a sibling of the button itself, which would add a 3rd child to a
   `justify-content:space-between` row and break its spacing. Same --d-error token as
   .app-notice--error, just inline rather than a full banner (there's no fixed page-level
   anchor point per card/modal button). */
.connect-error-msg { display: block; margin-top: 0.5rem; font-size: 0.82rem; font-weight: 600; color: var(--d-error); }

/* ── Provider note (.provider-note) ────────────────────────────────────────────
   "Not enabled in this environment" muted note line on unavailable provider cards.
   Matches the muted convention of .provider-empty / .manage-muted — same tertiary
   ink + small size. Not .provider-empty (that's a centered no-results line) and
   not .provider-notify (that's a coloured "Notify me" link). */
.provider-note { font-size: 0.82rem; color: var(--text-tertiary); line-height: 1.55; margin-top: 0.25rem; }

/* ── Housecall Pro paste-key panel (.hcp-*) ─────────────────────────────────────
   The HCP connect/reconnect form lives in a <details> disclosure so it collapses
   when the key is already saved. The panel itself is a calm contained inset (white
   surface, subtle border) that sits inside a .provider-card. The key input follows
   the canonical app text-input recipe (.subnav-search input / .field input). */
.hcp-connect-form { margin: 0; }
/* The summary IS the connect button (.btn dark / .btn-ghost) — let that variant own the
   text colour. A hard-coded `color: var(--text-primary)` here painted charcoal text onto
   the .btn.dark charcoal fill (text-primary == the dark btn background), blacking it out. */
.hcp-details > summary { cursor: pointer; font-weight: 600; list-style: none; }
.hcp-details > summary::-webkit-details-marker { display: none; }
.hcp-key-panel { display: flex; flex-direction: column; gap: 0.65rem; margin-top: 0.65rem; padding: 0.9rem 1rem; background: var(--bg-base); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); }
.hcp-hint { font-size: 0.8rem; color: var(--text-tertiary); line-height: 1.5; }
.hcp-key-input { width: 100%; font: inherit; font-size: 0.88rem; color: var(--text-primary); background: var(--bg-base); border: 1px solid var(--border-strong); border-radius: var(--radius-md); padding: 0.6rem 0.75rem; }
.hcp-key-input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }

/* ── Integration "How to connect" help panel (.connect-help) ────────────────
   A <details> disclosure collapsed by default; sits below the card foot on
   every live provider card. Reuses the same tokens as .hcp-hint / .hcp-key-panel. */
.connect-help { margin-top: .5rem; }
.connect-help > summary { cursor: pointer; font-size: .85rem; color: var(--text-tertiary); list-style: none; }
.connect-help > summary::-webkit-details-marker { display: none; }
.connect-help-steps { margin: .5rem 0 0; padding-left: 1.1rem; font-size: .85rem; color: var(--text-tertiary); line-height: 1.5; }
.connect-help-steps li { margin-bottom: .25rem; }

/* ── Settings tabs ── */
.settings-tabs { display: flex; gap: 0.3rem; border-bottom: 1px solid var(--border-strong); margin-bottom: 1.5rem; flex-wrap: wrap; }
.tab { appearance: none; background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer; font: inherit; font-weight: 600; font-size: 0.92rem; color: var(--text-secondary); padding: 0.65rem 0.9rem; margin-bottom: -1px; text-decoration: none; }
.tab:hover { color: var(--text-primary); }
.tab[aria-selected="true"] { color: var(--accent-blue); border-bottom-color: var(--accent-blue); }

/* ── Mobile: collapse the sidebar into an off-canvas drawer ── */
@media (max-width: 768px) {
    /* Both the 2-col and 3-col (.has-subnav, higher specificity) shells flatten to one column.
       minmax(0, 1fr) — NOT a bare 1fr. A bare 1fr track's minimum size defaults to `auto`
       (content-based): the track refuses to shrink below its widest descendant's intrinsic
       width, so a single unbreakable-width child anywhere downstream (a long token/number/id
       with no space or hyphen break opportunity) silently pushes the WHOLE document past the
       viewport — .app-main's own `min-width: 0` cannot compensate for a track that never gave
       it the chance to be asked to shrink. Same defect class as the /compare overflow fix. */
    .app-shell, .app-shell.has-subnav { grid-template-columns: minmax(0, 1fr); }
    /* The contextual column drops its sticky full-height frame and flows inline above main as a
       plain panel (the rail goes off-canvas via the drawer; the subnav stays reachable in-flow). */
    .subnav { position: static; height: auto; border-right: none; border-bottom: 1px solid var(--border-strong); padding: 1.1rem 1.1rem 0.5rem; }
    .app-topbar { display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: var(--bg-surface); border-bottom: 1px solid var(--border-strong); position: sticky; top: 0; z-index: 20; }
    @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
        .app-topbar {
            background: rgba(255,255,255,0.78);
            backdrop-filter: blur(12px) saturate(160%);
            -webkit-backdrop-filter: blur(12px) saturate(160%);
        }
    }
    .app-topbar .logo { display: flex; align-items: center; gap: 0.5rem; text-decoration: none; color: var(--text-primary); font-weight: 700; font-size: 1.05rem; letter-spacing: -0.03em; }
    .app-topbar .logo img { height: 24px; width: auto; }
    .app-main { padding: 1.5rem 1.1rem 3rem; }
    /* Closed drawer is hidden with visibility (not just transform) so its links leave the keyboard
       tab order + screen-reader tree off-canvas — mirrors .mobile-nav-overlay in styles.css. The
       transform still animates the slide; visibility flips discretely at the ends. */
    .sidebar { position: fixed; top: 0; left: 0; bottom: 0; height: 100vh; width: min(82vw, var(--sidebar-w)); z-index: 40; transform: translateX(-100%); visibility: hidden; transition: transform var(--transition-fast, 0.2s ease), visibility var(--transition-fast, 0.2s ease); box-shadow: 0 18px 50px rgba(0,0,0,0.18); }
    .app-shell.nav-open .sidebar { transform: none; visibility: visible; }
    .nav-backdrop { display: block; position: fixed; inset: 0; background: rgba(0,0,0,0.35); z-index: 30; }
    .nav-backdrop[hidden] { display: none; }
}
@media (prefers-reduced-motion: reduce) {
    .sidebar { transition: none; }
    .nav-item { transition: none; }
}

/* ════════════════════════════════════════════════════════════════════════
   App Design Foundation (Phase 1) — new component layer for Phase-2 pages.
   Everything below is ADDITIVE: no existing selector above is renamed or
   removed, so the current five templates keep working unchanged until each
   is migrated. Ports the public site's Apple-light language (Plus Jakarta
   Sans display face, restrained frosted surfaces) into the workspace at
   information-density scale — no hero sections, no marketing motion.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Page header (new — .page-head above stays for any page not yet migrated) ──
   .page-header h1 uses the display face; --font-display degrades to Inter if the
   Google Fonts request fails or is blocked, so the layout never breaks. */
.page-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.4rem; }
.page-header h1 { font-family: var(--font-display); font-size: clamp(1.6rem, 2.6vw, 2rem); font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; color: var(--text-primary); }
.page-header-actions { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.page-lead { color: var(--text-secondary); margin: 0.35rem 0 1.75rem; font-size: 0.95rem; line-height: 1.55; max-width: 60ch; }

/* ── Card head row (icon chip + title + optional trailing action link) ──
   Additive pattern for cards that want a differentiated header instead of the
   plain uppercase eyebrow (.card h2 keeps working for cards that don't opt in). */
.card-head { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem; }
.card-head .card-head-title { flex: 1 1 auto; min-width: 0; font-size: 0.95rem; font-weight: 700; letter-spacing: -0.01em; color: var(--text-primary); }
.card-head .card-head-action { flex-shrink: 0; font-size: 0.82rem; font-weight: 600; color: var(--accent-blue); text-decoration: none; }
.card-head .card-head-action:hover { text-decoration: underline; }

/* ── Icon chip — 36px rounded square holding an inline SVG glyph; used inside
   .card-head or standalone wherever a card needs a neutral icon anchor. ── */
.icon-chip { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; flex-shrink: 0; border-radius: var(--radius-md); background: var(--bg-muted); color: var(--text-primary); }
.icon-chip svg { width: 18px; height: 18px; }

/* ── Form kit (additive — nothing existing depends on these; Phase-2 pages opt in) ──
   Inputs are scoped to .app-shell (marketing pages never load app.css, so this scoping
   is belt-and-braces rather than strictly necessary — it keeps intent explicit and
   matches the file's existing convention of naming surfaces plainly). Charcoal focus
   ring: 2px solid ring + a soft 3px outer glow, matching the site's non-blue app accent. */
.app-shell .form-field { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1.1rem; }
.app-shell .form-field label { font-size: 0.8rem; font-weight: 600; color: var(--text-primary); }
.app-shell .form-field .form-help { font-size: 0.78rem; color: var(--text-tertiary); line-height: 1.45; }
.app-shell .form-field input,
.app-shell .form-field select,
.app-shell .form-field textarea {
    font: inherit; font-size: 0.9rem; color: var(--text-primary); background: var(--bg-surface);
    border: 1px solid var(--border-strong); border-radius: 11px; padding: 0.65rem 0.8rem;
    transition: border-color var(--transition-fast, 0.15s ease), box-shadow var(--transition-fast, 0.15s ease);
}
.app-shell .form-field input::placeholder,
.app-shell .form-field textarea::placeholder { color: var(--text-tertiary); }
.app-shell .form-field input:focus-visible,
.app-shell .form-field select:focus-visible,
.app-shell .form-field textarea:focus-visible {
    outline: 2px solid var(--focus-ring); outline-offset: 1px;
    box-shadow: 0 0 0 3px rgba(0,0,0,0.08);
}
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1.25rem; }
.form-actions { display: flex; align-items: center; gap: 0.6rem; padding-top: 1.1rem; margin-top: 0.4rem; border-top: 1px solid var(--border-subtle); }
@media (max-width: 640px) {
    .form-row { grid-template-columns: 1fr; gap: 0; }
}
@media (prefers-reduced-motion: reduce) {
    .app-shell .form-field input,
    .app-shell .form-field select,
    .app-shell .form-field textarea { transition: none; }
}

/* ── Table kit (additive — existing per-page tables are untouched until Phase 2 adopts .tbl) ── */
.tbl { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.tbl th { text-align: left; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-tertiary); border-bottom: 1px solid var(--border-strong); padding: 0.8rem 1rem; }
.tbl td { padding: 0.8rem 1rem; border-bottom: 1px solid var(--border-subtle); color: var(--text-body); }
.tbl tbody tr { transition: background var(--transition-fast, 0.15s ease); }
.tbl tbody tr:hover { background: var(--bg-muted); }
.tbl th:first-child, .tbl td:first-child { padding-left: 0; }
.tbl th:last-child, .tbl td:last-child { padding-right: 0; }
@media (prefers-reduced-motion: reduce) {
    .tbl tbody tr { transition: none; }
}

/* ── Table scroll safety net (.tbl-scroll) ──
   A .tbl can be intrinsically wider than its column count × comfortable cell padding suggests
   (dashboard.html's own .tbl-wrap already builds this per-page for the Recent-activity table).
   This shared, shell-level equivalent exists so any FUTURE page that drops a .tbl straight into
   a .card without remembering its own wrapper still gets a horizontally-scrollable table inside
   its own box instead of pushing the page wide — mirrors the /compare overflow-fix lesson
   (content scrolls in its wrapper, the page itself never widens). Purely additive: does not
   rename or touch dashboard.html's existing .tbl-wrap class. */
.tbl-scroll { width: 100%; min-width: 0; overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ════════════════════════════════════════════════════════════════════════
   Global "Report an issue" (dashboard bug reports) — a quiet sidebar entry
   point + modal, present on every dashboard page via _app_shell.html.
   Deliberately NEW, distinct class names throughout ("bugreport-*" /
   "sidebar-report-btn") rather than reusing .manage-overlay/.manage-panel or
   .nav-item:
     - .manage-overlay is wired by static/integrations-catalog.js via
       document.querySelectorAll('.manage-overlay'); sharing that class would
       let that page-specific script double-bind THIS shell-level modal on
       the Integrations page (which also extends this shell).
     - a class sharing the nav anchors' own token would inflate
       _smoke_app_design.py's aria-current-per-anchor count, which counts
       occurrences of that token as an HTML class attribute value (a
       hyphenated variant of it would still be counted).
   The select/textarea inside the modal reuse the existing .app-shell
   .form-field recipe — no new form-input CSS needed. Token-based throughout
   (no bare hex), matching this file's existing discipline.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Sidebar entry point ── */
.sidebar-report-btn { display: flex; align-items: center; gap: 0.7rem; width: 100%; margin-top: 1.1rem; padding: 0.62rem 0.7rem; border: none; border-radius: var(--radius-md); background: none; color: var(--text-tertiary); font: inherit; font-weight: 600; font-size: 0.86rem; text-align: left; cursor: pointer; transition: background var(--transition-fast, 0.15s ease), color var(--transition-fast, 0.15s ease); }
.sidebar-report-btn svg { width: 17px; height: 17px; flex-shrink: 0; opacity: 0.7; }
.sidebar-report-btn:hover { background: var(--bg-muted); color: var(--text-primary); }
.sidebar-report-btn:hover svg { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .sidebar-report-btn { transition: none; } }

/* ── Notice dismiss (×) — first use of a dismiss affordance on .app-notice; scoped to its own
   class so every OTHER .app-notice usage (dashboard_settings.html billing/security/followups)
   stays exactly as it was (2 children: icon + text) — this only applies where the 3rd child is
   actually present. ── */
.bugreport-notice-dismiss { margin-left: auto; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: none; border-radius: var(--radius-sm); background: transparent; color: inherit; opacity: 0.6; cursor: pointer; }
.bugreport-notice-dismiss:hover { opacity: 1; background: rgba(0,0,0,0.06); }
.bugreport-notice-dismiss svg { width: 14px; height: 14px; }

/* ── Modal (mirrors .manage-overlay/.manage-panel's shape, distinct class names — see above) ── */
.bugreport-overlay { position: fixed; inset: 0; z-index: 70; display: flex; align-items: center; justify-content: center; padding: 1.25rem; background: rgba(0,0,0,0.45); }
.bugreport-overlay[hidden] { display: none; }
.bugreport-panel { position: relative; width: min(440px, 100%); max-height: calc(100vh - 2.5rem); overflow-y: auto; background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--radius-xl); box-shadow: var(--shadow-xl); padding: 1.5rem; }
.bugreport-panel:focus { outline: none; }
.bugreport-close { position: absolute; top: 0.9rem; right: 0.9rem; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; border-radius: var(--radius-md); background: transparent; color: var(--text-tertiary); cursor: pointer; }
.bugreport-close:hover { background: var(--bg-muted); color: var(--text-primary); }
.bugreport-close svg { width: 18px; height: 18px; }
.bugreport-title { font-size: 1.2rem; font-weight: 800; letter-spacing: -0.02em; padding-right: 2rem; }
.bugreport-sub { color: var(--text-secondary); font-size: 0.88rem; margin: 0.3rem 0 1.1rem; line-height: 1.5; }
.bugreport-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; margin-top: 0.3rem; flex-wrap: wrap; }
.bugreport-foot-note { font-size: 0.78rem; color: var(--text-tertiary); }
.bugreport-foot-note a { color: var(--text-secondary); text-decoration: underline; }
@media (max-width: 768px) {
    .bugreport-panel { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
    .sidebar-report-btn, .bugreport-notice-dismiss { transition: none; }
}
