/* Nimbopanel panel — design system (ui-principles.md).
   cPanel-familiar structure, superior surface. Two first-class themes.
   Components reference role tokens only — never raw hex. */

/* ---------- Self-hosted fonts (Stratus). Google Fonts CDN is blocked by the
   panel CSP (style-src/font-src 'self'), so the woff2 subsets are bundled under
   /static/fonts and served same-origin. Display=Bricolage Grotesque,
   Body=IBM Plex Sans, Mono=IBM Plex Mono. Latin subset; font-display:swap. --- */
@font-face{font-family:'Bricolage Grotesque';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/bricolage-700.woff2') format('woff2')}
@font-face{font-family:'Bricolage Grotesque';font-style:normal;font-weight:800;font-display:swap;src:url('fonts/bricolage-800.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/plexsans-400.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url('fonts/plexsans-500.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:600;font-display:swap;src:url('fonts/plexsans-600.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:700;font-display:swap;src:url('fonts/plexsans-700.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url('fonts/plexmono-400.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url('fonts/plexmono-500.woff2') format('woff2')}

/* ---------- Tokens — "Stratus" identity (teal atmosferik + sinyal apricot).
   Sengaja keluar dari biru/indigo: neutral ber-undertone hijau-teal ("ink"),
   bukan navy. Status tetap konvensional — active/healthy = green. ---------- */
:root, :root[data-theme="light"] {
  --brand:#0C8E84; --brand-hover:#0A736B; --brand-tint:#E1EEEC; --brand-contrast:#ffffff;
  --accent:#D9762F; --accent-tint:rgba(217,118,47,.14);
  /* The DEFAULT theme is now the brighter of the two: an almost-white ground with
     a green cast, so the panel reads as daylight rather than as a tinted grey.
     The ground is a desaturated relative of the brand teal rather than a separate
     green, so the accent sits on it as the same family instead of as a guest.
     Every pair below was measured before shipping; the tints are unchanged
     precisely because they are what set the --brand-strong value. */
  --bg:#F7FDFA; --surface:#ffffff; --surface-2:#EFF9F3; --border:#DCEDE4;
  /* --border draws DECORATIVE separation (card edges, dividers) and only has to be
     visible. --border-strong draws the boundary of an INTERACTIVE CONTROL — an input,
     a select, a textarea — which WCAG requires at 3:1 because that line is the only
     thing telling you where the field is. They were one token until 2026-08-26, and
     measuring found the shipped value at 1.13:1 on the field's own background: every
     form in the panel had a boundary under a third of the required contrast, in the
     DEFAULT theme. Text contrast and focus rings were measured all along; the resting
     edge of a control never was. */
  --border-strong:#758B85;
  --text:#0C1A17; --text-muted:#4F615C;
  --shadow:0 1px 2px rgba(12,26,23,.05),0 8px 24px -12px rgba(12,26,23,.12);
  --success:#0E9E6E; --warning:#B67C0C; --danger:#CF3F3F; --info:#0C8E84;
  --success-tint:rgba(14,158,110,.12); --warn-tint:#F6EED7; --danger-tint:#F7E6E5; --info-tint:#E1EEEC;
  /* TEXT variants of the signal colours. The colours above are tuned to be seen
     as fills, borders and icons, where 3:1 is enough. As TEXT they were not:
     measured in a real browser, brand on white came to 4.02:1 and the status
     badges to 2.99:1 and 3.08:1, against the 4.5:1 minimum — so every link, every
     row action and every status pill in the panel sat just under readable. These
     are the same hues taken far enough down to pass on their own backgrounds; use
     them wherever the colour lands on TEXT. */
  --brand-strong:#0A736B; --brand-strong-hover:#085C55; --success-text:#076B4A; --warning-text:#8A5D00;
  --danger-text:#BF3333; --info-text:#0A736B;
  --scroll-shade:rgba(14,26,24,.26);
  --scrim:rgba(14,26,24,.42);
  --glass:var(--surface); --glass-border:var(--border); --glow:rgba(12,142,132,.06);
}
/* ---------- The dark theme, rebuilt 2026-08-26. -------------------------------
   Between 2026-08-26 morning and evening this slot held the FORMER LIGHT palette
   under the name "dark", because the owner found the real dark theme too dark to
   work in and asked for two daylight palettes. Living with it answered the
   question: a theme switch that moves between two near-white palettes does not
   read as a theme switch, it reads as a bug. So this is a genuine dark theme now.

   It is NOT the old one restored, and it is NOT an inversion of the light theme —
   inverting a palette is the classic dark-mode mistake, because a colour tuned to
   be read ON white is the wrong lightness to be read AS light. Every value here
   is its own tonal variant, measured on its own ground.

   The ground descends from the sidebar rather than from the light theme. The
   sidebar has always been ink-teal (#0F1D1B) in BOTH themes — it is the identity
   frame. Deriving the dark ground from that family is what finally makes the
   sidebar look like part of the page instead of a panel bolted onto it.

   Elevation inverts, as it must in the dark: light theme lifts a card by making it
   WHITER than the ground; here a card is LIGHTER than the ground for the same
   reason. Shadows carry almost no information on a dark ground, so separation is
   carried by surface lightness, not by shadow.

   Brand goes UP, not down. In the light theme the readable brand is DARKER than
   the token (--brand-strong #0A736B); here it must be LIGHTER (#3FD9C8), and the
   text on a brand-filled button flips from white to near-black. Anyone editing
   these must re-measure rather than mirror the light values.

   All 27 pairs measured before shipping; the detector self-checked against known
   values first. Body text 14.9:1, muted text 8.1:1, links 9.7:1. ---------- */
:root[data-theme="dark"] {
  --brand:#2CC3B4; --brand-hover:#3FD9C8; --brand-tint:rgba(44,195,180,.16); --brand-contrast:#04211E;
  --accent:#F0954E; --accent-tint:rgba(240,149,78,.16);
  --bg:#0B1614; --surface:#111F1C; --surface-2:#172A26; --border:#24403A;
  --border-strong:#628179;
  --text:#E8F2EF; --text-muted:#9FB8B2;
  --shadow:0 1px 2px rgba(0,0,0,.44),0 8px 24px -12px rgba(0,0,0,.66);
  --success:#2FBF85; --warning:#D9A036; --danger:#E5645F; --info:#2CC3B4;
  --success-tint:rgba(47,191,133,.16); --warn-tint:rgba(217,160,54,.16);
  --danger-tint:rgba(229,100,95,.16); --info-tint:rgba(44,195,180,.16);
  --brand-strong:#3FD9C8; --brand-strong-hover:#5FE5D6; --success-text:#3FD9A0; --warning-text:#F0BB4B;
  --danger-text:#FF8A80; --info-text:#3FD9C8;
  --scroll-shade:rgba(0,0,0,.52);
  --scrim:rgba(0,0,0,.62);
  --glass:var(--surface); --glass-border:var(--border); --glow:rgba(44,195,180,.10);
}
/* Follow the OS when the user hasn't chosen (no data-theme set). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    /* Identical to [data-theme="dark"] above — one answers the system preference,
       the other an explicit choice. Keep the two in step: a value changed in only
       one of them means the panel looks different depending on HOW you arrived at
       dark mode, which is close to impossible to diagnose from a bug report. */
    --brand:#2CC3B4; --brand-hover:#3FD9C8; --brand-tint:rgba(44,195,180,.16); --brand-contrast:#04211E;
    --accent:#F0954E; --accent-tint:rgba(240,149,78,.16);
    --bg:#0B1614; --surface:#111F1C; --surface-2:#172A26; --border:#24403A;
    --border-strong:#628179;
    --text:#E8F2EF; --text-muted:#9FB8B2;
    --shadow:0 1px 2px rgba(0,0,0,.44),0 8px 24px -12px rgba(0,0,0,.66);
    --success:#2FBF85; --warning:#D9A036; --danger:#E5645F; --info:#2CC3B4;
    --success-tint:rgba(47,191,133,.16); --warn-tint:rgba(217,160,54,.16);
    --danger-tint:rgba(229,100,95,.16); --info-tint:rgba(44,195,180,.16);
    --brand-strong:#3FD9C8; --brand-strong-hover:#5FE5D6; --success-text:#3FD9A0; --warning-text:#F0BB4B;
    --danger-text:#FF8A80; --info-text:#3FD9C8;
    --scroll-shade:rgba(0,0,0,.52);
    --scrim:rgba(0,0,0,.62);
    --glass:var(--surface); --glass-border:var(--border); --glow:rgba(44,195,180,.10);
  }
}

/* Signature ink-teal sidebar (Stratus) — fixed in BOTH themes: Nimbopanel's
   identity frame, distinct from cPanel (all-light) and ExtraCP's navy. The
   green-undertoned "ink" is what makes it read as Nimbopanel, not a blue clone. */
:root{
  --side-bg1:#0F1D1B; --side-bg2:#06100E; --side-fg:#E9F0EE; --side-muted:#8CA39E;
  /* Emphasis text on the sidebar (brand wordmark, active item, hovered item).
     These were six raw #fff. They measure 17.33:1 on the sidebar ink and the
     sidebar is theme-invariant, so no palette change could break them — but a
     raw value is a value nobody can retune, and six of them is a pattern. */
  --side-fg-strong:#ffffff;
  /* Glow under the brand mark. It was rgba(47,91,234,.45) — ExtraCP's navy,
     left behind by the Stratus rebrand, painting a blue halo under a teal
     logo. Theme-invariant like the rest of the sidebar. */
  --side-glow:rgba(44,195,180,.45);
  --side-hover:rgba(255,255,255,.06); --side-active:rgba(44,195,180,.16);
  --side-accent:#2CC3B4; --side-border:rgba(255,255,255,.08);
  --sidebar-w:240px; --topbar-h:60px; /* shell metrics — shared by sidebar/topbar + tool pages so they align exactly */
  /* Display font for the wordmark + headings (B1). Theme-invariant; overridden
     per brand at runtime by applyBranding, but only ever to an allowlisted stack
     (see FONT_STACKS in app.js / valid.BrandFonts) — never raw user input. Body
     and mono text are deliberately NOT tokenised here: they stay IBM Plex for
     readability regardless of the brand's display choice. */
  --font-display:'Bricolage Grotesque',sans-serif;
}

/* Flat Modern Dev-Tool surfaces (Palette B) — glassmorphism removed for a crisp,
   premium look consistent with the Nimbopanel storefront/brand. --glass stays the
   solid surface token defined above; chrome reads via clean 1px borders + a soft
   shadow rather than a frosted blur. */

*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
  /* Flat surface (Stratus): the twin radial-glow blobs were an AI-slop tell —
     removed. The atmospheric gradient now lives once, on the login hero only. */
  background:var(--bg); color:var(--text);
  font-family:'IBM Plex Sans',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
  font-size:14px; line-height:1.5; -webkit-font-smoothing:antialiased;
  transition:background .2s,color .2s;
}
a{color:var(--brand-strong);text-decoration:none}
a:hover{color:var(--brand-hover);text-decoration:underline}
.mono, .creds code, code{font-family:'IBM Plex Mono',ui-monospace,Menlo,Consolas,monospace}
.muted{color:var(--text-muted)}
/* The -text variants, not the base signal tokens. The base values are tuned to
   be seen as FILLS, borders and dots, where 3:1 is enough; as TEXT they measured
   3.18-4.40:1 against the 4.5:1 minimum. The variants exist for exactly this and
   these rules were simply missed when they were introduced. */
.good{color:var(--success-text)}
.warn{color:var(--warning-text)}
.small{font-size:13px}
.error{color:var(--danger-text)}
/* Inline per-field validation: an error anchored under its input, plus the
   aria-invalid boundary the field wears while it is wrong. Kept subtle — the
   message carries the signal, the control just needs to read as "flagged". */
.field-err{color:var(--danger-text);font-size:12.5px;margin:5px 0 0;line-height:1.35}
input[aria-invalid="true"],select[aria-invalid="true"],textarea[aria-invalid="true"]{
  border-color:var(--danger);box-shadow:0 0 0 1px var(--danger)
}
/* Skeleton placeholders: shimmering bars stand in for a list while it loads,
   so a slow table reads as "loading" rather than "empty". Colour comes only
   from role tokens; the shimmer is a moving highlight over the surface. */
.skel-row > td{padding:11px 12px}
.skel-line{display:block;height:12px;border-radius:6px;
  background:linear-gradient(90deg,var(--surface-2) 25%,var(--border) 37%,var(--surface-2) 63%);
  background-size:400% 100%;animation:skel-shimmer 1.4s ease-in-out infinite}
@keyframes skel-shimmer{0%{background-position:100% 0}100%{background-position:-100% 0}}
@media (prefers-reduced-motion: reduce){.skel-line{animation:none}}
.hidden,[hidden]{display:none!important}
::selection{background:var(--brand-tint);color:var(--text)}

/* Line icons take currentColor so they theme automatically. */
svg.i{width:20px;height:20px;stroke:currentColor;stroke-width:1.75;fill:none;stroke-linecap:round;stroke-linejoin:round;flex:none}
/* Small inline icon used inside table cells, badges and result banners. */
svg.i-sm{width:15px;height:15px;stroke:currentColor;stroke-width:1.9;fill:none;stroke-linecap:round;stroke-linejoin:round;vertical-align:-2px}
svg.i-sm.ok{stroke:var(--success)}
.fname{display:inline-flex;align-items:center;gap:6px}

:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:6px}

/* ---------- Login ---------- */
/* Login: the ONE purposeful atmospheric moment (Stratus "nimbo"), replacing the
   app-wide twin glow blobs. A single soft sky-band from the top, teal→apricot. */
#login-view{min-height:100dvh;display:flex;align-items:center;justify-content:center;padding:24px;position:relative;
  background:
    radial-gradient(90% 60% at 78% -8%, color-mix(in srgb,var(--brand) 12%,transparent), transparent 60%),
    radial-gradient(80% 55% at 12% 108%, color-mix(in srgb,var(--accent) 9%,transparent), transparent 55%),
    var(--bg)}
