/*
  UANT v1 — component kit (REDESIGN-1, Linear design system)

  Every token in §1 is taken from the Linear reference
  `/root/.hermes/skills/creative/popular-web-designs/templates/linear.app.md`
  (§2 palette, §3 typography, §5 radius/spacing, §6 elevation). Nothing here is
  invented: where a value was not in the reference it is interpolated from the
  two sizes Linear does give (e.g. display tracking -1.056px@48 → -0.704px@32 →
  -0.6px@28). Do not add colours or sizes ad hoc — extend the reference first.

  Two deliberate deviations, both named requirements outside Linear:
   1. --u-text is #e1e1ea rather than Linear's #f7f8f8. Linear's own rule is
      "never pure white"; #e1e1ea sits between its Primary (#f7f8f8) and
      Secondary (#d0d6e0) and is the off-white the design-review skill mandates
      for body copy. Headings keep #f7f8f8 (--u-text-strong).
   2. #fff is used only as text ON the accent fill (#5e6ad2), never as page text.

  Structure: tokens -> base -> shell/nav -> buttons -> forms -> data ->
  table -> states -> overlay -> money/tree -> utilities -> responsive.
  Mobile-first: base rules target 390px, @media(min-width:...) adds room.

  Legacy tokens.css / member.css / admin.css are UNCHANGED and still wired into
  resources/views/uant/**; they are a different (legacy) kit. Do not merge.
*/

/* ============================== 1. TOKENS ============================== */
:root{
  /* Color — Linear §2. Surface elevation happens by raising the white opacity
     of the background (0.02 -> 0.04 -> 0.05), never by shadow on dark. */
  --u-bg:#08090a;              /* Marketing Black  */
  --u-bg-2:#0f1011;            /* Panel Dark       */
  --u-panel:#0f1011;
  --u-panel-2:#191a1b;         /* Level 3 surface  */
  --u-panel-3:#28282c;         /* Secondary surface / hover */

  /* Borders — always semi-transparent white, never a solid dark colour */
  --u-line:#23252a;            /* Border Primary (solid, for edges) */
  --u-line-soft:rgba(255,255,255,.05);  /* Border Subtle — the default */
  --u-line-strong:rgba(255,255,255,.08);/* Border Standard — cards, inputs */
  --u-line-tint:#141516;       /* Line Tint — the faintest division */

  /* Text — off-white, never #fff. Contrast-verified against every surface the
     kit actually paints (bg #08090a, panel #0f1011, zebra #121314, bar #171819):
       --u-text-strong 18.73 / --u-text 15.33 / --u-soft 13.64 / --u-muted 6.13
       --u-faint 5.10 -> 4.55 (worst case, the chart-bar fill)
     --u-faint was Linear's Quaternary #62666d, which measures 3.45 on the page
     background and 3.08 on a bar — under AA for 11px metadata, i.e. every
     kicker, timestamp and column header in the kit. It is lifted to #7d8189,
     the nearest value on the same neutral ramp that clears 4.5 everywhere. */
  --u-text:#e1e1ea;
  --u-text-strong:#f7f8f8;
  --u-soft:#d0d6e0;
  /* Muted + faint, both >= 4.5:1 on the lightest surface the kit puts text on
     (--u-panel-3 #28282c): muted 5.13, faint 4.52. The old faint #7d8189 measured
     3.76 there and 4.46 on a zebra stripe — i.e. it failed exactly where a
     secondary label sits. Kept as two tiers so "supporting" and "incidental"
     still differ; the ordering (faint < muted) is what a page relies on. */
  --u-muted:#9499a1;
  --u-faint:#8a8f98;

  /* Accent — the only chromatic family in the chrome. Indigo = interactive. */
  --u-accent:#5e6ad2;
  --u-accent-2:#7170ff;
  --u-accent-hover:#828fff;
  --u-accent-bg:rgba(94,106,210,.14);
  /* Legacy alias. The pre-redesign kit had a second chromatic family (cyan
     #2fd8e6) for links, active rails and progress fills; the redesign made indigo
     the single interactive colour, but 10 member/admin pages and auth.css still
     reference var(--u-cyan). Without these two lines every one of those
     declarations is invalid at computed-value time and the link/active colour
     silently falls back to inherit. Mapped onto the indigo family rather than
     reintroducing a second hue. */
  --u-cyan:#8b97ff;
  --u-cyan-ink:#08090a;
  /* admin.css/member.css (loaded by no page today) size the old sidebar with
     this name; alias it so reviving either file cannot yield a 0-width rail. */
  --u-sidebar:var(--u-sidebar-w);
  /* On-accent ink is #ffffff, not #f7f8f8: on the indigo fill #f7f8f8 measures
     4.42 and #ffffff 4.70 — the only difference that clears AA at 13px. */
  --u-on-accent:#ffffff;
  --u-on-accent-hover:#08090a; /* hover lightens the fill; ink flips to dark */

  /* Status — Linear's green plus the semantic set. Functional use only. */
  --u-green:#10b981;
  --u-red:#ef4444;
  --u-gold:#f59e0b;
  --u-blue:#3b82f6;

  --u-status-success-bg:rgba(16,185,129,.13); --u-status-success-fg:var(--u-green);
  --u-status-warn-bg:rgba(245,158,11,.13);    --u-status-warn-fg:var(--u-gold);
  /* Badge foregrounds are lighter than the raw status hue: a badge paints its
     own 13%-tinted fill, and the raw hue on that fill measures 4.49 (danger) and
     4.50 (info) — both under AA for 11px. The lifted values measure 6.11 / 6.51
     on the same fill. The raw hues stay for text drawn straight on the page
     background, where they already clear AA (red 5.30, blue 5.42). */
  --u-status-danger-bg:rgba(239,68,68,.13);   --u-status-danger-fg:#f87171;
  --u-status-info-bg:rgba(59,130,246,.13);    --u-status-info-fg:#60a5fa;
  --u-status-idle-bg:rgba(255,255,255,.05);   --u-status-idle-fg:var(--u-muted);

  /* Semantic colour — the four status hues plus neutral, each carrying exactly
     ONE meaning. Two collisions this fixes:
       amber was "money out" AND "waiting"  -> debit is neutral; amber = waiting
       indigo was CTA AND nav-active AND a data highlight -> indigo = interactive
     Named aliases rather than new hues: a later theme changes one line here
     instead of hunting a literal in a page. */
  --u-pos:var(--u-green);        /* money in · delta up */
  --u-neg:var(--u-red);          /* money out that failed/reversed · delta down */
  --u-warn:var(--u-gold);        /* waiting / pending / limit reached */
  --u-info:var(--u-blue);        /* status information — never an action */
  --u-neutral:var(--u-muted);    /* debit · idle · plain label */
  /* The danger hue, named once. `--u-red` is the raw colour; `--u-danger` is the
     meaning (failed / reversed / burned) so a page states intent, not a hue. */
  --u-danger:var(--u-red);
  /* Diagram edge (binary tree connectors). A connector is a graphic, not a
     hairline: at the card-border weight (.08) the elbow vanished into the page
     and the tree read as floating cards. 38% flattens to #656667 on the page
     background — 3.46:1, i.e. it clears the 3:1 non-text-graphic floor with
     headroom (26% measured 2.19:1, under it). */
  --u-tree-line:rgba(255,255,255,.38);
  /* Binary-tree geometry. Fixed card HEIGHT (not min-height) is what makes the
     generation rail line up: band = card-h + elbow, for every generation, and a
     card that grows to fit its own text would ripple through every band below it.
     One card size for member cards AND empty seats. overflow:hidden because the
     height is a promise: content that does not fit is clipped, not allowed to
     stretch the grid. */
  --u-tree-card-w:150px; --u-tree-card-h:200px;
  --u-tree-gap:8px;    /* between siblings inside one leg */
  --u-tree-leg:44px;   /* extra padding between the two legs — see §12 */
  --u-tree-elbow:26px;
  /* The bus is drawn at HALF the gap, i.e. exactly between the parent's bottom
     edge and the children's top edge, so the stem above and the stem below it are
     the same length and the joint reads as one line. Re-declared on `.u-tree`
     below (not only here) because the mobile rule overrides the elbow: a root-level
     calc would freeze the half at the desktop value and the stem would miss the
     bus on a phone. */
  --u-tree-half:calc(var(--u-tree-elbow) / 2);
  --u-chain-live:var(--u-green); /* chain dot: its own token, so "chain is live"
                                    and "money came in" are not one value */
  --u-dir-in:var(--u-green);     /* table direction arrow, credit */
  --u-dir-out:var(--u-muted);    /* table direction arrow, debit — neutral */
  --u-delta-up:var(--u-green);
  --u-delta-down:var(--u-red);

  /* Type — Linear §3: Inter with "cv01","ss03" globally, JetBrains Mono for
     numbers/code. Weights 400 (read) / 510 (UI, emphasis) / 590 (announce);
     Linear's maximum is 590, so no 600/700 anywhere in this kit.
     Variable + static fallbacks both declared in fonts.css. */
  --u-sans:"Inter","Plus Jakarta Sans","Segoe UI",system-ui,-apple-system,sans-serif;
  --u-mono:"JetBrains Mono","IBM Plex Mono","SFMono-Regular",Consolas,monospace;
  --u-feat:"cv01","ss03";
  --u-num:"tnum" 1,"zero" 1;

  /* Type ladder — ONE closed scale: 40 / 28 / 24 / 18 / 15 / 13 / 11. Every size
     in the kit resolves to one of these seven, and a page-local `font-size:` off
     the ladder is a bug. 12px was dropped (it sat between 11 and 13, so "micro
     label", "meta" and "body" were indistinguishable); 20/26/36 were dropped for
     the same reason — 40 → 26 → 15 → 11 left no tier for a card title at all.
       40 hero stat   · 28 display (attention figure)
       24 page/section heading · 18 card title · 15 lead · 13 body · 11 micro */
  --u-fs-2xs:11px; --u-fs-xs:13px; --u-fs-sm:13px; --u-fs-base:13px; --u-fs-md:15px;
  --u-fs-lg:18px; --u-fs-xl:24px; --u-fs-2xl:28px; --u-fs-hero:40px;
  --u-lh-tight:1.2; --u-lh-base:1.55;

  /* Spacing — Linear §5 base 8 with 4px micro-steps. --u-sp-5 is 24, not 20:
     the old 20px card padding was off the 8px rhythm and made two adjacent cards
     differ by 4px for no reason. */
  --u-sp-1:4px; --u-sp-2:8px; --u-sp-3:12px; --u-sp-4:16px; --u-sp-5:24px;
  --u-sp-6:24px; --u-sp-7:32px; --u-sp-8:40px; --u-sp-9:56px; --u-sp-10:64px;

  /* Radius — Linear §5 closed scale. Names are the pre-existing ones so the
     twenty-two untouched pages pick up the tighter scale without an edit. */
  --u-r-2xs:2px; --u-r-xs:4px; --u-r-sm:6px; --u-r-md:8px; --u-r-lg:12px; --u-r-pill:999px;

  /* Elevation — Linear §6. On dark, depth is a hairline ring + faint shadow;
     page-level cards get NONE. Only floating layers (modal/drawer/toast) lift. */
  --u-shadow-1:0 8px 24px -8px rgba(0,0,0,.7);
  --u-shadow-2:0 16px 40px -12px rgba(0,0,0,.8);
  --u-focus:0 0 0 1px var(--u-bg),0 0 0 3px var(--u-accent-2);

  /* Fixed right rail. The dashboard hero band and the split below it must end on
     the same x, which only holds if both read the rail width from here. */
  --u-rail:340px;

  /* 256px is the member dashboard's rail width (the approved golden standard),
     and --u-topbar-h is 60px because every panel page pins its header at 60px.
     These two values are the shell's contract: `.u-sidebar` is 256 wide and
     `.u-main` offsets by the SAME token, so the two numbers can never drift
     apart. They used to disagree (236 token vs a 256 inline rail), which left a
     20px strip where the translucent sticky header painted OVER the rail and the
     seam stopped being a clean 90-degree corner. Never hand-size one side.
     --u-sidebar-bg is the rail surface: a step lighter than --u-bg so the fixed
     rail reads as its own plane and its 1px right border has something to sit on. */
  --u-sidebar-w:256px;
  --u-sidebar-bg:#0d0f18;
  --u-topbar-h:60px;
  --u-bottomnav-h:60px;
  --u-motion:.15s ease;
}

/* ============================== 2. BASE ============================== */
*,*::before,*::after{box-sizing:border-box}
/* color-scheme is not cosmetic: without it the file picker, date/select glyphs
   and scrollbars render in the light OS theme over this dark page. */
html{background:var(--u-bg);color-scheme:dark;scrollbar-color:var(--u-line) transparent;scrollbar-width:thin}
body{
  background:var(--u-bg);color:var(--u-text);margin:0;
  font:var(--u-fs-base)/var(--u-lh-base) var(--u-sans);font-feature-settings:var(--u-feat);
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}
/* A control with no kit class must still look like the kit. Without this a bare
   <input> renders at the UA default 13.3333px Arial: off the type ladder and the
   only non-Inter text on the page. */
