/*
  Centralized design system (phase 2, brief section 20).
  Every color in every template must come from these tokens -- nothing
  hard-codes a hex value in a template again. B2B industrial direction:
  trustworthy, professional, not a colorful consumer marketplace.

  L-11.2: --color-primary migrated from the original dark teal (#164E63)
  to the official brand red (#E11D2E). --color-primary-hover is the same
  ~19% darkening ratio the teal/teal-hover pair already used (#164E63 ->
  #123f50), applied to the new value, not an unrelated hue picked by eye.
  --color-danger is DELIBERATELY untouched (#B91C1C) and stays visually
  distinct from the new primary red -- brand-red is for CTAs/navigation/
  selection, danger-red is exclusively for destructive actions/errors;
  conflating the two was the one explicit risk this migration was asked
  to avoid. --color-active-bg (the soft selected/active background,
  previously #ECFEFF -- a pale cyan matching the old teal primary) moves
  to a soft rose (#FCE4E6) -- visibly more saturated than --color-danger's
  own soft background (#FEF2F2, .messages .error/.badge-rejected etc.,
  unchanged) specifically so a selected/active state can never be
  mistaken for an error state at a glance, even though both are now "a
  pale red."
*/

:root {
    --color-primary: #E11D2E;
    --color-primary-hover: #B61725;
    --color-accent: #D97706;
    --color-success: #15803D;
    --color-warning: #CA8A04;
    --color-danger: #B91C1C;
    /* L-11.2: the soft/tint backgrounds paired with warning/danger above
       already existed as repeated literal hex values across badges and
       status messages (#FFFBEB, #FEF2F2) -- tokenized here, values
       unchanged, so every existing usage stays pixel-identical while
       becoming update-safe in one place instead of N hardcoded spots. */
    --color-warning-soft: #FFFBEB;
    --color-danger-soft: #FEF2F2;
    --color-bg: #F8FAFC;
    --color-surface: #FFFFFF;
    --color-border: #E2E8F0;
    --color-text: #0F172A;
    --color-text-muted: #64748B;

    --radius-input: 8px;
    --radius-card: 14px;
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-6: 24px;
    --space-8: 32px;
    --touch-target-min: 44px;
}

* { box-sizing: border-box; }

body {
    font-family: 'Vazirmatn', Tahoma, sans-serif;
    background: var(--color-bg);
    color: var(--color-text);
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* --- Layout shells ---------------------------------------------------- */
header.site-header {
    height: var(--topbar-height, 60px);
    padding: 0 var(--space-6);
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface);
    display: flex;
    align-items: center;
    gap: var(--space-4);
    position: sticky;
    top: 0;
    z-index: 40;
}
.site-brand {
    display: flex; align-items: center; gap: 8px;
    font-weight: 700; font-size: 15px; color: var(--color-primary);
    text-decoration: none; flex-shrink: 0;
}
.site-brand svg { width: 20px; height: 20px; flex-shrink: 0; }
.site-brand-logo { width: 28px; height: 28px; flex-shrink: 0; display: block; }
.site-nav { display: flex; align-items: center; gap: var(--space-4); flex: 1; min-width: 0; }
.site-nav-link { color: var(--color-text); text-decoration: none; font-size: 14px; white-space: nowrap; }
.site-nav-link:hover { color: var(--color-primary); }
.site-header-actions { display: flex; align-items: center; gap: var(--space-3); flex-shrink: 0; }
.site-nav-toggle { display: none; cursor: pointer; color: var(--color-text); align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: var(--radius-sm); }
.site-nav-toggle:hover { background: var(--color-surface-hover); }
.site-nav-toggle svg { width: 20px; height: 20px; }

main.page { flex: 1; padding: var(--space-6) var(--space-4); max-width: 640px; margin: 0 auto; width: 100%; }
main.centered { display: flex; align-items: center; justify-content: center; }

/* --- Public site footer (UI-3) -- compact, real destinations only ------- */
.site-footer {
    border-top: 1px solid var(--color-border);
    padding: var(--space-6) var(--space-6);
    display: flex; flex-wrap: wrap; gap: var(--space-8);
    justify-content: center;
}
.site-footer-col { display: flex; flex-direction: column; gap: var(--space-2); min-width: 140px; }
.site-footer-col strong { font-size: 13px; margin-bottom: 2px; }
.site-footer-col a { color: var(--color-text-muted); text-decoration: none; font-size: 13px; }
.site-footer-col a:hover { color: var(--color-primary); }
.site-footer-note { color: var(--color-text-muted); font-size: 12px; }
/* Hotfix: the authenticated footer's "خروج" is a POST form/button (same
   CSRF-protected logout as templates/core/_account_menu.html), styled to
   read identically to the plain-link items beside it -- no new visual
   language, just resetting default button chrome. */
.site-footer-col form { margin: 0; }
.site-footer-linklike {
    background: none; border: none; padding: 0; margin: 0;
    font-family: inherit; cursor: pointer;
    color: var(--color-text-muted); text-decoration: none; font-size: 13px;
    display: block; text-align: start;
}
.site-footer-linklike:hover { color: var(--color-primary); }

/* --- Breadcrumb (public request detail) ---------------------------------- */
.breadcrumb { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: 12px; color: var(--color-text-muted); margin-bottom: var(--space-4); }
.breadcrumb a { color: var(--color-text-muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--color-primary); }
.breadcrumb [aria-current] { color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 260px; }

/* --- Card --------------------------------------------------------------- */
.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card);
    padding: var(--space-8) var(--space-6);
    width: 100%;
    max-width: 400px;
    box-shadow: 0 1px 2px rgba(15,23,42,0.04);
}
.card.wide { max-width: 640px; }
.card h1 { font-size: 20px; margin: 0 0 var(--space-1); }
.card p.subtitle { color: var(--color-text-muted); font-size: 14px; margin: 0 0 var(--space-6); }

/* --- Forms --------------------------------------------------------------- */
label { display: block; font-size: 13px; margin-bottom: var(--space-2); color: var(--color-text); }
input[type=text], input[type=number], input[type=date], input[type=tel], input[type=search],
input[type=password], input[type=url], select, textarea {
    width: 100%;
    padding: 11px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-input);
    font-family: inherit;
    font-size: 15px;
    background: var(--color-surface);
    margin-bottom: var(--space-4);
    min-height: var(--touch-target-min);
}
textarea { min-height: 120px; }
/* Chrome/Safari give type=search its own native pill shape + clear-icon
   chrome by default, silently overriding the shared input styling above
   -- reset to a plain text-input appearance so it actually looks like
   every other field in the design system instead of a browser default. */
input[type=search] { -webkit-appearance: none; appearance: none; }
input:focus, select:focus, textarea:focus {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
    border-color: var(--color-primary);
}
.field-row { margin-bottom: var(--space-1); }
.required-marker { color: var(--color-danger); }
.field-row.has-error input, .field-row.has-error select, .field-row.has-error textarea { border-color: var(--color-danger); }
/* Boolean/select dynamic attribute fields (Category.attribute_schema,
   apps.listings.forms.build_dynamic_attributes_form) render Django's
   default RadioSelect <ul> markup -- style it to read like the rest of
   the form instead of a bare bulleted list. */
.field-row ul { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: var(--space-4); }
.field-row ul li { margin: 0; }
.field-row ul label { display: flex; align-items: center; gap: 6px; font-weight: 400; margin: 0; cursor: pointer; min-height: var(--touch-target-min); }
.field-hint { font-size: 12px; color: var(--color-text-muted); margin-top: -10px; margin-bottom: var(--space-4); }
/* L-14: logo <input type=file> -- browsers already give this a sane
   native appearance (button + filename), it just needs the same
   bottom spacing as every other field above so a hint/error list under
   it doesn't crowd the next field. */
.field-row input[type=file] { margin-bottom: var(--space-2); min-height: var(--touch-target-min); }
.seller-logo-preview { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); }
.seller-logo-preview img { width: 56px; height: 56px; object-fit: cover; border-radius: var(--radius-input); border: 1px solid var(--color-border); }

/* --- Buttons --------------------------------------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: var(--touch-target-min);
    padding: 12px 20px;
    border-radius: var(--radius-input);
    font-family: inherit;
    font-size: 15px;
    font-weight: 500;
    cursor: pointer;
    border: 1px solid transparent;
    text-decoration: none;
}
/* A button's icon is a small supporting glyph, never the dominant element
   -- without this, an <svg> with only a viewBox and no explicit size (see
   apps.core.templatetags.ui_icons) falls back to the browser's oversized
   default replaced-element size, which is exactly the "giant plus icon in
   the CTA button" bug this rule fixes. */