#login-view .login-theme{position:fixed;top:18px;right:18px}
#login-view .brand-lockup{display:flex;align-items:center;justify-content:center;gap:10px;margin-bottom:6px}
#login-view .brand-lockup .facet{width:30px;height:30px;color:var(--brand)}
#login-view .brand{font-family:var(--font-display);font-weight:800;font-size:28px;letter-spacing:.01em;color:var(--text)}
#login-view .brand .cp{color:var(--text-muted);font-weight:500}
#login-form.card{width:min(400px,100%);padding:30px 28px;display:flex;flex-direction:column;text-align:left}
#login-form .subtitle{color:var(--text-muted);text-align:center;margin-bottom:22px}
#login-form label{font-weight:600;font-size:13px;margin:12px 0 6px}
#login-form button[type=submit]{margin-top:20px;width:100%;justify-content:center;padding:12px}

/* ---------- Top bar ---------- */
.topbar{
  position:sticky;top:0;z-index:30;height:var(--topbar-h);display:flex;align-items:center;gap:16px;
  padding:0 20px;background:var(--glass);border-bottom:1px solid var(--border);
}
.topbar .brand{display:flex;align-items:center;gap:10px;font-family:var(--font-display);font-weight:800;font-size:20px;letter-spacing:.01em;color:var(--text)}
.topbar .brand:hover{text-decoration:none}
.topbar .brand .facet{width:24px;height:24px;color:var(--brand)}
.topbar .brand .cp{color:var(--text-muted);font-weight:500}
.spacer{flex:1}
.tb-actions{display:flex;align-items:center;gap:10px}
.icon-btn{
  width:38px;height:38px;display:grid;place-items:center;border-radius:10px;cursor:pointer;
  color:var(--text-muted);background:transparent;border:1px solid transparent;transition:.16s;
}
.icon-btn:hover{background:var(--surface-2);color:var(--text)}
.role{
  display:inline-flex;align-items:center;gap:7px;padding:6px 12px;border-radius:999px;
  background:var(--brand-tint);color:var(--brand-strong);font-weight:600;font-size:12.5px;text-transform:capitalize;
}
.role .dot{width:6px;height:6px;border-radius:50%;background:currentColor}
.whoami{color:var(--text-muted);font-size:13px;font-weight:500}
.avatar{width:34px;height:34px;border-radius:50%;background:linear-gradient(135deg,var(--brand-strong),var(--brand-hover));display:grid;place-items:center;color:var(--brand-contrast);font-weight:700;font-size:13px;flex:none}

/* ---------- Shell: sidebar + main ---------- */
.shell{display:flex;align-items:flex-start;min-height:calc(100dvh - 60px)}
/* Sticky, viewport-tall sidebar: the nav stays put while the main content
   scrolls (no more empty rail on long pages), and scrolls internally if the
   menu is taller than the viewport. */
.sidebar{
  width:var(--sidebar-w);flex:none;padding:14px 12px 12px;border-right:1px solid var(--side-border);
  background:linear-gradient(185deg,var(--side-bg1),var(--side-bg2));color:var(--side-fg);
  position:sticky;top:var(--topbar-h);height:calc(100dvh - var(--topbar-h));overflow-y:auto;overscroll-behavior:contain;
  display:flex;flex-direction:column;
}
.sidebar::-webkit-scrollbar{width:7px}
.sidebar::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:8px}
#sidebar-nav{flex:1 1 auto}
/* Brand lockup inside the sidebar (mockup) */
.side-brand{display:flex;align-items:center;gap:11px;padding:4px 10px 14px;text-decoration:none}
.side-brand .facet{width:30px;height:30px;color:var(--side-accent);flex:none;filter:drop-shadow(0 3px 8px var(--side-glow))}
.side-brand .wm{display:flex;flex-direction:column;line-height:1.05;min-width:0}
.side-brand .wm b{font-family:var(--font-display);font-weight:800;font-size:18px;letter-spacing:.01em;color:var(--side-fg-strong)}
.side-brand .wm b .cp{color:var(--side-muted);font-weight:500}
.side-brand .wm small{font-size:10.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--side-muted);font-weight:600}
.side-brand .brand-logo{height:26px;max-width:150px}
/* User card pinned to the sidebar bottom (mockup) */
.side-user{display:flex;align-items:center;gap:10px;margin-top:8px;padding:9px 8px;border-top:1px solid var(--side-border)}
.side-user .avatar{width:34px;height:34px;border-radius:9px;flex:none}
.side-user .su{flex:1;min-width:0}
.side-user .su b{display:block;font-size:13px;color:var(--side-fg-strong);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.side-user .su span{font-size:11.5px;color:var(--side-muted)}
.side-user .icon-btn{color:var(--side-muted)}
.side-user .icon-btn:hover{background:var(--side-hover);color:var(--side-fg-strong)}
.nav-label{font-size:10.5px;text-transform:uppercase;letter-spacing:.13em;color:var(--side-muted);padding:12px 12px 6px;font-weight:600}
.nav-label:first-child{padding-top:2px}
/* Nav count badge */
.nav-cnt{margin-left:auto;font-size:11px;color:var(--side-muted);background:rgba(255,255,255,.07);padding:1px 8px;border-radius:20px;font-variant-numeric:tabular-nums}
.nav-cnt:empty{display:none}
#sidebar-nav .nl{flex:1}
#sidebar-nav a,#sidebar-nav button{
  position:relative;display:flex;align-items:center;gap:11px;width:100%;padding:9px 12px;border-radius:9px;
  color:var(--side-fg);font-weight:500;font-size:14px;font-family:inherit;text-align:left;
  margin-bottom:2px;border:none;background:none;transition:.16s;cursor:pointer;
}
#sidebar-nav a svg,#sidebar-nav button svg{opacity:.82;transition:.16s}
#sidebar-nav a:hover,#sidebar-nav button:hover{background:var(--side-hover);color:var(--side-fg-strong);text-decoration:none}
#sidebar-nav a:hover svg,#sidebar-nav button:hover svg{opacity:1}
#sidebar-nav a.active,#sidebar-nav button.active{background:var(--side-active);color:var(--side-fg-strong)}
#sidebar-nav a.active svg,#sidebar-nav button.active svg{color:var(--side-accent);opacity:1}
/* Hexagon-accent marker on the active item (Nimbopanel identity) */
#sidebar-nav a.active::before,#sidebar-nav button.active::before{
  content:"";position:absolute;left:-12px;top:50%;transform:translateY(-50%);
  width:4px;height:20px;border-radius:0 4px 4px 0;background:var(--side-accent);
}
#sidebar-nav button:focus-visible{outline:2px solid var(--side-accent);outline-offset:2px}

.main{flex:0 1 1440px;min-width:0;padding:26px 32px;max-width:1440px;margin-inline:auto}
.page-h{font-size:20px;font-weight:700;margin-bottom:4px}
.page-sub{color:var(--text-muted);margin-bottom:22px}

/* ---------- Stats strip ---------- */
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:24px}
.stat{background:var(--glass);border:1px solid var(--border);border-radius:14px;padding:15px 18px;box-shadow:var(--shadow)}
.stat .k{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:7px;margin-bottom:8px}
.stat .k svg{width:15px;height:15px}
.stat .v{font-size:24px;font-weight:700;letter-spacing:-.01em}
.stat .v small{font-size:13px;color:var(--text-muted);font-weight:500}
.stat .v .up{color:var(--success-text);font-size:12px;font-weight:600;margin-left:6px}