button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:inherit;line-height:inherit}
h1,h2,h3,h4{
  color:var(--u-text-strong);font-family:var(--u-sans);font-weight:590;
  letter-spacing:-.02em;line-height:var(--u-lh-tight);margin:0;
}
p{margin:0 0 var(--u-sp-3)}
a{color:inherit;text-decoration:none}
button{font-family:var(--u-sans)}
ul{margin:0;padding:0;list-style:none}
code,kbd,samp,pre{font-family:var(--u-mono)}
code{background:rgba(255,255,255,.04);border:1px solid var(--u-line-soft);border-radius:var(--u-r-xs);font-size:11px;padding:1px 5px}
::selection{background:var(--u-accent-bg)}
/* Keyboard focus: a 2px accent outline with a 2px offset, on EVERY focusable
   element without each page adding a rule. An outline (not a box-shadow ring)
   because it follows the element's own shape and never gets confused with the
   element's border. --u-focus (the box-shadow form) stays for the controls that
   paint their ring on a sibling — a toggle track, a segmented label. */
:focus-visible{outline:2px solid var(--u-accent-2);outline-offset:2px;border-radius:var(--u-r-sm)}
/* The keyboard-only focus ring above never fires on a mouse click, which is the
   point; a mouse user still gets the hover/active states below. */
.u-scrollbar{scrollbar-width:thin;scrollbar-color:var(--u-line) transparent}
@media(prefers-reduced-motion:reduce){
  *{animation-duration:.001ms!important;transition-duration:.001ms!important}
  .u-skeleton{background:var(--u-panel-2)}
}

/* ============================ 3. APP SHELL / NAV ============================ */
.u-shell{min-height:100vh}
/* Base rule is the MOBILE shell: the bottom nav is fixed and carries its own
   env(safe-area-inset-bottom) below the 60px bar, so the scroll column has to
   clear bar + inset or the last row of content sits under the nav. The >=900px
   block below replaces this (nav hidden there). */
.u-main{padding-bottom:calc(var(--u-bottomnav-h) + var(--u-sp-4) + env(safe-area-inset-bottom))}

/* Topbar — one brand mark, one live indicator, one identity. A page title is NOT
   repeated here: the sidebar marks location and the page head states the title. */
.u-topbar{
  align-items:center;background:color-mix(in srgb,var(--u-bg) 88%,transparent);
  backdrop-filter:blur(16px);border-bottom:1px solid var(--u-line-soft);
  display:flex;gap:var(--u-sp-3);height:var(--u-topbar-h);justify-content:space-between;
  padding:0 var(--u-sp-4);position:sticky;top:0;z-index:30;
}
.u-topbar-btn{
  align-items:center;background:none;border:0;border-radius:var(--u-r-sm);
  color:var(--u-muted);display:flex;height:44px;justify-content:center;width:44px;font-size:15px;
}
/* 44px is the touch floor; only a pointer gets the dense 32px control. */
@media(min-width:900px){ .u-topbar-btn{height:32px;width:32px} }
.u-topbar-btn:hover{background:rgba(255,255,255,.04);color:var(--u-text-strong)}
/* The brand mark is a small gradient-free tile + wordmark, the way Linear renders
   its own logomark: an inline glyph, not a coloured headline. */
.u-logo{align-items:center;color:var(--u-text-strong);display:flex;font-size:15px;font-weight:590;gap:8px;letter-spacing:-.01em}
.u-logo i{color:var(--u-accent-hover);font-size:15px}
.u-logo:hover{color:var(--u-text-strong)}
.u-topbar-user{align-items:center;display:flex;gap:var(--u-sp-2);margin-left:auto;color:var(--u-soft);font-size:var(--u-fs-sm)}
.u-topbar-user:hover{color:var(--u-text-strong)}
.u-topbar-live{align-items:center;color:var(--u-faint);display:none;font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);gap:6px}
.u-live-dot{animation:u-pulse 2.4s ease-in-out infinite;background:var(--u-green);border-radius:99px;flex:none;height:6px;width:6px}
@keyframes u-pulse{0%,100%{opacity:1}50%{opacity:.45}}

/* Topbar popovers — the two identity controls (bell, user pill) open one of
   these. A control that carries a pointer cursor and a chevron owes the user a
   surface, so the panel is part of the control, not a decoration: it is closed
   by default, opened by shell.js ([data-u-pop] -> #id), and closed by outside
   click or Escape. Widths are capped to the viewport so the 320px notification
   panel never overflows a 390px phone. */
.u-pop{background:var(--u-panel-2);border:1px solid var(--u-line-strong);border-radius:var(--u-r-lg);box-shadow:var(--u-shadow-2);display:none;overflow:hidden;position:absolute;right:0;top:calc(100% + 10px);z-index:45}
.u-pop.is-open{display:block}
.u-pop-notif{width:min(340px,calc(100vw - 32px))}
.u-pop-user{width:min(240px,calc(100vw - 32px))}
/* Below 900px the trigger sits near the right edge, so `right:0` on a 340px
   panel pushes its left edge off a 390px screen. Stretch both panels across the
   topbar box instead — the topbar is `position:sticky`, so it is already the
   containing block (the backdrop-filter would make it one anyway) and it spans
   the viewport: 12px insets always land on screen. */
@media(max-width:899px){
  .u-pop{left:12px;right:12px;top:calc(100% + 8px);position:fixed;width:auto}
}
.u-pop-head{align-items:center;border-bottom:1px solid var(--u-line-soft);display:flex;gap:var(--u-sp-2);justify-content:space-between;padding:var(--u-sp-3) var(--u-sp-4)}
.u-pop-head b{color:var(--u-text-strong);font-size:var(--u-fs-xs);font-weight:590}
.u-pop-body{max-height:300px;overflow-y:auto}
.u-pop-item{border-bottom:1px solid var(--u-line-soft);display:flex;gap:var(--u-sp-3);padding:var(--u-sp-3) var(--u-sp-4)}
.u-pop-item:last-child{border-bottom:0}
.u-pop-ico{align-items:center;background:var(--u-accent-bg);border-radius:50%;color:var(--u-accent-hover);display:flex;flex:none;font-size:var(--u-fs-2xs);height:30px;justify-content:center;width:30px}
.u-pop-t{color:var(--u-text-strong);font-size:var(--u-fs-xs);font-weight:510;line-height:1.3}
.u-pop-c{color:var(--u-muted);font-size:var(--u-fs-2xs);line-height:1.5;margin-top:2px}
.u-pop-when{color:var(--u-faint);font-size:var(--u-fs-2xs);margin-top:4px}
.u-pop-empty{color:var(--u-muted);font-size:var(--u-fs-xs);padding:var(--u-sp-6) var(--u-sp-4);text-align:center}
.u-pop-empty i{color:var(--u-faint);display:block;font-size:var(--u-fs-md);margin-bottom:var(--u-sp-2)}
.u-pop-sep{border-top:1px solid var(--u-line-soft)}
.u-pop-id{padding:var(--u-sp-3) var(--u-sp-4)}
.u-pop-id b{color:var(--u-text-strong);display:block;font-size:var(--u-fs-xs);font-weight:590}
.u-pop-id span{color:var(--u-muted);font-size:var(--u-fs-2xs)}
.u-pop-link{align-items:center;color:var(--u-soft);display:flex;font-size:var(--u-fs-xs);gap:var(--u-sp-3);min-height:44px;padding:0 var(--u-sp-4);text-decoration:none}
.u-pop-link:hover{background:rgba(255,255,255,.03);color:var(--u-text-strong)}
.u-pop-link i{color:var(--u-muted);font-size:var(--u-fs-xs);text-align:center;width:14px}
.u-pill-accent{background:var(--u-accent-bg);border-color:rgba(113,112,255,.45);color:var(--u-accent-hover)}
[data-u-chevron]{transition:transform var(--u-motion)}
[data-u-chevron].is-open{transform:rotate(180deg)}

/* Breadcrumb — a single small trail. Never restates the page title. */
.u-breadcrumb{align-items:center;color:var(--u-faint);display:flex;flex-wrap:wrap;font-size:var(--u-fs-xs);gap:6px}
.u-breadcrumb a{color:var(--u-muted)}
.u-breadcrumb a:hover{color:var(--u-accent-hover)}
/* A separator has to be seen to separate: --u-line is a 5%-white hairline and
   flattens to ~1.06:1 as text. It is punctuation, so it reads at the faint tier. */
.u-breadcrumb .sep{color:var(--u-faint);font-size:var(--u-fs-2xs)}
.u-breadcrumb .current{color:var(--u-soft)}

/* Desktop sidebar — hidden below 900px, where [data-u-menu] opens it as a drawer
   over the content (scrim + Escape close, wired in shell.js).
   z-index sits ABOVE .u-topbar on purpose: the topbar is a translucent
   blurred bar inside the scrolling column, so if the two ever overlap any part
   of the rail shows the header blur painted over it and the seam stops reading
   as a corner. The fixed rail is the backmost shell layer; content stacks on it. */
.u-sidebar{
  background:var(--u-sidebar-bg);border-right:1px solid var(--u-line-soft);display:flex;
  flex-direction:column;height:100vh;left:0;padding:var(--u-sp-4) var(--u-sp-3) var(--u-sp-3);
  position:fixed;top:0;width:var(--u-sidebar-w);z-index:40;
}
@media(max-width:899px){
  .u-sidebar{background:var(--u-bg-2);transform:translateX(-100%);transition:transform var(--u-motion);width:min(276px,84vw)}
  .u-sidebar.is-open{transform:translateX(0)}
}
.u-sidebar.is-open{transform:translateX(0)}
.u-scrim{background:rgba(0,0,0,.6);bottom:0;display:none;left:0;position:fixed;right:0;top:0;z-index:35}
.u-scrim.is-open{display:block}

/* Chain indicator — dot + label, not a decorative pill. */
.u-chain{align-items:center;color:var(--u-faint);display:flex;font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);gap:6px;margin:var(--u-sp-4) var(--u-sp-2) var(--u-sp-4)}
.u-chain i{background:var(--u-green);border-radius:99px;flex:none;height:6px;width:6px}
.u-nav{display:flex;flex:1;flex-direction:column;gap:1px;overflow-y:auto}
.u-nav-group{color:var(--u-faint);font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);letter-spacing:.06em;margin:var(--u-sp-4) var(--u-sp-2) 6px;text-transform:uppercase}
.u-nav-group:first-child{margin-top:0}
.u-nav-item{
  align-items:center;border-radius:var(--u-r-sm);color:var(--u-muted);display:flex;
  font-size:var(--u-fs-sm);font-weight:510;gap:10px;min-height:44px;padding:7px 10px;
  position:relative;transition:background var(--u-motion),color var(--u-motion);
}
@media(min-width:900px){ .u-nav-item{min-height:0} }
.u-nav-item i{color:var(--u-faint);flex:none;font-size:13px;text-align:center;width:16px}
.u-nav-item:hover{background:rgba(255,255,255,.04);color:var(--u-soft)}
.u-nav-item:hover i{color:var(--u-muted)}
/* Active = raised surface + lightened text + one 2px accent rail. Position is
   legible at a glance without a second colour entering the palette. */
.u-nav-item.is-active{background:rgba(255,255,255,.06);color:var(--u-text-strong)}
.u-nav-item.is-active i{color:var(--u-accent-hover)}
.u-nav-item.is-active::before{background:var(--u-accent-2);border-radius:99px;bottom:6px;content:"";left:-3px;position:absolute;top:6px;width:2px}
.u-sidebar-foot{border-top:1px solid var(--u-line-soft);color:var(--u-faint);font:400 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);line-height:1.7;margin-top:var(--u-sp-3);padding:var(--u-sp-3) var(--u-sp-2) 0}
.u-sidebar-foot a{color:var(--u-muted)}
.u-sidebar-foot a:hover{color:var(--u-accent-hover)}

/* Mobile bottom nav — primary nav below 900px, 5 items max, 44px+ targets */
.u-bottomnav{
  background:color-mix(in srgb,var(--u-bg-2) 94%,transparent);backdrop-filter:blur(12px);
  border-top:1px solid var(--u-line-soft);bottom:0;display:flex;height:var(--u-bottomnav-h);
  justify-content:space-around;left:0;padding-bottom:env(safe-area-inset-bottom);
  position:fixed;right:0;z-index:25;
}
.u-bottomnav a{
  align-items:center;color:var(--u-faint);display:flex;flex:1;flex-direction:column;
  font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);gap:3px;justify-content:center;transition:color var(--u-motion);
}
.u-bottomnav a i{font-size:var(--u-fs-md)}
.u-bottomnav a:hover{color:var(--u-soft)}
.u-bottomnav a.is-active{color:var(--u-accent-hover)}

/* Page head — the h1 is the one place a page states its name. The breadcrumb is
   forced onto its own line (flex-basis:100%) so the title and the page actions
   share the row below it and a page with no actions still reads the same. */
.u-page-head{align-items:center;display:flex;flex-wrap:wrap;gap:var(--u-sp-3);justify-content:space-between;margin-bottom:var(--u-sp-6)}
.u-page-head>nav,.u-page-head>.u-breadcrumb{flex-basis:100%}
.u-page-title{font-size:var(--u-fs-xl);font-weight:590;letter-spacing:-.03em;text-wrap:balance}
.u-page-kicker{color:var(--u-muted);font-size:var(--u-fs-sm);line-height:1.5}
.u-page-actions{align-items:center;display:flex;flex-wrap:wrap;gap:var(--u-sp-2)}

