/* ==========================================================================
   Design system — layered on top of Pico CSS (pico.min.css).
   Structure:
     1. Design tokens (:root, light + dark)
     2. Pico variable bridge — retheme Pico's own components via its
        existing custom properties, so buttons/forms/tables/cards/nav/
        dropdowns/modals restyle with zero HTML changes.
     3. Typography
     4. Layout (nav, containers)
     5. Buttons
     6. Forms
     7. Tables
     9. Alerts / flash
    10. Cards (article)
    11. Auth shell
    12. Empty states
    13. Focus / disabled / motion
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens — dark-first: near-black ink with a vermilion signature
   accent. Light is the secondary variant, same roles, opted into via
   prefers-color-scheme:light or an explicit data-theme="light".
   -------------------------------------------------------------------------- */

:root {
  /* Color — ink black, signature vermilion accent */
  --color-background: #0b0d10;
  --color-surface: #15181d;
  --color-surface-elevated: #1c2027;
  --color-border: #262b33;
  --color-border-strong: #363c46;
  --color-text: #e8eaed;
  --color-text-muted: #8b92a0;
  --color-text-faint: #5b6270;

  --color-primary: #ff5a1f;
  --color-primary-hover: #ff7440;
  --color-primary-tint: #2c1c14;
  /* Badges pair a tinted background with the role color as text at a small
     size — --color-primary/--color-focus themselves read fine on that tint
     here, but not once the light theme lightens the tint (see the light
     overrides below), so badges get their own darker-in-light variants
     instead of reusing the general-purpose tokens. */
  --color-badge-text-primary: var(--color-primary);
  --color-badge-text-focus: var(--color-focus);
  --color-badge-text-warning: var(--color-warning);

  --color-success: #3dd68c;
  --color-success-tint: #123023;
  --color-warning: #f5a623;
  --color-warning-tint: #302411;
  --color-danger: #ff4d4f;
  --color-danger-tint: #331617;

  /* Focus ring — deliberately NOT the accent: vermilion sits one hue away
     from danger-red, so an autofocused field outlined in accent color reads
     as "this field has an error" before the user's typed anything. A cool
     blue is unambiguous "you are here" chrome, distinct from both. */
  --color-focus: #4da3ff;

  /* Spacing — 4px rhythm */
  --spacing-xs: 0.25rem;  /* 4px */
  --spacing-sm: 0.5rem;   /* 8px */
  --spacing-md: 1rem;     /* 16px */
  --spacing-lg: 1.5rem;   /* 24px */
  --spacing-xl: 2rem;     /* 32px */
  --spacing-2xl: 3rem;    /* 48px */

  /* Radius — small, quiet */
  --radius-sm: 4px;
  --radius-md: 6px;

  /* Shadow — used sparingly (auth card, dropdowns only) */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 1px 1px rgba(0, 0, 0, 0.3);

  /* Motion */
  --transition-fast: 120ms ease;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --color-background: #fafaf9;
    --color-surface: #ffffff;
    --color-surface-elevated: #ffffff;
    --color-border: #e4e4e7;
    --color-border-strong: #d4d4d8;
    --color-text: #16181c;
    --color-text-muted: #6b7280;
    --color-text-faint: #9aa0ab;

    --color-primary: #e6491a;
    --color-primary-hover: #c93c12;
    --color-primary-tint: #fdece4;

    --color-success: #1f8a56;
    --color-success-tint: #e8f6ef;
    --color-warning: #b06e0c;
    --color-warning-tint: #fbf1de;
    --color-danger: #d63333;
    --color-danger-tint: #fbe9e9;

    /* Deeper than the dark-theme value — needs more contrast against a
       near-white background than against near-black ink. */
    --color-focus: #1f6fd6;

    /* --color-primary/--color-focus on their own badge tints fall to
       ~3.4:1 / ~2.5:1 here — below the 4.5:1 AA minimum for the badges'
       small (0.68rem) text. Darkened just for that use. */
    --color-badge-text-primary: #b03510;
    --color-badge-text-focus: #14509c;
    --color-badge-text-warning: #7a4c08;

    --shadow-sm: 0 1px 2px rgba(22, 24, 28, 0.05), 0 1px 1px rgba(22, 24, 28, 0.04);
  }
}
:root[data-theme="light"] {
  --color-background: #fafaf9;
  --color-surface: #ffffff;
  --color-surface-elevated: #ffffff;
  --color-border: #e4e4e7;
  --color-border-strong: #d4d4d8;
  --color-text: #16181c;
  --color-text-muted: #6b7280;
  --color-text-faint: #9aa0ab;

  --color-primary: #e6491a;
  --color-primary-hover: #c93c12;
  --color-primary-tint: #fdece4;

  --color-success: #1f8a56;
  --color-success-tint: #e8f6ef;
  --color-warning: #b06e0c;
  --color-warning-tint: #fbf1de;
  --color-danger: #d63333;
  --color-danger-tint: #fbe9e9;

  --color-focus: #1f6fd6;

  --color-badge-text-primary: #b03510;
  --color-badge-text-focus: #14509c;
  --color-badge-text-warning: #7a4c08;

  --shadow-sm: 0 1px 2px rgba(22, 24, 28, 0.05), 0 1px 1px rgba(22, 24, 28, 0.04);
}

/* --------------------------------------------------------------------------
   2. Pico variable bridge — everything below retheme automatically:
      buttons, inputs, selects, tables, cards, dropdowns, nav, modals.
   Pico itself redefines these same properties on `:root:not([data-theme=…])`
   (specificity 0,2,0) for its light/dark variants, which beats a plain
   `:root` block (0,1,0) regardless of source order — every value below is
   `!important` so our tokens always win over Pico's built-in indigo theme.
   -------------------------------------------------------------------------- */

