/* =============================================================================
   Superservice Design System
   Canonical reference: docs/design/design-system.md
   Direction: minimal, polished, spacious, fast. Apple-like clarity,
   Telegram-like simplicity. Light-first with an automatic dark theme.
   Edit tokens here; do not hardcode colors/spacing in templates.
   ========================================================================== */

/* ---- Tokens ---------------------------------------------------------------*/
:root {
  color-scheme: light;

  /* Brand */
  --accent: #0071e3;
  --accent-hover: #0077ed;
  --accent-press: #0064c8;
  --accent-soft: rgba(0, 113, 227, 0.10);
  --on-accent: #ffffff;

  /* Neutral surfaces */
  --bg: #fbfbfd;
  --surface: #ffffff;
  --surface-2: #f5f5f7;
  --border: rgba(0, 0, 0, 0.08);
  --border-strong: rgba(0, 0, 0, 0.16);

  /* Text */
  --text: #1d1d1f;
  --text-muted: #6e6e73;
  --text-subtle: #86868b;

  /* Feedback */
  --success: #1d8a4e;
  --success-soft: rgba(29, 138, 78, 0.12);
  --danger: #d70015;
  --danger-soft: rgba(215, 0, 21, 0.10);

  /* Typography */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
    Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
  --step--1: clamp(0.82rem, 0.79rem + 0.15vw, 0.9rem);
  --step-0: clamp(0.95rem, 0.92rem + 0.18vw, 1.05rem);
  --step-1: clamp(1.12rem, 1.04rem + 0.4vw, 1.35rem);
  --step-2: clamp(1.4rem, 1.25rem + 0.75vw, 1.85rem);
  --step-3: clamp(1.8rem, 1.5rem + 1.5vw, 2.6rem);
  --step-4: clamp(2.4rem, 1.9rem + 2.6vw, 3.75rem);
  --step-5: clamp(3rem, 2.2rem + 4vw, 5rem);

  /* Spacing scale (4px base) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 2.75rem;
  --space-8: 3.5rem;
  --space-9: 5rem;
  --space-10: 7rem;

  /* Radius */
  --radius-sm: 10px;
  --radius: 16px;
  --radius-lg: 24px;
  --radius-xl: 32px;
  --radius-pill: 999px;

  /* Shadows (soft, low-opacity) */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 3px rgba(0, 0, 0, 0.06);
  --shadow: 0 10px 30px rgba(0, 0, 0, 0.06), 0 2px 8px rgba(0, 0, 0, 0.04);
  --shadow-lg: 0 24px 60px rgba(0, 0, 0, 0.10), 0 6px 18px rgba(0, 0, 0, 0.06);

  /* Layout + motion */
  --container: 1120px;
  --gutter: clamp(1.25rem, 0.8rem + 2vw, 2rem);
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur: 200ms;
  --ring: 0 0 0 4px var(--accent-soft);
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --accent: #2997ff;
    --accent-hover: #47a7ff;
    --accent-press: #1a86f0;
    --accent-soft: rgba(41, 151, 255, 0.16);
    --on-accent: #04122a;

    --bg: #000000;
    --surface: #111114;
    --surface-2: #1c1c1f;
    --border: rgba(255, 255, 255, 0.10);
    --border-strong: rgba(255, 255, 255, 0.20);

    --text: #f5f5f7;
    --text-muted: #a1a1a6;
    --text-subtle: #86868b;

    --success: #40c463;
    --success-soft: rgba(64, 196, 99, 0.16);
    --danger: #ff5a5f;
    --danger-soft: rgba(255, 90, 95, 0.14);

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow: 0 12px 34px rgba(0, 0, 0, 0.5);
    --shadow-lg: 0 28px 64px rgba(0, 0, 0, 0.6);
  }
}

/* ---- Base -----------------------------------------------------------------*/
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

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

h1, h2, h3, h4 { margin: 0; line-height: 1.08; letter-spacing: -0.02em; font-weight: 650; }
/* An unbroken word at this size overflows a phone — a display name falls back to
   an email address, which has no break opportunity and pushed the page sideways. */
h1 { font-size: var(--step-4); letter-spacing: -0.04em; overflow-wrap: anywhere; }
h2 { font-size: var(--step-2); }
h3 { font-size: var(--step-1); }
p { margin: 0; }

a { color: var(--accent); text-decoration: none; transition: color var(--dur) var(--ease); }
a:hover { color: var(--accent-hover); }

img { max-width: 100%; height: auto; display: block; }

:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--radius-sm); }

.skip-link {
  /* Off-screen until focused. `inset-inline-start` (not `left`) so it lands on
     the correct side in RTL; no shadow while hidden or it bleeds into view. */
  position: absolute; inset-inline-start: var(--space-4); top: -3rem;
  background: var(--surface); color: var(--text); padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm); z-index: 100;
  transition: top var(--dur) var(--ease);
}
.brand__logo { display: block; height: 1.75rem; width: auto; max-width: 12rem; object-fit: contain; }
.skip-link:focus { top: var(--space-4); box-shadow: var(--shadow); }

/* ---- Layout ---------------------------------------------------------------*/
.wrap { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.section { padding-block: var(--space-8); }
.section--tight { padding-block: var(--space-6); }

.stack > * + * { margin-top: var(--space-4); }
.stack-lg > * + * { margin-top: var(--space-6); }

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

.grid { display: grid; gap: var(--space-5); grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); }

.eyebrow {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--step--1); font-weight: 600; letter-spacing: 0.02em;
  color: var(--text-muted); text-transform: none;
}
.muted { color: var(--text-muted); }
.subtle { color: var(--text-subtle); }
.lead { font-size: var(--step-1); color: var(--text-muted); line-height: 1.5; }