.btn svg { width: 18px; height: 18px; flex-shrink: 0; }
.btn-primary { background: var(--color-primary); color: #fff; width: 100%; border: none; }
.btn-primary:hover { background: var(--color-primary-hover); }
.btn-primary:disabled { opacity: 0.6; cursor: not-allowed; }
.btn-secondary { background: var(--color-surface); color: var(--color-text); border-color: var(--color-border); }
.btn-ghost { background: transparent; color: var(--color-text-muted); border: none; }
.btn-danger { background: var(--color-danger); color: #fff; border: none; }

/* --- Alerts / messages --------------------------------------------------- */
.errorlist { list-style: none; padding: 0; margin: -10px 0 var(--space-4); color: var(--color-danger); font-size: 13px; }
.messages { list-style: none; padding: 0; margin: 0 0 var(--space-4); }
.messages li { padding: 10px 14px; border-radius: var(--radius-input); font-size: 13px; margin-bottom: var(--space-2); }
.messages .error { background: var(--color-danger-soft); color: var(--color-danger); }
.messages .success { background: #F0FDF4; color: var(--color-success); }

/* --- Badge --------------------------------------------------------------- */
.badge { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 500; }
.badge-open { background: #F0FDF4; color: var(--color-success); }
.badge-matched { background: var(--color-warning-soft); color: var(--color-warning); }
.badge-closed { background: #F1F5F9; color: var(--color-text-muted); }
/* Phase 3: Offer.status badges -- same semantic tokens as above, just
   mapped to Offer's own status values instead of Listing's. */
.badge-pending { background: var(--color-warning-soft); color: var(--color-warning); }
.badge-accepted { background: #F0FDF4; color: var(--color-success); }
.badge-rejected { background: var(--color-danger-soft); color: var(--color-danger); }
.badge-withdrawn { background: #F1F5F9; color: var(--color-text-muted); }
/* Phase UI-1 audit finding: Listing.STATUS_CHOICES also includes
   "cancelled" and "expired" (apps/listings/models.py), but templates
   already render `badge-{{ listing.status }}` for both -- with no
   matching class, either fell back to an unstyled bare pill. Same for
   the archive state (deleted_at != NULL, independent of `status` --
   templates/listings/my_listing_detail.html's own comment on why it's a
   separate label), previously rendered with the bare `.badge` class and
   no color at all. */
.badge-cancelled { background: #F1F5F9; color: var(--color-text-muted); }
.badge-expired { background: #F1F5F9; color: var(--color-text-muted); }
.badge-archived { background: #F1F5F9; color: var(--color-text-muted); }
/* L-04: Ticket.status badges -- "open"/"closed" already match the
   Listing-status classes above (same words, same semantics), only the
   three remaining values need their own class (same UI-1 lesson as the
   comment above this block: badge-{{ status }} with no matching class
   silently falls back to an unstyled bare pill). */
.badge-waiting_for_support { background: var(--color-warning-soft); color: var(--color-warning); }
.badge-waiting_for_user { background: var(--color-warning-soft); color: var(--color-warning); }
.badge-resolved { background: #F0FDF4; color: var(--color-success); }
/* Ticket.priority badges. */
.badge-priority-normal { background: #F1F5F9; color: var(--color-text-muted); }
.badge-priority-important { background: var(--color-warning-soft); color: var(--color-warning); }
.badge-priority-urgent { background: var(--color-danger-soft); color: var(--color-danger); }

/* --- Wizard progress ------------------------------------------------------ */
.wizard-progress { margin-bottom: var(--space-6); }
.wizard-progress .step-label { font-size: 13px; color: var(--color-text-muted); margin-bottom: var(--space-2); }
.wizard-progress .bar { height: 4px; background: var(--color-border); border-radius: 2px; overflow: hidden; }
.wizard-progress .bar-fill { height: 100%; background: var(--color-primary); }
/* UI-4: labeled desktop stepper, CSS-switched with the compact bar above
   (mobile keeps the compact "مرحله X از Y" bar only -- section 5's own
   requirement). Each step's own border-top doubles as the connecting
   line between dots -- direction-agnostic, no RTL-specific positioning. */
.wizard-progress .stepper { display: none; }
@media (min-width: 760px) {
    .wizard-progress .step-label, .wizard-progress .bar { display: none; }
    .wizard-progress .stepper { display: flex; list-style: none; padding: 0; margin: 0; }
    .stepper-step {
        flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px;
        text-align: center; font-size: 12px; color: var(--color-text-muted);
        border-top: 2px solid var(--color-border); padding-top: var(--space-3);
    }
    .stepper-dot {
        width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--color-border);
        display: flex; align-items: center; justify-content: center; font-size: 12px;
        color: var(--color-text-muted); background: var(--color-surface);
        margin-top: -13px;
    }
    .stepper-dot svg { width: 14px; height: 14px; }
    .stepper-label { white-space: nowrap; }
    .stepper-step.is-current { border-top-color: var(--color-primary); color: var(--color-text); font-weight: 700; }
    .stepper-step.is-current .stepper-dot { border-color: var(--color-primary); border-width: 2px; color: var(--color-primary); }
    .stepper-step.is-done { border-top-color: var(--color-primary); }
    .stepper-step.is-done .stepper-dot { background: var(--color-primary); border-color: var(--color-primary); color: #fff; }
}

/* --- Sticky mobile action bar --------------------------------------------- */
.sticky-actions {
    position: sticky;
    bottom: 0;
    background: var(--color-surface);
    border-top: 1px solid var(--color-border);
    padding: var(--space-3) 0;
    margin-top: var(--space-4);
    display: flex;
    gap: var(--space-3);
}
.sticky-actions .btn { flex: 1; }

/* --- Preview summary rows -------------------------------------------------- */
dl.summary dt { font-size: 12px; color: var(--color-text-muted); margin-top: var(--space-4); }
dl.summary dd { margin: 2px 0 0; font-size: 15px; }
.summary-section { border: 1px solid var(--color-border); border-radius: var(--radius-input); padding: var(--space-4); margin-bottom: var(--space-4); position: relative; }
.summary-section .edit-link { position: absolute; top: var(--space-4); left: var(--space-4); font-size: 13px; color: var(--color-primary); text-decoration: none; }
/* UI-4: sub-headers WITHIN one editable review block (several review
   groups can map to the same underlying wizard step -- see step_preview.
   html -- so this groups information visually without duplicating the
   ویرایش link per group). */
.summary-group-title { font-size: 12px; font-weight: 700; color: var(--color-text-muted); margin: var(--space-4) 0 0; padding-inline-end: 60px; }
.summary-group-title:first-of-type { margin-top: 0; }
dl.summary:has(+ .summary-group-title) { margin-bottom: 0; }

/* --- Choice-pill buttons (e.g. "برند را می‌دانید؟ بله/خیر") ---------------- */
.pill-choice { display: flex; gap: var(--space-2); margin-bottom: var(--space-4); }
.pill-choice label {
    flex: 1;
    text-align: center;
    padding: 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-input);
    cursor: pointer;
    min-height: var(--touch-target-min);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 0;
}
.pill-choice input { position: absolute; opacity: 0; }
.pill-choice input:checked + label,
.pill-choice label:has(input:checked) { border-color: var(--color-primary); background: var(--color-active-bg); color: var(--color-primary); }

@media (max-width: 480px) {
    main.page { padding: var(--space-4) var(--space-3); }
    .card { padding: var(--space-6) var(--space-4); }
}

/* ==========================================================================
   Phase 2.1 additions
   ========================================================================== */

/* --- Category selection cards (Step 1) ----------------------------------- */
.category-cards {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-3);
}
@media (min-width: 640px) {
    .category-cards { grid-template-columns: repeat(3, 1fr); }
}
.category-card {
    position: relative;
    display: block;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card);
    padding: var(--space-4);
    cursor: pointer;
    background: var(--color-surface);
    transition: border-color 0.15s ease, background 0.15s ease;
    min-height: var(--touch-target-min);
    /* Hotfix (pre-UI-6): this class was originally authored only for a
       <label> wrapper (the request wizard's step_category.html), which
       has no browser-default styling to reset. UI-3 reused it for a
       plain <a href> on the public home page -- without this, the
       anchor's user-agent-default blue/underline showed through
       untouched. Same reset already used for .listing-card (an
       identical <a>-wrapped card elsewhere in this file); a no-op on
       the <label> usage, since labels have no default color/underline. */
    text-decoration: none;
    color: inherit;
}
.category-card:hover { border-color: var(--color-primary); }
.category-card .cc-icon { color: var(--color-primary); margin-bottom: var(--space-2); }
.category-card .cc-icon svg { width: 28px; height: 28px; }
.category-card .cc-title { font-weight: 500; font-size: 14px; display: block; }
.category-card .cc-hint { font-size: 12px; color: var(--color-text-muted); display: block; margin-top: 2px; }
.category-card .cc-arrow {
    position: absolute; inset-inline-end: var(--space-3); inset-block-end: var(--space-3);
    color: var(--color-text-muted); font-size: 16px;
}
/* Real <input type=radio> kept in the DOM for accessibility -- only its
   default appearance is hidden; the card itself becomes the visible
   control via the :checked sibling selector below. */
.category-card input[type=radio] {
    position: absolute;
    opacity: 0;
    width: 1px; height: 1px;
    pointer-events: none;
}
.category-card:has(input:checked) {
    border-color: var(--color-primary);
    border-width: 2px;
    background: var(--color-active-bg);
}
.category-card:has(input:focus-visible) {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* --- Section cards (Step 3 grouping) --------------------------------------- */
.form-section {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card);
    padding: var(--space-4);
    margin-bottom: var(--space-4);
}
/* UI-6 section 8: offer comparison -- a responsive grid (auto-fit), never
   a fixed-width table, so it collapses to one column per row on mobile
   with no horizontal-overflow risk. Each card reuses the same border/
   radius/padding language as .form-section, not a new visual style. */
.compare-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}
.compare-card {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card);
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.compare-card-title { font-size: 14px; }
.compare-card dl.summary { flex: 1; margin-bottom: var(--space-2); }
/* L-17: seller subscription pricing page -- same card/grid language as
   .compare-grid/.compare-card just above (border/radius/padding reused,
   not reinvented), plus the SAME highlight tokens .seller-action-panel
   already uses for "this is the primary/recommended option" (--color-
   primary border + --color-active-bg fill), never a new brand color. */
.pricing-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}
.pricing-card {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card);
    padding: var(--space-6) var(--space-4) var(--space-4);
    display: flex;
    flex-direction: column;
    position: relative;
}
.pricing-card.is-featured { border-color: var(--color-primary); border-width: 2px; background: var(--color-active-bg); }
.pricing-featured-badge {
    position: absolute; top: -12px; inset-inline-start: var(--space-4);
    background: var(--color-primary); color: #fff; font-size: 12px; font-weight: 600;
    padding: 3px 12px; border-radius: 999px;
}
.pricing-card-name { font-size: 16px; margin: 0 0 var(--space-2); }
.pricing-card-price { font-size: 28px; font-weight: 700; line-height: 1.2; margin-bottom: 2px; }
.pricing-card-price-unit { font-size: 13px; font-weight: 400; color: var(--color-text-muted); margin-inline-start: 6px; }
.pricing-card-duration { font-size: 13px; color: var(--color-text-muted); margin: 0 0 var(--space-4); }
.pricing-card-detail { margin: 0 0 var(--space-4); font-size: 13px; }
.pricing-card-detail dt { color: var(--color-text-muted); display: inline; }
.pricing-card-detail dd { display: inline; margin: 0 0 0 4px; font-weight: 600; }
.pricing-card-detail dt::after { content: ": "; }
.pricing-card-detail dt:not(:first-child) { display: block; margin-top: 4px; }
.pricing-card-saving { color: var(--color-success); }
.pricing-card-description { font-size: 13px; color: var(--color-text-muted); margin: 0 0 var(--space-4); flex: 1; }
.pricing-card form { margin-top: auto; }

/* UI-5 section 15: the seller's decision point on an opportunity page
   needs to read as the page's primary action, not just another info
   card -- same restrained highlight language already used for a
   selected pill/tri-choice option (--color-active-bg), never a new color. */
.seller-action-panel { border-color: var(--color-primary); background: var(--color-active-bg); }
.seller-action-panel h2 { margin-bottom: var(--space-2); }
.seller-action-panel > p { margin: 0 0 var(--space-4); font-size: 14px; }
.form-section h2 {
    font-size: 14px;
    font-weight: 700;
    color: var(--color-primary);
    margin: 0 0 var(--space-4);
}

/* --- Three-way know/don't-know/doesn't-matter choice ----------------------- */
.tri-choice { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }
.tri-choice label {
    flex: 1 1 auto;
    min-width: 90px;
    text-align: center;
    padding: 10px 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-input);
    cursor: pointer;
    font-size: 13px;
    min-height: var(--touch-target-min);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 0;
}
.tri-choice input { position: absolute; opacity: 0; pointer-events: none; }
.tri-choice label:has(input:checked) { border-color: var(--color-primary); border-width: 2px; background: var(--color-active-bg); color: var(--color-primary); }

/* --- Part number rows ------------------------------------------------------ */
.part-number-row { display: flex; gap: var(--space-2); align-items: center; margin-bottom: var(--space-2); }
.part-number-row input[type=text] { margin-bottom: 0; flex: 1; }
.part-number-row label { width: auto; white-space: nowrap; margin-bottom: 0; display: flex; align-items: center; gap: 4px; font-size: 13px; }

/* --- File upload rows ------------------------------------------------------- */
.upload-hint { font-size: 12px; color: var(--color-text-muted); margin: -6px 0 var(--space-4); }
.file-row { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; border: 1px solid var(--color-border); border-radius: var(--radius-input); margin-bottom: var(--space-2); font-size: 13px; }
.file-row .file-status-ok { color: var(--color-success); }
.file-row .file-status-bad { color: var(--color-danger); }
.file-row-actions { display: flex; align-items: center; gap: var(--space-3); }
.file-remove-btn {
    display: flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; padding: 0; border: none; background: none;
    color: var(--color-text-muted); cursor: pointer; border-radius: var(--radius-sm);
}
.file-remove-btn svg { width: 14px; height: 14px; }
.file-remove-btn:hover { color: var(--color-danger); background: var(--color-surface-hover); }

/* --- Wizard step dots ------------------------------------------------------- */
.wizard-dots { display: flex; gap: 6px; margin-bottom: var(--space-2); }
.wizard-dots span { width: 8px; height: 8px; border-radius: 50%; background: var(--color-border); }
.wizard-dots span.done { background: var(--color-primary); }
.wizard-dots span.current { background: var(--color-primary); outline: 2px solid #A5D8DE; }

/* --- Listing / preview cards ------------------------------------------------ */
.listing-card { display: block; text-decoration: none; color: inherit; }
.listing-card .lc-meta { display: flex; flex-wrap: wrap; gap: var(--space-3); font-size: 12px; color: var(--color-text-muted); margin-top: var(--space-2); }
.listing-card .lc-meta span { display: inline-flex; align-items: center; gap: 4px; }

/* --- L-11.2: conversation-list last-message preview (templates/messaging/
   my_conversations.html) -- its own small class, NOT a reuse of
   .field-hint (that class's negative top margin, authored for text sitting
   directly under a form <input>, was pulling this line up into the badges/
   timestamp row above it -- the exact reported bug). A plain, small,
   positive top margin instead; word-break so one long unbroken token
   (a URL, a long model number) can't force horizontal overflow, matching
   the same wrapping rule .review-row-comment/.notif-row-text already use
   for other arbitrary user-entered text. */
.conversation-preview {
    margin: 4px 0 0;
    font-size: 12px;
    color: var(--color-text-muted);
    line-height: 1.6;
    word-break: break-word;
}

/* --- Empty state ------------------------------------------------------------ */
.empty-state { text-align: center; padding: var(--space-8) var(--space-4); color: var(--color-text-muted); }
.empty-state p { margin-bottom: var(--space-4); }

/* --- Dashboard (purely additive -- every other page's 640px/.card layout
   is untouched; the dashboard just uses a wider variant of the same
   shell, same tokens, same components). --------------------------------- */
main.page.dashboard-page { max-width: 1200px; }
.card.dashboard-card { max-width: 1200px; }
.dashboard-welcome { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-6); }
.dashboard-welcome h1 { font-size: 24px; margin: 0 0 4px; }
.dashboard-welcome .subtitle { color: var(--color-text-muted); font-size: 13px; margin: 0; }
.dashboard-header-actions { display: flex; align-items: flex-start; gap: var(--space-3); flex-wrap: wrap; }
.profile-completion { max-width: 220px; }
.profile-completion .bar { height: 6px; background: var(--color-border); border-radius: 3px; overflow: hidden; margin-top: 4px; }
.profile-completion .bar-fill { height: 100%; background: var(--color-primary); }

.dashboard-section { margin-bottom: var(--space-8); }
.dashboard-section-title { font-size: 15px; font-weight: 700; margin: 0 0 var(--space-3); display: flex; justify-content: space-between; align-items: center; }
.dashboard-section-title a { font-size: 13px; font-weight: 400; color: var(--color-primary); text-decoration: none; }

.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: var(--space-3); }
.kpi-card {
    display: block; text-decoration: none; color: inherit;
    border: 1px solid var(--color-border); border-radius: var(--radius-input);
    padding: var(--space-4); background: var(--color-surface);
    transition: border-color 0.15s ease;
}
a.kpi-card:hover { border-color: var(--color-primary); }
/* Small, non-decorative (UI-2 section 17: "No huge decorative icons") --
   same 18px inline-SVG set already used in the sidebar/action center,
   just muted to sit quietly above the label instead of competing with
   the number for attention. */
.kpi-card .kpi-icon { display: block; color: var(--color-text-muted); margin-bottom: var(--space-2); }
.kpi-card .kpi-icon svg { width: 18px; height: 18px; }
.kpi-card .kpi-label { font-size: 13px; color: var(--color-text-muted); margin-bottom: var(--space-2); }
.kpi-card .kpi-value { font-size: 26px; font-weight: 700; line-height: 1.2; }
.kpi-card .kpi-status { font-size: 12px; color: var(--color-primary); margin-top: var(--space-2); }
.kpi-card .kpi-status.is-muted { color: var(--color-text-muted); }

/* .action-center / .action-item are defined once, in the "App Shell" section
   further down -- this stale duplicate (different colors, different
   structure) used to live here and silently won on source order alone;
   removed rather than left to rot as a second, conflicting definition. */

.timeline { display: flex; flex-direction: column; gap: var(--space-3); }
.timeline-item { display: flex; gap: var(--space-3); font-size: 13px; }
.timeline-item .ti-icon { flex-shrink: 0; }
.timeline-item .ti-time { color: var(--color-text-muted); font-size: 12px; }

.quick-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.quick-actions a { flex: 1 1 160px; text-align: center; }

/* UI-2 section 17: tablet and mobile both want a clean 2-column KPI grid
   -- widened from 640px because auto-fit alone computed 3 uneven tracks
   at 768px (verified via browser QA screenshot), orphaning the last card
   alone on its own row instead of pairing evenly. */
@media (max-width: 900px) {
    .kpi-grid { grid-template-columns: 1fr 1fr; }
}

/* ==========================================================================
   Phase 4 -- Authenticated App Shell (workspace_base.html)
   Public pages (templates/base.html) are untouched; everything below only
   ever applies inside the sidebar shell used by the logged-in workspace.
   ========================================================================== */

:root {
    --sidebar-width: 264px;
    --topbar-height: 60px;
    --color-surface-hover: #F1F5F9;
    --color-active-bg: #FCE4E6;
    --radius-sm: 6px;
}

body.workspace-body { display: block; }

.workspace-shell { display: flex; min-height: 100vh; align-items: stretch; }

/* --- Sidebar -------------------------------------------------------------- */
.workspace-sidebar {
    width: var(--sidebar-width);
    flex-shrink: 0;
    background: var(--color-surface);
    border-inline-end: 1px solid var(--color-border);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
    z-index: 40;
}
/* L-15 bugfix: the divider below the logo/brand used to be border-bottom
   on .sidebar-brand itself -- an inline-content-width <a>, so the rule
   only ever drew a line as wide as the logo+text, not the full sidebar.
   Moved onto this wrapper (a block-level flex row spanning the whole
   sidebar width) so the line now reaches edge to edge; .sidebar-brand
   below keeps only its own content layout, no border/spacing of its own. */
.sidebar-header {
    display: flex;
    align-items: center;
    padding: var(--space-4) var(--space-4) var(--space-3);
    border-bottom: 1px solid var(--color-border);
    margin-bottom: var(--space-2);
}
.sidebar-brand {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 700;
    font-size: 15px;
    color: var(--color-primary);
    text-decoration: none;
}
.sidebar-brand svg { width: 20px; height: 20px; flex-shrink: 0; }
.sidebar-brand-logo { width: 28px; height: 28px; flex-shrink: 0; display: block; }
/* L-15 bugfix (real root cause of the close button showing at every
   width, not a breakpoint or caching issue): the label in the markup
   carries BOTH class="icon-btn sidebar-close". .icon-btn itself (defined
   further down this file) sets `display: inline-flex` as its own base
   style, at the SAME specificity (one class) as this rule -- so on equal
   specificity, whichever of the two rules comes LATER in the file wins,
   regardless of any media query on either one. .icon-btn happens to be
   defined after this point, so it was silently overriding `display: none`
   at every viewport width, all the time -- the element was never
   conditional on screen size at all. Compounding the selector to
   `.icon-btn.sidebar-close` (two classes, higher specificity) makes this
   win over plain `.icon-btn` unconditionally, independent of source
   order, so this can't silently break again if the file gets reordered. */
.icon-btn.sidebar-close { display: none; }

.sidebar-nav { flex: 1; padding: 0 var(--space-3); }
.sidebar-group { margin-bottom: var(--space-4); }
.sidebar-group-label {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-muted);
    padding: 0 var(--space-3);
    margin-bottom: var(--space-1);
    letter-spacing: 0.02em;
}
.sidebar-link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: 9px var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--color-text);
    text-decoration: none;
    font-size: 14px;
    min-height: 40px;
    border-inline-start: 3px solid transparent;
    position: relative;
}
.sidebar-link svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--color-text-muted); }
.sidebar-link span.label { flex: 1; }
.sidebar-link:hover { background: var(--color-surface-hover); }
.sidebar-link.active { background: var(--color-active-bg); color: var(--color-primary); border-inline-start-color: var(--color-primary); font-weight: 500; }
.sidebar-link.active svg { color: var(--color-primary); }
.sidebar-link .nav-badge {
    background: var(--color-accent); color: #fff; font-size: 11px; font-weight: 700;
    min-width: 18px; height: 18px; border-radius: 999px; display: inline-flex;
    align-items: center; justify-content: center; padding: 0 5px;
}
.sidebar-cta { margin: var(--space-2) var(--space-3) var(--space-3); }
.sidebar-cta .btn { width: 100%; }