:root {
  --pico-background-color: var(--color-background) !important;
  --pico-color: var(--color-text) !important;
  --pico-muted-color: var(--color-text-muted) !important;
  --pico-muted-border-color: var(--color-border) !important;
  --pico-border-radius: var(--radius-md) !important;

  --pico-h1-color: var(--color-text) !important;
  --pico-h2-color: var(--color-text) !important;
  --pico-h3-color: var(--color-text) !important;
  --pico-h4-color: var(--color-text) !important;
  --pico-h5-color: var(--color-text) !important;
  --pico-h6-color: var(--color-text) !important;

  --pico-primary: var(--color-primary) !important;
  --pico-primary-background: var(--color-primary) !important;
  --pico-primary-border: var(--color-primary) !important;
  --pico-primary-underline: transparent !important;
  --pico-primary-hover: var(--color-primary-hover) !important;
  --pico-primary-hover-background: var(--color-primary-hover) !important;
  --pico-primary-hover-border: var(--color-primary-hover) !important;
  --pico-primary-hover-underline: transparent !important;
  --pico-primary-focus: color-mix(in srgb, var(--color-primary) 25%, transparent) !important;
  --pico-primary-inverse: #ffffff !important;
  --pico-button-box-shadow: none !important;
  --pico-button-hover-box-shadow: none !important;

  --pico-secondary: var(--color-text-muted) !important;
  --pico-secondary-background: transparent !important;
  --pico-secondary-border: var(--color-border-strong) !important;
  --pico-secondary-underline: transparent !important;
  --pico-secondary-hover: var(--color-text) !important;
  --pico-secondary-hover-background: var(--color-background) !important;
  --pico-secondary-hover-border: var(--color-border-strong) !important;
  --pico-secondary-hover-underline: transparent !important;
  --pico-secondary-focus: color-mix(in srgb, var(--color-text-muted) 20%, transparent) !important;
  --pico-secondary-inverse: var(--color-text) !important;

  --pico-form-element-background-color: var(--color-surface) !important;
  --pico-form-element-border-color: var(--color-border-strong) !important;
  --pico-form-element-color: var(--color-text) !important;
  --pico-form-element-placeholder-color: var(--color-text-faint) !important;
  --pico-form-element-active-background-color: var(--color-surface) !important;
  --pico-form-element-active-border-color: var(--color-focus) !important;
  --pico-form-element-focus-color: var(--color-focus) !important;
  --pico-form-element-disabled-opacity: 0.5 !important;
  --pico-form-element-selected-background-color: var(--color-primary-tint) !important;

  --pico-card-background-color: var(--color-surface) !important;
  --pico-card-border-color: var(--color-border) !important;
  --pico-card-box-shadow: none !important;
  --pico-card-sectioning-background-color: var(--color-surface) !important;

  --pico-table-border-color: var(--color-border) !important;
  --pico-table-row-stripped-background-color: transparent !important;

  --pico-dropdown-background-color: var(--color-surface-elevated) !important;
  --pico-dropdown-border-color: var(--color-border) !important;
  --pico-dropdown-color: var(--color-text) !important;
  --pico-dropdown-hover-background-color: var(--color-background) !important;
  --pico-dropdown-box-shadow: var(--shadow-sm) !important;

  --pico-ins-color: var(--color-success) !important;
  --pico-del-color: var(--color-danger) !important;

  --pico-modal-overlay-background-color: color-mix(in srgb, var(--color-text) 45%, transparent) !important;
  --pico-box-shadow: var(--shadow-sm) !important;
}

/* --------------------------------------------------------------------------
   3. Typography — Space Grotesk for display/headings, Inter for body/UI,
   JetBrains Mono for data/codes/badges. Loaded via Google Fonts in the
   layout heads; fallback stacks keep pages legible if that request fails.
   -------------------------------------------------------------------------- */

:root {
  --font-display: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

body {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  font-weight: 650;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
h1 { font-size: 1.6rem; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1.05rem; }

hgroup {
  margin-bottom: var(--spacing-lg);
}
hgroup h1 { margin-bottom: var(--spacing-xs); }
hgroup p {
  color: var(--color-text-muted);
  font-size: 0.9rem;
  margin: 0;
}

/* Eyebrow — the page's signature label: a short vermilion rule + tracked-out
   mono caps above a heading, echoing the badge/invite-code "stamp" motif
   used elsewhere on the page. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  margin-bottom: var(--spacing-sm);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-primary);
}
.eyebrow::before {
  content: "";
  display: block;
  width: 1.25rem;
  height: 2px;
  background: var(--color-primary);
  flex-shrink: 0;
}

/* --------------------------------------------------------------------------
   4. Layout — sidebar app shell + unauthenticated container
   -------------------------------------------------------------------------- */

body { background: var(--color-background); }

main.container {
  padding-top: var(--spacing-xl);
  padding-bottom: var(--spacing-2xl);
}

/* App shell: topbar (brand + account menu) + scrolling content area. No
   sidebar — with a single destination ("My handovers", which the topbar
   brand already links to), a persistent 240px sidebar cost real width for
   zero navigational value; a plain topbar carries the same two things
   (brand, account menu) without permanently consuming that space. */
.app-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.topbar-brand {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-weight: 650;
  font-size: 0.95rem;
  letter-spacing: -0.01em;
  color: var(--color-text);
  text-decoration: none;
}

.brand-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: #ffffff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.85rem;
}

.topbar {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  padding: var(--spacing-sm) var(--spacing-lg);
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}

.content {
  flex: 1;
  padding: var(--spacing-xl) var(--spacing-lg) var(--spacing-2xl);
}

/* Account menu — plain <details>/<summary>, no Pico dropdown chrome, so it
   can hold an avatar + email instead of a single label. */
.account-menu { position: relative; }
.account-menu summary {
  list-style: none;
  cursor: pointer;
}
.account-menu summary::-webkit-details-marker { display: none; }

/* Avatar-only trigger — the email used to sit next to it here, but that
   made the button as long as the address was, forcing an ellipsis/tooltip
   on anything longer than a short name. The email now lives as the first
   line inside the dropdown instead (below), where there's room for the
   whole thing. */
.account-trigger {
  display: flex;
  align-items: center;
  padding: var(--spacing-xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  transition: background var(--transition-fast);
}
.account-trigger:hover { background: var(--color-background); }

.account-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--color-primary-tint);
  color: var(--color-primary);
  font-family: var(--font-display);
  font-size: 0.7rem;
  font-weight: 650;
}

