/* =========================================================================
   ZifraForms — design system layer.
   Loaded after app.css. Holds the application shell and the primitives that
   app.css never had: menus, tooltips, skeletons, toasts, pagination,
   drawers, segmented controls and a real data table.
   ========================================================================= */

/* ---------- Application shell ---------- */

.app-shell {
  display: block;
  min-height: 100vh;
  min-height: 100dvh;
}

@media (min-width: 900px) {
  .app-shell {
    display: grid;
    grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
    align-items: start;
  }

  .app-shell.is-collapsed {
    grid-template-columns: var(--sidebar-w-collapsed) minmax(0, 1fr);
  }
}

.app-shell__main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  container-type: inline-size;
  container-name: shell-main;
}

/* ---------- Sidebar ---------- */

.app-sidebar {
  display: none;
}

@media (min-width: 900px) {
  .app-sidebar {
    position: sticky;
    top: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    height: 100vh;
    height: 100dvh;
    padding: var(--space-5) var(--space-3) var(--space-4);
    /* The rail is a vertical panel, so it carries the same brand hairline
       .surface and .cab-panel draw along their top edge — rotated onto its right
       edge. `border-right` is gone: the two would stack into a 2px seam. */
    background:
      var(--gradient-hairline) right / 1px 100% no-repeat,
      var(--gradient-surface);
    /* Chrome blur is a step lower than --glass-blur: this surface is viewport
       sized and stays on screen under the whole page, so its blur is paid on
       every scroll frame, unlike a card that scrolls away with the page. */
    backdrop-filter: blur(12px) saturate(var(--glass-saturate));
    -webkit-backdrop-filter: blur(12px) saturate(var(--glass-saturate));
    /* A faint violet bleed to the right so the rail reads as its own plane
       rather than a slightly tinted edge of the page. Scaled by the glow token,
       so .perf-lite leaves only the flat seam. */
    box-shadow: 0 0 calc(22px * var(--glow-strength)) color-mix(in srgb, var(--violet) 9%, transparent);
    /* Menus are fixed-positioned; keep the rail itself from clipping labels. */
    overflow: visible;
  }
}

.app-sidebar__brand {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 0 var(--space-2);
  min-height: 2.5rem;
}

.app-sidebar__brand .brand {
  flex: 0 1 auto;
  min-width: 0;
  max-width: none;
}

.app-sidebar__brand .brand__mark,
.app-sidebar__brand .brand__name {
  font-size: 0.88rem;
  overflow: visible !important;
  text-overflow: clip !important;
  white-space: nowrap;
}

/* The collapse control only appears on hover so the brand keeps full width. */
.app-sidebar__collapse {
  position: absolute;
  top: 50%;
  right: var(--space-1);
  transform: translateY(-50%);
  opacity: 0;
  transition: opacity 0.16s ease;
  z-index: 1;
}

.app-sidebar:hover .app-sidebar__collapse,
.app-sidebar:focus-within .app-sidebar__collapse,
.app-shell.is-collapsed .app-sidebar__collapse {
  opacity: 1;
}

.app-sidebar__nav {
  display: grid;
  gap: 2px;
  align-content: start;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
}

.app-nav-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--touch-min);
  padding: var(--space-2) var(--space-3);
  /* --radius-md rather than --radius-sm: the highlight is 44px tall inside a
     236px rail, so the softer corner matches the panels it sits beside. */
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  font-weight: 650;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
}

.app-nav-link:hover {
  color: var(--cream);
  /* A hint of brand in the hover fill, so pointing at the rail belongs to the
     same family as the active item while staying clearly quieter than it. */
  background: color-mix(in srgb, var(--magenta) 9%, var(--fill-soft));
  text-decoration: none;
}

.app-nav-link__icon {
  flex: 0 0 auto;
  width: 1.15rem;
  height: 1.15rem;
  stroke: currentColor;
  stroke-width: 1.7;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.app-nav-link.is-active {
  color: var(--cream);
  /* Active = a light brand gradient plus a soft halo. The halo has to live on
     box-shadow, which does not stack across rules, so the keyboard ring is
     re-declared *after* this rule (see .app-nav-link:focus-visible). */
  background: linear-gradient(
    105deg,
    color-mix(in srgb, var(--magenta) 20%, transparent),
    color-mix(in srgb, var(--violet) 14%, transparent)
  );
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--magenta) 24%, transparent),
    0 0 calc(16px * var(--glow-strength)) color-mix(in srgb, var(--magenta) 16%, transparent);
}

/* Declared after .is-active on purpose: same specificity, so the active halo
   would otherwise swallow the ring for a keyboard user on the current page. */