.sidebar-profile {
    border-top: 1px solid var(--color-border);
    padding: var(--space-3);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    position: relative;
}
.sidebar-avatar {
    width: 34px; height: 34px; border-radius: 50%; background: var(--color-primary); color: #fff;
    display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700;
    flex-shrink: 0;
}
.sidebar-profile-info { flex: 1; min-width: 0; }
.sidebar-profile-info strong { display: block; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-profile-info span { font-size: 12px; color: var(--color-text-muted); }
.sidebar-profile-menu {
    position: absolute; bottom: calc(100% + 4px); inset-inline-start: var(--space-3); inset-inline-end: var(--space-3);
    background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-input);
    box-shadow: 0 4px 16px rgba(15,23,42,0.1); padding: var(--space-2); display: none; z-index: 50;
}
.sidebar-profile-menu.open { display: block; }
.sidebar-profile-menu a, .sidebar-profile-menu button {
    display: flex; align-items: center; gap: var(--space-2); width: 100%; text-align: start;
    padding: 8px var(--space-2); border-radius: var(--radius-sm); font-size: 13px; color: var(--color-text);
    text-decoration: none; background: none; border: none; font-family: inherit; cursor: pointer; min-height: 36px;
}
.sidebar-profile-menu a:hover, .sidebar-profile-menu button:hover { background: var(--color-surface-hover); }
.sidebar-profile-menu svg { width: 16px; height: 16px; color: var(--color-text-muted); }

.sidebar-backdrop { display: none; }

/* Checkbox hack (see templates/workspace_base.html's comment): visually
   hidden but still focusable/tabbable, never display:none/visibility:hidden
   (that would remove it from the keyboard tab order). */