/* ---- Navigation -----------------------------------------------------------*/
.nav {
  /* Above .filter-bar (z-index 30): the nav creates a stacking context, so the
     open-menu scrim inside it can only cover the page if the whole header does. */
  position: sticky; top: 0; z-index: 50;
  /* No backdrop-filter here: it makes the header a containing block for
     position:fixed descendants, which clipped the full-screen menu scrim to the
     header's own 56px. Fully opaque rather than the 94% it used to be — without a
     blur behind it, 6% transparency just showed page content sliding through the
     menu while scrolling. */
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.nav-inner { min-height: 56px; display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.brand { font-weight: 700; letter-spacing: -0.03em; font-size: var(--step-1); color: var(--text); white-space: nowrap; }

/* Nav: state lives in a hidden checkbox (see base.html for why not <details>).
   Mobile — links collapse into a panel behind the burger. Desktop — burger hidden,
   links inline, subcategories on hover/focus. */
.nav-burger {
  display: inline-flex; align-items: center; justify-content: center;
  width: 3rem; height: 3rem; cursor: pointer;
  border-radius: var(--radius-sm); color: var(--text);
}
.nav-burger .icon { width: 1.6em; height: 1.6em; }
.nav-burger:hover { background: var(--surface-2); }
.nav-toggle:focus-visible + .nav-burger { outline: none; box-shadow: var(--ring); }

/* Mobile menu is a near-full-width sheet with generous rows. A cramped dropdown
   is hard for anyone with larger fingers, reduced vision or an older device —
   and this same markup ships inside the native app wrappers. */
.nav-links {
  position: absolute; z-index: 40;
  inset-inline: var(--space-4); top: calc(100% + var(--space-2));
  display: none; flex-direction: column; align-items: stretch; gap: var(--space-1);
  max-height: 75vh; overflow-y: auto; padding: var(--space-3);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
}
.nav-toggle:checked ~ .nav-links { display: flex; }

.nav-scrim { display: none; position: fixed; inset: 0; z-index: 35; cursor: default; }
.nav-toggle:checked ~ .nav-scrim { display: block; }

.nav-item { display: flex; align-items: center; }
.nav-links a {
  padding: var(--space-4); border-radius: var(--radius);
  color: var(--text); font-size: var(--step-1); font-weight: 600; flex: 1;
  min-height: 3rem; display: flex; align-items: center;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav-links a:hover { background: var(--surface-2); color: var(--text); }

/* A parent category shows its own link plus an arrow that reveals its children,
   so a visitor can reach a subcategory without loading the category page first. */
.nav-item--parent { display: flex; flex-wrap: wrap; align-items: center; }
.nav-sub-arrow {
  display: inline-flex; align-items: center; justify-content: center;
  width: 3rem; height: 3rem; cursor: pointer; border-radius: var(--radius);
  color: var(--text-muted); flex: none;
}
.nav-sub-arrow:hover { background: var(--surface-2); color: var(--text); }
.nav-sub-toggle:focus-visible + .nav-sub-arrow { outline: none; box-shadow: var(--ring); }
.caret {
  width: 0.45em; height: 0.45em;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); margin-top: -0.2em;
  transition: transform var(--dur) var(--ease);
}
.nav-sub-toggle:checked + .nav-sub-arrow .caret { transform: rotate(-135deg); margin-top: 0.15em; }
.nav-sub {
  display: none; flex-direction: column; width: 100%; gap: var(--space-1);
  padding-inline-start: var(--space-4); padding-block-end: var(--space-2);
}
.nav-sub-toggle:checked ~ .nav-sub { display: flex; }
.nav-sub a { font-size: var(--step-0); color: var(--text-muted); font-weight: 500; min-height: 2.75rem; }

.lang-switch { display: inline-flex; gap: var(--space-1); align-items: center; }
.lang-switch form { display: inline; margin: 0; }
.lang-option { font: inherit; font-size: var(--step--1); font-weight: 600; cursor: pointer; padding: var(--space-1) var(--space-2); border-radius: var(--radius-pill); border: 1px solid var(--border); background: var(--surface); color: var(--text-muted); }
.lang-option:hover { background: var(--surface-2); color: var(--text); }
.lang-option--active { background: var(--accent-soft); color: var(--accent); border-color: transparent; }

@media (min-width: 720px) {
  .nav-burger, .nav-scrim { display: none; }
  .nav-toggle:checked ~ .nav-scrim { display: none; }
  .nav-links {
    position: static; display: flex; flex-direction: row; flex-wrap: wrap;
    align-items: center; justify-content: flex-end; gap: var(--space-1);
    min-width: 0; max-height: none; overflow: visible; padding: 0;
    background: none; border: 0; box-shadow: none;
  }
  .nav-links > a, .nav-item--parent > a {
    padding: var(--space-2) var(--space-3); border-radius: var(--radius-pill);
    color: var(--text-muted); font-size: var(--step--1); font-weight: 550; flex: initial;
  }
  .nav-links a:hover { color: var(--text); }
  .nav-item--cta { color: var(--accent) !important; font-weight: 650 !important; }

  /* Subcategories open on hover or keyboard focus. On desktop the arrow is part of
     the same hover target as the label — a separate control next to it contradicts
     the mental model, and clicking it fought the hover state (the caret flipped
     while the panel stayed open). Here it is a pure affordance: pointer-events are
     off so a click falls through to the category link. */
  .nav-item--parent { position: relative; flex-wrap: nowrap; align-items: center; }
  .nav-item--parent > a { display: inline-flex; align-items: center; gap: var(--space-2); }
  .nav-sub-arrow {
    position: absolute; inset-inline-end: var(--space-2); top: 50%;
    transform: translateY(-50%);
    width: auto; height: auto; pointer-events: none; background: none;
  }
  .nav-item--parent > a { padding-inline-end: var(--space-6); }
  /* Hover/focus drives the caret, not the checkbox, so the two never disagree. */
  .nav-sub-toggle:checked + .nav-sub-arrow .caret { transform: rotate(45deg); margin-top: -0.2em; }
  .nav-item--parent:hover .nav-sub-arrow .caret,
  .nav-item--parent:focus-within .nav-sub-arrow .caret { transform: rotate(-135deg); margin-top: 0.15em; }
  /* The panel floats below the bar. Without this it inherits the mobile rule
     (static, full width) and renders inside the menu bar itself. */
  .nav-sub {
    position: absolute; inset-inline-start: 0; top: calc(100% + var(--space-2));
    min-width: 13rem; width: max-content; max-width: 22rem;
    padding: var(--space-2); gap: 2px;
    background: var(--surface); border: 1px solid var(--border-strong);
    border-radius: var(--radius); box-shadow: var(--shadow);
  }
  /* Bridge the visual gap between the item and its panel. Without it the mouse
     crosses a strip that belongs to neither element, :hover is lost and the panel
     closes mid-travel. The bridge is inside .nav-sub, so hovering it keeps the
     ancestor .nav-item--parent hovered. Invisible, and never on mobile where the
     panel is in flow. */
  .nav-sub::before {
    content: ""; position: absolute; inset-inline: 0;
    bottom: 100%; height: var(--space-3);
  }
  .nav-item--parent:hover .nav-sub,
  .nav-item--parent:focus-within .nav-sub { display: flex; }
  .nav-sub a {
    padding: var(--space-2) var(--space-3); font-size: var(--step--1);
    color: var(--text); border-radius: var(--radius-sm);
  }
  .nav-sub a:hover { background: var(--surface-2); }
}

/* ---- Icons ----------------------------------------------------------------*/
.icon {
  width: 1.25em; height: 1.25em; flex: none;
  vertical-align: -0.2em; /* optical alignment with adjacent text */
}
.icon--sm { width: 1em; height: 1em; }

/* ---- Hero -----------------------------------------------------------------*/
.hero { text-align: center; padding-block: var(--space-7) var(--space-5); }
/* Directory and profile pages lead with content, not with a landing-page hero:
   the filter bar and results should be near the fold. A full hero belongs on the
   home page, where setting the scene is the job. */
.hero--compact { padding-block: var(--space-5) var(--space-4); }
.hero--compact h1 { font-size: var(--step-3); }
.hero--compact .lead { margin-top: var(--space-3); font-size: var(--step-0); }
.hero h1 { max-width: 16ch; margin-inline: auto; }
.hero .lead { max-width: 62ch; margin: var(--space-5) auto 0; }
.hero .cluster { margin-top: var(--space-6); }

/* ---- Buttons --------------------------------------------------------------*/
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: 0.68em 1.25em; border-radius: var(--radius-pill);
  font: inherit; font-weight: 600; font-size: var(--step-0);
  border: 1px solid transparent; cursor: pointer; white-space: nowrap;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
    border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { background: var(--accent-hover); color: var(--on-accent); }
.btn--primary:active { background: var(--accent-press); }
.btn--secondary { background: var(--surface); color: var(--text); border-color: var(--border-strong); }
.btn--secondary:hover { background: var(--surface-2); color: var(--text); }
.btn--ghost { background: transparent; color: var(--accent); }
.btn--ghost:hover { background: var(--accent-soft); }
.btn--sm { padding: 0.5em 0.95em; font-size: var(--step--1); }
.btn--block { width: 100%; }

/* ---- Cards ----------------------------------------------------------------*/
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: var(--space-5);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.card--interactive:hover { transform: translateY(-3px); box-shadow: var(--shadow); border-color: var(--border-strong); }
.card > * + * { margin-top: var(--space-3); }
.card h3 { letter-spacing: -0.02em; }
.card__foot { margin-top: var(--space-4); }

/* ---- Chips, pills, badges -------------------------------------------------*/
.pill, .chip, .badge {
  display: inline-flex; align-items: center; gap: var(--space-2);
  border-radius: var(--radius-pill); font-size: var(--step--1); font-weight: 550;
  padding: 0.4em 0.85em; border: 1px solid var(--border);
  background: var(--surface); color: var(--text-muted);
}
.chip { transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease); }
a.chip:hover { background: var(--surface-2); color: var(--text); border-color: var(--border-strong); }
.badge--accent { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.badge--success { background: var(--success-soft); color: var(--success); border-color: transparent; }

/* ---- Tabs -----------------------------------------------------------------*/
/* Profile section tabs. All sections are rendered; only the selected one shows.
   No JavaScript and no extra URLs — a hidden radio drives it, and because each
   radio sits immediately before its own label and panel, the rules below need no
   per-profile ids. `order` lifts every label into a row above the panels. */
.tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: flex-start; }
.tabs__radio {
  /* Hidden but focusable: clip rather than display:none, or the tabs stop working
     from the keyboard and a screen reader loses the control entirely. */
  position: absolute; width: 1px; height: 1px; margin: -1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
.tabs__label {
  order: 1; cursor: pointer;
  display: inline-flex; align-items: center; min-height: 2.75rem;
  padding: 0.45em 1em; border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong); background: var(--surface);
  font-size: var(--step--1); font-weight: 600; color: var(--text-muted);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.tabs__label:hover { background: var(--surface-2); color: var(--text); }
.tabs__radio:checked + .tabs__label {
  background: var(--accent); color: var(--on-accent); border-color: transparent;
}
.tabs__radio:focus-visible + .tabs__label { outline: none; box-shadow: var(--ring); }
.tabs__panel { order: 2; flex: 1 1 100%; display: none; margin-top: var(--space-5); }
.tabs__radio:checked + .tabs__label + .tabs__panel { display: block; }
.tab {
  border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  padding: var(--space-2) var(--space-4); background: var(--surface);
  color: var(--text); font-weight: 600; font-size: var(--step--1);
}
.tab:hover { background: var(--surface-2); color: var(--text); }

/* ---- Forms ----------------------------------------------------------------*/
.field { display: grid; gap: var(--space-2); text-align: start; }
.label { font-size: var(--step--1); font-weight: 650; color: var(--text); }
.label .req { color: var(--danger); }
.help { font-size: var(--step--1); color: var(--text-subtle); }
.input, .textarea, .select {
  width: 100%; font: inherit; color: var(--text);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); padding: 0.7em 0.85em;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.input:focus, .textarea:focus, .select:focus { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
.textarea { min-height: 7.5rem; resize: vertical; }
.select[multiple] { min-height: 8rem; padding: var(--space-2); }
.field--error .input, .field--error .textarea, .field--error .select { border-color: var(--danger); }
.field__error { font-size: var(--step--1); color: var(--danger); font-weight: 550; }
.form-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); align-items: start; }
.field--wide { grid-column: 1 / -1; }
/* Checkbox beside its wording, not stacked under it: a bare checkbox stretched
   into a grid cell rendered adrift from the label it belongs to. `order` does the
   swap so the markup stays shared with every other field type. */
.field--check { grid-template-columns: auto minmax(0, 1fr); align-items: center; }
.field--check input[type="checkbox"] { order: 1; width: 1.1rem; height: 1.1rem; margin: 0; accent-color: var(--accent); }
.field--check .label { order: 2; font-weight: 550; }
.field--check .help, .field--check .field__error { order: 3; grid-column: 1 / -1; }

/* ---- Filter bar (dropdown facets) -----------------------------------------*/
.filter-bar {
  /* Sticks to the top so the filters stay reachable while scrolling results,
     without a modal or an interstitial step. `top: -1px` + a slightly stronger
     shadow once stuck keeps it from looking like a detached floating bar. */
  position: sticky; top: -1px; z-index: 30;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: var(--space-4);
  transition: box-shadow var(--dur) var(--ease);
}
@supports (animation-timeline: view()) {
  .filter-bar { box-shadow: var(--shadow-sm); }
}
.filter-bar__fields { display: grid; gap: var(--space-3); grid-template-columns: 1fr; }
.filter-bar__actions { display: flex; gap: var(--space-2); align-items: center; }
.filter-bar__selected {
  display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center;
  margin-top: var(--space-4); padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
.filter-note { margin-top: var(--space-3); }

/* Dropdown field: <details> styled as a form control, panel overlays content. */
.dropdown { position: relative; }
.dropdown__control {
  display: flex; align-items: baseline; gap: var(--space-2);
  width: 100%; cursor: pointer; list-style: none;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 0.7em 0.85em; font-size: var(--step-0);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.dropdown__control::-webkit-details-marker { display: none; }
.dropdown__control:hover { border-color: var(--accent); }
.dropdown[open] > .dropdown__control { border-color: var(--accent); box-shadow: var(--ring); }
.dropdown__control:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
.dropdown__label { font-size: var(--step--1); font-weight: 650; color: var(--text-muted); }
.dropdown__value { font-weight: 600; flex: 1; }
.dropdown__caret {
  /* Physical borders are deliberate here: right+bottom rotated 45deg is a
     symmetric down-chevron, so it reads the same in RTL. Logical properties
     would flip it to point the wrong way. */
  width: 0.5em; height: 0.5em; align-self: center; flex: none;
  border-right: 2px solid var(--text-subtle); border-bottom: 2px solid var(--text-subtle);
  transform: rotate(45deg); margin-top: -0.25em;
}
.dropdown[open] .dropdown__caret { transform: rotate(-135deg); margin-top: 0.15em; }
/* Mobile: fields are stacked, so the panel stays in flow — an overlay would
   cover the next field and the Apply button. Desktop switches to an overlay
   below so the results grid is never pushed down. */
/* The panel itself no longer scrolls — the tree inside it does. When the panel was
   the scroll container, the "Selected" strip and the Apply button scrolled with it:
   sticky kept them on screen, but the rows slid *underneath* and the last option was
   permanently half-hidden behind the button. Three rows in a column, with only the
   middle one scrolling, is the shape that has no overlap to fix. */
.dropdown__panel {
  margin-block-start: var(--space-2);
  min-width: 100%; max-height: min(65vh, 30rem);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: var(--space-2); display: flex; flex-direction: column; gap: 2px;
}
.picker__tree { overflow-y: auto; min-height: 0; flex: 1; }
.option {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 0.5em 0.6em; border-radius: var(--radius-sm); cursor: pointer;
  font-size: var(--step--1); color: var(--text); white-space: nowrap;
}
.option input { width: 1.15em; height: 1.15em; }
.option:hover { background: var(--surface-2); }
.option input { accent-color: var(--accent); flex: none; margin: 0; }
.option__name { flex: 1; }
.option__count { color: var(--text-subtle); font-variant-numeric: tabular-nums; }
/* An ancestor of the current selection. Not checked — checking Alborz as well as
   Karaj would mean the whole province — but the branch you are inside has to look
   different from the thirty provinces you are not. */
.option--on-path .option__name { color: var(--text); font-weight: 650; }
.option--on-path { background: var(--surface-2); }

/* ---- Tree picker (expandable multi-select over a hierarchy) -----------------
   Replaced a flat indented list, where depth was one class per level and so had a
   hard ceiling — locations have five levels and the list could style three. Nesting
   the markup instead means indentation is structural: a child is inside its parent,
   at any depth, and a branch can collapse. Everything below mirrors in RTL because
   the indent is `padding-inline-start` on the nested list, not a per-level margin. */
.picker__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; }
.picker__children > .picker__list {
  padding-inline-start: var(--space-4);
  border-inline-start: 2px solid var(--border);
  margin-inline-start: var(--space-3);
}
.picker__summary {
  display: flex; align-items: center; gap: var(--space-1);
  list-style: none; cursor: pointer; border-radius: var(--radius-sm);
}
.picker__summary::-webkit-details-marker { display: none; }
.picker__summary:hover { background: var(--surface-2); }
.picker__summary .option { flex: 1; min-width: 0; }
.picker__summary .option:hover { background: transparent; }
.picker__summary .option__name { overflow: hidden; text-overflow: ellipsis; }

/* The twisty belongs to the disclosure, the control to the choice. Keeping them
   visibly separate is the whole reason a branch can be opened without being picked —
   but they were separate and *tiny*, so opening a province meant hitting a 7px
   chevron. The arrow still looks small; its hit area is 2.75rem square, which is the
   size a thumb actually needs. The box is the target, the chevron is just the paint. */
.picker__twisty {
  position: relative; flex: none;
  width: 2.75rem; height: 2.75rem;
  border-radius: var(--radius-sm);
}
.picker__twisty::after {
  content: ""; position: absolute; inset-block-start: 50%; inset-inline-start: 50%;
  width: 0.5em; height: 0.5em;
  margin-block-start: -0.35em; margin-inline-start: -0.25em;
  border-right: 2px solid var(--text-muted); border-bottom: 2px solid var(--text-muted);
  transform: rotate(-45deg); transform-origin: center;
  transition: transform var(--dur) var(--ease);
}
.picker__summary:hover .picker__twisty { background: var(--surface-3, var(--surface-2)); }
.picker__branch[open] > .picker__summary > .picker__twisty::after { transform: rotate(45deg); }
.picker__summary:focus-visible { outline: none; box-shadow: var(--ring); }
/* Leaves line up with their siblings' labels, not with the twisty they do not have. */
.option--leaf { margin-inline-start: 2.75rem; }
.picker__children .option--leaf { margin-inline-start: 2.75rem; }

/* A branch that contains the choice. Its own control stays unchecked — picking Karaj
   is not picking Alborz — so the mark says "in here" rather than "this". The script
   also sets `indeterminate`, which renders as a dash in the box itself. */
.option--ancestor { background: var(--accent-soft); }
.option--ancestor .option__name { color: var(--text); font-weight: 650; }

/* A parent that cannot be chosen at all: sections are leaf-only, so it is a heading
   with a disclosure, and it must not look like a control that failed to work. */
.option--container { cursor: pointer; }
.option--container .option__name {
  font-weight: 650; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.03em; font-size: var(--step--2);
}

/* Rows are touch-sized. A picker is used with a thumb, in a scrolling panel, where a
   mis-tap either chooses the wrong place or collapses the branch you just opened. */
.picker__list .option { min-height: 2.75rem; }
.option__name { overflow-wrap: anywhere; white-space: normal; }

/* Chosen options, above the tree and outside its scroll area — the answer to "what
   have I picked?" must not scroll away with the thing you are picking from. */
.picker__chosen {
  flex: none;
  display: grid; gap: var(--space-2);
  padding: var(--space-2); margin-block-end: var(--space-2);
  background: var(--surface-2); border-radius: var(--radius-sm);
}
.picker__chosen[hidden] { display: none; }
.picker__chosen-label {
  font-size: var(--step--2); font-weight: 650; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.picker__chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chip {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 0.25em 0.6em; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--border-strong);
  font-size: var(--step--2); color: var(--text); line-height: 1.6;
}
.chip--removable { cursor: pointer; font: inherit; font-size: var(--step--2); }
.chip--removable:hover { border-color: var(--accent); }
.chip__x { color: var(--text-subtle); font-size: 1.15em; line-height: 1; }
.picker__actions {
  flex: none;
  padding-block-start: var(--space-2); margin-block-start: var(--space-2);
  border-top: 1px solid var(--border); background: var(--surface);
}

/* Selection tree — an already-chosen hierarchy shown in place (owner panel).
   Rows, not cards: a card per node split one nested place into several tiles that
   read as unrelated areas. Depth comes from a `--depth` custom property set on the
   row, so it scales to all five location levels without a class per level, and the
   spine plus indent use logical properties to mirror in RTL. */
.tree { list-style: none; margin: 0; padding: 0; }
.tree__row {
  display: flex; align-items: center; gap: var(--space-3);
  /* Deep rows carry a long name plus type, flag and Remove. Without wrapping,
     a long name (Persian district names run long) overlapped the type label at
     390px — the row must reflow instead. */
  flex-wrap: wrap;
  padding-block: var(--space-2);
  /* Mobile-first: a neighborhood sits four levels deep, and the wider desktop
     step would spend a quarter of a 390px screen on indent alone. */
  padding-inline-start: calc(var(--depth, 0) * var(--space-3));
  font-size: var(--step--1);
}
.tree__row + .tree__row { border-top: 1px solid var(--border); }
.tree__name { font-weight: 600; color: var(--text); min-width: 0; overflow-wrap: anywhere; }
.tree__type { color: var(--text-subtle); white-space: nowrap; }
/* Ancestors nobody picked: present for orientation, so they recede and offer no
   controls. Without them a bare "Karaj" never says which province it is in. */
.tree__row--context .tree__name { font-weight: 500; color: var(--text-muted); }
.tree__flag {
  font-size: var(--step--1); font-weight: 700; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--accent); white-space: nowrap;
}
/* Not-yet-live states are information, not achievements — they should not read
   as loud as the accent colour reserved for "this is working". */
.tree__flag--muted { color: var(--text-subtle); }
/* Supporting text (a section's bio excerpt). Truncates rather than pushing the
   controls off the row; on a narrow screen it wraps to its own line instead. */
/* Supporting text (a section's bio excerpt). Truncates rather than pushing the
   controls off the row. On mobile it always takes its own line — sharing the row
   left it inline on short names and wrapped on long ones, which read as broken. */
.tree__note {
  color: var(--text-muted); flex: 1 1 100%; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tree__actions { margin-inline-start: auto; flex: none; }
/* Separate root hierarchies from each other more strongly than sibling rows. */
.tree + .tree { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 2px solid var(--border-strong); }

/* Profile setup checklist (owner dashboard). Shares the tree's row rhythm but is
   flat, so it gets its own names — `.tree__*` means hierarchy. Done steps recede
   to one quiet line; what is left to do is what the block is about. */
.checklist { list-style: none; margin: 0; padding: 0; }
.checklist__item {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding-block: var(--space-3); font-size: var(--step--1);
}
.checklist__item + .checklist__item { border-top: 1px solid var(--border); }
.checklist__mark { flex: none; font-weight: 700; color: var(--text-subtle); }
.checklist__item--done { padding-block: var(--space-2); }
.checklist__item--done .checklist__mark { color: var(--success); }
.checklist__item--done .checklist__label { color: var(--text-muted); }
.checklist__label { font-weight: 600; color: var(--text); }
/* Required is a property of the step, not a thing the button does — the button
   always says what it does. */
.checklist__required {
  flex: none; font-size: var(--step--1); font-weight: 700; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--danger);
}
/* The reason, not the instruction. Its own line on mobile, inline when there is
   room — on a wide screen three stacked lines per step read as disconnected. */
.checklist__why { color: var(--text-muted); flex: 1 1 100%; min-width: 0; }
.checklist__action { margin-inline-start: auto; flex: none; }

/* Media grids. Fixed aspect tiles so a wall of mixed portrait and landscape photos
   still reads as a grid rather than a ragged column.

   The tinted background is a deliberate placeholder: with width/height on every img
   the browser reserves the box before the bytes arrive, so a slow connection sees a
   calm grey block that becomes a photo — not a page that jumps. That is cheaper and
   steadier than a skeleton animation, which mostly advertises that something is
   slow. */
.media-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fill, minmax(min(100%, 180px), 1fr)); }
.media-tile { margin: 0; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--surface); }
.media-tile img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; background: var(--surface-2); }
.media-tile figcaption { padding: var(--space-3); font-size: var(--step--1); }
.media-tile--logo { border-color: var(--accent); }