/* ---------- Colorful KPI cards (customer dashboard — Webuzo-inspired) ---------- */
/* Flexible KPI row: cards flow and GROW to fill each row, so an odd count (e.g.
   7 on the admin dashboard) never leaves one card stranded alone on a second row
   with empty space beside it — the last row always stretches full width. */
.stats.kstats{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:18px}
.stats.kstats>*{flex:1 1 158px;min-width:0}
.kstat{display:flex;flex-direction:column;gap:12px;background:var(--glass);border:1px solid var(--border);border-radius:16px;padding:16px 17px;box-shadow:var(--shadow);position:relative;overflow:hidden}
.kstat::after{content:"";position:absolute;top:0;right:0;width:118px;height:118px;background:radial-gradient(circle at 100% 0,var(--c-tint,transparent),transparent 72%);pointer-events:none}
.kstat .ktop{display:flex;align-items:center;justify-content:space-between;gap:10px;position:relative}
.kstat .kic{width:40px;height:40px;flex:none;border-radius:12px;display:grid;place-items:center;background:var(--c-tint,var(--brand-tint));color:var(--c,var(--brand))}
.kstat .kic svg{width:21px;height:21px}
/* Wrap rather than truncate: eight cards share one row, and an ellipsised label
   ("Hosting ac…", "Hosting ba…") leaves the admin guessing which figure they are
   reading. Two short lines cost a little height; a hidden word costs meaning. */
.kstat .k{font-size:12.5px;color:var(--text-muted);font-weight:600;line-height:1.25;overflow-wrap:anywhere}
.kstat .v{font-size:23px;font-weight:800;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;position:relative}
.kstat .v small,.kstat .v .u{font-size:13px;color:var(--text-muted);font-weight:600}
.kstat .v .mono{font-size:20px}
.kstat .kbar{height:7px;border-radius:20px;background:var(--surface-2);overflow:hidden;position:relative}
.kstat .kbar>i{display:block;height:100%;width:0;border-radius:20px;background:var(--c,var(--brand));transition:width .5s ease}
.kstat .ksub{font-size:12px;color:var(--text-muted);position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.kstat .ksub .good{font-weight:600}
/* Accent — Stratus: SATU keluarga warna (teal brand). Pelangi 6-warna lama
   (biru/hijau/amber/ungu/cyan/rose) dihapus — itu penanda AI-slop; badge & tile
   kini tenang/monokrom, wayfinding lewat bentuk ikon + label. */
.acc-blue,.acc-green,.acc-amber,.acc-purple,.acc-cyan,.acc-rose{--c:var(--brand);--c-tint:var(--brand-tint)}
/* General Information panel (customer aside) */
.info-row{display:flex;justify-content:space-between;gap:12px;padding:8px 0;border-bottom:1px solid var(--border);font-size:12.5px}
.info-row:last-child{border-bottom:none}
.info-row .il{color:var(--text-muted);flex:none}
.info-row .iv{color:var(--text);font-weight:600;text-align:right;word-break:break-word;overflow-wrap:anywhere}

/* ---------- Cards ---------- */
.card,.sys-card,.acct-card{
  background:var(--glass);border:1px solid var(--border);border-radius:16px;box-shadow:var(--shadow);
  padding:20px 22px;margin-bottom:18px;
}
.sys-card h2,.acct-card h2{
  font-size:15px;font-weight:700;display:flex;align-items:center;gap:10px;
  margin:-20px -22px 18px;padding:15px 22px;border-bottom:1px solid var(--border);
}
.sys-card h3,.db-body h3{font-size:14px;font-weight:700;margin:18px 0 10px}
.sys-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:6px 0}

/* ---------- Feature tiles (heart of the UI) ---------- */
.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:8px}
.tile{
  display:flex;flex-direction:column;align-items:center;gap:9px;text-align:center;
  padding:18px 10px;border-radius:12px;cursor:pointer;color:var(--text);
  border:1px solid transparent;background:transparent;transition:.16s;font:inherit;
}
.tile:hover{background:var(--surface-2);transform:translateY(-2px);text-decoration:none}
.tile svg{width:26px;height:26px;color:var(--text-muted);transition:.16s}
.tile:hover svg{color:var(--brand)}
/* Colored icon badge inside a feature tile (Webuzo-inspired) */
.tile .tile-ic{width:46px;height:46px;border-radius:14px;display:grid;place-items:center;background:var(--c-tint,var(--surface-2));color:var(--c,var(--text-muted));transition:.16s}
.tile:hover .tile-ic{transform:scale(1.06)}
.tile .tile-ic svg{width:24px;height:24px;color:inherit}
.tile:hover .tile-ic svg{color:inherit}
/* A label with no natural break point — "(Node/Python/Ruby)" — overflowed its
   tile and was cut off mid-word against the neighbour. Break anywhere rather
   than hide part of the name of a tool. */
.tile span{font-size:12.5px;font-weight:600;line-height:1.25;overflow-wrap:anywhere;max-width:100%}
.tile.danger:hover{background:var(--danger-tint)}
.tile.danger:hover svg{color:var(--danger)}

/* ---------- Feature groups (customer dashboard — mockup layout) ---------- */
.acct-summary{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:-4px 0 22px;color:var(--text-muted);font-size:13.5px}
.acct-summary strong{color:var(--text);font-weight:700}
.groups{display:grid;grid-template-columns:repeat(2,1fr);gap:18px}
.group{background:var(--glass);border:1px solid var(--border);border-radius:16px;box-shadow:var(--shadow);overflow:hidden}
.card-h{display:flex;align-items:center;gap:10px;padding:15px 20px;border-bottom:1px solid var(--border);font-weight:700;font-size:15px}
.card-h .gi{color:var(--brand);width:20px;height:20px}
.group .tiles{grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:6px;padding:14px}
@media (max-width:900px){.groups{grid-template-columns:1fr}}

/* ---------- Customer grouped dashboard (collapsible + right column) ---------- */
.cust-layout{display:grid;grid-template-columns:1fr 340px;gap:20px;align-items:start}
/* Feature groups sit in a 2-column grid (mockup): short cards, not full-width rows. */
#eu-groups{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start}
#eu-groups .group{margin-bottom:0}
@media (max-width:680px){#eu-groups{grid-template-columns:1fr}}
.group .card-h{cursor:pointer;user-select:none}
.group .card-h .chev{margin-left:auto;color:var(--text-muted);transition:transform .2s}
.group.collapsed .card-h .chev{transform:rotate(-90deg)}
.group.collapsed .tiles{display:none}
.side-card{background:var(--glass);border:1px solid var(--border);border-radius:14px;box-shadow:var(--shadow);padding:16px 18px;margin-bottom:16px}
.side-card h2{font-size:13px;font-weight:700;margin-bottom:10px;display:flex;align-items:center;gap:8px}
.side-card h2 .gi{color:var(--brand);width:16px;height:16px}
.kv{display:flex;justify-content:space-between;gap:10px;font-size:12.5px;padding:5px 0;color:var(--text-muted)}
.kv b{color:var(--text);font-weight:600;text-align:right;word-break:break-word}
@media (max-width:1050px){.cust-layout{grid-template-columns:1fr}}

/* ---------- Customer dashboard v2 (cPanel × Webuzo, Nimbopanel identity) ---------- */
/* Quick-action chips (welcome row) */
.quick{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
.chip{display:inline-flex;align-items:center;gap:7px;padding:9px 14px;border-radius:10px;
  border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:13px;font-weight:600;
  cursor:pointer;transition:.16s;box-shadow:var(--shadow)}
.chip svg{width:15px;height:15px;color:var(--brand)}
.chip:hover{border-color:var(--brand);transform:translateY(-1px)}
.chip.primary{background:var(--brand-strong);color:var(--brand-contrast);border-color:transparent}
.chip.primary svg{color:currentColor}

/* Topbar: crumb + inline tool search + mobile menu button (mockup) */
.crumb{font-size:15px;font-weight:700;color:var(--text);letter-spacing:-.01em;white-space:nowrap}
.menu-btn{display:none}
.topsearch{flex:1;max-width:440px;margin-left:4px;display:flex;align-items:center;gap:9px;
  background:var(--surface-2);border:1px solid var(--border);border-radius:11px;padding:0 13px;color:var(--text-muted);transition:border-color .16s,box-shadow .16s}
.topsearch:focus-within{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-tint)}
.topsearch>svg{width:16px;height:16px;flex:none}
.topsearch input{border:none;background:none;box-shadow:none;padding:10px 0;font-size:13.5px;color:var(--text);flex:1;min-width:0}
.topsearch input:focus{outline:none;border:none;box-shadow:none}
.topsearch kbd{font-family:inherit;font-size:11px;color:var(--text-muted);border:1px solid var(--border);border-radius:6px;padding:1px 7px;background:var(--surface);flex:none}
#side-scrim{display:none}

/* Welcome hero: greeting left, quick chips right (mockup) */
.hello{display:flex;align-items:flex-end;justify-content:space-between;gap:16px 24px;flex-wrap:wrap;margin-bottom:22px}
.hello .hello-t h1{font-size:24px;font-weight:800;letter-spacing:-.02em;line-height:1.15}
.hello .hello-t h1 .accent{color:var(--brand-strong)}
.hello .hello-t p{color:var(--text-muted);margin-top:5px;font-size:14px}
.hello .quick{margin-bottom:0}

/* Customer footer (mockup) */
.eu-foot{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-top:26px;padding-top:16px;border-top:1px solid var(--border);color:var(--text-muted);font-size:12.5px}
.eu-foot .fl{display:flex;gap:16px;flex-wrap:wrap;align-items:center}
/* 12.5px text gave these a 19px-tall hit area — under the 24px minimum, and
   awkward with a thumb on the widths where this footer is actually read.
   Padding grows the target without changing how the link looks. */
.eu-foot a{color:var(--text-muted);font-weight:500;display:inline-flex;align-items:center;padding:7px 0}
.eu-foot a:hover{color:var(--brand-strong)}
.eu-foot .pill{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:650;padding:3px 10px;border-radius:20px;background:var(--success-tint);color:var(--success-text)}
.eu-foot .pill .pd{width:6px;height:6px;border-radius:50%;background:currentColor}

/* Tool search */
.eu-search{display:flex;align-items:center;gap:10px;background:var(--glass);border:1px solid var(--border);
  border-radius:12px;padding:0 14px;margin-bottom:18px;box-shadow:var(--shadow);max-width:540px}
/* The ring has to move to the wrapper, exactly as .topsearch and .man-search-box
   do. Without this the rule below stripped the focus ring and put nothing back,
   so a keyboard user tabbing onto the customer's tile search had no way to tell
   the field was focused. */
.eu-search:focus-within{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-tint)}
.eu-search>svg{width:18px;height:18px;color:var(--text-muted);flex:none}
.eu-search input{border:none;background:none;box-shadow:none;padding:12px 0;font-size:14px;flex:1;min-width:0}
.eu-search input:focus{outline:none;border:none;box-shadow:none}
.eu-search kbd{font-family:inherit;font-size:11px;color:var(--text-muted);border:1px solid var(--border);border-radius:6px;padding:1px 7px;background:var(--surface-2);flex:none}
.eu-noresult{grid-column:1/-1;color:var(--text-muted);font-size:13.5px;padding:6px 2px}
.tile.hide,.group.hide{display:none}