.sidebar-toggle-checkbox {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* --- Topbar + main column -------------------------------------------------- */
.workspace-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.workspace-topbar {
    height: var(--topbar-height);
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 var(--space-4);
    position: sticky;
    top: 0;
    z-index: 30;
    gap: var(--space-3);
}
.sidebar-toggle {
    display: none;
    background: none; border: 1px solid var(--color-border); border-radius: var(--radius-sm);
    width: 36px; height: 36px; align-items: center; justify-content: center; cursor: pointer; color: var(--color-text);
}
.sidebar-toggle svg { width: 18px; height: 18px; }
.topbar-title { font-size: 14px; font-weight: 500; color: var(--color-text-muted); flex: 1; }
.topbar-actions { display: flex; align-items: center; gap: var(--space-2); }

.icon-btn {
    position: relative;
    width: 38px; height: 38px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%; border: none; background: none; color: var(--color-text); cursor: pointer;
}
.icon-btn:hover { background: var(--color-surface-hover); }
.icon-btn svg { width: 18px; height: 18px; }
.icon-btn .nav-badge {
    position: absolute; top: 2px; inset-inline-end: 2px;
    background: var(--color-danger); color: #fff; font-size: 10px; font-weight: 700;
    min-width: 16px; height: 16px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; padding: 0 3px;
}

.notif-dropdown-wrap { position: relative; }
.notif-dropdown {
    position: absolute; top: calc(100% + 8px); inset-inline-end: 0; width: 340px; max-width: calc(100vw - var(--space-4) * 2);
    background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-card);
    box-shadow: 0 8px 24px rgba(15,23,42,0.12); display: none; z-index: 50; overflow: hidden;
}
.notif-dropdown.open { display: block; }
.notif-dropdown-header { display: flex; justify-content: space-between; align-items: center; padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--color-border); }
.notif-dropdown-header strong { font-size: 14px; }
.notif-dropdown-header button { background: none; border: none; color: var(--color-primary); font-size: 12px; cursor: pointer; font-family: inherit; padding: 4px; }
.notif-list { max-height: 360px; overflow-y: auto; }
.notif-item { display: flex; gap: var(--space-2); padding: var(--space-3) var(--space-4); text-decoration: none; color: inherit; border-bottom: 1px solid var(--color-border); }
.notif-item:last-child { border-bottom: none; }
.notif-item:hover { background: var(--color-surface-hover); }
.notif-item .wf-icon { color: var(--color-primary); flex-shrink: 0; display: flex; }
.notif-item .wf-icon svg { width: 18px; height: 18px; }
.notif-item .notif-text { font-size: 13px; flex: 1; }
.notif-item .notif-time { font-size: 11px; color: var(--color-text-muted); display: block; margin-top: 2px; }
.notif-item.is-unread { background: #F8FBFC; }
.notif-item.is-unread .notif-text { font-weight: 500; }
.notif-empty { padding: var(--space-6) var(--space-4); text-align: center; color: var(--color-text-muted); font-size: 13px; }
/* Hotfix BUG-2: an unread dropdown row is a <button class="notif-item">
   inside a <form> (so clicking it marks-then-navigates), not an <a> --
   reset the UA button chrome only, every layout/color rule above still
   applies via the shared .notif-item class regardless of tag. */
form.notif-item-form { margin: 0; }
button.notif-item { background: none; border: none; width: 100%; text-align: inherit; font: inherit; cursor: pointer; }

/* --- Notification Center (UI-9) -------------------------------------------
   Calm, dense, business-oriented (brief: no huge cards, no gradients/glass,
   restrained unread state) -- reuses .empty-state/.quick-actions/.btn as-is,
   only the list itself is new. Unread is marked by a dot + bold weight, not
   color alone (accessibility: color is never the only signal). */
.notif-center-header { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-4); }
.notif-center-header h1 { margin-bottom: var(--space-1); }

.notif-filters { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; margin-bottom: var(--space-4); border-bottom: 1px solid var(--color-border); padding-bottom: var(--space-3); }
.notif-filter { padding: 6px 12px; border-radius: 999px; font-size: 13px; color: var(--color-text-muted); text-decoration: none; }
.notif-filter:hover { background: var(--color-surface-hover); color: var(--color-text); }
.notif-filter.active { background: var(--color-active-bg); color: var(--color-primary); font-weight: 500; }
.notif-filters-sep { width: 1px; align-self: stretch; background: var(--color-border); margin: 0 4px; }

.notif-center-list { display: flex; flex-direction: column; gap: var(--space-4); }
.notif-group-label { font-size: 12px; font-weight: 600; color: var(--color-text-muted); margin: 0 0 var(--space-2); }
.notif-row { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3) var(--space-2); border-bottom: 1px solid var(--color-border); }
.notif-row:last-child { border-bottom: none; }
.notif-row.is-unread { background: #F8FBFC; }
.notif-row-icon { color: var(--color-primary); flex-shrink: 0; display: flex; margin-top: 2px; }
.notif-row-icon svg { width: 18px; height: 18px; }
.notif-row-body { flex: 1; min-width: 0; }
.notif-row-text { margin: 0; font-size: 13px; line-height: 1.6; word-break: break-word; }
.notif-row.is-unread .notif-row-text { font-weight: 500; }
.notif-row-time { font-size: 12px; color: var(--color-text-muted); }
.notif-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--color-primary); margin-inline-end: 6px; vertical-align: middle; }
.notif-row-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }
.notif-row-actions .btn { text-decoration: none; white-space: nowrap; }
.notif-row-cta-form { margin: 0; }

@media (max-width: 480px) {
    .notif-row { flex-wrap: wrap; }
    .notif-row-actions { width: 100%; justify-content: flex-end; margin-top: var(--space-2); }
}

.visually-hidden {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* --- Workspace page container -------------------------------------------- */
main.page.workspace-page { max-width: none; padding: var(--space-6); }
/* The card itself stays capped (1180/960px) but is centered within the now-
   unbounded <main> -- otherwise, on a wide monitor, it would sit flush
   against the trailing edge with a large dead gap on the other side instead
   of reading as a deliberate column. Scoped to .workspace-page so it can
   never bleed into the public site's OWN .card.wide (640px, templates/base.html
   pages -- the listing wizard, public home) -- a previous version of
   this rule redefined .card.wide globally and silently widened those public
   pages too; this selector makes that impossible by construction. Public
   browse (.discovery-page below) is the one deliberate, narrowly-scoped
   exception -- see that rule's own comment for why. */
main.page.workspace-page > .card { margin: 0 auto; }
main.page.workspace-page .card.wide { max-width: 1180px; }

/* UI-10 layout hotfix: public browse (/browse/) needs the same two-column
   room as opportunities above -- its own search+filter layout was
   squeezed into the shared 640px public-page cap, leaving the results
   column at ~300px with large dead margins on both sides. Scoped to
   .discovery-page (not a global .card.wide change, for the exact reason
   explained above -- login/home/etc. must stay narrow) so only this one
   page opts into the wider column. */
main.page.discovery-page { max-width: 1180px; }
main.page.discovery-page .card.wide { max-width: 1180px; }

/* --- Action Center severity + icon layout ---------------------------------
   Text-first (brief: "CONTENT FIRST, ACTION SECOND, ICON THIRD"): severity
   is carried by a slim semantic accent border + a small inline-tinted icon,
   never a large colored icon-in-a-circle badge -- that badge (previously
   34px) was the exact "icon dominates the row" problem this replaces. */
.action-center { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-card); padding: 0 var(--space-4); }
.action-item {
    display: flex; align-items: center; gap: 10px; padding: var(--space-3) 0 var(--space-3) var(--space-3);
    border-bottom: 1px solid var(--color-border);
    border-inline-start: 3px solid var(--color-border);
    margin-inline-start: calc(-1 * var(--space-4));
    padding-inline-start: calc(var(--space-4) - 3px);
}
.action-item:last-child { border-bottom: none; }
.action-item-icon { flex-shrink: 0; width: 16px; height: 16px; display: flex; }
.action-item-icon svg { width: 16px; height: 16px; }
.action-item-text { flex: 1; font-size: 14px; }
.action-item .btn { width: auto; padding: 8px 16px; font-size: 13px; min-height: 36px; flex-shrink: 0; }
.action-item-urgent { border-inline-start-color: var(--color-danger); }
.action-item-urgent .action-item-icon { color: var(--color-danger); }
.action-item-warn { border-inline-start-color: var(--color-warning); }
.action-item-warn .action-item-icon { color: var(--color-warning); }
.action-item-success { border-inline-start-color: var(--color-success); }
.action-item-success .action-item-icon { color: var(--color-success); }
.action-item-info { border-inline-start-color: var(--color-primary); }
.action-item-info .action-item-icon { color: var(--color-primary); }
.action-center.all-clear {
    display: flex; align-items: center; gap: 10px; text-align: start; padding: var(--space-4);
    border-inline-start: 3px solid var(--color-success);
}
.action-center.all-clear .action-item-icon { color: var(--color-success); }
.action-center.all-clear strong { display: block; font-size: 14px; margin-bottom: 2px; }
.action-center.all-clear p { margin: 0; font-size: 13px; color: var(--color-text-muted); }
.action-center.all-clear a { color: var(--color-primary); }

/* --- Two-column main content grid (brief: main workflow gets more visual
   weight than secondary/activity info) -- real CSS grid, no float/absolute
   hacks; RTL-correct by construction (the grid direction follows the
   ancestor <html dir="rtl">, so the first/"main" column already renders on
   the visually-leading side with no manual flipping). Collapses to a single
   stacked column once there isn't room for two comfortable columns. ------- */
.dashboard-columns { display: grid; grid-template-columns: 7fr 3fr; gap: var(--space-6); align-items: start; }
.dashboard-main, .dashboard-secondary { min-width: 0; }
/* Even split (UI-2 section 18) -- two peer workflow lists of equal
   importance (e.g. "آخرین درخواست‌های من" | "پیشنهادهای اخیر"), unlike
   the 7fr/3fr main/secondary split above. Same collapse-to-one-column
   breakpoint, same gap token. */
.dashboard-columns.is-even { grid-template-columns: 1fr 1fr; }
.dashboard-columns.is-even > div { min-width: 0; }
@media (max-width: 860px) {
    .dashboard-columns { grid-template-columns: 1fr; }
}

/* Groups a buyer + seller section pair for a `both`-role account (UI-2
   section 12) -- only rendered when both roles are present, so a
   single-role dashboard never shows this extra label. */
.dashboard-group-title { font-size: 13px; font-weight: 700; color: var(--color-text-muted); margin: 0 0 var(--space-3); }

/* --- Compact CTA variant -- 38px, for dense workspace chrome (dashboard
   header, sidebar) where the global 44px --touch-target-min would look
   oversized. The 44px floor itself is left untouched everywhere it protects
   a real tap target (forms, primary submit actions). ---------------------- */
.btn-compact { min-height: 38px; padding: 8px 16px; font-size: 13px; }
/* Counters .btn-primary/.btn-danger's width:100% for a button that should
   size to its own content instead of filling its container -- replaces the
   repeated inline `style="width:auto; ..."` these needed before. */
.btn-inline { width: auto; white-space: nowrap; }

