/*
  FWF Apps Design System — Components
  Layout-neutral brand controls + primitives: button, icon-button, badge, status,
  note, card/panel, field/input, table, tabs, modal, toast, link, avatar, skeleton,
  empty-state, plus type and layout primitives (kicker/title/subtitle, page
  header, section, toolbar, stack/cluster/grid/split/container). Compose into
  any layout. Requires foundation. This file implements
  docs/fwfa-component-contract.md; add reusable classes here before adding
  product-local CSS.
*/
@layer fwfa.components {
.fwfa-container {
  width: min(100%, var(--fwfa-content-max));
  margin-inline: auto;
}

.fwfa-page-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--fwfa-space-4);
  margin-bottom: var(--fwfa-space-5);
}

.fwfa-page-header-actions,
.fwfa-action-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fwfa-space-2);
}

.fwfa-section {
  display: grid;
  gap: var(--fwfa-space-4);
}

.fwfa-section + .fwfa-section {
  margin-top: var(--fwfa-space-6);
}

.fwfa-section-header {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: var(--fwfa-space-3);
}

.fwfa-stack {
  display: flex;
  flex-direction: column;
  gap: var(--fwfa-stack-gap, var(--fwfa-space-4));
}

.fwfa-cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fwfa-cluster-gap, var(--fwfa-space-3));
}

.fwfa-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--fwfa-grid-min, 16rem)), 1fr));
  gap: var(--fwfa-grid-gap, var(--fwfa-space-4));
}

.fwfa-split {
  display: grid;
  grid-template-columns: minmax(0, var(--split-main, 1fr)) minmax(min(100%, var(--split-side-min, 16rem)), var(--split-side, 22rem));
  gap: var(--fwfa-grid-gap, var(--fwfa-space-4));
  align-items: start;
}

.fwfa-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--fwfa-space-3);
  padding: var(--fwfa-space-3);
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-md);
  background: var(--fwfa-surface);
}

.fwfa-toolbar-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fwfa-space-2);
}

.fwfa-kicker {
  margin: 0;
  margin-bottom: var(--fwfa-space-3);
  color: var(--fwfa-text-muted);
  font-family: var(--fwfa-font-mono);
  font-size: var(--fwfa-type-micro);
  font-weight: var(--fwfa-weight-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.fwfa-title {
  margin: 0;
  color: var(--fwfa-text);
  font-family: var(--fwfa-font-display);
  font-size: var(--fwfa-type-h1);
  /* Display font is Ubuntu (300/400/500/700 — no 600); titles stay bold at 700. */
  font-weight: 700;
  line-height: var(--fwfa-leading-tight);
  letter-spacing: 0;
}

.fwfa-subtitle {
  max-width: 68ch;
  margin: 0;
  color: var(--fwfa-text-soft);
  font-size: var(--fwfa-type-body);
}

.fwfa-title--section {
  font-size: var(--fwfa-type-h2);
}

.fwfa-title--component {
  font-size: var(--fwfa-type-h3);
}

/* Compact subtitle — the supporting line inside a card/specimen, a step down
   from the section-lede default so it doesn't compete with the title above it. */
.fwfa-subtitle--sm {
  font-size: var(--fwfa-type-sm);
}

/* Card / panel — flat tonal grouping. Use cards only for repeated items,
   specimens, and overlays that need a real frame; otherwise group with space. */
.fwfa-popover {
  max-width: calc(100vw - var(--fwfa-space-4) * 2);
  max-height: min(20rem, calc(100dvh - var(--fwfa-space-8)));
  overflow-y: auto;
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-md);
  background: var(--fwfa-surface-opaque);
  color: var(--fwfa-text);
}

/* Menu — the open dropdown / listbox panel: the floating list a select trigger,
   command menu, or row action opens. A solid floated frame (so it reads above
   arbitrary content) holding quiet item rows that warm on hover; the current
   choice carries a sky tick, never the spent yellow. In a real app this is
   positioned (fixed / anchored to its trigger via the popover or native APIs) and
   toggled; the showcase renders it open. Give the trigger aria-expanded + the menu
   role="listbox"/"menu" and items role="option"/"menuitem" in markup. */
.fwfa-menu {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 12rem;
  max-width: calc(100vw - var(--fwfa-space-4) * 2);
  max-height: min(20rem, calc(100dvh - var(--fwfa-space-8)));
  overflow-y: auto;
  padding: var(--fwfa-space-1);
  border: 1px solid var(--fwfa-border-strong);
  border-radius: var(--fwfa-radius-md);
  /* Opaque floating panel — must not be translucent or the content behind shows
     through the open menu. */
  background: var(--fwfa-surface-opaque);
  box-shadow: var(--fwfa-shadow-float);
}

.fwfa-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--fwfa-space-3);
  width: 100%;
  min-height: var(--fwfa-control);
  border: 0;
  border-radius: var(--fwfa-radius-sm);
  padding: var(--fwfa-space-2) var(--fwfa-space-3);
  background: transparent;
  color: var(--fwfa-text-soft);
  font: inherit;
  font-size: var(--fwfa-type-sm);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--fwfa-dur-fast) var(--fwfa-ease),
    color var(--fwfa-dur-fast) var(--fwfa-ease);
}

.fwfa-menu-item:hover {
  background: var(--fwfa-surface-hover);
  color: var(--fwfa-text);
}

/* Current selection — the label firms and a sky tick appears in the trailing slot
   (the calm structural mark beside the choice, matching the link/focus accent). */
.fwfa-menu-item[aria-selected="true"],
.fwfa-menu-item[aria-checked="true"] {
  color: var(--fwfa-text);
}

.fwfa-menu-item[aria-selected="true"]::after,
.fwfa-menu-item[aria-checked="true"]::after {
  content: "";
  flex: none;
  width: 1rem;
  height: 1rem;
  background-color: var(--fwfa-accent-calm);
  -webkit-mask: var(--fwfa-icon-check) center / 1rem no-repeat;
  mask: var(--fwfa-icon-check) center / 1rem no-repeat;
}

/* A muted leading hint (shortcut, count, group). Pushes the tick to the far edge. */
.fwfa-menu-item .fwfa-menu-item-meta {
  margin-left: auto;
  color: var(--fwfa-text-muted);
  font-family: var(--fwfa-font-mono);
  font-size: var(--fwfa-type-xs);
}

.fwfa-menu-item[aria-selected="true"] .fwfa-menu-item-meta,
.fwfa-menu-item[aria-checked="true"] .fwfa-menu-item-meta {
  margin-left: auto;
}

.fwfa-menu-separator {
  height: 1px;
  margin: var(--fwfa-space-1) 0;
  background: var(--fwfa-border-faint);
}

.fwfa-panel {
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-md);
  padding: var(--fwfa-space-5);
  color: var(--fwfa-text);
  background: var(--fwfa-card-bg);
  box-shadow: var(--fwfa-shadow-hairline);
}

.fwfa-card {
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-md);
  padding: var(--fwfa-space-4);
  color: var(--fwfa-text);
  background: var(--fwfa-card-bg);
  box-shadow: var(--fwfa-shadow-hairline);
  transition:
    transform var(--fwfa-dur-base) var(--fwfa-ease),
    background var(--fwfa-dur-base) var(--fwfa-ease),
    border-color var(--fwfa-dur-base) var(--fwfa-ease);
}

.fwfa-card[href],
.fwfa-card[role="button"],
.fwfa-card--interactive {
  cursor: pointer;
}

/* Interactive cards move by state and strengthen the existing border. */
.fwfa-card[href]:hover,
.fwfa-card[role="button"]:hover,
.fwfa-card--interactive:hover {
  transform: translateY(-2px);
  border-color: var(--fwfa-border-strong);
  background: var(--fwfa-surface-hover);
}

/* Brand mark — theme-aware FWF lockup. Prefer this in app chrome over a raw
   image src so [data-fwf-app-theme="light"] gets the navy-on-light asset. */
.fwfa-fwf-logo {
  display: block;
  width: var(--logo-width, 7rem);
  max-width: 100%;
  aspect-ratio: 429 / 188;
  flex: none;
  background: var(--fwfa-fwf-logo) left center / contain no-repeat;
}

