/* ==========================================================================
   Air_DS primitives - Button, Icon Button, Badge, Avatar, Tabs, Icon
   --------------------------------------------------------------------------
   Hand-authored, but every value is transcribed from the Figma component's
   *bound variables* (pulled with get_design_context + the Plugin API), never
   eyeballed from a screenshot. Where Figma resolves a property to a variable,
   this file uses the matching custom property from tokens.css - so a token
   change in Figma flows through without touching this file.

   Token name mapping: Figma's Dev Mode emits `--color-air-action-primary`,
   `--radius-air-sm`, `--space-air-8`. We generate the same values under
   shorter names (`--color-action-primary`, `--radius-sm`, `--space-8`) because
   the `air` infix is redundant in a repo with one design system. Drop the
   `-air` when transcribing Dev Mode output.

   Known Figma-side discrepancy: the Button page's description text says
   "Corner radius is radius.xs (4px)". The components are actually bound to
   radius/sm (6px). The binding wins - the prose is stale.
   ========================================================================== */

/* ---------------- Icon ----------------
   One <symbol> per glyph in assets/icons/sprite.svg, painted with
   currentColor so a single sprite serves dark-on-light and light-on-dark. */

.ic {
  width: var(--ic-size, 20px);
  height: var(--ic-size, 20px);
  flex: none;
  fill: currentColor;
  stroke: currentColor;
  /* Icons-page glyphs are filled shapes; SideMenu glyphs are strokes. Setting
     both and letting each symbol's own attributes win keeps one class working
     for both families. */
  stroke-width: 0;
  pointer-events: none;
}
.ic--nav { stroke-width: 1.4625; fill: none; }
.ic-16 { --ic-size: 16px; }
.ic-18 { --ic-size: 18px; }
.ic-20 { --ic-size: 20px; }
.ic-24 { --ic-size: 24px; }
.ic-32 { --ic-size: 32px; }

/* ---------------- Button ----------------
   Figma: Button / {Primary,Secondary,Outline,Ghost,Destructive,Link}
          x Size {Small,Default,Large} x State {Default,Hover,Pressed,Focus,Disabled}
   Shared: radius sm (6px), gap 8px, Manrope Medium, 20px icons. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font-family: var(--font-main);
  font-weight: 500;
  white-space: nowrap;
  text-decoration: none;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.btn:hover { text-decoration: none; }
.btn .ic { --ic-size: 20px; }

/* Sizes. Heights are explicit rather than derived from padding + line-height,
   because Figma fixes them (24 / 32 / 40) and letting them be intrinsic makes
   a button 1px taller the moment a descender lands in the label. */
.btn--sm { height: 24px; padding: 0 var(--space-8); font-size: 13px; }
.btn--md { height: 32px; padding: 0 var(--space-12); font-size: 14px; }
.btn--lg { height: 40px; padding: 0 var(--space-16); font-size: 16px; }

.btn--primary { background: var(--color-action-primary); color: var(--color-text-inverse); }
.btn--primary:hover { background: var(--color-action-primary-hover); }
.btn--primary:active { background: var(--color-action-primary-active); }

.btn--secondary { background: var(--color-action-secondary); color: var(--color-text-primary); }
.btn--secondary:hover { background: var(--color-action-secondary-hover); }
.btn--secondary:active { background: var(--color-action-secondary-active); }

.btn--outline {
  background: var(--color-surface-canvas);
  color: var(--color-text-primary);
  border-color: var(--color-border-default);
}
.btn--outline:hover { background: var(--color-action-secondary-hover); }
.btn--outline:active { background: var(--color-action-secondary-active); }

.btn--ghost { background: transparent; color: var(--color-text-primary); }
.btn--ghost:hover { background: var(--color-action-secondary-hover); }
.btn--ghost:active { background: var(--color-action-secondary-active); }

.btn--destructive { background: var(--color-critical-50); color: var(--color-status-critical); }
.btn--destructive:hover { background: var(--color-critical-100); }
.btn--destructive:active { background: var(--color-critical-200); }

.btn--link { background: transparent; color: var(--color-text-link); padding: 0; height: auto; }
.btn--link:hover { text-decoration: underline; }