/* --- Active workflow list (messages + negotiations) ----------------------- */
.workflow-list { display: flex; flex-direction: column; gap: var(--space-2); }
.workflow-item {
    display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3);
    border: 1px solid var(--color-border); border-radius: var(--radius-input); text-decoration: none; color: inherit;
}
.workflow-item:hover { border-color: var(--color-primary); }
.workflow-item.is-unread { border-color: var(--color-primary); background: var(--color-active-bg); }
.workflow-item .wf-icon { color: var(--color-primary); flex-shrink: 0; }
.workflow-item .wf-icon svg { width: 18px; height: 18px; }
.workflow-item .wf-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.workflow-item .wf-body strong { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.workflow-item .wf-preview { font-size: 12px; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.workflow-item .wf-arrow { color: var(--color-text-muted); flex-shrink: 0; }
.workflow-item .wf-arrow svg { width: 16px; height: 16px; }

/* --- Timeline: a small tinted glyph, no icon badge/circle -- a light,
   text-first activity feed (brief section 7), not a "promotional card". */
.timeline-item { align-items: flex-start; gap: 10px; }
.timeline-item .ti-icon {
    width: 16px; height: 16px; flex-shrink: 0; color: var(--color-text-muted);
    display: flex; margin-top: 2px;
}
.timeline-item .ti-icon svg { width: 16px; height: 16px; }

/* --- Responsive: sidebar becomes an off-canvas drawer ----------------------
   L-15 bugfix: was max-width:1024px, well above the 900/901px threshold
   every OTHER desktop/mobile split in this file already uses (.discovery-
   filters, .filter-toggle-btn) -- a browser window anywhere in the
   901-1024px range (common on a laptop, not just a phone/tablet) got the
   off-canvas drawer treatment (close button, backdrop, fixed positioning)
   instead of the persistent sidebar, even though the visitor was on a
   normal desktop. Aligned to 900px so "desktop" means the same viewport
   width everywhere in this file. Kept in sync with the bottom-nav
   breakpoint below (same reason: they're one mobile/desktop switch, not
   two independent ones -- a persistent sidebar AND the mobile bottom nav
   both visible at once would be exactly the broken half-and-half state
   this fix is for). */
@media (max-width: 900px) {
    .sidebar-toggle { display: inline-flex; }
    .workspace-sidebar {
        position: fixed; inset-inline-start: 0; top: 0; height: 100vh;
        transform: translateX(-100%);
        transition: transform 0.2s ease;
        box-shadow: 0 0 0 rgba(0,0,0,0);
    }
    html[dir="rtl"] .workspace-sidebar { transform: translateX(100%); }
    .icon-btn.sidebar-close { display: inline-flex; margin-inline-start: auto; }
    .sidebar-backdrop {
        display: none; position: fixed; inset: 0; background: rgba(15,23,42,0.4); z-index: 39; cursor: default;
    }
    main.page.workspace-page { padding: var(--space-4) var(--space-3); }

    /* Checkbox-hack open state (templates/workspace_base.html) -- no JS
       required; :checked alone drives the drawer. */
    #sidebar-toggle-checkbox:checked ~ .workspace-shell .workspace-sidebar {
        transform: translateX(0);
        box-shadow: 4px 0 24px rgba(15,23,42,0.15);
    }
    #sidebar-toggle-checkbox:checked ~ .workspace-shell .sidebar-backdrop { display: block; }
    #sidebar-toggle-checkbox:focus-visible ~ .workspace-shell .sidebar-toggle { outline: 2px solid var(--color-primary); outline-offset: 2px; }
}

@media (max-width: 480px) {
    .notif-dropdown { position: fixed; top: var(--topbar-height); inset-inline: var(--space-2); width: auto; }
    .action-item { flex-wrap: wrap; }
    .action-item .btn { margin-inline-start: 46px; }
}

/* ==========================================================================
   Phase 5 -- Public site polish (login/OTP, homepage, browse, detail)
   ========================================================================== */

/* --- Bidi-safe display for phone numbers/technical values embedded inline
   inside RTL text (see apps.accounts.templatetags.accounts_extras.bidi_ltr) --------- */
.bidi-ltr { unicode-bidi: isolate; direction: ltr; font-variant-numeric: tabular-nums; }

/* --- Account menu (compact header dropdown, opens downward) --------------- */
.account-menu { position: relative; }
.account-menu-toggle {
    display: flex; align-items: center; gap: 8px;
    background: none; border: 1px solid var(--color-border); border-radius: 999px;
    padding: 4px 10px 4px 4px; cursor: pointer; font-family: inherit; color: var(--color-text);
}
.account-menu-toggle:hover { background: var(--color-surface-hover); }
.account-avatar { width: 28px; height: 28px; font-size: 12px; flex-shrink: 0; }
.account-menu-label { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.3; }
.account-menu-label strong { font-size: 13px; font-weight: 500; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-menu-label span { font-size: 11px; color: var(--color-text-muted); }
.account-menu-chevron { display: flex; color: var(--color-text-muted); flex-shrink: 0; }
.account-menu-chevron svg { width: 16px; height: 16px; transform: rotate(90deg); }
.account-menu-dropdown {
    position: absolute; top: calc(100% + 8px); inset-inline-end: 0; min-width: 190px;
    background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-input);
    box-shadow: 0 8px 24px rgba(15,23,42,0.12); padding: var(--space-2); display: none; z-index: 50;
}
.account-menu-dropdown.open { display: block; }
.account-menu-dropdown a, .account-menu-dropdown button {
    display: flex; align-items: center; gap: 8px; width: 100%; text-align: start;
    padding: 8px var(--space-2); border-radius: var(--radius-sm); font-size: 13px; color: var(--color-text);
    text-decoration: none; background: none; border: none; font-family: inherit; cursor: pointer; min-height: 36px;
}
.account-menu-dropdown a:hover, .account-menu-dropdown button:hover { background: var(--color-surface-hover); }
.account-menu-dropdown svg { width: 16px; height: 16px; color: var(--color-text-muted); flex-shrink: 0; }
.account-menu-dropdown form { margin: 0; }

/* --- Profile page header (avatar + name/phone/role summary) --------------- */
.profile-header { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-6); }
.profile-header .profile-avatar { width: 52px; height: 52px; font-size: 20px; flex-shrink: 0; }
.profile-header strong { display: block; font-size: 16px; margin-bottom: 2px; }
.profile-header > div > .bidi-ltr { display: block; font-size: 13px; color: var(--color-text-muted); margin-bottom: 6px; }

/* --- Auth flow (login + OTP verify) -- single-focus, step-by-step --------- */
.auth-flow h1 { font-size: 22px; margin: 0 0 4px; }
.auth-flow .subtitle { margin-bottom: var(--space-6); }
.auth-flow .subtitle strong { color: var(--color-text); font-weight: 500; }
.otp-code-input {
    text-align: center;
    letter-spacing: 10px;
    font-size: 22px;
    font-weight: 700;
    font-family: 'Vazirmatn', monospace;
}
.auth-flow-links {
    display: flex; justify-content: space-between; align-items: center;
    margin-top: var(--space-2); flex-wrap: wrap; gap: var(--space-2);
}
.auth-flow-links form { margin: 0; }
.btn-link {
    background: none; border: none; padding: 0; font: inherit; font-size: 13px;
    color: var(--color-primary); cursor: pointer; text-decoration: none;
}
.btn-link:disabled { color: var(--color-text-muted); cursor: default; }
.auth-flow-links a { font-size: 13px; color: var(--color-text-muted); text-decoration: none; }
.auth-flow-links a:hover { color: var(--color-text); }

/* --- Marketing landing page (public home) ---------------------------------- */
.hero { text-align: center; padding: var(--space-8) 0 var(--space-6); }
.hero h1 { font-size: 30px; line-height: 1.35; margin: 0 0 var(--space-3); max-width: 600px; margin-inline: auto; }
.hero .subtitle { font-size: 15px; max-width: 480px; margin: 0 auto var(--space-6); }
.hero-actions { display: flex; justify-content: center; gap: var(--space-3); flex-wrap: wrap; }
.hero-actions .btn { width: auto; padding: 12px 28px; }

.landing-section { padding: var(--space-8) 0; border-top: 1px solid var(--color-border); }
.landing-section h2 { font-size: 20px; margin: 0 0 var(--space-6); text-align: center; }

.steps-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--space-6); counter-reset: step; }
.steps-list li { list-style: none; text-align: center; }
.steps-list .step-number {
    width: 32px; height: 32px; border-radius: 50%; background: var(--color-active-bg); color: var(--color-primary);
    display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 14px;
    margin: 0 auto var(--space-3);
}
.steps-list strong { display: block; font-size: 14px; margin-bottom: 4px; }
.steps-list span { font-size: 13px; color: var(--color-text-muted); }

@media (max-width: 480px) {
    .hero h1 { font-size: 24px; }
}

/* --- Site header responsive (public/compact header, templates/base.html) -- */
@media (max-width: 768px) {
    .site-nav-toggle { display: inline-flex; }
    /* Anonymous-only nav (خانه/مرور درخواست‌ها) collapses into a dropdown
       panel under the header -- same pure-CSS checkbox pattern as the
       workspace sidebar drawer (no JS required). Only reached when
       #public-nav-toggle exists at all, i.e. the anonymous branch of
       templates/base.html; the authenticated branch never renders it and
       so is entirely unaffected by this rule. */
    #public-nav-toggle ~ .site-nav {
        display: none;
        position: absolute; top: 100%; inset-inline: 0;
        flex-direction: column; align-items: stretch; gap: 0;
        background: var(--color-surface); border-bottom: 1px solid var(--color-border);
        padding: var(--space-2) var(--space-4);
        box-shadow: 0 8px 16px rgba(15,23,42,0.08);
    }
    #public-nav-toggle:checked ~ .site-nav { display: flex; }
    #public-nav-toggle ~ .site-nav .site-nav-link { padding: 10px 0; border-bottom: 1px solid var(--color-border); }
    #public-nav-toggle ~ .site-nav .site-nav-link:last-child { border-bottom: none; }
}

@media (max-width: 480px) {
    header.site-header { padding: 0 var(--space-3); gap: var(--space-2); }
    .site-brand span { display: none; }
    .account-menu-label { display: none; }
    .account-menu-toggle { padding: 4px; border: none; }
}

/* ==========================================================================
   Phase UI-1 -- accessibility, loading states, mobile bottom nav,
   reusable page-header. Audit findings: no global focus-visible rule (a
   few one-off selectors existed, e.g. .category-card:has(input:focus-
   visible)), no prefers-reduced-motion handling at all, no loading/
   skeleton treatment for the four existing HTMX polling targets
   (dashboard-live, notifications summary, messaging thread, deal-status),
   no mobile bottom navigation, no shared page-header partial (every list
   page hand-rolled its own <h1>+<p class="subtitle">).
   ========================================================================== */

/* --- Global keyboard focus (a11y) ------------------------------------------
   Every interactive element gets a visible ring on keyboard focus (never
   on mouse click, via :focus-visible) -- input/select/textarea already had
   their own focus rule above; this covers everything else (links, buttons,
   nav items) that didn't. */
a:focus-visible, button:focus-visible, .btn:focus-visible,
.site-nav-link:focus-visible, .sidebar-link:focus-visible,
.kpi-card:focus-visible, .workflow-item:focus-visible, .listing-card:focus-visible,
/* L-11.2.1: <summary> isn't an <a>/<button>/[tabindex] -- it's natively
   focusable on its own (part of the <details> disclosure widget), but
   had no explicit ring here, same gap this whole rule already exists to
   close for everything else. */
summary:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* --- Reduced motion --------------------------------------------------------
   Every transition/animation this stylesheet defines (dropdowns, the
   sidebar drawer, hover states) collapses to instant for a user who has
   asked the OS for reduced motion -- nothing here is essential motion
   (no loading spinner depends on animation to convey state; the
   .htmx-indicator/.skeleton rules below use opacity/display, not motion,
   so they're unaffected and still communicate loading state). */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* --- HTMX loading indicator -------------------------------------------------
   Declarative opt-in via the standard `hx-indicator="#id"` attribute --
   htmx itself toggles the `htmx-request` class on the indicator element
   while a request is in flight; this is pure CSS, no new JS. Applied to
   the four existing polling targets (dashboard-live, notifications
   summary, messaging thread, deal-status) -- their poll INTERVAL and
   endpoint are unchanged, this only makes an in-flight request visible
   instead of silent. */
.htmx-indicator { display: none; align-items: center; gap: 6px; font-size: 12px; color: var(--color-text-muted); }
.htmx-request.htmx-indicator, .htmx-request .htmx-indicator { display: inline-flex; }
.htmx-indicator .spinner {
    width: 12px; height: 12px; border-radius: 50%;
    border: 2px solid var(--color-border); border-top-color: var(--color-primary);
    animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* --- Skeleton placeholder ---------------------------------------------------
   For a section that has no content yet to show while its first load is
   in flight (server-rendered pages here always render real content
   immediately -- see workspace_base.html's progressive-enhancement
   comments -- so this is for the rare case of a client-side-only wait,
   kept simple and available rather than applied everywhere by default). */
.skeleton { position: relative; overflow: hidden; background: var(--color-border); border-radius: var(--radius-sm); color: transparent !important; }
.skeleton::after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent);
    animation: skeleton-shimmer 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .skeleton::after { animation: none; } }
@keyframes skeleton-shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

/* --- Reusable page-header component (brief section 12) --------------------
   templates/components/_page_header.html -- title + optional description +
   optional primary/secondary actions, replacing the hand-rolled
   <h1>+<p class="subtitle"> pattern each list page repeated on its own. */
.page-header { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-6); }
.page-header h1 { margin: 0 0 4px; }
.page-header .subtitle { margin: 0; }
.page-header-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; flex-shrink: 0; }
.page-header-actions .btn { width: auto; }

/* --- Mobile bottom navigation (brief section 8) -----------------------------
   Supplements, never replaces, the sidebar drawer (still reachable via the
   topbar hamburger on the same breakpoint) -- this is just the 4 most
   common destinations, one tap away, role-aware via the same context
   variables the sidebar already uses (apps.core.context_processors.
   role_permissions), never a second/different permission check. */