.app-nav-link:focus-visible {
  outline: 0;
  color: var(--cream);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.app-nav-link.is-active .app-nav-link__icon {
  stroke: color-mix(in srgb, var(--magenta) 72%, var(--cream));
}

.app-nav-link__text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-nav-link__badge {
  margin-left: auto;
  flex: 0 0 auto;
  padding: 0.1rem 0.34rem;
  border-radius: var(--radius-full);
  border: 1px solid color-mix(in srgb, var(--magenta) 45%, var(--violet));
  font-size: var(--text-2xs);
  font-weight: 800;
  letter-spacing: 0.08em;
  line-height: 1.25;
  color: color-mix(in srgb, var(--magenta) 80%, var(--cream));
}

.app-sidebar__foot {
  display: grid;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.app-theme-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: var(--touch-min);
  margin-top: auto;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  font: inherit;
  font-size: var(--text-base);
  font-weight: 650;
  color: var(--muted);
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.16s ease, color 0.16s ease;
}

.app-theme-toggle:hover {
  color: var(--cream);
  background: var(--fill-soft);
}

.app-theme-toggle:focus-visible {
  outline: 0;
  color: var(--cream);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.app-theme-toggle__icons {
  position: relative;
  flex: 0 0 auto;
  width: 1.15rem;
  height: 1.15rem;
}

.app-theme-toggle__sun,
.app-theme-toggle__moon {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.app-theme-toggle__moon {
  display: none;
}

[data-theme="light"] .app-theme-toggle__sun {
  display: none;
}

[data-theme="light"] .app-theme-toggle__moon {
  display: block;
}

.app-theme-toggle__text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Collapsed rail */
.app-shell.is-collapsed .app-sidebar {
  padding-inline: var(--space-2);
}

.app-shell.is-collapsed .app-sidebar__brand .brand {
  display: none;
}

.app-shell.is-collapsed .app-sidebar__brand {
  justify-content: center;
  padding-inline: 0;
}

.app-shell.is-collapsed .app-sidebar__brand > .app-sidebar__collapse[data-tip] {
  position: static;
  transform: none;
}

.app-shell.is-collapsed .app-nav-link,
.app-shell.is-collapsed .app-theme-toggle,
.app-shell.is-collapsed .app-userbtn,
.app-shell.is-collapsed .app-wsbtn {
  justify-content: center;
  padding-inline: var(--space-2);
  width: 100%;
}

.app-shell.is-collapsed .app-nav-link__text,
.app-shell.is-collapsed .app-nav-link__badge,
.app-shell.is-collapsed .app-theme-toggle__text,
.app-shell.is-collapsed .app-sidebar__brand-name,
.app-shell.is-collapsed .app-userbtn__body,
.app-shell.is-collapsed .app-wsbtn__body,
.app-shell.is-collapsed .app-wsbtn__chevron {
  display: none;
}

.app-shell.is-collapsed .app-wsbtn,
.app-shell.is-collapsed .app-userbtn {
  width: 100%;
  min-width: 0;
}

.app-shell.is-collapsed .app-sidebar__foot > * {
  min-width: 0;
}

.app-shell.is-collapsed .app-sidebar__user .app-logout {
  justify-content: center;
  padding-inline: var(--space-2);
}

.app-shell.is-collapsed .app-logout__text {
  display: none;
}

.app-shell.is-collapsed .app-avatar {
  margin-inline: auto;
}

/* ---------- Workspace + user controls ---------- */

.app-wsbtn,
.app-userbtn {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: var(--touch-min);
  padding: var(--space-2) var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--fill-soft);
  color: var(--cream);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease;
}

.app-wsbtn:hover,
.app-userbtn:hover {
  background: var(--fill);
  border-color: var(--line);
}

.app-wsbtn:focus-visible,
.app-userbtn:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.app-avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 1.85rem;
  height: 1.85rem;
  border-radius: var(--radius-sm);
  background: linear-gradient(135deg, var(--magenta), var(--violet));
  color: #fff;
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.app-avatar--ws {
  background: linear-gradient(135deg, var(--violet), var(--cyan));
}

.app-wsbtn__body,
.app-userbtn__body {
  min-width: 0;
  display: grid;
  gap: 1px;
}

.app-wsbtn__label,
.app-userbtn__label {
  font-size: var(--text-xs);
  color: var(--muted);
  line-height: 1.2;
}

.app-wsbtn__value,
.app-userbtn__value {
  font-size: var(--text-base);
  font-weight: 700;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.app-wsbtn__chevron {
  margin-left: auto;
  flex: 0 0 auto;
  width: 0.9rem;
  height: 0.9rem;
  stroke: var(--muted);
  stroke-width: 2;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- Mobile top bar ---------- */

.app-topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 3.5rem;
  padding: max(var(--space-2), env(safe-area-inset-top)) var(--space-4) var(--space-2);
  /* Same brand hairline as the rail and .surface, on the edge that faces the
     content. It replaces the old plain border-bottom so the phone header and the
     desktop rail are visibly the same family. */
  background:
    var(--gradient-hairline) bottom / 100% 1px no-repeat,
    color-mix(in srgb, var(--panel) 88%, transparent);
  /* Same reduced chrome blur as the rail — see .app-sidebar. */
  backdrop-filter: blur(12px) saturate(var(--glass-saturate));
  -webkit-backdrop-filter: blur(12px) saturate(var(--glass-saturate));
}

@media (min-width: 900px) {
  .app-topbar {
    display: none;
  }
}

.app-topbar__brand {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  flex: 0 0 auto;
  min-width: 0;
  color: var(--cream);
  text-decoration: none;
}

.app-topbar__brand .brand__mark,
.app-topbar__brand .brand__name {
  overflow: visible;
  text-overflow: unset;
  white-space: nowrap;
}

@media (max-width: 420px) {
  .app-topbar__brand .brand__name {
    display: none;
  }
}

.app-topbar__spacer {
  margin-left: auto;
}

/* ---------- Mobile tab bar ---------- */

.app-tabbar {
  --app-tabbar-h: 3.5rem;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-sticky);
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 2px;
  padding: var(--space-1) var(--space-2) max(var(--space-1), env(safe-area-inset-bottom));
  /* Hairline on the edge facing the content, mirroring .app-topbar so the phone
     chrome above and below the page reads as one frame. */
  background:
    var(--gradient-hairline) top / 100% 1px no-repeat,
    color-mix(in srgb, var(--panel) 94%, transparent);
  /* Same reduced chrome blur as the rail — see .app-sidebar. */
  backdrop-filter: blur(12px) saturate(var(--glass-saturate));
  -webkit-backdrop-filter: blur(12px) saturate(var(--glass-saturate));
}

@media (min-width: 900px) {
  .app-tabbar {
    display: none;
  }
}

/* Keep theme FAB above the mobile tab bar — never overlapping tabs.
   On desktop the theme control lives in the sidebar, so hide the FAB. */
@media (max-width: 899px) {
  body:has(.app-tabbar) .fab-dock {
    --fab-bottom: calc(
      var(--app-tabbar-h, 3.5rem) + max(10px, env(safe-area-inset-bottom))
    );
  }
}

@media (min-width: 900px) {
  body:has(.app-theme-toggle) .theme-fab {
    display: none;
  }
}

.app-tab {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 2px;
  min-height: 3.1rem;
  min-width: 0;
  padding: var(--space-1);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: var(--text-2xs);
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
}

.app-tab > span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.app-tab:hover,
.app-tab:focus-visible {
  color: var(--cream);
  text-decoration: none;
  outline: 0;
}

.app-tab.is-active {
  color: var(--cream);
  /* Mirrors .app-nav-link.is-active so the phone bar matches the rail, but
     without the outer halo: the bar is compact and sits right on the screen
     edge, so a gradient plus a 1px brand seam is enough to mark the tab. */
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--magenta) 18%, transparent),
    color-mix(in srgb, var(--violet) 12%, transparent)
  );
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--magenta) 22%, transparent);
}

/* Declared after .is-active on purpose: same specificity, so the seam above
   would otherwise suppress the ring for a keyboard user on the current tab. */
.app-tab:focus-visible {
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.app-tab__icon {
  width: 1.3rem;
  height: 1.3rem;
  stroke: currentColor;
  stroke-width: 1.7;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.app-tab.is-active .app-tab__icon {
  stroke: color-mix(in srgb, var(--magenta) 78%, var(--cream));
}

.app-sidebar__brand > .app-sidebar__collapse[data-tip] {
  position: absolute;
}

.app-shell.is-collapsed .app-sidebar__collapse svg {
  transform: rotate(180deg);
}

.app-wsbtn--static {
  cursor: default;
}

.app-wsbtn--static:hover {
  background: var(--fill-soft);
  border-color: transparent;
}

.app-wsbtn-wrap,
.app-sidebar__user {
  display: block;
  position: relative;
}

/* Topbar variant: compact, content-width, no label line. */
.app-topbar .app-wsbtn,
.app-topbar .app-wsbtn-wrap {
  width: auto;
}

.app-topbar .app-wsbtn {
  padding: var(--space-1) var(--space-2);
  gap: var(--space-2);
  max-width: min(12rem, 46vw);
  min-width: 0;
}

.app-topbar .app-wsbtn__label {
  display: none;
}

.app-topbar .app-wsbtn__value {
  font-size: var(--text-sm);
}

.app-tab-more {
  display: block;
  position: relative;
}

.app-tab-more > .app-tab {
  width: 100%;
}

.app-tabbar .menu {
  min-width: 13rem;
}

/* ---------- Page container ---------- */

.app-page {
  width: min(1180px, 100%);
  margin: 0 auto;
  padding: var(--space-6) var(--space-5) var(--fab-clearance);
  min-width: 0;
}

@media (max-width: 899px) {
  .app-page {
    padding: var(--space-4) var(--space-4) calc(var(--fab-clearance) + 4rem);
  }
}

@media (max-width: 639px) {
  .app-page {
    padding-inline: var(--space-3);
  }
}

.app-page:has(> .editor-bar) {
  padding-top: 0;
}

.app-page:has(> .editor-bar) > .alert:first-child,
.app-page:has(> .editor-bar) > [data-flash]:first-child {
  margin-top: var(--space-5);
}

/* The dashboard panels used to live inside the .cab card; without it they
   need their own breathing room. */
.app-page > .alert {
  margin-bottom: var(--space-5);
}

/* ---------- Page header ---------- */

.page-bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-6);
}