/* Focus is a brand-teal ring rather than the UA outline, matching State=Focus. */
.btn:focus-visible { outline: none; border-color: var(--color-border-focus); }

.btn:disabled,
.btn[aria-disabled="true"] {
  background: var(--color-action-action-disabled);
  color: var(--color-text-disabled);
  border-color: transparent;
  cursor: not-allowed;
  pointer-events: none;
}

/* ---------------- Icon Button ----------------
   Figma: square, radius xs (4px) - note this differs from Button's radius sm. */

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  color: var(--color-text-primary);
  transition: background-color 120ms ease, border-color 120ms ease;
}
.icon-btn--sm { width: 24px; height: 24px; }
.icon-btn--sm .ic { --ic-size: 16px; }
.icon-btn--md { width: 32px; height: 32px; }
.icon-btn--md .ic { --ic-size: 20px; }
.icon-btn--lg { width: 40px; height: 40px; }
.icon-btn--lg .ic { --ic-size: 24px; }

.icon-btn--primary { background: var(--color-action-primary); color: var(--color-text-inverse); }
.icon-btn--primary:hover { background: var(--color-action-primary-hover); }
.icon-btn--primary:active { background: var(--color-action-primary-active); }

.icon-btn--secondary { background: var(--color-action-secondary); }
.icon-btn--secondary:hover { background: var(--color-action-secondary-hover); }
.icon-btn--secondary:active { background: var(--color-action-secondary-active); }

.icon-btn--outline { background: var(--color-surface-canvas); border-color: var(--color-border-default); }
.icon-btn--outline:hover { background: var(--color-action-secondary-hover); }
.icon-btn--outline:active { background: var(--color-action-secondary-active); }

.icon-btn--ghost { background: transparent; }
.icon-btn--ghost:hover { background: var(--color-action-secondary-hover); }
.icon-btn--ghost:active { background: var(--color-action-secondary-active); }

.icon-btn--destructive { background: var(--color-critical-50); color: var(--color-status-critical); }
.icon-btn--destructive:hover { background: var(--color-critical-100); }

.icon-btn:focus-visible { outline: none; border-color: var(--color-border-focus); }
.icon-btn:disabled { background: var(--color-action-action-disabled); color: var(--color-text-disabled); pointer-events: none; }

/* ---------------- Badge ----------------
   Figma: Type x Size {Small,Default} x Shape {Square,Rounded}. Square is
   radius xs, Rounded is radius full. */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  font-family: var(--font-main);
  font-weight: 500;
  white-space: nowrap;
}
.badge--sm { height: 24px; padding: 0 var(--space-8); font-size: 12px; }
.badge--md { height: 32px; padding: 0 var(--space-12); font-size: 14px; }
.badge--round { border-radius: var(--radius-full); }
.badge .ic { --ic-size: 14px; }

.badge--default { background: var(--color-action-primary); color: var(--color-text-inverse); }
.badge--secondary { background: var(--color-action-secondary); color: var(--color-text-primary); }
.badge--outline { background: transparent; color: var(--color-text-primary); border-color: var(--color-border-default); }
.badge--success { background: var(--color-status-success-bg); color: var(--color-status-success-fg-strong); border-color: var(--color-status-success-border); }
.badge--warning { background: var(--color-status-warning-bg); color: var(--color-status-warning-fg-strong); border-color: var(--color-status-warning-border); }
.badge--critical { background: var(--color-status-critical-bg); color: var(--color-status-critical-fg-strong); border-color: var(--color-status-critical-border); }

/* ---------------- Avatar ----------------
   Figma: Size {sm 24, default 32, lg 40} x Content {Image, Fallback}. */

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  overflow: hidden;
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  font-family: var(--font-main);
  font-weight: 500;
  font-size: 12px;
  text-transform: uppercase;
  user-select: none;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--sm { width: 24px; height: 24px; }
.avatar--md { width: 32px; height: 32px; }
.avatar--lg { width: 40px; height: 40px; }

/* A count badge pinned to an avatar - the shell's unread-notifications pattern. */
.avatar-wrap { position: relative; display: inline-flex; flex: none; }
.avatar-count {
  position: absolute;
  top: -6px;
  left: -6px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  background: var(--color-action-primary);
  color: var(--color-text-inverse);
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
}