/* Profile logo on the public page and on result cards. */
/* `margin-inline: auto` because the global reset makes images block-level, so the
   hero's text-align does not centre them. */
/* Avatars. The slot is the same size whether or not there is a photo, so a listing
   has one layout rather than two, and a profile without a picture reads as "not yet"
   instead of "broken". Sizes are set here rather than by the image, so swapping a
   preset never changes the page. */
/* Reorderable rows. The grip only appears once the script has made them draggable,
   so it never advertises an affordance that does not work. */
.tree__grip { display: none; color: var(--text-subtle); cursor: grab; letter-spacing: -2px; }
.is-reorderable .tree__grip { display: inline; }
.is-reorderable .tree__row { background: var(--surface); }
.tree__row.is-dragging { opacity: 0.5; }
.block-image { width: 100%; border-radius: var(--radius-sm); object-fit: cover; background: var(--surface-2); }
.category-hero { width: 100%; max-height: 22rem; object-fit: cover; border-radius: var(--radius); background: var(--surface-2); }
.avatar {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm); object-fit: cover; background: var(--surface-2);
  overflow: hidden;
}
.avatar--sm { width: 2.5rem; height: 2.5rem; }
.avatar--lg { width: 4.5rem; height: 4.5rem; margin-inline: auto; border-radius: var(--radius); }
/* The initial, when there is no photo. Two tints so a page of them is not a grid of
   identical grey squares; both hold contrast against their text in either theme. */