.bottom-nav {
    display: none;
    position: fixed; bottom: 0; inset-inline: 0; z-index: 45;
    background: var(--color-surface); border-top: 1px solid var(--color-border);
    padding-bottom: env(safe-area-inset-bottom, 0);
}
.bottom-nav-inner { display: flex; }
.bottom-nav-link {
    flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; padding: 8px 4px; min-height: 52px; color: var(--color-text-muted);
    text-decoration: none; font-size: 11px; position: relative;
}
.bottom-nav-link svg { width: 20px; height: 20px; }
.bottom-nav-link.active { color: var(--color-primary); }
.bottom-nav-link .nav-badge { position: absolute; top: 2px; inset-inline-start: calc(50% + 6px); }
/* L-15: kept in sync with the sidebar drawer's own breakpoint above
   (900px, was 1024px on both -- see that rule's comment). */
@media (max-width: 900px) {
    .bottom-nav { display: block; }
    /* Room for the fixed bottom nav so page content (and the sidebar
       drawer's own backdrop) never sits underneath it. */
    .workspace-main { padding-bottom: 56px; }
    main.page.workspace-page { padding-bottom: calc(var(--space-6) + 56px); }
    /* The popup-notification stack (templates/workspace_base.html's
       #popup-container) is bottom-anchored too -- pushed up above the
       bottom nav here so the two never overlap. */
    #popup-container { bottom: calc(56px + var(--space-4)) !important; }
}
@media (max-width: 480px) {
    main.page.workspace-page { padding-bottom: calc(var(--space-4) + 56px); }
}

/* --- UI-8: Account IA layout + section nav --------------------------------
   .account-columns is its own small grid (not a reuse of .dashboard-columns'
   7fr/3fr split, which is tuned for that page's main-content/KPI-card
   pairing, not a fixed-width nav list) -- a narrow fixed nav column + a
   flexible content column, collapsing to a single stacked column under the
   same 860px breakpoint every other two-column workspace page already uses,
   so nav-above-content on mobile is just normal source order, never a
   squeezed-down desktop sidebar. -------------------------------------------- */
.account-columns { display: grid; grid-template-columns: 220px 1fr; gap: var(--space-6); align-items: start; }
.account-columns > div, .account-columns > nav { min-width: 0; }
@media (max-width: 860px) {
    .account-columns { grid-template-columns: 1fr; }
}
.account-nav {
    display: flex; flex-direction: column; gap: 2px;
    border: 1px solid var(--color-border); border-radius: var(--radius-card);
    padding: var(--space-2); background: var(--color-surface);
}
.account-nav-link {
    display: flex; align-items: center; gap: var(--space-3);
    padding: 10px var(--space-3); border-radius: var(--radius-input);
    color: var(--color-text); text-decoration: none; font-size: 14px; font-weight: 500;
    min-height: var(--touch-target-min);
}
.account-nav-link svg { flex-shrink: 0; color: var(--color-text-muted); }
.account-nav-link:hover { background: var(--color-bg); }
.account-nav-link.active { background: var(--color-active-bg); color: var(--color-primary); }
.account-nav-link.active svg { color: var(--color-primary); }
@media (max-width: 860px) {
    .account-nav { margin-bottom: var(--space-6); }
}

/* Personal/business completion indicator on the Account Overview page --
   same .bar/.bar-fill visual as .profile-completion (dashboard header
   nudge), scoped separately so this page's own full-width card layout
   isn't constrained by that component's 220px max-width. */
.account-completion .bar { height: 6px; background: var(--color-border); border-radius: 3px; overflow: hidden; margin-top: 4px; }
.account-completion .bar-fill { height: 100%; background: var(--color-primary); }

/* ==========================================================================
   UI-10 -- Search & Discovery (buyer public browse + seller opportunities).
   Calm, dense, no huge cards/gradients -- same restraint as the rest of
   this design system. The category tree, chips, and drawer are all plain
   HTML (<details>, checkbox-hack) -- zero JS required for any of it to work.
   ========================================================================== */
.discovery-toolbar { display: flex; gap: var(--space-2); margin-bottom: var(--space-3); flex-wrap: wrap; }
.discovery-toolbar input[type=search] { flex: 1 1 240px; min-width: 0; }
.filter-toggle-btn { display: none; align-items: center; gap: 6px; cursor: pointer; text-decoration: none; }

.active-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: var(--space-4); }
.chip {
    display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 999px;
    background: var(--color-active-bg); color: var(--color-primary); font-size: 12px; text-decoration: none;
    border: 1px solid var(--color-border);
}
.chip:hover { background: #F8D7DA; }
.chip-clear { background: var(--color-surface); color: var(--color-text-muted); }
.chip-clear:hover { background: var(--color-surface-hover); }

.discovery-result-count { font-size: 13px; color: var(--color-text-muted); margin: 0 0 var(--space-3); }

.discovery-layout { display: flex; gap: var(--space-6); align-items: flex-start; }
/* UI-10 layout hotfix: reuses --sidebar-width (264px, the workspace app
   shell's own nav sidebar -- design-system.css's "Workspace" section)
   instead of an unrelated hard-coded number, so this filter sidebar reads
   as the same deliberate column width already established elsewhere in
   the product, not an arbitrary new one. Results gets flex:1 -- whatever
   room main.page/.card.wide (see .discovery-page above) actually has. */
.discovery-filters { flex: 0 0 var(--sidebar-width); min-width: 0; position: sticky; top: var(--space-4); max-height: calc(100vh - var(--space-8)); overflow-y: auto; }
.discovery-filters-header { display: none; justify-content: space-between; align-items: center; margin-bottom: var(--space-3); }
.discovery-results { flex: 1; min-width: 0; }
@media (min-width: 901px) {
    /* This sidebar has its own scrollbar (7+ fields + a 146-node category
       tree inside a capped max-height) -- inheriting direction:rtl from
       <html dir="rtl"> puts THAT scrollbar on the left in Chrome, out of
       step with the page's own (physical-right, direction-independent)
       scrollbar a few pixels away. Flip the scroll container itself back
       to ltr so its scrollbar matches the page, and restore rtl on
       .discovery-filters-inner for the actual content. Desktop-only:
       under 901px .discovery-filters becomes a position:fixed drawer
       positioned with inset-inline-start (below), which resolves against
       this element's OWN direction -- flipping it there would silently
       dock the drawer on the wrong edge. */
    .discovery-filters { direction: ltr; padding-right: var(--space-2); }
    .discovery-filters-inner { direction: rtl; }
}

.filter-group { margin-bottom: var(--space-4); }
.filter-group-label { display: block; font-size: 12px; font-weight: 600; color: var(--color-text-muted); margin-bottom: var(--space-2); }

.cat-tree { display: flex; flex-direction: column; gap: 1px; }
.cat-tree-node { margin-bottom: 1px; }
.cat-tree-node summary {
    cursor: pointer; list-style: none; display: flex; align-items: center; gap: 2px;
}
.cat-tree-node summary::-webkit-details-marker { display: none; }
.cat-tree-node summary::marker { content: ""; }
/* Visual "this group expands" cue -- previously a non-leaf row looked
   pixel-identical to a plain leaf link, with no hint it was collapsible.
   Points sideways when closed, rotates to point down when the <details>
   is open; no JS, driven entirely by the [open] attribute selector. */
.cat-tree-chevron {
    flex-shrink: 0; width: 14px; height: 14px; margin-inline-start: 2px;
    color: var(--color-text-muted); transition: transform .15s ease;
}
.cat-tree-node[open] > summary .cat-tree-chevron { transform: rotate(90deg); color: var(--color-primary); }
.cat-tree-link {
    display: block; flex: 1; min-width: 0; padding: 6px 8px; border-radius: 6px; font-size: 13px;
    color: var(--color-text); text-decoration: none; word-break: break-word;
    border-inline-start: 2px solid transparent; transition: background-color .12s ease;
}
.cat-tree-link:hover { background: var(--color-surface-hover); }
.cat-tree-link.active {
    background: var(--color-active-bg); color: var(--color-primary); font-weight: 600;
    border-inline-start-color: var(--color-primary);
}
.cat-tree-children {
    padding-inline-start: var(--space-4); border-inline-start: 2px solid var(--color-border);
    margin-inline-start: 9px; margin-top: 1px;
}

/* L-16: seller "حوزه‌های کاری" checkbox tree (templates/accounts/
   _business_areas_tree.html) -- same row shape/hover/active treatment as
   .cat-tree-link above, just a <label>+checkbox instead of an <a>, so a
   whole node (not only leaves) can be multi-selected. */
.cat-tree-checkbox-row {
    display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; padding: 6px 8px;
    border-radius: 6px; font-size: 13px; font-weight: 400; color: var(--color-text); cursor: pointer;
    border-inline-start: 2px solid transparent; transition: background-color .12s ease; margin-bottom: 0;
}
.cat-tree-checkbox-row input { flex-shrink: 0; margin: 0; }
.cat-tree-checkbox-row:hover { background: var(--color-surface-hover); }
.cat-tree-checkbox-row:has(input:checked) {
    background: var(--color-active-bg); color: var(--color-primary); font-weight: 600;
    border-inline-start-color: var(--color-primary);
}

.filter-drawer-actions { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-4); }

/* Checkbox-hack drawer toggle (mobile) -- same visually-hidden-but-
   focusable technique as #sidebar-toggle-checkbox (workspace_base.html). */
.filter-drawer-toggle {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
/* L-15: compound selector, not bare .filter-drawer-close -- see the
   .icon-btn.sidebar-close comment above for why (this label also carries
   class="icon-btn filter-drawer-close" in the markup, and .icon-btn's own
   `display: inline-flex` must never be able to out-cascade this). This
   one happened to already win by sitting after .icon-btn in the file, but
   that's fragile positioning, not a real guarantee -- fixed the same way
   regardless. */
.icon-btn.filter-drawer-close, .filter-drawer-backdrop { display: none; }

@media (max-width: 900px) {
    .filter-toggle-btn { display: inline-flex; }
    .discovery-layout { display: block; }
    .discovery-filters {
        position: fixed; inset-inline-start: 0; top: 0; height: 100vh; width: 300px; max-width: 85vw;
        background: var(--color-surface); z-index: 200; padding: var(--space-4); box-sizing: border-box;
        max-height: none; transform: translateX(-100%); transition: transform 0.2s ease;
    }
    html[dir="rtl"] .discovery-filters { transform: translateX(100%); }
    .discovery-filters-header { display: flex; }
    .icon-btn.filter-drawer-close { display: inline-flex; }
    .filter-drawer-backdrop { display: none; position: fixed; inset: 0; background: rgba(15,23,42,.4); z-index: 190; }

    #filter-drawer-toggle:checked ~ .discovery-search-form .discovery-filters {
        transform: translateX(0);
        box-shadow: 4px 0 24px rgba(15,23,42,.15);
    }
    #filter-drawer-toggle:checked ~ .discovery-search-form .filter-drawer-backdrop { display: block; }
    #filter-drawer-toggle:focus-visible ~ .discovery-search-form .filter-toggle-btn { outline: 2px solid var(--color-primary); outline-offset: 2px; }
}

/* ==========================================================================
   UI-11 -- Reviews & Trust (rating stars, review form, review list, trust
   summary). Calm and restrained by design (brief: real trust, not the
   appearance of it) -- no gradients, no badges shaped like verification
   seals, the star glyph is the only decorative element and it always
   carries a real text label alongside it (never color/shape alone).
   ========================================================================== */
.stars { color: var(--color-warning); font-size: 15px; letter-spacing: 1px; }
.stars-inline { display: inline-flex; align-items: center; gap: 6px; }

.review-card .field-hint { margin-top: 0; }