/* ---------------- Tabs ----------------
   Figma: Tabs Trigger, Style {Default, Line} x State (5). h28, radius md. */

.tabs { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.tabs--enclosed {
  background: var(--color-surface-sunken);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  gap: var(--space-4);
  display: inline-flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  max-width: 100%;
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  height: 28px;
  padding: var(--space-4) var(--space-8);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  font-family: var(--font-main);
  font-size: 14px;
  font-weight: 400;
  color: var(--color-text-secondary);
  white-space: nowrap;
  transition: background-color 120ms ease, color 120ms ease;
}
.tab:hover { color: var(--color-text-primary); }
.tab[aria-selected="true"] { background: var(--color-surface-default); color: var(--color-text-primary); }
.tab:focus-visible { outline: none; border-color: var(--color-border-focus); }
.tab:disabled { color: var(--color-text-disabled); pointer-events: none; }

/* Style=Line: the active marker is an underline rather than a filled pill. */
.tabs--line { gap: var(--space-16); border-bottom: 1px solid var(--color-border-subtle); }
.tabs--line .tab {
  height: 35px;
  border-radius: 0;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.tabs--line .tab[aria-selected="true"] {
  background: transparent;
  border-bottom-color: var(--color-action-primary);
}

/* Panel mode (UI.tabs called with per-item `panel`s). The tablist and its one
   re-rendered panel are stacked in a column; the panel is a plain block so its
   own content owns the layout. The panel gains a focus ring when arrowed to,
   matching the roving-focus contract in components.js. */
.tabs-wrap { display: flex; flex-direction: column; gap: var(--space-16); }
.tab-panel { min-width: 0; }
.tab-panel:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--color-border-focus); border-radius: var(--radius-sm); }
@media (prefers-reduced-motion: no-preference) {
  /* A 120ms cross-fade on swap, per V6-SPEC.md 15. Keyed on the panel node
     being replaced wholesale (re-render, not hide), so the fade always plays
     from a fresh element. */
  .tab-panel { animation: ui-tab-fade 120ms ease; }
}
@keyframes ui-tab-fade { from { opacity: 0; } to { opacity: 1; } }

/* ---------------- Field (studio row) ----------------
   One studio field row (V6-SPEC.md 10.1 / 10.5): a label + ownership badge on
   the head line, then the control, an optional warning, and help text. A
   primitive, so it lives here beside Menu rather than in a v6 sheet, and it is
   design-agnostic - the studio surface classes (.v6-field*) wrap it.

   Ownership badges are deliberately monochrome. Teal is reserved for verified
   and nothing else, and a measured field is not a verified one, so none of the
   four classes borrows a status colour - the word ("measured") carries it. */

.ui-field { display: flex; flex-direction: column; gap: var(--space-8); }
.ui-field-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-12); }

.ui-field-label {
  font-family: var(--font-main);
  font-size: var(--text-body-m-size);
  font-weight: 500;
  color: var(--color-text-primary);
}

.ui-field-own {
  flex: none;
  font-family: var(--font-main);
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.02em;
  padding: 4px 8px;
  border-radius: var(--radius-full);
  text-transform: lowercase;
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  border: 1px solid var(--color-border-subtle);
}
/* Measured / community / editorial stay in the neutral family - a slightly
   heavier weight is the only distinction, so nothing competes with teal. */
.ui-field-own--measured,
.ui-field-own--community,
.ui-field-own--editorial { color: var(--color-text-tertiary); }

.ui-field-input {
  width: 100%;
  font-family: var(--font-main);
  font-size: var(--text-body-m-size);
  color: var(--color-text-primary);
  background: var(--color-surface-canvas);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sm);
  padding: var(--space-8) var(--space-12);
}
textarea.ui-field-input { min-height: 96px; resize: vertical; line-height: var(--text-body-m-line); }
.ui-field-input:focus-visible { outline: none; border-color: var(--color-border-focus); }

/* Read-only value for a non-authored field. No input affordances: flat
   surface, no border box, so it never reads as editable. */
.ui-field-ro {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-12);
  font-family: var(--font-main);
  font-size: var(--text-body-m-size);
  color: var(--color-text-primary);
}
.ui-field-report {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  color: var(--color-text-link);
  text-decoration: none;
  white-space: nowrap;
}
.ui-field-report:hover { text-decoration: underline; }
.ui-field-report .ic { --ic-size: 16px; }