.avatar--empty { font-weight: 700; color: var(--text-muted); letter-spacing: -0.02em; }
.avatar--empty[data-tint="a"] { background: var(--surface-2); }
.avatar--empty[data-tint="b"] { background: var(--accent-soft, var(--surface-2)); }
.avatar--sm.avatar--empty { font-size: var(--step-0); }
.avatar--lg.avatar--empty { font-size: var(--step-2); display: flex; }

/* Selected-filter chips (removable) */
.facet-group__label { font-size: var(--step--1); font-weight: 700; color: var(--text-muted); }
.facet {
  display: inline-flex; align-items: center; gap: var(--space-2);
  border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  padding: 0.4em 0.85em; font-size: var(--step--1); font-weight: 600;
  color: var(--text); background: var(--surface);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.facet:hover { background: var(--surface-2); color: var(--text); border-color: var(--border-strong); }
.facet--active { background: var(--accent); color: var(--on-accent); border-color: transparent; }
.facet--active:hover { background: var(--accent-hover); color: var(--on-accent); }
.facet__x { font-weight: 700; margin-inline-start: var(--space-1); }

@media (min-width: 720px) {
  .filter-bar__fields {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
    gap: var(--space-4); align-items: center;
  }
  .dropdown__panel {
    position: absolute; z-index: 20;
    inset-inline-start: 0; inset-block-start: calc(100% + var(--space-2));
    margin-block-start: 0;
  }
  /* Room to spare here, so give the hierarchy a clearer step than on mobile. */
  .tree__row { padding-inline-start: calc(var(--depth, 0) * var(--space-5)); }
  /* Wide enough for the note to share the row with the name and controls. */
  .tree__note { flex: 1 1 12rem; }
  .checklist__why { flex: 1 1 14rem; }
}

/* ---- Category tiles (home) ------------------------------------------------*/
.tiles { display: grid; gap: var(--space-3); grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr)); }
.tile {
  display: flex; flex-direction: column; gap: var(--space-1);
  min-height: 4.5rem; justify-content: center;
  padding: var(--space-4) var(--space-5);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); color: var(--text);
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.tile:hover { border-color: var(--accent); transform: translateY(-2px); color: var(--text); }
.tile__name { font-weight: 650; font-size: var(--step-0); }
.tile__meta { font-size: var(--step--1); color: var(--text-subtle); }