.page-bar__titles {
  min-width: 0;
  display: grid;
  gap: 0;
  align-content: start;
}

.page-bar__titles .back-link {
  margin-bottom: var(--space-3);
}

.page-bar__eyebrow {
  margin: 0 0 var(--space-1);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--muted);
}

.page-bar__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 1.1rem + 1.4vw, var(--text-2xl));
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
  color: var(--heading);
  overflow-wrap: break-word;
}

.page-bar__sub {
  margin: var(--space-2) 0 0;
  font-size: var(--text-base);
  color: var(--muted);
  line-height: var(--leading-snug);
}

.page-bar__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.page-bar__actions .btn {
  white-space: nowrap;
  flex: 0 0 auto;
}

/* Same height for siblings in the action row; primary weight is color, not size. */
.page-bar__actions .btn:not(.btn-icon) {
  box-sizing: border-box;
  height: var(--touch-min);
  min-height: var(--touch-min);
  padding-top: 0;
  padding-bottom: 0;
}

/* Status in page-bar matches sibling button height; weight comes from tone, not size.
   In the editor header the status sits in its own grid column (see .editor-bar). */
.page-bar__actions .editor-meta__status,
.editor-bar > .editor-meta__status {
  --editor-status-control-h: var(--touch-min);
  --editor-status-control-w: 9.25rem;
  align-self: center;
}

.page-bar__actions .editor-meta__status .c-select__btn,
.editor-bar > .editor-meta__status .c-select__btn {
  font-size: var(--text-base);
  border-radius: 14px;
  box-shadow: none;
}

/* ---- Main-section headers (stage 4A: dashboard, 4B: forms, 5A: account) ----
   Scoped to a modifier so the Editor/Responses/Workspaces headers keep their
   current metrics. The three sidebar destinations (Overview, Forms, Account)
   carry more display weight and their eyebrow picks up the brand accent. All
   modifiers are listed together so those headers cannot drift apart. */
.page-bar--dashboard .page-bar__eyebrow,
.page-bar--forms .page-bar__eyebrow,
.page-bar--account .page-bar__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: var(--text-2xs);
  color: color-mix(in srgb, var(--magenta) 62%, var(--cream));
}

/* Small lit dot — a decorative accent, not a status indicator. */
.page-bar--dashboard .page-bar__eyebrow::before,
.page-bar--forms .page-bar__eyebrow::before,
.page-bar--account .page-bar__eyebrow::before {
  content: "";
  flex: 0 0 auto;
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: var(--gradient-brand);
  box-shadow: 0 0 calc(8px * var(--glow-strength)) var(--glow-brand);
}

.page-bar--dashboard .page-bar__title,
.page-bar--forms .page-bar__title,
.page-bar--account .page-bar__title {
  font-size: clamp(1.75rem, 1.25rem + 1.7vw, var(--text-3xl));
}

.page-bar--dashboard .page-bar__sub,
.page-bar--forms .page-bar__sub,
.page-bar--account .page-bar__sub {
  max-width: 42rem;
  line-height: var(--leading-normal);
}

/* ---------- Surface ---------- */

.surface {
  border: 1px solid var(--line);
  border-radius: var(--radius-2xl);
  /* Three layers: a brand hairline confined to the top edge, a faint glass lift,
     then the panel gradient. All are clipped by border-radius, so no wrapper or
     pseudo-element is needed. */
  background:
    var(--gradient-hairline) top / 100% 1px no-repeat,
    linear-gradient(var(--glass-tint), var(--glass-tint)),
    var(--gradient-surface);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  box-shadow: var(--elev-glow-1);
}

.surface--pad {
  padding: var(--space-5);
}

/* ---------- Icon button ---------- */

.btn-icon {
  display: inline-grid;
  place-items: center;
  width: var(--control-sm);
  height: var(--control-sm);
  min-width: var(--control-sm);
  min-height: var(--control-sm);
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease;
}

.btn-icon:hover {
  background: var(--fill);
  color: var(--cream);
}

.btn-icon:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.btn-icon svg {
  width: 1.05rem;
  height: 1.05rem;
  stroke: currentColor;
  stroke-width: 1.8;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.btn > svg {
  flex: 0 0 auto;
  width: 1.05rem;
  height: 1.05rem;
  stroke: currentColor;
  stroke-width: 2;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.btn-icon--danger:hover {
  color: var(--danger);
  background: var(--danger-bg);
}

/* ---------- Dropdown menu ---------- */

.menu-wrap {
  position: relative;
  display: inline-flex;
}

.menu {
  position: absolute;
  z-index: var(--z-popover);
  min-width: 12.5rem;
  max-width: min(18rem, 90vw);
  padding: var(--space-1);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--panel-solid);
  box-shadow: var(--elev-3);
  display: grid;
  gap: 1px;
}

.menu[hidden] {
  display: none;
}

.menu--end {
  right: 0;
}

.menu--start {
  left: 0;
}

.menu--below {
  top: calc(100% + var(--space-2));
}

.menu--above {
  bottom: calc(100% + var(--space-2));
}

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: var(--touch-min);
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--cream);
  font: inherit;
  font-size: var(--text-base);
  font-weight: 600;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.menu__item:hover {
  background: var(--fill);
  color: var(--cream);
  text-decoration: none;
}

.menu__item:focus-visible {
  outline: 0;
  background: var(--fill);
  box-shadow: 0 0 0 2px var(--focus-ring) inset;
}

.menu__item[aria-checked="true"]::after {
  content: "";
  margin-left: auto;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--magenta);
}

.menu__item svg {
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
  stroke: currentColor;
  stroke-width: 1.8;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.menu__item--danger {
  color: var(--danger);
}

.menu__item--danger:hover {
  background: var(--danger-bg);
  color: var(--danger);
}

.menu__sep {
  height: 1px;
  margin: var(--space-1) var(--space-2);
  background: var(--line);
}

.menu__label {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--muted);
}

.menu__form {
  margin: 0;
  display: block;
}

/* ---------- Tooltip ---------- */

[data-tip] {
  position: relative;
}

