:root {
  /* Matches the public kepixel.online brand tokens (wp-content/themes/kepixel/assets/css/theme.css)
     so the client dashboard, admin dashboard and marketing site read as one product. */
  --canvas: #f6faff;
  --surface: #ffffff;
  --surface-soft: #f7faff;
  --text: #22365a;
  --strong: #081426;
  --muted: #5d6f86;
  --border: #cbd8e8;
  --brand-950: #071a3d;
  --brand-900: #0b3fae;
  --brand-700: #2367f2;
  --brand-600: #3d7ff5;
  --brand-100: #eef7ff;
  --accent: #2367f2;
  --success: #059669;
  --warning: #9a5b00;
  --danger: #b4233b;
  --shadow: 0 16px 44px rgba(7, 26, 61, .09);
  --shadow-xs: 0 1px 2px rgba(7, 26, 61, 0.04);
  --shadow-sm: 0 6px 18px rgba(7, 26, 61, 0.055);
  --shadow-md: 0 18px 44px rgba(7, 26, 61, 0.09);
  --shadow-lg: 0 28px 72px rgba(7, 26, 61, 0.14);
  --radius-sm: 10px;
  --radius-md: 18px;
  --radius-lg: 24px;
  --radius-xl: 26px;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --font-heading: "Space Grotesk", "IBM Plex Sans Arabic", sans-serif;
  /* Collapsed-by-default, expands on hover/focus -- see the sidebar rules below. */
  --sidebar: 76px;
  --sidebar-expanded: 268px;
  /* Status/badge/notice/trend tints -- each preserves the exact hex app.css
     already used before this token pass (including pre-existing drift
     between e.g. notice vs status greens), so routing through these is a
     token-only change with zero visual diff. */
  --success-bg: #e6f7ed;
  --success-ink: #17673b;
  --success-notice-bg: #ecfaf2;
  --success-notice-border: #bfe8cf;
  --warning-bg: #fff5d9;
  --warning-ink: #765711;
  --warning-notice-bg: #fff8e8;
  --warning-notice-border: #efd99c;
  --warning-notice-ink: #7a5811;
  --danger-bg: #ffe9e9;
  --danger-ink: #8b2b2b;
  --danger-notice-bg: #fff0f0;
  --danger-notice-border: #f0c1c1;
  --neutral-bg: #edf1f7;
  --neutral-ink: #52617b;
}

h1, h2, h3 {
  font-family: var(--font-heading);
}

html[dir="rtl"] body,
html[dir="rtl"] h1,
html[dir="rtl"] h2,
html[dir="rtl"] h3 {
  font-family: "IBM Plex Sans Arabic", sans-serif;
}

html,
body {
  background-color: var(--canvas);
}