.fwfa-fwf-logo--sm { --logo-width: 5rem; /* @local */ }
.fwfa-fwf-logo--lg { --logo-width: 11.5rem; /* @local */ }

/* Icon primitive — a bare glyph by default. Do not wrap icons in their own fill or
   outline unless the framed modifier is deliberately requested for a specimen. */
.fwfa-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--icon-size, 1.25rem);
  height: var(--icon-size, 1.25rem);
  flex: none;
  /* Bare icon paints nothing and reserves no padding — only --framed adds a
     surface/border. Explicit resets so a bare glyph never reads as a chip, and
     vertical-align middle keeps it on the text mid-line when used inline. */
  padding: 0;
  background: none;
  color: var(--icon-colour, currentColor);
  line-height: 1;
  vertical-align: middle;
}

.fwfa-icon > svg,
.fwfa-icon > img {
  display: block;
  width: 100%;
  height: 100%;
}

.fwfa-icon--framed {
  --icon-size: 1.25rem; /* @local */        /* glyph stays at its natural inline size */
  box-sizing: content-box;                  /* padding grows the tile, not the glyph */
  padding: var(--fwfa-space-3);             /* 0.75rem consistent internal padding → ~44px tile */
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-md);
  background: var(--fwfa-surface);
  color: var(--fwfa-text-soft);
  box-shadow: var(--fwfa-shadow-hairline);  /* subtle lift, already theme-aware */
}

.fwfa-button,
.fwfa-icon-button {
  min-width: var(--fwfa-control);
  min-height: var(--fwfa-control);
  border: 0;
  cursor: pointer;
  text-decoration: none;
}

.fwfa-button {
  /* The variant's label colour, named so the spinner and selected overlay can
     track it while the loading state sets `color: transparent` (mirrors the core
     system's --btn-ink). */
  --btn-ink: var(--fwfa-text); /* @local */
  /* Consistency floor for the standard text button (the compact variant below and
     .fwfa-icon-button keep their own smaller square floors). */
  min-width: var(--fwfa-button-min);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--fwfa-space-2);
  border-radius: var(--fwfa-radius-sm);
  padding: 0 var(--fwfa-space-3);
  font-size: var(--fwfa-type-sm);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.02em;
  white-space: nowrap;
  /* All channels at dur-fast, matching the core system's button transition. */
  transition:
    background-color var(--fwfa-dur-fast) var(--fwfa-ease),
    border-color var(--fwfa-dur-fast) var(--fwfa-ease),
    color var(--fwfa-dur-fast) var(--fwfa-ease),
    box-shadow var(--fwfa-dur-fast) var(--fwfa-ease),
    transform var(--fwfa-dur-fast) var(--fwfa-ease);
}

.fwfa-root .fwfa-button {
  font-size: var(--fwfa-type-sm);
}

.fwfa-button:hover {
  transform: translateY(-1px);
}

.fwfa-button:active {
  transform: translateY(0);
}

.fwfa-button--compact {
  min-width: var(--fwfa-control-compact);
  min-height: var(--fwfa-control-compact);
  padding: 0 var(--fwfa-space-3);
}

.fwfa-root .fwfa-button--compact {
  font-size: var(--fwfa-type-xs);
}

/* Primary — a neutral solid, never a brand fill. Dark: white block, dark label.
   Light: navy block, white label (a white-on-white block would collapse into the
   ghost). Hover/active deepen the fill a neutral step and lift — no hue change, since
   all blue is reserved for links and focus, per the core system. */
.fwfa-button--primary {
  --btn-ink: var(--fwfa-primary-ink); /* @local */
  border: 1px solid var(--fwfa-text);
  background: var(--fwfa-text);
  color: var(--fwfa-primary-ink);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);
}

.fwfa-button--primary:hover {
  background: var(--fwfa-primary-hover);
  border-color: var(--fwfa-primary-hover);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.3);
}

.fwfa-button--primary:active {
  background: var(--fwfa-primary-active);
  border-color: var(--fwfa-primary-active);
  box-shadow: none;
}

/* Compact variant: scale the shadow down to match the smaller button body.
   The full-size 10px/24px shadow is proportional for a 2.25rem button; at
   1.6rem (compact) it overwhelms the control and bleeds into adjacent content. */
.fwfa-button--primary.fwfa-button--compact {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.fwfa-button--primary.fwfa-button--compact:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.38), 0 1px 3px rgba(0, 0, 0, 0.22);
}

/* Light theme takes its navy fill steps from the --fwfa-primary-* tokens; only the
   shadow tint differs from the dark default, so that's all this override carries.
   Ink colour is now --fwfa-primary-ink (#ffffff in light) which inherits from the
   nearest theme ancestor — no descendant-combinator override needed here. */
[data-fwf-app-theme="light"] .fwfa-button--primary:hover {
  box-shadow: 0 6px 16px rgba(8, 27, 51, 0.22), 0 1px 3px rgba(8, 27, 51, 0.18);
}
[data-fwf-app-theme="light"] .fwfa-button--primary.fwfa-button--compact:hover {
  box-shadow: 0 4px 12px rgba(8, 27, 51, 0.2), 0 1px 3px rgba(8, 27, 51, 0.14);
}

.fwfa-button--secondary {
  /* --fwfa-border-strong (not the hairline --fwfa-border) at rest: on the dark
     app register the secondary outline is the only thing separating the button
     from the panel, and the hairline collapsed into the surface. Matches the
     badge--soft outline so chips and buttons read at the same weight. */
  border: 1px solid var(--fwfa-border-strong);
  background: var(--fwfa-surface);
  color: var(--fwfa-text);
}

/* Secondary interaction — surface lifts on hover, deepening on press (mirrors the
   core system's base button), so it gives the same colour feedback as primary and
   ghost rather than only the shared translateY. The border is already firm at rest. */
.fwfa-button--secondary:hover {
  background: var(--fwfa-surface-hover);
}

.fwfa-button--secondary:active {
  background: var(--fwfa-surface-strong);
}

.fwfa-button--ghost {
  background: transparent;
  color: var(--fwfa-text-soft);
}

.fwfa-button--ghost:hover {
  background: var(--fwfa-surface-hover);
  color: var(--fwfa-text);
}

/* Danger — the destructive variant. (Catalyst models destructive as color="red";
   FWF has no red, so danger is orange.) Outline-first, matching the system's
   restraint: orange border + orange label on transparent, filling to a soft orange
   tint as intent grows on hover/press rather than shouting at rest. Destructive is a
   *variant*, not a transient button state — a failed action surfaces as a toast, and
   selection lives in the segmented control, so the button keeps no is-error /
   is-selected modifier. */
.fwfa-button--danger {
  /* Neutral label, orange frame. A saturated #fe5100 can't be legible as small text
     on the dark shell (~3.3:1), so the danger is carried by the orange border and a
     tint that ignites on hover — not by colouring the label, which keeps the text
     crisp and high-contrast and avoids a lighter-label / deeper-border mismatch.
     Quiet at rest, alarms on intent: the FWF restraint thesis applied to destruction. */
  --btn-ink: var(--fwfa-text); /* @local */
  border: 1px solid var(--fwfa-danger);
  background: transparent;
  color: var(--fwfa-text-soft);
}

.fwfa-button--danger:hover {
  border-color: var(--fwfa-danger);
  background: color-mix(in srgb, var(--fwfa-danger) 16%, transparent);
  color: var(--fwfa-text);
}

.fwfa-button--danger:active {
  background: color-mix(in srgb, var(--fwfa-danger) 24%, transparent);
}

/* Light theme — the label flips to the dark ink; the orange border holds the danger
   read (a 1px border needs only 3:1, which the orange clears on white). */
[data-fwf-app-theme="light"] .fwfa-button--danger {
  --btn-ink: var(--fwfa-text); /* @local */
  color: var(--fwfa-text-soft);
}

/* Loading — label hides, a spinner rides on top. The spinner tracks the variant's
   label via --btn-ink (currentColor goes transparent with the label). */
.fwfa-button.is-loading {
  position: relative;
  color: transparent;
  pointer-events: none;
}