/* L-15: the not-yet-reviewed state is a popup (<dialog>), not an
   always-visible top-of-page card -- see templates/reviews/_review_card.
   html's own comment on the showModal() progressive-enhancement. No
   `position`/`inset` override here on purpose: dialog:modal's UA default
   (position:fixed, centered) must stay in effect for the real popup case;
   the no-JS fallback (plain [open], no showModal()) keeps the browser's
   own default placement too. */
.review-modal {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card);
    padding: var(--space-6);
    max-width: 380px;
    width: 92vw;
    background: var(--color-surface);
    color: var(--color-text);
}
.review-modal::backdrop { background: rgba(15, 23, 42, .5); }
.review-modal h2 { margin-top: 0; font-size: 16px; padding-inline-end: 28px; }
.review-modal-close { position: absolute; top: var(--space-3); inset-inline-end: var(--space-3); }

.star-input-options { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.star-input-option {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    width: 48px; min-height: var(--touch-target-min); padding: 6px 0;
    border: 1px solid var(--color-border); border-radius: var(--radius-input);
    cursor: pointer; font-size: 18px; color: var(--color-text-muted);
}
/* L-23: cumulative fill -- clicking e.g. the 3rd star must also light up
   stars 1-2, not just the one clicked. Relies on apps.reviews.forms.
   ReviewForm.rating listing its choices 5->1 (not 1->5), so the DOM
   order of these boxes is already highest-value-first; a box lights up
   either because IT is the checked one, or because a LATER box in DOM
   (:has(~ ...), a lower rating value) is checked -- which visually means
   every star from the start edge up through the clicked one. */
.star-input-option:has(input:checked),
.star-input-option:has(~ .star-input-option input:checked) {
    border-color: var(--color-warning); color: var(--color-warning); background: var(--color-warning-soft);
}
.star-input-option:has(input:focus-visible) { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.star-input-option input { position: absolute; opacity: 0; width: 1px; height: 1px; }

.trust-summary { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.trust-summary .stars { font-size: 17px; }
.trust-summary-count { font-size: 12px; color: var(--color-text-muted); }
.trust-summary-none { font-size: 12px; color: var(--color-text-muted); }

/* L-14: compact seller-identity block (templates/offers/_seller_summary.
   html), reused wherever an already-accepted Offer's seller is shown --
   deliberately small (a 40px logo, no card border/shadow of its own) so
   it reads as a sub-block of the surrounding offer card/row, never a
   second competing card. */
.seller-summary { display: flex; align-items: flex-start; gap: var(--space-3); }
.seller-summary-logo { width: 40px; height: 40px; object-fit: cover; border-radius: var(--radius-input); border: 1px solid var(--color-border); flex-shrink: 0; }
.seller-summary-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.seller-summary-name { font-size: 14px; }
.seller-summary-meta { display: flex; flex-wrap: wrap; gap: var(--space-2); font-size: 12px; color: var(--color-text-muted); }
.seller-summary-link { align-self: flex-start; }

.review-list { display: flex; flex-direction: column; gap: var(--space-4); }
.review-row { padding: var(--space-3) 0; border-bottom: 1px solid var(--color-border); }
.review-row:last-child { border-bottom: none; }
.review-row-meta { display: block; font-size: 12px; color: var(--color-text-muted); margin-top: 4px; }
.review-row-comment { margin: var(--space-2) 0 0; font-size: 13px; line-height: 1.7; word-break: break-word; }

@media (max-width: 480px) {
    .star-input-option { width: 44px; }
}

/* ============================================================
   UI-12: Admin & Moderation workspace. Deliberately reuses the
   existing .workspace-shell/.workspace-sidebar/.kpi-grid/.badge-*
   component set wholesale (templates/admin_workspace/base.html uses
   the exact same shell classes as workspace_base.html) -- only the
   pieces genuinely new to a staff moderation list (a GET filter bar,
   a stacked-card list row that never becomes a real <table>, and a
   muted inline link) are added here.
   ============================================================ */

.link-muted { color: var(--color-text-muted); font-size: 13px; text-decoration: underline; }
.link-muted:hover { color: var(--color-primary); }

.admin-toolbar {
    display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: flex-end;
    margin-bottom: var(--space-4); padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--color-border);
}
.admin-toolbar .field-row { margin: 0; min-width: 140px; flex: 1 1 140px; }
.admin-toolbar label { display: block; font-size: 12px; color: var(--color-text-muted); margin-bottom: 4px; }
.admin-toolbar-actions { display: flex; gap: var(--space-2); flex: 0 0 auto; }

/* Stacked-card list, not a literal <table> -- the same row markup
   renders identically at 1280px and 320px (brief section 17: no
   forced desktop table on mobile), just with more items per row's
   flex-wrap at wider widths. */
.admin-list { display: flex; flex-direction: column; gap: var(--space-3); }
.admin-row {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4);
    padding: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-input);
}
.admin-row-primary { flex: 1 1 220px; min-width: 0; }
.admin-row-primary strong { display: block; word-break: break-word; }
.admin-row-primary span { display: block; font-size: 12px; color: var(--color-text-muted); margin-top: 2px; }
.admin-row-meta { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; font-size: 13px; color: var(--color-text-muted); }
.admin-row-actions { display: flex; gap: var(--space-2); flex: 0 0 auto; margin-inline-start: auto; }
.admin-row-actions .btn { width: auto; padding: 6px 14px; font-size: 13px; min-height: 36px; }

.admin-detail-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-4); }
@media (min-width: 900px) {
    .admin-detail-grid { grid-template-columns: 2fr 1fr; align-items: start; }
}

@media (max-width: 480px) {
    .admin-row-actions { margin-inline-start: 0; width: 100%; }
    .admin-row-actions .btn { flex: 1; }
}

/* ============================================================
   Admin auth hotfix: the login page is a deliberately standalone
   document (no site-header, no workspace shell) so it reads as a
   distinct, dedicated surface rather than a themed variant of the
   public OTP login -- a dark identity band up top instead of the
   public site's light header/brand row.
   ============================================================ */

.admin-auth-page {
    min-height: 100vh; display: flex; flex-direction: column;
    align-items: center; background: var(--color-bg);
}
.admin-auth-band {
    width: 100%; background: var(--color-primary); color: #fff;
    padding: var(--space-6) var(--space-4); text-align: center;
}
.admin-auth-band svg { width: 28px; height: 28px; }
.admin-auth-band strong { display: block; margin-top: var(--space-2); font-size: 16px; }
.admin-auth-page .card { margin: var(--space-8) auto; }
.admin-auth-page .field-row input[type=password] { margin-bottom: 0; }
.admin-account-actions { display: flex; flex-direction: column; gap: var(--space-3); align-items: flex-start; }

/* ============================================================
   L-06: public informational/legal pages (about/contact/help/terms/
   privacy/rules/prohibited-items). Reuses .card.wide/.landing-section/
   .summary-section from above for layout -- only the two content-prose
   patterns below (long-form legal sections, FAQ accordions) are new.
   ============================================================ */

.legal-meta { color: var(--color-text-muted); font-size: 13px; margin: 0 0 var(--space-6); }
.legal-content h2 { font-size: 15px; margin: var(--space-6) 0 var(--space-2); }
.legal-content h2:first-child { margin-top: 0; }
.legal-content p { font-size: 14px; line-height: 2; margin: 0 0 var(--space-3); }
.legal-content ul { margin: 0 0 var(--space-3); padding-inline-start: var(--space-6); font-size: 14px; line-height: 2; }
.legal-content li { margin-bottom: 4px; }

.faq-list { display: flex; flex-direction: column; gap: var(--space-2); }
.faq-item { border: 1px solid var(--color-border); border-radius: var(--radius-input); padding: var(--space-3) var(--space-4); }
.faq-item summary { cursor: pointer; font-size: 14px; font-weight: 500; list-style: none; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::before { content: "+"; display: inline-block; width: 1em; color: var(--color-primary); }
.faq-item[open] summary::before { content: "−"; }
.faq-item p { font-size: 13px; color: var(--color-text-muted); line-height: 1.9; margin: var(--space-2) 0 0; }

/* ============================================================
   L-07: error pages (404/403/500). 404/403 extend base.html (full
   context, full design system); 500.html is a deliberately standalone
   document (see its own comment) that reuses these same class names.
   ============================================================ */

.error-page { text-align: center; padding: var(--space-8) 0; }
.error-code { display: block; font-size: 48px; font-weight: 700; color: var(--color-border); line-height: 1; margin-bottom: var(--space-4); }
.error-page h1 { font-size: 20px; margin: 0 0 var(--space-2); }
.error-page .subtitle { margin: 0 0 var(--space-6); }
.error-actions { display: flex; justify-content: center; gap: var(--space-3); flex-wrap: wrap; }

/* ============================================================
   L-14: opportunity detail (seller reading a buyer's request) + the
   offer-submission form -- visual polish only, scoped entirely to new
   `opp-*`/`offer-*` classes so the shared dl.summary/.summary-section/
   .form-section styling used by every OTHER page (step_preview,
   my_offer_detail, moderation_detail, compare_offers, public request
   detail, ...) is completely untouched. Nothing here changes what data
   is shown, which URLs exist, or any form field name/id -- purely how
   the same information reads.
   ============================================================ */

/* --- Hero header: badges + title + meta ----------------------------------- */
.opp-hero { margin-bottom: var(--space-4); }
.opp-badges { display: flex; gap: var(--space-2); margin-bottom: var(--space-3); }
.opp-title { font-size: 24px; line-height: 1.35; margin: 0 0 4px; }
.opp-meta { margin-bottom: 0; }

/* Honest per-urgency color instead of reusing the unrelated "matched"
   status color -- امروز reads as time-pressured, این هفته as a heads-up,
   انعطاف‌پذیر as neutral, the same soft-background/solid-text language
   every other .badge-* already uses (no new color introduced). */
.badge-urgency-today { background: var(--color-danger-soft); color: var(--color-danger); }
.badge-urgency-this_week { background: var(--color-warning-soft); color: var(--color-warning); }
.badge-urgency-flexible { background: #F1F5F9; color: var(--color-text-muted); }

/* --- Budget: the one number a seller decides on first, pulled out of the
   flat fact list and given real visual weight -- same soft-rose/primary
   "highlighted panel" language .seller-action-panel already established,
   not a new color. --------------------------------------------------- */
.opp-budget-stat {
    display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap;
    background: var(--color-active-bg); border: 1px solid var(--color-primary);
    border-radius: var(--radius-card); padding: var(--space-4);
    margin-bottom: var(--space-4);
}
.opp-budget-label { font-size: 13px; color: var(--color-primary); font-weight: 500; }
.opp-budget-value { font-size: 22px; font-weight: 700; color: var(--color-text); }
.opp-budget-unit { font-size: 13px; font-weight: 400; color: var(--color-text-muted); margin-inline-start: 6px; }
.opp-budget-value-compact { font-size: 17px; }

/* --- Spec groups: a datasheet reading, not a repeated card-per-field grid --
   label above value, aligned in columns, one hairline separating the
   group's heading from its rows -- no border/shadow per item, so it reads
   as one coherent sheet instead of the generic "SaaS card kit" look. --- */
.opp-spec-section { margin-bottom: var(--space-4); }
.opp-spec-title {
    font-size: 13px; font-weight: 700; color: var(--color-text-muted);
    margin: 0 0 var(--space-3); padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--color-border);
}
.opp-spec-grid {
    display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4) var(--space-4);
    margin: 0;
}
@media (min-width: 640px) {
    .opp-spec-grid { grid-template-columns: repeat(3, 1fr); }
}
.opp-spec-item { min-width: 0; }
.opp-spec-item dt { font-size: 12px; color: var(--color-text-muted); margin: 0 0 3px; }
.opp-spec-item dd { margin: 0; font-size: 15px; font-weight: 500; word-break: break-word; }
.opp-spec-item-wide { grid-column: 1 / -1; }

