/* ============================================================================
   menu.css — MPMenu, the shared dropdown-menu primitive.
   ----------------------------------------------------------------------------
   SSOT: this file lives in dashboard/static/css/lib/ and is copied VERBATIM to
   home/static/css/lib/ by `python3 scripts/sync_shared_assets.py`. Never edit
   home's copy; dashboard/tests/core/test_shared_assets_sync.py fails on drift.
   Because it is shared it may reference ONLY tokens that exist in BOTH
   tokens.css (dashboard) and home.css's :root:
     --bg-elevated --border-default --text-primary --text-secondary --text-muted
     --red --accent --font-sans --tr-body --ease-out --shadow-pop
   No raw hex/rgba for any themed surface; everything derives from those.

   PORTED FROM: 21st.dev/@shadcn/components/dropdown-menu, i.e. shadcn/ui's
   dropdown-menu.tsx (React + Radix UI + Tailwind). This repo has no bundler,
   no framework and no Tailwind, so the Tailwind class strings were resolved to
   their computed values and rewritten as hand-authored CSS. The behaviour half
   (roving focus, typeahead, placement, ...) that Radix supplies lives in the
   sibling js/lib/menu.js.

   CARRIED OVER VERBATIM (shadcn class -> value -> rule below)
     Content   min-w-[8rem]        128px  -> --mpm-min-w
               p-1                 4px    -> --mpm-pad
               rounded-md/border/shadow-md
               max-h-[--available-height] + overflow-y-auto
               data-[state=open]:  animate-in fade-in-0 zoom-in-95
                                   slide-in-from-top-2  (8px, side=bottom)
                                   slide-in-from-bottom-2 (side=top)
               data-[state=closed]:animate-out fade-out-0 zoom-out-95
               origin-(--radix-dropdown-menu-content-transform-origin)
     Item      flex items-center gap-2 (8px) rounded-sm px-2 (8px) py-1.5 (6px)
               text-sm (14px) select-none
               focus:bg-accent focus:text-accent-foreground
               data-[disabled]:pointer-events-none data-[disabled]:opacity-50
               data-[variant=destructive]:text-destructive
                 + focus:bg-destructive/10 (dark /20) + destructive svg
               [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4 (16px)
     Label     px-2 py-1.5 (shadcn CommandGroup heading flavour: text-xs,
               font-medium, text-muted-foreground -- see ADAPTED below)
     Separator -mx-1 my-1 h-px bg-border
     Shortcut  ml-auto text-xs tracking-widest text-muted-foreground
     w-56      224px -> .mpm-menu--w56 (the width shadcn's own account-menu demo
               uses)

   ADAPTED, and why
     * Colours come from our token SSOT, not shadcn's. `bg-accent` (shadcn's
       neutral highlight wash, NOT a brand colour) maps to an 8% --text-primary
       mix so it tracks both themes; `bg-popover` -> --bg-elevated;
       `text-destructive` -> --red; `border` -> --border-default.
     * SIZE UNIT. Every length is `calc(<the Tailwind px / 16> * var(--mpm-u))`
       instead of a bare rem. --mpm-u falls back to 1rem (the dashboard, whose
       root font is already fluid); a host overrides it by setting --mpm-unit
       on any ancestor. home binds it to its own --ha-u so the menu scales with
       the rest of home's top-right cluster (home's root font clamp FLOORS at
       20px, so a bare rem would draw laptop menus at 27" size -- see the
       Gotchas entry in CLAUDE.md).
     * Group LABEL uses shadcn's Command/Sidebar group-heading flavour (text-xs,
       muted) rather than DropdownMenuLabel's full-strength text-sm, because our
       menus carry three groups and three full-strength headings read as noise.
       The identity block at the top is the full-strength label.
     * cursor:pointer, not shadcn's cursor-default: our items are real links and
       navigation targets, and a menu row that navigates should say so.
     * Icons lift from muted to secondary on highlight (shadcn pins them muted).
     * .mpm-user is shadcn's nav-user label block (avatar h-8 w-8 rounded-lg +
       a grid of a semibold name over a text-xs line), which the reference page
       does not ship but every shadcn account menu does.
   ============================================================================ */

/* ── Scale + derived metrics ──────────────────────────────────────────────
   ONE KNOB: --mpm-unit. A host that needs a different scale sets it on any
   ancestor and every length below follows (home does it on .home-account,
   binding it to that cluster's --ha-u). It is read through an inherited
   indirection rather than by overriding --mpm-u directly so a host never has
   to win a specificity or load-order fight with this file. */