.account-dropdown-email {
  padding: var(--spacing-sm);
  padding-top: var(--spacing-xs);
  font-size: 0.82rem;
  color: var(--color-text-muted);
  overflow-wrap: break-word;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--spacing-xs);
}

.account-dropdown {
  position: absolute;
  right: 0;
  top: calc(100% + var(--spacing-xs));
  z-index: 20;
  min-width: 190px;
  margin: 0;
  padding: var(--spacing-xs);
  list-style: none;
  background: var(--color-surface-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}
/* Pico's base `ul li { list-style: square }` targets the <li> directly,
   which wins over the inherited `list-style: none` above. */
.account-dropdown li {
  list-style: none;
}
.account-dropdown li + li { margin-top: 2px; }
.account-dropdown a,
.account-dropdown .link-button {
  display: block;
  width: 100%;
  padding: var(--spacing-sm);
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--color-text);
  font-size: 0.88rem;
  font-family: inherit;
  /* Overrides the global button font-weight:550 (section 5) so the
     logout <button> matches its <a> sibling instead of reading bolder. */
  font-weight: 400;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.account-dropdown a:hover,
.account-dropdown .link-button:hover {
  background: var(--color-background);
}
.account-dropdown form { max-width: none; margin: 0; }

.theme-switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-sm);
  padding: var(--spacing-sm);
  font-size: 0.88rem;
  color: var(--color-text);
}

/* Minimal track+knob toggle switch, built on a plain checkbox for free
   keyboard/screen-reader support (space to toggle, announced as a switch
   via the checkbox role) — no ARIA reimplementation needed. */
/* label.switch (not just .switch): Pico's own
   `label:has([type="checkbox"], [type="radio"]) { width: fit-content }`
   is one type-selector more specific than a bare class and would otherwise
   win — and since the checkbox inside is position:absolute (out of flow),
   fit-content resolves to a 0-width label with nothing visibly clickable. */