.opp-spec-grid-compact { grid-template-columns: repeat(2, 1fr); gap: var(--space-3); }
.opp-spec-grid-compact .opp-spec-item dd { font-size: 14px; font-weight: 400; }

/* --- Attachments: same thumbnails, real classes instead of inline styles -- */
.opp-attachments { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.opp-attachment-thumb {
    display: block; width: 96px; height: 96px; border-radius: var(--radius-input);
    border: 1px solid var(--color-border); overflow: hidden;
}
.opp-attachment-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* --- Offer form: two-column on desktop (form + a sticky read-only summary
   of what's being quoted), single column on mobile -- the price field is
   this page's one real focal point (see .offer-price-section), everything
   else stays quiet. ---------------------------------------------------- */
.offer-layout { display: flex; flex-direction: column; gap: var(--space-4); }
.offer-aside { min-width: 0; }
@media (min-width: 901px) {
    .offer-layout { flex-direction: row; align-items: flex-start; }
    .offer-main { flex: 1 1 auto; min-width: 0; }
    .offer-aside { flex: 0 0 320px; position: sticky; top: var(--space-4); }
}
.offer-summary-card { border: 1px solid var(--color-border); border-radius: var(--radius-card); padding: var(--space-4); }
.offer-summary-title { font-size: 14px; margin: 0 0 var(--space-3); }
.offer-summary-budget { margin-top: var(--space-4); padding-top: var(--space-3); border-top: 1px solid var(--color-border); display: flex; flex-direction: column; gap: 2px; }

.offer-price-section { border-color: var(--color-primary); background: var(--color-active-bg); }
.offer-price-row { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.offer-price-field { flex: 2 1 220px; }
.offer-delivery-field { flex: 1 1 160px; }
.offer-price-field input {
    font-size: 22px; font-weight: 700; text-align: center;
}
.admin-account-actions form { margin: 0; }

/* ============================================================
   L-13: Offer Comparison -- buyer-side selection (my_listing_detail.html)
   + the resulting side-by-side table (compare_offers.html). Reuses
   .opp-spec-grid/.opp-spec-section (L-14) for the "خلاصه‌ی درخواست" block,
   .compare-grid/.compare-card (UI-6, untouched) for the unfiltered card
   view, .trust-summary/.badge-*/.btn-*/.confirm-form (all untouched) for
   everything else -- only the pieces genuinely new to this phase (the
   checkbox selection row and the real <table>) are added below.
   ============================================================ */

/* --- Selection checkboxes on my_listing_detail.html's active-offers list */
.compare-selectable-card { display: flex; align-items: stretch; gap: var(--space-2); }
.compare-select-checkbox {
    display: flex; align-items: flex-start; padding-top: var(--space-4);
    cursor: pointer; flex-shrink: 0;
}
.compare-select-checkbox input[type="checkbox"] {
    width: 20px; height: 20px; margin: 0; cursor: pointer; accent-color: var(--color-primary);
}
.compare-select-checkbox input[type="checkbox"]:disabled { opacity: 0.35; cursor: not-allowed; }
.compare-selectable-link { display: block; flex: 1; min-width: 0; text-decoration: none; color: inherit; }
.compare-select-toolbar {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
    flex-wrap: wrap; margin-top: var(--space-3);
}
.compare-select-toolbar .btn { width: auto; padding: 10px 20px; }
.compare-select-count { color: var(--color-text-muted); }

/* --- The comparison table itself --------------------------------------
   A real <table>, not this project's usual stacked-card pattern -- see
   compare_offers.html's own comment on why this one place is a
   deliberate exception. Horizontal scroll is contained entirely inside
   .compare-table-wrap; the page itself never overflows (ticket section
   16). The label column is pinned with position:sticky so it stays
   legible while the offer columns scroll under it, working the same
   direction in RTL as in LTR since inset-inline-start is a logical
   property (right in this project's RTL, exactly where the label column
   already sits). L-25: below 640px, the @media block further down turns
   this into a stacked card list instead -- horizontal scroll was never a
   good mobile answer for a 7-column table, so it doesn't get the chance
   to trigger at that width at all. */
.compare-table-wrap {
    overflow-x: auto; border: 1px solid var(--color-border); border-radius: var(--radius-card);
    margin-bottom: var(--space-4);
}
.compare-table { border-collapse: collapse; width: 100%; }
.compare-table th, .compare-table td {
    padding: var(--space-3) var(--space-4); text-align: start; vertical-align: top;
    border-bottom: 1px solid var(--color-border); font-weight: 400; font-size: 14px;
}
/* L-15: one ROW per offer now (not one column per offer -- see
   compare_offers.html's own comment on why) -- thead holds the field
   labels, one per column, each reasonably narrow since they're short
   headings, not offer-identity blocks. */
.compare-table thead th {
    border-bottom: 2px solid var(--color-border); min-width: 110px;
    font-size: 12px; color: var(--color-text-muted); font-weight: 500; white-space: nowrap;
}
.compare-table tbody tr:last-child th, .compare-table tbody tr:last-child td { border-bottom: none; }
.compare-table-label-col { min-width: 160px; white-space: normal; }
/* Sticky column is each offer's OWN identity now (th[scope="row"]) --
   exactly the one column a buyer scrolling sideways through the other
   fields on a narrow screen most needs to keep in view. */
.compare-table th[scope="row"], .compare-table th.compare-table-label-col {
    position: sticky; inset-inline-start: 0; background: var(--color-surface); z-index: 1;
}
.compare-table-offer-title { display: block; margin-top: 6px; font-size: 14px; font-weight: 500; color: var(--color-text); }
.compare-table-price { font-weight: 700; font-size: 15px; }
.compare-table-message { max-width: 260px; word-break: break-word; white-space: normal; }
.compare-table-empty { color: var(--color-text-muted); }
/* Both actions sit inline, side by side, in that same offer's own row --
   not a separate stacked block. */
.compare-table-actions { display: flex; flex-direction: row; flex-wrap: wrap; gap: var(--space-2); min-width: 240px; }
.compare-table-actions .btn { width: auto; padding: 8px 16px; font-size: 13px; }
.compare-table-actions form { margin: 0; }

@media (max-width: 480px) {
    .compare-select-toolbar { flex-direction: column; align-items: stretch; }
    .compare-select-toolbar .btn { width: 100%; }
}

/* L-25: below 640px this becomes a stacked card list -- same visual
   language as .compare-card in the unfiltered branch (badge+title, then
   label-above-value rows, then a full-width action) -- instead of a
   horizontally-scrolling table; .compare-table-wrap's own overflow-x
   simply never triggers here since nothing needs to scroll anymore.
   Same <table>/<th scope="row"/"col"> markup at every width (see
   compare_offers.html's own comment on why a real table exists at all)
   -- only display/layout changes, so screen-reader table semantics are
   never lost, just visually reflowed. `data-label` on each <td> (set in
   the template) supplies the small label ::before shows above its value;
   the rating cell has none on purpose, reading as part of the identity
   block above it, same as .compare-card. */
@media (max-width: 640px) {
    .compare-table-wrap { overflow-x: visible; border: none; }
    .compare-table { display: block; border-collapse: separate; width: auto; }
    .compare-table thead { display: none; }
    .compare-table tbody { display: block; }
    .compare-table tr {
        display: block; border: 1px solid var(--color-border); border-radius: var(--radius-card);
        padding: var(--space-4); margin-bottom: var(--space-4);
    }
    .compare-table tbody tr:last-child { margin-bottom: 0; }
    .compare-table th, .compare-table td { display: block; border-bottom: none; padding: 0; }
    .compare-table th[scope="row"] { position: static; background: none; margin-bottom: var(--space-2); }
    .compare-table td { margin-top: var(--space-2); }
    .compare-table td[data-label]::before {
        content: attr(data-label); display: block; font-size: 12px; color: var(--color-text-muted); margin-bottom: 2px;
    }
    .compare-table-message { max-width: none; }
    .compare-table-actions { min-width: 0; margin-top: var(--space-3); }
    .compare-table-actions .btn { flex: 1; }
}

/* --- L-22: compact reply bar (templates/messaging/_conversation_panel.html)
   -- paperclip -> growing text input -> round send button, one pill-shaped
   row, replacing the previous stacked textarea/file-input/wide-button
   layout. Light-theme counterpart of a dark reference the user supplied --
   same shape/structure, this project's own tokens (brand red, existing
   radii/spacing), never the reference's own dark colors. ------------------ */
.reply-bar-section { padding-top: var(--space-3); padding-bottom: var(--space-3); }
.reply-bar {
    position: relative;
    display: flex; align-items: center; gap: 4px;
    background: var(--color-surface); border: 1px solid var(--color-border);
    border-radius: 999px; padding: 6px;
}
.reply-bar:focus-within { border-color: var(--color-primary); }
.reply-attach-wrap { position: relative; flex-shrink: 0; }
.reply-attach-btn { cursor: pointer; }
/* L-24: opens UPWARD (bottom: 100%), same reasoning as .sidebar-profile-menu
   -- this sits at the bottom of the page, above the keyboard on mobile,
   so a downward-opening menu would have nowhere to render. */
.reply-attach-menu {
    position: absolute; bottom: calc(100% + 8px); inset-inline-start: 0; min-width: 180px;
    background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-input);
    box-shadow: 0 8px 24px rgba(15,23,42,0.12); padding: var(--space-2); display: none; z-index: 50;
}
.reply-attach-menu.open { display: block; }
.reply-attach-menu button {
    display: flex; align-items: center; gap: 8px; width: 100%; text-align: start;
    padding: 8px var(--space-2); border-radius: var(--radius-sm); font-size: 13px; color: var(--color-text);
    background: none; border: none; font-family: inherit; cursor: pointer; min-height: 36px;
}
.reply-attach-menu button:hover { background: var(--color-surface-hover); }
.reply-attach-menu svg { width: 16px; height: 16px; color: var(--color-text-muted); flex-shrink: 0; }
/* Clip-based hidden, not display:none -- a <label for=...> must still be
   able to reach and focus this input (see the template's own comment). */
.reply-attachment-input {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.reply-body-input {
    flex: 1; resize: none; border: none; background: none;
    margin: 0; padding: 8px 6px; min-height: 20px; max-height: 140px;
    font-size: 15px; line-height: 1.4; overflow-y: auto;
}
.reply-body-input:focus { outline: none; border-color: transparent; }
.reply-send-btn {
    flex-shrink: 0; width: 38px; height: 38px; border-radius: 50%; border: none;
    background: var(--color-primary); color: #fff; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
}
.reply-send-btn svg { width: 18px; height: 18px; }
.reply-send-btn:hover { filter: brightness(0.92); }
.reply-send-btn:disabled { opacity: 0.6; cursor: default; }
/* Author-origin `display: flex` below would otherwise unconditionally
   beat the UA stylesheet's `[hidden] { display: none }` regardless of
   selector order/specificity (any author rule always wins over UA
   origin) -- this same-specificity attribute-selector override is what
   actually hides it once the [hidden] attribute is toggled off by JS. */
.reply-attachment-chip[hidden] { display: none; }
.reply-attachment-chip {
    display: flex; align-items: center; gap: var(--space-2);
    background: var(--color-active-bg); border: 1px solid var(--color-primary);
    border-radius: var(--radius-input); padding: 6px 10px; margin: 0 0 var(--space-2);
    font-size: 13px; color: var(--color-text); max-width: fit-content;
}
.reply-attachment-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.reply-attachment-chip button { background: none; border: none; padding: 0; display: inline-flex; cursor: pointer; color: var(--color-text-muted); }
.reply-attachment-chip button svg { width: 14px; height: 14px; }
