/* app.css — page-specific widgets (switch, tag select, datepicker, cropper, channel identity…).
   Tokens live in tokens.css and the shared components in ui.css; everything here is built from
   those tokens only — no literal colour, radius or shadow. See docs/design-system.md. */

html,
body {
  margin: 0;
  padding: 0;
}

/* NOTE: deliberately NO unlayered `a { color: inherit }` here. Tailwind's preflight already
   resets links (`a { color: inherit; text-decoration: inherit }`) inside `@layer base`, which
   correctly sits BELOW `@layer utilities`. An UNLAYERED rule like that would WIN over every
   text-color utility (e.g. `text-white`, `text-primary`) on <a> elements — because in the CSS
   cascade unlayered styles beat layered ones regardless of specificity — turning navy links with
   `text-white` into dark-on-dark. Let preflight + utilities handle links. (Buttons were unaffected,
   which is why only <a> links showed the bug.) */

/* Alpine: hide x-cloak elements until Alpine initializes (avoids drawer flash). */
[x-cloak] {
  display: none !important;
}
@media (min-width: 1024px) {
  /* On desktop the sidebar is always shown even before Alpine loads. */
  aside[x-cloak] {
    display: flex !important;
  }
}

/* File-upload component: reveal the saved-file row when a file is present. */
[data-uploader][data-has-file] [data-file-display] {
  display: flex !important;
}

/* Compact file-upload variant: the empty row and the filled row are mutually exclusive, so
   the control keeps ONE fixed height whether or not a file is attached. */
[data-uploader][data-has-file] [data-file-empty] {
  display: none !important;
}

/* ── Switch (used by the hook form's sticky publish control) ────────────────────────────────
   Plain CSS rather than a peer-checked Tailwind chain: the toggle is drawn from an ::after
   knob whose transform must flip for RTL, which is exactly the kind of thing the browser
   Tailwind build should not have to express. */
.fl-switch {
  position: relative;
  display: inline-block;
  box-sizing: border-box;
  width: 38px;
  height: 22px;
  flex: none;
  border-radius: 9999px;
  background: var(--color-border-dark);
  transition: background-color var(--duration-base) var(--ease-standard);
}
.fl-switch::after {
  content: '';
  position: absolute;
  top: 3px;
  right: 3px;
  width: 16px;
  height: 16px;
  border-radius: 9999px;
  background: var(--color-white);
  box-shadow: var(--shadow-sm);
  transition: transform var(--duration-base) var(--ease-standard);
}
.fl-switch-input:checked + .fl-switch {
  background: var(--color-status-done);
}
.fl-switch-input:checked + .fl-switch::after {
  transform: translateX(-16px);
}
.fl-switch-input:focus-visible + .fl-switch {
  box-shadow: var(--shadow-focus);
}

/* ── Lead-form field chips (hook form) ──────────────────────────────────────────────────────
   Every row of «فیلدهای فعال در فرم» is the SAME chip, whether it can be switched off or not:
   the optional fields toggle, the mobile number and its OTP are locked on. Plain CSS rather
   than a peer-checked utility chain because the checked state has to restyle DESCENDANTS of
   the label (the leading icon and the state icon), which `peer-*` cannot reach.
   The accent is the theme's blue (`--color-status-doing`), darkened one step on light so blue
   label text still clears the contrast bar against the pale blue fill. */
.fl-fieldchip {
  --chip-accent: var(--color-accent-strong);
  display: flex;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--color-border-medium);
  border-radius: var(--radius-xl);
  background: var(--color-bg-secondary);
  color: var(--color-text-secondary);
  font-size: var(--text-xs);
  transition: border-color var(--duration-fast) var(--ease-standard), background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}