label.switch {
  position: relative;
  display: inline-block;
  width: 2.25rem;
  height: 1.25rem;
  flex-shrink: 0;
}
.switch input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.switch-track {
  position: absolute;
  inset: 0;
  /* The input already covers this exact area and handles all clicks/taps —
     the track is purely decorative. Without this, it sits on top in paint
     order (later in the DOM) and silently swallows every click. */
  pointer-events: none;
  background: var(--color-border-strong);
  border-radius: 999px;
  transition: background var(--transition-fast);
}
.switch-track::before {
  content: "";
  position: absolute;
  width: 1rem;
  height: 1rem;
  left: 0.125rem;
  top: 0.125rem;
  background: #ffffff;
  border-radius: 50%;
  transition: transform var(--transition-fast);
}
.switch input:checked ~ .switch-track {
  background: var(--color-primary);
}
.switch input:checked ~ .switch-track::before {
  transform: translateX(1rem);
}
.switch input:focus-visible ~ .switch-track {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */

/* Pico buttons default to width:100% (block), which is right for the
   single-field auth-card sign-in pattern but wrong everywhere else — on a
   wide settings/members card it stretched a "Save" button across the full
   card width, far past the input it belongs to. Buttons size to their
   content by default; the auth card explicitly opts back into full width
   below (section 11), matching the conventional sign-in-form shape. */
/* Selector list matches Pico's own `button[type="submit"]` (and friends) by
   element+attribute, not just `[type="submit"]` — otherwise Pico's
   higher-specificity width:100% wins regardless of source order, which is
   what caused Create/Send-invite buttons in flex toolbars to balloon to
   nearly full width while their sibling input got squeezed to ~40px. */
button,
button[type="submit"],
button[type="button"],
[type="submit"],
[type="button"],
[role="button"] {
  width: auto;
  font-weight: 550;
  letter-spacing: -0.005em;
}

/* Compact tier for table-row / inline actions (Update, Remove, Delete). */
button.small,
.inline-form button {
  padding: var(--spacing-xs) var(--spacing-sm);
  font-size: 0.8rem;
  margin: 0;
  --pico-border-radius: var(--radius-sm);
}

/* Destructive actions (delete workspace / remove member / delete project):
   quiet red outline, not a shouting solid fill — reserved for genuinely
   destructive actions so it stays meaningful. A bare `.outline` (i.e. not
   also `.secondary`) reads Pico's --pico-primary* variables for its color,
   not --pico-secondary* — overriding both here so it works whichever way
   it's combined. Scoped to the button itself, so this can't leak into
   unrelated primary-colored elements on the page. */
.button-danger {
  --pico-primary: var(--color-danger);
  --pico-primary-hover: color-mix(in srgb, var(--color-danger) 85%, black);
  --pico-primary-focus: color-mix(in srgb, var(--color-danger) 25%, transparent);
  --pico-primary-background: var(--color-danger);
  --pico-primary-border: var(--color-danger);
  --pico-primary-hover-background: color-mix(in srgb, var(--color-danger) 85%, black);
  --pico-primary-hover-border: color-mix(in srgb, var(--color-danger) 85%, black);
}

/* Secondary/low-emphasis actions (e.g. "Resend" sitting next to a
   destructive "Revoke"): neutral ink instead of the vermilion primary, so it
   doesn't compete for attention with either the primary accent or a
   .button-danger sibling in the same row. */
.button-secondary {
  --pico-primary: var(--color-text-muted);
  --pico-primary-hover: var(--color-text);
  --pico-primary-focus: color-mix(in srgb, var(--color-text-muted) 25%, transparent);
  --pico-primary-background: var(--color-text-muted);
  --pico-primary-border: var(--color-border-strong);
  --pico-primary-hover-background: var(--color-text);
  --pico-primary-hover-border: var(--color-text);
}

/* Toolbar-style create form (dashboard "new project"): input + button on
   one line instead of Pico's default full-width stacked form. */
.toolbar-form {
  display: flex;
  gap: var(--spacing-sm);
  max-width: 420px;
  margin-bottom: var(--spacing-lg);
}
.toolbar-form input { flex: 1; margin: 0; }
.toolbar-form button { margin: 0; white-space: nowrap; }

/* --------------------------------------------------------------------------
   6. Forms — constrain width so fields and buttons stay proportionate to
   their content instead of stretching across a wide card/container.
   -------------------------------------------------------------------------- */

form {
  max-width: 420px;
}

/* Live character count under a data-char-counter textarea (see app.js) —
   sits right under the field, same treatment as the muted "(optional)"
   label hint so it reads as metadata, not as its own form field. */
.char-counter {
  display: block;
  margin: calc(var(--spacing-xs) * -1) 0 var(--spacing-md);
  font-size: 0.78rem;
  color: var(--color-text-faint);
  text-align: right;
}

/* One-off: centers a form's own button(s) instead of the default
   left-aligned block — used for the final "Complete handover" action,
   which reads as more deliberate/celebratory centered than left-aligned
   alongside the rest of the wizard's left-aligned Continue buttons. */
.form-centered {
  display: flex;
  justify-content: center;
}

label {
  font-size: 0.85rem;
  font-weight: 550;
  color: var(--color-text);
}

/* Soft, non-blocking inline warning (see data-warn-same-as in app.js) —
   deliberately styled as a caution, not an error (no red), since the
   condition it flags is unusual but not invalid. */
.field-warning {
  display: block;
  margin-top: var(--spacing-xs);
  font-size: 0.78rem;
  font-weight: 550;
  color: var(--color-badge-text-warning, var(--color-warning));
}

/* Hard (blocking) inline error — same shape as .field-warning but in the
   danger color, since it's the one place client-side validation used to
   fall back to a native window.alert() instead of the app's own error
   styling (the empty-signature check on the sign pad). */
.field-error {
  display: block;
  margin-top: var(--spacing-xs);
  font-size: 0.78rem;
  font-weight: 550;
  color: var(--color-danger);
}

input, select, textarea {
  font-size: 0.95rem;
}

/* --------------------------------------------------------------------------
   7. Tables — compact, quiet separators, clear row states
   -------------------------------------------------------------------------- */

/* Lets a wide table (long emails, timestamps + actor + detail columns)
   scroll horizontally within its own card on narrow viewports instead of
   overflowing the card's edge into the page background. */
.table-scroll {
  overflow-x: auto;
}

table {
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.9rem;
}

thead th {
  position: sticky;
  top: 0;
  background: var(--color-background);
  color: var(--color-text-muted);
  font-weight: 600;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: var(--spacing-sm) var(--spacing-sm);
  border-bottom: 1px solid var(--color-border-strong);
}

tbody td {
  padding: var(--spacing-sm) var(--spacing-sm);
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text);
}

tbody tr {
  transition: background var(--transition-fast);
}
/* Rows sit directly on the page background (no card wrapper), so hovering
   to that same --color-background was a no-op — this had no visible effect
   at all. A small color-mix tint scales sensibly in both themes rather than
   depending on --color-surface happening to read as distinct from
   --color-background (they're nearly identical in light mode). */
tbody tr:hover {
  background: color-mix(in srgb, var(--color-text) 6%, var(--color-background));
}
tbody tr:last-child td {
  border-bottom: none;
}

/* Action columns (Delete/Update/Remove) hold one short control — let them
   shrink to fit instead of taking an even share of the table's width,
   which left a wide band of empty space next to a single button. */
th:last-child,
td:last-child {
  width: 1%;
  white-space: nowrap;
}

/* Reserved for future row selection (e.g. bulk actions) — unused today,
   zero-cost to have ready. */
tbody tr[aria-selected="true"],
tbody tr.is-selected {
  background: var(--color-primary-tint);
}

.truncate {
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Whole-row click target for the dashboard table (see data-row-href in
   app.js) — matches the mobile card list, which was already fully
   tappable, so desktop rows aren't a smaller/inconsistent target just
   because they render as a table. */
tr.is-linked-row {
  cursor: pointer;
}

/* --------------------------------------------------------------------------
   9. Alerts / flash — quiet left-accent banner, not a solid tinted block
   -------------------------------------------------------------------------- */

.flash {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-sm);
  padding: var(--spacing-sm) var(--spacing-md);
  margin-bottom: var(--spacing-lg);
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-text-muted);
  background: var(--color-surface);
  font-size: 0.88rem;
  color: var(--color-text);
}
.flash-dismiss {
  all: unset;
  cursor: pointer;
  color: var(--color-text-muted);
  font-size: 1.1rem;
  line-height: 1;
  flex-shrink: 0;
  padding: 0 var(--spacing-xs);
}
.flash-dismiss:hover { color: var(--color-text); }
.flash-dismiss:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}
/* Left-border accent only — no solid tint fill. A full green/red block next
   to the single-accent (vermilion) system reads as a second, competing
   brand color; the border-only treatment keeps flashes legible without
   introducing new saturated color into the page. */
