/*!
 * Shahoosh application shell + component library — Phase 2.1
 * ---------------------------------------------------------------------------
 * Reusable across every future migrated tool. Nothing here is Trade-specific.
 * All colour/spacing/radius comes from tokens.css; no raw values below.
 * RTL-first: logical properties only (inline-start/end, never left/right).
 */

*,*::before,*::after{box-sizing:border-box}

/* `hidden` must win over any component display rule (the settings grid sets
   display:flex on .sh-field, which would otherwise reveal a hidden field). */
[hidden]{ display:none !important; }

/* Every inline icon is 20px unless a component overrides it. Without this an
   SVG with no intrinsic size stretches to fill its flex parent. */
svg{ inline-size:20px; block-size:20px; flex:0 0 auto; }

html{ -webkit-text-size-adjust:100%; }

body.sh-body{
  margin:0;
  min-height:100vh;
  /* Phase 2.13.19-MQA-B. 100vh on mobile is the LARGE viewport — it includes the
     browser chrome that is currently on screen, so the page is taller than what
     the user can see and the bottom of the layout falls below the fold. 100dvh
     tracks the visible height instead. The 100vh line above stays as the
     fallback for engines without dvh. */
  min-height:100dvh;
  background:var(--sh-bg);
  color:var(--sh-text);
  font-family:var(--sh-font);
  font-size:var(--sh-fs-base);
  line-height:var(--sh-lh-base);
  direction:rtl;
  text-align:start;
  overflow-x:hidden;
  transition:background var(--sh-dur-base) var(--sh-ease),
             color var(--sh-dur-base) var(--sh-ease);
}

/* Latin runs inside Persian text (model names, BTCUSDT, Binance, 1h …) */
.sh-ltr{ direction:ltr; unicode-bidi:isolate; display:inline-block; font-family:var(--sh-font-latin); }

:where(button,input,select,textarea){ font:inherit; color:inherit; }
:where(button){ cursor:pointer; }
:where(a){ color:inherit; }

:where(button,a,input,select,textarea,[tabindex]):focus-visible{
  outline:2px solid var(--sh-focus-ring);
  outline-offset:2px;
  border-radius:var(--sh-radius-field);
}

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

/* =========================================================== AppShell ==== */
.sh-shell{
  min-height:100vh;
  min-height:100dvh;          /* see body.sh-body */
  display:flex;
  flex-direction:column;
  padding-inline:var(--sh-gutter);
  padding-block:var(--sh-gutter) calc(var(--sh-space-6) + env(safe-area-inset-bottom, 0px));
  gap:var(--sh-space-6);
}

/* ============================================================= Header ==== */
/* A floating card on the tinted ground, not a full-bleed bar. [FIGMA] */
.sh-header{
  flex:0 0 auto;
  min-height:var(--sh-header-h);
  display:flex;
  align-items:center;
  gap:var(--sh-space-3);
  padding-inline:var(--sh-space-6);
  background:var(--sh-surface);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  box-shadow:var(--sh-shadow-card);
}
.sh-header__start{ display:flex; align-items:center; gap:var(--sh-space-2); }
.sh-header__brand{
  flex:1 1 auto;
  display:flex; align-items:center; justify-content:center; gap:var(--sh-space-2);
  font-weight:var(--sh-fw-black); font-size:var(--sh-fs-lg);
  letter-spacing:-.01em;
}
.sh-header__brand img{ display:block; height:34px; width:auto; }
/* The only wordmark asset the platform ships is a dark logotype. In dark mode
   it is flattened to solid white, matching the Figma header. The crown is a
   separate image so its gold is preserved. */
.sh-header__mark{ height:30px !important; }
.sh-header__word{ display:inline-flex; align-items:center; color:var(--sh-text); }
.sh-header__word svg{ inline-size:auto; block-size:30px; }
@media (max-width:640px){ .sh-header__word svg{ block-size:22px; } .sh-header__mark{ height:22px !important; } }
.sh-header__end{ display:flex; align-items:center; gap:var(--sh-space-2); }

.sh-iconbtn{
  inline-size:40px; block-size:40px;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-pill);
  color:var(--sh-text-muted);
  transition:background var(--sh-dur-fast) var(--sh-ease),
             color var(--sh-dur-fast) var(--sh-ease);
}
.sh-iconbtn:hover{ background:var(--sh-surface-3); color:var(--sh-text); }
.sh-iconbtn svg{ inline-size:20px; block-size:20px; }
.sh-iconbtn--ghost{ background:transparent; border-color:transparent; }

.sh-authbtn{
  height:var(--sh-control-h);
  padding-inline:var(--sh-space-5);
  display:inline-flex; align-items:center;
  background:var(--sh-brand); color:var(--sh-text-on-brand);
  border:0; border-radius:var(--sh-radius-pill);
  font-weight:var(--sh-fw-medium); font-size:var(--sh-fs-sm);
  white-space:nowrap;
}
.sh-authbtn:hover{ background:var(--sh-brand-hover); }
.sh-authbtn--ghost{
  background:transparent; color:var(--sh-text);
  border:1px solid var(--sh-border-strong);
}
.sh-authbtn--ghost:hover{ background:var(--sh-surface-2); }

/* ============================================================= Drawer ==== */
/* Right-hand drawer [FIGMA]. In RTL that is inline-start. */
.sh-drawer-scrim{
  position:fixed; inset:0;
  background:rgba(10,2,18,.45);
  opacity:0; pointer-events:none;
  transition:opacity var(--sh-dur-base) var(--sh-ease);
  z-index:40;
}
.sh-drawer-scrim.is-open{ opacity:1; pointer-events:auto; }

.sh-drawer{
  position:fixed;
  inset-block:var(--sh-gutter);
  inset-inline-start:var(--sh-gutter);   /* RTL → visually the right side */
  inline-size:var(--sh-drawer-w);
  max-inline-size:calc(100vw - (var(--sh-gutter) * 2));
  display:flex; flex-direction:column;
  gap:var(--sh-space-4);
  padding:var(--sh-space-5);
  background:var(--sh-surface);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  box-shadow:var(--sh-shadow-pop);
  overflow-y:auto;
  z-index:41;
  /* The closed state must not push the panel outside the viewport: a fixed
     element parked beyond the inline-end edge extends the scrollable area, and
     in an RTL document that shifts the whole page sideways. It is hidden with
     visibility + opacity and only nudged a little, so nothing ever overflows. */
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  /* nudge must stay inside the slack between the panel and the viewport
     edge, or it reintroduces the overflow it exists to avoid */
  transform:translateX(var(--sh-drawer-nudge, 16px)) scale(.99);
  transform-origin:100% 50%;
  transition:opacity var(--sh-dur-base) var(--sh-ease),
             transform var(--sh-dur-base) var(--sh-ease),
             visibility 0s linear var(--sh-dur-base);
}
.sh-drawer.is-open{
  opacity:1;
  visibility:visible;
  pointer-events:auto;
  transform:translateX(0) scale(1);
  transition:opacity var(--sh-dur-base) var(--sh-ease),
             transform var(--sh-dur-base) var(--sh-ease),
             visibility 0s;
}

.sh-drawer__user{
  display:flex; align-items:center; gap:var(--sh-space-3);
  padding-block-end:var(--sh-space-4);
  border-block-end:1px solid var(--sh-border);
}
.sh-drawer__avatar{
  inline-size:44px; block-size:44px; border-radius:var(--sh-radius-pill);
  background:var(--sh-surface-3); flex:0 0 auto;
  display:flex; align-items:center; justify-content:center;
  color:var(--sh-text-muted); font-weight:var(--sh-fw-bold);
  overflow:hidden;
}
.sh-drawer__avatar img{ inline-size:100%; block-size:100%; object-fit:cover; }
.sh-drawer__name{ font-weight:var(--sh-fw-bold); line-height:var(--sh-lh-tight); }
.sh-drawer__sub{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); }

/* Sign out (Phase 2.10). It sits in the user block, beside the close button —
   an action on the account, not another place to go, so it is deliberately not
   a row in the navigation list below. */
.sh-drawer__signout{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  flex:0 0 auto;
  padding:6px 12px;
  border-radius:var(--sh-radius-pill);
  border:1px solid var(--sh-border);
  background:var(--sh-surface-2);
  color:var(--sh-text-muted);
  font-size:var(--sh-fs-xs);
  font-weight:var(--sh-fw-medium);
  text-decoration:none;
  cursor:pointer;
  transition:background var(--sh-dur-fast) var(--sh-ease),
             color var(--sh-dur-fast) var(--sh-ease),
             border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-drawer__signout svg{ inline-size:16px; block-size:16px; flex:0 0 auto; }
.sh-drawer__signout:hover{
  background:var(--sh-danger-bg);
  border-color:var(--sh-danger-border);
  color:var(--sh-danger);
}
.sh-drawer__signout:focus-visible{ outline:2px solid var(--sh-focus-ring); outline-offset:2px; }
.sh-drawer__signout.is-busy{ opacity:.6; pointer-events:none; }
/* the label is the first thing to go when the drawer is narrow */
@media (max-width:360px){
  .sh-drawer__signout-text{ display:none; }
  .sh-drawer__signout{ padding:8px; }
}

.sh-drawer__section{ display:flex; flex-direction:column; gap:var(--sh-space-2); }
.sh-drawer__label{
  font-size:var(--sh-fs-xs); color:var(--sh-text-faint);
  font-weight:var(--sh-fw-medium); padding-inline:var(--sh-space-2);
}

/* Tool grid — 2 columns [FIGMA] */
.sh-drawer__grid{ display:grid; grid-template-columns:1fr 1fr; gap:var(--sh-space-2); }

.sh-tile{
  display:flex; align-items:center; justify-content:space-between; gap:var(--sh-space-2);
  padding:var(--sh-space-3) var(--sh-space-4);
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
  color:var(--sh-text); text-decoration:none;
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium);
  transition:background var(--sh-dur-fast) var(--sh-ease),
             border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-tile:hover{ background:var(--sh-surface-3); border-color:var(--sh-border-strong); }
.sh-tile.is-active{
  border-color:var(--sh-brand); background:var(--sh-surface-3);
  box-shadow:inset 0 0 0 1px var(--sh-brand);
}
.sh-tile svg{ inline-size:18px; block-size:18px; flex:0 0 auto; color:var(--sh-text-muted); }
.sh-tile.is-active svg{ color:var(--sh-brand-text); }

/* Row item — full width with a chevron */
.sh-drawer__row{
  display:flex; align-items:center; gap:var(--sh-space-3);
  padding:var(--sh-space-3) var(--sh-space-2);
  color:var(--sh-text); text-decoration:none;
  border-radius:var(--sh-radius-field);
  font-size:var(--sh-fs-sm);
  transition:background var(--sh-dur-fast) var(--sh-ease);
}
.sh-drawer__row:hover{ background:var(--sh-surface-2); }
.sh-drawer__row svg{ inline-size:18px; block-size:18px; color:var(--sh-text-muted); flex:0 0 auto; }
.sh-drawer__row-text{ flex:1 1 auto; }
/* chevron points toward the inline-start edge in RTL [FIGMA] */
.sh-drawer__chev{ color:var(--sh-text-faint); transform:scaleX(1); }

.sh-drawer__spacer{ flex:1 1 auto; min-block-size:var(--sh-space-2); }

/* ====================================================== CreditDisplay ==== */
.sh-credit{
  display:flex; align-items:center; justify-content:space-between; gap:var(--sh-space-3);
  padding:var(--sh-space-3) var(--sh-space-4);
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
}
.sh-credit__label{ display:flex; align-items:center; gap:var(--sh-space-2);
  font-size:var(--sh-fs-sm); color:var(--sh-text-muted); }
.sh-credit__label svg{ inline-size:18px; block-size:18px; }
.sh-credit__value{ font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-sm); }
.sh-credit__value small{ display:block; font-size:var(--sh-fs-xs);
  font-weight:var(--sh-fw-regular); color:var(--sh-text-faint); }
.sh-credit.is-low  .sh-credit__value{ color:var(--sh-warn); }
.sh-credit.is-error .sh-credit__value{ color:var(--sh-danger); }

.sh-cta-gold{
  display:flex; align-items:center; justify-content:center; gap:var(--sh-space-2);
  min-block-size:48px; padding-inline:var(--sh-space-5);
  background:var(--sh-gold); color:var(--sh-text-on-gold);
  border:0; border-radius:var(--sh-radius-field);
  font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-sm);
  text-decoration:none;
}
.sh-cta-gold svg{ inline-size:20px; block-size:20px; }
.sh-cta-gold:hover{ background:var(--sh-gold-strong); }

/* =============================================================== Main ==== */
.sh-main{
  flex:1 1 auto;
  display:flex; flex-direction:column;
  align-items:center;
  gap:var(--sh-space-6);
  inline-size:100%;
}
.sh-col{ inline-size:100%; max-inline-size:var(--sh-content-max); }

/* Phase 2.13.19-MQA-B — composer sits at the bottom of the usable viewport.
   .sh-main is already `flex:1 1 auto` in a column shell, so it DOES fill the
   space under the header. What it did not do was pass that height on: .sh-col
   and each route's wrapper <div> were both `flex:0 1 auto`, so they took content
   height, sat at the top, and every leftover pixel piled up BELOW the composer —
   measured at 216px on a 390px-wide phone and 134px at 360px.

   The chain is made to stretch and the composer takes the bottom of it with an
   auto margin. An auto margin collapses to zero once the thread is long enough
   to fill the column, so a long conversation still scrolls normally — no fixed
   height, no sticky positioning, no second scroll container.

   `min-block-size:0` on each link of the chain is what lets a long thread shrink
   and scroll instead of forcing the page taller.

   Scoped to the 640px breakpoint the rest of this sheet already uses. The defect
   was reported on mobile, and desktop composition is a Wave-8-verified layout
   that this phase has no reason to move: measured, desktop is byte-for-byte
   unchanged by this rule. Lifting the media query is a one-line change if the
   bottom-anchored composer is wanted on desktop too. */
@media (max-width:640px){
  .sh-main > .sh-col{
    flex:1 1 auto;
    display:flex; flex-direction:column;
    min-block-size:0;
  }
  /* Every conversational route hands the shell one wrapper <div>. Routes that
     pass a different shape simply do not match, and keep their existing layout. */
  .sh-main > .sh-col > div:only-child{
    flex:1 1 auto;
    display:flex; flex-direction:column;
    min-block-size:0;
  }
  .sh-main .sh-composer{ margin-block-start:auto; }
}

/* Phase 2.13.19-MQA-C — on mobile the THREAD scrolls, not the page.
   ---------------------------------------------------------------------------
   MQA-B anchored the composer to the bottom but left the document as the scroll
   container: .sh-shell is `min-height:100dvh`, and a min-height permits growth.
   So once the conversation passed one screen the shell simply got taller, the
   document scrolled, and the header scrolled away with it.

   Nothing in the chain had to shrink, because the chain had no ceiling. Adding
   `overflow-y:auto` to the thread alone would have done nothing for the same
   reason — the thread was never the element being asked to give up space.

   So the shell gets a real height on mobile, every link between it and the
   thread is allowed to shrink (`min-block-size:0`), the header and composer are
   pinned out of the shrink (`flex:0 0 auto`), and the thread takes the leftover
   and scrolls inside it.

   `:has(.sh-thread)` keeps this to conversational screens only. /app/account/,
   /app/tickets/, /app/plans/ and /app/checkout/ have no .sh-thread — verified in
   the live DOM, not assumed — so they never match and keep normal page scroll.
   A browser without :has() support simply falls back to MQA-B behaviour. */
/* Breakpoint: the same <=640px this sheet uses elsewhere, so desktop is
   untouched. A height branch for landscape was tried and removed: at 844x390 the
   hero (310px) and composer (209px) alone exceed the 237px the column can offer,
   so constraining there starves the thread to nothing. Landscape keeps the page
   scroll it already had, which at least reaches every message. */
@media (max-width:640px){
  /* The SHELL gets the fixed height; the BODY deliberately does not.
     Several workspaces have chrome taller than a small viewport on their own —
     measured at 360x780: video hero 275 + composer 467, trade 249 + 685, against
     ~668px of usable column. Pinning the body to 100dvh as well would CLIP that
     chrome with no way to reach it. Leaving the body unbounded means the shell's
     overflow still contributes to the page's scrollable area, so those screens
     degrade to the page scroll they already had instead of losing content. */
  .sh-shell:has(.sh-main .sh-thread){
    min-height:0;
    height:100dvh;
  }
  .sh-shell:has(.sh-main .sh-thread) > .sh-header{ flex:0 0 auto; }

  /* The whole chain is restated here rather than leaning on the MQA-B block
     above: min-block-size alone is inert unless the element is also a flex item
     in a flex column. Landscape proved that — the wrapper kept its block layout
     and simply grew to 4040px while the column sat at 237px. */
  .sh-main:has(.sh-thread){ min-block-size:0; }
  .sh-main > .sh-col:has(.sh-thread){
    flex:1 1 auto; display:flex; flex-direction:column; min-block-size:0;
  }
  .sh-main > .sh-col > div:only-child:has(.sh-thread){
    flex:1 1 auto; display:flex; flex-direction:column; min-block-size:0;
  }

  /* The composer keeps MQA-B's auto top margin, so an EMPTY thread still pushes
     it to the bottom; it is simply no longer allowed to be shrunk away. */
  .sh-main:has(.sh-thread) .sh-composer{ flex:0 0 auto; }

  /* Once a conversation exists the hero is the empty state and has done its job.
     app/chat and app/face already hide it in JS on the first turn — this applies
     the same rule on mobile to the routes that do not, purely so the thread has
     room: the hero is 249-310px, which is the difference between a usable thread
     and a sliver on a 360px-tall column. Desktop keeps the hero throughout. */
  .sh-main > .sh-col > div:only-child:has(.sh-thread:not(:empty)) > .sh-hero{
    display:none;
  }

  /* The one scroll container. `:not(:empty)` because an empty thread is
     display:none and must not claim the leftover space — that is what keeps the
     MQA-B empty and short-thread composition intact.

     THE FLOOR IS LOAD-BEARING  (Phase 2.13.19-MQA-AA1)
     -----------------------------------------------------------------------
     This rule used to say `min-block-size:0`, which let the thread shrink to
     NOTHING. On a tool whose composer is taller than the column, it did.
     MEASURED on the real Trade page the moment the thread stopped being empty:

         viewport    composer    thread    thread content
         360x780      685px       0px        1133px
         390x844      662px       1px        1107px
         430x932      662px      86px        1034px
         600x900      662px      55px         891px

     Trade's composer is nine fields, a file picker, a hint bar and an action
     row. `.sh-composer{flex:0 0 auto}` correctly refuses to shrink it, so the
     whole deficit landed on the only flexible item in the column — the thread —
     and `min-block-size:0` permitted exactly that. `overflow-y:auto` then
     clipped the entire conversation into a 0-1px strip.
     `elementFromPoint` at the card's own coordinates returned
     `.sh-composer__title`: the analysis was painted underneath the composer.

     The owner's report is this arithmetic, exactly: the hero
     (`display:none` once the thread is non-empty, two rules above) vanished,
     and nothing replaced it, because what replaced it was one pixel tall.
     Desktop never showed it — above 640px none of this block applies, which is
     why a desktop-only verification passed while the product was broken.

     A floor fixes it without changing anyone's layout or UX. The thread still
     scrolls internally, because 40dvh is far below the content height; it
     simply can no longer be squeezed out of existence. The column then
     overflows the shell, and the page regains the scroll this sheet
     deliberately preserves for exactly this case ("Leaving the body unbounded
     means the shell's overflow still contributes to the page's scrollable
     area"). That degradation was always the intended fallback — the thread
     collapsing to zero was not.

     40dvh is the smallest value that clears the measured content: a user
     message is 85px and the result card's head plus opening paragraph is
     ~250px, so 312-373px across the phones above shows a readable answer
     rather than a sliver. Tools with a short composer are unaffected — chat's
     thread already exceeds the floor, so the floor never binds. */
  .sh-main .sh-thread:not(:empty){
    flex:1 1 auto;
    min-block-size:40dvh;
    overflow-y:auto;
    -webkit-overflow-scrolling:touch;   /* momentum scrolling on iOS */
    overscroll-behavior-y:contain;      /* a thread overscroll must not drag the page */
  }
}

/* ============================================================= Hero ====== */
.sh-hero{ display:flex; flex-direction:column; align-items:center; gap:var(--sh-space-3);
  text-align:center; padding-block:var(--sh-space-8); }
.sh-hero__owl{ inline-size:190px; block-size:190px; object-fit:contain; }
.sh-hero__title{ margin:0; font-size:var(--sh-fs-xl); font-weight:var(--sh-fw-black); }
.sh-hero__sub{ margin:0; color:var(--sh-text-muted); font-size:var(--sh-fs-base); }