.fwfa-button.is-loading::after {
  content: "";
  position: absolute;
  width: 0.9rem;
  height: 0.9rem;
  border: 2px solid var(--btn-ink);
  border-right-color: transparent;
  border-radius: 50%;
  animation: fwfa-spin 0.7s linear infinite;
}

@keyframes fwfa-spin {
  to {
    transform: rotate(360deg);
  }
}

.fwfa-button:disabled,
.fwfa-icon-button:disabled {
  cursor: not-allowed;
  opacity: 0.38;
  transform: none;
}

.fwfa-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--fwfa-radius-sm);
  background: transparent;
  color: var(--fwfa-text-muted);
  transition:
    background-color var(--fwfa-dur-base) var(--fwfa-ease),
    color var(--fwfa-dur-base) var(--fwfa-ease),
    transform var(--fwfa-dur-fast) var(--fwfa-ease);
}

.fwfa-icon-button:hover {
  background: var(--fwfa-surface-hover);
  color: var(--fwfa-text);
  transform: translateY(-1px);
}

/* Status — dot + label, never a pill: a read-only status must not look like a
   button. Mirrors the core FWF `.status--dot`. The calm default keeps the label
   neutral (--fwfa-text-soft) and the dot static; the alert variant is the one row
   that pulls the eye, bolding and colouring the label. */
.fwfa-status {
  display: inline-flex;
  align-items: center;
  gap: var(--fwfa-space-2);
  color: var(--fwfa-text-soft);
  font-size: var(--fwfa-type-sm);
  font-weight: 500;
}

.fwfa-status-dot {
  width: 0.5rem;
  height: 0.5rem;
  flex: 0 0 auto;
  border-radius: var(--fwfa-radius-pill);
  background: var(--status-colour, var(--fwfa-text-muted));
}

.fwfa-status--live .fwfa-status-dot,
.fwfa-status[data-live="true"] .fwfa-status-dot,
.fwfa-status-dot--live {
  animation: fwfa-status-glow 2.6s ease-in-out infinite;
}

/* Tone sets the dot colour; the label stays calm unless the status is an alert. */
.fwfa-status[data-tone="success"] { --status-colour: var(--fwfa-success); }
.fwfa-status[data-tone="info"]    { --status-colour: var(--fwfa-info); }
.fwfa-status[data-tone="warning"] { --status-colour: var(--fwfa-warning); }
.fwfa-status[data-tone="danger"]  { --status-colour: var(--fwfa-danger); }

/* Alert — the standout. Label bolds and takes the tone colour (readable mixes on
   the dark shell); on the calm default these same hues stay in the dot only. */
.fwfa-status--alert { font-weight: 600; }
.fwfa-status--alert[data-tone="success"] { color: color-mix(in srgb, var(--fwfa-success) 76%, white); }
.fwfa-status--alert[data-tone="info"]    { color: color-mix(in srgb, var(--fwfa-info) 76%, white); }
.fwfa-status--alert[data-tone="warning"] { color: var(--fwfa-accent); }
.fwfa-status--alert[data-tone="danger"]  { color: color-mix(in srgb, var(--fwfa-danger) 72%, white); }

/* The opt-in glow: a soft halo in the dot's own colour, breathing slowly. Use it
   only for changing/live state. Honoured by the global reduced-motion override. */
@keyframes fwfa-status-glow {
  0%, 100% { box-shadow: 0 0 2px 0 color-mix(in srgb, var(--status-colour, var(--fwfa-text-muted)) 35%, transparent); }
  50%      { box-shadow: 0 0 8px 1px color-mix(in srgb, var(--status-colour, var(--fwfa-text-muted)) 70%, transparent); }
}

/* Type label — names *what a block is* (an author, a section, a message kind),
   as opposed to .fwfa-status which signals *state*. Quiet regular-case metadata
   that recedes: never uppercase-tracked (that eyebrow weight is for section
   kickers, not repeated row labels) and never a status tone, so the coloured
   status dot stays the one true-state signal beside it. Pair the soft modifier
   for a secondary descriptor; its leading middot keeps the two from reading as
   one phrase. */
.fwfa-type-label {
  margin: 0; /* safe on inline spans; lets the label stand in for a kicker as a block heading without inheriting default <p> margins */
  color: var(--fwfa-text-muted);
  font-size: var(--fwfa-type-xs);
  font-weight: 500;
}

.fwfa-type-label--soft {
  color: var(--fwfa-text-muted);
  font-weight: 400;
}

.fwfa-type-label--soft::before {
  content: "·";
  margin-right: var(--fwfa-space-2);
}

.fwfa-field {
  display: grid;
  gap: var(--fwfa-space-2);
}

/* A form label is functional, not decorative — it names a control the user must
   parse and act on, it's the clickable `for` target announced by screen readers,
   and it can run long ("Preferred contact method"). So it leaves the small
   mono/uppercase register the kicker/badge/table-header share (see
   --fwfa-weight-label) and sits in the readable UI register instead: full-contrast
   --fwfa-text (essential, must clear 4.5:1 — muted micro caps was the worst case for
   the one element that has to be most legible), body font, control-text size to
   group with the input, sentence case to keep word shapes, no decorative tracking.
   Quiet secondary copy belongs in .fwfa-helper below the field, not in the label. */
.fwfa-label {
  color: var(--fwfa-text);
  font-family: var(--fwfa-font-body);
  font-size: var(--fwfa-type-label);
  line-height: var(--fwfa-leading-snug);
  font-weight: var(--fwfa-weight-label);
  letter-spacing: normal;
  text-transform: none;
}

.fwfa-input,
.fwfa-select,
.fwfa-textarea {
  width: 100%;
  min-height: var(--fwfa-tap);
  /* Form controls don't inherit the page font; without this they fall back to the
     browser's default UA font, which renders larger than body text. Sized to match
     the other interactive controls (button, menu-item, tab) at --fwfa-type-sm, not
     prose body. */
  font: inherit;
  font-size: var(--fwfa-type-sm);
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-sm);
  background: var(--fwfa-field-bg);
  color: var(--fwfa-text);
  padding: 0 var(--fwfa-space-3);
  transition:
    border-color var(--fwfa-dur-base) var(--fwfa-ease),
    box-shadow var(--fwfa-dur-base) var(--fwfa-ease),
    background-color var(--fwfa-dur-base) var(--fwfa-ease);
}

.fwfa-textarea {
  min-height: 7rem;
  padding-block: var(--fwfa-space-3);
  resize: vertical;
}

/* Select — the shared field skin plus a custom chevron and reserved room for it.
   appearance:none drops the platform arrow (which crowds the right edge and varies
   by OS); the chevron is a themed data-URI background so the control matches the
   form vocabulary, while the popup itself stays the native, accessible one. The
   right padding reserves the chevron's lane so long option text never runs under
   it. */
.fwfa-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: calc(var(--fwfa-space-3) + 1.1rem + var(--fwfa-space-2));
  background-image: var(--fwfa-icon-chevron);
  background-repeat: no-repeat;
  background-position: right var(--fwfa-space-3) center;
  background-size: 1.1rem;
}

.fwfa-input::placeholder,
.fwfa-textarea::placeholder {
  color: var(--fwfa-text-muted);
}

.fwfa-input:hover,
.fwfa-select:hover,
.fwfa-textarea:hover {
  border-color: var(--fwfa-border-strong);
}

.fwfa-input:focus,
.fwfa-select:focus,
.fwfa-textarea:focus {
  border-color: var(--fwfa-focus-ring);
  /* Focus brightens the well one white-alpha step (2% → 5%), same neutral family as
     the resting --fwfa-field-bg. The focus colour stays on the border/ring, never
     the fill. */
  background: var(--fwfa-surface-hover);
}

/* Text fields take an inset hairline in the focus colour rather than the offset
   outline — matching the core system, which reserves the offset ring for buttons,
   links, cards, and tabs (focus-visible fires on click for inputs). The colour is
   the shared --fwfa-focus-ring so it clears 3:1 in both themes (navy on light). */
.fwfa-input:focus-visible,
.fwfa-select:focus-visible,
.fwfa-textarea:focus-visible {
  outline: none;
  border-color: var(--fwfa-focus-ring);
  box-shadow: inset 0 0 0 1px var(--fwfa-focus-ring);
}