[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  z-index: var(--z-popover);
  left: 50%;
  bottom: calc(100% + 0.4rem);
  transform: translateX(-50%) translateY(0.2rem);
  padding: 0.3rem 0.5rem;
  border-radius: var(--radius-xs);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  box-shadow: var(--elev-2);
  color: var(--cream);
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.14s ease, transform 0.14s ease;
}

[data-tip]:hover::after,
[data-tip]:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

[data-tip-side="right"]::after {
  left: calc(100% + 0.4rem);
  bottom: auto;
  top: 50%;
  transform: translateY(-50%) translateX(-0.2rem);
}

[data-tip-side="right"]:hover::after,
[data-tip-side="right"]:focus-visible::after {
  transform: translateY(-50%) translateX(0);
}

@media (hover: none) {
  [data-tip]::after {
    display: none;
  }
}

/* ---------- Skeleton ---------- */

.skeleton {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--fill-soft);
}

.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in srgb, var(--cream) 10%, transparent),
    transparent
  );
  animation: skeleton-sweep 1.4s ease-in-out infinite;
}

.skeleton--text {
  height: 0.75rem;
  margin-block: 0.28rem;
}

.skeleton--line {
  height: 1.1rem;
}

.skeleton--block {
  height: 8rem;
}

@keyframes skeleton-sweep {
  100% { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton::after {
    animation: none;
  }
}

/* ---------- Toasts ---------- */

.toast-stack {
  position: fixed;
  z-index: var(--z-modal);
  left: 50%;
  bottom: max(var(--space-5), env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: grid;
  gap: var(--space-2);
  width: min(28rem, calc(100vw - 2rem));
  pointer-events: none;
}

@media (max-width: 899px) {
  body:has(.app-tabbar) .toast-stack {
    bottom: calc(3.5rem + max(var(--space-1), env(safe-area-inset-bottom)) + var(--space-3));
  }
}

@media (min-width: 900px) {
  .toast-stack {
    left: auto;
    right: var(--space-6);
    bottom: var(--space-6);
    transform: none;
  }
}

/* Two producers emit this markup — the React FormsToast and the legacy
   shell.js showToast() — and both write the exact same nodes (toast + a
   toast--TYPE class + toast__dot/text/close). So the surface is defined once
   here and a status only swaps a colour. Previously the same border, gradient
   and five-layer shadow were repeated per variant, and the "info" type that
   shell.js falls back to (and that the server-flash migration maps a neutral
   flash onto) had no rule at all — it silently rendered as brand pink. */
.toast {
  /* Per-status accent, as a custom property, so a variant rule overrides the
     colour and inherits the entire geometry below. */
  --toast-accent: var(--magenta);
  --toast-tint: 11%;

  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid color-mix(in srgb, var(--toast-accent) 35%, var(--line-strong));
  /* --radius-lg, matching .data-table-wrap and the other floating panels, so a
     toast stops being the only 14px-cornered surface in the shell. */
  border-radius: var(--radius-lg);
  background:
    linear-gradient(
      135deg,
      color-mix(in srgb, var(--toast-accent) var(--toast-tint), transparent),
      transparent 72%
    ),
    var(--panel);
  /* One ring plus one halo tier. The old stack carried two glow tiers whose
     blur roughly doubled the visual weight of a surface this small. */
  box-shadow:
    var(--elev-3),
    0 0 0 1px color-mix(in srgb, var(--toast-accent) 18%, transparent),
    0 0 calc(20px * var(--glow-strength)) color-mix(in srgb, var(--toast-accent) 20%, transparent),
    inset 0 1px 0 color-mix(in srgb, var(--cream) 10%, transparent);
  color: var(--cream);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  pointer-events: auto;
  animation: toast-in 0.22s ease both;
}

.toast--success {
  --toast-accent: var(--ok);
}

.toast--error {
  --toast-accent: var(--danger);
}

.toast--warn {
  --toast-accent: var(--warn);
}

/* shell.js defaults to "info" and initFlash() maps a neutral server flash onto
   it, so this is the one status that is not an outcome: it leans on violet
   instead of claiming success or failure. */
.toast--info {
  --toast-accent: var(--violet);
  --toast-tint: 13%;
}

.toast.is-leaving {
  animation: toast-out 0.18s ease both;
}

.toast__dot {
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  /* Inherits the status accent from .toast, so the four per-variant dot rules
     collapse into one. */
  background: var(--toast-accent);
  box-shadow: 0 0 10px color-mix(in srgb, var(--toast-accent) 70%, transparent);
}

.toast__text {
  min-width: 0;
  overflow-wrap: break-word;
}

.toast__close {
  /* Never squashed by a long message. Hover/focus come from .btn-icon, which
     this button already carries. */
  flex: 0 0 auto;
  margin-left: auto;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(0.5rem);
    filter: brightness(1.15);
  }
  to {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

@keyframes toast-out {
  to {
    opacity: 0;
    transform: translateY(0.35rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  .toast,
  .toast.is-leaving {
    animation: none;
  }
}

/* ---------- Segmented control ---------- */

.seg {
  display: inline-flex;
  max-width: 100%;
  min-width: 0;
  padding: 2px;
  gap: 2px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--fill-soft);
  /* Too many options for a narrow screen: swipe sideways instead of overflowing the page. */
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.seg::-webkit-scrollbar {
  display: none;
}

/* Fade the edge that hides more options (set by the scroll-row script). */
.seg[data-more="end"] {
  mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
}

.seg[data-more="start"] {
  mask-image: linear-gradient(to left, #000 calc(100% - 2.5rem), transparent);
}

.seg[data-more="both"] {
  mask-image: linear-gradient(to right, transparent, #000 2.5rem, #000 calc(100% - 2.5rem), transparent);
}

.seg__btn {
  flex: 0 0 auto;
}

.seg__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--control-sm);
  padding: 0 var(--space-3);
  border: 0;
  border-radius: calc(var(--radius-sm) - 2px);
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.16s ease, color 0.16s ease;
}

.seg__btn:hover {
  color: var(--cream);
  text-decoration: none;
}

.seg__btn:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.seg__btn.is-active {
  color: var(--cream);
  background: var(--fill-hover);
  box-shadow: var(--elev-1);
}

.seg__btn svg {
  width: 1rem;
  height: 1rem;
  stroke: currentColor;
  stroke-width: 1.8;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- Toolbar ---------- */

.toolbar {
  --toolbar-control-h: calc(var(--control-sm) + 6px); /* matches .seg outer height */
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

.toolbar > .btn,
.toolbar > form,
.toolbar > .cluster,
.toolbar > .menu-wrap {
  flex: 0 0 auto;
}

.toolbar .btn {
  white-space: nowrap;
}

.toolbar .btn:not(.btn-icon) {
  box-sizing: border-box;
  height: var(--toolbar-control-h);
  min-height: var(--toolbar-control-h);
  padding-top: 0;
  padding-bottom: 0;
  font-size: var(--text-sm);
  border-radius: var(--radius-sm);
}

.toolbar .search-field input {
  min-height: var(--toolbar-control-h);
  height: var(--toolbar-control-h);
}

.toolbar__spacer {
  margin-left: auto;
}

.search-field {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-width: min(18rem, 100%);
  flex: 1 1 14rem;
}

.search-field__icon {
  position: absolute;
  left: var(--space-3);
  width: 1rem;
  height: 1rem;
  stroke: var(--muted);
  stroke-width: 1.8;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
}

.search-field input {
  width: 100%;
  min-height: var(--control-sm);
  padding: 0 var(--space-3) 0 2.15rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--fill-soft);
  color: var(--cream);
  font: inherit;
  font-size: var(--text-base);
}

.search-field input::placeholder {
  color: var(--muted);
}

.search-field input:focus-visible {
  outline: 0;
  border-color: color-mix(in srgb, var(--magenta) 55%, var(--violet));
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* ---------- Data table ---------- */

.data-table-wrap {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  box-shadow: var(--shadow-sm);
}

@media (max-width: 900px) {
  .data-table-wrap {
    overflow: visible;
    border: 0;
    border-radius: 0;
    background: transparent;
  }

  .data-table tbody tr:hover td {
    background: transparent;
  }

  /* Card layout must win over desktop table-cell / width:1% rules. */
  .data-table,
  .data-table tbody,
  .data-table tr,
  .data-table td,
  .data-table td.row-actions {
    display: grid;
  }

  .data-table {
    width: 100%;
    min-width: 0;
  }

  .data-table tbody {
    gap: 0.75rem;
  }

  .data-table tr {
    gap: 0.65rem;
    width: 100%;
    min-width: 0;
  }

  .data-table td,
  .data-table td.row-actions {
    width: auto !important;
    max-width: none;
    min-width: 0;
    white-space: normal;
    grid-template-columns: 6.5rem minmax(0, 1fr);
    align-items: center;
    justify-items: stretch;
  }

  .data-table td.row-actions {
    grid-template-columns: 1fr;
    justify-items: stretch;
    max-width: 100%;
  }

  .data-table td.row-actions .btn {
    width: auto;
    max-width: 100%;
    justify-content: center;
  }

  .data-table td.row-actions .row-actions__group {
    display: flex;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
    white-space: normal;
  }

  .data-table td.row-actions .row-actions__group .btn {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
  }

  .data-table td.row-actions > form,
  .data-table td.row-actions > .btn {
    width: 100%;
  }

  .data-table td.row-actions > form .btn {
    width: 100%;
  }

  .responses-view,
  .responses-view__panel,
  .page-tabs__panel,
  [data-table-filter] {
    min-width: 0;
    max-width: 100%;
  }

  .data-table td:not([data-label]) {
    grid-template-columns: minmax(0, 1fr);
  }

  .data-table td:not([data-label])::before {
    display: none;
  }

  .data-table td.row-actions:not(:has(button, a, .btn, form)) {
    display: none;
  }

  .data-table .ws-role-form,
  .data-table .ws-role-select,
  .data-table .c-select,
  .data-table .status-pill {
    max-width: none;
    width: 100%;
  }

  .data-table__tight,
  #members .data-table td:has(.ws-role-form),
  #members .data-table td:has(> .status-pill),
  #members .data-table th:nth-child(2) {
    width: auto !important;
    white-space: normal;
  }

  .data-table__cell {
    max-width: none;
    white-space: normal;
    overflow: visible;
    text-overflow: unset;
  }

  #members .ws-role-form,
  #members .ws-role-form .c-select {
    max-width: none;
    width: 100%;
  }

  #members .ws-role-form .c-select__btn {
    width: 100%;
  }

  .filter-bar {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
  }

  .filter-chips {
    display: flex;
    flex-wrap: nowrap;
    gap: 0.4rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    padding-bottom: 0.15rem;
    margin-inline: -0.15rem;
    padding-inline: 0.15rem;
    scrollbar-width: thin;
  }

  .filter-chip {
    flex: 0 0 auto;
    white-space: nowrap;
    max-width: 15rem;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .page-bar__actions {
    width: 100%;
  }
}

@media (max-width: 639px) {
  /* A lone action keeps its natural width; only a row of several shares the width. */
  .page-bar__actions .btn:not(.btn-icon):not(:only-child) {
    flex: 1 1 auto;
  }

  .editor-bar .page-bar__actions {
    width: 100%;
  }

  /* Stack label above value — two-column cards feel crooked at ~390px. */
  .data-table td:not(.row-actions) {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.28rem;
    justify-items: start;
  }

  .data-table td:not(.row-actions)::before {
    padding-top: 0;
  }

  .data-table td.row-actions {
    padding-top: 0.15rem;
  }
}

.data-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--text-base);
}

.data-table th,
.data-table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--line);
}

.data-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  /* Fully opaque. --panel-solid is the flat colour and --bg carries the theme's
     page tint, so the header keeps its own shade in both themes without letting
     a single row show through while the header is pinned. It also stays opaque
     during horizontal scrolling, where the old translucent mix revealed the
     cells sliding underneath the pinned header. */
  background: color-mix(in srgb, var(--panel-solid) 96%, var(--bg));
  /* No backdrop-filter here. The header is opaque now, so an 8px blur had
     nothing left to blur while costing a raster pass per sticky cell. */
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}