.flash-success {
  border-left-color: var(--color-success);
}
.flash-error {
  border-left-color: var(--color-danger);
}

/* Same left-accent shape as .flash, but for a static, non-dismissible inline
   note (e.g. "both parties have signed" on the confirm page) rather than a
   transient server-set message tied to a redirect — kept as its own class
   instead of reusing .flash so the two don't get confused as the same kind
   of thing (one persists as long as its condition is true; the other is a
   one-shot notice the user dismisses or navigates away from). */
.banner {
  padding: var(--spacing-sm) var(--spacing-md);
  margin-bottom: var(--spacing-lg);
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-text-muted);
  background: var(--color-surface);
  font-size: 0.88rem;
  color: var(--color-text);
}
.banner-success {
  border-left-color: var(--color-success);
}
/* --------------------------------------------------------------------------
   10. Cards (article) + danger zone
   -------------------------------------------------------------------------- */

/* Generous gap between cards (not just a border) is what actually reads as
   "these are separate sections" — a handful of bordered boxes sitting
   right on top of each other reads as one busy block regardless of the
   borders between them. */
article {
  margin-bottom: var(--spacing-xl);
  padding: var(--spacing-lg);
}
article:last-child { margin-bottom: 0; }
/* Plain heading + a single rule, not a separately-colored band — a
   two-tone header on every stacked card was most of what made a page with
   several cards (e.g. workspace settings) feel noisy. */
article > header {
  margin: 0 0 var(--spacing-lg);
  padding-bottom: var(--spacing-md);
  border-bottom: 1px solid var(--color-border);
  font-size: 0.9rem;
  color: var(--color-text);
}
article > header p {
  margin: var(--spacing-xs) 0 0;
  font-size: 0.82rem;
  font-weight: 400;
  color: var(--color-text-muted);
}
article form:last-child { margin-bottom: 0; }

/* Simple bordered rows for a read-only list with no per-row controls (e.g.
   who a project is shared with) — lighter than pulling in a full <table>
   for a single column of plain text. */
.plain-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Pico's base `ul li { list-style: square }` targets the <li> directly,
   which wins over the inherited `list-style: none` above. */
.plain-list li {
  list-style: none;
  padding: var(--spacing-sm) 0;
  border-bottom: 1px solid var(--color-border);
}
.plain-list li:last-child {
  border-bottom: none;
}

/* --------------------------------------------------------------------------
   11. Auth shell — centered card for signup/login/reset/invite-accept
   -------------------------------------------------------------------------- */

.auth-body {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-background);
}
.auth-shell {
  width: 100%;
  max-width: 440px;
  padding: var(--spacing-lg);
}
/* The certificate page (handovers/certificate.ejs) reuses this shell for
   its no-sidebar, public layout, but a signed document with photos and
   two signatures reads as cramped at the same 440px cap that suits a
   plain login/check-email card. Widening only applies once there's actual
   room for it — on a phone viewport the shell already fills 100% width
   regardless of this cap, so mobile is unaffected either way. */
.auth-shell-wide {
  max-width: 640px;
}
.auth-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-sm);
  font-family: var(--font-display);
  font-weight: 650;
  font-size: 1rem;
  letter-spacing: -0.01em;
  margin-bottom: var(--spacing-lg);
  color: var(--color-text);
}
.auth-card {
  margin: 0;
  padding: var(--spacing-xl);
  box-shadow: var(--shadow-sm);
}
.auth-card p:last-child { margin-bottom: 0; }
.auth-card > .flash:first-child { margin-bottom: var(--spacing-md); }
/* The one deliberate exception to "buttons size to content" (section 5):
   a full-width primary action is the expected shape for a sign-in form. */
.auth-card form { max-width: none; }
.auth-card button[type="submit"] { width: 100%; }

.pager {
  display: flex;
  justify-content: space-between;
  margin-top: var(--spacing-sm);
  font-size: 0.85rem;
}

/* --------------------------------------------------------------------------
   12. Empty states — quiet, centered, no decoration
   -------------------------------------------------------------------------- */

.empty-state {
  padding: var(--spacing-xl) var(--spacing-md);
  text-align: center;
  color: var(--color-text-muted);
  font-size: 0.9rem;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  margin-bottom: var(--spacing-lg);
}

/* Keeps a real <label> for assistive tech on compact "quick add" inputs
   (e.g. the dashboard toolbar) where a visible label would break the
   single-line pattern and the placeholder already carries the field's
   purpose for sighted users. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   13. Focus / disabled / motion
   -------------------------------------------------------------------------- */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
[role="button"]:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* Off-screen until focused (first Tab stop on every page) — lets keyboard
   users jump past the topbar straight to the page's actual content. */
.skip-link {
  position: absolute;
  top: -3rem;
  left: var(--spacing-sm);
  z-index: 200;
  padding: var(--spacing-sm) var(--spacing-md);
  background: var(--color-primary);
  color: #ffffff;
  border-radius: var(--radius-sm);
  transition: top var(--transition-fast);
}
.skip-link:focus {
  top: var(--spacing-sm);
}

[disabled] { cursor: not-allowed; }
/* Set on a submit button the instant its form actually submits (see
   app.js) — cursor:wait plus a dimmed look reads as "working", not
   "broken", distinct from a plain disabled/inactive control. */