.fwfa-input:disabled,
.fwfa-select:disabled,
.fwfa-textarea:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

.fwfa-input.is-error,
.fwfa-select.is-error,
.fwfa-textarea.is-error {
  border-color: var(--fwfa-danger);
  box-shadow: inset 0 0 0 1px var(--fwfa-danger);
}

/* Checkbox — a native checkbox restyled to the form vocabulary. Checked shows a
   sky Lucide tick with NO fill: the accent stays on the mark + border, never a
   filled block (same rule as the input focus story and the menu-item tick). The
   real <input> is visually hidden but remains the focusable, accessible control;
   the box and tick are painted on a sibling span so the tick's mask never clips
   the box's border. The tick reuses --fwfa-icon-check masked in the calm accent,
   so it recolours with the theme rather than baking a hex. */
.fwfa-checkbox {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--fwfa-space-2);
  cursor: pointer;
  color: var(--fwfa-text);
  font-family: var(--fwfa-font-body);
  font-size: var(--fwfa-type-sm);
  line-height: var(--fwfa-leading-snug);
}

/* Visually hidden, still focusable and hit-testable (the box sits directly on top
   via the sibling; the label wrapper forwards clicks to the input). */
.fwfa-checkbox__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.fwfa-checkbox__box {
  position: relative;
  flex: none;
  width: 1.15rem;
  height: 1.15rem;
  /* Optically centre the box against the first line of label text. */
  margin-top: 0.05rem;
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-sm);
  background: var(--fwfa-field-bg);
  transition:
    border-color var(--fwfa-dur-base) var(--fwfa-ease),
    box-shadow var(--fwfa-dur-base) var(--fwfa-ease);
}

.fwfa-checkbox:hover .fwfa-checkbox__box {
  border-color: var(--fwfa-border-strong);
}

/* Checked — sky border + sky tick, no fill. */
.fwfa-checkbox__input:checked + .fwfa-checkbox__box {
  border-color: var(--fwfa-accent-calm);
}

.fwfa-checkbox__input:checked + .fwfa-checkbox__box::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--fwfa-accent-calm);
  -webkit-mask: var(--fwfa-icon-check) center / 0.85rem no-repeat;
  mask: var(--fwfa-icon-check) center / 0.85rem no-repeat;
}

/* Keyboard focus takes the offset ring the core system uses for click/key
   controls (not the inset well hairline reserved for text fields). Offset is a
   tighter 2px for this compact box; colour/width follow the shared tokens. */
.fwfa-checkbox__input:focus-visible + .fwfa-checkbox__box {
  outline: var(--fwfa-focus-ring-width) solid var(--fwfa-focus-ring);
  outline-offset: 2px;
}

.fwfa-checkbox__input:disabled + .fwfa-checkbox__box {
  opacity: 0.55;
}

.fwfa-checkbox:has(.fwfa-checkbox__input:disabled) {
  cursor: not-allowed;
}

.fwfa-checkbox__input.is-error + .fwfa-checkbox__box {
  border-color: var(--fwfa-danger);
}

/* Helper / validation text under a field. */
.fwfa-helper {
  color: var(--fwfa-text-muted);
  font-size: var(--fwfa-type-xs);
  line-height: 1.4;
}

.fwfa-helper--error {
  color: color-mix(in srgb, var(--fwfa-danger) 72%, white);
}

.fwfa-empty-state {
  display: grid;
  justify-items: center;
  gap: var(--fwfa-space-3);
  padding: var(--fwfa-space-7) var(--fwfa-space-4);
  text-align: center;
  color: var(--fwfa-text-muted);
}

.fwfa-empty-state h2,
.fwfa-empty-state h3 {
  margin: 0;
  color: var(--fwfa-text);
  font-family: var(--fwfa-font-display);
}

.fwfa-skeleton {
  display: block;
  min-height: 1rem;
  border-radius: var(--fwfa-radius-sm);
  background: linear-gradient(90deg, var(--fwfa-surface) 0%, var(--fwfa-surface-strong) 48%, var(--fwfa-surface) 100%);
  background-size: 220% 100%;
  animation: fwfa-skeleton 1.4s var(--fwfa-ease) infinite;
}

@keyframes fwfa-skeleton {
  to {
    background-position-x: -220%;
  }
}

/* Link — sky text with a soft underline that firms up on hover. Sky (the 30%
   calm role), never yellow: links are structural, not the one spent accent. */
.fwfa-link {
  color: var(--fwfa-accent-calm);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--fwfa-accent-calm) 45%, transparent);
  transition:
    color var(--fwfa-dur-fast) var(--fwfa-ease),
    border-color var(--fwfa-dur-fast) var(--fwfa-ease);
}

.fwfa-link:hover {
  color: color-mix(in srgb, var(--fwfa-accent-calm) 80%, white);
  border-bottom-color: currentColor;
}

/* Keyboard key — a single keycap for documenting a shortcut. Quiet by default: a
   mono glyph on a faint surface with a hairline border and a 1px inner base shadow
   so it reads as a physical key without shouting. Compose in a cluster for chords
   (⌘ then K). This is the *token an app renders its shortcut hints with* — the
   accelerator behaviour itself (command palette, slash menu, key handlers) belongs
   to the consuming app, not the design system. */
.fwfa-kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  height: 1.5rem;
  padding: 0 var(--fwfa-space-2);
  border: 1px solid var(--fwfa-border-strong);
  border-radius: var(--fwfa-radius-sm);
  background: var(--fwfa-surface);
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);
  color: var(--fwfa-text-soft);
  font-family: var(--fwfa-font-mono);
  font-size: var(--fwfa-type-xs);
  font-weight: 600;
  line-height: 1;
}

/* Callout / note — tinted surface block with a bare leading icon (no icon
   background, no coloured side bar). Info = sky, tip = sky, warn = yellow. */
.fwfa-note {
  position: relative;
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-md);
  background: var(--fwfa-surface);
  color: var(--fwfa-text-soft);
  font-size: var(--fwfa-type-sm);
  line-height: var(--fwfa-leading-body);
  padding: var(--fwfa-space-3) var(--fwfa-space-4);
  padding-left: calc(var(--fwfa-space-4) + 1.25rem + var(--fwfa-space-3));
}

.fwfa-note::before {
  content: "";
  position: absolute;
  left: var(--fwfa-space-4);
  /* Box spans exactly the first text line (padding-top + one line-height) and
     centres a fixed-size glyph within it, so the icon's optical centre tracks
     the text — no magic offset to re-tune if the type scale or leading change. */
  top: var(--fwfa-space-3);
  width: 1.25rem;
  height: calc(var(--fwfa-type-sm) * var(--fwfa-leading-body));
  background-color: var(--fwfa-info);
  -webkit-mask: var(--fwfa-icon-info) center / 1.25rem no-repeat;
  mask: var(--fwfa-icon-info) center / 1.25rem no-repeat;
}

.fwfa-note strong {
  color: var(--fwfa-text);
  font-weight: 600;
}

.fwfa-note--tip::before {
  background-color: var(--fwfa-accent-calm);
  -webkit-mask-image: var(--fwfa-icon-tip);
  mask-image: var(--fwfa-icon-tip);
}

.fwfa-note--warn::before {
  background-color: var(--fwfa-accent);
  -webkit-mask-image: var(--fwfa-icon-warn);
  mask-image: var(--fwfa-icon-warn);
}

/* Badge / tag — the core FWF `.badge` recipe, re-tokenised for app surfaces:
   uppercase tracked stamp, outline-first, rounded-rect (never a pill, so it never
   borrows the CTA affordance). Padding (--s-1/--s-3), radius (0.375rem), the
   softened 85% outline, and the type ramp mirror the overarching system so this
   reads as the same component — not a new one. */
.fwfa-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--fwfa-space-2);
  border: 1px solid color-mix(in srgb, var(--fwfa-text) 85%, transparent);
  border-radius: var(--fwfa-radius-md);
  padding: var(--fwfa-space-1) var(--fwfa-space-3);
  background: transparent;
  color: var(--fwfa-text);
  font-size: var(--fwfa-type-micro);
  font-weight: var(--fwfa-weight-label);
  letter-spacing: 0.1em;
  line-height: 1.4;
  text-transform: uppercase;
}