/* ---- Breadcrumb -----------------------------------------------------------*/
.breadcrumb { padding-block-start: var(--space-5); font-size: var(--step--1); }
.breadcrumb ol { list-style: none; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; padding: 0; }
.breadcrumb li { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--text-subtle); }
/* Separator is a slash rather than an arrow: direction-neutral, so it needs no RTL
   variant. Generated content is decorative and hidden from assistive tech. */
.breadcrumb li + li::before { content: "/"; color: var(--border-strong); }
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: var(--text); text-decoration: underline; }
.breadcrumb [aria-current="page"] { color: var(--text); font-weight: 550; }

/* ---- Directory results -----------------------------------------------------*/
.results-bar {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  align-items: baseline; justify-content: space-between;
}
.results-count { color: var(--text-muted); font-size: var(--step--1); margin: 0; }
.sort { display: flex; flex-wrap: wrap; gap: var(--space-1); align-items: center; }
.sort__label { font-size: var(--step--1); color: var(--text-subtle); margin-inline-end: var(--space-1); }
.sort__option {
  font-size: var(--step--1); font-weight: 550; color: var(--text-muted);
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-pill);
}
.sort__option:hover { background: var(--surface-2); color: var(--text); }
.sort__option--active { background: var(--accent-soft); color: var(--accent); font-weight: 650; }