.is-busy {
  cursor: wait !important;
  opacity: 0.7;
}

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

/* --------------------------------------------------------------------------
   13. Handovers — status badges, wizard steps, photos, signatures,
   certificate card
   -------------------------------------------------------------------------- */

.status-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.55rem;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border: 1px solid var(--color-border-strong);
  color: var(--color-text-muted);
  background: var(--color-background);
}
.status-badge.status-completed {
  color: var(--color-success);
  border-color: color-mix(in srgb, var(--color-success) 35%, transparent);
  background: var(--color-success-tint);
}
.status-badge.status-awaiting_signatures {
  color: var(--color-badge-text-warning);
  border-color: color-mix(in srgb, var(--color-warning) 35%, transparent);
  background: var(--color-warning-tint);
}
.status-badge.status-draft {
  border-style: dashed;
  font-style: italic;
}

/* Dashboard From/To columns: the signed-in user's own name is replaced with
   "Me" (see dashboard/index.ejs) so a row's from/to pair reads at a glance
   as "which side am I on" without a separate role column. */
.dashboard-you {
  color: var(--color-primary);
  font-weight: 600;
}

/* Wizard step header — a "← Back" link plus a plain "Step 2 of 4 · Where"
   caption. Replaced an earlier numbered-circle/connecting-line stepper,
   which read as more decoration than information at this step count. */
.wizard-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-md);
  margin: 0 0 var(--spacing-lg);
}
.wizard-back {
  font-size: 0.85rem;
  font-weight: 550;
  color: var(--color-text-muted);
  text-decoration: none;
}
.wizard-back:hover {
  color: var(--color-text);
}
.wizard-steps-caption {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 550;
  color: var(--color-text-muted);
}

.photo-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
  margin: var(--spacing-sm) 0 var(--spacing-md);
}
.photo-grid img {
  width: 96px;
  height: 96px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
}
.photo-thumb {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-xs);
}
.photo-thumb form {
  margin: 0;
}
.photo-thumb button {
  /* 44px min tap target (WCAG 2.5.5) — this used to be a ~24px button
     directly under the one piece of evidence a user is most likely to
     want to remove/redo, on a control that's primarily used on mobile. */
  min-height: 2.75rem;
  font-size: 0.78rem;
  padding: 0.4rem 0.75rem;
}

/* Custom-styled file picker trigger for the photo input (new-object.ejs) —
   the real <input type="file"> is visually hidden (.visually-hidden) but
   stays focusable/keyboard-operable; this <label for> is what's shown and
   matches the rest of the button system instead of the browser's default
   "Choose Files" control, the one native-styled element in the product. */
.file-picker {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  margin: var(--spacing-sm) 0 var(--spacing-md);
}
.file-picker label {
  margin: 0;
  cursor: pointer;
}
.file-picker [data-file-status] {
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

/* Gives the signature pad the same card-level ceremony as .party-group —
   previously it was a plain canvas sitting at the same visual weight as any
   other form field, which undersold the one moment in the flow that's
   actually consequential. */
.signature-block {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: var(--spacing-md) var(--spacing-lg) var(--spacing-lg);
  margin: var(--spacing-md) 0 var(--spacing-lg);
}
.signature-block label:first-of-type {
  margin-top: 0;
}

.signature-pad {
  display: block;
  width: 100%;
  max-width: 500px;
  height: 200px;
  touch-action: none;
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-md);
  /* Fixed white "paper" rather than var(--color-background) — the ink drawn
     into the canvas bitmap (see SIGNATURE_INK in app.js) is fixed dark too,
     so this has to stay fixed white as well or dark-mode ink would be
     invisible against a dark-themed pad while actually signing. */
  background: #ffffff;
  margin-bottom: var(--spacing-sm);
}

/* Toggled on <html> for the duration of an active signature stroke (see
   pointerdown/pointerup in app.js) — locks scrolling at the document level
   so nothing (rubber-band overscroll, a stray wheel/trackpad scroll) can
   move the page out from under a mid-stroke finger or cursor. */
html.is-signing,
html.is-signing body {
  overflow: hidden;
  touch-action: none;
}

/* The signer name field is prefilled from the parties step and locked at
   signing time (readonly, not editable) — styled visibly inert (muted
   background, no focus ring) so it doesn't look like a skippable required
   field that's merely pre-filled. */
input[readonly] {
  background: var(--color-background);
  color: var(--color-text-muted);
  cursor: default;
}

.sign-actions {
  display: flex;
  gap: var(--spacing-sm);
  max-width: 500px; /* matches .signature-pad's own max-width */
}
.sign-actions button {
  margin: 0;
  flex: 1;
}

/* On a phone held upright, a landscape-shaped signature pad only ever gets
   the phone's cramped width to work with, well short of what a full name
   needs — .sig-rotate-hint nudges the signer to turn the device sideways
   for more room, rather than faking a rotated layout in place (a real
   device rotation lets the pad, and its Clear/Sign buttons, stay normally
   oriented and readable). Hidden once the phone actually is in landscape,
   or on a large-enough screen where the hint doesn't apply anyway. */
.sig-rotate-hint {
  display: none;
  margin: 0 0 var(--spacing-sm);
  font-size: 0.82rem;
  color: var(--color-text-muted);
}

@media (max-width: 640px) and (orientation: portrait) {
  .sig-rotate-hint {
    display: block;
  }
}

/* Once the signer actually takes the .sig-rotate-hint above and turns the
   phone sideways, make good on it — the pad's normal 500px cap leaves most
   of a landscape phone's width unused. max-height keeps this to phones
   specifically (a tablet in landscape is still tall enough to skip it);
   max-width guards against a short-but-wide desktop window matching by
   accident. */