/* Tabs — Linear's underline. Active tab carries the accent and nothing else.
   The bar scrolls sideways when the labels outgrow the width (a 390px phone with
   three named tabs: "Sync trxHash / Antrian approval / WD pending" is 400px of
   text). A sideways scroll that CLIPS mid-word reads as broken text, so below the
   sidebar breakpoint the bar wraps instead: two rows of tabs, nothing cut. */
.u-tabs{border-bottom:1px solid var(--u-line-soft);display:flex;flex-wrap:wrap;gap:var(--u-sp-5);overflow-x:auto;scrollbar-width:none}
.u-tabs::-webkit-scrollbar{display:none}
@media(max-width:899px){ .u-tabs{gap:var(--u-sp-4) var(--u-sp-4);overflow-x:visible} }
.u-tab{
  background:none;border:0;border-bottom:2px solid transparent;color:var(--u-muted);
  cursor:pointer;font:510 var(--u-fs-sm) var(--u-sans);font-feature-settings:var(--u-feat);margin-bottom:-1px;
  min-height:44px;padding:var(--u-sp-3) 2px;transition:color var(--u-motion),border-color var(--u-motion);white-space:nowrap;
}
.u-tab:hover{color:var(--u-soft)}
.u-tab.is-active{border-color:var(--u-accent-2);color:var(--u-text-strong)}
.u-tab .u-count{background:rgba(255,255,255,.06);border-radius:99px;color:var(--u-muted);font-size:var(--u-fs-2xs);margin-left:6px;padding:1px 6px}
.u-tab.is-active .u-count{background:var(--u-accent-bg);color:var(--u-accent-hover)}
/* The tab pattern's body half: the kit hides a non-active [data-tab-panel]
   above, but nothing gave the revealed body its breathing room, so every page
   that used the pattern declared a pad of its own (staking.html). One rule here
   keeps the pad identical wherever the tabs appear. */
.u-tab-body-pad{padding-top:var(--u-sp-5)}
[data-tab-panel][hidden]{display:none}

@media(min-width:900px){
  .u-topbar-btn[data-u-menu]{display:none}
  .u-sidebar{display:flex}
  .u-main{margin-left:var(--u-sidebar-w);padding-bottom:var(--u-sp-7)}
  .u-bottomnav{display:none}
  .u-topbar-live{display:flex}
  /* Topbar indent = `.u-content` horizontal pad (sp-7 = 32), so the breadcrumb
     and the user pill sit on the same x as the card edges below them. One value
     for both: the base rule keeps sp-4, which is what `.u-content` uses under
     900px. */
  .u-topbar{padding:0 var(--u-sp-7)}
}
.u-content{margin:0 auto;max-width:1200px;padding:var(--u-sp-5) var(--u-sp-4) var(--u-sp-8)}
@media(min-width:900px){ .u-content{padding:var(--u-sp-8) var(--u-sp-7) var(--u-sp-10)} }

/* Section rhythm — 40px between blocks (2x the 20px row unit), no divider lines
   needed (Linear §5: on dark, absence separates). Nothing in the kit may use a
   margin that is not on the 4/8/12/16/24/32/40 ladder.

   The gap belongs to the container, not to each block: asking every page to put
   `.u-section` on the right block is what left eight admin pages at 0px. An owl
   stack on the content column means a page cannot forget it, and the older
   `.u-section` / `u-mb-4` classes on top-level blocks now collapse into this
   same 40px. Nested gaps (a panel inside a column) keep their own class. */
.u-content > * + *{margin-top:var(--u-sp-8)}
.u-section{margin-bottom:var(--u-sp-8)}
.u-section:last-child{margin-bottom:0}
/* Page footer: closes the page. Without it a short page ends in a black void
   under the last panel (measured ~200px on the dashboard, ~370px on the index),
   which reads as "still loading" rather than "this is all there is". The rule
   sits on the container so a page cannot forget it; `margin-top:auto` needs the
   content column to be a flex column, which it is not, so the spacer is a fixed
   block that simply always paints. */
.u-page-foot{
  border-top:1px solid var(--u-line-soft);color:var(--u-faint);
  display:flex;flex-wrap:wrap;font-size:var(--u-fs-2xs);gap:var(--u-sp-2) var(--u-sp-5);
  justify-content:space-between;margin-top:var(--u-sp-8);padding-top:var(--u-sp-4);
}
.u-page-foot code{font-size:var(--u-fs-2xs)}

/* ============================== 4. BUTTONS ============================== */
.u-btn{
  align-items:center;border:1px solid transparent;border-radius:var(--u-r-sm);
  cursor:pointer;display:inline-flex;font:510 var(--u-fs-sm) var(--u-sans);font-feature-settings:var(--u-feat);gap:7px;
  justify-content:center;min-height:44px;padding:0 14px;
  transition:background var(--u-motion),border-color var(--u-motion),color var(--u-motion),filter var(--u-motion);
  white-space:nowrap;
}
@media(min-width:900px){ .u-btn{min-height:32px} }
.u-btn:disabled{cursor:not-allowed;opacity:.4}
/* Linear's Primary Brand Button: solid indigo, 6px radius, #fff text.
   Hover lightens the fill to Linear's accent-hover (#828fff); white ink on that
   fill is only 2.87, so the ink flips to the page black (6.95) instead. */
.u-btn-primary{background:var(--u-accent);color:var(--u-on-accent)}
.u-btn-primary:hover:not(:disabled){background:var(--u-accent-hover);color:var(--u-on-accent-hover)}
.u-btn-primary:active:not(:disabled){background:var(--u-accent);color:var(--u-on-accent)}
/* Ghost: near-zero-opacity fill + hairline — Linear's default button. */
.u-btn-secondary,.u-btn-ghost{background:rgba(255,255,255,.02);border-color:var(--u-line-strong);color:var(--u-soft)}
.u-btn-secondary:hover:not(:disabled),.u-btn-ghost:hover:not(:disabled){background:rgba(255,255,255,.05);border-color:rgba(255,255,255,.12);color:var(--u-text-strong)}
.u-btn-ghost{border-color:var(--u-line-soft)}
.u-btn-danger{background:rgba(239,68,68,.14);border-color:rgba(239,68,68,.3);color:#fca5a5}
.u-btn-danger:hover:not(:disabled){background:rgba(239,68,68,.22);border-color:rgba(239,68,68,.45)}
.u-btn-icon{min-width:44px;padding:0}
@media(min-width:900px){ .u-btn-icon{min-width:32px} }
.u-btn-sm{font-size:var(--u-fs-xs);min-height:44px;padding:0 10px}
@media(min-width:900px){ .u-btn-sm{min-height:28px} }
.u-spin{animation:u-spin .8s linear infinite;display:inline-block}
.u-btn.is-loading{color:transparent;position:relative}
.u-btn.is-loading::after{
  animation:u-spin .8s linear infinite;border:2px solid currentColor;border-radius:99px;
  border-top-color:transparent;color:var(--u-soft);content:"";height:14px;left:50%;
  position:absolute;top:50%;transform:translate(-50%,-50%);width:14px;
}
.u-btn-primary.is-loading::after{color:var(--u-on-accent)}
@keyframes u-spin{to{transform:rotate(360deg)}}

/* ============================== 5. FORMS ============================== */
.u-field{display:flex;flex-direction:column;gap:6px}
.u-label{color:var(--u-soft);font:510 var(--u-fs-xs) var(--u-sans);font-feature-settings:var(--u-feat)}
.u-hint{color:var(--u-muted);font-size:var(--u-fs-xs);line-height:1.5}
.u-hint code{font-size:11px}
.u-input,.u-select,.u-textarea{
  background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);
  border-radius:var(--u-r-sm);color:var(--u-text-strong);font:400 var(--u-fs-sm) var(--u-sans);font-feature-settings:var(--u-feat);
  min-height:44px;padding:0 12px;transition:border-color var(--u-motion),background var(--u-motion);width:100%;
}
@media(min-width:900px){ .u-input,.u-select,.u-textarea{min-height:32px} }
.u-textarea{min-height:88px;padding:10px 12px;resize:vertical}
.u-input::placeholder,.u-textarea::placeholder{color:var(--u-faint)}
.u-input:hover:not(:disabled):not([readonly]),.u-select:hover:not(:disabled),.u-textarea:hover:not(:disabled){border-color:rgba(255,255,255,.14)}
.u-input:focus,.u-select:focus,.u-textarea:focus{background:rgba(255,255,255,.04);border-color:var(--u-accent-2)}
.u-input:disabled,.u-select:disabled,.u-textarea:disabled,.u-input[readonly]{
  background:rgba(255,255,255,.015);color:var(--u-muted);cursor:not-allowed;
}
.u-input[readonly]{cursor:text}
.u-input.is-invalid,.u-select.is-invalid{border-color:var(--u-red)}
.u-error-inline{align-items:center;color:var(--u-red);display:flex;font-size:var(--u-fs-xs);gap:5px}
/* `display:flex` above would otherwise beat the UA `[hidden]{display:none}`,
   so an inline error painted as an orphan warning icon with no text. */
.u-error-inline[hidden]{display:none}

.u-select{appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--u-muted) 50%),linear-gradient(135deg,var(--u-muted) 50%,transparent 50%);background-position:calc(100% - 17px) center,calc(100% - 12px) center;background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:var(--u-sp-6)}

/* Toggle switch */
.u-toggle{align-items:center;cursor:pointer;display:inline-flex;gap:var(--u-sp-2);font-size:var(--u-fs-sm)}
.u-toggle input{height:0;position:absolute;width:0;opacity:0}
.u-toggle-track{background:rgba(255,255,255,.08);border:1px solid var(--u-line-strong);border-radius:99px;display:inline-block;flex:none;height:20px;position:relative;transition:background var(--u-motion);width:34px}
.u-toggle-track::after{background:var(--u-muted);border-radius:99px;content:"";height:14px;left:2px;position:absolute;top:2px;transition:transform var(--u-motion),background var(--u-motion);width:14px}
.u-toggle input:checked+.u-toggle-track{background:var(--u-accent);border-color:var(--u-accent)}
.u-toggle input:checked+.u-toggle-track::after{background:var(--u-on-accent);transform:translateX(14px)}
.u-toggle input:focus-visible+.u-toggle-track{box-shadow:var(--u-focus)}

/* Segmented control — one control for an exclusive 2-3 way choice, instead of
   two radios that read as unrelated rows on a phone. */
.u-segmented{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);display:flex;gap:2px;padding:2px}
.u-segmented input{position:absolute;opacity:0;height:0;width:0}
.u-segmented label{
  border-radius:var(--u-r-xs);color:var(--u-muted);cursor:pointer;flex:1;font:510 var(--u-fs-xs) var(--u-sans);font-feature-settings:var(--u-feat);
  min-height:44px;display:flex;align-items:center;justify-content:center;padding:7px 10px;text-align:center;
  transition:background var(--u-motion),color var(--u-motion);
}
.u-segmented label:hover{color:var(--u-soft)}
/* The selected segment must be unmistakable: an accent fill plus a ring, not the
   4% white wash it used to be (that read as a hover, so the filter's state was
   invisible — audit C4). */
.u-segmented input:checked+label{background:var(--u-accent-bg);box-shadow:inset 0 0 0 1px rgba(113,112,255,.55);color:var(--u-text-strong)}
.u-segmented input:focus-visible+label{box-shadow:var(--u-focus)}
@media(min-width:900px){ .u-segmented label{min-height:30px;padding:5px 10px} }

/* Input group — read-only value + action on one row; the input shrinks first
   (min-width:0) so the row can never force horizontal scroll. */
.u-input-group{display:flex;gap:8px}
.u-input-group .u-input{flex:1 1 auto;min-width:0}
.u-input-group .u-btn{flex:none}
@media(max-width:520px){ .u-input-group{flex-wrap:wrap} .u-input-group .u-input{flex-basis:100%} }

/* ============================== 6. DATA: CARDS / STATS ============================== */
/* Linear card: translucent surface, hairline standard border, 8px radius, zero
   shadow. Never a solid fill, never a gradient. */
.u-panel{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-md);padding:var(--u-sp-5)}
.u-panel-head{align-items:flex-start;display:flex;gap:var(--u-sp-3);justify-content:space-between;margin-bottom:var(--u-sp-4)}
/* ONE card-title tier. A card title is 18px everywhere; a card earns dominance
   from its own weight (border, figure size, position on the page), never from a
   second title size — the 20/22/23/18 free-for-all is what made one dashboard
   read as five unrelated pages. Only the page H1 (24) and the hero figure (40)
   sit above this tier. `text-wrap:balance` kills the one-word last line ("hari /
   ini") without a hand-written <br> per page. */