body {
  background:
    radial-gradient(circle at 88% -12%, rgba(31, 111, 235, .14), transparent 30rem),
    linear-gradient(180deg, #ffffff 0, var(--canvas) 24rem);
}

::selection {
  color: var(--brand-950);
  background: var(--brand-100);
}

button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible {
  outline-color: rgba(31, 111, 235, .28);
}

.sidebar {
  background: linear-gradient(180deg, var(--brand-950), var(--brand-900));
}

.metric-card--brand {
  background: var(--brand-900);
}

.progress span {
  background: linear-gradient(90deg, var(--brand-700), var(--brand-600));
}

.secure-dot {
  background: #ffffff;
  box-shadow: 0 0 0 5px rgba(255, 255, 255, .14);
}

.button--primary {
  background: var(--brand-700);
  color: #ffffff;
}

.button--primary:hover {
  background: var(--brand-900);
}

input:focus,
select:focus,
textarea:focus {
  border-color: var(--brand-600);
  box-shadow: 0 0 0 3px rgba(31, 111, 235, .15);
}

.setup-step.is-current,
.directory-item.is-active {
  border-color: #8db7f7;
  box-shadow: 0 0 0 3px rgba(31, 111, 235, .10);
}

.directory-item.is-active {
  background: #f2f7ff;
}

.progress-pill,
.privacy-note,
.platform-icon,
.avatar {
  background: var(--brand-100);
  color: var(--brand-700);
}

.notice--info {
  background: #eef5ff;
  border-color: #bfd5f7;
  color: var(--brand-900);
}

/* Dashboard density: keep copy readable without truncating controls */
body {
  font-size: 15px;
}

.main-content {
  width: min(1520px, calc(100% - 40px));
}

.page-heading h1 {
  font-size: clamp(1.7rem, 3.2vw, 2.55rem);
}

.page-heading p,
.panel-heading p,
.auth-heading p,
.required-note,
.field-hint {
  font-size: .9rem;
  line-height: 1.55;
}

label {
  font-size: .84rem;
  gap: 6px;
}

input,
select,
textarea {
  font-size: .9rem;
  padding: 10px 11px;
  min-width: 0;
}

select {
  text-overflow: ellipsis;
}

.form-grid,
.credential-grid,
.destination-form-grid {
  gap: 12px;
}

.panel {
  padding: 20px;
}

.panel-heading h2,
.subpanel h3 {
  font-size: 1.05rem;
}

.button {
  font-size: .9rem;
}

.auth-heading h1 {
  font-size: clamp(1.7rem, 3.2vw, 2.45rem);
}

.auth-visual h2 {
  font-size: clamp(1.65rem, 3.2vw, 2.7rem);
}

@media (max-width: 980px) {
  .form-grid,
  .credential-grid,
  .destination-form-grid {
    grid-template-columns: 1fr;
  }
}

/* Google-Analytics-style sidebar: collapsed to icons by default, expands on
   hover/keyboard-focus, or permanently via the pin toggle (.is-pinned).
   .sidebar is position:fixed in app.css, so it never reflows the page on
   its own -- .workspace's margin-left (below) is what actually reserves
   room for it, and now tracks whichever width state is active instead of
   staying fixed at the collapsed rail width. Previously it stayed pinned
   to the collapsed width in EVERY state, so an expanded sidebar (hover or
   pinned) simply sat on top of real dashboard content with no visual
   acknowledgment that it had opened -- reported live, 2026-10-15, as the
   sidebar "entering on top of" and corrupting the dashboard layout,
   worst with .is-pinned since that is meant to be a permanent, livable
   state, not a momentary overlay. Hover and pin now share this one
   reflow mechanism rather than one reflowing and the other overlaying,
   per that same report's explicit requirement.

   Labels fade in via opacity+max-width AFTER the width expansion mostly
   finishes (transition-delay), not an instant display:none -> grid/inline
   pop -- that instant pop was rendering as visibly truncated text (e.g.
   "KePixel" clipped to "KEPIX") for the ~180ms the sidebar was still
   mid-width-transition, since `display` cannot itself be animated/delayed.
   This reveals text only once there is actual room for it, and hides it
   again before the sidebar starts narrowing back down.

   Collapsing back down is deliberately delayed (transition-delay below,
   NOT on the expand direction, which stays instant/responsive) -- Mostafa
   flagged live that the sidebar was flickering open/closed repeatedly
   near the collapsed edge. A 76px collapsed rail is a narrow hover target
   right next to the real page content, so ordinary mouse jitter crossing
   that boundary while reaching for something nearby re-triggers
   mouseenter/mouseleave rapidly. Delaying the shrink-back gives a grace
   window: leaving and immediately re-entering within it just cancels the
   pending collapse instead of visibly flickering, and only actually
   collapses once the mouse has genuinely moved away and stayed away.
   .workspace's margin-left below uses this identical timing so the page
   reflow stays visually synchronized with the sidebar's own width
   animation in both directions, rather than jumping ahead of or lagging
   behind it. */
.sidebar {
  overflow: hidden;
  z-index: 40;
  transition: width .22s ease .22s, box-shadow .22s ease .22s, overflow 0s .44s;
}

.sidebar:hover,
.sidebar:focus-within,
.sidebar.is-pinned {
  width: var(--sidebar-expanded);
  box-shadow: 16px 0 48px rgba(7, 26, 61, .28);
  overflow: visible;
  transition: width .22s ease, box-shadow .22s ease, overflow 0s;
}

/* min-width:821px matches app.css's mobile cutoff (.sidebar{display:none}
   at max-width:820px) exactly. .is-pinned is a real DOM class that can be
   server-rendered even while the sidebar is display:none on mobile (it's
   a persisted preference, not a hover state) -- :has() matches on the
   DOM/class regardless of visibility, so without this guard a pinned
   account would get an unwanted 268px margin-left pushing the whole
   mobile dashboard off-screen, overriding app.css's own
   .workspace{margin-left:0} mobile rule purely on higher specificity. */
@media (min-width: 821px) {
  .workspace {
    transition: margin-left .22s ease .22s;
  }

  .client-layout:has(.sidebar:hover) .workspace,
  .client-layout:has(.sidebar:focus-within) .workspace,
  .client-layout:has(.sidebar.is-pinned) .workspace {
    margin-left: var(--sidebar-expanded);
    transition: margin-left .22s ease;
  }
}

/* Pin toggle lives in its own row with the brand logo, not inside the
   .brand <a> itself (a button can't nest inside an anchor). .brand keeps
   governing its own icon/label layout exactly as before; it just grows to
   fill the row instead of the whole sidebar width. */
.sidebar-top {
  display: flex;
  align-items: center;
  gap: 8px;
}

.sidebar-top .brand {
  flex: 1;
  min-width: 0;
}

.sidebar-pin-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: rgba(255, 255, 255, .1);
  color: #bfc8df;
  flex-shrink: 0;
}

