/* ═══ Core Components — Daftarche Design System (Priority 8.2) ═══
   Contract layer on top of the 8.1 token foundation (base.css).
   Components: Button · Icon Button · Input · Chip · Card · Task Card ·
   Progress · Status/Badge · Modal · Bottom Sheet · Toast · Empty State ·
   Navigation · Character Slot
   All interactive components inherit :focus-visible / reduced-motion /
   RTL from base.css. */

/* ═══ 1. Button ═══
   Variants: primary · secondary · ghost · destructive (· icon = 2)
   States: default · hover · pressed · focus-visible · disabled (· loading · success) */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
  font-family:var(--font-ui);font-weight:600;line-height:1.4;min-height:44px;
  padding:10px var(--space-4);border-radius:var(--radius-sm);cursor:pointer;
  border:none;background:transparent;color:var(--color-text-primary);
  transition:transform var(--duration-fast) var(--ease-spring),box-shadow var(--duration-fast),background var(--duration-fast),color var(--duration-fast),border-color var(--duration-fast),opacity var(--duration-fast);
  -webkit-tap-highlight-color:transparent}
.btn:active{transform:translateY(1px) scale(.99)}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.45;cursor:not-allowed}
.btn:disabled:active{transform:none}

/* Hover: a barely-there lift — enhancement only, never essential feedback */
.btn-primary:hover{transform:translateY(-1px)}
.btn-primary:active{transform:translateY(2px);box-shadow:0 1px 0 0 rgba(120,55,20,.35)}

.btn-secondary{background:transparent;color:var(--color-accent);border:1.5px solid var(--color-border-strong)}
.btn-secondary:hover{border-color:var(--color-accent);transform:translateY(-1px)}

.btn-ghost{background:transparent;color:var(--color-text-secondary);border:none}
.btn-ghost:hover{color:var(--color-accent);background:var(--color-accent-soft)}

.btn-destructive{background:var(--color-danger);color:var(--color-text-on-accent)}
.btn-destructive:hover{transform:translateY(-1px);box-shadow:0 3px 12px -4px var(--color-danger)}

/* Success state — 3D shadow dropped, spring-in, real data flowing */
.btn-success{background:var(--color-success);color:var(--color-text-on-accent)}
.btn-success:active{transform:scale(.97)}

/* Loading state — spinner glyph via ::before */
.btn.loading{pointer-events:none;color:transparent!important;position:relative}
.btn.loading::before{content:'';position:absolute;width:18px;height:18px;border-radius:50%;
  border:2.5px solid color-mix(in srgb,var(--color-text-on-accent) 35%,transparent);
  border-top-color:var(--color-text-on-accent);animation:spin var(--duration-base) linear infinite}
.btn-ghost.loading::before,.btn-secondary.loading::before{border-color:color-mix(in srgb,var(--color-accent) 30%,transparent);border-top-color:var(--color-accent)}

/* ═══ 2. Icon Button ═══
   Square hit-area ≥ 40px (44 on coarse pointers), inline SVG inherits color. */
.btn-icon{width:44px;height:44px;padding:0;border-radius:var(--radius-sm);display:grid;place-items:center;flex-shrink:0}
.btn-icon:active{transform:scale(.94)}
.btn-icon.btn-destructive:hover{color:var(--color-danger);background:color-mix(in srgb,var(--color-danger) 12%,transparent);box-shadow:none;transform:scale(1.1)}
@media (hover:none){.btn-icon{width:44px;height:44px}}
@media (pointer:coarse){.btn-icon{width:44px;height:44px}}

/* ═══ 3. Input ═══
   Label → Control → Helper/Error. Visually lighter than a Card. */
.field{display:flex;flex-direction:column;gap:var(--space-1);min-width:0}
.field-label{font-size:12px;font-weight:600;color:var(--color-text-secondary)}
.field-control{width:100%;font-family:var(--font-ui);font-size:14px;font-weight:400;color:var(--color-text-primary);
  background:transparent;border:1.5px solid var(--color-border-strong);border-radius:var(--radius-sm);
  padding:10px var(--space-3);outline:none;min-height:44px;min-width:0;
  transition:border-color var(--duration-fast),box-shadow var(--duration-fast),background var(--duration-fast)}