.ui-field-warn {
  display: flex;
  align-items: flex-start;
  gap: var(--space-8);
  font-family: var(--font-main);
  font-size: var(--text-body-s-size);
  line-height: var(--text-body-s-line);
  color: var(--color-status-warning-fg-strong);
}
.ui-field-warn .ic { --ic-size: 16px; flex: none; margin-top: 1px; color: var(--color-status-warning); }

.ui-field-help {
  font-family: var(--font-main);
  font-size: var(--text-body-s-size);
  line-height: var(--text-body-s-line);
  color: var(--color-text-tertiary);
}

/* ---------------- Tooltip ----------------
   Figma: Tooltip (223:41), Side={Top,Right,Bottom,Left}.
   pad 8/12, radius xs, surface/inverse, Manrope Medium 11, text/inverse.

   Air's four variants differ only in which side they sit on, so this is one
   rule plus a placement modifier rather than four blocks. Rendered as a
   pseudo-element on [data-tip] so any element can carry one without extra
   markup - which is how the collapsed sidebar already used it. */

[data-tip] { position: relative; }

[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  z-index: 60;
  padding: var(--space-8) var(--space-12);
  border-radius: var(--radius-xs);
  background: var(--color-surface-inverse);
  color: var(--color-text-inverse);
  font-family: var(--font-main);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 120ms ease, visibility 120ms ease;
}

/* Side=Right is the default: it is the one the collapsed icon rail needs. */
[data-tip]::after { left: calc(100% + var(--space-8)); top: 50%; transform: translateY(-50%); }
[data-tip-side="top"]::after { left: 50%; top: auto; bottom: calc(100% + var(--space-8)); transform: translateX(-50%); }
[data-tip-side="bottom"]::after { left: 50%; top: calc(100% + var(--space-8)); transform: translateX(-50%); }
[data-tip-side="left"]::after { left: auto; right: calc(100% + var(--space-8)); top: 50%; transform: translateY(-50%); }

/* Opt-in: an element only shows its tip where the surrounding UI wants one.
   The sidebar enables this for the collapsed rail (see store.css). */
[data-tip][data-tip-always]:hover::after,
[data-tip][data-tip-always]:focus-visible::after {
  opacity: 1; visibility: visible; transition-delay: 250ms;
}

/* ---------------- Dialog ----------------
   Figma: Dialog (page 181:32). 420 wide, pad 24, gap 16, radius lg,
   surface/elevated on a border/subtle hairline, shadow.lg.
   Header is a 8px-gap column of Title (Manrope SemiBold 18) + Description
   (Manrope Regular 14, text/secondary). Footer is an 8px-gap row of buttons. */

.dialog-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: var(--space-24);
  /* Air does not specify a scrim. neutral/900 at 45% keeps the dialog's own
     shadow.lg readable, which a lighter wash would flatten. */
  background: rgba(17, 23, 23, 0.45);
}

.dialog {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-16);
  width: min(420px, 100%);
  padding: var(--space-24);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-lg);
  background: var(--color-surface-elevated);
  box-shadow: var(--shadow-lg);
}
/* Media dialogs (the video modal) need to be far wider than a confirm dialog,
   and the frame is the content, so it drops the padding. */
.dialog--media { width: min(900px, 100%); padding: var(--space-12); gap: var(--space-8); }

.dialog-header { display: flex; flex-direction: column; gap: var(--space-8); }
.dialog-title {
  font-family: var(--font-main);
  font-size: var(--text-title-size);
  line-height: var(--text-title-line);
  font-weight: var(--text-title-weight);
  letter-spacing: var(--text-title-tracking);
  color: var(--color-text-primary);
}
.dialog-desc {
  font-family: var(--font-main);
  font-size: var(--text-body-m-size);
  line-height: var(--text-body-m-line);
  color: var(--color-text-secondary);
}
.dialog-footer { display: flex; gap: var(--space-8); justify-content: flex-end; }

.dialog-close {
  position: absolute;
  top: var(--space-12);
  right: var(--space-12);
  width: 24px; height: 24px;
  display: grid; place-items: center;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-text-tertiary);
}
.dialog-close:hover { background: var(--color-action-secondary-hover); color: var(--color-text-primary); }
.dialog-close:focus-visible { outline: none; border-color: var(--color-border-focus); }
.dialog-close .ic { --ic-size: 16px; }