/* ================================================== ConversationThread === */
.sh-thread{ display:flex; flex-direction:column; gap:var(--sh-space-5); inline-size:100%; }
/* The thread and the composer are plain siblings in normal flow, and the
   thread carried no bottom margin — so every generated message, result card,
   loader and error notice sat flush against the composer. The gap is applied
   only when the thread actually holds content, and scales with viewport
   height rather than being one fixed value that suits a single screen. */
.sh-thread:empty{ display:none; }
.sh-thread:not(:empty){ margin-block-end:var(--sh-thread-gap); }

.sh-msg{ display:flex; gap:var(--sh-space-3); align-items:flex-start; }
.sh-msg__avatar{
  inline-size:40px; block-size:40px; border-radius:var(--sh-radius-pill);
  background:var(--sh-surface-3); flex:0 0 auto; overflow:hidden;
  display:flex; align-items:center; justify-content:center;
  color:var(--sh-text-muted);
}
.sh-msg__avatar svg{ inline-size:20px; block-size:20px; }

/* Phase 2.13.19-MQA-B — WHICH SIDE a turn sits on is decided by its ROLE, and
   nothing else. The row therefore lays out on a fixed axis: with the row left to
   the page's RTL direction, a message whose first strong character is Latin
   flipped the whole row, so an English user turn drifted to the left. Direction
   is restored inside .sh-bubble, where it belongs — alignment and text direction
   are separate concerns.

   DOM order is already correct and unchanged (user: bubble then avatar; bot:
   avatar then bubble), so on a fixed LTR axis each row reads outward from its
   own edge:  [bubble][avatar] hugging the right   /   [avatar][bubble] hugging
   the left. Only the packing edge differs.

   Before this, both roles used justify-content:flex-start, which put the USER's
   avatar to the left of their bubble and the ASSISTANT's avatar at the far
   right — each avatar on the opposite side from its own bubble. */
.sh-msg{ direction:ltr; }

/* User message — brand-accent bubble, hugging the right with its avatar outermost */
.sh-msg--user{ flex-direction:row; }
.sh-msg--user .sh-bubble{
  background:var(--sh-accent); color:#fff;
  border-radius:var(--sh-radius-panel);
  padding:var(--sh-space-4) var(--sh-space-5);
  max-inline-size:78%;
}
.sh-msg--user{ justify-content:flex-end; }

/* Assistant message — surface card, hugging the left with its avatar outermost */
.sh-msg--bot{ justify-content:flex-start; }
.sh-msg--bot .sh-bubble{
  background:var(--sh-surface);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-panel);
  padding:var(--sh-space-5);
  inline-size:100%;
  box-shadow:var(--sh-shadow-card);
}
/* The row is laid out on a fixed axis (above); the CONTENT goes back to the
   product's direction. `plaintext` gives each paragraph first-strong behaviour,
   the same convention already used by .sh-n8n-text: Persian reads RTL, English
   reads LTR, and a mixed line follows its first strong character — without any
   of that moving the bubble to the other side of the thread. */
.sh-bubble{ direction:rtl; }
/* unicode-bidi does not inherit, and userMessage() wraps its text in a plain
   <div>, so the rule has to name that holder too or the paragraph keeps the
   page direction and an English turn renders as an RTL paragraph. */
.sh-msg--user .sh-bubble,
.sh-msg--user .sh-bubble > div{ unicode-bidi:plaintext; text-align:start; }
.sh-bubble p{ margin:0 0 var(--sh-space-3); }
.sh-bubble p:last-child{ margin-block-end:0; }

/* ====================================================== LoadingState ===== */
.sh-loading{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:var(--sh-space-3);
  min-block-size:260px;
  background:var(--sh-surface-4);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-panel);
  color:var(--sh-text-muted); font-size:var(--sh-fs-sm);
}
/* Indeterminate only — the backend returns no percentage and no ETA. */
.sh-owl-spin{ position:relative; inline-size:92px; block-size:92px; }
.sh-owl-spin img{ inline-size:56px; block-size:56px; object-fit:contain;
  position:absolute; inset:50% auto auto 50%; transform:translate(-50%,-50%); opacity:.9; }
.sh-owl-spin::after{
  content:''; position:absolute; inset:0;
  border-radius:var(--sh-radius-pill);
  border:3px solid var(--sh-border-strong);
  border-block-start-color:var(--sh-accent);
  animation:sh-spin 900ms linear infinite;
}
@keyframes sh-spin{ to{ transform:rotate(360deg); } }
@media (prefers-reduced-motion:reduce){
  .sh-owl-spin::after{ animation-duration:2400ms; }
}

/* ============================================================ Notice ===== */
.sh-notice{
  display:flex; gap:var(--sh-space-3); align-items:flex-start;
  padding:var(--sh-space-4) var(--sh-space-5);
  border-radius:var(--sh-radius-panel);
  border:1px solid var(--sh-border);
  background:var(--sh-surface-2);
  font-size:var(--sh-fs-sm);
}
.sh-notice svg{ inline-size:20px; block-size:20px; flex:0 0 auto; margin-block-start:2px; }
.sh-notice__body{ flex:1 1 auto; }
.sh-notice__title{ font-weight:var(--sh-fw-bold); margin-block-end:var(--sh-space-1); }
.sh-notice__actions{ display:flex; gap:var(--sh-space-2); margin-block-start:var(--sh-space-3); flex-wrap:wrap; }
.sh-notice--error{ background:var(--sh-danger-bg); border-color:var(--sh-danger-border); color:var(--sh-danger); }
.sh-notice--error .sh-notice__body{ color:var(--sh-text); }
.sh-notice--warn { background:var(--sh-warn-bg);  border-color:var(--sh-warn-border);  color:var(--sh-warn); }
.sh-notice--warn  .sh-notice__body{ color:var(--sh-text); }
.sh-notice--ok   { background:var(--sh-ok-bg);    border-color:var(--sh-ok-border);    color:var(--sh-ok); }
.sh-notice--ok    .sh-notice__body{ color:var(--sh-text); }

.sh-btn-sm{
  height:34px; padding-inline:var(--sh-space-4);
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  border-radius:var(--sh-radius-pill);
  border:1px solid var(--sh-border-strong);
  background:var(--sh-surface);
  color:var(--sh-text);
  font-size:var(--sh-fs-xs); font-weight:var(--sh-fw-medium);
  text-decoration:none;
}
.sh-btn-sm:hover{ background:var(--sh-surface-2); }
.sh-btn-sm--brand{ background:var(--sh-brand); border-color:var(--sh-brand); color:var(--sh-text-on-brand); }
.sh-btn-sm--brand:hover{ background:var(--sh-brand-hover); }

/* =========================================================== Composer ==== */
.sh-composer{
  inline-size:100%;
  background:var(--sh-surface);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  box-shadow:var(--sh-shadow-card);
  padding:var(--sh-space-5);
  display:flex; flex-direction:column; gap:var(--sh-space-4);
}
.sh-composer__title{ font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-md); }

/* Settings grid — label above control, wraps down to 1 column on mobile */
/* Figma lays the settings out as one dense row of pills across the composer.
   auto-fit + a small min keeps that on desktop and reflows down to two columns
   on mobile without a separate layout. */
.sh-fields{
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(104px, 1fr));
  gap:var(--sh-space-3) var(--sh-space-2);
}
.sh-field{ display:flex; flex-direction:column; gap:var(--sh-space-2); min-inline-size:0; }
/* Inside a modal the columns get a larger minimum. A select reserves 38px for
   its chevron, so at the 104px the page grid uses, Persian option labels like
   «جزئیات زیاد» truncate to «جزئیات ز…». The page-level grid is left alone —
   Trade lays its 11 fields out across the full card width, where 104px is right. */
.sh-modal .sh-fields{ grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); }

.sh-field__label{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); font-weight:var(--sh-fw-medium); }

.sh-select, .sh-input{
  inline-size:100%; min-inline-size:0;
  height:var(--sh-control-h);
  padding-inline:var(--sh-space-4);
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-pill);
  color:var(--sh-text);
  font-size:var(--sh-fs-input);   /* R5.7 iOS focus-zoom floor */
  transition:border-color var(--sh-dur-fast) var(--sh-ease),
             background var(--sh-dur-fast) var(--sh-ease);
}
.sh-select:hover, .sh-input:hover{ border-color:var(--sh-border-strong); }
.sh-input::placeholder{ color:var(--sh-text-faint); }
/* A field value belongs to the form, not to its own language: inside an RTL
   form the value must start at the same edge as the label. The input keeps
   dir="auto" so a Latin ticker still renders as a proper LTR run — only the
   box alignment follows the form. :dir() is the logical way to ask "did this
   content resolve LTR?"; where it is unsupported the rule simply no-ops.
   Prose is different and deliberately excluded: the prompt textarea keeps
   aligning with the language the user is writing in. */
[dir="rtl"] .sh-input:dir(ltr){ text-align:end; }
/* native chevron replaced so it sits on the correct RTL edge */
/* Native chevron removed so the marker sits on the correct RTL edge. The
   SVG is inlined as a data URI and tinted via --sh-chevron. */
.sh-select{
  appearance:none;
  --sh-chevron:%236B6383;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%236B6383' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:left var(--sh-space-4) center;
  background-size:12px 8px;
  /* The chevron sits at the inline-END (physical left in RTL), so the room for
     it belongs to padding-inline-END. These two were the wrong way round: the
     value was pushed 38px off the inline-start edge — reading as centred — and
     had only 12px before the chevron, which it collided with. */
  text-align:start;
  padding-inline-start:var(--sh-space-4);
  padding-inline-end:calc(var(--sh-space-4) + 22px);
  text-overflow:ellipsis;
}
:root[data-theme="dark"] .sh-select,
html:not([data-theme="light"]) .sh-select{ }
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .sh-select{
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%23B9AFD0' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  }
}
:root[data-theme="dark"] .sh-select{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%23B9AFD0' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.sh-select option{ background:var(--sh-surface); color:var(--sh-text); }

/* Prompt / free-text area */
.sh-prompt{
  inline-size:100%;
  min-block-size:96px;
  resize:vertical;
  padding:var(--sh-space-4) var(--sh-space-5);
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-panel);
  color:var(--sh-text);
  font-size:var(--sh-fs-base);
  line-height:var(--sh-lh-base);
}
.sh-prompt::placeholder{ color:var(--sh-text-faint); }

/* File picker — the native control renders an un-localised English button,
   so it is visually replaced while staying a real <input type=file>. */
.sh-file{ position:relative; display:block; }
.sh-file input[type=file]{
  position:absolute; inset:0; inline-size:100%; block-size:100%;
  opacity:0; cursor:pointer;
}
.sh-file__face{
  display:flex; align-items:center; gap:var(--sh-space-3);
  min-block-size:var(--sh-control-h);
  padding-inline:var(--sh-space-4);
  background:var(--sh-surface-2);
  border:1px dashed var(--sh-border-strong);
  border-radius:var(--sh-radius-pill);
  color:var(--sh-text-muted); font-size:var(--sh-fs-sm);
}
.sh-file input[type=file]:focus-visible + .sh-file__face{
  outline:2px solid var(--sh-focus-ring); outline-offset:2px;
}
.sh-file:hover .sh-file__face{ border-color:var(--sh-brand); color:var(--sh-text); }

/* Read-only informational strip (e.g. "results appear here") */
.sh-hintbar{
  padding:var(--sh-space-3) var(--sh-space-5);
  background:var(--sh-surface-2);
  border:1px dashed var(--sh-border-strong);
  border-radius:var(--sh-radius-panel);
  color:var(--sh-text-muted);
  font-size:var(--sh-fs-sm);
}

/* Action row — primary action at inline-end (RTL: visually left) [FIGMA] */
.sh-actions{
  display:flex; align-items:center; gap:var(--sh-space-3);
  flex-wrap:wrap-reverse;
}
.sh-actions__spacer{ flex:1 1 auto; }

.sh-btn{
  height:var(--sh-control-h);
  padding-inline:var(--sh-space-6);
  display:inline-flex; align-items:center; justify-content:center; gap:var(--sh-space-2);
  background:var(--sh-brand); color:var(--sh-text-on-brand);
  border:0; border-radius:var(--sh-radius-pill);
  font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-sm);
  white-space:nowrap;
  transition:background var(--sh-dur-fast) var(--sh-ease);
}
.sh-btn:hover:not(:disabled){ background:var(--sh-brand-hover); }
.sh-btn:active:not(:disabled){ background:var(--sh-brand-active); }
.sh-btn:disabled{ opacity:.5; cursor:not-allowed; }
.sh-btn__cost{
  font-weight:var(--sh-fw-regular); font-size:var(--sh-fs-xs);
  opacity:.85; padding-inline-start:var(--sh-space-2);
  border-inline-start:1px solid rgba(255,255,255,.35);
  margin-inline-start:var(--sh-space-1);
}
.sh-btn--ghost{
  background:var(--sh-surface-2); color:var(--sh-text);
  border:1px solid var(--sh-border-strong);
}
.sh-btn--ghost:hover:not(:disabled){ background:var(--sh-surface-3); }

/* Toggle row (e.g. fundamental analysis) [FIGMA] */
.sh-toggle{ display:inline-flex; align-items:center; gap:var(--sh-space-3); user-select:none; }
.sh-toggle__label{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); }
.sh-toggle input{ position:absolute; opacity:0; inline-size:1px; block-size:1px; }
.sh-toggle__track{
  --sh-toggle-w:44px; --sh-toggle-thumb:18px; --sh-toggle-pad:3px; --sh-toggle-bd:1px;
  /* the thumb is positioned against the PADDING box, so the border has to come
     out of the travel or the knob overshoots the far edge by 2px */
  --sh-toggle-travel:calc(var(--sh-toggle-w) - var(--sh-toggle-bd) * 2
                          - var(--sh-toggle-thumb) - var(--sh-toggle-pad) * 2);
  /* same signed-direction trick as .sh-switch: translateX is physical, so the
     sign has to flip with the writing direction for the movement to be logical */
  --sh-toggle-dir:-1;
  inline-size:var(--sh-toggle-w); block-size:26px; border-radius:var(--sh-radius-pill);
  background:var(--sh-surface-3); border:1px solid var(--sh-border);
  position:relative; flex:0 0 auto;
  transition:background var(--sh-dur-fast) var(--sh-ease);
}
[dir="rtl"] .sh-toggle__track{ --sh-toggle-dir:1; }
.sh-toggle__track::after{
  content:''; position:absolute; inset-block-start:var(--sh-toggle-pad);
  inset-inline-end:var(--sh-toggle-pad);
  inline-size:var(--sh-toggle-thumb); block-size:var(--sh-toggle-thumb);
  border-radius:var(--sh-radius-pill);
  background:var(--sh-text-faint);
  transition:transform var(--sh-dur-fast) var(--sh-ease), background var(--sh-dur-fast) var(--sh-ease);
}
.sh-toggle input:checked + .sh-toggle__track{ background:var(--sh-brand); border-color:var(--sh-brand); }
/* Off rests at the inline-end edge (physical left in RTL); on travels to the
   inline-start edge. Previously it rested at inline-START and moved a bare
   -18px, so in RTL it ran right-to-left — the opposite of .sh-switch in the
   same product. The distance was right; the direction and the resting edge
   were not. Travel is now derived from the geometry, minus the border, since
   the thumb is positioned against the padding box. */
.sh-toggle input:checked + .sh-toggle__track::after{
  transform:translateX(calc(var(--sh-toggle-dir) * var(--sh-toggle-travel)));
  background:#fff;
}
.sh-toggle input:focus-visible + .sh-toggle__track{ outline:2px solid var(--sh-focus-ring); outline-offset:2px; }

/* Symbol chip shown above the hero [FIGMA] */
.sh-chip{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  height:38px; padding-inline:var(--sh-space-5);
  background:var(--sh-surface); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-pill);
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium);
  box-shadow:var(--sh-shadow-card);
}
.sh-chip__dot{ inline-size:8px; block-size:8px; border-radius:var(--sh-radius-pill);
  background:var(--sh-gold); flex:0 0 auto; }

/* ======================================================= Result card ===== */
.sh-result{ display:flex; flex-direction:column; gap:var(--sh-space-4); }
.sh-result__head{ display:flex; align-items:center; gap:var(--sh-space-3); flex-wrap:wrap; }
.sh-badge{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  height:30px; padding-inline:var(--sh-space-4);
  border-radius:var(--sh-radius-pill);
  font-size:var(--sh-fs-xs); font-weight:var(--sh-fw-bold);
  border:1px solid var(--sh-border);
  background:var(--sh-surface-2);
}
.sh-badge--long { background:var(--sh-ok-bg);     border-color:var(--sh-ok-border);     color:var(--sh-ok); }
.sh-badge--short{ background:var(--sh-danger-bg); border-color:var(--sh-danger-border); color:var(--sh-danger); }

.sh-stats{ display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:var(--sh-space-3); }
.sh-stat{
  padding:var(--sh-space-4);
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
}
.sh-stat__k{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); }
.sh-stat__v{ font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-md); margin-block-start:var(--sh-space-1); }

.sh-meter{ block-size:8px; border-radius:var(--sh-radius-pill); background:var(--sh-surface-3); overflow:hidden; }
.sh-meter__fill{ block-size:100%; background:var(--sh-accent); border-radius:var(--sh-radius-pill); }

.sh-levels{ display:flex; flex-wrap:wrap; gap:var(--sh-space-2); }
.sh-level{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  height:30px; padding-inline:var(--sh-space-3);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-pill); font-size:var(--sh-fs-xs);
}

/* ======================================================= Toast stack ===== */
.sh-toasts{
  position:fixed; inset-block-end:var(--sh-space-6);
  inset-inline-start:50%; transform:translateX(50%);
  display:flex; flex-direction:column; gap:var(--sh-space-2);
  z-index:60; pointer-events:none; inline-size:min(520px, calc(100vw - 32px));
}
.sh-toast{
  pointer-events:auto;
  padding:var(--sh-space-3) var(--sh-space-5);
  border-radius:var(--sh-radius-pill);
  background:var(--sh-surface); border:1px solid var(--sh-border);
  box-shadow:var(--sh-shadow-pop); font-size:var(--sh-fs-sm);
  animation:sh-toast-in var(--sh-dur-base) var(--sh-ease);
}
.sh-toast--error{ border-color:var(--sh-danger-border); }
@keyframes sh-toast-in{ from{ opacity:0; transform:translateY(8px); } }

/* ====================================================== Tablet [DECISION] */
@media (max-width:1024px){
  .sh-header__brand{ justify-content:flex-start; }
  .sh-hero__owl{ inline-size:150px; block-size:150px; }
}

/* ======================================================== Mobile [FIGMA]  */
@media (max-width:640px){
  .sh-shell{ gap:var(--sh-space-4); padding-block-start:var(--sh-space-3); }
  .sh-header{ padding-inline:var(--sh-space-3); gap:var(--sh-space-2); }
  .sh-header__brand{ font-size:var(--sh-fs-base); }
  .sh-header__brand img{ height:26px; }
  .sh-iconbtn{ inline-size:36px; block-size:36px; }
  .sh-authbtn{ height:36px; padding-inline:var(--sh-space-3); font-size:var(--sh-fs-xs); }

  .sh-drawer{ inset-block:var(--sh-space-3); inset-inline-start:var(--sh-space-3);
              inline-size:calc(100vw - (var(--sh-space-3) * 2));
              --sh-drawer-nudge:6px; }

  .sh-hero{ padding-block:var(--sh-space-5); }
  .sh-hero__owl{ inline-size:120px; block-size:120px; }

  .sh-composer{ padding:var(--sh-space-4); }
  .sh-fields{ grid-template-columns:1fr 1fr; }
  .sh-msg--user .sh-bubble{ max-inline-size:86%; }
  .sh-msg__avatar{ inline-size:32px; block-size:32px; }
  .sh-actions{ gap:var(--sh-space-2); }
  .sh-btn{ inline-size:100%; }
  .sh-actions__spacer{ display:none; }
  .sh-stats{ grid-template-columns:1fr 1fr; }
}

/* ======================================================= Upload (2.2) ==== */
.sh-up{ display:flex; flex-direction:column; gap:var(--sh-space-2); min-inline-size:0; }
.sh-up__face{
  position:relative; display:flex; align-items:center; gap:var(--sh-space-2);
  min-block-size:var(--sh-control-h); padding-inline:var(--sh-space-4);
  background:var(--sh-surface-2); border:1px dashed var(--sh-border-strong);
  border-radius:var(--sh-radius-pill); color:var(--sh-text-muted);
  font-size:var(--sh-fs-sm); cursor:pointer; overflow:hidden;
  transition:border-color var(--sh-dur-fast) var(--sh-ease), color var(--sh-dur-fast) var(--sh-ease);
}
.sh-up__face:hover{ border-color:var(--sh-brand); color:var(--sh-text); }
.sh-up__text{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.sh-up__opt{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); margin-inline-start:auto; }
.sh-up__input{ position:absolute; inset:0; opacity:0; cursor:pointer; inline-size:100%; block-size:100%; }
.sh-up__face:focus-within{ outline:2px solid var(--sh-focus-ring); outline-offset:2px; }