.data-table tbody tr:last-child td {
  border-bottom: 0;
}

.data-table tbody tr:hover td {
  background: var(--fill-soft);
}

.data-table tbody tr.is-selected td {
  background: color-mix(in srgb, var(--magenta) 8%, transparent);
}

.data-table .row-actions {
  text-align: right;
  vertical-align: middle;
}

@media (min-width: 901px) {
  .data-table .row-actions {
    width: 1%;
    min-width: max-content;
    white-space: nowrap;
  }

  .data-table td.row-actions {
    display: table-cell;
  }
}

.data-table .row-actions .btn {
  white-space: nowrap;
}

.data-table .ws-role-form,
.data-table .ws-role-select,
.data-table .c-select {
  max-width: 12rem;
}

/* Members: keep role + exclude tightly grouped on the right (desktop). */
@media (min-width: 901px) {
  #members .data-table td:has(.ws-role-form),
  #members .data-table td:has(> .status-pill),
  #members .data-table th:nth-child(2) {
    width: 1%;
    white-space: nowrap;
  }
}

.data-table__num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.data-table__tight {
  width: auto;
}

@media (min-width: 901px) {
  .data-table__tight {
    width: 1%;
    white-space: nowrap;
  }
}

.data-table__cell {
  display: block;
  max-width: 22rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 900px) {
  .data-table__cell {
    max-width: none;
    overflow: visible;
    text-overflow: unset;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  .data-table-wrap,
  .data-table,
  .data-table tr,
  .data-table td {
    width: 100%;
    max-width: 100%;
    min-width: 0;
  }
}

/* ---------- Pagination ---------- */

.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-top: var(--space-4);
}

.pagination__info {
  font-size: var(--text-sm);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.pagination__pages {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-wrap: wrap;
}

.pagination__page {
  display: inline-grid;
  place-items: center;
  min-width: var(--control-sm);
  min-height: var(--control-sm);
  padding: 0 var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-soft);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  cursor: pointer;
}