@media (max-width: 900px) and (max-height: 500px) and (orientation: landscape) {
  /* A phone's landscape height is short enough that even a compacted page
     (heading, name field, hints, topbar) pushes the pad below the fold,
     needing a scroll to reach the one control that matters. Instead this
     drops into a focused, full-screen mode: topbar and every bit of page
     chrome hidden, canvas + Clear/Sign filling the whole viewport. Nothing
     here is a dead end — rotating back to portrait brings all of it back,
     including Back and the typed-signature fallback. */
  .app-shell:has(.sign-page) {
    /* .app-shell is normally min-height:100vh so a long page can grow
       taller and scroll — exactly what this mode needs to NOT happen.
       Without a hard height here, flex-grow below just lets its children
       take whatever size they want and the shell grows to fit, instead of
       forcing them to actually fit inside one screen. */
    height: 100vh;
    overflow: hidden;
  }
  .app-shell:has(.sign-page) .topbar {
    display: none;
  }
  main.content:has(.sign-page) {
    display: flex;
    flex-direction: column;
    padding: var(--spacing-sm);
    min-height: 0;
  }
  .sign-page {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
  }
  .sign-page .wizard-header,
  .sign-page .eyebrow,
  .sign-page hgroup,
  .sign-page .sign-form > label,
  .sign-page .sig-rotate-hint,
  .sign-page .sig-mode-toggle,
  .sign-page .signature-block > label {
    display: none;
  }
  .sign-page .sign-form {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    max-width: none;
  }
  .sign-page [data-sig-draw-mode] {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
  }
  .sign-page .signature-block {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    border: none;
    background: none;
    padding: 0;
    margin: 0;
  }
  .sign-page .signature-pad {
    flex: 1;
    min-height: 60px;
    width: 100%;
    max-width: none;
    height: auto;
    margin-bottom: var(--spacing-sm);
  }
  .sign-page .sign-actions {
    max-width: none;
    flex-shrink: 0;
  }
}

/* Toggle between drawing and typing a signature (see data-sig-mode-toggle
   in app.js) — a plain text link rather than a button, so it doesn't
   visually compete with Clear/Sign for "which one do I press" on a device
   just handed to a second, unfamiliar signer. */
.sig-mode-toggle {
  display: block;
  width: auto;
  margin: var(--spacing-sm) 0 0;
  padding: 0;
  border: none;
  background: none;
  font-size: 0.85rem;
  font-weight: 550;
  color: var(--color-focus);
  text-decoration: underline;
  cursor: pointer;
}
.sig-mode-toggle:hover {
  color: var(--color-text);
}

.signature-image {
  /* min() rather than a bare 240px — the certificate now shows From/To
     side by side (.certificate-parties), and a fixed 240px overflowed the
     narrower column on the mobile-first card width. */
  max-width: min(240px, 100%);
  height: auto;
  display: block;
  margin: var(--spacing-xs) 0 var(--spacing-md);
}

.certificate-card {
  margin: 0 0 var(--spacing-lg);
  padding: var(--spacing-xl);
}
.certificate-card h3 {
  margin: var(--spacing-md) 0 var(--spacing-xs);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
}
.certificate-card h3:first-of-type {
  margin-top: 0;
}
.certificate-card p {
  margin: 0;
}

/* Completed-certificate treatment — a finalized certificate is the one
   artifact in the app meant to be shared/printed/trusted out of context, so
   it gets its own visual weight (accent top edge + a real shadow) instead of
   looking like any other content card. Not applied to the confirm-step
   preview (same component, no .is-final), which stays visually "in
   progress" until the record is actually final. */
.certificate-card.is-final {
  border-top: 3px solid var(--color-primary);
  box-shadow: var(--shadow-sm);
}

/* Certificate ID promoted out of the h3/p field list and into a header
   badge — previously it sat at the same weight as "Photographic evidence"
   at the very bottom of the card, when it's the one value a verifier
   actually needs to locate first. */
.certificate-id-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-sm);
  flex-wrap: wrap;
  margin-bottom: var(--spacing-md);
}
.certificate-id-group {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-sm);
}
.certificate-id-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.65rem;
  border-radius: var(--radius-sm);
  border: 1px solid color-mix(in srgb, var(--color-primary) 35%, transparent);
  background: var(--color-primary-tint);
  color: var(--color-badge-text-primary);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
}

/* Side-by-side From/To on wide screens (confirm preview + final
   certificate) — plain stacked <h3>/<p> pairs read as one undifferentiated
   list of fields; grouping the two parties the way .party-group already
   does on the wizard step restores that grouping here too. Single column
   below 640px, same breakpoint as the rest of the mobile-first layout. */
.certificate-parties {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0 var(--spacing-lg);
}
@media (min-width: 641px) {
  .certificate-parties {
    grid-template-columns: 1fr 1fr;
  }
}

/* Gives each party block on the "Who's involved?" step (new-parties.ejs)
   real visual separation — a plain <legend> alone read at the same weight
   as a field label, which risked the two blocks (six near-identical
   fields) blurring into one list at a skim. Same card treatment as
   .certificate-card/.handover-card so it reads as an established pattern,
   not a one-off. */
.party-group {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: var(--spacing-md) var(--spacing-lg) var(--spacing-lg);
  margin: 0 0 var(--spacing-lg);
}
.party-group legend {
  padding: 0 var(--spacing-xs);
  margin-left: calc(var(--spacing-xs) * -1);
  font-size: 0.75rem;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
}
.party-group label:first-of-type {
  margin-top: var(--spacing-sm);
}

/* --------------------------------------------------------------------------
   15. Mobile layout pass — dashboard card list and topbar page title. Same
   color tokens throughout, just different structure at narrow widths.
   -------------------------------------------------------------------------- */