/* Group header: coloured icon badge + tool count. The whole group shares one
   accent via .acc-* on the .group, so badge + tiles read as a colour family. */
.group .card-h{gap:11px;font-size:14.5px}
.gi-badge{width:32px;height:32px;flex:none;border-radius:9px;display:grid;place-items:center;
  background:var(--c-tint,var(--brand-tint));color:var(--c,var(--brand))}
.gi-badge svg{width:17px;height:17px}
.card-h .gcount{margin-left:auto;font-size:12px;color:var(--text-muted);font-weight:500}
.group .card-h .chev{margin-left:8px}

/* Dense, left-aligned tiles (mockup layout) */
.group .tiles{grid-template-columns:repeat(auto-fill,minmax(108px,1fr));gap:4px;padding:10px}
.group .tile{position:relative;flex-direction:column;align-items:flex-start;text-align:left;gap:9px;padding:11px 10px}
.group .tile .tile-ic{width:38px;height:38px;border-radius:11px}
.group .tile .tile-ic svg{width:20px;height:20px}
.group .tile .tile-lbl,.group .tile>span:not(.tile-dot):not(.tile-ic){font-size:12.5px;font-weight:600;line-height:1.25}
/* Category dot (Stratus): one small signal, top-right. Two tones only. */
.tile-dot{position:absolute;top:9px;right:9px;width:6px;height:6px;border-radius:50%;opacity:.85}

/* Plan card (gradient) */
.side-plan{background:linear-gradient(135deg,var(--brand-strong),var(--brand-hover));color:var(--brand-contrast);border:none}
.side-plan h2{color:var(--brand-contrast)}
.side-plan .pt{font-size:11px;text-transform:uppercase;letter-spacing:.12em;opacity:.85;font-weight:600}
.side-plan .pn{font-size:19px;font-weight:800;margin:3px 0 2px;letter-spacing:-.01em}
.side-plan .pr{font-size:12.5px;opacity:.92}
.side-plan .pbtn{margin-top:12px;display:inline-flex;align-items:center;gap:7px;background:rgba(255,255,255,.16);
  border:1px solid rgba(255,255,255,.28);color:var(--side-fg-strong);font-size:13px;font-weight:700;padding:8px 14px;border-radius:10px;cursor:pointer;transition:.16s}
.side-plan .pbtn:hover{background:rgba(255,255,255,.26)}
.side-plan .pbtn svg{width:15px;height:15px;color:var(--brand-contrast)}

/* Usage summary — labelled progress bars */
.use{padding:11px 0;border-bottom:1px solid var(--border)}
.use:last-child{border-bottom:none;padding-bottom:0}
.use .ut{display:flex;justify-content:space-between;gap:10px;font-size:12.5px;margin-bottom:7px}
.use .ut b{font-weight:600;color:var(--text)}
.use .ut span{color:var(--text-muted);font-variant-numeric:tabular-nums}
.use .track{height:8px;border-radius:20px;background:var(--surface-2);overflow:hidden}
.use .track>i{display:block;height:100%;width:0;border-radius:20px;background:var(--c,var(--brand));transition:width .5s ease}

/* Quick links card */
.side-links{display:flex;flex-direction:column;gap:1px}
.side-links a{display:flex;align-items:center;gap:10px;padding:8px 6px;border-radius:8px;color:var(--text);font-size:13px;font-weight:500}
.side-links a:hover{background:var(--surface-2);text-decoration:none}
.side-links a svg{width:16px;height:16px;color:var(--text-muted);flex:none}
.side-links a:hover svg{color:var(--brand)}

/* KPI sparkline (real GoAccess series) */
.kspark{height:30px}
.kspark svg{width:100%;height:30px;display:block}
/* Recent Activity list (real per-account events) */
.act-list{display:flex;flex-direction:column;gap:1px}
.act{display:flex;gap:11px;padding:8px 0;border-bottom:1px solid var(--border)}
.act:last-child{border-bottom:none;padding-bottom:0}
.act-ic{width:30px;height:30px;border-radius:9px;flex:none;display:grid;place-items:center;background:var(--c-tint,var(--surface-2));color:var(--c,var(--text-muted))}
.act-ic svg{width:15px;height:15px}
.act-b{flex:1;min-width:0}
.act-t{font-size:12.5px;line-height:1.35;color:var(--text);overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.act-time{font-size:11px;color:var(--text-muted);margin-top:1px}

/* Tool "page mode": a routed customer tool fills the content area as a full page
   whose content aligns EXACTLY with the dashboard — same 1440 max-width, same
   32px gutters, same centering (uses the shared --sidebar-w/--topbar-h metrics).
   The outer box is transparent so the tool's own cards/tables sit on the page
   like the dashboard, not inside a floating card. Admin/reseller modals (no
   .as-page) stay centred overlays. */
.modal.as-page{position:fixed;inset:var(--topbar-h) 0 0 var(--sidebar-w);padding:0;background:var(--bg);
  display:block;overflow:auto;z-index:25;-webkit-backdrop-filter:none;backdrop-filter:none}
.modal.as-page .modal-box{width:100%;max-width:1440px;min-height:100%;margin:0 auto;
  background:transparent;border:none;border-radius:0;box-shadow:none;max-height:none;padding:24px 32px 48px}
.modal.as-page .modal-head{position:sticky;top:0;z-index:2;margin:-24px -32px 20px;padding:16px 32px;
  background:color-mix(in srgb,var(--bg) 90%,transparent);border-bottom:1px solid var(--border);
  -webkit-backdrop-filter:saturate(180%) blur(10px);backdrop-filter:saturate(180%) blur(10px)}
@media (max-width:900px){
  .modal.as-page{inset:var(--topbar-h) 0 0 0}
  .modal.as-page .modal-box{padding:16px 16px 40px}
  .modal.as-page .modal-head{margin:-16px -16px 14px;padding:14px 16px}
}

/* ---------- Admin server dashboard (gauges + overview + services) ---------- */
.dash-card{background:var(--glass);border:1px solid var(--border);border-radius:14px;box-shadow:var(--shadow);margin-bottom:18px;overflow:hidden}
/* Dashboard quick-actions: a dense, flexible row of shortcut buttons that fill
   the width evenly (flex-grow), adding utility + density to the overview. */
.qa-row{display:flex;flex-wrap:wrap;gap:10px;padding:14px 16px}
.qa-btn{flex:1 1 130px;min-width:0;display:flex;align-items:center;gap:10px;
  background:var(--surface-2);border:1px solid var(--border);border-radius:12px;padding:12px 14px;
  color:var(--text);font:inherit;font-size:13px;font-weight:600;cursor:pointer;text-align:left;
  transition:border-color .15s,transform .15s,background .15s}
.qa-btn:hover{border-color:color-mix(in srgb,var(--brand) 45%,var(--border));transform:translateY(-2px);background:var(--brand-tint)}
.qa-btn svg{width:18px;height:18px;flex:none;color:var(--brand)}
.dash-card .card-h{padding:14px 18px;font-size:14.5px;border-bottom:1px solid var(--border)}
.dash-card .card-b{padding:16px 18px}
.cols{display:grid;grid-template-columns:1.4fr 1fr;gap:18px;align-items:start}
.cols .dash-card{margin-bottom:0}
/* Per-menu pages (sidebar router): the content area swaps with a light fade;
   single-card pages take the full width. Reduced-motion users get no motion. */
.page-swap{animation:pageIn .18s ease-out}
@keyframes pageIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.page-swap{animation:none}}
.cust-layout.full{grid-template-columns:1fr}
.cols.one{grid-template-columns:1fr}
#page-title:focus{outline:none}
/* "New Account Info" summary shown once after a create (cPanel-style). */
.acct-result{margin-top:10px;border:1px solid var(--border);border-radius:10px;padding:12px 16px;background:var(--surface-2)}
.acct-result .ar-h{display:flex;align-items:center;gap:8px;font-weight:700;margin-bottom:8px;color:var(--success-text)}
.acct-result .ar-row{display:flex;gap:12px;padding:3px 0;font-size:13px}
.acct-result .ar-row span{min-width:110px;color:var(--text-muted)}
.acct-result .ar-row b{font-weight:600;word-break:break-all}
.gauges{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:18px}
.gauge{background:var(--glass);border:1px solid var(--border);border-radius:14px;box-shadow:var(--shadow);padding:16px 18px;display:flex;align-items:center;gap:16px}
.gauge .ring{width:74px;height:74px;flex:none;position:relative}
.gauge .ring svg{transform:rotate(-90deg)}
.gauge .ring .pct{position:absolute;inset:0;display:grid;place-items:center;font-weight:700;font-size:15px}
.gauge .lbl{font-size:12px;color:var(--text-muted);margin-bottom:3px}
.gauge .gval{font-weight:700;font-size:14px}
.gauge .gsub{font-size:11.5px;color:var(--text-muted)}
.overview{display:grid;grid-template-columns:repeat(3,1fr);gap:2px 18px}
.ov{padding:10px 0}
.ov .t{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:7px;margin-bottom:4px}
.ov .t svg{width:14px;height:14px}
.ov .n{font-size:18px;font-weight:700}
.ov .n small{font-size:12px;color:var(--text-muted);font-weight:500}
.ov .n .good{color:var(--success-text)}
.svc{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--border)}
.svc:last-child{border-bottom:0}
.svc .dot{width:8px;height:8px;border-radius:50%;background:var(--success);box-shadow:0 0 8px var(--success);flex:none}
.svc.down .dot{background:var(--danger);box-shadow:0 0 8px var(--danger)}
.svc .nm{font-weight:600}
.svc .st{margin-left:auto;font-size:11.5px;color:var(--success-text);font-weight:600}
.svc.down .st{color:var(--danger-text)}
/* Service Manager control buttons (admin) */
.svc-ctl{display:inline-flex;gap:6px;margin-left:12px}
.svc-ctl .btn-xs{margin-left:0}
.svc-ctl .btn-xs:disabled{opacity:.5;cursor:default}
.lean{display:flex;align-items:baseline;gap:8px;margin-bottom:8px}
.lean .big{font-size:30px;font-weight:800;font-family:'IBM Plex Mono',monospace}
.lean .u{color:var(--text-muted);font-size:14px}
.lean .tag{margin-left:auto;background:var(--brand-tint);color:var(--brand-strong);font-weight:700;font-size:11px;padding:3px 9px;border-radius:999px}
@media (max-width:980px){.gauges{grid-template-columns:repeat(2,1fr)}.cols{grid-template-columns:1fr}.cols .dash-card{margin-bottom:18px}}