.sidebar-pin-toggle:hover,
.sidebar-pin-toggle[aria-pressed="true"] {
  background: rgba(255, 255, 255, .18);
  color: #fff;
}

.sidebar-pin-toggle span {
  width: 16px;
  height: 16px;
}

.sidebar-pin-toggle svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.sidebar-pin-toggle[aria-pressed="true"] svg {
  fill: currentColor;
}

.brand {
  justify-content: center;
}

.sidebar:hover .brand,
.sidebar:focus-within .brand,
.sidebar.is-pinned .brand {
  justify-content: flex-start;
}

/* .sidebar-pin-toggle is hidden (zero width) in the collapsed/unhovered
   state, same as the labels below -- it only needs to be reachable while
   the sidebar is already expanded (hover reveals it to pin; once pinned
   the sidebar stays expanded, so it stays reachable to unpin). */
.brand span,
.client-context,
.nav-link strong,
.sidebar-nav .profile-popover__group-label,
.sidebar-footer,
.sidebar-pin-toggle {
  opacity: 0;
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  /* Matches .sidebar's own .44s collapse grace-period (see its comment
     above) so labels only start fading out once the sidebar itself has
     actually begun shrinking, not the instant the mouse first leaves. */
  transition: opacity .12s ease .44s, max-width .12s ease .44s;
}

.client-context,
.sidebar-footer {
  max-height: 0;
  margin-block: 0;
  transition: opacity .12s ease .44s, max-width .12s ease .44s, max-height .12s ease .44s, margin-block .12s ease .44s;
}

.sidebar:hover .brand span,
.sidebar:hover .client-context,
.sidebar:hover .nav-link strong,
.sidebar:hover .sidebar-nav .profile-popover__group-label,
.sidebar:hover .sidebar-footer,
.sidebar:hover .sidebar-pin-toggle,
.sidebar:focus-within .brand span,
.sidebar:focus-within .client-context,
.sidebar:focus-within .nav-link strong,
.sidebar:focus-within .sidebar-nav .profile-popover__group-label,
.sidebar:focus-within .sidebar-footer,
.sidebar:focus-within .sidebar-pin-toggle,
.sidebar.is-pinned .brand span,
.sidebar.is-pinned .client-context,
.sidebar.is-pinned .nav-link strong,
.sidebar.is-pinned .sidebar-nav .profile-popover__group-label,
.sidebar.is-pinned .sidebar-footer,
.sidebar.is-pinned .sidebar-pin-toggle {
  opacity: 1;
  max-width: 220px;
  transition: opacity .16s ease .14s, max-width .16s ease .14s;
}