.mpm-menu{
  --mpm-u:            var(--mpm-unit, 1rem);  /* the "16px" Tailwind assumes */
  --mpm-pad:          calc(0.25  * var(--mpm-u));  /* p-1                        */
  --mpm-gap:          calc(0.5   * var(--mpm-u));  /* gap-2                      */
  --mpm-item-px:      calc(0.5   * var(--mpm-u));  /* px-2                       */
  --mpm-item-py:      calc(0.375 * var(--mpm-u));  /* py-1.5                     */
  --mpm-text:         calc(0.875 * var(--mpm-u));  /* text-sm                    */
  --mpm-text-xs:      calc(0.75  * var(--mpm-u));  /* text-xs                    */
  --mpm-icon:         calc(1     * var(--mpm-u));  /* size-4                     */
  --mpm-min-w:        calc(8     * var(--mpm-u));  /* min-w-[8rem]               */
  --mpm-radius:       calc(0.625 * var(--mpm-u));  /* content radius             */
  --mpm-radius-item:  calc(0.5   * var(--mpm-u));  /* item radius                */
  --mpm-slide:        calc(0.5   * var(--mpm-u));  /* slide-in-from-*-2          */
  /* shadcn `bg-accent`: a neutral wash, not the brand accent. */
  --mpm-highlight:    color-mix(in srgb, var(--text-primary) 8%, transparent);
  --mpm-highlight-destructive: color-mix(in srgb, var(--red) 12%, transparent);
}

/* ── Content ──────────────────────────────────────────────────────────────
   position:fixed, placed by js/lib/menu.js from the trigger's box. NOT
   portalled to <body> (Radix does): staying in the DOM where it is keeps CSS
   custom properties inheriting from the host cluster, which is how home's
   --ha-u reaches --mpm-u. Both hosts' nearest positioned ancestors are
   transform-free, so fixed still resolves against the viewport. */
.mpm-menu{
  position:fixed;
  z-index:60;
  min-width:var(--mpm-min-w);
  max-width:calc(100vw - 2 * var(--mpm-u));
  /* JS writes the real cap (Radix's --radix-…-available-height) inline. */
  overflow-y:auto;
  overflow-x:hidden;
  overscroll-behavior:contain;
  padding:var(--mpm-pad);
  border:1px solid var(--border-default);
  border-radius:var(--mpm-radius);
  background:var(--bg-elevated);
  color:var(--text-primary);
  box-shadow:var(--shadow-pop);
  font-family:var(--font-sans);
  letter-spacing:var(--tr-body);
  outline:none;
  scrollbar-width:thin;
  scrollbar-color:var(--border-default) transparent;
}
.mpm-menu[hidden]{display:none;}
.mpm-menu::-webkit-scrollbar{width:calc(0.25 * var(--mpm-u));}
.mpm-menu::-webkit-scrollbar-thumb{background:var(--border-default);border-radius:999px;}

/* shadcn's account-menu demo width (w-56). */
.mpm-menu--w56{width:calc(14 * var(--mpm-u));}

/* ── Motion — the reference's animate-in / animate-out pair ───────────────
   open : fade-in-0 + zoom-in-95 + slide-in-from-<opposite side>-2
   close: fade-out-0 + zoom-out-95 (the reference does NOT slide on the way out)
   Transform origin is Radix's: JS writes --mpm-origin from the trigger anchor. */
@keyframes mpm-enter{
  from{opacity:0;transform:scale(0.95) translateY(var(--mpm-slide-from, 0));}
  to  {opacity:1;transform:scale(1)    translateY(0);}
}
@keyframes mpm-exit{
  from{opacity:1;transform:scale(1);}
  to  {opacity:0;transform:scale(0.95);}
}
.mpm-menu[data-state="open"]{
  transform-origin:var(--mpm-origin, center top);
  animation:mpm-enter 150ms var(--ease-out) both;
  /* An open menu MUST be hit-testable no matter what it is parented to.
     pointer-events is INHERITED, and the dashboard's <aside class="sidebar">
     is deliberately pointer-events:none (a hit-transparent shell, so the
     strip between --rail-w and --sidebar-w stays click-through); it expects
     interactive children to opt back in. The menu is not portalled to <body>
     (that is how home's --ha-u scale reaches it), so it inherits that none
     and every row goes dead while still LOOKING perfectly fine. */
  pointer-events:auto;
}
.mpm-menu[data-state="closed"]{
  transform-origin:var(--mpm-origin, center top);
  animation:mpm-exit 120ms var(--ease-out) both;
  pointer-events:none;
}
.mpm-menu[data-side="bottom"]{--mpm-slide-from:calc(-1 * var(--mpm-slide));}
.mpm-menu[data-side="top"]   {--mpm-slide-from:var(--mpm-slide);}

/* ── Item ─────────────────────────────────────────────────────────────────
   One rule for <button> and <a> rows alike. Highlight is driven by
   [data-highlighted], which menu.js stamps from real DOM focus — the same
   single source of truth Radix uses, so pointer hover and keyboard arrows
   can never disagree about which row is live. */