.fl-fieldchip-ico {
  flex: none;
  width: 16px;
  text-align: center;
  color: var(--color-text-muted);
}
.fl-fieldchip-label {
  font-weight: 500;
}
.fl-fieldchip-state {
  flex: none;
  margin-inline-start: auto;
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
.fl-fieldchip-on {
  display: none;
}

.fl-fieldchip-wrap {
  position: relative;
  display: block;
  cursor: pointer;
}
/* The real checkbox stays in the DOM (it is what POSTs) but out of sight. Not `display:none`:
   that would drop it out of the focus order and off the keyboard entirely. */
.fl-fieldchip-input {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
}
.fl-fieldchip-wrap:hover .fl-fieldchip {
  border-color: var(--color-border-dark);
}
.fl-fieldchip-input:focus-visible + .fl-fieldchip {
  box-shadow: var(--shadow-focus);
}
.fl-fieldchip-input:checked + .fl-fieldchip,
.fl-fieldchip.is-locked {
  border-color: var(--chip-accent);
  background: var(--color-accent-soft);
  color: var(--chip-accent);
}
.fl-fieldchip-input:checked + .fl-fieldchip .fl-fieldchip-ico,
.fl-fieldchip-input:checked + .fl-fieldchip .fl-fieldchip-state,
.fl-fieldchip.is-locked .fl-fieldchip-ico,
.fl-fieldchip.is-locked .fl-fieldchip-state {
  color: var(--chip-accent);
}
.fl-fieldchip-input:checked + .fl-fieldchip .fl-fieldchip-off {
  display: none;
}
.fl-fieldchip-input:checked + .fl-fieldchip .fl-fieldchip-on {
  display: inline-block;
}

/* ── Tag multi-select (hook form sidebar) ───────────────────────────────────────────────────
   A search-and-pick control over the tag list: the closed control carries the selected tags as
   removable badges, the panel filters as you type. Everything hangs off real checkboxes, so
   the form posts exactly what it posted before.

   PROGRESSIVE ENHANCEMENT: the panel is open and the control hidden until hook-form.js sets
   `data-ready`. If the script never runs the user still sees a plain, usable checkbox list
   instead of an empty box that eats their tags. */
.fl-tagselect {
  position: relative;
}
.fl-tagselect-control {
  display: none;
}
.fl-tagselect[data-ready] .fl-tagselect-control {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px;
  box-sizing: border-box;
  width: 100%;
  min-height: 34px;
  padding: 5px 8px;
  border: 1px solid var(--color-border-medium);
  border-radius: var(--radius-lg);
  background: var(--color-bg-primary);
  cursor: pointer;
}
.fl-tagselect[data-ready][data-open] .fl-tagselect-control,
.fl-tagselect[data-ready] .fl-tagselect-control:focus-visible {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: var(--shadow-focus);
}
/* `display:contents` so each chip is a direct flex item of the control and wraps on its own. */
.fl-tagselect-chips {
  display: contents;
}
.fl-tagselect-placeholder {
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
.fl-tagselect-caret {
  margin-inline-start: auto;
  font-size: var(--text-3xs);
  color: var(--color-text-muted);
  transition: transform 0.15s ease;
}
.fl-tagselect[data-open] .fl-tagselect-caret {
  transform: rotate(180deg);
}

/* The panel is styled off ITSELF, never off an ancestor, because the script hands it to
   `kitLayer` (uikit.js) and it stops being a descendant of .fl-tagselect: it is parked on
   <body>, in the top layer, and positioned against the viewport. That is what stops it being
   painted under the sticky save bar — the sidebar card is a `.glass-panel`, and
   `backdrop-filter` opens a stacking context that no z-index inside it can climb out of.
   `display`, `width`, `left/top` and `max-height` all belong to the layer engine; everything
   below is the panel's SKIN, plus the column layout that lets the engine's max-height shrink
   the list instead of overflowing the box. */
.fl-tagselect-panel[data-kit-layer] {
  display: flex;
  flex-direction: column;
  padding: 6px;
  border: 1px solid var(--color-border-medium);
  border-radius: var(--radius-xl);
  background: var(--color-bg-primary);
  box-shadow: var(--shadow-popover);
}
.fl-tagselect-search {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: 6px;
  padding: 2px 6px 6px;
  border-bottom: 1px solid var(--color-border-light);
  margin-bottom: 4px;
}
.fl-tagselect-search i {
  font-size: var(--text-3xs);
  color: var(--color-text-muted);
}
.fl-tagselect-search input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--color-text-primary);
  font-size: var(--text-2xs);
}
.fl-tagselect-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 0;
  max-height: 216px;
  overflow-y: auto;
}
.fl-tagselect-opt {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  border-radius: var(--radius-md);
  cursor: pointer;
}
/* A utility class sets `display:flex` above, which outranks the UA rule behind [hidden]. */
.fl-tagselect-opt[hidden] {
  display: none;
}
.fl-tagselect-opt:hover {
  background: var(--color-bg-secondary);
}
.fl-tagselect-opt input {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
}
.fl-tagselect-opt input:focus-visible ~ .fl-tag-badge {
  box-shadow: var(--shadow-focus);
}
.fl-tagselect-tick {
  margin-inline-start: auto;
  font-size: var(--text-3xs);
  color: var(--color-accent);
  visibility: hidden;
}
.fl-tagselect-opt input:checked ~ .fl-tagselect-tick {
  visibility: visible;
}
.fl-tagselect-empty {
  display: none;
  padding: 8px 6px;
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
.fl-tagselect-panel[data-no-match] .fl-tagselect-empty {
  display: block;
}
.fl-tagselect-none {
  padding: 8px 6px;
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
.fl-tagselect-none[hidden] {
  display: none;
}
/* «برچسب جدید» — pinned under the scrolling list, so it is always one reach away. */
.fl-tagselect-foot {
  flex-shrink: 0;
  padding: 6px 6px 2px;
  margin-top: 4px;
  border-top: 1px solid var(--color-border-light);
}

/* The badge — the shape the tags already had on this form, kept verbatim so the control, the
   option rows and the chips all read as the same object in three places. */
.fl-tag-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 100%;
  padding: 2px 8px;
  border: 1px solid var(--color-border-medium);
  border-radius: 9999px;
  background: var(--color-bg-primary);
  color: var(--color-text-secondary);
  font-size: var(--text-3xs);
  line-height: 1.7;
}
.fl-tag-badge > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fl-tagselect-chip button {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  font-size: var(--text-3xs);
  line-height: 1;
  cursor: pointer;
}
.fl-tagselect-chip button:hover {
  color: var(--color-danger);
}

/* ── Brand marks that Font Awesome does not carry ───────────────────────────────────────────
   `fl-icon fl-icon--x` is drop-in interchangeable with a Font Awesome class: it is worn by an
   `<i>`, it sizes itself from `font-size` (1em square), and it paints itself in `currentColor`
   — so every place that already writes `<i class="{{icon}}">` and tints it (`.fl-ch-ink`, a
   muted grey on a disabled tile, white inside a filled pill) keeps working untouched. That is
   the whole reason it is a MASK and not an `<img>`: an <img> would ignore the ink around it and
   force a second, colour-aware branch into ten templates.

   Bale is the one that needs it. It was drawn as `fa-comment-dots` — a generic speech balloon
   that says «a messenger», not «Bale» — while the channel it stands for has a real, published
   mark. `public/assets/img/bale.svg` IS that mark: the app-icon half of the official logo at
   bale.ai, single-colour, so tinting it with the brand's own #00b894 reproduces the logo exactly
   rather than approximating it.

   The URL is RELATIVE, deliberately: assets are served from their own origin (see app.ts), and
   a relative url() resolves against THIS stylesheet, so it follows the assets wherever they are
   mounted. A root-absolute `/assets/...` would resolve against the tenant subdomain instead. */
.fl-icon {
  display: inline-block;
  /* The BOX is 1em square so the inline metrics match a Font Awesome glyph exactly; the MARK is
     painted at 0.86em inside it, because an FA glyph does not fill its own em box either and a
     logo that does would sit visibly larger than the icons beside it. */
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
  background-color: currentColor;
  -webkit-mask: var(--fl-icon) center / 0.86em 0.86em no-repeat;
  mask: var(--fl-icon) center / 0.86em 0.86em no-repeat;
}
.fl-icon--bale {
  --fl-icon: url('../img/bale.svg');
}

/* ── Channel identity ─────────────────────────────────────────────────────────────────────────
   Each channel carries its own brand hue on the «کانال‌های ارسال پیام» settings tabs — Bale's own
   #00b894 (lifted from its own logo — see `public/assets/img/bale.svg`), WhatsApp's green, a sky
   blue for email, a violet for the voice call, and the app's own ink for SMS — so the tab is
   recognised before it is read.

   EMAIL IS BLUE, NOT GMAIL RED. In THIS product red is the failure colour — it is what a blocked
   send, a bounced address and a destructive action are all painted in — so an email tile in red
   read as an alert. Sky blue carries no such freight and still reads as "mail".

   The hue is bound to `data-channel` HERE and not emitted from a ViewModel, because it has to
   change with the theme: Bale's green survives a dark background, but SMS's near-black does not
   and has to flip to a light slate. Which theme is on is a CSS fact. Templates stamp
   `data-channel="BALE"` and everything downstream reads var(--ch).

   `--ch-soft` is DERIVED, so a hue is corrected in exactly one place. */
[data-channel] {
  --ch: var(--color-primary);
  --ch-soft: color-mix(in srgb, var(--ch) 12%, transparent);
}
[data-channel='SMS'] {
  --ch: var(--color-channel-sms);
}
[data-channel='BALE'] {
  --ch: var(--color-channel-bale);
}
[data-channel='WHATSAPP'] {
  --ch: var(--color-channel-whatsapp);
}
[data-channel='EMAIL'] {
  --ch: var(--color-channel-email);
}
[data-channel='VOICE'] {
  --ch: var(--color-channel-voice);
}
.fl-ch-ink {
  color: var(--ch);
}
.fl-ch-tint {
  background: var(--ch-soft);
}

/* ── Disclosure (<details>) ──────────────────────────────────────────────────────────────────
   Used by «وضعیت سرویس‌ها» → «جزئیات» to fold the incident history away while nothing is wrong.

   Plain CSS rather than Tailwind's `open:`/`group-open:` variants for one reason: this markup is
   delivered by an htmx swap into an already-rendered page, and the browser Tailwind build only
   scans what it has seen. A variant that happens not to have been emitted yet would leave the
   chevron stuck and both labels showing at once — a state the reader would read as a bug. Nothing
   here is load-bearing for the toggle itself; <details> opens with or without it. */
.fl-disclosure > summary {
  list-style: none;
  cursor: pointer;
  user-select: none;
}
/* Safari/older WebKit draw their own triangle, which ignores `list-style`. */
.fl-disclosure > summary::-webkit-details-marker {
  display: none;
}
.fl-disclosure > summary .fl-disclosure-chevron {
  transition: transform 0.15s ease;
}
.fl-disclosure[open] > summary .fl-disclosure-chevron {
  transform: rotate(180deg);
}
/* One control, two labels: whichever one is not the current action is removed. */
.fl-disclosure-hide {
  display: none;
}
.fl-disclosure[open] > summary .fl-disclosure-show {
  display: none;
}
.fl-disclosure[open] > summary .fl-disclosure-hide {
  display: inline;
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Persian datepicker (public/assets/js/persian-datepicker.js)

   Every value here is a THEME TOKEN from views/shared/layouts/_theme.ejs — no new colour, no
   new radius, no new shadow. The popup is appended to <body> (or to the enclosing <dialog>),
   so it inherits the tokens from :root and follows `.theme-dark` for free.

   Written as plain CSS rather than Tailwind classes because the markup is BUILT IN SCRIPT: the
   browser Tailwind build only emits utilities it has seen in the served HTML, and a class that
   first appears from `innerHTML` after load would simply have no rule behind it.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
.fl-dp-input {
  cursor: pointer;
  /* The value is Persian text now, not an ISO string, so it reads right-to-left like the rest
     of the panel. `text-align: right` is not enough on its own inside `.fbar-ctl--date`, which
     still carries the native control's `dir="ltr"` in some render paths. */
  text-align: right;
}

/* The panel is placed and kept on top by `kitLayer` (uikit.js) — the house rule for anything
   that has to float over the page, because `.glass-panel`'s backdrop-filter opens a stacking
   context AND a fixed-position containing block that no z-index can climb out of.

   So the skin is written on `[data-kit-layer]`, the attribute `attach` stamps: uikit injects its
   own `.kit-layer` reset (transparent, no padding, no border) from a runtime <style> that lands
   AFTER this file, and at equal specificity the later rule would win. The attribute selector is
   one step more specific, which is exactly what the tag-select panel does for the same reason.

   `overflow-y: auto` matters: `place()` clamps the panel's height to the room beside its anchor,
   and a calendar clamped shorter than its grid must scroll rather than spill. */
.fl-dp[data-kit-layer] {
  box-sizing: border-box;
  width: 268px;
  max-width: calc(100vw - 16px);
  padding: 10px;
  direction: rtl;
  overflow-y: auto;
  background: var(--color-bg-primary);
  border: 1px solid var(--color-border-medium);
  border-radius: var(--radius-2xl);
  box-shadow: var(--shadow-popover);
  font-family: var(--font-sans);
  color: var(--color-text-primary);
}

.fl-dp-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-bottom: 8px;
}
.fl-dp-title {
  font-size: var(--text-xs);
  font-weight: 700;
}
.fl-dp-nav {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-lg);
  background: var(--color-bg-secondary);
  color: var(--color-text-secondary);
  font-size: var(--text-3xs);
  cursor: pointer;
}
.fl-dp-nav:hover {
  background: var(--color-bg-tertiary);
  color: var(--color-text-primary);
}