.sh-up__preview{
  display:flex; align-items:center; gap:var(--sh-space-3);
  padding:var(--sh-space-2) var(--sh-space-3);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field); min-inline-size:0;
}
.sh-up__media{ inline-size:44px; block-size:44px; border-radius:var(--sh-radius-field);
  overflow:hidden; flex:0 0 auto; background:var(--sh-surface-3); }
.sh-up__media img, .sh-up__media video{ inline-size:100%; block-size:100%; object-fit:cover; display:block; }
.sh-up__name{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); min-inline-size:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sh-up__spacer{ flex:1 1 auto; }
.sh-up__act{
  inline-size:32px; block-size:32px; flex:0 0 auto;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:var(--sh-radius-pill); border:1px solid var(--sh-border);
  background:var(--sh-surface); color:var(--sh-text-muted);
}
.sh-up__act:hover{ background:var(--sh-surface-3); color:var(--sh-text); }
.sh-up__act svg{ inline-size:16px; block-size:16px; }
.sh-up__act--danger:hover{ color:var(--sh-danger); border-color:var(--sh-danger-border); }
.sh-up__err{ font-size:var(--sh-fs-xs); color:var(--sh-danger); padding-inline:var(--sh-space-3); }

/* Keyframe pair — Figma groups first + last frame in one container */
.sh-keyframes{
  display:grid; grid-template-columns:1fr 1fr; gap:var(--sh-space-3);
  padding:var(--sh-space-3);
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border-strong);
  border-radius:var(--sh-radius-panel);
}
.sh-keyframes__title{
  grid-column:1 / -1; font-size:var(--sh-fs-xs); color:var(--sh-text-muted);
  font-weight:var(--sh-fw-medium);
}

/* Reference tray — [DECISION] not covered by Figma; same vocabulary as above */
.sh-reftray{
  display:flex; flex-direction:column; gap:var(--sh-space-3);
  padding:var(--sh-space-3);
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border-strong);
  border-radius:var(--sh-radius-panel);
}
.sh-reftray__head{ display:flex; align-items:center; gap:var(--sh-space-2);
  font-size:var(--sh-fs-xs); color:var(--sh-text-muted); font-weight:var(--sh-fw-medium); }
.sh-reftray__count{ color:var(--sh-text-faint); }
.sh-reftray__grid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(150px,1fr)); gap:var(--sh-space-2); }

/* Model picker */
.sh-modelbar{ display:flex; align-items:center; gap:var(--sh-space-3); flex-wrap:wrap; }
.sh-modelbtn{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  height:var(--sh-control-h); padding-inline:var(--sh-space-4);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-pill); color:var(--sh-text);
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium); max-inline-size:100%;
}
.sh-modelbtn:hover{ border-color:var(--sh-brand); }
.sh-modelbtn__name{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sh-modelbtn__dot{ inline-size:8px; block-size:8px; border-radius:var(--sh-radius-pill);
  background:var(--sh-gold); flex:0 0 auto; }

.sh-sheet-scrim{ position:fixed; inset:0; background:rgba(10,2,18,.5); z-index:50;
  opacity:0; pointer-events:none; transition:opacity var(--sh-dur-base) var(--sh-ease); }
.sh-sheet-scrim.is-open{ opacity:1; pointer-events:auto; }
.sh-sheet{
  position:fixed; inset-block:5vh; inset-inline:0; margin-inline:auto;
  inline-size:min(760px, calc(100vw - 32px));
  display:flex; flex-direction:column; gap:var(--sh-space-4);
  padding:var(--sh-space-5);
  background:var(--sh-surface); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card); box-shadow:var(--sh-shadow-pop);
  z-index:51; opacity:0; visibility:hidden; transform:translateY(12px);
  transition:opacity var(--sh-dur-base) var(--sh-ease), transform var(--sh-dur-base) var(--sh-ease),
             visibility 0s linear var(--sh-dur-base);
}
.sh-sheet.is-open{ opacity:1; visibility:visible; transform:none;
  transition:opacity var(--sh-dur-base) var(--sh-ease), transform var(--sh-dur-base) var(--sh-ease), visibility 0s; }
.sh-sheet__head{ display:flex; align-items:center; gap:var(--sh-space-3); }
.sh-sheet__title{ font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-md); }
.sh-sheet__filters{ display:flex; gap:var(--sh-space-2); flex-wrap:wrap; }
.sh-filter{
  height:32px; padding-inline:var(--sh-space-4);
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  border-radius:var(--sh-radius-pill); border:1px solid var(--sh-border);
  background:var(--sh-surface-2); color:var(--sh-text-muted); font-size:var(--sh-fs-xs);
}
.sh-filter.is-active{ background:var(--sh-brand); border-color:var(--sh-brand); color:var(--sh-text-on-brand); }
/* Phase 2.13.19-MQA-M — generation-mode icon inside a filter chip. The chip is
   already inline-flex with a logical gap, so the icon needs a size and nothing
   else: no positioning, and RTL puts it at the inline-start on its own. It must
   not shrink, or a long Persian label would squash it into a sliver. */
.sh-filter svg{ inline-size:16px; block-size:16px; flex:0 0 auto; }
.sh-sheet__list{ overflow-y:auto; display:grid;
  grid-template-columns:repeat(auto-fill,minmax(230px,1fr)); gap:var(--sh-space-2); }
.sh-mcard{
  display:flex; flex-direction:column; gap:var(--sh-space-2); align-items:stretch;
  padding:var(--sh-space-3) var(--sh-space-4); text-align:start;
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field); color:var(--sh-text);
}
.sh-mcard:hover{ border-color:var(--sh-brand); background:var(--sh-surface-3); }
.sh-mcard.is-active{ border-color:var(--sh-brand); box-shadow:inset 0 0 0 1px var(--sh-brand); }
.sh-mcard__top{ display:flex; align-items:center; gap:var(--sh-space-2); }
.sh-mcard__name{ font-weight:var(--sh-fw-medium); font-size:var(--sh-fs-sm);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sh-mcard__badges{ display:flex; flex-wrap:wrap; gap:var(--sh-space-1); }
.sh-tag{
  font-size:10px; padding:2px var(--sh-space-2); border-radius:var(--sh-radius-pill);
  background:var(--sh-surface); border:1px solid var(--sh-border); color:var(--sh-text-muted);
  white-space:nowrap;
}
.sh-mcard__meta{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }

/* Result media */
.sh-media{ inline-size:100%; border-radius:var(--sh-radius-panel); overflow:hidden;
  background:var(--sh-surface-3); border:1px solid var(--sh-border); }
.sh-media video, .sh-media img{ display:block; inline-size:100%; block-size:auto; max-block-size:60vh; object-fit:contain; background:#000; }
.sh-resultbar{ display:flex; gap:var(--sh-space-2); flex-wrap:wrap; margin-block-start:var(--sh-space-3); }

@media (max-width:640px){
  .sh-reftray__grid{ grid-template-columns:1fr 1fr; }
  .sh-keyframes{ grid-template-columns:1fr; }
  .sh-sheet{ inset-block:2vh; inline-size:calc(100vw - 20px); padding:var(--sh-space-4); }
  .sh-sheet__list{ grid-template-columns:1fr; }
}
.sh-reftray__role{ height:34px; font-size:var(--sh-fs-xs);
  padding-inline-start:calc(var(--sh-space-3) + 18px); padding-inline-end:var(--sh-space-3); }

/* ===================================================================== */
/*  Chat composer  (Phase 2.2.1)                                          */
/*  A reusable variant of .sh-composer matching the Figma chat composer    */
/*  (Shahoosh-Ai-Chat(1) dark / (2) light). Geometry below is MEASURED     */
/*  from those frames; see reports/36-phase221-video-form-polish.md.       */
/*  Card 990x180 · padding 22 · prompt on top · ONE action row at bottom.  */
/* ===================================================================== */
.sh-composer--chat{
  padding:var(--sh-composer-pad);
  gap:var(--sh-space-3);
}

/* The prompt used to paint transparent with no border, so the exact area the
   user can type in was indistinguishable from the card around it. It now sits
   in its own recessed well: a subtle border and a background tinted toward the
   card's purple, so it reads as part of the design language rather than as a
   default HTML textarea. Field radius and token colours come from the design
   system, so light and dark both follow automatically. */
.sh-chat-prompt{
  inline-size:100%;
  min-block-size:68px;
  max-block-size:220px;
  resize:vertical;
  background:var(--sh-prompt-bg);
  border:1px solid var(--sh-prompt-bd);
  border-radius:var(--sh-radius-field);
  color:var(--sh-text);
  font-family:inherit;
  font-size:var(--sh-fs-input);   /* R5.7 iOS focus-zoom floor */
  line-height:var(--sh-lh-base);
  padding:var(--sh-space-3) var(--sh-space-4);
  transition:border-color var(--sh-dur-fast) var(--sh-ease),
             box-shadow   var(--sh-dur-fast) var(--sh-ease);
}
.sh-chat-prompt::placeholder{ color:var(--sh-text-faint); }
/* dir="auto" has nothing to measure while the field is empty, so Chrome
   resolves it LTR and the Persian placeholder rendered against the left edge
   of an otherwise right-aligned form. While the placeholder is showing the
   field follows the page direction; the moment the user types, dir="auto"
   takes over again and an English prompt still reads LTR. */
.sh-chat-prompt:placeholder-shown{ direction:rtl; }
.sh-chat-prompt:hover:not(:focus){ border-color:var(--sh-prompt-bd-hover); }
/* Focus is carried by the well's own border plus a soft ring, so the boundary
   the user is typing inside is the thing that lights up. */
.sh-chat-prompt:focus{
  outline:none;
  border-color:var(--sh-prompt-bd-focus);
  box-shadow:0 0 0 3px var(--sh-prompt-ring);
}
.sh-chat-prompt:focus-visible{
  border-color:var(--sh-prompt-bd-focus);
  box-shadow:0 0 0 3px var(--sh-prompt-ring);
}
.sh-composer--chat:focus-within{ border-color:var(--sh-border-strong); }

/* Attachment previews live above the action row, inside the card. */
/* The Figma anchors the preview at the card's inline-END padding edge — the
   physical left in RTL — so it sits clear of the prompt text, which starts at
   the opposite edge. Measured flush against --sh-composer-pad. */
.sh-chat-attachments{
  display:flex; flex-wrap:wrap; gap:var(--sh-space-2);
  justify-content:flex-end;
}
.sh-chat-attachments:empty{ display:none; }

/* The single action row. RTL: first child renders at the physical right.
   Figma keeps this on ONE line, so the chip strip scrolls rather than wrapping
   and pushing the primary action onto a second row. */
.sh-chatrow{
  display:flex; align-items:center; gap:var(--sh-row-gap);
  flex-wrap:nowrap; min-inline-size:0;
}
.sh-chatrow__spacer{ flex:1 1 auto; min-inline-size:var(--sh-space-2); }
.sh-chatrow__chips{
  display:flex; align-items:center; gap:var(--sh-space-2);
  min-inline-size:0; flex:0 1 auto;
  overflow-x:auto; overflow-y:hidden;
  scrollbar-width:none; padding-block:2px;
}
.sh-chatrow__chips::-webkit-scrollbar{ display:none; }
.sh-chatrow__chips > *{ flex:0 0 auto; }
/* Broad-capability models offer more chips than one line can hold. Scrolling
   clipped a supported affordance out of view, so the strip WRAPS instead:
   every mounted chip is always visible. Figma never draws a model with more
   than one input family, so this is a necessary adaptation.

   At narrower widths the strip alone is not enough — the whole row has to
   wrap — so it wraps too, and .sh-chatrow__tail below keeps the trailing
   controls together when it does. */
.sh-chatrow__chips{ flex-wrap:wrap; overflow:visible; row-gap:var(--sh-space-2); }
.sh-chatrow{ flex-wrap:wrap; row-gap:var(--sh-space-3); }
/* toggle + mic + generate travel together and stay at the inline-end whether
   they sit on the first line or are pushed onto a second one */
.sh-chatrow__tail{
  display:flex; align-items:center; gap:var(--sh-row-gap);
  flex:0 0 auto; margin-inline-start:auto;
}

/* ---- chip: upload / mode selector --------------------------------------
   Icon sits at the inline-END (physical left in RTL), matching the Figma. */
.sh-chip-btn{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  flex-direction:row-reverse;
  block-size:var(--sh-chip-h);
  padding-inline:13px;
  background:var(--sh-chip-bg);
  border:1px solid transparent;
  border-radius:var(--sh-radius-pill);
  color:var(--sh-text);
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium);
  white-space:nowrap;
  transition:background var(--sh-dur-fast) var(--sh-ease),
             border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-chip-btn svg{ inline-size:18px; block-size:18px; color:var(--sh-chip-icon); }
.sh-chip-btn:hover{ border-color:var(--sh-brand); }
.sh-chip-btn.is-active{
  border-color:var(--sh-brand);
  box-shadow:inset 0 0 0 1px var(--sh-brand);
}
.sh-chip-btn.has-file{ border-color:var(--sh-brand); }
.sh-chip-btn__dot{
  inline-size:6px; block-size:6px; border-radius:var(--sh-radius-pill);
  background:var(--sh-brand); flex:0 0 auto;
}

/* ---- grouped chips (first + last frame) --------------------------------
   Figma draws the keyframe pair inside ONE rounded container. */
.sh-chip-group{
  display:inline-flex; align-items:center; gap:var(--sh-space-1);
  block-size:var(--sh-chip-group-h);
  padding:var(--sh-space-1);
  background:var(--sh-chip-group);
  border:1px solid transparent;
  border-radius:var(--sh-radius-pill);
}
.sh-chip-group.is-active{ border-color:var(--sh-brand); }
.sh-chip-group .sh-chip-btn{ background:var(--sh-chip-inner); }

/* ---- settings pill ----------------------------------------------------- */
/* ---- chatrow action pill ------------------------------------------------
   One base for every raised configuration trigger in the action row — the
   model selector and «تنظیمات ویدیو» — so they cannot drift apart. Children
   are appended in logical order and RTL places the chevron on the inline-end
   edge by itself; no flex-direction trick is needed.
   Modifiers carry only what genuinely differs. */
.sh-chatrow__action{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  block-size:var(--sh-chip-set-h);
  padding-inline:13px;
  min-inline-size:0;
  background:var(--sh-chip-raised);
  border:1px solid var(--sh-chip-raised-bd);
  border-radius:var(--sh-radius-pill);
  color:var(--sh-text);
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium);
  white-space:nowrap;
  transition:border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-chatrow__action:hover{ border-color:var(--sh-brand); }
.sh-chatrow__action svg{ inline-size:14px; block-size:14px; color:var(--sh-text-muted); flex:0 0 auto; }

/* «مدل» reads as a quiet prefix, the display name carries the weight, and a
   long name truncates rather than pushing the row apart. */
.sh-chatrow__action__label{
  font-size:var(--sh-fs-xs); color:var(--sh-text-faint);
  font-weight:var(--sh-fw-regular); flex:0 0 auto;
}
.sh-chatrow__action__name{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  min-inline-size:0; max-inline-size:min(24ch, 38vw);
}
.sh-chatrow__action--model .sh-orow__flag{ flex:0 0 auto; }

/* ---- mic --------------------------------------------------------------- */
.sh-mic{
  inline-size:var(--sh-mic-size); block-size:var(--sh-mic-size);
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--sh-chip-bg); border:1px solid transparent;
  border-radius:var(--sh-radius-pill);
  color:var(--sh-chip-icon); flex:0 0 auto;
}
.sh-mic svg{ inline-size:18px; block-size:18px; }

/* ---- small switch (بهبود پرامت) ---------------------------------------- */
.sh-switch{ display:inline-flex; align-items:center; gap:var(--sh-space-2);
  flex-direction:row-reverse; user-select:none; white-space:nowrap; }
.sh-switch__label{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); }
.sh-switch input{ position:absolute; opacity:0; inline-size:1px; block-size:1px; }
.sh-switch__track{
  position:relative; flex:0 0 auto;
  --sh-switch-w:34px; --sh-switch-thumb:14px; --sh-switch-pad:2px;
  /* how far the thumb travels from one end of the track to the other */
  --sh-switch-travel:calc(var(--sh-switch-w) - var(--sh-switch-thumb) - var(--sh-switch-pad) * 2);
  /* translateX is PHYSICAL — it ignores `direction`. The thumb rests at the
     inline-end edge, so moving it to the inline-start edge means -X in LTR and
     +X in RTL. A signed variable keeps the movement logical in both. */
  --sh-switch-dir:-1;
  inline-size:var(--sh-switch-w); block-size:18px;
  border-radius:var(--sh-radius-pill);
  background:var(--sh-chip-bg);
  transition:background var(--sh-dur-fast) var(--sh-ease);
}
[dir="rtl"] .sh-switch__track{ --sh-switch-dir:1; }
.sh-switch__track::after{
  content:''; position:absolute; inset-block-start:var(--sh-switch-pad);
  inset-inline-end:var(--sh-switch-pad);
  inline-size:var(--sh-switch-thumb); block-size:var(--sh-switch-thumb);
  border-radius:var(--sh-radius-pill);
  background:var(--sh-text-faint);
  transition:transform var(--sh-dur-fast) var(--sh-ease), background var(--sh-dur-fast) var(--sh-ease);
}
.sh-switch input:checked + .sh-switch__track{ background:var(--sh-brand); }
/* ON moves the thumb to the opposite end of the track. Previously this was a
   bare translateX(-16px), which in RTL pushed the thumb off the left edge it
   already rested against — the track read as empty when the toggle was on. */
.sh-switch input:checked + .sh-switch__track::after{
  transform:translateX(calc(var(--sh-switch-dir) * var(--sh-switch-travel)));
  background:#fff;
}
.sh-switch input:focus-visible + .sh-switch__track{ outline:2px solid var(--sh-focus-ring); outline-offset:2px; }

/* ---- generate ---------------------------------------------------------- */
.sh-gen{
  display:inline-flex; align-items:center; justify-content:center;
  gap:var(--sh-space-2);
  min-inline-size:var(--sh-gen-w); block-size:var(--sh-gen-h);
  padding-inline:var(--sh-space-5);
  background:var(--sh-brand); color:var(--sh-text-on-brand);
  border:0; border-radius:var(--sh-gen-radius);
  font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-base);
  white-space:nowrap; flex:0 0 auto;
  /* Phase 2.13.19-MQA-AJ — a button is never underlined, whatever tag it is.
     `.sh-gen` is a <button> almost everywhere, where the UA adds no underline,
     so this was never noticed. app/checkout/checkout.js renders it as an <a>
     for the three result actions — and the UA's `a:-webkit-any-link` rule then
     underlined «انتخاب دوباره پلن». `.sh-btn-sm`, the secondary action beside
     it, has carried this same line since it was written, which is why only one
     of the two buttons was underlined.
     This touches the BUTTON class only: ordinary links, `.sh-md__link` and the
     rest of the sheet keep their underlines. */
  text-decoration:none;
  transition:background var(--sh-dur-fast) var(--sh-ease);
}
/* stated for every state, so no future :hover/:visited rule reintroduces it */
.sh-gen:link,
.sh-gen:visited,
.sh-gen:hover,
.sh-gen:focus,
.sh-gen:focus-visible,
.sh-gen:active{ text-decoration:none; }
.sh-gen:hover:not(:disabled){ background:var(--sh-brand-hover); }
.sh-gen:active:not(:disabled){ background:var(--sh-brand-active); }
.sh-gen:disabled{ opacity:.55; cursor:not-allowed; }
.sh-gen__cost{
  font-size:var(--sh-fs-xs); font-weight:var(--sh-fw-regular); opacity:.85;
  padding-inline-start:var(--sh-space-2);
  border-inline-start:1px solid rgba(255,255,255,.32);
}

/* ---- settings popover -------------------------------------------------- */
.sh-pop{
  position:absolute; z-index:30;
  inline-size:min(420px, calc(100vw - 32px));
  padding:var(--sh-space-4);
  background:var(--sh-surface);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-panel);
  box-shadow:var(--sh-shadow-pop);
  display:none;
}
.sh-pop.is-open{ display:block; }
.sh-pop__grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:var(--sh-space-3); }
.sh-pop__empty{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }
.sh-popwrap{ position:relative; display:inline-flex; }

/* ---- attachment preview inside the card -------------------------------- */
/* ---- selected media  [FIGMA: Items(6)/(7)/(27)/(28) @2x] ----------------
   The design shows the chosen file as a bare square thumbnail with a small
   remove badge on its top inline-end corner — no card, no border, no filename.
   Measured: 50x50 thumb, 13px radius, cover crop; 26px badge, 6px out, 5px up.
   The previous treatment was a 230px bordered card carrying a role label and
   the filename, which is a file-manager idiom the Figma never uses. */