.pagination__page:hover {
  background: var(--fill);
  color: var(--cream);
  text-decoration: none;
}

.pagination__page:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.pagination__page[aria-current="page"] {
  color: #fff;
  border-color: transparent;
  background: linear-gradient(135deg, var(--magenta), var(--violet));
}

.pagination__page[aria-disabled="true"] {
  opacity: 0.45;
  pointer-events: none;
}

.pagination__gap {
  padding-inline: var(--space-1);
  color: var(--muted);
}

/* ---------- Drawer ---------- */

.drawer {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  justify-items: end;
}

.drawer[hidden] {
  display: none;
}

.drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  animation: drawer-fade 0.18s ease both;
}

.drawer__panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(36rem, 100%);
  height: 100%;
  border-left: 1px solid var(--line-strong);
  background: var(--panel);
  box-shadow: var(--elev-3);
  animation: drawer-in 0.22s ease both;
}

.drawer__head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-5);
  border-bottom: 1px solid var(--line);
}

.drawer__title {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--heading);
}

.drawer__sub {
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
  color: var(--muted);
}

.drawer__close {
  margin-left: auto;
}

.drawer__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-5);
  display: grid;
  gap: var(--space-4);
  align-content: start;
}

.drawer__foot {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5) max(var(--space-4), env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
}

@media (max-width: 639px) {
  .drawer {
    justify-items: stretch;
    align-items: end;
  }

  .drawer__panel {
    width: 100%;
    height: min(88dvh, 100%);
    border-left: 0;
    border-top: 1px solid var(--line-strong);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    animation-name: drawer-up;
  }
}

@keyframes drawer-fade {
  from { opacity: 0; }
}

@keyframes drawer-in {
  from { transform: translateX(2rem); opacity: 0; }
}

@keyframes drawer-up {
  from { transform: translateY(2rem); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .drawer__scrim,
  .drawer__panel {
    animation: none;
  }
}

/* ---------- Modal dialog ---------- */

.modal {
  width: min(32rem, calc(100vw - 2rem));
  max-width: none;
  padding: 0;
  border: 1px solid var(--line-strong);
  /* --radius-2xl to match .confirm-dialog__card and .surface: the two dialog
     families had drifted one radius step apart. */
  border-radius: var(--radius-2xl);
  /* Brand hairline + glass lift over the opaque panel. The panel stays opaque
     on purpose — a dialog has to hold its text over the scrim, and .surface's
     --gradient-surface would let the page show through it. */
  background:
    var(--gradient-hairline) top / 100% 1px no-repeat,
    linear-gradient(var(--glass-tint), var(--glass-tint)),
    var(--panel);
  color: var(--cream);
  box-shadow: var(--elev-3);
  /* Stays visible on purpose: .c-select__menu is absolutely positioned, and the
     create-user dialog opens a role select inside this element. Turning the
     dialog into a scroll container clips that menu. */
  overflow: visible;
}

.modal::backdrop {
  /* One shared scrim with .confirm-dialog::backdrop instead of a different
     treatment per dialog family. --bg-rgb is themed, so the dim stays graphite
     in the dark theme and milky in the light one. 8px of blur separates the
     panel without the heavy frosted screen the confirm dialog used to produce. */
  background: rgba(var(--bg-rgb), 0.58);
  backdrop-filter: blur(8px) saturate(1.15);
  -webkit-backdrop-filter: blur(8px) saturate(1.15);
}

.modal__panel {
  display: grid;
  margin: 0;
}

.modal__head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-3);
}

.modal__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  color: var(--heading);
}

.modal__close {
  margin-left: auto;
}

.modal__body {
  display: grid;
  gap: var(--space-4);
  padding: 0 var(--space-5) var(--space-5);
}

.modal__hint {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--muted);
  line-height: var(--leading-normal);
}

.modal__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--line);
}

@media (max-width: 639px) {
  .modal__foot {
    flex-direction: column-reverse;
  }

  .modal__foot .btn {
    width: 100%;
  }
}

/* ---------- Form field ---------- */

.form-field {
  display: grid;
  gap: var(--space-2);
}

.form-field__label {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-soft);
}

.form-field__hint {
  font-size: var(--text-xs);
  color: var(--muted);
  line-height: var(--leading-snug);
}

/* Input + inline action (e.g. "Запросить подтверждение"): one row on wide
   screens, the button wraps under the field when the space runs out. */
.form-field__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.form-field__row > input {
  flex: 1 1 15rem;
  min-width: 0;
}

.form-field__row > .btn {
  flex: 0 1 auto;
  max-width: 100%;
  white-space: normal;
}

.form-field input[type="text"],
.form-field input[type="url"],
.form-field input[type="number"],
.form-field input[type="email"],
.form-field input[type="password"],
.form-field input[type="search"],
.form-field select,
.form-field textarea {
  width: 100%;
  min-height: var(--touch-min);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  /* background-color only: the shorthand would drop the global select arrow image. */
  background-color: var(--field-bg);
  color: var(--cream);
  font: inherit;
  font-size: var(--text-md);
}

.form-field select {
  padding-right: 2.6rem;
}

/* Inside an auto-width table cell, width:100% collapses the column and clips the role label. */
.ws-role-picker {
  width: auto;
  min-width: 10.5rem;
}

.ws-role-picker .c-select__btn {
  min-height: var(--control-sm);
  padding: 0.35rem 0.65rem;
  border-radius: 10px;
  background: var(--fill);
  box-shadow: none;
  font-size: 0.88rem;
}

.ws-role-picker .c-select__btn:hover {
  background: var(--fill-hover);
}

.ws-invite-role {
  min-width: 12rem;
  max-width: 22rem;
}

/* Field states share one background family (--field-bg → --field-bg-focus) and one
   focus halo (--field-ring), so .form-field and .c-select stay visually identical.
   `select` was missing from the old :focus-visible pair and relied on the global
   rule; including it here keeps the arrow image (background-color only). */
.form-field input:focus-visible,
.form-field textarea:focus-visible,
/* `select` also needs plain :focus: a mouse-opened dropdown does not always match
   :focus-visible, and the global select:focus rule would then win with --fill. */
.form-field select:focus,
.form-field select:focus-visible {
  outline: 0;
  border-color: color-mix(in srgb, var(--magenta) 55%, var(--violet));
  background-color: var(--field-bg-focus);
  box-shadow: 0 0 0 3px var(--field-ring);
}

/* The global `input:disabled` rule paints --fill-soft, which in the dark theme is
   a dark tint — that would read as a different family from the enabled --field-bg.
   Scoped here so public-form fields keep their existing disabled styling. */
.form-field input:disabled,
.form-field select:disabled,
.form-field textarea:disabled {
  background-color: var(--field-bg);
}

/* A React Select inside a field group must match the neighbour inputs
   (.form-field input = --radius-sm), not the standalone 14px trigger. */
.form-field .c-select__btn {
  border-radius: var(--radius-sm);
}

/* ---------- List filtering ---------- */