.u-panel-title{color:var(--u-text-strong);font-size:var(--u-fs-lg);font-weight:590;letter-spacing:-.015em;text-wrap:balance}
.u-panel-kicker{color:var(--u-faint);font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);letter-spacing:.04em;text-transform:uppercase}
.u-panel-link{color:var(--u-accent-hover);font:510 var(--u-fs-xs) var(--u-sans);font-feature-settings:var(--u-feat);white-space:nowrap}
.u-panel-link:hover{color:#a5aaff}

/* Stat grid — hierarchy comes from SIZE, not from repetition. One hero stat
   (full width, 36px) dominates; secondary stats are smaller and never equal to
   it in weight. `data-cols="2|3|4"` sets the desktop column count without
   letting an inline grid-template-columns escape the responsive collapse. */
/* Two columns on a phone (owner's ask), four from 1100px.
   `minmax(0,1fr)` not `1fr`: a bare `1fr` track refuses to shrink below its
   content's min-content width, so one wide number would push the whole grid past
   the viewport (measured: +185px at 390px on the staking page).
   Half a phone screen cannot hold a 28px display-tier number, so the VALUE is
   what gives way on narrow screens — not the column count. */
.u-stat-grid{display:grid;gap:var(--u-sp-3);grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:640px){ .u-stat-grid{gap:var(--u-sp-4)} }
@media(min-width:1100px){ .u-stat-grid{grid-template-columns:repeat(4,minmax(0,1fr))} }
/* An odd card in a 2-column grid would leave a hole next to it; the last one
   takes the full row instead (measured on the network page: 3 compact cards
   went 4 rows → 2). */
@media(max-width:1099px){ .u-stat-grid > .u-stat:last-child:nth-child(odd){grid-column:1/-1} }
/* Explicit column counts keep their desktop width but still collapse on small
   screens. An inline `grid-template-columns` cannot be overridden by a media
   query, so pages that want 3 (or 4) columns say so with data-cols instead. */
.u-stat-grid[data-cols="2"]{grid-template-columns:repeat(2,minmax(0,1fr))}
.u-stat-grid[data-cols="3"]{grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:1100px){ .u-stat-grid[data-cols="3"]{grid-template-columns:repeat(3,minmax(0,1fr))} }
.u-stat-grid[data-cols="4"]{grid-template-columns:repeat(2,minmax(0,1fr))}
@media(min-width:1100px){ .u-stat-grid[data-cols="4"]{grid-template-columns:repeat(4,minmax(0,1fr))} }

.u-stat{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-md);padding:var(--u-sp-5)}
.u-stat-label{color:var(--u-muted);font-size:var(--u-fs-xs)}
/* Numbers are always the monospace, tabular ones — this is what stops digits
   from drifting between rows and from the header above them.
   `.u-stat strong` is here because the older pages mark the value up as
   `<span class="u-panel-kicker">Label</span><strong>1.234</strong>`; without this
   rule those values inherit 13px body type and every card ends up the same size. */
.u-stat strong,.u-stat-value{color:var(--u-text-strong);display:block;font:590 var(--u-fs-2xl)/1.15 var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num);letter-spacing:-.03em;margin-top:var(--u-sp-2)}
/* On a phone the grid is 2 columns, so a card is ~45vw wide. A 28px figure like
   `12.480,0000 UAN` measures 160px and cannot fit that, so the figure scales to
   the card instead of the card scaling to the figure — the alternative was
   stacking to 1 column, which the owner explicitly did not want.
   The mobile sizes are two rungs of the ladder (18 / 28) rather than a clamp: an
   interpolated `clamp()` lands between rungs (22px at 500px wide), and the
   ladder admits only its own seven values. */
@media(max-width:520px){ .u-stat strong,.u-stat-value{font-size:var(--u-fs-lg)} }
@media(max-width:520px){ .u-stat-hero strong,.u-stat-hero .u-stat-value{font-size:var(--u-fs-2xl)} }
.u-stat .u-panel-kicker{display:block;font-size:var(--u-fs-xs);letter-spacing:0;text-transform:none}
.u-stat small{color:var(--u-faint);display:block;font:400 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);margin-top:var(--u-sp-2)}
.u-stat-sub{color:var(--u-faint);font:400 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);margin-top:var(--u-sp-2)}
/* Hero: the single dominant element on the page. */
.u-stat-hero{grid-column:1/-1;background:rgba(255,255,255,.03);padding:var(--u-sp-5)}
/* Quiet KPI: the same card, one tier down. A dashboard that shows a dominant
   attention block plus a supporting strip needs a way to say "these four are
   context, not headlines" without four inline `font-size:` overrides — which is
   how the admin dashboard was doing it, and why its figures drifted off the
   ladder the moment the ladder changed. */
/* quiet deprecated */
/* A vanity figure (downline count) is not money. It sits one tier below the
   quiet stats so the eye reaches Bonus before Downline — the audit's point that
   a count of people must not weigh the same as a balance. */
/* minor deprecated */

.u-stat-hero .u-stat-value,.u-stat-hero strong{font-size:var(--u-fs-hero);letter-spacing:-.04em}
.u-stat-hero .u-stat-label,.u-stat-hero .u-panel-kicker{color:var(--u-soft);font-size:var(--u-fs-sm)}

/* Inline delta — arrow + sign, so direction is not colour-only (a11y). */
.u-money-delta{align-items:center;display:inline-flex;font:510 var(--u-fs-sm) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num);gap:4px}
.u-money-delta-up{color:var(--u-green)}
.u-money-delta-down{color:var(--u-red)}

/* History list — compact rows for activity feeds, not a table */
.u-list{display:flex;flex-direction:column}
.u-list-row{align-items:center;border-top:1px solid var(--u-line-soft);display:flex;gap:var(--u-sp-3);padding:var(--u-sp-3) 0}
.u-list-row:first-child{border-top:0}
.u-list-icon{align-items:center;background:rgba(255,255,255,.04);border:1px solid var(--u-line-soft);border-radius:var(--u-r-sm);color:var(--u-muted);display:flex;flex:none;height:32px;justify-content:center;width:32px;font-size:var(--u-fs-sm)}
.u-list-body{flex:1;min-width:0}
.u-list-title{color:var(--u-text-strong);font-size:var(--u-fs-sm);font-weight:510}
.u-list-meta{color:var(--u-faint);font:400 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat)}
.u-list-amount{flex:none;font:510 var(--u-fs-sm) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num);text-align:right}

/* Status badge — semantic colour + a dot, never decorative, never colour-only */
.u-badge{border-radius:var(--u-r-pill);display:inline-flex;align-items:center;font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);gap:5px;letter-spacing:.02em;padding:3px 9px;text-transform:uppercase;white-space:nowrap}
.u-badge::before{border-radius:99px;content:"";height:5px;width:5px}
.u-badge-success{background:var(--u-status-success-bg);color:var(--u-status-success-fg)}
.u-badge-success::before{background:var(--u-status-success-fg)}
.u-badge-warn{background:var(--u-status-warn-bg);color:var(--u-status-warn-fg)}
.u-badge-warn::before{background:var(--u-status-warn-fg)}
.u-badge-danger{background:var(--u-status-danger-bg);color:var(--u-status-danger-fg)}
.u-badge-danger::before{background:var(--u-status-danger-fg)}
.u-badge-info{background:var(--u-status-info-bg);color:var(--u-status-info-fg)}
.u-badge-info::before{background:var(--u-status-info-fg)}
.u-badge-idle{background:var(--u-status-idle-bg);color:var(--u-status-idle-fg)}
.u-badge-idle::before{background:var(--u-status-idle-fg)}
/* A badge with no modifier is a neutral label (referral code, tier name), and a
   badge with an unknown modifier must still land on a surface. The old kit left
   both with no background at all, so they rendered as bare uppercase text —
   which is why `u-badge-process` and the plain badges elsewhere read as unstyled. */
.u-badge-plain,.u-badge-process,.u-badge:not([class*="u-badge-"]){background:var(--u-status-idle-bg);color:var(--u-soft)}
.u-badge-plain::before,.u-badge-process::before,.u-badge:not([class*="u-badge-"])::before{background:var(--u-muted)}
.u-badge-process{background:var(--u-status-warn-bg);color:var(--u-status-warn-fg)}
.u-badge-process::before{background:var(--u-status-warn-fg)}

/* Leg tag — binary-tree left/right marker. Outline pill: it is a label, not a
   status, so it must not compete with the status badges for attention. */
.u-pill{border:1px solid var(--u-line-strong);border-radius:var(--u-r-pill);color:var(--u-muted);display:inline-flex;font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);letter-spacing:.02em;padding:2px 8px;text-transform:uppercase}
.u-pill-left{border-color:rgba(113,112,255,.4);color:var(--u-accent-hover)}
.u-pill-right{border-color:rgba(16,185,129,.35);color:var(--u-green)}
.u-pill-small-leg{background:var(--u-status-warn-bg);border-color:transparent;color:var(--u-status-warn-fg)}

/* ============================== 7. DENSE TABLE ============================== */
.u-toolbar{align-items:flex-end;display:flex;flex-wrap:wrap;gap:var(--u-sp-3);margin-bottom:var(--u-sp-4)}
.u-toolbar-search{flex:1 1 220px;min-width:0}
.u-toolbar .u-btn{flex:none}
.u-table-wrap{border:1px solid var(--u-line-soft);border-radius:var(--u-r-md);overflow-x:auto}
.u-table{border-collapse:collapse;font-size:var(--u-fs-sm);min-width:560px;width:100%}
/* Header: uppercase, 11px, muted, mono — visibly a different layer from data.
   Alignment is declared for th and td together so a number column can never
   drift out from under its own header. */
.u-table th{
  background:rgba(255,255,255,.02);border-bottom:1px solid var(--u-line-strong);
  color:var(--u-muted);font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);
  letter-spacing:.06em;padding:12px;text-align:start;text-transform:uppercase;white-space:nowrap;
}
/* `text-align:start` on th, not `left`: td has no text-align of its own, so it
   inherits `start`. Declaring both in the same keyword keeps the computed value
   on a header identical to the value on the cells under it — which is what
   actually guarantees a number column cannot drift from its own header. */
.u-table th.u-num-col,.u-table td.u-num-col{text-align:right}
.u-table td{border-top:1px solid var(--u-line-soft);color:var(--u-soft);padding:12px;vertical-align:middle;white-space:nowrap}
/* Cells never wrap mid-value: a date or a figure broken across two lines is
   unreadable and it silently makes every row taller. The wrap container already
   scrolls horizontally, and `min-width:560px` only means anything if the table
   is allowed to exceed it. Opt back in per cell with `.u-cell-wrap` when the
   column really is prose. Inner blocks (the name/refid stack) still break, since
   nowrap only affects inline content. */
.u-table td.u-cell-wrap{white-space:normal}
.u-table td strong,.u-table td b{color:var(--u-text-strong);font-weight:510}
.u-table tbody tr{transition:background var(--u-motion)}
.u-table tbody tr:hover td{background:rgba(255,255,255,.03)}
/* Zebra: opt-in. A table separates rows ONE way — stripes xor row borders. The
   modifier drops the border and keeps a 2% stripe, so nothing reads as both. */
.u-table-zebra tbody tr td{border-top:0}
.u-table-zebra tbody tr:nth-child(even) td{background:rgba(255,255,255,.02)}
.u-table-zebra tbody tr:hover td{background:rgba(255,255,255,.045)}
.u-table-foot{align-items:center;display:flex;flex-wrap:wrap;gap:var(--u-sp-3);justify-content:space-between;margin-top:var(--u-sp-4)}
.u-pager-info{color:var(--u-faint);font:400 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat)}

.u-pagination{align-items:center;display:flex;gap:4px}
.u-pagination button{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);color:var(--u-muted);cursor:pointer;font:510 var(--u-fs-xs) var(--u-mono);font-feature-settings:var(--u-feat);height:44px;min-width:44px;padding:0 8px}
@media(min-width:900px){ .u-pagination button{height:30px;min-width:30px} }
.u-pagination button:hover:not(:disabled){background:rgba(255,255,255,.05);color:var(--u-text-strong)}
.u-pagination button.is-active{background:var(--u-accent);border-color:var(--u-accent);color:var(--u-on-accent)}
.u-pagination button:disabled{cursor:not-allowed;opacity:.35}

/* DataTables (jQuery 1.x) ships markup but no stylesheet — map the handful of
   classes it emits onto this kit by hand, once, here, rather than letting every
   page grow a second pager that drifts from this one. */
.u-table-top,.u-table-foot{align-items:center;display:flex;flex-wrap:wrap;gap:var(--u-sp-3);justify-content:space-between}
.u-toolbar .dataTables_filter,.u-table-foot .dataTables_filter{color:var(--u-muted);flex:1 1 220px;font-size:var(--u-fs-sm)}
.u-table-foot .dataTables_length{color:var(--u-muted);font-size:var(--u-fs-sm)}
/* DataTables injects this <select> itself, so it matched no rule above and fell
   back to the UA default (37x19, platform font, light chrome on a dark page). */