.result-meta { display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: center; color: var(--text-subtle); font-size: var(--step--1); }
.meta-item { display: inline-flex; align-items: center; gap: var(--space-2); }
.result-meta .dot { width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: 0.6; }

/* ---- Alerts / notices -----------------------------------------------------*/
.alert {
  display: flex; gap: var(--space-3); align-items: flex-start;
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--space-4); background: var(--surface); color: var(--text-muted);
}
.alert--success { background: var(--success-soft); border-color: transparent; color: var(--success); }
.alert--info { background: var(--accent-soft); border-color: transparent; color: var(--accent); }
.alert--danger, .alert--error { background: var(--danger-soft); border-color: transparent; color: var(--danger); }
.notice { border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-4); background: var(--surface-2); color: var(--text-muted); }

/* ---- Footer ---------------------------------------------------------------*/
.footer { border-top: 1px solid var(--border); margin-top: var(--space-9); padding-block: var(--space-6); color: var(--text-subtle); font-size: var(--step--1); }
.footer-inner { display: flex; flex-wrap: wrap; gap: var(--space-4); justify-content: space-between; align-items: center; }

/* ---- Utilities ------------------------------------------------------------*/
.text-center { text-align: center; }
.divider { height: 1px; background: var(--border); border: 0; margin-block: var(--space-6); }

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

/* Shared confirmation dialog. Small, centred, and phrased by the product rather than
   by the browser — window.confirm cannot be styled and, on some mobile browsers,
   offers to suppress every later dialog. */
.confirm {
  border: none; border-radius: var(--radius); padding: 0;
  max-width: min(92vw, 26rem); background: var(--surface); color: var(--text);
  box-shadow: var(--shadow);
}
.confirm::backdrop { background: rgba(0, 0, 0, 0.5); }
.confirm__body { display: grid; gap: var(--space-4); padding: var(--space-5); }
.confirm__message { margin: 0; font-weight: 550; }
.btn--danger { background: var(--danger); color: #fff; border-color: transparent; }
.btn--danger:hover { filter: brightness(0.94); }

/* Reviews. Stars are text, not images: they scale with the type, need no request,
   and read correctly to a screen reader through the label beside them. */
.rating { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--step--1); }
.rating__stars { color: var(--accent); letter-spacing: 1px; }
.reviews { display: grid; gap: var(--space-3); margin-top: var(--space-5); padding-top: var(--space-5); border-top: 1px solid var(--border); }
.review { padding-block: var(--space-3); border-top: 1px solid var(--border); }
/* The reviewer's own review, shown back to them rather than as a filled form. */
.review--mine { background: var(--surface-2); border-radius: var(--radius); padding: var(--space-4); border-top: none; }
.review__edit summary { cursor: pointer; }
.review + .review { margin-top: 0; }
.review__reply {
  margin-top: var(--space-3); padding: var(--space-3);
  border-inline-start: 3px solid var(--border-strong); background: var(--surface-2);
  border-radius: var(--radius-sm); font-size: var(--step--1);
}
.rating__choice { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--step--1); cursor: pointer; }
.rating__choice input { accent-color: var(--accent); }

/* ---- Authentication ---------------------------------------------------------
   Signing in is the one screen where someone types something half-remembered, on a
   phone, often in a hurry, and where failing means not becoming a user at all. It
   gets a narrower column, larger controls and more space than a settings form.

   The alternative route sits *outside* the card, as a full-width line rather than a
   footnote under the button. It used to be small ghost text below the form, which is
   how someone who registered by phone ended up staring at a password field. */