.list-empty {
  margin: var(--space-6) 0;
  padding: var(--space-10) var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-2xl);
  /* Same painted surface as .empty, but with no icon or title: this is the
     "your query matched nothing" placeholder, which has to stay visually a step
     lighter than "there is no data yet". */
  background:
    var(--gradient-hairline) top / 100% 1px no-repeat,
    linear-gradient(var(--glass-tint), var(--glass-tint)),
    var(--gradient-surface);
  box-shadow: var(--elev-glow-1);
  text-align: center;
  color: var(--muted);
  line-height: var(--leading-normal);
}

[data-form-row][hidden],
[data-table-row][hidden] {
  display: none !important;
}

/* ---------- Status pill ---------- */

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--control-sm);
  padding: 0 var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-full);
  background: var(--fill-soft);
  color: var(--cream);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
}

.status-pill:hover {
  background: var(--fill);
}

.status-pill:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.status-pill__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--muted);
}

.status-pill--published .status-pill__dot,
.status-pill--ok .status-pill__dot { background: var(--ok); }
.status-pill--draft .status-pill__dot,
.status-pill--idle .status-pill__dot { background: var(--muted); }
.status-pill--closed .status-pill__dot,
.status-pill--danger .status-pill__dot { background: var(--danger); }
.status-pill--completed .status-pill__dot,
.status-pill--info .status-pill__dot { background: var(--cyan); }

.status-pill--static {
  cursor: default;
}

/* ---------- Empty state ---------- */

.empty {
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  padding: var(--space-12) var(--space-5);
  text-align: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-2xl);
  /* The brand-hairline + glass stack used by .surface / .cab-panel, so a
     placeholder reads as part of the product instead of as the generic dashed
     box. The page-level variants below keep their larger padding/gap, so
     Overview, /forms and /account are visually unchanged. */
  background:
    var(--gradient-hairline) top / 100% 1px no-repeat,
    linear-gradient(var(--glass-tint), var(--glass-tint)),
    var(--gradient-surface);
  box-shadow: var(--elev-glow-1);
}

.empty__icon {
  width: 2.5rem;
  height: 2.5rem;
  stroke: color-mix(in srgb, var(--violet) 62%, var(--cyan));
  stroke-width: 1.4;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(
    0 0 calc(10px * var(--glow-strength)) color-mix(in srgb, var(--violet) 40%, transparent)
  );
}

.empty__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  color: var(--heading);
}

.empty__text {
  margin: 0;
  max-width: 34rem;
  font-size: var(--text-base);
  color: var(--muted);
  line-height: var(--leading-normal);
}

/* "Nothing matched the filter" reuses .empty, but must stay clearly lighter than
   the "no data yet" placeholder it shares a class with. The hook is already in
   the ResponsesTable markup, so no rename is needed. */
.filter-empty {
  gap: var(--space-2);
  padding: var(--space-10) var(--space-5);
  border-style: dashed;
  background: transparent;
  box-shadow: none;
}

/* ---- Page-level empty states (stage 4A: dashboard, 4B: forms, 5A: account) ----
   These placeholders match the panel language (brand hairline + soft surface)
   instead of the generic dashed box, so a screen with nothing in it still looks
   like the same product. Scoped to modifiers (and to the settings tab that owns
   one) because .empty is shared by eight other screens. */
.empty--dashboard,
.empty--forms,
.page-tabs--account .empty {
  gap: var(--space-4);
  padding: var(--space-16) var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-2xl);
  background:
    var(--gradient-hairline) top / 100% 1px no-repeat,
    linear-gradient(var(--glass-tint), var(--glass-tint)),
    var(--gradient-surface);
  box-shadow: var(--elev-glow-1);
}

.empty--dashboard .empty__icon,
.empty--forms .empty__icon,
.page-tabs--account .empty .empty__icon {
  width: 3rem;
  height: 3rem;
  stroke: color-mix(in srgb, var(--violet) 62%, var(--cyan));
  filter: drop-shadow(
    0 0 calc(10px * var(--glow-strength)) color-mix(in srgb, var(--violet) 40%, transparent)
  );
}

.empty--dashboard .empty__title,
.empty--forms .empty__title,
.page-tabs--account .empty .empty__title {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  letter-spacing: var(--tracking-tight);
}

/* ---------- Utilities ---------- */

.stack {
  display: grid;
  gap: var(--space-4);
  align-content: start;
}

.stack--sm { gap: var(--space-2); }
.stack--lg { gap: var(--space-6); }

.cluster {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.text-muted { color: var(--muted); }
.text-sm { font-size: var(--text-sm); }
.nums { font-variant-numeric: tabular-nums; }

/* The ambient scene is a marketing asset. Behind dense data views it washes
   out text and tints neutral surfaces, so the admin shell tones it down. */
.admin-body .scene__glow {
  opacity: 0.35;
}

.admin-body .blob {
  opacity: calc(var(--blob-opacity) * 0.4);
}

.admin-body .scene__grid {
  opacity: 0.5;
}

/* ---------- Editor chrome ---------- */

/* Stage 6.1: the editor header is a three-column grid instead of a wrapping
   flex row. The action cluster used to fall onto a second line, which doubled
   the bar height and shortened the sticky inspector column. The status select
   keeps the row it belongs to (the form itself) and in the two-line layout it
   stays beside the title, so the four actions fit on one line at 640–1199px. */
.editor-bar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  box-sizing: border-box;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas: "titles status actions";
  align-items: center;
  column-gap: var(--space-4);
  row-gap: var(--space-3);
  /* Bleed the frosted background across the main column (sidebar → right
     edge). Horizontal padding keeps titles/actions on the .app-page grid. */
  width: 100cqw;
  max-width: none;
  margin-left: calc(50% - 50cqw);
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
  padding-left: calc(50cqw - 50%);
  padding-right: calc(50cqw - 50%);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg) 84%, transparent);
  /* Same reduced chrome blur as the rail — see .app-sidebar. */
  backdrop-filter: blur(12px) saturate(var(--glass-saturate));
  -webkit-backdrop-filter: blur(12px) saturate(var(--glass-saturate));
}

.editor-bar > .page-bar__titles {
  grid-area: titles;
}

.editor-bar > .editor-meta__status {
  grid-area: status;
}

.editor-bar > .page-bar__actions {
  grid-area: actions;
}

/* At --bp-lg and below the actions no longer fit beside the titles. */
@media (max-width: 1199px) {
  .editor-bar {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "titles status"
      "actions actions";
  }
}

/* On phones the status would squeeze the one-line title down to a few
   characters, so it drops to its own row and the title keeps the full width. */
@media (max-width: 639px) {
  .editor-bar {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "titles"
      "status"
      "actions";
  }

  .editor-bar > .editor-meta__status {
    justify-self: start;
  }
}

@supports not (width: 1cqw) {
  .editor-bar {
    width: auto;
    margin-left: 0;
    padding-left: 0;
    padding-right: 0;
  }
}

/* The legacy editor (admin/form-edit.php) keeps the status select inside
   .page-bar__actions, so nothing lands in the `status` area of the template
   above. An empty `auto` track costs no width, but its gaps do: at 1200px and up
   the column-gap showed as a doubled gap between the title and the actions, and
   at <=1199px the empty row also spent an extra row-gap. React
   (FormEditorHeader) always renders the status as a direct child of .editor-bar,
   so :has() keeps this overlay inert for it. */