/* Solid — the one high-emphasis filled exception: ink fill, substrate-colour label.
   The label uses --fwfa-primary-ink (the theme's "readable label on an ink block"
   role: navy-950 in dark, white in light) rather than a hardcoded navy — otherwise
   the light theme paints navy text on its navy ink fill and the label disappears. */
.fwfa-badge--solid {
  border-color: var(--fwfa-text);
  background: var(--fwfa-text);
  color: var(--fwfa-primary-ink);
}

/* Soft — quiet register for low-stress meta (counts, versions): neutral outline
   + soft text, still fill-free. Uses --fwfa-border-strong rather than the hairline
   --fwfa-border so the stamp stays legible on large high-contrast panels, while
   reading clearly quieter than the default (text-colour) and accent outlines. */
.fwfa-badge--soft {
  border-color: var(--fwfa-border-strong);
  color: var(--fwfa-text-soft);
}

/* Accent — outline only, the mark colour on border + text. The single standout chip. */
.fwfa-badge--accent {
  border-color: var(--fwfa-accent);
  color: var(--fwfa-accent);
}

/* Avatar — a round identity chip (initials or image), ported from the universal
   showcase to fwfa roles. Default 3rem; --sm / --lg for the dense and feature
   sizes. An image fills and clips to the circle. */
.fwfa-avatar {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  flex: none;
  overflow: hidden;
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-pill);
  background: var(--fwfa-surface-strong);
  color: var(--fwfa-text-soft);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.fwfa-avatar img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
}

.fwfa-avatar--sm { width: 2rem; height: 2rem; font-size: var(--fwfa-type-xs); }
.fwfa-avatar--lg { width: 4rem; height: 4rem; font-size: var(--fwfa-type-h3); }

/* Avatar group — an overlapping stack; each chip takes a ring in the chrome
   colour so it punches cleanly out of the one behind it. */
.fwfa-avatar-group {
  display: inline-flex;
}

.fwfa-avatar-group .fwfa-avatar {
  border-color: transparent;
  box-shadow: 0 0 0 2px var(--fwfa-shell-bg);
}

.fwfa-avatar-group .fwfa-avatar + .fwfa-avatar {
  margin-left: -0.7rem;
}

/* Identity button: the account/menu trigger, composed from Avatar + text. The
   default is transparent: it contributes semantics, touch target, and states
   without adding a pill. Use --surface only when the trigger needs separation in
   dense chrome. */
.fwfa-identity-button {
  min-height: var(--fwfa-control);
  display: inline-flex;
  align-items: center;
  gap: var(--fwfa-space-2);
  max-width: 100%;
  border: 0;
  border-radius: var(--fwfa-radius-pill);
  padding: var(--fwfa-space-1);
  background: transparent;
  color: var(--fwfa-text-soft);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--fwfa-dur-fast) var(--fwfa-ease),
    color var(--fwfa-dur-fast) var(--fwfa-ease),
    box-shadow var(--fwfa-dur-fast) var(--fwfa-ease),
    transform var(--fwfa-dur-fast) var(--fwfa-ease);
}

.fwfa-identity-button--surface {
  padding-right: var(--fwfa-space-3);
  background: var(--fwfa-surface);
}

.fwfa-identity-button-text {
  display: grid;
  gap: 0.125rem;
  min-width: 0;
}

.fwfa-identity-button-name,
.fwfa-identity-button-meta {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fwfa-identity-button-name {
  color: var(--fwfa-text);
  font-size: var(--fwfa-type-sm);
  font-weight: 600;
  line-height: 1.1;
}

.fwfa-identity-button-meta {
  color: var(--fwfa-text-muted);
  font-size: var(--fwfa-type-xs);
  line-height: 1.1;
}

.fwfa-identity-button:hover,
.fwfa-identity-button[aria-expanded="true"] {
  color: var(--fwfa-text);
}

.fwfa-identity-button--surface:hover,
.fwfa-identity-button--surface[aria-expanded="true"] {
  background: var(--fwfa-surface-hover);
}

.fwfa-identity-button:active {
  transform: translateY(1px);
}

.fwfa-identity-button:focus-visible {
  outline: 2px solid var(--fwfa-focus-ring);
  outline-offset: 2px;
}

.fwfa-identity-button:disabled {
  cursor: not-allowed;
  opacity: 0.38;
  transform: none;
}

/* Table — quiet rows under a mono header. Scroll-wrap on small screens. */
.fwfa-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.fwfa-table {
  width: 100%;
  min-width: 30rem;
  border-collapse: collapse;
  font-size: var(--fwfa-type-sm);
}

.fwfa-table th,
.fwfa-table td {
  text-align: left;
  padding: var(--fwfa-space-3);
  border-bottom: 1px solid var(--fwfa-border);
}

.fwfa-table th {
  color: var(--fwfa-text-muted);
  font-family: var(--fwfa-font-mono);
  font-size: var(--fwfa-type-xs);
  font-weight: var(--fwfa-weight-label);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--fwfa-border-strong);
}

.fwfa-table td {
  color: var(--fwfa-text-soft);
}

.fwfa-table td.fwfa-table-mono {
  font-family: var(--fwfa-font-mono);
  font-size: var(--fwfa-type-xs);
  color: var(--fwfa-text-muted);
}

.fwfa-table tbody tr:hover td {
  background: var(--fwfa-shell-bg-muted);
}

/* Tabs — underline indicator in sky (structural mark, not the spent yellow). */
.fwfa-tabs {
  display: flex;
  gap: var(--fwfa-space-2);
  border-bottom: 1px solid var(--fwfa-border);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.fwfa-tabs::-webkit-scrollbar {
  display: none;
}

/* Select fallback for narrow screens when data-tabs="select" is set.
   Hidden at all sizes by default; the @media (max-width:29.99em) block below
   shows it and hides the .fwfa-tab buttons. Styled as a field to match the
   form vocabulary; the chevron arrow is the same themed data-URI as .fwfa-select. */
.fwfa-tabs-select {
  display: none;
  width: 100%;
  min-height: var(--fwfa-tap);
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  font-size: var(--fwfa-type-sm);
  padding: 0 var(--fwfa-space-3);
  padding-right: calc(var(--fwfa-space-3) + 1.1rem + var(--fwfa-space-2));
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-sm);
  background-color: var(--fwfa-field-bg);
  background-image: var(--fwfa-icon-chevron);
  background-repeat: no-repeat;
  background-position: right var(--fwfa-space-3) center;
  background-size: 1.1rem;
  color: var(--fwfa-text);
  transition:
    border-color var(--fwfa-dur-base) var(--fwfa-ease),
    box-shadow var(--fwfa-dur-base) var(--fwfa-ease);
}

.fwfa-tabs-select:focus-visible {
  outline: none;
  border-color: var(--fwfa-focus-ring);
  box-shadow: inset 0 0 0 1px var(--fwfa-focus-ring);
}

.fwfa-tab {
  min-height: var(--fwfa-tap);
  margin-bottom: -1px;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: var(--fwfa-space-3) var(--fwfa-space-4);
  background: none;
  color: var(--fwfa-text-muted);
  font-size: var(--fwfa-type-sm);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition:
    color var(--fwfa-dur-fast) var(--fwfa-ease),
    border-color var(--fwfa-dur-fast) var(--fwfa-ease);
}

.fwfa-tab:hover {
  color: var(--fwfa-text);
}

.fwfa-tab[aria-selected="true"],
.fwfa-tab[aria-pressed="true"] {
  color: var(--fwfa-text);
  border-bottom-color: var(--fwfa-accent-calm);
}

/* Modal — scrim + dialog. The scrim is fixed in real use; the showcase renders
   the dialog inline. */
.fwfa-scrim {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  /* Pad past the safe area so a centred dialog never tucks under a notch / home
     indicator; insets are 0 off-device. The mobile sheet (below) re-points this. */
  padding: max(var(--fwfa-space-4), var(--fwfa-safe-top))
    max(var(--fwfa-space-4), var(--fwfa-safe-right))
    max(var(--fwfa-space-4), var(--fwfa-safe-bottom))
    max(var(--fwfa-space-4), var(--fwfa-safe-left));
  background: var(--fwfa-scrim);
  backdrop-filter: blur(4px);
}