.dataTables_length select,.u-table-foot .dataTables_length select{
  background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);
  color:var(--u-text);font:400 var(--u-fs-xs) var(--u-sans);font-feature-settings:var(--u-feat);min-height:44px;padding:0 8px;
}
.dataTables_length select:focus,.u-table-foot .dataTables_length select:focus{border-color:var(--u-accent-2);outline:none}
@media(min-width:900px){ .dataTables_length select,.u-table-foot .dataTables_length select{min-height:30px} }
.u-toolbar .dataTables_filter input,.u-table-foot .dataTables_filter input{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);color:var(--u-text);font:inherit;font-feature-settings:var(--u-feat);margin-left:var(--u-sp-2);min-height:30px;padding:0 12px}
.u-toolbar .dataTables_filter input:focus,.u-table-foot .dataTables_filter input:focus{border-color:var(--u-accent-2);outline:none}
.u-table-foot .dataTables_paginate{align-items:center;display:flex;gap:4px}
.u-table-foot .dataTables_paginate .paginate_button{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);color:var(--u-muted);cursor:pointer;font-size:var(--u-fs-xs);min-height:44px;min-width:44px;padding:5px 9px;text-align:center}
@media(min-width:900px){ .u-table-foot .dataTables_paginate .paginate_button{min-height:30px;min-width:30px} }
.u-table-foot .dataTables_paginate .paginate_button:hover:not(.disabled):not(.current){background:rgba(255,255,255,.05);color:var(--u-text-strong)}
.u-table-foot .dataTables_paginate .paginate_button.current{background:var(--u-accent);border-color:var(--u-accent);color:var(--u-on-accent)}
.u-table-foot .dataTables_paginate .paginate_button.disabled{cursor:not-allowed;opacity:.35}
.u-table-foot .dataTables_info{color:var(--u-faint);font:400 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat)}
.u-table-top{margin-bottom:var(--u-sp-3)}
.dataTables_wrapper{position:relative}
.dataTables_filter input{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);color:var(--u-text);font:400 var(--u-fs-sm) var(--u-sans);font-feature-settings:var(--u-feat);min-height:32px;padding:0 12px}
.dataTables_filter input:focus{border-color:var(--u-accent-2)}
.dataTables_processing{background:var(--u-panel-2);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);color:var(--u-soft);font:400 var(--u-fs-xs) var(--u-mono);font-feature-settings:var(--u-feat);left:50%;padding:8px 14px;position:absolute;top:0;transform:translateX(-50%)}
.dataTables_info{color:var(--u-faint);font:400 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);letter-spacing:.02em}
table.dataTable thead th{cursor:pointer;user-select:none}
/* Sorted column: the accent marks it, so sort state is not an invisible change */
table.dataTable thead th.sorting_asc{color:var(--u-text-strong)}
table.dataTable thead th.sorting_desc{color:var(--u-text-strong)}
table.dataTable thead th.sorting_asc::after{color:var(--u-accent-hover);content:" \2191"}
table.dataTable thead th.sorting_desc::after{color:var(--u-accent-hover);content:" \2193"}
/* These are text buttons ("Sebelumnya" / "Berikutnya"), not figures — the code
   font belongs on the page numbers, and this generic rule only carries the word
   buttons (the `n / m` label has its own mono span). */
.paginate_button{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);color:var(--u-muted);cursor:pointer;display:inline-flex;font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);height:44px;align-items:center;justify-content:center;min-width:44px;padding:0 8px}
@media(min-width:900px){ .paginate_button{height:30px;min-width:30px} }
.paginate_button:hover:not(.disabled){background:rgba(255,255,255,.05);color:var(--u-text-strong)}
.paginate_button.current{background:var(--u-accent);border-color:var(--u-accent);color:var(--u-on-accent)}
.paginate_button.disabled{cursor:not-allowed;opacity:.35}

/* Sticky last column (row actions) so the buttons of a wide table stay reachable. */
.u-table .u-col-actions{background:var(--u-bg);border-left:1px solid var(--u-line-soft);position:sticky;right:0}
.u-table tbody tr:hover .u-col-actions{background:#131415}
@media(min-width:900px){ .u-table-zebra tbody tr:nth-child(even) .u-col-actions{background:var(--u-panel)} }
/* Desktop only: this keeps the sticky actions cell opaque where the zebra tint
   would otherwise show through it. Below 900px the actions cell is static, so the
   rule just painted a near-black patch on every second card. */

/* Narrow screens: a wide table cannot shrink honestly and `overflow-x:auto`
   scrolls the row actions off-screen. Opted-in tables (`data-stack`) become one
   labelled card per row; every other table keeps the scroll it had, so this
   cannot regress a panel it was not added to. DataTables draws its own cells, so
   member-tables.js/admin-tables.js copy the header text onto each td[data-label]. */
@media(max-width:899px){
  /* Only the border comes off here: rows become cards, so a box around them is
     noise. `overflow-x` is NOT switched off — that turned off scrolling for every
     table that is not stacked too, and with cells now `white-space:nowrap` such a
     table simply pushed the page wider than the viewport instead of scrolling.
     A stacked table needs no scroller (it is width:100%), so leaving `auto` on
     costs nothing and keeps every other table intact. */
  .u-table-wrap{border:0;border-radius:0}
  .u-table[data-stack]{min-width:0}
  .u-table[data-stack] thead{display:none}
  .u-table[data-stack],.u-table[data-stack] tbody,.u-table[data-stack] tr,.u-table[data-stack] td{display:block;width:100%}
  .u-table[data-stack] tr{border-top:1px solid var(--u-line-soft);padding:10px 0}
  .u-table[data-stack] tr:first-child{border-top:0}
  .u-table[data-stack] td{border-top:0;display:flex;flex-wrap:wrap;gap:4px 10px;justify-content:space-between;padding:5px 12px;text-align:right;white-space:normal}
  .u-table[data-stack] td::before{color:var(--u-faint);content:attr(data-label);font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);letter-spacing:.06em;text-align:left;text-transform:uppercase}
  .u-table[data-stack] td:not([data-label]),.u-table[data-stack] td:empty{display:none}
  .u-table[data-stack] .u-col-actions{border-left:0;position:static}
  .u-table[data-stack] .u-col-actions::before{content:attr(data-label)}
}

/* ============================== 8. STATES (loading/empty/error/data) ============================== */
[data-state-view][hidden]{display:none}
.u-state{align-items:center;color:var(--u-muted);display:flex;flex-direction:column;gap:var(--u-sp-3);padding:var(--u-sp-9) var(--u-sp-4);text-align:center}
.u-state-icon{align-items:center;background:rgba(255,255,255,.04);border:1px solid var(--u-line-soft);border-radius:50%;color:var(--u-muted);display:flex;font-size:var(--u-fs-lg);height:44px;justify-content:center;width:44px}
.u-state-title{color:var(--u-text-strong);font-size:var(--u-fs-sm);font-weight:510}
.u-state-copy{font-size:var(--u-fs-xs);line-height:1.6;max-width:300px}
.u-state-loading .u-state-icon{animation:u-spin .8s linear infinite;background:none;border:2px solid var(--u-line);border-top-color:var(--u-accent-2)}
.u-state-error .u-state-icon{border-color:rgba(239,68,68,.3);color:var(--u-red)}
.u-state-empty .u-state-icon{background:none}
.u-skeleton-line{display:flex;flex-direction:column;gap:8px;width:min(280px,100%)}
.u-skeleton{animation:u-shimmer 1.4s infinite;background:linear-gradient(90deg,var(--u-panel-2) 25%,var(--u-panel-3) 50%,var(--u-panel-2) 75%);background-size:200% 100%;border-radius:var(--u-r-xs);display:block;height:9px}
.u-skeleton-line .u-skeleton:last-child{width:60%}
/* Table-shaped loading placeholder: the two-bar skeleton understates a panel
   whose real shape is a grid, so the panel visibly collapses while the feed is
   in flight. Four rows x four cells, full panel width, same shimmer. */
.u-skeleton-table{display:flex;flex-direction:column;gap:12px;width:100%}
.u-skeleton-table>div{display:grid;gap:var(--u-sp-4);grid-template-columns:1.3fr 1fr 1fr .8fr}
.u-skeleton-table .u-skeleton{height:12px}
@keyframes u-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
.u-skeleton-row{background:var(--u-panel-2);border-radius:var(--u-r-xs);height:14px}

/* ============================== 9. MODAL / DRAWER / TOAST / TOOLTIP ============================== */
.u-modal-backdrop{align-items:center;background:rgba(0,0,0,.85);display:flex;inset:0;justify-content:center;padding:var(--u-sp-4);position:fixed;z-index:60}
/* The author `display` above outranks the UA [hidden]{display:none}; without
   this line the modal paints on page load. */
.u-modal-backdrop[hidden]{display:none}
.u-modal{background:var(--u-panel-2);border:1px solid var(--u-line-strong);border-radius:var(--u-r-lg);box-shadow:var(--u-shadow-2);max-width:440px;padding:var(--u-sp-5);width:100%}
/* Height cap, not a per-page scroll region: a form modal taller than the
   viewport (the profile writer is ~1090px) otherwise opens with its header AND
   its save button off-screen, and a modal whose action cannot be reached is a
   dead end. Capping here means every page gets the behaviour, instead of each
   one hand-rolling a scroll wrapper like `.u-modal-scroll` does for the staking
   detail modal. dvh, not vh: mobile browser chrome shrinks the visual viewport
   and vh would still overflow. */
.u-modal{max-height:calc(100dvh - 2*var(--u-sp-4));overflow-y:auto}
.u-modal-head{align-items:flex-start;display:flex;justify-content:space-between;margin-bottom:var(--u-sp-4)}
.u-modal-title{color:var(--u-text-strong);font-size:var(--u-fs-md);font-weight:590}
.u-modal-foot{display:flex;gap:var(--u-sp-2);justify-content:flex-end;margin-top:var(--u-sp-5)}

.u-drawer{background:var(--u-panel-2);border-left:1px solid var(--u-line-strong);box-shadow:var(--u-shadow-2);height:100vh;max-width:92vw;overflow-y:auto;padding:var(--u-sp-5);position:fixed;right:0;top:0;transform:translateX(100%);transition:transform .2s ease;width:400px;z-index:60}
.u-drawer.is-open{transform:translateX(0)}
.u-drawer-scrim{background:rgba(0,0,0,.7);bottom:0;left:0;opacity:0;pointer-events:none;position:fixed;right:0;top:0;transition:opacity .2s ease;z-index:59}
.u-drawer-scrim.is-open{opacity:1;pointer-events:auto}
.u-drawer-close{background:rgba(255,255,255,.04);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);color:var(--u-muted);cursor:pointer;height:30px;width:30px}
.u-drawer-close:hover{color:var(--u-text-strong)}
.u-drawer-title{color:var(--u-text-strong);font-size:var(--u-fs-md);font-weight:590;margin:var(--u-sp-2) 0 var(--u-sp-4)}

.u-toast-stack{bottom:calc(var(--u-bottomnav-h) + var(--u-sp-3));display:flex;flex-direction:column;gap:8px;left:var(--u-sp-3);position:fixed;right:var(--u-sp-3);z-index:80}
@media(min-width:900px){ .u-toast-stack{bottom:var(--u-sp-4);left:auto;max-width:340px;right:var(--u-sp-4)} }
.u-toast{background:var(--u-panel-3);border:1px solid var(--u-line-strong);border-left:2px solid var(--u-accent-2);border-radius:var(--u-r-sm);box-shadow:var(--u-shadow-1);color:var(--u-text);font-size:var(--u-fs-sm);line-height:1.5;padding:11px 12px;transition:opacity .2s,transform .2s}
.u-toast-info{border-left-color:var(--u-accent-2)}
.u-toast-success{border-left-color:var(--u-green)}
.u-toast-error{border-left-color:var(--u-red)}
.u-toast.is-out{opacity:0;transform:translateY(6px)}

.u-tooltip{position:relative}
.u-tooltip [role="tooltip"]{background:var(--u-panel-3);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);bottom:calc(100% + 8px);color:var(--u-text-strong);font-size:var(--u-fs-2xs);left:50%;opacity:0;padding:5px 9px;pointer-events:none;position:absolute;transform:translateX(-50%);transition:opacity var(--u-motion);white-space:nowrap;z-index:40}
.u-tooltip:hover [role="tooltip"],.u-tooltip:focus-within [role="tooltip"]{opacity:1}

/* Skip link — offscreen until focused, then the first thing a keyboard user
   lands on. Deliberately NOT .u-hide-mobile: a hidden skip link is a dead control. */
.u-skip-link{background:var(--u-panel-3);border:1px solid var(--u-accent-2);border-radius:var(--u-r-sm);color:var(--u-text-strong);left:var(--u-sp-2);padding:var(--u-sp-2) var(--u-sp-3);position:absolute;top:-60px;z-index:90}
.u-skip-link:focus{top:var(--u-sp-2)}

/* ============================== 10. AVATAR / EMPTY STATE ============================== */
.u-avatar{align-items:center;background:rgba(255,255,255,.05);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);color:var(--u-soft);display:flex;flex:none;font:510 var(--u-fs-xs) var(--u-sans);font-feature-settings:var(--u-feat);justify-content:center;text-transform:uppercase}
.u-avatar-sm{height:26px;width:26px;font-size:var(--u-fs-2xs)}
.u-avatar-md{height:34px;width:34px}
.u-avatar-lg{height:48px;width:48px;font-size:var(--u-fs-md)}
.u-empty{align-items:center;display:flex;flex-direction:column;gap:var(--u-sp-3);padding:var(--u-sp-9) var(--u-sp-4);text-align:center}
.u-empty .u-state-icon{height:52px;width:52px;font-size:var(--u-fs-xl)}

/* Key/value preview — read-back block next to an edit form (fee config, profit
   share, bonus-limit simulation): label + right-aligned value, dashed rules, so
   an operator can diff "what will be saved" against the form above. */