.sh-media-prev{
  position:relative;
  inline-size:var(--sh-media-prev-size);
  block-size:var(--sh-media-prev-size);
  flex:0 0 auto;
}
.sh-media-prev__thumb{
  inline-size:100%; block-size:100%;
  padding:0; border:0; background:var(--sh-surface-3);
  border-radius:var(--sh-media-prev-radius);
  overflow:hidden; display:block; cursor:pointer;
  transition:box-shadow var(--sh-dur-fast) var(--sh-ease);
}
.sh-media-prev__thumb:disabled{ cursor:default; }
.sh-media-prev__img{
  inline-size:100%; block-size:100%;
  object-fit:cover; object-position:center;   /* never distort the source */
  display:block;
}
.sh-media-prev__thumb:hover:not(:disabled),
.sh-media-prev__thumb:focus-visible{
  box-shadow:0 0 0 2px var(--sh-brand);
  outline:none;
}
/* Measured on the Figma frames: the badge sits on the thumbnail's PHYSICAL
   right, overhanging 6px and rising 5px above it. The design is authored RTL,
   so physical right is the inline-START edge — using inline-end mirrored the
   badge to the wrong corner. Logical property, correct edge. */
.sh-media-prev__x{
  position:absolute;
  inset-block-start:-5px;
  inset-inline-start:-6px;
  inline-size:var(--sh-media-prev-badge);
  block-size:var(--sh-media-prev-badge);
  display:inline-flex; align-items:center; justify-content:center;
  padding:0; border:0;
  background:var(--sh-media-prev-badge-bg);
  color:var(--sh-media-prev-badge-fg);
  border-radius:var(--sh-media-prev-badge-radius);
  box-shadow:var(--sh-shadow-card);
  cursor:pointer;
  transition:background var(--sh-dur-fast) var(--sh-ease),
             color var(--sh-dur-fast) var(--sh-ease);
}
.sh-media-prev__x svg{ inline-size:14px; block-size:14px; stroke-width:2.4; }
.sh-media-prev__x:hover{ background:var(--sh-danger); color:#fff; }
.sh-media-prev__x:focus-visible{ outline:2px solid var(--sh-focus-ring); outline-offset:2px; }

/* ---- hint line under the row -------------------------------------------
   Not present in the Figma; kept because it carries the model note and the
   dual-unit price, and made as light as possible so the card stays close to
   the reference height. */
.sh-chat-hint{
  font-size:var(--sh-fs-xs); color:var(--sh-text-faint);
  line-height:1.5; margin-block-start:calc(var(--sh-space-1) * -1);
  display:flex; gap:var(--sh-space-2); flex-wrap:wrap;
}
.sh-chat-hint__cost{ color:var(--sh-text-muted); }

@media (max-width:640px){
  .sh-composer--chat{ padding:var(--sh-space-4); }
  .sh-chatrow{ gap:var(--sh-space-2); flex-wrap:wrap; }
  .sh-chatrow__spacer{ display:none; }
  /* The model decides which chips exist, so the configuration pair still
     leads the stack: order them ahead of the chip strip's own order:-1. */
  .sh-chatrow__action{ order:-2; }
  .sh-chatrow__chips{ order:-1; inline-size:100%; }
  /* the tail is the row's own line here, and the button fills it */
  .sh-chatrow__tail{ inline-size:100%; margin-inline-start:0; flex-wrap:wrap; }
  .sh-gen{ inline-size:100%; order:99; }
  .sh-chip-btn{ font-size:var(--sh-fs-xs); padding-inline:var(--sh-space-3); }
  .sh-chip-group{ flex-wrap:nowrap; }
}

/* ===================================================================== */
/*  Shared modal  (Phase 2.2.2)                                           */
/*  Desktop: centred card. Mobile: bottom sheet.                          */
/*  [DECISION] the bottom-sheet presentation is not drawn in Figma; it is  */
/*  the standard adaptation for a 390px viewport and keeps the close       */
/*  control and the list reachable with one thumb.                         */
/* ===================================================================== */
.sh-noscroll{ overflow:hidden !important; }

.sh-modal-scrim{
  position:fixed; inset:0; z-index:70;
  display:flex; align-items:center; justify-content:center;
  padding:var(--sh-space-6);
  background:rgba(10,2,18,.55);
  -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px);
  opacity:0; transition:opacity var(--sh-dur-base) var(--sh-ease);
}
.sh-modal-scrim.is-open{ opacity:1; }

.sh-modal{
  display:flex; flex-direction:column;
  inline-size:100%; max-block-size:min(760px, calc(100vh - 80px));
  background:var(--sh-surface);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  box-shadow:var(--sh-shadow-pop);
  overflow:hidden;
  transform:translateY(14px) scale(.985);
  opacity:0;
  transition:transform var(--sh-dur-base) var(--sh-ease), opacity var(--sh-dur-base) var(--sh-ease);
}
.sh-modal.is-open{ transform:none; opacity:1; }
/* Phase 2.13.19-MQA-H — the dialog takes focus on open so that no text input
   does, which is what keeps the mobile keyboard shut. That focus is a landing
   point for assistive tech, not something to draw a ring around; the controls
   inside keep their own focus styling. */
.sh-modal:focus{ outline:none; }
.sh-modal:focus-visible{ outline:none; }
.sh-modal--sm{ max-inline-size:440px; }
.sh-modal--md{ max-inline-size:620px; }
.sh-modal--lg{ max-inline-size:860px; }

.sh-modal__head{
  display:flex; align-items:flex-start; gap:var(--sh-space-3);
  padding:var(--sh-space-5) var(--sh-space-6);
  border-block-end:1px solid var(--sh-border);
  flex:0 0 auto;
}
.sh-modal__titles{ min-inline-size:0; }
.sh-modal__title{ margin:0; font-size:var(--sh-fs-md); font-weight:var(--sh-fw-bold); }
.sh-modal__sub{ margin:var(--sh-space-1) 0 0; font-size:var(--sh-fs-xs); color:var(--sh-text-muted); }
.sh-modal__x{ flex:0 0 auto; }

.sh-modal__body{
  padding:var(--sh-space-5) var(--sh-space-6);
  overflow-y:auto; overscroll-behavior:contain;
  flex:1 1 auto;
}
.sh-modal__foot{
  display:flex; justify-content:flex-start; gap:var(--sh-space-2);
  padding:var(--sh-space-4) var(--sh-space-6);
  border-block-start:1px solid var(--sh-border);
  flex:0 0 auto;
}

/* search field inside a modal */
.sh-modal__search{
  inline-size:100%; block-size:var(--sh-control-h);
  padding-inline:var(--sh-space-4);
  margin-block-end:var(--sh-space-4);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-pill); color:var(--sh-text); font-size:var(--sh-fs-input);
}
.sh-modal__filters{ display:flex; gap:var(--sh-space-2); flex-wrap:wrap; margin-block-end:var(--sh-space-4); }
.sh-modal__list{ display:flex; flex-direction:column; gap:var(--sh-space-2); }

/* Sticky group headings vs. the scroller's own top padding  (Phase 2.13.19-MQA-I)
   ---------------------------------------------------------------------------
   `.sh-modal__group` pins with `inset-block-start:0`, and a sticky offset is
   measured from the scroll container's CONTENT edge — so `.sh-modal__body`'s
   `padding-block-start` sat ABOVE the pin point while still being inside the
   scrollport. Measured: every heading stopped 20px down, and list rows kept
   scrolling through the strip above it. `elementFromPoint` in the middle of
   that band returned a row's price, its badges or its name at every scroll
   position, on desktop and at 390px alike — so it was not merely empty, it was
   content sliding past above the heading that was supposed to be the top.

   Fixed on the SCROLLER, not on the heading: the heading keeps exactly the
   section spacing it has in normal flow (§2 — ordinary spacing between
   non-sticky sections is not the bug). The 20px is not deleted either, it is
   moved inside the scrolled content, where it scrolls away like anything else,
   so the list looks identical at rest.

   Scoped with :has() to the lists that actually pin headings, so every other
   modal keeps its padding untouched. Where :has() is unsupported the rules
   simply do not apply and the previous behaviour stands — no worse than before. */
.sh-modal__body:has(.sh-modal__list > .sh-modal__group){ padding-block-start:0; }
.sh-modal__body:has(.sh-modal__list > .sh-modal__group) > :first-child{
  margin-block-start:var(--sh-space-5);
}
.sh-modal__empty{ padding:var(--sh-space-8) var(--sh-space-4); text-align:center; color:var(--sh-text-muted); }
.sh-modal__empty svg{ inline-size:44px; block-size:44px; color:var(--sh-text-faint); margin-block-end:var(--sh-space-3); }
.sh-modal__empty-title{ font-weight:var(--sh-fw-bold); color:var(--sh-text); margin-block-end:var(--sh-space-1); }

/* selectable row */
.sh-orow{
  display:flex; align-items:center; gap:var(--sh-space-3);
  inline-size:100%; text-align:start;
  padding:var(--sh-space-3) var(--sh-space-4);
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
  color:var(--sh-text);
  transition:background var(--sh-dur-fast) var(--sh-ease), border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-orow:hover{ background:var(--sh-surface-3); border-color:var(--sh-border-strong); }
.sh-orow.is-selected{ border-color:var(--sh-brand); box-shadow:inset 0 0 0 1px var(--sh-brand); }
.sh-orow__main{ flex:1 1 auto; min-inline-size:0; display:flex; flex-direction:column; gap:var(--sh-space-1); }
.sh-orow__top{ display:flex; align-items:center; gap:var(--sh-space-2); }
.sh-orow__name{ font-weight:var(--sh-fw-medium); font-size:var(--sh-fs-sm);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sh-orow__flag{ inline-size:8px; block-size:8px; border-radius:var(--sh-radius-pill);
  background:var(--sh-gold); flex:0 0 auto; }
.sh-orow__badges{ display:flex; flex-wrap:wrap; gap:var(--sh-space-1); }
.sh-orow__meta{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }
.sh-orow__tick{ flex:0 0 auto; color:var(--sh-brand-text); opacity:0; }
.sh-orow__tick svg{ inline-size:20px; block-size:20px; }
.sh-orow.is-selected .sh-orow__tick{ opacity:1; }

/* settings grid inside a modal */
.sh-setgrid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:var(--sh-space-4); }
.sh-setgrid .sh-field__label{ margin-block-end:var(--sh-space-1); }
.sh-setnote{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); margin-block-start:var(--sh-space-4); }

@media (max-width:640px){
  .sh-modal-scrim{ padding:0; align-items:flex-end; }
  .sh-modal{
    max-inline-size:100%; inline-size:100%;
    max-block-size:88vh;
    border-radius:var(--sh-radius-card) var(--sh-radius-card) 0 0;
    border-inline:0; border-block-end:0;
    transform:translateY(100%);
  }
  .sh-modal.is-open{ transform:none; }
  .sh-modal__head, .sh-modal__body, .sh-modal__foot{ padding-inline:var(--sh-space-4); }
  .sh-setgrid{ grid-template-columns:1fr; }
}

/* ===================================================================== */
/*  Header controls — corrected to the Figma  (Phase 2.2.2)               */
/*  Dark : account = filled #431EAB pill, icon buttons = outlined circles  */
/*  Light: account = white pill with a purple border, icons outlined       */
/*  Measured: all three controls 40px tall, account pill radius full.      */
/* ===================================================================== */
.sh-account{
  display:inline-flex; align-items:center; justify-content:center;
  block-size:40px; padding-inline:var(--sh-space-5);
  border-radius:var(--sh-radius-pill);
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium);
  white-space:nowrap;
  /* the underline that appeared before was the anchor default; the Figma has
     none, so it is removed and replaced by a real focus/active treatment */
  text-decoration:none;
  background:var(--sh-account-bg, var(--sh-surface));
  color:var(--sh-account-fg, var(--sh-text));
  border:1px solid var(--sh-account-bd, var(--sh-border-strong));
  transition:background var(--sh-dur-fast) var(--sh-ease),
             border-color var(--sh-dur-fast) var(--sh-ease),
             box-shadow var(--sh-dur-fast) var(--sh-ease);
}
.sh-account:hover{ border-color:var(--sh-brand); }
.sh-account:active{ transform:translateY(1px); }
/* current page / active: the design marks state by weight, so a brand
   underline bar is used rather than inventing a new surface colour */
.sh-account[aria-current="page"]{ box-shadow:inset 0 -2px 0 0 var(--sh-brand); }

.sh-iconcircle{
  inline-size:40px; block-size:40px; flex:0 0 auto;
  display:inline-flex; align-items:center; justify-content:center;
  position:relative;
  background:transparent;
  border:1px solid var(--sh-iconcircle-bd, var(--sh-border-strong));
  border-radius:var(--sh-radius-pill);
  color:var(--sh-iconcircle-fg, var(--sh-text-muted));
  transition:background var(--sh-dur-fast) var(--sh-ease),
             border-color var(--sh-dur-fast) var(--sh-ease),
             color var(--sh-dur-fast) var(--sh-ease);
}
.sh-iconcircle:hover{ background:var(--sh-surface-2); border-color:var(--sh-brand); color:var(--sh-text); }
.sh-iconcircle svg{ inline-size:20px; block-size:20px; }
.sh-iconcircle__badge{
  position:absolute; inset-block-start:-2px; inset-inline-end:-2px;
  min-inline-size:18px; block-size:18px; padding-inline:5px;
  display:none; align-items:center; justify-content:center;
  background:var(--sh-danger); color:#fff;
  border:2px solid var(--sh-surface);
  border-radius:var(--sh-radius-pill);
  font-size:10px; font-weight:var(--sh-fw-bold); line-height:1;
}
.sh-iconcircle.has-badge .sh-iconcircle__badge{ display:inline-flex; }

@media (max-width:640px){
  .sh-account{ block-size:36px; padding-inline:var(--sh-space-4); font-size:var(--sh-fs-xs); }
  .sh-iconcircle{ inline-size:36px; block-size:36px; }
  .sh-iconcircle svg{ inline-size:18px; block-size:18px; }
}

/* ---- model selector row: FIRST control inside the card (Phase 2.2.2) ---- */
/* ---- selected audio file (Phase 2.4) -----------------------------------
   Audio has no thumbnail worth showing, so the selected state is a compact chip
   in the same vocabulary: glyph, filename, real duration/size. No waveform —
   we have no peak data and a drawn shape would be decoration posing as data. */
.sh-audio-prev{
  position:relative; display:inline-flex; align-items:center;
  gap:var(--sh-space-2); max-inline-size:min(320px, 100%);
}
.sh-audio-prev__main{
  display:inline-flex; align-items:center; gap:var(--sh-space-3);
  min-inline-size:0; flex:1 1 auto;
  padding:var(--sh-space-2) var(--sh-space-3);
  padding-inline-end:var(--sh-space-6);
  background:var(--sh-chip-bg); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field); color:var(--sh-text);
  text-align:start; cursor:pointer;
  transition:border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-audio-prev__main:disabled{ cursor:default; }
.sh-audio-prev__main:hover:not(:disabled),
.sh-audio-prev__main:focus-visible{ border-color:var(--sh-brand); outline:none; }
.sh-audio-prev__main svg{ inline-size:18px; block-size:18px; flex:0 0 auto; color:var(--sh-brand-text); }
.sh-audio-prev__meta{ min-inline-size:0; }
.sh-audio-prev__name{
  font-size:var(--sh-fs-xs); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.sh-audio-prev__sub{ font-size:10px; color:var(--sh-text-faint); }
.sh-audio-prev__x{
  position:absolute; inset-block-start:50%; inset-inline-end:var(--sh-space-2);
  transform:translateY(-50%);
  inline-size:22px; block-size:22px; padding:0; border:0;
  display:inline-flex; align-items:center; justify-content:center;
  background:transparent; color:var(--sh-text-faint);
  border-radius:var(--sh-radius-pill); cursor:pointer;
}
.sh-audio-prev__x:hover{ color:var(--sh-danger); background:var(--sh-surface-3); }
.sh-audio-prev__x svg{ inline-size:13px; block-size:13px; }
.sh-audio-prev__x:focus-visible{ outline:2px solid var(--sh-focus-ring); outline-offset:2px; }

/* ---- audio result player (Phase 2.4) ------------------------------------
   One transport for every audio result. A real seekable progress track, real
   times from the media element. */
.sh-player{
  display:flex; align-items:center; gap:var(--sh-space-3);
  padding:var(--sh-space-3) var(--sh-space-4);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
}
.sh-player__play{
  inline-size:40px; block-size:40px; flex:0 0 auto; padding:0;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--sh-brand); color:var(--sh-text-on-brand);
  border:0; border-radius:var(--sh-radius-pill); cursor:pointer;
}
.sh-player__play:hover{ background:var(--sh-brand-hover); }
.sh-player__play:focus-visible{ outline:2px solid var(--sh-focus-ring); outline-offset:2px; }
.sh-player__play svg{ inline-size:18px; block-size:18px; }
.sh-player__track{
  position:relative; flex:1 1 auto; min-inline-size:0;
  block-size:6px; border-radius:var(--sh-radius-pill);
  background:var(--sh-surface-4);
}
.sh-player__fill{
  position:absolute; inset-block:0; inset-inline-start:0; inline-size:0%;
  background:var(--sh-brand); border-radius:var(--sh-radius-pill);
  pointer-events:none;
}
.sh-player__range{
  position:absolute; inset:-8px 0; inline-size:100%; block-size:22px;
  margin:0; opacity:0; cursor:pointer;
}
.sh-player__range:focus-visible + *, .sh-player__track:focus-within{ outline:2px solid var(--sh-focus-ring); outline-offset:3px; }
.sh-player__time{
  flex:0 0 auto; font-size:var(--sh-fs-xs); color:var(--sh-text-muted);
  font-variant-numeric:tabular-nums; white-space:nowrap;
}
.sh-player__dur{ color:var(--sh-text-faint); }

/* ---- transcription result (Phase 2.4) ---------------------------------- */
.sh-transcript{
  padding:var(--sh-space-4);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
  color:var(--sh-text); font-size:var(--sh-fs-sm); line-height:var(--sh-lh-base);
  white-space:pre-wrap; overflow-wrap:anywhere;
  max-block-size:40vh; overflow-y:auto;
}

/* ---- image result grid (Phase 2.3) -------------------------------------
   Image endpoints answer with 1-4 urls. The grid keeps them inside the thread
   at any width and never lets a result exceed the viewport. */
.sh-imggrid{
  display:grid; gap:var(--sh-space-3);
  grid-template-columns:repeat(auto-fit, minmax(min(220px, 100%), 1fr));
}
.sh-imggrid__cell{ display:flex; flex-direction:column; gap:var(--sh-space-2); min-inline-size:0; }
.sh-imggrid__cell img{
  inline-size:100%; block-size:auto; max-block-size:60vh; object-fit:contain;
  border-radius:var(--sh-radius-field); background:var(--sh-surface-2);
  border:1px solid var(--sh-border);
}

/* ---- compact settings summary chips inside the card --------------------
   Belongs to the GENERATORS (chat, audio, face, image, video), which render a
   horizontal strip of .sh-sumchip. The checkout order summary is a different
   thing and has its own block class — see .sh-ordersum below. */
.sh-summary{ display:flex; flex-wrap:wrap; gap:var(--sh-space-2); }
.sh-summary:empty{ display:none; }
.sh-sumchip{
  display:inline-flex; align-items:center; gap:var(--sh-space-1);
  block-size:30px; padding-inline:var(--sh-space-3);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-pill);
  color:var(--sh-text); font-size:var(--sh-fs-xs); white-space:nowrap;
}
.sh-sumchip:hover{ border-color:var(--sh-brand); }
.sh-sumchip__k{ color:var(--sh-text-faint); }

/* ---- bare hamburger, as the Figma draws it ----------------------------- */
.sh-menubtn{
  inline-size:40px; block-size:40px; flex:0 0 auto;
  display:inline-flex; align-items:center; justify-content:center;
  background:transparent; border:0; border-radius:var(--sh-radius-pill);
  color:var(--sh-text);
  transition:background var(--sh-dur-fast) var(--sh-ease);
}
.sh-menubtn:hover{ background:var(--sh-surface-2); }
.sh-menubtn svg{ inline-size:24px; block-size:24px; stroke-width:2.2; }
@media (max-width:640px){
  .sh-menubtn{ inline-size:36px; block-size:36px; }
  .sh-menubtn svg{ inline-size:22px; block-size:22px; }
}

/* modal close: bare icon, matching the Figma's drawer/close treatment */
.sh-modal__x.sh-iconbtn{
  background:transparent; border-color:transparent;
  inline-size:36px; block-size:36px; color:var(--sh-text-muted);
}
.sh-modal__x.sh-iconbtn:hover{ background:var(--sh-surface-2); color:var(--sh-text); }

/* ---- very narrow phones (≤380px) --------------------------------------
   At 360px the account pill + two icon circles + gutters exceeded the header
   line by a fraction of a pixel, pushing the row off the inline-end edge.
   The controls are allowed to shrink and the account label to truncate. */