.field-control::placeholder{color:var(--color-text-muted)}
.field-control:focus{border-color:var(--color-accent);box-shadow:0 0 0 4px var(--color-accent-soft)}
.field-control:disabled{opacity:.45;cursor:not-allowed;background:color-mix(in srgb,var(--color-border) 40%,transparent)}
.field-control:read-only{background:color-mix(in srgb,var(--color-border) 30%,transparent)}
.field[data-error] .field-control{border-color:var(--color-danger)}
.field[data-error] .field-control:focus{box-shadow:0 0 0 4px color-mix(in srgb,var(--color-danger) 15%,transparent)}
.field-helper{font-size:11px;color:var(--color-text-muted);line-height:1.6}
.field[data-error] .field-helper{color:var(--color-danger);font-weight:500}

/* ═══ 4. Chip ═══
   Roles: neutral (metadata) · semantic (status-tinted) · interactive (clickable filter) */
.chip{display:inline-flex;align-items:center;gap:5px;font-family:var(--font-ui);font-size:11px;font-weight:500;
  line-height:1.4;padding:3px 10px;border-radius:var(--radius-pill);white-space:nowrap;
  border:1px solid var(--color-border);background:transparent;color:var(--color-text-muted)}
.chip-interactive{cursor:pointer;transition:transform var(--duration-fast),border-color var(--duration-fast),background var(--duration-fast),box-shadow var(--duration-fast)}
.chip-interactive:hover{border-color:var(--color-text-muted)}
.chip-interactive:active{transform:scale(.93)}
.chip-interactive[aria-pressed="true"],.chip-interactive.sel{background:var(--color-accent);border-color:var(--color-accent);color:var(--color-text-on-accent);font-weight:600}
.chip .dot{width:7px;height:7px;border-radius:50%;background:var(--cc,var(--color-text-muted));flex-shrink:0}

/* ═══ 5. Card ═══
   Content (flat, recessed on bg) / Featured (raised surface) / Floating (overlays). */
.card{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-1)}
.card-content{background:var(--color-bg);box-shadow:var(--shadow-0)}
.card-featured{border-radius:var(--radius-xl);box-shadow:var(--shadow-1)}
.card-floating{box-shadow:var(--shadow-2);border-radius:var(--radius-xl)}

/* ═══ 6. Task Card ═══
   Hierarchy: priority stripe → checkbox → title → metadata → secondary actions.
   Completed = reduced weight, still readable. (Structure lives in tasks.css; the
   contract here keeps the visual hierarchy contract explicit.) */
.task .title{font-weight:500}
.task.done{opacity:.6}
.task.done .title{color:var(--color-text-muted);font-weight:400}

/* ═══ 7. Progress ═══
   Bar: `.progress-bar > i` · Ring: `.progress-ring` (conic, --pct in %).
   Decorative gradients are out of the contract. */
.progress-bar{height:10px;border-radius:var(--radius-pill);background:color-mix(in srgb,var(--color-text-primary) 9%,transparent);overflow:hidden}
.progress-bar i{display:block;height:100%;width:0;border-radius:var(--radius-pill);background:var(--color-accent);transition:width var(--duration-slow) var(--ease-out)}
.progress-ring{position:relative;display:grid;place-items:center;border-radius:50%;
  background:conic-gradient(var(--color-accent) calc(var(--pct,0) * 1%),var(--color-accent-soft) 0)}

/* ═══ 8. Status / Badge ═══
   Neutral · Success · Warning · Danger · Focus — meaning never color-only (icons/text live in markup). */
.badge-status{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:600;
  padding:2px 9px;border-radius:var(--radius-pill);line-height:1.5;white-space:nowrap}