.editor-bar:not(:has(> .editor-meta__status)) {
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "titles actions";
}

/* Also drops the empty status row on phones, where the bar stacks into one
   column. Higher specificity than the base breakpoints above, so it wins
   without duplicating their other declarations. */
@media (max-width: 1199px) {
  .editor-bar:not(:has(> .editor-meta__status)) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "titles"
      "actions";
  }
}

/* Single column keeps long titles from stretching the track. */
.editor-bar .page-bar__titles {
  grid-template-columns: minmax(0, 1fr);
}

/* The name is editable in the card right below, so it stays on one line: a long
   title is truncated instead of growing the sticky bar. min-width:0 lets the
   nowrap item shrink below its min-content width so the ellipsis can kick in. */
.editor-bar .page-bar__title {
  min-width: 0;
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.editor-bar .back-link {
  margin-bottom: 0.35rem;
}

.editor-bar .page-bar__sub {
  margin-top: 0.3rem;
  overflow: hidden;
  font-size: var(--text-sm);
  white-space: nowrap;
  text-overflow: ellipsis;
}

.question-card:not(.is-editing) .q-fields,
.question-card:not(.is-editing) .unique-items-field {
  display: none;
}

.question-card:not(.is-editing) .question-card__body {
  padding-block: 0.65rem;
}

.question-card:not(.is-editing) .q-compact-title {
  display: inline-flex;
}

/* ---------- Responses ---------- */

.responses-view {
  display: grid;
  gap: var(--space-5);
}

.responses-view__tabs {
  width: fit-content;
}

.responses-kpis {
  display: flex;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}

div.row-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-1);
}

.link-btn {
  border: 0;
  padding: 0;
  background: none;
  color: var(--link);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

.link-btn:hover {
  color: var(--link-hover);
}

.share-inline {
  margin: calc(var(--space-4) * -1) 0 var(--space-5);
}

.share-inline .link-btn {
  margin-left: var(--space-2);
}

body.is-drawer-open {
  overflow: hidden;
}

.drawer__panel {
  background: var(--bg);
}

.filter-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.response-detail {
  display: grid;
  gap: var(--space-4);
}

@media (max-width: 639px) {
  /* Wrapped to three rows the bar covers ~30% of a phone viewport; keep it in flow. */
  .editor-bar {
    position: static;
  }

  .editor-bar .page-bar__actions {
    width: 100%;
  }
}

/* ---------- Page tabs ---------- */

.page-tabs {
  display: grid;
  gap: var(--space-5);
}

.page-tabs__bar {
  display: flex;
  gap: var(--space-1);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  margin: calc(var(--space-2) * -1) calc(var(--space-2) * -1) 0;
  padding: var(--space-1) var(--space-2) 0;
  border-bottom: 1px solid var(--line);
  scrollbar-width: none;
}

.page-tabs__bar::-webkit-scrollbar {
  display: none;
}

.page-tabs__btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--control-md, 2.5rem);
  padding: 0 var(--space-3);
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
}

.page-tabs__btn:hover {
  color: var(--cream);
}

.page-tabs__btn:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px var(--focus-ring);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}

.page-tabs__btn.is-active {
  color: var(--heading);
  border-bottom-color: var(--magenta);
}

.page-tabs__count {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--muted);
}

.page-tabs__btn.is-active .page-tabs__count {
  color: inherit;
}

.page-tabs__panel {
  min-width: 0;
}

.page-tabs__lead {
  margin: 0 0 var(--space-4);
  font-size: var(--text-base);
  color: var(--muted);
  line-height: var(--leading-snug);
}

.settings-card__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--heading);
}

.settings-card__form {
  margin-top: var(--space-4);
}

.catalog-toolbar {
  padding: var(--space-4) var(--space-4) 0;
}

.page-tabs[data-page-tabs-ready] .page-tabs__panel[hidden] {
  display: none;
}

/* ---------- Identity + workspace cards ---------- */

.identity {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}

.identity .app-avatar {
  width: 2.15rem;
  height: 2.15rem;
  font-size: 0.72rem;
}

.identity__text {
  min-width: 0;
  display: grid;
  gap: 2px;
}

.identity__name {
  font-weight: 700;
  color: var(--heading);
  overflow-wrap: anywhere;
}

.identity__meta {
  font-size: var(--text-sm);
  color: var(--muted);
}

.ws-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
}

.ws-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  text-align: left;
}

.ws-card.is-current {
  border-color: color-mix(in srgb, var(--magenta) 42%, var(--line));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--magenta) 28%, transparent);
}

.ws-card__body {
  min-width: 0;
  flex: 1 1 auto;
  display: grid;
  gap: 2px;
}

.ws-card__name {
  margin: 0;
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--heading);
  overflow-wrap: anywhere;
}

.ws-card__meta {
  font-size: var(--text-sm);
  color: var(--muted);
}

.ws-card__action {
  flex: 0 0 auto;
}

.invite-banner {
  display: grid;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
  border-color: color-mix(in srgb, var(--ok) 35%, var(--line));
}

.invite-banner__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.invite-banner__link {
  flex: 1 1 14rem;
  min-width: 0;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--fill-soft);
  overflow-wrap: anywhere;
}

.invite-banner__link code {
  font-size: var(--text-sm);
}

.settings-card {
  max-width: 36rem;
}

/* Standalone status page (e.g. email confirmation): the single card sits in the
   middle of the viewport. `margin: auto` on the child centres it and, unlike
   `align-items: center`, keeps the top of tall content reachable by scrolling. */
.auth-page {
  display: flex;
  min-height: 100vh;
  min-height: 100dvh;
  padding: clamp(1rem, 5vh, 3rem) clamp(1rem, 5vw, 2rem);
  box-sizing: border-box;
}

.auth-page > * {
  margin: auto;
  width: 100%;
  min-width: 0;
}

.fields-2 {
  display: grid;
  gap: var(--space-4);
}

@media (min-width: 640px) {
  .fields-2 {
    grid-template-columns: 1fr 1fr;
  }
}

.catalog-block {
  border-bottom: 1px solid var(--line);
}

.catalog-block:last-child {
  border-bottom: 0;
}

.catalog-block__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  cursor: pointer;
  list-style: none;
}

.catalog-block__summary::-webkit-details-marker {
  display: none;
}

.catalog-block__summary::after {
  content: "";
  flex: 0 0 0.55rem;
  width: 0.55rem;
  height: 0.55rem;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  transition: transform 0.16s ease;
}

.catalog-block[open] > .catalog-block__summary::after {
  transform: rotate(-135deg);
}

.catalog-block__summary:hover {
  background: var(--fill-soft);
}

.catalog-block__summary:focus-visible {
  outline: 0;
  box-shadow: inset 0 0 0 3px var(--focus-ring);
}

.catalog-block__body {
  padding: 0 var(--space-4) var(--space-4);
}

.narrow-form {
  max-width: 28rem;
}