.fl-dp-week,
.fl-dp-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
.fl-dp-week {
  margin-bottom: 4px;
}
.fl-dp-week span {
  text-align: center;
  font-size: var(--text-3xs);
  color: var(--color-text-muted);
  padding: 3px 0;
}

.fl-dp-day {
  height: 30px;
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  background: none;
  color: var(--color-text-secondary);
  font-family: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
}
.fl-dp-day.is-empty {
  cursor: default;
}
.fl-dp-day:hover:not(.is-empty) {
  background: var(--color-bg-secondary);
  color: var(--color-text-primary);
}
/* Today is an outline, the selection is a fill — so «today» never looks like «chosen». */
.fl-dp-day.is-today {
  border-color: var(--color-border-dark);
  font-weight: 700;
}
.fl-dp-day.is-sel {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
  font-weight: 700;
}

.fl-dp-time {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 9px;
  padding-top: 9px;
  border-top: 1px solid var(--color-border-light);
}
.fl-dp-time-lbl {
  flex: 1;
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
.fl-dp-colon {
  color: var(--color-text-muted);
}
.fl-dp-sel {
  height: 30px;
  padding: 0 6px;
  border: 1px solid var(--color-border-medium);
  border-radius: var(--radius-lg);
  background: var(--color-bg-primary);
  color: var(--color-text-primary);
  font-family: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
}

.fl-dp-foot {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 9px;
  padding-top: 9px;
  border-top: 1px solid var(--color-border-light);
}
.fl-dp-act {
  flex: 1;
  height: 30px;
  border: 1px solid var(--color-border-medium);
  border-radius: var(--radius-lg);
  background: var(--color-bg-secondary);
  color: var(--color-text-primary);
  font-family: inherit;
  font-size: var(--text-2xs);
  font-weight: 600;
  cursor: pointer;
}
.fl-dp-act:hover {
  background: var(--color-bg-tertiary);
}
.fl-dp-act--muted {
  color: var(--color-text-muted);
}

/* On a phone the calendar takes the screen minus the margin kitLayer keeps at the rim. */
@media (max-width: 400px) {
  .fl-dp[data-kit-layer] {
    width: calc(100vw - 16px);
  }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   Cover cropper (public/assets/js/uploader.js)

   Theme tokens only — the dialog chrome deliberately matches `.kit-modal` in _theme.ejs: a
   tinted head, a flat body, a tinted foot. Plain CSS for the same reason the datepicker is:
   this markup is built in script, and the browser Tailwind build only emits utilities it has
   already seen in the served HTML.
   ══════════════════════════════════════════════════════════════════════════════════════════ */
/* The one dlg-sm whose buttons must never scroll out of view: «تأیید و آپلود» IS the task. Its
   height cap is 30% above the shared dlg-sm's 70vh (→ 91vh; on a phone the kit's own 90vh). */
.fl-crop-dialog {
  --fl-crop-cap: 91vh;
}
@media (max-width: 640px) {
  .fl-crop-dialog {
    --fl-crop-cap: 90vh;
  }
  /* The kit stretches every phone dialog to its cap (`height: auto` on a fixed, inset-0 box),
     which left this card pinned to the top of the screen. Sized to its content, it centres. */
  .kit-dialog.dlg-sm.fl-crop-dialog[open] {
    height: fit-content !important;
  }
}
.kit-dialog.dlg-sm.fl-crop-dialog[open] {
  max-height: var(--fl-crop-cap);
}
.fl-crop {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: var(--radius-dialog);
  border: 1px solid var(--color-border-light);
  background: var(--color-bg-primary);
  box-shadow: var(--shadow-lg);
}
.fl-crop-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--color-border-light);
}
.fl-crop-title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text-primary);
}
.fl-crop-sub {
  margin-top: 2px;
  font-size: var(--text-2xs);
  line-height: 1.7;
  color: var(--color-text-muted);
}
.fl-crop-x {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--radius-xl);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
}
.fl-crop-x:hover {
  background: var(--color-bg-tertiary);
  color: var(--color-text-primary);
}