.badge-status.neutral{color:var(--color-text-secondary);background:color-mix(in srgb,var(--color-border-strong) 40%,transparent)}
.badge-status.success{color:var(--color-success);background:color-mix(in srgb,var(--color-success) 14%,transparent)}
.badge-status.warning{color:var(--color-warning);background:color-mix(in srgb,var(--color-warning) 16%,transparent)}
.badge-status.danger{color:var(--color-danger);background:color-mix(in srgb,var(--color-danger) 12%,transparent)}
.badge-status.focus{color:var(--color-accent);background:var(--color-accent-soft)}
html[data-mode="dark"] .badge-status.warning{color:#ffcd8a}

/* ═══ 9. Modal ═══
   Pattern only — existing overlay markup/behavior unchanged. */
.modal-panel{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-xl);
  box-shadow:var(--shadow-2);width:100%;max-width:360px}

/* ═══ 10. Bottom Sheet ═══
   Pattern only — behavior (drag, swipe, focus trap) stays in existing code. */
.bottom-sheet{position:fixed;bottom:0;left:0;right:0;z-index:95;background:var(--color-surface);
  border-radius:var(--radius-xl) var(--radius-xl) 0 0;box-shadow:var(--shadow-2);
  max-height:70vh;display:flex;flex-direction:column;padding-bottom:env(safe-area-inset-bottom,0)}

/* ═══ 11. Toast ═══
   Non-blocking, short, with optional action + auto-dismiss progress bar. */
.toast{position:fixed;bottom:calc(var(--nav-height) + env(safe-area-inset-bottom,0) + 16px);
  inset-inline-start:50%;transform:translateX(50%);z-index:99;
  display:flex;align-items:center;gap:var(--space-3);
  background:var(--color-text-primary);color:var(--color-bg);
  font-family:var(--font-ui);font-size:13px;font-weight:500;
  padding:10px 12px;padding-inline-end:12px;border-radius:var(--radius-md);
  box-shadow:var(--shadow-2);overflow:hidden;max-width:calc(100vw - 32px)}
html[data-mode="dark"] .toast{background:var(--color-surface-elevated);color:var(--color-text-primary);border:1px solid var(--color-border-strong)}
.toast[hidden]{display:none}
.toast.show{animation:toastIn var(--duration-base) var(--ease-spring)}
/* The inherited entrance slides sideways by 50% of the box, which only made
   sense for the old inset-inline-start:50% centring. This note rises instead. */
.toast-sticky.show{animation:toastStickyIn var(--duration-base) var(--ease-spring)}
@keyframes toastStickyIn{from{opacity:0;transform:translateY(16px)}}
@keyframes toastIn{from{opacity:0;transform:translateX(50%) translateY(16px)}}
.toast-btn{border:none;background:transparent;color:var(--color-accent);font-family:var(--font-ui);
  font-size:13px;font-weight:700;cursor:pointer;padding:2px 4px;flex-shrink:0}
html[data-mode="dark"] .toast-btn{color:var(--color-accent)}
.toast-bar{position:absolute;bottom:0;inset-inline-start:0;height:3px;width:100%;
  background:var(--color-accent);transform-origin:right;animation:toastBar 5s linear forwards}
.toast-bar[dir="ltr"],[dir="ltr"] .toast-bar{transform-origin:left}
@keyframes toastBar{from{transform:scaleX(1)}to{transform:scaleX(0)}}

/* Sticky variant: no auto-dismiss bar, stays until the user acts.
   The split is by lifetime, not by subject, and it is the whole rule:
     «.that goes away on its own» -> dark bar above («پاک شد», four seconds, gone)
     «.that waits for you»      -> this slip of paper (the update prompt, and
                                        the storage warning — both need an answer)
   Dressing up the blips would make every deletion feel like an event; leaving
   the two notes as dark bars would make them look like the app shouting.

   The paper, the ruled pitch, the ink and the tape all come from .sheet-paper
   (css/base.css) — the one declaration the two calendars and the birthday greeting
   read too — so what is left below is the note's own: where it sits, how it
   centres, and what its line and button look like.

   position:fixed, not relative: .toast pins itself to the bottom of the screen,
   and .toast-sticky comes later at equal weight, so any other value here would
   silently turn a note the reader must answer into one that scrolls off with
   the page.

   The centring is restated too. .toast centres with inset-inline-start:50% plus a
   +50% X translate, which leaves only half the viewport as the box's available
   width; a shrink-to-fit box therefore clamps itself to that half. Measured at a
   320px viewport: a 152px note with «نسخهٔ» on one line and «جدید» on the next. Two insets plus an auto
   margin centre it properly and hand it the full width, and width:fit-content
   keeps it from stretching across the screen. The entrance animation is restated
   for the same reason — the inherited keyframe translates by 50% of the box, which
   would drag the note sideways on show. */