.mpm-item{
  position:relative;
  display:flex;
  align-items:center;
  gap:var(--mpm-gap);
  width:100%;
  min-height:calc(2 * var(--mpm-u));
  padding:var(--mpm-item-py) var(--mpm-item-px);
  border:0;
  border-radius:var(--mpm-radius-item);
  background:transparent;
  color:var(--text-primary);
  font-family:inherit;
  font-size:var(--mpm-text);
  font-weight:500;
  line-height:1.45;
  letter-spacing:inherit;
  text-align:left;
  text-decoration:none;
  cursor:pointer;
  user-select:none;
  -webkit-user-select:none;
  outline:none;
  transition:background-color 120ms var(--ease-out), color 120ms var(--ease-out);
}
.mpm-item[data-highlighted]{
  background:var(--mpm-highlight);
  color:var(--text-primary);
}
/* Keyboard-only ring. Inset box-shadow rather than a 1px outline: rounded 1px
   borders render as bowtie artifacts in headless captures (see CLAUDE.md).
   [data-pointer] is stamped by menu.js while the pointer is driving the
   highlight, so a merely hovered row never picks up the keyboard ring —
   :focus-visible alone is a browser heuristic and guesses wrong when the
   component moves focus programmatically. */
.mpm-menu:not([data-pointer]) .mpm-item:focus-visible{
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--accent) 55%, transparent);
}
.mpm-item[aria-disabled="true"]{
  opacity:0.5;
  pointer-events:none;
  cursor:default;
}

/* Destructive variant — shadcn's data-[variant=destructive]. */
.mpm-item[data-variant="destructive"]{color:var(--red);}
.mpm-item[data-variant="destructive"] .mpm-icon{color:var(--red);}
.mpm-item[data-variant="destructive"][data-highlighted]{
  background:var(--mpm-highlight-destructive);
  color:var(--red);
}
.mpm-item[data-variant="destructive"][data-highlighted] .mpm-icon{color:var(--red);}

/* ── Item parts ───────────────────────────────────────────────────────────*/
.mpm-icon{
  flex:0 0 auto;
  display:block;
  width:var(--mpm-icon);
  height:var(--mpm-icon);
  color:var(--text-muted);
  pointer-events:none;
  transition:color 120ms var(--ease-out);
}
.mpm-item[data-highlighted] .mpm-icon{color:var(--text-secondary);}

.mpm-label-text{
  flex:1 1 auto;
  min-width:0;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

/* shadcn DropdownMenuShortcut: ml-auto text-xs tracking-widest muted. */
.mpm-shortcut{
  flex:0 0 auto;
  margin-left:auto;
  padding-left:var(--mpm-gap);
  font-size:var(--mpm-text-xs);
  font-weight:600;
  letter-spacing:0.1em;
  color:var(--text-muted);
}
.mpm-item[data-highlighted] .mpm-shortcut{color:var(--text-secondary);}

/* ── Group label + separator ──────────────────────────────────────────────*/
.mpm-label{
  padding:var(--mpm-item-py) var(--mpm-item-px);
  font-size:var(--mpm-text-xs);
  font-weight:500;
  color:var(--text-muted);
  user-select:none;
  -webkit-user-select:none;
}
.mpm-sep{
  height:1px;
  margin:var(--mpm-pad) calc(-1 * var(--mpm-pad));
  background:var(--border-default);
}

/* ── Identity block (shadcn nav-user label) ───────────────────────────────*/
.mpm-user{
  display:flex;
  align-items:center;
  gap:var(--mpm-gap);
  padding:var(--mpm-item-py) var(--mpm-item-px);
  border-radius:var(--mpm-radius-item);
  user-select:none;
  -webkit-user-select:none;
}
.mpm-user-avatar{
  flex:0 0 auto;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:calc(2 * var(--mpm-u));
  height:calc(2 * var(--mpm-u));
  border-radius:var(--mpm-radius-item);
  object-fit:cover;
  background:color-mix(in srgb, var(--accent) 14%, transparent);
  color:var(--accent);
  font-size:var(--mpm-text-xs);
  font-weight:700;
  overflow:hidden;
}
.mpm-user-avatar img{width:100%;height:100%;object-fit:cover;display:block;}
.mpm-user-text{
  display:grid;
  flex:1 1 auto;
  min-width:0;
  text-align:left;
  line-height:1.3;
}
.mpm-user-name{
  font-size:var(--mpm-text);
  font-weight:600;
  color:var(--text-primary);
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
.mpm-user-sub{
  font-size:var(--mpm-text-xs);
  font-weight:450;
  color:var(--text-muted);
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
/* Rank/plan pill, right-aligned like a shortcut. */
.mpm-user-tag{
  flex:0 0 auto;
  margin-left:auto;
  padding:1px calc(0.3125 * var(--mpm-u));
  border-radius:calc(0.25 * var(--mpm-u));
  background:color-mix(in srgb, var(--accent) 14%, transparent);
  color:var(--accent);
  font-size:calc(0.625 * var(--mpm-u));
  font-weight:700;
  letter-spacing:0.08em;
  text-transform:uppercase;
  line-height:1.6;
}

/* ── Reduced motion ───────────────────────────────────────────────────────*/
@media (prefers-reduced-motion: reduce){
  @keyframes mpm-enter{from{opacity:0;}to{opacity:1;}}
  @keyframes mpm-exit {from{opacity:1;}to{opacity:0;}}
  .mpm-menu[data-state="open"]  {animation:mpm-enter 100ms linear both;transform:none;}
  .mpm-menu[data-state="closed"]{animation:mpm-exit   80ms linear both;transform:none;}
  .mpm-item,.mpm-icon,.mpm-shortcut{transition:none;}
}
