/* GenUI curator — design tokens and shell.
 *
 * Both themes are first-class. The preview stage judges patterns in light and
 * dark, so the application itself must be honest in both — a dark mode that is
 * an afterthought here would hide exactly the defects the stage exists to find.
 */

:root {
  /* Neutrals carry a slight cool cast rather than being pure grey — chosen,
     not inherited from a default. */
  --bg:        #fbfbfd;
  --surface:   #ffffff;
  --surface-2: #f4f5f8;
  --border:    #e2e4ea;
  --border-strong: #c9ccd6;
  --text:      #1a1d24;
  --text-dim:  #5c6274;
  --text-faint:#8b91a3;

  --accent:      #3b5bdb;
  --accent-soft: #edf0fd;
  --accent-text: #ffffff;

  /* Semantic colour is separate from the accent and never doubles as it. */
  --ok:      #197b4f;
  --ok-soft: #e6f4ed;
  --warn:      #9a6412;
  --warn-soft: #fdf2e0;
  --crit:      #b3261e;
  --crit-soft: #fdeceb;

  --radius: 7px;
  --radius-sm: 5px;
  --shadow: 0 1px 2px rgb(20 22 30 / 6%), 0 4px 12px rgb(20 22 30 / 5%);

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;

  --sidebar-w: 232px;
  --header-h: 52px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg:        #0f1115;
    --surface:   #161920;
    --surface-2: #1d212a;
    --border:    #2a2f3a;
    --border-strong: #3d4350;
    --text:      #e8eaf0;
    --text-dim:  #a0a7b8;
    --text-faint:#6f7688;

    --accent:      #7391ff;
    --accent-soft: #1c2340;
    --accent-text: #0f1115;

    --ok:      #4ade80;
    --ok-soft: #12291d;
    --warn:      #fbbf47;
    --warn-soft: #2e2312;
    --crit:      #ff8078;
    --crit-soft: #331917;

    --shadow: 0 1px 2px rgb(0 0 0 / 40%), 0 4px 14px rgb(0 0 0 / 30%);
  }
}

/* The explicit toggle must win over the media query in BOTH directions. */
:root[data-theme="dark"] {
  --bg: #0f1115; --surface: #161920; --surface-2: #1d212a;
  --border: #2a2f3a; --border-strong: #3d4350;
  --text: #e8eaf0; --text-dim: #a0a7b8; --text-faint: #6f7688;
  --accent: #7391ff; --accent-soft: #1c2340; --accent-text: #0f1115;
  --ok: #4ade80; --ok-soft: #12291d;
  --warn: #fbbf47; --warn-soft: #2e2312;
  --crit: #ff8078; --crit-soft: #331917;
  --shadow: 0 1px 2px rgb(0 0 0 / 40%), 0 4px 14px rgb(0 0 0 / 30%);
}

:root[data-theme="light"] {
  --bg: #fbfbfd; --surface: #ffffff; --surface-2: #f4f5f8;
  --border: #e2e4ea; --border-strong: #c9ccd6;
  --text: #1a1d24; --text-dim: #5c6274; --text-faint: #8b91a3;
  --accent: #3b5bdb; --accent-soft: #edf0fd; --accent-text: #ffffff;
  --ok: #197b4f; --ok-soft: #e6f4ed;
  --warn: #9a6412; --warn-soft: #fdf2e0;
  --crit: #b3261e; --crit-soft: #fdeceb;
  --shadow: 0 1px 2px rgb(20 22 30 / 6%), 0 4px 12px rgb(20 22 30 / 5%);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.55;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; text-wrap: balance; font-weight: 620; letter-spacing: -0.011em; }
h1 { font-size: 19px; }
h2 { font-size: 15px; }
h3 { font-size: 13px; }
p  { margin: 0; }
a  { color: var(--accent); }

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

/* ---- pattern: admin-shell ------------------------------------------------ */

.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  grid-template-rows: var(--header-h) 1fr;
  grid-template-areas: "brand header" "nav content";
  height: 100vh;
  transition: grid-template-columns 160ms ease;
}

/* Collapsing is ONE token change — the grid, brand, and nav all derive their
   width from it, so there is no second source of truth to drift. */
:root[data-nav="collapsed"] { --sidebar-w: 56px; }