.toast-sticky{position:fixed;inset-inline:16px;width:fit-content;max-width:calc(100vw - 32px);
  margin-inline:auto;transform:none;font-size:14px;gap:var(--space-3);
  padding:20px 16px 16px;border-radius:var(--radius-md);
  /* .toast clips with overflow:hidden so its progress bar cannot poke out of its
     rounded corner, and .sheet-paper's visible cannot outrank it on order — both are
     bare classes — so the opt-out has to be said here. Measured without it: the tape
     is simply gone and the note reads as a plain rectangle, which is the look this
     was meant to replace. */
  overflow:visible;
  /* Same reason as #nameCard and .cal-card: .toast (line 130) carries --shadow-2
     and is later in the cascade, so the recipe's own box-shadow:none loses to it. */
  box-shadow:none}
/* The tape is .sheet-paper's, not ours — this note only says where it hangs. Same token,
   same size, same tilt; it sits further in because its paper is a narrow slip
   rather than a card, and 20px on a 250px sheet is closer to the corner than it
   looks on a 372px calendar. */
.toast-sticky{--sheet-tape-inset:16px}
/* The dark-mode rule above repaints .toast-sticky as a raised surface, so this
   has to outrank it rather than lose on order: html[data-mode] adds an attribute
   selector and therefore specificity. One line, because the recipe is a token. */
html[data-mode="dark"] .toast-sticky{background:var(--sheet-paper);
  color:var(--color-text-primary);border:1px solid var(--color-border);box-shadow:none}
/* The line and the action share one row, both on the ruled 22px pitch of
   .sheet-paper, so the text sits on a rule the way it would on paper. align-items:stretch would make
   the text block as tall as the button and drift it off the line, so they are
   centred on each other instead and the line-height does the aligning. */
.toast-sticky .toast-text{line-height:1.7;font-weight:500;align-self:center;
  /* min-width:0 so the text column is allowed to be narrower than its content.
     Without it a flex item refuses to shrink below the longest word, which at
     narrow widths means the button never gets a fair share of the row. */
  flex:1 1 150px;min-width:0}
.toast-sticky .toast-btn{padding:10px 16px;min-height:44px;display:inline-flex;align-items:center;
  border-radius:var(--radius-pill);background:var(--color-accent);
  /* Not a literal #fff: that was unreadable in seven of the nine palettes. The
     token exists for exactly this and every palette declares its own value. */
  color:var(--color-text-on-accent);
  transition:transform var(--duration-fast) var(--ease-out)}
html[data-mode="dark"] .toast-sticky .toast-btn{color:var(--color-text-on-accent)}
/* Below this the row cannot hold both parts side by side, and flex will not
   break it on its own: measured at a 320px viewport, the note collapsed to 163px
   and set «نسخهٔ جدید اومده!» one word to a line. Rather than let the sentence
   shatter, the note stacks — the line on the ruled paper, the pill underneath it.
   The threshold is the note's own measured two-column width (163 + 100 pill +
   gap + 32 padding) plus a little slack. */
@media (max-width:400px){
  .toast-sticky{flex-wrap:wrap;row-gap:var(--space-2);padding:20px 14px 14px}
  .toast-sticky .toast-text{flex:1 1 100%;align-self:start}
  .toast-sticky .toast-btn{align-self:flex-start}
}
@media (hover:hover){
  .toast-sticky .toast-btn:hover{transform:translateY(-1px)}
  .toast-sticky .toast-btn:active{transform:translateY(1px)}
}
@media (prefers-reduced-motion: reduce){
  .toast-sticky,.toast-sticky .toast-btn{transition:none}
  .toast-sticky .toast-btn:hover,.toast-sticky .toast-btn:active{transform:none}
}

/* ═══ 12. Empty State ═══
   Context + Character + Next Step. Never generic text. */