.auth { max-width: 26rem; margin-inline: auto; }
.auth__head { text-align: center; margin-block-end: var(--space-5); }
.auth__head h1 { margin-block: var(--space-2) 0; }
.auth__head .lead { margin-block-start: var(--space-3); font-size: var(--step--1); }
.auth__card { gap: var(--space-5); }
.auth__card .input {
  /* 16px minimum: anything smaller makes iOS Safari zoom the page on focus, which
     scrolls the field out of view at the exact moment of typing into it. */
  font-size: max(1rem, var(--step-0));
  padding-block: 0.85em;
}
.auth__alt {
  margin-block-start: var(--space-5);
  text-align: center;
  font-size: var(--step--1);
  color: var(--text-muted);
}
.auth__alt a { font-weight: 600; }

/* ---- Switch -----------------------------------------------------------------
   `is_active` was a bare checkbox labelled "Is active", which reads as a form field
   to fill in rather than a thing that is currently on. A switch says which state you
   are in *now* and that flipping it takes effect — the same information the checkbox
   technically carried and nobody read.

   Still a real `<input type="checkbox">` underneath: it submits, it focuses, screen
   readers announce it, and with CSS off it degrades to a checkbox rather than
   vanishing. Only the paint changes. */
.switch {
  display: flex; align-items: center; gap: var(--space-3);
  cursor: pointer; min-height: 2.75rem;
}
.switch input {
  appearance: none; -webkit-appearance: none;
  flex: none; margin: 0; cursor: pointer;
  width: 2.75rem; height: 1.6rem; border-radius: 999px;
  background: var(--border-strong); position: relative;
  transition: background var(--dur) var(--ease);
}
.switch input::after {
  content: ""; position: absolute; inset-block-start: 0.2rem; inset-inline-start: 0.2rem;
  width: 1.2rem; height: 1.2rem; border-radius: 50%;
  background: var(--surface); box-shadow: var(--shadow-sm);
  transition: transform var(--dur) var(--ease);
}
.switch input:checked { background: var(--accent); }
/* Logical translation, so the knob travels the correct way in RTL. */
.switch input:checked::after { transform: translateX(1.15rem); }
[dir="rtl"] .switch input:checked::after { transform: translateX(-1.15rem); }
.switch input:focus-visible { outline: none; box-shadow: var(--ring); }
.switch__text { font-weight: 550; }
.switch__state { color: var(--text-subtle); font-size: var(--step--1); }

/* A row whose thing is switched off. Legible, clearly secondary, never invisible —
   an owner has to be able to find what they turned off in order to turn it back on. */
.is-off { opacity: 0.55; }
.is-off .tree__name { text-decoration: line-through; text-decoration-thickness: 1px; }

/* ---- Owner section rows -----------------------------------------------------
   Name, status, path and bio were one line, so the most important thing on the
   screen — which section is this — had to be picked out of a sentence. Two lines:
   what it is, then everything about it. */