/* ---------- Reseller branding ---------- */
.brand-logo{height:26px;max-width:150px;width:auto;object-fit:contain;display:block}
.foot{padding:18px 32px;color:var(--text-muted);font-size:12px;font-family:'IBM Plex Mono',monospace;letter-spacing:.04em;border-top:1px solid var(--border);margin-top:8px}
.brand-preview{margin-top:14px;display:flex;flex-direction:column;gap:8px}
.brand-preview-bar{display:flex;align-items:center;gap:14px;background:var(--surface-2);border:1px solid var(--border);border-radius:12px;padding:12px 16px}
.brand-preview-bar .brand{font-family:var(--font-display);font-weight:800;font-size:18px;display:flex;align-items:center;gap:10px}
.brand-preview-bar .brand .facet{color:var(--brand)}
.brand-preview-btn{background:var(--brand-strong);color:var(--brand-contrast);border:0;border-radius:8px;padding:7px 14px;font-weight:700;font-size:13px}
.brand-preview-row{display:flex;align-items:center;gap:16px;margin-top:10px;flex-wrap:wrap}
.brand-contrast-report{margin-top:12px}
.ccheck{display:flex;justify-content:space-between;align-items:center;gap:12px;margin:5px 0;font-size:13px;max-width:360px}
.tk-thread{display:flex;flex-direction:column;gap:10px;max-height:380px;overflow-y:auto}
.tk-msg{border:1px solid var(--border);border-radius:10px;padding:10px 14px;background:var(--surface)}
.tk-msg.staff{background:var(--surface-2);border-color:var(--border-strong)}
.tk-msg-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:4px}
.tk-msg-body{font-size:14px;line-height:1.5;word-break:break-word}

/* ---------- Billing (gateway on/off list) ---------- */
.gateway-list{display:flex;flex-direction:column;gap:8px}
.gateway-row{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:var(--surface-2)}
.gateway-row .gw-state{margin-left:auto;font-size:12.5px;color:var(--text-muted)}
.gateway-row .gw-state.on{color:var(--brand-strong);font-weight:600}
.btn-sm.gw-on{border-color:var(--brand);color:var(--brand-strong)}

/* ---------- Buttons ---------- */
button{font:inherit;cursor:pointer;border:1px solid transparent;border-radius:10px}
/* Primary = unclassed <button> (create/login/save/primary actions). The folder
   tree's toggle/label buttons opt out (they are plain, not primary controls). */
button:not(.link):not(.btn-sm):not(.btn-xs):not(.icon-btn):not(.tile):not(.fm-ttog):not(.fm-tlabel):not(.pbtn):not(.chip){
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:10px 18px;font-weight:700;font-size:14px;
  background:var(--brand-strong);color:var(--brand-contrast);border-color:var(--brand-strong);transition:.16s;
}
button:not(.link):not(.btn-sm):not(.btn-xs):not(.icon-btn):not(.tile):not(.fm-ttog):not(.fm-tlabel):not(.pbtn):not(.chip):hover{background:var(--brand-strong-hover);border-color:var(--brand-strong-hover)}
button:disabled{opacity:.6;cursor:not-allowed}
/* Secondary small */
.btn-sm,.btn-file{
  display:inline-flex;align-items:center;gap:7px;padding:8px 14px;border-radius:10px;font-weight:600;font-size:13px;
  background:var(--surface-2);color:var(--text);border:1px solid var(--border-strong);transition:.16s;
}
.btn-sm:hover,.btn-file:hover{border-color:var(--brand);color:var(--brand-strong)}
.btn-sm.danger{color:var(--danger-text);border-color:transparent;background:var(--danger-tint)}
.btn-sm.danger:hover{border-color:var(--danger)}
.btn-file{cursor:pointer}
/* Ghost / text (row actions) */
.link{
  background:transparent;border:0;padding:5px 8px;border-radius:8px;
  color:var(--brand-strong);font-weight:600;font-size:13px;transition:.14s;cursor:pointer;
}
.link:hover{background:var(--brand-tint);text-decoration:none}
.link.danger{color:var(--danger-text)}
.link.danger:hover{background:var(--danger-tint)}
a.link{display:inline-flex;align-items:center}

/* ---------- Inputs ---------- */
input[type=text],input[type=password],input[type=email],input[type=number],select,textarea,.fm-prompt{
  /* --border-strong, not --border: this line is the only thing that tells a user
     where the field is, so WCAG treats it as a UI component boundary (3:1), not as
     decoration. It measured 1.13:1 here until 2026-08-26. */
  width:100%;background:var(--surface-2);color:var(--text);border:1px solid var(--border-strong);
  border-radius:10px;padding:10px 12px;font:inherit;transition:border-color .16s,box-shadow .16s;
}
input:focus,select:focus,textarea:focus,.fm-prompt:focus{
  outline:0;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-tint);
}
input::placeholder,textarea::placeholder{color:var(--text-muted)}
select{cursor:pointer;appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--text-muted) 50%),linear-gradient(135deg,var(--text-muted) 50%,transparent 50%);background-position:calc(100% - 16px) 55%,calc(100% - 11px) 55%;background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:34px}
label{color:var(--text)}

/* Create/allocation forms */
/* Row alignment. A .create-form is a wrapping flex row of label+control fields.
   It used to be align-items:flex-end, which aligns the BOTTOM of each field BOX —
   so any field whose box was taller than its neighbours pushed its own label and
   control upward. The reseller password field, which carries a hint under its
   input, sat 30px above everything beside it for exactly that reason.
   Instead: top-align the boxes and give every label row the same height, so the
   CONTROLS line up even when one label wraps to two lines or a hint hangs below. */
.create-form{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-start;margin-bottom:14px;--label-h:32px}
.create-form .field{display:flex;flex-direction:column;gap:6px;min-width:160px}
.create-form .field.grow{flex:1;min-width:220px}
.create-form .field label{font-size:12.5px;font-weight:600;color:var(--text-muted)}
.create-form button{white-space:nowrap}
/* --label-h fits a one- OR two-line 12.5px label, so both reserve the same space.
   gap restores the word space that display:flex collapses between a label's text
   and a nested <span>. */
.create-form .field > label:not(.pkg-feat-item){
  min-height:var(--label-h);display:flex;align-items:flex-end;flex-wrap:wrap;gap:.35em;
}
/* A field whose control IS its label (a lone checkbox) has no label row at all,
   so it needs that row's height plus the column gap to reach the control line,
   and a box as tall as a text input so the checkbox centres on the same line. */
.create-form .field > label:only-child{
  margin-top:calc(var(--label-h) + 6px);
  min-height:43px;display:flex;align-items:center;gap:6px;padding:0;
}
.create-form > button{align-self:flex-start;margin-top:calc(var(--label-h) + 6px)}
/* A create-form is a WRAPPING flex row, which is right for a 3-6 field form. The
   reseller form is 12 fields with an OPTIONAL section, and two things break there:
   a separator that is itself a flex item only draws its line across its own box,
   and loose fields wrap onto rows shared with the required fields above them —
   that is how "Nameserver 1" and "Nameserver 2" ended up on different rows with
   the submit button between them. .field-full forces a real full-width row;
   .field-group keeps one section's fields together as their own sub-row. */
.create-form .field-full{flex:1 1 100%;min-width:0}
.create-form .field-group{
  flex:1 1 100%;min-width:0;
  display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;
}
.rs-packages{display:flex;flex-wrap:wrap;gap:.4rem .9rem;align-items:center}
/* Match a text input's height so a row of package checkboxes centres on the same
   line as the inputs beside it instead of riding 10px high. */
.create-form .field > .rs-packages{min-height:43px;align-content:center}
.rs-pkg{display:inline-flex;align-items:center;gap:.35rem;font-size:13px;white-space:nowrap;cursor:pointer}
.rs-pkg input{width:auto}

/* ---------- Tables ---------- */
/* A row of fourteen actions is wider than the card on any normal laptop, so this
   container scrolls — correctly. What it did not do was SAY so: at 1440px the
   admin saw "Passw" sliced mid-word at the right edge with nothing to suggest
   more existed, and an action you cannot see is an action you do not have. The
   classic background-attachment pair paints a soft shade at whichever edge has
   content beyond it and fades out as you reach the end, with no JS and no
   scroll listener. */
.table-scroll{
  overflow-x:auto;border:1px solid var(--border);border-radius:12px;
  background:
    linear-gradient(to right, var(--surface) 30%, transparent),
    linear-gradient(to right, transparent, var(--surface) 70%) right,
    radial-gradient(farthest-side at 0 50%, var(--scroll-shade), transparent),
    radial-gradient(farthest-side at 100% 50%, var(--scroll-shade), transparent) right;
  background-repeat:no-repeat;
  background-size:44px 100%, 44px 100%, 22px 100%, 22px 100%;
  background-attachment:local, local, scroll, scroll;
}
.acct-table,.fm-table{width:100%;border-collapse:collapse;font-size:13.5px}
.acct-table th,.fm-table th{
  text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);
  font-weight:600;padding:11px 14px;background:var(--surface-2);border-bottom:1px solid var(--border);white-space:nowrap;
}
.acct-table td,.fm-table td{padding:12px 14px;border-bottom:1px solid var(--border);vertical-align:middle}
.acct-table tr:last-child td,.fm-table tr:last-child td{border-bottom:0}
.acct-table tbody tr:hover,.fm-table tbody tr:hover{background:var(--surface-2)}
.row-actions{text-align:right;white-space:nowrap}
.row-actions .link,.row-actions a.link{margin-left:2px}

/* ---------- Badges ---------- */
.badge{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;font-size:11.5px;font-weight:600;text-transform:capitalize;white-space:nowrap}
.badge::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
.badge .b-dot{display:none}
.badge.badge-ok,.badge.ok{background:var(--success-tint);color:var(--success-text)}
.badge.badge-bad,.badge.bad{background:var(--danger-tint);color:var(--danger-text)}
.badge.badge-wait{background:var(--info-tint);color:var(--info-text)}
.badge.badge-info{background:var(--info-tint);color:var(--info-text)}
.badge.badge-warn{background:var(--warn-tint);color:var(--warning-text)}
/* provisioning pulse (info) */
.badge.badge-info::before{animation:pulse 1.4s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}