.empty-state{display:flex;flex-direction:column;align-items:center;gap:var(--space-2);
  text-align:center;padding:var(--space-8) var(--space-4);color:var(--color-text-muted)}
.empty-state-char{width:96px;height:96px;flex-shrink:0}
.empty-state-char svg{width:100%;height:100%}
.empty-state-title{font-family:var(--font-personality);font-size:16px;font-weight:700;color:var(--color-text-secondary);line-height:1.7}
.empty-state-hint{font-size:13px;line-height:1.9;max-width:300px;margin:0}

/* ═══ 13. Navigation ═══
   Bottom dock — architecture/glider unchanged; states standardized on tokens. */
.nav-tab:disabled,.nav-tab[aria-disabled="true"]{opacity:.4;cursor:not-allowed}

/* ═══ 14. Character Slot ═══
   Visual abstraction for placing a character in a context. Palettes stay scoped. */
.char-slot{display:grid;place-items:center;flex-shrink:0}
.char-slot svg{display:block;max-width:100%;max-height:100%}
.char-slot-hero{width:104px;height:104px}
.char-slot-avatar{width:48px;height:48px;border-radius:var(--radius-sm)}
.char-slot-empty{width:96px;height:96px}
.char-slot-emotional{width:64px;height:64px}
@media (max-width:480px){
  .char-slot-hero{width:90px;height:90px}
  .char-slot-empty{width:84px;height:84px}
}

/* ═══ Responsive ═══ */
/* ═══ 15. Picker Row ═══
   One row of small choices, one tab stop, arrow keys — the control behind the
   theme picker in settings and the share card's template picker
   (js/chipgroup.js builds both). A chip can wear a palette (data-palette) and
   shows it in its dot: base.css fills --pick-dot-* from the palette table, and
   without one the dot falls back to the interface's own colours — which is what
   the card's «follow the app» chip is meant to show. */
.pick-field{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.pick-label{flex:0 0 auto;font-size:11.5px;font-weight:600;color:var(--color-text-muted)}
.pick-row{display:flex;flex:1;flex-wrap:wrap;gap:6px;min-width:0}
.pick-chip{display:inline-flex;align-items:center;gap:6px;min-height:34px;padding:7px 11px;
  border:1.5px solid var(--color-border);border-radius:var(--radius-pill);background:transparent;
  color:var(--color-text-muted);font-family:var(--font-ui);font-size:12px;font-weight:600;cursor:pointer;
  transition:color var(--duration-base),border-color var(--duration-base),background var(--duration-base)}
.pick-chip.sel{color:var(--color-accent);border-color:var(--color-accent);background:var(--color-accent-soft)}
.pick-chip:focus-visible{outline:2px solid var(--color-focus);outline-offset:2px}
.pick-dot{flex:0 0 auto;width:13px;height:13px;border-radius:50%;
  background:var(--pick-dot-bg,var(--color-bg));box-shadow:inset 0 0 0 3px var(--pick-dot-ring,var(--color-accent))}
/* The stacked form of a field: the label, and the end of its line for a counter
   the row itself cannot show — «۳ از ۳» is easier to read there than a fourth
   disabled chip would be. */
.pick-head{display:flex;align-items:center;gap:8px}
.pick-count{font-size:10.5px;font-weight:700;line-height:1.7;padding:1px 8px;border-radius:var(--radius-pill);
  color:var(--text);background:var(--accent-soft)}
.pick-count:empty{display:none}
/* The checkbox of a row that holds several answers. Unpicked it is an empty
   square; picked it holds the number of the place that answer takes in what the
   row builds — the one place a picker can show order rather than just state. */
.pick-box{flex:0 0 auto;display:grid;place-items:center;width:18px;height:18px;border-radius:6px;
  border:1.5px solid var(--color-border);background:transparent;font-size:10.5px;font-weight:700;line-height:1}
.pick-chip.sel .pick-box{border-color:var(--color-accent);background:var(--accent-soft);color:var(--text)}
@media (prefers-reduced-motion:reduce){.pick-chip{transition:none}}

/* ═══ 16. Segmented Control ═══
   Two exclusive answers, exactly one always on — the mode switch above the
   theme swatches, and the card layout row in the share panel. A filled track
   with the chosen half lifted out of it, so the state is read before it is
   read as a word. */
.seg{display:inline-flex;align-items:center;gap:2px;padding:3px;border:1.5px solid var(--color-border);
  border-radius:var(--radius-pill);background:var(--color-bg)}
.seg-btn{appearance:none;border:none;background:transparent;color:var(--color-text-muted);
  font-family:var(--font-ui);font-size:12.5px;font-weight:600;line-height:1;cursor:pointer;
  padding:0 15px;min-height:38px;border-radius:var(--radius-pill);
  transition:color var(--duration-base),background var(--duration-base),box-shadow var(--duration-base)}
.seg-btn.sel{background:var(--color-accent-soft);color:var(--color-text-primary);
  box-shadow:inset 0 0 0 1.5px var(--color-accent)}
.seg-btn:focus-visible{outline:2px solid var(--color-focus);outline-offset:2px}
/* A finger deserves the full target even though the control reads small. */
@media (pointer:coarse){.seg-btn{min-height:44px}}

/* ═══ 17. Swatch Row ═══
   The theme picker's second half: the themes of one half of the table, each a
   miniature of the interface it would paint. The three bands come from the
   palette the chip wears (data-palette), never from a colour written here — so a
   swatch is a promise the CSS keeps, not a picture that can drift from it.

   Two columns, not four: with four themes in a half, four columns on a 320px
   phone would squeeze each swatch to 63px and the name inside it to nothing. A
   2×2 grid keeps every swatch wide enough to read its own preview at a glance.

   All eight themes are in the DOM; the four of the other half are hidden by the
   rule at the bottom, keyed on the host's data-mode. */
.swatch-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;width:100%}
.swatch{display:flex;flex-direction:column;align-items:stretch;gap:6px;min-width:0;padding:8px;
  border:1.5px solid var(--color-border);border-radius:var(--radius-md);background:transparent;
  font-family:var(--font-ui);cursor:pointer;
  transition:border-color var(--duration-base),background var(--duration-base),transform var(--duration-fast)}