/* Verbatim text inside a dialog - the install prompt the CLI hand-off shows
   before it opens a terminal (openCliDisclosure, app.js). Monospace because it
   is a payload being quoted, not prose, and wrapping is not optional: a pinned
   repo URL carries a 40-character sha, which in a plain <pre> would slide out
   of a 420px dialog and hide the very words the sheet exists to disclose. */
.cli-prompt {
  margin: 0;
  padding: var(--space-12);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-md);
  background: var(--color-surface-canvas);
  color: var(--color-text-primary);
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 12px;
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 40vh;
  overflow-y: auto;
}
.cli-prompt:focus-visible { outline: none; border-color: var(--color-border-focus); }

/* ---------------- Menu (anchored popover) ----------------
   Net-new for v5 (V5-SPEC.md §7). A primitive, so it lives here (not scoped to
   a design). Positioned with fixed top/left set by JS; width set inline. Sits
   above the v3 topbar (z 60), below full dialogs (z 100) and below the toast
   (z 110, store.css) - the mobile variant IS a dialog and layers there. */
.ui-menu {
  position: fixed;
  z-index: 95;
  display: flex;
  flex-direction: column;
  padding: var(--space-8) 0;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-lg);
  background: var(--color-surface-floating);
  box-shadow: var(--shadow-lg);
  /* Kept opaque at rest and animated by transform only: a fade that relies on a
     CSS transition can be left mid-way (invisible) when the tab is not actively
     rendering. Worst case here the menu paints 4px high, never hidden. */
  transform: translateY(-4px);
  transform-origin: top right;
  transition: transform 140ms cubic-bezier(.2,.8,.2,1);
}
.ui-menu.in { transform: translateY(0); }
.ui-menu[data-flip="up"] { transform-origin: bottom right; }

.ui-menu-heading {
  padding: var(--space-8) var(--space-12) var(--space-4);
  font-family: var(--font-main);
  font-size: 11px; font-weight: 600; line-height: 1.4;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--color-text-tertiary);
}
.ui-menu-divider {
  height: 1px; margin: var(--space-4) 0;
  background: var(--color-border-subtle);
}
.ui-menu-item {
  display: flex; align-items: center; gap: var(--space-12);
  width: 100%; text-align: left;
  padding: 10px var(--space-12);
  border: 0; background: none; cursor: pointer;
  color: var(--color-text-primary);
  transition: background-color 80ms ease;
}
.ui-menu-item:hover { background: var(--color-neutral-100); }
.ui-menu-item:active { background: var(--color-neutral-150); }
.ui-menu-item:focus-visible { outline: none; background: var(--color-neutral-100); box-shadow: inset 0 0 0 2px var(--color-border-focus); }
.ui-menu-item[aria-disabled="true"] { color: var(--color-text-disabled); cursor: default; }
.ui-menu-ic { flex: none; width: 20px; height: 20px; display: grid; place-items: center; color: var(--color-text-secondary); }
.ui-menu-ic svg { width: 20px; height: 20px; }
.ui-menu-item[aria-disabled="true"] .ui-menu-ic { color: var(--color-text-disabled); }
.ui-menu-text { display: grid; gap: 1px; min-width: 0; }
.ui-menu-label { font-family: var(--font-main); font-size: var(--text-body-m-size); font-weight: 500; line-height: 1.3; }
.ui-menu-sub { font-family: var(--font-main); font-size: 12px; line-height: 1.35; color: var(--color-text-tertiary); }
.ui-menu-rec {
  margin-left: auto; flex: none;
  font-family: var(--font-main); font-size: 11px; font-weight: 600; line-height: 1;
  padding: 3px 7px; border-radius: var(--radius-full);
  color: var(--color-brand-700); background: var(--color-brand-50);
}
.cta-sheet-heading {
  padding: var(--space-12) var(--space-4) var(--space-4);
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--color-text-tertiary);
}

@media (prefers-reduced-motion: reduce) {
  .ui-menu { transition: none; transform: none; }
  .ui-menu.in { transform: none; }
}