@media (max-width:380px){
  .sh-header{ padding-inline:var(--sh-space-2); gap:var(--sh-space-1); }
  .sh-header__end{ min-inline-size:0; gap:var(--sh-space-1); }
  .sh-account{
    padding-inline:var(--sh-space-3);
    min-inline-size:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .sh-header__brand{ min-inline-size:0; }
}

/* ==========================================================================
   Phase 2.5 — chat thread rendering
   ========================================================================== */

/* A group heading inside a chooser list (content preset categories). */
.sh-modal__group{
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-bold);
  color:var(--sh-text-muted);
  padding-block:var(--sh-space-3) var(--sh-space-1);
  padding-inline:var(--sh-space-1);
  position:sticky; inset-block-start:0; background:var(--sh-surface); z-index:1;
}

/* Filenames attached to a user turn. */
.sh-msg__files{
  display:flex; flex-wrap:wrap; gap:var(--sh-space-2);
  margin-block-start:var(--sh-space-2);
}

.sh-result--chat{ display:flex; flex-direction:column; gap:var(--sh-space-3); }
.sh-result__img img{
  max-inline-size:100%; block-size:auto; display:block;
  border-radius:var(--sh-radius-card);
}

/* ---- markdown -----------------------------------------------------------
   The conversation is RTL. Code is not: it is always LTR and always scrolls
   inside its own box, so a long line can never widen the page. Tables get the
   same treatment — the wrapper scrolls, the document does not. */
.sh-md{ display:flex; flex-direction:column; gap:var(--sh-space-3); min-inline-size:0; }
.sh-md__p{ margin:0; line-height:var(--sh-lh-relaxed, 1.9); overflow-wrap:anywhere; }
.sh-md__h{ margin:0; font-weight:var(--sh-fw-bold); line-height:1.5; }
.sh-md h3.sh-md__h{ font-size:var(--sh-fs-md); }
.sh-md h4.sh-md__h{ font-size:var(--sh-fs-base); }
.sh-md h5.sh-md__h,
.sh-md h6.sh-md__h{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); }

.sh-md__list{ margin:0; padding-inline-start:var(--sh-space-5); display:flex;
  flex-direction:column; gap:var(--sh-space-1); }
.sh-md__list li{ line-height:var(--sh-lh-relaxed, 1.9); overflow-wrap:anywhere; }

.sh-md__link{ color:var(--sh-brand-text, var(--sh-brand)); text-decoration:underline;
  overflow-wrap:anywhere; }

/* An inline code span reads LTR inside Persian prose without breaking the line. */
.sh-md__code{
  display:inline-block; unicode-bidi:isolate;
  font-family:var(--sh-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size:.92em; padding:.1em .4em; border-radius:var(--sh-radius-sm, 6px);
  background:var(--sh-chip-bg); color:var(--sh-text);
}

.sh-md__pre{
  border:1px solid var(--sh-border); border-radius:var(--sh-radius-card);
  background:var(--sh-code-bg, var(--sh-chip-bg)); overflow:hidden; min-inline-size:0;
}
.sh-md__pre-head{
  display:flex; align-items:center; gap:var(--sh-space-2);
  padding:var(--sh-space-2) var(--sh-space-3);
  border-block-end:1px solid var(--sh-border);
  background:var(--sh-surface-2, transparent);
}
.sh-md__lang{
  font-size:var(--sh-fs-xs, .75rem); color:var(--sh-text-muted);
  font-family:var(--sh-font-mono, ui-monospace, monospace);
  text-transform:lowercase; direction:ltr;
}
.sh-md__copy{ margin-inline-start:auto; }
.sh-md__precode{
  margin:0; padding:var(--sh-space-3);
  overflow-x:auto; overflow-y:hidden;
  direction:ltr; text-align:left;
  font-family:var(--sh-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size:var(--sh-fs-sm); line-height:1.7;
  white-space:pre; tab-size:2;
}
.sh-md__precode code{ font:inherit; color:var(--sh-text); }

.sh-md__tablewrap{ overflow-x:auto; max-inline-size:100%; }
.sh-md__table{
  border-collapse:collapse; inline-size:max-content; min-inline-size:100%;
  font-size:var(--sh-fs-sm);
}
.sh-md__table th,
.sh-md__table td{
  border:1px solid var(--sh-border);
  padding:var(--sh-space-2) var(--sh-space-3);
  text-align:start; vertical-align:top;
}
.sh-md__table th{ font-weight:var(--sh-fw-bold); background:var(--sh-chip-bg); }

/* Nothing inside a turn may push the thread wider than its column. */
.sh-msg,
.sh-result--chat{ min-inline-size:0; max-inline-size:100%; }

/* ==========================================================================
   Phase 2.6 — face / motion workspace
   ========================================================================== */

/* Two named input slots side by side. Each carries its own label so a
   two-input workflow reads as two explicit asks rather than one pile of
   uploads. They stack at narrow widths. */
.sh-slots{
  display:flex; flex-wrap:wrap; gap:var(--sh-space-3);
  align-items:flex-start;
}
.sh-slots:empty{ display:none; }
.sh-slot{
  display:flex; flex-direction:column; gap:var(--sh-space-2);
  min-inline-size:0; flex:0 1 auto;
}
.sh-slot__label{
  font-size:var(--sh-fs-sm); color:var(--sh-text-muted);
  font-weight:var(--sh-fw-bold);
}

/* The finished clip. The browser's own transport is used deliberately: these
   are long videos the user will scrub, and a custom player would add nothing. */
.sh-vresult{ display:flex; flex-direction:column; gap:var(--sh-space-3); min-inline-size:0; }
.sh-vresult__video{
  inline-size:100%; max-inline-size:100%; block-size:auto; display:block;
  border-radius:var(--sh-radius-card); background:#000;
  aspect-ratio:16 / 9; object-fit:contain;
}

@media (max-width: 560px){
  .sh-slots{ flex-direction:column; }
  .sh-slot{ inline-size:100%; }
}

/* ==========================================================================
   Phase 2.7 — account / history / wallet
   These are panels, not generators: cards and rows rather than a composer.
   ========================================================================== */

.sh-acct{ display:flex; flex-direction:column; gap:var(--sh-space-5); min-inline-size:0; }

/* section switcher */
.sh-acctnav{
  display:flex; gap:var(--sh-space-2); flex-wrap:wrap;
  border-block-end:1px solid var(--sh-border);
  padding-block-end:var(--sh-space-3);
}
.sh-acctnav__item{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  padding:var(--sh-space-2) var(--sh-space-4);
  border:1px solid var(--sh-border); border-radius:var(--sh-radius-pill);
  background:transparent; color:var(--sh-text-muted);
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-bold);
  cursor:pointer; white-space:nowrap;
  transition:background var(--sh-dur-fast) var(--sh-ease),
             color var(--sh-dur-fast) var(--sh-ease),
             border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-acctnav__item:hover{ border-color:var(--sh-border-strong); color:var(--sh-text); }
.sh-acctnav__item.is-active{
  background:var(--sh-brand); border-color:var(--sh-brand); color:var(--sh-text-on-brand);
}
.sh-acct__panel{ display:flex; flex-direction:column; gap:var(--sh-space-4); min-inline-size:0; }

/* generic card */
.sh-card{
  background:var(--sh-surface-2, var(--sh-chip-bg));
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  padding:var(--sh-space-4);
  min-inline-size:0;
}
.sh-card__title{ font-size:var(--sh-fs-md); font-weight:var(--sh-fw-bold); margin:0 0 var(--sh-space-3); }
.sh-card__subtitle{ font-size:var(--sh-fs-base); font-weight:var(--sh-fw-bold);
  margin:var(--sh-space-4) 0 var(--sh-space-2); }

/* identity */
.sh-idcard{ display:flex; align-items:center; gap:var(--sh-space-4); }
.sh-idcard__avatar{
  inline-size:64px; block-size:64px; border-radius:var(--sh-radius-pill);
  object-fit:cover; flex:0 0 auto; background:var(--sh-chip-bg);
}
.sh-idcard__who{ min-inline-size:0; display:flex; flex-direction:column; gap:2px; }
.sh-idcard__name{ font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-md); overflow-wrap:anywhere; }
.sh-idcard__meta{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); overflow-wrap:anywhere; }

/* form */
.sh-form__actions{ display:flex; justify-content:flex-start; margin-block-start:var(--sh-space-4); }
.sh-input--area{ inline-size:100%; resize:vertical; font-family:inherit;
  padding:var(--sh-space-3) var(--sh-space-4); line-height:var(--sh-lh-base);
  /* A multi-line box inherited the single-line pill radius, so a 96px textarea
     was drawn with 999px corners. The field radius is the one that belongs to a
     box this tall; the pill stays on the single-line controls. */
  border-radius:var(--sh-radius-field);
  block-size:auto; }
.sh-field__hint{ font-size:var(--sh-fs-xs, .75rem); color:var(--sh-text-muted);
  margin-block-start:var(--sh-space-1); }
/* Phase 2.10: the way out for someone who does not know their current password.
   It sits with the hint, not in the navigation. */
.sh-field__link{
  display:inline-block;
  margin-block-start:var(--sh-space-1);
  font-size:var(--sh-fs-xs);
  color:var(--sh-chip-icon);
  text-decoration:none;
}
.sh-field__link:hover{ text-decoration:underline; }
.sh-field__link:focus-visible{ outline:2px solid var(--sh-focus-ring); outline-offset:2px; }

/* filters */
.sh-filters{ display:flex; gap:var(--sh-space-2); flex-wrap:wrap; }
.sh-filter{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  padding:var(--sh-space-1) var(--sh-space-3);
  border:1px solid var(--sh-border); border-radius:var(--sh-radius-pill);
  background:transparent; color:var(--sh-text-muted);
  font-size:var(--sh-fs-sm); cursor:pointer; white-space:nowrap;
}
.sh-filter.is-active{ background:var(--sh-brand); border-color:var(--sh-brand);
  color:var(--sh-text-on-brand); }
.sh-filter__n{ font-size:var(--sh-fs-xs, .75rem); opacity:.75; }

/* history rows */
.sh-hlist,
.sh-txlist{ display:flex; flex-direction:column; gap:var(--sh-space-2); min-inline-size:0; }
.sh-hrow,
.sh-txrow{
  display:flex; align-items:center; gap:var(--sh-space-4);
  padding:var(--sh-space-3) var(--sh-space-4);
  background:var(--sh-surface-2, var(--sh-chip-bg));
  border:1px solid var(--sh-border); border-radius:var(--sh-radius-card);
  min-inline-size:0;
}
.sh-hrow__main,
.sh-txrow__main{ flex:1 1 auto; min-inline-size:0; display:flex; flex-direction:column; gap:var(--sh-space-2); }
.sh-hrow__title,
.sh-txrow__title{
  font-size:var(--sh-fs-sm); line-height:1.7;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; overflow-wrap:anywhere;
}
.sh-hrow__meta,
.sh-txrow__meta{ display:flex; gap:var(--sh-space-2); align-items:center; flex-wrap:wrap; }
.sh-hrow__date,
.sh-txrow__date{ font-size:var(--sh-fs-xs, .75rem); color:var(--sh-text-muted); }
.sh-hrow__side{ display:flex; flex-direction:column; align-items:flex-end;
  gap:var(--sh-space-2); flex:0 0 auto; }
.sh-hrow__cost{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); white-space:nowrap; }
.sh-hrow__gone{ font-size:var(--sh-fs-xs, .75rem); color:var(--sh-text-faint); white-space:nowrap; }

/* a status badge must read differently per state, in both themes */
/* --------------------------------------------------- 30-day retention -- *
   The notice is informational, never alarming: it states a normal policy, so
   it uses the info tone rather than warning/danger.

   An expired row is DIMMED, not hidden. The usage record and its cost are
   still true and still belong in the ledger; only the file is gone, and the
   row has to keep saying so after the file stops existing. */
.sh-retention-notice{ margin-block-end:12px; }

.sh-hrow.is-expired{ opacity:.72; }
.sh-hrow.is-expired .sh-hrow__title{ color:var(--sh-text-muted); }

.sh-badge--expired{
  background:var(--sh-muted-bg, rgba(127,127,127,.12));
  border-color:var(--sh-border);
  color:var(--sh-text-muted);
}

/* The per-item expiry date is secondary information sitting in a metadata row
   that already wraps on narrow screens, so it must be allowed to wrap with it
   rather than force the card wider. */
.sh-hrow__expiry{
  font-size:var(--sh-fs-xs, .75rem);
  color:var(--sh-text-faint);
}

.sh-hrow__gone--expired{
  white-space:normal;          /* the sentence is longer than the short state */
  max-inline-size:22ch;
  text-align:start;
}

.sh-badge--status.is-completed{ color:var(--sh-ok, #30a46c); }
.sh-badge--status.is-refunded { color:var(--sh-warn, #f5a524); }
.sh-badge--status.is-failed   { color:var(--sh-danger, #e5484d); }

/* transactions: the sign carries direction and must survive both themes */
.sh-txrow__amt{
  display:flex; align-items:baseline; gap:var(--sh-space-1);
  font-weight:var(--sh-fw-bold); white-space:nowrap; flex:0 0 auto;
  font-variant-numeric:tabular-nums;
}
.sh-txrow__sign{ font-size:1.1em; }
.sh-txrow.is-credit .sh-txrow__amt{ color:var(--sh-ok, #30a46c); }
.sh-txrow.is-debit  .sh-txrow__amt{ color:var(--sh-text); }
.sh-txrow.is-refund .sh-txrow__amt{ color:var(--sh-warn, #f5a524); }

/* wallet cards */
.sh-wcards{ display:flex; gap:var(--sh-space-3); flex-wrap:wrap; }
.sh-wcard{ flex:1 1 200px; min-inline-size:0; display:flex; flex-direction:column; gap:var(--sh-space-1); }
.sh-wcard.is-primary{ border-color:var(--sh-brand); }
.sh-wcard__k{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); }
.sh-wcard__v{ font-size:var(--sh-fs-lg, 1.25rem); font-weight:var(--sh-fw-bold);
  font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
.sh-wcard__sub{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); overflow-wrap:anywhere; }

.sh-subcard{ display:flex; align-items:center; justify-content:space-between;
  gap:var(--sh-space-3); flex-wrap:wrap; }
.sh-subcard__k{ color:var(--sh-text-muted); font-size:var(--sh-fs-sm); }
.sh-subcard__v{ font-weight:var(--sh-fw-bold); }
.sh-subcard.is-expired .sh-subcard__k{ color:var(--sh-danger, #e5484d); }
.sh-tx__heading{ margin-block-start:var(--sh-space-4); }

/* empty + pager */
.sh-empty{
  text-align:center; padding:var(--sh-space-8) var(--sh-space-4);
  border:1px dashed var(--sh-border); border-radius:var(--sh-radius-card);
  color:var(--sh-text-muted);
}
.sh-empty__title{ font-weight:var(--sh-fw-bold); color:var(--sh-text); margin-block-end:var(--sh-space-2); }
.sh-empty__body{ font-size:var(--sh-fs-sm); }

.sh-pager{ display:flex; align-items:center; gap:var(--sh-space-3);
  justify-content:center; flex-wrap:wrap; margin-block-start:var(--sh-space-3); }
.sh-pager__n{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); white-space:nowrap; }

@media (max-width: 620px){
  .sh-hrow, .sh-txrow{ flex-direction:column; align-items:stretch; }
  .sh-hrow__side{ flex-direction:row; align-items:center; justify-content:space-between; }
  .sh-txrow__amt{ justify-content:flex-end; }
  /* W8-18 (Phase 2.13.16-R5) — the avatar and the name must stay side by side.
     This rule stacked them: on a phone the picture sat on one line and the name
     underneath, which the owner flagged from the device. The row is kept at
     every width; what changes on a narrow screen is the SCALE, not the axis.
     `min-inline-size:0` on the text column lets a long name wrap inside its own
     column instead of pushing the avatar down or widening the page. */
  .sh-idcard{ flex-direction:row; align-items:center; gap:var(--sh-space-3); }
  .sh-idcard__avatar{ inline-size:48px; block-size:48px; flex:0 0 auto; }
  .sh-idcard__who{ min-inline-size:0; flex:1 1 auto; }
  .sh-idcard__name{ overflow-wrap:anywhere; }
}

/* ==========================================================================
   Phase 2.7.1 — account form scale, and mobile chatrow density
   ========================================================================== */

/* ---- account form grid --------------------------------------------------
   `.sh-fields` is tuned for the compact control grids inside settings modals:
   minmax(104px, 1fr) auto-fit. On a full-width profile card that produced SIX
   152px columns on one row, which reads as a dense admin form rather than this
   product. The modal grid is left exactly as it is — seven pages depend on it —
   and the form gets its own scale. */
.sh-fields--form{
  grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));
  gap:var(--sh-space-4) var(--sh-space-3);
}
/* a field that should own its whole row regardless of how many columns fit */
.sh-fields--form > .sh-field--wide{ grid-column:1 / -1; }

/* semantic groups, so the form reads personal / contact / bio / password */
.sh-formgroup{ display:flex; flex-direction:column; gap:var(--sh-space-3); }
.sh-formgroup + .sh-formgroup{ margin-block-start:var(--sh-space-5); }
.sh-formgroup__title{
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-bold);
  color:var(--sh-text-muted); margin:0;
}

/* Inputs on this form feel substantial rather than admin-dense. Tokens only —
   no new field style is invented, the existing one is given room. */
.sh-form .sh-input{
  min-block-size:46px;
  padding-inline:var(--sh-space-4);
  font-size:var(--sh-fs-input);   /* R5.7 iOS focus-zoom floor */
}
.sh-form .sh-input--area{ min-block-size:96px; padding-block:var(--sh-space-3); }
.sh-form .sh-field__label{ font-size:var(--sh-fs-sm); }

/* A read-only control must keep the same dimensions as its neighbours — it is
   still a field, it just cannot be typed in. */
.sh-form .sh-input:disabled,
.sh-form .sh-input[readonly]{
  background:var(--sh-chip-bg);
  color:var(--sh-text-muted);
  cursor:default;
  opacity:1;                      /* the UA's dimming made it look broken */
}
/* the explanation under the phone must not stretch the row it sits in */
.sh-field__hint{ max-inline-size:44ch; }

/* No tablet override is needed: one 260px minimum produces the whole ladder on
   its own — three columns while the card is ~950px, two once it drops to ~730px
   at 768, and one below. A second breakpoint with a smaller minimum put three
   223px columns on a tablet, which is the cramped look this phase removes. */
@media (max-width:640px){
  .sh-fields--form{ grid-template-columns:1fr; }
  .sh-field__hint{ max-inline-size:none; }
}

/* ---- mobile chatrow density --------------------------------------------
   On a phone the configuration pills — «ابزار», «مدل», «صدا», «تنظیمات» — sized
   to their own content (`flex:0 1 auto`, basis auto). Flexbox decides wrapping
   from those content widths BEFORE it shrinks anything, so as soon as a pair's
   natural widths exceeded the row each pill took a line of its own and left the
   rest of that line empty: at 390px the video row spent two whole lines on a
   model pill and a settings pill that together need one.

   Below 640px they become two equal columns instead. A fixed basis of half the
   row means the wrap decision no longer depends on how long a model name
   happens to be — «Google Gemini Omni» and «Flux 2 Pro» both pair with their
   settings pill — and the name ellipsises into whatever column it is given.

   `flex-grow` stays 0 deliberately: an odd pill on the last line keeps its
   column instead of stretching edge to edge, which is the look the composer is
   being polished away from.

   This applies ONLY to `.sh-chatrow__action`. Upload and enhancement chips live
   in `.sh-chatrow__chips` and keep their compact content sizing — nothing is
   widened merely for being in the row. */
@media (max-width:640px){
  .sh-chatrow__action{
    flex:0 0 calc(50% - (var(--sh-row-gap) / 2));
    min-inline-size:0;
    justify-content:space-between;
  }
  /* the pill now owns a known width, so the name may use all of it and
     ellipsise at the column edge rather than at an inherited character cap */
  .sh-chatrow__action .sh-chatrow__action__name{ max-inline-size:none; }
  /* a single configuration pill has nothing to pair with: let it size to its
     own content rather than sit in a half-width column */
  .sh-chatrow__action:only-of-type{ flex:0 1 auto; }
}

/* ==========================================================================
   Phase 2.8 — plans, checkout and payment result
   No Figma frame exists for commerce (report 25 maps Items(1)-(68) to the
   shell, generators and conversations only), so these are built from the
   established Phase 2 language: the same card, field, pill and notice
   primitives the migrated pages already use, with no new visual vocabulary.
   ========================================================================== */

.sh-page{ display:flex; flex-direction:column; gap:var(--sh-space-5); }
.sh-pad{ padding:var(--sh-space-6); text-align:center; }

.sh-pagehead__title{ font-size:var(--sh-fs-xl); font-weight:var(--sh-fw-bold); margin:0; }
.sh-pagehead__sub{
  margin:var(--sh-space-2) 0 0; color:var(--sh-text-muted);
  font-size:var(--sh-fs-sm); line-height:1.8; max-inline-size:68ch;
}

/* ---- plan cards ---------------------------------------------------------
   auto-fit rather than a fixed count: the catalogue is whatever WooCommerce
   publishes, and it is four today only by coincidence. */
.sh-plangrid{
  display:grid; gap:var(--sh-space-4);
  /* 200px lets the four published plans share one row on a desktop card
     (4x200 + 3 gaps fits inside ~990px) instead of leaving a lone card
     stranded on a second row. auto-fit still adapts if the catalogue
     changes — the count is WooCommerce's to decide, not this file's. */
  grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));
}
.sh-plan{
  display:flex; flex-direction:column; gap:var(--sh-space-4);
  padding:var(--sh-space-5);
  background:var(--sh-surface-2, var(--sh-chip-bg));
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  transition:border-color var(--sh-dur-fast) var(--sh-ease),
             transform var(--sh-dur-fast) var(--sh-ease);
}
.sh-plan:hover{ border-color:var(--sh-brand); transform:translateY(-2px); }
/* so the price can size against the CARD rather than the viewport: a card in a
   four-column grid and the same card full-width on a phone are different
   contexts, and only the card knows which it is in */
.sh-plan{ container-type:inline-size; }
.sh-plan__head{ display:flex; flex-direction:column; gap:var(--sh-space-1); }
.sh-plan__name{ margin:0; font-size:var(--sh-fs-lg); font-weight:var(--sh-fw-bold); }
.sh-plan__sub{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }

.sh-plan__pricebox{ display:flex; flex-direction:column; gap:2px; }
.sh-plan__was{ font-size:var(--sh-fs-sm); color:var(--sh-text-faint); }
.sh-plan__price{
  display:flex; align-items:baseline; gap:6px; flex-wrap:wrap;
  color:var(--sh-brand-text);
  /* a long price must never push the card wider than its grid column */
  overflow-wrap:anywhere;
}
/* The amount scales with the column so the longest real price — ۱۵,۲۹۸,۰۰۰ —
   stays on one line in a four-column grid, without shrinking on a phone where
   there is room for it. */
.sh-plan__amount{
  font-size:clamp(var(--sh-fs-lg), 4.4cqi, var(--sh-fs-xl));
  font-weight:var(--sh-fw-bold); line-height:1.3;
}
.sh-plan__cur{ font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium); }

.sh-plan__facts{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--sh-space-2); }
.sh-plan__fact{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--sh-space-3); }
.sh-plan__fact__k{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); }
.sh-plan__fact__v{ font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-bold); }
.sh-plan__cta{ margin-block-start:auto; }