/* ---------- File Access (SFTP + FTP/FTPS) ---------- */
/* ---------- License warning banner ---------- */
.lic-banner{display:flex;align-items:center;gap:10px;margin:0 0 16px;padding:12px 14px;border-radius:12px;
  border:1px solid var(--warning);background:var(--warn-tint);color:var(--text);font-size:14px;line-height:1.45}
.lic-banner.lic-bad{border-color:var(--danger);background:var(--danger-tint)}
.lic-banner-ic{font-size:18px;line-height:1;flex:0 0 auto}
.lic-banner-text{flex:1 1 auto}
.lic-banner-text b{font-weight:700}
.lic-banner .btn-sm{flex:0 0 auto}
.lic-banner-x{flex:0 0 auto;background:transparent;border:none;color:var(--text-muted);font-size:20px;line-height:1;cursor:pointer;padding:0 4px}
.lic-banner-x:hover{color:var(--text)}

/* ---------- File Access (SFTP + FTP/FTPS) ---------- */
.fa-section{border:1px solid var(--border);border-radius:12px;padding:12px 14px;background:var(--surface-2)}
.fa-title{display:flex;align-items:center;gap:8px;margin-bottom:4px}
.btn-xs{margin-left:8px;padding:6px 12px;font-size:12.5px;font-weight:600;border-radius:8px;border:1px solid var(--border);background:transparent;color:var(--text);cursor:pointer}
.btn-xs:hover{border-color:var(--brand);color:var(--brand-strong)}
/* Stopping nginx or MariaDB takes every site on the server offline, so it
   must not look like the benign Restart beside it. Same size, different
   weight — the danger colour the rest of the panel already uses. */
.btn-xs.danger{color:var(--danger-text);border-color:var(--danger-tint);background:var(--danger-tint)}
.btn-xs.danger:hover{border-color:var(--danger);color:var(--danger-text)}
b.mono .danger,.mono .danger,span.danger{color:var(--danger-text)}

/* ---------- Modals ---------- */
.modal{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;padding:20px;background:var(--scrim);backdrop-filter:blur(2px)}
.modal-box{background:var(--glass);border:1px solid var(--border);border-radius:16px;box-shadow:var(--shadow);width:min(560px,100%);max-height:88dvh;overflow:auto;padding:20px 22px}
.modal-box.fm-box{width:min(820px,100%)}
.modal-box.editor-box{width:min(860px,100%)}
.modal-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:16px;font-size:16px;font-weight:700}
.modal-head strong{font-weight:700}
.modal-actions{display:flex;align-items:center;justify-content:flex-end;gap:12px;margin-top:16px}
.db-body{display:block}
.breadcrumb{margin-top:6px;font-size:12.5px;color:var(--text-muted)}
.breadcrumb .link{padding:2px 5px;font-weight:500}
.fm-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:12px 0}
.fm-toolbar .fm-prompt{width:auto;flex:1;min-width:180px}
/* Controls share a base rule of width:100%, which is right inside a form column
   and wrong in a flex toolbar: the bulk-action select stretched to 1330px and
   read as a banner rather than a chooser. Size to content here. */
.fm-toolbar select{width:auto;flex:0 0 auto;min-width:150px}
.fm-msg{font-size:13px;color:var(--text-muted)}
.fm-msg.error{color:var(--danger-text)}
/* DNSSEC: per-zone signing panel inside the DNS modal */
.dnssec{margin-top:16px;padding:14px 16px;border:1px solid var(--border);border-radius:12px;background:var(--surface-2)}
.dnssec-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
.dnssec-title{display:flex;align-items:center;gap:10px}
.dnssec .ds-box{margin:8px 0 10px;padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:var(--surface);font-size:12.5px;white-space:pre-wrap;word-break:break-all;overflow-x:auto}
.dnssec p{margin:8px 0}
/* Email depth: inline autoresponder + filters editors, catch-all */
.depth-editor{margin:12px 0;padding:12px 14px;border:1px solid var(--border);border-radius:10px;background:var(--surface-2)}
.depth-editor[hidden]{display:none}
.depth-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px}
.depth-check{display:inline-flex;align-items:center;gap:7px;font-size:13.5px;color:var(--text);cursor:pointer}
.depth-check input{width:auto;flex:none;min-width:0}
.depth-editor textarea.fm-prompt{width:100%;flex:none;min-height:88px;resize:vertical}
.flt-row{margin:8px 0}
.flt-row .fm-prompt{flex:none}
.btn-sm.ghost{background:transparent;color:var(--brand-strong);border:1px solid var(--border)}
.btn-sm.ghost:hover{background:var(--surface);border-color:var(--brand)}
/* Billing email templates: one editable block per dunning event, with a copyable
   list of placeholder tokens. */
.emailtpl-chips{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 12px}
.emailtpl-chip{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;padding:2px 8px;border:1px solid var(--border-strong);border-radius:999px;background:var(--surface-2);color:var(--text)}
.emailtpl-block{margin:10px 0;padding:0 16px;border:1px solid var(--border);border-radius:12px;background:var(--surface-2)}
.emailtpl-block>summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:10px;padding:13px 0;font-size:14.5px;font-weight:600;color:var(--text)}
.emailtpl-block>summary::-webkit-details-marker{display:none}
.emailtpl-block[open]>summary{border-bottom:1px solid var(--border);margin-bottom:12px}
.emailtpl-name{flex:1}
.emailtpl-state{font-size:11px;font-weight:600;letter-spacing:.02em;color:var(--text-muted);border:1px solid var(--border-strong);border-radius:999px;padding:1px 9px}
.emailtpl-state.is-custom{color:var(--brand-strong);border-color:var(--brand-strong)}
.emailtpl-block .emailtpl-desc{margin:0 0 10px;font-size:12.5px;color:var(--text-muted)}
.emailtpl-block .field{margin-bottom:10px}
.emailtpl-block textarea{width:100%;min-height:96px;resize:vertical}
.emailtpl-actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.emailtpl-msg{font-size:13px;color:var(--text-muted)}
.emailtpl-msg.error{color:var(--danger-text)}
/* Log viewer: segmented Access/Error toggle + scrollable monospace pane */
.seg{display:inline-flex;border:1px solid var(--border);border-radius:8px;overflow:hidden}
.seg .btn-sm{border:none;border-radius:0;margin:0}
.seg .btn-sm+.btn-sm{border-left:1px solid var(--border)}
.seg .btn-sm[aria-pressed="true"]{background:var(--brand-strong);color:var(--brand-contrast)}
.seg .btn-sm[aria-pressed="true"]:hover{color:var(--brand-contrast)}
/* --border, not --border-strong: a read-only log pane is not an interactive
   control, so its edge is decoration and a 3:1 outline would only add noise. */
.logs-pane{margin:0;max-height:52vh;overflow:auto;background:var(--surface-2);color:var(--text);border:1px solid var(--border);border-radius:8px;padding:12px 14px;font-size:12.5px;line-height:1.5;white-space:pre-wrap;word-break:break-word;tab-size:4}
.logs-pane .logs-empty{color:var(--text-muted)}
/* Visitor statistics (GoAccess summary) */
.stats-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin:14px 0}
.stats-strip .stat{padding:12px 14px}
.stats-strip .stat .v{font-size:20px}
.stats-day{margin:6px 0 4px}
.stats-day h3,.stat-table h3{font-size:13px;font-weight:600;margin:0 0 8px;color:var(--text)}
.stat-bars{display:flex;align-items:flex-end;gap:6px;height:120px;padding:6px 2px 0;background:var(--surface-2);border:1px solid var(--border);border-radius:8px}
.stat-barcol{flex:1 1 0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%;min-width:0}
.stat-barfill{width:70%;max-width:22px;min-height:3px;background:var(--brand);border-radius:3px 3px 0 0;transition:height .2s}
.stat-barlab{font-size:11px;color:var(--text-muted);margin-top:5px;white-space:nowrap}
.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px;margin-top:14px}
.stat-table table{width:100%;border-collapse:collapse;font-size:12.5px}
.stat-table th{text-align:left;font-weight:600;color:var(--text-muted);padding:6px 8px;border-bottom:1px solid var(--border)}
.stat-table th.num,.stat-table td.num{text-align:right;white-space:nowrap}
.stat-table td{padding:6px 8px;border-bottom:1px solid var(--border)}
.stat-table tbody tr:last-child td{border-bottom:none}
.stat-label{position:relative;max-width:0;width:100%}
.stat-bar{position:absolute;left:0;top:2px;bottom:2px;background:var(--brand-tint);border-radius:4px;z-index:0}
.stat-txt{position:relative;z-index:1;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* PHP settings (php.ini) card */
.phpini-card{margin-top:16px;padding-top:14px;border-top:1px solid var(--border)}
.phpini-grid{display:grid;grid-template-columns:1fr;gap:10px;margin:10px 0}
@media(min-width:560px){.phpini-grid{grid-template-columns:1fr 1fr}}
.phpini-row{display:grid;grid-template-columns:1fr;gap:3px;align-items:baseline}
.phpini-label{font-weight:600;color:var(--text)}
.phpini-row input,.phpini-row select{width:100%}
.phpini-hint{margin-top:2px}
/* PHP extensions: grouped checkbox list (per-account toggles) */
.phpext-groups{display:grid;grid-template-columns:1fr;gap:12px;margin:10px 0}
@media(min-width:560px){.phpext-groups{grid-template-columns:1fr 1fr}}
.phpext-group{border:1px solid var(--border);border-radius:8px;padding:8px 10px}
.phpext-group h3{margin:0 0 6px;font-size:.78rem;text-transform:uppercase;letter-spacing:.04em;color:var(--text-muted)}
.phpext-item{display:flex;align-items:center;gap:8px;padding:3px 0;font-size:.9rem}
.phpext-item input{margin:0}
.phpext-item.locked{color:var(--text-muted)}
.phpext-lock{font-size:.72rem;color:var(--text-muted)}
.phpext-empty{color:var(--text-muted)}
/* HTTPS modal: bring-your-own certificate upload (collapsible) */
.ssl-upload{margin-top:16px;padding-top:14px;border-top:1px solid var(--border)}
.ssl-upload>summary{cursor:pointer;font-weight:600;color:var(--text);list-style:none}
.ssl-upload>summary::-webkit-details-marker{display:none}
.ssl-upload>summary::before{content:"▸";display:inline-block;margin-right:8px;color:var(--text-muted);transition:transform .15s}
.ssl-upload[open]>summary::before{transform:rotate(90deg)}
.ssl-fld{display:block;margin:12px 0}
.ssl-fld>span{display:block;margin-bottom:4px;font-size:13px;font-weight:600;color:var(--text)}
.ssl-fld textarea{width:100%;resize:vertical;font-family:'IBM Plex Mono',monospace;font-size:12px;line-height:1.5}
/* File manager: multi-select checkbox column + bulk-action bar */
.fm-checkcol{width:34px;text-align:center;padding-right:0!important}
.fm-checkcol input{width:16px;height:16px;accent-color:var(--brand);cursor:pointer;margin:0;vertical-align:middle}
.fm-bulk{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0 0 12px;padding:8px 12px;background:var(--brand-tint);border:1px solid var(--border);border-radius:10px}
.fm-bulk-count{font-size:13px;font-weight:600;color:var(--text);margin-right:auto}
.fm-bulk .i-sm{vertical-align:-2px}
/* File manager: folder-tree sidebar + main pane */
.fm-layout{display:flex;gap:14px;align-items:flex-start}
.fm-tree-pane{flex:0 0 210px;max-width:210px;border:1px solid var(--border);border-radius:12px;background:var(--surface-2);padding:8px 6px;max-height:64dvh;overflow:auto}
.fm-tree-head{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);padding:2px 6px 8px}
.fm-main{flex:1 1 auto;min-width:0}
.fm-tree,.fm-tkids{list-style:none;margin:0;padding:0}
.fm-tkids{padding-left:14px}
.fm-trow{display:flex;align-items:center;gap:2px;border-radius:7px;padding:1px 4px}
.fm-trow:hover{background:var(--brand-tint)}
.fm-trow.active{background:var(--brand-tint)}
.fm-trow.active .fm-tlabel{color:var(--brand-strong);font-weight:600}
/* 24x24 is the smallest comfortable target; this was 20x22. The chevron itself
   stays the same size — only the area you can hit grows. */