/* The frame IS the answer to «at what size is my cover shown?» — it is the landing hero's own
   box, and its aspect ratio is set from the upload policy in script. */
.fl-crop-stage {
  position: relative;
  overflow: hidden;
  /* The frame gives way before the buttons do. It is as wide as the dialog, unless the screen is
     too short for that frame plus the head, the zoom row and the foot (~240px together) — then it
     narrows, keeping its exact aspect ratio, so the whole cropper fits without scrolling. */
  width: min(calc(100% - 36px), calc((var(--fl-crop-cap, 91vh) - 240px) * var(--fl-crop-ar, 1.7778)));
  margin: 14px auto 0;
  border-radius: var(--radius-2xl);
  background: var(--color-bg-secondary);
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.fl-crop-stage:active {
  cursor: grabbing;
}
.fl-crop-stage img {
  position: absolute;
  top: 0;
  right: auto;
  left: auto;
  transform-origin: top left;
  max-width: none;
  /* The image is positioned by an explicit translate in script; `inset-inline` would fight it
     under RTL, so the box is pinned to the physical left edge. */
  inset-inline-start: auto;
  left: 0;
  pointer-events: none;
  -webkit-user-drag: none;
}

.fl-crop-zoom {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px 0;
  color: var(--color-text-muted);
  font-size: var(--text-xs);
}

.fl-crop-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;
  padding: 12px 18px;
  border-top: 1px solid var(--color-border-light);
  background: var(--color-bg-secondary);
}
.fl-crop-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: var(--control-h);
  padding: 0 16px;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
}
.fl-crop-btn--ghost {
  background: var(--color-bg-primary);
  border-color: var(--color-border-medium);
  color: var(--color-text-secondary);
}
.fl-crop-btn--ghost:hover {
  background: var(--color-bg-tertiary);
}
.fl-crop-btn--primary {
  background: var(--color-primary);
  color: var(--color-on-primary);
  box-shadow: var(--shadow-button);
}
.fl-crop-btn--primary:hover {
  background: var(--color-primary-hover);
}

/* ── Questionnaire «نقطهٔ شروع» (hook create form) ───────────────────────────────────────────
   Radio cards: the real radio stays in the DOM (it is what POSTs and what the keyboard reaches);
   the card shows its state. Same accent, border and focus ring as the field chips above, so the
   form keeps one visual language. */
.q-start-opt {
  position: relative;
  display: block;
  cursor: pointer;
}
.q-start-card {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  height: 100%;
  box-sizing: border-box;
  padding: 12px;
  border: 1px solid var(--color-border-medium);
  border-radius: var(--radius-xl);
  background: var(--color-bg-secondary);
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}
.q-start-ico {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-xl);
  background: var(--color-bg-primary);
  color: var(--color-text-muted);
}
.q-start-tick {
  flex: none;
  color: var(--color-accent);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.q-start-opt:hover .q-start-card {
  border-color: var(--color-border-dark);
}
.q-start-opt input:focus-visible + .q-start-card {
  box-shadow: var(--shadow-focus);
}
.q-start-opt input:checked + .q-start-card {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.q-start-opt input:checked + .q-start-card .q-start-ico {
  color: var(--color-accent-strong);
}
.q-start-opt input:checked + .q-start-card .q-start-tick {
  opacity: 1;
}

/* The picked template under «الگوهای آماده»: a quiet tile, red edge when the save was refused for it. */
.q-tpl-picked {
  display: flex;
  align-items: center;
  gap: 12px;
}
.q-tpl-picked[aria-invalid='true'] {
  border-color: var(--color-danger-line);
  background: var(--color-danger-soft);
}

/* ── Hook form: the landing address and its copy button — one LTR line, flush left ─────────── */
.q-landing-line {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 4px;
  min-width: 0;
  margin-top: 4px;
}
.q-landing-url {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-xs);
  line-height: 24px;
  color: var(--color-text-secondary);
  direction: ltr;
  text-align: left;
}
.q-landing-copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  color: var(--color-text-muted);
  font-size: var(--text-2xs);
  transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}