/* ---- checkout layout ----------------------------------------------------
   The summary is an aside on a wide screen and leads on a narrow one, because
   on a phone the amount is the thing worth seeing before the form. */
.sh-checkout{
  display:grid; gap:var(--sh-space-5); align-items:start;
  grid-template-columns:minmax(0,1.6fr) minmax(0,1fr);
  grid-template-areas:"main aside" "main submit";
  grid-template-rows:auto 1fr;
}
.sh-checkout__main  { grid-area:main; }
.sh-checkout__aside { grid-area:aside; }
.sh-checkout__submit{ grid-area:submit; }
.sh-checkout__main,
.sh-checkout__aside,
.sh-checkout__submit{ display:flex; flex-direction:column; gap:var(--sh-space-4); min-inline-size:0; }
.sh-checkout__pay{ inline-size:100%; }
.sh-checkout__change{ align-self:center; }

/* On one column the summary leads, because the amount is what a customer wants
   to see first, but the button follows the form it validates. */
@media (max-width:900px){
  .sh-checkout{
    grid-template-columns:1fr;
    grid-template-areas:"aside" "main" "submit";
    grid-template-rows:auto;
  }
}

/* ---- order summary ------------------------------------------------------ */
/* ---- checkout order summary --------------------------------------------
   This card previously carried `.sh-summary`, which is the GENERATORS' chip
   strip: `display:flex; flex-wrap:wrap; gap:8px`. Two things followed from
   that, and neither was a spacing bug:

     * the children shrink-wrapped instead of filling the card (the title was
       102px inside a 341px card), and the flex `gap` was added ON TOP of the
       margins already authored for block flow — 12px + 8px between every pair;
     * at 768px, where the card is ~694px wide, the title, the plan row and the
       subtotal all fitted on ONE flex line and were laid out side by side.

   A card is block flow. The rhythm below is stated once here rather than left
   to inherited margins, so it cannot drift again. */
.sh-ordersum{ display:block; }
.sh-ordersum > * { inline-size:100%; }
.sh-ordersum .sh-card__title{ margin-block-end:var(--sh-space-3); }

.sh-ordersum__item{ display:flex; gap:var(--sh-space-3); align-items:flex-start; justify-content:space-between; padding-block:var(--sh-space-3); border-block-end:1px solid var(--sh-border); }
.sh-ordersum__item__main{ min-inline-size:0; display:flex; flex-direction:column; gap:2px; }
.sh-ordersum__item__name{ font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-sm); }
.sh-ordersum__item__meta{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); }
.sh-ordersum__item__price{ font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-bold); white-space:nowrap; }

.sh-ordersum__rows{ margin:var(--sh-space-3) 0 0; display:flex; flex-direction:column; gap:var(--sh-space-2); }
.sh-ordersum__row{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--sh-space-3); }
.sh-ordersum__row dt{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); }
.sh-ordersum__row dd{ margin:0; font-size:var(--sh-fs-sm); overflow-wrap:anywhere; text-align:start; }
.sh-ordersum__row--discount dd{ color:var(--sh-ok); }
.sh-ordersum__row--total{
  margin-block-start:var(--sh-space-2); padding-block-start:var(--sh-space-3);
  border-block-start:1px solid var(--sh-border);
}
.sh-ordersum__row--total dt{ font-size:var(--sh-fs-base); color:var(--sh-text); font-weight:var(--sh-fw-bold); }
.sh-ordersum__row--total dd{ font-size:var(--sh-fs-lg); font-weight:var(--sh-fw-bold); color:var(--sh-brand-text); }

/* ---- coupon ------------------------------------------------------------- */
.sh-coupon{ margin-block-start:var(--sh-space-4); display:flex; flex-direction:column; gap:var(--sh-space-2); }
.sh-coupon__row{ display:flex; gap:var(--sh-space-2); align-items:center; }
/* The coupon sits in the summary card rather than inside .sh-form, so it never
   picked up the form's 46px control height and was drawn 4px shorter than the
   billing inputs beside it. The row matches them, button included. */
.sh-coupon__row{ align-items:stretch; }
.sh-coupon__row .sh-input{ flex:1 1 auto; min-inline-size:0; min-block-size:46px; }
.sh-coupon__row .sh-btn-sm{ min-block-size:46px; padding-inline:var(--sh-space-5); flex:0 0 auto; }
.sh-coupon__applied{
  display:flex; align-items:center; gap:var(--sh-space-2); flex-wrap:wrap;
  padding:var(--sh-space-2) var(--sh-space-3);
  background:var(--sh-chip-bg); border-radius:var(--sh-radius-field);
}
.sh-coupon__code{ font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-sm); }
.sh-coupon__amount{ color:var(--sh-ok); font-size:var(--sh-fs-sm); margin-inline-start:auto; }

/* ---- payment methods ----------------------------------------------------
   Radio cards. The gateway id lives in the input's value and is never drawn;
   only the customer-facing title the gateway itself reports is shown. */
.sh-paylist{ display:flex; flex-direction:column; gap:var(--sh-space-3); }
.sh-payopt{
  display:flex; align-items:flex-start; gap:var(--sh-space-3);
  padding:var(--sh-space-4);
  border:1px solid var(--sh-border); border-radius:var(--sh-radius-field);
  cursor:pointer;
  transition:border-color var(--sh-dur-fast) var(--sh-ease), background var(--sh-dur-fast) var(--sh-ease);
}
.sh-payopt:hover{ border-color:var(--sh-brand); }
.sh-payopt.is-selected{ border-color:var(--sh-brand); background:var(--sh-chip-bg); }
.sh-payopt input{ margin-block-start:3px; accent-color:var(--sh-brand); flex:0 0 auto; }
.sh-payopt__body{ min-inline-size:0; display:flex; flex-direction:column; gap:2px; }
.sh-payopt__title{ font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-sm); }
.sh-payopt__desc{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); line-height:1.7; }

/* ---- payment result -----------------------------------------------------
 * SCOPED (Phase 2.13.19-MQA-AA). This line used to be a bare `.sh-result`, a
 * second declaration of the generator result card defined ~1500 lines above.
 * Same selector, later in the file, so it won round every cascade — and it
 * added `align-items:center` and `text-align:center`, which are right for a
 * payment receipt and wrong for every generator.
 *
 * MEASURED on the live Trade result at 1440px: the stats grid rendered
 * 140px wide inside a 896px card — `align-items:center` stopped the grid
 * stretching, so `repeat(auto-fit,minmax(140px,1fr))` collapsed to its own
 * minimum — and the Persian analysis paragraph was centre-aligned.
 * Trade, Chat, Image, Audio and Video all share `.sh-result` and all had it.
 *
 * `.sh-card.sh-result` is exact rather than approximate: BOTH checkout cards
 * are `sh-card sh-result`, and no generator card carries `sh-card`. Nothing
 * below is re-scoped — the __icon/__title/__actions rules only ever match
 * elements checkout alone creates. */
.sh-card.sh-result{ display:flex; flex-direction:column; align-items:center; gap:var(--sh-space-4); text-align:center; }
.sh-result__icon{
  inline-size:64px; block-size:64px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:var(--sh-chip-bg);
}
.sh-result__icon svg{ inline-size:28px; block-size:28px; }
.sh-result--ok      .sh-result__icon{ color:var(--sh-ok); }
.sh-result--error   .sh-result__icon{ color:var(--sh-danger); }
.sh-result--pending .sh-result__icon{ color:var(--sh-warn); }
.sh-result__title{ margin:0; font-size:var(--sh-fs-lg); font-weight:var(--sh-fw-bold); }
.sh-result .sh-ordersum__rows{ inline-size:100%; max-inline-size:440px; text-align:start; }
.sh-result__actions{ display:flex; flex-wrap:wrap; gap:var(--sh-space-3); align-items:center; justify-content:center; }
.sh-result__actions .sh-gen{ inline-size:auto; padding-inline:var(--sh-space-6); }

/* On a tablet, auto-fit produced three columns and left the fourth plan alone
   on its own row. Two even columns read as a deliberate pair of rows instead. */
@media (max-width:900px){
  .sh-plangrid{ grid-template-columns:repeat(2, minmax(0, 1fr)); }
}
@media (max-width:640px){
  .sh-plangrid{ grid-template-columns:1fr; }
  .sh-result__actions{ inline-size:100%; flex-direction:column; }
  .sh-result__actions .sh-gen,
  .sh-result__actions .sh-btn-sm{ inline-size:100%; justify-content:center; }
}

/* ---- account: open orders (Phase 2.8) -----------------------------------
   A narrow addition to the wallet panel, not a redesign. Paid orders already
   appear as credit transactions below it, so only unpaid ones are listed —
   they carry no credit and would otherwise be invisible. */
.sh-openorders__head{ display:flex; align-items:center; justify-content:space-between; gap:var(--sh-space-3); flex-wrap:wrap; }
.sh-openorders__head .sh-card__title{ margin:0; }
.sh-openorders__none{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); margin-block-start:var(--sh-space-2); }
.sh-openorders__list{ display:flex; flex-direction:column; gap:var(--sh-space-2); margin-block-start:var(--sh-space-3); }
.sh-openorders__row{
  display:flex; align-items:center; gap:var(--sh-space-3); flex-wrap:wrap;
  padding:var(--sh-space-3); background:var(--sh-chip-bg); border-radius:var(--sh-radius-field);
}
.sh-openorders__main{ display:flex; flex-direction:column; gap:2px; min-inline-size:0; flex:1 1 200px; }
.sh-openorders__num{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }
.sh-openorders__items{ font-size:var(--sh-fs-sm); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.sh-openorders__total{ font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-bold); white-space:nowrap; }

/* ==========================================================================
   Phase 2.8.1 — the authored specification block
   Restored from the legacy plans page. Phase 2.8 dropped it, which was a
   migration regression: it is product-owner-authored content, not catalogue
   data. The legacy markup carried inline colours and hard-coded pixel sizes;
   only that presentation is replaced, never the wording.
   ========================================================================== */

/* No underline: the product owner does not want one. It stays a real button —
   it scrolls to and opens the specification block — so the affordance is
   carried by colour, the cursor, a hover shift and a visible focus ring
   instead. `text-decoration:none` is stated explicitly because a <button> is
   not underlined by default but a future <a> here would be. */
.sh-plan__spec{
  background:none; border:0; padding:0; cursor:pointer; text-align:start;
  font:inherit; font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium);
  color:var(--sh-brand-text);
  text-decoration:none;
}
.sh-plan__spec:hover,
.sh-plan__spec:focus-visible{ color:var(--sh-text); text-decoration:none; }
/* keyboard users still get an unmistakable target */
.sh-plan__spec:focus-visible{
  outline:2px solid var(--sh-brand);
  outline-offset:3px;
  border-radius:var(--sh-radius-field);
}

.sh-features{ display:flex; flex-direction:column; gap:var(--sh-space-3); margin-block-start:var(--sh-space-6); scroll-margin-block-start:var(--sh-space-6); }
.sh-features__head{ margin-block-end:var(--sh-space-2); }
.sh-features__title{ margin:0; font-size:var(--sh-fs-lg); font-weight:var(--sh-fw-bold); }
.sh-features__sub{ margin:var(--sh-space-2) 0 0; font-size:var(--sh-fs-sm); color:var(--sh-text-muted); line-height:1.8; }

/* one category per disclosure, as the authored markup had it */
.sh-feat{
  background:var(--sh-surface-2, var(--sh-chip-bg));
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  overflow:hidden;
}
.sh-feat__sum{
  display:flex; align-items:center; gap:var(--sh-space-3);
  padding:var(--sh-space-4); cursor:pointer; list-style:none;
}
.sh-feat__sum::-webkit-details-marker{ display:none; }
.sh-feat__icon{ font-size:22px; line-height:1; flex:0 0 auto; }
.sh-feat__sumtext{ flex:1 1 auto; min-inline-size:0; }
.sh-feat__name{ font-size:var(--sh-fs-base); font-weight:var(--sh-fw-bold); }
.sh-feat__desc{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); margin-block-start:2px; line-height:1.7; }
/* A CSS chevron, so the control needs no icon asset.
   PHYSICAL borders on purpose: `border-inline-end` resolves to the LEFT side in
   RTL, which rotated the mark into a sideways "<" instead of pointing down. A
   disclosure chevron points down when closed and up when open in both scripts,
   so it must not flip with direction. */
.sh-feat__chev{
  inline-size:9px; block-size:9px; flex:0 0 auto;
  border-right:2px solid var(--sh-text-muted);
  border-bottom:2px solid var(--sh-text-muted);
  transform:rotate(45deg);
  transform-origin:center;
  margin-block-end:4px;                 /* optical centring of the rotated box */
  transition:transform var(--sh-dur-fast) var(--sh-ease);
}
.sh-feat[open] .sh-feat__chev{ transform:rotate(-135deg); margin-block:4px 0; }
.sh-feat__body{ padding:0 var(--sh-space-4) var(--sh-space-4); display:flex; flex-direction:column; gap:var(--sh-space-3); }

.sh-tier{
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
  padding:var(--sh-space-3);
}
.sh-tier__head{ display:flex; align-items:center; justify-content:space-between; gap:var(--sh-space-3); margin-block-end:var(--sh-space-3); }
.sh-tier__title{ font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-bold); }
.sh-tier__badge{
  font-size:var(--sh-fs-xs); font-weight:var(--sh-fw-bold);
  background:var(--sh-gold); color:var(--sh-text-on-gold);
  border-radius:var(--sh-radius-pill); padding:3px 10px; white-space:nowrap;
}
/* the authored grid used minmax(230px, 1fr); the same intent in tokens */
.sh-tier__grid{ display:grid; gap:var(--sh-space-2); grid-template-columns:repeat(auto-fit, minmax(210px, 1fr)); }
.sh-item{
  display:flex; align-items:center; justify-content:space-between; gap:var(--sh-space-2);
  min-block-size:42px; padding:var(--sh-space-2) var(--sh-space-3);
  background:var(--sh-chip-bg); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
}
.sh-item__name{ font-size:var(--sh-fs-sm); min-inline-size:0; overflow-wrap:anywhere; }
.sh-item__cost{
  font-size:var(--sh-fs-xs); font-weight:var(--sh-fw-bold); color:var(--sh-brand-text);
  white-space:nowrap; flex:0 0 auto;
}

@media (max-width:640px){
  .sh-tier__grid{ grid-template-columns:1fr; }
  /* a long authored cost string ("۳ + هر ۵۰۰ کلمه ۱.۵") must wrap rather than
     push the row wider than the screen */
  .sh-item{ flex-wrap:wrap; }
  .sh-item__cost{ white-space:normal; }
}

/* ==========================================================================
   Phase 2.8.3 — loading skeletons
   The Plans and Checkout pages showed a full-page «در حال بارگذاری» for about
   1.1s while three sequential round trips completed (CSRF nonce → REST nonce →
   data). The page structure now stays on screen from the first paint and only
   the values arrive late.
   ========================================================================== */

.sh-skel{
  display:block;
  /* --sh-chip-bg is almost the card's own colour: measured 1.08 contrast in
     dark and 1.02 in light, which is invisible. --sh-border-strong is an
     existing token that reads in both themes (1.52 dark, 1.37 light) without
     being as loud as text. */
  background:var(--sh-border-strong);
  border-radius:var(--sh-radius-field);
  block-size:12px;
  /* the shimmer is a hint that something is coming, not an animation to watch */
  animation:sh-skel-pulse 1.4s ease-in-out infinite;
}
@keyframes sh-skel-pulse{ 0%,100%{ opacity:.55 } 50%{ opacity:.9 } }

/* Motion is a preference, and a pulsing block is exactly the kind of thing a
   vestibular-sensitive reader turns off. The placeholder still reads as one. */
@media (prefers-reduced-motion: reduce){
  .sh-skel{ animation:none; opacity:.7; }
}

.sh-skelbox{ display:flex; flex-direction:column; gap:var(--sh-space-3); }

/* a skeleton plan card carries the SAME structure as a real one, so nothing
   moves when the data replaces it */
/* Heights are the MEASURED heights of a real card's parts — name 35, subtitle
   21, price 26, fact row 23, spec link 23, CTA 43 — so the skeleton occupies
   the same 311px and the grid does not grow when the catalogue lands. */
.sh-plan--skel{ pointer-events:none; }
.sh-plan--skel .sh-skel--title{ block-size:35px; inline-size:42%; }
.sh-plan--skel .sh-skel--sub{ block-size:21px; inline-size:70%; }
.sh-plan--skel .sh-skel--price{ block-size:26px; inline-size:58%; }
.sh-plan--skel .sh-skel--fact{ block-size:23px; inline-size:100%; }
.sh-plan--skel .sh-skel--link{ block-size:23px; inline-size:55%; }
.sh-plan--skel .sh-skel--cta{ block-size:43px; inline-size:100%; border-radius:var(--sh-gen-radius); }

/* checkout: the two columns and their cards are real from the first paint;
   only the contents are placeholders */
/* Again the MEASURED heights: a field label is 20px, a payment option 79px,
   the order total 26px, so the skeleton column is the same height as the real
   one and the page does not grow underneath the reader. */
.sh-skel--line{ block-size:20px; }
.sh-skel--row{ block-size:18px; }
.sh-skel--field{ block-size:46px; border-radius:var(--sh-radius-pill); }
.sh-skel--area{ block-size:105px; }
.sh-skel--pay{ block-size:79px; border-radius:var(--sh-radius-field); }
.sh-skel--total{ block-size:26px; inline-size:62%; }
.sh-skel--change{ block-size:34px; inline-size:96px; border-radius:var(--sh-radius-pill); align-self:center; }

/* ==========================================================================
   Phase 2.9 — support tickets
   No Figma frame exists for support (report 25 records Login/Register as
   "NOT DEFINED IN FIGMA", and no ticket frame was exported), so this is the
   established Phase 2 language: the same card, chip, field and notice
   primitives the migrated pages already use.
   ========================================================================== */

.sh-tkhead{ display:flex; align-items:flex-start; justify-content:space-between; gap:var(--sh-space-4); flex-wrap:wrap; }
.sh-tkhead__new{ inline-size:auto; padding-inline:var(--sh-space-6); flex:0 0 auto; }

.sh-tkfilters{ display:flex; gap:var(--sh-space-2); flex-wrap:wrap; }