.u-preview{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-md);padding:var(--u-sp-4)}
.u-preview-row{align-items:center;display:flex;font-size:var(--u-fs-sm);gap:var(--u-sp-3);justify-content:space-between;padding:var(--u-sp-2) 0}
.u-preview-row+.u-preview-row{border-top:1px dashed var(--u-line-strong)}
.u-preview-row b{color:var(--u-text-strong);font:510 var(--u-fs-base) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num);min-width:0;overflow-wrap:anywhere;text-align:right}
.u-preview-row span{color:var(--u-muted);flex:none}

/* Editable grid cell (bonus-limit tiers) — input flush inside td, focus visible */
.u-cell-input{background:transparent;border:1px solid transparent;border-radius:var(--u-r-sm);color:var(--u-text-strong);font:510 var(--u-fs-sm) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num);min-height:30px;padding:5px 8px;width:100%}
.u-cell-input:hover{border-color:var(--u-line-strong)}
.u-cell-input:focus{background:rgba(255,255,255,.04);border-color:var(--u-accent-2)}
.u-row-dirty td{background:rgba(245,158,11,.05)}
.u-row-dirty td:first-child{box-shadow:inset 2px 0 0 var(--u-gold)}

/* Web-vs-chain comparison (staking on-chain) */
.u-diff-ok{color:var(--u-green);font:510 var(--u-fs-xs) var(--u-mono);font-feature-settings:var(--u-feat)}
.u-diff-bad{color:var(--u-red);font:510 var(--u-fs-xs) var(--u-mono);font-feature-settings:var(--u-feat)}

/* ============================== 11. MONEY DISPLAY ============================== */
.u-money{font:510 var(--u-fs-base) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num)}
.u-money-sub{color:var(--u-faint);font:400 var(--u-fs-2xs) var(--u-mono);font-feature-settings:var(--u-feat)}
.u-money-hero{color:var(--u-text-strong);font:590 var(--u-fs-hero)/1.1 var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num);letter-spacing:-.04em}
.u-money-pos{color:var(--u-green)}
.u-money-neg{color:var(--u-red)}
/* Token qty with 8 decimals: allowed to wrap onto its own line, never allowed to
   widen the row or break a table/card at narrow widths. */
.u-token-qty{display:inline-flex;flex-wrap:wrap;font:400 var(--u-fs-base) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num);gap:2px;justify-content:flex-end;max-width:100%}
.u-token-qty small{color:var(--u-faint);font-size:var(--u-fs-2xs)}

/* ============================== 12. BINARY TREE — LENS ============================== */
/* The panel draws a LENS, not the whole tree: three rows out of one focus node —
   the focus on top, its two children, then their four children (2 forks, always).
   Clicking any card of the account's own subtree re-roots the lens on it, so the
   drawn hierarchy can never disagree with the breadcrumb.
   Symmetry is a construction, not a hope, and it is measured
   (docs/qa/redesign4-lens.py, 1440 + 390, `getBoundingClientRect`):
     cards per row:                   exactly 1 / 2 / 4, lens is three rows deep
     bus midpoint vs parent centre-X: 0.00px (worst fork, was 70.00px when a leg
     with two grandchildren sat next to a leg holding one slot and the elbows were
     welded to the parent card's bottom edge)
     stem vs child card centre-X:    0.00px, every drop, both widths
     card height per row:             200px each (the token, not min-height — a
     card that grew to fit its own text pushed every band below it out of step)
     empty seat vs member card:       150x200 both, dW=0 dH=0
     rail band:                       card-h + elbow, 0.00px drift on every row
   Since every fork has two legs and both legs hold the same shape (one card + one
   fork of two), their content widths are equal by construction — which is the one
   thing the old variable-width legs got wrong. */
.u-tree{align-items:center;display:flex;flex-direction:column;--u-tree-half:calc(var(--u-tree-elbow)/2)}
.u-tree-subtree{align-items:center;display:flex;flex:none;flex-direction:column;position:relative}
/* Leg filter (Semua/Kiri/Kanan): the leg that is filtered out leaves the flow
   entirely, and the remaining single leg drops its elbow — a one-child fork drawn
   with the two-child rules would hang half a bar in mid-air. */
.u-tree[data-leg="left"] > .u-tree-subtree > .u-tree-children > .u-tree-subtree[data-leg="right"],
.u-tree[data-leg="right"] > .u-tree-subtree > .u-tree-children > .u-tree-subtree[data-leg="left"]{display:none}
.u-tree[data-leg="left"] > .u-tree-subtree > .u-tree-children,
.u-tree[data-leg="right"] > .u-tree-subtree > .u-tree-children{padding-top:0}
.u-tree[data-leg="left"] > .u-tree-subtree > .u-tree-children > *::before,
.u-tree[data-leg="left"] > .u-tree-subtree > .u-tree-children > *::after,
.u-tree[data-leg="right"] > .u-tree-subtree > .u-tree-children > *::before,
.u-tree[data-leg="right"] > .u-tree-subtree > .u-tree-children > *::after,
.u-tree[data-leg="left"] > .u-tree-subtree > .u-tree-node::after,
.u-tree[data-leg="right"] > .u-tree-subtree > .u-tree-node::after{display:none}
.u-tree-children{display:flex;gap:var(--u-tree-fork);justify-content:center;padding-top:var(--u-tree-elbow);position:relative;--u-tree-fork:var(--u-tree-gap);--u-tree-half:calc(var(--u-tree-elbow)/2)}
/* The focus's own fork is the split between the two legs, so it opens wider than
   the forks inside one leg. */
.u-tree > .u-tree-subtree > .u-tree-children{--u-tree-fork:var(--u-tree-leg)}
/* Stem from the bus up to the child's top-centre — the growth point. */
.u-tree-children > *::before{background:var(--u-tree-line);content:"";height:var(--u-tree-half);left:50%;position:absolute;top:calc(-1*var(--u-tree-half));transform:translateX(-1px);width:2px}
/* Half the bus. Each leg draws its own half, from its own centre to the middle of
   the fork gap: with equal legs the two halves meet exactly under the parent. */
.u-tree-children > *::after{background:var(--u-tree-line);content:"";height:2px;position:absolute;top:calc(-1*var(--u-tree-half))}
.u-tree-children > *:first-child::after{left:50%;right:calc(var(--u-tree-fork)/-2)}
.u-tree-children > *:last-child::after{left:calc(var(--u-tree-fork)/-2);right:50%}
/* Stem from the parent's bottom edge down to the bus. Only a node that actually
   forks in this view gets one, so a leaf never drops a line into nothing. */
.u-tree-node:has(+ .u-tree-children)::after{background:var(--u-tree-line);bottom:calc(-1*var(--u-tree-half));content:"";height:var(--u-tree-half);left:50%;position:absolute;transform:translateX(-1px);width:2px}
.u-tree-canvas{overflow-x:auto;padding:var(--u-sp-2) 0 var(--u-sp-4);position:relative;scroll-behavior:smooth}
/* Row rail: Fokus / Anak / Cucu. One label per band, each label centred on the
   card it names — the column is card-h tall per label plus one elbow of gap, so it
   lands on the rows without measuring anything at runtime (verified 0.00px). */
.u-tree-gens{display:none;flex-direction:column;gap:var(--u-tree-elbow);left:0;position:absolute;top:var(--u-sp-2);width:78px}
@media(min-width:900px){ .u-tree-gens{display:flex} .u-tree-canvas{padding-left:78px} }
.u-tree-gens span{align-items:center;color:var(--u-faint);display:flex;font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);height:var(--u-tree-card-h);letter-spacing:.06em;text-transform:uppercase}
.u-tree-node{
  background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-md);
  display:flex;flex-direction:column;flex:none;gap:3px;height:var(--u-tree-card-h);overflow:hidden;
  padding:var(--u-sp-3);position:relative;text-align:left;width:var(--u-tree-card-w);
}
.u-tree-node[role="button"]{cursor:pointer;transition:background var(--u-motion),border-color var(--u-motion)}
.u-tree-node[role="button"]:hover{background:rgba(255,255,255,.05);border-color:rgba(255,255,255,.18)}
.u-tree-node[role="button"]:focus-visible{outline:2px solid var(--u-accent-2);outline-offset:2px}
/* The card at the top of the lens: the one treatment that says "this is the point
   the page is centred on". Accent ring + an explicit label, never colour alone. */
.u-tree-node.is-focus,.u-tree-node.is-owner{border-color:var(--u-accent-2);box-shadow:inset 0 0 0 1px var(--u-accent-2)}
.u-tree-node.is-focus{background:rgba(94,106,210,.08)}
/* Kaki tersedikit: an amber OUTLINE, not a fill — the node is still an ordinary
   active member, and amber is reserved for "needs attention", so it may only
   tint the edge. */
.u-tree-node.is-small-leg{border-color:rgba(245,158,11,.5);box-shadow:inset 0 0 0 1px rgba(245,158,11,.22)}
.u-tree-node .u-badge{position:absolute;right:8px;top:8px}
/* An empty seat keeps the member card's box — same padding, same width, same
   bottom row — so the four cards of a row stay on one grid. */
.u-tree-node.is-empty{border-style:dashed;color:var(--u-faint)}
.u-tree-node.is-empty .u-avatar{background:none;border-style:dashed;color:var(--u-faint)}
.u-tree-node.is-empty .u-tree-node-name{color:var(--u-faint);font-weight:400}
/* The auto-placement seat is a POSITION, not a warning: info hue, not amber —
   amber in this lens means exactly one thing (the least-footed leg). */
.u-tree-node.is-next{border-color:var(--u-info)}
/* A placeable empty seat is the registration entry point: it keeps the dashed
   box but reads as actionable and names the member it will sit under. */
.u-tree-node.is-empty.is-registrable{border-color:var(--u-accent-2);color:var(--u-soft)}
.u-tree-node.is-empty.is-registrable:hover{background:rgba(94,106,210,.10)}
/* A seat under an empty parent: same box, still clickable, but it explains it is
   locked instead of opening a form for a slot that cannot be filled yet. */
.u-tree-node.is-empty.is-blocked{color:var(--u-faint)}
.u-tree-node.is-empty.is-blocked:hover{background:rgba(255,255,255,.04)}
.u-tree-node-cta{
  align-self:flex-start;color:var(--u-accent-hover);font:510 var(--u-fs-2xs) var(--u-sans);
  font-feature-settings:var(--u-feat);margin-top:auto;
}
.u-tree-node-cta.is-locked{color:var(--u-muted)}
/* Per-leg omset on a member card: kiri + kanan, each labelled. */
.u-tree-node-legs{display:flex;gap:10px;flex-wrap:wrap;align-self:flex-start}
.u-tree-node-name{color:var(--u-text-strong);font:510 var(--u-fs-xs) var(--u-sans);font-feature-settings:var(--u-feat);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.u-tree-node-ref{color:var(--u-muted);font:400 var(--u-fs-2xs) var(--u-mono);font-feature-settings:var(--u-feat);letter-spacing:.02em}
.u-tree-node-money{color:var(--u-soft);font:510 var(--u-fs-2xs) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num)}
/* A node's caption line ("8 member", "di bawah Rahmat · kiri-1") is a caption, not
   a value, so it takes the sans like every other caption in the kit. */
.u-tree-node-meta{color:var(--u-faint);font:400 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);line-height:1.45}
.u-tree-node .u-pill{align-self:flex-start;margin-top:auto}
.u-tree-focus-tag{
  align-self:flex-start;background:var(--u-accent-bg);border-radius:var(--u-r-pill);
  color:var(--u-accent-hover);font:510 var(--u-fs-2xs) var(--u-sans);font-feature-settings:var(--u-feat);
  letter-spacing:.04em;margin-top:auto;padding:2px 8px;text-transform:uppercase;
}
/* Exactly one drill marker per card — chevron when it can be opened, leaf when it
   cannot. Both are decoration: the card itself is the button, so neither is a
   nested control and the keyboard never lands on the same node twice. */
.u-tree-toggle,.u-tree-leaf{
  align-items:center;border:1px solid var(--u-line-strong);border-radius:var(--u-r-xs);
  bottom:6px;color:var(--u-muted);display:flex;height:22px;justify-content:center;
  position:absolute;right:6px;width:22px;
}
.u-tree-leaf{background:none;border-color:transparent}
.u-tree-leaf i{font-size:var(--u-fs-2xs)}
.u-tree-toggle i{font-size:var(--u-fs-2xs)}
.u-tree-node[role="button"]:hover .u-tree-toggle{color:var(--u-text-strong)}
.u-tree-crumb{align-items:center;color:var(--u-muted);display:flex;flex-wrap:wrap;font-size:var(--u-fs-xs);gap:6px}
.u-tree-crumb button{background:none;border:0;color:var(--u-accent-hover);cursor:pointer;font:inherit;padding:0}
.u-tree-crumb button:hover{text-decoration:underline}
.u-tree-crumb .sep{color:var(--u-faint)}
.u-tree-crumb .current{color:var(--u-text-strong)}
/* Key for the lens. Lives in the kit, not on the page: the preview and the real
   panel draw the same cards, so they must explain them with the same swatches.
   An entry is painted only while the lens actually shows that state (the renderer
   sets `hidden`), so the key never lists a swatch no card on screen carries. */