.fwfa-modal {
  width: min(100%, 30rem);
  border: 1px solid var(--fwfa-border-strong);
  border-radius: var(--fwfa-radius-md);
  /* Opaque floating panel — sits over a scrim, so it must be fully opaque (the 2%
     surface twin baked onto the ground), never the translucent field/surface fill. */
  background: var(--fwfa-surface-opaque);
  box-shadow: var(--fwfa-shadow-float);
  padding: var(--fwfa-space-5);
  color: var(--fwfa-text-soft);
}

.fwfa-modal h2,
.fwfa-modal h3 {
  margin: 0 0 var(--fwfa-space-2);
  color: var(--fwfa-text);
  font-family: var(--fwfa-font-display);
  font-size: var(--fwfa-type-h3);
}

.fwfa-modal p {
  margin: 0;
}

.fwfa-modal-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--fwfa-space-2);
  margin-top: var(--fwfa-space-4);
}

/* Toast — a transient, elevated notification; stacks in a corner region. Solid
   surface so it reads above arbitrary content; the intent rides the border and a
   leading glyph (outline-first, no loud fill), matching the note/badge language.
   Ported from the universal showcase. Announce the stack via aria-live in markup. */
.fwfa-toast-stack {
  display: flex;
  flex-direction: column;
  gap: var(--fwfa-space-3);
  width: 100%;
  max-width: 22rem;
}

.fwfa-toast {
  --toast-accent: var(--fwfa-info);
  --toast-glyph: var(--fwfa-icon-info);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--fwfa-space-3);
  align-items: start;
  border: 1px solid var(--toast-accent);
  border-radius: var(--fwfa-radius-md);
  padding: var(--fwfa-space-3) var(--fwfa-space-4);
  /* Opaque — toasts float over content; a translucent fill would bleed. */
  background: var(--fwfa-surface-opaque);
  color: var(--fwfa-text-soft);
  font-size: var(--fwfa-type-sm);
  line-height: var(--fwfa-leading-body);
  box-shadow: var(--fwfa-shadow-soft);
}

/* Leading intent glyph — a mask tinted by the tone accent, so it themes for free.
   Sized to the first text line so it tracks the type, like the note callout. */
.fwfa-toast::before {
  content: "";
  flex: none;
  width: 1.25rem;
  height: calc(var(--fwfa-type-sm) * var(--fwfa-leading-body));
  background-color: var(--toast-accent);
  -webkit-mask: var(--toast-glyph) center / 1.25rem no-repeat;
  mask: var(--toast-glyph) center / 1.25rem no-repeat;
}

.fwfa-toast-body {
  min-width: 0;
}

.fwfa-toast-body strong {
  display: block;
  color: var(--fwfa-text);
  font-weight: 600;
}

/* Trailing slot — an action (text button) and/or a dismiss control, kept in the
   one trailing grid column and centred against the body. */
.fwfa-toast-action {
  display: flex;
  align-items: center;
  gap: var(--fwfa-space-2);
  align-self: center;
}

.fwfa-toast--info    { --toast-accent: var(--fwfa-info);    --toast-glyph: var(--fwfa-icon-info); }
.fwfa-toast--success { --toast-accent: var(--fwfa-success); --toast-glyph: var(--fwfa-icon-success); }
.fwfa-toast--warning { --toast-accent: var(--fwfa-warning); --toast-glyph: var(--fwfa-icon-warn); }
.fwfa-toast--danger  { --toast-accent: var(--fwfa-danger);  --toast-glyph: var(--fwfa-icon-danger); }

/* Gradient accent bar — the one brand signature device. Once per artefact. */
.fwfa-accent-bar {
  height: 2px;
  border-radius: var(--fwfa-radius-pill);
  background: linear-gradient(90deg, var(--fwfa-accent-strong), var(--fwfa-accent));
}

/* Progress — the determinate track for boot, install, and long tasks. The fill
   takes the accent by default: active progress is usually the one thing the
   screen is about, so this is a legitimate spend of gold. The non-negotiable is
   the failure state: a stopped task sets data-tone="danger" so the bar changes
   register instead of freezing mid-accent — an accent bar stuck at 88% reads as
   "still working", not "failed". Pair the bar with a labelled Status line in the
   same tone; the bar alone must never be the only carrier of state. */
.fwfa-progress {
  --progress-colour: var(--fwfa-accent); /* @local */
  height: 0.375rem;
  border-radius: var(--fwfa-radius-pill);
  background: var(--fwfa-surface-strong);
  overflow: hidden;
}

.fwfa-progress[data-tone="danger"]  { --progress-colour: var(--fwfa-danger); }
.fwfa-progress[data-tone="success"] { --progress-colour: var(--fwfa-success); }
.fwfa-progress[data-tone="info"]    { --progress-colour: var(--fwfa-info); }

.fwfa-progress-fill {
  width: var(--fwfa-progress-value, 0%);
  height: 100%;
  border-radius: inherit;
  background: var(--progress-colour);
  transition: width var(--fwfa-dur-slow) var(--fwfa-ease);
}

/* Indeterminate — no measurable fraction: a short segment sweeps the track.
   The global reduced-motion rule freezes the sweep to a static partial fill,
   which still reads as "in progress" without implying a percentage. */
.fwfa-progress--indeterminate .fwfa-progress-fill {
  width: 40%;
  animation: fwfa-progress-sweep 1.6s var(--fwfa-ease) infinite;
}

@keyframes fwfa-progress-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(250%); }
}

/* Stepper — the wizard's wayfinding spine: where you are in a short linear flow.
   The current step is the neutral solid (white marker, ink numeral) — the same
   emphasis token as the primary button, because a marker carrying a numeral is a
   filled block carrying text and so must not take a brand fill. Done steps show
   the sky tick with no fill, matching the checkbox's checked treatment. No gold
   anywhere: in a wizard the spend belongs to the step's primary action, and the
   stepper is wayfinding, not the call to action. Connectors are structure, so
   they stay on border tokens. Set aria-current="step" alongside .is-current. */
.fwfa-stepper {
  display: flex;
  align-items: center;
  gap: var(--fwfa-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.fwfa-step {
  display: flex;
  align-items: center;
  gap: var(--fwfa-space-2);
  color: var(--fwfa-text-muted);
  font-size: var(--fwfa-type-sm);
}

/* Connector — a hairline between steps, firming once the earlier step is done. */
.fwfa-step + .fwfa-step::before {
  content: "";
  flex: none;
  width: 1.5rem;
  height: 1px;
  background: var(--fwfa-border-faint);
}

.fwfa-step.is-done + .fwfa-step::before {
  background: var(--fwfa-border);
}

.fwfa-step-marker {
  display: grid;
  flex: none;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-pill);
  background: var(--fwfa-surface);
  color: var(--fwfa-text-muted);
  font-family: var(--fwfa-font-mono);
  font-size: var(--fwfa-type-xs);
}

.fwfa-step.is-current {
  color: var(--fwfa-text);
  font-weight: var(--fwfa-weight-label);
}

.fwfa-step.is-current .fwfa-step-marker {
  border-color: var(--fwfa-text);
  background: var(--fwfa-text);
  color: var(--fwfa-primary-ink);
  font-weight: var(--fwfa-weight-label);
}

.fwfa-step.is-done {
  color: var(--fwfa-text-soft);
}

/* Done — the numeral gives way to the tick (markup keeps the numeral so plain
   HTML stays uniform; font-size 0 retires it without layout shift). */
.fwfa-step.is-done .fwfa-step-marker {
  position: relative;
  border-color: var(--fwfa-accent-calm);
  font-size: 0;
}

.fwfa-step.is-done .fwfa-step-marker::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--fwfa-accent-calm);
  -webkit-mask: var(--fwfa-icon-check) center / 0.85rem no-repeat;
  mask: var(--fwfa-icon-check) center / 0.85rem no-repeat;
}