.sh-tklist{ display:flex; flex-direction:column; gap:var(--sh-space-3); }
.sh-tkrow{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--sh-space-3); inline-size:100%; text-align:start;
  padding:var(--sh-space-4);
  background:var(--sh-surface-2, var(--sh-chip-bg));
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  cursor:pointer; font:inherit; color:inherit;
  transition:border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-tkrow:hover{ border-color:var(--sh-brand); }
.sh-tkrow--skel{ cursor:default; pointer-events:none; }
.sh-tkrow__main{ display:flex; flex-direction:column; gap:var(--sh-space-2); min-inline-size:0; flex:1 1 auto; }
.sh-tkrow__subject{ font-size:var(--sh-fs-base); font-weight:var(--sh-fw-bold); overflow-wrap:anywhere; }
/* a long first message is a preview, not the message: two lines is enough */
.sh-tkrow__preview{
  font-size:var(--sh-fs-sm); color:var(--sh-text-muted); line-height:1.7;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; overflow-wrap:anywhere;
}
.sh-tkrow__meta{ display:flex; gap:var(--sh-space-2); align-items:center; flex-wrap:wrap; }
.sh-tkrow__date{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }

/* status colours carry meaning, so each one is distinct rather than decorative */
.sh-tkstatus{
  flex:0 0 auto; white-space:nowrap;
  font-size:var(--sh-fs-xs); font-weight:var(--sh-fw-bold);
  padding:5px var(--sh-space-3); border-radius:var(--sh-radius-pill);
  border:1px solid var(--sh-border);
}
.sh-tkstatus--open       { color:var(--sh-brand-text);  border-color:var(--sh-brand); }
.sh-tkstatus--answered   { color:var(--sh-ok);     border-color:var(--sh-ok); }
.sh-tkstatus--user_reply { color:var(--sh-warn);   border-color:var(--sh-warn); }
.sh-tkstatus--closed     { color:var(--sh-text-faint); }

/* ---- detail ------------------------------------------------------------- */
.sh-tkback{ align-self:flex-start; }
.sh-tkhero__top{ display:flex; align-items:flex-start; justify-content:space-between; gap:var(--sh-space-3); }
.sh-tkhero__subject{ margin:0; font-size:var(--sh-fs-lg); font-weight:var(--sh-fw-bold); overflow-wrap:anywhere; }
.sh-tkhero__meta{ display:flex; gap:var(--sh-space-2); align-items:center; flex-wrap:wrap; margin-block-start:var(--sh-space-3); }

.sh-tkthread{ display:flex; flex-direction:column; gap:var(--sh-space-3); }
.sh-tkmsg{
  padding:var(--sh-space-4);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  background:var(--sh-surface-2, var(--sh-chip-bg));
  /* the thread is a conversation, so the two authors are visibly different */
  border-inline-start:3px solid var(--sh-border-strong);
}
.sh-tkmsg--staff{ border-inline-start-color:var(--sh-brand); }
.sh-tkmsg--user { border-inline-start-color:var(--sh-border-strong); }
.sh-tkmsg__head{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--sh-space-3); margin-block-end:var(--sh-space-2); }
.sh-tkmsg__who{ font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-bold); }
.sh-tkmsg--staff .sh-tkmsg__who{ color:var(--sh-brand-text); }
.sh-tkmsg__date{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }
.sh-tkmsg__body{ font-size:var(--sh-fs-sm); line-height:1.9; white-space:pre-wrap; overflow-wrap:anywhere; }

.sh-tkreply__actions{ display:flex; gap:var(--sh-space-3); align-items:center; flex-wrap:wrap; margin-block-start:var(--sh-space-3); }
.sh-tkreply__actions .sh-gen{ inline-size:auto; padding-inline:var(--sh-space-6); }

.sh-pager{ display:flex; align-items:center; justify-content:center; gap:var(--sh-space-3); margin-block-start:var(--sh-space-4); }
.sh-pager__info{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); }

.sh-skel--chip{ block-size:26px; inline-size:78px; border-radius:var(--sh-radius-pill); }

@media (max-width:640px){
  .sh-tkhead__new{ inline-size:100%; }
  .sh-tkrow{ flex-direction:column; }
  .sh-tkstatus{ align-self:flex-start; }
  .sh-tkreply__actions .sh-gen,
  .sh-tkreply__actions .sh-btn-sm{ inline-size:100%; justify-content:center; }
}

/* ---- Phase 2.9-T.1 — ticket attachments --------------------------------
   A file is a link to the private download endpoint. It reads as a compact
   row rather than a media preview: these are support documents, not generated
   media, so the generator's media-preview language would be the wrong signal. */
.sh-tkfiles{ display:flex; flex-direction:column; gap:var(--sh-space-2); margin-block-start:var(--sh-space-3); }
.sh-tkfile{
  display:flex; align-items:center; justify-content:space-between; gap:var(--sh-space-3);
  padding:var(--sh-space-2) var(--sh-space-3);
  background:var(--sh-chip-bg); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
  color:var(--sh-text); text-decoration:none; font-size:var(--sh-fs-sm);
  transition:border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-tkfile:hover{ border-color:var(--sh-brand); }
.sh-tkfile__name{ min-inline-size:0; overflow-wrap:anywhere; }
.sh-tkfile__size{ flex:0 0 auto; font-size:var(--sh-fs-xs); color:var(--sh-text-faint); white-space:nowrap; }
.sh-tkhero__fileslabel{ margin-block-start:var(--sh-space-4); font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-bold); }

/* the picker: a native input is hidden behind a real <label> so the control
   keeps keyboard and screen-reader behaviour without the browser's own chrome */
.sh-tkpick{ display:flex; flex-direction:column; gap:var(--sh-space-2); margin-block-start:var(--sh-space-3); }
.sh-tkpick__input{
  position:absolute; inline-size:1px; block-size:1px;
  padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.sh-tkpick__btn{ align-self:flex-start; cursor:pointer; }
.sh-tkpick__input:focus-visible + .sh-tkpick__btn,
.sh-tkpick__btn:focus-visible{ outline:2px solid var(--sh-brand); outline-offset:3px; }
.sh-tkpick__hint{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); }
.sh-tkpick__chips{ display:flex; flex-wrap:wrap; gap:var(--sh-space-2); }
.sh-tkchip{
  display:inline-flex; align-items:center; gap:var(--sh-space-2);
  padding:var(--sh-space-1) var(--sh-space-3);
  background:var(--sh-chip-bg); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-pill); font-size:var(--sh-fs-xs); max-inline-size:100%;
}
.sh-tkchip__name{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-inline-size:18ch; }
.sh-tkchip__size{ color:var(--sh-text-faint); white-space:nowrap; }
.sh-tkchip__x{
  background:none; border:0; cursor:pointer; color:var(--sh-text-muted);
  font-size:var(--sh-fs-base); line-height:1; padding:0 2px;
}
.sh-tkchip__x:hover{ color:var(--sh-danger); }

@media (max-width:640px){
  .sh-tkfile{ flex-wrap:wrap; }
  .sh-tkchip__name{ max-inline-size:12ch; }
}

/* =========================================================================
   PHASE 2.12 — n8n workspace, Storyboard workspace, Orders
   RTL-first: every box uses logical properties, so the same rules serve an
   LTR locale without a mirrored stylesheet. User-authored strings carry
   `unicode-bidi:plaintext` so a Latin workflow name inside a Persian card
   reads correctly by first-strong direction instead of being forced RTL.
   ========================================================================= */

/* ---- workspace switch (shared by /app/image/ generator + storyboard) ---- */
.sh-wsswitch{
  display:flex; gap:var(--sh-space-1); align-self:center;
  padding:var(--sh-space-1);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-pill);
  margin-block-end:var(--sh-space-5);
  inline-size:fit-content; margin-inline:auto;
}
.sh-wsswitch__item{
  display:inline-flex; align-items:center; justify-content:center;
  padding:var(--sh-space-2) var(--sh-space-5);
  border-radius:var(--sh-radius-pill);
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium);
  color:var(--sh-text-muted); text-decoration:none; cursor:pointer;
  transition:background var(--sh-dur-fast) var(--sh-ease),
             color var(--sh-dur-fast) var(--sh-ease);
  white-space:nowrap;
}
.sh-wsswitch__item:hover{ color:var(--sh-text); }
.sh-wsswitch__item.is-active{
  background:var(--sh-brand); color:var(--sh-text-on-brand); cursor:default;
}
.sh-wsswitch__item:focus-visible{ outline:2px solid var(--sh-brand); outline-offset:2px; }

/* ---- n8n --------------------------------------------------------------- */
.sh-n8n{ display:flex; flex-direction:column; gap:var(--sh-space-4); }
.sh-n8n-text{ white-space:pre-wrap; unicode-bidi:plaintext; line-height:var(--sh-lh-base); }
.sh-n8n-out{ display:flex; flex-direction:column; gap:var(--sh-space-3); }

.sh-n8n-starters{ display:flex; flex-direction:column; gap:var(--sh-space-2); }
.sh-n8n-starters__label{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }
.sh-n8n-starters__row{ display:flex; flex-wrap:wrap; gap:var(--sh-space-2); }
.sh-n8n-starters .sh-chip-btn{
  max-inline-size:100%; text-align:start; unicode-bidi:plaintext;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

.sh-n8ncard{
  display:flex; flex-direction:column; gap:var(--sh-space-2);
  padding:var(--sh-space-4);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
}
.sh-n8ncard__head{
  display:flex; align-items:center; gap:var(--sh-space-2); flex-wrap:wrap;
}
.sh-n8ncard__title{
  font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-md);
  unicode-bidi:plaintext; min-inline-size:0;
  overflow-wrap:anywhere;
}
.sh-n8ncard__mode{
  font-size:var(--sh-fs-xs); color:var(--sh-text-muted);
  padding:2px var(--sh-space-2);
  background:var(--sh-chip-bg); border-radius:var(--sh-radius-pill);
}
/* an n8n workflow id is machine text: always LTR, never wrapped mid-token */
.sh-n8ncard__id{
  font-family:var(--sh-font-latin); direction:ltr; unicode-bidi:isolate;
  font-size:var(--sh-fs-xs); color:var(--sh-text-faint);
  overflow-wrap:anywhere;
}
.sh-n8ncard__summary{ unicode-bidi:plaintext; white-space:pre-wrap; font-size:var(--sh-fs-sm); }
.sh-n8ncard__k{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }
.sh-n8ncard__v{ font-size:var(--sh-fs-sm); unicode-bidi:plaintext; }

/* ---- storyboard -------------------------------------------------------- */
.sh-sb{ display:flex; flex-direction:column; gap:var(--sh-space-5); }
.sh-hero--tight{ padding-block:var(--sh-space-4); }

.sh-sbform{ display:flex; flex-direction:column; gap:var(--sh-space-4); }
.sh-sbform__hint{
  font-size:var(--sh-fs-xs); color:var(--sh-text-muted);
  line-height:var(--sh-lh-base); margin:0;
}
.sh-sbfield{ display:flex; flex-direction:column; gap:var(--sh-space-2); }
.sh-sbfield__label{ font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium); color:var(--sh-text-muted); }
/* hug the chips: `.sh-chip-group` paints a filled track, and at card width an
   empty one stretches most of the way across with four chips huddled at the end */
.sh-sbchips{ display:flex; flex-wrap:wrap; gap:var(--sh-space-2);
             inline-size:fit-content; max-inline-size:100%; }

.sh-sbout{ display:flex; flex-direction:column; gap:var(--sh-space-4); }
.sh-sbout__head{ display:flex; align-items:baseline; gap:var(--sh-space-3); flex-wrap:wrap; }
.sh-sbout__title{ font-size:var(--sh-fs-lg); font-weight:var(--sh-fw-bold); margin:0; }
.sh-sbout__count{ font-size:var(--sh-fs-sm); color:var(--sh-text-faint); }
.sh-sbout__summary{ unicode-bidi:plaintext; font-size:var(--sh-fs-sm); color:var(--sh-text-muted); margin:0; }

/* auto-fit, so 4 / 6 / 8 frames all lay out without a per-count rule */
.sh-sbgrid{
  display:grid; gap:var(--sh-space-4);
  grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));
}
.sh-sbscene{ display:flex; flex-direction:column; gap:var(--sh-space-3); }
.sh-sbscene__head{ display:flex; align-items:center; gap:var(--sh-space-2); flex-wrap:wrap; }
.sh-sbscene__no{
  font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-sm); color:var(--sh-brand-text);
}
.sh-sbscene__title{ font-size:var(--sh-fs-md); font-weight:var(--sh-fw-medium); margin:0; unicode-bidi:plaintext; }
.sh-sbscene__desc{
  font-size:var(--sh-fs-sm); color:var(--sh-text-muted);
  line-height:var(--sh-lh-base); margin:0; unicode-bidi:plaintext;
}
.sh-sbscene__meta{ display:flex; gap:var(--sh-space-2); font-size:var(--sh-fs-xs); }
.sh-sbscene__k{ color:var(--sh-text-faint); flex:0 0 auto; }
.sh-sbscene__v{ color:var(--sh-text-muted); unicode-bidi:plaintext; min-inline-size:0; }
/* reserves nothing until there is something to show: an empty shot box would
   leave a grey hole under every unbuilt frame */
.sh-sbscene__shot:empty{ display:none; }
.sh-sbscene__shot{ display:flex; flex-direction:column; gap:var(--sh-space-2); }
.sh-sbscene__img{
  inline-size:100%; block-size:auto; display:block;
  border-radius:var(--sh-radius-field); background:var(--sh-surface-3);
}
.sh-sbscene__open{ align-self:flex-start; }
.sh-sbscene__make{ align-self:flex-start; margin-block-start:auto; }

/* ---- orders (account) --------------------------------------------------- */
.sh-orders__head{ display:flex; align-items:baseline; gap:var(--sh-space-3); flex-wrap:wrap; }
.sh-orders__count{ font-size:var(--sh-fs-sm); color:var(--sh-text-faint); }
.sh-orders__buy{ margin-inline-start:auto; }
.sh-orders__list{ display:flex; flex-direction:column; gap:var(--sh-space-2); }

.sh-ordrow{
  display:flex; align-items:center; gap:var(--sh-space-3);
  inline-size:100%; text-align:start; cursor:pointer;
  padding:var(--sh-space-3) var(--sh-space-4);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field); color:inherit; font:inherit;
  transition:background var(--sh-dur-fast) var(--sh-ease),
             border-color var(--sh-dur-fast) var(--sh-ease);
}
.sh-ordrow:hover{ background:var(--sh-surface-3); border-color:var(--sh-border-strong); }
.sh-ordrow:focus-visible{ outline:2px solid var(--sh-brand); outline-offset:2px; }
.sh-ordrow__top,
.sh-ordrow__bot{
  display:flex; align-items:center; gap:var(--sh-space-2); min-inline-size:0;
}
.sh-ordrow__top{ flex:1 1 auto; flex-wrap:wrap; }
/* an order NUMBER is a figure, not prose: keep it LTR-isolated so the '#' and
   the digits cannot be reordered by the surrounding RTL run */
.sh-ordrow__num{
  font-weight:var(--sh-fw-bold); font-size:var(--sh-fs-sm);
  direction:ltr; unicode-bidi:isolate; flex:0 0 auto;
}
.sh-ordrow__items{
  font-size:var(--sh-fs-sm); color:var(--sh-text-muted);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  min-inline-size:0; unicode-bidi:plaintext;
}
.sh-ordrow__bot{ flex:0 0 auto; gap:var(--sh-space-4); }
.sh-ordrow__date{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); white-space:nowrap; }
.sh-ordrow__total{ font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium); white-space:nowrap; }
/* `U.icon('chevron')` IS the <svg>, and its path already points to the INLINE
   START (left). The drawer's row chevron uses it unrotated for exactly that
   reason. An earlier version here rotated it 90deg, which turned a left-facing
   chevron into an upward-facing one — visible in the capture, not in any
   measurement. Mirrored for LTR so it keeps pointing at the row's end. */
.sh-ordrow__chev{
  flex:0 0 auto; color:var(--sh-text-faint);
  inline-size:18px; block-size:18px;
}
:dir(ltr) .sh-ordrow__chev{ transform:scaleX(-1); }

.sh-ostat{
  display:inline-flex; align-items:center; flex:0 0 auto;
  padding:2px var(--sh-space-2);
  border-radius:var(--sh-radius-pill);
  font-size:var(--sh-fs-xs); font-weight:var(--sh-fw-medium);
  border:1px solid transparent; white-space:nowrap;
}
.sh-ostat--ok{   background:var(--sh-ok-bg);     color:var(--sh-ok);     border-color:var(--sh-ok-border); }
.sh-ostat--warn{ background:var(--sh-warn-bg);   color:var(--sh-warn);   border-color:var(--sh-warn-border); }
.sh-ostat--error{  background:var(--sh-danger-bg); color:var(--sh-danger); border-color:var(--sh-danger-border); }
.sh-ostat--muted{ background:var(--sh-chip-bg);   color:var(--sh-text-muted); border-color:var(--sh-border); }

.sh-opager{
  display:flex; align-items:center; justify-content:center;
  gap:var(--sh-space-3); padding-block-start:var(--sh-space-3);
}
.sh-opager__at{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); }

/* the back control is a bare <button>, so it carries its own affordance */
.sh-obk{
  align-self:flex-start; display:inline-flex; align-items:center; gap:var(--sh-space-1);
  background:none; border:0; padding:var(--sh-space-1) 0; cursor:pointer;
  color:var(--sh-text-muted); font:inherit; font-size:var(--sh-fs-sm);
}
.sh-obk:hover{ color:var(--sh-text); }
.sh-obk:focus-visible{ outline:2px solid var(--sh-brand); outline-offset:2px; border-radius:var(--sh-radius-field); }
.sh-obk svg{ inline-size:16px; block-size:16px; }
/* "back" points the way the reader came from, which flips with the script */
:dir(rtl) .sh-obk svg{ transform:rotate(180deg); }

.sh-odet{ display:flex; flex-direction:column; gap:var(--sh-space-4); }
.sh-odet__head{ display:flex; align-items:center; gap:var(--sh-space-3); flex-wrap:wrap; }
.sh-odet__sub{ font-size:var(--sh-fs-sm); color:var(--sh-text-faint); margin:0; }
/* A <dl> whose children are dt,dd,dt,dd… — auto-fit columns would have put a
   dt in one column and its own dd in the next, pairing each label with the
   WRONG value. Two explicit columns keep every label beside its own value, and
   `max-content` lets the labels size themselves without a magic width. */
.sh-odet__meta{
  display:grid; grid-template-columns:max-content 1fr;
  gap:var(--sh-space-2) var(--sh-space-4); margin:0; font-size:var(--sh-fs-sm);
  align-items:baseline;
}
.sh-odet__meta dt{ color:var(--sh-text-faint); font-size:var(--sh-fs-xs); white-space:nowrap; }
.sh-odet__meta dd{ margin:0; unicode-bidi:plaintext; min-inline-size:0; }
.sh-odet__items{ display:flex; flex-direction:column; gap:var(--sh-space-2); }
.sh-odet__item{
  display:flex; align-items:center; gap:var(--sh-space-3);
  padding:var(--sh-space-3) var(--sh-space-4);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
}
.sh-odet__iname{ flex:1 1 auto; min-inline-size:0; font-size:var(--sh-fs-sm); unicode-bidi:plaintext; }
.sh-odet__iqty{ flex:0 0 auto; font-size:var(--sh-fs-xs); color:var(--sh-text-faint); white-space:nowrap; }
.sh-odet__itotal{ flex:0 0 auto; font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium); white-space:nowrap; }

@media (max-width:640px){
  .sh-wsswitch__item{ padding-inline:var(--sh-space-4); font-size:var(--sh-fs-xs); }
  .sh-sbgrid{ grid-template-columns:1fr; }
  /* the row stacks rather than squeezing the product names to two characters */
  .sh-ordrow{ flex-wrap:wrap; }
  .sh-ordrow__bot{ inline-size:100%; justify-content:space-between; }
  .sh-orders__buy{ margin-inline-start:0; }
}

/* ---- /app/ landing (Phase 2.12) ---------------------------------------- */
.sh-lnd{ display:flex; flex-direction:column; gap:var(--sh-space-6); }

.sh-lnd-credit{ display:flex; flex-direction:column; gap:var(--sh-space-4); }
.sh-lnd-credit__head{ display:flex; align-items:baseline; gap:var(--sh-space-2); flex-wrap:wrap; }
.sh-lnd-credit__k{ font-size:var(--sh-fs-sm); color:var(--sh-text-muted); inline-size:100%; }
.sh-lnd-credit__v{ font-size:var(--sh-fs-xl); font-weight:var(--sh-fw-black); }
.sh-lnd-credit__t{ font-size:var(--sh-fs-sm); color:var(--sh-text-faint); }