.u-legend{align-items:center;display:flex;flex-wrap:wrap;gap:var(--u-sp-4);margin-top:var(--u-sp-5)}
.u-legend-item{align-items:center;color:var(--u-muted);display:flex;font-size:var(--u-fs-xs);gap:6px}
/* `display:flex` above would otherwise beat the UA `[hidden]{display:none}`. */
.u-legend-item[hidden]{display:none}
.u-legend-swatch{border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);height:14px;width:22px}
.u-legend-swatch.is-focus{border-color:var(--u-accent-2)}
.u-legend-swatch.is-empty{border-style:dashed}
/* Amber is the least-footed leg and nothing else. The auto-placement seat is a
   POSITION, not a warning, so it takes the info hue — otherwise one amber border
   meant "behind on volume" and "next member lands here" at the same time. */
.u-legend-swatch.is-small{border-color:rgba(245,158,11,.5)}
.u-legend-swatch.is-next{border-color:var(--u-info);border-style:dashed}
.u-tree-node .u-avatar{margin-bottom:2px}
/* Sticky walk-back bar above the lens: the breadcrumb and "Kaki pertama" stay
   reachable while the tree is scrolled sideways on a phone. */
.u-lens-bar{
  align-items:center;background:var(--u-bg);border-bottom:1px solid var(--u-line-soft);
  display:flex;flex-wrap:wrap;gap:var(--u-sp-2) var(--u-sp-3);justify-content:space-between;
  margin-bottom:var(--u-sp-4);padding-bottom:var(--u-sp-3);position:sticky;top:0;z-index:5;
}
@media(max-width:899px){ .u-lens-bar{top:var(--u-topbar-h)} }
@media(max-width:899px){
  /* A 4-wide lens does not fit a phone: keep the cards a size you can read and
     let the canvas pan (the kit allows horizontal scroll on this one element).
     Width shrinks; the HEIGHT stays the desktop token, because the fixed height
     is what keeps the generation bands aligned on both widths. */
  .u-tree{--u-tree-card-w:132px;--u-tree-elbow:22px;--u-tree-leg:32px}
  .u-tree-canvas{overflow-x:auto}
  .u-tree-toggle,.u-tree-leaf{height:26px;width:26px}
}

/* ============================== 13. UTILITIES ============================== */
.u-flex{display:flex} .u-flex-col{display:flex;flex-direction:column}
.u-gap-1{gap:var(--u-sp-1)} .u-gap-2{gap:var(--u-sp-2)} .u-gap-3{gap:var(--u-sp-3)} .u-gap-4{gap:var(--u-sp-4)}
.u-items-center{align-items:center} .u-items-end{align-items:flex-end} .u-justify-between{justify-content:space-between}
.u-w-full{width:100%} .u-text-right{text-align:right} .u-text-muted{color:var(--u-muted)}
.u-mt-1{margin-top:var(--u-sp-1)} .u-mt-2{margin-top:var(--u-sp-2)} .u-mt-3{margin-top:var(--u-sp-3)} .u-mt-4{margin-top:var(--u-sp-4)}
.u-mb-1{margin-bottom:var(--u-sp-1)} .u-mb-2{margin-bottom:var(--u-sp-2)} .u-mb-3{margin-bottom:var(--u-sp-3)} .u-mb-4{margin-bottom:var(--u-sp-4)} .u-mb-5{margin-bottom:var(--u-sp-5)} .u-mb-6{margin-bottom:var(--u-sp-6)}
/* Display utilities carry !important because the elements that need them also
   carry an inline `display:flex` (the topbar network pill). A utility that an
   inline style can override is not a utility: at 390px the pill stayed visible,
   pushed the header past the viewport and gave the page 180px of horizontal
   scroll. */
.u-hide-mobile{display:none!important} .u-hide-desktop{display:block!important}
@media(min-width:900px){ .u-hide-mobile{display:block!important} .u-hide-desktop{display:none!important} }
/* Two-column split: content + aside. Collapses to one column below 1100px so the
   aside never squeezes the table it sits next to. */
.u-split{display:grid;gap:var(--u-sp-6);grid-template-columns:minmax(0,1fr)}
/* Two columns of independently-sized panels rarely end on the same line; measured
   87px of dead space under the dashboard's left column. Letting the last panel in
   the main column absorb the slack makes both columns finish together at any
   viewport, instead of filling the gap with a card that exists only to pad. */
.u-split > div{display:flex;flex-direction:column}
.u-split > div > *:last-child{flex:1 1 auto}
/* `minmax(0,1fr)`, not `1fr`: a bare 1fr is `minmax(auto,1fr)`, so the track
   refuses to shrink below its content's min-content width. The main column holds
   a table whose min-content is ~673px, which pushed the whole grid — and the
   page — 85px wider than the viewport at 900px. Zero as the floor lets the
   .u-table-wrap scroll instead, which is what its overflow-x:auto is for. */
@media(min-width:1100px){ .u-split{grid-template-columns:minmax(0,1fr) var(--u-rail)} }
/* 1.85fr, not 1.6fr: the aside's min-content is 0 so it yields space freely, but
   the main column's 5-column table needs 673px and the panel padding takes 42,
   i.e. a 715px track. At 1.6fr the track capped at 684 and the desktop table sat
   in a permanent 31px horizontal scrollbar. 1.85fr gives 722. */

/* ============================== 14. RESPONSIVE NOTES ============================== */
/* Breakpoints: 520 (tight form rows), 640 (stat grid 2col), 900 (sidebar vs
   bottom-nav — the only structural breakpoint), 1100 (stat grid 4col / split).
   Base rules always target 390px first; every @media below adds room, never
   removes function. See components.md for the full class/rule table. */


/* ============================== 15. REDESIGN-2 ADDITIONS ============================== */

/* Money cell: the unit is metadata, the figure is the data. Dimming the suffix
   stops "1.500,0000 UAN" from reading as one long number, and tabular figures
   keep the digits on the same column edge across rows. */
.u-money small{color:var(--u-faint);font-size:var(--u-fs-2xs);font-weight:400;margin-left:4px}
.u-money-cur{color:var(--u-faint);font-weight:400}
/* A unit inside a stat figure stays on the same baseline as the digits. The
   generic `.u-stat small` rule stacks it under the value, which is right for a
   caption but wrong for a unit. */
.u-stat-value small,.u-stat strong small{display:inline;margin-left:4px;margin-top:0}
/* Direction: an arrow plus a hue, never a word badge. Two badges per row (one
   for "arah", one for status) made every row shout twice. The arrow is not
   colour-only — it carries the direction on its own. */
.u-dir{font:510 var(--u-fs-sm) var(--u-mono);font-feature-settings:var(--u-feat)}
.u-dir-in{color:var(--u-dir-in)}
.u-dir-out{color:var(--u-dir-out)}
.u-dir-label{color:var(--u-faint);font-size:var(--u-fs-2xs);font-weight:400;margin-left:4px}

/* Delta with its baseline. A bare "12%" is a number with no referent; the
   baseline rides in the same line at micro size. */
.u-delta{display:inline-flex;align-items:baseline;gap:6px}
.u-delta-baseline{color:var(--u-faint);font-size:var(--u-fs-2xs);font-weight:400}

/* Dashboard grid with a fixed right rail. The hero band and the split below it
   both resolve to this template, so the main column ends on one x and the right
   edge stops being ragged. */
.u-rail-grid{display:grid;gap:var(--u-sp-6);grid-template-columns:minmax(0,1fr)}
@media(min-width:1100px){ .u-rail-grid{grid-template-columns:minmax(0,1fr) var(--u-rail)} }
.u-rail-grid>.u-rail{min-width:0}
@media(min-width:1100px){ .u-rail-grid>.u-rail{grid-column:2;grid-row:1/-1} }

/* Chart: hairline gridlines + a value axis, so a bar height is a quantity and
   not decoration. The plot area owns the rules; the labels sit outside it. */
.u-chart{display:grid;gap:var(--u-sp-2);grid-template-columns:auto minmax(0,1fr)}
.u-chart-y{align-items:flex-end;color:var(--u-faint);display:flex;flex-direction:column;
  font:400 var(--u-fs-2xs) var(--u-mono);font-feature-settings:var(--u-feat);
  justify-content:space-between;padding-bottom:var(--u-sp-5);text-align:right}
.u-chart-plot{
  background-image:linear-gradient(to top,var(--u-line-soft) 1px,transparent 1px);
  background-size:100% 25%;background-position:0 100%;
  border-bottom:1px solid var(--u-line-strong);position:relative;
}
.u-chart-unit{color:var(--u-faint);font:400 var(--u-fs-2xs) var(--u-sans);grid-column:1/-1;text-align:right}

/* Secondary line inside a table cell (a member's email under their name, a ref
   code under a label). It is metadata, so it drops to the micro tier and stays
   on the token — the pages were each writing `font:10px var(--u-mono)` inline,
   which is both off-ladder and mono-for-everything. */
.u-cell-sub{color:var(--u-muted);display:block;font-size:var(--u-fs-2xs);line-height:1.4;margin-top:2px}
/* A chain address / tx hash in a cell: an identifier, so monospace is right —
   but it must be allowed to break, or a 42-char hash widens the whole column. */
.u-cell-mono{font:400 var(--u-fs-2xs) var(--u-mono);font-feature-settings:var(--u-feat);word-break:break-all}

/* Direction marker for a ledger row: an arrow plus a hue. Replaces a text badge
   ("Masuk"/"Keluar") that duplicated a status badge on the same row and made
   every row shout twice. The arrow carries the direction without colour, so it
   survives greyscale and colour-blind readers. */
.u-dir-arrow{font:510 var(--u-fs-sm) var(--u-mono);font-feature-settings:var(--u-feat)}


/* --- Bespoke Modern Sidebar Component Styles --- */
.u-sidebar .u-nav-item {
  color: var(--u-soft);
  position: relative;
}
.u-sidebar .u-nav-item i {
  color: var(--u-muted);
  transition: color 0.15s ease;
}
.u-sidebar .u-nav-item:hover {
  background: rgba(255, 255, 255, 0.04);
  color: #ffffff;
}
.u-sidebar .u-nav-item:hover i {
  color: #818cf8;
}
.u-sidebar .u-nav-item.is-active {
  background: linear-gradient(90deg, rgba(99, 102, 241, 0.15) 0%, rgba(99, 102, 241, 0.05) 100%);
  color: #ffffff;
  font-weight: 600;
  border-left: 3px solid #6366f1;
}
.u-sidebar .u-nav-item.is-active i {
  color: #818cf8;
}

/* ============================== 16. MODUL BESPOKE PER-HALAMAN ============================== */
/* Tiap modul di bawah dipakai oleh SATU halaman saja, dan itu disengaja: kualitas
   kriya (craft) yang disamakan, bukan bentuk layoutnya. Semua warna/ukuran tetap
   dari token di §1 — tidak ada px/hex lepas di blok ini.
   Bahasa visualnya sama (hairline, radius token, mono untuk angka); yang berbeda
   hanya susunan dan bobotnya, sesuai use-case halaman. */

/* -- 16.1 Kartu dompet (wallet.html): tiga mata uang berdampingan, satu rail aksen
   per jenis agar tiga kartu tidak terbaca sebagai satu blok abu-abu. -- */
.u-wallet-cards{display:grid;gap:var(--u-sp-4);grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){ .u-wallet-cards{grid-template-columns:repeat(3,minmax(0,1fr))} }
.u-wallet-card{
  background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-lg);
  display:flex;flex-direction:column;gap:var(--u-sp-4);overflow:hidden;padding:var(--u-sp-5);
  position:relative;transition:border-color var(--u-motion),background var(--u-motion);
}
.u-wallet-card:hover{background:rgba(255,255,255,.035);border-color:rgba(255,255,255,.14)}
/* Rail kiri: 3px, satu warna per dompet. Bukan dekorasi — kartu USD/UAN/Temp
   berbeda hak pakai (tarik / staking / restack-or-tarik), jadi identitasnya harus
   kebaca sebelum angkanya. */
.u-wallet-card::before{background:var(--u-w-line,var(--u-accent));content:"";inset:0 auto 0 0;position:absolute;width:3px}
.u-wallet-card.is-usd{--u-w-line:var(--u-green)}
.u-wallet-card.is-uan{--u-w-line:var(--u-accent-2)}
.u-wallet-card.is-temp{--u-w-line:var(--u-gold)}
.u-wallet-head{align-items:center;display:flex;gap:var(--u-sp-3)}
.u-wallet-icon{align-items:center;background:rgba(255,255,255,.04);border:1px solid var(--u-line-strong);border-radius:var(--u-r-sm);color:var(--u-soft);display:flex;flex:none;height:36px;justify-content:center;width:36px}
.u-wallet-name{color:var(--u-text-strong);font-size:var(--u-fs-sm);font-weight:590}
.u-wallet-code{color:var(--u-faint);font:510 var(--u-fs-2xs) var(--u-mono);font-feature-settings:var(--u-feat);letter-spacing:.06em;text-transform:uppercase}
.u-wallet-bal{color:var(--u-text-strong);font:590 var(--u-fs-2xl)/1.1 var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num);letter-spacing:-.03em}
.u-wallet-bal small{color:var(--u-faint);font-size:var(--u-fs-xs);font-weight:400;margin-left:5px}
.u-wallet-note{color:var(--u-muted);font-size:var(--u-fs-2xs);line-height:1.5}
.u-wallet-actions{display:flex;flex-wrap:wrap;gap:var(--u-sp-2);margin-top:auto}
/* Dompet tanpa hak tarik tidak boleh menawarkan tombol tarik: kunci tombolnya ke
   catatan, bukan disembunyikan tanpa alasan. */