button.q-landing-copy:hover {
  background: var(--color-bg-tertiary);
  color: var(--color-text-primary);
}
.q-landing-copy:focus-visible {
  box-shadow: var(--shadow-focus);
}
.q-landing-copy.is-disabled {
  color: var(--color-text-disabled);
  cursor: not-allowed;
}

/* ── «ورود از فایل JSON»: the three-step strip, the box, the live check ─────────────────────────
   The strip is the whole method in one line, so nobody has to open the guide to know what to do;
   the guide is one tap away at its end. */
/* One column with real gaps between the strip, the field and the check (an alert directly under
   the strip is what made the panel look glued together). The check row disappears when empty. */
.q-json-panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
}
.q-json-panel[hidden] {
  display: none;
}
.q-json-panel > [data-q-check]:not(:has(*)) {
  display: none;
}
.q-ai-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  margin: 0;
  padding: 10px 12px;
  list-style: none;
  border: 1px solid var(--color-accent-line);
  border-radius: var(--radius-2xl);
  background: var(--color-accent-soft);
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}
.q-ai-strip > li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.q-ai-strip .btn-link {
  font-size: inherit;
  font-weight: 500;
}
.q-ai-strip-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 9999px;
  background: var(--color-bg-primary);
  border: 1px solid var(--color-accent-line);
  color: var(--color-accent-strong);
  font-size: var(--text-3xs);
  font-weight: 600;
}
.q-ai-strip-help {
  margin-inline-start: auto;
}
@media (max-width: 639px) {
  .q-ai-strip { flex-direction: column; align-items: flex-start; }
  .q-ai-strip-help { margin-inline-start: 0; }
}
.q-json-area {
  min-height: 150px;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  line-height: 1.7;
}

.q-check {
  margin: 0;
}
.q-check p + p,
.q-check .q-check-sum,
.q-check .q-check-list,
.q-check .q-check-actions,
.q-check .q-check-notes {
  margin-top: 6px;
}
.q-check-where {
  display: inline-block;
  margin-inline-end: 4px;
  padding: 0 6px;
  border-radius: var(--radius-md);
  background: var(--color-bg-primary);
  border: 1px solid var(--color-border-light);
  color: var(--color-text-primary);
  font-size: var(--text-3xs);
  font-weight: 500;
}
.q-check-hint {
  color: var(--color-text-muted);
  font-size: var(--text-2xs);
  overflow-wrap: anywhere;
}
.q-check-sum {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
}
.q-check-sum b {
  color: var(--color-text-primary);
  font-weight: 600;
}
.q-check-sum span {
  color: var(--color-text-muted);
  font-size: var(--text-2xs);
}
.q-check-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.q-check-list > li {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 7px 10px;
  border-radius: var(--radius-lg);
  background: var(--color-bg-primary);
  border: 1px solid var(--color-border-light);
}
.q-check-list > li > i {
  margin-top: 4px;
  font-size: var(--text-2xs);
}
.q-check-list > li.is-error > i { color: var(--color-danger); }
.q-check-list > li.is-warning > i { color: var(--color-warning); }
.q-check-list > li.is-info > i { color: var(--color-accent); }
.q-check-list > li.is-more {
  background: none;
  border: 0;
  padding: 0 2px;
  color: var(--color-text-muted);
  font-size: var(--text-2xs);
}
.q-check-list small {
  display: block;
  margin-top: 2px;
  color: var(--color-text-muted);
  font-size: var(--text-2xs);
  overflow-wrap: anywhere;
}
.q-check-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
.q-check-notes {
  color: var(--color-text-muted);
  font-size: var(--text-2xs);
}
.q-check-more > summary {
  margin-top: 6px;
  cursor: pointer;
  color: var(--color-text-secondary);
  font-weight: 500;
}
.q-check-more[open] > summary {
  margin-bottom: 6px;
}

/* ── «الگوهای آماده» dialog ─────────────────────────────────────────────────────────────────────
   Header: title, search, close. Body: the category rail on the right (RTL start) and the cards or
   one template's details on the left. Footer only in the details view, where the decision is made.
   The modal is a fixed-height column on large screens so switching categories never resizes it. */
.q-tpl-modal {
  height: min(90vh, 46rem);
}
.q-tpl-head {
  align-items: center;
}
.q-tpl-search {
  position: relative;
  flex: 0 1 18rem;
  margin-inline-start: auto;
}
.q-tpl-search > i {
  position: absolute;
  inset-block: 0;
  right: 11px;
  display: flex;
  align-items: center;
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
  pointer-events: none;
}
.q-tpl-search > .input {
  padding-right: 30px;
}
.q-tpl-body {
  display: flex;
  flex: 1;
  min-height: 0;
}
.q-tpl-cats {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: none;
  width: 15rem;
  padding: 12px;
  overflow-y: auto;
  border-left: 1px solid var(--color-border-light);
  background: var(--color-bg-secondary);
}
.q-tpl-cat {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 38px;
  padding: 6px 10px;
  border: 0;
  border-radius: var(--radius-lg);
  background: none;
  font-family: inherit;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--color-text-secondary);
  text-align: start;
  transition: background-color var(--duration-fast) var(--ease-standard), color var(--duration-fast) var(--ease-standard);
}
.q-tpl-cat > i {
  width: 16px;
  text-align: center;
  color: var(--color-text-muted);
}
.q-tpl-cat > span {
  flex: 1;
  min-width: 0;
}
.q-tpl-cat > b {
  font-size: var(--text-3xs);
  font-weight: 500;
  color: var(--color-text-muted);
}
.q-tpl-cat:hover {
  background: var(--color-bg-tertiary);
  color: var(--color-text-primary);
}
.q-tpl-cat.is-active {
  background: var(--color-bg-primary);
  color: var(--color-text-primary);
  font-weight: 600;
  box-shadow: var(--shadow-sm);
}
.q-tpl-cat.is-active > i {
  color: var(--color-text-primary);
}
.q-tpl-main {
  flex: 1;
  min-width: 0;
}
.q-tpl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15.5rem, 1fr));
  gap: 12px;
}
.q-tpl-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px;
  border: 1px solid var(--color-border-light);
  border-radius: var(--radius-card);
  background: var(--color-bg-primary);
  box-shadow: var(--shadow-card);
  font-family: inherit;
  text-align: start;
  transition: border-color var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}