.fm-ttog{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;background:none;color:var(--text-muted);cursor:pointer;flex:0 0 auto}
.fm-ttog svg{transition:transform .12s ease;transform:rotate(-90deg)}
.fm-trow.open>.fm-ttog svg{transform:rotate(0deg)}
.fm-tlabel{display:inline-flex;align-items:center;gap:6px;padding:3px 4px;border:none;background:none;color:var(--text);cursor:pointer;font-size:13px;text-align:left;min-width:0;flex:1 1 auto}
.fm-tlabel span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fm-tlabel .i-sm{color:var(--text-muted);flex:0 0 auto}
.fm-tempty{font-size:12px;padding:2px 8px}
/* File manager: Trash view */
.fm-trash-head{display:flex;align-items:center;gap:12px;margin:2px 0 12px}
.fm-trash-head strong{margin-right:auto;font-size:15px}
.viewer-body{width:100%;max-height:64dvh;overflow:auto;margin:0;white-space:pre-wrap;word-break:break-word;font-family:'IBM Plex Mono',monospace;font-size:13px;line-height:1.6;background:var(--surface-2);border:1px solid var(--border);border-radius:10px;padding:12px 14px}
.viewer-body.muted{color:var(--text-muted);font-family:inherit;white-space:normal}
#editor-text{width:100%;min-height:52dvh;resize:vertical;font-family:'IBM Plex Mono',monospace;font-size:13px;line-height:1.6}
/* Security modal: three stacked sections (Directory Privacy / IP Blocker / Hotlink) */
.sec-card{padding:16px 0;border-top:1px solid var(--border)}
.sec-card:first-child{border-top:none;padding-top:4px}
.sec-card h2{display:flex;align-items:center;gap:8px;margin:0 0 6px;font-size:15px}
.sec-textarea{width:100%;min-height:96px;resize:vertical;font-family:'IBM Plex Mono',monospace;font-size:13px;line-height:1.6}
.sec-check{display:flex;align-items:center;gap:8px;margin:8px 0;font-size:14px;cursor:pointer}
.sec-check input{width:16px;height:16px;accent-color:var(--brand);cursor:pointer}
.sec-field{display:block;font-size:13px;color:var(--text-muted);margin:8px 0}
.sec-field .fm-prompt{display:block;width:100%;margin-top:4px}
.db-secret{background:var(--info-tint);border:1px solid var(--border);border-radius:12px;padding:14px 16px;margin-bottom:14px;font-size:13px}
.creds{margin-top:8px;line-height:1.9}
.creds code,.db-body code{background:var(--surface-2);border:1px solid var(--border);padding:.12rem .45rem;border-radius:6px;font-size:12.5px;overflow-wrap:anywhere}
.backup-codes{display:flex;flex-wrap:wrap;gap:.45rem .6rem}
.backup-codes code{font-size:.95rem;letter-spacing:.5px}

/* ---------- Footer / misc ---------- */
.foot{padding:20px 32px;color:var(--text-muted);font-size:12px;font-family:'IBM Plex Mono',monospace;letter-spacing:.04em}

/* ---------- Responsive ---------- */
@media (max-width:900px){
  /* Sidebar becomes an off-canvas drawer opened by the topbar menu button. */
  .menu-btn{display:grid}
  .shell{display:block}
  .sidebar{position:fixed;left:0;top:0;height:100dvh;z-index:70;width:250px;
    transform:translateX(-100%);transition:transform .25s ease;border-right:1px solid var(--side-border)}
  .sidebar.open{transform:translateX(0)}
  #side-scrim.on{display:block;position:fixed;inset:0;background:var(--scrim);z-index:65}
  .topsearch{display:none}
  .stats{grid-template-columns:repeat(2,1fr)}
  .main{padding:20px 16px}
  .topbar{padding:0 14px;gap:10px}
  .hello{align-items:flex-start}
  /* File manager: stack the folder tree above the listing on small screens. */
  .fm-layout{flex-direction:column}
  .fm-tree-pane{flex:none;max-width:none;width:100%;max-height:180px}
}
@media (max-width:560px){
  .create-form .field,.create-form .field.grow{flex:1 1 100%;min-width:0}
  .create-form button{width:100%}
  .stats{grid-template-columns:1fr 1fr}
  .role{display:none}
}

/* ---------- Toasts ---------- */
.toast-region{position:fixed;right:18px;bottom:18px;z-index:1000;display:flex;flex-direction:column;gap:8px;max-width:min(360px,calc(100vw - 36px));pointer-events:none}
.toast{pointer-events:auto;padding:11px 15px;border-radius:11px;font-size:13.5px;line-height:1.4;color:var(--text);background:var(--surface);border:1px solid var(--border);box-shadow:0 6px 24px rgba(0,0,0,.16);animation:toast-in .2s ease-out}
.toast-success{border-color:var(--success);background:var(--success-tint)}
.toast-error{border-color:var(--danger);background:var(--danger-tint)}
.toast-out{opacity:0;transform:translateY(6px);transition:opacity .2s,transform .2s}
@keyframes toast-in{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}

/* ---------- Motion preferences ---------- */
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{transition:none!important;animation:none!important}
  .tile:hover{transform:none}
  .toast{animation:none}
}

/* ---- Manual page (bilingual built-in docs) ------------------------------- */
.man-wrap{max-width:960px;margin:0 auto}
.man-sub{margin:0 0 14px;max-width:70ch}
.man-toolbar{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.man-search-box{display:flex;align-items:center;gap:9px;flex:1;min-width:220px;height:44px;
  /* --border-strong: this box IS the search control (its input is deliberately
     borderless), so the wrapper edge is the only thing marking the field.
     Measured 1.18:1 against the page. Same defect as the inputs, and it was
     missed because CONTROL_BOUNDARY walks up only when the input has no edge
     at all — here the wrapper HAD one, it was just invisible. */
  background:var(--surface-2);border:1px solid var(--border-strong);border-radius:10px;padding:0 12px}
.man-search-box:focus-within{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-tint)}
.man-search-box .i{width:18px;height:18px;color:var(--text-muted);flex:none}
.man-search-box input{flex:1;min-width:0;height:100%;background:none;border:0;outline:none;color:var(--text);font:inherit}
.man-langs .btn-sm{min-width:56px;height:44px;padding:0 14px;font-weight:600;cursor:pointer;background:var(--surface);color:var(--text)}
.man-count{margin:10px 0 2px;font-variant-numeric:tabular-nums}
.man-group{margin:24px 0 0}
.man-group h2{display:flex;align-items:center;gap:10px;font-size:1.04rem;margin:0 0 10px}
.man-gic{width:30px;height:30px;border-radius:8px;background:var(--brand-tint);color:var(--brand-strong);display:grid;place-items:center;flex:none}
.man-gic .i{width:16px;height:16px}
.man-items{display:grid;gap:10px}
.man-item{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:14px 16px}
.man-item-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px;flex-wrap:wrap}
.man-item h3{margin:0;font-size:.98rem}
.man-path{font-size:.72rem;font-family:ui-monospace,Consolas,monospace;color:var(--text-muted);
  background:var(--surface-2);border:1px solid var(--border);border-radius:6px;padding:3px 8px;white-space:nowrap}
.man-item p{margin:8px 0 0;line-height:1.62;color:var(--text);max-width:78ch}
.man-tip{font-size:.86rem;border-left:3px solid var(--brand);padding-left:10px;color:var(--text-muted)}
.man-empty{padding:26px 0;text-align:center}
@media (max-width:640px){ .man-path{white-space:normal} }

/* ============================================================================
   Nimbopanel redesign polish (2026-08-19) — flat Modern Dev-Tool, Palette B.
   Augment-only: refines the inherited layout into the Nimbopanel brand look
   (matches the storefront/artifact) WITHOUT changing any structure/IDs/JS, so
   every function is preserved. Glassmorphism was removed above; these rules add
   crisp accents, hover feedback and identity.
   ============================================================================ */

/* Stratus: keep the "blue" accent on the brand family (no leftover indigo). */
.acc-blue{--c:var(--brand);--c-tint:var(--brand-tint)}

/* KPI stat cards: lift + accent edge on hover (alive, not static). */
.stat,.kstat{transition:border-color .16s,transform .16s,box-shadow .16s}
.stat:hover,.kstat:hover{transform:translateY(-2px);
  border-color:color-mix(in srgb,var(--brand) 45%,var(--border));
  box-shadow:0 10px 26px -18px var(--brand)}

/* Tool tiles (the heart of the UI): accent ring + soft indigo glow on hover. */
.tile{border-radius:14px}
.tile:hover{border-color:color-mix(in srgb,var(--brand) 42%,var(--border));
  box-shadow:0 8px 20px -14px var(--brand)}

/* Section (group) cards: a top accent hairline that lights up on hover — the
   Nimbopanel signature, clipped by the card's rounded corners. */
.group{position:relative}
.group::after{content:"";position:absolute;inset:0 0 auto 0;height:2px;pointer-events:none;
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--brand) 60%,transparent),transparent);
  opacity:0;transition:opacity .2s}
.group:hover::after{opacity:.9}
.group{transition:border-color .16s}
.group:hover{border-color:color-mix(in srgb,var(--brand) 30%,var(--border))}