.tree__row--rich {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-areas: "grip name actions" ". meta actions";
  gap: var(--space-2) var(--space-3);
  align-items: center;
  padding-block: var(--space-4);
}
.tree__row--rich .tree__grip { grid-area: grip; font-size: var(--step-1); }
.tree__row--rich .tree__head { grid-area: name; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.tree__row--rich .tree__meta { grid-area: meta; display: grid; gap: var(--space-1); min-width: 0; }
.tree__row--rich .tree__actions { grid-area: actions; }
.tree__row--rich .tree__name { font-size: var(--step-0); font-weight: 650; }
.tree__row--rich .tree__path { font-size: var(--step--2); color: var(--text-subtle); }
.tree__row--rich .tree__note { font-size: var(--step--1); color: var(--text-muted); white-space: normal; }
@media (max-width: 559px) {
  /* On a phone the controls get their own row rather than squeezing the name. */
  .tree__row--rich {
    grid-template-columns: auto 1fr;
    grid-template-areas: "grip name" ". meta" ". actions";
  }
}
/* The grip only appears once the script has made rows draggable, so it never
   advertises an affordance that does not work. Sized for a thumb. */
.is-reorderable .tree__row--rich .tree__grip {
  display: flex; align-items: center; justify-content: center;
  width: 2.5rem; height: 2.5rem; border-radius: var(--radius-sm);
}
.is-reorderable .tree__row--rich .tree__grip:hover { background: var(--surface-2); }
/* A switch spans the form grid: it is a statement about the whole thing being
   edited, not one cell of a two-column layout. */
.field--switch { grid-column: 1 / -1; }

/* The same switch as a submit button, for toggling a row in place. `role="switch"`
   with `aria-checked` is the accessible equivalent of the checkbox version, and it
   needs no JavaScript: pressing it posts and the row comes back in its new state. */
.switch__button {
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: none; padding: var(--space-2); cursor: pointer;
  min-width: 2.75rem; min-height: 2.75rem; border-radius: var(--radius-sm);
}
.switch__button:hover { background: var(--surface-2); }
.switch__button:focus-visible { outline: none; box-shadow: var(--ring); }
.switch__track {
  width: 2.25rem; height: 1.3rem; border-radius: 999px;
  background: var(--border-strong); position: relative;
  transition: background var(--dur) var(--ease);
}
.switch__track::after {
  content: ""; position: absolute; inset-block-start: 0.15rem; inset-inline-start: 0.15rem;
  width: 1rem; height: 1rem; border-radius: 50%;
  background: var(--surface); box-shadow: var(--shadow-sm);
  transition: transform var(--dur) var(--ease);
}
[role="switch"][aria-checked="true"] .switch__track { background: var(--accent); }
[role="switch"][aria-checked="true"] .switch__track::after { transform: translateX(0.95rem); }
[dir="rtl"] [role="switch"][aria-checked="true"] .switch__track::after { transform: translateX(-0.95rem); }

/* Text for screen readers only. The row toggle's meaning is carried by position and
   colour for sighted users; someone using a screen reader needs the words. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* The switch's words stack: what it is, then what turning it off does. Inline they
   ran into one sentence — "Show in the directory Turn off to hide it without
   deleting anything" — which reads as a single confused instruction. */
.switch__body { display: grid; gap: var(--space-1); min-width: 0; }
.switch__body .help { margin: 0; }

/* ---- Link cards -------------------------------------------------------------
   The parts of one section, reachable from the section. Content and services are
   per subcategory but lived only under their own dashboard entries, so editing a
   section meant going back out to a list and finding the same subcategory again. */
.linkcards { display: grid; gap: var(--space-3); grid-template-columns: 1fr; }
@media (min-width: 560px) { .linkcards { grid-template-columns: repeat(3, 1fr); } }
.linkcard {
  display: grid; gap: var(--space-1); align-content: start;
  padding: var(--space-4); min-height: 4.5rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: inherit; text-decoration: none;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.linkcard:hover { border-color: var(--accent); box-shadow: var(--shadow-sm); }
.linkcard__title { font-weight: 650; color: var(--accent); }
.linkcard__note { font-size: var(--step--1); color: var(--text-subtle); }

/* ---- Test-mode code ---------------------------------------------------------
   Deliberately loud and deliberately ugly. The danger of a mode that shows one-time
   codes on screen is not that it exists — it is that somebody forgets it is on. A
   page that announces its own insecurity gets noticed; a quiet admin setting does
   not. This is meant to be the first thing you want to remove. */
.test-code {
  border: 2px dashed var(--danger);
  border-radius: var(--radius);
  padding: var(--space-4);
  background: var(--danger-soft);
  display: grid; gap: var(--space-2);
}
.test-code__badge {
  justify-self: start;
  background: var(--danger); color: #fff;
  font-size: var(--step--2); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em;
  padding: 0.15em 0.6em; border-radius: 999px;
}
.test-code__body { margin: 0; }
.test-code__value {
  font-size: var(--step-2); font-variant-numeric: tabular-nums;
  letter-spacing: 0.15em; margin-inline-start: 0.35em;
  user-select: all;  /* one tap selects the whole code */
}
.test-code__warning { margin: 0; font-size: var(--step--1); color: var(--danger); }
/* A heading inside a form, separating the section's own fields from the ones its
   subcategory declares. Spans the grid so it reads as a divider, not a label. */
.form-section-title {
  grid-column: 1 / -1;
  margin-block: var(--space-4) 0;
  font-size: var(--step-0);
  padding-block-start: var(--space-4);
  border-top: 1px solid var(--border);
}

/* ---- Specs — a subcategory's declared fields, answered -----------------------
   These replaced feature chips. A chip could only say a field was present; these
   carry the answer, which is what a visitor actually compares profiles on. */
.spec {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-3); padding-block: var(--space-2);
  border-bottom: 1px solid var(--border);
}
.spec:last-child { border-bottom: 0; }
.spec__name { color: var(--text-muted); font-size: var(--step--1); }
.spec__value { font-weight: 600; text-align: end; }
/* Specs stack. They briefly inherited the chip row's wrapping flex and rendered
   side by side, so a name and its answer could end up on different lines from each
   other while two different specs sat together. */
.specs { list-style: none; margin: 0; padding: 0; display: grid; }

/* ---- Feature filters --------------------------------------------------------
   A subcategory's own fields as filters. Plain controls, not the tree picker: a
   price is a range and a level is a short list, and the picker's whole job is depth
   that these do not have. */
.ffilter { display: grid; gap: var(--space-2); padding: var(--space-3) var(--space-2); }
.ffilter + .ffilter { border-top: 1px solid var(--border); }
.ffilter__label { font-size: var(--step--1); font-weight: 650; color: var(--text-muted); }
.ffilter__range { display: flex; align-items: center; gap: var(--space-2); }
.ffilter__range .input { flex: 1; min-width: 0; }
.ffilter__dash { color: var(--text-subtle); flex: none; }
.input--sm, .select--sm { padding-block: 0.5em; font-size: var(--step--1); }

/* ---- Profile card -----------------------------------------------------------
   What someone chooses between two listings on: who, where, whether it can be
   booked, what it scored, and the one or two numbers its subcategory says matter.
   The rating and the specs were already in the database and shown nowhere. */
.pcard { display: grid; gap: var(--space-3); align-content: start; }
.pcard__head { display: grid; grid-template-columns: auto 1fr auto; gap: var(--space-3); align-items: center; }
.pcard__title { min-width: 0; }
.pcard__title h3 { margin: 0; font-size: var(--step-0); overflow-wrap: anywhere; }
.pcard__niche { font-size: var(--step--2); color: var(--text-subtle); }
.pcard__snippet { margin: 0; font-size: var(--step--1); }

/* A score without its count is unreadable — 5.0 from one review is not 4.6 from
   ninety — so the two never appear apart. */
.rating-badge {
  display: inline-flex; align-items: center; gap: 0.25em; flex: none;
  font-size: var(--step--1); font-weight: 650;
}
.rating-badge .icon { color: var(--warning, #e8a33d); }
.rating-badge__count { color: var(--text-subtle); font-weight: 500; font-size: var(--step--2); }

/* Two specs, laid out as label-then-value pairs so a price reads as a price. */
.pcard__specs { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
.pcard__spec { font-size: var(--step--1); font-weight: 600; }
.pcard__spec-name { color: var(--text-subtle); font-weight: 500; }

/* ---- Contact box ------------------------------------------------------------
   The three things a visitor came to do, under the name, as equal targets. They
   used to be scattered: the phone at the foot of the page, the locations in a card
   below that, the booking form inside a tab. */
.contact-box {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: var(--space-2); margin-block-end: var(--space-4);
}
.contact-box__action {
  display: grid; justify-items: center; gap: var(--space-1);
  padding: var(--space-3) var(--space-2); min-height: 3.5rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--accent);
  font-size: var(--step--2); font-weight: 600; text-decoration: none;
  cursor: pointer; font-family: inherit;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.contact-box__action:hover { border-color: var(--accent); background: var(--accent-soft); }
.contact-box__action:focus-visible { outline: none; box-shadow: var(--ring); }
.contact-box__action .icon { width: 1.35em; height: 1.35em; }

/* A dialog used as a phone-style sheet: full width at the bottom on small screens,
   a centred card on larger ones. */
.sheet {
  border: 0; padding: 0; border-radius: var(--radius) var(--radius) 0 0;
  background: var(--surface); color: var(--text);
  width: 100%; max-width: 34rem; margin-inline: auto;
  margin-block-start: auto; margin-block-end: 0;
}
.sheet::backdrop { background: rgba(0, 0, 0, 0.45); }
.sheet__body { display: grid; gap: var(--space-3); padding: var(--space-5); }
@media (min-width: 560px) {
  .sheet { margin-block: auto; border-radius: var(--radius); }
}

/* ---- Offerings with places --------------------------------------------------
   A class that is full still shows: a card that vanishes when it fills reads as a
   page that lost something, and someone who came back for it needs to be told. */
.offering__state { margin: 0; font-size: var(--step--1); font-weight: 650; }
.offering__state--open { color: var(--success, #1c6b3c); }
.offering__state--full { color: var(--text-subtle); }
.offering--closed { opacity: 0.72; }
/* A sheet holding a long form scrolls inside itself rather than growing past the
   viewport, so the submit button is always reachable. */
.sheet--tall { max-height: 88vh; display: flex; }
.sheet--tall .sheet__body { overflow-y: auto; }