.swatch.sel{border-color:var(--color-accent);background:var(--color-accent-soft)}
.swatch.sel .swatch-name{color:var(--color-text-primary)}
.swatch:active{transform:scale(.98)}
.swatch:focus-visible{outline:2px solid var(--color-focus);outline-offset:2px}
.swatch-preview{display:grid;grid-template-columns:1.5fr 1fr .8fr;height:34px;border-radius:var(--radius-xs);
  overflow:hidden;box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-text-primary) 14%,transparent)}
.swatch-preview i{display:block}
.swatch-preview i:nth-child(1){background:var(--card-bg,var(--color-bg))}
.swatch-preview i:nth-child(2){background:var(--card-surface,var(--color-surface))}
.swatch-preview i:nth-child(3){background:var(--card-accent,var(--color-accent))}
.swatch-name{font-size:11.5px;font-weight:600;line-height:1.5;text-align:center;color:var(--color-text-secondary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* One half at a time: the host says which, the CSS hides the rest. */
.swatch-row[data-mode="light"] .swatch[data-mode="dark"],
.swatch-row[data-mode="dark"] .swatch[data-mode="light"]{display:none}
/* The same rule for a plain chip row that wears modes — the share card offers
   the palettes of the half the app is in. A chip with no mode (its «برنامه»)
   belongs to neither half and is never hidden by this. */
.pick-row[data-mode="light"] .pick-chip[data-mode="dark"],
.pick-row[data-mode="dark"] .pick-chip[data-mode="light"]{display:none}
@media (prefers-reduced-motion:reduce){
  .seg-btn,.swatch{transition:none}
  .swatch:active{transform:none}
}
@media (max-width:360px){
  .swatch{padding:6px;gap:5px}
  .swatch-preview{height:30px}
  .swatch-name{font-size:11px}
  .seg-btn{padding:0 11px}
}

@media (max-width:480px){
  .btn{min-height:44px;padding:9px var(--space-3)}
  .toast{bottom:calc(var(--nav-height) + env(safe-area-inset-bottom,0) + 12px)}
}