.u-wallet-actions .u-btn{flex:1 1 auto}
@media(min-width:900px){ .u-wallet-actions .u-btn{min-width:0;padding:0 10px} }

/* -- 16.2 Chip filter kategori (wallet ledger). Radio asli, jadi keyboard dan
   pembaca layar dapat perilaku pilihan yang benar tanpa satu baris JS. -- */
.u-chips{display:flex;flex-wrap:wrap;gap:var(--u-sp-2)}
.u-chip{
  align-items:center;background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);
  border-radius:var(--u-r-pill);color:var(--u-muted);cursor:pointer;display:inline-flex;
  font:510 var(--u-fs-xs) var(--u-sans);font-feature-settings:var(--u-feat);gap:6px;min-height:44px;padding:0 14px;
}
@media(min-width:900px){ .u-chip{min-height:30px} }
.u-chip:hover{background:rgba(255,255,255,.05);color:var(--u-text-strong)}
.u-chip input{position:absolute;height:1px;width:1px;opacity:0}
.u-chip:has(input:checked){background:var(--u-accent-bg);border-color:rgba(113,112,255,.45);color:var(--u-text-strong)}
.u-chip:has(input:focus-visible){outline:2px solid var(--u-accent-2);outline-offset:2px}
.u-chip .u-count{background:rgba(255,255,255,.06);border-radius:99px;color:var(--u-muted);font-size:var(--u-fs-2xs);padding:1px 6px}

/* -- 16.3 Bar progres (durasi posisi staking, progres batch UAN Temp). -- */
.u-progress{background:rgba(255,255,255,.06);border-radius:var(--u-r-pill);height:6px;overflow:hidden}
.u-progress-fill{background:linear-gradient(90deg,var(--u-accent),var(--u-accent-2));display:block;height:100%}
.u-progress-fill.is-out{background:linear-gradient(90deg,var(--u-accent-2),var(--u-green))}
.u-progress-legend{align-items:center;display:flex;font-size:var(--u-fs-2xs);gap:var(--u-sp-2);justify-content:space-between;margin-top:6px}
.u-progress-legend span{color:var(--u-muted)}

/* -- 16.4 Katalog paket staking: tiga kolom, satu di antaranya ditandai. -- */
.u-catalog{display:grid;gap:var(--u-sp-4);grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){ .u-catalog{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))} }
.u-package{
  background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-lg);
  display:flex;flex-direction:column;gap:var(--u-sp-3);padding:var(--u-sp-5);position:relative;
}
.u-package.is-featured{background:linear-gradient(180deg,rgba(94,106,210,.10),rgba(255,255,255,.02));border-color:rgba(113,112,255,.45)}
.u-package-head{align-items:center;display:flex;gap:var(--u-sp-2);justify-content:space-between}
.u-package-name{color:var(--u-text-strong);font-size:var(--u-fs-lg);font-weight:590;letter-spacing:-.02em}
.u-package-apr{color:var(--u-text-strong);font:590 var(--u-fs-xl) var(--u-mono);font-feature-settings:var(--u-feat);letter-spacing:-.03em}
.u-package-apr small{color:var(--u-faint);font-size:var(--u-fs-2xs);font-weight:400;margin-left:4px}
.u-package-rows{border-top:1px solid var(--u-line-soft);display:flex;flex-direction:column;margin-top:var(--u-sp-1)}
.u-package-row{align-items:center;border-bottom:1px solid var(--u-line-soft);display:flex;font-size:var(--u-fs-xs);gap:var(--u-sp-3);justify-content:space-between;padding:9px 0}
.u-package-row span{color:var(--u-muted)}
.u-package-row b{color:var(--u-text-strong);font:510 var(--u-fs-xs) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num)}
.u-package .u-btn{margin-top:auto}

/* -- 16.5 Kartu posisi aktif (staking): kartu besar, bukan baris tabel, karena
   yang dibaca member adalah SISA DURASI, bukan kolom tanggal. -- */
.u-positions{display:grid;gap:var(--u-sp-4);grid-template-columns:minmax(0,1fr)}
@media(min-width:1100px){ .u-positions{grid-template-columns:repeat(2,minmax(0,1fr))} }
.u-position{background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-lg);display:flex;flex-direction:column;gap:var(--u-sp-3);padding:var(--u-sp-5)}
.u-position-head{align-items:flex-start;display:flex;gap:var(--u-sp-3);justify-content:space-between}
.u-position-figs{display:grid;gap:var(--u-sp-3);grid-template-columns:repeat(2,minmax(0,1fr))}
.u-position-fig{display:flex;flex-direction:column;gap:2px}
.u-position-fig span{color:var(--u-faint);font-size:var(--u-fs-2xs)}
.u-position-fig b{color:var(--u-text-strong);font:510 var(--u-fs-sm) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num)}
.u-position-foot{align-items:center;display:flex;gap:var(--u-sp-2);justify-content:flex-end}

/* -- 16.6 Kartu pilihan aksi (UAN Temp): dua jalur uang keluar, fee ditulis
   langsung di pilihannya supaya pilihan diambil sadar biaya. -- */
.u-choice-grid{display:grid;gap:var(--u-sp-4);grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){ .u-choice-grid{grid-template-columns:repeat(2,minmax(0,1fr))} }
.u-choice{
  background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-lg);
  display:flex;flex-direction:column;gap:var(--u-sp-2);padding:var(--u-sp-5);position:relative;
}
.u-choice::before{background:var(--u-c-line,var(--u-accent));content:"";inset:0 auto 0 0;position:absolute;width:3px}
.u-choice.is-restack{--u-c-line:var(--u-green)}
.u-choice.is-out{--u-c-line:var(--u-gold)}
.u-choice-fee{color:var(--u-text-strong);font:590 var(--u-fs-xl) var(--u-mono);font-feature-settings:var(--u-feat);letter-spacing:-.03em}
.u-choice-fee small{color:var(--u-faint);font-size:var(--u-fs-2xs);font-weight:400;margin-left:4px}

/* -- 16.7 Progres batch (UAN Temp): langkah mendatar yang menyala sesuai state
   nyata record, bukan animasi dekoratif. -- */
.u-batch{display:grid;gap:var(--u-sp-3);grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){ .u-batch{grid-template-columns:repeat(3,minmax(0,1fr))} }
.u-batch-step{background:rgba(255,255,255,.02);border:1px solid var(--u-line-soft);border-radius:var(--u-r-md);display:flex;flex-direction:column;gap:4px;padding:var(--u-sp-3) var(--u-sp-4)}
.u-batch-step.is-done{border-color:rgba(16,185,129,.35)}
.u-batch-step.is-current{background:var(--u-accent-bg);border-color:rgba(113,112,255,.45)}
.u-batch-step b{color:var(--u-text-strong);font-size:var(--u-fs-xs);font-weight:590}
.u-batch-step span{color:var(--u-muted);font-size:var(--u-fs-2xs);line-height:1.5}
.u-batch-step em{color:var(--u-text-strong);font:510 var(--u-fs-sm) var(--u-mono);font-style:normal;font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num)}

/* -- 16.8 Panel info sisi kanan (tarik UAN): daftar fakta + panduan, tinggi
   isi sendiri sehingga tidak pernah menyamai tinggi form di sebelahnya. -- */
.u-fact-list{display:flex;flex-direction:column}
.u-fact{align-items:baseline;border-top:1px solid var(--u-line-soft);display:flex;font-size:var(--u-fs-xs);gap:var(--u-sp-3);justify-content:space-between;padding:10px 0}
.u-fact:first-child{border-top:0}
.u-fact span{color:var(--u-muted)}
.u-fact b{color:var(--u-text-strong);font:510 var(--u-fs-xs) var(--u-mono);font-feature-settings:var(--u-feat);font-variant-numeric:var(--u-num);text-align:right}
.u-guide{display:flex;flex-direction:column;gap:var(--u-sp-3);list-style:none;margin-top:var(--u-sp-2);padding:0}
.u-guide li{align-items:flex-start;color:var(--u-muted);display:flex;font-size:var(--u-fs-xs);gap:var(--u-sp-2);line-height:1.55}
.u-guide li i{color:var(--u-accent-hover);font-size:var(--u-fs-2xs);margin-top:4px}

/* -- 16.9 Alamat wallet tersimpan (profil): baris salin, mono, satu per jaringan. -- */
.u-addr{align-items:center;background:rgba(255,255,255,.02);border:1px solid var(--u-line-strong);border-radius:var(--u-r-md);display:flex;gap:var(--u-sp-3);justify-content:space-between;padding:var(--u-sp-3) var(--u-sp-4)}
.u-addr-body{min-width:0}
.u-addr-body b{color:var(--u-text-strong);display:block;font:510 var(--u-fs-2xs) var(--u-mono);font-feature-settings:var(--u-feat);overflow-wrap:anywhere}
.u-addr-body span{color:var(--u-faint);font-size:var(--u-fs-2xs)}
.u-addr-list{display:flex;flex-direction:column;gap:var(--u-sp-3)}

/* -- 16.10 Kotak unggah berkas (profil member + admin): satu komponen untuk
   foto profil dan berkas identitas — klik atau seret-lepas, pratinjau langsung,
   tombol Ganti/Hapus pilihan lokal. Markup di uant/partials/upload.blade.php,
   perilaku di UShell.mountUploads(). -- */
.u-upload{display:grid;gap:var(--u-sp-2);grid-template-columns:minmax(0,1fr);min-width:0}
/* Input berkas tetap di DOM (nama field & tab stop), hanya dipangkas
   secara visual; cincin fokusnya digambar di kotak lewat sibling selector. */
.u-upload-input{clip:rect(0 0 0 0);clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}
.u-upload-input:focus-visible+.u-upload-zone{box-shadow:var(--u-focus)}
.u-upload-zone{align-items:center;background:rgba(255,255,255,.02);border:1px dashed var(--u-line-strong,#23445f);border-radius:var(--u-r-md);cursor:pointer;display:flex;gap:var(--u-sp-3);min-height:88px;padding:var(--u-sp-3);transition:border-color .15s,background .15s;width:100%}
.u-upload-zone:hover{background:rgba(255,255,255,.04);border-color:var(--u-accent)}
.u-upload.is-drag .u-upload-zone{background:var(--u-accent-bg);border-color:var(--u-accent)}
.u-upload.is-invalid .u-upload-zone{border-color:var(--u-red);border-style:solid}
.u-upload-frame{align-items:center;background:#071523;border:1px solid var(--u-line-soft,#1b3a52);border-radius:var(--u-r-sm);display:flex;flex:none;height:72px;justify-content:center;overflow:hidden;width:72px}
.u-upload-img{height:100%;object-fit:cover;width:100%}
.u-upload-icon{color:var(--u-faint);font-size:20px}
.u-upload-copy{display:flex;flex-direction:column;gap:2px;min-width:0}
.u-upload-copy b{color:var(--u-text-strong);font:510 var(--u-fs-base) var(--u-sans);font-feature-settings:var(--u-feat);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.u-upload-copy span{color:var(--u-muted);font-size:var(--u-fs-sm)}
.u-upload-drop{color:var(--u-faint)}
/* Dokumen identitas: bingkai lebih lebar (rasio kartu), teks tetap satu baris
   berkat ellipsis — aman di 390px tanpa overflow. */
.u-upload-wide .u-upload-frame{height:66px;width:100px}
.u-upload-actions{align-items:center;display:flex;flex-wrap:wrap;gap:var(--u-sp-2)}
.u-upload-err{color:var(--u-red);font-size:var(--u-fs-sm);min-height:13px}

/* -- 16.11 Kartu identitas + avatar (profil member & admin): foto besar dari
   berkas tersimpan, bukan teks "Foto saat ini: nama.jpg". -- */
.u-profile-id{align-items:center;background:rgba(255,255,255,.02);border:1px solid var(--u-line-soft,#1b3a52);border-radius:var(--u-r-md);display:flex;flex-wrap:wrap;gap:var(--u-sp-4);padding:var(--u-sp-4)}
.u-profile-photo{border-radius:50%;height:72px;overflow:hidden;padding:0;width:72px}
.u-profile-photo img{height:100%;object-fit:cover;width:100%}
.u-profile-id-body{display:flex;flex-direction:column;gap:2px;min-width:0}
.u-profile-id-body b{color:var(--u-text-strong);font:590 var(--u-fs-md) var(--u-sans);font-feature-settings:var(--u-feat);overflow-wrap:anywhere}
.u-profile-id-body span{color:var(--u-muted);font-size:var(--u-fs-sm);overflow-wrap:anywhere}
.u-profile-id .u-btn{margin-left:auto}
/* Layar sempit: tombol aksi turun baris penuh — target ketuk besar, bukan
   tombol terapung di kanan dengan ruang kosong menggantung. */
@media(max-width:520px){ .u-profile-id .u-btn{flex:1 1 100%;margin-left:0} }