/* inset-inline-start, not left: parked off the START edge, which is the right
   edge in RTL. Hardcoding `left` would park it off-screen in one direction and
   partly on-screen in the other. */
.skip-link {
  position: absolute; inset-inline-start: -9999px;
  padding: 10px 16px; background: var(--accent); color: var(--accent-text);
  border-radius: var(--radius); z-index: 100;
}
.skip-link:focus { inset-inline-start: 12px; top: 12px; }

.brand {
  grid-area: brand;
  display: flex; align-items: center; gap: 9px;
  padding: 0 16px;
  border-inline-end: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  font-weight: 640; letter-spacing: -0.02em;
  overflow: hidden;
}
.brand-text { white-space: nowrap; }
/* Pushed to the edge expanded; the only thing left collapsed. */
.nav-toggle { margin-inline-start: auto; flex: none; font-size: 13px; }
.brand-mark {
  width: 21px; height: 21px; border-radius: 6px; flex: none;
  background: linear-gradient(135deg, var(--accent), color-mix(in oklab, var(--accent), #9c6bff 55%));
}
.brand small {
  font-weight: 450; color: var(--text-faint); font-size: 11px;
  letter-spacing: 0.02em; text-transform: uppercase;
}

/* ---- pattern: app-header ------------------------------------------------- */

.header {
  grid-area: header;
  display: flex; align-items: center; gap: 12px;
  padding: 0 16px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.header-search {
  flex: 1; max-width: 440px;
  padding: 6px 11px;
  font: inherit; font-size: 13px;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.header-search::placeholder { color: var(--text-faint); }
.header-spacer { flex: 1; }

/* Scope is always visible, never behind a menu: acting in the wrong scope
   should be noticed before acting, not after. */
.scope {
  display: flex; align-items: center; gap: 7px;
  padding: 4px 10px 4px 8px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: 12.5px;
}
.scope-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); }
.scope-label { color: var(--text-faint); }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  color: var(--text-dim);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.icon-btn:hover { background: var(--surface-2); border-color: var(--border); color: var(--text); }

/* ---- identity corner: app-header's user menu ------------------------------ */

.user-menu { position: relative; flex: none; }

/* A button, not a div: it has to be focusable, announce its expanded state,
   and open on Enter/Space without help. */
.avatar {
  width: 27px; height: 27px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent);
  font: inherit; font-size: 11px; font-weight: 640;
  border: 1px solid var(--border);
  cursor: pointer; padding: 0;
}
.avatar:hover { border-color: var(--accent); }
.avatar[aria-expanded="true"] {
  background: var(--accent); color: var(--accent-text); border-color: var(--accent);
}

/* inset-inline-end, not right: the flyout aligns to the avatar's inline-end
   edge, so it opens leftward in LTR and rightward in RTL from one rule. This
   is the pattern's direction contract, not a preference. */
.user-menu-list {
  position: absolute; inset-inline-end: 0; top: calc(100% + 6px);
  z-index: 50;
  /* min-width, never a fixed width: menu labels run 2-3x longer in some
     locales and a fixed box would clip them (localisation contract). */
  min-width: 168px;
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgb(0 0 0 / 18%);
}
.user-menu-id {
  padding: 7px 10px 8px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.user-menu-name { font-size: 13px; font-weight: 600; }
.user-menu-role { font-size: 11.5px; color: var(--text-faint); }
.user-menu-item {
  display: block; width: 100%;
  padding: 7px 10px;
  font: inherit; font-size: 13px; text-align: start;
  color: var(--text-dim);
  background: none; border: none; border-radius: var(--radius-sm);
  cursor: pointer;
  /* Wraps rather than truncates: a clipped "Account settings" is
     indistinguishable from "Account". */
  white-space: normal;
}
.user-menu-item:hover, .user-menu-item:focus-visible {
  background: var(--surface-2); color: var(--text);
}
.user-menu-sep { height: 1px; background: var(--border); margin: 4px 0; }

/* ---- pattern: sidebar-nav-2level ----------------------------------------- */

.nav {
  grid-area: nav;
  padding: 12px 10px;
  border-inline-end: 1px solid var(--border);
  background: var(--surface);
  overflow-y: auto;
}
.nav-group + .nav-group { margin-top: 16px; }
.nav-group-label {
  padding: 0 8px 6px;
  font-size: 10.5px; font-weight: 640; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--text-faint);
}
.nav-item {
  display: flex; align-items: center; gap: 9px;
  width: 100%; padding: 6px 8px;
  margin-bottom: 1px;
  font: inherit; font-size: 13px; text-align: start;
  color: var(--text-dim);
  background: none; border: none; border-radius: var(--radius-sm);
  cursor: pointer;
}
.nav-item:hover { background: var(--surface-2); color: var(--text); }
.nav-item[aria-current="page"] {
  background: var(--accent-soft); color: var(--accent); font-weight: 570;
}
.nav-count {
  margin-inline-start: auto;
  font-size: 11px; font-variant-numeric: tabular-nums;
  color: var(--text-faint);
}

/* The slot is fixed-width and always present, so labels align down the column
   whether or not a given item carries an icon. */
.nav-icon {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  font-size: 12px; line-height: 1;   /* only reached by the initial fallback */
  opacity: 0.75;
}
/* 16 inside an 18px slot: Lucide's 24-unit canvas is drawn to be optically
   lighter than its bounding box, so matching the box makes it read too heavy. */
.nav-icon svg { width: 16px; height: 16px; }
.nav-toggle svg { width: 15px; height: 15px; }
.icon-btn { display: inline-flex; align-items: center; justify-content: center; }
.nav-item[aria-current="page"] .nav-icon { opacity: 1; }
.nav-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---- collapsed sidebar --------------------------------------------------- */

/* Text goes, the icon stays and carries the meaning. Nothing is display:none'd
   that carries the accessible name — .nav-label is clipped, not removed, so
   the button keeps announcing "Patterns" to a screen reader. */
:root[data-nav="collapsed"] .brand-text,
:root[data-nav="collapsed"] .nav-label,
:root[data-nav="collapsed"] .nav-group-label {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
/* 56px does not fit the mark and the toggle side by side. The mark is
   decorative here and the toggle is the only way back out, so the mark yields. */
:root[data-nav="collapsed"] .brand { padding: 0; justify-content: center; }
:root[data-nav="collapsed"] .brand-mark { display: none; }
:root[data-nav="collapsed"] .nav-toggle { margin-inline-start: 0; }
:root[data-nav="collapsed"] .nav { padding: 12px 8px; }
:root[data-nav="collapsed"] .nav-item { justify-content: center; padding: 8px 0; }
:root[data-nav="collapsed"] .nav-count { display: none; }
:root[data-nav="collapsed"] .nav-icon { width: auto; font-size: 15px; }
/* Groups still need separating once their headings are gone. */
:root[data-nav="collapsed"] .nav-group + .nav-group {
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
}

/* ---- content ------------------------------------------------------------- */

.content { grid-area: content; overflow-y: auto; padding: 22px 26px 60px; }
.view-head { margin-bottom: 18px; }
.view-head p { color: var(--text-dim); margin-top: 3px; max-width: 68ch; }

.toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  margin-bottom: 14px;
}
.chip {
  padding: 4px 10px;
  font: inherit; font-size: 12.5px;
  color: var(--text-dim);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 100px;
  cursor: pointer;
}
.chip:hover { border-color: var(--border-strong); color: var(--text); }
.chip[aria-pressed="true"] {
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
  font-weight: 560;
}
.chip-n { opacity: 0.65; font-variant-numeric: tabular-nums; margin-inline-start: 3px; }

/* ---- cards / table ------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.card + .card { margin-top: 12px; }

.table-wrap { overflow-x: auto; border-radius: var(--radius); }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th {
  padding: 9px 12px; text-align: start;
  font-size: 11px; font-weight: 620; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-faint);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
tbody tr { cursor: pointer; }
tbody tr:hover td { background: var(--surface-2); }

/* Only the LAST group's last row loses its rule. With one tbody per
   containment level, `tbody tr:last-child` fires once per group and would cut
   the line above every heading. */
tbody:last-of-type tr:last-child td { border-bottom: none; }

/* ---- grouped list (V3 by containment) ------------------------------------ */

/* A real <th scope="colgroup"> inside its own <tbody>, not a styled <td>: the
   grouping has to reach a screen reader, which reads the row as a heading for
   the columns beneath it. A visually-bold data cell conveys nothing. */
.group-head th {
  position: sticky; top: 0; z-index: 1;
  padding: 7px 12px;
  text-transform: none; letter-spacing: 0;
  font-size: 12px; font-weight: 620;
  color: var(--text-dim);
  background: var(--surface-2);
  border-top: 1px solid var(--border);
  text-align: start;
}
/* The heading is not a destination — only data rows navigate. */
.group-head { cursor: default; }
.group-head:hover th { background: var(--surface-2); }
tbody.group:first-of-type .group-head th { border-top: none; }

/* The count belongs to the label beside it, so it must trail the label rather
   than drifting to the far edge of the row.
   margin-inline-start on the count, NOT `display:flex` on the th: flexing a
   table cell takes it out of the table layout, which silently kills the
   colspan — the heading's background then stops partway across instead of
   spanning the columns it heads. Caught by measuring; it looks fine until you
   compare the heading's width against a data row's. */
.group-n {
  margin-inline-start: 8px;
  font-weight: 500; font-variant-numeric: tabular-nums;
  color: var(--text-faint);
}

.cell-name { font-weight: 570; color: var(--text); }
.cell-sub { color: var(--text-faint); font-size: 12px; margin-top: 1px; }

/* ---- badges -------------------------------------------------------------- */

.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 7px;
  font-size: 11px; font-weight: 570;
  border-radius: 100px;
  background: var(--surface-2); color: var(--text-dim);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.badge.stable    { background: var(--ok-soft);   color: var(--ok);   border-color: transparent; }
.badge.candidate { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.badge.proposed  { background: var(--surface-2); color: var(--text-dim); }
.badge.deprecated,
.badge.rejected  { background: var(--crit-soft); color: var(--crit); border-color: transparent; }
.badge.standard  { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.badge.critical  { background: var(--crit-soft); color: var(--crit); border-color: transparent; }
.badge.important { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.badge.preferred { background: var(--surface-2); color: var(--text-dim); }

.mono { font-family: var(--mono); font-size: 12px; }

/* ---- pattern: empty-state ------------------------------------------------ */

.empty {
  display: grid; place-items: center; gap: 7px;
  padding: 52px 24px; text-align: center;
}
.empty-title { font-weight: 600; }
.empty-body { color: var(--text-dim); max-width: 46ch; }
.empty-icon { font-size: 26px; opacity: 0.4; }

/* ---- buttons ------------------------------------------------------------- */

.btn {
  padding: 6px 13px;
  font: inherit; font-size: 13px; font-weight: 540;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.btn:hover { background: var(--surface-2); }
.btn.primary {
  background: var(--accent); border-color: var(--accent); color: var(--accent-text);
}
.btn.primary:hover { filter: brightness(1.08); }
.btn.danger { color: var(--crit); border-color: color-mix(in oklab, var(--crit), transparent 60%); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.spinner {
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- direction ----------------------------------------------------------- */

/* Icons that encode a DIRECTION rather than a thing must mirror; icons that
   depict an object must not. The panel-left-* pair points at the edge the
   sidebar collapses toward, so in RTL — where the sidebar is on the right —
   an unmirrored glyph points away from where the panel actually goes.
   The sun, the moon, and the pie chart stay as drawn. */
[dir="rtl"] .nav-toggle svg { transform: scaleX(-1); }

/* ---- sign in ------------------------------------------------------------- */

/* Centred in the viewport, not in the shell: the sign-in wall replaces the
   application rather than appearing inside it, because a sidebar and a tenant
   badge around a login form would advertise a scope the caller has not been
   granted. */
.signin {
  min-height: 100dvh;
  display: grid; place-items: center;
  padding: 24px;
}
.signin-card {
  width: min(26rem, 100%);
  padding: 28px;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.signin-card .brand-mark { margin: 0 auto 14px; width: 34px; height: 34px; }
.signin-card h1 { font-size: 1.15rem; margin: 0 0 8px; }
.signin-card p {
  margin: 0 0 20px;
  font-size: 13px; line-height: 1.55; color: var(--text-dim);
}
.signin-card .btn { width: 100%; justify-content: center; }