.q-tpl-card:hover {
  border-color: var(--color-border-dark);
  box-shadow: var(--shadow-sm);
}
.q-tpl-card-title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text-primary);
}
.q-tpl-card-desc {
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.q-tpl-card-outcomes {
  font-size: var(--text-2xs);
  color: var(--color-text-secondary);
}
.q-tpl-card-outcomes > span {
  color: var(--color-text-muted);
}
.q-tpl-list-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 10px 16px;
  margin-bottom: 16px;
}
.q-tpl-list-head .section-title {
  display: flex;
  align-items: center;
  gap: 8px;
}
.q-tpl-card-source {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: var(--text-3xs);
  color: var(--color-text-muted);
}
.q-tpl-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px solid var(--color-border-light);
  font-size: var(--text-3xs);
  color: var(--color-text-muted);
}
.q-tpl-card-meta i {
  margin-inline-end: 3px;
}

.q-tpl-detail {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 44rem;
}
.q-tpl-detail[hidden] {
  display: none;
}
.q-tpl-back {
  align-self: flex-start;
  margin-inline-start: -8px;
}
.q-tpl-detail-title {
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--color-text-primary);
}
.q-tpl-detail-desc {
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}
.q-tpl-note {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}
.q-tpl-note > i {
  color: var(--color-text-muted);
}
.q-tpl-h {
  margin-top: 4px;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-text-primary);
}
.q-tpl-outcomes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: 8px;
  margin: -6px 0 0;
  padding: 0;
  list-style: none;
}
.q-tpl-outcomes > li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
}
.q-tpl-outcomes b {
  display: block;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-text-primary);
}
.q-tpl-outcomes span > span {
  display: block;
  margin-top: 2px;
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
.q-tpl-outcome-emoji {
  flex: none;
  font-size: var(--text-md);
  line-height: 1.4;
}
.q-tpl-samples {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: -6px 0 0;
  padding-inline-start: 20px;
  list-style: persian;
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}
.q-tpl-foot {
  gap: 8px;
}
.q-tpl-foot-note {
  margin-inline-end: auto;
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
@media (max-width: 767px) {
  .q-tpl-modal { height: 90vh; }
  .q-tpl-head { flex-wrap: wrap; }
  .q-tpl-search { order: 3; flex-basis: 100%; margin-inline-start: 0; }
  .q-tpl-body { flex-direction: column; }
  .q-tpl-cats {
    flex-direction: row;
    width: auto;
    padding: 8px 12px;
    overflow-x: auto;
    overflow-y: hidden;
    border-left: 0;
    border-bottom: 1px solid var(--color-border-light);
  }
  .q-tpl-cat { flex: none; width: auto; min-height: 32px; white-space: nowrap; }
  .q-tpl-cat > span { flex: none; }
  .q-tpl-foot-note { display: none; }
}

/* ── «ساخت پرسش‌نامه با هوش مصنوعی» dialog ──────────────────────────────────────────────────── */
.q-ai-steps {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}
.q-ai-step {
  position: relative;
  display: flex;
  gap: 14px;
  padding-bottom: 18px;
}
/* The thread between the numbers: the steps read as one path, not five separate notes. */
.q-ai-step:not(:last-child)::before {
  content: '';
  position: absolute;
  top: 30px;
  bottom: 2px;
  right: 13px;
  width: 1px;
  background: var(--color-border-medium);
}
.q-ai-step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 9999px;
  background: var(--color-primary);
  color: var(--color-on-primary);
  font-size: var(--text-xs);
  font-weight: 600;
}
.q-ai-step-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text-primary);
}
.q-ai-step-title > i {
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
.q-ai-step-body {
  margin-top: 2px;
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}
.q-ai-starter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px dashed var(--color-border-dark);
  border-radius: var(--radius-2xl);
  background: var(--color-bg-secondary);
}
.q-ai-starter > p {
  flex: 1 1 16rem;
  font-size: var(--text-xs);
  color: var(--color-text-primary);
}
/* A small picture of what to look for at the end of the chat: a code box with its «Copy» button. */
.q-ai-codeshot {
  margin-top: 8px;
  max-width: 22rem;
  overflow: hidden;
  border: 1px solid var(--color-border-medium);
  border-radius: var(--radius-xl);
  background: var(--color-bg-secondary);
  direction: ltr;
}
.q-ai-codeshot-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 5px 10px;
  border-bottom: 1px solid var(--color-border-light);
  font-family: var(--font-mono);
  font-size: var(--text-3xs);
  color: var(--color-text-muted);
}
.q-ai-codeshot-copy {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 8px;
  border-radius: var(--radius-md);
  background: var(--color-accent-soft);
  border: 1px solid var(--color-accent-line);
  color: var(--color-accent-strong);
  font-family: var(--font-sans);
  font-weight: 600;
}
.q-ai-codeshot pre {
  margin: 0;
  padding: 8px 12px;
  font-family: var(--font-mono);
  font-size: var(--text-3xs);
  line-height: 1.6;
  color: var(--color-text-secondary);
  white-space: pre;
}
.q-ai-tips {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 8px;
  margin-top: 4px;
}
.q-ai-tip {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.q-ai-tip > i {
  margin-top: 4px;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.q-ai-tip p {
  font-size: var(--text-2xs);
  color: var(--color-text-muted);
}
.q-ai-tip .q-ai-tip-title {
  margin-bottom: 2px;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-text-primary);
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
   «ساخت فرایند جدید» — the dialog behind its doors (public/assets/js/automation-wizard.js)

   «از الگوهای آماده» reuses the template-library widget above (.q-tpl-*), the same one as the
   questionnaire library; «با هوش مصنوعی» and the personalization of a template are the forms
   below. Every value is a token; the markup is built in script, so it is plain CSS (the browser
   Tailwind build never sees these classes).
   ══════════════════════════════════════════════════════════════════════════════════════════ */
.kit-dialog.fw-dialog[open] { height: min(88vh, 780px); }
.fw-modal { height: 100%; }
.fw-head { align-items: center; }
.fw-foot { justify-content: space-between; flex-wrap: wrap; }
/* Components that set their own display outrank the UA rule behind [hidden]. */
.fw-foot[hidden], .fw-steps[hidden], .fw-head .q-tpl-search[hidden] { display: none; }

.fw-steps { display: flex; flex: none; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--color-border-light); }
.fw-step { display: flex; flex: 1 1 0; min-width: 0; flex-direction: column; gap: 6px; padding: 0; border: 0; background: none; text-align: start; font-family: inherit; font-size: var(--text-2xs); color: var(--color-text-muted); cursor: pointer; }
.fw-step:disabled { cursor: default; }
.fw-step-bar { height: 4px; border-radius: 9999px; background: var(--color-bg-tertiary); }
.fw-step.is-done .fw-step-bar { background: var(--color-primary); }
.fw-step.is-current .fw-step-bar { background: var(--color-accent); }
.fw-step.is-current { color: var(--color-text-primary); font-weight: 600; }
.fw-step-l { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

.fw-main { display: flex; flex: 1 1 auto; min-height: 0; flex-direction: column; }
.fw-main > .q-tpl-body { flex: 1; min-height: 0; }
.fw-body { flex: 1; min-height: 0; overflow-y: auto; }

.fw-stack { display: flex; flex-direction: column; gap: 22px; }
.fw-stack-sm { display: flex; flex-direction: column; gap: 10px; }
.fw-sec { display: flex; min-width: 0; flex-direction: column; gap: 10px; }
.fw-q { font-size: var(--text-md); font-weight: 600; line-height: 1.55; color: var(--color-text-primary); }
.fw-q-sm { font-size: var(--text-sm); font-weight: 600; line-height: 1.55; color: var(--color-text-primary); }
.fw-why { font-size: var(--text-xs); line-height: 1.7; color: var(--color-text-muted); }
.fw-hr { height: 1px; margin: 0; border: 0; background: var(--color-border-light); }
.fw-grid2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }

/* A template's outline, as a chain of short steps (cards, the personalization's header). */
.fw-mini { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.fw-mini span { padding: 1px 8px; border-radius: 9999px; background: var(--color-bg-tertiary); font-size: var(--text-2xs); line-height: 1.7; color: var(--color-text-secondary); }
.fw-mini i { font-style: normal; font-size: var(--text-2xs); color: var(--color-text-disabled); }
.q-tpl-card .q-tpl-card-meta i { margin-inline-end: 4px; }
/* The chosen template above its personalization. */
.fw-tpl { display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px; background: var(--color-bg-secondary); border: 1px solid var(--color-border-light); border-radius: var(--radius-2xl); }
.fw-tpl > .min-w-0 { display: flex; flex: 1; flex-direction: column; gap: 6px; }

/* Radio list: the goal. */
.fw-rl { display: flex; flex-direction: column; gap: 8px; }
.fw-ri { display: flex; width: 100%; align-items: flex-start; gap: 10px; padding: 10px 12px; text-align: start; font-family: inherit; color: inherit; cursor: pointer; background: var(--color-bg-primary); border: 1px solid var(--color-border-medium); border-radius: var(--radius-xl); }
.fw-ri:hover { border-color: var(--color-border-dark); }
.fw-ri[aria-checked='true'] { border-color: var(--color-accent); background: var(--color-accent-soft); }
.fw-ri:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.fw-ri-dot { display: inline-flex; flex: none; align-items: center; justify-content: center; width: 16px; height: 16px; margin-top: 3px; border-radius: 9999px; border: 1.5px solid var(--color-border-dark); background: var(--color-bg-primary); }
.fw-ri[aria-checked='true'] .fw-ri-dot { border-color: var(--color-accent); }
.fw-ri[aria-checked='true'] .fw-ri-dot::after { content: ''; width: 8px; height: 8px; border-radius: 9999px; background: var(--color-accent); }
.fw-ri-body { display: flex; min-width: 0; flex: 1; flex-direction: column; gap: 2px; }
.fw-ri-t { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: var(--text-sm); font-weight: 500; color: var(--color-text-primary); }
.fw-ri-d { font-size: var(--text-2xs); line-height: 1.6; color: var(--color-text-muted); }

/* A channel chip's state: green tested, blue set up and untested, grey not set up. */
.fw-dot { display: inline-block; flex: none; width: 8px; height: 8px; margin-inline-end: 6px; border-radius: 9999px; background: var(--color-neutral); }
.fw-dot.is-ok { background: var(--color-success); }
.fw-dot.is-untested { background: var(--color-accent); }
.chip[aria-pressed='true'] .fw-dot { box-shadow: 0 0 0 1.5px var(--color-on-primary); }

.fw-switch { display: inline-flex; align-items: flex-start; gap: 10px; padding: 0; text-align: start; font-family: inherit; color: inherit; cursor: pointer; background: none; border: 0; }
.fw-switch:focus-visible { outline: none; box-shadow: var(--shadow-focus); border-radius: var(--radius-lg); }
.fw-switch-track { position: relative; display: inline-block; flex: none; width: 38px; height: 22px; margin-top: 1px; border-radius: 9999px; background: var(--color-border-dark); transition: background-color var(--duration-base) var(--ease-standard); }
.fw-switch-track::after { content: ''; position: absolute; top: 3px; right: 3px; width: 16px; height: 16px; border-radius: 9999px; background: var(--color-white); box-shadow: var(--shadow-sm); transition: transform var(--duration-base) var(--ease-standard); }
.fw-switch[aria-checked='true'] .fw-switch-track { background: var(--color-success); }
.fw-switch[aria-checked='true'] .fw-switch-track::after { transform: translateX(-16px); }
.fw-switch-body { display: flex; flex-direction: column; gap: 2px; }
.fw-switch-t { font-size: var(--text-sm); font-weight: 600; color: var(--color-text-primary); }
.fw-ab-note { padding: 12px 14px; background: var(--color-bg-secondary); border: 1px solid var(--color-border-light); border-radius: var(--radius-2xl); }

/* The description box, its voice typing and the checklist of what a good one says. */
.fw-ta { position: relative; }
.fw-ta textarea.input { padding-bottom: 48px; line-height: 1.9; }
.fw-ta-tools { position: absolute; bottom: 8px; inset-inline-end: 8px; display: flex; align-items: center; gap: 8px; }
.fw-listening { border-color: var(--color-accent-line); background: var(--color-accent-soft); color: var(--color-accent-strong); }
.fw-ta textarea.input[readonly] { border-color: var(--color-accent-line); }
.fw-voice-note { min-width: 0; font-size: var(--text-2xs); line-height: 1.6; color: var(--color-text-muted); text-align: end; }
.fw-checks { display: flex; flex-direction: column; gap: 8px; }
.fw-check-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 16px; margin: 0; padding: 0; list-style: none; }
.fw-check-list li { position: relative; display: flex; flex-direction: column; gap: 1px; padding-inline-start: 18px; }
.fw-check-list li::before { content: ''; position: absolute; top: 6px; inset-inline-start: 0; width: 10px; height: 10px; border-radius: 9999px; border: 1.5px solid var(--color-border-dark); }
.fw-check-list li.is-ok::before { background: var(--color-success); border-color: var(--color-success); }
.fw-check-list b { font-size: var(--text-xs); font-weight: 600; color: var(--color-text-primary); }
.fw-check-list li.is-ok b { color: var(--color-success-strong); }
.fw-check-list span { font-size: var(--text-2xs); line-height: 1.6; color: var(--color-text-muted); }
.fw-ex-h { font-size: var(--text-2xs); font-weight: 500; color: var(--color-text-muted); }
.fw-ex { display: flex; width: 100%; align-items: flex-start; justify-content: space-between; gap: 10px; padding: 8px 10px; text-align: start; font-family: inherit; font-size: var(--text-xs); line-height: 1.7; color: var(--color-text-secondary); cursor: pointer; background: var(--color-bg-secondary); border: 1px dashed var(--color-border-medium); border-radius: var(--radius-xl); }
.fw-ex:hover { border-color: var(--color-border-dark); color: var(--color-text-primary); }
.fw-ex b { flex: none; font-size: var(--text-2xs); font-weight: 600; color: var(--color-accent-strong); }

/* A template's path in its details: numbered steps on a line. */
.fw-ladder { display: flex; flex-direction: column; margin: -6px 0 0; padding: 0; list-style: none; }
.fw-ladder li { position: relative; display: flex; min-height: 40px; align-items: center; gap: 10px; }
.fw-ladder li:not(:last-child)::before { content: ''; position: absolute; top: 30px; bottom: -10px; inset-inline-start: 11px; width: 2px; background: var(--color-border-light); }
.fw-lad-n { position: relative; z-index: 1; display: inline-flex; flex: none; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 9999px; border: 1px solid var(--color-border-medium); background: var(--color-bg-primary); font-size: var(--text-2xs); font-weight: 600; color: var(--color-text-secondary); }
.fw-lad-t { min-width: 0; flex: 1; font-size: var(--text-sm); }

details.fw-more { border: 1px solid var(--color-border-light); border-radius: var(--radius-2xl); }
details.fw-more > summary { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 14px; list-style: none; cursor: pointer; font-size: var(--text-sm); font-weight: 600; color: var(--color-text-primary); }
details.fw-more > summary::-webkit-details-marker { display: none; }
details.fw-more > summary .fw-chev { font-size: var(--text-2xs); color: var(--color-text-muted); transition: transform var(--duration-base) var(--ease-standard); }
details.fw-more[open] > summary .fw-chev { transform: rotate(-90deg); }
.fw-more-b { display: flex; flex-direction: column; gap: 12px; padding: 0 14px 14px; }
.fw-qc { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; align-items: end; gap: 8px; padding: 12px; background: var(--color-bg-secondary); border: 1px solid var(--color-border-light); border-radius: var(--radius-xl); }
.fw-qc .field-error { grid-column: 1 / -1; }
.fw-src { display: flex; align-items: center; gap: 8px; padding: 8px 10px; font-size: var(--text-xs); background: var(--color-bg-secondary); border: 1px solid var(--color-border-light); border-radius: var(--radius-xl); }
.fw-src > i { color: var(--color-text-muted); }
.fw-drop { min-height: 56px; padding: 12px; font-size: var(--text-xs); color: var(--color-text-secondary); }
.fw-mono { font-family: var(--font-mono); font-size: var(--text-xs); }
.fw-clar { display: flex; flex-direction: column; gap: 8px; }

.fw-done { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 24px 8px; text-align: center; }
.fw-done > .alert, .fw-done > .tile { width: 100%; max-width: 34rem; text-align: start; }
.fw-done-title { font-size: var(--text-lg); font-weight: 600; color: var(--color-text-primary); }
.fw-done-text { max-width: 46ch; font-size: var(--text-xs); line-height: 1.8; color: var(--color-text-secondary); }
.fw-done-list { margin: 4px 0 0; padding-inline-start: 18px; list-style: disc; font-size: var(--text-xs); line-height: 1.9; color: var(--color-text-secondary); }
.fw-spin { width: 28px; height: 28px; border-width: 3px; color: var(--color-accent); }

/* On a phone the library's search takes its own row (.q-tpl-head); the title shares the first with «×». */
@media (max-width: 767px) { .fw-head > .min-w-0 { flex: 1 1 0; } }
@media (max-width: 640px) {
  .kit-dialog.fw-dialog[open] { height: 92vh; }
  .fw-steps { padding-inline: 16px; }
  .fw-grid2, .fw-qc, .fw-check-list { grid-template-columns: minmax(0, 1fr); }
  .fw-tpl { flex-wrap: wrap; }
}
