  :root{
    --bg: #0a0c0a;
    --surface: #12150f;
    --surface-2: #191d14;
    --border: #262a20;
    --border-soft: #1d2118;
    --lime: #c8ff3d;
    --lime-text: #c8ff3d;
    --lime-dim: #8fcb2a;
    --lime-wash: rgba(var(--lime-rgb),0.08);
    --lime-rgb: 200,255,61;
    --chart-grid: rgba(255,255,255,0.045);
    --text: #edf0e6;
    --text-muted: #9aa190;
    --text-dim: #5b6154;
    --radius: 10px;
    --font-display: 'Space Grotesk', sans-serif;
    --font-body: 'Inter', sans-serif;
    --font-mono: 'IBM Plex Mono', monospace;
  }

  *{box-sizing:border-box; margin:0; padding:0;}

  /* overflow-x:hidden was previously set on BOTH html and body — a known
     browser footgun where setting overflow on both the root and body
     elements at once breaks position:sticky for every descendant on the
     page (the two conflicting scroll-propagation rules confuse which box
     the browser treats as the real scrolling viewport, so sticky elements
     compute their offset against the wrong one and just scroll away with
     the page). Confirmed via isolated reproduction. Left on body only;
     html no longer sets it. This was the actual reason .topbar/.dv-topbar
     never stayed put on scroll, despite the CSS otherwise being correct. */
  body{
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-body);
    display:flex;
    min-height:100vh;
    font-size:14px;
    overflow-x:hidden;
  }

  /* Loading screen: the app stays hidden until script.js has restored the saved panel/theme; only the loader shows meanwhile. */
  html.pd-booting body{ visibility:hidden; }
  #pdBootLoader{
    position:fixed; inset:0; z-index:100000; display:grid; place-items:center; visibility:visible;
    background:radial-gradient(60% 60% at 50% 40%, var(--pd-1b1530) 0%, #0b0b0b 70%); color:#edf0e6;
    font-family:'Inter',system-ui,sans-serif; opacity:1; transition:opacity .35s ease, visibility 0s linear .35s;
  }
  html.pd-boot-light #pdBootLoader{ background:radial-gradient(60% 60% at 50% 40%, #ffffff 0%, #eceaf6 75%); color:#1c1b26; }
  html:not(.pd-booting) #pdBootLoader{ opacity:0; visibility:hidden; pointer-events:none; }
  .pd-boot-card{ display:flex; flex-direction:column; align-items:center; gap:10px; text-align:center; }
  .pd-boot-logo{ position:relative; display:grid; place-items:center; width:92px; height:92px; margin-bottom:8px; }
  .pd-boot-logo img{ width:34px; height:auto; z-index:1; }
  html.pd-boot-light .pd-boot-logo img{ filter:invert(1); }
  .pd-boot-ring{ position:absolute; inset:0; border-radius:50%; border:3px solid rgba(var(--pd-rgb),.2); border-top-color:var(--pd-a855f7); animation:pdBootSpin 1s linear infinite; }
  .pd-boot-name{ font:600 20px/1.2 'Space Grotesk','Inter',sans-serif; letter-spacing:.01em; }
  .pd-boot-sub{ font-size:13px; opacity:.65; }
  .pd-boot-bar{ width:160px; height:3px; margin-top:10px; overflow:hidden; border-radius:99px; background:rgba(var(--pd-rgb),.2); }
  .pd-boot-bar i{ display:block; width:40%; height:100%; border-radius:inherit; background:var(--pd-a855f7); animation:pdBootBar 1.3s ease-in-out infinite; }
  @keyframes pdBootSpin{ to{ transform:rotate(360deg); } }
  @keyframes pdBootBar{ 0%{ transform:translateX(-100%); } 100%{ transform:translateX(260%); } }
  @media (prefers-reduced-motion:reduce){ .pd-boot-ring, .pd-boot-bar i{ animation-duration:3s; } }

  /* ============ SIDEBAR ============ */
  .sidebar{
    width:240px;
    min-width:240px;
    background: var(--surface);
    border-right:1px solid var(--border);
    display:flex;
    flex-direction:column;
    padding:20px 14px;
    overflow:hidden;
    position:fixed;
    top:0;
    left:0;
    height:100vh;
    z-index:170;
    transition: width .15s cubic-bezier(.4,0,.2,1), min-width .15s cubic-bezier(.4,0,.2,1), padding .15s cubic-bezier(.4,0,.2,1), box-shadow .15s cubic-bezier(.4,0,.2,1);
  }
  .sidebar.collapsed{
    width:60px;
    min-width:60px;
    padding:20px 10px;
  }
  /* Everything under the logo row scrolls inside this box, so the logo row stays put and the sections slide out underneath
     its bottom line. It reaches out over the sidebar's side padding (so the active-item bar at the edge isn't clipped). */
  .sidebar-scroll{
    flex:1 1 auto; min-height:0;
    display:flex; flex-direction:column;
    margin:0 -14px; padding:0 14px;
    overflow-x:hidden; overflow-y:auto;
    scrollbar-width:none; -ms-overflow-style:none;
    transition: margin .15s cubic-bezier(.4,0,.2,1), padding .15s cubic-bezier(.4,0,.2,1);
  }
  .sidebar-scroll::-webkit-scrollbar{ display:none; }
  .sidebar.collapsed .sidebar-scroll{ margin:0 -10px; padding:0 10px; }
  .sidebar.collapsed.peek .sidebar-scroll{ margin:0 -14px; padding:0 14px; }
  .sidebar{
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .sidebar::-webkit-scrollbar{ display:none; }

  /* .brand's own height is fixed at 36px (padding + logo) in both
     expanded and collapsed states, so together with the sidebar's own
     20px top padding it always totals 56px — exactly the topbar's own
     height (see .topbar) — and the border line below it (now drawn by
     ::after, not the box's own border-bottom) lines up with the
     topbar's border-bottom regardless of collapse state. The ::after
     line is positioned independently of .brand's padding (via its own
     negative left/right), so it can reach the sidebar's true outer
     edges without .brand's padding having to be hacked to match —
     unlike the old margin/padding trick this replaced, which tied the
     line's position to the same padding used to lay out the logo/name/
     button and made all three fragile to change together. */
  .brand{
    position:relative;
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:6px;
    /* 10px left, not the old 18px -- lines up the logo slot's left edge
       with every nav-item's own icon below it (sidebar's 14px padding +
       nav-item's own 10px padding = 24px inset; brand needs the same
       10px here to land on that same 24px). Vertical padding is 4px, not
       5px, to match .brand-logo-slot's new 28px square height (see its
       own comment) while keeping .brand's total height at the same 36px
       the topbar-alignment comment above depends on (4+28+4=36). */
    padding:4px 10px;
    transition: padding .15s cubic-bezier(.4,0,.2,1);
  }
  .brand::after{
    content:'';
    position:absolute;
    left:-24px;
    right:-24px;
    bottom:0;
    border-bottom:1px solid var(--border);
  }
  .sidebar.collapsed .brand::after{
    left:-16px;
    right:-16px;
  }
  .brand-name{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* The logo mark lives in a fixed-size square slot (28x28) that never resizes
     between expanded and collapsed, so the logo itself never moves or
     changes size when the sidebar toggles — only the space around it
     (the nav labels, the rest of the row) appears or disappears. While
     collapsed, .brand's own padding is symmetric (left==right, top==
     bottom) and the slot is centered in it via justify-content/align-
     items; the collapse toggle button is centered in that exact same
     box independently via inset:0+margin:auto, so the two always land
     on the same point with no shared pixel math to keep in sync. The
     toggle cross-fades with the logo on hover, so it looks like the
     toggle disappears into the logo rather than anything jumping.
     The `.just-toggled` overrides (added briefly by JS right after a
     click) force the end state instantly instead of fading, because
     the mouse is still physically over `.brand` the moment the sidebar
     collapses — without them the stale :hover would keep the toggle
     button showing and the logo hidden until the mouse actually moves. */
  .brand-logo-slot{
    width:28px;
    height:28px;
    flex-shrink:0;
    display:flex;
    align-items:center;
    justify-content:center;
    /* Nudged up and left -- transform, not padding/margin, so it can't
       disturb any of the centering math above (inset:0+margin:auto for
       the collapse button, justify-content for the collapsed rail) or
       the row's own fixed 36px height. */
    transform: translate(-7px, -10px);
  }
  /* Left padding (14px) is deliberately NOT symmetric with the right
     (2px, just enough to keep the slot off the sidebar's own edge) --
     it's chosen so the slot's left edge lands at the exact
     same 24px-from-the-sidebar's-own-edge spot .brand's own padding
     produces when expanded/peeked (10px sidebar padding + 14px here =
     24px, matching the non-collapsed 14+10 -- see .brand's own comment).
     Centering the slot in the narrow rail instead (the previous design)
     put it 8px further left than that, which meant hovering into a peek
     visibly shifted the logo sideways as the row widened -- same
     element, two different resting positions. This trades "centered
     inside the collapsed square" for "never moves at all," which is
     what was actually asked for the second time around. */
  .sidebar.collapsed .brand{
    padding:4px 2px 4px 14px;
  }
  .sidebar.collapsed .brand-logo-img{
    transition: opacity .15s ease;
  }
  .sidebar.collapsed .brand:hover .brand-logo-img{
    opacity:0;
  }
  /* The wordmark fades out where it stands (it used to vanish the instant the rail started closing). It leaves the row's flow
     but is pinned to the exact spot it had beside the logo, so nothing shifts while it fades. */
  .brand-name{ transition: opacity .15s ease; }
  .sidebar.collapsed:not(.peek) .brand-name{ position:absolute; left:44px; opacity:0; pointer-events:none; }
  .sidebar.collapsed .sidebar-collapse-btn{
    position:absolute;
    /* Was inset:0+margin:auto (centers on .brand's own box) -- that only
       matched the logo slot's position while the slot was itself
       centered in .brand's box. Now that the slot sits at a fixed 14px
       left inset instead (see .brand's own comment just above), the
       button is centered explicitly on that same point: left:28px is
       the slot's own horizontal center (14px padding + half its 28px
       width), top:50% is its vertical center (the slot fills .brand's
       full content height, so .brand's own vertical midpoint already
       matches it). The extra translate(-5px,-10px) reproduces the same
       nudge the base .sidebar-collapse-btn rule applies everywhere
       else -- transform here fully replaces that rule's value rather
       than stacking with it, so it has to be repeated in this one. */
    left:28px;
    top:50%;
    transform: translate(-50%, -50%) translate(-7px, -10px);
    opacity:0;
    pointer-events:none;
    transition: opacity .15s ease;
  }
  .sidebar.collapsed .brand:hover .sidebar-collapse-btn{
    opacity:1;
    pointer-events:auto;
  }
  .sidebar.collapsed .brand.just-toggled .brand-logo-img{
    opacity:1 !important;
    transition:none !important;
  }
  .sidebar.collapsed .brand.just-toggled .sidebar-collapse-btn{
    opacity:0 !important;
    pointer-events:none !important;
    transition:none !important;
  }
  /* Isometric cube mark — white + light-gray facets, already built for
     dark surfaces (Glass/Dark), so it shows as-is by default. That's the
     mirror image of the old wordmark (which was dark and needed
     invert(1) to go white on dark themes): this one needs the inversion
     on Light instead, to turn white/gray into black/charcoal for a light
     background. */
  .brand-logo-img{
    height:26px;
    width:auto;
    display:block;
    flex-shrink:0;
  }
  body.theme-light .brand-logo-img{
    filter: invert(1);
  }
  .brand-mark{
    width:26px; height:26px;
    border-radius:7px;
    background: var(--lime);
    position:relative;
    flex-shrink:0;
  }
  .brand-mark::after{
    content:'';
    position:absolute;
    top:50%; left:50%;
    width:6px; height:6px;
    background: var(--bg);
    border-radius:50%;
    transform:translate(-50%,-50%);
    box-shadow: 0 0 0 2px var(--bg);
  }
  .brand-name{
    font-family: var(--font-display);
    font-weight:800;
    /* Was 14.5px/-0.01em -- "Publishers Data" no longer fit the space
       actually left for it once the logo slot, its two gaps, and the
       collapse button are accounted for in the 216px rail, and got
       silently ellipsis-ed instead of showing in full. Sized down just
       enough to fit the whole wordmark at that width. */
    font-size:13px;
    letter-spacing:-0.02em;
    color: var(--text);
    transform: translate(-7px, -10px);
  }

  /* Sidebar search — sits right under the logo divider, above the
     Dashboard nav item. A real always-visible bar in the expanded rail;
     shrinks to just its icon (centered, matching every other collapsed
     control's look) while the rail itself is collapsed and not peeked
     open, since a 60px rail has no room for a text input. No live search
     wired up — this is the input shell only. */
  .sidebar-search{
    display:flex; align-items:center; gap:8px;
    height:34px;
    margin:16px 0 4px;
    padding:0 10px;
    border-radius:8px;
    border:1px solid var(--border);
    background: var(--surface-2);
    flex-shrink:0;
    width:100%; box-sizing:border-box;
    transition: padding .18s ease, width .18s cubic-bezier(.4,0,.2,1), margin .18s ease, border-color .18s ease, background-color .18s ease;
  }
  .sidebar-search-icon{ width:15px; height:15px; stroke: var(--text-dim); fill:none; stroke-width:1.8; flex-shrink:0; }
  .sidebar-search-input{
    flex:1; min-width:0;
    border:none; background:none; outline:none;
    color: var(--text);
    font-family: var(--font-body);
    font-size:12.5px;
    transition: opacity .15s ease, visibility 0s linear 0s;
  }
  .sidebar-search-input::placeholder{ color: var(--text-dim); }
  /* Collapsed rail, resting (not peeked): just the bare icon, no
     border/background box around it — every other collapsed sidebar
     control (nav icons) is bare too, nothing pill- or circle-shaped
     until you're actually hovering to peek. Input hidden entirely
     rather than just visually — a 0-width text input would still be
     focusable/tabbable otherwise. */
  .sidebar.collapsed:not(.peek) .sidebar-search{
    width:100%;
    /* Icon stays at the left of the row (not centered) and the box only drops its border/background, so closing never
       slides the icon sideways: 11.5px puts it on the middle of the 40px resting rail, right where it already sits. */
    padding:0 0 0 11.5px;
    border-color:transparent;
    background:none;
  }
  .sidebar.collapsed:not(.peek) .sidebar-search-input{ flex:0 0 0; width:0; opacity:0; visibility:hidden; pointer-events:none; transition: opacity .1s ease, visibility 0s linear .1s; }

  .nav-group{
    margin-top:26px;
  }
  .nav-group + .nav-group{
    margin-top:18px;
    padding-top:18px;
    /* var(--border), not the softer --border-soft used for the footer's
       single line above the account card — one faint line reads fine on
       its own, but repeated every ~4 nav items down the rail it needs
       more contrast to actually register as a separator rather than
       just looking like a rendering artifact. */
    border-top:1px solid var(--border);
  }
  .nav-item + .nav-item{ margin-top:2px; }
  .nav-label{
    font-family: var(--font-mono);
    font-size:10px;
    letter-spacing:0.12em;
    text-transform:uppercase;
    color: var(--text-dim);
    padding: 0 10px 8px 10px;
    max-height:28px;
    white-space:nowrap;
    overflow:hidden;
    transition: opacity .16s ease, max-height .18s cubic-bezier(.4,0,.2,1), padding .18s cubic-bezier(.4,0,.2,1);
  }
  /* Resting collapsed rail: the section titles are invisible anyway, so their rows fold away and the icons close up. */
  .sidebar.collapsed:not(.peek) .nav-label{ max-height:0; padding-bottom:0; }

  .nav-item{
    display:flex;
    align-items:center;
    gap:6px;
    padding:8px 10px;
    border-radius:8px;
    color: var(--text-muted);
    cursor:pointer;
    font-size:13px;
    font-weight:400;
    transition: background .12s ease, color .12s ease, padding-left .15s cubic-bezier(.4,0,.2,1), padding-right .15s cubic-bezier(.4,0,.2,1);
    position:relative;
    user-select:none;
  }
  .nav-item:hover{
    background: var(--surface-2);
    color: var(--text);
  }
  .nav-item.active{
    background: var(--lime-wash);
    color: var(--lime-text);
  }
  .nav-item.active::before{
    content:'';
    position:absolute;
    left:-14px;
    top:50%; transform:translateY(-50%);
    width:3px; height:16px;
    background: var(--lime);
    border-radius:2px;
    transition: left .08s cubic-bezier(.4,0,.2,1);
  }
  .nav-icon{
    width:16px; height:16px;
    flex-shrink:0;
    display:flex; align-items:center; justify-content:center;
  }
  .nav-icon svg{ width:16px; height:16px; stroke: currentColor; fill:none; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; }

  .nav-badge{
    font-family: var(--font-mono);
    font-size:9.5px;
    color: var(--bg);
    background: var(--lime);
    padding:1px 6px;
    border-radius:20px;
    font-weight:600;
    transition: opacity .16s ease;
  }
  .nav-badge{ flex:0 0 auto; white-space:nowrap; max-width:80px; overflow:hidden; transition: opacity .16s ease, max-width .18s cubic-bezier(.4,0,.2,1), padding .18s ease; }
  .sidebar.collapsed:not(.peek) .nav-badge{ max-width:0; padding-left:0; padding-right:0; border-width:0; }
  .nav-badge.soon{ background: var(--surface-2); color: var(--text-dim); border:1px solid var(--border); }
  .nav-badge.mcp{ background:#ef4444; color:#fff; }
  .smart-dashboard-beta{ display:inline-flex; align-items:center; justify-content:center; margin-left:5px; padding:1px 5px; border-radius:999px; background:rgba(var(--pd-rgb),.18); color:var(--pd-b878ff); font-family:var(--font-mono); font-size:8px; font-weight:700; letter-spacing:.06em; line-height:1.35; vertical-align:middle; }
  .nav-badge.smart-dashboard-beta{ margin-left:0; }
  .dash-grid.smart-dashboards-only > .dash-card:not(.traffic-test-dash-card):not(.user-dash-card){ display:none !important; }
  /* Greyed-out nav items for not-yet-shipped features (Analytics,
     Automation Rules, Autonomous Ads) — still clickable through to their
     "coming soon" panel, just visually dimmed and without a submenu that
     would promise sub-pages that don't exist yet. */
  .nav-item.disabled{ opacity:0.45; }
  .nav-item.disabled:hover{ opacity:0.7; }

  .nav-label-text{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; transition: opacity .16s ease; }

  .nav-chev{
    flex-shrink:0;
    color: var(--text-dim);
    font-size:16px;
    line-height:1;
    padding:2px 4px;
    border-radius:5px;
    transition: transform .15s ease, background .12s ease, color .12s ease, opacity .16s ease;
  }
  /* Thin, simple arrow: the "›" character is hidden and a 1.4px-line chevron is drawn instead; it turns down when the section opens. */
  .nav-chev{ position:relative; width:14px; height:14px; padding:0; font-size:0; color:var(--text-dim); }
  .nav-chev::after{
    content:'';
    position:absolute;
    left:50%; top:50%;
    width:5px; height:5px;
    border-right:1.4px solid currentColor;
    border-bottom:1.4px solid currentColor;
    border-radius:0 0 1px 0;
    transform: translate(-65%, -50%) rotate(-45deg);
  }
  .nav-chev:hover{ background:none; }
  .nav-item:hover .nav-chev{ color: var(--text); }
  .nav-item.active .nav-chev{ color: var(--lime-text); }
  .nav-chev:hover{ background:none; }
  .nav-item.nav-sub-open .nav-chev{ transform: rotate(90deg); }

  .nav-submenu{
    position:absolute;
    top:0;
    left:calc(100% + 6px);
    width:180px;
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:10px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.4);
    padding:6px;
    opacity:0;
    transform: translateX(-6px);
    pointer-events:none;
    transition: opacity .12s ease, transform .12s ease;
    z-index:40;
  }
  .nav-item.nav-sub-open .nav-submenu{
    opacity:1;
    transform: translateX(0);
    pointer-events:auto;
  }
  .nav-sub-item{
    padding:8px 10px;
    border-radius:7px;
    font-size:12.5px;
    font-weight:500;
    color: var(--text);
    cursor:pointer;
  }
  .nav-sub-item:hover{ background: var(--surface-2); color: var(--lime-text); }

  .nav-help-group.open .nav-help-chev{ transform: rotate(90deg); }

  .nav-help-content{
    max-height:0;
    overflow:hidden;
    transition: max-height .2s ease;
    margin-left:18px;
    padding-left:12px;
    border-left:1px solid var(--border);
    display:flex;
    flex-direction:column;
    gap:1px;
  }
  .nav-help-group.open .nav-help-content{
    max-height:300px;
    margin-top:2px;
  }
  .nav-help-item{
    display:flex;
    align-items:center;
    padding:7px 10px;
    font-size:12.5px;
    color: var(--text-muted);
    cursor:pointer;
    border-radius:7px;
  }
  .nav-help-item:hover{ background: var(--surface-2); color: var(--text); }

  .sidebar.collapsed .nav-help-content,
  .sidebar.collapsed .nav-help-chev{ display:none; }
  /* (Help group only; the platform groups are handled below.) Kept in layout, folded shut, so it unfolds instead of popping. */
  .sidebar.collapsed .nav-help-group:not(.nav-platform-group) .nav-help-content{ display:flex; max-height:0; margin-top:0; }
  .sidebar.collapsed .nav-help-group:not(.nav-platform-group) .nav-help-chev{ display:block; }
  .sidebar.collapsed.peek .nav-help-group.open:not(.nav-platform-group) .nav-help-content{ max-height:300px; margin-top:2px; }

  /* Analytics platform groups (Publishing / Advertising / Direct Sales): same inline dropdown as Help, with the vertical line. */
  /* Smooth open/close: animate the row height from 0fr to 1fr (exact content height, no max-height guessing). */
  .nav-platform-group .nav-help-content{ display:grid; grid-template-rows:0fr; max-height:none; margin-top:0; transition: grid-template-rows .24s cubic-bezier(.4,0,.2,1), margin-top .24s cubic-bezier(.4,0,.2,1); }
  .nav-platform-group.open .nav-help-content{ grid-template-rows:1fr; max-height:none; margin-top:2px; }
  .nav-platform-inner{ min-height:0; overflow:hidden; display:flex; flex-direction:column; gap:1px; opacity:0; transition: opacity .2s ease; }
  .nav-platform-group.open .nav-platform-inner{ opacity:1; }
  .sidebar.collapsed.peek .nav-platform-group .nav-help-content{ display:grid; }
  /* Never display:none the groups while the rail is collapsed (that made them pop in/out, shoving the rows below, when the
     sidebar opened): they stay in the layout, folded shut, and unfold with the same animation when the sidebar opens. */
  .sidebar.collapsed .nav-platform-group .nav-help-content{ display:grid; }
  .sidebar.collapsed:not(.peek) .nav-platform-group .nav-help-content{ grid-template-rows:0fr; margin-top:0; }
  .sidebar.collapsed .nav-platform-group .nav-help-chev{ display:block; }
  .sidebar.collapsed:not(.peek) .nav-platform-group .nav-platform-inner{ opacity:0; }
  .nav-platform-toggle{ cursor:pointer; }
  /* Same 2px rhythm as the plain .nav-item rows: between the groups, and between the last group and the next item. */
  .nav-platform-group + .nav-platform-group,
  .nav-platform-group + .nav-item{ margin-top:2px; }
  .nav-platform-group.open .nav-help-content{ margin-bottom:2px; }
  .nav-platform-group.open .nav-help-chev{ transform: rotate(90deg); }
  .nav-platform-group .nav-help-item{ justify-content:space-between; gap:8px; }
  .nav-platform-group .nav-help-item.active{ background: var(--surface-2); color: var(--lime-text); }
  .nav-platform-group .nav-help-item.is-disabled{ opacity:.5; cursor:default; pointer-events:none; }
  .nav-platform-group .nav-help-item .nav-badge{ flex:0 0 auto; }
  .nav-platform-group .nav-help-item > span:first-child{ white-space:nowrap; flex:0 0 auto; }
  body.theme-light .nav-platform-group .nav-help-item.active{ background:#f3f0ff; color:var(--pd-6d28d9); }

  .sidebar-footer{
    margin-top:auto;
    padding-top:16px;
    border-top:1px solid var(--border-soft);
  }

  /* ---- Collapsed sidebar ---- */
  /* .nav-submenu (an absolutely-positioned flyout, already opacity-faded
     by its own .nav-sub-open rule) and .brand-name (its own dedicated
     crossfade with the collapse toggle, see .brand's comment) still hard-
     hide via display:none — display doesn't affect either one's layout
     since the submenu is out of flow and .brand-name has its own
     mechanism already. .collapse-label matches no element in the current
     markup; harmless leftover, left as-is. */
  .sidebar.collapsed .nav-submenu,
  .sidebar.collapsed .collapse-label{
    display:none;
  }
  /* Nav item labels/chevrons/badges/account info: opacity-only, still
     laid out (not display:none), so hovering the collapsed rail into a
     peek never triggers a hard, un-transitionable content reflow. Before
     this, revealing them via display:none -> revert meant the row's full
     content (icon + label) snapped into its expanded layout the instant
     .peek was added, one frame ahead of the rail's own .08s width
     transition -- the icon visibly jumped to its new position first, then
     the rail caught up and widened around it. Leaving the peek, the same
     display flip made the label vanish instantly rather than closing
     with the rail. Keeping these in flow the whole time (nav-label-text's
     flex:1;min-width:0 already lets it shrink to ~0 width in the narrow
     resting rail) means only their opacity animates, in step with the
     rail's own width transition, and the icon's own position — driven
     purely by .nav-item's padding, unified below — never moves at all. */
  .sidebar.collapsed .nav-label,
  .sidebar.collapsed .nav-label-text,
  .sidebar.collapsed .nav-chev,
  .sidebar.collapsed .nav-badge,
  .sidebar.collapsed .account-info,
  .sidebar.collapsed .account-upgrade{
    opacity:0;
    pointer-events:none;
  }
  /* Same padding whether resting-collapsed or peeked (no separate peek
     override anymore) — the icon's offset from .nav-item's own edge is
     now identical in both states, so nothing about its position depends
     on which one is active. 12px left / 8px right centers the 16px icon
     in the narrow 60px rail's ~20px content box (see .sidebar.collapsed's
     own padding above); in the wider peeked/expanded row it just reads
     as normal icon-to-edge spacing. */
  .sidebar.collapsed .nav-item{ padding:8px 8px 8px 12px; }
  .sidebar.collapsed .nav-item.active::before{ left:-10px; }

  /* ---- Hover-to-peek ----
     While collapsed, hovering the rail temporarily re-expands it as a
     floating overlay (a JS-toggled .peek class, not :hover directly, so
     leaving can be debounced without flicker). Only .sidebar itself grows —
     body.sidebar-collapsed stays put, so .main's margin never shifts and the
     peeked rail simply floats over the page content, collapsing back the
     moment the pointer leaves. Every "hidden while collapsed" rule above
     needs an explicit peek override since a single class can't un-match its
     own earlier selector. */
  .sidebar.collapsed.peek{
    width:240px;
    min-width:240px;
    padding:20px 14px;
    /* Own transition, not the base .sidebar's .08s -- hover-to-peek
       opening specifically wants .1s, independent of the click-to-
       collapse/expand toggle's speed. Only applies while opening (this
       rule is what's active); closing (peek removed) falls back to the
       base .sidebar rule's .08s, matching the toggle button. */
    transition: width .18s cubic-bezier(.4,0,.2,1), min-width .18s cubic-bezier(.4,0,.2,1), padding .18s cubic-bezier(.4,0,.2,1), box-shadow .18s cubic-bezier(.4,0,.2,1);
    /* Above .chartb-overlay (420) on purpose — peeking is meant to float
       fully over whatever's on screen, including a full-screen surface
       like the chart builder. At the base z-index:50 this used to sit
       *under* the builder overlay instead, so the wider peeked rail's
       labels only showed wherever they happened to fall in the sliver the
       overlay didn't cover — a half-visible, truncated-looking sidebar
       bleeding through rather than a clean floating one. Still below
       .auth-screen (500) and the chart tooltip layer (450). */
    z-index:430;
  }
  .sidebar.collapsed.peek .brand-name,
  .sidebar.collapsed.peek .nav-submenu,
  .sidebar.collapsed.peek .collapse-label,
  .sidebar.collapsed.peek .nav-help-content,
  .sidebar.collapsed.peek .nav-help-chev{
    display:revert;
  }
  .sidebar.collapsed.peek .nav-label,
  .sidebar.collapsed.peek .nav-label-text,
  .sidebar.collapsed.peek .nav-chev,
  .sidebar.collapsed.peek .nav-badge,
  .sidebar.collapsed.peek .account-info,
  .sidebar.collapsed.peek .account-upgrade{
    opacity:1;
    pointer-events:auto;
  }
  .sidebar.collapsed.peek .brand{ padding:4px 10px; justify-content:space-between; }
  .sidebar.collapsed.peek .brand::after{ left:-24px; right:-24px; }
  .sidebar.collapsed.peek .brand:hover .brand-logo-img{ opacity:1; }
  .sidebar.collapsed.peek .sidebar-collapse-btn{
    position:static;
    inset:auto;
    margin:0;
    opacity:1;
    pointer-events:auto;
    /* Same translateY(-10px) as the expanded rail's button (base rule), so
       it sits at the identical spot. Must be restated here: the collapsed
       rule's centering transform would otherwise still apply in peek. */
    transform: translateY(-10px);
  }
  .sidebar.collapsed.peek .nav-item.active::before{ left:-14px; }
  /* JS-added the instant .peek comes off (see the mouseleave handler's
     own comment) -- kills the collapse button's opacity with no fade,
     before its position has a chance to render at its new (centered-
     over-the-logo) spot while still visibly opaque from peek. */
  .sidebar.collapsed.peek-closing .sidebar-collapse-btn{
    opacity:0 !important;
    transition:none !important;
  }

  /* Expanded rail: hidden at rest, same "only on hover" treatment the
     collapsed rail's own version already had (see .sidebar.collapsed
     .sidebar-collapse-btn above) -- just fading opacity, not the
     collapsed version's absolute-position-over-the-logo trick, since
     there's no logo to hide behind here. */
  .sidebar:not(.collapsed) .sidebar-collapse-btn{
    opacity:1;
    pointer-events:auto;
  }
  .sidebar:not(.collapsed) .brand:hover .sidebar-collapse-btn{
    opacity:1;
    pointer-events:auto;
  }
  .sidebar-collapse-btn{
    display:flex;
    align-items:center;
    justify-content:center;
    width:22px;
    height:22px;
    flex-shrink:0;
    border-radius:6px;
    color: var(--text-dim);
    cursor:pointer;
    transition: background .12s ease, color .12s ease, opacity .15s ease;
    /* Vertical-only nudge, matching the logo/wordmark's own -10px so
       everything in the row stays level -- NOT their -7px horizontal
       shift too: in this normal-flex-item context (expanded/peeked),
       the button sits at the row's opposite end from the logo, so
       shifting it left as well only pulled it out of line with where
       the rest of the row's controls actually are, for no reason tied
       to the logo at all. The horizontal shift only makes sense for the
       *collapsed* state below, where this same button is absolutely
       positioned to sit centered directly on top of the logo. */
    transform: translateY(-10px);
  }
  .sidebar-collapse-btn:hover{ background: var(--surface-2); color: var(--text); }
  .sidebar-collapse-btn svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.6; flex-shrink:0; }
  .sidebar.collapsed .sidebar-collapse-btn svg{ transform: rotate(180deg); }
  /* The arrow only ever changes direction with the state; it must never visibly spin when the rail peeks open or closes. */
  .sidebar-collapse-btn svg{ transition:none !important; }
  /* Peek visually looks exactly like the expanded rail (same width,
     same padding, same content) -- the icon should point the same way
     it does when genuinely expanded ("click to collapse"), not the
     collapsed rail's own "click to expand" rotation. .peek still
     carries .collapsed (it's the same sidebar, just temporarily
     widened), so without this override the icon showed backwards the
     entire time a peek was open. */
  .sidebar.collapsed.peek .sidebar-collapse-btn svg{ transform: rotate(0deg); }

  .account-card{
    display:flex;
    align-items:center;
    gap:10px;
    padding:8px;
    border-radius:var(--radius);
    cursor:pointer;
    transition: background .12s ease;
  }
  .account-card:hover{ background: var(--surface-2); }
  .account-avatar{
    width:30px;
    height:30px;
    min-width:30px;
    border-radius:50%;
    background: var(--lime-wash);
    color: var(--lime-text);
    display:flex;
    align-items:center;
    justify-content:center;
    font-family: var(--font-display);
    font-weight:700;
    font-size:12px;
  }
  .account-info{ flex:1; min-width:0; transition: opacity .16s ease; }
  .account-name{
    font-size:12.5px;
    font-weight:600;
    color: var(--text);
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
  }
  .account-tier{
    font-size:10.5px;
    color: var(--text-dim);
    white-space:nowrap;
  }
  .account-upgrade{
    display:flex;
    align-items:center;
    justify-content:center;
    width:26px;
    height:26px;
    color: var(--bg);
    background: var(--lime);
    border-radius:50%;
    text-decoration:none;
    flex-shrink:0;
    transition: background .12s ease, opacity .16s ease;
  }
  .account-upgrade svg{ width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:2.2; }
  .account-upgrade:hover{ background: var(--lime-dim); }

  /* ============ MAIN ============ */
  .main{
    flex:1;
    min-width:0;
    display:flex;
    flex-direction:column;
    margin-left:240px;
    margin-right:0;
    /* margin-right follows the side chat's own slide (same time and curve), so the page narrows as the chat arrives instead of snapping narrow first and leaving a gap */
    transition: margin-left .08s cubic-bezier(.4,0,.2,1), margin-right .26s cubic-bezier(.2,.7,.2,1);
  }
  body.sidebar-collapsed .main{ margin-left:60px; }
  body.ai-drawer-open .main{ margin-right:400px; }

  .topbar{
    height:56px;
    border-bottom:1px solid var(--border);
    display:flex;
    align-items:center;
    justify-content:space-between;
    padding:0 24px;
    flex-shrink:0;
    position:sticky;
    top:0;
    background: var(--bg);
    z-index:160;
  }

  .topbar-left{
    display:flex; align-items:center; gap:8px;
    font-size:13px; color: var(--text-muted);
    min-width:0; overflow:hidden;
  }
  .topbar-left .crumb-current{
    color:var(--text); font-weight:600;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0;
  }
  /* Root crumb and the "last page" crumb (below) are the only clickable
     segments. The current-page crumb next to them is deliberately left
     plain text: you're already on that page, so a link back to itself
     would be a no-op click. */
  .topbar-left .crumb-home{
    cursor:pointer;
  }
  .topbar-left .crumb-home:hover{
    color: var(--text);
    text-decoration: underline;
  }
  /* Shows whatever panel you were on right before the current one --
     e.g. "Publishers Data / KDP Reports / Amazon Ads" -- so clicking it
     acts as a page-aware "Back" without a dedicated button anywhere.
     Shown/hidden and given its label from showPanel()'s own panel-
     history tracking in script.js, not from CSS. A <button>, not a
     <span> like the other crumbs, since it needs real click/keyboard
     semantics -- reset to look like the plain text crumbs around it. */
  .topbar-left .crumb-prev{
    font: inherit; color: inherit; background: none; border: none; padding: 0;
    cursor: pointer; white-space: nowrap;
  }
  .topbar-left .crumb-prev:hover{
    color: var(--text);
    text-decoration: underline;
  }

  .topbar-right{
    display:flex; align-items:center; gap:7px;
    flex-shrink:0;
  }

  /* Empty on every page except Amazon Ads. Once you've scrolled past that
     page's own marketplace/date-range/campaign-type row, initAmzTopbarDock
     (see the Amazon Ads JS section) moves that actual row of pills in
     here — not a copy of it, the same elements with the same click
     handlers — so it appears inside the app's already-pinned top bar
     instead of you having to scroll back up to reach it. Hidden by
     default so it takes up no space the rest of the time; .docked is
     only ever added while it holds that row. */
  .topbar-amz-filters{ display:none; align-items:center; gap:10px; }
  .topbar-amz-filters.docked{ display:flex; }

  /* Day / night button: one plain click toggles; it shows the mode you are in (yellow crescent moon at night, sun by day). */
  .theme-toggle svg{ width:13px; height:13px; }
  .theme-toggle .tt-sun{ display:none; stroke:#b45309; stroke-width:2; }
  .theme-toggle .tt-moon{ fill:none; stroke:#facc15; stroke-width:1.4; }
  body.theme-light .theme-toggle .tt-sun{ display:block; }
  body.theme-light .theme-toggle .tt-moon{ display:none; }

  /* Collapsed to just its sparkle icon at rest, expanding to the full
     "Ask Sphere" pill on hover — same icon-only-until-hovered mechanism
     as the Create Campaign button (.collapsible-btn), reimplemented here
     rather than shared with it since this keeps its own neutral
     surface/border colors instead of that button's lime fill. */
  .ask-osp{
    position:relative;
    display:flex; align-items:center; justify-content:center; gap:0;
    background: var(--surface-2);
    border:1px solid var(--border);
    width:28px; height:28px; padding:0;
    border-radius:50%;
    font-size:12.5px;
    color: var(--text);
    cursor:pointer;
    transition: border-color .15s ease;
  }
  .ask-osp svg{ flex-shrink:0; }
  /* Real footage now (a looping animated PNG — see Video/sphere_orb.apng —
     of an actual glass sphere render, black background keyed out via a
     fixed circular alpha mask rather than color-keying: the sphere's own
     interior shading dips close to black in places, so keying by color
     was punching holes in the sphere itself; masking by the sphere's
     known, fixed circular bounds instead sidesteps that entirely).
     Replaces the old pure-CSS gradient/pseudo-element version. object-fit
     + border-radius crop it to the button's circle exactly like before. */
  .ask-osp-orb-wrap{
    flex-shrink:0;
    width:26px; height:26px; aspect-ratio:1/1; border-radius:50%;
    overflow:hidden;
    display:block;
    box-shadow: 0 0 6px rgba(var(--lime-rgb),0.5);
  }
  .ask-osp-orb{
    width:100%; height:100%;
    object-fit:contain;
    object-position:center;
    transform: scale(2.4);
    transform-origin:center;
  }
  .ask-osp:hover{ border-color: var(--lime-dim); }
  /* "AI" tag on the orb itself, instead of a separate label that used to
     slide out on hover — small enough to sit on the button without
     covering the sphere, dark-on-lime like the Sphere nav item's badge
     so it stays legible over the orb's bright gradient. */
  .ask-osp-badge{
    position:absolute;
    bottom:-2px; right:-2px;
    font-family: var(--font-mono);
    font-size:7px;
    font-weight:700;
    letter-spacing:0.02em;
    color: var(--bg);
    background: var(--lime);
    padding:1px 3px;
    border-radius:6px;
    border:1.5px solid var(--surface);
    line-height:1;
    pointer-events:none;
  }
  .ask-osp .dot{
    width:6px; height:6px; border-radius:50%;
    background: var(--lime);
    box-shadow: 0 0 6px var(--lime);
  }

  .avatar{
    width:28px; height:28px;
    border-radius:50%;
    background: var(--surface-2);
    border:1px solid var(--border);
    display:flex; align-items:center; justify-content:center;
    font-family: var(--font-mono);
    font-size:11px;
    color: var(--text-muted);
    cursor:pointer;
    transition: border-color .12s ease;
  }
  .avatar:hover{ border-color: var(--lime-dim); }

  /* ---- Profile dropdown ---- */
  .profile-menu-wrap{ position:relative; }
  .profile-dropdown{
    position:absolute;
    top:38px; right:0;
    width:200px;
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:12px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.4);
    padding:6px;
    opacity:0;
    transform: translateY(-6px);
    pointer-events:none;
    transition: opacity .15s ease, transform .15s ease;
    z-index:50;
  }
  .profile-dropdown.open{
    opacity:1;
    transform: translateY(0);
    pointer-events:auto;
  }
  .pd-item{
    display:flex; align-items:center; justify-content:space-between;
    padding:9px 10px;
    border-radius:8px;
    font-size:13px;
    color: var(--text);
    cursor:pointer;
    position:relative;
  }
  .pd-item:hover{ background: var(--surface-2); }
  .pd-item .pd-chev{ color: var(--text-dim); font-size:12px; }
  .pd-divider{ height:1px; background: var(--border); margin:6px 4px; }
  .pd-item.pd-logout{ color:#ff6b5e; }
  .pd-item.pd-logout:hover{ background: rgba(255,107,94,0.1); }

  .pd-submenu{
    position:absolute;
    top:0; right:calc(100% + 6px); left:auto;
    width:190px;
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:12px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.4);
    padding:6px;
    opacity:0;
    transform: translateX(6px);
    pointer-events:none;
    transition: opacity .12s ease, transform .12s ease;
  }
  .pd-item.pd-has-sub:hover > .pd-submenu,
  .pd-submenu.pd-force-open{
    opacity:1;
    transform: translateX(0);
    pointer-events:auto;
  }

  .theme-option{
    display:flex; align-items:center; gap:10px;
    padding:8px 10px;
    border-radius:8px;
    font-size:13px;
    cursor:pointer;
  }
  .theme-option:hover{ background: var(--surface-2); }
  .theme-swatch{
    width:18px; height:18px;
    border-radius:50%;
    flex-shrink:0;
    border:1px solid var(--border);
  }
  .theme-swatch.swatch-dark{ background: #0a0c0a; }
  .theme-swatch.swatch-light{ background: #f5f6f2; }

  .theme-option .tick{ margin-left:auto; color: var(--lime-text); font-size:12px; opacity:0; }
  .theme-option.active .tick{ opacity:1; }

  /* ---- Light theme ---- */
  body.theme-light{
    --bg: #FCFBF8;
    --surface: #ffffff;
    --surface-2: #f3f0ff;
    --border: #dde1d6;
    --border-soft: #e6e9e0;
    --text: #14170f;
    --text-muted: #4a5040;
    --text-dim: #6e7863;
    /* Same purple recolor as the Glass theme (see its comment) — every
       lime consumer (buttons, badges, checkmarks, chart lines, borders,
       --lime-wash since it's computed from --lime-rgb) flips to purple
       here too, sitewide, instead of just in Glass. */
    --lime-rgb: 81,80,246;
    --lime: var(--pd-af5aff);
    --lime-text: var(--pd-af5aff);
    --lime-dim: var(--pd-8846c6);
    /* --lime-wash is computed at :root, so without this line it kept :root's
       lime-green rgb here -- redefine it so it follows the purple. */
    --lime-wash: rgba(var(--pd-rgb),0.12);
    --chart-grid: rgba(20,23,15,0.06);
  }
  /* Keep the selected/clicked navigation treatment explicitly purple in White
     mode. This also covers the short pointer-down state before showPanel has
     moved the persistent .active class to the new destination. */
  body.theme-light .nav-item.active{
    background:rgba(var(--pd-rgb),.14);
    color:var(--pd-6d28d9);
  }
  body.theme-light .nav-item:hover:not(.active){
    background:#eceee9;
    color:var(--text);
  }
  body.theme-light .nav-item:active{ background:rgba(var(--pd-rgb),.18); }
  body.theme-light .nav-item.active::before{ background:var(--pd-af5aff); }
  body.theme-light .nav-item.active .nav-chev{
    color:var(--pd-6d28d9);
  }
  /* Any lime-background pill/button uses --bg for its text color, which is
     nearly white — fine on dark bg, invisible on white. Force it to black here. */
  body.theme-light .nav-badge,
  body.theme-light .btn-primary-lime,
  body.theme-light .toggle-opt.active,
  body.theme-light .plan-tier-badge,
  body.theme-light .perm-save{
    color: var(--text) !important;
  }
  body.theme-light .ai-send-btn svg,
  body.theme-light .ai-expand-send svg{
    stroke: var(--text) !important;
  }

  /* GROUP A — big structural chrome: sidebar, topbar, drawers, modals.
     Large, persistent surfaces that sit directly over the aurora bg, so
     they carry the strongest live blur + rim-light of the whole site.
     There are only a handful of these on screen at once regardless of
     page, so the heavier blur radius here is cheap in practice. */

  /* The rest of the Amazon Ads + KDP Report + Dashboard section's big
     cards -- Performance/Royalties/Revenue charts, Campaigns table, Top
     Books, custom-dashboard tiles, the alerts banner, and the same
     components in the Campaign/Ad Group drill-down pages -- drop their
     real per-card blur too, same cheap trick as the KPI cards above
     (tinted gradient + bright top-rim border + shadow only). Scoped by
     id (higher specificity than Group B's plain `.dv-card`/`.dash-card`
     above, so it wins there without touching those classes anywhere
     else) so Billing/Integrations/Plans/etc. keep their real blur
     untouched. Book Analytics/Book Detail get the same treatment too,
     but via their own pre-existing id-scoped rule further down (see its
     comment) rather than here. */

  /* Floating overlay menus need to stay legible over whatever is behind them.
     They're only ever one at a time on-screen (open on click, closed on
     click-away), so a live blur here is still cheap regardless of how
     busy the rest of the page is. */

  /* The KPI info tip is a different story from the menus above — every
     KPI card on the page has one, so it doesn't get the live blur: a
     near-opaque flat fill reads just as legible without paying for a
     blur layer on every single card, most of which never get hovered
     in a given visit. */

  /* Already ~85% opaque lime — a blur underneath added negligible visual
     value here, so it's dropped entirely rather than shrunk. */

  /* Active nav item: a soft, muted lime highlight (a tinted pill, same
     idea as a reference app's pastel-blue active row) rather than a
     bold opaque fill — same lime accent color as everywhere else in the
     app, just applied as a gentle wash instead of a solid button-style
     background, so the icon/text/left accent bar all stay legible lime
     against it instead of flipping to dark-on-lime. */

  /* ---- Dark mode, liquid glass ----
     Same frosted-glass recipe as the Liquid Glass theme above (live blur,
     translucent fill, brighter top edge), applied to plain Dark mode too —
     but with the lime/blue aurora tint stripped out. Every tint here is
     white-on-near-black instead, so the glow and the glass itself stay
     strictly black, white, and gray. Scoped to "dark and not already
     Glass" so it never doubles up with (or fights) the colorful theme
     above; Light mode is unaffected either way.

     The base palette itself was also never neutral — --bg/--surface/
     --border etc. are a warm, faintly olive near-black, and --lime is a
     genuine green accent. Redefining those tokens right here (custom
     properties inherit) flips every one of their consumers at once —
     nav highlights, badges, borders, KPI up-deltas, toggle switches,
     buttons — to true grayscale, instead of hunting each consumer
     individually. Only Dark mode's copy of the tokens changes; Light
     and the colorful Liquid Glass theme keep the originals. */
  body:not(.theme-light):not(.theme-glass){
    --bg: #0a0a0a;
    --surface: #141414;
    --surface-2: #1c1c1c;
    --border: #2a2a2a;
    --border-soft: #202020;
    --lime-rgb: 81,80,246;
    --lime: var(--pd-af5aff);
    --lime-text: var(--pd-af5aff);
    --lime-dim: var(--pd-8846c6);
    --lime-wash: rgba(var(--pd-rgb),0.12);
    --text: #f0f0f0;
    --text-muted: #a3a3a3;
    --text-dim: #6a6a6a;
    background: var(--bg);
  }
  body:not(.theme-light):not(.theme-glass) .sidebar,
  body:not(.theme-light):not(.theme-glass) .topbar,
  body:not(.theme-light):not(.theme-glass) .ai-drawer,
  body:not(.theme-light):not(.theme-glass) .ai-drawer-footer,
  body:not(.theme-light):not(.theme-glass) .account-card,
  body:not(.theme-light):not(.theme-glass) .perm-modal,
  body:not(.theme-light):not(.theme-glass) .ai-expand-window,
  body:not(.theme-light):not(.theme-glass) .ai-expand-sidebar,
  body:not(.theme-light):not(.theme-glass) .nav-help-toggle,
  body:not(.theme-light):not(.theme-glass) .ai-input-box,
  body:not(.theme-light):not(.theme-glass) .na-input-box,
  body:not(.theme-light):not(.theme-glass) .int-search{
    background: rgba(255,255,255,0.045) !important;
    border-color: rgba(255,255,255,0.10) !important;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.06);
  }
  /* Dark mode: no blur on the side panel or top header (a live blur is re-sampled on every scroll/repaint). The side panel is a semi-transparent dark colour (nothing scrolls under it); the top header is solid, because page content does scroll under it. */
  body:not(.theme-light):not(.theme-glass) .sidebar{ background: #141414 !important; border-color: var(--border) !important; box-shadow:none; }
  body:not(.theme-light):not(.theme-glass) .topbar{ background: #141414 !important; border-color: var(--border) !important; box-shadow:none; }
  /* Card tier: the same fake-glass look as the Liquid Glass theme above
     (tinted gradient + bright top rim + drop shadow), with no blur of its
     own — a backdrop-filter per element is expensive. The pills/buttons/
     icon badges that used to share this rule dropped to a flat tinted look
     below — this was the main fix for the scroll lag/pop-in reported on
     Dark mode. */
  body:not(.theme-light):not(.theme-glass) .dash-card,
  body:not(.theme-light):not(.theme-glass) .chart-card,
  body:not(.theme-light):not(.theme-glass) .plan-tier,
  body:not(.theme-light):not(.theme-glass) .int-card,
  body:not(.theme-light):not(.theme-glass) .comm-card,
  body:not(.theme-light):not(.theme-glass) .dv-card,
  body:not(.theme-light):not(.theme-glass) .dv-kpi-card,
  body:not(.theme-light):not(.theme-glass) .dv-kpi-add-card{
    background: linear-gradient(155deg, rgba(255,255,255,0.07), rgba(255,255,255,0.015) 45%, rgba(255,255,255,0.03) 100%) !important;
    border-color: rgba(255,255,255,0.10) !important;
    border-top-color: rgba(255,255,255,0.24) !important;
    box-shadow: 0 10px 26px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.08);
  }
  /* Same "no row container, cheap-glass cards only" treatment as Glass
     theme's own Amazon Ads/KDP/Book Analytics KPI row rule above -- see
     its comment. */
  body:not(.theme-light):not(.theme-glass) .amz-kpi-row .dv-kpi-card,
  body:not(.theme-light):not(.theme-glass) .amz-kpi-row .dv-kpi-add-card {
    background: linear-gradient(155deg, rgba(255,255,255,0.07), rgba(255,255,255,0.015) 45%, rgba(255,255,255,0.03) 100%) !important;
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
    border-color: rgba(255,255,255,0.10) !important;
    border-top-color: rgba(255,255,255,0.24) !important;
    box-shadow: 0 10px 26px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.08) !important;
  }
  /* Same Amazon Ads + KDP Report + Dashboard section treatment as Glass
     theme's own rule above -- see its comment. */
  body:not(.theme-light):not(.theme-glass) #panel-dashboard .dash-card,
  body:not(.theme-light):not(.theme-glass) #panel-dashboard-react .dash-card,
  body:not(.theme-light):not(.theme-glass) #panel-dashboard-view .dv-card,
  body:not(.theme-light):not(.theme-glass) #panel-dashboard-view .dv-kpi-card,
  body:not(.theme-light):not(.theme-glass) #panel-dashboard-view .dv-kpi-add-card,
  body:not(.theme-light):not(.theme-glass) #panel-dashboard-blank .dv-kpi-card,
  body:not(.theme-light):not(.theme-glass) #panel-dashboard-blank-react .dv-kpi-card,
  body:not(.theme-light):not(.theme-glass) #panel-dashboard-blank-react .dv-kpi-add-card,
  body:not(.theme-light):not(.theme-glass) #panel-dashboard-blank .dv-kpi-add-card {
    background: linear-gradient(155deg, rgba(255,255,255,0.07), rgba(255,255,255,0.015) 45%, rgba(255,255,255,0.03) 100%) !important;
    backdrop-filter:none !important;
    -webkit-backdrop-filter:none !important;
    border-color: rgba(255,255,255,0.10) !important;
    border-top-color: rgba(255,255,255,0.24) !important;
    box-shadow: 0 10px 26px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.08) !important;
  }
  body:not(.theme-light):not(.theme-glass) .dv-back,
  body:not(.theme-light):not(.theme-glass) .dv-pill,
  body:not(.theme-light):not(.theme-glass) .dv-icon-btn,
  body:not(.theme-light):not(.theme-glass) .na-mode-pill,
  body:not(.theme-light):not(.theme-glass) .na-quick-pill,
  body:not(.theme-light):not(.theme-glass) .na-send-btn,
  body:not(.theme-light):not(.theme-glass) .na-input-plus,
  body:not(.theme-light):not(.theme-glass) .na-showcase-icon,
  body:not(.theme-light):not(.theme-glass) .ai-suggestion,
  body:not(.theme-light):not(.theme-glass) .ai-ask-chip,
  body:not(.theme-light):not(.theme-glass) .claude-badge,
  body:not(.theme-light):not(.theme-glass) .ai-sug-icon,
  body:not(.theme-light):not(.theme-glass) .ai-settings-btn,
  body:not(.theme-light):not(.theme-glass) .ai-send-btn,
  body:not(.theme-light):not(.theme-glass) .btn-outline{
    background: linear-gradient(155deg, rgba(255,255,255,0.07), rgba(255,255,255,0.015) 45%, rgba(255,255,255,0.03) 100%) !important;
    border-color: rgba(255,255,255,0.10) !important;
    border-top-color: rgba(255,255,255,0.24) !important;
    box-shadow: 0 10px 26px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.08);
  }
  body:not(.theme-light):not(.theme-glass) .profile-dropdown,
  body:not(.theme-light):not(.theme-glass) .pd-submenu,
  body:not(.theme-light):not(.theme-glass) .nav-submenu,
  body:not(.theme-light):not(.theme-glass) .date-range-popover{
    background: rgba(10,10,10,0.94) !important;
    border-color: rgba(255,255,255,0.09) !important;
    box-shadow: 0 20px 50px rgba(0,0,0,0.55);
  }
  /* No live blur here — see the matching Liquid Glass rule above for why
     (one per KPI card, most never hovered in a given visit). */
  body:not(.theme-light):not(.theme-glass) .dv-kpi-info-tip{
    background: rgba(8,8,8,0.97) !important;
    border-color: rgba(255,255,255,0.09) !important;
    box-shadow: 0 20px 50px rgba(0,0,0,0.55);
  }
  /* Round/square icon badges are the closest thing this UI has to Apple's
     Liquid Glass control buttons (the lock/mirroring/bell orbs), so they
     get the fuller treatment instead of just the flat frosted-card recipe
     above: a radial highlight punched into the top-left, like light
     catching the curved face of a glass bead, plus an inset rim so the
     bottom edge reads as the "underside" of the glass. Their lime-wash
     fill is dropped for a plain white-on-black glass, and the icon itself
     picks up a soft white glow so it still reads crisp against it.
     No backdrop-filter here on purpose — there can be a dozen-plus of
     these on one page (every KPI card, every suggestion row), each one
     nested on top of a card that's already blurred, so a second live
     blur per badge was pure GPU cost for almost no visible gain. The
     radial highlight is what actually sells the glass-bead look; it's
     a plain gradient, not a filter, so it's essentially free. */
  body:not(.theme-light):not(.theme-glass) .dv-icon-btn,
  body:not(.theme-light):not(.theme-glass) .dv-kpi-icon,
  body:not(.theme-light):not(.theme-glass) .tpl-card-icon,
  body:not(.theme-light):not(.theme-glass) .tpl-blank-icon,
  body:not(.theme-light):not(.theme-glass) .na-send-btn,
  body:not(.theme-light):not(.theme-glass) .na-input-plus,
  body:not(.theme-light):not(.theme-glass) .na-showcase-icon,
  body:not(.theme-light):not(.theme-glass) .ai-settings-btn,
  body:not(.theme-light):not(.theme-glass) .ai-send-btn,
  body:not(.theme-light):not(.theme-glass) .ai-sug-icon{
    /* The highlight's stops used to jump from 0.60 down to 0.08 by the
       42% mark — on a small full circle that reads as a hard-edged
       "half-moon" (a bright crescent against a near-black remainder)
       rather than a soft glass highlight. Spreading the same fall-off
       across the whole circle (stops pushed out to 55%/100%, and the
       peak brightness trimmed) keeps the corner highlight but blends it
       smoothly into the base instead of leaving a visible seam. */
    background:
      radial-gradient(circle at 35% 25%, rgba(var(--pd-rgb),0.55), rgba(var(--pd-rgb),0.2) 55%, rgba(var(--pd-rgb),0.08) 100%) !important;
    border-color: rgba(var(--pd-rgb),0.4) !important;
    color:var(--pd-on,#fff) !important;
    box-shadow:
      inset 0 1px 1px rgba(255,255,255,0.55),
      inset 0 -6px 10px rgba(0,0,0,0.30),
      0 4px 14px rgba(0,0,0,0.35);
  }
  body:not(.theme-light):not(.theme-glass) .dv-icon-btn:hover{
    border-color: rgba(255,255,255,0.4) !important;
    color: #fff !important;
  }
  /* The glow itself — every icon glyph that sits on the new glass gets a
     faint white bloom, "a bit" per the brief: small radius, low opacity,
     so it reads as light through glass rather than a lit-up icon. */
  body:not(.theme-light):not(.theme-glass) .dv-icon-btn svg,
  body:not(.theme-light):not(.theme-glass) .dv-kpi-icon svg,
  body:not(.theme-light):not(.theme-glass) .tpl-card-icon svg,
  body:not(.theme-light):not(.theme-glass) .tpl-blank-icon svg,
  body:not(.theme-light):not(.theme-glass) .dv-pill svg,
  body:not(.theme-light):not(.theme-glass) .na-input-plus svg,
  body:not(.theme-light):not(.theme-glass) .na-showcase-icon svg,
  body:not(.theme-light):not(.theme-glass) .na-mode-pill svg,
  body:not(.theme-light):not(.theme-glass) .na-quick-pill svg,
  body:not(.theme-light):not(.theme-glass) .ai-settings-btn svg,
  body:not(.theme-light):not(.theme-glass) .ai-sug-icon svg,
  body:not(.theme-light):not(.theme-glass) .ai-ask-chip svg{
    filter: drop-shadow(0 0 3px rgba(255,255,255,0.5));
  }
  /* A handful of glows/pulses are hardcoded to the lime rgb triplet
     rather than the --lime variable above, so they didn't pick up the
     grayscale override automatically. These are the ones visible often
     enough sitewide to matter: the primary button's own glow, the AI
     chat send button, and the two mic "recording" pulse rings (which
     share their keyframes with the Liquid-Glass theme switcher icon,
     so they get their own mono keyframe copies rather than editing the
     shared ones — that icon is supposed to stay lime, it's advertising
     the colorful theme by name). */
  body:not(.theme-light):not(.theme-glass) .btn-primary-lime{
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.55),
      inset 0 -1px 2px rgba(0,0,0,0.12),
      0 4px 16px rgba(var(--pd-rgb),0.35) !important;
  }
  body:not(.theme-light):not(.theme-glass) .btn-primary-lime:hover{
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.65),
      inset 0 -1px 2px rgba(0,0,0,0.12),
      0 6px 20px rgba(var(--pd-rgb),0.5) !important;
  }
  body:not(.theme-light):not(.theme-glass) .ai-send-btn{
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.55),
      inset 0 -1px 2px rgba(0,0,0,0.12),
      0 3px 12px rgba(var(--pd-rgb),0.4) !important;
  }

  .content{
    flex:1;
    padding:36px 40px 130px 40px;
  }

  /* ---- panel switching ---- */
  .panel{ display:none; }
  .panel.active{ display:block; }

  /* ---- Dashboard hero ---- */
  .hero-eyebrow{
    font-family: var(--font-mono);
    font-size:11px;
    letter-spacing:0.1em;
    text-transform:uppercase;
    color: var(--lime-dim);
    margin-bottom:10px;
  }
  .hero-title{
    font-family: var(--font-display);
    font-weight:600;
    font-size:34px;
    letter-spacing:-0.02em;
    margin-bottom:26px;
  }

  .search-bar{
    display:flex; align-items:center; gap:10px;
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:10px;
    padding:13px 16px;
    max-width:640px;
    color: var(--text-dim);
    margin-bottom:34px;
  }
  .search-bar svg{ width:16px; height:16px; stroke:var(--text-dim); fill:none; stroke-width:1.6; }
  .search-bar .kbd{
    margin-left:auto;
    display:flex; gap:4px;
  }
  .search-bar .kbd span{
    font-family:var(--font-mono);
    font-size:10.5px;
    border:1px solid var(--border);
    border-radius:4px;
    padding:2px 6px;
    color: var(--text-dim);
  }

  .grid-3{
    display:grid;
    grid-template-columns: repeat(3, 1fr);
    gap:20px;
    margin-bottom:36px;
  }

  .stat-card{
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:var(--radius);
    padding:18px 20px;
  }
  .stat-card .label{
    font-family: var(--font-mono);
    font-size:10.5px;
    letter-spacing:0.08em;
    text-transform:uppercase;
    color: var(--text-dim);
    margin-bottom:10px;
  }
  .stat-card .value{
    font-family: var(--font-display);
    font-size:26px;
    font-weight:600;
  }
  .stat-card .delta{
    font-family: var(--font-mono);
    font-size:11.5px;
    margin-top:6px;
  }
  .delta.up{ color: #4ade80; }
  .delta.down{ color:#ff6b5e; }

  .section-row{
    display:flex;
    align-items:center;
    justify-content:space-between;
    margin-bottom:12px;
  }
  .section-title{
    font-family: var(--font-display);
    font-size:15px;
    font-weight:600;
    display:flex; align-items:center; gap:6px;
    color: var(--text-muted);
  }
  .section-title .chev{ color: var(--text-dim); font-size:13px; }

  .cols-3{
    display:grid;
    grid-template-columns: repeat(3, 1fr);
    gap:28px;
  }

  /* ---- Dashboards listing page ---- */
  .dash-topbar-row{
    display:flex;
    align-items:flex-start;
    justify-content:space-between;
    gap:20px;
    margin-bottom:10px;
  }
  .dash-title-group{
    display:flex;
    align-items:center;
    gap:12px;
  }
  .dash-icon-badge{
    width:36px; height:36px;
    border-radius:9px;
    background: var(--surface-2);
    border:1px solid var(--border);
    display:flex; align-items:center; justify-content:center;
    color: var(--lime-text);
    flex-shrink:0;
  }
  .dash-icon-badge svg{ width:18px; height:18px; stroke:currentColor; fill:none; stroke-width:1.7; }
  .dash-page-title{
    font-family: var(--font-display);
    font-size:28px;
    font-weight:600;
    letter-spacing:-0.02em;
  }
  .dash-actions{ display:flex; gap:10px; flex-shrink:0; }
  .btn-outline{
    background: var(--surface-2);
    border:1px solid var(--border);
    color: var(--text);
    padding:9px 16px;
    border-radius:8px;
    font-family: var(--font-body);
    font-size:13px;
    font-weight:600;
    cursor:pointer;
    transition: border-color .12s ease, box-shadow .12s ease;
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.08),
      inset 0 -1px 1px rgba(0,0,0,0.15);
  }
  .btn-outline:hover{ border-color: var(--lime-dim); }
  .btn-outline:disabled, .btn-primary-lime:disabled{
    opacity:0.5; cursor:not-allowed; pointer-events:none; filter:none; box-shadow:none;
  }
  .btn-primary-lime{
    background: var(--lime);
    border:none;
    color: var(--bg);
    padding:9px 16px;
    border-radius:8px;
    font-family: var(--font-body);
    font-size:13px;
    font-weight:700;
    cursor:pointer;
    display:flex; align-items:center; gap:6px;
    transition: filter .12s ease, box-shadow .12s ease;
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.55),
      inset 0 -1px 2px rgba(0,0,0,0.12),
      0 4px 16px rgba(var(--lime-rgb),0.30);
  }
  .btn-primary-lime:hover{
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.65),
      inset 0 -1px 2px rgba(0,0,0,0.12),
      0 6px 20px rgba(var(--lime-rgb),0.42);
  }
  .btn-primary-lime:hover{ filter: brightness(1.08); }

  .dash-subtitle{
    color: var(--text-muted);
    font-size:14px;
    max-width:640px;
    margin:10px 0 28px 0;
    line-height:1.5;
  }

  .dash-toolbar{
    display:flex;
    gap:10px;
    margin-bottom:26px;
  }
  .dash-search{
    flex:1;
    display:flex; align-items:center; gap:10px;
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:10px;
    padding:11px 16px;
    color: var(--text-dim);
  }
  .dash-search svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.6; }
  .view-toggle{
    display:flex;
    border:1px solid var(--border);
    border-radius:8px;
    overflow:hidden;
    flex-shrink:0;
  }
  .view-toggle button{
    width:38px;
    background: var(--surface);
    border:none;
    color: var(--text-dim);
    cursor:pointer;
    display:flex; align-items:center; justify-content:center;
  }
  .view-toggle button + button{ border-left:1px solid var(--border); }
  .view-toggle button.active{ background: var(--surface-2); color: var(--lime-text); }
  .view-toggle button svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.6; }

  .dash-grid{
    display:grid;
    grid-template-columns: repeat(3, 1fr);
    gap:20px;
  }
  .dash-card{
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:12px;
    overflow:hidden;
    cursor:pointer;
    transition: border-color .15s ease, transform .15s ease;
  }
  .dash-card:hover{ border-color: var(--lime-dim); transform: translateY(-2px); }
  .dash-card-top{
    display:flex;
    gap:1px;
    background: var(--border);
  }
  .mini-widget{
    flex:1;
    background: var(--surface-2);
    padding:12px 0;
    display:flex; align-items:center; justify-content:center; gap:4px;
    font-family: var(--font-mono);
    color: var(--text-dim);
    font-size:13px;
  }
  .mini-widget.starred{ color: var(--lime-text); }
  .mini-widget svg{ width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:1.6; }
  .dash-card-chart{ padding:16px 16px 0 16px; }
  .dash-card-chart svg{ width:100%; height:64px; display:block; }
  .dash-card-body{
    padding:14px 16px 16px 16px;
    border-top:1px solid var(--border);
    margin-top:14px;
  }
  .dash-card-title{ font-family: var(--font-display); font-weight:600; font-size:15px; margin-bottom:2px; }
  .dash-card-meta{ font-size:12px; color: var(--text-dim); margin-bottom:10px; }
  .dash-card-sub{ font-size:11px; color: var(--text-muted); line-height:1.6; }

  /* ---- Dashboards listing: narrow-viewport reflow ----
     Previously the only thing here was a fixed 3-column grid with no
     fallback — at any narrower width (two windows side by side, a
     smaller laptop, a real phone) the columns just got squeezed thinner
     and thinner instead of ever reflowing, cramming card text/charts
     into a sliver instead of stacking. These breakpoints are keyed off
     the actual viewport, not .main's own width, since .main's width
     already varies with the sidebar's collapsed/expanded state (a
     variable neither media queries nor plain CSS can read) — close
     enough in practice, and the sidebar itself is out of scope for this
     pass (starting with the Dashboard section specifically, per request).
     1100px: drop the fixed 3-up grid to 2. 760px: down to a single
     column (the "recognized like phone" ask) and let the title row's
     actions wrap onto their own line under the title instead of
     squeezing the title down to make room. 560px: the two header
     buttons go full-width/stacked, same as the two toolbar controls --
     side-by-side flex:1 buttons at that width get too narrow to read
     their own labels. */
  @media (max-width: 1100px){
    .dash-grid{ grid-template-columns: repeat(2, 1fr); }
  }
  @media (max-width: 760px){
    .content{ padding:24px 20px 120px 20px; }
    .dash-grid{ grid-template-columns: 1fr; }
    .dash-topbar-row{ flex-wrap:wrap; }
    .dash-page-title{ font-size:22px; }
    .dash-actions{ width:100%; }
    .dash-actions button{ flex:1; }
  }
  @media (max-width: 560px){
    .dash-actions{ flex-direction:column; }
    .dash-toolbar{ flex-wrap:wrap; }
    .dash-search{ min-width:100%; }
  }

  /* ---- Dashboard view (single opened dashboard) ---- */
  /* Not sticky — only the app-wide .topbar (breadcrumb/Ask Sphere/avatar)
     stays pinned on scroll. Each page's own title row scrolls away
     normally with the rest of its content. Amazon Ads is the one
     exception, but it doesn't get a sticky row of its own: past a
     certain scroll point its marketplace/date-range/campaign-type pills
     move into the app topbar itself instead — see .topbar-amz-filters
     and initAmzTopbarDock. */
  .dv-topbar{
    display:flex; align-items:flex-start; justify-content:space-between; margin-bottom:24px; flex-wrap:wrap; gap:14px;
  }
  .dv-title-group{ display:flex; align-items:flex-start; gap:12px; min-width:0; flex:1 1 auto; overflow:hidden; }
  .dv-back{
    width:32px; height:32px; border-radius:8px; border:1px solid var(--border);
    display:flex; align-items:center; justify-content:center; cursor:pointer; flex-shrink:0; margin-top:2px;
    color: var(--text-muted);
  }
  .dv-back:hover{ border-color: var(--lime-dim); color: var(--text); }
  .dv-back svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .dv-title{ font-family: var(--font-display); font-weight:700; font-size:24px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:100%; }
  .dv-subtitle{ font-size:13px; color: var(--text-muted); margin-top:4px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:100%; }
  .dv-actions{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
  .dv-pill{
    display:flex; align-items:center; gap:6px;
    background: var(--surface-2); border:1px solid var(--border); color: var(--text);
    padding:8px 14px; border-radius:8px; font-size:12.5px; font-weight:600; cursor:pointer;
    font-family: var(--font-body);
    max-width:100%; min-width:0;
  }
  .dv-pill span{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
  .dv-pill svg{ flex-shrink:0; width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .dv-pill.small{ padding:6px 10px; font-size:11.5px; }
  /* Compact-view state for #amzViewModeBtn (see initAmzViewMode) — same
     "on" treatment .legend-chip.active/.amz-order-opt.active already use
     elsewhere, so this reads as the same kind of toggle. */
  .dv-pill.active{ background: rgba(var(--lime-rgb),0.14); border-color: var(--lime-dim); color: var(--lime-text); }
  .dv-icon-btn{
    position:relative; width:34px; height:34px; border-radius:50%; border:1px solid var(--border);
    display:flex; align-items:center; justify-content:center; color: var(--text-muted); cursor:pointer;
  }
  .dv-icon-btn:hover{ border-color: var(--lime-dim); color: var(--text); }
  /* Same "on" treatment .dv-pill.active/.legend-chip.active use elsewhere
     — a round icon button has no visible label to carry that state, so
     the tint + border are all it has to show it's the active mode. */
  .dv-icon-btn.active{ background: rgba(var(--lime-rgb),0.14); border-color: var(--lime-dim); color: var(--lime-text); }
  /* Visually hidden but still readable by assistive tech — for icon-only
     controls (like the round Full/Compact view toggle) that still want a
     real text label under the hood, not just a title attribute. */
  .sr-only{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
  }
  .dv-icon-btn svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.7; }
  .amz-filter-btn{ width:26px; height:26px; }
  .amz-filter-btn svg{ width:12px; height:12px; }
  .dv-badge-dot{ position:absolute; top:2px; right:2px; width:7px; height:7px; border-radius:50%; background: var(--lime); border:1.5px solid var(--surface); }

  /* position+z-index here (not just on the "Add metric" dropdown itself)
     so the whole row's stacking level sits above whatever card follows
     it — the Performance/chart card below has its own stacking context
     (Glass theme's backdrop-filter), which was winning against the
     dropdown otherwise, showing it "behind the graph" instead of on
     top of it. */
  .dv-kpi-row{ display:grid; grid-template-columns:repeat(5, 1fr); gap:16px; margin-bottom:20px; position:relative; z-index:2; }
  .dv-kpi-card{ background: var(--surface); border:1px solid var(--border); border-radius:12px; padding:16px; min-width:0; overflow:hidden; }
  /* Icon sits beside its label now, not stacked above it. */
  .dv-kpi-head{ display:flex; align-items:center; gap:8px; margin-bottom:10px; min-width:0; }
  .dv-kpi-icon{
    width:26px; height:26px; border-radius:7px; background: var(--lime-wash); color: var(--lime-text);
    display:flex; align-items:center; justify-content:center; font-weight:700; font-family: var(--font-display);
    flex-shrink:0;
  }
  .dv-kpi-icon svg{ width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .dv-kpi-label{ font-size:12px; color: var(--text-muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
  /* Value + delta percentage sit side by side now, delta on the right --
     per user request (was value alone on its own line, delta stacked
     below it as a separate block). flex-wrap so a delta that also
     carries the "vs last period" text (see deltaHtml, createKpiRowController)
     can still drop to its own line on a narrow card instead of
     overflowing, rather than forcing everything onto one cramped row. */
  .dv-kpi-value-row{ display:flex; align-items:baseline; flex-wrap:wrap; gap:8px; margin-bottom:0; }
  .dv-kpi-value{ font-family: var(--font-display); font-weight:700; font-size:21px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .dv-kpi-delta{ font-size:11.5px; font-weight:600; }
  .dv-kpi-delta.up{ color: #4ade80; }
  .dv-kpi-delta.down{ color: #ff6b6b; }
  /* "vs last period" / the comparison-period picker now sits on its own
     line UNDER the value+delta row -- per user request (percentage beside
     the number on the right, period text stays under the number). */
  .dv-kpi-period-row{ margin-top:2px; font-size:11.5px; color: var(--text-dim); font-weight:400; }
  /* Comparison-period picker (see createKpiRowController's comparePeriod
     param) -- same muted, no-pill text treatment as the plain "vs last
     period" span it replaces, just with a chevron + hover state signaling
     it's clickable. */
  .dv-kpi-period-trigger{
    display:inline-flex; align-items:center; gap:2px;
    color: var(--text-dim); font-weight:400;
    cursor:pointer;
  }
  .dv-kpi-period-trigger:hover{ color: var(--text); }
  .dv-kpi-period-chevron{ width:9px; height:9px; stroke:currentColor; fill:none; stroke-width:2.2; }
  .dv-kpi-period-menu{
    position:absolute; z-index:200;
    display:none;
    background: var(--surface); border:1px solid var(--border); border-radius:10px;
    padding:6px; min-width:140px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.35);
  }
  .dv-kpi-period-menu.open{ display:block; }
  .dv-kpi-period-opt{
    display:block; width:100%; text-align:left;
    padding:7px 10px; border-radius:6px;
    background:none; border:none;
    font-family: var(--font-body); font-size:12.5px; color: var(--text);
    cursor:pointer;
  }
  .dv-kpi-period-opt:hover{ background: rgba(var(--lime-rgb),0.12); color: var(--lime-text); }

  /* KDP Reports-only decorative background chart (see kdpKpiBgSparkSvg /
     kdpKpiController's opts.bgSpark in script.js) -- an ascending purple
     line with its area gradient-filled underneath, sitting low behind the
     card's real label/value/delta text. z-index:-1 (not just position:
     absolute) is what actually keeps it behind the card's normal-flow
     text content: per the stacking spec, positioned descendants with
     z-index:auto paint ABOVE static in-flow content regardless of DOM
     order, so without the negative z-index this would sit on top of the
     numbers instead of behind them. Sits above the card's own background
     (var(--surface)) since negative z-index only escapes below normal
     flow content, not below the box's own background/border. */
  .dv-kpi-bg-spark{
    position:absolute; left:0; right:0; bottom:0;
    width:100%; height:70%;
    opacity:0.7;
    z-index:-1;
    pointer-events:none;
  }

  /* Customizable KPI row: drag to reorder, remove, add new metrics */
  .dv-kpi-card{ position:relative; cursor:grab; }
  .dv-kpi-card.dragging{ opacity:0.4; cursor:grabbing; }
  .dv-kpi-card.drag-over{ border-color: var(--lime-dim); }
  .dv-kpi-remove{
    position:absolute; top:8px; right:8px;
    width:18px; height:18px; border-radius:50%;
    display:none; align-items:center; justify-content:center;
    background:transparent; border:1px solid var(--border); color: var(--text-dim);
    cursor:pointer; font-size:12px; line-height:1;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
  }
  .dv-kpi-card:hover .dv-kpi-remove{ display:flex; }
  /* Explicit drag handle — dragging now starts only from this icon (see
     createKpiRowController in script.js), not from anywhere on the card,
     so clicking/selecting the value or hovering the info tooltip can't
     accidentally kick off a drag. Same reveal-on-hover treatment as
     .dv-kpi-remove, opposite corner. */
  .dv-kpi-drag-handle{
    position:absolute; bottom:8px; right:8px;
    width:18px; height:18px; border-radius:5px;
    display:none; align-items:center; justify-content:center;
    color: var(--text-dim); cursor:grab;
  }
  .dv-kpi-card:hover .dv-kpi-drag-handle{ display:flex; }
  .dv-kpi-drag-handle svg{ width:12px; height:12px; fill:currentColor; stroke:none; }
  .dv-kpi-drag-handle:active{ cursor:grabbing; }
  /* The floating tip below (position:absolute, top:100%) pokes out past
     the card's own box, so the card can't keep clipping its children to
     that box while it's being hovered — only while hovered, since that's
     the only time the tip can be showing. z-index is the other half of
     that same escape: every .dv-kpi-card now carries its own live-blur
     stacking context (Group B above), so the tip landing over the NEXT
     row was actually racing that row's card for paint order — and on a
     tie, the later card in the grid (DOM order) was winning, painting
     over the earlier card's tip instead of under it. Explicitly promoting
     just the hovered card above its un-hovered siblings settles that
     unambiguously, regardless of which two cards happen to be involved. */
  .dv-kpi-card:hover{ overflow:visible; z-index:30; }
  .dv-kpi-remove:hover{ background: rgba(255,107,107,0.12); color:#ff6b6b; border-color:#ff6b6b; }
  /* Small "?" sitting right after the label text (not a corner badge),
     only shown while hovering the card — same reveal-on-hover behavior
     as the remove "✕". Hover the "?" itself for a plain-English,
     book-publisher-specific explanation of that metric. Circle outline
     is currentColor, so it's always exactly the same color as the "?"
     glyph it's drawn around — never a separate, mismatched border tone. */
  .dv-kpi-info{
    display:none; vertical-align:middle; margin-left:5px;
    width:11px; height:11px; border-radius:50%; flex-shrink:0;
    align-items:center; justify-content:center;
    background:transparent; border:1px solid currentColor; color: var(--text-dim);
    cursor:default; font-size:7px; font-weight:700; font-family: var(--font-body);
    transition: color .15s ease;
    /* line-height:1 (a full font em, including the descender space "!"
       never actually uses since it has no descender) was what threw the
       centering off — flex still centers the *line box*, not the glyph's
       own ink, so that unused descender space below pushed the visible
       character above true center. line-height:0 collapses the line box
       down to just the glyph itself, which is what actually lines up
       with the circle's real center. */
    line-height:0;
  }
  .dv-kpi-card:hover .dv-kpi-info{ display:inline-flex; }
  .dv-kpi-info:hover{ color: var(--text); }
  /* Hovering the "?" pops a floating card-width tip below the card,
     rather than flipping the card's own content black in place — it
     hovers over whatever sits underneath (the next row) instead of
     fighting the value/delta text for the same pixels. Positioned
     against .dv-kpi-card itself (the nearest *positioned* ancestor,
     skipping right past .dv-kpi-info, which is deliberately left
     unpositioned so this escapes it) so it spans the full card width
     regardless of where inside the card the "?" sits. Plain low-
     transparency black by default; body.theme-glass overrides this to
     the same live-blur recipe as the site's other floating overlays
     (profile dropdown, nav submenu). */
  .dv-kpi-info-tip{
    position:absolute; top:calc(100% + 8px); left:0; right:0; z-index:20; box-sizing:border-box;
    background: rgba(8,9,6,0.94); border:1px solid var(--border); border-radius:10px;
    box-shadow: 0 16px 40px rgba(0,0,0,0.45);
    display:flex; flex-direction:column; align-items:stretch; justify-content:flex-start; gap:3px; text-align:left;
    padding:12px 14px; opacity:0; pointer-events:none; transform:translateY(-4px);
    transition: opacity .15s ease, transform .15s ease;
    /* .dv-kpi-label (an ancestor here, via .dv-kpi-info) sets
       white-space:nowrap for its own ellipsis-truncated text — an
       inherited property, so without resetting it here the tip's own
       text inherited "nowrap" too and refused to wrap at all, running
       straight past the box's edges instead of using the width the
       flex column below stretches it to. overflow:hidden is a backstop
       clip so nothing can visibly escape the box's own rounded corners
       even if content is ever wider than expected. */
    white-space:normal; overflow:hidden;
  }
  .dv-kpi-info:hover .dv-kpi-info-tip{ opacity:1; pointer-events:auto; transform:translateY(0); }
  /* Flips the tip to open upward instead of down — added by JS (see the
     delegated mouseover listener near the top of the script) only when
     this specific card has another KPI card sitting below it, i.e. it's
     in the top row of a grid that's wrapped to two-or-more rows (adding
     enough metrics via "+ Add metric" does this). Opening downward there
     would drop the tip right on top of that next row's cards instead of
     over open space. A single-row KPI grid (the common case) never gets
     this class at all, so it keeps opening downward same as always. The
     hover rule above already animates to translateY(0) either way, so
     flipping which *base* offset it animates from is the only change
     needed here. */
  .dv-kpi-info.tip-up .dv-kpi-info-tip{ top:auto; bottom:calc(100% + 8px); transform:translateY(4px); }
  /* align-items:stretch (not flex-start) is what actually matters here —
     in a column flex container, flex-start sizes each line to its own
     content width instead of the container's, so a long description had
     no width to wrap against and just ran on as one line past the card's
     edge. Stretching them to the full width, plus break-word as a
     backstop for any single very long word, is what makes text wrap
     instead of bleeding out. */
  .dv-kpi-info-tip b{ font-family: var(--font-display); font-weight:700; font-size:10.5px; color: var(--lime-text); overflow-wrap:break-word; }
  .dv-kpi-info-tip span{ font-size:9.5px; font-weight:400; line-height:1.4; color: var(--text); overflow-wrap:break-word; }
  .dv-kpi-add-card{
    background:none; border:1.5px dashed var(--border); border-radius:12px;
    display:flex; align-items:center; justify-content:center; gap:6px;
    color: var(--text-dim); font-size:13px; font-weight:600; cursor:pointer;
    min-height:96px; position:relative;
  }
  .dv-kpi-add-card:hover{ border-color: var(--lime-dim); color: var(--text); }
  .dv-kpi-add-card svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:2; }
  .dv-kpi-add-menu{
    position:absolute; top:100%; left:50%; transform:translateX(-50%); margin-top:6px;
    background: var(--surface); border:1px solid var(--border); border-radius:10px;
    padding:6px; min-width:200px; z-index:20;
    box-shadow: 0 12px 32px rgba(0,0,0,0.35);
    display:none; text-align:left;
  }
  .dv-kpi-add-menu.open{ display:block; }
  .dv-kpi-add-item{ padding:8px 10px; border-radius:7px; font-size:12.5px; color: var(--text); cursor:pointer; font-weight:400; }
  .dv-kpi-add-item:hover{ background: var(--surface-2); }
  .dv-kpi-add-empty{ padding:8px 10px; font-size:12px; color: var(--text-dim); }

  /* ---- Custom (blank) dashboard: chart widgets ---- */
  .custom-chart-grid{ display:grid; grid-template-columns:repeat(2, 1fr); gap:16px; margin-top:16px; }
  .custom-chart-card{ padding:18px; }
  .custom-chart-card .dv-linechart{ height:70px; }
  .custom-chart-card .dv-kpi-remove{ top:14px; right:14px; }
  .custom-chart-title{ font-family: var(--font-display); font-weight:700; font-size:14px; margin-bottom:2px; }
  .custom-chart-value{ font-size:22px; font-weight:700; font-family: var(--font-display); margin-bottom:10px; }
  .custom-chart-add-card{ min-height:200px; }
  .custom-chart-add-card svg{ width:18px; height:18px; }
  .custom-chart-add-card span{ font-size:13px; }

  /* ---- Test dashboard: isolated traffic-dashboard experiment ---- */
  .traffic-test-panel{ max-width:none; margin:0; }
  .amazon-ads-react-pill{ border:1px solid rgba(var(--pd-rgb),.38); background:rgba(var(--pd-rgb),.14); color:var(--pd-c084fc); font-size:8px; letter-spacing:.03em; }
  body.theme-light .amazon-ads-react-pill{ border-color:#c4b5fd; background:#f3f0ff; color:var(--pd-6d28d9); }
  .amazon-ads-react-panel{ max-width:none; margin:0; }
  .traffic-test-panel:has(#testDashboardReactRoot) > :not(#testDashboardReactRoot){ display:none; }
  .traffic-test-dash-card{ cursor:pointer; }
  .traffic-test-head,.traffic-test-toolbar{ display:flex; align-items:center; justify-content:space-between; gap:14px; flex-wrap:wrap; }
  .traffic-test-head{ margin-bottom:16px; }
  .traffic-test-title-group{ display:flex; align-items:flex-start; gap:12px; min-width:0; }
  .traffic-test-title{ font-family:var(--font-display); font-size:25px; font-weight:700; letter-spacing:-.02em; }
  .traffic-test-subtitle{ margin-top:4px; font-size:12.5px; color:var(--text-muted); }
  .traffic-test-actions,.traffic-test-toolbar-right{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
  .traffic-test-btn{ display:inline-flex; align-items:center; gap:7px; min-height:38px; padding:0 13px; }
  .traffic-test-btn svg,.traffic-test-actions .btn-primary-lime svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .traffic-test-actions .btn-primary-lime{ display:inline-flex; align-items:center; gap:7px; min-height:38px; }
  .traffic-test-toolbar{ padding:14px 0; border-top:1px solid var(--border); border-bottom:1px solid var(--border); }
  .traffic-test-add-wrap,.traffic-test-filter-wrap,.traffic-test-range-wrap{ position:relative; }
  .traffic-test-popover{ position:absolute; top:calc(100% + 7px); right:0; z-index:80; display:none; min-width:188px; padding:6px; border:1px solid var(--border); border-radius:10px; background:var(--surface); box-shadow:0 14px 34px rgba(0,0,0,.32); }
  .traffic-test-popover.open{ display:block; }
  .traffic-test-filter-popover{ left:0; right:auto; min-width:272px; padding:12px; }
  .traffic-test-menu-item{ display:flex; align-items:center; gap:8px; width:100%; padding:8px 9px; border:0; border-radius:7px; background:none; color:var(--text); font:inherit; font-size:12.5px; text-align:left; cursor:pointer; }
  .traffic-test-menu-item:hover{ background:var(--surface-2); color:var(--lime-text); }
  .traffic-test-filter-popover label{ display:block; margin:0 0 5px; color:var(--text-muted); font-size:11px; font-weight:600; }
  .traffic-test-filter-popover select,.traffic-test-filter-popover input{ width:100%; margin-bottom:10px; padding:8px 9px; border:1px solid var(--border); border-radius:7px; outline:none; background:var(--surface-2); color:var(--text); font:inherit; font-size:12px; }
  .traffic-test-filter-popover input:focus,.traffic-test-filter-popover select:focus{ border-color:var(--lime-dim); }
  .traffic-test-filter-apply{ width:100%; padding:8px 10px; border:0; border-radius:7px; background:var(--lime); color:#10120d; font:inherit; font-size:12px; font-weight:700; cursor:pointer; }
  .traffic-test-live-dot{ width:7px; height:7px; border-radius:50%; background:var(--text-dim); }
  .traffic-test-live-dot.on{ background:#4ade80; box-shadow:0 0 0 3px rgba(74,222,128,.12); }
  .traffic-test-filter-chips{ display:flex; flex-wrap:wrap; gap:7px; min-height:0; padding-top:10px; }
  .traffic-test-chip{ display:inline-flex; align-items:center; gap:6px; padding:5px 8px; border:1px solid var(--lime-dim); border-radius:20px; background:var(--lime-wash); color:var(--lime-text); font-size:11.5px; font-weight:600; }
  .traffic-test-chip button{ border:0; padding:0; background:none; color:inherit; cursor:pointer; font-size:14px; line-height:1; }
  .traffic-test-status{ min-height:18px; padding-top:6px; color:var(--text-dim); font-size:11.5px; }
  /* `align-items:start` is essential for independent resizing: CSS Grid
     otherwise stretches every card in a row to the height of the one card
     being resized. */
  /* Matches the supplied grid template's collision behavior: cards retain
     document order while arranging, so entering an occupied position pushes
     following cards down instead of back-filling a gap out of sight. */
  .traffic-test-metrics,.traffic-test-grid{ display:grid; grid-template-columns:repeat(12,minmax(0,1fr)); grid-auto-flow:row; align-items:start; gap:16px; }
  .traffic-test-metrics{ margin:10px 0 14px; }
  .traffic-test-metric{ position:relative; grid-column:span 3; min-width:260px; min-height:176px; padding:16px; border:1px solid var(--border); border-radius:12px; background:var(--surface); overflow:hidden; }
  .traffic-test-card{ position:relative; min-width:0; overflow:hidden; border:1px solid var(--border); border-radius:12px; background:var(--surface); }
  .traffic-test-card.span-full{ grid-column:span 12; }
  .traffic-test-card.span-8{ grid-column:span 8; }
  .traffic-test-card.span-4{ grid-column:span 4; }
  .traffic-test-card-head{ display:flex; align-items:center; justify-content:space-between; gap:8px; padding:15px 16px 7px; }
  .traffic-test-card-title,.traffic-test-metric-label{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--text-muted); font-size:12.5px; font-weight:600; }
  .traffic-test-card-menu{ display:flex; align-items:center; justify-content:center; width:24px; height:24px; margin:-4px -5px 0 0; border:0; border-radius:6px; background:none; color:var(--text-dim); font-size:17px; line-height:1; cursor:pointer; }
  .traffic-test-card-menu:hover{ color:var(--text); background:var(--surface-2); }
  /* The cursor itself is the move hint: hover an open part of a container
     to see the native four-direction arrow, then drag it into position. */
  .traffic-test-metric,.traffic-test-card{ cursor:move; }
  .traffic-test-metric button,.traffic-test-card button{ cursor:pointer; }
  .traffic-test-metric button.traffic-test-resize-n,.traffic-test-metric button.traffic-test-resize-s,.traffic-test-card button.traffic-test-resize-n,.traffic-test-card button.traffic-test-resize-s{ cursor:ns-resize; }
  .traffic-test-metric button.traffic-test-resize-e,.traffic-test-metric button.traffic-test-resize-w,.traffic-test-card button.traffic-test-resize-e,.traffic-test-card button.traffic-test-resize-w{ cursor:ew-resize; }
  .traffic-test-metric button.traffic-test-resize-ne,.traffic-test-metric button.traffic-test-resize-sw,.traffic-test-card button.traffic-test-resize-ne,.traffic-test-card button.traffic-test-resize-sw{ cursor:nesw-resize; }
  .traffic-test-metric button.traffic-test-resize-nw,.traffic-test-metric button.traffic-test-resize-se,.traffic-test-card button.traffic-test-resize-nw,.traffic-test-card button.traffic-test-resize-se{ cursor:nwse-resize; }
  /* Keep the original grid slot during a drag, but turn it into a quiet
     empty placeholder. The full card is supplied separately as the native
     drag image under the cursor. */
  .traffic-test-dragging{ opacity:.70; background:rgba(var(--lime-rgb),.18); border-color:rgba(var(--lime-rgb),.50); box-shadow:none; }
  .traffic-test-dragging > *{ visibility:hidden !important; }
  .traffic-test-drag-ghost{ position:fixed; z-index:1000; margin:0; pointer-events:none; opacity:.96; cursor:move; box-shadow:0 20px 42px rgba(0,0,0,.42),0 0 0 1px rgba(255,255,255,.08); transition:transform .08s linear; will-change:transform; }
  .traffic-test-card-body{ min-height:184px; padding:8px 16px 16px; }
  .traffic-test-metric-value{ display:flex; align-items:baseline; flex-wrap:nowrap; gap:8px; min-width:0; margin:12px 0; font-family:var(--font-display); font-size:28px; font-weight:700; line-height:1.06; font-variant-numeric:tabular-nums; white-space:nowrap; }
  .traffic-test-metric-change{ flex-shrink:0; font-family:var(--font-body); font-size:12px; color:#4ade80; line-height:1.2; white-space:nowrap; }
  .traffic-test-metric-change.down{ color:#ff8a7a; }
  .traffic-test-spark{ width:100%; height:55px; display:block; }
  .traffic-test-line{ width:100%; height:220px; display:block; overflow:visible; }
  .traffic-test-grid-line{ stroke:var(--border-soft); stroke-width:1; stroke-dasharray:3 4; }
  .traffic-test-axis{ fill:var(--text-dim); font-size:10px; }
  .traffic-test-line-path{ fill:none; stroke:var(--lime); stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; }
  .traffic-test-bars{ display:flex; flex-direction:column; gap:11px; padding-top:4px; }
  .traffic-test-bar-row{ display:grid; grid-template-columns:minmax(72px,1fr) minmax(50px,2fr) 42px; align-items:center; gap:9px; font-size:11.5px; }
  .traffic-test-bar-label{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--text-muted); }
  .traffic-test-bar-track{ height:9px; overflow:hidden; border-radius:999px; background:var(--surface-2); }
  .traffic-test-bar-fill{ height:100%; border-radius:inherit; background:var(--lime); }
  .traffic-test-bar-value{ text-align:right; color:var(--text); font-variant-numeric:tabular-nums; }
  .traffic-test-empty{ display:flex; align-items:center; justify-content:center; min-height:184px; color:var(--text-dim); font-size:12px; }
  /* A visible lower-right grip plus hover-revealed edges/corners provides
     true all-direction resizing. These sit over the card rather than adding
     layout chrome, so chart content and the responsive grid remain clean. */
  .traffic-test-resize{ position:absolute; z-index:4; display:block; padding:0; border:0; background:transparent; opacity:0; touch-action:none; }
  .traffic-test-resize-se{ right:5px; bottom:5px; width:16px; height:16px; border-radius:4px 0 5px 0; cursor:nwse-resize; opacity:.54; background:repeating-linear-gradient(135deg,transparent 0 3px,var(--text-muted) 3px 4px,transparent 4px 6px); transition:opacity .15s ease,background .15s ease; }
  .traffic-test-resize-n,.traffic-test-resize-s{ left:17px; right:17px; height:8px; cursor:ns-resize; }
  .traffic-test-resize-n{ top:-4px; } .traffic-test-resize-s{ bottom:-4px; }
  .traffic-test-resize-e,.traffic-test-resize-w{ top:17px; bottom:17px; width:8px; cursor:ew-resize; }
  .traffic-test-resize-e{ right:-4px; } .traffic-test-resize-w{ left:-4px; }
  .traffic-test-resize-ne,.traffic-test-resize-nw,.traffic-test-resize-sw{ width:15px; height:15px; }
  .traffic-test-resize-ne{ top:-4px; right:-4px; cursor:nesw-resize; } .traffic-test-resize-nw{ top:-4px; left:-4px; cursor:nwse-resize; } .traffic-test-resize-sw{ bottom:-4px; left:-4px; cursor:nesw-resize; }
  .traffic-test-metric:hover .traffic-test-resize,.traffic-test-card:hover .traffic-test-resize,.traffic-test-resize:focus{ opacity:.82; }
  .traffic-test-resize-se:hover,.is-resizing .traffic-test-resize-se{ opacity:1; background:repeating-linear-gradient(135deg,transparent 0 3px,var(--lime) 3px 4px,transparent 4px 6px); }
  .traffic-test-resize:focus{ outline:1px solid var(--lime); outline-offset:1px; }
  .traffic-test-metric.is-resizing,.traffic-test-card.is-resizing{ user-select:none; outline:1px solid var(--lime-dim); }
  .traffic-test-grid-resizing .traffic-test-metric,.traffic-test-grid-resizing .traffic-test-card{ will-change:transform; }
  @media (max-width:1279px){ .traffic-test-metrics,.traffic-test-grid{ grid-template-columns:repeat(6,minmax(0,1fr)); } .traffic-test-metric,.traffic-test-card.span-4{ grid-column:span 3; } .traffic-test-card.span-full,.traffic-test-card.span-8{ grid-column:span 6; } }
  @media (max-width:767px){ .traffic-test-metrics,.traffic-test-grid{ grid-template-columns:minmax(0,1fr); } .traffic-test-metric,.traffic-test-card.span-full,.traffic-test-card.span-8,.traffic-test-card.span-4{ grid-column:1 !important; min-width:0; width:100% !important; } .traffic-test-actions,.traffic-test-toolbar-right{ width:100%; } .traffic-test-actions > *,.traffic-test-toolbar-right > *{ flex:1; } .traffic-test-toolbar-right .traffic-test-range-wrap{ flex:1; } .traffic-test-toolbar-right .traffic-test-range-wrap button{ width:100%; justify-content:center; } }

  /* A brand-new blank dashboard (no charts yet) shows this instead of the
     lone dashed "Add a chart" tile — a full picker of chart types, so the
     very first thing a new dashboard offers is "which kind of chart do
     you want", not an empty grid with one small plus icon in the corner.
     Spans both columns of .custom-chart-grid since it's the grid's only
     child whenever it's shown. */
  .chart-start-empty{ grid-column: 1 / -1; text-align:center; padding:28px 0 4px; }
  .chart-start-title{ font-size:13.5px; color: var(--text-muted); margin-bottom:20px; }
  .chart-start-grid{ display:grid; grid-template-columns:repeat(3, 260px); justify-content:center; gap:14px; text-align:left; }
  .chart-start-card{
    background: var(--surface); border:1px solid var(--border); border-radius:12px;
    padding:16px; cursor:pointer; transition: border-color .12s ease, background .12s ease;
  }
  .chart-start-card:hover{ border-color: var(--lime-dim); background: var(--surface-2); }
  .chart-start-icon{ display:flex; align-items:center; width:20px; height:20px; color: var(--text-dim); margin-bottom:14px; font-family: var(--font-mono); font-weight:700; font-size:15px; }
  .chart-start-icon svg{ width:20px; height:20px; stroke:currentColor; fill:none; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; }
  .chart-start-card-title{ font-weight:700; font-size:13.5px; margin-bottom:4px; color: var(--text); }
  .chart-start-card-desc{ font-size:12px; color: var(--text-muted); line-height:1.4; }
  @media (max-width: 900px){
    .chart-start-grid{ grid-template-columns:repeat(auto-fit, minmax(150px, 260px)); }
  }

  /* ---- Chart builder: full-page takeover for "Add a chart" ---- */
  /* left:240px (not inset:0) — same offset .main itself sits at, so the
     app's own sidebar stays visible and usable to its left instead of
     getting covered up, matching the reference's own layout. Kept in
     sync with the sidebar's collapsed/expanded width the same way .main
     already does (see body.sidebar-collapsed below). */
  .chartb-overlay{
    position:fixed; top:0; right:0; bottom:0; left:240px; z-index:420;
    background:var(--bg); display:flex; flex-direction:column;
    opacity:0; pointer-events:none;
    transition: opacity .15s ease, left .08s cubic-bezier(.4,0,.2,1);
  }
  .chartb-overlay.open{ opacity:1; pointer-events:auto; }
  body.sidebar-collapsed .chartb-overlay{ left:60px; }
  .chartb-topbar{
    display:flex; align-items:center; justify-content:space-between;
    margin:20px 20px 0; padding:18px 22px;
    border:1px solid var(--border); border-radius:14px; background:var(--surface);
    box-shadow:none; flex-shrink:0;
  }
  .chartb-title{ font-family:var(--font-display); font-weight:700; font-size:22px; letter-spacing:-0.02em; }
  .chartb-actions{ display:flex; gap:10px; }
  /* The builder follows the same composition as the reference: its controls
     live in one distinct, inset configuration card while the preview stays
     on the dotted workspace behind it.  Keeping this on the body (rather
     than only on the canvas) lets the card have breathing room on all sides. */
  .chartb-body{
    flex:1; min-height:0; display:flex; gap:20px; padding:20px;
    background-image: radial-gradient(var(--border) 1px, transparent 1px);
    background-size:22px 22px;
  }
  .chartb-sidebar{
    width:380px; flex-shrink:0; overflow-y:auto;
    border:1px solid var(--border); border-radius:14px;
    background:var(--surface); box-shadow:none;
    display:flex; flex-direction:column;
    position:relative; overflow-x:hidden;
  }
  /* Everything above the AI panel blurs and goes inert while it's open —
     the same "this is the thing that has focus now" cue a real modal
     gives, without actually leaving the sidebar (the panel slides up
     over the bottom of it rather than replacing the page). */
  .chartb-sidebar.ai-open .chartb-section,
  .chartb-sidebar.ai-open .chartb-ai-row{
     opacity:0.45; pointer-events:none;
  }
  .chartb-section, .chartb-ai-row{ transition: filter .2s ease, opacity .2s ease; }
  .chartb-section + .chartb-section{ border-top:1px solid var(--border); }
  .chartb-section-head{
    display:flex; align-items:center; gap:8px; padding:15px 20px;
    background:linear-gradient(90deg, rgba(255,255,255,0.045), rgba(255,255,255,0.018));
    font-family:var(--font-display); font-weight:650; font-size:13.5px; cursor:pointer; user-select:none;
  }
  .chartb-section-head svg{ width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:2; transition: transform .15s ease; flex-shrink:0; }
  .chartb-section.collapsed .chartb-section-head svg{ transform:rotate(-90deg); }
  .chartb-section-body{ padding:18px 20px 20px; }
  .chartb-section.collapsed .chartb-section-body{ display:none; }
  .chartb-field-label{ font-size:12px; font-weight:650; color:var(--text-muted); margin-bottom:8px; }
  .chartb-field-label.chartb-filters-label{ margin-top:19px; }
  .chartb-dropdown-wrap{ position:relative; }
  .chartb-dropdown-btn{
    width:100%; min-height:46px; justify-content:space-between;
    border-color:var(--border); border-radius:10px;
    background:linear-gradient(180deg, rgba(255,255,255,0.045), rgba(255,255,255,0.018));
  }
  .chartb-dropdown-btn:hover{ border-color:var(--lime-dim); background:var(--surface-2); }
  .chartb-dropdown-btn svg:last-child{ margin-left:auto; }
  .chartb-type-icon{ font-weight:700; width:16px; text-align:center; flex-shrink:0; }
  .chartb-type-icon svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; display:block; }
  /* Grouped like the reference: a muted, unclickable label heading each
     cluster of related chart types, matching how this app's own metrics
     naturally fall into "a trend over time" vs "compare these things
     side by side" vs "where in the world" rather than one flat list. */
  .chartb-type-menu{ min-width:240px; max-height:360px; overflow-y:auto; }
  .chartb-type-item{
    display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:7px;
    font-size:12.5px; color: var(--text); cursor:pointer;
  }
  .chartb-type-item:hover{ background: var(--surface-2); }
  .chartb-type-item .chartb-type-icon{ color: var(--text-dim); }
  .chartb-type-item-check{ margin-left:auto; width:14px; height:14px; stroke: var(--lime-text); fill:none; stroke-width:2.2; flex-shrink:0; }
  .chartb-type-group-label{
    font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color: var(--text-dim);
    padding:10px 10px 4px 10px;
  }
  .chartb-type-group-label:first-child{ padding-top:6px; }
  .chartb-type-divider{ height:1px; background: var(--border-soft); margin:6px 4px; }
  .chartb-filter-list{ display:flex; flex-direction:column; gap:8px; }
  .chartb-filter-row{
    display:flex; align-items:center; gap:8px;
    background:var(--surface-2); border:1px solid var(--border); border-radius:10px; padding:8px 10px;
  }
  .chartb-filter-row select, .chartb-filter-row input{
    flex:1; min-width:0; background:none; border:none; outline:none;
    color: var(--text); font-size:12.5px; font-family: inherit;
  }
  .chartb-filter-remove{
    display:flex; align-items:center; justify-content:center; width:18px; height:18px;
    color: var(--text-dim); cursor:pointer; flex-shrink:0;
  }
  .chartb-filter-remove:hover{ color: var(--text); }
  .chartb-add-filter{
    display:flex; align-items:center; gap:6px; margin-top:11px;
    font-size:12.5px; font-weight:600; color: var(--lime-text); cursor:pointer; width:fit-content;
  }
  .chartb-add-filter svg{ width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:2; }
  .chartb-ai-row{
    margin-top:auto; display:flex; align-items:center; gap:8px;
    padding:17px 20px; border-top:1px solid var(--border);
    background:linear-gradient(90deg, rgba(255,255,255,0.045), rgba(255,255,255,0.018));
    font-size:13px; font-weight:600; color: var(--text-muted); cursor:pointer;
  }
  .chartb-ai-row:hover{ color: var(--text); }
  .chartb-ai-row svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.8; }
  /* Slides up from below the sidebar's own bottom edge rather than
     replacing any of it — translateY(100%) at rest puts it fully out of
     view under the sidebar's own clipped box, .open brings it flush. */
  .chartb-ai-panel{
    position:sticky; bottom:0; z-index:3; flex-shrink:0;
    max-height:0; overflow:hidden; padding:0 20px; border-top:0 solid var(--border);
    background: var(--surface);
    transition: max-height .25s cubic-bezier(.4,0,.2,1), padding .25s cubic-bezier(.4,0,.2,1);
    box-shadow:none;
  }
  /* Closed it takes NO space (max-height:0) so scrolling the sidebar never reveals it; open it is stuck to the sidebar's visible bottom edge. */
  .chartb-ai-panel.open{ max-height:420px; padding:16px 20px 20px 20px; border-top-width:1px; }
  .chartb-ai-panel-head{ display:flex; align-items:center; justify-content:flex-end; font-family: var(--font-display); font-weight:600; font-size:13.5px; margin-bottom:12px; }
  .chartb-ai-panel-head span:first-child{ display:flex; align-items:center; gap:7px; }
  .chartb-ai-panel-head svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .chartb-ai-panel-close{ display:flex; color: var(--text-dim); cursor:pointer; }
  .chartb-ai-panel-close:hover{ color: var(--text); }
  .chartb-ai-panel-close svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:2; }
  .chartb-ai-input-row{
    display:flex; align-items:center; gap:8px;
    background: var(--surface-2); border:1px solid var(--border); border-radius:10px;
    padding:9px 9px 9px 12px; margin-bottom:14px;
  }
  .chartb-ai-input-row input{ flex:1; min-width:0; background:none; border:none; outline:none; color: var(--text); font-size:13px; font-family: inherit; }
  .chartb-ai-send{
    width:26px; height:26px; border-radius:50%; flex-shrink:0; border:none; cursor:pointer;
    background: var(--lime); color:#0a0c0a; display:flex; align-items:center; justify-content:center;
  }
  .chartb-ai-send svg{ width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:2.4; }
  .chartb-ai-examples-label{ font-size:11.5px; color: var(--text-muted); margin-bottom:8px; }
  .chartb-ai-example{ display:flex; align-items:center; gap:8px; font-size:12.5px; font-weight:600; color: var(--text); padding:7px 0; cursor:pointer; }
  .chartb-ai-example:hover{ color: var(--lime-text); }
  .chartb-ai-example svg{ width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:2; flex-shrink:0; }
  .chartb-canvas{
    flex:1; min-width:0; position:relative; overflow-y:auto; padding:20px 8px 20px 0;
    display:flex; flex-direction:column;
    background:none;
  }
  /* A real header row, not two independently-positioned pieces — the
     date pill used to be position:absolute while the label sat in normal
     flow at the same top offset, which put them directly on top of each
     other instead of side by side. */
  .chartb-canvas-head{
    display:flex; align-items:center; justify-content:space-between; margin-bottom:14px;
    padding:0 4px; flex-shrink:0;
  }
  .chartb-daterange-wrap{ position:relative; }
  .chartb-preview-label{ font-size:12.5px; color:var(--text-muted); font-weight:600; }
  /* Centered in whatever room is left under the header, not pinned to
     the top-left corner of a mostly-empty canvas. */
  .chartb-canvas-body{ position:relative; flex:1; min-height:0; overflow:hidden; padding:0 4px; }
  .chartb-preview-card-wrap{
    position:absolute; width:480px; height:260px; max-width:none;
    touch-action:none; user-select:none;
  }
  /* Timeseries is a real multi-line chart, not a compact stat card — it
     gets the full width of whatever room the canvas actually has instead
     of the same ~420px cap every other chart type's card uses. */
  .chartb-preview-card-wrap.wide{ max-width:none; }
  .chartb-preview-move-handle,
  .chartb-preview-resize-handle{
    position:absolute; z-index:12; display:grid; place-items:center; margin:0;
    border:1px solid var(--border); background:var(--surface); color:var(--text-muted);
    box-shadow:none; cursor:grab; touch-action:none;
  }
  .chartb-preview-move-handle{
    top:-12px; left:50%; width:34px; height:24px; border-radius:999px;
    transform:translateX(-50%);
  }
  .chartb-preview-move-handle:active{ cursor:grabbing; }
  .chartb-preview-move-handle svg{ width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
  .chartb-preview-resize-handle{
    right:-7px; bottom:-7px; width:24px; height:24px; border-radius:7px; cursor:nwse-resize;
  }
  .chartb-preview-resize-handle svg{ width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:1.8; stroke-linecap:round; }
  .chartb-preview-move-handle:hover,
  .chartb-preview-resize-handle:hover{ border-color:var(--lime-dim); color:var(--lime-text); }
  .chartb-preview-card-wrap.is-manipulating .chartb-preview-card{ pointer-events:none; }
  /* Stat uses the exact same React Grid Layout interaction as the live
     dashboard: drag by its header, resize from the lower-right cross, and
     snap to the same 15-column/57px-row grid. The custom freeform controls
     are only a fallback for the older non-React preview renderers. */
  .chartb-preview-card-wrap.is-react-grid{ inset:0!important; width:auto!important; height:auto!important; }
  .chartb-preview-card-wrap.is-react-grid > .chartb-preview-move-handle,
  .chartb-preview-card-wrap.is-react-grid > .chartb-preview-resize-handle{ display:none; }
  .chartb-ts-legend{ display:flex; flex-wrap:wrap; gap:14px; row-gap:6px; font-size:12px; color: var(--text-muted); margin-bottom:14px; }
  .chartb-ts-legend-item{ display:flex; align-items:center; gap:6px; }
  .chartb-ts-legend-item b{ color: var(--text); font-weight:700; }
  .chartb-ts-legend-dot{ width:8px; height:8px; border-radius:50%; flex-shrink:0; }
  .chartb-ts-svg{ width:100%; height:220px; display:block; overflow:visible; }
  .chartb-ts-axis-label{ fill: var(--text-dim); font-size:10px; }
  .chartb-ts-grid-line{ stroke: var(--border-soft); stroke-width:1; }
  /* A real liquid-glass card, same recipe used everywhere else in this
     app for a floating/featured surface (the alerts panel, the Bid
     History popover): translucent gradient + backdrop blur, a bright top
     rim to sell the curved-glass edge, and a soft tinted glow tucked in
     the corner — rather than the flat solid card every other KPI tile
     uses, since this one is meant to read as the centerpiece. */
  .chartb-preview-card{
    position:relative; overflow:hidden;
    background: linear-gradient(155deg, rgba(255,255,255,0.14), rgba(255,255,255,0.03) 45%, rgba(255,255,255,0.05) 100%);
    border:1px solid rgba(255,255,255,0.14);
    border-top-color: rgba(255,255,255,0.34);
    box-shadow:none;
    box-sizing:border-box; width:100%; height:100%; border-radius:14px; min-height:0; padding:24px;
  }
  .chartb-preview-card::before{ display:none; }
  .chartb-preview-card > *{ position:relative; }
  /* .dv-kpi-card:hover normally flips overflow to visible (so a dragged
     KPI card's overflow content — the remove "✕", tooltips — isn't
     clipped). This card is a static chart preview, not a draggable KPI
     tile, so that rule only had the side effect of un-clipping the glow
     blob above and letting it bleed out over the whole canvas on hover —
     it must stay clipped here. */
  .chartb-preview-card:hover{ overflow:hidden; }
  .chartb-preview-card .dv-linechart{ height:80px; }
  /* Stat previews mount the actual React KPI card. The host itself becomes
     layout-only so there is one thin outline (the real card's), not a
     doubled preview-card shell around it. */
  .chartb-preview-card.is-stat-preview{
    min-height:0; height:100%; padding:0; overflow:visible; border:0; border-radius:0;
    background:none; box-shadow:none; backdrop-filter:none; -webkit-backdrop-filter:none;
  }
  .chartb-preview-card.is-stat-preview::before{ display:none; }
  .chartb-preview-card.is-stat-preview:hover{ overflow:visible; }
  .chartb-stat-card{ min-height:0; height:100%; box-shadow:none!important; }
  .chartb-stat-grid-host{ width:100%; height:100%; min-height:0; overflow:hidden; }
  .chartb-stat-grid{ width:100%; height:100%!important; min-height:100%; }
  .chartb-stat-grid .react-grid-item{ min-width:0; }
  /* Category-comparison chart types (Horizontal Bar, Top List,
     Marketplace Map) — a labeled row + a fill bar, plain HTML rather
     than SVG since laying out text next to a bar is what HTML/flexbox is
     already good at, instead of fighting SVG font-metrics for it. */
  .chartb-preview-rows{ display:flex; flex-direction:column; gap:7px; margin-top:4px; }
  .chartb-preview-row{ display:flex; align-items:center; gap:8px; font-size:11.5px; }
  .chartb-preview-row-label{ width:120px; flex-shrink:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color: var(--text-muted); }
  .chartb-preview-row-bar-track{ flex:1; height:8px; border-radius:4px; background: var(--surface-2); overflow:hidden; }
  .chartb-preview-row-bar{ height:100%; background: var(--lime); border-radius:4px; }
  .chartb-preview-row-value{ width:36px; text-align:right; flex-shrink:0; color: var(--text-dim); }
  /* Doughnut Chart */
  /* Legend on top, wrapping across lines, ring centered below it — same
     shape as the reference's own Doughnut Chart preview, not the old
     small ring-beside-a-list layout. */
  .chartb-donut-head{ display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:12px; }
  .chartb-donut-head-title{ font-family: var(--font-display); font-weight:700; font-size:14px; color: var(--text); }
  .chartb-donut-menu-btn{
    width:24px; height:24px; border-radius:6px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
    color: var(--text-dim); cursor:pointer; transition: background .12s ease, color .12s ease;
  }
  .chartb-donut-menu-btn:hover{ background: var(--surface-2); color: var(--text); }
  .chartb-donut-menu-btn svg{ width:16px; height:16px; fill:currentColor; }
  .chartb-donut-legend-wrap{ display:flex; flex-wrap:wrap; gap:8px 18px; margin-top:2px; font-size:12.5px; color: var(--text-muted); }
  .chartb-donut-legend-item{ display:flex; align-items:center; gap:6px; }
  .chartb-donut-legend-item b{ color: var(--text); font-weight:700; }
  .chartb-donut-legend-dot{ width:8px; height:8px; border-radius:50%; flex-shrink:0; }
  .chartb-donut-legend-more{ color: var(--text-dim); flex-basis:100%; }
  .chartb-donut-ring-wrap{ position:relative; width:170px; height:170px; margin:20px auto 0 auto; }
  /* transform-box:view-box makes the transform-origin below resolve
     against the <svg>'s own "0 0 120 120" viewBox coordinates rather than
     this one path's own bounding box — so 60px 60px is the ring's actual
     center regardless of which segment it is, and scaling grows/pops the
     whole segment out from that shared center instead of from its own
     off-center midpoint. */
  .chartb-donut-seg{
    transform-box: view-box; transform-origin: 60px 60px;
    transition: transform .35s cubic-bezier(.25,.1,.25,1); cursor:pointer;
  }
  .chartb-donut-seg:hover{ transform: scale(1.1); }
  /* In-slice percentage labels -- sits in the same viewBox coordinate
     space as the segments themselves (not a separate overlay), so it
     scales and positions with the ring for free at any rendered size.
     pointer-events:none so hovering the number still hits the segment
     underneath it for the tooltip. */
  .chartb-donut-seg-label{
    font-family: var(--font-display); font-weight:700; font-size:7px;
    fill:#fff; paint-order: stroke; stroke: rgba(0,0,0,0.45); stroke-width:2px; stroke-linejoin:round;
    pointer-events:none;
  }
  /* Positioned by JS in real pixels off each segment's own stored
     midpoint (see buildDonut/wireDonutHover) — plain absolute + a
     transform to center/float it above that point, same pattern as
     every other chart tooltip in this app. */
  .chartb-donut-tip{
    position:absolute; transform: translate(-50%, -130%);
    align-items:center; gap:6px;
    background: var(--surface); border:1px solid var(--border); border-radius:8px;
    padding:6px 10px; font-size:12px; color: var(--text); white-space:nowrap;
    box-shadow: 0 8px 20px rgba(0,0,0,0.3);
    pointer-events:none; display:none;
  }
  .chartb-donut-tip b{ font-weight:700; }
  .chartb-donut-tip-dot{ width:8px; height:8px; border-radius:50%; display:inline-block; margin-right:2px; }
  /* Conversion Funnel */
  .chartb-funnel-stage{ display:flex; align-items:center; justify-content:center; height:20px; border-radius:4px; color:#0a0c0a; font-size:10.5px; font-weight:700; margin:0 auto 6px auto; }
  .custom-chart-empty-hint{ grid-column:1 / -1; text-align:center; color: var(--text-dim); font-size:12.5px; padding:8px 0 4px 0; }
  /* Dashboard title is contenteditable — a plain caret with no visual
     affordance otherwise, so give it a hover/focus outline so it reads as
     editable rather than looking like static text that happens to blink
     a cursor when clicked. */
  .dv-title[contenteditable]{ outline:none; border-radius:6px; padding:2px 6px; margin:-2px -6px; cursor:text; }
  .dv-title[contenteditable]:hover{ background: var(--surface-2); }
  .dv-title[contenteditable]:focus{ background: var(--surface-2); box-shadow: 0 0 0 1.5px var(--lime-dim); }

  .dv-main-row{ display:grid; grid-template-columns:1.6fr 1fr; gap:16px; margin-bottom:16px; }
  .dv-main-row-3{ grid-template-columns:1fr 1fr 1fr; }
  .dv-main-row-2{ grid-template-columns:1fr 1fr; }
  .billing-grid{ display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-bottom:16px; }
  .billing-kpi-row{ grid-template-columns:repeat(4, 1fr); }
  .billing-plan-card .plan-tier-price-block{ margin:10px 0 14px 0; }
  .billing-plan-card .plan-tier-features{ margin-bottom:16px; }
  .billing-pm-row{ display:flex; align-items:center; gap:12px; margin-bottom:16px; }
  .billing-pm-icon{
    width:40px; height:26px; border-radius:5px; background:#1a1f71; color:#fff;
    display:flex; align-items:center; justify-content:center;
    font-family: var(--font-display); font-weight:700; font-size:10.5px; letter-spacing:0.02em; flex-shrink:0;
  }
  .billing-pm-name{ font-weight:600; font-size:13.5px; }
  .billing-pm-sub{ font-size:12px; color: var(--text-dim); margin-top:2px; }
  .billing-invoice-link{ color: var(--lime-text); font-size:12.5px; font-weight:600; text-decoration:none; }
  .billing-invoice-link:hover{ text-decoration:underline; }
  /* Explicit, unambiguous z-index (not just position:relative) so this
     never re-triggers the ".dv-kpi-row z-index:2" fix above — Glass mode's
     (and now Dark mode's) backdrop-filter turns every .dv-card into its
     own stacking context, and comparing an implicit filter-made context
     against .dv-kpi-row's explicit z-index:2 is exactly the ambiguity
     that let the KPI info tooltip render behind whichever .dv-card
     followed the row (the Performance/Revenue chart, mainly). Pinning
     this to a plain z-index:1 removes the ambiguity outright: 2 beats 1,
     no stacking-context edge case involved. */
  .dv-card{ position:relative; z-index:1; background: var(--surface); border:1px solid var(--border); border-radius:12px; padding:18px; }
  .dv-card-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:10px; gap:10px; }
  /* Wraps the expand button (and, on cards that get one — see
     .card-hide-btn — the hide/eye button) so the pair moves as a single
     right-aligned group. margin-left:auto lives here now instead of on
     .card-expand-btn directly, since a lone auto-margin on each of two
     adjacent flex children would split the free space between them
     (a visible gap opening up between the two icons) instead of just
     pushing the pair as a whole to the row's right edge. */
  .card-head-actions{ display:flex; align-items:center; gap:6px; flex-shrink:0; margin-left:auto; }
  .card-expand-btn, .card-hide-btn, .card-remove-btn, .card-drag-handle{
    display:flex; align-items:center; justify-content:center; flex-shrink:0;
    width:26px; height:26px; border-radius:7px; color: var(--text-dim); cursor:pointer;
  }
  .card-expand-btn:hover, .card-hide-btn:hover, .card-remove-btn:hover, .card-drag-handle:hover{ background: var(--surface-2); color: var(--text); }
  .card-expand-btn svg, .card-hide-btn svg, .card-remove-btn svg, .card-drag-handle svg{ width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .card-remove-btn:hover{ color:#ff6b6b; }
  /* A hidden section collapses to just its title bar — everything else
     inside the card (chart, table, whatever) is removed from layout
     rather than merely faded, so it doesn't leave a tall empty card
     taking up space the user asked to reclaim. */
  .dv-card.section-hidden > .dv-card-head{ margin-bottom:0; }
  .dv-card.section-hidden > *:not(.dv-card-head){ display:none !important; }
  .card-hide-btn.active{ color: var(--lime-text, var(--text)); }
  /* Whole-card drag-to-reorder handle ("6 dots") — sits to the right of
     the expand button in the same .card-head-actions group. Dragging is
     scoped to this handle only (not the whole card head), same reasoning
     as .dv-kpi-drag-handle: the card body underneath (charts, tables) has
     its own mouse-drag behaviors (pan, column resize/reorder, scroll)
     that a whole-card-draggable would collide with. */
  .card-drag-handle{ cursor:grab; }
  .card-drag-handle svg{ fill:currentColor; stroke:none; }
  .card-drag-handle:active{ cursor:grabbing; }
  .dv-card.section-dragging{ opacity:0.4; }
  .dv-card.section-drag-over{ outline:2px dashed var(--lime-dim); outline-offset:-2px; }
  /* Spacing between the reorderable Amazon Ads cards used to come from
     each card's own identity — .amz-chart-card's margin-bottom:28px, an
     inline margin-bottom:28px baked into the Campaigns card specifically,
     and nothing at all on Ad Spend by Book since it was originally last.
     Dragging a card to a different position moved that identity-tied
     margin along with it instead of leaving it behind, so a card with no
     margin left a zero-gap collision wherever it landed mid-list, and a
     card with one left a stray trailing gap if it landed last. Scoping
     the gap to *position* instead (every direct .dv-card gets it, the
     actual last one loses it) keeps the spacing correct no matter which
     card ends up where; this ID-scoped rule already outranks the older
     .amz-chart-card one on specificity. */
  .card-expand-overlay{
    position:fixed; inset:0; z-index:300;
    background: rgba(5,6,4,0.55);
    display:flex; align-items:center; justify-content:center;
    opacity:0; pointer-events:none;
    transition: opacity .2s ease;
  }
  .card-expand-overlay.open{ opacity:1; pointer-events:auto; }
  .card-expand-window{
    position:relative;
    width:min(1000px, 92vw); max-height:88vh;
    background:none;
    padding:0;
    overflow:visible;
    display:flex; flex-direction:column;
    transform: scale(0.97);
    transition: transform .2s ease;
  }
  .card-expand-overlay.open .card-expand-window{ transform: scale(1); }
  /* The Performance chart specifically takes the whole screen on expand,
     not the ~1000px/88vh centered modal every other card gets -- there's
     real value in a chart using every pixel available (more room per
     data point, taller line movement), where a table or KPI card just
     gets awkward whitespace padding it out to full width/height for no
     reason. Scoped with :has() to only the chart's own card so this
     doesn't change the generic expand behavior everything else still
     uses. */
  .card-expand-overlay.open:has(.amz-chart-wrap) .card-expand-window{
    width:100vw; max-width:100vw; height:100vh; max-height:100vh;
  }
  .card-expand-overlay.open:has(.amz-chart-wrap) .card-expand-body{
    max-height:100vh; height:100vh; border-radius:0;
  }
  .card-expand-body{
    overflow:auto; cursor:grab; user-select:none;
    max-height:88vh;
    border-radius:12px;
    box-shadow: 0 30px 80px rgba(0,0,0,0.5);
  }
  .card-expand-body.grabbing{ cursor:grabbing; }
  .card-expand-body .dv-linechart{ height:340px; }
  /* .chart-plot-wrap is the SVG's own immediate parent, with a fixed
     height:200px normally (see its base rule) so the hover dots
     positioned by percentage of this same box always land on the exact
     curve. Without this override, the SVG grows to the 340px above but
     its 200px-tall parent doesn't, so the extra 140px spills straight out
     the bottom of the box and lands on top of .chart-xaxis right below
     it -- the dates-overlapping-the-chart bug this fixes. */
  .card-expand-body .chart-plot-wrap{ height:340px; }
  .card-expand-body .dv-card{ margin:0; }

  .amz-chart-expand-toolbar{
    display:none;
    align-items:center;
    justify-content:flex-end;
    gap:10px;
    margin-left:auto;
    flex-wrap:wrap;
  }
  .card-expand-body:has(.amz-chart-wrap) .amz-chart-expand-toolbar{
    display:flex;
  }
  /* .card-head-actions (the expand button's wrapper — see script.js, and
     .card-hide-btn above) also carries its own margin-left:auto, needed
     normally so it alone gets pushed to the far right when it's the only
     thing after the title. With our toolbar directly before it *also*
     claiming margin-left:auto, .dv-card-head split the free space between
     the two of them instead of concentrating it all before the toolbar —
     zeroing it out here so the toolbar's own auto margin is the only one
     doing the pushing. */
  .amz-chart-expand-toolbar + .card-head-actions{
    margin-left:0;
  }

  /* ---- Create a Dashboard: template picker modal ---- */
  .tpl-modal-overlay{
    position:fixed; inset:0; z-index:400;
    background: rgba(5,6,4,0.55);
    display:flex; align-items:center; justify-content:center;
    opacity:0; pointer-events:none; transition: opacity .2s ease;
  }
  .tpl-modal-overlay.open{ opacity:1; pointer-events:auto; }
  .tpl-modal{
    width:min(760px, 92vw); max-height:86vh; overflow:auto;
    background: var(--surface); border:1px solid var(--border); border-radius:14px;
    padding:24px; transform: scale(0.97); transition: transform .2s ease;
    box-shadow: 0 30px 80px rgba(0,0,0,0.5);
  }
  .tpl-modal-overlay.open .tpl-modal{ transform: scale(1); }
  .tpl-modal-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:12px; margin-bottom:18px; }
  .tpl-modal-title{ font-family: var(--font-display); font-weight:700; font-size:19px; }
  .tpl-modal-close{
    width:30px; height:30px; border-radius:8px; flex-shrink:0;
    display:flex; align-items:center; justify-content:center; color: var(--text-dim); cursor:pointer;
  }
  .tpl-modal-close:hover{ background: var(--surface-2); color: var(--text); }
  .tpl-modal-close svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.8; }
  /* Pulled out of the template list entirely — a blank dashboard isn't
     really "a template" among the others, it's the alternative to
     picking one, so it gets its own prominent row up top instead of
     just being the last card in the grid. */
  .tpl-blank-card{
    display:flex; align-items:center; gap:14px;
    background: var(--surface-2); border:1px solid var(--border); border-radius:12px;
    padding:16px 18px; cursor:pointer; margin-bottom:18px;
    transition: border-color .15s ease, transform .15s ease;
  }
  .tpl-blank-card:hover{ border-color: var(--lime-dim); transform: translateY(-2px); }
  .tpl-blank-icon{
    width:34px; height:34px; border-radius:9px; background: rgba(var(--pd-rgb),0.16); color: var(--lime-text);
    display:flex; align-items:center; justify-content:center; flex-shrink:0;
  }
  .tpl-blank-icon svg{ width:18px; height:18px; stroke:currentColor; fill:none; stroke-width:1.6; }
  .tpl-blank-name{ font-family: var(--font-display); font-weight:600; font-size:14.5px; }
  .tpl-blank-desc{ font-size:12px; color: var(--text-muted); margin-top:2px; }
  .tpl-divider{
    display:flex; align-items:center; gap:12px;
    font-size:12px; color: var(--text-muted); margin-bottom:16px;
  }
  .tpl-divider::before, .tpl-divider::after{ content:''; flex:1; height:1px; background: var(--border); }
  .tpl-search{
    display:flex; align-items:center; gap:10px;
    background: var(--surface-2); border:1px solid var(--border); border-radius:10px;
    padding:10px 14px; margin-bottom:16px;
  }
  .tpl-search svg{ width:15px; height:15px; stroke: var(--text-dim); fill:none; stroke-width:1.8; flex-shrink:0; }
  .tpl-search input{
    flex:1; min-width:0; background:none; border:none; outline:none;
    color: var(--text); font-size:13px; font-family: inherit;
  }
  .tpl-search input::placeholder{ color: var(--text-dim); }
  /* A vertical list, not a 2-up grid — every template row gets the full
     width to lay its description out on, matching how the blank-
     dashboard row above it reads (same card recipe, just repeated). */
  .tpl-list{ display:flex; flex-direction:column; gap:12px; max-height:340px; overflow-y:auto; }
  .tpl-card{
    display:flex; align-items:flex-start; gap:14px;
    background: var(--surface-2); border:1px solid var(--border); border-radius:12px;
    padding:16px 18px; cursor:pointer; transition: border-color .15s ease, transform .15s ease;
  }
  .tpl-card:hover{ border-color: var(--lime-dim); transform: translateY(-2px); }
  .tpl-card-icon{
    width:34px; height:34px; border-radius:9px; background: rgba(var(--pd-rgb),0.16); color: var(--lime-text);
    display:flex; align-items:center; justify-content:center; flex-shrink:0; margin-top:1px;
  }
  .tpl-card-icon svg{ width:17px; height:17px; stroke:currentColor; fill:none; stroke-width:1.7; }
  .tpl-card-body{ flex:1; min-width:0; }
  .tpl-card-name{ font-family: var(--font-display); font-weight:600; font-size:14.5px; margin-bottom:3px; }
  .tpl-card-desc{ font-size:12px; color: var(--text-muted); line-height:1.5; }
  .tpl-list-empty{ font-size:12.5px; color: var(--text-muted); text-align:center; padding:20px 0; }

  .dv-card-title{ font-family: var(--font-display); font-weight:600; font-size:15px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
  .dv-legend{ display:flex; gap:16px; font-size:12px; color: var(--text-muted); margin-bottom:8px; align-items:center; }
  .dot{ width:8px; height:8px; border-radius:50%; display:inline-block; margin-right:5px; flex-shrink:0; }
  .dot.lime{ background: var(--lime); }
  .dot.grey, .dot.dim{ background: var(--text-dim); }
  .dot.mid{ background: var(--lime-dim); }
  .dv-linechart{ width:100%; height:200px; display:block; }
  /* Wraps the Dashboard's own Revenue & Profit chart (static markup, no
     render cycle) so its hand-placed .chart-point-dots can be
     positioned by percentage against the exact same box the SVG
     renders into. */
  .dv-chart-plot-static{ position:relative; width:100%; height:200px; }
  .dv-chart-plot-static .dv-linechart{ position:absolute; inset:0; }

  .dv-donut{ display:flex; align-items:center; justify-content:center; position:relative; margin:8px 0 16px 0; }
  .dv-donut-ring{
    width:150px; height:150px; border-radius:50%;
    background: conic-gradient(var(--lime) 0% 58.9%, var(--lime-dim) 58.9% 84.6%, var(--border) 84.6% 100%);
    -webkit-mask: radial-gradient(circle, transparent 58%, black 59%);
    mask: radial-gradient(circle, transparent 58%, black 59%);
  }
  .dv-donut-center{ position:absolute; text-align:center; }
  .dv-donut-total{ font-family: var(--font-display); font-weight:700; font-size:17px; }
  .dv-donut-sub{ font-size:10.5px; color: var(--text-dim); }
  .dv-donut-legend{ display:flex; flex-direction:column; gap:9px; margin-bottom:4px; }
  .dv-donut-row{ display:flex; align-items:center; font-size:12.5px; color: var(--text-muted); gap:4px; }
  .dv-donut-row b{ color: var(--text); font-weight:600; margin-left:auto; margin-right:8px; }
  /* KDP donuts only (see renderSimpleDonut in script.js) -- the raw
     count sits right after the %, both riding <b>'s own margin-left:
     auto push to the row's right edge as one group (count itself just
     needs a small gap from <b>, not its own auto-margin). */
  .dv-donut-row-count{ margin-left:6px; color: var(--text-dim); font-size:12px; }
  .dv-donut-row span:last-child{ color: var(--text-dim); font-size:11.5px; min-width:36px; text-align:right; }
  /* KDP's two donuts only (Book Analytics/Dashboard's own donuts keep
     the legend stacked above/below, unwrapped by this container): ring
     on the left, legend as a right-hand column instead, each row's dot
     + label on the left and its % pinned to the row's own right edge
     (.dv-donut-row b's existing margin-left:auto already does that,
     unchanged -- just more room to work with here than the narrower
     stacked layout gave it). */
  /* align-items:center per user request (ring at left-middle) -- was
     flex-start specifically to avoid the two rings landing at different
     heights (Marketplace has far more legend rows than Format Sales).
     That height difference is still real with the 3-column legend below
     (each column is still one row per country/format, just split
     sideways into 3 parallel columns instead of one combined line --
     doesn't actually get shorter), so this consciously keeps that
     tradeoff rather than pretending it went away, per explicit request. */
  .dv-donut-flex{ display:flex; align-items:center; gap:28px; }
  .dv-donut-flex .dv-donut{ margin:8px 0; flex-shrink:0; }
  /* Legend redone as 3 separate stacked columns (labels/countries,
     copies, percentage), each with every row, divided by a real
     continuous vertical border on the column itself -- NOT a per-row
     divider between individual entries (tried first, wrong per user
     feedback: "each section alone, percentage alone, then copies and
     countries alone" means group by FIELD into its own column, not
     separate each row from its neighbor). See renderSimpleDonut in
     script.js for the matching 3-column HTML this targets. */
  .dv-donut-flex .dv-donut-legend{ flex:1; min-width:0; margin-bottom:0; display:flex; flex-direction:row; align-items:flex-start; gap:0; }
  .dv-donut-col{ display:flex; flex-direction:column; gap:9px; padding:0 14px; font-size:13.5px; }
  .dv-donut-col:first-child{ padding-left:0; }
  .dv-donut-col-counts, .dv-donut-col-pcts{ border-left:1px solid rgba(255,255,255,0.14); }
  .dv-donut-col-labels{ flex:1; min-width:0; }
  /* Fixed line-height, not each column's own font-size default -- the 3
     columns use different font-sizes (labels 13.5px, counts 12.5px,
     pcts' <b> 14px), which without this each resolve to a slightly
     different row height and drift a country's row out of line with
     its own count/% the further down the list you go. Pinning every
     row (in every column) to the same box height keeps them pixel-
     aligned regardless of what font-size that column uses. */
  .dv-donut-col-row{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:17px; }
  .dv-donut-col-pcts b{ font-size:14px; color: var(--text); font-weight:600; }
  .dv-donut-col-counts .dv-donut-col-row{ color: var(--text-dim); font-size:12.5px; }
  /* Sphere Insights card -- same "AI" sparkle language as the app's
     other AI touchpoints (Create-with-AI's own icon in the chart legend
     menu), a handful of short callouts instead of a chart. Built from
     real numbers already on this page (top marketplace/format share,
     top-earning book), not a live model call -- see script.js's own
     comment on why. */
  .sphere-insights-badge{
    display:flex; align-items:center; gap:4px;
    font-size:10.5px; font-weight:700; color: var(--lime-text);
    background: var(--lime-wash); border-radius:20px; padding:3px 9px;
  }
  .sphere-insights-badge svg{ width:11px; height:11px; fill:currentColor; stroke:none; }
  .sphere-insights-list{ display:flex; flex-direction:column; gap:16px; margin-top:6px; }
  .sphere-insight-row{ display:flex; align-items:flex-start; gap:9px; }
  .sphere-insight-icon{ flex-shrink:0; width:20px; height:20px; border-radius:7px; background: var(--lime-wash); color: var(--lime-text); display:flex; align-items:center; justify-content:center; margin-top:1px; }
  .sphere-insight-icon svg{ width:11px; height:11px; fill:currentColor; stroke:none; }
  /* Each row used to be one flat sentence at 12.5px -- read as small,
     dense text with nothing to anchor the eye. Split into a bold title
     (what the insight is about) and a description sentence below it
     (the actual real-data claim, same text as before), both bumped up a
     size from the old flat row. */
  .sphere-insight-body{ flex:1; min-width:0; }
  .sphere-insight-title{ font-size:14px; font-weight:700; color: var(--text); margin-bottom:3px; }
  .sphere-insight-desc{ font-size:13px; color: var(--text-muted); line-height:1.5; }
  /* Right-aligned already, but that alone only pins it right after
     whatever content happens to be above it -- if this card gets
     stretched taller than its own content (e.g. to match a taller
     sibling in the same row, the default behavior of a CSS grid/flex
     row), there was empty space left BELOW the link instead of the link
     sitting at the card's true bottom edge. Making all 3 cards in this
     row a flex column and pushing this link down with margin-top:auto
     is what actually pins it to the bottom regardless of how tall the
     card ends up -- originally scoped to just the 2 donut cards (Sphere
     Insights had no viewall link of its own yet), widened to all 3 once
     Sphere Insights got its own "See more insights" link in the same
     .dv-donut-viewall/-link shape. */
  /* When this card gets stretched taller than its own content (grid row
     height matches its taller sibling -- Format Sales' short 3-row
     legend next to Marketplace's 8-row one, for example), .dv-donut-flex
     used to just sit at its own intrinsic height right under the title,
     leaving the extra space as a dead gap below it before the
     margin-top:auto-pinned "View full breakdown" link -- reading as
     "stuck to the top" rather than centered. flex:1 lets it claim that
     whole leftover area, and its own align-items:center (already set)
     then centers the ring+legend within it, so the ring sits at the
     card's true middle-left regardless of how short its own legend is.
     Sphere Insights has no .dv-donut-flex, so this doesn't touch it. */
  .dv-donut-viewall{ text-align:right; margin-top:14px; }
  .dv-donut-viewall-link{
    display:inline-flex; align-items:center; gap:4px;
    font-size:12.5px; font-weight:600; color: var(--lime-text);
    text-decoration:none;
  }
  .dv-donut-viewall-link:hover{ text-decoration:underline; }
  .dv-donut-viewall-link svg{ flex-shrink:0; transition: transform .15s ease; }
  /* Sales by Marketplace's own "View full breakdown"/"Show fewer
     countries" toggle (see renderKdpMarketplaceLegend in script.js) --
     arrow points down instead of right once expanded, same "flip to
     mean collapse" convention the filter menu chevrons already use. */
  .dv-donut-viewall-link.dv-donut-viewall-open svg{ transform: rotate(90deg); }

  .dv-mini-grid{ display:grid; grid-template-columns:1fr 1fr; gap:14px; }
  .dv-mini-label{ font-size:11.5px; color: var(--text-muted); margin-bottom:4px; }
  .dv-mini-value{ font-family: var(--font-display); font-weight:700; font-size:16px; margin-bottom:3px; }
  .dv-mini-delta{ font-size:11px; font-weight:600; }
  .dv-mini-delta.up{ color: #4ade80; }
  .dv-mini-delta.down{ color: #ff6b6b; }

  .dv-books-card{ margin-bottom:28px; }
  .dv-books-card .dv-card-head{ margin-bottom:18px; }
  .dv-book-row{ display:flex; align-items:center; gap:10px; padding:9px 0; border-bottom:1px solid var(--border-soft); }
  .dv-book-row:last-of-type{ border-bottom:none; }
  .dv-book-cover{
    width:30px; height:30px; border-radius:6px; background: var(--lime-wash); color: var(--lime-text);
    display:flex; align-items:center; justify-content:center; font-family: var(--font-display); font-weight:700; font-size:12px; flex-shrink:0;
  }
  .dv-book-info{ flex:1; min-width:0; }
  .dv-book-title{ font-size:13px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .dv-book-sub{ font-size:11px; color: var(--text-dim); }
  .dv-book-value{ font-size:13px; font-weight:700; color: var(--lime-text); font-family: var(--font-display); }

  /* KDP Report's "Top Books" card -- each book its own card in a grid,
     a real rectangular cover on top (portrait, like an actual book
     spine) instead of the small square initials slot .dv-book-cover
     uses in the plain list row. --accent/--rgb set per card in JS (same
     fields BOOKS already carries for Book Analytics' own .ba-card),
     inherited down into the cover's gradient/glow. */
  /* Exactly 5 across, however wide the card ends up being -- filling
     the row rather than a fixed min-width wrapping to fewer per row on
     a narrower window (see .ba-grid's own 3-column version above, same
     idea, different count). */
  /* Was a 5-column CSS grid with cards paged in/out via display:none --
     switched to a single continuous horizontally-scrolling flex row (all
     cards always present, in one line) so paging can be a real smooth
     scroll-slide (see showKdpBooksPage in script.js, which calls
     scrollTo({left, behavior:'smooth'}) on this element) instead of an
     instant visibility swap. overflow-x:auto, not hidden -- tried hidden
     first on the assumption it'd still allow JS-driven scrollLeft/
     scrollTo while just blocking user wheel/touch/drag input, which
     turned out to be wrong: confirmed by direct test that Chromium
     ignores scrollLeft assignments entirely on an overflow:hidden
     element, so the arrow did nothing. `auto` + the scrollbar hidden via
     its own rule below gets the same visual (no visible scrollbar) while
     actually being scrollable, and as a side effect lets the user drag/
     swipe the row by hand too, which is a reasonable bonus given the ask
     was for it to "scroll to the side" like a real carousel. Each card's
     own width is an explicit flex-basis (below) instead of an implicit
     grid column-span, since flex items don't have a "span N columns"
     concept the way grid items do. */
  .dv-book-grid{ display:flex; gap:16px; overflow-x:auto; scroll-behavior:smooth; scrollbar-width:none; -ms-overflow-style:none; }
  .dv-book-grid::-webkit-scrollbar{ display:none; }
  .dv-book-grid > .dv-book-card{ flex:0 0 calc((100% - 4 * 16px) / 5); min-width:0; }
  /* #1's own width -- 3 of the row's 5 column-units (page 1 is #1 + 2
     regular = 3+1+1 = 5, so #1 fills the space a would-be 4th card used
     to occupy), an explicit flex-basis of 3 unit-widths + the 2 gaps
     between them, since flex items have no CSS-grid "span N" concept of
     their own (this used to be `grid-column:span 3` back when
     .dv-book-grid was a real CSS grid -- see its own comment on why it's
     a scrolling flex row now instead). Deliberately the more specific
     `.dv-book-grid > .dv-book-card.dv-book-card-featured` here, not just
     `.dv-book-card-featured` alone -- a plain single-class selector has
     LOWER specificity than the plain-regular-card rule directly above
     (an ancestor-combinator + class, vs. just a class), so it silently
     lost that fight and every card rendered the same width regardless of
     source order until this was scoped to win outright. */
  .dv-book-grid > .dv-book-card.dv-book-card-featured{
    flex: 0 0 calc((100% - 4 * 16px) / 5 * 3 + 2 * 16px);
    min-width:0;
  }
  /* "Cheap glass" -- same recipe as every other one in this app (the
     Amazon Ads KPI cards, the AI drawer's suggestion chips, Book
     Analytics' own .ba-card): a tinted gradient fill + a bright top-rim
     border + a shadow, all fake -- no backdrop-filter, no live blur.
     Reads as glass sitting on the page without the actual GPU cost of
     blurring anything, which matters here since there can be 5-8 of
     these on screen (and re-rendered) at once. Tint uses each card's
     own --rgb custom property (set per book in script.js) instead of a
     flat color, so every book's card picks up a faint wash of its own
     accent color rather than all looking identical. */
  .dv-book-card{
    position:relative;
    background: linear-gradient(155deg, rgba(255,255,255,0.09), rgba(255,255,255,0.02) 45%, rgba(var(--rgb,255,255,255),0.07) 100%);
    border:1px solid rgba(255,255,255,0.12);
    border-top-color: rgba(255,255,255,0.32);
    box-shadow: 0 10px 26px rgba(0,0,0,0.30), inset 0 1px 0 rgba(255,255,255,0.10);
    border-radius:16px;
    padding:14px;
    cursor:pointer;
    transition: border-color .18s ease;
  }
  .dv-book-card:hover{
    border-color: rgba(var(--rgb,255,255,255),0.5);
  }
  .dv-book-card-cover{
    position:relative;
    width:100%; aspect-ratio: 3 / 4; border-radius:9px;
    margin-bottom:10px;
    background: linear-gradient(155deg, rgba(255,255,255,0.22), rgba(255,255,255,0.02) 55%, rgba(var(--rgb,var(--lime-rgb)),0.18) 100%);
    border:1px solid rgba(var(--rgb,255,255,255),0.35);
    box-shadow: inset 0 0 22px rgba(var(--rgb,var(--lime-rgb)),0.3), 0 2px 10px rgba(0,0,0,0.3);
    display:flex; align-items:center; justify-content:center;
    font-family: var(--font-display); font-weight:700; font-size:28px; color: var(--accent, var(--lime-text));
  }
  /* Real cover art: the box's own border/shadow/rounding/aspect-ratio
     stay exactly as they are for the gradient+initials placeholder
     above -- just an <img> filling it edge to edge instead of centered
     text, clipped to the same rounded corners. */
  .dv-book-card-cover-img{ overflow:hidden; background:none; border-color:transparent; box-shadow: 0 2px 10px rgba(0,0,0,0.3); }
  /* object-fit:cover -- fills the box entirely (no letterboxing), at
     the cost of cropping whichever edges don't match each cover's own
     native aspect ratio against this box's fixed 3/4. object-position:
     top so any vertical crop this forces comes off the bottom -- covers
     keep their titles/faces (almost always near the top) intact, and
     losing a bit of the bottom margin instead is the safer tradeoff. */
  .dv-book-card-cover-img img{ width:100%; height:100%; object-fit:cover; object-position:top; display:block; }
  .dv-book-card-title{ position:relative; font-size:13px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .dv-book-card-sub{ position:relative; font-size:11px; color: var(--text-dim); margin-bottom:10px; }
  .dv-book-list-no-tags{ color: var(--text-dim); }
  /* Regular cards: a row of 3 metric columns (Royalties/Ad Spend/Net
     Profit, label above value), same order/shape as the featured card's
     own metrics row now -- the featured-card override further down only
     re-tunes sizing/spacing for its own wider layout, not the label/value
     order or metric count any more (both used to differ; see Log.md). */
  .dv-book-card-metrics{ position:relative; display:flex; gap:20px; padding-top:10px; border-top:1px solid rgba(255,255,255,0.10); }
  .dv-book-card-metric{ position:relative; display:flex; flex-direction:column; gap:2px; }
  /* Thin vertical divider between Royalties/Ad Spend/Net Profit -- base
     version for regular (non-featured) cards, offset to sit in the
     middle of this row's own 20px gap. The featured card's own override
     just below re-tunes this same rule's offset for its wider 32px gap;
     both share the one rule instead of duplicating it. */
  .dv-book-card-metric + .dv-book-card-metric::before{
    content:'';
    position:absolute;
    left:-10px; top:0; bottom:0;
    width:1px;
    background: rgba(255,255,255,0.12);
  }
  .dv-book-card-metric-label{ font-size:11px; color: var(--text-dim); }
  .dv-book-card-metric-value{ font-size:15px; font-weight:700; color: var(--lime-text); font-family: var(--font-display); }
  .dv-book-card-metric-value.metric-royalties{ color:#4ade80; }
  .dv-book-card-metric-value.metric-adspend{ color:#ff6b6b; }
  .dv-book-card-metric-value.metric-profit{ color:#4ade80; }
  /* Small delta chips -- shared with the KPI row elsewhere, but this
     card renders its own markup (deltaSpan in script.js) rather than
     going through createKpiRowController, so the color rule is repeated
     here rather than shared. */
  .dv-book-card-delta{ font-size:11px; font-weight:600; font-family: var(--font-display); }
  .dv-book-card-delta.up{ color:#4ade80; }
  .dv-book-card-delta.down{ color:#ff6b6b; }
  /* #1 by profit: twice the grid width, cover and title/metrics side by
     side instead of stacked -- .dv-book-card-info is a plain block
     wrapper for every other card (no layout change there), so this is
     the only place its own flex behavior kicks in.
     Cover width is the fixed side (42%, same as the original design) --
     an earlier version instead stretched the cover to match the info
     column's own content height, which lined the two up exactly but let
     a tall info column blow the cover's derived width (and the whole
     card) up bigger than intended. Width-driven keeps the featured
     card's own size predictable and bounded regardless of how tall the
     info column gets; info is vertically centered against whatever
     height that leaves rather than forced to match it exactly.
     On top of that (not instead of it): a zoomed, full-card background
     photo sits behind both the boxed cover and the info column (::before,
     z-index:0, --cover-bg set per-card in script.js), with a left-to-
     right gradient (::after, z-index:1) fading from solid --surface to
     transparent so it doesn't fight the text for legibility. Both boxed
     cover and info sit above that at z-index:2, unchanged from the
     original design otherwise. overflow:hidden clips the zoomed photo to
     the card's own rounded corners. */
  .dv-book-card-featured{
    position:relative;
    overflow:hidden;
    display:flex;
    gap:20px;
  }
  /* Photo + left-to-right fade, stacked as two background-image layers on
     the same pseudo-element (previously the fade was a separate ::after)
     -- freed up ::after entirely for the animated rainbow border below,
     since the border technique needs its own dedicated overlay and this
     element already had both pseudo-element slots spoken for. */
  .dv-book-card-featured::before{
    content:'';
    position:absolute;
    inset:0;
    background-image:
      linear-gradient(to right, var(--surface) 0%, var(--surface) 55%, transparent 100%),
      var(--cover-bg, none);
    /* `cover` on the photo layer -- a hand-tuned width % (128%, then
       82% auto trying for "less cropped") kept needing to be re-tuned
       by hand every time this card's own box proportions changed
       elsewhere (span-3 width, row-height tweaks, etc.), and a fixed
       width% with height:auto has no guarantee the resulting height
       actually reaches the box's own height -- when it falls short,
       that's a real gap where the photo just doesn't fill the box
       (reported as the background "not fitting"/misaligned). `cover`
       scales to the minimum needed to always fill the box with zero
       gaps, whatever its aspect ratio happens to be now or later, at
       the cost of cropping a bit more of the image than a hand-tuned
       percentage might. Only the photo (2nd layer) uses `cover` -- the
       fade (1st layer) stays full-size. */
    background-size: 100% 100%, cover;
    background-position: 0 0, left center;
    background-repeat: no-repeat;
    z-index:0;
  }
  .dv-book-card-featured .dv-book-card-cover{
    position:relative;
    z-index:2;
    width:42%;
    flex-shrink:0;
    margin-bottom:0;
    /* Centered, not flex-start -- with the info column often taller
       than the (width-driven, fixed-aspect-ratio) cover, flex-start
       left all the leftover row height sitting below the cover as one
       lopsided gap. Centering it splits that leftover evenly above and
       below instead, same idea as .dv-book-card-info's own
       justify-content:center below. */
    align-self:center;
  }
  /* Shine sweep -- top 3 books only (#1 plus the two regular cards that
     share its page), not every card reachable via pagination. Was
     briefly every card with a cover regardless of rank; scoped back down
     with :nth-child(-n+3) (rank == DOM position, BOOKS is pre-sorted by
     profit) per the user's own "that's only for top 3 books" request.
     A diagonal light streak that darts across quickly, then stays hidden
     off to the side for most of the 8s cycle before doing it again. A
     translated fixed-size band (transform), not an animated
     background-position percentage -- that approach rendered
     inconsistently against this box's non-square (3/4) aspect ratio,
     which is what looked "glitched". Regular cards don't need their own
     clip-path (below): their cover has no border-radius of its own
     (flush/full-bleed, radius:0 -- see the :not(.dv-book-card-featured)
     override), and .dv-book-card-cover-img's own base overflow:hidden
     already clips the streak to that plain rectangle; the outer card's
     own overflow:hidden+16px radius rounds just the very corners on top
     of that, same as it already does for the static photo underneath. */
  .dv-book-card-featured .dv-book-card-cover-img{
    /* Belt-and-suspenders clip on top of overflow:hidden -- large
       transform distances on the shine streak below shouldn't be able
       to paint outside this box regardless, but clip-path forces it at
       the compositor level instead of relying on overflow alone. */
    clip-path: inset(0 round 9px);
  }
  @keyframes dv-cover-shine{
    0%{ transform: translateX(-120%) rotate(20deg); opacity:0; }
    4%{ opacity:1; }
    14%{ opacity:1; }
    18%{ transform: translateX(480%) rotate(20deg); opacity:0; }
    100%{ transform: translateX(480%) rotate(20deg); opacity:0; }
  }
  .dv-book-card-featured .dv-book-card-info{
    position:relative;
    z-index:2;
    flex:1;
    min-width:0;
    display:flex;
    flex-direction:column;
    justify-content:center;
  }
  .dv-book-card-badges{ display:flex; align-items:center; gap:8px; margin-bottom:10px; }
  /* Base badge look, shared by every card's #N -- purple by default
     (regular cards keep just this). #1 gets the animated rainbow on top
     via .dv-book-card-rank-rainbow below; every card (#1 included) is
     pinned to the container's own top-left corner over the cover. */
  .dv-book-card-rank{
    position:absolute;
    /* Flush with the cover's own top-left corner (right on its border
       line), not inset into the box -- sits right at the edge instead
       of floating a few px in from it. */
    top:0; left:0;
    z-index:2;
    font-family: var(--font-display); font-weight:700; font-size:11.5px;
    color:var(--pd-on,#fff); text-shadow: 0 1px 2px rgba(0,0,0,0.35);
    background:var(--pd-7c3aed);
    box-shadow: 0 2px 8px rgba(0,0,0,0.35);
    /* Sharp on the corner/left edge it's flush against, rounded on the
       side facing into the cover -- reads as a tag/ribbon sitting
       exactly in the corner rather than a rounded chip overlapping it. */
    border-radius:0 6px 6px 0;
    padding:2px 7px;
  }
  /* Cover actions sit together in the top-right corner. The AI star is
     anchored on the far right; its separate text pill appears to the
     left, leaving the familiar round icon completely unchanged. */
  .dv-book-card-cover-actions{
    position:absolute; top:8px; right:8px; z-index:3;
    display:flex; align-items:center; gap:5px;
    opacity:0; transform:translateY(-3px); pointer-events:none;
    transition:opacity .16s ease, transform .16s ease;
  }
  /* Book #1 uses the outer card as its action anchor, leaving its cover
     clear while the controls remain at the far top-right. */
  .dv-book-card > .dv-book-card-cover-actions{
    top:12px;
    right:12px;
  }
  .dv-book-card:hover .dv-book-card-cover-actions,
  .dv-book-card:focus-within .dv-book-card-cover-actions{
    opacity:1; transform:translateY(0); pointer-events:auto;
  }
  .dv-book-card-ai-btn,
  .dv-book-card-expand-btn{
    position:relative;
    z-index:3;
    width:24px; height:24px;
    border-radius:50%; border:none; background:none; padding:0;
    display:flex; align-items:center; justify-content:center;
    cursor:pointer; overflow:hidden;
    box-shadow:0 2px 8px rgba(0,0,0,0.4);
  }
  .dv-book-card-ai-btn{ transition:transform .15s ease; flex-shrink:0; }
  .dv-book-card-expand-btn{ flex-shrink:0; transition:transform .15s ease, background .15s ease; }
  .dv-book-card-ai-bg{
    position:absolute; inset:0;
    border-radius:inherit;
    background: rgba(0,0,0,0.7);
  }
  .dv-book-card-ai-btn:hover,
  .dv-book-card-expand-btn:hover{ transform:scale(1.1); }
  .dv-book-card-ai-btn svg,
  .dv-book-card-expand-btn svg{
    position:relative; z-index:1;
    width:13px; height:13px;
    fill:#fff; stroke:none;
    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.3));
  }
  .dv-book-card-expand-btn svg{ fill:none; stroke:#fff; stroke-width:2; }
  /* Plain white, no animation -- the animated rainbow gradient-text this
     class name still implies was removed per user request. Kept the
     class name/selector as-is rather than touching script.js's own
     reference to it, just redefined what it does. */
  .dv-book-card-title-rainbow{ color:#fff; }
  /* Regular (non-#1) cards: full-bleed cover photo flush with the card's
     own top corners, rank badge floating over its bottom-left corner as a
     pill, title/metrics on plain card background below it -- the layout
     from the reference mockup the user shared (a booking-app profile
     card: full-width photo, a small pill tag near the photo's bottom
     edge, name below the photo, details further below that). #1 is
     deliberately excluded (:not(.dv-book-card-featured)) and keeps its
     existing side-by-side boxed-cover layout untouched, per the user's
     ask to leave book #1 as it is. */
  .dv-book-card:not(.dv-book-card-featured){
    padding:0;
    overflow:hidden;
  }
  .dv-book-card:not(.dv-book-card-featured) .dv-book-card-cover{
    margin-bottom:0;
    border-radius:0;
    border:none;
    box-shadow:none;
  }
  .dv-book-card:not(.dv-book-card-featured) .dv-book-card-cover-img{
    box-shadow:none;
  }
  .dv-book-card:not(.dv-book-card-featured) .dv-book-card-info{
    padding:14px;
  }
  /* Pill instead of the corner-flush ribbon shape (.dv-book-card-rank's
     base rule) -- floating over a photo edge reads as a tag/chip, not a
     ribbon pinned to a boxed cover's actual corner, which no longer
     exists here. Pinned to the top now that the title overlay (below)
     occupies the cover's bottom edge instead. Now applies to EVERY card
     including #1 (was :not(.dv-book-card-featured)-scoped, back when #1
     used the special star-image badge instead of a pill -- removed per
     user request, #1 sits on the exact same pill as every other card
     now). Kept as a compound selector (.dv-book-card .dv-book-card-rank,
     specificity 0,2,0) rather than a bare repeat of the base rule's own
     selector (0,1,0) -- guarantees this always wins regardless of the
     two rules' order in the file, instead of quietly depending on this
     one happening to appear later. */
  .dv-book-card .dv-book-card-rank{
    top:8px; left:8px;
    border-radius:20px;
  }
  /* Title moved onto the cover itself, in the corner the rank pill used
     to occupy, at the user's request -- a dark bottom-up scrim behind it
     keeps white text readable regardless of how bright the photo is at
     that edge, without needing a real blur. Deliberately ::before, not
     ::after -- .dv-book-card-cover-img's own ::after is the shine sweep
     (below), and both selectors match the exact same element for a photo
     card (coverHtml puts both classes on one div), so putting the scrim
     on ::after too would have let its higher specificity silently win
     the cascade for the shared `background` property and blank out the
     shine's own gradient entirely. */
  .dv-book-card:not(.dv-book-card-featured) .dv-book-card-cover::before{
    content:'';
    position:absolute; left:0; right:0; bottom:0; height:62%;
    background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
    pointer-events:none;
    z-index:1;
  }
  .dv-book-card-cover-title{
    position:absolute;
    left:8px; right:8px; bottom:8px;
    z-index:2;
  }
  /* Tag row sits below the title (bookTagsChipsHtml(id, 3) is emitted
     last in coverTitleHtml) -- reset both margins from the shared
     .book-tag-chips rule, which assumes it's below other text in the
     info column, not stacked under a title inside a photo overlay. */
  .dv-book-card-cover-title .book-tag-chips{ margin:5px 0 0; }
  /* Plain grey text here, not the lime pill the shared .book-tag-chip
     look uses everywhere else (Book Analytics, Book Detail, list rows) --
     a fixed light grey rather than a theme variable since this sits over
     a photo (same reasoning as the title's own fixed white), and no
     pill background/padding at the user's request. */
  .dv-book-card-cover-title .book-tag-chip{
    background:none; padding:0; border-radius:0;
    color: rgba(255,255,255,0.6); font-weight:500;
    text-shadow: 0 1px 2px rgba(0,0,0,0.6);
  }
  .dv-book-card-cover-title-row{
    display:flex; align-items:center; gap:6px;
    font-family: var(--font-display); font-weight:700; font-size:15px;
    color:#fff; text-shadow: 0 1px 3px rgba(0,0,0,0.6);
    white-space:nowrap; overflow:hidden;
  }
  .dv-book-card-cover-title-row > *:last-child{ overflow:hidden; text-overflow:ellipsis; }
  /* The book's own accent color (already set as --accent/--rgb per card,
     same custom properties the placeholder-cover glow uses) as a small
     dot before the title -- ties the overlay back to the card's assigned
     color now that the boxed, tinted cover it used to live on is gone. */
  .dv-book-card-cover-dot{
    width:7px; height:7px; border-radius:50%; flex-shrink:0;
    background: var(--accent, var(--lime-text));
    box-shadow: 0 0 4px rgba(var(--rgb, var(--lime-rgb)), 0.8);
  }
  .dv-book-card-badges .dv-book-card-delta{ font-size:12px; }
  .dv-book-card-badges-sub{ font-size:11.5px; color: var(--text-dim); }
  .dv-book-card-featured .dv-book-card-title{ font-size:19px; white-space:normal; margin-bottom:6px; }
  .dv-book-card-featured .dv-book-card-sub{ font-size:13px; margin-bottom:20px; }
  /* Border-bottom, not just the top one already separating this row
     from the title/sales line above -- same thin-line treatment on
     both sides of the metrics row now, with extra padding on both
     edges (was 16px top-only) for more breathing room around the
     numbers overall. */
  .dv-book-card-featured .dv-book-card-metrics{
    gap:32px;
    padding:20px 0;
    border-bottom:1px solid rgba(255,255,255,0.10);
  }
  .dv-book-card-featured .dv-book-card-metric{ position:relative; gap:6px; }
  /* Thin vertical divider between Royalties/Ad Spend/Net Profit --
     sits exactly in the middle of the row's own 32px gap (::before on
     every metric but the first, offset half the gap to its left)
     rather than adding its own padding/border, so it doesn't disturb
     the existing gap-based spacing at all. */
  .dv-book-card-featured .dv-book-card-metric + .dv-book-card-metric::before{
    content:'';
    position:absolute;
    left:-16px; top:0; bottom:0;
    width:1px;
    background: rgba(255,255,255,0.12);
  }
  .dv-book-card-featured .dv-book-card-metric-label{ font-size:12.5px; }
  .dv-book-card-featured .dv-book-card-metric-value{ font-size:16px; }
  .dv-book-card-featured .dv-book-card-view-btn{
    display:inline-flex; align-items:center; gap:6px;
    margin-top:22px; align-self:flex-start;
    font-size:12.5px; padding:9px 16px;
  }
  .dv-book-card-featured .dv-book-card-view-btn svg{ stroke:currentColor; }

  .dv-viewall{ width:100%; justify-content:center; margin-top:12px; }

  /* Same "Show more" button style as the Amazon Ads Campaigns table's own
     (see initAmzRowsCap in script.js -- that one toggles a capped table
     open/closed; this one just navigates to Book Analytics, but the
     look/feel is meant to match exactly) -- text-only, no pill background
     or border, small muted label with a chevron that's the only real
     affordance. */
  .amz-show-more-btn{
    pointer-events:auto;
    display:flex; align-items:center; gap:4px;
    background:none; border:none;
    font-family: var(--font-body); font-weight:300; font-size:12px; letter-spacing:.01em;
    color: var(--text-muted); cursor:pointer;
  }
  .amz-show-more-btn:hover{ color: var(--text); }
  .amz-show-more-btn svg{ width:12px; height:12px; stroke:currentColor; fill:none; stroke-width:1.8; transition: transform .15s ease; }
  .amz-show-more-btn.expanded svg{ transform: rotate(180deg); }
  /* KDP Report's "Top Books" card: one row's worth of cards shown at a
     time (page 1 is #1's featured row: 3+1+1 = 5 column units; every
     page after has no featured card, so 5 regular cards fill the same
     row instead), paged in place with the arrow/dots below rather than
     growing the card forever. Card/List view is a separate, independent
     toggle (see script.js's showKdpBooksPage/kdpBooksViewToggle) — it
     still swaps the whole thing for the full list-view table. */
  .dv-books-wrap{ position:relative; }
  /* List-view state: the whole card grid (including #1's featured card)
     hides in favor of the list-view table below, see .dv-book-list and
     setKdpBooksView in script.js. */
  .dv-book-grid.hidden{ display:none; }
  /* Right-edge "next page" control -- inside the card (not overlapping
     its border, which would risk clipping against the panel's own
     edge), vertically centered against the row of cards. */
  /* Page-position dots, above "See more books" -- grey by default, the
     active one switches to the app's own accent color (--lime, purple in
     every current theme) so it reads at a glance which page is showing. */
  .amz-show-more{
    position:relative;
    z-index:1;
    display:flex; align-items:center; justify-content:center;
    margin-top:14px;
    pointer-events:auto;
    transition: opacity .18s ease;
  }
  .dv-books-card{ position:relative; }
  .amz-show-more.hidden{ opacity:0; pointer-events:none; visibility:hidden; }
  /* "See more books" expanded state: every book as a table row instead of
     a card -- small cover thumbnail + title in one cell, each metric its
     own column, so a long catalog reads as a scannable table rather than
     a wall of cards. Reuses the app's own .amz-table row/cell look. */
  .dv-book-list.hidden{ display:none; }
  /* Sticky header instead of the Campaigns table's full pinned/scroll-
     pane split -- this table has far fewer columns and no horizontal
     scroll to coordinate a frozen pane against, so a plain
     position:sticky on the header cells gets the same "header stays put
     while rows scroll" result without needing that whole apparatus. */
  /* No background here, deliberately -- matches the Campaigns table's own
     sticky header (.amz-table th), which stays fully transparent so rows
     can show faintly through it while scrolling underneath instead of
     sitting on an opaque slab. Previously had `background: var(--surface)`,
     which was the one place this table's header diverged from Campaigns'. */
  .dv-book-list-table th:first-child, .dv-book-list-table td:first-child{ padding-left:0; }
  .dv-book-list-table th:last-child, .dv-book-list-table td:last-child{ padding-right:0; }
  .dv-book-list-table tbody tr{ cursor:pointer; transition: background .12s ease; }
  .dv-book-list-table tbody tr:hover{ background: rgba(255,255,255,0.04); }
  .dv-book-list-title-cell{ display:flex; align-items:center; gap:12px; white-space:nowrap; }
  /* Bigger per user request (was 30x40/11px) -- row height in the
     virtualized list box (kdpListRowH) is measured off the real rendered
     row every render, so it picks this size up automatically, no
     separate fix needed there. */
  .dv-book-list-cover{
    width:42px; height:56px; flex-shrink:0; border-radius:6px; overflow:hidden;
    background: linear-gradient(155deg, rgba(255,255,255,0.22), rgba(255,255,255,0.02) 55%, rgba(var(--lime-rgb),0.18) 100%);
    border:1px solid rgba(255,255,255,0.18);
    box-shadow: 0 2px 8px rgba(0,0,0,0.3);
    display:flex; align-items:center; justify-content:center;
    font-family: var(--font-display); font-weight:700; font-size:14px;
  }
  .dv-book-list-cover img{ width:100%; height:100%; object-fit:cover; object-position:top; display:block; }
  .dv-book-list-title{ font-size:14.5px; font-weight:600; }
  .dv-book-list-table td.metric-royalties, .dv-book-list-table td.metric-profit{ color:#4ade80; font-weight:700; font-family: var(--font-display); }
  .dv-book-list-table td.metric-adspend{ color:#ff6b6b; font-weight:700; font-family: var(--font-display); }
  /* Dark and Glass mode get a brighter, more saturated green for every
     positive/"up" number than Light mode's own #4ade80 above -- Light's
     white-ish surface already gives #4ade80 enough contrast, but on the
     darker Dark/Glass surfaces this brighter green reads better. */
  body:not(.theme-light) .delta.up,
  body:not(.theme-light) .dv-kpi-delta.up,
  body:not(.theme-light) .dv-mini-delta.up,
  body:not(.theme-light) .dv-book-card-metric-value.metric-royalties,
  body:not(.theme-light) .dv-book-card-metric-value.metric-profit,
  body:not(.theme-light) .dv-book-card-delta.up,
  body:not(.theme-light) .dv-book-list-table td.metric-royalties,
  body:not(.theme-light) .dv-book-list-table td.metric-profit{
    color:#7cfc00;
  }

  .dv-opp-row, .dv-alert-row{ display:flex; align-items:center; gap:10px; padding:10px 0; border-bottom:1px solid var(--border-soft); }
  .dv-opp-row:last-of-type, .dv-alert-row:last-of-type{ border-bottom:none; }
  .dv-opp-icon{ width:30px; height:30px; border-radius:8px; background: var(--surface-2); display:flex; align-items:center; justify-content:center; font-size:15px; flex-shrink:0; }
  .dv-opp-info, .dv-alert-info{ flex:1; min-width:0; }
  .dv-opp-title, .dv-alert-title, .dv-rec-title{ font-size:12.5px; font-weight:600; }
  .dv-opp-sub, .dv-alert-sub, .dv-rec-sub{ font-size:11px; color: var(--text-dim); margin-top:2px; }
  .dv-opp-score{ font-family: var(--font-display); font-weight:700; font-size:14px; color: var(--lime-text); background: var(--lime-wash); border-radius:50%; width:30px; height:30px; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
  .dv-alert-time{ font-size:11px; color: var(--text-dim); flex-shrink:0; }
  .dv-rec-row{ padding:10px 0; border-bottom:1px solid var(--border-soft); }
  .dv-rec-row:last-of-type{ border-bottom:none; }

  .list-item{
    display:flex; align-items:center; gap:10px;
    padding:11px 4px;
    border-bottom:1px solid var(--border-soft);
    cursor:pointer;
  }
  .list-item:last-child{ border-bottom:none; }
  .list-item:hover .list-name{ color: var(--lime-text); }
  .list-icon{
    width:16px; height:16px;
    color: var(--text-dim);
    flex-shrink:0;
    display:flex; align-items:center; justify-content:center;
  }
  .list-icon svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.6; }
  .list-name{
    font-size:13.5px;
    font-weight:500;
    color: var(--text);
    transition:color .12s ease;
  }
  .list-item .arrow{
    margin-left:auto;
    color: var(--text-dim);
    font-size:13px;
  }

  /* ---- Analytics panel ---- */
  .chart-card{
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:var(--radius);
    padding:22px;
    margin-bottom:22px;
  }
  .chart-card .label{
    font-family:var(--font-mono);
    font-size:10.5px;
    letter-spacing:0.08em;
    text-transform:uppercase;
    color: var(--text-dim);
    margin-bottom:16px;
  }
  .bars{
    display:flex;
    align-items:flex-end;
    gap:8px;
    height:140px;
  }
  .bars .bar{
    flex:1;
    background: var(--surface-2);
    border:1px solid var(--border);
    border-radius:4px 4px 0 0;
    position:relative;
  }
  .bars .bar.hi{ background: linear-gradient(to top, var(--lime-wash), transparent); border-color:var(--lime-dim); }

  /* ---- Settings panel ---- */
  .settings-row{
    display:flex;
    justify-content:space-between;
    align-items:flex-start;
    padding:20px 0;
    border-bottom:1px solid var(--border-soft);
    max-width:820px;
  }
  .settings-row:last-child{ border-bottom:none; }
  .settings-row .s-title{ font-weight:600; font-size:14px; margin-bottom:4px; }
  .settings-row .s-desc{ font-size:12.5px; color: var(--text-muted); max-width:420px; line-height:1.5; }
  .toggle{
    width:38px; height:22px;
    background: var(--surface-2);
    border:1px solid var(--border);
    border-radius:20px;
    position:relative;
    cursor:pointer;
    flex-shrink:0;
  }
  .toggle.on{ background: var(--lime-wash); border-color:var(--lime-dim); }
  .toggle::after{
    content:'';
    position:absolute;
    top:2px; left:2px;
    width:16px; height:16px;
    border-radius:50%;
    background: var(--text-dim);
    transition: left .15s ease, background .15s ease;
  }
  .toggle.on::after{ left:18px; background: var(--lime); }
  /* Row selection checkbox, sits to the toggle's left in the same cell
     (see initAmzCampaignsSelection in script.js) — hidden until you
     hover that row, EXCEPT once it's actually checked, which keeps a
     selected row visibly marked even after the mouse moves away
     (otherwise selecting something and looking elsewhere would make it
     look like the selection silently vanished). The header's own
     "select all" checkbox (.amz-select-all) is deliberately NOT scoped
     by this rule -- it's always visible, a different affordance than a
     per-row action. */
  .amz-row-select-wrap{ display:flex; align-items:center; gap:6px; }
  /* Round, not the browser's native rectangular checkbox -- appearance:none
     drops the native box (and its checkmark) entirely, replaced with a
     plain circle + a small centered dot that only shows once :checked,
     rather than trying to force a checkmark glyph into a tiny circle. */
  .amz-row-select, .amz-select-all{
    appearance:none; -webkit-appearance:none;
    width:14px; height:14px; flex-shrink:0; cursor:pointer;
    border-radius:50%;
    /* Thin accent stroke at 70% opacity instead of a plain 1.5px grey
       border -- same stroke treatment as the KDP search pill's own
       border, applied here too for consistency across the app's
       selection controls. */
    border:1px solid rgba(var(--lime-rgb),0.7);
    background: var(--surface-2);
    position:relative;
    transition: background .12s ease, border-color .12s ease;
  }
  .amz-row-select:checked, .amz-select-all:checked{
    background: var(--lime);
    border-color: var(--lime);
  }
  .amz-row-select:checked::after, .amz-select-all:checked::after{
    content:'';
    position:absolute; top:50%; left:50%;
    width:4px; height:4px;
    border-radius:50%;
    background:#fff;
    transform: translate(-50%,-50%);
  }
  .amz-row-select{
    opacity:0;
    transition: opacity .12s ease, background .12s ease, border-color .12s ease;
  }
  tr:hover .amz-row-select,
  .amz-row-select:checked{ opacity:1; }
  .settings-field{ margin:16px 0; }
  .settings-field label{
    display:block; font-size:11.5px; color: var(--text-dim); margin-bottom:6px;
    font-family: var(--font-mono); letter-spacing:0.04em; text-transform:uppercase;
  }
  .settings-field input{
    width:100%; background: var(--surface-2); border:1px solid var(--border); border-radius:8px;
    padding:10px 12px; color: var(--text); font-size:13.5px; font-family: var(--font-body);
  }
  .settings-field input:focus{ outline:none; border-color: var(--lime-dim); }

  /* ---- Ask Sphere drawer (inline, pushes layout) ---- */
  /* Slides in via transform, not width. It used to animate width:0→400px,
     which — on a position:fixed, 100vh panel carrying a live
     backdrop-filter — forces the browser to re-run layout AND re-sample
     the blur on every single frame of the transition (width changes are
     main-thread layout work; blur has to resample its capture region as
     the box resizes). That's what "laggy on open" actually was. Keeping
     the box permanently at its full 400px and only moving it with
     translateX instead means the size never changes — just its position
     — so the whole animation is compositor-only (GPU), the same way a
     transform-based animation always is regardless of what's on the box. */
  .ai-drawer{
    width:400px;
    max-width:400px;
    flex-shrink:0;
    background: var(--surface);
    border-left:1px solid var(--border);
    overflow:hidden;
    transform: translateX(100%);
    transition: transform .26s cubic-bezier(.2,.7,.2,1);
    will-change: transform;
    display:flex;
    flex-direction:column;
    position:fixed;
    top:0;
    right:0;
    height:100vh;
    z-index:60;
    pointer-events:none;
  }
  .ai-drawer.open{
    transform: translateX(0);
    pointer-events:auto;
  }

  .ai-drawer-header, .ai-drawer-body, .ai-drawer-footer{
    width:400px;
  }
  .ai-drawer-header{
    height:56px;
    flex-shrink:0;
    display:flex;
    align-items:center;
    justify-content:space-between;
    padding:0 16px;
    border-bottom:1px solid var(--border);
    background: var(--surface);
    z-index:5;
  }
  .ai-drawer-title{
    display:flex; align-items:center; gap:8px;
    font-family: var(--font-display);
    font-weight:600;
    font-size:14px;
  }
  .ai-drawer-title .dot{
    width:6px; height:6px; border-radius:50%;
    background: var(--lime);
    box-shadow: 0 0 6px var(--lime);
    flex-shrink:0;
  }
  .ai-drawer-title-text{ display:flex; flex-direction:column; line-height:1.25; }
  .ai-drawer-name{ font-family: var(--font-display); font-weight:600; font-size:14px; color: var(--text); }
  .ai-drawer-sub{ font-family: var(--font-body); font-weight:400; font-size:11px; color: var(--text-dim); }
  /* Small "powered by Claude" attribution — reused wherever the Sphere
     assistant surfaces (drawer, expanded window, full-page panel) so it's
     always clear which model the chat is running on. The mark itself is
     the actual Claude logo the user supplied, embedded once here and
     referenced by every badge instance below via background-image so the
     ~110KB payload isn't duplicated per usage. */
  .claude-logo-icon{
    display:inline-block;
    width:11px; height:11px;
    flex-shrink:0;
    background-image: url(assets/claude-logo-icon.png);
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
  }
  .claude-badge{
    display:inline-flex; align-items:center; gap:5px;
    font-size:10.5px; color: var(--text-dim); font-weight:500;
    white-space:nowrap;
  }
  .na-claude-badge{ font-size:12px; margin-top:10px; gap:6px; color: var(--text-muted); }
  .na-claude-badge .claude-logo-icon{ width:14px; height:14px; }
  /* Sits centered under the rotating sphere mark now, not squeezed into
     the topbar next to the close button (where its text used to truncate
     against it) — see .ai-expand-center below. */
  .ai-expand-claude-badge{ margin-bottom:16px; }
  .ai-drawer-actions{ display:flex; align-items:center; gap:14px; color: var(--text-dim); }
  .ai-drawer-actions svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.6; cursor:pointer; }
  .ai-drawer-actions svg:hover{ stroke: var(--text); }
  .ai-drawer-actions .claude-badge{ cursor:default; }

  .ai-drawer-body{
    flex:1;
    overflow-y:auto;
    padding:32px 20px 16px 20px;
    display:flex;
    flex-direction:column;
    align-items:center;
    text-align:center;
    background-image: radial-gradient(var(--border) 1px, transparent 1px);
    background-size: 16px 16px;
    background-position: -4px -4px;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .ai-drawer-body::-webkit-scrollbar{ display:none; }
  .ai-mark{
    width:112px; height:112px;
    margin-bottom:18px;
    position:relative;
    display:flex;
    align-items:center;
    justify-content:center;
    --na-avatar-size:112;
  }
  .ai-greeting{
    font-family: var(--font-display);
    font-size:20px;
    font-weight:600;
    margin-bottom:4px;
  }
  .ai-subgreeting{
    font-size:13px;
    color: var(--text-muted);
    margin-bottom:26px;
  }

  /* Chat log — the actual back-and-forth once a message is sent, styled
     to match the landing page's Sphere AI mockup exactly (same bubble
     shapes/colors) rather than approximating it. Hidden until the first
     message; .ai-drawer-body.chatting swaps the greeting/suggestions out
     for this. */
  .ai-chat-log{ display:none; width:100%; text-align:left; }
  .ai-drawer-body.chatting .ai-greeting,
  .ai-drawer-body.chatting .ai-subgreeting,
  .ai-drawer-body.chatting .ai-suggestions{ display:none; }
  .ai-drawer-body.chatting{ align-items:stretch; justify-content:flex-start; }
  /* In a conversation the avatar is not shown (mountSphereHub also stops rendering it). */
  .ai-drawer-body.chatting .ai-mark{ display:none; }
  .ai-drawer-body.chatting .ai-chat-log{ display:block; }
  .chat-row{ display:flex; margin-bottom:10px; }
  .chat-row.me{ justify-content:flex-end; }
  .chat-bubble{
    max-width:86%; padding:11px 15px; border-radius:14px;
    font-size:13.5px; line-height:1.55;
  }
  .chat-bubble.me{
    background: rgba(var(--lime-rgb),0.12);
    border:1px solid rgba(var(--lime-rgb),0.34);
    border-bottom-right-radius:5px;
    color: var(--text);
  }
  .chat-bubble.ai{
    background: var(--surface-2);
    border:1px solid var(--border);
    border-bottom-left-radius:5px;
    color: var(--text-muted);
  }
  .chat-bubble.ai b{ color: var(--text); font-weight:600; }
  /* ORB's own styles (answers, tables, thinking status, model menu, expanded chat layout) live in orb/frontend/orb.css */
  .chat-typing{ display:inline-flex; gap:4px; align-items:center; }
  .chat-typing i{
    width:5px; height:5px; border-radius:50%; background: var(--lime); display:block;
    animation: chatTyping 1.3s ease-in-out infinite;
  }
  .chat-typing i:nth-child(2){ animation-delay:.18s; }
  .chat-typing i:nth-child(3){ animation-delay:.36s; }
  @keyframes chatTyping{ 0%,60%,100%{ opacity:.28; transform:translateY(0);} 30%{ opacity:1; transform:translateY(-3px);} }

  .ai-suggestions{
    width:100%;
    display:flex;
    flex-direction:column;
    gap:8px;
    text-align:left;
    padding:0 10px;
  }
  .ai-suggestion{
    display:flex;
    align-items:center;
    gap:10px;
    background: var(--surface-2);
    border:1px solid var(--border-soft);
    border-radius:9px;
    padding:9px 11px;
    height:52px;
    cursor:pointer;
    position:relative;
    width:70%;
    margin:0 auto;
    transform: scale(1);
    transition: border-color .15s ease, background .15s ease, transform .15s ease, box-shadow .15s ease;
  }
  .ai-suggestion::before{
    content:'';
    position:absolute;
    left:0; top:50%;
    height:16px;
    width:3px;
    border-radius:2px;
    /* Hardcoded purple, not var(--lime) -- that variable's actual color
       varies by theme (real lime green in some, white in dark mode) and
       is only recolored to purple inside .raa (Amazon Ads React); this
       card lives in the global AI drawer, so it showed as faded lime
       green in whichever theme didn't happen to override --lime already. */
    background: var(--pd-a855f7);
    opacity:0;
    transform: translateY(-50%) scaleY(0);
    transition: opacity .2s ease, transform .25s cubic-bezier(.34,1.56,.64,1);
  }
  .ai-suggestion:hover{
    border-color: var(--pd-8846c6);
    background: rgba(var(--pd-rgb),.1);
    transform: scale(1.012);
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    z-index:1;
  }
  .ai-suggestion:hover::before{ opacity:1; transform: translateY(-50%) scaleY(1); }
  .ai-suggestion:hover .ai-sug-icon{ border-color: var(--pd-8846c6); color: var(--pd-c084fc); }
  .ai-suggestion .ai-sug-icon{
    width:26px; height:26px;
    border-radius:7px;
    background: var(--surface);
    border:1px solid var(--border);
    display:flex; align-items:center; justify-content:center;
    flex-shrink:0;
    color: var(--text-dim);
  }
  .ai-suggestion .ai-sug-icon svg{ width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:1.6; }
  .ai-suggestion .ai-sug-text{ flex:1; min-width:0; }
  .ai-suggestion .ai-sug-title{ font-size:12px; font-weight:600; color: var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .ai-suggestion .ai-sug-desc{ font-size:10.5px; color: var(--text-muted); margin-top:1px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .ai-suggestion .ai-sug-arrow{
    flex-shrink:0;
    color: var(--text-dim);
    opacity:0;
    transform: translateX(-4px);
    transition: opacity .15s ease, transform .15s ease, color .15s ease;
  }
  .ai-suggestion .ai-sug-arrow svg{ width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .ai-suggestion:hover .ai-sug-arrow{ opacity:1; transform: translateX(0); color: var(--pd-c084fc); }

  .ai-drawer-footer{
    flex-shrink:0;
    padding:14px 16px 16px 16px;
    border-top:1px solid var(--border-soft);
    background: var(--surface);
  }
  .ai-input-box{
    background: var(--bg);
    border:1px solid var(--lime-dim);
    border-radius:12px;
    padding:12px 14px;
  }
  .ai-input-box input{
    width:100%;
    background:none;
    border:none;
    outline:none;
    color: var(--text);
    font-family: var(--font-body);
    font-size:13px;
  }
  .ai-input-box input::placeholder{ color: var(--text-dim); }
  .ai-input-row{
    display:flex;
    align-items:center;
    justify-content:space-between;
    margin-top:10px;
  }
  .ai-ask-chip{
    display:flex; align-items:center; gap:6px;
    border:1px solid var(--border);
    border-radius:20px;
    padding:5px 12px;
    font-size:12px;
    color: var(--text-muted);
  }
  .ai-ask-chip svg{ width:13px; height:13px; stroke:currentColor; fill:none; stroke-width:1.6; }
  .ai-input-actions{
    display:flex;
    align-items:center;
    gap:8px;
  }
  .ai-settings-btn{
    width:30px; height:30px;
    border-radius:50%;
    background: var(--surface-2);
    border:1px solid var(--border);
    display:flex; align-items:center; justify-content:center;
    cursor:pointer;
    color: var(--text-muted);
    transition: border-color .12s ease, color .12s ease;
  }
  .ai-settings-btn:hover{ border-color: var(--lime-dim); color: var(--lime-text); }
  .ai-settings-btn svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.6; }
  .ai-send-btn{
    width:30px; height:30px;
    border-radius:50%;
    background: var(--lime);
    border:none;
    display:flex; align-items:center; justify-content:center;
    cursor:pointer;
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.55),
      inset 0 -1px 2px rgba(0,0,0,0.12),
      0 3px 12px rgba(var(--lime-rgb),0.35);
  }
  .ai-send-btn svg{ width:15px; height:15px; stroke: var(--bg); fill:none; stroke-width:2.2; }

  .placeholder-panel{
    display:flex;
    flex-direction:column;
    align-items:flex-start;
    gap:8px;
    padding:60px 0;
    color: var(--text-dim);
    font-family: var(--font-mono);
    font-size:12.5px;
  }

  /* ---- "Coming soon" gradient-black placeholder (grey'd-out features) ---- */
  .coming-soon-panel{
    min-height:calc(100vh - 160px);
    border-radius:16px;
    border:1px solid var(--border);
    background:
      radial-gradient(circle at 22% 18%, rgba(255,255,255,0.05), transparent 55%),
      linear-gradient(165deg, #0d0f0b 0%, #000000 75%);
    display:flex;
    flex-direction:column;
    align-items:flex-start;
    justify-content:center;
    padding:60px;
  }
  .coming-soon-badge{
    font-family: var(--font-mono);
    font-size:11px;
    letter-spacing:0.1em;
    text-transform:uppercase;
    color: var(--text-dim);
    margin-bottom:10px;
  }
  .coming-soon-title{
    font-family: var(--font-display);
    font-weight:700;
    font-size:32px;
    color: var(--text-muted);
    margin-bottom:12px;
  }
  .coming-soon-desc{
    font-size:14px;
    color: var(--text-dim);
    max-width:460px;
    line-height:1.65;
    margin-bottom:22px;
  }
  .coming-soon-tag{
    display:inline-flex; align-items:center; gap:8px;
    font-family: var(--font-mono); font-size:11px; font-weight:600; letter-spacing:0.04em;
    color: var(--text-muted);
    background: rgba(255,255,255,0.04);
    border:1px solid var(--border);
    padding:7px 15px;
    border-radius:999px;
  }
  .coming-soon-tag .dot{ box-shadow:none; opacity:0.6; }

  /* ---- Integrations page ---- */
  .int-subtitle{ font-size:13.5px; color: var(--text-muted); margin:8px 0 22px 0; max-width:640px; }
  .int-search{
    display:flex; align-items:center; gap:10px;
    background: var(--surface); border:1px solid var(--border); border-radius:10px;
    padding:12px 16px; color: var(--text-dim); font-size:13.5px; max-width:420px; margin-bottom:28px;
  }
  .int-search svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.6; flex-shrink:0; }
  .int-section-label{
    font-family: var(--font-mono); font-size:11px; letter-spacing:0.1em; text-transform:uppercase;
    color:#fff; margin:0 0 12px 0;
  }
  body.theme-light .int-section-label{ color:#000; }
  .int-grid{ display:grid; grid-template-columns:repeat(4, 1fr); gap:16px; margin-bottom:32px; }
  .int-card{
    background: var(--surface); border:1px solid var(--border); border-radius:12px;
    padding:18px; display:flex; flex-direction:column; transition: border-color .15s ease, transform .15s ease;
  }
  .int-card:hover{ border-color: var(--lime-dim); transform: translateY(-2px); }
  .int-card.coming-soon{ opacity:0.55; }
  .int-card.coming-soon:hover{ border-color: var(--border); transform:none; }

  /* ---- Community page ---- */
  .comm-toolbar{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin:8px 0 26px 0; }
  .comm-toolbar .int-search{ flex:1; margin-bottom:0; }
  .comm-grid{ display:grid; grid-template-columns:repeat(3, 1fr); gap:16px; margin-bottom:32px; }
  .comm-card{
    background: var(--surface); border:1px solid var(--border); border-radius:12px;
    padding:18px; transition: border-color .15s ease, transform .15s ease;
  }
  .comm-card:hover{ border-color: var(--lime-dim); transform: translateY(-2px); }
  .comm-card-tag{
    display:inline-block; font-size:10.5px; font-weight:700; letter-spacing:0.02em;
    padding:3px 9px; border-radius:100px; background: var(--lime-wash); color: var(--lime-text);
    margin-bottom:12px;
  }
  .comm-card-name{ font-family: var(--font-display); font-weight:600; font-size:14.5px; margin-bottom:5px; }
  .comm-card-desc{ font-size:12px; color: var(--text-muted); line-height:1.5; margin-bottom:14px; min-height:52px; }
  .comm-card-foot{
    display:flex; align-items:center; justify-content:space-between; gap:10px;
    font-size:11.5px; color: var(--text-dim); border-top:1px solid var(--border); padding-top:12px;
  }
  .comm-card-author{ display:flex; align-items:center; gap:7px; min-width:0; }
  .comm-card-avatar{
    width:19px; height:19px; border-radius:50%; flex-shrink:0;
    background: var(--surface-2); color: var(--text-muted);
    display:flex; align-items:center; justify-content:center;
    font-size:9px; font-weight:700; font-family: var(--font-display);
  }
  .comm-card-author-name{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .comm-card-stats{ flex-shrink:0; }
  .int-top{ display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; }
  .int-icon{
    width:36px; height:36px; border-radius:9px;
    display:flex; align-items:center; justify-content:center;
    font-family: var(--font-display); font-weight:700; font-size:12px; letter-spacing:-0.02em; flex-shrink:0;
  }
  .int-status{
    display:flex; align-items:center; gap:6px;
    font-size:11px; color: var(--text-dim); font-weight:600;
  }
  .int-status .dot{ width:6px; height:6px; border-radius:50%; background: var(--text-dim); }
  .int-status.connected{ color: var(--lime-text); }
  .int-status.connected .dot{ background: var(--lime); }
  .int-status.connected{ color:#22c55e; }
  .int-status.connected .dot{ background:#22c55e; }
  body.theme-light .int-status.connected{ color:#16a34a; }
  body.theme-light .int-status.connected .dot{ background:#16a34a; }
  .int-status.disconnected{ color:#ef4444; }
  .int-status.disconnected .dot{ background:#ef4444; }
  body.theme-light .int-status.disconnected{ color:#dc2626; }
  body.theme-light .int-status.disconnected .dot{ background:#dc2626; }
  .int-name{ font-family: var(--font-display); font-weight:600; font-size:15px; margin-bottom:6px; }
  .int-desc{ font-size:12.5px; color: var(--text-muted); line-height:1.5; flex:1; margin-bottom:12px; }
  .int-meta{ font-size:11px; color: var(--text-dim); margin-bottom:14px; }
  .int-btn{ width:100%; justify-content:center; }

  /* Drag-to-resize handle on the right edge of each column header — also
     used to keep the Smart Order grid's column widths in sync (see
     initResizableTable), since both views share the same columns. */
  /* Drag-to-resize handle: previously just an invisible 8px strip that
     only revealed itself (a faint highlight) once you were already
     hovering exactly over it — nothing hinted a column edge was
     draggable until you found it by accident. Two small pill-shaped bars
     make that affordance visible up front, like a grip icon; the drag
     behavior itself (initAmzColumnResize) is unchanged. */
  .col-resize{
    position:absolute; top:0; right:-4px; width:8px; height:100%;
    cursor:col-resize; z-index:2;
    display:flex; align-items:center; justify-content:center; gap:2px;
  }
  .col-resize::before, .col-resize::after{
    content:'';
    width:2px; height:14px;
    border-radius:2px;
    /* Tinted with the theme's own accent (--lime, which resolves to
       purple in Light/Glass and green in Dark) at rest, not a flat grey
       border color — brightening to the full, un-dimmed accent on hover
       below is what actually signals "grab this". */
    background: var(--lime-dim);
    transition: background .12s ease;
  }
  /* KDP Top Books list header reuses this exact same purple pill look
     between its metric column titles (Royalties/Ad Spend/Net Profit) --
     per user request to match Campaigns' header separators exactly,
     rather than the plain 1px border tried first. Its columns aren't
     resizable (no initAmzColumnResize wiring for this table), so
     `.static` strips the drag cursor/hover-brighten affordance that
     would otherwise falsely imply they are, while keeping the identical
     two-bar shape/size/color. */
  .col-resize.static{ cursor:default; pointer-events:none; }
  .col-resize:hover, .col-resize.resizing{ background: rgba(var(--lime-rgb),0.18); }
  .col-resize:hover::before, .col-resize:hover::after,
  .col-resize.resizing::before, .col-resize.resizing::after{ background: var(--lime); }
  .amz-status{
    display:inline-flex; align-items:center; gap:5px;
    font-size:11.5px; font-weight:600; padding:3px 10px; border-radius:999px;
  }
  .amz-status::before{ content:''; width:6px; height:6px; border-radius:50%; }
  /* Fixed semantic colors, not var(--lime)/theme accent — a campaign's
     delivering/paused status is a real state, the same meaning in every
     theme, not a brand color that should shift with Light/Glass/Dark
     (Glass and Light both recolor --lime to purple, which is exactly why
     "Delivering" used to show purple instead of reading as "good"). Amber
     for paused rather than red or the old neutral gray — red specifically
     means a payment failure elsewhere in this app, so it can't also mean
     "you paused this on purpose", and plain gray didn't read as a state
     needing attention at all. */
  .amz-status.delivering{ background: rgba(34,197,94,0.14); color: #22c55e; }
  .amz-status.delivering::before{ background: #22c55e; }
  .amz-status.paused{ background: rgba(245,158,11,0.14); color: #f59e0b; }
  .amz-status.paused::before{ background: #f59e0b; }

  /* Interactive chart legend */
  .chart-legend{ position:relative; }
  .legend-chip{
    display:inline-flex; align-items:center; cursor:pointer;
    padding:4px 8px; border-radius:6px; opacity:0.4; transition: opacity .12s ease, background .12s ease;
  }
  .legend-chip.active{ opacity:1; }
  .legend-chip:hover{ background: var(--surface-2); }
  .dot.orange{ background:#ff8a5b; }
  .dot.blue{ background:#5b9bff; }
  .dot.purple{ background:var(--pd-b07bff); }
  .dot.red{ background:#ff5b6b; }
  .dot.green{ background:#5bff9c; }
  .dot.teal{ background:#40e0d0; }
  .legend-add-wrap{ position:relative; margin-left:2px; }
  .legend-add-btn{
    display:flex; align-items:center; justify-content:center;
    width:20px; height:20px; border-radius:50%;
    border:1px solid var(--border); color: var(--text-dim);
    cursor:pointer; font-size:14px; line-height:1;
  }
  .legend-add-btn:hover{ border-color: var(--lime-dim); color: var(--text); }
  .legend-add-menu{
    position:absolute; top:26px; left:0; z-index:20;
    background: var(--surface); border:1px solid var(--border); border-radius:10px;
    padding:6px; min-width:130px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.35);
    display:none;
  }
  .legend-add-menu.open{ display:block; }
  .legend-add-item{
    display:flex; align-items:center; padding:7px 8px; border-radius:7px;
    font-size:12.5px; color: var(--text); cursor:pointer;
  }
  .legend-add-item:hover{ background: var(--surface-2); }
  .legend-add-item.added{ opacity:0.4; pointer-events:none; }
  .legend-add-divider{ height:1px; background: var(--border-soft); margin:5px 4px; }
  /* A small inline SVG rather than a sparkle emoji -- an emoji renders
     with whatever the OS's own emoji font/size happens to be, which
     never quite lines up with every other icon on this page (all real
     SVGs, sized and stroked to match). var(--lime-text) ties it to the
     same accent the rest of the AI surface (Sphere orb, chat bubbles)
     uses, same as the item's own text color below. */
  .legend-add-ai-icon{
    display:inline-flex; align-items:center; justify-content:center;
    width:13px; height:13px; margin-right:6px; flex-shrink:0;
  }
  .legend-add-ai-icon svg{ width:100%; height:100%; fill: var(--lime-text); }
  .legend-add-ai-item{ color: var(--lime-text); font-weight:600; }
  /* Delete button for a metric someone named themselves via Create with
     AI (see .legend-chip-ai in initCreateMetricWithAi, script.js). Hidden
     until hover so it doesn't clutter the legend at rest; opacity rather
     than display so the chip's own width doesn't jump when it appears.
     The "✕" is a ::before, not real text content -- this chip's own
     textContent doubles as its display label elsewhere (tooltip-building
     code reads it directly), so an actual text node here would leak "✕"
     onto the end of that label. */
  .legend-chip-remove{
    display:inline-flex; align-items:center; justify-content:center;
    margin-left:6px; width:14px; height:14px; border-radius:50%;
    opacity:0; transition: opacity .12s ease, background .12s ease;
  }
  .legend-chip-remove::before{ content:"✕"; color:#ff5b6b; font-size:9px; line-height:1; }
  .legend-chip-ai:hover .legend-chip-remove{ opacity:1; }
  .legend-chip-remove:hover{ background: rgba(255,91,107,0.18); }
  /* Extra dot colors for metrics a person names themselves via Create
     with AI -- the fixed set above (orange/blue/purple/red/green/teal,
     plus lime/mid/dim already spoken for by Impressions/Clicks/Ad spend)
     is already fully assigned to specific built-in metrics, so a custom
     one needs its own palette rather than colliding with an existing
     metric's color. Rotated through by count so a second and third
     custom metric are still visually distinct from the first. */
  .dot.aipink{ background:#ff6fae; }
  .dot.aigold{ background:#ffd166; }
  .dot.aicyan{ background:#22d3ee; }
  .dot.aiviolet{ background:#7978f8; }

  .amz-title-cluster{ display:flex; align-items:center; gap:10px; min-width:0; }
  /* Top Books' own title cluster -- book count sits UNDER "Top Books",
     not beside it, unlike Amazon Ads' header (which reuses the same base
     .amz-title-cluster row layout for title + several icon buttons side
     by side). Scoped to this specific instance via a second class rather
     than changing the shared rule, which Ads still needs as a row. */
  /* Search moved to sit where the view-toggle button used to (rightmost,
     flush against .dv-card-head's own right edge via its
     justify-content:space-between). The shared .amz-search-wrap pattern
     grows its input as a normal flex sibling of the icon -- fine for
     Campaigns (search leads its row, so the only thing it can push is
     Filter, which is the intended/accepted behavior there), but here
     any flex-based growth (either direction -- tried both) shoves a
     neighbor (View-toggle, or the card's own edge) sideways, which
     reads as the icon itself moving. Real fix: take the input out of
     flow entirely (position:absolute, anchored off the icon's own
     right edge) so growing it on hover is purely visual and never
     touches any other element's layout position -- the icon's own box
     size no longer includes the input at all, so nothing beside it
     ever moves. Per explicit request, the pill opens to the RIGHT of
     the icon (not left) -- the card has no overflow:hidden on it and
     this card is full-width, so there's open page space for it to
     expand into without getting clipped. */
  /* Base .amz-search-input keeps a plain var(--border) grey stroke in
     every state, expanded or not -- gave the expanded pill its own
     accent-colored border here instead of leaving it grey, using the
     same var(--lime) every other hover/active control in the app reads
     its accent from (resolves to this app's purple in the Light theme,
     lime-green in Dark, so it stays theme-correct rather than a
     hardcoded purple that would look wrong outside Light theme). */

  .amz-chart-card{ margin-bottom:28px; }
  .dv-card-sub-label{ font-size:11.5px; color: var(--text-dim); }

  /* KDP: sales-by-marketplace globe */
  .globe-section{ display:flex; gap:32px; align-items:center; flex-wrap:wrap; }
  .globe-wrap{ position:relative; flex-shrink:0; width:260px; height:260px; }
  .globe-wrap canvas{ width:260px; height:260px; cursor:grab; display:block; }
  .globe-wrap canvas.grabbing{ cursor:grabbing; }
  .globe-tooltip{
    position:absolute; top:0; left:0;
    background: var(--surface); border:1px solid var(--border); border-radius:8px;
    padding:6px 10px; font-size:12px; white-space:nowrap;
    box-shadow: 0 10px 26px rgba(0,0,0,0.35);
    pointer-events:none; display:none; transform:translate(-50%,-120%);
  }
  .globe-legend{ flex:1; min-width:240px; display:flex; flex-direction:column; gap:2px; }
  .globe-legend-row{
    display:grid; grid-template-columns: 24px 1.4fr 1fr 1fr;
    align-items:center; gap:10px; padding:8px 0; border-bottom:1px solid var(--border-soft);
    font-size:12.5px;
  }
  .globe-legend-row:last-child{ border-bottom:none; }
  .globe-flag{ font-size:15px; }
  .globe-country{ font-weight:600; }
  .globe-units{ color: var(--text-muted); }
  .globe-roy{ color: var(--lime-text); font-weight:600; text-align:right; }

  /* ---- Sphere hub page ---- */
  .na-hero{ text-align:center; padding:20px 0 8px 0; max-width:720px; margin:0 auto; }
  .na-mark{ display:grid; width:240px; height:240px; margin:0 auto 20px auto; place-items:center; }
  .na-sphere-character{
    position:relative; width:220px; height:220px; overflow:hidden; border-radius:50%;
    /* One shared 3D scene for both eyes, projected from the sphere's own
       centre (perspective-origin defaults to 50% 50%). Keep this in step
       with .na-sphere-eye's --na-eye-depth-fix. */
    perspective:640px;
    background:radial-gradient(circle at 34% 27%,var(--pd-bd5cff) 0,var(--pd-a12fff) 28%,var(--pd-8420f1) 60%,var(--pd-6812d0) 82%,var(--pd-4e079f) 100%);
    border:1px solid rgba(255,255,255,.22);
    box-shadow:inset 0 3px 5px rgba(255,255,255,.28),inset 0 -22px 32px rgba(var(--pd-rgb),.28),0 0 38px rgba(var(--pd-rgb),.22);
  }
  .na-sphere-character::before{ display:none; }
  .na-sphere-character::after{ display:none; }
  /* Eyes sit on the sphere's actual surface rather than sliding across it
     as flat stickers. The parent supplies ONE shared 3D scene (its own
     `perspective` above); each eye starts centred on the sphere's centre
     (left/top 50% + negative margins, so its own transform-origin IS the
     sphere centre), is rotated to its longitude/latitude, then pushed out
     to the surface by --na-sphere-r. The browser derives depth and
     horizontal foreshortening from that angle, so an eye swinging toward
     the rim squashes and recedes on its own. That used to be faked with
     nine hand-tuned sets of translate/scaleX/roll values per gaze, which
     is why the eyes never bent to match the ball: they only ever moved in
     a flat plane (translate3d's z was always 0) and spun about their own
     centres. Gaze is now just two angles, --na-gaze-yaw/--na-gaze-pitch
     (positive pitch = looking up), with each eye's own fixed offset in
     --na-eye-yaw. --na-eye-depth-fix cancels the perspective
     magnification that sitting --na-sphere-r closer to the camera adds,
     keeping the eyes their intended on-screen size; re-derive it as
     (perspective - --na-sphere-r) / perspective if either value changes.
     Blink is --na-blink so there's one transform chain here, not four
     near-identical copies that have to be kept in sync. */
  .na-sphere-eye{
    position:absolute; z-index:1; left:50%; top:50%; width:28px; height:70px;
    margin:-35px 0 0 -14px;
    overflow:visible; filter:drop-shadow(0 0 6px rgba(255,255,255,.2));
    backface-visibility:hidden; transform-origin:50% 50%;
    /* --na-blink is NOT declared here on purpose: the component sets it on
       the parent, and a custom property declared on the element itself
       beats the value inherited from that parent, which pinned the eyes
       permanently open. Same reason --na-gaze-* are only ever read with a
       var() fallback. Only genuinely per-eye constants belong on this rule. */
    --na-sphere-r:104px; --na-eye-depth-fix:.84;
    transform:
      rotateY(calc(var(--na-gaze-yaw,0deg) + var(--na-eye-yaw,0deg)))
      rotateX(var(--na-gaze-pitch,0deg))
      translateZ(var(--na-sphere-r))
      scale(var(--na-eye-depth-fix))
      scaleY(var(--na-blink,1));
    transition:transform .5s cubic-bezier(.22,.8,.24,1);
  }
  /* fill is no longer set here on purpose: each path now carries its own
     fill="url(#...)" pointing at the glossy radial gradient the component
     declares per instance (see SphereHubCharacter.tsx) -- a CSS rule here
     would win over that SVG presentation attribute and flatten the shine
     back to solid white. */
  .na-sphere-eye-shine{ fill:#fff; opacity:.85; pointer-events:none; }
  /* 16.5deg, not 20: the perspective divide magnifies the eye's position as
     well as its size, so 16.5deg is what lands the resting eyes at the same
     +/-35px from the sphere's centre they sat at before this became a real
     3D placement. Measured, not guessed -- re-measure if --na-sphere-r or
     the container's perspective changes. */
  .na-sphere-eye:first-child{ --na-eye-yaw:-16.5deg; }
  .na-sphere-eye:last-child{ --na-eye-yaw:16.5deg; }
  .na-sphere-character.is-blinking .na-sphere-eye{
    transition-duration:.075s;
  }
  /* Cursor-tracking (AI side chat only, see SphereHubCharacter.tsx's
     trackCursorIn) sets a new gaze on every mousemove -- dozens of times a
     second. The base .5s cubic-bezier above is tuned for the slow, rare
     jumps between idle-look presets; reusing it here means each new target
     restarts a fresh half-second ease before the eyes can even reach the
     last one, so they permanently lag/float far behind the real cursor
     instead of following it. A short, linear transition keeps just enough
     smoothing to not look like a raw per-pixel snap, without introducing
     that lag. */
  .na-sphere-character.is-tracking .na-sphere-eye{
    transition:transform .08s linear;
  }
  /* Never transform:none here -- both eyes are centred on the sphere's
     centre by default, so killing the transform would stack them. The
     component already freezes the gaze at centre under reduced motion. */
  @media (prefers-reduced-motion:reduce){ .na-sphere-eye{ transition:none; } }
  /* Reusable avatar: the same character at any size, for the AI drawer, the
     expanded chat and the Sphere Insights headers. The character keeps its
     native 220px geometry and the whole thing is scaled instead, so every
     instance is provably the identical avatar and none of the 3D constants
     above (--na-sphere-r, --na-eye-depth-fix, the container's perspective)
     ever need re-deriving per size. --na-avatar-size is UNITLESS, a pixel
     count: calc() divides a number by a number, but cannot divide a length
     by a length, which is what a literal `26px / 220px` ratio would need.
     The scale transitions so the AI drawer's own 112px -> 220px (voice) ->
     40px (chatting) size changes still animate as they did. */
  .na-sphere-avatar{
    /* display:block matters: SphereAvatar renders a <span>, and an inline
       element ignores width/height, so the slot would contribute no layout
       size at all and the ball would overlap whatever sits beside it. Both
       real parents happen to be flex (which blockifies children anyway) --
       this makes it correct regardless of where it's dropped. */
    display:block; position:relative; flex:0 0 auto;
    width:calc(var(--na-avatar-size,26) * 1px);
    height:calc(var(--na-avatar-size,26) * 1px);
  }
  .na-sphere-avatar .na-sphere-character{
    position:absolute; top:0; left:0;
    transform:scale(calc(var(--na-avatar-size,26) / 220));
    transform-origin:0 0;
    transition:transform .45s cubic-bezier(.2,.8,.2,1);
  }
  .na-greeting{ font-family: var(--font-display); font-weight:700; font-size:26px; letter-spacing:-0.01em; }
  .na-subtitle{ color: var(--text-muted); font-size:14px; margin-top:8px; }
  .na-input-box{
    background: var(--surface); border:1px solid var(--border); border-radius:16px;
    padding:16px 18px; margin-top:28px; text-align:left;
  }
  .na-input{
    width:100%; background:none; border:none; outline:none;
    color: var(--text); font-size:15px; font-family: var(--font-body); margin-bottom:14px;
  }
  .na-input::placeholder{ color: var(--text-dim); }
  .na-input-row{ display:flex; align-items:center; gap:10px; }
  .na-input-plus{
    width:28px; height:28px; border-radius:50%; border:1px solid var(--border);
    display:flex; align-items:center; justify-content:center; color: var(--text-dim); cursor:pointer; flex-shrink:0;
  }
  .na-input-plus svg{ width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .na-mode-pill{
    display:flex; align-items:center; gap:5px;
    background: var(--surface-2); border:1px solid var(--border); border-radius:999px;
    padding:6px 12px; font-size:12px; font-weight:600; color: var(--text); cursor:pointer;
    font-family: var(--font-body);
  }
  .na-mode-pill svg{ width:12px; height:12px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .na-input-spacer{ flex:1; }

  /* Fullscreen voice overlay */
  .na-send-btn{
    width:32px; height:32px; border-radius:50%; background: var(--lime); border:none;
    display:flex; align-items:center; justify-content:center; cursor:pointer; flex-shrink:0;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.55), 0 3px 12px rgba(var(--lime-rgb),0.35);
  }
  .na-send-btn svg{ width:15px; height:15px; stroke: var(--bg); fill:none; stroke-width:2.2; }
  .na-quick-row{ display:flex; flex-wrap:wrap; justify-content:center; gap:10px; margin-top:22px; }
  .na-quick-pill{
    display:flex; align-items:center; gap:7px;
    background: var(--surface); border:1px solid var(--border); border-radius:999px;
    padding:9px 16px; font-size:12.5px; font-weight:600; color: var(--text); cursor:pointer;
    font-family: var(--font-body);
  }
  .na-quick-pill:hover{ border-color: var(--lime-dim); }
  .na-quick-pill.disabled{ opacity:0.5; }
  .na-quick-pill.disabled:hover{ border-color: var(--border); opacity:0.75; }
  .na-quick-pill svg{ width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:1.8; }

  .na-showcase{ margin-top:56px; }
  .na-showcase-title{ font-family: var(--font-display); font-weight:700; font-size:19px; text-align:center; margin-bottom:22px; }
  .na-showcase-grid{ display:grid; grid-template-columns:repeat(3, 1fr); gap:16px; }
  .na-showcase-card{ display:flex; flex-direction:column; gap:10px; }
  .na-showcase-icon{
    width:34px; height:34px; border-radius:9px; background: rgba(var(--pd-rgb),0.16); color: var(--lime-text);
    display:flex; align-items:center; justify-content:center;
  }
  .na-showcase-icon svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .na-showcase-card-title{ font-family: var(--font-display); font-weight:600; font-size:14.5px; display:flex; align-items:center; gap:8px; }
  .na-showcase-card-desc{ font-size:12.5px; color: var(--text-muted); line-height:1.55; }

  .amz-chart-wrap{ position:relative; }
  .chart-tooltip{
    /* position:fixed + re-parented to <body> on first show (see the
       mousemove handler) rather than position:absolute inside
       .amz-chart-wrap. z-index alone can't win this fight: .dv-kpi-row
       is explicitly z-index:2 (see its own comment — that was itself a
       fix for a *different* tooltip losing to the .dv-card that follows
       it), and this chart's card is a .dv-card at z-index:1, so any
       z-index this tooltip has is trapped inside that losing z:1 context
       no matter how high a number it is. Escaping to <body> sidesteps
       the whole stacking-context comparison instead of extending it. */
    position:fixed;
    /* top/left are set per-hover in JS, in viewport px (getBoundingClientRect
       coordinates, matching position:fixed) — wherever the highest
       currently-visible point sits at that column. This transform is what
       actually places the tooltip: translateY(-100%) stacks the
       tooltip's own *current* height (however many metric rows it
       happens to be showing) fully above that mark, and the extra -14px
       on top of that is the deliberate gap so it never touches the point. */
    transform:translate(-50%, calc(-100% - 14px));
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:9px;
    padding:8px 12px;
    font-size:12px;
    white-space:nowrap;
    box-shadow: 0 10px 26px rgba(0,0,0,0.35);
    pointer-events:none;
    display:none;
    z-index:450; /* above everything ordinary, distinct from .auth-screen's 500 */
  }
  .chart-tooltip-date{ font-family: var(--font-display); font-weight:600; font-size:12px; margin-bottom:4px; }
  .chart-tooltip-row{ display:flex; align-items:center; gap:6px; color: var(--text-muted); padding:1.5px 0; }
  .chart-tooltip-row b{ color: var(--text); margin-left:auto; padding-left:10px; }
  #chartHoverOverlay{ cursor:crosshair; }
  /* Round joins/caps on every metric line — the default miter join can
     show as a small point right at a curve's peak/valley, which reads as
     a "not quite smooth" kink even though the path itself is a proper
     Catmull-Rom curve underneath. */
  #chartSvg path[data-metric]{ stroke-linecap:round; stroke-linejoin:round; }
  .amz-chart-body{ display:flex; align-items:stretch; gap:8px; }
  /* Fixed height matching .dv-linechart's own — not left to flex-stretch —
     so the hover dots below, positioned by percentage of this box, always
     land on the exact same 200px-tall box the SVG curve itself occupies. */
  .chart-plot-wrap{ position:relative; flex:1; min-width:0; height:200px; }
  /* Sized/positioned in real pixels + percentages (not SVG user-units),
     so it stays a true circle even though the chart's own viewBox is
     stretched non-uniformly to fill the card — see the markup comment. */
  .chart-hover-dot{
    position:absolute;
    width:9px; height:9px;
    border-radius:50%;
    transform:translate(-50%,-50%);
    display:none;
    pointer-events:none;
    z-index:4;
  }
  /* Permanent per-point markers (script.js's own renderChart, one <div>
     per data point per visible line) -- same real-pixel-circle trick as
     .chart-hover-dot above and for the same reason (the chart's own
     viewBox is stretched non-uniformly, so an SVG <circle> would render
     as an ellipse). A light border ring + small shadow (was flat/glow-
     free, same color as its own line and roughly the line's own stroke
     width -- easy to lose against the curve itself, which read as "no
     dot until you hover") makes every dot a clearly distinct marker at
     rest, not just something the bigger hover dot reveals. */
  .chart-point-dots{ position:absolute; inset:0; pointer-events:none; z-index:3; }
  .chart-point-dot{
    position:absolute;
    width:9px; height:9px;
    border-radius:50%;
    border:1.5px solid rgba(255,255,255,0.85);
    box-shadow: 0 1px 3px rgba(0,0,0,0.45);
    transform:translate(-50%,-50%);
  }
  .chart-yaxis{
    display:flex; flex-direction:column; justify-content:space-between;
    padding:14px 0; font-size:10.5px; color: var(--text-dim); text-align:right; width:38px; flex-shrink:0;
  }
  .chart-xaxis{
    position:relative;
    height:16px;
    margin:6px 0 0 46px;
    font-size:10.5px; color: var(--text-dim);
  }
  .chart-xaxis span{
    position:absolute; top:0; transform:translateX(-50%);
    white-space:nowrap;
  }
  .chart-xaxis span:first-child{ transform:translateX(0); }
  .chart-xaxis span:last-child{ transform:translateX(-100%); }

  /* Shared collapsible action: animate a real numeric width so the pill
     opens sideways smoothly. The old width:auto transition caused the
     snapping/flicker. Child content stays pointer-transparent so the full
     visible pill remains one stable hover/click target. */
  .collapsible-btn{
    --collapsible-open-width:120px;
    box-sizing:border-box; display:flex; flex:0 0 auto; align-items:center; justify-content:flex-start; gap:0; overflow:hidden;
    width:28px; height:28px; padding:0 7px !important; border-radius:999px !important;
    transition:width .2s cubic-bezier(.4,0,.2,1);
  }
  .collapsible-btn svg{ flex-shrink:0; pointer-events:none; }
  .collapsible-btn-text{
    max-width:160px; margin-left:7px; opacity:0; white-space:nowrap; overflow:hidden;
    pointer-events:none; transition:opacity .12s ease .04s;
  }
  .collapsible-btn:hover, .collapsible-btn:focus-visible, .collapsible-btn.force-open{
    width:var(--collapsible-open-width);
  }
  .collapsible-btn:hover .collapsible-btn-text,
  .collapsible-btn:focus-visible .collapsible-btn-text,
  .collapsible-btn.force-open .collapsible-btn-text{ opacity:1; }

  /* Campaign filter menu — position:fixed with its top/left set from JS
     (the button's own getBoundingClientRect, on open) rather than
     position:absolute anchored to .amz-filter-wrap. The card it lives in
     scrolls horizontally (overflow-x:auto), and an absolutely-positioned
     menu stays pinned to its scrolling ancestor's content box — so it
     was scrolling (and getting clipped by the card's own edge) right
     along with the table underneath it instead of floating free above
     everything, the way a dropdown should. Fixed positioning anchors it
     to the viewport instead, immune to any ancestor's scroll or overflow. */
  /* Search icon that expands into a pill-shaped text input on hover (see
     initAmzCampaignsSearch in script.js) — collapsed by default so it
     reads as just another round icon button next to Filter, matching it
     exactly (.amz-filter-btn) until you actually want it. Kept expanded
     while focused or holding real text via :not(:placeholder-shown), not
     just :hover, so it can't collapse itself out from under you mid-type
     if the cursor happens to drift off it. */
  .amz-search-wrap{ position:relative; display:flex; align-items:center; }
  .amz-search-input{
    width:0; opacity:0; padding:0; margin-left:0; border:1px solid var(--border); border-radius:999px;
    background: var(--surface-2); color: var(--text); font-family: var(--font-body); font-size:12.5px;
    height:26px; box-sizing:border-box;
    transition: width .18s ease, opacity .18s ease, padding .18s ease, margin-left .18s ease;
  }
  .amz-search-wrap:hover .amz-search-input,
  .amz-search-input:focus,
  .amz-search-input:not(:placeholder-shown){
    width:190px; opacity:1; padding:0 12px; margin-left:8px;
  }
  .amz-filter-wrap{ position:relative; }
  /* Outer shell only handles position:fixed placement + the pointer
     caret — it can't also be the thing that scrolls (see
     .amz-filter-menu-inner below) since an overflow:auto box clips
     anything positioned outside its own bounds, including a caret meant
     to poke out above it. Left plain/unclipped here so the caret is
     always free to render. */
  .amz-filter-menu{
    position:fixed; top:0; left:0; z-index:200;
    display:none;
  }
  .amz-filter-menu.open{ display:block; }
  /* The pointer caret — a small rotated square, half tucked behind the
     inner box's top edge so only its outer corner peeks out above it,
     same trick every native tooltip/popover caret uses. Its horizontal
     position (--caret-left) is set from JS to track wherever the filter
     button actually is under the menu, since the menu's own left edge
     shifts around to stay on-screen (see positionAmzFilterMenu). */
  .amz-filter-menu::before{
    content:'';
    position:absolute;
    top:3px; left: var(--caret-left, 20px);
    width:10px; height:10px;
    background: var(--surface); border-left:1px solid var(--border); border-top:1px solid var(--border);
    transform: translateX(-50%) rotate(45deg);
  }
  .amz-filter-menu-inner{
    position:relative; margin-top:8px;
    background: var(--surface); border:1px solid var(--border); border-radius:10px;
    padding:12px 14px; min-width:190px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.35);
    /* Room for more filter groups than just Status/Type — max-height is
       also reset from JS on every open (see positionAmzFilterMenu) to
       whatever actually fits between the button and the bottom of the
       screen, so a tall menu scrolls internally instead of ever running
       off the viewport. This 380px is just the fallback ceiling. */
    max-height:380px; overflow-y:auto; scrollbar-width:thin;
  }
  .amz-filter-label{ font-size:11px; text-transform:uppercase; letter-spacing:0.06em; color: var(--text-dim); }
  .amz-filter-check{ display:flex; align-items:center; gap:8px; font-size:12.5px; color: var(--text); padding:4px 0; cursor:pointer; }
  .amz-filter-check input[type="checkbox"]{ accent-color: var(--lime); width:14px; height:14px; cursor:pointer; }
  /* Each filter section is its own little accordion — with the list now
     running to a couple dozen metrics deep in places (Columns, Off-Amazon
     conversions), being able to fold a group away is what keeps the menu
     itself from being the thing you have to scroll through just to reach
     Status/Type. Height is animated via grid-template-rows 1fr→0fr on
     .amz-filter-group-body rather than max-height, since that animates to
     the section's real content height with no fixed number to guess or
     update as items get added — .amz-filter-group-inner is the required
     overflow:hidden partner that trick needs on the child. */
  .amz-filter-group{ margin-top:10px; }
  .amz-filter-group:first-child{ margin-top:0; }
  .amz-filter-group-head{
    display:flex; align-items:center; justify-content:space-between;
    cursor:pointer; user-select:none; padding:2px 0 6px 0;
  }
  .amz-filter-group-head:hover .amz-filter-label{ color: var(--text); }
  .amz-filter-chevron{
    width:11px; height:11px; stroke: var(--text-dim); fill:none; stroke-width:2; flex-shrink:0;
    transition: transform .18s ease, stroke .15s ease;
  }
  .amz-filter-group-head:hover .amz-filter-chevron{ stroke: var(--text); }
  .amz-filter-group.collapsed .amz-filter-chevron{ transform: rotate(-90deg); }
  .amz-filter-group-body{ display:grid; grid-template-rows:1fr; transition: grid-template-rows .18s ease; }
  .amz-filter-group.collapsed .amz-filter-group-body{ grid-template-rows:0fr; }
  .amz-filter-group-inner{ overflow:hidden; }

  /* Date-range picker popover — position:fixed + JS-positioned/re-parented
     to <body> on open, same reasoning as .amz-filter-menu above. Laid out
     as presets down the left, a two-month calendar on the right, matching
     the standard "date range picker" shape used sitewide by most
     analytics tools, just in this app's own dark/lime palette instead of
     copying a reference screenshot's colors literally. */
  .date-range-popover{
    /* z-index:350, not 200 -- this is a single shared popover reused by
       every date-range pill on the page (see initDateRangePicker), fixed-
       positioned as a body-level sibling rather than a DOM descendant of
       whichever pill triggered it. When that pill is one of the controls
       relocated into an expanded chart's toolbar (see
       relocateAmzChartToolbar), the popover needs to beat
       .card-expand-overlay's own z-index:300 in that same global fixed-
       position stacking context, or it opens invisibly behind the
       fullscreen overlay instead of on top of it. */
    position:fixed; top:0; left:0; z-index:1100; display:none;
    background: var(--surface); border:1px solid var(--border); border-radius:12px;
    box-shadow: 0 20px 50px rgba(0,0,0,0.45);
    overflow:hidden;
  }
  .date-range-popover.open{ display:flex; }
  .kdp-comparison-date-popover{
    z-index:1100;
    max-width:calc(100vw - 16px);
    max-height:calc(100vh - 16px);
    overflow:auto;
  }
  /* Atomic Dashboard uses the same comparison calendar with a denser
     footprint, so it remains practical beside compact KPI cards. */
  .atomic-comparison-date-popover .drp-presets{ width:112px; padding:6px 4px; max-height:340px; }
  .atomic-comparison-date-popover .drp-preset{ padding:6px 8px; font-size:11px; }
  .atomic-comparison-date-popover .drp-calendars{ padding:10px 12px; }
  .atomic-comparison-date-popover .drp-compare-heading{ margin-bottom:6px; font-size:11px; }
  .atomic-comparison-date-popover .drp-cal-nav{ margin-bottom:5px; }
  .atomic-comparison-date-popover .drp-nav-btn{ width:20px; height:20px; }
  .atomic-comparison-date-popover .drp-cal-row{ gap:12px; }
  .atomic-comparison-date-popover .drp-cal{ width:168px; }
  .atomic-comparison-date-popover .drp-cal-title{ margin-bottom:4px; font-size:11px; }
  .atomic-comparison-date-popover .drp-cal-dow{ padding:2px 0; font-size:8px; }
  .atomic-comparison-date-popover .drp-day{ height:22px; font-size:10px; border-radius:5px; }
  .atomic-comparison-date-popover .drp-footer{ margin-top:8px; padding-top:8px; }
  @media (max-width:720px){
    .kdp-comparison-date-popover:not(.atomic-comparison-date-popover) .drp-presets{ width:104px; padding:6px 4px; }
    .kdp-comparison-date-popover:not(.atomic-comparison-date-popover) .drp-calendars{ padding:10px; }
    .kdp-comparison-date-popover:not(.atomic-comparison-date-popover) .drp-cal-row{ gap:10px; }
    .kdp-comparison-date-popover:not(.atomic-comparison-date-popover) .drp-cal{ width:180px; }
  }
  @media (max-width:560px){
    .kdp-comparison-date-popover:not(.atomic-comparison-date-popover) .drp-presets{ width:88px; }
    .kdp-comparison-date-popover:not(.atomic-comparison-date-popover) .drp-cal{ width:168px; }
    .kdp-comparison-date-popover:not(.atomic-comparison-date-popover) .drp-cal:nth-child(2){ display:none; }
    .kdp-comparison-date-popover:not(.atomic-comparison-date-popover) .drp-footer{ align-items:flex-start; flex-direction:column; gap:8px; }
  }
  .drp-compare-heading{
    margin:0 0 10px; color:var(--text-muted); font-size:12px; font-weight:600;
  }
  .drp-presets{
    width:150px; flex-shrink:0; padding:10px 6px;
    border-right:1px solid var(--border-soft);
    max-height:420px; overflow-y:auto; scrollbar-width:thin;
  }
  .drp-preset{
    padding:8px 12px; border-radius:7px; font-size:12.5px; color: var(--text);
    cursor:pointer; white-space:nowrap;
  }
  .drp-preset:hover{ background: var(--surface-2); }
  /* Date-range selection is intentionally purple in every theme. It
     distinguishes a chosen reporting window from the app's green/white
     action accent without creating a heavy solid block across the days. */
  .drp-preset.active{ background: rgba(var(--pd-rgb),0.14); color: var(--pd-c99cff); font-weight:600; }
  .drp-calendars{ padding:14px 16px; min-width:0; }
  .drp-cal-nav{ display:flex; align-items:center; margin-bottom:8px; }
  .drp-nav-btn{
    width:24px; height:24px; border-radius:50%; display:flex; align-items:center; justify-content:center;
    color: var(--text-dim); cursor:pointer; flex-shrink:0;
  }
  .drp-nav-btn:hover{ background: var(--surface-2); color: var(--text); }
  .drp-nav-btn svg{ width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:2; }
  .drp-nav-spacer{ flex:1; }
  .drp-cal-row{ display:flex; gap:24px; }
  .drp-cal{ width:224px; }
  .drp-cal-title{ font-family: var(--font-display); font-weight:700; font-size:13px; text-align:center; margin-bottom:8px; }
  .drp-cal-grid{ display:grid; grid-template-columns:repeat(7, 1fr); gap:2px; }
  .drp-cal-dow{ font-size:10px; text-transform:uppercase; letter-spacing:0.04em; color: var(--text-dim); text-align:center; padding:4px 0; }
  .drp-day{
    height:28px; display:flex; align-items:center; justify-content:center;
    font-size:12px; color: var(--text); border-radius:6px; cursor:pointer;
  }
  .drp-day:hover{ background: var(--surface-2); }
  .drp-day.empty{ cursor:default; }
  .drp-day.empty:hover{ background:none; }
  .drp-day.disabled{ color:var(--text-dim); cursor:default; opacity:.4; }
  .drp-day.disabled:hover{ background:none; }
  .drp-day.in-range{ background: rgba(var(--pd-rgb),0.20); border-radius:0; }
  .drp-day.range-start{ background: var(--pd-af5aff); color:var(--pd-on,#fff); border-radius:6px 0 0 6px; font-weight:700; }
  .drp-day.range-end{ background: var(--pd-af5aff); color:var(--pd-on,#fff); border-radius:0 6px 6px 0; font-weight:700; }
  .drp-day.range-start.range-end{ border-radius:6px; }
  .drp-footer{
    display:flex; align-items:center; justify-content:space-between; gap:16px;
    margin-top:14px; padding-top:12px; border-top:1px solid var(--border-soft);
  }
  .drp-range-label{ font-size:12px; color: var(--text-muted); }
  .drp-footer-actions{ display:flex; gap:8px; }

  /* Marketplace selector */
  .amz-marketplace-wrap{ position:relative; }
  .amz-marketplace-menu{
    position:absolute; top:38px; left:0; z-index:20;
    background: var(--surface); border:1px solid var(--border); border-radius:10px;
    padding:6px; min-width:220px; max-height:360px; overflow-y:auto;
    box-shadow: 0 12px 32px rgba(0,0,0,0.35);
    display:none;
  }
  .amz-marketplace-menu.open{ display:block; }
  .amz-marketplace-divider{ height:1px; background: var(--border-soft); margin:5px 4px; }
  .amz-marketplace-item{
    display:flex; align-items:center; gap:8px;
    padding:8px 10px; border-radius:7px; font-size:12.5px; color: var(--text); cursor:pointer;
  }
  .amz-marketplace-item:hover{ background: var(--surface-2); }
  .amz-marketplace-item .amz-mp-name{ flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  /* Selection indicator -- an empty ring by default, filled solid purple
     with a visible checkmark once this is the active marketplace. Picked
     purple specifically (not the site's lime accent) per how this was
     asked for; reuses the same var(--pd-b07bff) the chart legend's "Sales" dot
     already uses elsewhere, rather than inventing a second purple. */
  .amz-marketplace-item .amz-mp-check{
    width:16px; height:16px; border-radius:50%; flex-shrink:0;
    border:1.5px solid var(--border); display:flex; align-items:center; justify-content:center;
    transition: background .12s ease, border-color .12s ease;
  }
  .amz-marketplace-item .amz-mp-check svg{
    width:10px; height:10px; stroke:#fff; stroke-width:2.6; fill:none;
    opacity:0; transition: opacity .12s ease;
  }
  .amz-marketplace-item.active .amz-mp-check{ background:var(--pd-b07bff); border-color:var(--pd-b07bff); }
  .amz-marketplace-item.active .amz-mp-check svg{ opacity:1; }

  /* The floating background photo itself is a real image (an aurora
     graphic reused from the pricing page), not a CSS gradient — so
     recoloring the *rendered gradients* around it (nav, buttons, cards)
     never touched the photo's own green/blue hue. mix-blend-mode:color
     forces this solid tint's hue+saturation onto the image while
     keeping its actual light/dark shape (the nebula's highlights and
     shadows), which is what a plain color overlay or hue-rotate can't
     do reliably without knowing the source image's exact original hue. */

  /* ---- Login / Sign up screen (always strong liquid glass, any theme) ---- */
  .auth-screen{
    position:fixed;
    inset:0;
    z-index:500;
    display:none;
    align-items:center;
    justify-content:center;
    background:#050604;
    overflow:hidden;
  }
  .auth-screen.open{ display:flex; }
  .auth-bg-image{
    position:absolute;
    top:-8%; left:-8%; right:-8%; bottom:-8%;
    width:auto; height:auto;
    object-fit:cover;
    animation: auroraDrift 16s ease-in-out infinite;
    opacity:0.9;
     /* static now — computed once, not re-run every frame */
  }
  /* This is the same reused photo as `.glass-bg-layer`/`.plans-bg-image`
     (see their comments) — its green/blue hue is baked into the pixels.
     This screen is deliberately fixed-look regardless of the app's theme
     setting, so unlike those two this tint isn't theme-scoped: it's the
     app's current (purple) brand color, always on, matching what the
     green tint used to mean back when green was the only brand color. */
  .auth-bg-tint{
    position:absolute;
    top:-8%; left:-8%; right:-8%; bottom:-8%;
    background: rgb(81,80,246);
    mix-blend-mode: color;
    pointer-events:none;
  }
  .auth-card{
    position:relative;
    z-index:1;
    width:min(420px, 90vw);
    padding:36px 34px;
    border-radius:22px;
    background: rgba(var(--pd-dark-rgb),0.34);
    border:1px solid rgba(255,255,255,0.16);
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.25),
      0 30px 80px rgba(0,0,0,0.55);
  }
  .auth-brand{ display:flex; align-items:center; gap:9px; justify-content:center; margin-bottom:26px; }
  /* No invert here either — same reasoning as .brand-logo-img: this mark
     is already white/light-gray, built for the auth screen's dark aurora
     background, so it needs to show as-is. invert(1) was left over from
     the old dark-colored wordmark and would turn this one black. */
  .auth-logo-img{ height:26px; width:auto; }
  .auth-brand-name{ font-family: var(--font-display); font-weight:800; font-size:17px; color:#ffffff; }
  .auth-google-btn{
    width:100%;
    display:flex; align-items:center; justify-content:center; gap:10px;
    background: rgba(255,255,255,0.94);
    border:1px solid rgba(255,255,255,0.2);
    border-radius:10px;
    padding:11px 0;
    font-family: var(--font-body);
    font-size:13.5px; font-weight:600;
    color:#1f2320;
    cursor:pointer;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.8), 0 2px 10px rgba(0,0,0,0.25);
    transition: filter .12s ease;
  }
  .auth-google-btn:hover{ filter: brightness(0.97); }
  .auth-divider{
    display:flex; align-items:center; gap:12px;
    margin:18px 0;
    color: rgba(255,255,255,0.45);
    font-size:11.5px;
  }
  .auth-divider::before, .auth-divider::after{
    content:'';
    flex:1;
    height:1px;
    background: rgba(255,255,255,0.16);
  }
  .auth-tabs{
    display:flex;
    background: rgba(0,0,0,0.28);
    border:1px solid rgba(255,255,255,0.12);
    border-radius:999px;
    padding:3px;
    margin-bottom:26px;
  }
  .auth-tab{
    flex:1;
    border:none; background:none; cursor:pointer;
    padding:9px 0;
    border-radius:999px;
    font-family: var(--font-body);
    font-size:13px; font-weight:600;
    color: rgba(255,255,255,0.65);
    transition: background .15s ease, color .15s ease;
  }
  .auth-tab.active{
    background: var(--lime);
    color:#0a0c0a;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.55), 0 3px 12px rgba(var(--lime-rgb),0.35);
  }
  .auth-title{ font-family: var(--font-display); font-weight:700; font-size:22px; color:#ffffff; margin-bottom:6px; }
  .auth-subtitle{ font-size:13px; color: rgba(255,255,255,0.72); margin-bottom:22px; line-height:1.5; }
  .auth-label{ font-size:12px; font-weight:600; color: rgba(255,255,255,0.85); margin-bottom:6px; display:block; }
  .auth-input{
    width:100%;
    background: rgba(0,0,0,0.32);
    border:1px solid rgba(255,255,255,0.16);
    border-radius:10px;
    padding:12px 14px;
    color:#ffffff;
    font-family: var(--font-body);
    font-size:13.5px;
    margin-bottom:16px;
    outline:none;
  }
  .auth-input::placeholder{ color: rgba(255,255,255,0.4); }
  .auth-input:focus{ border-color: var(--lime); }
  .auth-row-between{ display:flex; align-items:center; justify-content:space-between; margin-bottom:20px; }
  .auth-checkbox{ display:flex; align-items:center; gap:6px; font-size:12px; color: rgba(255,255,255,0.75); }
  .auth-link{ color: var(--lime); font-size:12.5px; font-weight:600; text-decoration:none; }
  .auth-link:hover{ text-decoration:underline; }
  .auth-submit{ width:100%; justify-content:center; padding:12px 0; font-size:14px; }
  .auth-switch{ text-align:center; font-size:12.5px; color: rgba(255,255,255,0.7); margin-top:20px; }

  /* ---- Forgot password ---- */
  .auth-back-row{ margin-bottom:18px; }
  .auth-back-link{
    display:inline-flex; align-items:center; gap:4px;
    color: rgba(255,255,255,0.65);
    font-size:12.5px; font-weight:600;
    text-decoration:none;
  }
  .auth-back-link:hover{ color:#ffffff; }
  .auth-back-link svg{ width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:2.2; }
  /* Same dark-glass look as .btn-outline elsewhere, but not the theme
     token version — every color on this screen is hardcoded rgba/white
     (see .auth-input etc. above), deliberately fixed-look regardless of
     the app's Dark/Light setting, so this reuses that same recipe
     instead of pulling in --surface-2/--border/--text, which would just
     go white-on-white here whenever Light mode happened to be active. */
  .auth-submit-secondary{
    width:100%;
    justify-content:center;
    background: rgba(255,255,255,0.08);
    border:1px solid rgba(255,255,255,0.18);
    color:#ffffff;
    padding:12px 0;
    border-radius:10px;
    font-family: var(--font-body);
    font-size:14px;
    font-weight:600;
    cursor:pointer;
    transition: background .12s ease, border-color .12s ease;
  }
  .auth-submit-secondary:hover{ background: rgba(255,255,255,0.14); border-color: rgba(255,255,255,0.3); }
  .auth-sent-icon{
    width:52px; height:52px;
    margin:4px auto 16px;
    border-radius:50%;
    background: rgba(var(--lime-rgb),0.16);
    border:1px solid rgba(var(--lime-rgb),0.4);
    display:flex; align-items:center; justify-content:center;
    color: var(--lime);
  }
  .auth-sent-icon svg{ width:22px; height:22px; stroke:currentColor; fill:none; stroke-width:1.8; }
  .auth-sent-text{
    text-align:center;
    font-size:13px;
    line-height:1.55;
    color: rgba(255,255,255,0.75);
    margin-bottom:22px;
  }
  .auth-sent-text span{ color:#ffffff; font-weight:600; }

  /* ---- Plan upgrade page ---- */
  /* overflow:hidden clips the background photo below (it intentionally
     bleeds past the panel's own edges so the drift/zoom animation never
     shows a hard border) — without it, that bleed still counted as real
     scrollable page content, leaving dead empty space below the cards
     whenever the panel wasn't tall enough to need scrolling at all.
     No background color here (transparent): with the photo hidden, an
     opaque fill just painted a flat black rectangle over the sitewide
     ambient background (the aurora glow visible elsewhere, e.g. behind
     the sidebar), instead of letting that same background show through
     consistently behind this panel too. */
  #panel-plans{ position:relative; overflow:hidden; }
  /* Blur is intentionally NOT part of this animation anymore. Animating
     `filter: blur()` forces the browser to recompute the blur convolution
     every frame, forever, on a full-viewport layer — one of the most
     expensive things a CSS animation can do. The blur radius here was
     already a constant 6px across every keyframe (never actually varied),
     so it's moved to a static `filter: blur(6px)` on each element instead
     (computed once, then cached) and only the cheap, GPU-composited
     transform keeps animating. The brightness "breathing" pulse that used
     to ride along with it is dropped for the same reason — it shared the
     same filter chain, so keeping it would have kept forcing a recompute.
  */
  @keyframes auroraDrift{
    0%   { transform: scale(1.08) translate(0,0); }
    50%  { transform: scale(1.13) translate(-1.5%, 1.5%); }
    100% { transform: scale(1.08) translate(0,0); }
  }
  .plans-bg-image{
    display:none; /* the drifting aurora photo behind the pricing cards — hidden
      per request, leaves the panel's plain #050604 background instead */
    position:absolute;
    top:-36px; left:-40px; right:-40px; bottom:-60px;
    width:auto; height:auto;
    object-fit:cover;
    object-position:center;
    pointer-events:none;
    z-index:0;
    opacity:0.85;
    animation: auroraDrift 12s ease-in-out infinite;
     /* static now — computed once, not re-run every frame */
  }
  /* Tints `.plans-bg-image` for Glass theme (the same mix-blend-mode:color
     trick `.glass-bg-layer` uses, just on a sibling div instead of an
     ::after — img elements don't reliably render pseudo-elements) — moot
     now that the photo itself is hidden above, so this stays off too. */
  .plans-bg-tint{
    display:none;
    position:absolute;
    top:-36px; left:-40px; right:-40px; bottom:-60px;
    z-index:0;
    pointer-events:none;
  }
  .plans-hero, .plans-grid, .plans-footnote{ position:relative; z-index:1; }

  .plans-hero{ text-align:center; padding:20px 0 36px 0; }
  .plans-hero-title{
    font-family: var(--font-display);
    font-weight:700;
    font-size:34px;
    letter-spacing:-0.01em;
  }
  .plans-hero-title span{ color: var(--lime-text); }
  .plans-hero-sub{ color: var(--text-muted); font-size:14.5px; margin-top:8px; }

  .billing-wrap{ position:relative; display:inline-block; margin-top:20px; }
  .billing-toggle{
    display:inline-flex;
    align-items:center;
    gap:2px;
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:999px;
    padding:2px;
  }
  .toggle-opt{
    font-family: var(--font-body);
    font-size:11.5px;
    font-weight:600;
    color: var(--text-muted);
    background:none;
    border:none;
    padding:5px 14px;
    border-radius:999px;
    cursor:pointer;
    transition: background .15s ease, color .15s ease;
  }
  .toggle-opt.active{
    background: var(--lime);
    color: var(--bg);
  }
  .billing-note{
    position:absolute;
    left:100%;
    top:50%;
    transform:translateY(-50%);
    margin-left:4px;
    display:flex;
    flex-direction:row;
    align-items:center;
    gap:4px;
    color: var(--lime-text);
    pointer-events:none;
    white-space:nowrap;
  }
  .billing-note-arrow{ width:48px; height:18px; }
  .billing-note-text{
    font-family:'Caveat', cursive;
    font-weight:600;
    font-size:19px;
    line-height:1;
    transform: rotate(-3deg);
  }

  .plans-grid{
    display:grid;
    grid-template-columns:repeat(3, minmax(0,1fr));
    grid-auto-rows:1fr;
    gap:20px;
    max-width:960px;
    margin:0 auto;
  }
  .plan-tier{
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:16px;
    padding:26px 22px;
    display:flex;
    flex-direction:column;
    position:relative;
    height:100%;
  }
  .plan-tier.featured{
    border-color: var(--lime);
    box-shadow: 0 0 0 1px var(--lime), 0 20px 50px rgba(var(--lime-rgb),0.08);
  }
  .plan-tier-badge{
    position:absolute;
    top:-13px; left:50%;
    transform:translateX(-50%);
    background: var(--lime);
    color: var(--bg);
    font-size:11px;
    font-weight:700;
    padding:4px 12px;
    border-radius:999px;
    white-space:nowrap;
  }
  .plan-tier-name{ font-family: var(--font-display); font-weight:700; font-size:18px; margin-top:6px; }
  .plan-tier-desc{ color: var(--text-muted); font-size:12.5px; margin-top:6px; min-height:32px; }
  .plan-tier-price-block{
    min-height:92px;
    display:flex;
    flex-direction:column;
    justify-content:center;
    margin:14px 0 0 0;
  }
  .plan-tier-price{
    font-family: var(--font-display);
    font-weight:700;
    font-size:34px;
    line-height:1;
  }
  .plan-tier-price span{ font-size:16px; vertical-align:top; margin-right:2px; }
  .plan-tier-price .price-num{ font-size:34px; vertical-align:baseline; margin-right:0; }
  .plan-tier-price small{ font-size:13px; font-weight:400; color: var(--text-dim); margin-left:3px; }
  .plan-tier-price-custom{ font-size:34px; }
  .plan-tier-annual-note{
    font-size:12px;
    color: var(--text-dim);
    margin-top:8px;
  }
  .plan-tier-save{
    margin-top:4px;
    font-size:12px;
    font-weight:600;
    color: var(--lime-text);
  }
  .plan-tier-save[hidden]{ display:none; }
  .plan-tier-features{
    list-style:none;
    padding:16px 0 22px 0;
    margin:14px 0 0 0;
    border-top:1px solid var(--border-soft);
    flex:1;
  }
  .plan-tier-features li{
    display:flex;
    align-items:flex-start;
    gap:8px;
    font-size:13px;
    color: var(--text-muted);
    padding:6px 0;
  }
  .plan-tier-features li::before{
    content:'';
    width:14px; height:14px;
    margin-top:2px;
    flex-shrink:0;
    border-radius:50%;
    background: var(--lime);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E") center/9px no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3'%3E%3Cpath d='M20 6L9 17l-5-5'/%3E%3C/svg%3E") center/9px no-repeat;
  }
  .plan-tier-features li.off{ color: var(--text-dim); }
  .plan-tier-features li.off::before{ background: var(--text-dim); opacity:0.4; }
  .plan-tier-btn{ width:100%; justify-content:center; }

  .plans-footnote{
    display:flex; align-items:center; justify-content:center; gap:7px;
    margin-top:28px;
    font-size:12px;
    color: var(--text-dim);
  }
  .plans-footnote svg{ flex-shrink:0; }

  /* ---- Sphere permissions modal ---- */
  /* ---- Expanded chat window (full overlay) ---- */
  .ai-expand-overlay{
    position:fixed;
    inset:0;
    background: rgba(0,0,0,0.55);
    display:flex;
    align-items:center;
    justify-content:center;
    z-index:200;
    opacity:0;
    pointer-events:none;
    transition: opacity .2s ease;
  }
  .ai-expand-overlay.open{ opacity:1; pointer-events:auto; }
  .ai-expand-window{
    width:min(1180px, 94vw);
    height:min(780px, 90vh);
    background: var(--bg);
    border:1px solid var(--border);
    border-radius:20px;
    display:flex;
    overflow:hidden;
    box-shadow: 0 30px 80px rgba(0,0,0,0.5);
    transform: scale(0.97);
    transition: transform .2s ease;
  }
  .ai-expand-overlay.open .ai-expand-window{ transform: scale(1); }

  .ai-expand-sidebar{
    width:260px;
    min-width:260px;
    background: var(--surface);
    border-right:1px solid var(--border);
    padding:18px 14px;
    overflow-y:auto;
  }
  .ai-expand-sidebar-header{
    display:flex; align-items:center; gap:10px;
    font-family: var(--font-display);
    font-weight:700;
    font-size:15px;
    color: var(--lime-text);
    padding:4px 6px 18px 6px;
  }
  .ai-expand-mini-mark{
    width:20px; height:20px;
    object-fit:contain;
    flex-shrink:0;
    filter: invert(1);
  }
  body.theme-light .ai-expand-mini-mark{ filter: none; }
  .ai-expand-newchat, .ai-expand-search{
    display:flex; align-items:center; gap:10px;
    padding:9px 8px;
    border-radius:8px;
    font-size:13.5px;
    color: var(--text-muted);
    cursor:pointer;
  }
  .ai-expand-newchat svg, .ai-expand-search svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.6; }
  .ai-expand-newchat:hover, .ai-expand-search:hover{ background: var(--surface-2); color: var(--text); }
  .ai-expand-group-label{
    font-family: var(--font-mono);
    font-size:10px;
    letter-spacing:0.1em;
    text-transform:uppercase;
    color: var(--text-dim);
    padding:16px 8px 6px 8px;
  }
  .ai-expand-chat-item{
    padding:8px 8px;
    border-radius:7px;
    font-size:13px;
    color: var(--text-muted);
    cursor:pointer;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
  }
  .ai-expand-chat-item:hover{ background: var(--surface-2); color: var(--text); }

  .ai-expand-main{
    flex:1;
    display:flex;
    flex-direction:column;
    position:relative;
    background-image: radial-gradient(var(--border) 1px, transparent 1px);
    background-size: 18px 18px;
  }
  .ai-expand-topbar{
    display:flex;
    justify-content:flex-end;
    gap:10px;
    padding:16px 18px;
  }
  .ai-expand-topbar span{
    width:32px; height:32px;
    border-radius:8px;
    display:flex; align-items:center; justify-content:center;
    color: var(--text-dim);
    cursor:pointer;
    border:1px solid var(--border);
    background: var(--surface);
  }
  .ai-expand-topbar span:hover{ color: var(--text); background: var(--surface-2); }
  .ai-expand-topbar svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.7; }

  .ai-expand-center{
    flex:1;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    padding:0 40px 60px 40px;
  }
  .ai-expand-mark{ width:140px; height:140px; margin-bottom:14px; --na-avatar-size:140; }
  .ai-expand-title{
    font-family: var(--font-display);
    font-weight:600;
    font-size:30px;
    margin-bottom:28px;
    text-align:center;
  }
  .ai-expand-input-box{
    width:100%;
    max-width:720px;
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:16px;
    padding:16px 18px;
  }
  .ai-expand-input-box input{
    width:100%;
    background:none;
    border:none;
    outline:none;
    color: var(--text);
    font-size:15px;
    font-family: var(--font-body);
    margin-bottom:14px;
  }
  .ai-expand-input-box input::placeholder{ color: var(--text-dim); }
  .ai-expand-input-row{ display:flex; align-items:center; justify-content:space-between; }
  .ai-expand-send{
    width:34px; height:34px;
    border-radius:50%;
    background: var(--lime);
    border:none;
    display:flex; align-items:center; justify-content:center;
    cursor:pointer;
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.55),
      inset 0 -1px 2px rgba(0,0,0,0.12),
      0 3px 12px rgba(var(--lime-rgb),0.35);
  }
  .ai-expand-send svg{ width:15px; height:15px; stroke: var(--bg); fill:none; stroke-width:2; }
  .ai-expand-suggestions-row{
    display:flex;
    gap:12px;
    margin-top:22px;
    width:100%;
    max-width:720px;
  }
  .ai-expand-pill{
    flex:1;
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:12px;
    padding:12px 14px;
    cursor:pointer;
  }

  .ai-expand-pill:hover{ border-color: var(--border-strong, var(--border)); background: var(--surface-2); }
  .ai-expand-pill-title{ font-size:13px; font-weight:600; margin-bottom:3px; }
  .ai-expand-pill-desc{ font-size:11.5px; color: var(--text-muted); }

  .perm-overlay{
    position:fixed;
    inset:0;
    background: rgba(0,0,0,0.55);
    display:flex;
    align-items:center;
    justify-content:center;
    opacity:0;
    pointer-events:none;
    transition: opacity .15s ease;
    z-index:440; /* above the sidebar (170), the header (160) and the hover-opened sidebar (430), so the whole app dims, like the other full-screen modals */
  }
  .perm-overlay.open{ opacity:1; pointer-events:auto; }

  .perm-modal{
    width:460px;
    max-width:92vw;
    max-height:86vh;
    display:flex;
    flex-direction:column;
    background: var(--surface);
    border:1px solid var(--border);
    border-radius:14px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.5);
    transform: translateY(10px) scale(.98);
    transition: transform .18s ease;
  }
  .perm-overlay.open .perm-modal{ transform: translateY(0) scale(1); }

  .perm-header{
    display:flex;
    align-items:center;
    justify-content:space-between;
    padding:20px 22px;
    border-bottom:1px solid var(--border);
    flex-shrink:0;
  }
  .perm-header h3{
    font-family: var(--font-display);
    font-size:17px;
    font-weight:600;
  }
  .perm-close{
    color: var(--text-dim);
    cursor:pointer;
    display:flex;
  }
  .perm-close svg{ width:18px; height:18px; stroke:currentColor; fill:none; stroke-width:1.7; }
  .perm-close:hover{ color: var(--text); }

  .perm-body{
    padding:20px 22px 22px 22px;
    overflow-y:auto;
    flex:1;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .perm-body::-webkit-scrollbar{ display:none; }
  .perm-label{
    font-size:12px;
    color: var(--text-muted);
    margin-bottom:10px;
  }

  .perm-access-row{
    display:grid;
    grid-template-columns: repeat(3, 1fr);
    gap:10px;
    margin-bottom:16px;
  }
  .perm-access-card{
    border:1px solid var(--border);
    background: var(--surface-2);
    border-radius:10px;
    padding:14px 10px;
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:8px;
    cursor:pointer;
    transition: border-color .12s ease, background .12s ease;
  }
  .perm-access-card svg{ width:18px; height:18px; stroke: var(--text-muted); fill:none; stroke-width:1.6; }
  .perm-access-card span{ font-size:12.5px; font-weight:600; color: var(--text-muted); }
  .perm-access-card:hover{ border-color: var(--lime-dim); }
  .perm-access-card.selected{
    border-color: var(--lime);
    background: var(--lime-wash);
  }
  .perm-access-card.selected svg{ stroke: var(--lime); }
  .perm-access-card.selected span{ color: var(--lime-text); }

  .perm-summary{
    font-size:12.5px;
    color: var(--text);
    margin-bottom:14px;
  }
  .perm-summary .n{ color: var(--lime-text); font-weight:600; }
  .perm-summary .sep{ color: var(--text-dim); margin:0 6px; }
  .perm-summary .rw{ color: var(--text-muted); }

  .perm-search{
    display:flex; align-items:center; gap:8px;
    background: var(--bg);
    border:1px solid var(--border);
    border-radius:9px;
    padding:10px 12px;
    margin-bottom:14px;
  }
  .perm-search svg{ width:14px; height:14px; stroke: var(--text-dim); fill:none; stroke-width:1.6; }
  .perm-search input{
    background:none; border:none; outline:none;
    color: var(--text);
    font-family: var(--font-body);
    font-size:12.5px;
    width:100%;
  }
  .perm-search input::placeholder{ color: var(--text-dim); }

  .perm-list{
    border:1px solid var(--border);
    border-radius:10px;
    overflow:hidden;
  }
  .perm-cat{
    display:flex; align-items:center; gap:10px;
    padding:13px 14px;
    background: var(--surface-2);
    border-bottom:1px solid var(--border);
    cursor:pointer;
    transition: background .12s ease;
  }
  .perm-cat:last-child{ border-bottom:none; }
  .perm-cat:hover{ background: var(--lime-wash); }
  .perm-cat .p-icon{
    width:16px; height:16px;
    color: var(--text-muted);
    flex-shrink:0;
    display:flex; align-items:center; justify-content:center;
  }
  .perm-cat .p-icon svg{ width:15px; height:15px; stroke:currentColor; fill:none; stroke-width:1.6; }
  .perm-cat .p-name{ font-size:13px; font-weight:600; color: var(--text); flex:1; }
  .perm-cat .p-count{ font-family: var(--font-mono); font-size:12.5px; color: var(--lime-text); font-weight:600; }
  .perm-cat .p-chev{ color: var(--text-dim); display:flex; }
  .perm-cat .p-chev svg{ width:14px; height:14px; stroke:currentColor; fill:none; stroke-width:1.7; }

  .perm-footer{
    padding:16px 22px 22px 22px;
    flex-shrink:0;
  }
  .perm-save{
    width:100%;
    background: var(--lime);
    color: var(--bg);
    border:none;
    border-radius:10px;
    padding:12px;
    font-family: var(--font-body);
    font-weight:600;
    font-size:13.5px;
    cursor:pointer;
    transition: filter .12s ease;
  }
  .perm-save:hover{ filter: brightness(1.08); }

  /* Toast notifications (see script.js's toast()) -- simple rounded card
     with a faded-color icon circle, same recipe as the Blitz Campaign
     modal's own icon/option treatment, top-center instead of the old
     top-right solid green/red boxes. Uses theme surface/border/text
     tokens so it matches light/dark/glass automatically. */
  #pdToastHost{
    position:fixed; top:66px; /* just under the 56px app header */ left:50%;
    transform:translateX(-50%);
    z-index:10000;
    display:flex; flex-direction:column; align-items:center; gap:8px;
    max-width:min(92vw,380px);
    pointer-events:none;
  }
  .pd-toast{
    display:flex; align-items:center; gap:10px;
    padding:8px 16px 8px 8px;
    border:1px solid var(--border);
    border-radius:999px;
    background:var(--surface);
    color:var(--text);
    font:13px/1.4 system-ui,sans-serif;
    box-shadow:0 10px 30px rgba(0,0,0,.28);
    pointer-events:auto;
    animation: pdToastIn .18s ease;
  }
  .pd-toast-icon{
    display:flex; width:22px; height:22px; flex:0 0 22px;
    align-items:center; justify-content:center;
    border-radius:50%;
  }
  .pd-toast-icon svg{ width:12px; height:12px; fill:none; stroke:currentColor; stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
  .pd-toast.is-success .pd-toast-icon{ background:rgba(34,197,94,.18); color:#22c55e; }
  .pd-toast.is-error .pd-toast-icon{ background:rgba(239,68,68,.18); color:#ef4444; }
  @keyframes pdToastIn{ from{ opacity:0; transform:translateY(-6px); } to{ opacity:1; transform:translateY(0); } }
  .chartb-beta{ display:inline-block; margin-left:6px; padding:1px 6px; border-radius:999px; background:rgba(var(--pd-rgb),.14); color:var(--pd-7c5cff); font-size:9px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; line-height:1.5; }
  .amazon-ads-react-pill.is-done{ border-color:rgba(34,197,94,.45); background:rgba(34,197,94,.15); color:#4ade80; }
  body.theme-light .amazon-ads-react-pill.is-done{ border-color:#86efac; background:#dcfce7; color:#15803d; }

  /* Sphere's hello bubble: a speech bubble beside the Sphere face when its page opens. */
  .na-hero{ position:relative; }
  /* Sphere's hello bubble: a speech bubble with a tail toward the face. Sits inside the hero so it scrolls with the page. */
  #sphereHelloBubble{
    position:absolute; z-index:5; width:max-content; max-width:min(300px,70vw); padding:14px 18px; text-align:left;
    border:1px solid var(--border); border-radius:20px; background:var(--surface); color:var(--text);
    font:600 14px/1.4 var(--font-body); box-shadow:0 10px 30px rgba(0,0,0,.28);
    opacity:0; transform:translateX(-8px) scale(.96); pointer-events:none; transition:opacity .22s ease, transform .22s ease;
  }
  #sphereHelloBubble.is-visible{ opacity:1; transform:none; }
  #sphereHelloBubble::before{
    content:""; position:absolute; left:-8px; top:50%; width:14px; height:14px; margin-top:-7px;
    border-left:1px solid var(--border); border-bottom:1px solid var(--border); background:var(--surface); transform:rotate(45deg);
  }

/* ---- Sidebar platform groups: hover swaps the icon for an arrow; the guide line gets a small active marker ---- */
.nav-platform-toggle .nav-icon{ transition: opacity .14s ease; }
.nav-platform-toggle .nav-chev{
  position:absolute; left:11px; top:50%; margin-top:-7px;
  opacity:0; pointer-events:none;
  transition: transform .15s ease, opacity .14s ease, color .12s ease;
}
.nav-platform-toggle:hover .nav-icon{ opacity:0; }
.nav-platform-group .nav-platform-toggle:hover .nav-chev{ opacity:1; }
.sidebar.collapsed .nav-platform-toggle .nav-chev{ left:13px; }
.nav-platform-group.open .nav-platform-toggle .nav-chev{ transform: rotate(90deg); }

/* guide line moved from border to a pseudo so the marker can sit on it without being clipped */
.nav-platform-group .nav-help-content{ position:relative; border-left:0; }
.nav-platform-group .nav-help-content::before{
  content:''; position:absolute; left:1px; top:0; bottom:0; width:1px; background: var(--border);
}
.nav-platform-group .nav-platform-inner{ margin-left:-12px; padding-left:12px; }
.nav-platform-group .nav-help-item{ position:relative; }
.nav-platform-group .nav-help-item::before{
  content:''; position:absolute; left:-12px; top:50%;
  width:3px; height:14px; margin-top:-7px; border-radius:2px;
  background: var(--lime);
  opacity:0; transform: scaleY(.4);
  transition: opacity .12s ease, transform .15s ease;
}
.nav-platform-group .nav-help-item:not(.is-disabled):hover::before,
.nav-platform-group .nav-help-item.active::before{ opacity:1; transform: scaleY(1); }

/* the collapsed-peek rule forces every chevron visible; keep platform arrows hidden until their title is hovered */
.sidebar .nav-platform-group .nav-platform-toggle .nav-chev,
.sidebar.collapsed.peek .nav-platform-group .nav-platform-toggle .nav-chev{ opacity:0; pointer-events:none; }
.sidebar .nav-platform-group .nav-platform-toggle:hover .nav-chev,
.sidebar.collapsed.peek .nav-platform-group .nav-platform-toggle:hover .nav-chev{ opacity:1; }

/* ---- Sidebar Dashboard dropdown: pin + drag-to-reorder rows ---- */
.nav-platform-group .nav-dash-item{ cursor:pointer; gap:6px; }
.nav-platform-group .nav-dash-item > span.nav-dash-name:first-child{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.nav-dash-grip{ flex:0 0 auto; width:16px; height:16px; display:flex; align-items:center; justify-content:center; color:var(--text-dim); opacity:0; cursor:grab; transition: opacity .12s ease; }
.nav-dash-grip svg{ width:12px; height:12px; fill:currentColor; }
.nav-dash-item:hover .nav-dash-grip{ opacity:.8; }
.nav-dash-item.is-dragging{ opacity:.4; }
.nav-dash-item.drop-before{ box-shadow: 0 -2px 0 var(--lime); }
.nav-dash-item.drop-after{ box-shadow: 0 2px 0 var(--lime); }
.nav-dash-more span{ font-size:12px; color:var(--text-dim); }

/* ---- Demo pages for Publishers Create / Research (sample content only) ---- */
.demo-hero{ padding:26px 28px; border:1px solid var(--border); border-radius:14px; background:var(--surface-2); margin-bottom:22px; }
.demo-create{ background:linear-gradient(135deg, rgba(20,184,166,.16), transparent 60%), var(--surface-2); }
.demo-research{ background:linear-gradient(135deg, rgba(245,158,11,.16), transparent 60%), var(--surface-2); }
.demo-eyebrow{ font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--text-dim); margin-bottom:8px; }
.demo-title{ font-family:var(--font-display, inherit); font-size:26px; font-weight:700; letter-spacing:-.02em; color:var(--text); line-height:1.15; }
.demo-sub{ margin-top:8px; max-width:620px; font-size:13px; line-height:1.5; color:var(--text-dim); }
.demo-actions{ display:flex; gap:10px; margin-top:18px; flex-wrap:wrap; }
.demo-section-title{ margin:24px 2px 10px; font-size:13px; font-weight:600; color:var(--text); }
.demo-grid{ display:grid; gap:12px; }
.demo-grid-3{ grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); }
.demo-grid-4{ grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); }
.demo-card{ padding:16px; border:1px solid var(--border); border-radius:12px; background:var(--surface-2); display:flex; flex-direction:column; gap:6px; }
.demo-card-top{ display:flex; align-items:center; justify-content:space-between; margin-bottom:4px; }
.demo-card-title{ font-size:14px; font-weight:600; color:var(--text); }
.demo-meta{ font-size:12px; color:var(--text-dim); line-height:1.4; }
.demo-chip{ display:inline-flex; align-items:center; height:20px; padding:0 8px; border-radius:999px; border:1px solid var(--border); font-size:10.5px; color:var(--text-muted); }
.demo-progress{ height:6px; border-radius:99px; background:rgba(128,128,128,.2); overflow:hidden; margin-top:6px; }
.demo-progress i{ display:block; height:100%; border-radius:99px; background:#2dd4bf; }
.demo-tool-icon{ width:34px; height:34px; border-radius:9px; display:flex; align-items:center; justify-content:center; background:rgba(20,184,166,.16); color:#2dd4bf; margin-bottom:4px; }
.demo-tool-icon svg{ width:18px; height:18px; fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
.demo-list{ gap:0; padding:6px 16px; }
.demo-row{ display:flex; align-items:center; gap:10px; padding:11px 0; border-bottom:1px solid var(--border); font-size:13px; color:var(--text); }
.demo-row:last-child{ border-bottom:0; }
.demo-row.done{ color:var(--text-dim); }
.demo-check{ width:18px; height:18px; border-radius:50%; border:1.5px solid var(--border); display:inline-flex; align-items:center; justify-content:center; font-size:11px; color:#fff; flex:0 0 auto; }
.demo-row.done .demo-check{ background:#14b8a6; border-color:#14b8a6; }
.demo-search{ display:flex; align-items:center; gap:10px; margin-top:18px; max-width:640px; height:44px; padding:0 6px 0 14px; border:1px solid var(--border); border-radius:11px; background:var(--surface); color:var(--text-dim); font-size:13px; }
.demo-search svg{ width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:1.8; flex:0 0 auto; }
.demo-search span{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.demo-kpi-value{ font-family:var(--font-display, inherit); font-size:28px; font-weight:700; letter-spacing:-.02em; color:var(--text); }
.demo-kpi-value small{ font-size:14px; color:var(--text-dim); font-weight:500; margin-left:2px; }
.demo-table-card{ padding:6px 16px 10px; overflow-x:auto; }
.demo-table{ width:100%; border-collapse:collapse; font-size:13px; }
.demo-table th{ padding:10px 8px; text-align:left; font-size:11px; font-weight:600; color:var(--text-dim); border-bottom:1px solid var(--border); white-space:nowrap; }
.demo-table td{ padding:11px 8px; border-bottom:1px solid var(--border); color:var(--text); white-space:nowrap; }
.demo-table tr:last-child td{ border-bottom:0; }
.demo-score{ display:inline-flex; align-items:center; justify-content:center; min-width:34px; height:22px; border-radius:6px; font-size:12px; font-weight:600; background:rgba(245,158,11,.18); color:#fbbf24; }
body.theme-light .demo-score{ background:#fef3c7; color:#b45309; }
.demo-trend{ margin-top:6px; font-size:12.5px; font-weight:600; }
.demo-trend.up{ color:#22c55e; }
.demo-trend.down{ color:#ef4444; }

/* ---- Sidebar footer (plan card + Upgrade): stuck to the bottom of the sidebar, the menu scrolls under it ---- */

/* Upgrade is a labelled pill */
.account-upgrade{ width:auto; min-width:26px; padding:0 10px; gap:5px; border-radius:999px; font-size:11.5px; font-weight:600; }
.account-upgrade-label{ white-space:nowrap; }
.sidebar.collapsed:not(.peek) .account-upgrade-label{ display:none; }

/* ---- Upgrade card ---- */
.account-upgrade{ width:26px; min-width:26px; padding:0; gap:0; }
.account-card .account-name{ color:#d97706; }
body:not(.theme-light) .account-card .account-name{ color:#fbbf24; }

.account-card{ position:relative; }

/* ---- Collapsed rail: the crown/name step aside (out of flow, faded) and only the round Upgrade button shows,
        sending out sonar-style rings ---- */
.sidebar.collapsed:not(.peek) .account-info{ opacity:0; pointer-events:none; }
.sidebar.collapsed:not(.peek) .account-card{ background:none; }
/* The Upgrade button keeps its place in the row (the name block shrinks to nothing beside it) and glides to the middle
   with the rail's own width change, instead of jumping there the instant the panel starts to close. */
.account-upgrade{ transition: background .12s ease, opacity .16s ease, margin-left .15s cubic-bezier(.4,0,.2,1); }
.sidebar.collapsed:not(.peek) .account-upgrade{ margin-left:-11px; }
.sidebar.collapsed:not(.peek) .account-upgrade{ opacity:1; pointer-events:auto; }
.account-upgrade{ position:relative; }

/* ---- Footer: no solid patch in the glass theme, and no gap under it ---- */
.sidebar, .sidebar.collapsed{ padding-bottom:0; }
.sidebar-footer{ flex:0 0 auto; margin-top:0; padding:10px 0 6px; }

/* hover-opened (peek) rail had its own 20px bottom padding, leaving a gap under the Upgrade card */
.sidebar.collapsed.peek{ padding-bottom:0; }

/* ---- "Coming soon" cover for a page whose content is only a preview (Community): content covered by a semi-transparent wash (no blur), one clean card on top ---- */
#panel-community{ position:relative; min-height:calc(100vh - 120px); }
.soon-blurred{  opacity:.55; pointer-events:none; user-select:none; }
.soon-overlay{ position:absolute; inset:0; display:flex; align-items:flex-start; justify-content:center; padding-top:18vh; z-index:5; background:color-mix(in srgb, var(--bg,#0a0a0a) 72%, transparent); }
.soon-card{ display:flex; flex-direction:column; align-items:center; gap:12px; max-width:360px; padding:34px 36px; text-align:center; border:1px solid var(--border); border-radius:16px; background: var(--surface); box-shadow: 0 18px 50px rgba(0,0,0,.18); }
.soon-logo{ height:30px; width:auto; display:block; margin-bottom:4px; }
body.theme-light .soon-logo{ filter: invert(1); }
.soon-pill{ display:inline-flex; align-items:center; gap:7px; height:24px; padding:0 11px; border-radius:999px; border:1px solid var(--border); font-size:11.5px; font-weight:500; color:var(--text-muted); }
.soon-dot{ width:6px; height:6px; border-radius:50%; background:var(--lime); }
.soon-title{ font-family:var(--font-display, inherit); font-size:22px; font-weight:700; letter-spacing:-.02em; color:var(--text); }
.soon-desc{ font-size:13px; line-height:1.5; color:var(--text-dim); }

#panel-attribution, #panel-automation, #panel-autonomousads{ position:relative; min-height:calc(100vh - 120px); }
.soon-skeleton{ display:flex; flex-direction:column; gap:14px; }
.soon-sk-row{ display:grid; grid-template-columns:repeat(4,1fr); gap:14px; }
.soon-sk-row.two{ grid-template-columns:repeat(2,1fr); }
.soon-sk-row i, .soon-sk-wide{ display:block; height:110px; border:1px solid var(--border); border-radius:12px; background:var(--surface-2); }
.soon-sk-wide{ height:240px; }
.soon-sk-row.two i{ height:200px; }

/* ---- Header icon buttons (hide books eye, notifications bell) + every header icon 20% smaller ---- */
.topbar-icon-btn{ position:relative; padding:2px; border-radius:999px; background:var(--surface-2); border:1px solid var(--border); color:var(--text-muted); display:flex; align-items:center; justify-content:center; cursor:pointer; transition: color .15s ease, background .15s ease; }
.topbar-icon-dot{ width:22px; height:22px; border-radius:50%; display:flex; align-items:center; justify-content:center; background:none; color:inherit; }
.topbar-icon-btn:hover{ color:var(--text); }
.topbar-icon-btn svg{ width:11px; height:11px; fill:none; stroke:currentColor; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; }
.topbar-right > .topbar-icon-btn, .notif-wrap > .topbar-icon-btn, .ask-osp, .avatar{ zoom:.96; }
body.theme-light :is(.topbar-icon-btn, .avatar){ background:#FCFBF8; } /* the header round buttons (theme switch, notifications, profile) use the page cream */
.notif-wrap{ position:relative; display:flex; align-items:center; }
.notif-badge{ position:absolute; top:-4px; right:-4px; min-width:16px; height:16px; padding:0 4px; border-radius:99px; background:#ef4444; color:#fff; font-size:10px; font-weight:700; line-height:16px; text-align:center; box-sizing:border-box; }
.notif-panel{ position:absolute; top:calc(100% + 10px); right:0; width:300px; border:1px solid var(--border); border-radius:12px; background:var(--surface); box-shadow:0 18px 50px rgba(0,0,0,.25); display:none; z-index:60; overflow:hidden; }
.notif-wrap.open .notif-panel{ display:block; }
.notif-head{ display:flex; align-items:center; justify-content:space-between; padding:11px 14px; border-bottom:1px solid var(--border); font-size:13px; font-weight:600; color:var(--text); }
.notif-clear{ border:0; background:none; color:var(--text-dim); font:inherit; font-size:11.5px; font-weight:500; cursor:pointer; }
.notif-clear:hover{ color:var(--text); }
.notif-list{ max-height:320px; overflow-y:auto; }
.notif-empty{ padding:26px 14px; text-align:center; font-size:12.5px; color:var(--text-dim); line-height:1.5; }
.notif-item{ padding:11px 14px; border-bottom:1px solid var(--border); }
.notif-item:last-child{ border-bottom:0; }
.notif-item b{ display:block; font-size:12.5px; font-weight:600; color:var(--text); }
.notif-item span{ display:block; margin-top:2px; font-size:12px; line-height:1.4; color:var(--text-dim); }

/* ---- Hide books: titles scramble, covers become a flat placeholder (no blur) ---- */
body.books-hidden [data-bk-scr]{ position:relative; color:transparent !important; text-shadow:none !important; user-select:none; -webkit-user-select:none; }
body.books-hidden [data-bk-scr]::after{ content:attr(data-bk-scr); position:absolute; left:0; top:0; max-width:100%; overflow:hidden; white-space:nowrap; color:var(--text-dim); letter-spacing:.06em; pointer-events:none; }
body.books-hidden [data-bk-scr] *{ visibility:hidden; }
body.books-hidden .rtd-top-books-list-cover img, body.books-hidden .rtd-top-book-cover img, body.books-hidden .rtd-top-book-detail-cover, body.books-hidden .rtd-top-book-detail-backdrop, body.books-hidden .bkl-board-row > img{
  content:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 90'%3E%3Crect width='60' height='90' fill='%23343842'/%3E%3Cg fill='none' stroke='%239aa0ad' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round' transform='translate(18 33)'%3E%3Cpath d='M0 12s5-9 12-9 12 9 12 9-5 9-12 9S0 12 0 12z'/%3E%3Cpath d='M3 24L21 0'/%3E%3C/g%3E%3C/svg%3E");
  object-fit:cover;
}
body.books-hidden .ba-cover{ background:#343842 !important; }

/* Sphere talking: the face glows and breathes while its voice is playing */

/* ---- MCP integration (agents & chat bots) ---- */
.mcp-server{ display:flex; align-items:center; justify-content:space-between; gap:24px; background: var(--surface); border:1px solid var(--border); border-radius:12px; padding:18px 20px; margin-bottom:28px; }
.mcp-server-main{ display:flex; align-items:center; gap:14px; min-width:0; flex:1; }
.mcp-server-text .int-desc{ margin-bottom:0; max-width:560px; }
.mcp-server-side{ display:flex; flex-direction:column; align-items:flex-end; gap:8px; flex-shrink:0; }
.mcp-url-row{ display:flex; align-items:center; gap:8px; }
.mcp-url-row code, .mcp-field code{ font-family: var(--font-mono); font-size:12px; color: var(--text); background: var(--surface-2); border:1px solid var(--border); border-radius:8px; padding:7px 10px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.mcp-mini-btn{ padding:6px 12px; font-size:12px; }
.mcp-card-actions{ display:flex; gap:8px; }
.mcp-card-actions .int-btn{ flex:1; }
.int-card.mcp-on{ border-color: var(--lime-dim); }
.mcp-modal{ width:560px; }
.mcp-intro{ font-size:12.5px; color: var(--text-muted); line-height:1.55; margin-bottom:16px; }
.mcp-block{ margin-bottom:18px; }
.mcp-block-label{ font-size:12px; font-weight:600; color: var(--text); margin-bottom:8px; }
.mcp-field{ display:flex; align-items:center; gap:8px; }
.mcp-field code{ flex:1; min-width:0; }
.mcp-field .mcp-mini-btn{ flex-shrink:0; }
.mcp-steps{ margin:0 0 0 18px; padding:0; font-size:12.5px; color: var(--text-muted); line-height:1.7; }
.mcp-steps b{ color: var(--text); font-weight:600; }
.mcp-code{ position:relative; font-family: var(--font-mono); font-size:11.5px; line-height:1.55; color: var(--text); background: var(--surface-2); border:1px solid var(--border); border-radius:10px; padding:12px 14px; white-space:pre; overflow-x:auto; margin:0; }
.mcp-code-copy{ margin-top:8px; }
.mcp-seg{ display:grid; grid-template-columns:1fr 1fr; gap:10px; }
.mcp-seg button{ border:1px solid var(--border); background: var(--surface-2); color: var(--text-muted); border-radius:10px; padding:11px 12px; font:inherit; font-size:12.5px; font-weight:600; cursor:pointer; text-align:left; transition: border-color .12s ease, background .12s ease; }
.mcp-seg button small{ display:block; font-weight:400; font-size:11px; color: var(--text-dim); margin-top:2px; }
.mcp-seg button:hover{ border-color: var(--lime-dim); }
.mcp-seg button.on{ border-color: var(--lime); background: var(--lime-wash); color: var(--text); }
.mcp-scopes{ display:grid; grid-template-columns:1fr 1fr; gap:8px 14px; }
.mcp-scopes label{ display:flex; align-items:center; gap:8px; font-size:12.5px; color: var(--text-muted); cursor:pointer; }
.mcp-text{ width:100%; box-sizing:border-box; background: var(--surface-2); border:1px solid var(--border); border-radius:8px; padding:9px 11px; color: var(--text); font:inherit; font-size:12.5px; outline:none; }
.mcp-text:focus{ border-color: var(--lime-dim); }
.mcp-modal-foot{ display:flex; align-items:center; gap:10px; padding:14px 22px; border-top:1px solid var(--border); flex-shrink:0; }
.mcp-modal-foot .mcp-test-result{ font-size:12px; color: var(--text-dim); flex:1; min-width:0; }
.mcp-modal-foot .mcp-test-result.ok{ color: var(--lime-text); }
.mcp-danger{ color:#f87171; }
@media (max-width:900px){ .mcp-server{ flex-direction:column; align-items:flex-start; } .mcp-server-side{ align-items:flex-start; } }

/* Connect-agent popup: reuses the Sphere Settings (.perm-*) look; these are only the bits that popup does not have */
.mcp-src .p-name small{ display:block; font-weight:400; font-size:11px; color: var(--text-dim); margin-top:2px; }
.mcp-src .mcp-tick{ width:20px; height:20px; border-radius:6px; border:1px solid var(--border); display:flex; align-items:center; justify-content:center; flex-shrink:0; transition: background .12s ease, border-color .12s ease; }
.mcp-src .mcp-tick svg{ width:12px; height:12px; stroke:#0b0b0b; fill:none; stroke-width:2.4; opacity:0; }
.mcp-src.on .mcp-tick{ background: var(--lime); border-color: var(--lime); }
.mcp-src.on .mcp-tick svg{ opacity:1; }
.perm-list.is-locked .mcp-src:hover{ background: var(--surface-2); }
.perm-list.is-locked .mcp-src{ opacity:.7; }
.perm-list.is-locked .mcp-src.on{ opacity:1; }
.mcp-warn{ width:420px; }
.mcp-warn-icon{ width:48px; height:48px; border-radius:14px; background:rgba(248,113,113,.12); display:flex; align-items:center; justify-content:center; margin:0 auto 14px; }
.mcp-warn-icon svg{ width:20px; height:20px; stroke:#f87171; fill:none; stroke-width:1.8; stroke-linejoin:round; stroke-linecap:round; }
.mcp-warn .mcp-intro b{ color: var(--text); }

/* While any popup built on .perm-overlay is open (Sphere Settings, Connect agent, its warning), the page behind must not scroll */
html:has(.perm-overlay.open), body:has(.perm-overlay.open){ overflow:hidden; }
.perm-overlay{ overscroll-behavior:contain; }
.mcp-warn .perm-header{ justify-content:center; position:relative; }
.mcp-warn .mcp-intro{ text-align:center; }

/* --lime-wash is computed once at :root (lime), so the White and Glass themes' purple --lime-rgb never reached it: the selected
   access card and row hover washes in the popups showed pale lime green. Re-state it here, scoped to the popups. */
body.theme-light .perm-overlay{ --lime-wash: rgba(var(--pd-rgb),0.12); }

/* Connect agent popup (Manage on the MCP cards) and its warning: solid colour, not the see-through glass the other popups get */
body:not(.x):not(.y):not(.z) .perm-overlay .perm-modal.mcp-modal, body:not(.x):not(.y):not(.z) .perm-overlay .perm-modal.mcp-warn{ background: var(--surface) !important; border-color: var(--border) !important; box-shadow: 0 20px 60px rgba(0,0,0,0.5) !important; backdrop-filter:none !important; -webkit-backdrop-filter:none !important; }

/* Sphere side chat (dark mode): solid panel, not the see-through glass tint */
body:not(.theme-light):not(.theme-glass):not(.x) .ai-drawer, body:not(.theme-light):not(.theme-glass):not(.x) .ai-drawer-footer{ background: var(--surface) !important; backdrop-filter:none !important; -webkit-backdrop-filter:none !important; }

/* Date range / comparison calendars (dark mode): solid, no see-through tint */
body:not(.theme-light):not(.theme-glass):not(.x) .date-range-popover{ background: var(--surface) !important; backdrop-filter:none !important; -webkit-backdrop-filter:none !important; }

/* Filter buttons (every page): the label is capped so a long value (e.g. the Lifetime range "Jan 1, 2011 – Oct 17, 2026", several marketplaces, long author names) can't stretch the button; the end is cut with "…" */
.dv-pill > span,
.date-range-pill .date-range-pill-label{ display:inline-block; max-width:150px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; vertical-align:middle; }
.dv-pill:has(> svg > rect[x="3"][y="4"][width="18"]) > span, .date-range-pill .date-range-pill-label{ max-width:130px; }

/* Book Analytics: once the filter buttons have moved up into the top header (on scroll) they show only their icon; hover shows the name */
#topbarBookAnalyticsReactFilters .dv-pill > span, #topbarBookAnalyticsReactFilters .dv-pill > svg:last-child{ display:none; }
#topbarBookAnalyticsReactFilters .dv-pill{ padding:8px 10px; }

/* Connect popup: per-action dropdown under each data source (mock-up, see script.js) */
.mcp-src-wrap{ border-bottom:1px solid var(--border); }
.mcp-src-wrap:last-child{ border-bottom:none; }
.mcp-src-wrap .perm-cat{ border-bottom:none; }
.mcp-src .mcp-drop, .mcp-src .mcp-nw{ display:none; }
.mcp-src-wrap.has-drop .mcp-drop{ display:flex; cursor:pointer; }
.mcp-src-wrap.has-drop .mcp-nw{ display:block; }
.mcp-src .mcp-drop svg{ transition: transform .15s ease; }
.mcp-src-wrap.open .mcp-drop svg{ transform: rotate(180deg); }
.mcp-sub{ display:none; padding:4px 14px 12px 49px; background: var(--surface-2); }
.mcp-src-wrap.open .mcp-sub{ display:grid; grid-template-columns:1fr; gap:8px; }
.mcp-act{ display:flex; align-items:center; gap:8px; font-size:12.5px; color: var(--text-muted); }
.mcp-act input{ accent-color: var(--lime); }
.mcp-nw{ position:relative; width:8px; height:8px; border-radius:50%; background:#ef4444; flex-shrink:0; cursor:help; animation: mcp-nw-blink 1.1s ease-in-out infinite; }
.mcp-nw::before{ content:''; position:absolute; inset:0; border-radius:50%; background:rgba(239,68,68,.55); pointer-events:none; animation: mcp-nw-ring 1.1s ease-in-out infinite; }
.mcp-nw::after{ content:attr(data-tip); position:absolute; right:calc(100% + 8px); top:50%; transform:translateY(-50%); background:#ef4444; color:#fff; font-size:11px; font-weight:600; padding:4px 8px; border-radius:6px; white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .12s ease; }
.mcp-nw:hover::after{ opacity:1; }
@keyframes mcp-nw-blink{ 0%,100%{ opacity:1; } 50%{ opacity:.35; } }
@keyframes mcp-nw-ring{ 0%,100%{ transform:scale(1); opacity:.55; } 50%{ transform:scale(2); opacity:0; } }
.mcp-act.is-write{ display:none; }
.perm-list.show-write .mcp-act.is-write{ display:flex; }

/* Sync alert (test): compact solid card centred on the screen at the top, just under the top bar (script.js sets its top), with a red animated dot */
.pd-sync-alert{ position:fixed; top:68px; left:50%; z-index:58; display:flex; align-items:flex-start; gap:12px; width:480px; max-width:calc(100vw - 32px); box-sizing:border-box; padding:13px 12px 13px 14px; border-radius:10px; background:var(--surface,#141414); border:1px solid var(--border,#34372f); box-shadow:0 10px 30px rgba(0,0,0,.35); color:var(--text,#edf0e6); opacity:0; pointer-events:none; transform:translate(-50%,-8px); transition:opacity .2s ease, transform .2s ease; }
.pd-sync-alert.open{ opacity:1; pointer-events:auto; transform:translate(-50%,0); }
.pd-sync-alert-dot{ position:relative; display:inline-grid; place-items:center; width:18px; height:18px; flex:none; margin-top:1px; }
.pd-sync-alert-core{ position:relative; z-index:1; width:8px; height:8px; border-radius:50%; background:#ef4444; box-shadow:0 0 6px rgba(239,68,68,.8); }
.pd-sync-alert-ripple{ position:absolute; inset:0; border-radius:50%; background:rgba(239,68,68,.5); animation:pdSyncAlertRipple 2s ease-out infinite; pointer-events:none; }
.pd-sync-alert-ripple--late{ animation-delay:1s; }
@keyframes pdSyncAlertRipple{ 0%{ transform:scale(.4); opacity:.9; } 100%{ transform:scale(1.7); opacity:0; } }
.pd-sync-alert-copy{ display:flex; flex-direction:column; gap:3px; min-width:0; flex:1; padding-bottom:24px; }
.pd-sync-alert-title{ font-size:13px; font-weight:600; }
.pd-sync-alert-text{ font-size:12px; line-height:1.45; color:var(--text-muted,#a8aea0); }
.pd-sync-alert-action{ position:absolute; right:14px; bottom:11px; display:inline-flex; align-items:center; gap:5px; margin:0; padding:0; white-space:nowrap; border:0; background:none; color:#ef4444; font:inherit; font-size:12.5px; font-weight:600; cursor:pointer; }
.pd-sync-alert-action svg{ width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; transition:transform .15s ease; }
.pd-sync-alert-action:hover svg{ transform:translateX(3px); }
.pd-sync-alert-action:hover .pd-sync-alert-action-label{ text-decoration:underline; text-underline-offset:3px; }
.pd-sync-alert.is-steps .pd-sync-alert-action svg{ display:none; }
body.theme-light .pd-sync-alert-action{ color:#dc2626; }
.pd-sync-alert-close{ flex:none; width:22px; height:22px; margin:-2px 0 0; display:grid; place-items:center; border:0; border-radius:6px; background:transparent; color:var(--text-dim,#757b6e); cursor:pointer; }
.pd-sync-alert-close:hover{ background:var(--surface-2,#1d1d1d); color:var(--text,#edf0e6); }
.pd-sync-alert-close svg{ width:12px; height:12px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; }
@media (prefers-reduced-motion:reduce){ .pd-sync-alert-ripple{ animation:none; opacity:.3; transform:scale(1.4); } .pd-sync-alert{ transition:none; } }

/* Green dot beside a finished page's sidebar item; hovering it slides out "Done". */
.nav-done-dot{ position:relative; flex:0 0 auto; display:inline-flex; align-items:center; margin-left:auto; padding:4px; cursor:default; outline:none; }
.nav-done-core{ position:relative; width:7px; height:7px; border-radius:50%; background:#22c55e; box-shadow:0 0 6px rgba(34,197,94,.8); }
.nav-done-text{ max-width:0; overflow:hidden; opacity:0; margin-left:0; color:#22c55e; font-family:var(--font-mono); font-size:9.5px; font-weight:600; white-space:nowrap; transition:max-width .18s ease, opacity .16s ease, margin-left .18s ease; }
.nav-done-dot:hover .nav-done-text, .nav-done-dot:focus-visible .nav-done-text{ max-width:40px; opacity:1; margin-left:6px; }
.sidebar.collapsed:not(.peek) .nav-done-dot{ display:none; }

/* Blue dot beside every sidebar section that is built in React; hovering it slides out "React". */
.nav-react-dot{ position:relative; flex:0 0 auto; display:inline-flex; align-items:center; margin-left:auto; padding:4px; cursor:default; outline:none; }
.nav-react-core{ position:relative; width:7px; height:7px; border-radius:50%; background:#3b9cff; box-shadow:0 0 6px rgba(59,156,255,.85); }
.nav-react-text{ max-width:0; overflow:hidden; opacity:0; margin-left:0; color:#3b9cff; font-family:var(--font-mono); font-size:9.5px; font-weight:600; white-space:nowrap; transition:max-width .18s ease, opacity .16s ease, margin-left .18s ease; }
.nav-react-dot:hover .nav-react-text, .nav-react-dot:focus-visible .nav-react-text{ max-width:40px; opacity:1; margin-left:6px; }
.nav-react-dot + .nav-done-dot{ margin-left:0; }
.sidebar.collapsed:not(.peek) .nav-react-dot{ display:none; }
@media (prefers-reduced-motion:reduce){ .nav-react-core::before, .nav-react-core::after{ animation:none; opacity:.25; transform:scale(1.6); } }

/* Integrations (React): search box + the note that points to the classic MCP manager. */
.integrations-page .int-search-box{ display:flex; align-items:center; gap:10px; max-width:420px; margin:18px 0 22px; padding:0 14px; height:40px; border:1px solid var(--border); border-radius:10px; background:var(--surface); color:var(--text-dim); }
.integrations-page .int-search-box svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.6; flex-shrink:0; }
.integrations-page .int-search-box input{ flex:1; min-width:0; border:0; outline:0; background:transparent; color:var(--text); font:inherit; font-size:13px; }
.integrations-page .int-empty{ color:var(--text-dim); font-size:13px; }
.integrations-page .int-classic-note{ display:flex; align-items:center; justify-content:space-between; gap:16px; padding:16px 18px; border:1px solid var(--border); border-radius:12px; background:var(--surface); }
.integrations-page .int-classic-note strong{ display:block; font-size:14px; margin-bottom:4px; }
.integrations-page .int-classic-note span{ font-size:12.5px; color:var(--text-muted); }
.integrations-page .int-classic-note .int-btn{ width:auto; flex:0 0 auto; padding:0 16px; }
@media (max-width:1200px){ .integrations-page .int-grid{ grid-template-columns:repeat(3,1fr); } }
@media (max-width:900px){ .integrations-page .int-grid{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:560px){ .integrations-page .int-grid{ grid-template-columns:1fr; } .integrations-page .int-classic-note{ flex-direction:column; align-items:flex-start; } }

/* Black & white mode button (yin-yang) in the top bar. */

/* Purple accent as variables: the normal look keeps the original purples; Black & white mode (yin-yang button) swaps them for white in dark mode and black in light mode. */
:root{ --pd-dark-rgb:32,22,52; --pd-1b1530:#1b1530; --pd-rgb:81,80,246; --pd-on:#fff; --pd-7c5cff:#5150f6; --pd-b07bff:#7978f8; --pd-d8b4fe:#a5a4fa; --pd-bd5cff:#7978f8; --pd-a12fff:#5150f6; --pd-8420f1:#4544d1; --pd-6812d0:#302fa3; --pd-4e079f:#302fa3; --pd-a855f7:#5150f6; --pd-c084fc:#7978f8; --pd-7c3aed:#4544d1; --pd-6d28d9:#4544d1; --pd-7e22ce:#4544d1; --pd-9333ea:#5150f6; --pd-8b5cf6:#7978f8; --pd-4c1d95:#302fa3; --pd-c99cff:#a5a4fa; --pd-b878ff:#7978f8; --pd-6366f1:#4544d1; --pd-f3e8ff:#eeeefb; --pd-ead8ff:#d4d4fd; --pd-af5aff:#5150f6; --pd-8846c6:#4544d1; }
html.pd-mono.pd-mono body{ --pd-dark-rgb:24,24,24; --pd-1b1530:#181818; --pd-rgb:255,255,255; --pd-on:#000; --pd-7c5cff:#d9d9d9; --pd-b07bff:#f5f5f5; --pd-d8b4fe:#ffffff; --pd-bd5cff:#f0f0f0; --pd-a12fff:#e9e9e9; --pd-8420f1:#dddddd; --pd-6812d0:#cfcfcf; --pd-4e079f:#b5b5b5; --pd-a855f7:#ffffff; --pd-c084fc:#ffffff; --pd-7c3aed:#e8e8e8; --pd-6d28d9:#d4d4d4; --pd-7e22ce:#e0e0e0; --pd-9333ea:#dcdcdc; --pd-8b5cf6:#ececec; --pd-4c1d95:#bdbdbd; --pd-c99cff:#ffffff; --pd-b878ff:#f2f2f2; --pd-6366f1:#e6e6e6; --pd-f3e8ff:#262626; --pd-ead8ff:#2e2e2e; --pd-af5aff:#ffffff; --pd-8846c6:#cfcfcf; --lime-rgb:255,255,255; --lime:#ffffff; --lime-text:#ffffff; --lime-dim:#cfcfcf; }
html.pd-mono.pd-mono body.theme-light{ --pd-dark-rgb:24,24,24; --pd-1b1530:#181818; --pd-rgb:0,0,0; --pd-on:#fff; --pd-7c5cff:#161616; --pd-b07bff:#252525; --pd-d8b4fe:#303030; --pd-bd5cff:#202020; --pd-a12fff:#141414; --pd-8420f1:#121212; --pd-6812d0:#0e0e0e; --pd-4e079f:#080808; --pd-a855f7:#000000; --pd-c084fc:#2a2a2a; --pd-7c3aed:#111111; --pd-6d28d9:#1a1a1a; --pd-7e22ce:#161616; --pd-9333ea:#191919; --pd-8b5cf6:#1f1f1f; --pd-4c1d95:#000000; --pd-c99cff:#333333; --pd-b878ff:#262626; --pd-6366f1:#222222; --pd-f3e8ff:#ededed; --pd-ead8ff:#e3e3e3; --pd-af5aff:#000000; --pd-8846c6:#333333; --lime-rgb:0,0,0; --lime:#000000; --lime-text:#000000; --lime-dim:#333333; }

/* Black & white mode fixes: text on a filled (black or white) button always contrasts with it, and the coloured glow behind buttons is gone. */
html.pd-mono.pd-mono body.theme-light :is(.nav-badge:not(.soon), .btn-primary-lime, .toggle-opt.active, .plan-tier-badge, .perm-save, .chartb-ai-send, .auth-tab.active, .traffic-test-filter-apply, .ai-send-btn, .ai-expand-send, .na-send-btn){ color:#fff !important; }
html.pd-mono.pd-mono body.theme-light :is(.ai-send-btn, .ai-expand-send, .na-send-btn, .chartb-ai-send) svg{ stroke:#fff !important; }
html.pd-mono.pd-mono :is(.btn-primary-lime, .btn-primary-lime:hover, .ai-send-btn, .ai-send-btn:hover, .ai-expand-send, .na-send-btn, .chartb-ai-send, .perm-save, .account-upgrade, .ask-osp-orb-wrap, .traffic-test-filter-apply){ box-shadow:none !important; text-shadow:none !important; }
html.pd-mono.pd-mono :is(.btn-primary-lime, .ai-send-btn, .na-send-btn, .account-upgrade)::before,
html.pd-mono.pd-mono :is(.btn-primary-lime, .ai-send-btn, .na-send-btn, .account-upgrade)::after{ box-shadow:none !important; filter:none !important; }

.theme-toggle{ color:var(--text); }

/* Yin-yang button: still until clicked. A click plays the chosen animation once (pick it in the Components Library). */
.mono-yy{ width:19px; height:19px; overflow:visible; }
.mono-yy *{ transform-box:fill-box; transform-origin:center; }
.mono-yy.yy-play[data-anim="merge"] .yy-dark{ animation:yyMergeA .85s cubic-bezier(.22,.9,.3,1); }
.mono-yy.yy-play[data-anim="merge"] .yy-dot-a{ animation:yyMergeA .85s cubic-bezier(.22,.9,.3,1); }
.mono-yy.yy-play[data-anim="merge"] .yy-dot-b{ animation:yyMergeB .85s cubic-bezier(.22,.9,.3,1); }
.mono-yy.yy-play[data-anim="merge"] .yy-ring{ animation:yyPop .85s ease-out; }
.mono-yy.yy-play[data-anim="spin"] .yy-body{ animation:yySpin .8s cubic-bezier(.3,.8,.3,1); }
.mono-yy.yy-play[data-anim="flip"] .yy-body{ animation:yyFlip .8s cubic-bezier(.3,.8,.3,1); }
.mono-yy.yy-play[data-anim="bloom"] .yy-body{ animation:yyBloom .75s cubic-bezier(.3,1.4,.4,1); }
.mono-yy.yy-play[data-anim="split"] .yy-dark{ animation:yySplitA .8s cubic-bezier(.3,.9,.3,1); }
.mono-yy.yy-play[data-anim="split"] .yy-dot-a{ animation:yySplitA .8s cubic-bezier(.3,.9,.3,1); }
.mono-yy.yy-play[data-anim="split"] .yy-dot-b{ animation:yySplitB .8s cubic-bezier(.3,.9,.3,1); }
.mono-yy.yy-play[data-anim="orbit"] .yy-dot-a{ animation:yyOrbitA .9s ease-in-out; transform-origin:12px 12px; transform-box:view-box; }
.mono-yy.yy-play[data-anim="orbit"] .yy-dot-b{ animation:yyOrbitA .9s ease-in-out; transform-origin:12px 12px; transform-box:view-box; }
.mono-yy.yy-play[data-anim="orbit"] .yy-body{ animation:yyTurn .9s ease-in-out; }
@keyframes yyMergeA{ 0%{ transform:translate(9px,-9px) rotate(70deg) scale(.4); opacity:0; } 55%{ opacity:1; } 100%{ transform:none; opacity:1; } }
@keyframes yyMergeB{ 0%{ transform:translate(-9px,9px) rotate(-70deg) scale(.4); opacity:0; } 55%{ opacity:1; } 100%{ transform:none; opacity:1; } }
@keyframes yyPop{ 0%{ transform:scale(.55); opacity:.2; } 70%{ transform:scale(1.12); opacity:1; } 100%{ transform:none; } }
@keyframes yySpin{ from{ transform:rotate(-360deg); } to{ transform:rotate(0); } }
@keyframes yyFlip{ 0%{ transform:perspective(60px) rotateY(0); } 50%{ transform:perspective(60px) rotateY(90deg) scale(.8); } 100%{ transform:perspective(60px) rotateY(0); } }
@keyframes yyBloom{ 0%{ transform:scale(.2); opacity:0; } 100%{ transform:none; opacity:1; } }
@keyframes yySplitA{ 0%,100%{ transform:none; } 40%{ transform:translate(3px,-6px); } }
@keyframes yySplitB{ 0%,100%{ transform:none; } 40%{ transform:translate(-3px,6px); } }
@keyframes yyOrbitA{ from{ transform:rotate(0); } to{ transform:rotate(360deg); } }
@keyframes yyTurn{ 0%,100%{ transform:none; } 50%{ transform:scale(.88); } }
@media (prefers-reduced-motion:reduce){ .mono-yy.yy-play *{ animation-duration:.01s !important; } }

.theme-toggle .mono-yy{ transform:scale(1.45); }
/* Notification and theme (yin-yang) buttons light up with the same purple outline as the profile icon on hover. */
.topbar-icon-btn{ transition:border-color .15s ease, color .15s ease; }
.topbar-icon-btn:hover, .topbar-icon-btn:focus-visible{ border-color:var(--lime-dim); }

/* Docked into the top bar, the filter buttons fold down to just their icon (flag / calendar / target), like Book Analytics: Amazon Ads, KDP Reports, dashboards and campaign pages. */
.pill-type-icon{ display:none; }
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters) .pill-type-icon{ display:block; }
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters) .dv-pill > span[title],
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters) .dv-pill > svg:last-child{ display:none; }
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters) .dv-pill{ padding:8px 10px; }
/* Collapsed: every filter button gets the same fixed box so the flag (a text character) is no bigger than the calendar / target icons. */
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters) .raa-actions .dv-pill{ box-sizing:border-box; width:28px; height:28px; min-width:28px; padding:0; border-radius:50%; zoom:.96; justify-content:center; }
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters) .raa-actions .dv-pill > span:not([title]){ display:flex; align-items:center; justify-content:center; width:14px; height:14px; font-size:12px; line-height:1; overflow:visible; }


/* The Ask Sphere (side chat) button in the top bar is not shown on the Sphere Agent page, which has its own chat. */
body.on-sphere-page .ask-osp { display:none !important; }

/* Book Analytics: collapsed date button is round too, same size as the others. */
#topbarBookAnalyticsReactFilters .raa-actions .dv-pill{ box-sizing:border-box; width:28px; height:28px; min-width:28px; padding:0; border-radius:50%; zoom:.96; justify-content:center; }

/* Collapsed filter buttons (round): same border, colours and hover as the dark-mode button -- thin purple outline on hover, nothing else changes. */
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill{ background:var(--surface-2); border:1px solid var(--border); color:var(--text-muted); transition:border-color .15s ease, color .15s ease; }
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill:hover,
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill:focus-visible{ background:var(--surface-2); border-color:var(--lime-dim); color:var(--text); }
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill svg{ stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }

/* One shared border for every round header button (dark mode, notifications, Sphere, profile, and the collapsed filter buttons): 1px, same colour, same thin purple outline on hover. Edit here to change all of them together. */
:is(.topbar-icon-btn, .ask-osp, .avatar, :is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill){ border:1px solid var(--pd-round-border); box-shadow:none; outline:none; transition:border-color .15s ease, color .15s ease; }
:is(.topbar-icon-btn, .ask-osp, .avatar, :is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill):is(:hover, :focus-visible){ border-color:var(--lime-dim); }

/* Dark mode: the collapsed round filter buttons drop the glass gradient, bright top edge, drop shadow and icon glow, so they look like the dark-mode / bell / profile buttons. */
body:not(.theme-light):not(.theme-glass) :is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill{ background:var(--surface-2) !important; border-color:var(--pd-round-border) !important; border-top-color:var(--pd-round-border) !important; box-shadow:none !important; backdrop-filter:none !important; -webkit-backdrop-filter:none !important; }
body:not(.theme-light):not(.theme-glass) :is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill:is(:hover, :focus-visible){ border-color:var(--lime-dim) !important; border-top-color:var(--lime-dim) !important; }
body:not(.theme-light):not(.theme-glass) :is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill svg{ filter:none !important; }

/* Same .96 zoom as the other round header buttons, so their 1px border is drawn exactly as thin as theirs. */
:is(#topbarAdsReactFilters, #topbarKdpReportsReactFilters, #topbarPublishingOverviewFilters, #topbarAdvertisingOverviewFilters, #topbarBookAnalyticsReactFilters) .raa-actions .dv-pill{ zoom:.96; }

/* Border colour of the round header buttons: the normal border with 20% of the text colour mixed in (stronger than before). Change 20% to adjust. */
body{ --pd-round-border:color-mix(in srgb, var(--border) 80%, var(--text) 20%); }

/* Space between the docked filter buttons (marketplace, date, ...) and the dark-mode icon: 20px (the 7px header gap + 13px here). */
.topbar-amz-filters.docked{ margin-right:13px; }

/* KDP Reports note under the sync alert: same card, but a purple (info) dot instead of the red alert dot, and no action button so no extra bottom padding. */
.pd-sync-note .pd-sync-alert-core{ background:var(--lime-dim,#4544d1); box-shadow:0 0 6px rgba(136,70,198,.7); }
.pd-sync-note .pd-sync-alert-ripple{ background:rgba(136,70,198,.45); }
.pd-sync-note .pd-sync-alert-copy{ padding-bottom:0; }


/* ---- Flat buttons: no glass shine, glow, rim light or frosted blur on any button, in any theme. Plain fill + thin outline. ---- */
:is(.btn-outline, .dv-pill, .dv-icon-btn, .dv-back, .na-mode-pill, .na-quick-pill, .na-send-btn, .na-input-plus,
    .ai-suggestion, .ai-ask-chip, .ai-settings-btn, .ai-send-btn, .ai-expand-send, .chartb-ai-send, .claude-badge){
  background: var(--surface-2) !important;
  border:1px solid var(--border) !important;
  box-shadow:none !important;
  backdrop-filter:none !important; -webkit-backdrop-filter:none !important;
  text-shadow:none !important;
}
:is(.btn-outline, .dv-pill, .dv-icon-btn, .dv-back, .na-mode-pill, .na-quick-pill, .na-send-btn, .na-input-plus,
    .ai-suggestion, .ai-ask-chip, .ai-settings-btn, .ai-send-btn, .ai-expand-send, .chartb-ai-send) svg{ filter:none !important; }
:is(.btn-outline, .dv-pill, .dv-icon-btn, .dv-back, .na-mode-pill, .na-quick-pill, .na-send-btn, .na-input-plus,
    .ai-suggestion, .ai-ask-chip, .ai-settings-btn, .ai-send-btn):hover{
  border-color: var(--lime-dim) !important; box-shadow:none !important;
}
.btn-primary-lime, .btn-primary-lime:hover, .ai-send-btn, .na-send-btn{ box-shadow:none !important; }
.dv-pill.active{ background: rgba(var(--lime-rgb),0.14) !important; border-color: var(--lime-dim) !important; }
.ai-send-btn, .na-send-btn, .ai-expand-send, .chartb-ai-send{ background: var(--lime) !important; border:none !important; }
.ai-send-btn:hover, .na-send-btn:hover{ filter:brightness(1.08); }

/* The support button/popup sit above everything (z-index 9500), so they showed on top of the Log in / Sign up screen. Hide them while it is open. */
body:has(#authScreen.open) :is(.pd-sup-fab, .pd-sup-panel){ display:none !important; }


/* Each software shows its own sidebar menu; the wrapper itself adds no box. */
.app-menu{ display:contents; }

/* ---- Each software has its own colour: Data = purple (default), Create = teal, Research = orange. Only the accent changes. ---- */
body[data-app="research"][data-app="research"]{ --pd-1b1530:#291b12; --pd-7c5cff:#ff802b; --pd-b07bff:#ff9046; --pd-d8b4fe:#fdad78; --pd-bd5cff:#ff802b; --pd-a12fff:#ff6905; --pd-8420f1:#dd610e; --pd-6812d0:#b3510f; --pd-4e079f:#893a06; --pd-a855f7:#f57a29; --pd-c084fc:#fb9450; --pd-7c3aed:#e86a15; --pd-6d28d9:#bc5f21; --pd-7e22ce:#b1581d; --pd-9333ea:#de6717; --pd-8b5cf6:#f47e2f; --pd-4c1d95:#804219; --pd-c99cff:#ffa162; --pd-b878ff:#ff8f44; --pd-6366f1:#ed8037; --pd-f3e8ff:#ffc8a4; --pd-ead8ff:#ffc096; --pd-af5aff:#ff7f2a; --pd-8846c6:#b06736; --pd-rgb:245,122,41; --pd-dark-rgb:52,34,22; --lime-rgb:255,127,42; }
body[data-app="create"][data-app="create"]{ --pd-1b1530:#102320; --pd-7c5cff:#08f9d9; --pd-b07bff:#1ef9dc; --pd-d8b4fe:#49f8e1; --pd-bd5cff:#08f9d9; --pd-a12fff:#06dabe; --pd-8420f1:#10baa3; --pd-6812d0:#119685; --pd-4e079f:#087365; --pd-a855f7:#11e5c9; --pd-c084fc:#28f4d9; --pd-7c3aed:#17c3ac; --pd-6d28d9:#209e8d; --pd-7e22ce:#1c9585; --pd-9333ea:#18bba5; --pd-8b5cf6:#13e7cb; --pd-4c1d95:#186c61; --pd-c99cff:#36fae0; --pd-b878ff:#1cf9dc; --pd-6366f1:#1ae2c7; --pd-f3e8ff:#6dfbe8; --pd-ead8ff:#62fbe7; --pd-af5aff:#07f9d8; --pd-8846c6:#319588; --pd-rgb:17,229,201; --pd-dark-rgb:22,52,48; --lime-rgb:7,249,216; }

/* ---- Software switcher: a floating glass bar of three tabs; the open one glows in its own colour and shows its name ---- */
.app-switcher{ position:relative; display:flex; gap:4px; margin:16px 0 0; padding:4px; border:1px solid var(--border); border-radius:14px; background:linear-gradient(180deg, rgba(255,255,255,.045), rgba(255,255,255,.015)), var(--surface-2); box-shadow:inset 0 1px 0 rgba(255,255,255,.06); flex-shrink:0; box-sizing:border-box; width:100%; overflow:hidden; max-height:50px; transition:max-height .18s ease, opacity .14s ease, margin .18s ease, padding .18s ease, border-width .18s ease; }
.app-switch-btn{ --app:#5150f6; position:relative; display:flex; align-items:center; justify-content:center; gap:0; flex:1 1 0; min-width:0; height:34px; padding:0 8px; border:0; border-radius:10px; background:none; color:var(--text-dim); font:inherit; font-size:12.5px; font-weight:600; letter-spacing:.01em; cursor:pointer; white-space:nowrap; overflow:hidden; transition:flex-grow .26s cubic-bezier(.4,0,.2,1), background .2s ease, color .2s ease, box-shadow .2s ease, gap .22s ease; }
.app-switch-btn[data-app="create"]{ --app:#14b8a6; }
.app-switch-btn[data-app="research"]{ --app:#f97316; }
.app-switch-btn svg{ width:18px; height:18px; flex:0 0 auto; fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; transition:transform .2s ease; }
.app-switch-btn svg circle[r=".6"]{ fill:currentColor; stroke:none; }
.app-switch-label{ max-width:0; opacity:0; overflow:hidden; transition:max-width .26s cubic-bezier(.4,0,.2,1), opacity .18s ease; }
.app-switch-btn:hover:not(.active), .app-switch-btn:focus-visible{ color:var(--app); background:color-mix(in srgb, var(--app) 9%, transparent); outline:none; }
.app-switch-btn:hover:not(.active) svg{ transform:scale(1.1); }
.app-switch-btn.active{ flex-grow:3; min-width:max-content; padding:0 13px; gap:8px; color:#fff; background:linear-gradient(135deg, color-mix(in srgb, var(--app) 88%, #fff 0%), color-mix(in srgb, var(--app) 62%, #000 0%)); box-shadow:0 6px 16px -6px var(--app), inset 0 1px 0 rgba(255,255,255,.28); }
.app-switch-btn.active .app-switch-label{ max-width:90px; opacity:1; }
body.theme-light .app-switch-btn.active{ color:#fff; }
.app-switcher + .sidebar-search{ margin-top:8px; }
.sidebar.collapsed:not(.peek) .app-switcher{ max-height:0; opacity:0; margin-top:0; padding-top:0; padding-bottom:0; border-width:0; pointer-events:none; }
/* The second word of the sidebar title takes the open software's colour. */
.brand-app{ color:var(--lime); transition:color .25s ease; }
/* The title is two lines so the longest name ("Research") always fits: a small "Publishers" over the software's name in its own colour. */
.brand-name{ display:flex; flex-direction:column; justify-content:center; gap:1px; line-height:1.05; }
.brand-co{ color:var(--text-muted); font-size:10px; font-weight:600; letter-spacing:.12em; text-transform:uppercase; }
.brand-name .brand-app{ font-size:10px; font-weight:800; letter-spacing:.12em; text-transform:uppercase; }

/* Publishers Suite: blue */
body[data-app="suite"][data-app="suite"]{ --pd-1b1530:#131c2a; --pd-7c5cff:#3782fa; --pd-b07bff:#5293fb; --pd-d8b4fe:#84b1fa; --pd-bd5cff:#3782fa; --pd-a12fff:#116af9; --pd-8420f1:#1361dd; --pd-6812d0:#1451b3; --pd-4e079f:#0a3a89; --pd-a855f7:#347cf0; --pd-c084fc:#5b97f7; --pd-7c3aed:#1f6be5; --pd-6d28d9:#2660bc; --pd-7e22ce:#2259b1; --pd-9333ea:#1d67de; --pd-8b5cf6:#3a80ef; --pd-4c1d95:#1c4380; --pd-c99cff:#6ea4fb; --pd-b878ff:#5091fb; --pd-6366f1:#4282e9; --pd-f3e8ff:#b0cdfd; --pd-ead8ff:#a2c5fd; --pd-af5aff:#3681fa; --pd-8846c6:#3b68b1; --pd-rgb:52,124,240; --pd-dark-rgb:22,33,52; --lime-rgb:54,129,250; }
.app-switch-btn[data-app="suite"]{ --app:#3b82f6; }
/* Side panel per app: Data shows its own menu, Create and Research theirs, and the Suite shows all three together. */
.sidebar[data-app="research"] .app-menu[data-app-menu="data"], .sidebar[data-app="create"] .app-menu[data-app-menu="data"]{ display:none; }
.sidebar[data-app="data"] .app-menu[data-app-menu="create"], .sidebar[data-app="data"] .app-menu[data-app-menu="research"],
.sidebar[data-app="research"] .app-menu[data-app-menu="create"], .sidebar[data-app="create"] .app-menu[data-app-menu="research"]{ display:none; }
.app-menu-empty{ margin:2px 6px 0; padding:10px 12px; border:1px dashed var(--border); border-radius:10px; color:var(--text-dim); font-size:12px; line-height:1.45; }
/* Launch screen while an app loads. */
.app-loading{ position:fixed; inset:0; z-index:9650; display:grid; place-items:center; background:radial-gradient(ellipse at 50% 42%, color-mix(in srgb, var(--app) 16%, var(--bg,#0a0a0a)), var(--bg,#0a0a0a) 70%); opacity:0; visibility:hidden; transition:opacity .22s ease, visibility 0s linear .22s; }
.app-loading.is-on{ opacity:1; visibility:visible; transition:opacity .15s ease; }
.app-loading-card{ display:flex; flex-direction:column; align-items:center; gap:14px; text-align:center; color:var(--text); }
.app-loading-card img{ display:block; filter:drop-shadow(0 0 18px color-mix(in srgb, var(--app) 55%, transparent)); }
.app-loading-name{ display:flex; flex-direction:column; gap:2px; line-height:1.05; }
.app-loading-name small{ color:var(--text-muted); font-size:11px; font-weight:600; letter-spacing:.2em; text-transform:uppercase; }
.app-loading-name b{ color:var(--app); font-family:var(--font-display,inherit); font-size:30px; font-weight:800; letter-spacing:-.02em; }
.app-loading-bar{ position:relative; width:180px; height:3px; border-radius:99px; background:color-mix(in srgb, var(--app) 18%, transparent); overflow:hidden; }
.app-loading-bar i{ position:absolute; top:0; bottom:0; left:0; width:42%; border-radius:inherit; background:var(--app); animation:appLoadSlide 1s ease-in-out infinite; }
@keyframes appLoadSlide{ 0%{ transform:translateX(-107%); } 100%{ transform:translateX(250%); } }
.app-loading-card span{ color:var(--text-dim); font-size:12px; }
@media (prefers-reduced-motion:reduce){ .app-loading-bar i{ animation-duration:2.4s; } }
.app-switcher[hidden]{ display:none !important; }
.topbar-right > .ask-osp{ margin-left:5px; }
/* The sidebar's collapse icon is the same side-panel icon as Orb AI's, drawn on its own (no box or hover square around it). It no longer flips when collapsed: the panel icon looks the same either way. */
.sidebar-collapse-btn svg, .sidebar.collapsed .sidebar-collapse-btn svg, .sidebar.collapsed.peek .sidebar-collapse-btn svg{ transform:none !important; }
.sidebar-collapse-btn:hover{ background:none; }

/* Publishers Research colour concepts: the palette button on the left of the header (shown only in Research) and its menu. */
.tone-wrap{ display:none; position:relative; margin-left:12px; }
body[data-app="research"] .tone-wrap{ display:inline-block; }
.tone-btn{ position:relative; display:grid; place-items:center; width:28px; height:28px; padding:0; border:1px solid var(--border); border-radius:50%; background:var(--surface-2); color:var(--text-muted); cursor:pointer; transition:border-color .15s ease, color .15s ease; }
.tone-btn:hover, .tone-wrap.open .tone-btn{ border-color:var(--tone,var(--lime)); color:var(--text); }
.tone-btn svg{ width:15px; height:15px; fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
.tone-dot{ position:absolute; right:-1px; bottom:-1px; width:9px; height:9px; border-radius:50%; background:var(--tone,var(--lime)); border:2px solid var(--surface,#141414); box-sizing:content-box; }
.tone-menu{ display:none; position:absolute; top:calc(100% + 8px); left:0; z-index:80; width:236px; padding:12px; border:1px solid var(--border); border-radius:14px; background:var(--surface); box-shadow:0 14px 40px rgba(0,0,0,.45); }
.tone-wrap.open .tone-menu{ display:block; }
.tone-title{ margin:0 2px 10px; color:var(--text-dim); font-size:11px; font-weight:600; letter-spacing:.05em; text-transform:uppercase; }
.tone-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:6px; }
.tone-opt{ display:flex; align-items:center; gap:9px; padding:7px 9px; border:1px solid transparent; border-radius:10px; background:none; color:var(--text); font:inherit; font-size:12.5px; cursor:pointer; text-align:left; }
.tone-opt:hover{ background:var(--surface-2); }
.tone-opt.is-on{ border-color:var(--lime); background:var(--surface-2); }
.tone-opt i{ flex:none; width:18px; height:18px; border-radius:50%; box-shadow:inset 0 0 0 1px rgba(255,255,255,.25); }
/* The colour menu hangs below the header, so the breadcrumb row must not clip it (the page name still shortens itself with an ellipsis). */
body[data-app="research"] .topbar-left{ overflow:visible; }

/* Side chat: the line under "Orb AI" is a small drop-down that lists the past chats. */
.ai-drawer-sub.orb-sub-pick{ display:inline-flex; align-items:center; gap:4px; width:max-content; max-width:100%; cursor:pointer; border-radius:6px; padding:1px 4px 1px 0; }
.ai-drawer-sub.orb-sub-pick:hover, .ai-drawer-sub.orb-sub-pick.is-open{ color:var(--text); }
.orb-sub-text{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:200px; }
.orb-sub-arrow{ width:12px; height:12px; flex:none; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; transition:transform .15s ease; }
.ai-drawer-sub.is-open .orb-sub-arrow{ transform:rotate(180deg); }
.orb-sub-pop{ position:fixed; z-index:9802; width:280px; max-height:min(60vh,420px); overflow-y:auto; padding:6px; border:1px solid var(--border); border-radius:14px; background:var(--surface); color:var(--text); }
.orb-sub-pop button{ display:block; width:100%; padding:9px 10px; border:0; border-radius:9px; background:none; color:var(--text); font:inherit; font-size:13px; text-align:left; cursor:pointer; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.orb-sub-pop button:hover, .orb-sub-pop button.is-active{ background:var(--surface-2); }
.orb-sub-pop .orb-sub-new{ color:var(--lime-text,#5150f6); font-weight:600; }
.orb-sub-group{ padding:10px 10px 4px; color:var(--text-dim); font-family:var(--font-mono,'Fira Code',monospace); font-size:10px; letter-spacing:.09em; text-transform:uppercase; }
.orb-sub-empty{ padding:10px; color:var(--text-dim); font-size:12.5px; }

/* While the theme changes (script.js applyTheme adds this class for a moment) nothing fades: every colour switches together. */
body.theme-switching *, body.theme-switching *::before, body.theme-switching *::after { transition:none !important; }

/* profile menu: the "New look" on/off row */


/* Dot AI has no dropdown any more, so there is no chevron to swap the icon for on hover: the icon stays. */
.sidebar .nav-platform-group .nav-platform-toggle.no-sub:hover .nav-icon{ opacity:1; }
.sidebar .nav-platform-group .nav-platform-toggle.no-sub .nav-chev{ display:none; }

/* The old big side-chat window is retired: Expand on the side chat opens the Dot AI page instead. */
.ai-expand-overlay{ display:none !important; }

/* Locked menu item ("Soon"): Community. Dimmed, no hover or click, and a thin purple "Soon" with a dot before it beside the name. */
.nav-item.is-locked, .nav-item[data-soon]{ cursor:not-allowed; }
/* The icon and name of a "Soon" item stay dimmed, also on hover; only the Soon tag is solid. */
.nav-item.is-locked :is(.nav-icon, .nav-label-text), .nav-item[data-soon] :is(.nav-icon, .nav-label-text),
.nav-item.is-locked:hover :is(.nav-icon, .nav-label-text), .nav-item[data-soon]:hover :is(.nav-icon, .nav-label-text){ opacity:.5 !important; color:var(--text-dim) !important; }
.nav-item[data-soon]:hover{ background:transparent; }
.nav-item.is-locked:hover{ background:transparent; color:inherit; }
.nav-soon{ display:inline-flex; align-items:center; gap:4px; flex:0 0 auto; margin-left:auto; opacity:1; color:var(--pd-a855f7); font-size:11px; font-weight:300; letter-spacing:.02em; white-space:nowrap; }
.nav-soon i{ width:3px; height:3px; flex:0 0 3px; border-radius:50%; background:currentColor; }
body.theme-light .nav-soon{ color:var(--pd-6d28d9); }
.sidebar.collapsed:not(.peek) .nav-soon{ display:none; }

/* While the chart creator ("Add a chart") is open, the page behind it must not scroll; scrolling inside it stays inside it. */
html:has(.chartb-overlay.open), body:has(.chartb-overlay.open){ overflow:hidden; }
.chartb-overlay, .chartb-sidebar, .chartb-canvas, .chartb-canvas-body{ overscroll-behavior:contain; }

/* Community is locked ("Soon" in the menu): its pages can never be shown, whatever tries to open them. */
#panel-community, #panel-community-react{ display:none !important; }

/* "Coming soon" popup (Community in the menu), Hyros style: a header band with the brand zebra print behind a lavender icon, centred text, a zebra-striped list of what is coming, one button. */
.soon-popup-overlay{ position:fixed; inset:0; z-index:2300; display:flex; align-items:center; justify-content:center; padding:20px; background:rgba(10,10,14,.5); }
body.theme-light .soon-popup-overlay{ background:rgba(31,30,29,.35); }
.soon-popup{ --sp-hair:color-mix(in srgb, var(--text) 13%, transparent); --sp-lavbg:color-mix(in srgb, #5150F6 22%, var(--surface)); --sp-lav:#a5a4fa; --sp-stripe:color-mix(in srgb, var(--text) 5%, transparent);
  width:380px; max-width:100%; box-sizing:border-box; overflow:hidden; border:1px solid var(--sp-hair); border-radius:12px; background:var(--surface); color:var(--text); box-shadow:0 16px 44px rgba(0,0,0,.35); font-family:'Inter',system-ui,sans-serif; }
body.theme-light .soon-popup{ --sp-hair:rgba(31,30,29,.11); --sp-lavbg:#EEEEFB; --sp-lav:#5150F6; --sp-stripe:rgba(31,30,29,.04); background:#fff; color:#1F1E1D; box-shadow:0 16px 44px rgba(31,30,29,.18); }
.soon-popup-top{ position:relative; display:grid; height:104px; place-items:center; border-bottom:1px solid var(--sp-hair); background:color-mix(in srgb, #5150F6 12%, var(--surface)); overflow:hidden; }
body.theme-light .soon-popup-top{ background:#F4F4FD; }
.soon-popup-top::before{ content:""; position:absolute; inset:0; background:url('assets/zebra-white.svg') center / cover no-repeat; opacity:.07; }
body.theme-light .soon-popup-top::before{ background-image:url('assets/zebra-purple.svg'); opacity:.16; }
.soon-popup-icon{ position:relative; display:grid; width:48px; height:48px; place-items:center; border:1px solid var(--sp-hair); border-radius:12px; background:var(--sp-lavbg); color:var(--sp-lav); }
body.theme-light .soon-popup-icon{ background:#fff; }
.soon-popup-icon svg{ width:24px; height:24px; fill:none; stroke:currentColor; stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round; }
.soon-popup-body{ display:flex; flex-direction:column; align-items:center; gap:6px; padding:18px 22px 20px; text-align:center; }
.soon-popup-tag{ display:inline-flex; align-items:center; gap:4px; color:var(--pd-a855f7); font-size:11px; font-weight:300; letter-spacing:.02em; }
body.theme-light .soon-popup-tag{ color:var(--pd-6d28d9); }
.soon-popup-tag i{ width:3px; height:3px; border-radius:50%; background:currentColor; }
.soon-popup strong{ font-size:17px; font-weight:500; letter-spacing:-.01em; }
.soon-popup p{ max-width:300px; margin:0 0 6px; color:var(--text-muted); font-size:12.5px; line-height:1.5; }
body.theme-light .soon-popup p{ color:#55534E; }
.soon-popup-list{ width:100%; margin:6px 0 14px; padding:0; overflow:hidden; border:1px solid var(--sp-hair); border-radius:10px; list-style:none; text-align:left; }
.soon-popup-list li{ display:flex; align-items:center; gap:10px; padding:9px 12px; font-size:12.5px; }
.soon-popup-list li:nth-child(odd){ background:var(--sp-stripe); }
.soon-popup-list li::before{ content:""; width:5px; height:5px; flex:0 0 5px; border-radius:50%; background:var(--sp-lav); }
.soon-popup-ok{ width:100%; height:34px; border:0; border-radius:8px; background:color-mix(in srgb, var(--text) 12%, var(--surface)); color:var(--text); font:inherit; font-size:12.5px; font-weight:500; cursor:pointer; }
body.theme-light .soon-popup-ok{ background:#1F1E1D; color:#fff; }
.soon-popup-ok:hover{ opacity:.85; }
.soon-popup-ok:focus-visible{ outline:2px solid var(--pd-a855f7); outline-offset:2px; }

  /* ---- Apps: Mac-style window shells ---- */
  .apps-test-panel{ padding:0; margin:0; max-width:none; }
  .apps-page{ padding:28px 32px; }
  .apps-page[hidden]{ display:none; }
  .apps-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); gap:18px; }
  .mac-window{ background:var(--surface); border:1px solid var(--border); border-radius:12px; overflow:hidden; box-shadow:0 1px 2px rgba(0,0,0,.08), 0 8px 24px rgba(0,0,0,.10); }
  .mac-bar{ display:flex; align-items:center; gap:12px; height:38px; padding:0 14px; border-bottom:1px solid var(--border); background:var(--bg); }
  .mac-dots{ display:inline-flex; gap:7px; flex-shrink:0; }
  .mac-dots i{ width:10px; height:10px; border-radius:50%; background:var(--text-dim); display:block; }
  .mac-title{ flex:1; text-align:center; font-size:12.5px; color:var(--text-muted); margin-right:60px; }
  .mac-back{ margin-left:auto; background:none; border:1px solid var(--border); color:var(--text-muted); font:inherit; font-size:12px; padding:3px 10px; border-radius:6px; cursor:pointer; }
  .mac-back:hover{ color:var(--text); border-color:var(--text-dim); }
  .mac-card{ display:block; width:100%; text-align:left; padding:0; font:inherit; color:inherit; cursor:pointer; transition:transform .15s ease, border-color .15s ease; }
  .mac-card:hover{ transform:translateY(-2px); border-color:var(--text-dim); }
  .mac-card .mac-bar{ display:flex; }
  .mac-body{ display:block; padding:18px 16px 20px; }
  .mac-card-name{ font-family:var(--font-display); font-size:16px; font-weight:600; margin-right:8px; }
  .mac-card-tag{ font-size:10.5px; text-transform:uppercase; letter-spacing:.06em; color:var(--text-muted); border:1px solid var(--border); border-radius:999px; padding:1px 7px; }
  .mac-card-desc{ display:block; margin-top:8px; font-size:13px; line-height:1.45; color:var(--text-muted); }
  /* An open app: the page itself does not scroll. The window fills the space under the top bar and only the app inside it scrolls. */
  body.apps-view-open, html:has(body.apps-view-open){ overflow:hidden; }
  body.apps-view-open .content{ padding:0; }
  .apps-view{ padding:20px 32px 24px; height:calc(100vh - 56px); box-sizing:border-box; }
  .mac-window-large{ height:100%; display:flex; flex-direction:column; }
  .mac-window-large .apps-frame{ display:block; flex:1 1 auto; min-height:0; width:100%; border:0; background:transparent; }


/* ---- Apps page: the brand zebra print behind the title band (same files and strength as the Coming-soon popup) ---- */
.apps-hero{ position:relative; overflow:hidden; border:1px solid var(--border); border-radius:12px; padding:28px 28px 8px; margin-bottom:24px; background:color-mix(in srgb, #5150F6 12%, var(--surface)); }
body.theme-light .apps-hero{ background:#F4F4FD; }
.apps-hero::before{ content:""; position:absolute; inset:0; background:url('assets/zebra-white.svg') center / cover no-repeat; opacity:.07; pointer-events:none; }
body.theme-light .apps-hero::before{ background-image:url('assets/zebra-purple.svg'); opacity:.16; }
.apps-hero > *{ position:relative; }

/* ---- Vibe Apps: the official Hyros zebra print (go.hyros.com/assets/brand/zebra). Light = "Lavender": #5150F6 stripes at .09 on #EEEEFB. Dark = "Dark": white stripes at .07 on #161514. Strength is baked into the files. ---- */
#appsList .apps-hero{ background:#161514; }
body.theme-light #appsList .apps-hero{ background:#EEEEFB; }
#appsList .apps-hero::before{ background-image:url('assets/hyros-zebra-white.svg'); opacity:1; }
body.theme-light #appsList .apps-hero::before{ background-image:url('assets/hyros-zebra-brand.svg'); opacity:1; }

/* ---- Apps: loading screen over the app window (decrypting text) ---- */
.mac-window-large{ position:relative; }
.apps-loader{ position:absolute; left:0; right:0; bottom:0; top:38px; z-index:2; display:grid; place-items:center; background:var(--surface); }
.apps-loader[hidden]{ display:none; }
.apps-loader-inner{ text-align:center; padding:0 20px; }
.apps-cipher{ font-family:var(--font-mono); font-size:20px; letter-spacing:.14em; color:var(--lime-text); min-height:28px; white-space:nowrap; overflow:hidden; text-overflow:clip; max-width:88vw; }
.apps-loader-label{ margin-top:14px; font-size:12.5px; color:var(--text-muted); }
.apps-loader .mac-back{ margin-top:14px; }

/* ---- Apps: green expand dot in the app window header ---- */
.mac-window-large .mac-dots{ gap:8px; align-items:center; }
.mac-window-large .mac-dots i{ width:12px; height:12px; }
.mac-expand{ position:relative; width:12px; height:12px; padding:0; border:0; border-radius:50%; background:#28c840; cursor:pointer; display:grid; place-items:center; color:#0a4d14; transition:transform .18s cubic-bezier(.3,1.5,.5,1), box-shadow .18s ease; animation:mac-pulse 1.6s ease-out 2 .6s; }
.mac-expand svg{ position:absolute; width:8px; height:8px; fill:none; stroke:currentColor; stroke-width:1.3; stroke-linecap:round; stroke-linejoin:round; opacity:0; transform:scale(.4) rotate(-45deg); transition:opacity .16s ease, transform .22s cubic-bezier(.3,1.5,.5,1); }
.mac-expand .ico-collapse{ display:none; }
.mac-window-large .mac-bar:hover .mac-expand svg.ico-expand, .mac-expand:focus-visible svg.ico-expand{ opacity:1; transform:scale(1) rotate(0); }
.mac-expand:hover{ transform:scale(1.25); box-shadow:0 0 0 4px rgba(40,200,64,.22); }
.mac-expand:active{ transform:scale(.92); }
.mac-expand:focus-visible{ outline:2px solid #28c840; outline-offset:3px; }
.mac-window-large.is-expanded .mac-expand .ico-expand{ display:none; }
.mac-window-large.is-expanded .mac-expand .ico-collapse{ display:block; }
.mac-window-large.is-expanded .mac-bar:hover .mac-expand svg.ico-collapse, .mac-window-large.is-expanded .mac-expand:focus-visible svg.ico-collapse{ opacity:1; transform:scale(1) rotate(0); }
@keyframes mac-pulse{ 0%{ box-shadow:0 0 0 0 rgba(40,200,64,.55); } 100%{ box-shadow:0 0 0 9px rgba(40,200,64,0); } }
/* Growing to full screen and back */
.mac-window-large.is-fixed{ position:fixed; z-index:400; margin:0; transition:left .32s cubic-bezier(.2,.8,.2,1), top .32s cubic-bezier(.2,.8,.2,1), width .32s cubic-bezier(.2,.8,.2,1), height .32s cubic-bezier(.2,.8,.2,1), border-radius .32s ease, box-shadow .32s ease; }
.mac-window-large.is-expanded{ border-radius:0; box-shadow:none; }
@media (prefers-reduced-motion: reduce){
  .mac-window-large.is-fixed{ transition:none; }
  .mac-expand{ animation:none; }
}

/* ---- Apps: red (delete) and yellow (minimize) dots, floating chip, delete warning, toast ---- */
.mac-dot-btn{ position:relative; width:12px; height:12px; padding:0; border:0; border-radius:50%; cursor:pointer; display:grid; place-items:center; transition:transform .18s cubic-bezier(.3,1.5,.5,1), box-shadow .18s ease; }
.mac-dot-close{ background:#ff5f57; color:#5a0b07; }
.mac-dot-min{ background:#febc2e; color:#5a3b00; }
.mac-dot-btn svg{ position:absolute; width:8px; height:8px; fill:none; stroke:currentColor; stroke-width:1.4; stroke-linecap:round; opacity:0; transform:scale(.4); transition:opacity .16s ease, transform .22s cubic-bezier(.3,1.5,.5,1); }
.mac-window-large .mac-bar:hover .mac-dot-btn svg, .mac-dot-btn:focus-visible svg{ opacity:1; transform:scale(1); }
.mac-dot-close:hover{ transform:scale(1.25); box-shadow:0 0 0 4px rgba(255,95,87,.22); }
.mac-dot-min:hover{ transform:scale(1.25); box-shadow:0 0 0 4px rgba(254,188,46,.25); }
.mac-dot-btn:active{ transform:scale(.92); }
.mac-dot-btn:focus-visible{ outline:2px solid currentColor; outline-offset:3px; }
.mac-window-large.is-minimizing{ transform-origin:100% 100%; transform:translate(8%, 18%) scale(.2); opacity:0; transition:transform .24s cubic-bezier(.5,0,.8,.4), opacity .24s ease; }

.apps-pill{ position:fixed; right:20px; bottom:20px; z-index:300; display:flex; align-items:center; gap:10px; padding:9px 14px 9px 12px; background:var(--surface); color:var(--text); border:1px solid var(--border); border-radius:12px; box-shadow:0 8px 28px rgba(0,0,0,.28); font:inherit; font-size:13px; cursor:pointer; opacity:0; transform:translateY(16px) scale(.85); transition:opacity .22s ease, transform .3s cubic-bezier(.3,1.4,.5,1), border-color .15s ease; }
.apps-pill[hidden]{ display:none; }
.apps-pill.is-in{ opacity:1; transform:none; animation:apps-float 3.2s ease-in-out .4s infinite; }
.apps-pill:hover{ border-color:var(--text-dim); animation:none; transform:translateY(-2px) scale(1.03); }
.apps-pill-dots{ display:inline-flex; gap:5px; }
.apps-pill-dots i{ width:8px; height:8px; border-radius:50%; display:block; }
.apps-pill-dots i:nth-child(1){ background:#ff5f57; } .apps-pill-dots i:nth-child(2){ background:#febc2e; } .apps-pill-dots i:nth-child(3){ background:#28c840; }
.apps-pill-name{ font-weight:600; }
.apps-pill-open{ font-size:11px; color:var(--text-muted); border:1px solid var(--border); border-radius:999px; padding:1px 8px; }
@keyframes apps-float{ 0%,100%{ transform:translateY(0); } 50%{ transform:translateY(-4px); } }

.apps-modal{ position:fixed; inset:0; z-index:500; display:grid; place-items:center; background:rgba(0,0,0,.5); opacity:0; transition:opacity .18s ease; }
.apps-modal[hidden]{ display:none; }
.apps-modal.is-in{ opacity:1; }
.apps-modal-card{ width:min(420px, 92vw); background:var(--surface); border:1px solid var(--border); border-radius:14px; padding:24px; text-align:center; box-shadow:0 20px 60px rgba(0,0,0,.4); transform:translateY(10px) scale(.96); transition:transform .24s cubic-bezier(.3,1.4,.5,1); }
.apps-modal.is-in .apps-modal-card{ transform:none; }
.apps-modal-icon{ width:44px; height:44px; margin:0 auto 12px; border-radius:50%; display:grid; place-items:center; background:rgba(255,95,87,.14); color:#ff5f57; }
.apps-modal-icon svg{ width:22px; height:22px; fill:none; stroke:currentColor; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
.apps-modal-title{ font-family:var(--font-display); font-size:18px; font-weight:600; }
.apps-modal-text{ margin-top:8px; font-size:13.5px; line-height:1.5; color:var(--text-muted); }
.apps-modal-actions{ display:flex; gap:10px; justify-content:center; margin-top:20px; }
.apps-modal-actions button{ font:inherit; font-size:13px; padding:8px 16px; border-radius:8px; cursor:pointer; border:1px solid var(--border); background:none; color:var(--text); }
.apps-modal-actions button:hover{ border-color:var(--text-dim); }
.apps-modal-actions .apps-modal-delete{ background:#e5484d; border-color:#e5484d; color:#fff; }
.apps-modal-actions .apps-modal-delete:hover{ background:#d13a3f; }

.apps-toast{ position:fixed; left:50%; bottom:28px; z-index:510; transform:translate(-50%, 12px); background:var(--text); color:var(--bg); font-size:13px; padding:10px 16px; border-radius:10px; max-width:92vw; opacity:0; transition:opacity .2s ease, transform .24s ease; }
.apps-toast[hidden]{ display:none; }
.apps-toast.is-in{ opacity:1; transform:translate(-50%, 0); }
@media (prefers-reduced-motion: reduce){
  .apps-pill.is-in{ animation:none; }
  .mac-window-large.is-minimizing, .apps-pill, .apps-modal, .apps-modal-card, .apps-toast{ transition:none; }
}

/* ---- Sidebar title in a coding font (Geist Mono, the brand's label font), with the cipher letters for its load animation ---- */
.brand-name .brand-co, .brand-name .brand-app{ font-family:'Geist Mono','IBM Plex Mono',ui-monospace,monospace; font-weight:500; font-size:10.5px; letter-spacing:.1em; font-variant-ligatures:none; }

/* ---- Vibe Apps: the zebra print sits at X 0, Y 197 (px from centred), Zoom 100 (fills the band's width), as picked. ---- */
#appsList .apps-hero::before{ background-size:100% auto; background-position:50% calc(50% + 197px); }
.apps-hero > .apps-connect{ margin:14px 0 18px; }

/* ---- Sidebar icons: each plays its own short animation ONCE when you start hovering (it does not loop; it plays again the next time you hover). Dashboards squares pop, AI sparkles twinkle, Vibe Apps dots blink, book flips, megaphone shakes, storefront drops, chart bars grow, people hop, plug plugs in, briefcase tilts. Manage Account and Help are left out: their icon turns into an arrow on hover. ---- */
.sidebar .nav-item[data-overview="dashboard-react"]:hover .nav-icon svg rect{ transform-box:fill-box; transform-origin:center; animation:navi-pop .5s cubic-bezier(.3,1.5,.5,1) both; }
.sidebar .nav-item[data-overview="dashboard-react"]:hover .nav-icon svg rect:nth-child(2){ animation-delay:0.07s; }
.sidebar .nav-item[data-overview="dashboard-react"]:hover .nav-icon svg rect:nth-child(3){ animation-delay:0.14s; }
.sidebar .nav-item[data-overview="dashboard-react"]:hover .nav-icon svg rect:nth-child(4){ animation-delay:0.21s; }
.sidebar #navHome:hover .nav-icon svg{ animation:navi-twinkle .65s ease-in-out 1; }
.sidebar .nav-item[data-overview="publishing-overview-react"]:hover .nav-icon svg{ animation:navi-flip .7s ease-in-out 1; }
.sidebar .nav-item[data-overview="advertising-overview-react"]:hover .nav-icon svg{ transform-origin:30% 70%; animation:navi-shake .6s ease-in-out 1; }
.sidebar .nav-item[data-soon="Direct sales platforms"]:hover .nav-icon svg{ animation:navi-drop .5s ease-out 1; }
.sidebar #navBookAnalytics:hover .nav-icon svg path:nth-child(n+2){ transform-box:fill-box; transform-origin:bottom; animation:navi-grow .45s cubic-bezier(.3,1.4,.5,1) both; }
.sidebar #navBookAnalytics:hover .nav-icon svg path:nth-child(3){ animation-delay:0.08s; }
.sidebar #navBookAnalytics:hover .nav-icon svg path:nth-child(4){ animation-delay:0.16s; }
.sidebar .nav-item.is-locked:not([data-soon]):hover .nav-icon svg{ animation:navi-hop .55s ease-out 1; }
.sidebar .nav-item[data-panel="integrations-react"]:hover .nav-icon svg{ animation:navi-plug .5s ease-out 1; }
.sidebar #navBookShelf:hover .nav-icon svg{ animation:navi-case .55s ease-in-out 1; }

@keyframes navi-pop{ 0%{ transform:scale(.3); opacity:.3; } 100%{ transform:scale(1); opacity:1; } }
@keyframes navi-twinkle{ 0%{ transform:scale(1) rotate(0); } 35%{ transform:scale(1.25) rotate(-18deg); } 70%{ transform:scale(.95) rotate(8deg); } 100%{ transform:scale(1) rotate(0); } }
@keyframes navi-flip{ 0%{ transform:perspective(80px) rotateY(0); } 100%{ transform:perspective(80px) rotateY(360deg); } }
@keyframes navi-shake{ 0%{ transform:rotate(0); } 20%{ transform:rotate(-14deg); } 40%{ transform:rotate(10deg); } 60%{ transform:rotate(-6deg); } 80%{ transform:rotate(3deg); } 100%{ transform:rotate(0); } }
@keyframes navi-drop{ 0%{ transform:translateY(-4px); } 50%{ transform:translateY(1px); } 75%{ transform:translateY(-1px); } 100%{ transform:translateY(0); } }
@keyframes navi-grow{ 0%{ transform:scaleY(0); } 100%{ transform:scaleY(1); } }
@keyframes navi-hop{ 0%{ transform:translateY(0); } 35%{ transform:translateY(-3px); } 65%{ transform:translateY(0); } 80%{ transform:translateY(-1px); } 100%{ transform:translateY(0); } }
@keyframes navi-plug{ 0%{ transform:translateY(-4px); } 60%{ transform:translateY(1px); } 80%{ transform:translateY(-.5px); } 100%{ transform:translateY(0); } }
@keyframes navi-case{ 0%{ transform:translateY(0) rotate(0); } 30%{ transform:translateY(-2px) rotate(-8deg); } 60%{ transform:translateY(0) rotate(6deg); } 100%{ transform:translateY(0) rotate(0); } }
@media (prefers-reduced-motion: reduce){ .sidebar .nav-icon svg, .sidebar .nav-icon svg *{ animation:none !important; } }

/* ---- Vibe Apps icon (the >_ command prompt): on hover, once, the > slides in and the _ cursor blinks twice, then stays visible. ---- */
.sidebar #navAppsTest:hover .nav-icon svg path:nth-child(1){ animation:navi-prompt-in .4s cubic-bezier(.3,1.3,.5,1) both; }
.sidebar #navAppsTest:hover .nav-icon svg path:nth-child(2){ animation:navi-prompt-cursor .8s steps(1, end) both; }
@keyframes navi-prompt-in{ 0%{ transform:translateX(-5px); opacity:0; } 100%{ transform:translateX(0); opacity:1; } }
@keyframes navi-prompt-cursor{ 0%{ opacity:0; } 15%{ opacity:1; } 35%{ opacity:0; } 55%{ opacity:1; } 75%{ opacity:0; } 90%,100%{ opacity:1; } }

/* ---- Temporary icon picker: a small bar at the bottom of the screen. Left stepper = which sidebar item, right stepper = which icon (1 is always the current one). ---- */
.icon-pick{ position:fixed; left:50%; bottom:18px; transform:translateX(-50%); z-index:5000; display:flex; align-items:center; flex-wrap:wrap; justify-content:center; gap:8px 10px; max-width:calc(100vw - 24px); padding:7px 12px; border:1px solid var(--border); border-radius:12px; background:var(--surface); box-shadow:0 6px 24px rgba(0,0,0,.18); font-size:12px; color:var(--text-muted); }
.icon-pick b{ color:var(--text); font-weight:600; }
.icon-pick .ip-grp{ display:inline-flex; align-items:center; gap:6px; }
.icon-pick .ip-v{ min-width:150px; text-align:center; color:var(--text); }
.icon-pick .ip-g .ip-v{ min-width:200px; color:var(--text-muted); }
.icon-pick button{ width:26px; height:26px; border:1px solid var(--border); border-radius:7px; background:none; color:var(--text-muted); font:inherit; font-size:16px; line-height:1; cursor:pointer; }
.icon-pick button:hover{ color:var(--text); }
/* a swapped-in icon has none of the original per-icon animation parts, so every swapped icon gets one simple twinkle on hover instead (once) */
.sidebar .nav-item.has-alt-icon:hover .nav-icon svg{ animation:navi-twinkle .65s ease-in-out 1 !important; }
.sidebar .nav-item.has-alt-icon:hover .nav-icon svg :is(path, rect, circle, line, polyline, ellipse, polygon){ animation:none !important; }

/* ---- Sidebar icon hover animations: the icon itself animates (one per sidebar row, chosen with data-ico / data-anim in the HTML) ---- */
.sidebar .nav-item.has-alt-icon[data-anim] .nav-icon svg > *{ transform-box:fill-box; transform-origin:50% 50%; }
.sidebar .nav-item.has-alt-icon[data-anim]:is(:hover,.anim-play) .nav-icon svg{ animation:none !important; }
@keyframes na-ai-1-0{0%,100%{transform:scaleY(1)} 20%,60%{transform:scaleY(.1)} 40%,80%{transform:scaleY(1)}}
.sidebar .nav-item.has-alt-icon[data-ico="ai"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(5){ animation:na-ai-1-0 .8s ease-in-out 0s 1 both !important; transform-origin:50% 50%; }
.sidebar .nav-item.has-alt-icon[data-ico="ai"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(6){ animation:na-ai-1-0 .8s ease-in-out 0s 1 both !important; transform-origin:50% 50%; }
@keyframes na-pub-4-0{0%,100%{transform:rotate(0)} 40%{transform:rotate(14deg)}}
.sidebar .nav-item.has-alt-icon[data-ico="pub"][data-anim="4"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(4){ animation:na-pub-4-0 .5s ease-in-out 0s 1 both !important; transform-origin:50% 100%; }
@keyframes na-pub-4-1{0%,100%{transform:rotate(0)} 40%{transform:rotate(14deg)}}
.sidebar .nav-item.has-alt-icon[data-ico="pub"][data-anim="4"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(3){ animation:na-pub-4-1 .5s ease-in-out 0.1s 1 both !important; transform-origin:50% 100%; }
@keyframes na-pub-4-2{0%,100%{transform:rotate(0)} 40%{transform:rotate(14deg)}}
.sidebar .nav-item.has-alt-icon[data-ico="pub"][data-anim="4"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(2){ animation:na-pub-4-2 .5s ease-in-out 0.2s 1 both !important; transform-origin:50% 100%; }
@keyframes na-pub-4-3{0%,100%{transform:rotate(0)} 40%{transform:rotate(14deg)}}
.sidebar .nav-item.has-alt-icon[data-ico="pub"][data-anim="4"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(1){ animation:na-pub-4-3 .5s ease-in-out 0.3s 1 both !important; transform-origin:50% 100%; }
@keyframes na-sales-4-0{0%,100%{transform:rotate(0)} 40%,60%{transform:rotate(-12deg)}}
.sidebar .nav-item.has-alt-icon[data-ico="sales"][data-anim="4"]:is(:hover,.anim-play) .nav-icon svg{ animation:na-sales-4-0 .8s ease-in-out 0s 1 both !important; transform-origin:20% 90%; }
@keyframes na-comm-4-0{0%,100%{transform:scale(1)} 20%{transform:scale(1.15)} 40%{transform:scale(1)} 60%{transform:scale(1.15)}}
.sidebar .nav-item.has-alt-icon[data-ico="comm"][data-anim="4"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(1){ animation:na-comm-4-0 .8s ease-in-out 0s 1 both !important; transform-origin:50% 50%; }
@keyframes na-help-1-0{0%{transform:rotate(0)} 100%{transform:rotate(360deg)}}
.sidebar .nav-item.has-alt-icon[data-ico="help"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg{ animation:na-help-1-0 .8s cubic-bezier(.5,0,.2,1) 0s 1 both !important; }
@media (prefers-reduced-motion: reduce){ .sidebar .nav-item[data-anim]:is(:hover,.anim-play) .nav-icon svg, .sidebar .nav-item[data-anim]:is(:hover,.anim-play) .nav-icon svg > *{ animation:none !important; } }
.icon-pick.anim-pick{ bottom:66px; }

/* ---- Sidebar icon hover animations for the rows that keep their current icon ---- */
.sidebar .nav-item[data-ico] .nav-icon svg > *{ transform-box:fill-box; }
.sidebar .nav-item:is([data-ico="dash"],[data-ico="apps"],[data-ico="adv"],[data-ico="books"],[data-ico="integ"],[data-ico="biz"],[data-ico="acct"]):is(:hover,.anim-play) .nav-icon svg{ animation:none !important; }
@keyframes nb-dash-1-0{0%,100%{transform:none} 45%{transform:scale(0.65)}}
.sidebar .nav-item[data-ico="dash"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(1){ animation:nb-dash-1-0 0.45s ease-in-out 0.0s 1 both !important; transform-origin:50% 50%; }
@keyframes nb-dash-1-1{0%,100%{transform:none} 45%{transform:scale(0.65)}}
.sidebar .nav-item[data-ico="dash"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(2){ animation:nb-dash-1-1 0.45s ease-in-out 0.1s 1 both !important; transform-origin:50% 50%; }
@keyframes nb-dash-1-2{0%,100%{transform:none} 45%{transform:scale(0.65)}}
.sidebar .nav-item[data-ico="dash"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(3){ animation:nb-dash-1-2 0.45s ease-in-out 0.2s 1 both !important; transform-origin:50% 50%; }
@keyframes nb-dash-1-3{0%,100%{transform:none} 45%{transform:scale(0.65)}}
.sidebar .nav-item[data-ico="dash"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(4){ animation:nb-dash-1-3 0.45s ease-in-out 0.3s 1 both !important; transform-origin:50% 50%; }
@keyframes nb-apps-1-0{0%,100%{opacity:1} 10%{opacity:.1} 36%{opacity:.1} 63%{opacity:.1} 23%{opacity:1} 50%{opacity:1} 76%{opacity:1}}
.sidebar .nav-item[data-ico="apps"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(2){ animation:nb-apps-1-0 0.9s ease-in-out 0s 1 both !important; transform-origin:50% 50%; }
@keyframes nb-adv-2-0{0%,100%{transform:rotate(0deg)} 45%{transform:rotate(-14deg)}}
.sidebar .nav-item[data-ico="adv"][data-anim="2"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(1){ animation:nb-adv-2-0 0.6s ease-in-out 0s 1 both !important; transform-origin:20% 90%; }
@keyframes nb-adv-2-1{0%,100%{transform:rotate(0deg)} 45%{transform:rotate(-14deg)}}
.sidebar .nav-item[data-ico="adv"][data-anim="2"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(2){ animation:nb-adv-2-1 0.6s ease-in-out 0s 1 both !important; transform-origin:20% 0%; }
@keyframes nb-books-1-0{0%{transform:scaleY(0)} 100%{transform:scaleY(1)}}
.sidebar .nav-item[data-ico="books"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(4){ animation:nb-books-1-0 0.5s ease-in-out 0.0s 1 both !important; transform-origin:50% 100%; }
@keyframes nb-books-1-1{0%{transform:scaleY(0)} 100%{transform:scaleY(1)}}
.sidebar .nav-item[data-ico="books"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(3){ animation:nb-books-1-1 0.5s ease-in-out 0.12s 1 both !important; transform-origin:50% 100%; }
@keyframes nb-books-1-2{0%{transform:scaleY(0)} 100%{transform:scaleY(1)}}
.sidebar .nav-item[data-ico="books"][data-anim="1"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(2){ animation:nb-books-1-2 0.5s ease-in-out 0.24s 1 both !important; transform-origin:50% 100%; }
@keyframes nb-integ-5-0{0%,100%{opacity:1} 10%{opacity:.1} 36%{opacity:.1} 63%{opacity:.1} 23%{opacity:1} 50%{opacity:1} 76%{opacity:1}}
.sidebar .nav-item[data-ico="integ"][data-anim="5"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(2){ animation:nb-integ-5-0 0.8s ease-in-out 0s 1 both !important; transform-origin:50% 50%; }
@keyframes nb-integ-5-1{0%,100%{opacity:1} 10%{opacity:.1} 36%{opacity:.1} 63%{opacity:.1} 23%{opacity:1} 50%{opacity:1} 76%{opacity:1}}
.sidebar .nav-item[data-ico="integ"][data-anim="5"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(3){ animation:nb-integ-5-1 0.8s ease-in-out 0.12s 1 both !important; transform-origin:50% 50%; }
@keyframes nb-biz-9-0{0%,100%{transform:none} 25%{transform:translate(0px,-3px)} 50%{transform:none} 75%{transform:translate(0px,-2px)}}
.sidebar .nav-item[data-ico="biz"][data-anim="9"]:is(:hover,.anim-play) .nav-icon svg{ animation:nb-biz-9-0 0.8s ease-in-out 0s 1 both !important; transform-origin:50% 50%; }
@keyframes nb-acct-2-0{0%,100%{transform:none} 30%{transform:translate(-1.8px,0px)} 65%{transform:translate(1.8px,0px)}}
.sidebar .nav-item[data-ico="acct"][data-anim="2"]:is(:hover,.anim-play) .nav-icon svg > :nth-child(2){ animation:nb-acct-2-0 0.8s ease-in-out 0s 1 both !important; transform-origin:50% 50%; }

/* ---- Floating support button (bottom right): on hover the headset tilts and the green dot pings. ---- */
.pd-sup-fab .pd-sup-ms{ transform-box:fill-box; transform-origin:50% 100%; }
@keyframes sup-tilt{0%,100%{transform:rotate(0)} 30%{transform:rotate(-12deg)} 65%{transform:rotate(10deg)}}
@keyframes sup-ping{0%{transform:scale(1);opacity:1} 50%{transform:scale(1.9);opacity:.35} 100%{transform:scale(1);opacity:1}}
.pd-sup-fab:hover .pd-sup-ms{ animation:sup-tilt .7s ease-in-out 1 both; }
.pd-sup-fab:hover .pd-sup-active{ animation:sup-ping .8s ease-out .1s 1 both; }

/* the sidebar logo and name open the public website */
.sidebar .brand .brand-logo-slot, .sidebar .brand .brand-name{ cursor:pointer; }