/* System screen — the full-surface takeover an app shows when the normal UI
   cannot run: workspace boot, reconnection, renderer crash, fatal error. One
   centred panel on the page ground; the kicker names the system state and is
   the screen's one label mark (accent by default — correct as a mark, never as
   a fill). data-tone="danger" on the panel is the failure register: kicker and
   frame take the danger tone and any progress bar inside inherits it, so a
   failed boot visibly changes state. Recovery actions follow the core rule —
   the primary action is the neutral-solid button, never an accent fill. */
.fwfa-system-screen {
  display: grid;
  place-items: center;
  min-height: 100dvh;
  padding: var(--fwfa-space-5);
  background: var(--fwfa-page-bg);
}

.fwfa-system-panel {
  width: min(100%, 32rem);
  padding: var(--fwfa-space-6);
  border: 1px solid var(--fwfa-border-faint);
  border-radius: var(--fwfa-radius-lg);
  background: var(--fwfa-surface);
  box-shadow: var(--fwfa-shadow-soft);
}

/* Inside the panel the kicker is the state label: accent-marked, and its own
   bottom margin yields to the panel's stack rhythm. */
.fwfa-system-panel .fwfa-kicker {
  margin-bottom: 0;
  color: var(--fwfa-accent);
}

.fwfa-system-panel[data-tone="danger"] {
  border-color: color-mix(in srgb, var(--fwfa-danger) 35%, transparent);
}

.fwfa-system-panel[data-tone="danger"] .fwfa-kicker {
  color: color-mix(in srgb, var(--fwfa-danger) 76%, white);
}

.fwfa-system-panel[data-tone="danger"] .fwfa-progress {
  --progress-colour: var(--fwfa-danger);
}

/* Diagnostic — the mono block for machine output a person may need to read or
   copy: an error message, a log path, a command to run. Pre-formatted, wraps
   long tokens, and scrolls past its cap instead of swallowing the screen. For
   detail the user usually won't need (a stack trace), fold it behind a
   .fwfa-diagnostic-details disclosure. */
.fwfa-diagnostic {
  margin: 0;
  padding: var(--fwfa-space-3);
  border: 1px solid var(--fwfa-border-faint);
  border-radius: var(--fwfa-radius-md);
  background: var(--fwfa-shell-bg-muted);
  color: var(--fwfa-text-soft);
  font-family: var(--fwfa-font-mono);
  font-size: var(--fwfa-type-xs);
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 14rem;
  overflow-y: auto;
}

.fwfa-diagnostic-details > summary {
  cursor: pointer;
  color: var(--fwfa-text-soft);
  font-size: var(--fwfa-type-sm);
}

.fwfa-diagnostic-details[open] > summary {
  margin-bottom: var(--fwfa-space-2);
}

@media (max-width: 47.99em) {
  .fwfa-page-header,
  .fwfa-section-header,
  .fwfa-toolbar {
    align-items: stretch;
  }

  /* minmax(0, 1fr), not bare 1fr — the bare track floors at the widest child's
     max-content and blows the column out (see the matching note in fwfa-shell). */
  .fwfa-split {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Touch-target floor on small screens — interactive controls reach 44px in both
     axes (the denser --fwfa-control height is a pointer-device affordance only).
     The icon button must also widen: its min-width is --fwfa-control off-mobile, so
     without the rule below it would be 44px tall but only ~29px wide. The compact
     button stays dense by design — an opt-in for pointer UIs, not primary touch. */
  .fwfa-button,
  .fwfa-icon-button,
  .fwfa-identity-button,
  .fwfa-menu-item {
    min-height: var(--fwfa-tap);
  }

  .fwfa-icon-button {
    min-width: var(--fwfa-tap);
  }

  /* Modals stay centred at every width by default; on small screens long content
     just caps its height and scrolls inside the dialog so it never overflows the
     viewport. dvh tracks the live viewport as the mobile URL bar shows/hides. */
  .fwfa-modal {
    max-height: 90dvh;
    overflow-y: auto;
  }

  /* The edge-filling variants (opt-in sheet, fullscreen) drop the scrim's safe-area
     padding so they sit flush to the screen edge. */
  .fwfa-scrim:has(> .fwfa-modal[data-modal="sheet"]),
  .fwfa-scrim:has(> .fwfa-modal[data-modal="fullscreen"]) {
    padding: 0;
  }

  /* Bottom sheet — opt-in via the `sheet` prop / data-modal="sheet": docks to the
     bottom edge, full width, top corners only, bottom padding clears the home
     indicator. */
  .fwfa-scrim:has(> .fwfa-modal[data-modal="sheet"]) {
    place-items: end stretch;
  }

  .fwfa-modal[data-modal="sheet"] {
    width: 100%;
    border-radius: var(--fwfa-radius-lg) var(--fwfa-radius-lg) 0 0;
    padding-bottom: calc(var(--fwfa-space-5) + var(--fwfa-safe-bottom));
  }

  /* Full-screen variant — multi-step flows that own the whole viewport. */
  .fwfa-modal[data-modal="fullscreen"] {
    width: 100dvw;
    height: 100dvh;
    max-height: none;
    border: 0;
    border-radius: 0;
    padding-top: calc(var(--fwfa-space-5) + var(--fwfa-safe-top));
  }

  /* iOS auto-zoom prevention — Safari zooms in when a focused input has
     font-size < 16px (1rem). The DS uses --fwfa-type-sm (0.875rem = 14px) for form
     controls, which is correct for desktop density but triggers unwanted page-zoom on
     tap. max() keeps 14px on screens where 1rem < 14px (no such screen in practice)
     and clamps to 16px everywhere else, preventing the zoom without a visible jump. */
  .fwfa-input,
  .fwfa-select,
  .fwfa-textarea,
  .fwfa-tabs-select {
    font-size: max(1rem, var(--fwfa-type-sm));
  }

  /* Toast stack stretches to fill its positioning container on mobile so toasts
     span edge-to-edge rather than capping at 22rem. Positioning (fixed corner) is
     still the consumer's responsibility; pair with padding-bottom: var(--fwfa-safe-bottom)
     on the stack element when anchoring to the bottom of the screen. */
  .fwfa-toast-stack {
    max-width: none;
  }

  /* Tabs — on narrow screens, fade the right edge to signal horizontal scroll
     affordance. The scrollbar is already hidden above; on mobile there's no other
     visual cue. Always shown (pure CSS can't detect overflow without JS); on phones
     tabs almost always overflow so this is the right default. */
  .fwfa-tabs {
    mask-image: linear-gradient(to right, black calc(100% - 3rem), transparent 100%);
    -webkit-mask-image: linear-gradient(to right, black calc(100% - 3rem), transparent 100%);
  }

  /* Table card-reflow — opt-in via data-table="reflow" on the table wrapper.
     Below md each <tr> becomes a stacked label/value card; <thead> is hidden
     and each <td> must carry data-label="Column name" for the ::before label.
     Default horizontal-scroll behaviour is unchanged (non-breaking opt-in). */
  .fwfa-table-wrap[data-table="reflow"] {
    overflow-x: visible;
  }

  .fwfa-table-wrap[data-table="reflow"] .fwfa-table {
    min-width: 0;
  }

  .fwfa-table-wrap[data-table="reflow"] thead {
    display: none;
  }

  .fwfa-table-wrap[data-table="reflow"] tbody tr {
    display: block;
    padding: var(--fwfa-space-3) 0;
    border-bottom: 1px solid var(--fwfa-border-strong);
  }

  /* Hover tint doesn't apply to block-display rows — remove the inherited rule. */
  .fwfa-table-wrap[data-table="reflow"] tbody tr:hover td {
    background: transparent;
  }

  .fwfa-table-wrap[data-table="reflow"] td {
    display: flex;
    align-items: baseline;
    gap: var(--fwfa-space-3);
    border-bottom: 0;
    padding: var(--fwfa-space-1) 0;
  }

  .fwfa-table-wrap[data-table="reflow"] td::before {
    content: attr(data-label);
    flex: none;
    width: 7rem;
    color: var(--fwfa-text-muted);
    font-family: var(--fwfa-font-mono);
    font-size: var(--fwfa-type-xs);
    font-weight: var(--fwfa-weight-label);
    letter-spacing: 0.07em;
    text-transform: uppercase;
  }
}

/* Narrow-phone overrides — below bp-sm (30rem / 480px). Separate from the main
   mobile block (47.99em) because these only apply on very small screens. */
@media (max-width: 29.99em) {
  /* Tabs Phase 2 — collapse nav-style tab strips to a native <select> on very
     small screens. Requires data-tabs="select" on the strip and a
     .fwfa-tabs-select element rendered inside it (sync value/onChange at call site).
     The border-bottom is dropped: the select replaces the entire strip visually. */
  .fwfa-tabs[data-tabs="select"] {
    border-bottom: 0;
  }

  .fwfa-tabs[data-tabs="select"] .fwfa-tab {
    display: none;
  }

  .fwfa-tabs[data-tabs="select"] .fwfa-tabs-select {
    display: block;
  }

  /* Stepper — on very small screens only the current step keeps its label; the
     rest collapse to markers so a three-step flow fits without wrapping. The
     numbered markers still carry the sequence. */
  .fwfa-step:not(.is-current) .fwfa-step-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* Stat — a metric: a mono, tabular value over a quiet uppercase caption. The
   value sits in the mono face so a row of figures aligns digit-for-digit; the
   label uses the uppercase micro register (kicker-adjacent, without the kicker's
   wide tracking). Tone colours the figure only — `muted` dims a resting/zero
   value, `danger` flags one that needs the eye; a positive metric takes no tone.
   Compose several in a `.fwfa-stat-row`. */
.fwfa-stat {
  display: flex;
  flex-direction: column;
  gap: var(--fwfa-space-1);
  min-width: 0;
}

.fwfa-stat-value {
  font-family: var(--fwfa-font-mono);
  font-size: var(--fwfa-type-h1);
  font-weight: 600;
  line-height: var(--fwfa-leading-tight);
  font-variant-numeric: tabular-nums;
  color: var(--fwfa-text);
}

.fwfa-stat-label {
  font-size: var(--fwfa-type-micro);
  font-weight: var(--fwfa-weight-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fwfa-text-soft);
}

/* Tone colours the figure; the caption stays calm. */
.fwfa-stat[data-tone="muted"] .fwfa-stat-value { color: var(--fwfa-text-muted); }
.fwfa-stat[data-tone="danger"] .fwfa-stat-value { color: var(--fwfa-danger); }

/* Row — the horizontal cluster. Default is the dashboard hero register (large
   figures, roomy gap); `--compact` is the inline header cluster (smaller figures,
   tighter gap) for a page header or toolbar. */
.fwfa-stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--fwfa-space-6);
  align-items: flex-start;
}