.sidebar:hover .client-context,
.sidebar:hover .sidebar-footer,
.sidebar:focus-within .client-context,
.sidebar:focus-within .sidebar-footer,
.sidebar.is-pinned .client-context,
.sidebar.is-pinned .sidebar-footer {
  max-height: 220px;
  transition: opacity .16s ease .14s, max-width .16s ease .14s, max-height .16s ease .14s, margin-block .16s ease .14s;
}

/* Root cause of the workspace-switcher "only 1-2 items show, cut off,
   scrolling doesn't help" report: .client-context's own collapse
   animation above caps it at max-height:220px with overflow:hidden so the
   sidebar can slide it open smoothly. That's fine for the trigger button
   at rest, but once the dropdown inside it is actually open, its real
   content (search box + up to dozens of workspace options) is clipped to
   whatever fits under 220px total -- typically one or two rows -- no
   matter how many workspaces the account has or whether the user scrolls
   the page (there's nothing wrong with the data; it's an ancestor clip).
   While the menu is open, let .client-context grow to fit it; the menu
   itself already has its own max-height+scroll (see .workspace-switcher__menu
   in client-shell.js) for genuinely long lists. */
.sidebar:hover .client-context:has(.workspace-switcher__menu:not([hidden])),
.sidebar:focus-within .client-context:has(.workspace-switcher__menu:not([hidden])),
.sidebar.is-pinned .client-context:has(.workspace-switcher__menu:not([hidden])) {
  overflow: visible;
  max-height: none;
}

/* A dim backdrop was briefly added here for the open workspace menu, on
   the premise that the sidebar deliberately overlays the dashboard. That
   premise turned out to be the actual bug (see the "Google-Analytics-style
   sidebar" comment above, now rewritten) -- the sidebar reflows the
   dashboard now, in both the hover and pinned states, so nothing sits
   underneath the expanded sidebar or its menu needing a backdrop to read
   as intentional. Removed rather than left in: a backdrop over content
   that is also cleanly reflowing away would itself look like a bug. */

.sidebar:hover .client-context,
.sidebar.is-pinned .client-context {
  margin-block-end: 20px;
}

.sidebar:hover .sidebar-footer,
.sidebar.is-pinned .sidebar-footer {
  margin-block-start: auto;
}

.sidebar:focus-within .client-context {
  margin-block-end: 20px;
}

.sidebar:focus-within .sidebar-footer {
  margin-block-start: auto;
}

.nav-link {
  justify-content: center;
}

.sidebar:hover .nav-link,
.sidebar:focus-within .nav-link,
.sidebar.is-pinned .nav-link {
  justify-content: flex-start;
}

/* "Cards like the homepage" -- the exact hover interaction kepixel.online's
   .card-3d uses (wp-content/themes/kepixel/production/css/style.css),
   scaled down for a dense dashboard (vertical lift only, no 3D tilt --
   full tilt reads as gimmicky on information-dense panels). Applied only
   to genuinely discrete, tile-like cards, never to .panel itself: a panel
   is often a full-width content container someone is reading/scrolling
   inside, and lifting the whole thing on every mouse move over it would be
   distracting, not delightful. Purely hover/focus-triggered, never on
   mount, so it never "replays" on this app's full-rerender-on-state-change
   architecture -- see the metric-card.js keep-animations-interaction-only
   note for the same reasoning applied elsewhere in this app.
*/
.metric-card,
.setup-step,
.directory-item,
.destination-card,
.verification-card,
.pending-card,
.connector-card,
.platform-card,
.detail-metrics article {
  transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease;
}

.metric-card:hover,
.setup-step:hover,
.directory-item:hover,
.destination-card:hover,
.verification-card:hover,
.pending-card:hover,
.connector-card:hover,
.platform-card:hover,
.detail-metrics article:hover {
  transform: translateY(-5px);
  box-shadow: 0 22px 54px rgba(7, 26, 61, .12);
  border-color: #bae6fd;
}

/* Non-interactive "Coming Soon"/muted cards (aria-disabled="true") never
   lift on hover -- that would imply they are clickable when they are not. */
.platform-card--coming-soon:hover,
.platform-card.is-muted:hover {
  transform: none;
  box-shadow: none;
  border-color: var(--border);
}