.sh-lnd-stats{
  display:grid; gap:var(--sh-space-3);
  grid-template-columns:repeat(auto-fit, minmax(140px, 1fr));
}
.sh-lnd-stat{
  display:flex; flex-direction:column; gap:2px;
  padding:var(--sh-space-3);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
}
.sh-lnd-stat__k{ font-size:var(--sh-fs-xs); color:var(--sh-text-faint); }
.sh-lnd-stat__v{ font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium); }
.sh-lnd-stat--bad .sh-lnd-stat__v{ color:var(--sh-danger); }

.sh-lnd-sec{ display:flex; flex-direction:column; gap:var(--sh-space-3); }
.sh-lnd-sec__title{ font-size:var(--sh-fs-md); font-weight:var(--sh-fw-bold); margin:0; }

.sh-lnd-grid{
  display:grid; gap:var(--sh-space-3);
  grid-template-columns:repeat(auto-fill, minmax(150px, 1fr));
}
.sh-lnd-grid--sm{ grid-template-columns:repeat(auto-fill, minmax(190px, 1fr)); }

.sh-lnd-tile{
  display:flex; align-items:center; gap:var(--sh-space-3);
  padding:var(--sh-space-4);
  background:var(--sh-surface-2); border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-card);
  color:var(--sh-text); text-decoration:none;
  transition:background var(--sh-dur-fast) var(--sh-ease),
             border-color var(--sh-dur-fast) var(--sh-ease),
             transform var(--sh-dur-fast) var(--sh-ease);
}
.sh-lnd-tile:hover{ background:var(--sh-surface-3); border-color:var(--sh-brand); }
.sh-lnd-tile:focus-visible{ outline:2px solid var(--sh-brand); outline-offset:2px; }
.sh-lnd-tile--big{
  flex-direction:column; align-items:flex-start; justify-content:space-between;
  gap:var(--sh-space-5); min-block-size:120px;
}
.sh-lnd-tile__icon{
  display:inline-flex; align-items:center; justify-content:center;
  inline-size:40px; block-size:40px; flex:0 0 auto;
  border-radius:var(--sh-radius-pill);
  background:var(--sh-chip-bg); color:var(--sh-brand-text);
}
.sh-lnd-tile__icon svg{ inline-size:20px; block-size:20px; }
.sh-lnd-tile__label{
  font-size:var(--sh-fs-sm); font-weight:var(--sh-fw-medium);
  min-inline-size:0; overflow-wrap:anywhere;
}
.sh-lnd-tile--big .sh-lnd-tile__label{ font-size:var(--sh-fs-md); }

@media (prefers-reduced-motion:no-preference){
  .sh-lnd-tile:hover{ transform:translateY(-2px); }
}
@media (max-width:640px){
  .sh-lnd-grid{ grid-template-columns:repeat(auto-fill, minmax(130px, 1fr)); }
  .sh-lnd-grid--sm{ grid-template-columns:1fr; }
  .sh-lnd-tile--big{ min-block-size:100px; gap:var(--sh-space-4); }
}

/* ===========================================================================
   ANONYMOUS DISCOVERY  (Phase 2.13.16-R3)
   The chatbot is the public homepage, so the shell now renders for signed-out
   visitors. These rules cover the three things that state needs: a standing
   reason the tools are locked, a way in, and a lock that reads as "sign in",
   not as "broken".
   =========================================================================== */

/* The notice sits in normal flow at the top of the chat body — not fixed and
   not an overlay — so it cannot cover the composer, the header or the safe
   area at any width (§5). */
/* The notice is informational, not an error: it keeps the neutral surface and
   is marked by the brand edge rather than a danger colour. Defined so the tone
   class is real rather than silently falling through to the base rule. */
.sh-notice--info{
  border-inline-start:3px solid var(--sh-brand-text, var(--sh-brand));
}
.sh-anon-notice{
  margin-block-end:var(--sh-space-4);
}
.sh-anon-notice .sh-notice{ inline-size:100%; box-sizing:border-box; }
/* The CTA is a real tap target on a phone. */
.sh-anon-notice .sh-btn-sm{ min-block-size:40px; padding-inline:18px; }

/* Locked, not hidden (§14). The control keeps its label and its normal shape —
   the visitor must still be able to read what the feature is — and carries a
   small padlock. Deliberately NOT `opacity:.4` on everything, which is what
   makes a gated page look broken rather than gated. */
.sh-chatrow__action.sh-locked,
.sh-drawer__row--locked{
  position:relative;
}
.sh-chatrow__action.sh-locked::after,
.sh-drawer__row--locked::after{
  content:"";
  inline-size:13px; block-size:13px;
  flex:0 0 auto;
  margin-inline-start:6px;
  background:currentColor;
  opacity:.55;
  -webkit-mask:var(--sh-lock-icon) center/100% 100% no-repeat;
  mask:var(--sh-lock-icon) center/100% 100% no-repeat;
  --sh-lock-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a5 5 0 0 0-5 5v3H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2h-1V7a5 5 0 0 0-5-5Zm-3 5a3 3 0 1 1 6 0v3H9V7Z'/%3E%3C/svg%3E");
}

/* The header action for a signed-out visitor. It uses the account pill's own
   shape so the header keeps its rhythm, and the brand fill so the one thing
   they can do is the one thing that stands out. */
.sh-account--login{
  background:var(--sh-brand);
  color:#fff;
  border-color:transparent;
}
.sh-account--login:hover{ filter:brightness(1.08); }

/* Sign-in occupies the slot sign-out had, so the drawer keeps its layout. */
.sh-drawer__signin{ color:var(--sh-text); }

/* ===========================================================================
   PHASE 2.13.16-R5.7 — iOS Safari form-focus zoom, product-wide floor
   ===========================================================================
   iOS Safari auto-zooms a focused form control whose rendered font-size is
   under 16px. R5.2 removed the cause on the auth pages; this removes it
   everywhere else. The three shared control rules (.sh-input/.sh-select,
   .sh-chat-prompt, .sh-form .sh-input) now use --sh-fs-input, and this block is
   the floor for controls that carry no Shahoosh class at all — third-party,
   injected or future markup.

   :where() is used deliberately: it contributes ZERO specificity, so every
   component rule still wins. That matters in both directions — a control that
   intentionally renders ABOVE 16px keeps its size (the 18px OTP field is the
   case that must not regress), and a component that needs its own treatment can
   still have it without an !important war.

   NO WIDTH MEDIA QUERY. A max-width breakpoint would pass in portrait and fail
   in iPhone landscape, whose CSS width exceeds the usual mobile breakpoints.
   One pixel of extra type on desktop is not a regression; a landscape-only zoom
   bug is.

   NOT DONE, DELIBERATELY: user-scalable=no / maximum-scale=1 would also stop the
   zoom and are the usual shortcut. They are not used anywhere in this product,
   because they take pinch-zoom away from everyone who needs it.

   Non-text controls (checkbox, radio, range, color, button, submit, reset) are
   excluded: they take no typed input and cannot trigger the zoom. File inputs
   are excluded for the same reason — they open a picker, not a keyboard. */
:where(
  input[type="text"], input[type="password"], input[type="email"],
  input[type="tel"],  input[type="search"],   input[type="url"],
  input[type="number"], input[type="date"],   input[type="datetime-local"],
  input[type="month"], input[type="week"],    input[type="time"],
  input:not([type]),
  textarea, select,
  [contenteditable="true"], [contenteditable=""]
){
  font-size:var(--sh-fs-input);
}

/* ==========================================================================
   MQA-MOBILE-CHAT-COMPACTNESS — compact conversation history on phones
   --------------------------------------------------------------------------
   Owner QA on iPhone: short turns rendered as tall document cards. The height
   was not one oversized value but a stack of them — bubble padding 20px, a
   16/12px flex gap between answer text, the copy bar and the cost pill, a
   12px margin on the copy bar, a 34px button and a 12/20px dashed cost panel,
   inside a 20px-gap thread.

   Every rule lives inside this media query and NO shared/base rule was edited,
   so desktop is untouched by construction. Only presentation changes: no
   markup, no behaviour, no pricing, no direction logic.
   ========================================================================== */
@media (max-width:768px){

  /* §6 — thread rhythm: a conversation, not a stack of panels. */
  .sh-thread{ gap:var(--sh-space-3); }

  /* §7 — avatars set the row's floor height. MQA-B's role-based side logic is
     untouched; only the mark is smaller. */
  .sh-msg{ gap:var(--sh-space-2); }
  .sh-msg__avatar{ inline-size:32px; block-size:32px; }
  .sh-msg__avatar svg{ inline-size:16px; block-size:16px; }

  /* §1 — user bubble: content-driven. Was 16px/20px padding at base size.
     Phase 2.13.19-MQA-W — the user's own text drops a further 1px (14.5 -> 13.5)
     at the owner's request. The ASSISTANT bubble deliberately keeps 14.5px:
     that is the text people actually read at length, and the two are separate
     selectors so the change cannot leak into it. Line-height is nudged up to
     1.65 so the smaller Persian glyphs keep their breathing room. */
  .sh-thread .sh-msg--user .sh-bubble{
    padding:9px var(--sh-space-3);
    border-radius:16px;
    font-size:13.5px;
    line-height:1.65;
    max-inline-size:84%;
  }

  /* §2/§3 — assistant card. Persian stays readable at 1.75. */
  .sh-thread .sh-msg--bot .sh-bubble{
    padding:var(--sh-space-3);
    border-radius:16px;
    font-size:14.5px;
    line-height:1.75;
  }
  /* the inner stacks carried their own 16px/12px gaps on top of the padding */
  .sh-thread .sh-result,
  .sh-thread .sh-result--chat,
  .sh-thread .sh-md{ gap:var(--sh-space-2); }
  .sh-thread .sh-bubble p{ margin-block-end:var(--sh-space-2); }
  .sh-thread .sh-bubble p:last-child{ margin-block-end:0; }

  /* §4 — copy control: reads ~30px, but ::after restores a 44px tap target so
     nothing is lost for touch. */
  .sh-thread .sh-resultbar{ margin-block-start:var(--sh-space-2); gap:var(--sh-space-2); }
  .sh-thread .sh-resultbar .sh-btn-sm{
    height:30px;
    padding-inline:var(--sh-space-3);
    font-size:var(--sh-fs-xs);
    position:relative;
  }
  .sh-thread .sh-resultbar .sh-btn-sm::after{
    content:''; position:absolute; inset:-7px -6px;
  }
  .sh-thread .sh-resultbar .sh-btn-sm svg{ inline-size:14px; block-size:14px; }

  /* §5 — cost becomes an information pill. Values and wording unchanged. */
  .sh-thread .sh-hintbar{
    margin-block-start:0;
    padding:5px var(--sh-space-3);
    border-radius:var(--sh-radius-pill);
    font-size:var(--sh-fs-xs);
    line-height:1.5;
  }
}

/* ==========================================================================
   MQA-V — small-mobile composer accordion  (Video, Face, Trade)
   --------------------------------------------------------------------------
   These rules were always tool-agnostic and are UNCHANGED by MQA-AB, which
   only moved the JavaScript into ShUI.composerAccordion and added Trade as a
   third caller. The header exists in the DOM at all times but is inert until
   that helper adds `.sh-composer--acc`, which it only does when the composer
   is measured to be eating a small phone's viewport. Collapsing hides the siblings with
   display:none — that removes them from the focus order and the a11y tree, so
   no hidden control stays tabbable.

   The DOM is never rebuilt, so prompt, uploads, model, settings and quote all
   survive a collapse. This is presentation only.
   ========================================================================== */
.sh-vacc{ display:none; }

.sh-composer--acc{ position:relative; }
.sh-composer--acc .sh-vacc{
  display:flex; /* narrowed to <=430px by the media rule below */ align-items:center; gap:var(--sh-space-2);
  inline-size:100%; min-inline-size:0;
  padding:var(--sh-space-2) var(--sh-space-1);
  background:none; border:0; color:var(--sh-text);
  text-align:start; cursor:pointer; font:inherit;
}
.sh-composer--acc .sh-vacc__main{
  display:flex; flex-direction:column; gap:2px;
  min-inline-size:0; flex:1 1 auto; text-align:start;
}
/* Long Persian or English prompts must never widen the row. */
.sh-composer--acc .sh-vacc__prompt,
.sh-composer--acc .sh-vacc__meta{
  display:block; min-inline-size:0; max-inline-size:100%;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  unicode-bidi:plaintext;
}
.sh-composer--acc .sh-vacc__prompt{ font-size:14px; font-weight:var(--sh-fw-medium); }
.sh-composer--acc .sh-vacc__meta{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); }
.sh-composer--acc .sh-vacc svg{
  inline-size:18px; block-size:18px; flex:0 0 auto; color:var(--sh-text-muted);
  transition:transform .18s ease;
}
.sh-composer--acc.is-collapsed .sh-vacc svg{ transform:rotate(180deg); }
.sh-composer--acc .sh-vacc:focus-visible{
  outline:2px solid var(--sh-accent); outline-offset:2px; border-radius:var(--sh-radius-pill);
}

/* Collapsed: only the header survives. Both the hiding rule and the header
   itself live INSIDE the small-phone band, so above it the accordion simply
   does not exist — no need to undo anything, and in particular no `revert`,
   which would roll children back to UA defaults and destroy the author
   `display:flex` on rows like .sh-chatrow.
   `overflow:hidden` applies ONLY while collapsed, so an expanded composer
   never clips the model picker. */
@media (max-width:430px){
  .sh-composer--acc.is-collapsed{ overflow:hidden; }
  .sh-composer--acc.is-collapsed > *:not(.sh-vacc){ display:none; }
}

@media (prefers-reduced-motion:no-preference){
  .sh-composer--acc{ transition:padding .18s ease; }
}
@media (prefers-reduced-motion:reduce){
  .sh-composer--acc,
  .sh-composer--acc .sh-vacc svg{ transition:none; }
}

/* Above the small-phone band the header is hidden again, so a device that was
   collapsed and then rotated or resized returns to the normal composer with no
   state to unwind. Tablet and desktop never see any of this. */
@media (min-width:431px){
  .sh-composer--acc .sh-vacc{ display:none; }
}

/* ==========================================================================
   MQA-W — icon sizing for the two generic button families
   --------------------------------------------------------------------------
   The base rule is `svg{20px}`, which is right for a 40px icon circle and too
   heavy inside a 34px pill. §B5 asks for 14–18px and for control heights not
   to grow, so the two families that now carry action icons get their own size.

   `.sh-gen` carried no icons before this phase, so that rule is additive with
   nothing to regress. `.sh-btn-sm` does have one existing user — the chat
   "کپی متن" control — which moves 20px → 16px on desktop. That is intentional:
   it is the same consistency rule, and 20px was overweight for the pill. The
   mobile thread override (14px) still wins where it applies.
   ========================================================================== */
.sh-gen svg{ inline-size:18px; block-size:18px; flex:0 0 auto; }
.sh-btn-sm svg{ inline-size:16px; block-size:16px; flex:0 0 auto; }

/* ==========================================================================
   MQA-Y — leading glyph on the collapsed composer header (Video + Face)
   --------------------------------------------------------------------------
   .sh-vacc is already a flex row with a gap, so making the icon the FIRST
   child puts it at the logical inline-start: right in RTL, left in LTR, with
   no hardcoded offset to break either direction.

   The base rule is svg{20px}; 18px keeps the collapsed row the same height it
   was, which §D4 asks for. flex:0 0 auto stops it shrinking when a long
   Persian summary competes for the row.
   ========================================================================== */
.sh-vacc__icon{
  inline-size:18px; block-size:18px; flex:0 0 auto;
  color:var(--sh-text-muted);
}

/* ==========================================================================
   MQA-AC — candlestick chart inside a Trade analysis result
   --------------------------------------------------------------------------
   Restores the chart the legacy /trade/ page drew. That page gave it a fixed
   560px (420px under its one breakpoint) inside a full-width layout; here it
   lives inside a chat bubble whose width is set by the thread, and on a phone
   the thread is itself a scroll container. So the height is fluid rather than
   fixed — tall enough to read candles on a desktop card, short enough that it
   does not swallow a 40dvh mobile thread — and the width is always 100% of
   whatever the bubble gives it. The library is told to re-measure by a
   ResizeObserver on this box, so the composer accordion collapsing underneath
   it does not leave a stretched canvas.

   No fixed desktop width, and min-inline-size:0 so a flex/grid parent can
   never be widened by the canvas — that is what keeps the card free of
   horizontal overflow at 360px.
   ========================================================================== */
.sh-chart{
  display:flex; flex-direction:column; gap:var(--sh-space-2);
  inline-size:100%; min-inline-size:0;
}
.sh-chart__head{
  display:flex; align-items:center; gap:var(--sh-space-3);
  flex-wrap:wrap; min-inline-size:0;
}
.sh-chart__title{ font-size:var(--sh-fs-xs); color:var(--sh-text-muted); }
.sh-chart__legend{
  display:flex; align-items:center; gap:var(--sh-space-3);
  flex-wrap:wrap; min-inline-size:0;
  font-size:var(--sh-fs-xs); color:var(--sh-text-muted);
}
.sh-chart__leg{ display:inline-flex; align-items:center; gap:6px; }
.sh-chart__dot{
  inline-size:8px; block-size:8px; border-radius:var(--sh-radius-pill);
  flex:0 0 auto;
}
.sh-chart__box{
  inline-size:100%; min-inline-size:0;
  block-size:clamp(220px, 38vh, 420px);
  border:1px solid var(--sh-border);
  border-radius:var(--sh-radius-field);
  background:var(--sh-surface-2);
  overflow:hidden;                  /* the canvas may never widen the bubble */
  position:relative;
}
/* the loading line and every failure sentence share one centred slot */
.sh-chart__msg{
  position:absolute; inset:0;
  display:flex; align-items:center; justify-content:center;
  padding:var(--sh-space-4);
  text-align:center; line-height:1.9;
  font-size:var(--sh-fs-xs); color:var(--sh-text-muted);
}
/* A phone thread is already short; a 38vh chart plus a 985px card is a lot of
   scrolling, so the chart gives some height back where space is scarcest. */
@media (max-width:430px){
  .sh-chart__box{ block-size:clamp(200px, 30vh, 280px); }
  .sh-chart__legend{ gap:var(--sh-space-2); }
}

/* ==========================================================================
   MQA-AF — image bubble in a user turn
   --------------------------------------------------------------------------
   A user turn is normally a flex ROW of [bubble][avatar]. When the turn
   carries media it becomes a flex COLUMN — media, then that same row — so the
   picture sits ABOVE the text and the avatar stays beside the text where the
   role-based layout puts it. `align-items:flex-end` keeps the whole stack on
   the user's side; because .sh-msg is direction:ltr and the user row is
   justify-content:flex-end, that is the same edge the bubble already uses, in
   both RTL and LTR, with no hardcoded side.
   ========================================================================== */
.sh-msg--stack{
  flex-direction:column;
  align-items:flex-end;
  gap:var(--sh-space-2);
  min-inline-size:0;
}
/* the original role line, unchanged in behaviour */
.sh-msg--stack .sh-msg__line{
  display:flex; gap:var(--sh-space-3); align-items:flex-start;
  justify-content:flex-end; max-inline-size:100%; min-inline-size:0;
}
.sh-msg__media{
  display:flex; flex-wrap:wrap; gap:var(--sh-space-2);
  justify-content:flex-end; max-inline-size:100%; min-inline-size:0;
  /* the avatar column + its gap, so a preview lines up with the text bubble
     above it rather than sitting under the avatar */
  margin-inline-end:calc(40px + var(--sh-space-3));
}
.sh-msg__shot{
  max-inline-size:220px; min-inline-size:0;
  border-radius:var(--sh-radius-card);
  overflow:hidden;                       /* clips the image to the corners */
  background:var(--sh-surface-2);
  border:1px solid var(--sh-border);
  line-height:0;                         /* no descender gap under the img */
}
/* No fixed height: the natural ratio is preserved and portrait images are not
   cropped. max-block-size stops a very tall image owning the whole thread. */
.sh-msg__shot img{
  display:block;
  inline-size:auto; max-inline-size:100%;
  block-size:auto; max-block-size:260px;
  object-fit:contain;
}
.sh-msg__shot-fallback{
  display:block; padding:var(--sh-space-3);
  font-size:var(--sh-fs-xs); color:var(--sh-text-muted);
  line-height:1.8; text-align:center; unicode-bidi:plaintext;
}
/* two or more images share the row and shrink together rather than pushing
   the bubble sideways */
.sh-msg__media--multi .sh-msg__shot{ max-inline-size:150px; }
.sh-msg__media--multi .sh-msg__shot img{ max-block-size:170px; }

@media (max-width:430px){
  .sh-msg__media{ margin-inline-end:calc(32px + var(--sh-space-2)); }
  .sh-msg__shot{ max-inline-size:180px; }
  .sh-msg__shot img{ max-block-size:210px; }
  .sh-msg__media--multi .sh-msg__shot{ max-inline-size:120px; }
  .sh-msg__media--multi .sh-msg__shot img{ max-block-size:140px; }
}