/* Content cards + panels: consistent crisp radius. */
.card,.sys-card,.acct-card,.side-card{border-radius:14px}

/* Topbar: crisp hairline separation instead of a frosted blur. */
.topbar{box-shadow:0 1px 0 var(--border)}

/* Sidebar: flatten the gradient to a single deep surface for a calmer, more
   premium field (keeps the accent rail identity from above). */
.sidebar{background:var(--side-bg2)}

/* Page heading: tighter, more confident typographic tracking. */
.page-h{letter-spacing:-.02em}

/* Feature Manager: the package editor's toggle grid (module: feature-manager). */
.pkg-feat-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px;width:100%}
.pkg-feat-cat{border:1px solid var(--border);border-radius:10px;padding:10px 12px;margin:0;min-width:0}
.pkg-feat-cat legend{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);padding:0 6px}
.pkg-feat-item{display:flex;align-items:center;gap:8px;font-size:.86rem;padding:3px 0;cursor:pointer}
.pkg-feat-item input{width:auto;margin:0;flex:none}

/* Security Advisor: distinct critical badge (red) vs warning (amber). */
/* Tokens, like every other badge (see .badge-ok / .badge-warn above). The raw
   #ef4444 measured 3.10:1 in light and 3.92:1 in dark against a 4.5:1 minimum —
   failing in BOTH themes, and failing since long before the palette work. It
   survived because it only appears when there is a critical cPHulk alert, and no
   fixture ever produces that state: the contrast detector was fine, the fixtures
   never reached the element. Now 4.66:1 light / 6.13:1 dark. */
.badge-crit{background:var(--danger-tint);color:var(--danger-text);border:1px solid var(--danger)}

/* Accounts row "More ▾" dropdown — groups the less-common per-account tools so the
   row isn't overwhelming for a novice admin. */
.row-more{display:inline-block;position:relative}
.row-more>summary{list-style:none;cursor:pointer;color:var(--brand-strong);display:inline;font-size:inherit}
.row-more>summary::-webkit-details-marker{display:none}
.row-more[open]>summary{font-weight:600}
.row-more-pop{position:absolute;right:0;top:calc(100% + 4px);z-index:30;background:var(--surface);
  border:1px solid var(--border);border-radius:9px;box-shadow:0 10px 28px rgba(0,0,0,.22);
  padding:6px;min-width:160px;display:flex;flex-direction:column;gap:1px;text-align:left}
.row-more-pop .link{display:block;padding:6px 9px;white-space:nowrap;border-radius:6px}
.row-more-pop .link:hover{background:var(--surface-2)}

/* Billing — the shareable customer signup link (the "where do I send buyers?"
   answer), surfaced so the operator never has to guess the URL. */
.signup-link-box{margin:2px 0 16px;padding:13px 15px;border:1px solid var(--border);border-radius:12px;
  background:var(--brand-tint)}
.signup-link-box .sl-label{font-weight:600;font-size:.92rem;margin:0 0 9px}
/* An address that does not answer yet must not wear the same confident green as
   one that does — the reseller is being told whether to hand it to customers. */
.signup-link-box.pending{border-color:var(--warning);background:transparent}
.signup-link-box .sl-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.signup-link-box .sl-url{flex:1 1 260px;min-width:0;overflow-x:auto;white-space:nowrap;
  font-family:ui-monospace,Consolas,monospace;font-size:.86rem;color:var(--text);
  background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:8px 11px}
.signup-link-box a.btn-sm{display:inline-flex;align-items:center;text-decoration:none}

/* Dashboard first-run checklist — guides a novice from a fresh license to taking
   payments. Auto-hides once a gateway is live AND a plan exists. */
.getting-started{margin:0 0 18px;padding:16px 18px;border:1px solid var(--border);border-radius:16px;
  background:linear-gradient(180deg,var(--brand-tint),transparent);box-shadow:var(--shadow)}
.getting-started .gs-head{display:flex;align-items:flex-start;gap:12px;justify-content:space-between}
.getting-started h2{margin:0;font-size:1.06rem;border:0;padding:0}
.getting-started .gs-steps{list-style:none;margin:14px 0 0;padding:0;display:grid;gap:8px;counter-reset:gs}
.getting-started .gs-step{display:flex;align-items:center;gap:12px;padding:11px 13px;border:1px solid var(--border);
  border-radius:12px;background:var(--surface)}
.getting-started .gs-dot{width:22px;height:22px;flex:none;border-radius:50%;position:relative;
  box-shadow:inset 0 0 0 2px var(--border);counter-increment:gs}
.getting-started .gs-dot::after{content:counter(gs);position:absolute;inset:0;display:grid;place-items:center;
  font-size:.72rem;font-weight:700;color:var(--text-muted)}
.getting-started .gs-step.done .gs-dot{background:var(--brand);box-shadow:none}
.getting-started .gs-step.done .gs-dot::after{content:"✓";color:var(--brand-contrast)}
.getting-started .gs-step.done .gs-text strong{text-decoration:line-through;color:var(--text-muted)}
.getting-started .gs-text{display:flex;flex-direction:column;gap:1px;min-width:0}
.getting-started .gs-go{margin-left:auto;flex:none;text-decoration:none;display:inline-flex;align-items:center}
.getting-started .gs-step.done .gs-go{visibility:hidden}
@media (max-width:560px){ .getting-started .gs-go{margin-left:0} .getting-started .gs-step{flex-wrap:wrap} }

/* Server Setup Checklist (operator onboarding) — live DNS/HTTPS/mail readiness,
   rendered from /v1/system/setup-checklist. Reuses the getting-started card look;
   the status dot is colour-coded ok / warn / todo instead of a step number. */
.server-setup{margin:0 0 18px;padding:16px 18px;border:1px solid var(--border);border-radius:16px;
  background:linear-gradient(180deg,var(--brand-tint),transparent);box-shadow:var(--shadow)}
.server-setup .gs-head{display:flex;align-items:flex-start;gap:12px;justify-content:space-between}
.server-setup h2{margin:0;font-size:1.06rem;border:0;padding:0}
.server-setup .ss-head-actions{display:flex;gap:8px;align-items:center;flex:none}
#ss-body{display:grid;gap:8px;margin-top:14px}
.ss-item{display:flex;align-items:flex-start;gap:12px;padding:11px 13px;border:1px solid var(--border);
  border-radius:12px;background:var(--surface)}
.ss-dot{width:12px;height:12px;flex:none;border-radius:50%;margin-top:5px;box-shadow:inset 0 0 0 2px var(--border)}
.ss-ok .ss-dot{background:var(--brand);box-shadow:none}
/* --warning, not a raw #e0a458: that measured 2.18:1 on the light surface against
   the 3:1 a status indicator needs. Only visible while server setup is incomplete,
   which no fixture reproduces. Now 3.57:1 light / 7.30:1 dark. */
.ss-warn .ss-dot{background:var(--warning);box-shadow:none}
.ss-todo .ss-dot{background:transparent}
.ss-text{display:flex;flex-direction:column;gap:3px;min-width:0}
.ss-ok .ss-text strong{color:var(--text-muted)}
.ss-opt{margin-left:8px;font-size:.66rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;
  color:var(--text-muted);border:1px solid var(--border);border-radius:6px;padding:0 5px;vertical-align:middle}
.ss-records{display:flex;flex-wrap:wrap;gap:8px;margin-top:5px}
.ss-rec{display:inline-flex;align-items:center;gap:6px;min-width:0;max-width:100%}
.ss-rec code{background:var(--brand-tint);border:1px solid var(--border);border-radius:6px;padding:2px 7px;font-size:12px;overflow-wrap:anywhere;max-width:100%}
.ss-resolved{margin-top:2px}
.ss-steps{margin-top:6px}
.ss-steps summary{cursor:pointer}
.ss-steps ol{margin:6px 0 4px 18px;padding:0;display:grid;gap:4px;color:var(--text)}
.ss-from{margin:8px 0 0;font-style:italic}

/* Reseller Allocation page — "selling checklist" so the page guides, not just
   restates the dashboard's one-line allocation. */
.rs-nextsteps{margin:16px 0 0;padding:13px 15px;border:1px solid var(--border);border-radius:12px;background:var(--brand-tint)}
.rs-nextsteps-h{font-weight:600;font-size:.92rem;margin:0 0 8px}
.rs-nextsteps ul{margin:0;padding-left:18px;display:grid;gap:6px}
.rs-nextsteps li{font-size:13px;line-height:1.5}
.ar-note{margin-top:9px;padding:9px 11px;border-radius:9px;background:var(--brand-tint);
  border:1px solid var(--border);font-size:12.5px;line-height:1.5;color:var(--text)}
/* Nameserver card, zone warning: pointing ns1/ns2 at this server is only half
   the setup — a domain delegated here with no zone is refused by BIND and goes
   dark. Both variants read from the theme tokens, so they follow light/dark. */
.ar-note.warn{background:var(--warn-tint);border-color:var(--warning)}
.ar-note.danger{background:var(--danger-tint);border-color:var(--danger)}

/* Reseller auto-redirect interstitial — sends a white-label reseller from the
   shared panel to their own panel.<domain>, with an escape hatch. */
.panel-redirect{position:fixed;inset:0;z-index:200;display:grid;place-items:center;
  background:var(--scrim);backdrop-filter:blur(3px)}
.panel-redirect .pr-card{max-width:440px;width:calc(100% - 40px);background:var(--surface);
  border:1px solid var(--border);border-radius:16px;box-shadow:0 24px 60px rgba(0,0,0,.4);padding:22px 24px}
.panel-redirect .pr-card h2{margin:0 0 8px;border:0;padding:0}
.panel-redirect .pr-card p{margin:0;color:var(--text);line-height:1.55}
.panel-redirect .pr-actions{display:flex;align-items:center;gap:14px;margin-top:16px}
.panel-redirect .pr-actions .btn{text-decoration:none}
.ss-domain{margin-top:12px;padding-top:10px;border-top:1px dashed var(--border)}

.acct-more{margin-top:10px;text-align:center}
.acct-more[hidden]{display:none}

/* Web terminal container: always dark (a terminal has its own colour scheme,
   independent of the panel theme). Uses the always-on ink-teal token so the
   contrast checker measures xterm's light text against a dark ground. */
.term-screen{ background:var(--side-bg1); }

/* Apache directives manager (Handlers / MIME / Index Manager) */
.apachedir-sec{margin:14px 0;padding-top:12px;border-top:1px solid var(--border)}
.apachedir-sec h3{margin:0 0 8px}
.apachedir-row{display:flex;align-items:center;gap:8px;margin-bottom:6px;flex-wrap:wrap}
.apachedir-row .ad-arrow{color:var(--text-muted)}
.btn-sm.ghost{background:transparent;border:1px solid var(--border-strong);color:var(--text)}