/* Two renderings of the same handover rows (see dashboard/index.ejs):
   .table-scroll (desktop table) is the default, .handover-cards is hidden
   until the mobile breakpoint below swaps which one shows. */
.handover-cards {
  display: none;
  list-style: none;
  margin: 0;
  padding: 0;
  flex-direction: column;
  gap: var(--spacing-sm);
}
/* Pico's base `ul li { list-style: square }` targets the <li> directly,
   which wins over the inherited `list-style: none` above. */
.handover-cards li {
  list-style: none;
}
.handover-card {
  display: block;
  padding: var(--spacing-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--transition-fast), background var(--transition-fast);
}
a.handover-card:hover,
a.handover-card:active {
  border-color: var(--color-border-strong);
  background: var(--color-background);
}
.handover-card.is-inert {
  cursor: default;
}
.handover-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-sm);
}
.handover-card-title {
  font-size: 1rem;
  color: var(--color-text);
}
.handover-card-meta {
  margin: var(--spacing-xs) 0 0;
  font-size: 0.82rem;
  color: var(--color-text-muted);
}

@media (max-width: 640px) {
  .table-scroll {
    display: none;
  }
  .handover-cards {
    display: flex;
  }
}

/* --------------------------------------------------------------------------
   16. Photo lightbox (see [data-lightbox] in app.js) — confirm step +
   certificate page evidence photos.
   -------------------------------------------------------------------------- */

.photo-grid img[data-lightbox] {
  cursor: zoom-in;
}

.lightbox-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 100;
  align-items: center;
  justify-content: center;
  padding: var(--spacing-xl);
  background: color-mix(in srgb, #000 85%, transparent);
  cursor: zoom-out;
}
.lightbox-overlay.is-open {
  display: flex;
}
.lightbox-image {
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

/* PDF download link on the certificate page — a secondary action below the
   canonical certificate ID, not competing with the page's own content. */
.certificate-pdf-link {
  margin-top: var(--spacing-md) !important;
}

/* --------------------------------------------------------------------------
   17. Landing page — logged-out marketing page at "/". Deliberately not
   built on main.container/.auth-shell: those cap out around 440-960px,
   which reads as cramped for a full-bleed hero. Sections here manage their
   own max-width instead.
   -------------------------------------------------------------------------- */

.landing-body {
  background: var(--color-background);
}

.landing-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: 1100px;
  margin: 0 auto;
  padding: var(--spacing-lg) var(--spacing-lg) 0;
}
.landing-brand {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--color-text);
  text-decoration: none;
}
.landing-nav-links {
  display: flex;
  align-items: center;
  gap: var(--spacing-lg);
}
.landing-nav-links a {
  color: var(--color-text-muted);
  text-decoration: none;
  font-weight: 500;
}
.landing-nav-links a:hover {
  color: var(--color-text);
}
.landing-nav-cta {
  color: var(--color-background) !important;
  background: var(--color-primary);
  padding: var(--spacing-xs) var(--spacing-md) !important;
  border-radius: var(--radius-sm);
  font-weight: 600 !important;
}
.landing-nav-cta:hover {
  background: var(--color-primary-hover);
  color: var(--color-background) !important;
}

.landing-hero {
  max-width: 720px;
  margin: 0 auto;
  padding: var(--spacing-2xl) var(--spacing-lg) var(--spacing-xl);
  text-align: center;
}
.landing-hero .eyebrow {
  justify-content: center;
}
.landing-hero h1 {
  font-size: clamp(2.2rem, 5vw, 3.2rem);
  line-height: 1.1;
  margin-bottom: var(--spacing-md);
}
.landing-hero-sub {
  color: var(--color-text-muted);
  font-size: 1.05rem;
  max-width: 560px;
  margin: 0 auto var(--spacing-xl);
}
.landing-hero-actions {
  display: flex;
  gap: var(--spacing-md);
  justify-content: center;
  flex-wrap: wrap;
}
.landing-hero-actions [role="button"] {
  width: auto;
}

.landing-section {
  max-width: 900px;
  margin: 0 auto;
  padding: var(--spacing-2xl) var(--spacing-lg);
  border-top: 1px solid var(--color-border);
}
.landing-section h2 {
  font-size: clamp(1.5rem, 3vw, 2rem);
  max-width: 34ch;
}
.landing-lede {
  color: var(--color-text-muted);
  font-size: 1.05rem;
  max-width: 62ch;
}

.landing-step-grid,
.landing-feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--spacing-xl);
  margin-top: var(--spacing-xl);
}
.landing-step {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
}
.landing-step-number {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--color-primary);
  font-size: 0.85rem;
}
.landing-step h3,
.landing-feature h3 {
  font-family: var(--font-display);
  font-size: 1.05rem;
  margin: 0;
}
.landing-step p,
.landing-feature p {
  color: var(--color-text-muted);
  margin: 0;
}

.landing-cta {
  text-align: center;
  padding-bottom: var(--spacing-2xl);
}
.landing-cta h2 {
  max-width: none;
  margin: 0 auto var(--spacing-xs);
}
.landing-cta p {
  color: var(--color-text-muted);
  margin-bottom: var(--spacing-lg);
}
.landing-cta [role="button"] {
  width: auto;
}

.landing-footer {
  border-top: 1px solid var(--color-border);
  padding: var(--spacing-xl) var(--spacing-lg);
}
.landing-footer-inner {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--spacing-sm);
  color: var(--color-text-faint);
  font-size: 0.85rem;
}
.landing-footer-inner p {
  margin: 0;
}

@media (max-width: 640px) {
  .landing-nav-links {
    gap: var(--spacing-sm);
  }
}