.fwfa-stat-row--compact {
  gap: var(--fwfa-space-4);
}

.fwfa-stat-row--compact .fwfa-stat-value {
  font-size: var(--fwfa-type-h3);
}

/* --spread — even, centred distribution for a full-width dashboard hero banner:
   each stat takes an equal share of the row and centres its figure over its
   caption. Sits on the default (non-compact) register; stacks to a column on
   phones so several metrics never cramp. */
.fwfa-stat-row--spread .fwfa-stat {
  flex: 1 1 0;
  align-items: center;
  text-align: center;
  padding: var(--fwfa-space-3) var(--fwfa-space-2);
}

@media (max-width: 29.99em) {
  .fwfa-stat-row--spread {
    flex-direction: column;
  }
}

/* ---- Terminal — the frame around an embedded xterm / tool-call console.
   xterm paints its own canvas, themed via buildTerminalTheme() (which maps the
   --fwfa-terminal-* tokens into its ITheme); this class is only the surrounding
   surface. A terminal is a dark machine surface in every app theme, so the bg is
   the constant terminal bg, not the page surface. ---- */
.fwfa-terminal {
  background: var(--fwfa-terminal-bg);
  color: var(--fwfa-terminal-fg);
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-md);
  padding: var(--fwfa-space-3);
  font-family: var(--fwfa-font-mono);
  font-size: 0.8125rem;
  line-height: 1.55;
  overflow: hidden;
}

/* ---- Dropzone — a presentational drop-target surface. Like the slash-menu,
   the drag/drop wiring and file reading belong to the app; this is the
   affordance and its states. Idle is a quiet dashed outline; the accent appears
   only at the drag-active moment, and only as a fine SKY edge plus a
   barely-there wash — never a thick or yellow border, because accents are marks.
   Error swaps the edge to the danger tone. ---- */
.fwfa-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--fwfa-space-2);
  text-align: center;
  min-height: 7rem;
  padding: var(--fwfa-space-4);
  border: 1.5px dashed var(--fwfa-border);
  border-radius: var(--fwfa-radius-md);
  background: var(--fwfa-surface);
  color: var(--fwfa-text-muted);
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.fwfa-dropzone.is-dragging {
  border-color: var(--fwfa-accent-calm);
  background: color-mix(in srgb, var(--fwfa-accent-calm) 5%, var(--fwfa-surface));
  color: var(--fwfa-text-soft);
}
.fwfa-dropzone.is-error {
  border-color: var(--fwfa-danger);
}
.fwfa-dropzone-icon {
  display: inline-flex;
  color: var(--fwfa-text-muted);
}
.fwfa-dropzone.is-dragging .fwfa-dropzone-icon {
  color: var(--fwfa-accent-calm);
}
@media (prefers-reduced-motion: reduce) {
  .fwfa-dropzone { transition: none; }
}

/* ---- Attachment chip — a passive rounded-rect label for one attached file: an
   image thumbnail (or a generic file glyph), the name, an optional size, and a
   remove control. Passive = rectangular radius, no status tone: it names a
   thing, it is not a state. `.fwfa-attachment-list` flows the chips in a wrap. ---- */
.fwfa-attachment-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--fwfa-space-2);
}
.fwfa-attachment-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--fwfa-space-2);
  padding: 0.3rem 0.35rem 0.3rem 0.3rem;
  border: 1px solid var(--fwfa-border);
  border-radius: var(--fwfa-radius-sm);
  background: var(--fwfa-surface-strong);
  font-size: 0.75rem;
  color: var(--fwfa-text-soft);
  max-width: 100%;
}
.fwfa-attachment-chip-thumb {
  width: 1.7rem;
  height: 1.7rem;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--fwfa-radius-xs);
  overflow: hidden;
  background: var(--fwfa-surface-strong);
  color: var(--fwfa-text-muted);
}
.fwfa-attachment-chip-thumb img,
.fwfa-attachment-chip-thumb svg {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.fwfa-attachment-chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fwfa-attachment-chip-size {
  color: var(--fwfa-text-muted);
  flex: none;
}
.fwfa-attachment-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  padding: 2px;
  border: 0;
  border-radius: var(--fwfa-radius-xs);
  background: none;
  color: var(--fwfa-text-muted);
  cursor: pointer;
}
.fwfa-attachment-chip-remove:hover {
  color: var(--fwfa-text);
  background: var(--fwfa-surface-hover);
}

/* ---- Product mark — the FreshVibe wordmark / monogram, theme-aware via the
   --fwfa-product-* tokens (parallel to .fwfa-fwf-logo). Wordmark by default;
   --icon is the square monogram (favicon / collapsed sidebar); --lockup is a row
   for icon + wordmark. Apps use this instead of hardcoding a freshvibe-*.svg. ---- */
.fwfa-product-mark {
  display: block;
  width: var(--pm-width, 7.5rem); /* @local */
  max-width: 100%;
  aspect-ratio: 554 / 108;
  flex: none;
  background: var(--fwfa-product-wordmark) left center / contain no-repeat;
}
.fwfa-product-mark--sm { --pm-width: 5.5rem; /* @local */ }
.fwfa-product-mark--lg { --pm-width: 10rem; /* @local */ }
.fwfa-product-mark--icon {
  width: var(--pm-icon-size, 1.75rem); /* @local */
  aspect-ratio: 1;
  border-radius: var(--fwfa-radius-sm);
  background-image: var(--fwfa-product-icon);
}
.fwfa-product-lockup {
  display: inline-flex;
  align-items: center;
  gap: var(--fwfa-space-2);
}
}
