/* ============================================================================
   Firas AI — Definitive Design System (mature Claude.ai aesthetic)
   One stylesheet serves LTR + RTL via CSS logical properties.
   Light = default; [data-theme="dark"] = warm charcoal.
   Pure static, no build step. All JS hooks (ids/classes/data-attrs) preserved.
   ========================================================================== */

/* ----------------------------------------------------------------------------
   1. Design Tokens
---------------------------------------------------------------------------- */
:root {
  /* ---------- COLOR · LIGHT ----------
     Restored to the pre-redesign neutrals. The identity green was designed against THESE
     surfaces; on the cream I had swapped in, #237A68 fell to 4.47:1 and #2E7D5B to 4.31:1 —
     below AA. Putting the colour back without putting its ground back would have shipped an
     accessibility regression disguised as a restoration. */
  --color-bg:            #FAF9F5;
  --color-bg-subtle:     #F0EEE6;
  --color-surface:       #FFFFFF;
  /* Referenced by 5 presentation-deck rules but never defined anywhere, so those
     backgrounds resolved to `transparent` and the deck card lost every sunken panel. */
  --color-surface-sunken:#F0EEE6;
  --color-sidebar-bg:    #F5F4EE;

  --color-text-primary:  #1A1A18;
  --color-text-secondary:#6B6A63;
  /* The ONE value not restored verbatim. The original #908E85 measures 3.12:1 on #FAF9F5 —
     a confirmed AA failure, and this token colours every ::placeholder in the app. #6E6C64
     is the same tone one step darker and clears 4.5:1. Restoring the look must not mean
     restoring a bug the audit already caught. */
  --color-text-muted:    #6E6C64;

  --color-border-hairline:#E6E4DA;
  --color-border-strong:  #D8D6CB;
  /* ALIAS — 22 rules (most of Firas Code's chrome: toolbar buttons, the project
     card, template tiles, the diff card, the AI input) were written against a
     plain `--color-border` that was never defined anywhere. `border: 1px solid
     var(--color-border)` is then invalid-at-computed-value-time, so the WHOLE
     shorthand resets and border-style becomes `none` — those controls rendered
     as borderless labels floating on a same-coloured surface, which is what made
     the workspace look unfinished. Defined via the hairline token so it keeps
     following the theme (the dark block redefines that token below). */
  --color-border: var(--color-border-hairline);

  /* THE IDENTITY COLOUR — green tea. Restored to the exact pre-redesign values after a
     brass/gold detour: the owner's mark is this green and it is not up for reinterpretation.
     Verified against `git show HEAD:styles.css`. */
  --color-accent:        #237A68;
  --color-accent-hover:  #1A6253;
  --color-accent-soft:   rgba(44,138,120,0.08);
  --color-accent-ring:   rgba(44,138,120,0.40);
  --color-on-accent:     #FFFFFF;
  /* The mark's second tone. The Beam's lower arm sits a step deeper than the primary so the
     letter reads as mass with light falling across it rather than one flat silhouette. */
  --color-accent-deep:   #14544A;

  --color-success:       #2E7D5B;
  --color-error:         #B3261E;

  /* ---------- HAIRLINES ----------
     Named for their FUNCTION (a drawn rule), not for a material. The previous names said
     "brass", which stopped being true the moment the palette moved — a token whose name
     encodes a colour has to be renamed every time the brand shifts. These follow the accent. */
  --color-line:          var(--color-accent);
  --color-line-bright:   var(--color-accent-hover);
  --grain-opacity:       0.030;

  /* ---------- SPACING (4px base) ---------- */
  --space-0:  0;
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  /* --space-7 was MISSING while `.landing` referenced it. One unresolvable var()
     invalidates the whole `padding` shorthand, so the logged-out landing screen —
     the first thing every visitor sees — was rendering with padding:0 on all four
     sides, edge-to-edge, with the safe-area inset for notched phones silently lost. */
  --space-7:  28px;
  --space-8:  32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;

  /* ---------- RADII ----------
     An engraved instrument is cut, not moulded. Corners tighten to a machined
     edge; only genuinely circular parts (the ring, the rotor, avatars) stay round.
     Tightening these six values sharpens every panel in the app at once. */
  --radius-xs:  2px;
  --radius-sm:  3px;
  --radius-md:  5px;
  --radius-lg:  7px;
  --radius-xl:  9px;
  --radius-pill: 999px;

  /* ---------- BORDER WIDTHS ---------- */
  --border-hairline: 1px;
  --border-strong:   1.5px;

  /* ---------- SHADOWS (very soft, no glow) ---------- */
  --shadow-xs:    0 1px 2px rgba(0,0,0,0.04);
  --shadow-sm:    0 1px 2px rgba(0,0,0,0.04), 0 2px 6px rgba(0,0,0,0.05);
  --shadow-md:    0 4px 12px rgba(0,0,0,0.06);
  --shadow-lg:    0 8px 24px rgba(0,0,0,0.06);
  --shadow-focus: 0 0 0 3px var(--color-accent-ring);
  --shadow-composer: 0 1px 2px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.05);

  /* ---------- TYPOGRAPHY · FAMILIES ----------
     Display is Kufic because Kufic is what was actually cut into astrolabe plates —
     the display voice is the object's own lettering. Body is a workhorse: this is a
     surface people read in for hours, and Arabic legibility outranks character there.
     `--font-serif` is kept as a name because ~40 rules reference it, but it now points
     at the display stack; headings render Kufic in Arabic and Archivo in Latin. */
  /* Family order is per-glyph, and that is load-bearing here: Latin resolves to the
     first face that has it, Arabic falls through to the first face that does. Putting
     the Arabic face BEFORE system-ui is what stops Windows quietly serving Segoe UI
     for every Arabic character and erasing the type decision. */
  --font-display: "Archivo", "Reem Kufi", "Segoe UI", sans-serif;
  --font-serif: var(--font-display);
  --font-sans:  "Archivo", "Noto Sans Arabic", system-ui, -apple-system, sans-serif;
  --font-arabic:"Noto Sans Arabic", "Segoe UI", Tahoma, sans-serif;
  /* Arabic has its OWN display voice, and it needs its own token: 166 rules in this
     file point Arabic at --font-arabic, including one that captures every h1/h2/h3.
     Without this token the Kufic display face is never reached in the primary language —
     it would download and be used for nothing. Body text deliberately stays on the
     workhorse; Kufi is a display cut and gets unpleasant at 13px. */
  --font-arabic-display: "Reem Kufi", "Noto Sans Arabic", "Segoe UI", Tahoma, sans-serif;
  /* Mono is for code, data and measurement only — never as a costume for "technical". */
  --font-mono:  "JetBrains Mono", "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace;

  /* ---------- TYPE SCALE (size / line-height) ---------- */
  --text-xs:   12px;  --leading-xs:  16px;
  --text-sm:   13px;  --leading-sm:  20px;
  --text-base: 15px;  --leading-base:24px;
  --text-md:   16px;  --leading-md:  26px;
  --text-lg:   17px;  --leading-lg:  29px;
  --text-xl:   21px;  --leading-xl:  30px;
  --text-2xl:  26px;  --leading-2xl: 34px;
  --text-3xl:  32px;  --leading-3xl: 40px;

  --weight-regular: 400;
  --weight-medium:  500;
  --weight-semibold:600;
  /* Referenced by the Max-tier "beta" badge but never defined. font-weight inherits,
     so the badge was silently rendering at its parent button's weight — at 9px, weight
     was the only thing making it legible. */
  --weight-bold:    700;
  --tracking-wide:  0.06em;
  --tracking-tight: -0.01em;

  /* ---------- Z-INDEX ---------- */
  --z-base:        0;
  --z-sticky:      100;
  --z-scrollbtn:   200;
  --z-dropdown:    300;
  --z-drawer:      400;
  --z-overlay:     390;
  --z-toast:       500;

  /* ---------- MOTION ---------- */
  --dur-fast:   120ms;
  --dur-base:   160ms;
  --dur-slow:   220ms;
  --ease-out:   cubic-bezier(0.22, 1, 0.36, 1);
  --ease-inout: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-ios:   cubic-bezier(0.32, 0.72, 0, 1); /* the private iOS keyboard curve */
  /* Was an elastic overshoot curve (a >1 control point, so it travelled past its target
     and sprang back). A machined part does not bounce past its detent; it decelerates hard
     and stops. Retuned to ease-out-expo, which corrects every tap in the app from this one
     line. The old value is described rather than quoted so design scanners do not keep
     reporting it as still present. */
  --spring:     cubic-bezier(0.16, 1, 0.30, 1);
  --kb-dur:     0.30s; /* matches the native iOS keyboard slide */

  /* ---------- NATIVE SHELL (set by MainActivity on Android; env() defaults elsewhere) ---------- */
  --safe-top:    env(safe-area-inset-top, 0px);
  --safe-right:  env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left:   env(safe-area-inset-left, 0px);
  --kb: 0px;       /* iOS keyboard height, driven by app.js */
  --kb-inset: 0px; /* Android IME height, driven by MainActivity */

  /* ---------- LAYOUT ---------- */
  --sidebar-w: 268px;
  --topbar-h:  56px;
  --column-w:  744px;
}

/* ---------- COLOR · DARK — THE IDENTITY ----------
   The use scene decided this, not the category: an Arabic reader at night, phone or
   laptop, one lamp or none, checking an answer against a page before an exam. So the
   ground is the patina an unlit bronze instrument actually sits in — warm, slightly
   green, never neutral charcoal and never the blue-black every AI product ships.
   Light comes only from the engraving. */
[data-theme="dark"] {
  --color-bg:            #262624;
  --color-bg-subtle:     #1F1E1D;
  --color-surface:       #30302E;
  --color-surface-sunken:#1F1E1D;
  --color-sidebar-bg:    #1F1E1D;

  --color-text-primary:  #ECEAE3;
  --color-text-secondary:#A6A39A;
  /* Same exception as the light theme: the original #76746C measures 3.24:1 on #262624 and
     3.55:1 on the sidebar — both below AA, on the token that paints every placeholder.
     #9A978E is the same tone one step lighter and clears it. */
  --color-text-muted:    #9A978E;

  --color-border-hairline:#3A3A36;
  --color-border-strong:  #46453F;

  /* Green tea — the identity colour, restored exactly as it was. */
  --color-accent:        #57AE9C;
  --color-accent-hover:  #6BC0AE;
  --color-accent-soft:   rgba(87,174,156,0.14);
  --color-accent-ring:   rgba(87,174,156,0.45);
  --color-on-accent:     #1F1E1D;
  --color-accent-deep:   #2F6F62;

  --color-success:       #4BA784;
  --color-error:         #E06A60;

  --color-line:          var(--color-accent);
  --color-line-bright:   var(--color-accent-hover);
  --grain-opacity:       0.042;

  /* Metal sits on its surface; it does not hover above it. Offsets stay short and
     the blur stays tight, so panels read as machined plates rather than floating cards. */
  --shadow-xs:    0 1px 1px rgba(0,0,0,0.45);
  --shadow-sm:    0 1px 2px rgba(0,0,0,0.50), 0 2px 4px rgba(0,0,0,0.35);
  --shadow-md:    0 2px 8px rgba(0,0,0,0.50);
  --shadow-lg:    0 6px 20px rgba(0,0,0,0.58);
  --shadow-composer: 0 1px 2px rgba(0,0,0,0.50), 0 6px 18px rgba(0,0,0,0.45);
}

/* ----------------------------------------------------------------------------
   2. Base + Motion + Accessibility
---------------------------------------------------------------------------- */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

body {
  min-height: 100dvh;
  background: var(--color-bg);
  color: var(--color-text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-base);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow: hidden;
  transition: background var(--dur-slow) var(--ease-inout), color var(--dur-slow) var(--ease-inout);
}

/* The ground is a metal surface, so it has tooth. One fixed layer of fractal noise at
   a few percent — it never repaints and never scrolls.
   This replaces a corner radial-gradient glow: a coloured halo bleeding out of one
   corner is decoration standing in for depth, and it is the single most recognizable
   tell of the look this world refuses. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: var(--grain-opacity);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

button, input, textarea { font-family: inherit; color: inherit; }
button { cursor: pointer; border: none; background: none; }
a { color: var(--color-accent); text-decoration: none; }
ul, ol { margin: 0; }
h1, h2, h3 { margin: 0; font-family: var(--font-serif); font-weight: var(--weight-semibold); }

::selection { background: var(--color-accent-soft); }

/* Scrollbars */
* { scrollbar-width: thin; scrollbar-color: var(--color-border-strong) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--color-border-strong); border-radius: var(--radius-pill); }
*::-webkit-scrollbar-track { background: transparent; }

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

@keyframes msg-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes caret-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }

.message-enter { animation: msg-in var(--dur-slow) var(--ease-out); }

/* Reduced motion, meant rather than merely obeyed.
   The blanket rule this replaces also set `animation-iteration-count: 1`, which stops every
   SPINNER after a single turn. A frozen spinner is not a calmer spinner — it reads as a hung
   app, so the setting made the product less usable for the people it exists to protect.

   What reduced motion actually asks for is no vestibular movement: no travel, no scaling, no
   parallax. It does not ask for state to stop being reported. So movement goes, and anything
   whose job is to say "still working" keeps working — re-expressed as an opacity pulse, which
   carries the same meaning with nothing moving. */
@keyframes rm-working { 0%, 100% { opacity: 0.32; } 50% { opacity: 1; } }

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

  /* Busy + progress indicators keep reporting. */
  .landing__cta.is-busy::after,
  .auth__submit.is-busy::after,
  .cw-bar__btn--primary.is-busy .cw-bar__btn-ico,
  .cw-live__spin,
  .cw-plan__spin,
  .cw-status__save.is-saving::before,
  .thread-loading__dot,
  .firas-ask-loading__dots > span,
  .skeleton,
  .stream-caret::after {
    animation-name: rm-working !important;
    animation-duration: 1.5s !important;
    animation-iteration-count: infinite !important;
    animation-timing-function: var(--ease-inout) !important;
    transform: none !important;   /* the pulse replaces the rotation entirely */
  }
}

/* ----------------------------------------------------------------------------
   Arabic font swap (mandatory) — Plex Arabic replaces both serif & sans roles
---------------------------------------------------------------------------- */
[dir="rtl"], [lang="ar"], .is-arabic { font-family: var(--font-arabic); }

/* Long-form Arabic — assistant answers and rendered markdown — stays on the workhorse.
   This is reading material, sometimes for an hour, and legibility beats character. */
[dir="rtl"] .assistant-prose, [dir="rtl"] .md { font-family: var(--font-arabic); }

/* The product's own voice — headings, the wordmark, the welcome title — takes the Kufic
   display cut. Kufic is the script astrolabes were actually engraved in, so this is the
   one place the type carries the world rather than just carrying the words. */
/* h1/h2 only. Kufic is a display cut with tight counters — at the 15-16px an h3 card
   title actually renders, it stops being characterful and starts being hard to read.
   h3 keeps the workhorse. */
[dir="rtl"] .wordmark, [dir="rtl"] h1, [dir="rtl"] h2,
[lang="ar"] h1, [lang="ar"] h2,
[dir="rtl"] .welcome__title {
  font-family: var(--font-arabic-display);
  letter-spacing: 0;
  /* Reem Kufi ships a very narrow word-space — at display sizes "لماذا فِراس" closes up
     and reads as a single word. Opening the space is the difference between a Kufic
     heading and an unreadable one. */
  word-spacing: 0.16em;
}
/* …but NOT inside answers: a heading the model wrote is content, not chrome. */
[dir="rtl"] .md h1, [dir="rtl"] .md h2, [dir="rtl"] .md h3 { font-family: var(--font-arabic); }

/* ----------------------------------------------------------------------------
   Firas mark — a distinct rounded-square app-icon glyph (NOT a Claude sunburst).
   A confident, flat teal tile containing a crisp inline-SVG signal/node glyph.
   Used everywhere as the brand mark: topbar, welcome, avatar, auth, favicon.
---------------------------------------------------------------------------- */
/* A brass disc, not a rounded-square app tile. The astrolabe is a disc; making the
   mark one is the cheapest, truest identity move available, and it distinguishes the
   product instantly from every squircle in the category. The shallow gradient is the
   sheen real metal has under one light — it is material, not decoration. */
.nib {
  position: relative;
  display: inline-grid; place-items: center;
  inline-size: 24px; block-size: 24px;
  border-radius: 50%;
  background: linear-gradient(150deg, var(--color-accent-hover), var(--color-accent) 62%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.22), inset 0 -1px 0 rgba(0,0,0,0.18);
  flex: 0 0 auto;
  direction: ltr;
  overflow: hidden;
}
/* The geometric glyph is rendered as an inline SVG injected by JS (.fmark).
   A serif "F" fallback (.glyph) shows if SVG injection hasn't run. */
.nib .glyph {
  font-family: var(--font-serif);
  color: var(--color-on-accent);
  font-weight: var(--weight-semibold);
  font-size: 13px;
  line-height: 1;
}
/* 72%: the engraving runs nearly to the rim on a real plate. */
.nib .fmark { inline-size: 72%; block-size: 72%; display: block; color: var(--color-on-accent); }
.nib .fmark + .glyph { display: none; }
.nib[data-size="sm"] { inline-size: 20px; block-size: 20px; }
.nib[data-size="sm"] .glyph { font-size: 11px; }
.nib[data-size="lg"] { inline-size: 44px; block-size: 44px; }
.nib[data-size="lg"] .glyph { font-size: 24px; }
.nib[data-size="xl"] { inline-size: 76px; block-size: 76px; }
.nib[data-size="xl"] .glyph { font-size: 40px; }
.nib.is-aurora { background: linear-gradient(150deg, var(--color-accent-hover), var(--color-accent) 62%); }

/* Wordmark — serif, quiet, always LTR */
.wordmark {
  font-family: var(--font-serif);
  font-weight: var(--weight-semibold);
  font-size: 20px;
  letter-spacing: var(--tracking-tight);
  color: var(--color-text-primary);
  direction: ltr; unicode-bidi: isolate;
  white-space: nowrap;
}
.wordmark .firas { color: var(--color-text-primary); }
.wordmark .ai { color: var(--color-text-primary); }

.logo-lockup {
  display: inline-flex; align-items: center; gap: var(--space-2);
  direction: ltr; text-decoration: none;
}
.logo-lockup:hover { text-decoration: none; }

/* ----------------------------------------------------------------------------
   3. App Shell
---------------------------------------------------------------------------- */
.app {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  block-size: 100dvh;
}

/* ----- Sidebar ----------------------------------------------------------- */
.sidebar {
  display: flex; flex-direction: column;
  block-size: 100dvh;
  padding: var(--space-3);
  gap: var(--space-2);
  background: var(--color-sidebar-bg);
  border-inline-end: var(--border-hairline) solid var(--color-border-hairline);
  z-index: 30;
}
.sidebar__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-2) var(--space-2) var(--space-3);
}
.sidebar .sidebar__close { display: none; } /* hidden on desktop; .sidebar bump beats .icon-btn */

.btn-newchat {
  display: flex; align-items: center; gap: var(--space-2);
  inline-size: 100%;
  block-size: 40px;
  padding-inline: var(--space-3);
  border-radius: var(--radius-sm);
  border: var(--border-hairline) solid var(--color-border-hairline);
  background: transparent;
  color: var(--color-text-primary);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.btn-newchat svg { inline-size: 16px; block-size: 16px; color: var(--color-text-secondary); flex: 0 0 auto; }
.btn-newchat:hover { background: var(--color-surface); border-color: var(--color-border-strong); }

/* Search */
.sidebar__search { position: relative; margin-block: var(--space-2) var(--space-1); }
.sidebar__search svg {
  position: absolute; inset-block-start: 50%; inset-inline-start: var(--space-3);
  transform: translateY(-50%);
  inline-size: 15px; block-size: 15px; color: var(--color-text-muted); pointer-events: none;
}
.sidebar__search input {
  inline-size: 100%;
  block-size: 36px;
  padding-inline: 36px var(--space-3);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-sm);
  color: var(--color-text-primary);
  font-size: var(--text-sm);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.sidebar__search input::placeholder { color: var(--color-text-muted); }
.sidebar__search input:focus { outline: none; border-color: var(--color-border-strong); }

/* History */
.history { flex: 1; overflow-y: auto; margin-inline: calc(var(--space-1) * -1); padding-inline: var(--space-1); }
.history__group-label {
  padding: var(--space-3) var(--space-3) var(--space-1);
  font-size: var(--text-xs); line-height: var(--leading-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  color: var(--color-text-muted);
}
.history__empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--space-2); padding: var(--space-10) var(--space-4);
  color: var(--color-text-muted); font-size: var(--text-sm);
}
.history__empty-ic { display: grid; place-items: center; opacity: 0.6; }

.chat-item {
  position: relative;
  display: flex; align-items: center; gap: var(--space-2);
  block-size: 36px;
  padding-inline: var(--space-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--color-text-secondary);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.chat-item:hover { background: var(--color-bg-subtle); color: var(--color-text-primary); }
.chat-item.is-active { background: var(--color-bg-subtle); color: var(--color-text-primary); }
.chat-item.is-active .chat-item__title { font-weight: var(--weight-medium); }
.chat-item__title {
  flex: 1; min-inline-size: 0;
  font-size: var(--text-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chat-item__title input {
  inline-size: 100%; background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-strong); border-radius: var(--radius-xs);
  padding: 2px 6px; font-size: var(--text-sm); color: var(--color-text-primary);
}
.chat-item__actions { display: none; gap: 2px; flex: 0 0 auto; }
.chat-item:hover .chat-item__actions,
.chat-item.is-active .chat-item__actions { display: flex; }
.chat-item__actions button {
  display: grid; place-items: center;
  inline-size: 26px; block-size: 26px;
  border-radius: var(--radius-xs); color: var(--color-text-muted);
  transition: background var(--dur-fast), color var(--dur-fast);
}
.chat-item__actions button:hover { background: var(--color-surface); color: var(--color-text-secondary); }
.chat-item__actions svg { inline-size: 14px; block-size: 14px; }

/* Account pill */
.account-pill {
  display: flex; align-items: center; gap: var(--space-3);
  margin-top: var(--space-2); padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  border: var(--border-hairline) solid transparent;
  transition: background var(--dur-fast) var(--ease-out);
}
.account-pill:hover { background: var(--color-bg-subtle); }
.account-pill__avatar {
  inline-size: 28px; block-size: 28px; border-radius: var(--radius-pill);
  display: grid; place-items: center; flex: 0 0 auto;
  background: var(--color-accent); color: var(--color-on-accent);
  font-weight: var(--weight-semibold); font-size: var(--text-xs);
}
.account-pill__name { flex: 1; min-inline-size: 0; font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.account-pill__logout {
  display: grid; place-items: center; flex: 0 0 auto;
  inline-size: 30px; block-size: 30px; border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.account-pill__logout:hover { background: var(--color-surface); color: var(--color-text-primary); }
.account-pill__logout svg { inline-size: 16px; block-size: 16px; }
.account-pill__settings {
  display: grid; place-items: center; flex: 0 0 auto;
  inline-size: 30px; block-size: 30px; border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.account-pill__settings:hover { background: var(--color-surface); color: var(--color-text-primary); }
.account-pill__settings svg { inline-size: 16px; block-size: 16px; }

/* ----------------------------------------------------------------------------
   4. Main column
---------------------------------------------------------------------------- */
.main { position: relative; display: flex; flex-direction: column; block-size: 100dvh; min-inline-size: 0; }

/* Top bar */
.topbar {
  display: flex; align-items: center; gap: var(--space-3);
  block-size: var(--topbar-h);
  min-block-size: var(--topbar-h);
  padding-inline: var(--space-4);
  background: var(--color-bg);
  border-block-end: var(--border-hairline) solid var(--color-border-hairline);
  position: sticky; top: 0;
  z-index: var(--z-sticky);
}
.topbar .topbar__mobile-only { display: none; } /* hidden on desktop; .topbar bump beats .icon-btn */
.topbar__spacer { flex: 1; }

.topbar__desktop-mark .wordmark { font-size: 20px; }

/* ----- Tier switcher (segmented control) ----- */
.tier-switch {
  display: inline-flex; align-items: center;
  block-size: 32px;
  padding: 2px;
  gap: 2px;
  background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-sm);
}
.tier-switch__btn {
  display: inline-flex; align-items: center; gap: 5px;
  block-size: 26px;
  padding-inline: var(--space-3);
  border-radius: 6px;
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  color: var(--color-text-secondary);
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.tier-switch__btn .tier-ico { display: none; }      /* spark handled via ::before for ultra */
.tier-switch__btn:hover { color: var(--color-text-primary); }
.tier-switch__btn.is-active {
  background: var(--color-surface);
  color: var(--color-text-primary);
  box-shadow: var(--shadow-xs);
}
/* Ultra gets a small teal node dot before its label (not a Claude spark) */
.tier-switch__btn[data-tier="ultra"] .tier-name::before {
  content: "";
  display: inline-block;
  inline-size: 6px; block-size: 6px;
  margin-inline-end: 6px;
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  vertical-align: 1px;
}
.tier-switch__btn[data-tier="ultra"].is-active {
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font-weight: var(--weight-semibold);
  box-shadow: none;
}
.tier-switch__btn[data-tier="ultra"].is-active .tier-name::before { background: var(--color-accent); }

/* Max gets a PURPLE node dot + a premium glow when active */
.tier-switch__btn[data-tier="max"] .tier-name::before {
  content: ""; display: inline-block;
  inline-size: 6px; block-size: 6px; margin-inline-end: 6px;
  border-radius: var(--radius-pill); background: #8b5cf6; vertical-align: 1px;
}
.tier-switch__btn[data-tier="max"]:hover { color: #7c3aed; }
.tier-switch__btn[data-tier="max"].is-active {
  background: rgba(139, 92, 246, 0.13);
  color: #7c3aed; font-weight: var(--weight-semibold); box-shadow: none;
}
.tier-switch__btn[data-tier="max"].is-active .tier-name::before {
  background: #8b5cf6; animation: maxDotPulse 1.8s ease-in-out infinite;
}
@keyframes maxDotPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.5); }
  50%      { box-shadow: 0 0 0 5px rgba(139, 92, 246, 0); }
}
/* "beta" badge next to Max */
.tier-switch__btn[data-tier="max"] .tier-beta {
  display: inline-flex; align-items: center;
  margin-inline-start: 5px;
  padding: 1px 5px;
  font-size: 9px; font-weight: var(--weight-bold);
  letter-spacing: 0.04em; text-transform: uppercase;
  line-height: 1.4;
  color: #a78bfa;
  background: rgba(139, 92, 246, 0.16);
  border: 1px solid rgba(139, 92, 246, 0.35);
  border-radius: var(--radius-pill);
}
.tier-switch__btn[data-tier="max"].is-active .tier-beta {
  color: #7c3aed; background: rgba(139, 92, 246, 0.22);
}

/* Professional "switch" pop on whichever tier was just activated */
.tier-switch__btn.just-activated { animation: tierPop 0.42s var(--ease-out); }
@keyframes tierPop {
  0%   { transform: scale(0.9); }
  45%  { transform: scale(1.07); }
  100% { transform: scale(1); }
}
.tier-switch__btn[data-tier="max"].just-activated {
  animation: tierPop 0.42s var(--ease-out), maxGlow 0.65s var(--ease-out);
}
@keyframes maxGlow {
  0%   { box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.55); }
  100% { box-shadow: 0 0 0 16px rgba(139, 92, 246, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .tier-switch__btn.just-activated,
  .tier-switch__btn[data-tier="max"].is-active .tier-name::before { animation: none; }
}

/* Icon button */
.icon-btn {
  display: grid; place-items: center;
  inline-size: 36px; block-size: 36px;
  min-inline-size: 36px;
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.icon-btn:hover { background: var(--color-bg-subtle); }
.icon-btn:active { background: var(--color-border-hairline); }
.icon-btn svg { inline-size: 20px; block-size: 20px; }

.topbar__newchat {
  display: inline-flex; align-items: center; gap: var(--space-2);
  block-size: 36px;
  padding-inline: var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary); font-size: var(--text-sm); font-weight: var(--weight-medium);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.topbar__newchat:hover { background: var(--color-bg-subtle); color: var(--color-text-primary); }
.topbar__newchat svg { inline-size: 16px; block-size: 16px; }

/* Theme toggle — minimal icon button */
.theme-toggle {
  position: relative;
  display: grid; place-items: center;
  inline-size: 36px; block-size: 36px; min-inline-size: 36px;
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  background: transparent;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.theme-toggle:hover { background: var(--color-bg-subtle); color: var(--color-text-primary); }
.theme-toggle__knob { display: grid; place-items: center; }
.theme-toggle__knob svg { inline-size: 19px; block-size: 19px; }
.theme-toggle__knob .ico-sun { display: none; }
[data-theme="dark"] .theme-toggle__knob .ico-sun { display: block; }
[data-theme="dark"] .theme-toggle__knob .ico-moon { display: none; }

/* ----------------------------------------------------------------------------
   5. Chat scroll region
---------------------------------------------------------------------------- */
.chat-scroll {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain;
  /* NOTE: no scroll-behavior:smooth here — during streaming the app re-pins to the bottom every
     frame, and smooth turns each pin into a restarted animation (view forever chasing the bottom).
     The scroll-to-bottom BUTTON does its own smooth scroll in JS. */
  padding-bottom: 150px; /* room for composer */
}
.chat-inner {
  max-inline-size: var(--column-w);
  margin-inline: auto;
  padding: var(--space-8) var(--space-6) 0;
}

/* ----- Welcome / hero ---------------------------------------------------- */
.welcome {
  max-inline-size: var(--column-w);
  margin-inline: auto;
  padding: var(--space-16) var(--space-6) 0;
  display: flex; flex-direction: column;
}
.welcome__mark { margin-bottom: var(--space-5); }
.welcome__title {
  font-family: var(--font-serif);
  font-size: var(--text-3xl); line-height: var(--leading-3xl);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-tight);
  color: var(--color-text-primary);
}
[dir="rtl"] .welcome__title { line-height: 1.3; }

/* ----------------------------------------------------------------------------
   6. Messages
---------------------------------------------------------------------------- */
.turn {
  animation: msg-in var(--dur-slow) var(--ease-out);
  margin-bottom: var(--space-8);
  /* Long-thread smoothness: off-screen turns skip layout/paint entirely; the browser
     remembers each turn's real size after first render so scroll math stays accurate. */
  content-visibility: auto;
  contain-intrinsic-size: auto 320px;
}
/* The actively-streaming turn must stay fully rendered. ":last-child" was the whole rule, on
   the assumption that the live turn is always last — and in Firas Brain it is NOT: a sweep
   appends a "Sweeping the document… 5/40" notice AFTER it. The answer then lost the exemption,
   collapsed from its real 5814px to the 320px intrinsic estimate, the thread stopped
   overflowing at all, and the browser clamped scrollTop to 0 — throwing the reader back to the
   top of the prompt on every batch. `.is-live` states the intent instead of inferring it. */
.turn:last-child,
.turn.is-live { content-visibility: visible; }

/* ----- User bubble — aligned inline-end, quiet ----- */
.msg-user { display: flex; justify-content: flex-end; }
.msg-user__bubble {
  max-inline-size: 85%;
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  /* Long-press (any device) opens ONLY our Copy menu — never the OS callout and never a
     text selection. The user copies via the button, not by selecting the text. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  font-family: var(--font-sans);
  font-size: var(--text-base); line-height: var(--leading-base);
  color: var(--color-text-primary);
  white-space: pre-wrap;
  word-wrap: break-word; overflow-wrap: anywhere;
}
/* KaTeX inside a user bubble keeps its own layout; selection stays off */
.msg-user__bubble * { -webkit-user-select: none; user-select: none; }
.msg-user__bubble[dir="rtl"] { font-family: var(--font-arabic); }

/* ----- Assistant prose — no bubble, document on bg ----- */
.msg-ai { position: relative; }
.msg-ai__head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); }
.msg-ai__avatar {
  inline-size: 24px; block-size: 24px; border-radius: 26%;
  display: grid; place-items: center; flex: 0 0 auto;
  overflow: hidden;
  background: linear-gradient(145deg, var(--color-accent-hover), var(--color-accent));
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.14);
}
.msg-ai__avatar .fmark { inline-size: 64%; block-size: 64%; display: block; color: var(--color-on-accent); }
.msg-ai__avatar .fmark + .glyph { display: none; }
.msg-ai__avatar .glyph {
  font-family: var(--font-serif); font-weight: var(--weight-semibold);
  font-size: 13px; line-height: 1; color: var(--color-on-accent);
}
.msg-ai__name {
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
}

/* Per-message tier badge */
.msg-ai__badge {
  display: inline-flex; align-items: center; gap: 4px;
  margin-inline-start: var(--space-2);
  font-size: var(--text-xs); line-height: var(--leading-xs);
  letter-spacing: var(--tracking-wide); font-weight: var(--weight-semibold);
  text-transform: uppercase;
  color: var(--color-text-muted);
  direction: ltr; unicode-bidi: isolate;
}
.msg-ai__badge .dot { display: none; }
.msg-ai__badge[data-tier="ultra"] {
  color: var(--color-accent);
  border: var(--border-hairline) solid var(--color-accent);
  border-radius: var(--radius-pill);
  padding: 1px var(--space-2);
  background: var(--color-accent-soft);
}
.msg-ai__badge[data-tier="ultra"]::before {
  content: ""; inline-size: 6px; block-size: 6px;
  border-radius: var(--radius-pill); background: var(--color-accent);
  display: inline-block;
}
/* Max = the purple tier — its per-message badge gets a purple ring (was plain like Pro) */
.msg-ai__badge[data-tier="max"] {
  color: #7c3aed;
  border: var(--border-hairline) solid #7c3aed;
  border-radius: var(--radius-pill);
  padding: 1px var(--space-2);
  background: color-mix(in srgb, #7c3aed 10%, transparent);
}
.msg-ai__badge[data-tier="max"]::before {
  content: ""; inline-size: 6px; block-size: 6px;
  border-radius: var(--radius-pill); background: #7c3aed;
  display: inline-block;
}
[data-theme="dark"] .msg-ai__badge[data-tier="max"] { color: #a78bfa; border-color: #a78bfa; }
[data-theme="dark"] .msg-ai__badge[data-tier="max"]::before { background: #a78bfa; }

/* Prose body */
.msg-ai__body {
  position: relative;
  font-family: var(--font-serif);
  font-size: var(--text-lg); line-height: var(--leading-lg);
  font-weight: var(--weight-regular);
  color: var(--color-text-primary);
}
.msg-ai__body[dir="rtl"] { font-family: var(--font-arabic); line-height: 1.9; }

/* Streaming caret */
.stream-caret::after {
  content: ""; display: inline-block;
  inline-size: 2px; block-size: 1.05em;
  background: var(--color-accent);
  margin-inline-start: 2px;
  vertical-align: text-bottom;
  animation: caret-pulse 1s ease-in-out infinite;
}

/* Markdown prose */
/* WORD INTEGRITY.
   This used to be `overflow-wrap: anywhere; word-break: break-word`, which is why a word
   could split mid-letter — "مرحبا" coming out as "مرحب" on one line and "ا" on the next,
   worst of all inside table cells. Two separate reasons:

     · `anywhere` permits a break at ANY character whenever the line is tight, not only when
       the word cannot fit at all;
     · `anywhere` also shrinks the element's min-content width, so a table column is allowed
       to collapse to one character and then every word in it breaks.

   `break-word` breaks a word ONLY when that word alone cannot fit on a line of its own, and
   it leaves min-content sizing intact — so columns size to whole words. A monstrous URL
   still wraps instead of overflowing; ordinary Arabic and English never split. */
.md {
  font-family: inherit; font-size: inherit; line-height: inherit;
  color: var(--color-text-primary);
  overflow-wrap: break-word;
  word-break: normal;
  hyphens: none;                 /* never insert a hyphen mid-word */
}
/* Long unbroken tokens (URLs, hashes, paths) in inline code must wrap, not clip — code
   BLOCKS (pre) keep their own horizontal scroll. */
.md :not(pre) > code { overflow-wrap: anywhere; }
.md > *:first-child { margin-top: 0; }
.md > *:last-child { margin-bottom: 0; }
.md p { margin: 0 0 var(--space-4); }
.md h1, .md h2, .md h3 {
  font-family: var(--font-serif); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  margin: var(--space-6) 0 var(--space-3); line-height: 1.25;
}
[dir="rtl"] .md h1, [dir="rtl"] .md h2, [dir="rtl"] .md h3 { font-family: var(--font-arabic); }
.md h1 { font-size: var(--text-2xl); line-height: var(--leading-2xl); }
.md h2 { font-size: var(--text-2xl); line-height: var(--leading-2xl); }
.md h3 { font-size: var(--text-xl); line-height: var(--leading-xl); }
.md ul, .md ol { margin: 0 0 var(--space-4); padding-inline-start: var(--space-6); }
.md li { margin: var(--space-2) 0; }
.md a {
  color: var(--color-accent);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px;
}
.md a:hover { color: var(--color-accent-hover); }
.md strong { font-weight: var(--weight-semibold); }
.md blockquote {
  margin: 0 0 var(--space-4); padding-inline-start: var(--space-4);
  border-inline-start: var(--border-strong) solid var(--color-border-strong);
  color: var(--color-text-secondary);
}
.md hr { border: none; border-top: var(--border-hairline) solid var(--color-border-hairline); margin: var(--space-6) 0; }
.md table {
  inline-size: 100%; border-collapse: collapse; margin: 0 0 var(--space-4);
  font-family: var(--font-sans); font-size: var(--text-sm);
  display: block; overflow-x: auto;
}
.md th, .md td {
  border: var(--border-hairline) solid var(--color-border-hairline);
  padding: var(--space-2) var(--space-3);
  text-align: start;
  /* Restated at cell level so no later era of this stylesheet can re-introduce a mid-word
     break here — a table is the narrowest context in the app and the first place it shows. */
  overflow-wrap: break-word;
  word-break: normal;
  hyphens: none;
}
/* A cell should rather widen (the table scrolls) than chop its words to fit. */
.md table { table-layout: auto; }
.md th { background: var(--color-bg-subtle); font-weight: var(--weight-semibold); }

/* Inline code */
.md :not(pre) > code {
  font-family: var(--font-mono); font-size: var(--text-sm);
  background: var(--color-bg-subtle);
  border-radius: var(--radius-xs);
  padding: 2px 5px;
  direction: ltr; unicode-bidi: isolate;
}

/* Code blocks */
.code-block {
  margin: var(--space-4) 0;
  background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-md);
  overflow: hidden;
  direction: ltr; unicode-bidi: isolate; text-align: start;
}
.code-block__head {
  display: flex; align-items: center; justify-content: space-between;
  block-size: 36px;
  padding-inline: var(--space-3);
  border-block-end: var(--border-hairline) solid var(--color-border-hairline);
}
.code-block__lang {
  font-family: var(--font-sans);
  font-size: var(--text-xs); text-transform: uppercase; letter-spacing: var(--tracking-wide);
  color: var(--color-text-muted);
}
.code-block__copy {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-sans);
  font-size: var(--text-xs); color: var(--color-text-secondary);
  padding: 4px 8px; border-radius: var(--radius-xs);
  transition: color var(--dur-fast), background var(--dur-fast);
}
.code-block__copy:hover { color: var(--color-text-primary); background: var(--color-bg); }
.code-block__copy svg { inline-size: 14px; block-size: 14px; }
.code-block pre { margin: 0; padding: var(--space-4); overflow-x: auto; }
/* Sketch from LaTeX — rendered TikZ figures (TikZJax) */
.tikz-figure { margin: var(--space-4) 0; text-align: center; overflow-x: auto; }
.tikz-figure svg { max-width: 100%; height: auto; }
.tikz-figure .tikz-svg { display: block; margin-inline: auto; background: #fff; border: 1px solid var(--color-border-hairline, #e5e7eb); border-radius: 8px; }
.tikz-svg text.tikz-lbl { font-family: var(--font-sans); font-size: 13px; fill: #1f2937; }
.plot-figure { max-width: 480px; margin-inline: auto; }
.plot-figure { --plot-c1: var(--color-accent, #2563eb); --plot-c2: #3b82f6; --plot-c3: #ef4444; --plot-c4: #10b981; --plot-c5: #a855f7; }
.plot-figure svg { width: 100%; height: auto; font-family: var(--font-sans); }
.plot-legend { position: absolute; top: 8px; inset-inline-end: 10px; display: flex; flex-direction: column; gap: 2px;
  padding: 4px 9px; border-radius: 8px; pointer-events: none; max-inline-size: 66%;
  background: var(--color-bg-subtle, #fff); background: color-mix(in srgb, var(--color-bg-subtle, #fff) 86%, transparent);
  border: 1px solid var(--color-border-hairline, #e5e7eb); }
.plot-legend__row { display: flex; align-items: center; gap: 7px; line-height: 1.15; }
.plot-legend__sw { inline-size: 15px; block-size: 3px; border-radius: 2px; flex: 0 0 auto; }
.plot-legend__lb { color: var(--color-text-secondary, #374151); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.plot-legend__lb .katex { font-size: 0.98em; }
/* Right-click / long-press copy menu (user messages) */
.msg-menu {
  position: fixed; z-index: 1200; min-inline-size: 130px; padding: 5px;
  border-radius: 11px; background: var(--color-surface, var(--color-bg, #fff));
  border: var(--border-hairline, 1px) solid var(--color-border-hairline, #e5e7eb);
  box-shadow: 0 12px 34px rgba(0, 0, 0, 0.3); animation: msgMenuIn 0.12s var(--ease-out, ease-out);
}
.msg-menu__item {
  display: flex; align-items: center; gap: 9px; inline-size: 100%; padding: 8px 12px;
  border: 0; background: none; border-radius: 7px; cursor: pointer; font: inherit;
  font-size: var(--text-base, 14px); color: var(--color-text-primary, #111); text-align: start;
}
.msg-menu__item:hover { background: var(--color-bg-subtle, rgba(0, 0, 0, 0.05)); }
.msg-menu__item svg { inline-size: 16px; block-size: 16px; flex: 0 0 auto; }
@keyframes msgMenuIn { from { opacity: 0; transform: translateY(-5px) scale(0.96); } to { opacity: 1; transform: none; } }
/* ── Public shared-chat page (/?share=<id>) ── */
.share-page { min-height: 100vh; background: var(--color-bg); color: var(--color-text-primary); }
.share-page__head {
  position: sticky; top: 0; z-index: 5; display: flex; align-items: center; justify-content: space-between;
  padding: 12px clamp(16px, 4vw, 40px); background: var(--color-bg);
  border-bottom: var(--border-hairline) solid var(--color-border-hairline);
}
.share-page__brand { font-family: var(--font-serif, serif); font-weight: 700; font-size: 20px; }
.share-page__cta {
  padding: 9px 18px; border-radius: 999px; background: var(--color-accent); color: #fff;
  text-decoration: none; font-family: var(--font-sans); font-size: 14px; font-weight: 600;
}
.share-page__cta:hover { filter: brightness(1.06); }
.share-page__col { max-inline-size: var(--column-w, 760px); margin-inline: auto; padding: 28px clamp(14px, 4vw, 24px) 80px; }
.share-page__title { font-size: var(--text-2xl, 26px); margin: 0 0 22px; }
.share-page__user { display: flex; justify-content: flex-end; margin: 22px 0 14px; }
.share-page__user .msg-user__bubble { max-inline-size: 85%; }
.share-page__ai { margin-bottom: 10px; }
.share-page__missing { text-align: center; opacity: .7; padding: 60px 0; font-family: var(--font-sans); }

/* ── Product switcher (Firas AI / Agent / Code) ── */
.product-switch {
  display: flex; align-items: center; gap: 8px; margin: 2px 14px 10px; padding: 8px 12px;
  border-radius: 10px; cursor: pointer; font-family: var(--font-sans); font-size: 13px; font-weight: 600;
  color: var(--color-text-secondary); background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--color-border-hairline);
  transition: border-color .15s, color .15s;
}
.product-switch:hover { color: var(--color-text-primary); border-color: var(--color-border-strong, var(--color-border-hairline)); }
.product-switch__dot { inline-size: 8px; block-size: 8px; border-radius: 50%; background: var(--color-accent); flex: 0 0 auto; }
.product-switch__name { flex: 1 1 auto; text-align: start; }
.product-switch__chev { inline-size: 14px; block-size: 14px; opacity: .6; }
.product-menu {
  position: fixed; z-index: 1300; min-inline-size: 230px; padding: 6px;
  border-radius: 13px; background: var(--color-surface, var(--color-bg));
  border: var(--border-hairline) solid var(--color-border-hairline);
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.35); animation: msgMenuIn .12s var(--ease-out, ease-out);
}
.product-menu__item {
  display: grid; grid-template-columns: 1fr auto; grid-template-rows: auto auto; column-gap: 10px;
  inline-size: 100%; padding: 10px 12px; border: 0; background: none; border-radius: 9px; cursor: pointer;
  text-align: start; font-family: var(--font-sans); color: var(--color-text-primary);
}
.product-menu__item:hover { background: var(--color-bg-subtle); }
.product-menu__name { font-size: 14px; font-weight: 650; }
.product-menu__tag { grid-column: 1; font-size: 11.5px; color: var(--color-text-muted); margin-top: 2px; }
.product-menu__check { grid-row: 1 / span 2; align-self: center; color: var(--color-accent); font-weight: 700; }
.product-menu__soon { font-size: 10px; font-weight: 700; letter-spacing: .04em; color: var(--color-text-muted); border: 1px solid var(--color-border-hairline); border-radius: 999px; padding: 1px 7px; margin-inline-start: 6px; vertical-align: 2px; }
.product-menu__item.is-locked { opacity: .5; }
.product-menu__item.is-locked .product-menu__name { filter: grayscale(1); }
/* Under-development modal (Firas Code) */
.dev-modal { position: fixed; inset: 0; z-index: 1400; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.55); animation: lbFade .15s ease-out; }
.dev-modal__card { max-inline-size: 340px; margin: 20px; padding: 26px 24px 20px; border-radius: 16px; text-align: center; background: var(--color-surface, var(--color-bg)); border: var(--border-hairline) solid var(--color-border-hairline); box-shadow: 0 20px 60px rgba(0,0,0,.4); font-family: var(--font-sans); }
.dev-modal__icon { font-size: 34px; }
.dev-modal__title { margin: 10px 0 6px; font-size: 18px; color: var(--color-text-primary); }
.dev-modal__body { margin: 0 0 18px; font-size: 13.5px; line-height: 1.7; color: var(--color-text-secondary); }
.dev-modal__ok { padding: 9px 26px; border: 0; border-radius: 999px; cursor: pointer; background: var(--color-accent); color: #fff; font: 600 13.5px var(--font-sans); }
/* ── Firas Agent environment: same palette, elevated feel ── */
.product-agent .nib, .product-agent .product-switch__dot { background: linear-gradient(135deg, var(--color-accent) 20%, #5b8def 115%); }
.product-agent .chat-scroll { background-image: radial-gradient(640px 220px at 50% -60px, color-mix(in srgb, var(--color-accent) 9%, transparent), transparent); }
.product-agent .composer { border-color: color-mix(in srgb, var(--color-accent) 45%, var(--color-border-hairline)); box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-accent) 18%, transparent), var(--shadow-sm, 0 1px 3px rgba(0,0,0,.2)); }
.product-agent .product-switch { border-color: color-mix(in srgb, var(--color-accent) 40%, var(--color-border-hairline)); color: var(--color-text-primary); }
/* Agent runs on ONE permanent engine — no tier choice, anywhere */
.product-agent .tier-switch { display: none !important; }
/* The Agent thinks, plans and web-searches on its OWN — hide the thinking/web-search "+" menu and the
   Auto/Plan mode switch in the composer. Only attach-files, attach-images and send remain. */
.product-agent #modeSwitch,
.product-agent #toolsMenu { display: none !important; }
.msg-ai__badge--agent { background: linear-gradient(100deg, color-mix(in srgb, var(--color-accent) 24%, transparent), color-mix(in srgb, #5b8def 24%, transparent)); border: 1px solid color-mix(in srgb, var(--color-accent) 45%, transparent); color: var(--color-text-primary); }
.msg-ai__badge--agent .dot { background: linear-gradient(135deg, var(--color-accent), #5b8def); }
.msg-ai__avatar--agent { background: linear-gradient(135deg, var(--color-accent) 20%, #5b8def 115%); }
/* Solid, not gradient-clipped. Emphasis comes from weight and size; gradient text is
   decoration standing in for hierarchy, and the #5b8def it faded into was a foreign blue
   that belonged to no part of this palette. */
.agent-welcome__grad { color: var(--color-accent); font-weight: var(--weight-bold); }
.agent-welcome__sub { max-inline-size: 460px; margin: 10px auto 22px; font: 400 14.5px/1.8 var(--font-sans); color: var(--color-text-secondary); text-align: center; }
.agent-welcome__starters { display: flex; flex-wrap: wrap; gap: 9px; justify-content: center; max-inline-size: 560px; margin-inline: auto; }
.agent-starter {
  padding: 9px 14px; border-radius: 999px; cursor: pointer; font: 500 12.5px var(--font-sans);
  color: var(--color-text-secondary); background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--color-border-hairline);
  transition: border-color .15s, color .15s, transform .15s;
}
.agent-starter:hover { color: var(--color-text-primary); border-color: color-mix(in srgb, var(--color-accent) 55%, var(--color-border-hairline)); transform: translateY(-1px); }
/* ── Firas Agent run card (live plan + steps + result) ── */
.agent-card {
  border: 1px solid color-mix(in srgb, var(--color-accent) 32%, var(--color-border-hairline));
  border-radius: 14px; padding: 14px 16px 12px; background: var(--color-surface, var(--color-bg-subtle));
  font-family: var(--font-sans);
}
.agent-card__head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.agent-card__bolt { font-size: 15px; }
.agent-card__brand { font-weight: 700; font-size: 13.5px; letter-spacing: .02em; }
.agent-card__phase { margin-inline-start: auto; font-size: 11.5px; font-weight: 600; color: var(--color-text-muted); padding: 3px 10px; border-radius: 999px; border: 1px solid var(--color-border-hairline); }
.agent-card__phase.is-live { color: var(--color-accent); border-color: color-mix(in srgb, var(--color-accent) 50%, transparent); animation: agentPulse 1.6s ease-in-out infinite; }
@keyframes agentPulse { 50% { opacity: .55; } }
.agent-card__title { font-size: 15px; font-weight: 650; margin: 2px 0 10px; color: var(--color-text-primary); }
.agent-card__steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.agent-step { display: flex; align-items: flex-start; gap: 9px; padding: 5px 2px; font-size: 13.5px; color: var(--color-text-secondary); }
.agent-step__ic {
  flex: 0 0 auto; inline-size: 19px; block-size: 19px; margin-top: 1px; border-radius: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; border: 1.4px solid var(--color-border-strong, var(--color-border-hairline));
  color: transparent;
}
.agent-step.is-done > .agent-step__ic, .agent-step.is-done summary .agent-step__ic { background: var(--color-accent); border-color: var(--color-accent); color: #fff; }
.agent-step.is-fail > .agent-step__ic { border-color: #dc2626; color: #dc2626; }
.agent-step.is-run > .agent-step__ic { border-color: var(--color-accent); }
.agent-step.is-done { color: var(--color-text-primary); }
.agent-step.is-run { color: var(--color-accent); font-weight: 600; }
.agent-step__spin { inline-size: 9px; block-size: 9px; border-radius: 50%; border: 2px solid var(--color-accent); border-top-color: transparent; animation: tikz-spin 0.9s linear infinite; }
.agent-step__det { flex: 1 1 auto; min-inline-size: 0; }
.agent-step__det > summary { display: flex; align-items: flex-start; gap: 9px; cursor: pointer; list-style: none; }
.agent-step__det > summary::-webkit-details-marker { display: none; }
.agent-step__det[open] > summary .agent-step__t { color: var(--color-accent); }
.agent-step__out { margin: 8px 0 6px 28px; padding: 10px 14px; border-inline-start: 2.5px solid color-mix(in srgb, var(--color-accent) 45%, transparent); background: var(--color-bg-subtle); border-radius: 8px; font-size: 13.5px; }
[dir="rtl"] .agent-step__out { margin: 8px 28px 6px 0; }
.agent-card__final { margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--color-border-hairline); }
.agent-card__final-label { font-size: 12.5px; font-weight: 700; color: var(--color-text-muted); margin-bottom: 6px; }
.agent-card__prog { display: flex; align-items: center; gap: 10px; margin: 2px 0 12px; }
.agent-card__prog-bar { flex: 1 1 auto; block-size: 6px; border-radius: 999px; background: var(--color-bg-subtle); overflow: hidden; }
.agent-card__prog-fill { block-size: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--color-accent), #5b8def); transition: width .5s var(--ease-out, ease-out); }
.agent-card__prog-n { font-size: 11.5px; font-weight: 700; color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
.agent-step__kind { margin-inline-end: 6px; font-size: 12.5px; }
.agent-card__report { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--color-border-hairline); }
.agent-card__report-label { font-size: 11.5px; font-weight: 700; color: var(--color-text-muted); margin-inline-end: 4px; }
.agent-report-chip { font-size: 11.5px; font-weight: 600; padding: 4px 10px; border-radius: 999px; background: var(--color-bg-subtle); border: 1px solid var(--color-border-hairline); color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
.agent-live-preview { margin-top: 12px; border: 1px solid color-mix(in srgb, var(--color-accent) 32%, var(--color-border-hairline)); border-radius: 12px; overflow: hidden; background: #fff; }
.agent-live-preview__bar { display: flex; align-items: center; gap: 8px; padding: 8px 13px; font: 600 11.5px var(--font-sans); color: var(--color-text-secondary); background: var(--color-bg-subtle); border-bottom: 1px solid var(--color-border-hairline); }
.agent-live-preview__dot { inline-size: 8px; block-size: 8px; border-radius: 50%; background: var(--color-accent); animation: maxDotPulse 1.4s ease-in-out infinite; }
.agent-live-preview iframe { display: block; width: 100%; height: 420px; border: 0; background: #fff; }
.agent-card__resume { margin-top: 12px; padding: 9px 18px; border: 0; border-radius: 999px; cursor: pointer; background: linear-gradient(100deg, var(--color-accent), #5b8def); color: #fff; font: 600 13px var(--font-sans); }
.agent-card__resume:hover { filter: brightness(1.07); }
.agent-card__resume:disabled { opacity: .5; cursor: default; }
/* "Fix what's left" sits on a DONE mission, next to a delivered result — an amber repair
   affordance, not the blue "carry on" one, so the two never read as the same action. It can
   appear alongside nothing else, so it also carries its own top margin. */
.agent-card__fixrest { background: linear-gradient(100deg, #c98a2b, #b3622a); margin-inline-start: 8px; }
.agent-step__file { font-size: 11px; padding: 1px 7px; border-radius: 6px; background: var(--color-bg-subtle); border: 1px solid var(--color-border-hairline); color: var(--color-text-muted); margin-inline-start: 6px; }
/* ── Project (folder) card: file viewer + ZIP download ── */
.proj-card { border: 1px solid color-mix(in srgb, var(--color-accent) 32%, var(--color-border-hairline)); border-radius: 14px; background: var(--color-surface, var(--color-bg-subtle)); font-family: var(--font-sans); overflow: hidden; }
.proj-card__head { display: flex; align-items: center; gap: 11px; padding: 13px 15px; border-bottom: 1px solid var(--color-border-hairline); flex-wrap: wrap; }
.proj-card__ic { font-size: 22px; }
.proj-card__meta { flex: 1 1 auto; min-inline-size: 0; }
.proj-card__name { font-weight: 700; font-size: 14.5px; color: var(--color-text-primary); overflow-wrap: anywhere; }
.proj-card__sub { font-size: 11.5px; color: var(--color-text-muted); margin-top: 1px; }
.proj-card__zip { padding: 8px 15px; border: 0; border-radius: 999px; cursor: pointer; background: var(--color-accent); color: #fff; font: 600 12.5px var(--font-sans); white-space: nowrap; }
.proj-card__zip:hover { filter: brightness(1.07); }
.proj-card__preview { background: transparent; color: var(--color-accent); border: 1px solid color-mix(in srgb, var(--color-accent) 55%, transparent); }
.proj-card__preview:hover { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
.proj-card__files { display: flex; flex-direction: column; }
.proj-file + .proj-file { border-top: 1px solid var(--color-border-hairline); }
.proj-file__row { display: flex; align-items: center; gap: 9px; inline-size: 100%; padding: 10px 15px; border: 0; background: none; cursor: pointer; font: 500 13px var(--font-mono, monospace); color: var(--color-text-secondary); text-align: start; }
.proj-file__row:hover { background: var(--color-bg-subtle); }
.proj-file__path { flex: 1 1 auto; direction: ltr; text-align: start; overflow-wrap: anywhere; }
.proj-file__size { font-size: 11px; color: var(--color-text-muted); font-family: var(--font-sans); }
.proj-file__chev { font-size: 10px; opacity: .55; transition: transform .15s; }
.proj-file.is-open .proj-file__chev { transform: rotate(180deg); }
.proj-file__body { max-block-size: 420px; overflow: auto; border-top: 1px dashed var(--color-border-hairline); }
.proj-file__body pre { margin: 0; padding: 12px 15px; font-size: 12px; line-height: 1.6; direction: ltr; text-align: left; }
@media (max-width: 560px) { .proj-card__zip { inline-size: 100%; } }
@media (max-width: 560px) {
  .agent-card { padding: 12px 12px 10px; }
  .agent-step__out { margin-inline-start: 12px; }
  [dir="rtl"] .agent-step__out { margin: 8px 12px 6px 0; }
}
.plot-figure .plot-bg { fill: var(--color-bg-subtle, #fff); stroke: var(--color-border-hairline, #e5e7eb); stroke-width: 1; }
/* Grid = graph-paper lines: keyed to text-muted (NOT the hairline border) so they stay clearly
   visible in the LIGHT theme too (hairline #E6E4DA at .5 was invisible on the white paper). */
.plot-figure .plot-grid { stroke: var(--color-text-muted, #6b7280); opacity: .34; stroke-width: 1; }
.plot-figure .plot-axis { stroke: var(--color-text-secondary, #4b5563); stroke-width: 1.4; }
.plot-figure .plot-arrow { fill: var(--color-text-secondary, #4b5563); }
.plot-figure .plot-tick { fill: var(--color-text-muted, #6b7280); font-size: 9px; }
.plot-figure .plot-axislabel { fill: var(--color-text-secondary, #4b5563); font-size: 10.5px; font-style: italic; }
.plot-figure .plot-curve { fill: none; stroke-width: 2.2; stroke-linejoin: round; stroke-linecap: round; }
.plot-figure .plot-legend-bg { fill: var(--color-bg, #fff); opacity: .85; stroke: var(--color-border-hairline, #e5e7eb); stroke-width: .5; }
/* Interactive plot: drag to pan, wheel/pinch to zoom, ⟲ to reset */
.plot-figure { position: relative; }
.plot-interactive svg { cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none; }
.plot-interactive.plot-grabbing svg { cursor: grabbing; }
.plot-reset {
  position: absolute; top: 8px; inset-inline-start: 8px; inline-size: 26px; block-size: 26px;
  display: flex; align-items: center; justify-content: center; border-radius: 6px; cursor: pointer;
  border: 1px solid var(--color-border-hairline, #e5e7eb); background: var(--color-bg, #fff);
  color: var(--color-text-secondary, #4b5563); font-size: 15px; line-height: 1; padding: 0;
  opacity: 0; transition: opacity var(--dur-fast, .15s);
}
.plot-figure:hover .plot-reset, .plot-interactive.plot-grabbing .plot-reset { opacity: .92; }
.plot-reset:hover { color: var(--color-text-primary, #111); border-color: var(--color-text-muted, #9ca3af); }
.tikz-figure__spin { display: inline-block; padding: var(--space-3) var(--space-4); opacity: .55; font-size: 1.4em; animation: tikz-spin 1.1s linear infinite; }
@keyframes tikz-spin { to { transform: rotate(360deg); } }
.code-block code {
  font-family: var(--font-mono); font-size: var(--text-sm); line-height: 20px;
  color: var(--color-text-primary); background: none; padding: 0;
}

/* ----- Thinking disclosure ----------------------------------------------- */
.thinking { margin-bottom: var(--space-3); }
.thinking__head {
  display: inline-flex; align-items: center; gap: var(--space-2);
  block-size: 28px;
  padding-inline: var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  font-family: var(--font-serif); font-size: var(--text-sm); font-weight: var(--weight-medium);
  transition: background var(--dur-fast);
}
[dir="rtl"] .thinking__head { font-family: var(--font-arabic); }
.thinking__head:hover { background: var(--color-bg-subtle); }
.thinking__head .nib { display: none; } /* quiet caret-only disclosure */
.thinking__chevron {
  order: -1;
  inline-size: 12px; block-size: 12px; color: var(--color-text-muted);
  transition: transform var(--dur-fast) var(--ease-out);
}
.thinking.is-open .thinking__chevron { transform: rotate(90deg); }
[dir="rtl"] .thinking__chevron { transform: rotate(180deg); }
[dir="rtl"] .thinking.is-open .thinking__chevron { transform: rotate(90deg); }
.thinking__body {
  overflow: hidden; max-block-size: 0; opacity: 0;
  transition: max-block-size var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out), margin var(--dur-base) var(--ease-out);
}
.thinking.is-open .thinking__body { max-block-size: 600px; opacity: 1; margin-block-start: var(--space-2); }
.thinking__inner {
  font-family: var(--font-mono); font-size: var(--text-sm); line-height: 20px;
  color: var(--color-text-secondary);
  background: var(--color-bg-subtle);
  border-inline-start: var(--border-strong) solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  white-space: pre-wrap; word-break: break-word;
  max-block-size: 320px; overflow-y: auto;
  direction: ltr; unicode-bidi: plaintext;
}

/* ----- Message actions --------------------------------------------------- */
.msg-actions {
  display: flex; flex-wrap: wrap; gap: 2px; margin-block-start: var(--space-3);
  opacity: 1;                       /* always visible (copy / regenerate never disappear) */
  transition: opacity var(--dur-fast);
}
.msg-action {
  display: inline-flex; align-items: center; gap: 5px;
  block-size: 32px;
  padding-inline: var(--space-2);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-size: var(--text-sm); color: var(--color-text-muted);
  transition: background var(--dur-fast), color var(--dur-fast);
}
.msg-action:hover { background: var(--color-bg-subtle); color: var(--color-text-secondary); }
.msg-action svg { inline-size: 15px; block-size: 15px; }
.msg-action.is-ultra { color: var(--color-accent); }
.msg-action.is-ultra svg { color: var(--color-accent); }
.msg-action.is-ultra:hover { color: var(--color-accent-hover); background: var(--color-accent-soft); }
.msg-action .ok { color: var(--color-success); display: inline-flex; }
.msg-action .ok svg { color: var(--color-success); }

/* ----- Error notice ------------------------------------------------------ */
.msg-error {
  margin-block-start: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: var(--border-hairline) solid var(--color-border-hairline);
  background: var(--color-bg-subtle);
  border-radius: var(--radius-md);
  color: var(--color-error); font-family: var(--font-sans); font-size: var(--text-sm);
  display: flex; align-items: center; gap: var(--space-2);
}
.msg-error svg { inline-size: 16px; block-size: 16px; flex: 0 0 auto; }
.msg-error button { color: var(--color-accent); text-decoration: underline; font-size: inherit; margin-inline-start: auto; white-space: nowrap; }
.msg-error button:hover { color: var(--color-accent-hover); }

/* ----- Plan-mode "Start" quick-action --------------------------------------
   A premium teal pill shown under a plan-mode reply: one tap approves the plan
   and executes it through the normal send path. */
.plan-start {
  margin-block-start: var(--space-4);
  display: flex; justify-content: flex-start;
}
.plan-start__btn {
  display: inline-flex; align-items: center; gap: var(--space-2);
  block-size: 38px;
  padding-inline: var(--space-5);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  box-shadow: var(--shadow-xs);
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .plan-start__btn { font-family: var(--font-arabic); }
.plan-start__ico { display: inline-flex; }
.plan-start__ico svg { inline-size: 16px; block-size: 16px; }
.plan-start__btn:hover { background: var(--color-accent-hover); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.plan-start__btn:active { transform: translateY(0); }
.plan-start__btn:disabled { opacity: .6; pointer-events: none; }

/* ----------------------------------------------------------------------------
   Plan-mode interactive CHOICE LISTS (the firas-ask panel)
   Selectable radio/checkbox cards with a soft "Recommended" badge + Continue.
   Teal-accented, RTL-aware (logical props), light + dark, keyboard-accessible.
---------------------------------------------------------------------------- */
/* Plan-mode: calm placeholder shown while the raw firas-ask JSON streams. The
   user never sees the JSON — only this pill, then the cards on finalize. */
.firas-ask-loading {
  display: inline-flex; align-items: center; gap: var(--space-2);
  margin-block: var(--space-4);
  padding: var(--space-2) var(--space-3);
  border: var(--border-hairline) solid var(--color-accent-ring);
  border-radius: var(--radius-pill);
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  line-height: var(--leading-sm);
}
.firas-ask-loading__dots {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
}
.firas-ask-loading__dots > span {
  inline-size: 6px; block-size: 6px; border-radius: var(--radius-pill);
  background: currentColor;
  animation: firas-ask-dot 1.2s ease-in-out infinite;
}
.firas-ask-loading__dots > span:nth-child(2) { animation-delay: 0.18s; }
.firas-ask-loading__dots > span:nth-child(3) { animation-delay: 0.36s; }
.firas-ask-loading__text { white-space: nowrap; }
@keyframes firas-ask-dot {
  0%, 80%, 100% { opacity: 0.3; transform: translateY(0); }
  40% { opacity: 1; transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .firas-ask-loading__dots > span { animation: firas-ask-fade 1.4s ease-in-out infinite; }
  @keyframes firas-ask-fade { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
}

.firas-ask {
  margin-block: var(--space-4);
  padding: var(--space-4);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-xs);
}
.firas-ask__intro {
  margin: 0 0 var(--space-3);
  color: var(--color-text-secondary);
  font-size: var(--text-sm); line-height: var(--leading-sm);
}
.firas-ask .ask-q { border: none; margin: 0 0 var(--space-4); padding: 0; min-inline-size: 0; }
.firas-ask .ask-q:last-of-type { margin-block-end: var(--space-3); }
.ask-q__title {
  padding: 0; margin: 0 0 var(--space-2);
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
}
.ask-q__opts { display: flex; flex-direction: column; gap: var(--space-2); }

.choice-card {
  position: relative;
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3);
  border: var(--border-strong) solid var(--color-border-hairline);
  border-radius: var(--radius-md);
  background: var(--color-bg);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.choice-card:hover { border-color: var(--color-border-strong); background: var(--color-bg-subtle); }

/* The native input is visually hidden but stays focusable + accessible. */
.choice-card__input {
  position: absolute; inline-size: 1px; block-size: 1px;
  margin: 0; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
/* The drawn control (circle for radio, rounded square for checkbox). */
.choice-card__mark {
  flex: 0 0 auto; margin-block-start: 1px;
  inline-size: 18px; block-size: 18px;
  border: var(--border-strong) solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
  position: relative;
}
.choice-card__input[type="checkbox"] ~ .choice-card__mark { border-radius: var(--radius-xs); }
.choice-card__mark::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  opacity: 0; transition: opacity var(--dur-fast) var(--ease-out);
}
.choice-card__input[type="radio"] ~ .choice-card__mark::after {
  inline-size: 8px; block-size: 8px; border-radius: var(--radius-pill);
  background: var(--color-on-accent);
}
.choice-card__input[type="checkbox"] ~ .choice-card__mark::after {
  inline-size: 9px; block-size: 5px;
  /* physical borders (not logical) so the tick looks identical in LTR + RTL */
  border-left: 2px solid var(--color-on-accent);
  border-bottom: 2px solid var(--color-on-accent);
  transform: translateY(-1px) rotate(-45deg);
}
.choice-card__input:checked ~ .choice-card__mark { background: var(--color-accent); border-color: var(--color-accent); }
.choice-card__input:checked ~ .choice-card__mark::after { opacity: 1; }

/* Selected card highlight. */
.choice-card:has(.choice-card__input:checked) {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
/* Keyboard focus ring on the card. */
.choice-card:has(.choice-card__input:focus-visible) { box-shadow: var(--shadow-focus); }

.choice-card__main { display: flex; flex-direction: column; gap: 2px; min-inline-size: 0; }
.choice-card__label-row { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.choice-card__label {
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  color: var(--color-text-primary);
}
.choice-card__desc {
  font-size: var(--text-xs); line-height: var(--leading-xs);
  color: var(--color-text-secondary);
}
.choice-card__badge {
  display: inline-flex; align-items: center;
  padding: 2px 8px; border-radius: var(--radius-pill);
  background: var(--color-accent-soft); color: var(--color-accent);
  font-size: 11px; font-weight: var(--weight-semibold);
  white-space: nowrap;
}

.firas-ask__foot {
  display: flex; align-items: center; gap: var(--space-2);
  margin-block-start: var(--space-3); flex-wrap: wrap;
}
.firas-ask__extra {
  flex: 1 1 180px; min-inline-size: 0;
  block-size: 38px; padding-inline: var(--space-3);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text-primary);
  font-family: inherit; font-size: var(--text-sm);
}
.firas-ask__extra:focus-visible { outline: none; border-color: var(--color-accent); box-shadow: var(--shadow-focus); }
.firas-ask__continue {
  display: inline-flex; align-items: center; gap: var(--space-2);
  block-size: 38px; padding-inline: var(--space-5);
  border-radius: var(--radius-pill);
  background: var(--color-accent); color: var(--color-on-accent);
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  white-space: nowrap; box-shadow: var(--shadow-xs);
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .firas-ask__continue { font-family: var(--font-arabic); }
.firas-ask__continue svg { inline-size: 16px; block-size: 16px; }
[dir="rtl"] .firas-ask__continue svg { transform: scaleX(-1); }
.firas-ask__continue:hover { background: var(--color-accent-hover); transform: translateY(-1px); }
.firas-ask__continue:active { transform: translateY(0); }
.firas-ask__continue:disabled { opacity: .85; pointer-events: none; transform: none; }
.firas-ask__ok svg { inline-size: 15px; block-size: 15px; }
[dir="rtl"] .firas-ask__ok svg { transform: none; }

/* Wizard navigation (one question at a time): Back + Next/Confirm. */
.firas-ask__nav { display: inline-flex; align-items: center; gap: var(--space-2); margin-inline-start: auto; }
.firas-ask__back {
  display: inline-flex; align-items: center; gap: 6px;
  block-size: 38px; padding-inline: var(--space-4);
  border-radius: var(--radius-pill);
  border: var(--border-hairline) solid var(--color-border-strong);
  background: transparent; color: var(--color-text-secondary);
  font-family: var(--font-sans); font-size: var(--text-sm); font-weight: var(--weight-medium);
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .firas-ask__back { font-family: var(--font-arabic); }
.firas-ask__back:hover { background: var(--color-bg-subtle); color: var(--color-text-primary); }
.firas-ask__back[hidden] { display: none; }
.firas-ask__back svg { inline-size: 16px; block-size: 16px; transform: scaleX(-1); }
[dir="rtl"] .firas-ask__back svg { transform: none; }

/* Per-question step counter above the question text. */
.ask-q__step {
  display: block; margin-block-end: 6px;
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide); color: var(--color-accent);
}
.ask-q__q { display: block; }

/* Answered state — locked, de-emphasized. */
.firas-ask.is-answered { opacity: .72; }
.firas-ask.is-answered .choice-card { cursor: default; }
.firas-ask.is-answered .choice-card:hover { border-color: var(--color-border-hairline); background: var(--color-bg); }
.firas-ask.is-answered .choice-card:has(.choice-card__input:checked) {
  border-color: var(--color-accent); background: var(--color-accent-soft);
}

/* ----------------------------------------------------------------------------
   7. Composer
---------------------------------------------------------------------------- */
.composer-wrap {
  position: absolute; inset-inline: 0; inset-block-end: 0;
  display: flex; flex-direction: column; align-items: center;
  padding: var(--space-4) var(--space-6) max(var(--space-6), env(safe-area-inset-bottom));
  pointer-events: none;
  background: linear-gradient(transparent, var(--color-bg) 46%);
}
.composer {
  pointer-events: auto;
  inline-size: 100%; max-inline-size: var(--column-w);
  display: flex; flex-direction: column; align-items: stretch;
  padding: var(--space-3) var(--space-3) var(--space-2);
  border-radius: var(--radius-xl);
  border: var(--border-hairline) solid var(--color-border-hairline);
  background: var(--color-surface);
  box-shadow: var(--shadow-composer);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.composer.is-focused {
  border-color: var(--color-border-strong);
  box-shadow: var(--shadow-composer), var(--shadow-focus);
}
.composer__textarea {
  flex: 1; resize: none; border: none; background: transparent;
  font-family: var(--font-sans);
  font-size: var(--text-md); line-height: var(--leading-md);
  color: var(--color-text-primary);
  padding: 6px var(--space-2);
  max-block-size: 200px;
  overflow-y: auto;
}
.composer__textarea:focus { outline: none; }
.composer__textarea::placeholder { color: var(--color-text-muted); }
.composer__textarea[dir="rtl"] { font-family: var(--font-arabic); }

.composer__attach { flex: 0 0 auto; inline-size: 36px; block-size: 36px; min-inline-size: 36px; }
.composer__attach svg { inline-size: 20px; block-size: 20px; }

/* (Response-mode selector is now a compact dropdown — see section 8c .mode-select) */

/* Thinking toggle — composer toolbar pill (device pref) */
.think-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  flex: 0 0 auto;
  block-size: 32px;
  padding-inline: var(--space-3);
  border-radius: var(--radius-pill);
  border: var(--border-hairline) solid var(--color-border-hairline);
  background: transparent;
  color: var(--color-text-muted);
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .think-toggle { font-family: var(--font-arabic); }
.think-toggle__ico { inline-size: 16px; block-size: 16px; flex: 0 0 auto; }
.think-toggle:hover { color: var(--color-text-secondary); border-color: var(--color-border-strong); }
.think-toggle.is-on {
  color: var(--color-accent);
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
.think-toggle.is-on:hover { color: var(--color-accent-hover); }

.composer__send {
  flex: 0 0 auto;
  inline-size: 32px; block-size: 32px; min-inline-size: 32px;
  border-radius: var(--radius-sm);
  display: grid; place-items: center;
  background: var(--color-accent);
  color: var(--color-on-accent);
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.composer__send svg { inline-size: 18px; block-size: 18px; }
.composer__send:hover:not(:disabled) { background: var(--color-accent-hover); }
.composer__send:active:not(:disabled) { transform: scale(0.96); }
.composer__send:disabled { background: var(--color-border-strong); color: var(--color-text-muted); cursor: not-allowed; }
.composer__send.is-stop { background: var(--color-accent); }
.composer__send.is-stop .ico-send { display: none; }
.composer__send .ico-stop { display: none; }
.composer__send.is-stop .ico-stop { display: block; }

.send-pulse { animation: send-pulse var(--dur-slow) var(--ease-out); }
@keyframes send-pulse {
  0% { transform: scale(0.92); }
  100% { transform: scale(1); }
}

.composer__hint {
  pointer-events: none;
  margin-block-start: var(--space-2);
  text-align: center;
  font-size: var(--text-xs); color: var(--color-text-muted);
}

/* Scroll-to-bottom */
.scroll-bottom {
  position: absolute; inset-block-end: calc(100% + 10px); inset-inline-start: 50%;
  transform: translateX(-50%) translateY(6px);
  inline-size: 36px; block-size: 36px; border-radius: var(--radius-pill);
  display: grid; place-items: center;
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  box-shadow: var(--shadow-md);
  color: var(--color-text-secondary);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-base), transform var(--dur-base) var(--ease-out), border-color var(--dur-base);
  z-index: var(--z-scrollbtn);
}
.scroll-bottom:hover { background: var(--color-bg-subtle); border-color: var(--color-border-strong); }
.scroll-bottom.is-visible { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }
.scroll-bottom svg { inline-size: 18px; block-size: 18px; }

/* ----------------------------------------------------------------------------
   8. Mobile drawer + scrim
---------------------------------------------------------------------------- */
.scrim {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  background: rgba(0,0,0,0.52);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-base);
}
.scrim.is-open { opacity: 1; pointer-events: auto; }

/* ----------------------------------------------------------------------------
   8b. Landing / hero (logged-out) — polished, animated, fully responsive
---------------------------------------------------------------------------- */
.landing {
  position: fixed; inset: 0; z-index: var(--z-toast);
  display: flex; flex-direction: column; align-items: center;
  justify-content: safe center; gap: var(--space-8);
  min-block-size: 100dvh;
  padding: max(var(--space-7), env(safe-area-inset-top)) var(--space-6)
           max(var(--space-8), env(safe-area-inset-bottom));
  background: var(--color-bg);
  overflow: hidden auto;
}
.landing[hidden] { display: none; }
/* ---- The instrument behind the hero ----
   Replaces three 60px-blurred accent orbs. A blurred coloured blob is atmosphere
   standing in for an idea; these are the astrolabe's actual parts, drawn as real
   graduations. All three are pure CSS — no image, no canvas, no layout cost — and
   they sit behind content at z-index 0. */
.landing__bg { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.astro {
  position: absolute; inset-block-start: 50%; inset-inline-start: 50%;
  border-radius: 50%; aspect-ratio: 1;
  translate: -50% -50%;
  transform-origin: 50% 50%;
}

/* LIMB — the graduated outer ring. A tick every 3°, with a longer tick every 15°,
   drawn by two repeating conic gradients and masked to a narrow band. This is a real
   scale: 120 minor divisions, exactly as the degree ring is cut. */
.astro--limb {
  inline-size: min(132vmin, 1180px);
  background:
    repeating-conic-gradient(from 0deg, var(--color-line) 0deg 0.14deg, transparent 0.14deg 3deg);
  -webkit-mask: radial-gradient(circle, transparent calc(50% - 13px), #000 calc(50% - 13px) calc(50% - 1px), transparent calc(50% - 1px));
          mask: radial-gradient(circle, transparent calc(50% - 13px), #000 calc(50% - 13px) calc(50% - 1px), transparent calc(50% - 1px));
  opacity: 0.34;
}
.astro--limb::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: repeating-conic-gradient(from 0deg, var(--color-line) 0deg 0.3deg, transparent 0.3deg 15deg);
  -webkit-mask: radial-gradient(circle, transparent calc(50% - 26px), #000 calc(50% - 26px) calc(50% - 1px), transparent calc(50% - 1px));
          mask: radial-gradient(circle, transparent calc(50% - 26px), #000 calc(50% - 26px) calc(50% - 1px), transparent calc(50% - 1px));
}

/* PLATE — the fixed disc, engraved with the almucantar arcs you read altitude against.
   Concentric hairlines, tightening toward the rim exactly as the projection does. */
.astro--plate {
  inline-size: min(96vmin, 860px);
  background:
    repeating-radial-gradient(circle, transparent 0 38px, var(--color-line) 38px 38.7px);
  /* Clear the middle. The hero copy sits there, and an engraved arc running behind a
     line of text costs more legibility than the arc adds. */
  -webkit-mask: radial-gradient(circle, transparent 30%, #000 44%, #000 62%, transparent 78%);
          mask: radial-gradient(circle, transparent 30%, #000 44%, #000 62%, transparent 78%);
  opacity: 0.13;
}

/* RETE — the pierced star-map that turns over the plate. The page's ONE authored
   motion: four minutes per revolution, slow enough to read as an instrument settling
   rather than an animation playing. */
.astro--rete {
  inline-size: min(108vmin, 960px);
  background:
    repeating-conic-gradient(from 0deg, var(--color-line) 0deg 0.5deg, transparent 0.5deg 60deg),
    repeating-conic-gradient(from 30deg, var(--color-success) 0deg 0.35deg, transparent 0.35deg 120deg);
  /* The star-pointers stop well clear of the reading column — they belong to the rim. */
  -webkit-mask: radial-gradient(circle, transparent 33%, #000 46%, #000 49%, transparent 50%);
          mask: radial-gradient(circle, transparent 33%, #000 46%, #000 49%, transparent 50%);
  opacity: 0.26;
  animation: astro-turn 240s linear infinite;
}
@keyframes astro-turn { to { rotate: 360deg; } }
.landing__inner {
  position: relative; z-index: 1;
  inline-size: 100%; max-inline-size: 540px;
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--space-5);
}
/* The mark sits ON the plate — no drift, no float. A bobbing logo is a mood; an
   instrument's centre pin is fixed, and everything else moves around it. The old
   rgba(35,122,104) drop shadow was hard-coded teal the token layer could never reach. */
.landing__mark {
  animation: landing-rise var(--dur-slow) var(--ease-out) both;
  box-shadow: 0 2px 10px rgba(0,0,0,0.45);
}
.landing__wordmark { margin: 0; animation: landing-rise var(--dur-slow) var(--ease-out) 0.06s both; }
.landing__wordmark .wordmark { font-size: clamp(34px, 9vw, 52px); }
.landing__about {
  margin: 0; max-inline-size: 460px;
  color: var(--color-text-secondary);
  font-size: var(--text-md); line-height: var(--leading-md);
  animation: landing-rise var(--dur-slow) var(--ease-out) 0.12s both;
}
[dir="rtl"] .landing__about { line-height: 1.9; }
.landing__cta {
  margin-block-start: var(--space-2);
  min-block-size: 52px;
  padding-inline: var(--space-8);
  /* A struck brass key, not a pill. Cut corners and a hairline bright edge along the
     top read as machined metal catching light; the old 22px teal glow read as a button
     hovering above a page. */
  border-radius: var(--radius-md);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: var(--font-sans);
  font-size: var(--text-md); font-weight: var(--weight-semibold);
  letter-spacing: 0.01em;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.30), 0 2px 6px rgba(0,0,0,0.40);
  /* NO entrance animation here: the .landing__actions wrapper that now holds this
     button (+ the "Sign in" secondary) owns the row's entrance. Running
     `landing-rise` on both meant the button faded in INSIDE a fading-in parent —
     doubled opacity, and a child left stranded at keyframe 0 whenever the
     parent's animation was cancelled or replaced by the JS reveal. */
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .landing__cta { font-family: var(--font-arabic); }
.landing__cta:hover { background: var(--color-accent-hover); box-shadow: inset 0 1px 0 rgba(255,255,255,0.38), 0 4px 12px rgba(0,0,0,0.45); }
/* The lift is real-hover only: on touch, :hover sticks after a tap and the button
   would sit shifted -1px until focus moves. Colour/shadow still respond everywhere. */
@media (hover: hover) and (pointer: fine) {
  .landing__cta:hover { transform: translateY(-1px); }
}
.landing__cta:active { transform: translateY(0); }
.landing__cta:focus-visible { outline: 2px solid var(--color-accent-ring); outline-offset: 3px; }
.landing__cta.is-busy { opacity: .7; pointer-events: none; }
.landing__cta.is-busy::after {
  content: ""; inline-size: 15px; block-size: 15px; margin-inline-start: var(--space-2);
  border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;
  display: inline-block; vertical-align: -2px; animation: landing-spin .7s linear infinite;
}
@keyframes landing-spin { to { transform: rotate(360deg); } }

/* Primary "enter as guest" CTA + secondary "sign in" — stacked on phones,
   side by side once there is room. */
.landing__actions {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: var(--space-3);
  animation: landing-rise var(--dur-slow) var(--ease-out) 0.18s both;
}
.landing__signin {
  min-block-size: 52px; padding-inline: var(--space-5);
  border-radius: var(--radius-md);
  background: transparent; color: var(--color-text-secondary);
  border: var(--border-hairline) solid var(--color-border-strong);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .landing__signin { font-family: var(--font-arabic); }
.landing__signin:hover { color: var(--color-text-primary); border-color: var(--color-accent); background: var(--color-accent-soft); }
.landing__signin:focus-visible { outline: 2px solid var(--color-accent-ring); outline-offset: 2px; }
.landing__hint {
  margin: 0; color: var(--color-text-muted);
  font-size: var(--text-xs); line-height: var(--leading-sm);
  max-inline-size: 400px;
  animation: landing-rise var(--dur-slow) var(--ease-out) 0.21s both;
}
@media (max-width: 420px) {
  .landing__actions { flex-direction: column; inline-size: 100%; }
  .landing__cta, .landing__signin { inline-size: 100%; max-inline-size: 340px; }
}
@keyframes landing-rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  /* The rete stops turning, but the instrument stays drawn — the graduations ARE the
     composition, so killing them would remove content, not motion. */
  .astro--rete, .landing__mark { animation: none !important; }
  .landing__wordmark, .landing__about, .landing__actions, .landing__hint, .landing__mark,
  .landing__stats, .landing__details { animation: landing-rise 0.001ms both !important; }
  .landing__stat-dot { animation: none !important; }
}

/* ---- Landing: the product scale (under the CTA) ----
   Four marks on one graduated rule. Replaces the fabricated "+N users" counters;
   this states what the product is, and can never go stale or be untrue. */
.landing__stats {
  display: flex; align-items: center; justify-content: center;
  inline-size: 100%;
  margin-block-start: var(--space-4);
  animation: landing-rise var(--dur-slow) var(--ease-out) 0.24s both;
}
.landing__scale {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  inline-size: 100%; max-inline-size: 480px;
  border-block-start: 1px solid var(--color-border-strong);
}
.landing__mark-item {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding-block-start: var(--space-3); min-inline-size: 0;
}
/* Each product is a division cut into the rule — the tick is the graduation, and it
   sits ON the line, not beside it. */
.landing__mark-tick {
  inline-size: 1px; block-size: 9px; background: var(--color-line);
  margin-block-start: -1px; margin-block-end: 3px;
}
.landing__mark-name {
  font-family: var(--font-display); font-size: var(--text-sm);
  font-weight: var(--weight-semibold); letter-spacing: 0.02em;
  color: var(--color-text-primary);
}
.landing__mark-desc {
  font-size: var(--text-xs); line-height: 1.4; color: var(--color-text-muted);
  text-align: center; overflow-wrap: anywhere;
}
@media (max-width: 400px) {
  .landing__mark-desc { font-size: 11px; }
}
.landing__stat { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.landing__stat-num {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: clamp(20px, 5.5vw, 26px); font-weight: 700; line-height: 1;
  color: var(--color-text-primary);
}
.landing__stat-val { display: inline-block; }
.landing__stat-val.is-bumped { animation: stat-bump 0.45s var(--ease-out); }
@keyframes stat-bump {
  0% { transform: translateY(0); color: var(--color-accent); }
  45% { transform: translateY(-3px); }
  100% { transform: translateY(0); }
}
.landing__stat-label {
  font-family: var(--font-sans); font-size: var(--text-xs);
  color: var(--color-text-secondary); /* AA contrast on the landing bg */
}
[dir="rtl"] .landing__stat-label { font-family: var(--font-arabic); }
.landing__stat-dot {
  inline-size: 9px; block-size: 9px; border-radius: 50%; background: #2ecc71;
  animation: landing-live 1.6s ease-in-out infinite;
}
@keyframes landing-live {
  0%, 100% { box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.55); }
  50% { box-shadow: 0 0 0 6px rgba(46, 204, 113, 0); }
}
.landing__stat-div { inline-size: 1px; align-self: stretch; background: var(--color-border-hairline); }
@media (max-width: 480px) { .landing__stat-div { display: none; } }

/* ---- Landing: features + experimental note ---- */
.landing__details {
  position: relative; z-index: 1;
  inline-size: 100%; max-inline-size: 980px;
  display: flex; flex-direction: column; gap: var(--space-6);
  animation: landing-rise var(--dur-slow) var(--ease-out) 0.3s both;
}
.landing__details-head { text-align: center; display: flex; flex-direction: column; gap: var(--space-2); }
.landing__details-title { margin: 0; font-size: clamp(22px, 5vw, 30px); }
.landing__details-sub {
  margin: 0 auto; max-inline-size: 580px;
  color: var(--color-text-secondary); font-size: var(--text-md); line-height: var(--leading-md);
}
[dir="rtl"] .landing__details-sub { line-height: 1.9; }
.landing__features {
  display: grid; gap: var(--space-3);
  /* min(258px, 100%) lets the track shrink below 258px on ultra-narrow/foldable
     phones instead of overflowing the container. */
  grid-template-columns: repeat(auto-fit, minmax(min(258px, 100%), 1fr));
}
.landing__feature {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-4);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-lg);
  text-align: start;
  transition: border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.landing__feature:hover { border-color: var(--color-accent); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.landing__feature-ico {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  inline-size: 42px; block-size: 42px; border-radius: var(--radius-md);
  background: var(--color-accent-soft); color: var(--color-accent);
}
.landing__feature-ico svg { inline-size: 22px; block-size: 22px; }
.landing__feature-tx { display: flex; flex-direction: column; gap: 4px; min-inline-size: 0; }
.landing__feature-title {
  margin: 0; font-family: var(--font-sans);
  font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--color-text-primary);
}
[dir="rtl"] .landing__feature-title { font-family: var(--font-arabic); }
.landing__feature-desc {
  margin: 0; font-size: var(--text-sm); line-height: var(--leading-sm);
  color: var(--color-text-secondary);
}
[dir="rtl"] .landing__feature-desc { line-height: 1.8; }
.landing__imgnote {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, var(--color-accent-soft), transparent 80%);
  border: var(--border-hairline) solid var(--color-accent);
  text-align: start;
}
.landing__imgnote-ico {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  inline-size: 44px; block-size: 44px; border-radius: var(--radius-md);
  background: var(--color-accent); color: var(--color-on-accent);
}
.landing__imgnote-ico svg { inline-size: 24px; block-size: 24px; }
.landing__imgnote-tx { display: flex; flex-direction: column; gap: 5px; min-inline-size: 0; }
.landing__imgnote-title {
  margin: 0; display: inline-flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  font-family: var(--font-sans); font-size: var(--text-md); font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
}
[dir="rtl"] .landing__imgnote-title { font-family: var(--font-arabic); }
.landing__imgnote-badge {
  font-family: var(--font-sans); font-size: 10px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 2px 8px; border-radius: var(--radius-pill);
  background: var(--color-accent); color: var(--color-on-accent);
}
.landing__imgnote-body {
  margin: 0; font-size: var(--text-sm); line-height: var(--leading-sm);
  color: var(--color-text-secondary);
}
[dir="rtl"] .landing__imgnote-body { line-height: 1.85; }

/* Auth brand becomes a clickable button (returns to landing) */
.auth__brand-btn { background: none; border: none; cursor: pointer; padding: 0; }
.auth__brand-btn:hover { opacity: 0.85; }

/* ----------------------------------------------------------------------------
   8c. Mode selector dropdown (Auto / Plan) — compact trigger + menu
---------------------------------------------------------------------------- */
.mode-select { position: relative; display: inline-flex; flex: 0 0 auto; }
.mode-select__trigger {
  display: inline-flex; align-items: center; gap: 6px;
  block-size: 34px; min-block-size: 34px;
  padding-inline: var(--space-3);
  border-radius: var(--radius-pill);
  border: var(--border-hairline) solid var(--color-border-hairline);
  background: var(--color-bg-subtle);
  color: var(--color-text-secondary);
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .mode-select__trigger { font-family: var(--font-arabic); }
.mode-select__trigger .mode-ico { display: inline-flex; }
.mode-select__trigger .mode-ico svg { inline-size: 15px; block-size: 15px; }
.mode-select__trigger:hover { color: var(--color-text-primary); border-color: var(--color-border-strong); background: var(--color-surface); }
.mode-select.is-open .mode-select__trigger,
.mode-select__trigger[aria-expanded="true"] {
  color: var(--color-accent); border-color: var(--color-accent); background: var(--color-accent-soft);
}
.mode-select__caret { display: inline-flex; }
.mode-select__caret svg { inline-size: 13px; block-size: 13px; transition: transform var(--dur-fast) var(--ease-out); }
.mode-select.is-open .mode-select__caret svg { transform: rotate(180deg); }
.mode-select__menu {
  position: absolute; inset-block-end: calc(100% + 8px); inset-inline-start: 0;
  z-index: var(--z-dropdown);
  inline-size: 248px; max-inline-size: 78vw;
  padding: var(--space-1);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; gap: 2px;
  animation: msg-in var(--dur-fast) var(--ease-out);
}
/* RTL: the base `inset-inline-start: 0` already right-aligns the menu to the
   trigger and lets it extend leftward (on-screen). The old `inset-inline-end: 0`
   override flipped it to the left edge, pushing the 248px menu ~170px off-screen. */
.mode-select__item {
  display: flex; align-items: flex-start; gap: var(--space-3);
  inline-size: 100%;
  padding: var(--space-3);
  border-radius: var(--radius-md);
  text-align: start;
  color: var(--color-text-primary);
  transition: background var(--dur-fast) var(--ease-out);
}
.mode-select__item:hover, .mode-select__item:focus-visible { background: var(--color-bg-subtle); outline: none; }
.mode-select__item.is-active { background: var(--color-accent-soft); }
.mode-select__item .mode-ico { display: inline-flex; flex: 0 0 auto; margin-block-start: 1px; color: var(--color-text-secondary); }
.mode-select__item.is-active .mode-ico { color: var(--color-accent); }
.mode-select__item .mode-ico svg { inline-size: 18px; block-size: 18px; }
.mode-select__item-text { display: flex; flex-direction: column; gap: 2px; min-inline-size: 0; flex: 1 1 auto; }
.mode-select__item-label { font-family: var(--font-sans); font-size: var(--text-sm); font-weight: var(--weight-semibold); }
[dir="rtl"] .mode-select__item-label { font-family: var(--font-arabic); }
.mode-select__item.is-active .mode-select__item-label { color: var(--color-accent); }
.mode-select__item-hint { font-size: var(--text-xs); line-height: var(--leading-xs); color: var(--color-text-muted); }
.mode-select__check { display: inline-flex; flex: 0 0 auto; color: var(--color-accent); margin-block-start: 2px; }
.mode-select__check svg { inline-size: 16px; block-size: 16px; }
.mode-select__check:empty { inline-size: 16px; }

/* ----- Tools "+" menu (Web search + Thinking toggles) — compact, any screen ----- */
.tools-menu { position: relative; display: inline-flex; flex: 0 0 auto; }
.tools-menu__trigger {
  position: relative; display: inline-grid; place-items: center;
  inline-size: 34px; block-size: 34px; min-inline-size: 34px;
  border-radius: var(--radius-pill);
  border: var(--border-hairline) solid var(--color-border-hairline);
  background: var(--color-bg); color: var(--color-text-secondary);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.tools-menu__trigger svg { inline-size: 18px; block-size: 18px; }
.tools-menu__trigger:hover { color: var(--color-text-primary); border-color: var(--color-border-strong); background: var(--color-surface); }
.tools-menu.is-open .tools-menu__trigger { color: var(--color-accent); border-color: var(--color-accent); background: var(--color-accent-soft); }
/* small dot when a tool is active */
.tools-menu.has-active .tools-menu__trigger::after {
  content: ""; position: absolute; top: 1px; inset-inline-end: 1px;
  inline-size: 8px; block-size: 8px; border-radius: 50%;
  background: var(--color-accent); border: 2px solid var(--color-bg);
}
.tools-menu__panel {
  position: absolute; inset-block-end: calc(100% + 8px); inset-inline-start: 0;
  z-index: var(--z-dropdown);
  inline-size: 236px; max-inline-size: 80vw;
  padding: var(--space-1);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; gap: 2px;
  animation: msg-in var(--dur-fast) var(--ease-out);
}
.tools-menu__panel[hidden] { display: none; }
.tools-menu__item {
  display: flex; align-items: center; gap: var(--space-2);
  inline-size: 100%; padding: var(--space-2);
  border-radius: var(--radius-md); color: var(--color-text-primary);
  font-family: var(--font-sans); font-size: var(--text-sm); font-weight: var(--weight-medium);
  text-align: start; transition: background var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .tools-menu__item { font-family: var(--font-arabic); }
.tools-menu__item:hover, .tools-menu__item:focus-visible { background: var(--color-bg-subtle); outline: none; }
.tools-menu__ico { display: inline-flex; flex: 0 0 auto; color: var(--color-text-secondary); transition: color var(--dur-fast); }
.tools-menu__ico svg { inline-size: 18px; block-size: 18px; }
.tools-menu__item.is-on .tools-menu__ico { color: var(--color-accent); }
.tools-menu__label { flex: 1 1 auto; min-inline-size: 0; }
.tools-menu__switch {
  position: relative; flex: 0 0 auto; inline-size: 34px; block-size: 20px;
  border-radius: var(--radius-pill); background: var(--color-border-strong);
  transition: background var(--dur-fast) var(--ease-out);
}
.tools-menu__switch::after {
  content: ""; position: absolute; top: 2px; inset-inline-start: 2px;
  inline-size: 16px; block-size: 16px; border-radius: 50%;
  background: #fff; box-shadow: var(--shadow-xs);
  transition: transform var(--dur-fast) var(--ease-out);
}
.tools-menu__item.is-on .tools-menu__switch { background: var(--color-accent); }
.tools-menu__item.is-on .tools-menu__switch::after { transform: translateX(14px); }
[dir="rtl"] .tools-menu__item.is-on .tools-menu__switch::after { transform: translateX(-14px); }

/* ----------------------------------------------------------------------------
   8d. Composer trailing controls — tidy [mode][think] group + [attach][send]
---------------------------------------------------------------------------- */
.composer__tools { display: inline-flex; align-items: center; gap: var(--space-2); flex: 0 0 auto; }
.composer__actions {
  display: inline-flex; align-items: center; gap: var(--space-1);
  flex: 0 0 auto;
  padding: 2px; border-radius: var(--radius-pill);
  background: var(--color-bg-subtle);
}
.composer__actions .composer__attach {
  inline-size: 36px; block-size: 36px; min-inline-size: 36px;
  border-radius: var(--radius-pill); color: var(--color-text-secondary);
}
.composer__actions .composer__attach:hover { background: var(--color-surface); color: var(--color-text-primary); }
.composer__actions .composer__send { inline-size: 36px; block-size: 36px; min-inline-size: 36px; border-radius: var(--radius-pill); }
.composer__actions .composer__send svg { inline-size: 19px; block-size: 19px; }

/* ----------------------------------------------------------------------------
   8e. Collapsible sidebar (desktop taskbar) — hide/restore the conversation list
---------------------------------------------------------------------------- */
.topbar__desktop-only { display: grid; }
.topbar__sidebar-toggle { flex: 0 0 auto; }
@media (min-width: 769px) {
  body.sidebar-collapsed .app { grid-template-columns: 0 1fr; }
  body.sidebar-collapsed .sidebar {
    transform: translateX(-100%);
    inline-size: 0; min-inline-size: 0; padding: 0; border-inline-end: none;
    overflow: hidden; pointer-events: none;
  }
  [dir="rtl"] body.sidebar-collapsed .sidebar { transform: translateX(100%); }
  .sidebar {
    transition: transform var(--dur-base) var(--ease-out);
  }
  /* When collapsed, the topbar shows the single logo lockup (sidebar logo hidden);
     when expanded, the sidebar carries the only logo (avoid a duplicate mark). */
  body:not(.sidebar-collapsed) .topbar__desktop-mark { display: none; }
  body.sidebar-collapsed .topbar__desktop-mark { display: inline-flex; }
}

/* ----------------------------------------------------------------------------
   8f. File disclosure (masked file content under the file card)
---------------------------------------------------------------------------- */
.firas-file-loading { color: var(--color-accent); }
.file-disclosure {
  margin-block: var(--space-2) var(--space-1);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-md);
  background: var(--color-bg-subtle);
  overflow: hidden;
}
.file-disclosure__summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  color: var(--color-text-secondary);
  user-select: none;
  transition: color var(--dur-fast), background var(--dur-fast);
}
[dir="rtl"] .file-disclosure__summary { font-family: var(--font-arabic); }
.file-disclosure__summary::-webkit-details-marker { display: none; }
.file-disclosure__summary::before {
  content: ""; inline-size: 7px; block-size: 7px; flex: 0 0 auto;
  border-inline-end: 2px solid currentColor; border-block-end: 2px solid currentColor;
  transform: rotate(-45deg); transition: transform var(--dur-fast) var(--ease-out);
  margin-inline-end: 2px;
}
.file-disclosure[open] .file-disclosure__summary::before { transform: rotate(45deg); }
.file-disclosure__summary:hover { color: var(--color-accent); background: var(--color-accent-soft); }
.file-disclosure__body {
  padding: var(--space-3) var(--space-4) var(--space-4);
  border-block-start: var(--border-hairline) solid var(--color-border-hairline);
  background: var(--color-surface);
  font-family: var(--font-serif);
  font-size: var(--text-base); line-height: var(--leading-base);
}
[dir="rtl"] .file-disclosure__body { font-family: var(--font-arabic); }

/* ----------------------------------------------------------------------------
   9. Responsive
---------------------------------------------------------------------------- */
@media (max-width: 768px) {
  .app { grid-template-columns: 1fr; }

  /* Sidebar collapse is a desktop-only feature; on mobile the drawer rules win. */
  body.sidebar-collapsed .app { grid-template-columns: 1fr; }
  .topbar__desktop-only { display: none; }

  .sidebar {
    position: fixed; inset-block: 0; inset-inline-start: 0; z-index: var(--z-drawer);
    inline-size: min(288px, 88vw);
    transform: translateX(-100%);
    transition: transform var(--dur-base) var(--ease-out);
    box-shadow: var(--shadow-lg);
  }
  /* RTL drawer: base inset-inline-start:0 already pins it to the RIGHT edge in RTL,
     so only flip the hide direction (slide off the right). Overriding the inset here
     was the bug — it re-pinned to the left, leaving ~102px peeking on-screen. */
  [dir="rtl"] .sidebar { transform: translateX(100%); }
  .sidebar.is-open { transform: translateX(0); }
  .sidebar .sidebar__close { display: grid; }

  .topbar { padding-inline: var(--space-3); }
  .topbar .topbar__mobile-only { display: grid; }
  .topbar__desktop-mark { display: none; }
  .topbar__wordmark-center {
    position: absolute; inset-inline: 0; margin-inline: auto;
    inline-size: max-content; pointer-events: none;
  }
  @media (max-width: 600px) {
    .topbar .topbar__wordmark-center { display: none; } /* 0,2,0 to beat the mobile-only show bump */
  }
  .topbar__newchat-label { display: none; }
  .topbar__newchat { padding-inline: var(--space-2); }

  .chat-inner { padding-inline: var(--space-4); }
  .welcome { padding: var(--space-12) var(--space-4) 0; }
  .welcome__title { font-size: var(--text-2xl); line-height: var(--leading-2xl); }

  .msg-user__bubble { max-inline-size: 88%; }

  .composer-wrap { padding-inline: var(--space-4); }
}

/* Composer: on narrow widths put the textarea on its own row, with the tool
   group + action cluster on a second row so nothing overflows or clips. */
@media (max-width: 560px) {
  .composer__row { flex-wrap: wrap; }
  .composer__textarea { flex: 1 1 100%; inline-size: 100%; order: -1; }
  .composer__tools { flex: 1 1 auto; }
  .composer__actions { margin-inline-start: auto; }
}

/* Comfortable touch targets (>=44px) on touch / coarse pointers. */
@media (pointer: coarse), (max-width: 560px) {
  .composer__actions .composer__attach,
  .composer__actions .composer__send { inline-size: 44px; block-size: 44px; min-inline-size: 44px; }
  .composer__actions .composer__send svg { inline-size: 20px; block-size: 20px; }
  .mode-select__trigger { block-size: 40px; min-block-size: 40px; }
  .think-toggle { block-size: 40px; }
  .icon-btn { inline-size: 44px; block-size: 44px; min-inline-size: 44px; }
}

@media (max-width: 420px) {
  .tier-switch__btn .tier-name { font-size: 0; }
  .tier-switch__btn .tier-name::after { content: attr(data-short); font-size: var(--text-sm); }
  .tier-switch__btn[data-tier="ultra"] .tier-name::before { font-size: 10px; }
  /* Keep the composer roomy: thinking toggle becomes icon-only */
  .think-toggle__label { display: none; }
  .think-toggle { padding-inline: var(--space-2); }
  /* Mode selector becomes ICON-ONLY on a tight composer so its label never clips
     (the bolt vs checklist icon distinguishes Auto/Plan; the menu shows full
     labels + hints). */
  .mode-select__trigger .mode-name { display: none; }
  .mode-select__trigger { padding-inline: var(--space-2); gap: 4px; }
  .mode-select__menu { inline-size: min(86vw, 248px); }
}

/* With FOUR tiers + share + the notifications bell, the topbar is crowded on phones.
   The theme toggle moved into Settings; these rules make the remaining row bullet-proof:
   tight gaps, a shrinkable tier switch that can scroll internally instead of bursting out
   of its pill, compact icon buttons — nothing ever pushes past the screen edge. */
@media (max-width: 600px) {
  .topbar { gap: var(--space-2); }
  .tier-switch { min-inline-size: 0; flex: 0 1 auto; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .tier-switch::-webkit-scrollbar { display: none; }
  .tier-switch__btn { flex: 0 0 auto; }
}
@media (max-width: 480px) {
  .topbar { gap: var(--space-1); padding-inline: var(--space-2); }
  .topbar .icon-btn { inline-size: 34px; block-size: 34px; }
  .tier-switch { gap: 1px; }
  .tier-switch__btn { padding-inline: 6px; }
  .tier-switch__btn[data-tier="max"] .tier-beta { display: none; }
  .topbar__newchat { padding-inline: var(--space-1); }
  .topbar__newchat-label { display: none; }          /* + icon says it */
}
/* Very small phones (iPhone SE 320, Galaxy Fold 280) */
@media (max-width: 360px) {
  .topbar { gap: 3px; padding-inline: var(--space-1); }
  .tier-switch { gap: 1px; }
  .tier-switch__btn { padding-inline: 4px; font-size: 12px; gap: 3px; }
  .topbar .icon-btn { inline-size: 31px; block-size: 31px; }
}

/* ----------------------------------------------------------------------------
   Mobile-only drawer trigger uses the spark mark; hide aurora flourish hooks
---------------------------------------------------------------------------- */
.topbar__mobile-only-hide { /* legacy hook kept harmless */ }

/* ----------------------------------------------------------------------------
   10. Auth screen
---------------------------------------------------------------------------- */
.auth {
  position: fixed; inset: 0; z-index: var(--z-toast);
  display: grid; place-items: center;
  padding: var(--space-6);
  background: var(--color-bg);
  overflow-y: auto;
}
.auth[hidden] { display: none; }
.auth__card {
  inline-size: 100%; max-inline-size: 400px;
  display: flex; flex-direction: column;
  padding: var(--space-8);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  animation: msg-in var(--dur-slow) var(--ease-out);
}
.auth__brand {
  display: inline-flex; align-items: center; gap: var(--space-3);
  margin-bottom: var(--space-6);
  align-self: center;
}
.auth__title {
  font-family: var(--font-serif);
  font-size: var(--text-2xl); line-height: var(--leading-2xl);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-tight);
  color: var(--color-text-primary);
  text-align: start;
}
[dir="rtl"] .auth__title { font-family: var(--font-arabic); }
.auth__subtitle {
  margin-block-start: var(--space-1) var(--space-5);
  margin-bottom: var(--space-6);
  font-size: var(--text-base); line-height: var(--leading-base);
  color: var(--color-text-secondary);
  text-align: start;
}
/* Continue with Google — Claude/teal-tinted neutral button with brand "G" */
.auth__google {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-3);
  inline-size: 100%;
  block-size: 44px;
  padding-inline: var(--space-4);
  border-radius: var(--radius-sm);
  border: var(--border-hairline) solid var(--color-border-strong);
  background: var(--color-surface);
  color: var(--color-text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-base); font-weight: var(--weight-medium);
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .auth__google { font-family: var(--font-arabic); }
.auth__google[hidden] { display: none; }
.auth__google:hover:not(:disabled) {
  background: var(--color-bg-subtle);
  border-color: var(--color-accent);
}
.auth__google:disabled { opacity: 0.65; cursor: progress; }
.auth__google.is-loading { position: relative; color: transparent; }
.auth__google.is-loading .auth__google-icon { opacity: 0; }
.auth__google.is-loading::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  inline-size: 18px; block-size: 18px;
  border: 2px solid var(--color-accent); border-top-color: transparent;
  border-radius: var(--radius-pill);
  animation: auth-spin 0.7s linear infinite;
}
.auth__google-icon { display: inline-grid; place-items: center; flex: 0 0 auto; }
.auth__google-icon svg { display: block; inline-size: 18px; block-size: 18px; }

/* Subtle "or" divider between Google and email/password */
.auth__divider {
  display: flex; align-items: center; gap: var(--space-3);
  margin-block: var(--space-5);
  color: var(--color-text-muted);
  font-size: var(--text-xs); letter-spacing: var(--tracking-wide); text-transform: uppercase;
}
.auth__divider[hidden] { display: none; }
.auth__divider::before, .auth__divider::after {
  content: ""; flex: 1; block-size: var(--border-hairline);
  background: var(--color-border-hairline);
}

.auth__form { display: flex; flex-direction: column; gap: var(--space-4); }
.auth__field { display: flex; flex-direction: column; gap: var(--space-2); }
.auth__field[hidden] { display: none; }
.auth__label {
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  color: var(--color-text-secondary);
  text-align: start;
}
.auth__field input {
  block-size: 42px;
  inline-size: 100%;
  padding-inline: var(--space-3);
  background: var(--color-bg);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-sm);
  color: var(--color-text-primary);
  font-size: var(--text-base);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.auth__field input::placeholder { color: var(--color-text-muted); }
.auth__field input:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: var(--shadow-focus);
}
.auth__error {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--color-error);
  border-radius: var(--radius-sm);
  color: var(--color-error);
  font-size: var(--text-sm);
  text-align: start;
}
.auth__error[hidden] { display: none; }
.auth__forgot {
  align-self: flex-end;
  background: none; border: none; cursor: pointer;
  color: var(--color-text-secondary);
  font: 500 var(--text-sm)/1.4 var(--font-sans);
  padding: 2px 2px;
}
.auth__forgot:hover { color: var(--color-accent); text-decoration: underline; }
.auth__forgot[hidden] { display: none; }
.auth__forgot:disabled { opacity: .55; cursor: default; }
.auth__back {
  align-self: center; margin-top: 4px;
  background: none; border: none; cursor: pointer;
  color: var(--color-text-secondary);
  font: 500 var(--text-sm)/1.4 var(--font-sans);
  padding: 6px 4px;
}
.auth__back:hover { color: var(--color-accent); }
.auth__back[hidden] { display: none; }
.auth__note {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--color-accent-soft);
  border: var(--border-hairline) solid var(--color-accent);
  border-radius: var(--radius-sm);
  color: var(--color-text-primary);
  font-size: var(--text-sm);
  text-align: start;
}
.auth__note[hidden] { display: none; }

/* Cookie consent (auth screen only, until a choice is saved) */
.cookie-consent {
  position: fixed; inset-inline: 0; bottom: 0; z-index: calc(var(--z-toast) + 1);
  display: flex; align-items: center; justify-content: center; gap: 16px; flex-wrap: wrap;
  padding: 14px 20px;
  background: var(--color-surface);
  border-top: 1px solid var(--color-border-strong);
  box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.28);
  animation: cookieUp 0.3s var(--ease-out, ease);
}
.cookie-consent[hidden] { display: none; }
@keyframes cookieUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
.cookie-consent__text { margin: 0; flex: 1 1 320px; max-inline-size: 620px; font: 400 13.5px/1.6 var(--font-sans); color: var(--color-text-secondary); }
.cookie-consent__actions { display: flex; gap: 8px; flex: 0 0 auto; }
.cookie-consent__btn { cursor: pointer; border-radius: var(--radius-pill); padding: 9px 22px; font: 600 13px var(--font-sans); transition: background .15s, color .15s, border-color .15s; border: 1px solid transparent; }
.cookie-consent__accept { background: var(--color-accent); color: var(--color-on-accent); border-color: var(--color-accent); }
.cookie-consent__accept:hover { background: var(--color-accent-hover); border-color: var(--color-accent-hover); }
.cookie-consent__reject { background: transparent; color: var(--color-text-secondary); border-color: var(--color-border-strong); }
.cookie-consent__reject:hover { background: var(--color-bg-subtle); color: var(--color-text-primary); }
@media (max-width: 560px) {
  .cookie-consent { flex-direction: column; align-items: stretch; gap: 10px; padding: 14px 16px; text-align: center; }
  /* P0 — this banner was covering the whole sign-in form on phones.
     `.cookie-consent__text` carries `flex: 1 1 320px`, written for the default ROW layout
     where 320px is a basis on the INLINE axis. Flipping the container to `column` here turns
     that same 320px into a minimum BLOCK size, so the paragraph became 320px TALL and the
     banner grew to ~403px — half the screen. The banner is position:fixed at the bottom with
     a higher z-index than `.auth`, so it did not merely overlap the email field, the submit
     button and the "already have an account?" switch — it swallowed their taps too
     (elementFromPoint over the submit button returned the banner).
     It is shown only to visitors who have not chosen yet, i.e. every FIRST-TIME visitor:
     the app's front door was blocked for exactly the people arriving at it. */
  .cookie-consent__text { flex: 0 0 auto; }
  .cookie-consent__actions { justify-content: center; }
  .cookie-consent__btn { flex: 1; }
}

.auth__submit {
  block-size: 44px;
  margin-block-start: var(--space-1);
  border-radius: var(--radius-sm);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: var(--font-sans);
  font-size: var(--text-md); font-weight: var(--weight-semibold);
  transition: background var(--dur-fast) var(--ease-out), opacity var(--dur-fast);
}
[dir="rtl"] .auth__submit { font-family: var(--font-arabic); }
.auth__submit:hover:not(:disabled) { background: var(--color-accent-hover); }
.auth__submit:disabled { opacity: 0.65; cursor: progress; }
.auth__submit.is-loading { position: relative; color: transparent; }
.auth__submit.is-loading::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  inline-size: 18px; block-size: 18px;
  border: 2px solid var(--color-on-accent); border-top-color: transparent;
  border-radius: var(--radius-pill);
  animation: auth-spin 0.7s linear infinite;
}
@keyframes auth-spin { to { transform: rotate(360deg); } }
.auth__switch {
  margin-block-start: var(--space-6); margin-bottom: 0;
  text-align: center;
  font-size: var(--text-sm); color: var(--color-text-secondary);
}
.auth__switch-btn {
  margin-inline-start: 4px;
  color: var(--color-accent); font-weight: var(--weight-medium);
  font-size: inherit;
}
.auth__switch-btn:hover { color: var(--color-accent-hover); text-decoration: underline; }

/* ----------------------------------------------------------------------------
   11. Toast + thread loading
---------------------------------------------------------------------------- */
.toast {
  position: fixed; inset-block-end: var(--space-8); inset-inline: 0;
  margin-inline: auto; inline-size: max-content; max-inline-size: 90vw;
  z-index: var(--z-toast);
  padding: var(--space-3) var(--space-5);
  background: var(--color-text-primary); color: var(--color-bg);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-lg);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.toast.is-visible { opacity: 1; transform: translateY(0); }

.thread-loading {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: var(--space-16) 0;
}
/* Three graduations catching the light in turn, not three bouncing balls. The dots used to
   hop 4px vertically — a toy-app idiom that reads as impatience, and one that belongs to no
   part of an instrument. Weight and brightness travel along the row instead, which says
   "working" without anything moving out of place. */
.thread-loading__dot {
  inline-size: 8px; block-size: 8px; border-radius: var(--radius-pill);
  background: var(--color-text-muted);
  animation: thread-glow 1.2s var(--ease-inout) infinite;
}
.thread-loading__dot:nth-child(2) { animation-delay: 0.18s; }
.thread-loading__dot:nth-child(3) { animation-delay: 0.36s; }
@keyframes thread-glow {
  0%, 80%, 100% { opacity: 0.28; background: var(--color-text-muted); }
  40%           { opacity: 1;    background: var(--color-accent); }
}

/* ----------------------------------------------------------------------------
   11b. Skeletons — content-shaped loading placeholders.
   No palette change: everything is derived from existing theme tokens, so it
   adapts to light/dark automatically. The global prefers-reduced-motion rule
   (top of file) already freezes the shimmer into a calm static block, so no
   per-rule motion guard is needed here.
---------------------------------------------------------------------------- */
.skeleton {
  display: block;
  position: relative;
  overflow: hidden;
  background: var(--color-bg-subtle);
  border-radius: var(--radius-sm);
}
.skeleton::after {
  content: "";
  position: absolute; inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in srgb, var(--color-text-muted) 16%, transparent) 50%,
    transparent 100%
  );
  animation: skeleton-sweep 1.5s ease-in-out infinite;
}
@keyframes skeleton-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}
.skeleton--line { block-size: 11px; border-radius: var(--radius-xs); }

/* Sidebar history skeleton — mirrors the 36px chat-item rows */
.hist-skel { display: flex; flex-direction: column; gap: 2px; padding-block: var(--space-1); }
.hist-skel__label { block-size: 9px; inline-size: 44px; margin: var(--space-3) var(--space-3) var(--space-2); border-radius: var(--radius-xs); }
.hist-skel__row { display: flex; align-items: center; block-size: 36px; padding-inline: var(--space-3); }
.hist-skel__bar { block-size: 10px; border-radius: var(--radius-xs); }

/* Message-thread skeleton — mirrors the real .turn / .msg-user / .msg-ai layout */
.thread-skel { padding-block: var(--space-6) var(--space-4); }
.thread-skel__turn { margin-bottom: var(--space-8); }
.thread-skel__turn--user { display: flex; justify-content: flex-end; }
.thread-skel__bubble {
  max-inline-size: 62%; inline-size: 62%; min-block-size: 44px;
  border-radius: var(--radius-lg);
}
.thread-skel__head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-3); }
.thread-skel__avatar { inline-size: 24px; block-size: 24px; border-radius: 26%; flex: 0 0 auto; }
.thread-skel__name { inline-size: 68px; block-size: 11px; }
.thread-skel__lines { display: grid; gap: 10px; }

/* Firas Code recent-projects grid skeleton — injected into .cw-home__grid,
   sized to match the real .cw-proj tiles (~58px). */
.cw-skel-card { block-size: 58px; border-radius: var(--radius-lg); border: var(--border-hairline) solid var(--color-border-hairline); }

/* Recoverable in-thread error (failed conversation load) — not a dead blank */
.thread-error {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--space-1); padding: var(--space-16) var(--space-6);
  color: var(--color-text-secondary);
  animation: msg-in var(--dur-slow) var(--ease-out);
}
.thread-error__icon { color: var(--color-error); display: grid; place-items: center; margin-bottom: var(--space-2); }
.thread-error__title { margin: 0; font-family: var(--font-serif); font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--color-text-primary); }
.thread-error__sub { margin: 0; font-size: var(--text-sm); }
.thread-error__retry {
  margin-top: var(--space-4);
  padding: var(--space-2) var(--space-5);
  background: var(--color-accent); color: var(--color-on-accent);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.thread-error__retry:hover { background: var(--color-accent-hover); }
.thread-error__retry:active { transform: scale(0.97); }

/* ----------------------------------------------------------------------------
   11c. User reading-size + motion preferences (Settings). Same theme; only the
   conversation text scales, so chrome/layout is untouched.
---------------------------------------------------------------------------- */
:root { --read-scale: 1; }
:root[data-fontsize="sm"] { --read-scale: 0.92; }
:root[data-fontsize="lg"] { --read-scale: 1.14; }
.msg-ai__body { font-size: calc(var(--text-lg) * var(--read-scale)); line-height: calc(var(--leading-lg) * var(--read-scale)); }
.msg-user__bubble { font-size: calc(var(--text-base) * var(--read-scale)); line-height: calc(var(--leading-base) * var(--read-scale)); }

/* Explicit "reduce motion" toggle — mirrors the prefers-reduced-motion rule */
/* The explicit in-app "Reduce motion" toggle. Mirrors the media-query rule above exactly —
   including keeping busy indicators alive — so choosing it in Settings and setting it at the
   OS level produce the same product, not two different ones. */
:root[data-motion="off"] *,
:root[data-motion="off"] *::before,
:root[data-motion="off"] *::after {
  animation-duration: 0.001ms !important;
  animation-delay: 0ms !important;
  transition-duration: 0.001ms !important;
  scroll-behavior: auto !important;
}
:root[data-motion="off"] body::before { display: none; }
:root[data-motion="off"] .landing__cta.is-busy::after,
:root[data-motion="off"] .auth__submit.is-busy::after,
:root[data-motion="off"] .cw-bar__btn--primary.is-busy .cw-bar__btn-ico,
:root[data-motion="off"] .cw-live__spin,
:root[data-motion="off"] .cw-plan__spin,
:root[data-motion="off"] .cw-status__save.is-saving::before,
:root[data-motion="off"] .thread-loading__dot,
:root[data-motion="off"] .firas-ask-loading__dots > span,
:root[data-motion="off"] .skeleton,
:root[data-motion="off"] .stream-caret::after {
  animation-name: rm-working !important;
  animation-duration: 1.5s !important;
  animation-iteration-count: infinite !important;
  animation-timing-function: var(--ease-inout) !important;
  transform: none !important;
}

/* ----------------------------------------------------------------------------
   12. Image attachments (composer tray + in-message thumbnails)
---------------------------------------------------------------------------- */
/* Composer is a column: optional attachment tray sits above the input row */
.composer__row {
  display: flex; align-items: flex-end; gap: var(--space-2); inline-size: 100%;
}

.attach-tray {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  padding: 2px var(--space-2) var(--space-3);
}
.attach-tray:empty { display: none; }
.attach-thumb {
  position: relative; inline-size: 56px; block-size: 56px;
  border-radius: var(--radius-sm); overflow: hidden;
  border: var(--border-hairline) solid var(--color-border-hairline);
  background: var(--color-bg-subtle); flex: 0 0 auto;
}
.attach-thumb img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.attach-thumb.is-loading::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  inline-size: 16px; block-size: 16px;
  border: 2px solid var(--color-accent); border-top-color: transparent;
  border-radius: var(--radius-pill); animation: auth-spin 0.7s linear infinite;
}
.attach-thumb__remove {
  position: absolute; inset-block-start: 2px; inset-inline-end: 2px;
  inline-size: 18px; block-size: 18px; border-radius: var(--radius-pill);
  display: grid; place-items: center;
  background: rgba(0,0,0,0.55); color: #fff;
  transition: background var(--dur-fast);
}
.attach-thumb__remove:hover { background: rgba(0,0,0,0.75); }
.attach-thumb__remove svg { inline-size: 11px; block-size: 11px; }

/* Images rendered inside a sent user bubble */
.msg-user__images {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  margin-block-end: var(--space-2);
}
.msg-user__images img {
  max-inline-size: 180px; max-block-size: 180px;
  border-radius: var(--radius-sm);
  border: var(--border-hairline) solid var(--color-border-hairline);
  display: block; object-fit: cover;
}
.msg-user__bubble:has(.msg-user__images:only-child) { /* image-only bubble stays tidy */ }
.msg-user__images img { cursor: zoom-in; transition: filter var(--dur-fast) var(--ease-out); }
.msg-user__images img:hover { filter: brightness(1.06); }

/* Full-screen attached-image viewer */
.img-lightbox {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  padding: 24px; background: rgba(0, 0, 0, 0.82);
  cursor: zoom-out; animation: lbFade 0.15s ease;
}
@supports (backdrop-filter: blur(2px)) { .img-lightbox { backdrop-filter: blur(2px); } }
.img-lightbox__img {
  max-inline-size: 94vw; max-block-size: 92vh;
  border-radius: 8px; background: #fff; cursor: default;
  box-shadow: 0 16px 56px rgba(0, 0, 0, 0.55);
}
.img-lightbox__close {
  position: absolute; top: 16px; inset-inline-end: 20px;
  inline-size: 40px; block-size: 40px; border-radius: 50%; border: 0;
  background: rgba(255, 255, 255, 0.16); color: #fff;
  font-size: 26px; line-height: 1; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
.img-lightbox__close:hover { background: rgba(255, 255, 255, 0.3); }
@keyframes lbFade { from { opacity: 0; } to { opacity: 1; } }

/* ----------------------------------------------------------------------------
   13. KaTeX math — keep LTR for formulas even inside RTL bubbles
---------------------------------------------------------------------------- */
.md .katex, .md .katex-display { direction: ltr; unicode-bidi: isolate; }
.md .katex-display { overflow-x: auto; overflow-y: hidden; padding-block: 2px; }
[dir="rtl"] .md .katex-display { text-align: center; }
/* User-message math must ALSO stay LTR inside an RTL (Arabic) bubble, else the equation renders reversed */
.msg-user__bubble .katex, .msg-user__bubble .katex-display { direction: ltr; unicode-bidi: isolate; }
.msg-user__bubble .katex-display { overflow-x: auto; overflow-y: hidden; padding-block: 2px; }
[dir="rtl"] .msg-user__bubble .katex-display { text-align: center; }

/* ----------------------------------------------------------------------------
   14. Export menu (Download / تصدير) — anchored dropdown on assistant actions
---------------------------------------------------------------------------- */
.export-menu { position: relative; display: inline-flex; }
.export-menu__trigger .export-menu__caret {
  display: inline-flex; margin-inline-start: 1px;
}
.export-menu__trigger .export-menu__caret svg { inline-size: 13px; block-size: 13px; }
.export-menu__list {
  position: absolute; inset-block-end: calc(100% + 6px); inset-inline-start: 0;
  z-index: var(--z-dropdown);
  min-inline-size: 196px;
  padding: var(--space-1);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; gap: 1px;
  animation: msg-in var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .export-menu__list { inset-inline-start: auto; inset-inline-end: 0; }
.export-menu__item {
  display: flex; align-items: center; gap: var(--space-2);
  inline-size: 100%;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-size: var(--text-sm); color: var(--color-text-secondary);
  text-align: start; white-space: nowrap;
  transition: background var(--dur-fast), color var(--dur-fast);
}
[dir="rtl"] .export-menu__item { font-family: var(--font-arabic); }
.export-menu__item:hover, .export-menu__item:focus-visible {
  background: var(--color-bg-subtle); color: var(--color-text-primary); outline: none;
}
.export-menu__item svg { inline-size: 16px; block-size: 16px; flex: 0 0 auto; color: var(--color-text-muted); }
.export-menu__item:hover svg, .export-menu__item:focus-visible svg { color: var(--color-accent); }

/* ----------------------------------------------------------------------------
   File card — Claude-style downloadable file, shown when the user asked for a
   file (PDF / Word / Excel / PowerPoint). Bordered surface, teal accent.
---------------------------------------------------------------------------- */
.file-card {
  display: flex; align-items: center; gap: var(--space-3);
  inline-size: 100%; max-inline-size: 420px;
  margin-block: var(--space-3) var(--space-1);
  padding: var(--space-3);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.file-card:hover {
  border-color: var(--color-accent);
  box-shadow: var(--shadow-sm);
}
.file-card:focus-visible {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: var(--shadow-focus);
}
.file-card__icon {
  display: inline-flex; align-items: center; justify-content: center;
  inline-size: 40px; block-size: 40px; flex: 0 0 auto;
  border-radius: var(--radius-md);
  background: var(--color-accent-soft);
  color: var(--color-accent);
}
.file-card__icon svg { inline-size: 22px; block-size: 22px; }
.file-card__info {
  display: flex; flex-direction: column; gap: 2px;
  min-inline-size: 0; flex: 1 1 auto;
}
.file-card__name {
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-align: start;
}
.file-card__label {
  font-size: var(--text-xs); line-height: var(--leading-xs);
  color: var(--color-text-muted);
}
[dir="rtl"] .file-card__label { font-family: var(--font-arabic); }
.file-card__dl {
  display: inline-flex; align-items: center; gap: var(--space-1);
  flex: 0 0 auto;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .file-card__dl { font-family: var(--font-arabic); }
.file-card__dl:hover { background: var(--color-accent-hover); }
.file-card__dl:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.file-card__dl svg { inline-size: 15px; block-size: 15px; }

/* ---- Generated image card ---- */
.image-card {
  inline-size: 100%; max-inline-size: 420px;
  margin-block: var(--space-3) var(--space-1);
}
.image-card__frame {
  position: relative; overflow: hidden;
  inline-size: 100%; aspect-ratio: 1 / 1;
  border-radius: var(--radius-lg);
  background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--color-border-hairline);
}
/* Generating effect: glowing accent border + sweeping shimmer over the frame. */
.image-card.is-loading .image-card__frame {
  border-color: transparent;
  box-shadow: 0 0 0 1.5px var(--color-accent-soft), 0 10px 34px -12px var(--color-accent);
  animation: imgGlow 1.7s ease-in-out infinite;
}
/* The frame no longer runs its own shimmer: the loader below sweeps its own light across the
   plate, and two diagonal sweeps at different speeds read as a glitch rather than as craft. */
.image-card.is-loading .image-card__frame {
  background:
    radial-gradient(120% 90% at 50% 0%, color-mix(in srgb, var(--color-accent) 7%, transparent), transparent 60%),
    var(--color-bg-subtle);
}
@keyframes imgGlow {
  0%, 100% { box-shadow: 0 0 0 1px var(--color-accent-soft), 0 8px 24px -14px var(--color-accent); }
  50%      { box-shadow: 0 0 0 2px var(--color-accent), 0 16px 44px -10px var(--color-accent); }
}
@keyframes imgShimmer { from { background-position: 220% 0; } to { background-position: -220% 0; } }
.image-card__loader {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2);
  color: var(--color-text-secondary);
  font-family: var(--font-sans); font-size: var(--text-sm);
}
[dir="rtl"] .image-card__loader { font-family: var(--font-arabic); }
.image-card.is-done .image-card__loader { display: none; }
/* ══ THE IMAGE IS BEING MADE ══════════════════════════════════════════
   A picture at full quality takes minutes now that the background runner holds the clock, so this
   is looked at rather than glimpsed - and what is looked at for minutes has to be quiet.

   The first attempt was a six-bladed aperture spinning over a glow, with three dots bouncing
   underneath. Read back as toy-like, and rightly: fast rotation, a symmetrical flower and
   bouncing dots are the vocabulary of a game loading screen. Restraint is what separates this
   from that, so the whole thing is now three elements, none of them fast:

     · a light source drifting behind the plate, too slow to watch (11s)
     · ONE fine line travelling down the frame, the way a plate develops (4.2s)
     · a hairline progress bar - indeterminate, because we genuinely do not know

   Nothing rotates. Nothing bounces. Nothing shares a clock with anything else, so the composition
   never pulses in unison the way a progress bar pretends to. */
.imgload {
  position: relative;
  display: flex; align-items: flex-end; justify-content: center;
  inline-size: 100%; block-size: 100%;
  overflow: hidden;
  border-radius: inherit;
}

/* The light behind the plate. Two wide fields, heavily blurred, drifting out of step with each
   other so the frame is never evenly lit and never obviously repeating. */
.imgload__aura {
  position: absolute; inset: -30%;
  background:
    radial-gradient(42% 42% at 34% 30%, color-mix(in srgb, var(--color-accent) 20%, transparent), transparent 72%),
    radial-gradient(34% 34% at 70% 68%, color-mix(in srgb, var(--color-accent-deep, var(--color-accent)) 15%, transparent), transparent 74%);
  filter: blur(34px);
  opacity: 0.6;
  animation: imgloadAura 11s var(--ease-inout) infinite;
}
@keyframes imgloadAura {
  0%, 100% { transform: translate3d(-3%, -2%, 0) scale(1); }
  50%      { transform: translate3d(4%, 3%, 0) scale(1.08); }
}

/* The developing line. A single hairline with its own soft bloom, crossing the plate top to
   bottom and pausing at each end - the pause is what stops it reading as a machine part. */
.imgload__scan {
  position: absolute; inset-inline: 0; inset-block-start: 0;
  block-size: 1px;
  background: linear-gradient(90deg,
    transparent,
    color-mix(in srgb, var(--color-accent) 55%, transparent) 22%,
    color-mix(in srgb, var(--color-accent) 85%, transparent) 50%,
    color-mix(in srgb, var(--color-accent) 55%, transparent) 78%,
    transparent);
  box-shadow: 0 0 18px 2px color-mix(in srgb, var(--color-accent) 26%, transparent);
  animation: imgloadScan 4.2s var(--ease-inout) infinite;
}
/* Travels in PERCENT of the frame, not a fixed pixel distance. A transform percentage resolves
   against the element's own box, and this element is one pixel tall, so translateY(100%) would
   have moved it exactly one pixel; a fixed px travel instead stops partway down any frame taller
   than the guess. Offsetting the position resolves against the FRAME, which is the thing being
   crossed, so it reaches the bottom of a square, a banner and a portrait alike. */
@keyframes imgloadScan {
  0%       { inset-block-start: 0;    opacity: 0; }
  12%      { opacity: 0.85; }
  50%      { inset-block-start: 100%; opacity: 0.85; }
  62%      { opacity: 0; }
  100%     { inset-block-start: 100%; opacity: 0; }
}

/* Label and bar sit together at the foot of the plate rather than dead centre: centred, they
   compete with the light for the middle of the frame; at the foot they read as a caption. */
.imgload__meta {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin-block-end: 26px;
}
.imgload__label {
  font-family: var(--font-sans); font-size: 12px; font-weight: 500;
  letter-spacing: 0.06em;
  color: color-mix(in srgb, var(--color-text-secondary) 88%, transparent);
  animation: imgloadBreathe 3.4s var(--ease-inout) infinite;
}
[dir="rtl"] .imgload__label { font-family: var(--font-arabic); letter-spacing: 0; }
@keyframes imgloadBreathe { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }

/* Indeterminate on purpose: a filling bar would be a claim about how far along this is, and we
   do not know. A segment crossing a track says "running" without inventing a number. */
.imgload__bar {
  position: relative; display: block; overflow: hidden;
  inline-size: 108px; block-size: 2px; border-radius: 2px;
  background: color-mix(in srgb, var(--color-text-secondary) 16%, transparent);
}
.imgload__bar i {
  position: absolute; inset-block: 0; inset-inline-start: 0;
  inline-size: 42%; border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--color-accent), transparent);
  animation: imgloadTrack 1.9s var(--ease-inout) infinite;
}
/* translate, not inset-inline-start: a logical offset flips under RTL and the segment would
   travel out of its own track. A transform is direction-agnostic. */
@keyframes imgloadTrack {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(275%); }
}

/* A failed render keeps the frame and the message and stops every moving part - motion after a
   failure reads as "still trying", which is a lie. */
.image-card.is-error .imgload__scan,
.image-card.is-error .imgload__aura,
.image-card.is-error .imgload__bar { display: none; }
.image-card.is-error .imgload__label {
  animation: none; opacity: 1;
  color: var(--color-danger, var(--color-error));
}

@media (prefers-reduced-motion: reduce) {
  .imgload__aura, .imgload__scan, .imgload__label, .imgload__bar i { animation: none; }
  .imgload__scan { opacity: 0.5; }
  .imgload__label { opacity: 0.9; }
  .imgload__bar i { inline-size: 100%; opacity: 0.5; }
}
.image-card__img {
  display: block; position: absolute; inset: 0;
  inline-size: 100%; block-size: 100%; object-fit: cover;
  opacity: 0; transition: opacity var(--dur-base) var(--ease-out);
}
.image-card.is-done .image-card__frame { aspect-ratio: auto; }
.image-card.is-done .image-card__img { position: relative; block-size: auto; opacity: 1; }
.image-card__bar {
  display: flex; align-items: center; gap: var(--space-2);
  margin-block-start: var(--space-2);
}
.image-card__cap {
  flex: 1 1 auto; min-inline-size: 0;
  color: var(--color-text-muted); font-size: var(--text-xs);
  font-family: var(--font-sans);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
[dir="rtl"] .image-card__cap { font-family: var(--font-arabic); }
.image-card__dl {
  display: inline-flex; align-items: center; gap: var(--space-1); flex: 0 0 auto;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-accent); color: var(--color-on-accent);
  font-family: var(--font-sans); font-size: var(--text-sm); font-weight: var(--weight-medium);
  cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .image-card__dl { font-family: var(--font-arabic); }
.image-card__dl[hidden] { display: none; }
.image-card__dl:hover { background: var(--color-accent-hover); }
.image-card__dl svg { inline-size: 15px; block-size: 15px; }

/* ---- Code deliverable card (live editor window) — always dark, editor-style ---- */
.code-card {
  margin-block: var(--space-3) var(--space-1);
  inline-size: 100%;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: var(--border-hairline) solid rgba(255, 255, 255, 0.08);
  background: #0d1117;
  box-shadow: var(--shadow-sm);
  font-family: var(--font-sans);
}
.code-card__head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 8px 12px;
  background: #161b22;
  border-block-end: 1px solid rgba(255, 255, 255, 0.06);
}
.code-card__dots { display: inline-flex; gap: 6px; flex: 0 0 auto; }
.code-card__dots i { inline-size: 11px; block-size: 11px; border-radius: 50%; display: block; }
.code-card__dots i:nth-child(1) { background: #ff5f56; }
.code-card__dots i:nth-child(2) { background: #ffbd2e; }
.code-card__dots i:nth-child(3) { background: #27c93f; }
.code-card__name {
  color: #e6edf3; font-size: var(--text-xs); font-weight: var(--weight-semibold);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-inline-size: 38%;
}
.code-card__lang {
  flex: 0 0 auto; font-size: 10px; font-weight: var(--weight-semibold);
  color: #9da7b3; background: rgba(255, 255, 255, 0.07);
  padding: 2px 7px; border-radius: var(--radius-pill); letter-spacing: 0.04em;
}
.code-card__count { flex: 0 0 auto; color: #6e7681; font-size: 11px; }
.code-card__spacer { flex: 1 1 auto; }
.code-card__actions { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.code-card__writing { display: inline-flex; align-items: center; gap: 6px; color: #7d8590; font-size: 11px; }
.code-card__writing::before {
  content: ""; inline-size: 7px; block-size: 7px; border-radius: 50%;
  background: #27c93f; animation: codePulse 1.1s ease-in-out infinite;
}
@keyframes codePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(39, 201, 63, 0.5); }
  50% { box-shadow: 0 0 0 5px rgba(39, 201, 63, 0); }
}
.code-card__btn {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 9px; border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.06); color: #c9d1d9;
  font-size: 11px; font-weight: var(--weight-medium); cursor: pointer;
  border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.code-card__btn:hover { background: rgba(255, 255, 255, 0.12); border-color: rgba(255, 255, 255, 0.12); }
.code-card__btn svg { inline-size: 13px; block-size: 13px; }
.code-card__btn span { white-space: nowrap; }
/* Continue bar under the code — discoverable where the eye lands when code cuts off. */
.code-card__foot {
  display: flex; align-items: center; justify-content: flex-end; gap: 10px;
  padding: 7px 12px; background: #161b22;
  border-block-start: 1px solid rgba(255, 255, 255, 0.06);
}
.code-card__foot[hidden] { display: none; }
.code-card__foot-hint { color: #7d8590; font-size: 11.5px; }
.code-card__btn.code-card__contbtn {
  background: rgba(63, 185, 80, 0.16); color: #5dd77a;
  border-color: rgba(63, 185, 80, 0.35); font-weight: var(--weight-semibold);
}
.code-card__btn.code-card__contbtn:hover { background: rgba(63, 185, 80, 0.26); border-color: rgba(63, 185, 80, 0.55); }
.code-card__body {
  max-block-size: 460px; overflow: auto; background: #0d1117;
  scrollbar-width: thin; scrollbar-color: #30363d transparent;
}
.code-card__body::-webkit-scrollbar { inline-size: 10px; block-size: 10px; }
.code-card__body::-webkit-scrollbar-thumb { background: #30363d; border-radius: 6px; }
.code-card__body pre { margin: 0; padding: 14px 16px; }
.code-card__code {
  font-family: var(--font-mono, ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace);
  font-size: 12.5px; line-height: 1.65; color: #e6edf3;
  white-space: pre; tab-size: 2; display: block;
}
.code-card.is-streaming .code-card__code::after {
  content: "▋"; color: #58a6ff; animation: codeCaret 1s steps(1) infinite;
}
@keyframes codeCaret { 50% { opacity: 0; } }

@media (max-width: 560px) {
  .file-card { max-inline-size: 100%; gap: var(--space-2); padding: var(--space-2) var(--space-3); }
  .file-card__icon { inline-size: 36px; block-size: 36px; }
  .file-card__dl span { display: none; }
  .file-card__dl { padding: var(--space-2); border-radius: var(--radius-pill); }
}

/* Code-block head can now host Copy + Preview side by side */
.code-block__head { gap: var(--space-2); }
.code-block__head .code-block__lang { margin-inline-end: auto; }
.code-preview-btn { color: var(--color-accent); }
.code-preview-btn:hover { color: var(--color-accent-hover); background: var(--color-accent-soft); }
.code-preview-btn svg { inline-size: 14px; block-size: 14px; }

/* ----------------------------------------------------------------------------
   15. Live HTML preview modal (sandboxed iframe)
---------------------------------------------------------------------------- */
.preview-overlay {
  position: fixed; inset: 0; z-index: var(--z-toast);
  display: grid; place-items: center;
  /* No padding: the panel below fills the viewport edge to edge. The backdrop colour stays for
     the fade-in, and for the brief moment before the panel paints. */
  padding: 0;
  background: rgba(0,0,0,0.46);
  opacity: 0; transition: opacity var(--dur-base) var(--ease-out);
}
.preview-overlay.is-open { opacity: 1; }
.preview-panel {
  /* GENUINELY FULL SCREEN. This was capped at 1040px wide and 86dvh tall, so "preview" showed a
     1040-pixel letterbox of a page that was designed for a real viewport — a responsive layout
     would even resolve to its TABLET breakpoint inside it and misrepresent the work. The header
     bar keeps close / refresh / open-in-tab / download reachable, and Escape still closes. */
  inline-size: 100%; max-inline-size: none;
  block-size: 100dvh;
  display: flex; flex-direction: column;
  background: var(--color-surface);
  border: 0;
  border-radius: 0;
  overflow: hidden;
  transform: translateY(8px) scale(0.99);
  transition: transform var(--dur-base) var(--ease-out);
}
.preview-overlay.is-open .preview-panel { transform: none; }
.preview-panel__head {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  border-block-end: var(--border-hairline) solid var(--color-border-hairline);
  background: var(--color-sidebar-bg);
}
.preview-panel__title {
  display: inline-flex; align-items: center; gap: var(--space-2);
  margin-inline-end: auto;
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  color: var(--color-text-primary);
}
[dir="rtl"] .preview-panel__title { font-family: var(--font-arabic); }
.preview-panel__title svg { inline-size: 17px; block-size: 17px; color: var(--color-accent); }
.preview-panel__tools { display: inline-flex; align-items: center; gap: 2px; }
.preview-tool {
  display: grid; place-items: center;
  inline-size: 34px; block-size: 34px;
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  transition: background var(--dur-fast), color var(--dur-fast);
}
.preview-tool:hover { background: var(--color-bg-subtle); color: var(--color-text-primary); }
.preview-tool svg { inline-size: 17px; block-size: 17px; }
.preview-tool--close:hover { background: var(--color-accent-soft); color: var(--color-accent); }
.preview-frame {
  flex: 1; inline-size: 100%; border: none;
  background: #fff;
  display: block;
}

@media (max-width: 768px) {
  .preview-overlay { padding: var(--space-2); }
  .preview-panel { block-size: 92dvh; max-inline-size: 100%; border-radius: var(--radius-lg); }
  .export-menu__list { min-inline-size: 176px; }
}

/* Utility */
.sr-only {
  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;
}
.hidden { display: none !important; }
[hidden] { display: none !important; }

/* ===== Persistent memory viewer ===== */
.mem-overlay{position:fixed;inset:0;z-index:1200;display:flex;align-items:center;justify-content:center;padding:20px;background:rgba(0,0,0,.55);opacity:0;transition:opacity var(--dur-fast,.18s) var(--ease-out,ease)}
.mem-overlay.is-open{opacity:1}
.mem-card{width:min(540px,100%);max-height:82vh;display:flex;flex-direction:column;background:var(--color-surface);color:var(--color-text-primary);border:1px solid var(--color-border-strong);border-radius:var(--radius-lg);box-shadow:0 24px 64px rgba(0,0,0,.45);transform:translateY(10px) scale(.98);transition:transform var(--dur-fast,.18s) var(--ease-out,ease)}
.mem-overlay.is-open .mem-card{transform:none}
.mem-head{display:flex;align-items:flex-start;gap:10px;padding:18px 20px;border-bottom:1px solid var(--color-border-hairline)}
.mem-head h3{margin:0;font:600 17px/1.3 var(--font-sans)}
.mem-head p{margin:3px 0 0;font:400 13px/1.45 var(--font-sans);color:var(--color-text-secondary)}
.mem-x{background:none;border:none;color:var(--color-text-muted);cursor:pointer;padding:6px;border-radius:8px;display:flex;flex:0 0 auto}
.mem-x:hover{background:var(--color-bg-subtle);color:var(--color-text-primary)}
.mem-list{list-style:none;margin:0;padding:10px 12px;overflow:auto;flex:1}
.mem-item{display:flex;align-items:center;gap:10px;padding:9px 12px;border-radius:var(--radius-md)}
.mem-item:hover{background:var(--color-bg-subtle)}
.mem-item span{flex:1;font:400 14px/1.45 var(--font-sans)}
.mem-del{background:none;border:none;color:var(--color-text-muted);cursor:pointer;font-size:20px;line-height:1;padding:0 6px;border-radius:6px;flex:0 0 auto}
.mem-del:hover{color:var(--color-error);background:var(--color-bg-subtle)}
.mem-empty{padding:36px 22px;text-align:center;color:var(--color-text-secondary);font:400 14px/1.55 var(--font-sans)}
.mem-foot{padding:13px 20px;border-top:1px solid var(--color-border-hairline);display:flex;justify-content:space-between;align-items:center;gap:10px}
.mem-foot small{color:var(--color-text-muted);font:400 12px var(--font-sans)}
.mem-clear{background:none;border:1px solid var(--color-border-strong);color:var(--color-text-secondary);padding:7px 14px;border-radius:var(--radius-pill);cursor:pointer;font:500 13px var(--font-sans)}
.mem-clear:hover{border-color:var(--color-error);color:var(--color-error)}

/* ===== Notifications bell + site-updates panel ===== */
.topbar__notify{position:relative}
/* Unread updates → a single GREEN dot (site accent) on the bell, not a count. */
.notify-badge{position:absolute;top:-2px;inset-inline-end:-2px;width:9px;height:9px;padding:0;border-radius:50%;background:var(--color-accent);box-shadow:0 0 0 2px var(--color-surface),0 0 8px var(--color-accent);animation:annBadgePop .3s var(--ease-out,ease)}
@keyframes annBadgePop{from{transform:scale(.3);opacity:0}to{transform:scale(1);opacity:1}}

.ann-card{width:min(580px,100%);max-height:86vh}
.ann-card .mem-head{background:linear-gradient(180deg,var(--color-accent-soft),transparent)}
.ann-list{padding:14px 16px;display:flex;flex-direction:column;gap:10px;overscroll-behavior:contain}
.ann-item{display:flex;align-items:center;gap:12px;cursor:pointer;padding:12px 14px;border-radius:var(--radius-lg);border:1px solid var(--color-border-hairline);background:var(--color-surface);box-shadow:var(--shadow-sm,0 1px 2px rgba(0,0,0,.05));transition:box-shadow .18s var(--ease-out,ease),transform .18s var(--ease-out,ease),border-color .18s}
.ann-item:hover{box-shadow:0 8px 24px rgba(0,0,0,.12);transform:translateY(-1px);border-color:var(--color-border-strong)}
.ann-item:focus-visible{outline:none;box-shadow:var(--shadow-focus)}
.ann-item-thumb{inline-size:54px;block-size:54px;flex:0 0 auto;object-fit:cover;border-radius:var(--radius-md);background:var(--color-bg-subtle)}
.ann-item-main{flex:1;min-inline-size:0;display:flex;flex-direction:column;gap:3px}
.ann-item-title{margin:0;font:700 15px/1.35 var(--font-sans);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ann-item-body{margin:0;font:400 13px/1.55 var(--font-sans);color:var(--color-text-secondary);display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
.ann-item-body:empty{display:none}
.ann-item-date{font:500 11px var(--font-sans);color:var(--color-text-muted);letter-spacing:.02em}
.ann-item-go{flex:0 0 auto;color:var(--color-text-muted);display:inline-flex}
[dir=rtl] .ann-item-go svg{transform:scaleX(-1)}

/* Full-screen update reader */
.ann-reader-overlay{z-index:1300;align-items:center;justify-content:center;padding:22px}
.ann-reader{display:flex;flex-direction:column;width:min(880px,94vw);max-height:92vh;background:var(--color-surface);border:1px solid var(--color-border-strong);border-radius:var(--radius-lg);box-shadow:0 24px 64px rgba(0,0,0,.5);overflow:hidden;transform:translateY(10px) scale(.985);transition:transform var(--dur-fast,.18s) var(--ease-out,ease)}
.ann-reader-overlay.is-open .ann-reader{transform:none}
.ann-reader-bar{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 12px;border-bottom:1px solid var(--color-border-hairline);background:var(--color-bg-subtle)}
.ann-langs{display:inline-flex;gap:3px;background:var(--color-surface);border:1px solid var(--color-border-hairline);border-radius:var(--radius-pill);padding:3px}
.ann-lang{background:none;border:none;cursor:pointer;border-radius:var(--radius-pill);padding:0 13px;min-block-size:32px;font:600 12.5px var(--font-sans);color:var(--color-text-secondary);transition:.15s}
.ann-lang:hover{color:var(--color-text-primary)}
.ann-lang.is-on{background:var(--color-accent);color:var(--color-on-accent)}
.ann-lang:disabled{opacity:.6;cursor:default}
.ann-reader-actions{display:inline-flex;gap:4px;align-items:center}
.ann-icon-btn{display:inline-grid;place-items:center;inline-size:38px;block-size:38px;border:none;background:none;color:var(--color-text-muted);border-radius:var(--radius-sm);cursor:pointer;transition:.15s}
.ann-icon-btn:hover{background:var(--color-surface);color:var(--color-text-primary)}
.ann-reader-del:hover{color:var(--color-error)}
.ann-reader-body{padding:20px 26px 28px;overflow:auto;overscroll-behavior:contain}
.ann-reader-img{display:block;max-width:100%;border-radius:var(--radius-md);margin:0 0 16px;cursor:zoom-in;box-shadow:0 4px 18px rgba(0,0,0,.18)}
.ann-reader-title{margin:0 0 6px;font:700 22px/1.4 var(--font-sans);color:var(--color-text-primary);overflow-wrap:anywhere}
.ann-reader-title[hidden]{display:none}
.ann-reader-date{display:block;margin:0 0 18px;font:500 12px var(--font-sans);color:var(--color-text-muted)}
.ann-reader-text{font:400 15.5px/1.85 var(--font-sans);color:var(--color-text-primary);white-space:pre-wrap;overflow-wrap:anywhere}

/* Admin edit modal */
.ann-edit-overlay{z-index:1350}
.ann-edit-card{width:min(560px,100%);max-height:88vh}
.ann-edit-form{margin:14px 16px 16px}
.ann-img-remove{background:none;border:1px solid var(--color-border-strong);color:var(--color-text-secondary);border-radius:var(--radius-pill);padding:7px 14px;font:600 12.5px var(--font-sans);cursor:pointer;transition:.15s}
.ann-img-remove:hover{border-color:var(--color-error);color:var(--color-error)}
.ann-img-remove[hidden]{display:none}

.ann-form{display:flex;flex-direction:column;gap:9px;padding:16px;margin:12px 16px 2px;background:var(--color-bg-subtle);border:1px solid var(--color-border-hairline);border-radius:var(--radius-lg)}
.ann-in{width:100%;background:var(--color-surface);color:var(--color-text-primary);border:1px solid var(--color-border-strong);border-radius:var(--radius-md);padding:9px 12px;font:400 14px/1.5 var(--font-sans);resize:vertical;transition:border-color .15s,box-shadow .15s}
.ann-in:focus{outline:none;border-color:var(--color-accent);box-shadow:var(--shadow-focus)}
.ann-form-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.ann-img-btn{cursor:pointer;display:inline-flex;align-items:center;gap:6px;border:1px solid var(--color-border-strong);border-radius:var(--radius-pill);padding:7px 14px;font:600 12.5px var(--font-sans);color:var(--color-text-secondary);flex:0 0 auto;transition:.15s}
.ann-img-btn:hover{border-color:var(--color-accent);color:var(--color-accent)}
.ann-img-name{flex:1;min-width:0;font:400 12px var(--font-sans);color:var(--color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ann-post{margin-inline-start:auto;background:var(--color-accent);color:#fff;border:none;border-radius:var(--radius-pill);padding:8px 20px;font:700 13px var(--font-sans);cursor:pointer;flex:0 0 auto;transition:.15s}
.ann-post:hover{background:var(--color-accent-hover)}
.ann-post:disabled{opacity:.55;cursor:default}
.ann-img-preview{max-width:100%;border-radius:var(--radius-md);margin-top:4px}

/* Mobile: the updates panel becomes a bottom sheet; the reader goes truly full-screen */
@media (max-width:560px){
  .ann-overlay{align-items:flex-end;padding:0}
  .ann-card{width:100%;max-height:90vh;border-radius:var(--radius-lg) var(--radius-lg) 0 0;transform:translateY(18px)}
  .ann-overlay.is-open .ann-card{transform:none}
  .ann-form{margin:12px 12px 2px}
  .ann-list{padding:12px}
  .ann-reader-overlay{padding:0;align-items:stretch}
  .ann-reader{width:100%;max-height:100vh;min-height:100vh;border-radius:0;border:none}
  .ann-reader-body{padding:18px 18px 30px}
  .ann-reader-title{font-size:19px}
  .ann-edit-overlay{align-items:flex-end;padding:0}
  .ann-edit-card{width:100%;max-height:92vh;border-radius:var(--radius-lg) var(--radius-lg) 0 0}
}

/* Account settings panel */
.settings-card{width:min(470px,100%);max-height:88vh}
.set-body{padding:16px 18px 20px;overflow:auto;display:flex;flex-direction:column;gap:14px}
/* account hero */
.set-hero{display:flex;align-items:center;gap:14px;padding:16px 16px;border-radius:var(--radius-lg);background:linear-gradient(150deg,var(--color-accent-soft),transparent 70%),var(--color-bg-subtle);border:1px solid var(--color-border-hairline)}
.set-avatar{inline-size:48px;block-size:48px;border-radius:var(--radius-pill);display:grid;place-items:center;flex:0 0 auto;background:var(--color-accent);color:var(--color-on-accent);font:700 20px var(--font-sans);box-shadow:0 4px 14px var(--color-accent-ring)}
.set-hero-info{display:flex;flex-direction:column;min-width:0;gap:1px}
.set-hero-eyebrow{font:600 10.5px/1 var(--font-sans);letter-spacing:.08em;text-transform:uppercase;color:var(--color-text-muted)}
.set-acct-name{font:600 16px var(--font-sans);color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:2px}
.set-acct-email{font:400 13px var(--font-sans);color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* action cards */
.set-card{display:flex;flex-direction:column;gap:10px;padding:16px;border:1px solid var(--color-border-hairline);border-radius:var(--radius-lg);background:var(--color-surface)}
.set-card-h{display:flex;align-items:center;gap:8px;font:600 14px/1.3 var(--font-sans);color:var(--color-text-primary)}
.set-ico{display:inline-grid;place-items:center;inline-size:26px;block-size:26px;border-radius:8px;background:var(--color-accent-soft);color:var(--color-accent);flex:0 0 auto}
/* appearance: light/dark segmented control */
.set-theme-seg{display:flex;gap:6px;padding:4px;background:var(--color-bg-subtle);border:1px solid var(--color-border-hairline);border-radius:var(--radius-pill)}
.set-theme-opt{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:7px;padding:9px 14px;border-radius:var(--radius-pill);background:transparent;color:var(--color-text-secondary);font:500 13.5px var(--font-sans);transition:background .15s,color .15s,box-shadow .15s}
[dir="rtl"] .set-theme-opt{font-family:var(--font-arabic)}
.set-theme-opt:hover{color:var(--color-text-primary)}
.set-theme-opt.is-active{background:var(--color-surface);color:var(--color-text-primary);box-shadow:var(--shadow-xs);font-weight:600}
.set-theme-opt.is-active svg{color:var(--color-accent)}
.set-field{display:flex;flex-direction:column;gap:5px}
.set-lbl{font:500 12px/1.3 var(--font-sans);color:var(--color-text-secondary)}
.set-lbl-hint{color:var(--color-text-muted);font-weight:400}
.set-in{width:100%;background:var(--color-bg-subtle);color:var(--color-text-primary);border:1px solid var(--color-border-strong);border-radius:var(--radius-md);padding:10px 12px;font:400 14px/1.4 var(--font-sans);transition:border-color .15s,box-shadow .15s}
.set-in:focus{outline:none;border-color:var(--color-accent);box-shadow:var(--shadow-focus);background:var(--color-surface)}
.set-save{align-self:flex-start;margin-top:2px;background:var(--color-accent);color:var(--color-on-accent);border:none;border-radius:var(--radius-pill);padding:10px 22px;font:600 13px var(--font-sans);cursor:pointer;transition:.15s}
.set-save:hover{background:var(--color-accent-hover)}
.set-save:disabled{opacity:.55;cursor:default}
.set-err{font:500 12.5px/1.5 var(--font-sans);color:var(--color-error);background:color-mix(in srgb,var(--color-error) 13%,transparent);border-radius:var(--radius-sm);padding:8px 11px}
.set-danger{border-color:color-mix(in srgb,var(--color-error) 35%,var(--color-border-hairline));background:color-mix(in srgb,var(--color-error) 6%,var(--color-surface))}
.set-danger-h{color:var(--color-error)}
.set-danger-h .set-ico{background:color-mix(in srgb,var(--color-error) 14%,transparent);color:var(--color-error)}
.set-danger-note{margin:0;font:400 13px/1.65 var(--font-sans);color:var(--color-text-secondary)}
.set-del-btn,.set-del-final{align-self:flex-start;background:var(--color-error);color:#fff;border:none;border-radius:var(--radius-pill);padding:10px 20px;font:600 13px var(--font-sans);cursor:pointer;transition:.15s}
.set-del-btn:hover,.set-del-final:hover{filter:brightness(1.08)}
.set-del-confirm{display:flex;flex-direction:column;gap:10px;margin-top:4px;padding-top:12px;border-top:1px solid color-mix(in srgb,var(--color-error) 22%,transparent)}
.set-del-row{display:flex;gap:9px;flex-wrap:wrap}
.set-del-cancel{background:var(--color-surface);color:var(--color-text-primary);border:1px solid var(--color-border-strong);border-radius:var(--radius-pill);padding:10px 20px;font:600 13px var(--font-sans);cursor:pointer}
.set-del-cancel:hover{background:var(--color-bg-subtle)}
/* Expanded settings: side-by-side actions, secondary (outline) buttons, about row */
.set-btn-row{display:flex;gap:8px;margin-top:2px;flex-wrap:wrap}
.set-btn-row .set-save{flex:1;min-width:130px;align-self:stretch;text-align:center}
.set-save.set-secondary{background:transparent;color:var(--color-text-primary);border:1px solid var(--color-border-strong)}
.set-save.set-secondary:hover{background:var(--color-bg-subtle);border-color:var(--color-accent);color:var(--color-accent)}
.set-about-row{display:flex;align-items:center;justify-content:space-between;gap:8px;font:400 13px var(--font-sans);color:var(--color-text-secondary)}
.set-ver{color:var(--color-text-primary);font-weight:600;font-variant-numeric:tabular-nums;direction:ltr}
.set-ver.skeleton{display:inline-block;block-size:12px}

/* ─── Subscriptions / redeem / plans / admin codes (theme-token based) ─── */
:root{ --plan-gold:#B8862A; --plan-diamond:#3E7CB1; }
[data-theme="dark"]{ --plan-gold:#D8B45A; --plan-diamond:#8FB4E0; }
.sub-head{display:flex;flex-wrap:wrap;align-items:center;gap:8px;justify-content:space-between}
.sub-chip{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:var(--radius-pill);font:600 13px var(--font-sans);border:1px solid var(--color-border-strong);color:var(--color-text-primary);background:var(--color-bg-subtle)}
.sub-chip--gold{color:var(--plan-gold);border-color:color-mix(in srgb,var(--plan-gold) 55%,transparent);background:color-mix(in srgb,var(--plan-gold) 12%,transparent)}
.sub-chip--diamond,.sub-chip--unlimited{color:var(--plan-diamond);border-color:color-mix(in srgb,var(--plan-diamond) 55%,transparent);background:color-mix(in srgb,var(--plan-diamond) 12%,transparent)}
.sub-exp{margin:0;font-size:var(--text-xs);color:var(--color-text-muted)}
.sub-meters{display:grid;gap:10px;margin-top:2px}
.sub-meter__top{display:flex;justify-content:space-between;font-size:var(--text-xs);color:var(--color-text-secondary);margin-bottom:4px}
.sub-meter__val{font-variant-numeric:tabular-nums;color:var(--color-text-primary);font-weight:600}
.sub-meter__val.is-inf{color:var(--color-accent);font-size:15px}
.sub-meter__bar{block-size:6px;border-radius:var(--radius-pill);background:var(--color-bg-subtle);overflow:hidden}
.sub-meter__bar span{display:block;block-size:100%;background:var(--color-accent);border-radius:var(--radius-pill);transition:width var(--dur-slow) var(--ease-out)}
.sub-meter__bar span.is-full{background:var(--color-error)}

.redeem-body{display:flex;flex-direction:column;gap:12px;padding:4px 2px}
.redeem-in{width:100%;text-align:center;letter-spacing:.12em;text-transform:uppercase;font:600 16px var(--font-mono);padding:14px;border:1.5px solid var(--color-border-strong);border-radius:var(--radius-md);background:var(--color-bg-subtle);color:var(--color-text-primary)}
.redeem-in:focus{outline:none;border-color:var(--color-accent);box-shadow:var(--shadow-focus);background:var(--color-surface)}
.redeem-in.hidden,.redeem-go.hidden{display:none}
.redeem-msg{font:500 13px var(--font-sans);color:var(--color-error);background:color-mix(in srgb,var(--color-error) 12%,transparent);border-radius:var(--radius-sm);padding:9px 12px;text-align:center}
.redeem-go{align-self:stretch;text-align:center}
.redeem-ok{display:flex;flex-direction:column;align-items:center;gap:6px;text-align:center;padding:12px;border-radius:var(--radius-md);background:color-mix(in srgb,var(--color-success) 10%,transparent);animation:msg-in var(--dur-slow) var(--ease-out)}
.redeem-ok__badge{font-size:34px}
.redeem-ok strong{font-family:var(--font-serif);font-size:var(--text-md);color:var(--color-text-primary)}
.redeem-ok span{font-size:var(--text-sm);color:var(--color-text-secondary)}

.plans-card{max-inline-size:900px;width:min(92vw,900px)}
/* P0 — the subscription funnel was dead on every phone.
   This grid is a DIRECT child of .mem-card, which is a flex column capped at 82dvh, inside a
   position:fixed overlay with no overflow — and openPlansModal() calls lockBodyScroll(), so
   the page cannot scroll either. Three stacked plan cards measure ~1250px inside a ~692px
   box, so ~560px spilled out of the bottom with no gesture able to reach it: the Gold and
   Diamond "I have a code" buttons — the only way to redeem a subscription — rendered
   off-screen and untappable.

   Every sibling modal body in this file is already a scroller (.mem-list, .set-body,
   .ac-list); this grid was the one that was not. `min-block-size: 0` is required, not
   decorative: as a flex item its default `min-height: auto` keeps it at content size and
   would defeat the scroller entirely. */
.plans-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;padding:4px;
  overflow:auto;
  flex:1 1 auto;
  min-block-size:0;
  overscroll-behavior:contain;
}
.plan-card{position:relative;display:flex;flex-direction:column;gap:10px;padding:20px 18px;border:1.5px solid var(--color-border-hairline);border-radius:var(--radius-lg);background:var(--color-surface);text-align:center}
.plan-card--gold{border-color:color-mix(in srgb,var(--plan-gold) 40%,var(--color-border-hairline))}
.plan-card--diamond{border-color:color-mix(in srgb,var(--plan-diamond) 40%,var(--color-border-hairline))}
.plan-card.is-current{box-shadow:0 0 0 2px var(--color-accent) inset}
.plan-current{position:absolute;inset-block-start:-11px;inset-inline-start:50%;transform:translateX(-50%);background:var(--color-accent);color:var(--color-on-accent);font:600 11px var(--font-sans);padding:3px 12px;border-radius:var(--radius-pill);white-space:nowrap}
.plan-ic{font-size:30px}
.plan-card h4{font-family:var(--font-serif);font-size:var(--text-lg)}
.plan-price{font-size:var(--text-sm);color:var(--color-text-muted)}
.plan-feats{list-style:none;padding:0;margin:6px 0;display:grid;gap:8px;text-align:start}
.plan-feats li{display:flex;align-items:flex-start;gap:8px;font-size:var(--text-sm);color:var(--color-text-secondary)}
.plan-feats li svg{color:var(--color-accent);flex:0 0 auto;margin-top:3px}
[dir="rtl"] .plan-feats li{font-family:var(--font-arabic)}
.plan-cta{margin-top:auto;padding:10px;border-radius:var(--radius-pill);font:600 13px var(--font-sans);background:var(--color-accent);color:var(--color-on-accent);transition:background var(--dur-fast) var(--ease-out)}
.plan-cta:hover{background:var(--color-accent-hover)}
.plan-cta--muted{background:var(--color-bg-subtle);color:var(--color-text-muted);cursor:default}

.admincodes-card{max-inline-size:820px;width:min(94vw,820px)}
.ac-body{display:flex;flex-direction:column;gap:14px}
.ac-create{display:flex;flex-direction:column;gap:10px;padding:14px;border:1px solid var(--color-border-hairline);border-radius:var(--radius-lg);background:var(--color-bg-subtle)}
.ac-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}
.ac-fld{display:flex;flex-direction:column;gap:4px;font:500 12px var(--font-sans);color:var(--color-text-secondary)}
.ac-in{width:100%;padding:9px 11px;border:1px solid var(--color-border-strong);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text-primary);font:400 13.5px var(--font-sans)}
.ac-in:focus{outline:none;border-color:var(--color-accent);box-shadow:var(--shadow-focus)}
.ac-create-btn{align-self:flex-start}
.ac-search-row{display:flex}
.ac-list{display:flex;flex-direction:column;gap:8px;max-block-size:46vh;overflow:auto}
.ac-row{display:flex;flex-direction:column;gap:6px;padding:11px 13px;border:1px solid var(--color-border-hairline);border-radius:var(--radius-md);background:var(--color-surface)}
.ac-row--disabled,.ac-row--expired,.ac-row--used-up{opacity:.62}
.ac-row-main{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.ac-code{font:600 14px var(--font-mono);letter-spacing:.04em;background:var(--color-bg-subtle);padding:3px 9px;border-radius:var(--radius-sm);cursor:pointer;color:var(--color-text-primary)}
.ac-badge{font:600 11px var(--font-sans);text-transform:uppercase;letter-spacing:.04em;padding:2px 9px;border-radius:var(--radius-pill);background:var(--color-bg-subtle);color:var(--color-text-secondary)}
.ac-badge--gold{color:var(--plan-gold);background:color-mix(in srgb,var(--plan-gold) 13%,transparent)}
.ac-badge--diamond,.ac-badge--unlimited{color:var(--plan-diamond);background:color-mix(in srgb,var(--plan-diamond) 13%,transparent)}
.ac-status{margin-inline-start:auto;font:600 11px var(--font-sans);padding:2px 9px;border-radius:var(--radius-pill)}
.ac-status--active{color:var(--color-success);background:color-mix(in srgb,var(--color-success) 13%,transparent)}
.ac-status--disabled,.ac-status--expired,.ac-status--used-up{color:var(--color-text-muted);background:var(--color-bg-subtle)}
.ac-row-meta{display:flex;flex-wrap:wrap;gap:5px 14px;font-size:var(--text-xs);color:var(--color-text-muted)}
.ac-note-tag{color:var(--color-text-secondary)}
.ac-row-acts{display:flex;gap:7px;flex-wrap:wrap}
.ac-act{font:600 12px var(--font-sans);padding:5px 12px;border-radius:var(--radius-pill);border:1px solid var(--color-border-strong);background:transparent;color:var(--color-text-primary);transition:border-color var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out)}
.ac-act:hover{border-color:var(--color-accent);color:var(--color-accent)}
.ac-del:hover{border-color:var(--color-error);color:var(--color-error)}
@media (max-width:560px){
  .settings-overlay{align-items:flex-end;padding:0}
  .settings-card{width:100%;max-height:92vh;border-radius:var(--radius-lg) var(--radius-lg) 0 0;transform:translateY(18px)}
  .settings-overlay.is-open .settings-card{transform:none}
  .set-body{padding:14px}
}

/* Pinned conversations */
.chat-item__pin{opacity:0;transition:opacity .15s,color .15s}
.chat-item:hover .chat-item__pin,.chat-item.is-pinned .chat-item__pin{opacity:1}
.chat-item__pin.is-on{color:var(--color-accent)}
.history__group-label--pinned{display:flex;align-items:center;gap:5px;color:var(--color-accent)}
.history__group-label--pinned .pin-dot{display:inline-flex}
.history__group-label--pinned .pin-dot svg{width:12px;height:12px}

/* ===== Document attachments (PDF / code / text) ===== */
.attach-file{position:relative;display:flex;align-items:center;gap:8px;max-width:210px;padding-block:8px;padding-inline-start:9px;padding-inline-end:30px;background:var(--color-bg-subtle);border:1px solid var(--color-border-hairline);border-radius:var(--radius-md)}
.attach-file.is-loading{opacity:.6}
/* Only part of this file fitted in the request budget. The chip carries the full explanation
   in its title; this is the mark that makes the user look. --color-error is defined in both
   the light and dark palettes, so one rule covers both themes. */
.attach-file.is-cut .attach-file__icon{background:var(--color-error)}
.attach-file.is-cut::after{content:"…";position:absolute;inset-block-end:2px;inset-inline-end:22px;font:700 11px var(--font-mono);color:var(--color-error);line-height:1}
.attach-file__icon{font:700 9px/1 var(--font-mono);letter-spacing:.5px;color:var(--color-on-accent);background:var(--color-accent);padding:4px 5px;border-radius:5px;flex:0 0 auto}
.attach-file__name{font:500 12.5px var(--font-sans);color:var(--color-text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.msg-user__files{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}
.msg-file-chip{display:inline-flex;align-items:center;gap:6px;max-width:230px;padding:5px 11px;background:rgba(127,127,127,.18);border-radius:var(--radius-pill)}
.msg-file-chip__ic{font:700 9px/1 var(--font-mono);letter-spacing:.5px;opacity:.65}
.msg-file-chip__nm{font:500 12px var(--font-sans);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ═══ ⑥ In-browser deck presenter (animated slideshow) ═══ */
.file-card__present{display:inline-flex;align-items:center;gap:var(--space-1);flex:0 0 auto;margin-inline-end:var(--space-2);padding:var(--space-2) var(--space-3);border-radius:var(--radius-pill);background:transparent;border:1.5px solid var(--color-accent);color:var(--color-accent);font-family:var(--font-sans);font-size:var(--text-sm);font-weight:var(--weight-medium);white-space:nowrap;transition:background var(--dur-fast) var(--ease-out),color var(--dur-fast) var(--ease-out)}
[dir="rtl"] .file-card__present{font-family:var(--font-arabic)}
.file-card__present:hover{background:var(--color-accent);color:var(--color-on-accent)}
.deck-pres{position:fixed;inset:0;z-index:1200;background:#101014;display:flex;flex-direction:column;color:#fff}
.deck-pres__bar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;background:rgba(255,255,255,.04);border-bottom:1px solid rgba(255,255,255,.08)}
.deck-pres__name{font:600 14px var(--font-sans);opacity:.85;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
[dir="rtl"] .deck-pres__name{font-family:var(--font-arabic)}
.deck-pres__ctrls{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.deck-pres__btn{padding:6px 12px;border-radius:var(--radius-pill);background:rgba(255,255,255,.1);color:#fff;font:500 12.5px var(--font-sans);white-space:nowrap;transition:background .15s}
[dir="rtl"] .deck-pres__btn{font-family:var(--font-arabic)}
.deck-pres__btn:hover{background:rgba(255,255,255,.22)}
.deck-pres__btn--x{font-size:15px;padding:6px 11px}
.deck-pres__anim{padding:6px 10px;border-radius:var(--radius-pill);background:rgba(255,255,255,.1);color:#fff;border:none;font:500 12.5px var(--font-sans);cursor:pointer}
[dir="rtl"] .deck-pres__anim{font-family:var(--font-arabic)}
.deck-pres__anim option{color:#111}
.deck-pres__count{font:600 12.5px var(--font-mono);opacity:.75;letter-spacing:.06em}
.deck-pres__stage{flex:1;display:flex;align-items:center;justify-content:center;padding:22px;min-height:0;cursor:pointer}
.deck-pres__slide{width:min(96vw,1200px);aspect-ratio:16/9;max-height:100%;border-radius:14px;overflow:hidden;background:#fff;box-shadow:0 24px 80px rgba(0,0,0,.55)}
/* slide TRANSITIONS (switchable) */
.deck-pres__slide.dp-anim-slide.is-in{animation:dpSlideIn .5s var(--ease-out) both}
.deck-pres__slide.dp-anim-fade.is-in{animation:dpFadeIn .55s ease both}
.deck-pres__slide.dp-anim-zoom.is-in{animation:dpZoomIn .5s var(--ease-out) both}
@keyframes dpSlideIn{from{opacity:0;transform:translateX(46px)}to{opacity:1;transform:none}}
[dir="rtl"] .deck-pres__slide.dp-anim-slide.is-in{animation-name:dpSlideInR}
@keyframes dpSlideInR{from{opacity:0;transform:translateX(-46px)}to{opacity:1;transform:none}}
@keyframes dpFadeIn{from{opacity:0}to{opacity:1}}
@keyframes dpZoomIn{from{opacity:0;transform:scale(.92)}to{opacity:1;transform:none}}
/* CONTENT entrance animations (staggered) */
.dp-cover,.dp-section{height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:6%;position:relative;color:#fff}
.dp-cover__ring{position:absolute;inset:auto;width:44%;aspect-ratio:1;border:1.5px solid;border-radius:50%;top:-18%;inset-inline-end:-12%;opacity:.5}
.dp-cover__kicker{font:700 13px var(--font-sans);letter-spacing:.35em;animation:dpUp .6s .1s var(--ease-out) both}
.dp-cover__title{font:700 clamp(26px,4.6vw,52px)/1.25 var(--font-serif);margin:18px 0 6px;animation:dpUp .6s .22s var(--ease-out) both}
[dir="rtl"] .dp-cover__title{font-family:var(--font-arabic);font-weight:800}
.dp-cover__sub{font:500 clamp(14px,1.8vw,19px) var(--font-sans);opacity:.95;animation:dpUp .6s .34s var(--ease-out) both}
[dir="rtl"] .dp-cover__sub{font-family:var(--font-arabic)}
.dp-cover__rule{width:84px;height:4px;border-radius:2px;margin-top:22px;animation:dpGrow .5s .45s var(--ease-out) both}
.dp-cover__hint{position:absolute;bottom:5%;font:500 12.5px var(--font-sans);opacity:.5;animation:dpFadeIn 1s .9s both}
[dir="rtl"] .dp-cover__hint{font-family:var(--font-arabic)}
.dp-section__no{font:700 16px var(--font-mono);letter-spacing:.3em;animation:dpUp .5s .08s var(--ease-out) both}
.dp-section__title{font:700 clamp(24px,4vw,46px)/1.3 var(--font-serif);margin:14px 0 8px;animation:dpUp .55s .2s var(--ease-out) both}
[dir="rtl"] .dp-section__title{font-family:var(--font-arabic);font-weight:800}
.dp-slide{height:100%;display:flex;gap:4%;padding:4.5% 5%;color:#1a1a18;background:#fff;position:relative}
.dp-slide__bar{position:absolute;top:0;left:0;right:0;height:6px}
.dp-slide__txt{flex:1;min-width:0;display:flex;flex-direction:column}
.dp-slide__title{font:700 clamp(20px,3vw,34px)/1.3 var(--font-serif);animation:dpUp .5s .08s var(--ease-out) both}
[dir="rtl"] .dp-slide__title{font-family:var(--font-arabic);font-weight:800}
.dp-slide__rule{width:64px;height:4px;border-radius:2px;margin:10px 0 4%;animation:dpGrow .45s .2s var(--ease-out) both}
.dp-slide__txt ul{list-style:none;display:flex;flex-direction:column;gap:3.5%;flex:1;margin:0;padding:0}
.dp-slide__txt li{font:500 clamp(14px,1.9vw,21px)/1.55 var(--font-sans);padding-inline-start:1.15em;position:relative;animation:dpUp .5s calc(.3s + var(--i,0)*.14s) var(--ease-out) both}
[dir="rtl"] .dp-slide__txt li{font-family:var(--font-arabic)}
.dp-slide__txt li::before{content:"";position:absolute;inset-inline-start:0;top:.62em;width:.45em;height:.45em;border-radius:50%;background:currentColor;opacity:.45}
.dp-slide__fig{flex:0 0 38%;align-self:center;margin:0;animation:dpZoomIn .6s .35s var(--ease-out) both}
.dp-slide__fig img{width:100%;aspect-ratio:1/1;object-fit:cover;border-radius:12px;box-shadow:0 10px 34px rgba(0,0,0,.22)}
.dp-slide__fig figcaption{font:400 italic 12px var(--font-sans);opacity:.6;text-align:center;margin-top:6px}

/* ═══ Rich presenter layouts — stats / comparison / steps / cards / quote / hero / full-image ═══ */
.dp-rich{height:100%;display:flex;flex-direction:column;padding:4.5% 5.5%;background:#fff;color:#1a1a18;position:relative}
.dp-head{margin-bottom:3%}
.dp-rich .dp-slide__title{font:800 clamp(20px,3vw,34px)/1.25 var(--font-serif);animation:dpUp .5s .06s var(--ease-out) both}
[dir="rtl"] .dp-rich .dp-slide__title{font-family:var(--font-arabic)}
.dp-rich .dp-slide__rule{width:64px;height:4px;border-radius:2px;margin-top:12px;animation:dpGrow .45s .2s var(--ease-out) both}
/* Stats */
.dp-stats__grid{flex:1;display:flex;gap:3%;align-items:stretch;justify-content:center}
.dp-stat{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:.35em;padding:5% 3%;background:linear-gradient(160deg,#fff,#f5f8f6);border:1px solid #ecebe3;border-radius:20px;box-shadow:0 14px 38px rgba(0,0,0,.07);animation:dpUp .55s calc(.2s + var(--i)*.1s) var(--ease-out) both}
.dp-stat__v{font:800 clamp(34px,6.6vw,74px)/1 var(--font-sans);letter-spacing:-.025em}
.dp-stat__l{font:600 clamp(12px,1.5vw,17px)/1.4 var(--font-sans);color:#6b6a63}
[dir="rtl"] .dp-stat__l{font-family:var(--font-arabic)}
/* Comparison */
.dp-cmp__grid{flex:1;display:flex;gap:3%;align-items:stretch}
.dp-col{flex:1;display:flex;flex-direction:column;background:#fff;border:1px solid #ecebe3;border-radius:18px;overflow:hidden;box-shadow:0 14px 38px rgba(0,0,0,.07);animation:dpUp .55s calc(.2s + var(--i)*.12s) var(--ease-out) both}
.dp-col__h{padding:.7em 1em;color:#fff;font:800 clamp(15px,1.9vw,22px) var(--font-sans);text-align:center}
[dir="rtl"] .dp-col__h{font-family:var(--font-arabic)}
.dp-col ul{list-style:none;margin:0;padding:1.1em 1.2em;display:flex;flex-direction:column;gap:.65em}
.dp-col li{font:500 clamp(13px,1.6vw,18px)/1.5 var(--font-sans);padding-inline-start:1.1em;position:relative;color:#33322e}
[dir="rtl"] .dp-col li{font-family:var(--font-arabic)}
.dp-col li::before{content:"";position:absolute;inset-inline-start:0;top:.6em;width:.42em;height:.42em;border-radius:50%;background:currentColor;opacity:.4}
/* Steps / timeline */
.dp-steps__row{flex:1;display:flex;gap:2.5%;align-items:flex-start;justify-content:center;padding-top:2%}
.dp-step{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;gap:.5em;position:relative;animation:dpUp .55s calc(.2s + var(--i)*.12s) var(--ease-out) both}
.dp-step:not(:last-child)::after{content:"";position:absolute;top:1.05em;inset-inline-start:62%;width:76%;height:3px;background:#e4e2d9;z-index:0}
.dp-step__n{width:2.1em;height:2.1em;border-radius:50%;display:grid;place-items:center;color:#fff;font:800 clamp(15px,1.9vw,22px) var(--font-sans);z-index:1;box-shadow:0 8px 18px rgba(0,0,0,.16)}
.dp-step__t{font:800 clamp(14px,1.7vw,20px)/1.3 var(--font-sans)}
[dir="rtl"] .dp-step__t{font-family:var(--font-arabic)}
.dp-step__d{font:500 clamp(12px,1.35vw,15.5px)/1.5 var(--font-sans);color:#6b6a63;max-width:15em}
[dir="rtl"] .dp-step__d{font-family:var(--font-arabic)}
/* Cards */
.dp-cards__grid{flex:1;display:grid;gap:3%;grid-template-columns:repeat(2,1fr)}
.dp-cards__grid--3{grid-template-columns:repeat(3,1fr)}
.dp-cards__grid--1{grid-template-columns:1fr}
.dp-card{display:flex;flex-direction:column;gap:.35em;padding:4% 4.5%;background:linear-gradient(160deg,#fff,#f5f8f6);border:1px solid #ecebe3;border-inline-start:4px solid;border-radius:16px;box-shadow:0 12px 32px rgba(0,0,0,.06);animation:dpUp .5s calc(.2s + var(--i)*.09s) var(--ease-out) both}
.dp-card__ic{font-size:clamp(26px,3.4vw,40px);line-height:1;margin-bottom:.1em}
.dp-card__t{font:800 clamp(15px,1.8vw,21px)/1.25 var(--font-sans)}
[dir="rtl"] .dp-card__t{font-family:var(--font-arabic)}
.dp-card__d{font:500 clamp(12.5px,1.4vw,16px)/1.5 var(--font-sans);color:#6b6a63}
[dir="rtl"] .dp-card__d{font-family:var(--font-arabic)}
/* Quote */
.dp-quote{height:100%;display:flex;flex-direction:column;justify-content:center;padding:7% 9%;color:#fff;position:relative}
.dp-quote__mark{font:800 clamp(80px,14vw,150px)/.6 var(--font-serif);animation:dpUp .5s .1s var(--ease-out) both}
.dp-quote blockquote{margin:0;font:600 italic clamp(22px,3.6vw,44px)/1.35 var(--font-serif);animation:dpUp .6s .25s var(--ease-out) both}
[dir="rtl"] .dp-quote blockquote{font-family:var(--font-arabic);font-style:normal;font-weight:700}
.dp-quote cite{margin-top:1em;font:700 clamp(14px,1.8vw,20px) var(--font-sans);font-style:normal;animation:dpUp .6s .4s var(--ease-out) both}
[dir="rtl"] .dp-quote cite{font-family:var(--font-arabic)}
/* Hero */
.dp-hero{height:100%;display:flex;flex-direction:column;justify-content:center;padding:8% 9%;background:linear-gradient(160deg,#faf9f5,#eeece3);position:relative}
.dp-hero__bar{position:absolute;top:0;bottom:0;inset-inline-start:0;width:10px}
.dp-hero h1{margin:0;font:800 clamp(30px,5.5vw,64px)/1.15 var(--font-serif);letter-spacing:-.01em;animation:dpUp .6s .12s var(--ease-out) both}
[dir="rtl"] .dp-hero h1{font-family:var(--font-arabic)}
.dp-hero p{margin:.8em 0 0;font:500 clamp(16px,2.2vw,26px)/1.5 var(--font-sans);color:#4a4842;animation:dpUp .6s .3s var(--ease-out) both}
[dir="rtl"] .dp-hero p{font-family:var(--font-arabic)}
/* Full image */
.dp-imgfull{height:100%;position:relative;background:#111;overflow:hidden}
.dp-imgfull img{width:100%;height:100%;object-fit:cover;animation:dpZoomIn .8s var(--ease-out) both}
.dp-imgfull__grad{position:absolute;inset:0;background:linear-gradient(to top,rgba(0,0,0,.78),rgba(0,0,0,.05) 55%)}
.dp-imgfull h2{position:absolute;inset-inline:6% 6%;bottom:7%;margin:0;color:#fff;font:800 clamp(24px,4vw,46px)/1.2 var(--font-serif);text-shadow:0 2px 22px rgba(0,0,0,.55);animation:dpUp .6s .2s var(--ease-out) both;max-width:82%}
[dir="rtl"] .dp-imgfull h2{font-family:var(--font-arabic)}
@keyframes dpUp{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}
@keyframes dpGrow{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.deck-pres__nav{position:absolute;top:50%;transform:translateY(-50%);z-index:2;width:44px;height:64px;border-radius:12px;background:rgba(255,255,255,.07);color:#fff;font-size:20px;opacity:.6;transition:opacity .15s,background .15s}
.deck-pres__nav:hover{opacity:1;background:rgba(255,255,255,.18)}
.deck-pres__nav--prev{inset-inline-start:10px}
.deck-pres__nav--next{inset-inline-end:10px}
.deck-pres__notes{max-height:22vh;overflow:auto;padding:12px 20px;background:rgba(255,255,255,.06);border-top:1px solid rgba(255,255,255,.1);font:400 14px/1.7 var(--font-sans);color:#e8e6df}
[dir="rtl"] .deck-pres__notes{font-family:var(--font-arabic)}
.deck-pres__progress{height:4px;background:rgba(255,255,255,.08)}
.deck-pres__progress span{display:block;height:100%;background:linear-gradient(90deg,#c96442,#e0a458);transition:width .35s var(--ease-out)}
@media (max-width:640px){
  .dp-slide{flex-direction:column;overflow-y:auto}
  .dp-slide__fig{flex:0 0 auto;width:56%}
  .dp-slide__fig--chart{width:88%;align-self:center}
  .deck-pres__nav{display:none}
  .deck-pres__name{display:none}
  /* bar must WRAP — otherwise the ✕ close button lands off-screen on ≤360px phones (no Esc there!) */
  .deck-pres__bar{flex-wrap:wrap;padding:8px 10px;justify-content:flex-end}
  .deck-pres__ctrls{flex-wrap:wrap;justify-content:flex-end;min-width:0;row-gap:6px}
  /* portrait phones: a forced 16:9 slide is ~195px tall and CLIPS bullets — let it use the stage */
  .deck-pres__slide{aspect-ratio:auto;height:100%;width:96vw}
}
/* touch devices reach :fullscreen too (Android) — hover-reveal means INVISIBLE yet tappable
   controls that swallow taps; keep them visible where hover doesn't exist */
@media (hover:none){
  .deck-pres:fullscreen .deck-pres__bar,.deck-pres:fullscreen .deck-pres__nav{opacity:1}
}

/* ═══ Deck card (live-building + editable slides) ═══ */
.deck-card{border:1px solid var(--color-border);border-radius:var(--radius-lg);background:var(--color-surface);overflow:hidden;margin:2px 0}
.deck-card__head{display:flex;align-items:center;gap:12px;padding:14px 16px;border-bottom:1px solid var(--color-border)}
.deck-card__ic{flex:0 0 auto;width:42px;height:42px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:20px}
.deck-card__meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.deck-card__meta strong{font:600 15px var(--font-sans);color:var(--color-text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
[dir="rtl"] .deck-card__meta strong{font-family:var(--font-arabic)}
.deck-card__sub{font:400 12.5px var(--font-sans);color:var(--color-text-muted)}
[dir="rtl"] .deck-card__sub{font-family:var(--font-arabic)}
.deck-card__live{color:var(--color-accent);animation:deckPulse 1.4s ease-in-out infinite}
@keyframes deckPulse{0%,100%{opacity:1}50%{opacity:.45}}
.deck-card__acts{display:flex;gap:8px;flex:0 0 auto;flex-wrap:wrap}
.deck-card__btn{padding:7px 14px;border-radius:var(--radius-pill);border:1.5px solid var(--color-border);background:transparent;color:var(--color-text-primary);font:500 13px var(--font-sans);white-space:nowrap;transition:all .15s}
[dir="rtl"] .deck-card__btn{font-family:var(--font-arabic)}
.deck-card__btn:hover{border-color:var(--color-accent);color:var(--color-accent)}
.deck-card__btn--go{background:var(--color-accent);border-color:var(--color-accent);color:var(--color-on-accent)}
.deck-card__btn--go:hover{background:var(--color-accent-hover);color:var(--color-on-accent)}
.deck-card__btn--danger:hover{border-color:#c0392b;color:#c0392b}
.deck-card__btn:disabled{opacity:.55;pointer-events:none}
.deck-card__list{display:flex;flex-direction:column}
.deck-card__row,.deck-card__slide summary{display:flex;align-items:center;gap:10px;padding:10px 16px;font:500 13.5px var(--font-sans);color:var(--color-text-primary);border-bottom:1px solid var(--color-border)}
[dir="rtl"] .deck-card__row,[dir="rtl"] .deck-card__slide summary{font-family:var(--font-arabic)}
.deck-card__row:last-child{border-bottom:none}
.is-building .deck-card__row{animation:deckIn .4s var(--ease-out) both}
@keyframes deckIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.deck-card__n{flex:0 0 auto;width:24px;height:24px;border-radius:8px;background:var(--color-surface-sunken);display:flex;align-items:center;justify-content:center;font:600 11.5px var(--font-mono);color:var(--color-text-muted)}
.deck-card__row--sec{background:var(--color-surface-sunken);font-weight:600}
.deck-card__sec{font:600 10.5px var(--font-sans);color:var(--color-on-accent);background:var(--color-accent);border-radius:var(--radius-pill);padding:2px 9px}
[dir="rtl"] .deck-card__sec{font-family:var(--font-arabic)}
.deck-card__slide summary{cursor:pointer;list-style:none;user-select:none}
.deck-card__slide summary::-webkit-details-marker{display:none}
.deck-card__slide summary:hover{background:var(--color-surface-sunken)}
.deck-card__st{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.deck-card__editTag{flex:0 0 auto;font:500 11.5px var(--font-sans);color:var(--color-text-muted);opacity:0;transition:opacity .15s}
[dir="rtl"] .deck-card__editTag{font-family:var(--font-arabic)}
.deck-card__slide summary:hover .deck-card__editTag,.deck-card__slide[open] .deck-card__editTag{opacity:1;color:var(--color-accent)}
.deck-card__panel{padding:12px 16px 16px;background:var(--color-surface-sunken);border-bottom:1px solid var(--color-border);display:flex;flex-direction:column;gap:8px}
.deck-card__lbl{font:600 11.5px var(--font-sans);color:var(--color-text-muted);margin-top:4px}
[dir="rtl"] .deck-card__lbl{font-family:var(--font-arabic)}
.deck-card__inp{width:100%;padding:8px 11px;border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);color:var(--color-text-primary);font:400 13.5px var(--font-sans);resize:vertical}
[dir="rtl"] .deck-card__inp{font-family:var(--font-arabic)}
.deck-card__inp:focus{outline:none;border-color:var(--color-accent)}
.dk-brow{display:flex;gap:6px;align-items:center}
.dk-brow .deck-card__inp{flex:1}
.dk-x{flex:0 0 auto;width:26px;height:26px;border-radius:8px;background:transparent;color:var(--color-text-muted);font-size:13px;transition:all .15s}
.dk-x:hover{background:#c0392b1a;color:#c0392b}
.dk-bullets{display:flex;flex-direction:column;gap:6px}
.deck-card__mini{align-self:flex-start;padding:4px 12px;border-radius:var(--radius-pill);border:1px dashed var(--color-border);background:transparent;color:var(--color-text-muted);font:500 12px var(--font-sans);transition:all .15s}
[dir="rtl"] .deck-card__mini{font-family:var(--font-arabic)}
.deck-card__mini:hover{border-color:var(--color-accent);color:var(--color-accent)}
.dk-asset{display:flex;align-items:center;gap:8px;padding:7px 11px;border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);font:500 12.5px var(--font-sans);color:var(--color-text-primary)}
[dir="rtl"] .dk-asset{font-family:var(--font-arabic)}
.dk-asset__u{flex:1;min-width:0;font:400 11px var(--font-mono);color:var(--color-text-muted);direction:ltr;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.deck-card__panelacts{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}
.deck-card__lock{padding:10px 16px;font:500 12.5px var(--font-sans);color:var(--color-text-muted);background:var(--color-surface-sunken);text-align:center}
[dir="rtl"] .deck-card__lock{font-family:var(--font-arabic)}
/* chart figure slot in the presenter */
.dp-slide__fig--chart{background:#fff;border-radius:12px;padding:8px;box-shadow:0 8px 30px rgba(0,0,0,.12);flex:0 0 44%}
.dp-slide__fig--chart svg{width:100%;height:auto}
/* ═══ TRUE fullscreen — the slide fills the screen edge-to-edge like real PowerPoint ═══ */
.deck-pres:fullscreen{background:#000}
.deck-pres:fullscreen .deck-pres__stage{padding:0}
.deck-pres:fullscreen .deck-pres__slide{width:100vw;height:100vh;max-height:none;aspect-ratio:auto;border-radius:0;box-shadow:none}
.deck-pres:fullscreen .deck-pres__bar{position:absolute;top:0;left:0;right:0;z-index:4;background:linear-gradient(rgba(0,0,0,.55),transparent);border-bottom:none;opacity:0;transition:opacity .25s}
.deck-pres:fullscreen .deck-pres__bar:hover,.deck-pres:fullscreen .deck-pres__bar:focus-within{opacity:1}
.deck-pres:fullscreen .deck-pres__progress{position:absolute;bottom:0;left:0;right:0;z-index:4}
.deck-pres:fullscreen .deck-pres__notes{position:absolute;bottom:4px;left:0;right:0;z-index:4;background:rgba(0,0,0,.78)}
.deck-pres:fullscreen .deck-pres__nav{opacity:0}
.deck-pres:fullscreen .deck-pres__nav:hover{opacity:1}
@media (max-width:640px){.deck-card__head{flex-wrap:wrap}.deck-card__acts{width:100%;justify-content:stretch}.deck-card__acts .deck-card__btn{flex:1;text-align:center}.deck-card__panelacts .deck-card__btn{flex:1}}

/* ═══ Firas Code — the browser IDE workspace ═══ */
.product-code .chat-scroll,.product-code .composer-wrap{display:none}
.product-code #tierSwitch{display:none}
#codeWorkspace{display:none}
.product-code #codeWorkspace{display:flex;flex:1;min-height:0;flex-direction:column;background:var(--color-bg);position:relative}
/* home */
.cw-home{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:22px;padding:24px;overflow-y:auto}
.cw-home__hero{text-align:center;display:flex;flex-direction:column;align-items:center;gap:8px}
.cw-home__mark{width:64px;height:64px;border-radius:18px;display:grid;place-items:center;font-size:30px;color:#fff;background:linear-gradient(140deg,var(--color-accent),#5b8def);box-shadow:0 10px 30px color-mix(in srgb,var(--color-accent) 35%,transparent)}
.cw-home__hero h1{font:700 34px var(--font-serif);color:var(--color-text-primary)}
.cw-home__hero p{font:400 15px/1.6 var(--font-sans);color:var(--color-text-secondary);max-width:440px}
[dir="rtl"] .cw-home__hero p{font-family:var(--font-arabic)}
.cw-home__card{width:min(94vw,430px);margin-inline:auto;align-self:center;display:flex;flex-direction:column;gap:8px;padding:20px;border:1px solid var(--color-border);border-radius:var(--radius-lg);background:var(--color-surface)}
.cw-home__lbl{font:600 12px var(--font-sans);color:var(--color-text-muted)}
[dir="rtl"] .cw-home__lbl{font-family:var(--font-arabic)}
.cw-home__name{padding:11px 13px;border:1px solid var(--color-border-strong);border-radius:var(--radius-md);background:var(--color-bg-subtle);color:var(--color-text-primary);font:400 14px var(--font-sans)}
.cw-home__name:focus{outline:none;border-color:var(--color-accent)}
.cw-home__tpls{display:flex;gap:8px;flex-wrap:wrap}
.cw-home__tpl{flex:1;min-width:120px;padding:10px;border:1.5px solid var(--color-border);border-radius:var(--radius-md);background:transparent;color:var(--color-text-secondary);font:500 13px var(--font-sans);transition:.15s}
[dir="rtl"] .cw-home__tpl{font-family:var(--font-arabic)}
.cw-home__tpl.is-active{border-color:var(--color-accent);color:var(--color-accent);background:var(--color-accent-soft)}
.cw-home__create{margin-top:6px;padding:12px;border:none;border-radius:var(--radius-pill);background:var(--color-accent);color:var(--color-on-accent);font:600 14px var(--font-sans);cursor:pointer}
[dir="rtl"] .cw-home__create{font-family:var(--font-arabic)}
.cw-home__create:hover{background:var(--color-accent-hover)}
.cw-home__hint{font:400 12.5px var(--font-sans);color:var(--color-text-muted)}
[dir="rtl"] .cw-home__hint{font-family:var(--font-arabic)}
/* IDE frame */
.cw{flex:1;display:flex;flex-direction:column;min-height:0}
.cw-bar{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--color-border-hairline);background:var(--color-surface)}
.cw-bar__home{width:32px;height:32px;border-radius:9px;background:var(--color-bg-subtle);color:var(--color-text-primary);font-size:16px}
.cw-bar__home:hover{background:var(--color-accent-soft);color:var(--color-accent)}
.cw-bar__name{font:600 14px var(--font-sans);color:var(--color-text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
[dir="rtl"] .cw-bar__name{font-family:var(--font-arabic)}
.cw-bar__sp{flex:1}
.cw-bar__btn{padding:7px 13px;border-radius:var(--radius-pill);border:1px solid var(--color-border);background:transparent;color:var(--color-text-primary);font:500 12.5px var(--font-sans);white-space:nowrap;transition:.15s}
[dir="rtl"] .cw-bar__btn{font-family:var(--font-arabic)}
.cw-bar__btn:hover{border-color:var(--color-accent);color:var(--color-accent)}
.cw-main{flex:1;display:grid;grid-template-columns:200px minmax(0,1.1fr) minmax(0,1fr);min-height:0}
/* tree */
.cw-tree{border-inline-end:1px solid var(--color-border-hairline);background:var(--color-surface);overflow-y:auto}
.cw-file{display:flex;align-items:center;gap:7px;padding:8px 10px;cursor:pointer;border-bottom:1px solid var(--color-border-hairline);font:500 12.5px var(--font-mono);color:var(--color-text-secondary)}
.cw-file:hover{background:var(--color-bg-subtle)}
.cw-file.is-active{background:var(--color-accent-soft);color:var(--color-accent)}
.cw-file__p{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left}
.cw-file__size{font-size:10.5px;color:var(--color-text-muted)}
.cw-file__x{width:20px;height:20px;border-radius:6px;color:var(--color-text-muted);font-size:11px;opacity:0;transition:.15s}
.cw-file:hover .cw-file__x{opacity:1}
.cw-file__x:hover{background:#c0392b1a;color:#c0392b}
/* editor */
.cw-ed{min-width:0;display:flex;border-inline-end:1px solid var(--color-border-hairline);background:#fff}
[data-theme="dark"] .cw-ed{background:#1e1e1c}
.cw-ed .CodeMirror{flex:1;height:100%;font:13px/1.6 var(--font-mono);direction:ltr}
[data-theme="dark"] .cw-ed .CodeMirror{background:#1e1e1c;color:#e8e6df}
[data-theme="dark"] .cw-ed .CodeMirror-gutters{background:#262624;border-color:#3a3a37}
[data-theme="dark"] .cw-ed .CodeMirror-linenumber{color:#6d6b64}
[data-theme="dark"] .cw-ed .CodeMirror-cursor{border-color:#e8e6df}
.cw-ed__ta{flex:1;border:none;resize:none;padding:12px;font:13px/1.6 var(--font-mono);background:transparent;color:var(--color-text-primary);direction:ltr;outline:none}
/* right pane */
.cw-right{min-width:0;display:flex;flex-direction:column;background:var(--color-surface)}
.cw-tabs{display:flex;align-items:center;gap:4px;padding:6px 8px;border-bottom:1px solid var(--color-border-hairline)}
.cw-tab{padding:6px 14px;border-radius:var(--radius-pill);background:transparent;color:var(--color-text-secondary);font:500 12.5px var(--font-sans);position:relative}
[dir="rtl"] .cw-tab{font-family:var(--font-arabic)}
.cw-tab.is-active{background:var(--color-accent-soft);color:var(--color-accent);font-weight:600}
.cw-tab.has-errors::after{content:"";position:absolute;top:4px;inset-inline-end:4px;width:7px;height:7px;border-radius:50%;background:#c0392b}
.cw-conclear{margin-inline-start:auto;width:26px;height:26px;border-radius:8px;color:var(--color-text-muted);font-size:12px}
.cw-conclear:hover{background:var(--color-bg-subtle)}
.cw-preview{flex:1;border:none;background:#fff;width:100%}
.cw-console{flex:1;overflow-y:auto;background:#141412}
.cw-console__list{padding:8px 10px;display:flex;flex-direction:column;gap:2px}
.cw-conrow{font:12px/1.5 var(--font-mono);color:#c9c7bf;direction:ltr;text-align:left;white-space:pre-wrap;word-break:break-word;border-bottom:1px solid #ffffff0a;padding:2px 0}
.cw-conrow--error{color:#ff8a80}
.cw-conrow--warn{color:#ffd54f}
.cw-conrow--info{color:#81d4fa}
.cw-conrow--ok{color:#8fd694}
.cw-conrow--run{color:#81d4fa}
/* The app's OWN messages, as opposed to program output. .cw-conrow forces direction:ltr —
   correct for stdout and tracebacks, wrong for an Arabic sentence, whose trailing punctuation
   and Latin runs get reordered inside an LTR paragraph. plaintext lets the row take its
   direction from its own first strong character, which is what dir=auto on the element means. */
.cw-conrow.is-msg{unicode-bidi:plaintext;text-align:start}
/* AI bar */
.cw-ai{display:flex;gap:8px;padding:10px 12px;border-top:1px solid var(--color-border-hairline);background:var(--color-surface)}
.cw-ai__in{flex:1;padding:11px 14px;border:1.5px solid var(--color-border-strong);border-radius:var(--radius-pill);background:var(--color-bg-subtle);color:var(--color-text-primary);font:400 13.5px var(--font-sans)}
[dir="rtl"] .cw-ai__in{font-family:var(--font-arabic)}
.cw-ai__in:focus{outline:none;border-color:var(--color-accent)}
.cw-ai__go{padding:0 20px;border-radius:var(--radius-pill);border:none;background:var(--color-accent);color:var(--color-on-accent);font:600 13px var(--font-sans);white-space:nowrap}
[dir="rtl"] .cw-ai__go{font-family:var(--font-arabic)}
.cw-ai__go:hover{background:var(--color-accent-hover)}
.cw-ai__go:disabled{opacity:.6}
/* diff review overlay */
.cw-diff{position:absolute;inset:0;z-index:20;background:color-mix(in srgb,var(--color-bg) 55%,transparent);backdrop-filter:blur(3px);display:flex;align-items:center;justify-content:center;padding:18px}
.cw-diff__card{width:min(96vw,760px);max-height:86%;display:flex;flex-direction:column;border:1px solid var(--color-border);border-radius:var(--radius-lg);background:var(--color-surface);box-shadow:0 18px 60px rgba(0,0,0,.25);overflow:hidden}
.cw-diff__head{display:flex;align-items:center;gap:10px;padding:13px 16px;border-bottom:1px solid var(--color-border-hairline);font:600 14px var(--font-sans);color:var(--color-text-primary)}
[dir="rtl"] .cw-diff__head{font-family:var(--font-arabic)}
.cw-diff__sum{font:400 12.5px var(--font-sans);color:var(--color-text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cw-diff__list{flex:1;overflow-y:auto;padding:8px 12px;display:flex;flex-direction:column;gap:8px}
.cw-diff__file{border:1px solid var(--color-border-hairline);border-radius:var(--radius-md);overflow:hidden}
.cw-diff__file summary{display:flex;align-items:center;gap:8px;padding:9px 12px;cursor:pointer;list-style:none;background:var(--color-bg-subtle);font:500 12.5px var(--font-mono)}
.cw-diff__file summary::-webkit-details-marker{display:none}
.cw-diff__badge{font:600 10px var(--font-sans);padding:2px 8px;border-radius:var(--radius-pill)}
[dir="rtl"] .cw-diff__badge{font-family:var(--font-arabic)}
.cw-diff__badge--new{background:#1e824c22;color:#1e824c}
.cw-diff__badge--edit{background:#b9770e22;color:#b9770e}
.cw-diff__badge--del{background:#c0392b22;color:#c0392b}
.cw-diff__lines{max-height:300px;overflow:auto;direction:ltr;text-align:left}
.cw-dl{font:11.5px/1.55 var(--font-mono);white-space:pre-wrap;word-break:break-all;padding:0 10px}
.cw-dl--add{background:#1e824c18;color:#1e824c}
.cw-dl--del{background:#c0392b14;color:#c0392b;text-decoration:line-through}
.cw-dl--same{color:var(--color-text-secondary)}
.cw-dl--fold{text-align:center;color:var(--color-text-muted);background:var(--color-bg-subtle)}
.cw-diff__all{padding:10px 12px;font:500 12px var(--font-sans);color:var(--color-text-secondary)}
.cw-diff__pre{max-height:260px;overflow:auto;font:11.5px/1.55 var(--font-mono);direction:ltr;text-align:left;background:var(--color-bg-subtle);border-radius:8px;padding:8px;margin-top:6px}
.cw-diff__all--del{color:#c0392b;font-weight:600}
.cw-diff__acts{display:flex;gap:10px;padding:12px 16px;border-top:1px solid var(--color-border-hairline)}
.cw-diff__apply{flex:1;padding:11px;border:none;border-radius:var(--radius-pill);background:var(--color-accent);color:var(--color-on-accent);font:600 13.5px var(--font-sans)}
[dir="rtl"] .cw-diff__apply{font-family:var(--font-arabic)}
.cw-diff__apply:hover{background:var(--color-accent-hover)}
.cw-diff__cancel{padding:11px 22px;border:1px solid var(--color-border);border-radius:var(--radius-pill);background:transparent;color:var(--color-text-primary);font:500 13px var(--font-sans)}
[dir="rtl"] .cw-diff__cancel{font-family:var(--font-arabic)}
/* project-card bridge button */
.proj-card__opencode{background:linear-gradient(120deg,var(--color-accent),#5b8def) !important;color:#fff !important;border:none !important}
/* mobile: stacked panes */
@media (max-width:860px){
  .cw-main{grid-template-columns:1fr;grid-template-rows:auto minmax(0,1.05fr) minmax(0,1fr)}
  .cw-tree{max-height:120px;border-inline-end:none;border-bottom:1px solid var(--color-border-hairline)}
  .cw-ed{border-inline-end:none;border-bottom:1px solid var(--color-border-hairline)}
  .cw-bar{flex-wrap:wrap}
}

/* ═══ Firas Code — design polish v2 (premium warm-IDE look) ═══ */
/* entrance */
.cw,.cw-home{animation:cwIn .45s var(--ease-out) both}
@keyframes cwIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
/* home: dotted paper backdrop + glowing mark + template CARDS */
.cw-home{background:radial-gradient(circle at 50% -10%,color-mix(in srgb,var(--color-accent) 7%,transparent),transparent 55%),radial-gradient(color-mix(in srgb,var(--color-text-muted) 14%,transparent) 1px,transparent 1px);background-size:auto,22px 22px}
.cw-home__mark{position:relative;width:72px;height:72px;border-radius:20px;font-size:0;background:linear-gradient(140deg,var(--color-accent),#5b8def);box-shadow:0 14px 40px color-mix(in srgb,var(--color-accent) 40%,transparent),inset 0 1px 0 rgba(255,255,255,.35);animation:cwFloat 5s ease-in-out infinite}
.cw-home__mark-glyph{font:800 24px/1 var(--font-mono);color:#fff;letter-spacing:-1px}
@keyframes cwFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}
/* (Removed) A clipped-gradient headline lived here. Every one of its four properties was
   already overridden by the repair layer at "Firas Code home: remove the splash-screen
   vocabulary" further down, so it painted nothing — it only survived as a false positive in
   design scans and as one more era for the next reader to untangle. Deleted, not restyled. */
.cw-home__card{box-shadow:0 20px 60px color-mix(in srgb,var(--color-text-primary) 7%,transparent);border-radius:20px;backdrop-filter:blur(6px)}
.cw-home__tpls{flex-direction:column}
.cw-home__tpl{display:flex;align-items:center;gap:12px;text-align:start;padding:12px 14px;min-width:0;position:relative}
.cw-home__tpl-ic{flex:0 0 auto;width:38px;height:38px;border-radius:11px;display:grid;place-items:center;font-size:18px;background:var(--color-bg-subtle)}
.cw-home__tpl.is-active .cw-home__tpl-ic{background:color-mix(in srgb,var(--color-accent) 16%,transparent)}
.cw-home__tpl-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.cw-home__tpl-txt strong{font:600 13.5px var(--font-sans);color:var(--color-text-primary)}
.cw-home__tpl-txt small{font:400 11.5px var(--font-sans);color:var(--color-text-muted)}
[dir="rtl"] .cw-home__tpl-txt strong,[dir="rtl"] .cw-home__tpl-txt small{font-family:var(--font-arabic)}
.cw-home__tpl-check{flex:0 0 auto;width:20px;height:20px;border-radius:50%;display:grid;place-items:center;font-size:10px;color:#fff;background:var(--color-accent);opacity:0;transform:scale(.5);transition:.18s}
.cw-home__tpl.is-active .cw-home__tpl-check{opacity:1;transform:scale(1)}
.cw-home__tpl:hover{transform:translateY(-1px);box-shadow:0 6px 18px color-mix(in srgb,var(--color-text-primary) 8%,transparent)}
.cw-home__create{box-shadow:0 8px 24px color-mix(in srgb,var(--color-accent) 35%,transparent);transition:.2s}
.cw-home__create:hover{transform:translateY(-1px);box-shadow:0 12px 30px color-mix(in srgb,var(--color-accent) 45%,transparent)}
/* bar: glassy chrome with a brand chip */
.cw-bar{gap:10px;padding:9px 14px;background:color-mix(in srgb,var(--color-surface) 88%,transparent);backdrop-filter:blur(8px)}
.cw-bar__logo{width:28px;height:28px;border-radius:9px;display:grid;place-items:center;font:800 11px var(--font-mono);color:#fff;background:linear-gradient(140deg,var(--color-accent),#5b8def);box-shadow:0 4px 12px color-mix(in srgb,var(--color-accent) 35%,transparent)}
.cw-bar__count{font:500 11px var(--font-sans);color:var(--color-text-muted);background:var(--color-bg-subtle);border-radius:var(--radius-pill);padding:3px 10px}
[dir="rtl"] .cw-bar__count{font-family:var(--font-arabic)}
.cw-bar__btn{transition:.18s}
.cw-bar__btn:hover{transform:translateY(-1px);box-shadow:0 4px 12px color-mix(in srgb,var(--color-accent) 20%,transparent)}
.cw-run{border-color:var(--color-accent);color:var(--color-accent);background:color-mix(in srgb,var(--color-accent) 8%,transparent)}
/* tree */
.cw-tree__head{display:flex;align-items:center;gap:8px;padding:10px 12px 8px;font:700 10.5px var(--font-sans);letter-spacing:.14em;text-transform:uppercase;color:var(--color-text-muted)}
[dir="rtl"] .cw-tree__head{font-family:var(--font-arabic);letter-spacing:.04em}
.cw-tree__n{margin-inline-start:auto;font:600 10px var(--font-mono);background:var(--color-bg-subtle);border-radius:var(--radius-pill);padding:2px 8px}
.cw-file{border-bottom:none;border-radius:9px;margin:1px 6px;padding:7px 9px;position:relative;transition:background .15s,color .15s}
.cw-file.is-active{background:color-mix(in srgb,var(--color-accent) 11%,transparent);color:var(--color-accent);font-weight:600}
.cw-file.is-active::before{content:"";position:absolute;inset-inline-start:-6px;top:20%;bottom:20%;width:3px;border-radius:2px;background:var(--color-accent)}
.cw-file__dot{flex:0 0 auto;width:9px;height:9px;border-radius:3px}
.cw-file__dot[data-ext="html"]{background:#e0764f}
.cw-file__dot[data-ext="css"]{background:#5b8def}
.cw-file__dot[data-ext="js"]{background:#e0b35e}
.cw-file__dot[data-ext="py"]{background:#57ae9c}
.cw-file__dot[data-ext="json"]{background:#a78bfa}
.cw-file__dot[data-ext="md"],.cw-file__dot[data-ext="txt"]{background:var(--color-text-muted)}
/* editor: warm CodeMirror theme (light) */
.cw-ed{background:#FDFCF8}
.cw-ed .CodeMirror{background:#FDFCF8;color:#3f3e38;font-size:13.5px}
.cw-ed .CodeMirror-gutters{background:#F6F4EC;border-color:#ECE9DE}
.cw-ed .CodeMirror-linenumber{color:#B5B1A2}
.cw-ed .CodeMirror-activeline-background{background:#F3F0E5}
.cw-ed .CodeMirror-selected{background:#E8E2CF}
.cw-ed .cm-tag{color:#B3512F}
.cw-ed .cm-bracket{color:#8f8a7a}
.cw-ed .cm-attribute{color:#23786A}
.cw-ed .cm-string,.cw-ed .cm-string-2{color:#2E7D5B}
.cw-ed .cm-keyword{color:#A8562E;font-weight:600}
.cw-ed .cm-def{color:#5B6ABF}
.cw-ed .cm-variable{color:#3f3e38}
.cw-ed .cm-variable-2{color:#23786A}
.cw-ed .cm-property{color:#5B6ABF}
.cw-ed .cm-number,.cw-ed .cm-atom{color:#9C6B1F}
.cw-ed .cm-comment{color:#A5A294;font-style:italic}
.cw-ed .cm-operator{color:#8a5a3a}
.cw-ed .cm-qualifier{color:#23786A}
.cw-ed .cm-builtin{color:#7C3AED}
.cw-ed .cm-meta{color:#8f8a7a}
.cw-ed .cm-error{background:transparent;color:#c0392b}
.cw-ed .CodeMirror-cursor{border-color:#A8562E;border-width:1.6px}
/* editor: charcoal theme (dark) */
[data-theme="dark"] .cw-ed,[data-theme="dark"] .cw-ed .CodeMirror{background:#1E1E1C;color:#E4E1D6}
[data-theme="dark"] .cw-ed .CodeMirror-gutters{background:#242422;border-color:#34342f}
[data-theme="dark"] .cw-ed .CodeMirror-linenumber{color:#6d6b60}
[data-theme="dark"] .cw-ed .CodeMirror-activeline-background{background:#282824}
[data-theme="dark"] .cw-ed .CodeMirror-selected{background:#3a382e}
[data-theme="dark"] .cw-ed .cm-tag{color:#E08B66}
[data-theme="dark"] .cw-ed .cm-attribute{color:#6FB8AB}
[data-theme="dark"] .cw-ed .cm-string,[data-theme="dark"] .cw-ed .cm-string-2{color:#8FC9A8}
[data-theme="dark"] .cw-ed .cm-keyword{color:#E39A72}
[data-theme="dark"] .cw-ed .cm-def{color:#9BA8E8}
[data-theme="dark"] .cw-ed .cm-variable{color:#E4E1D6}
[data-theme="dark"] .cw-ed .cm-variable-2{color:#6FB8AB}
[data-theme="dark"] .cw-ed .cm-property{color:#9BA8E8}
[data-theme="dark"] .cw-ed .cm-number,[data-theme="dark"] .cw-ed .cm-atom{color:#E0B35E}
[data-theme="dark"] .cw-ed .cm-comment{color:#7a776a}
[data-theme="dark"] .cw-ed .cm-operator{color:#d9a583}
[data-theme="dark"] .cw-ed .cm-builtin{color:#C4A7F5}
[data-theme="dark"] .cw-ed .CodeMirror-cursor{border-color:#E39A72}
/* right pane: browser-style preview frame */
.cw-tabs{padding:8px 10px;gap:6px}
.cw-tab{border:1px solid transparent}
.cw-tab.is-active{border-color:color-mix(in srgb,var(--color-accent) 30%,transparent);box-shadow:0 2px 8px color-mix(in srgb,var(--color-accent) 14%,transparent)}
.cw-prevwrap{flex:1;display:flex;flex-direction:column;min-height:0;margin:0 10px 10px;border:1px solid var(--color-border-hairline);border-radius:12px;overflow:hidden;box-shadow:0 8px 26px color-mix(in srgb,var(--color-text-primary) 7%,transparent);background:#fff}
.cw-prevchrome{display:flex;align-items:center;gap:10px;padding:7px 12px;background:var(--color-bg-subtle);border-bottom:1px solid var(--color-border-hairline)}
.cw-prevdots{display:inline-flex;gap:5px;direction:ltr}
.cw-prevdots i{width:9px;height:9px;border-radius:50%;background:#e0764f;opacity:.75}
.cw-prevdots i:nth-child(2){background:#e0b35e}
.cw-prevdots i:nth-child(3){background:#57ae9c}
.cw-prevaddr{flex:1;text-align:center;font:500 10.5px var(--font-mono);color:var(--color-text-muted);background:var(--color-surface);border:1px solid var(--color-border-hairline);border-radius:var(--radius-pill);padding:3px 12px;direction:ltr}
.cw-preview{flex:1}
/* console: terminal feel */
.cw-console{margin:0 10px 10px;border-radius:12px;border:1px solid #ffffff14}
.cw-conrow{border-bottom:none;padding:2px 0 2px 16px;position:relative}
.cw-conrow::before{content:"›";position:absolute;left:2px;color:#57ae9c;opacity:.7}
.cw-conrow--error::before{content:"✕";color:#ff8a80}
.cw-conrow--warn::before{content:"⚠";font-size:10px;color:#ffd54f}
.cw-conrow--ok::before{content:"✓";color:#8fd694}
.cw-conrow--run::before{content:"▶";font-size:9px;color:#81d4fa}
/* On a message row the marker FLOWS instead of being pinned with `left`. An absolute marker
   cannot follow the text: dir="auto" does not match a [dir="rtl"] selector, and
   unicode-bidi:plaintext changes only how inline content is ordered, not the `direction`
   property that inset-inline-start resolves against — so every logical-position trick still
   lands it on the left while Arabic text sits on the right. As a static inline box it is a
   NEUTRAL character at the logical start, which the bidi algorithm places correctly in both
   directions with no branching at all. */
/* The layout half of `is-msg` lives further down, scoped to .cw--v3 — a later rule there
   re-applies the gutter this needs removed, and at equal specificity the later rule wins. */
/* AI bar: glowing focus + gradient action */
.cw-ai{gap:10px;padding:12px 14px}
.cw-ai__in{transition:border-color .2s,box-shadow .2s}
.cw-ai__in:focus{box-shadow:0 0 0 4px color-mix(in srgb,var(--color-accent) 14%,transparent)}
.cw-ai__go{background:linear-gradient(120deg,var(--color-accent),#5b8def);box-shadow:0 6px 18px color-mix(in srgb,var(--color-accent) 30%,transparent);transition:.2s}
.cw-ai__go:hover{transform:translateY(-1px);box-shadow:0 9px 24px color-mix(in srgb,var(--color-accent) 42%,transparent)}
/* diff overlay: scale-in + gutter signs */
.cw-diff__card{animation:cwDiffIn .3s var(--ease-out) both;border-radius:18px}
@keyframes cwDiffIn{from{opacity:0;transform:scale(.96) translateY(8px)}to{opacity:1;transform:none}}
.cw-dl{padding:0 10px 0 26px;position:relative}
.cw-dl--add::before{content:"+";position:absolute;left:8px;font-weight:700}
.cw-dl--del::before{content:"−";position:absolute;left:8px;font-weight:700}
.cw-dl--del{text-decoration:none;opacity:.8}
.cw-diff__head{background:linear-gradient(100deg,color-mix(in srgb,var(--color-accent) 9%,transparent),transparent)}
/* scrollbars inside the workspace */
#codeWorkspace ::-webkit-scrollbar{width:9px;height:9px}
#codeWorkspace ::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--color-text-muted) 32%,transparent);border-radius:8px;border:2px solid transparent;background-clip:content-box}
#codeWorkspace ::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--color-text-muted) 50%,transparent);background-clip:content-box;border:2px solid transparent}
#codeWorkspace ::-webkit-scrollbar-track{background:transparent}

/* ═══ Share sheet (link + guaranteed copy + OS share) ═══ */
.share-sheet{position:fixed;inset:0;z-index:1300;background:rgba(0,0,0,.35);backdrop-filter:blur(2px);display:flex;align-items:flex-end;justify-content:center;opacity:0;transition:opacity .18s}
.share-sheet.is-open{opacity:1}
.share-sheet__card{width:min(96vw,480px);margin-bottom:max(14px,env(safe-area-inset-bottom));background:var(--color-surface);border:1px solid var(--color-border);border-radius:18px;padding:16px;display:flex;flex-direction:column;gap:10px;box-shadow:0 18px 60px rgba(0,0,0,.3);transform:translateY(14px);transition:transform .2s var(--ease-out)}
.share-sheet.is-open .share-sheet__card{transform:none}
.share-sheet__head{display:flex;align-items:center;justify-content:space-between;font:600 15px var(--font-sans);color:var(--color-text-primary)}
[dir="rtl"] .share-sheet__head{font-family:var(--font-arabic)}
.share-sheet__x{width:40px;height:40px;border-radius:10px;color:var(--color-text-muted);font-size:15px;flex:0 0 auto}
.share-sheet__x:hover{background:var(--color-bg-subtle);color:var(--color-text-primary)}
.share-sheet__link{width:100%;padding:11px 12px;border:1px solid var(--color-border-strong);border-radius:var(--radius-md);background:var(--color-bg-subtle);color:var(--color-text-primary);font:500 12.5px var(--font-mono);text-overflow:ellipsis}
.share-sheet__link:focus{outline:none;border-color:var(--color-accent)}
.share-sheet__acts{display:flex;gap:8px;flex-wrap:wrap}
.share-sheet__copy{flex:1;padding:12px;border:none;border-radius:var(--radius-pill);background:var(--color-accent);color:var(--color-on-accent);font:600 13.5px var(--font-sans)}
[dir="rtl"] .share-sheet__copy{font-family:var(--font-arabic)}
.share-sheet__copy:hover{background:var(--color-accent-hover)}
.share-sheet__os{flex:1;padding:12px;border:1.5px solid var(--color-border);border-radius:var(--radius-pill);background:transparent;color:var(--color-text-primary);font:500 13px var(--font-sans)}
[dir="rtl"] .share-sheet__os{font-family:var(--font-arabic)}
.share-sheet__os:hover{border-color:var(--color-accent);color:var(--color-accent)}
.share-sheet__note{font:400 11.5px var(--font-sans);color:var(--color-text-muted);text-align:center;margin:0}
[dir="rtl"] .share-sheet__note{font-family:var(--font-arabic)}

/* ═══════════ FIRAS CODE — Batch 1 enhancements (50-agent dev) ═══════════ */
/* ═══ bottom STATUS BAR ═══ */
.cw-status{display:flex;align-items:center;gap:14px;padding:5px 14px;border-top:1px solid var(--color-border-hairline);background:var(--color-bg-subtle);font:500 11.5px var(--font-mono);color:var(--color-text-secondary);white-space:nowrap;overflow-x:auto;scrollbar-width:none}
.cw-status::-webkit-scrollbar{display:none}
.cw-status__seg{display:inline-flex;align-items:center;gap:5px;direction:ltr;unicode-bidi:isolate}
.cw-status__sp{flex:1}
.cw-status__save{display:inline-flex;align-items:center;gap:6px;padding:2px 10px;border-radius:var(--radius-pill);font:600 11px var(--font-sans);direction:ltr}
[dir="rtl"] .cw-status__save{font-family:var(--font-arabic);direction:rtl}
.cw-status__save::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor;flex:none}
.cw-status__save.is-saved{background:color-mix(in srgb,var(--color-accent) 14%,transparent);color:var(--color-accent)}
.cw-status__save.is-editing{background:#b9770e1f;color:#b9770e}
.cw-status__save.is-saving{background:#5b8def22;color:#5b8def}
.cw-status__save.is-saving::before{animation:cwStatusPulse .9s ease-in-out infinite}
@keyframes cwStatusPulse{0%,100%{opacity:1}50%{opacity:.3}}
@media (max-width:860px){.cw-status{gap:10px;font-size:11px}.cw-status__lang,.cw-status__tab{display:none}}
/* ═══ LIVE PREVIEW UPGRADES (device presets · auto-reload · reload · open-in-tab) ═══ */
.cw-prevchrome{flex-wrap:wrap;row-gap:6px}
.cw-prevaddr{min-width:80px}
.cw-prevseg{display:inline-flex;gap:2px;padding:2px;border-radius:var(--radius-pill);background:var(--color-surface);border:1px solid var(--color-border-hairline);direction:ltr}
.cw-prevdev{display:inline-flex;align-items:center;gap:5px;padding:4px 9px;border-radius:var(--radius-pill);background:transparent;color:var(--color-text-secondary);font:600 10.5px var(--font-sans);line-height:1;cursor:pointer;border:none;transition:.15s;white-space:nowrap}
[dir="rtl"] .cw-prevdev{font-family:var(--font-arabic)}
.cw-prevdev__t{font-size:10.5px}
.cw-prevdev:hover{color:var(--color-text-primary)}
.cw-prevdev.is-active{background:var(--color-accent-soft);color:var(--color-accent)}
@media(max-width:900px){.cw-prevdev__t{display:none}.cw-prevdev{padding:5px 8px}}
.cw-prevctl{display:inline-flex;align-items:center;gap:5px;direction:ltr}
.cw-prevbtn{display:inline-flex;align-items:center;justify-content:center;gap:5px;height:26px;min-width:26px;padding:0 8px;border-radius:8px;border:1px solid var(--color-border-hairline);background:var(--color-surface);color:var(--color-text-secondary);font:600 10.5px var(--font-sans);line-height:1;cursor:pointer;transition:.15s}
[dir="rtl"] .cw-prevbtn{font-family:var(--font-arabic)}
.cw-prevbtn:hover{border-color:var(--color-accent);color:var(--color-accent);transform:translateY(-1px)}
.cw-prevreload,.cw-prevtab{font-size:14px;padding:0}
.cw-prevauto .cw-prevauto__dot{width:7px;height:7px;border-radius:50%;background:var(--color-text-muted);transition:.2s}
.cw-prevauto.is-on{border-color:color-mix(in srgb,var(--color-accent) 45%,transparent);color:var(--color-accent);background:var(--color-accent-soft)}
.cw-prevauto.is-on .cw-prevauto__dot{background:#42b883;box-shadow:0 0 0 3px color-mix(in srgb,#42b883 22%,transparent);animation:cwPulse 1.8s ease-in-out infinite}
@keyframes cwPulse{0%,100%{opacity:1}50%{opacity:.55}}
.cw-prevstage{flex:1;min-height:0;display:flex;align-items:stretch;justify-content:center;overflow:auto;background:#fff}
.cw-prevstage.is-framed{align-items:center;padding:12px;background:repeating-linear-gradient(45deg,var(--color-bg-subtle),var(--color-bg-subtle) 10px,var(--color-bg) 10px,var(--color-bg) 20px)}
.cw-prevstage.is-framed .cw-preview{flex:0 0 auto;height:100%;max-height:100%;border-radius:14px;box-shadow:0 10px 40px color-mix(in srgb,var(--color-text-primary) 22%,transparent);border:1px solid var(--color-border-hairline);background:#fff}
[data-theme="dark"] .cw-prevstage.is-framed .cw-preview{box-shadow:0 10px 40px rgba(0,0,0,.5)}
.cw-prevstage .cw-preview{border:none;background:#fff;width:100%;height:100%}
/* ═══ FILE OPS (context menu + inline rename) ═══ */
.cw-tree__head{display:flex;align-items:center;gap:6px}
.cw-tree__newfolder{margin-inline-start:auto;width:24px;height:22px;border-radius:7px;color:var(--color-text-muted);font-size:12px;line-height:1;transition:.15s}
.cw-tree__newfolder:hover{background:var(--color-accent-soft);color:var(--color-accent)}
.cw-file__rename{width:100%;min-width:0;border:1.5px solid var(--color-accent);border-radius:6px;padding:2px 6px;background:var(--color-surface);color:var(--color-text-primary);font:500 12.5px var(--font-mono);direction:ltr;text-align:left;outline:none}
.cw-fmenu{position:fixed;z-index:60;min-width:196px;padding:6px;border-radius:12px;background:var(--color-surface);border:1px solid var(--color-border-strong);box-shadow:0 18px 50px color-mix(in srgb,var(--color-text-primary) 22%,transparent),0 2px 8px rgba(0,0,0,.08);animation:cwMenuIn .13s var(--ease-out) both}
@keyframes cwMenuIn{from{opacity:0;transform:scale(.96) translateY(-4px)}to{opacity:1;transform:none}}
.cw-fmenu__item{display:block;width:100%;text-align:start;padding:8px 12px;border-radius:8px;background:transparent;color:var(--color-text-primary);font:500 13px var(--font-sans);white-space:nowrap;cursor:pointer;transition:.12s}
[dir="rtl"] .cw-fmenu__item{font-family:var(--font-arabic)}
.cw-fmenu__item:hover{background:var(--color-accent-soft);color:var(--color-accent)}
.cw-fmenu__item.is-danger{color:#c0392b}
.cw-fmenu__item.is-danger:hover{background:#c0392b14;color:#c0392b}
.cw-fmenu__sep{height:1px;margin:5px 8px;background:var(--color-border-hairline)}
[data-theme="dark"] .cw-fmenu{box-shadow:0 18px 50px rgba(0,0,0,.55),0 2px 8px rgba(0,0,0,.4)}
/* ═══ PROJECT SNAPSHOTS / VERSION HISTORY ═══ */
.cw-snapbtn{gap:5px}
.cw-snap{position:absolute;inset:0;z-index:22;background:color-mix(in srgb,var(--color-bg) 55%,transparent);backdrop-filter:blur(3px);display:flex;align-items:center;justify-content:center;padding:18px}
.cw-snap__card{width:min(96vw,560px);max-height:86%;display:flex;flex-direction:column;border:1px solid var(--color-border-strong);border-radius:14px;background:var(--color-surface);box-shadow:0 18px 60px rgba(0,0,0,.25);overflow:hidden;animation:cwDiffIn .28s var(--ease-out) both}
.cw-snap__head{display:flex;align-items:center;gap:8px;padding:12px 14px;border-bottom:1px solid var(--color-border-hairline);font:600 14px var(--font-sans);color:var(--color-text-primary)}
[dir="rtl"] .cw-snap__head{font-family:var(--font-arabic)}
.cw-snap__head strong{margin-inline-end:auto}
.cw-snap__now{padding:6px 12px;border:none;border-radius:var(--radius-pill);background:var(--color-accent);color:#fff;font:600 11.5px var(--font-sans);cursor:pointer;transition:.15s}
.cw-snap__now:hover{filter:brightness(1.08)}
[dir="rtl"] .cw-snap__now{font-family:var(--font-arabic)}
.cw-snap__clear{padding:6px 10px;border:1px solid var(--color-border-strong);border-radius:var(--radius-pill);background:transparent;color:var(--color-text-muted);font:500 11px var(--font-sans);cursor:pointer;transition:.15s}
.cw-snap__clear:hover{border-color:#c0392b;color:#c0392b}
[dir="rtl"] .cw-snap__clear{font-family:var(--font-arabic)}
.cw-snap__x{width:26px;height:26px;border-radius:8px;background:var(--color-bg-subtle);color:var(--color-text-secondary);font-size:12px;cursor:pointer;transition:.15s}
.cw-snap__x:hover{background:var(--color-accent-soft);color:var(--color-accent)}
.cw-snap__list{flex:1;overflow-y:auto;padding:8px 10px;display:flex;flex-direction:column;gap:6px}
.cw-snap__row{display:flex;align-items:center;gap:10px;padding:9px 11px;border:1px solid var(--color-border-hairline);border-radius:10px;background:var(--color-bg-subtle);transition:.15s}
.cw-snap__row:hover{border-color:var(--color-border-strong)}
.cw-snap__row.is-fresh{animation:cwSnapFlash 1.1s var(--ease-out) both}
@keyframes cwSnapFlash{0%{background:var(--color-accent-soft);border-color:var(--color-accent)}100%{background:var(--color-bg-subtle)}}
.cw-snap__kind{flex:0 0 auto;font:600 9.5px var(--font-sans);letter-spacing:.04em;text-transform:uppercase;padding:3px 8px;border-radius:var(--radius-pill)}
[dir="rtl"] .cw-snap__kind{font-family:var(--font-arabic);letter-spacing:0}
.cw-snap__kind--manual{background:color-mix(in srgb,var(--color-accent) 16%,transparent);color:var(--color-accent)}
.cw-snap__kind--auto{background:var(--color-bg);color:var(--color-text-muted);border:1px solid var(--color-border-hairline)}
.cw-snap__meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.cw-snap__name{font:600 12.5px var(--font-mono);color:var(--color-text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:start}
.cw-snap__sub{font:400 11px var(--font-sans);color:var(--color-text-muted)}
[dir="rtl"] .cw-snap__sub{font-family:var(--font-arabic)}
.cw-snap__restore{flex:0 0 auto;padding:6px 14px;border:1px solid var(--color-accent);border-radius:var(--radius-pill);background:transparent;color:var(--color-accent);font:600 11.5px var(--font-sans);cursor:pointer;transition:.15s}
.cw-snap__restore:hover{background:var(--color-accent);color:#fff}
[dir="rtl"] .cw-snap__restore{font-family:var(--font-arabic)}
.cw-snap__del{flex:0 0 auto;width:24px;height:24px;border-radius:7px;background:transparent;color:var(--color-text-muted);font-size:11px;cursor:pointer;transition:.15s}
.cw-snap__del:hover{background:#c0392b1a;color:#c0392b}
.cw-snap__empty{padding:34px 18px;text-align:center;font:400 13px/1.6 var(--font-sans);color:var(--color-text-muted)}
[dir="rtl"] .cw-snap__empty{font-family:var(--font-arabic)}
/* ═══ COMMAND PALETTE (Ctrl+K / Ctrl+Shift+P) ═══ */
.cw-pal{position:absolute;inset:0;z-index:40;background:color-mix(in srgb,var(--color-bg) 45%,transparent);backdrop-filter:blur(4px);display:flex;align-items:flex-start;justify-content:center;padding:12vh 18px 18px;opacity:0;transition:opacity .16s var(--ease-out)}
.cw-pal.is-open{opacity:1}
.cw-pal__card{width:min(94vw,620px);max-height:66vh;display:flex;flex-direction:column;background:var(--color-surface);border:1px solid var(--color-border-strong);border-radius:16px;box-shadow:0 24px 70px rgba(0,0,0,.34);overflow:hidden;transform:translateY(-8px) scale(.98);transition:transform .18s var(--ease-out)}
.cw-pal.is-open .cw-pal__card{transform:none}
.cw-pal__in{border:none;border-bottom:1px solid var(--color-border-hairline);padding:16px 18px;background:transparent;color:var(--color-text-primary);font:500 15px var(--font-sans)}
[dir="rtl"] .cw-pal__in{font-family:var(--font-arabic)}
.cw-pal__in:focus{outline:none}
.cw-pal__in::placeholder{color:var(--color-text-muted)}
.cw-pal__list{flex:1;overflow-y:auto;padding:6px}
.cw-pal__row{display:flex;align-items:center;gap:12px;padding:9px 12px;border-radius:10px;cursor:pointer;color:var(--color-text-primary)}
.cw-pal__row.is-sel{background:var(--color-accent-soft)}
.cw-pal__ic{flex:0 0 auto;width:22px;text-align:center;font-size:14px;color:var(--color-accent);opacity:.9}
.cw-pal__lbl{flex:1;min-width:0;font:500 13.5px var(--font-sans);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
[dir="rtl"] .cw-pal__lbl{font-family:var(--font-arabic)}
.cw-pal__hit{background:transparent;color:var(--color-accent);font-weight:700;padding:0}
.cw-pal__hint{flex:0 0 auto;font:400 11.5px var(--font-sans);color:var(--color-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:44%}
[dir="rtl"] .cw-pal__hint{font-family:var(--font-arabic)}
.cw-pal__empty{padding:24px 14px;text-align:center;color:var(--color-text-muted);font:400 13px var(--font-sans)}
[dir="rtl"] .cw-pal__empty{font-family:var(--font-arabic)}
.cw-pal__foot{display:flex;gap:16px;padding:9px 16px;border-top:1px solid var(--color-border-hairline);background:var(--color-bg-subtle);font:400 11px var(--font-sans);color:var(--color-text-muted)}
[dir="rtl"] .cw-pal__foot{font-family:var(--font-arabic)}
.cw-pal__foot b{color:var(--color-text-secondary);font-weight:700}
/* ═══ TEMPLATES GALLERY — rich card badge + scroll cap ═══ */
.cw-home__card{max-height:min(72vh,620px);overflow-y:auto}
.cw-home__tpls{gap:7px}
.cw-home__tpl-badge{flex:0 0 auto;margin-inline-start:6px;padding:2px 8px;border-radius:var(--radius-pill);font:700 9.5px/1.4 var(--font-mono);letter-spacing:.02em;color:#fff;background:var(--_bc,var(--color-accent));white-space:nowrap;box-shadow:0 2px 6px color-mix(in srgb,var(--_bc,var(--color-accent)) 40%,transparent)}
[dir="rtl"] .cw-home__tpl-badge{font-family:var(--font-mono);letter-spacing:0}
.cw-home__tpl-check{margin-inline-start:2px}
[data-theme="dark"] .cw-home__tpl-badge{box-shadow:none;filter:saturate(.92)}
.cw-home__card::-webkit-scrollbar{width:8px}
.cw-home__card::-webkit-scrollbar-thumb{background:var(--color-border-strong);border-radius:8px}
.cw-home__card::-webkit-scrollbar-track{background:transparent}
/* ═══ FIRAS CODE — natural-language "Create with AI" on the home ═══ */
.cw-home__desc{width:100%;min-height:74px;resize:vertical;padding:11px 13px;border:1.5px solid var(--color-border);border-radius:12px;background:var(--color-surface);color:var(--color-text-primary);font:400 13.5px/1.55 var(--font-sans);margin-bottom:6px;transition:.15s}
[dir="rtl"] .cw-home__desc{font-family:var(--font-arabic)}
.cw-home__desc:focus{outline:none;border-color:var(--color-accent);box-shadow:0 0 0 3px var(--color-accent-soft)}
.cw-home__desc::placeholder{color:var(--color-text-muted)}
.cw-home__or{display:flex;align-items:center;gap:10px;margin:12px 0 4px;color:var(--color-text-muted);font:500 11px var(--font-sans)}
[dir="rtl"] .cw-home__or{font-family:var(--font-arabic)}
.cw-home__or::before,.cw-home__or::after{content:"";flex:1;height:1px;background:var(--color-border-hairline)}
.cw-home__create.is-building{opacity:.9;cursor:progress}
.cw-home__spin{display:inline-block;width:14px;height:14px;margin-inline-end:8px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;vertical-align:-2px;animation:cwSpin .7s linear infinite}
@keyframes cwSpin{to{transform:rotate(360deg)}}


/* ============================================================================
   FirasAI · CENTERED PROFESSIONAL CONVERSATION  —  FINAL
   Append at the END of styles.css (wins on source order at equal specificity).
   Additive · CSS-only · uses ONLY existing custom properties.
   Light + [data-theme="dark"] + [dir="rtl"] + mobile (<=640px).

   Model:
     · --column-w (744px, existing shared lever) keeps composer + welcome +
       chat frame perfectly edge-aligned by construction.
     · --reading-w (704px) clamps ONLY assistant/user TEXT RUNS to a calm
       ~66ch serif measure. .md and every embedded card keep full column width.
     · Assistant = bubble-less serif prose, whisper hairline between turns.
     · User = warm clay panel pinned inline-end (a hue no other chrome owns).

   SAFETY (never break — verified against app.js aiTurnEl L3877 / userTurnEl L3813):
     · .md and .msg-ai__body are NEVER clamped (cards mount + stream there).
     · Clamp is an explicit allowlist of flowing text runs; NO card class,
       NO .code-block / .plot-figure / .firas-ask / .katex-display / table.
     · .file-card / .plan-start / .msg-actions stay direct children of .turn,
       never reparented, so card.closest('.msg-ai') keeps resolving.
     · Typography rhythm is scoped to `.msg-ai__body > .md > *` DIRECT children,
       so it cannot cascade into .code-card / .file-disclosure internal markdown.
============================================================================ */

/* ---- 1. Tokens ----------------------------------------------------------- */
:root {
  /* --column-w stays 744px (defined L108) — composer/welcome/frame lever.   */
  --reading-w: 704px;                 /* prose measure (~66ch @ 17px serif)  */
  --turn-gap:  var(--space-10);       /* 40px vertical rhythm between turns  */
  --user-fill: color-mix(in srgb, var(--color-accent) 12%, var(--color-surface));
}
[data-theme="dark"] {
  /* Dark surfaces sit close in value — a slightly stronger clay reads best. */
  --user-fill: color-mix(in srgb, var(--color-accent) 16%, var(--color-surface));
}

/* ---- 2. Column frame — retune top rhythm only (centering already exists) - */
.chat-inner { padding-block-start: var(--space-10); }   /* a touch more air up top */

/* ---- 3. Turn rhythm + whisper separator ---------------------------------
   40px gap + an ultra-subtle top hairline BETWEEN turns only (never above the
   first). The authentic Claude.ai "divide without boxes". */
.chat-inner .turn { margin-bottom: var(--turn-gap); }
.chat-inner .turn + .turn {
  margin-top: var(--turn-gap);
  padding-top: var(--turn-gap);
  border-top: var(--border-hairline) solid var(--color-border-hairline);
}
.chat-inner .turn:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

/* ---- 4. User turn — warm clay panel, inline-end -------------------------- */
.msg-user__bubble {
  max-inline-size: min(80%, 544px);
  background: var(--user-fill);
  border-color: var(--color-border-hairline);
  border-radius: var(--radius-lg);      /* uniform — no asymmetric corner (RTL-safe) */
  box-shadow: none;                      /* the tint carries identity; no lift needed */
  color: var(--color-text-primary);
}
[data-theme="dark"] .msg-user__bubble {
  border-color: var(--color-border-hairline);
}

/* ---- 5. Assistant prose — measured TEXT RUNS, cards untouched ------------
   Clamp the flowing text elements to the reading measure; cards + code + math
   + tables keep the full column (some intentionally exceed prose width).
   Scoped to DIRECT children of .md so it never reaches card-internal markdown. */
.msg-ai__body > .md > p,
.msg-ai__body > .md > h1,
.msg-ai__body > .md > h2,
.msg-ai__body > .md > h3,
.msg-ai__body > .md > h4,
.msg-ai__body > .md > ul,
.msg-ai__body > .md > ol,
.msg-ai__body > .md > blockquote,
.msg-ai__body > .md > hr {
  max-inline-size: var(--reading-w);
}
/* A little more presence + separation for the assistant head. */
.msg-ai__head { margin-bottom: var(--space-4); }

/* Comfort rhythm (D5) — scoped to DIRECT prose children so it can't leak into
   a card's internal .md. Base already has serif 17/29; we relax block spacing. */
.msg-ai__body > .md > p { margin-block-end: var(--space-5); }          /* 20px */
.msg-ai__body > .md > ul,
.msg-ai__body > .md > ol { margin-block-end: var(--space-5); }
.msg-ai__body > .md > blockquote {
  margin-block: var(--space-5);
  color: var(--color-text-secondary);
}
.msg-ai__body > .md > h1,
.msg-ai__body > .md > h2 { margin-block-start: var(--space-8); }
.msg-ai__body > .md > h3,
.msg-ai__body > .md > h4 { margin-block-start: var(--space-6); }
.msg-ai__body > .md > *:first-child { margin-block-start: 0; }

/* Arabic: keep the base-tuned 1.9 leading (do NOT regress). RTL uses logical
   props above, so the measure clamp mirrors automatically. */

/* Dense elements sit calmly in serif prose — plain-markdown ONLY (direct child,
   so .code-card's internal .code-block is never touched). */
.msg-ai__body > .md > .code-block { margin-block: var(--space-5); }
.msg-ai__body .md :not(pre) > code {
  font-size: 0.86em;
  padding: 1px 5px;
  background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-xs);
  overflow-wrap: anywhere;              /* long tokens never overflow on mobile */
}
.msg-ai__body > .md > table { font-size: var(--text-sm); line-height: var(--leading-sm); }

/* ---- 6. Composer — floats over the same column, lifted (edges aligned) --- */
.composer {
  box-shadow:
    0 1px 2px rgba(0,0,0,0.04),
    0 12px 32px rgba(0,0,0,0.08);
}
.composer.is-focused {
  box-shadow:
    0 1px 2px rgba(0,0,0,0.04),
    0 12px 32px rgba(0,0,0,0.10),
    var(--shadow-focus);
}
[data-theme="dark"] .composer {
  box-shadow:
    0 1px 2px rgba(0,0,0,0.30),
    0 12px 32px rgba(0,0,0,0.42);
}
[data-theme="dark"] .composer.is-focused {
  box-shadow:
    0 1px 2px rgba(0,0,0,0.30),
    0 12px 32px rgba(0,0,0,0.48),
    var(--shadow-focus);
}

/* ---- 7. Mobile — deterministic full width, 640px (matches the prompt axis)
   Column -> 100%, clamps off, 16px gutters on all three wrappers, bubble 88%.
   Placed at top level (not nested) so it can't be missed. Note: the existing
   768px block already sets 16px gutters; these reinforce + reset the clamps. */
@media (max-width: 640px) {
  :root { --reading-w: 100%; --turn-gap: var(--space-8); }

  .chat-inner   { padding-inline: var(--space-4); }
  .welcome      { padding-inline: var(--space-4); }
  .composer-wrap { padding-inline: var(--space-4); }

  .msg-ai__body > .md > p,
  .msg-ai__body > .md > h1,
  .msg-ai__body > .md > h2,
  .msg-ai__body > .md > h3,
  .msg-ai__body > .md > h4,
  .msg-ai__body > .md > ul,
  .msg-ai__body > .md > ol,
  .msg-ai__body > .md > blockquote,
  .msg-ai__body > .md > hr { max-inline-size: none; }

  .msg-user__bubble { max-inline-size: 88%; }

  .chat-inner .turn + .turn { padding-top: var(--space-6); margin-top: var(--space-6); }
}

/* ---- 8. High-contrast courtesy ------------------------------------------- */
@media (prefers-contrast: more) {
  .chat-inner .turn + .turn { border-top-color: var(--color-border-strong); }
  .msg-user__bubble { border-color: var(--color-border-strong); }
}
/* ═══ FIRAS CODE — PLAN-THEN-BUILD step list (Claude-Code engine) ═══ */
.cw-plan__list{display:flex;flex-direction:column;gap:5px;padding:10px 4px;max-height:46vh;overflow-y:auto}
.cw-plan__row{display:flex;align-items:center;gap:9px;padding:7px 10px;border-radius:9px;background:var(--color-bg-subtle);font:500 12.5px var(--font-sans);color:var(--color-text-secondary);transition:.15s}
[dir="rtl"] .cw-plan__row{font-family:var(--font-arabic)}
.cw-plan__row--run{background:var(--color-accent-soft);color:var(--color-text-primary)}
.cw-plan__row--done{opacity:.72}
.cw-plan__row code{font-family:var(--font-mono,Consolas,monospace);font-size:11.5px;color:var(--color-text-primary);direction:ltr}
.cw-plan__does{flex:1;min-width:0;font-size:11px;color:var(--color-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cw-plan__ic{flex:0 0 auto;width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;font-size:11px}
.cw-plan__ic--ok{color:var(--color-accent);font-weight:700}
.cw-plan__ic--wait{width:8px;height:8px;border-radius:50%;border:1.5px solid var(--color-border-strong)}
.cw-plan__spin{flex:0 0 auto;width:14px;height:14px;border:2px solid var(--color-accent);border-top-color:transparent;border-radius:50%;animation:cwSpin .7s linear infinite}
.cw-plan__spin--lg{width:16px;height:16px}

/* ═══════════ FIRAS CODE v2 — direct create + recent projects + mobile ═══════════ */
.cw-home__inner{width:100%;max-width:640px;margin:0 auto;display:flex;flex-direction:column;gap:22px}
.cw-home__acts{display:flex;gap:10px;margin-top:12px}
.cw-home__create{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:7px;padding:13px 18px;border:none;border-radius:12px;background:var(--color-accent);color:#fff;font:700 14.5px var(--font-sans);cursor:pointer;transition:.15s}
[dir="rtl"] .cw-home__create{font-family:var(--font-arabic)}
.cw-home__create:hover{filter:brightness(1.07)}
.cw-home__blank{flex:0 0 auto;padding:13px 18px;border:1.5px solid var(--color-border);border-radius:12px;background:transparent;color:var(--color-text-secondary);font:600 13.5px var(--font-sans);cursor:pointer;transition:.15s}
[dir="rtl"] .cw-home__blank{font-family:var(--font-arabic)}
.cw-home__blank:hover{border-color:var(--color-accent);color:var(--color-accent)}
/* recent projects grid */
.cw-home__recent-h{font:700 13px var(--font-sans);color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px}
[dir="rtl"] .cw-home__recent-h{font-family:var(--font-arabic);letter-spacing:0}
.cw-home__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}
.cw-proj{display:flex;align-items:center;gap:10px;padding:12px 13px;border:1px solid var(--color-border-hairline);border-radius:12px;background:var(--color-surface);cursor:pointer;text-align:start;transition:.15s;position:relative}
.cw-proj:hover{border-color:var(--color-accent);transform:translateY(-1px);box-shadow:0 6px 18px color-mix(in srgb,var(--color-text-primary) 8%,transparent)}
.cw-proj__ic{flex:0 0 auto;width:34px;height:34px;display:grid;place-items:center;border-radius:9px;background:var(--color-accent-soft);color:var(--color-accent);font:700 13px var(--font-mono)}
.cw-proj__meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.cw-proj__meta strong{font:600 13.5px var(--font-sans);color:var(--color-text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
[dir="rtl"] .cw-proj__meta strong{font-family:var(--font-arabic)}
.cw-proj__meta small{font:400 11px var(--font-sans);color:var(--color-text-muted)}
.cw-proj__del{position:absolute;top:6px;inset-inline-end:6px;width:20px;height:20px;display:grid;place-items:center;border-radius:6px;color:var(--color-text-muted);font-size:10px;opacity:0;transition:.15s}
.cw-proj:hover .cw-proj__del{opacity:1}
.cw-proj__del:hover{background:#c0392b1a;color:#c0392b}
.cw-home__empty{grid-column:1/-1;display:flex;flex-direction:column;align-items:center;gap:8px;padding:26px;text-align:center;color:var(--color-text-muted);font:400 13px var(--font-sans);border:1px dashed var(--color-border-strong);border-radius:12px}
.cw-home__empty-ic{display:grid;place-items:center;opacity:.7}
[dir="rtl"] .cw-home__empty{font-family:var(--font-arabic)}
/* starter-templates gallery on the Code home */
.cw-home__tplrow{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}
.cw-tpl{display:flex;align-items:center;gap:10px;padding:12px 13px;border:1px solid var(--color-border-hairline);border-radius:12px;background:var(--color-surface);cursor:pointer;text-align:start;transition:.15s;position:relative}
.cw-tpl:hover{border-color:var(--color-accent);transform:translateY(-1px);box-shadow:0 6px 18px color-mix(in srgb,var(--color-text-primary) 8%,transparent)}
.cw-tpl__ic{flex:0 0 auto;width:34px;height:34px;display:grid;place-items:center;border-radius:9px;background:var(--color-accent-soft);font-size:17px}
.cw-tpl__meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.cw-tpl__meta strong{font:600 13.5px var(--font-sans);color:var(--color-text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
[dir="rtl"] .cw-tpl__meta strong{font-family:var(--font-arabic)}
.cw-tpl__meta small{font:400 11px var(--font-sans);color:var(--color-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
[dir="rtl"] .cw-tpl__meta small{font-family:var(--font-arabic)}
.cw-tpl__badge{flex:0 0 auto;padding:2px 8px;border-radius:999px;font:700 10px var(--font-mono);color:var(--tplc,#888);background:color-mix(in srgb,var(--tplc,#888) 14%,transparent);border:1px solid color-mix(in srgb,var(--tplc,#888) 35%,transparent)}
/* AI command bar: keep the INNER input+button centered to a comfortable measure, but the bar
   itself stays FULL-WIDTH at the bottom of the IDE (a narrow centered bar floated over the editor). */
.cw-ai{max-width:none;margin-inline:0}
.cw-ai__in{max-width:760px;margin-inline:auto}
.cw-diff__card{max-width:760px;margin-inline:auto}

/* ── MOBILE BOTTOM NAV + single-pane IDE (≤860px) ── */
.cw-mobnav{display:none}
@media (max-width:860px){
  .cw-mobnav{display:flex;flex:0 0 auto;border-top:1px solid var(--color-border-hairline);background:var(--color-surface)}
  .cw-mobnav__b{flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;padding:8px 4px 7px;background:transparent;border:none;color:var(--color-text-muted);font:600 10px var(--font-sans);cursor:pointer;transition:.12s}
  [dir="rtl"] .cw-mobnav__b{font-family:var(--font-arabic)}
  .cw-mobnav__b span{font-size:10px}
  .cw-mobnav__b.is-active{color:var(--color-accent)}
  .cw-mobnav__b.is-active::before{content:"";position:absolute;margin-top:-8px;width:22px;height:3px;border-radius:3px;background:var(--color-accent)}
  .cw-main{flex-direction:column;min-height:0}
  .cw[data-mob] .cw-tree,.cw[data-mob] .cw-ed,.cw[data-mob] .cw-right{display:none!important}
  .cw[data-mob="files"] .cw-tree{display:flex!important;width:100%!important;max-width:none;flex:1}
  .cw[data-mob="code"] .cw-ed{display:flex!important;width:100%!important;flex:1;min-height:0}
  .cw[data-mob="preview"] .cw-right{display:flex!important;width:100%!important;flex:1;min-height:0}
  .cw[data-mob="ai"] .cw-main{display:none}
  .cw-ai{display:none;order:9}
  .cw[data-mob="ai"] .cw-ai{display:flex;flex-direction:column;gap:10px;flex:1;padding:16px;max-width:none}
  .cw[data-mob="ai"] .cw-ai__in{width:100%;min-height:120px}
  .cw[data-mob="ai"] .cw-main{display:none}
  .cw-status{display:none}
  .cw-tree{max-width:none!important;border-inline-end:none;border-bottom:1px solid var(--color-border-hairline)}
  .cw-bar{flex-wrap:wrap;row-gap:6px}
  .cw-bar__name{max-width:40vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .cw-prevchrome{flex-wrap:wrap}
}

/* ═══════════ FIRAS CODE — BULLETPROOF IDE LAYOUT (SHIP1 #1, appended: source-order wins) ═══════════
   .cw is a full-height flex column. .cw-bar / .cw-ai / .cw-status / .cw-mobnav are FIXED-HEIGHT fences
   (flex:0 0 auto); .cw-main is the ONLY growing child. The AI command bar is FULL-WIDTH at the very
   bottom and can NEVER overlap / float over / squeeze the editor or preview — any viewport height,
   any project size, RTL+LTR, light+dark, desktop+mobile. */
.cw{position:relative;box-sizing:border-box}
.cw *,.cw *::before,.cw *::after{box-sizing:border-box}
.cw-bar{flex:0 0 auto}
.cw-ai{flex:0 0 auto;width:100%;max-width:none;margin-inline:0}
.cw-status{flex:0 0 auto}
.cw-mobnav{flex:0 0 auto}
.cw-main{flex:1 1 auto;min-height:0}
.cw-ed,.cw-prevwrap,.cw-console{min-height:180px}
.cw-ai{flex-wrap:nowrap}
.cw-ai__in{flex:1 1 auto;min-width:0;max-width:760px;margin-inline:auto}
.cw-ai__go{flex:0 0 auto}
@media (max-width:860px){
  .cw-main{grid-template-columns:1fr;grid-template-rows:1fr}
  .cw[data-mob="files"] .cw-tree{max-height:none!important}
  .cw[data-mob="ai"] .cw-ai{flex:1 1 auto}
  .cw-mobnav{padding-bottom:env(safe-area-inset-bottom)}
}
.cw-diff{inset:0}

/* ═══════════ FIRAS CODE — PHONE PERFECTION (SHIP1 #5, appended: source-order wins) ═══════════ */
/* Touch targets: any coarse pointer (phone/tablet), width-independent */
@media (pointer: coarse) {
  .cw-mobnav__b { min-height: 52px; }
  .cw-bar__btn  { padding: 9px 14px; min-height: 40px; }
  .cw-bar__home { width: 38px; height: 38px; }
  .cw-tab       { padding: 9px 16px; min-height: 38px; }
  .cw-conclear  { width: 34px; height: 34px; }
  .cw-prevbtn   { height: 34px; min-width: 34px; }
  .cw-prevdev   { padding: 8px 11px; }
  .cw-file      { padding: 11px 12px; }
  .cw-file__x   { width: 32px; height: 32px; font-size: 14px; opacity: .85; }
  .cw-tree__newfolder { width: 32px; height: 30px; font-size: 15px; }
}
/* PHONE (≤640px) */
@media (max-width: 640px) {
  .product-code #codeWorkspace { height: 100vh; height: 100dvh; }
  .cw-bar {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; gap: 8px;
    padding: 8px 12px calc(8px + env(safe-area-inset-top, 0px));
    padding-inline-start: max(12px, env(safe-area-inset-left));
    padding-inline-end: max(12px, env(safe-area-inset-right));
  }
  .cw-bar::-webkit-scrollbar { display: none; }
  .cw-bar__name { max-width: 34vw; }
  .cw-bar__count { display: none; }
  .cw-bar__btn { flex: 0 0 auto; }
  .cw-main {
    display: flex !important; flex-direction: column;
    grid-template-columns: none !important; grid-template-rows: none !important; min-height: 0;
  }
  .cw[data-mob] .cw-tree, .cw[data-mob] .cw-ed, .cw[data-mob] .cw-right { display: none !important; }
  .cw[data-mob="files"]   .cw-tree  { display: flex !important; flex-direction: column; width: 100% !important; max-width: none !important; max-height: none !important; flex: 1; overflow-y: auto; }
  .cw[data-mob="code"]    .cw-ed    { display: flex !important; width: 100% !important; flex: 1; min-height: 0; border-inline-end: none; border-bottom: none; }
  .cw[data-mob="preview"] .cw-right { display: flex !important; width: 100% !important; flex: 1; min-height: 0; }
  .cw-ed .CodeMirror { font-size: 14.5px; line-height: 1.7; }
  .cw-ed .CodeMirror-lines { padding: 6px 0; }
  .cw-ed__ta, .cw-ai__in, .cw-home__name, .cw-home__desc { font-size: 16px !important; }
  .cw-ai { display: none; order: 9; }
  .cw[data-mob="ai"] .cw-main { display: none !important; }
  .cw[data-mob="ai"] .cw-ai {
    display: flex; flex-direction: column; gap: 12px; flex: 1;
    padding: 16px; padding-bottom: max(16px, env(safe-area-inset-bottom));
    max-width: none; margin-inline: 0;
  }
  .cw[data-mob="ai"] .cw-ai__in {
    width: 100%; max-width: none; margin-inline: 0;
    min-height: 120px; resize: none; border-radius: 16px; line-height: 1.6; padding: 14px 16px;
  }
  .cw[data-mob="ai"] .cw-ai__go { width: 100%; padding: 15px; border-radius: 16px; font-size: 15px; min-height: 50px; }
  .cw-prevseg { display: none; }
  .cw-prevchrome { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding: 6px 10px; }
  .cw-prevchrome::-webkit-scrollbar { display: none; }
  .cw-prevstage.is-framed { padding: 0; background: #fff; }
  .cw-prevstage.is-framed .cw-preview { width: 100% !important; max-width: 100% !important; border-radius: 0; box-shadow: none; border: none; }
  .cw-status { display: flex !important; justify-content: flex-end; gap: 0; padding: 4px 12px; border-top: 1px solid var(--color-border-hairline); }
  .cw-status__seg, .cw-status__sp { display: none !important; }
  .cw-status__save { font-size: 11px; }
  .cw-mobnav {
    display: flex; flex: 0 0 auto;
    padding-bottom: env(safe-area-inset-bottom);
    padding-inline: max(0px, env(safe-area-inset-left)) max(0px, env(safe-area-inset-right));
    box-shadow: 0 -1px 0 var(--color-border-hairline), 0 -6px 24px color-mix(in srgb, var(--color-text-primary) 6%, transparent);
  }
  .cw-mobnav__b { position: relative; padding: 9px 4px 8px; font-size: 11px; }
  .cw-mobnav__b > :first-child { font-size: 17px; line-height: 1; }
  .cw-mobnav__b span { font-size: 10px; }
  .cw-mobnav__b.is-active::before { margin-top: -9px; }
  .cw-diff { padding: 12px; align-items: flex-end; }
  .cw-diff__card { width: 100%; max-height: 92%; }
  .cw-diff__acts { padding-bottom: max(12px, env(safe-area-inset-bottom)); }
  .cw-dl { font-size: 12px; word-break: break-word; }
  .cw-home { padding: 20px 16px calc(20px + env(safe-area-inset-bottom)); gap: 16px; }
  .cw-home__card { width: 100%; padding: 18px; }
  .cw-home__hero h1 { font-size: 27px; }
}
@media (max-width: 380px) {
  .cw-bar__name { max-width: 26vw; }
  .cw-mobnav__b span { display: none; }
  .cw-mobnav__b { padding: 12px 4px; }
  .cw-mobnav__b > :first-child { font-size: 19px; }
}
/* Desktop AI bar: keep the <textarea> a clean single-line pill (only phone lets it grow). */
.cw-ai__in{resize:none;line-height:1.4;height:auto;min-height:0;overflow:hidden}
@media(min-width:641px){.cw:not([data-mob="ai"]) .cw-ai__in{white-space:nowrap;overflow-x:auto}}

/* ═══ FIRAS CODE — game verify chip (reuses .cw-diff overlay host + .cw-plan spinner) ═══ */
.cw-verify{display:flex;align-items:center;gap:12px;padding:16px 20px;border:1px solid var(--color-border);border-radius:var(--radius-lg);background:var(--color-surface);box-shadow:0 18px 60px rgba(0,0,0,.22);max-width:min(92vw,420px);animation:cwIn .28s var(--ease-out) both}
.cw-verify__t{font:600 13.5px var(--font-sans);color:var(--color-text-primary)}
[dir="rtl"] .cw-verify__t{font-family:var(--font-arabic)}
[data-theme="dark"] .cw-verify{box-shadow:0 18px 60px rgba(0,0,0,.5)}
@media (max-width:860px){.cw-verify{max-width:88vw;padding:14px 16px}.cw-verify__t{font-size:12.5px}}

/* ═══════════ FIRAS CODE — UNIFIED PREMIUM DESIGN SYSTEM PASS (cw--v3, SHIP1 #15, appended LAST) ═══════════
   Cohesive restyle of every .cw-* surface. Scoped to .cw--v3 (marker on the IDE root) → additive,
   source-order-last, instantly revertible. Light+dark, RTL+LTR, mobile. Logical props only. */
.cw--v3{
  --cwv-radius-chip:10px; --cwv-radius-panel:14px; --cwv-radius-card:18px;
  --cwv-pad-bar:10px 16px; --cwv-gap:10px;
  --cwv-motion:.18s var(--ease-out); --cwv-motion-fast:.13s var(--ease-out);
  --cwv-shadow-panel:0 10px 30px color-mix(in srgb,var(--color-text-primary) 8%,transparent);
  --cwv-shadow-float:0 20px 60px color-mix(in srgb,var(--color-text-primary) 22%,transparent), 0 2px 8px rgba(0,0,0,.08);
  --cwv-hair:var(--color-border-hairline);
  position:relative;
}
[data-theme="dark"] .cw--v3{
  --cwv-shadow-panel:0 10px 30px rgba(0,0,0,.45);
  --cwv-shadow-float:0 20px 60px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.4);
}
.cw--v3 .cw-bar,.cw--v3 .cw-ai,.cw--v3 .cw-status,.cw--v3 .cw-mobnav{flex:0 0 auto}
.cw--v3 .cw-main{min-height:180px}
.cw--v3 .cw-bar{gap:var(--cwv-gap);padding:var(--cwv-pad-bar);background:color-mix(in srgb,var(--color-surface) 86%,transparent);backdrop-filter:saturate(1.4) blur(10px);-webkit-backdrop-filter:saturate(1.4) blur(10px);border-bottom:1px solid var(--cwv-hair)}
.cw--v3 .cw-bar__home{width:34px;height:34px;border-radius:var(--cwv-radius-chip);background:var(--color-bg-subtle);color:var(--color-text-secondary);transition:background var(--cwv-motion),color var(--cwv-motion),transform var(--cwv-motion)}
.cw--v3 .cw-bar__home:hover{background:var(--color-accent-soft);color:var(--color-accent);transform:translateY(-1px)}
.cw--v3 .cw-bar__home:active{transform:translateY(0) scale(.94)}
.cw--v3 .cw-bar__logo{width:30px;height:30px;border-radius:var(--cwv-radius-chip);display:grid;place-items:center;font:800 11px var(--font-mono);color:#fff;background:linear-gradient(140deg,var(--color-accent),#5b8def);box-shadow:0 4px 14px color-mix(in srgb,var(--color-accent) 34%,transparent)}
.cw--v3 .cw-bar__name{font:650 14px var(--font-sans);letter-spacing:-.01em}
.cw--v3 .cw-bar__count{font:600 11px var(--font-mono);color:var(--color-text-muted);background:var(--color-bg-subtle);border:1px solid var(--cwv-hair);border-radius:var(--radius-pill);padding:3px 10px;direction:ltr;unicode-bidi:isolate}
.cw--v3 .cw-bar__btn{padding:8px 15px;border-radius:var(--radius-pill);border:1px solid var(--color-border);background:var(--color-surface);color:var(--color-text-primary);font:550 12.5px var(--font-sans);white-space:nowrap;transition:border-color var(--cwv-motion),color var(--cwv-motion),background var(--cwv-motion),transform var(--cwv-motion),box-shadow var(--cwv-motion)}
.cw--v3 .cw-bar__btn:hover{border-color:var(--color-accent);color:var(--color-accent);transform:translateY(-1px);box-shadow:0 5px 14px color-mix(in srgb,var(--color-accent) 18%,transparent)}
.cw--v3 .cw-bar__btn:active{transform:translateY(0) scale(.97)}
.cw--v3 .cw-bar__btn.cw-run{border-color:transparent;color:#fff;background:linear-gradient(120deg,var(--color-accent),#3fa08c);box-shadow:0 5px 16px color-mix(in srgb,var(--color-accent) 28%,transparent)}
.cw--v3 .cw-bar__btn.cw-run:hover{color:#fff;box-shadow:0 8px 22px color-mix(in srgb,var(--color-accent) 40%,transparent)}
.cw--v3 .cw-tree{background:var(--color-surface);border-inline-end:1px solid var(--cwv-hair)}
.cw--v3 .cw-tree__head{padding:12px 14px 9px;font:700 10.5px var(--font-sans);letter-spacing:.14em;text-transform:uppercase;color:var(--color-text-muted)}
[dir="rtl"] .cw--v3 .cw-tree__head{font-family:var(--font-arabic);letter-spacing:.02em}
.cw--v3 .cw-tree__n{margin-inline-start:auto;font:600 10px var(--font-mono);background:var(--color-bg-subtle);border:1px solid var(--cwv-hair);border-radius:var(--radius-pill);padding:2px 8px}
.cw--v3 .cw-tree__newfolder{width:26px;height:24px;border-radius:8px;color:var(--color-text-muted);transition:background var(--cwv-motion),color var(--cwv-motion),transform var(--cwv-motion)}
.cw--v3 .cw-tree__newfolder:hover{background:var(--color-accent-soft);color:var(--color-accent);transform:scale(1.06)}
.cw--v3 .cw-file{gap:8px;padding:8px 12px;border-bottom:1px solid var(--cwv-hair);border-inline-start:2px solid transparent;transition:background var(--cwv-motion),color var(--cwv-motion),border-color var(--cwv-motion)}
.cw--v3 .cw-file:hover{background:var(--color-bg-subtle)}
.cw--v3 .cw-file.is-active{background:var(--color-accent-soft);color:var(--color-accent);border-inline-start-color:var(--color-accent);font-weight:600}
.cw--v3 .cw-file__dot{box-shadow:0 0 0 3px color-mix(in srgb,var(--color-bg-subtle) 70%,transparent)}
.cw--v3 .cw-file__x{width:22px;height:22px;border-radius:7px;color:var(--color-text-muted);transition:opacity var(--cwv-motion),background var(--cwv-motion),color var(--cwv-motion)}
.cw--v3 .cw-file__x:hover{background:#c0392b1a;color:#c0392b}
.cw--v3 .cw-ed{border-inline-end:1px solid var(--cwv-hair)}
.cw--v3 .cw-ed .CodeMirror{padding-block:4px}
.cw--v3 .cw-ed .CodeMirror-scrollbar-filler,.cw--v3 .cw-ed .CodeMirror-gutter-filler{background:transparent}
.cw--v3 .cw-ed .CodeMirror-selected{border-radius:2px}
.cw--v3 .cw-right{background:var(--color-surface)}
.cw--v3 .cw-tabs{gap:6px;padding:8px 10px;border-bottom:1px solid var(--cwv-hair)}
.cw--v3 .cw-tab{padding:6px 15px;border-radius:var(--radius-pill);color:var(--color-text-secondary);font:550 12.5px var(--font-sans);transition:background var(--cwv-motion),color var(--cwv-motion)}
.cw--v3 .cw-tab:hover{background:var(--color-bg-subtle);color:var(--color-text-primary)}
.cw--v3 .cw-tab.is-active{background:var(--color-accent-soft);color:var(--color-accent);font-weight:650;box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--color-accent) 22%,transparent)}
.cw--v3 .cw-tab.has-errors::after{box-shadow:0 0 0 3px color-mix(in srgb,#c0392b 24%,transparent);animation:cwvErrPulse 1.6s ease-in-out infinite}
@keyframes cwvErrPulse{0%,100%{opacity:1}50%{opacity:.45}}
.cw--v3 .cw-conclear{width:28px;height:28px;border-radius:8px;color:var(--color-text-muted);transition:background var(--cwv-motion),color var(--cwv-motion),transform var(--cwv-motion)}
.cw--v3 .cw-conclear:hover{background:var(--color-bg-subtle);color:var(--color-text-primary);transform:rotate(-8deg)}
.cw--v3 .cw-prevwrap{margin:0 12px 12px;border:1px solid var(--cwv-hair);border-radius:var(--cwv-radius-panel);box-shadow:var(--cwv-shadow-panel);transition:box-shadow var(--dur-slow) var(--ease-out)}
.cw--v3 .cw-prevchrome{padding:8px 12px;gap:10px;background:var(--color-bg-subtle);border-bottom:1px solid var(--cwv-hair)}
.cw--v3 .cw-prevdots i{transition:transform var(--cwv-motion)}
.cw--v3 .cw-prevwrap:hover .cw-prevdots i:nth-child(1){transform:scale(1.15)}
.cw--v3 .cw-prevaddr{font:550 10.5px var(--font-mono);color:var(--color-text-muted);background:var(--color-surface);border:1px solid var(--cwv-hair);border-radius:var(--radius-pill);padding:4px 14px}
.cw--v3 .cw-prevseg{gap:2px;padding:3px;border-radius:var(--radius-pill);background:var(--color-surface);border:1px solid var(--cwv-hair)}
.cw--v3 .cw-prevdev{padding:5px 11px;border-radius:var(--radius-pill);font:600 10.5px var(--font-sans);transition:background var(--cwv-motion),color var(--cwv-motion)}
.cw--v3 .cw-prevdev:hover{color:var(--color-text-primary)}
.cw--v3 .cw-prevdev.is-active{background:var(--color-accent-soft);color:var(--color-accent);box-shadow:0 1px 4px color-mix(in srgb,var(--color-accent) 18%,transparent)}
.cw--v3 .cw-prevbtn{height:28px;min-width:28px;border-radius:9px;border:1px solid var(--cwv-hair);background:var(--color-surface);color:var(--color-text-secondary);transition:border-color var(--cwv-motion),color var(--cwv-motion),transform var(--cwv-motion)}
.cw--v3 .cw-prevbtn:hover{border-color:var(--color-accent);color:var(--color-accent);transform:translateY(-1px)}
.cw--v3 .cw-prevbtn:active{transform:translateY(0) scale(.94)}
.cw--v3 .cw-prevreload:hover{transform:rotate(90deg)}
.cw--v3 .cw-console{margin:0 12px 12px;border-radius:var(--cwv-radius-panel);border:1px solid color-mix(in srgb,#ffffff 8%,transparent);background:linear-gradient(180deg,#171715,#131311);box-shadow:var(--cwv-shadow-panel)}
.cw--v3 .cw-console__list{padding:10px 12px;gap:3px}
.cw--v3 .cw-conrow{font:12px/1.55 var(--font-mono);padding:3px 8px;border-radius:6px;border-bottom:none;position:relative}
.cw--v3 .cw-conrow + .cw-conrow{border-top:1px solid #ffffff08}
.cw--v3 .cw-conrow--error{color:#ff8a80;background:#c0392b12}
.cw--v3 .cw-conrow--warn{color:#ffd54f;background:#b9770e10}
.cw--v3 .cw-conrow--info{color:#81d4fa}
.cw--v3 .cw-ai{gap:var(--cwv-gap);padding:12px 16px;border-top:1px solid var(--cwv-hair);background:color-mix(in srgb,var(--color-surface) 92%,transparent);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);max-width:none;margin-inline:0}
.cw--v3 .cw-ai__in{max-width:760px;margin-inline:auto;padding:12px 16px;border:1.5px solid var(--color-border-strong);border-radius:var(--radius-pill);background:var(--color-bg-subtle);font:450 13.5px var(--font-sans);transition:border-color var(--cwv-motion),box-shadow var(--cwv-motion),background var(--cwv-motion)}
.cw--v3 .cw-ai__in:hover{border-color:var(--color-accent-ring)}
.cw--v3 .cw-ai__in:focus{outline:none;border-color:var(--color-accent);background:var(--color-surface);box-shadow:0 0 0 4px color-mix(in srgb,var(--color-accent) 14%,transparent)}
.cw--v3 .cw-ai__go{padding:0 22px;border:none;border-radius:var(--radius-pill);color:var(--color-on-accent);font:650 13px var(--font-sans);white-space:nowrap;background:linear-gradient(120deg,var(--color-accent),#5b8def);box-shadow:0 6px 18px color-mix(in srgb,var(--color-accent) 30%,transparent);transition:transform var(--cwv-motion),box-shadow var(--cwv-motion),filter var(--cwv-motion)}
.cw--v3 .cw-ai__go:hover{transform:translateY(-1px);box-shadow:0 9px 26px color-mix(in srgb,var(--color-accent) 44%,transparent)}
.cw--v3 .cw-ai__go:active{transform:translateY(0) scale(.97)}
.cw--v3 .cw-ai__go:disabled{opacity:.55;transform:none;box-shadow:none;filter:grayscale(.3);cursor:default}
.cw--v3 .cw-status{gap:16px;padding:6px 16px;border-top:1px solid var(--cwv-hair);background:var(--color-bg-subtle);font:550 11.5px var(--font-mono);color:var(--color-text-secondary)}
.cw--v3 .cw-status__save{padding:3px 11px;border-radius:var(--radius-pill);font:600 11px var(--font-sans);transition:background var(--cwv-motion),color var(--cwv-motion)}
.cw--v3 .cw-status__save.is-saved{background:color-mix(in srgb,var(--color-accent) 15%,transparent);color:var(--color-accent)}
.cw--v3 .cw-status__save.is-editing{background:#b9770e20;color:#b9770e}
.cw--v3 .cw-status__save.is-saving{background:#5b8def22;color:#5b8def}
.cw--v3 .cw-diff{background:color-mix(in srgb,var(--color-bg) 58%,transparent);backdrop-filter:blur(5px);-webkit-backdrop-filter:blur(5px)}
.cw--v3 .cw-diff__card{max-width:760px;margin-inline:auto;border-radius:var(--cwv-radius-card);border:1px solid var(--color-border);box-shadow:var(--cwv-shadow-float)}
.cw--v3 .cw-diff__head{padding:14px 18px;font:650 14px var(--font-sans);background:linear-gradient(100deg,color-mix(in srgb,var(--color-accent) 10%,transparent),transparent)}
.cw--v3 .cw-diff__file{border:1px solid var(--cwv-hair);border-radius:var(--radius-md);transition:border-color var(--cwv-motion)}
.cw--v3 .cw-diff__file:hover{border-color:var(--color-border-strong)}
.cw--v3 .cw-diff__file summary{padding:10px 13px;background:var(--color-bg-subtle);font:550 12.5px var(--font-mono)}
.cw--v3 .cw-diff__badge{font-weight:650;padding:2px 9px;border-radius:var(--radius-pill)}
.cw--v3 .cw-diff__apply{padding:12px;border:none;border-radius:var(--radius-pill);color:var(--color-on-accent);font:650 13.5px var(--font-sans);background:linear-gradient(120deg,var(--color-accent),#3fa08c);box-shadow:0 6px 18px color-mix(in srgb,var(--color-accent) 28%,transparent);transition:transform var(--cwv-motion),box-shadow var(--cwv-motion)}
.cw--v3 .cw-diff__apply:hover{transform:translateY(-1px);box-shadow:0 9px 24px color-mix(in srgb,var(--color-accent) 40%,transparent)}
.cw--v3 .cw-diff__apply:active{transform:translateY(0) scale(.98)}
.cw--v3 .cw-diff__cancel{padding:12px 24px;border:1px solid var(--color-border);border-radius:var(--radius-pill);transition:border-color var(--cwv-motion),color var(--cwv-motion),background var(--cwv-motion)}
.cw--v3 .cw-diff__cancel:hover{border-color:var(--color-border-strong);background:var(--color-bg-subtle)}
.cw--v3 .cw-pal__card{border-radius:var(--cwv-radius-card);box-shadow:var(--cwv-shadow-float)}
.cw--v3 .cw-pal__in{padding:17px 20px;font:550 15px var(--font-sans)}
.cw--v3 .cw-pal__row{padding:10px 13px;border-radius:var(--radius-md);transition:background var(--cwv-motion-fast),transform var(--cwv-motion-fast)}
.cw--v3 .cw-pal__row:hover{background:var(--color-bg-subtle)}
.cw--v3 .cw-pal__row.is-sel{background:var(--color-accent-soft);box-shadow:inset 2px 0 0 var(--color-accent)}
[dir="rtl"] .cw--v3 .cw-pal__row.is-sel{box-shadow:inset -2px 0 0 var(--color-accent)}
.cw--v3 .cw-snap__card{border-radius:var(--cwv-radius-card);box-shadow:var(--cwv-shadow-float)}
.cw--v3 .cw-snap__row{padding:10px 12px;border:1px solid var(--cwv-hair);border-radius:var(--radius-md);transition:border-color var(--cwv-motion),transform var(--cwv-motion),box-shadow var(--cwv-motion)}
.cw--v3 .cw-snap__row:hover{border-color:var(--color-border-strong);transform:translateY(-1px);box-shadow:0 4px 12px color-mix(in srgb,var(--color-text-primary) 6%,transparent)}
.cw--v3 .cw-snap__restore{transition:background var(--cwv-motion),color var(--cwv-motion),transform var(--cwv-motion)}
.cw--v3 .cw-snap__restore:hover{transform:translateY(-1px)}
.cw--v3 .cw-fmenu{border-radius:var(--cwv-radius-panel);box-shadow:var(--cwv-shadow-float)}
.cw--v3 .cw-fmenu__item{padding:9px 13px;border-radius:9px;transition:background var(--cwv-motion-fast),color var(--cwv-motion-fast)}
@media (max-width:860px){
  .cw--v3 .cw-bar{padding:9px 12px;row-gap:7px}
  .cw--v3 .cw-mobnav{border-top:1px solid var(--cwv-hair);background:color-mix(in srgb,var(--color-surface) 94%,transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);padding-bottom:env(safe-area-inset-bottom)}
  .cw--v3 .cw-mobnav__b{min-height:46px;padding:9px 4px 8px;transition:color var(--cwv-motion-fast)}
  .cw--v3 .cw-mobnav__b:active{transform:scale(.92)}
  .cw--v3 .cw-mobnav__b.is-active::before{box-shadow:0 0 8px color-mix(in srgb,var(--color-accent) 55%,transparent)}
  .cw--v3 .cw-prevwrap,.cw--v3 .cw-console{margin:0 8px 8px}
  .cw--v3 .cw-bar__btn{min-height:38px}
}
@media (pointer:coarse){
  .cw--v3 .cw-tab,.cw--v3 .cw-prevbtn,.cw--v3 .cw-prevdev,.cw--v3 .cw-file__x,.cw--v3 .cw-conclear{min-height:38px}
  .cw--v3 .cw-file__x{opacity:1}
}
@media (prefers-reduced-motion:reduce){
  .cw--v3 *,.cw--v3 *::before,.cw--v3 *::after{animation-duration:.01ms!important;transition-duration:.06s!important}
  .cw--v3 .cw-bar__btn:hover,.cw--v3 .cw-bar__home:hover,.cw--v3 .cw-ai__go:hover,.cw--v3 .cw-prevbtn:hover,.cw--v3 .cw-snap__row:hover,.cw--v3 .cw-diff__apply:hover,.cw--v3 .cw-prevreload:hover{transform:none}
}

/* ═══════════ FIRAS CODE — CHAT THREAD (real persisted conversation with the AI) ═══════════ */
.cw-thread{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
.cw-thread__list{flex:1;min-height:0;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:12px}
.cw-thread__empty{margin:auto;max-width:340px;text-align:center;color:var(--color-text-muted);font:400 13px/1.6 var(--font-sans)}
[dir="rtl"] .cw-thread__empty{font-family:var(--font-arabic)}
.cw-thread__turn{display:flex;flex-direction:column;gap:4px;max-width:88%}
.cw-thread__turn--me{align-self:flex-end;align-items:flex-end}
.cw-thread__turn--ai{align-self:flex-start;align-items:flex-start}
.cw-thread__who{display:flex;align-items:center;gap:7px;font:600 10.5px var(--font-sans);letter-spacing:.02em;color:var(--color-text-muted);text-transform:uppercase}
.cw-thread__chip{text-transform:none;letter-spacing:0;font:600 10px var(--font-mono);color:var(--color-accent);background:var(--color-accent-soft);border-radius:var(--radius-pill);padding:1px 8px}
.cw-thread__bubble{font:400 13.5px/1.6 var(--font-sans);padding:9px 13px;border-radius:14px;white-space:pre-wrap;word-break:break-word}
[dir="rtl"] .cw-thread__bubble{font-family:var(--font-arabic)}
.cw-thread__turn--me .cw-thread__bubble{background:color-mix(in srgb,var(--color-accent) 14%,var(--color-surface));color:var(--color-text-primary);border-bottom-right-radius:5px}
.cw-thread__turn--ai .cw-thread__bubble{background:var(--color-bg-subtle);border:1px solid var(--color-border-hairline);color:var(--color-text-primary);border-bottom-left-radius:5px}
/* design-system pass */
.cw--v3 .cw-thread{background:var(--color-surface)}
.cw--v3 .cw-thread__bubble{box-shadow:0 1px 2px color-mix(in srgb,var(--color-text-primary) 5%,transparent)}

/* ═══════════ iOS / NATIVE-APP POLISH — safe areas, touch, no zoom-jank ═══════════
   The app shell previously only handled safe areas inside Firas Code, so on a notched
   iPhone the topbar sat under the status bar. Appended last so source-order wins. */
/* 1) topbar clears the notch/status bar; sidebar clears notch + home indicator */
.topbar{
  block-size:calc(var(--topbar-h) + var(--safe-top));
  min-block-size:calc(var(--topbar-h) + var(--safe-top));
  padding-block-start:var(--safe-top);
}
.sidebar{
  padding-block-start:var(--safe-top);
  padding-block-end:var(--safe-bottom);
}
/* 2) no blue tap flash; no long-press callout on chrome */
*{-webkit-tap-highlight-color:transparent}
button,a,[role="button"],.btn,.chip,.tab{-webkit-touch-callout:none}
/* 3) iOS auto-zooms on focus when an input's font-size < 16px — pin it on phones */
@media (max-width:640px){
  input,textarea,select{font-size:16px}
}
/* 4) dynamic viewport instead of 100vh (Safari bar / app chrome) */
.share-page{min-height:100vh;min-height:100dvh}
@media (max-width:860px){
  .ann-reader{max-height:100dvh;min-height:100dvh}
}
/* 5) kill the rubber-band bounce that exposes the system background */
html,body{overscroll-behavior:none}
/* 6) momentum scrolling on the scroll containers */
.chat-scroll,.sidebar,.cw-tree,.cw-thread__list,.cw-console__list{-webkit-overflow-scrolling:touch}

/* ════════════════════════════════════════════════════════════════
   NATIVE SHELL — keyboard lockstep · iOS-26 liquid glass · de-webbify
   Every rule gates on html.native-shell / .native-ios / .native-android,
   added by app.js ONLY when Capacitor.isNativePlatform() === true, so a
   plain browser is completely unaffected. Appended last → source wins.
   ════════════════════════════════════════════════════════════════ */

/* 1 — iOS: the composer is glued to the keyboard and rides it 1:1 -------- */
html.native-ios .composer-wrap{
  transform: translateY(calc(-1 * var(--kb, 0px)));
  transition: transform var(--kb-dur) var(--ease-ios),
              padding-block-end var(--kb-dur) var(--ease-ios);
}
html.native-ios.kb-open .composer-wrap{
  padding-block-end: var(--space-4); /* sit flush on the keyboard; drop the safe-area gap */
  will-change: transform;            /* promote a layer only while the keyboard is up */
}
html.native-ios .chat-scroll{ padding-bottom: calc(150px + var(--kb, 0px)); }
html.native-ios.kb-instant .composer-wrap{ transition: none; } /* didShow drift-fix: snap, no slide */

/* 2 — Android: lift by the real IME inset (edge-to-edge, window never shrinks) */
html.native-android .composer-wrap{
  transform: translateY(calc(-1 * var(--kb-inset, 0px)));
  transition: transform var(--dur-base) var(--ease-out);
  padding-block-end: max(var(--space-4), var(--safe-bottom));
}
html.native-android .chat-scroll{ padding-bottom: calc(150px + var(--kb-inset, 0px)); }

@media (prefers-reduced-motion: reduce){
  html.native-shell .composer-wrap{ transition: none; }
}

/* 3 — iOS-26 Liquid Glass chrome + buttons (theme-adaptive tint) --------- */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
  html.native-shell .topbar,
  html.native-shell .composer{
    -webkit-backdrop-filter: blur(22px) saturate(180%);
    backdrop-filter: blur(22px) saturate(180%);
    background-color: color-mix(in srgb, var(--color-surface) 62%, transparent);
    border: var(--border-hairline) solid color-mix(in srgb, var(--color-text-primary) 9%, transparent);
    box-shadow: inset 0 1px 0 color-mix(in srgb, #ffffff 14%, transparent),
                0 8px 30px rgba(0,0,0,.30);
  }
  html.native-shell .composer__send,
  html.native-shell .composer__attach,
  html.native-shell .icon-btn{
    -webkit-backdrop-filter: blur(14px) saturate(160%);
    backdrop-filter: blur(14px) saturate(160%);
    background-image: linear-gradient(180deg,
      color-mix(in srgb, #ffffff 16%, transparent),
      color-mix(in srgb, #ffffff 4%, transparent));
    border: var(--border-hairline) solid color-mix(in srgb, #ffffff 16%, transparent);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.20), 0 2px 10px rgba(0,0,0,.24);
    transition: transform var(--dur-fast) var(--spring), filter var(--dur-fast) var(--ease-out);
  }
  html.native-shell .composer__send:active,
  html.native-shell .composer__attach:active,
  html.native-shell .icon-btn:active{ transform: scale(.94); filter: brightness(1.16); }
}

/* 4 — De-webbify: kill the browser tells (native only) ------------------- */
html.native-shell,
html.native-shell *{
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}
html.native-shell{ -webkit-text-size-adjust: 100%; }
html.native-shell html, html.native-shell body{ overscroll-behavior: none; }
html.native-shell .chat-scroll,
html.native-shell .sidebar,
html.native-shell .cw-tree,
html.native-shell .cw-thread__list{ overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
/* …but keep selection where users legitimately need it: inputs, message text, code editor */
html.native-shell textarea,
html.native-shell input,
html.native-shell [contenteditable="true"],
html.native-shell .md, html.native-shell .md *,
html.native-shell .msg-ai__body, html.native-shell .msg-ai__body *,
html.native-shell pre, html.native-shell code,
html.native-shell .CodeMirror, html.native-shell .CodeMirror *{
  -webkit-user-select: text;
  user-select: text;
}

/* ============================================================================
   VOICE DICTATION — mic button, recording bar, dialect picker
   ========================================================================== */
.composer { position: relative; }
/* The mic inherits .icon-btn's 3px radius, which is invisible until something FILLS it — and
   `is-on` fills it with solid accent. The result was a hard 36px square sitting between the
   pill-shaped send button and the rounded composer, the one square edge in the whole cluster.
   Rounding it here (not on .icon-btn) keeps every other icon button exactly as it was, and it
   is set for ALL states so the hover shape and the recording shape are the same shape. */
.composer__mic { border-radius: var(--radius-pill); }
.composer__mic.is-on { color: #fff; background: var(--color-accent); border-radius: var(--radius-pill); }
/* Recording bar overlays the composer row while recording / transcribing */
.mic-rec {
  position: absolute; inset: 0; z-index: 6;
  display: flex; align-items: center; gap: 10px;
  padding: 0 12px; border-radius: inherit;
  background: var(--color-surface);
}
.mic-rec[hidden] { display: none; }
.mic-rec__dot { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; background: #e5484d; animation: micPulse 1.15s ease-in-out infinite; }
@keyframes micPulse { 50% { opacity: .3; transform: scale(.8); } }
.mic-rec__viz { flex: 1 1 auto; min-width: 0; height: 34px; }
.mic-rec__viz canvas { width: 100%; height: 100%; display: block; }
.mic-rec__time { flex: 0 0 auto; font: 600 12.5px var(--font-mono); color: var(--color-text-muted); min-width: 34px; text-align: center; font-variant-numeric: tabular-nums; }
.mic-rec__status { flex: 1 1 auto; font: 500 13px var(--font-sans); color: var(--color-text-muted); text-align: center; }
[dir="rtl"] .mic-rec__status { font-family: var(--font-arabic); }
.mic-rec__cancel, .mic-rec__done {
  flex: 0 0 auto; width: 34px; height: 34px; border: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer; transition: .15s;
  background: transparent; color: var(--color-text-muted);
}
.mic-rec__cancel svg, .mic-rec__done svg { width: 17px; height: 17px; }
.mic-rec__cancel:hover { color: #e5484d; background: color-mix(in srgb, #e5484d 12%, transparent); }
.mic-rec__done { color: #fff; background: var(--color-accent); }
.mic-rec__done:hover { transform: scale(1.06); }
.mic-rec__lang {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px; border: 1px solid var(--color-border-hairline); border-radius: var(--radius-pill);
  background: var(--color-bg-subtle); color: var(--color-text-secondary);
  font: 600 12px var(--font-sans); cursor: pointer; transition: .15s; white-space: nowrap;
}
[dir="rtl"] .mic-rec__lang { font-family: var(--font-arabic); }
.mic-rec__lang:hover { border-color: var(--color-accent); color: var(--color-accent); }
/* Dialect / language picker */
.mic-menu {
  position: absolute; bottom: calc(100% + 10px); inset-inline-end: 6px; z-index: 40;
  min-width: 240px; max-height: 330px; overflow: auto; overscroll-behavior: contain;
  padding: 6px; border: 1px solid var(--color-border-hairline); border-radius: 14px;
  background: var(--color-surface);
  box-shadow: 0 4px 10px rgba(0,0,0,.08), 0 16px 40px rgba(0,0,0,.16);
  animation: micMenuIn .16s ease;
}
@keyframes micMenuIn { from { opacity: 0; transform: translateY(6px) scale(.98); } }
.mic-menu[hidden] { display: none; }
.mic-menu__title {
  padding: 8px 12px 6px; font: 700 11px var(--font-sans); letter-spacing: .06em; text-transform: uppercase;
  color: var(--color-text-muted);
}
[dir="rtl"] .mic-menu__title { font-family: var(--font-arabic); letter-spacing: 0; }
.mic-menu__item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 11px; border: 0; border-radius: 9px; cursor: pointer;
  background: transparent; color: var(--color-text-secondary);
  font: 500 13.5px var(--font-sans); text-align: start; transition: .12s;
}
[dir="rtl"] .mic-menu__item { font-family: var(--font-arabic); }
.mic-menu__item:hover { background: var(--color-bg-subtle); }
.mic-menu__item.is-active { color: var(--color-accent); background: var(--color-accent-soft); font-weight: 600; }
.mic-menu__flag { flex: 0 0 auto; width: 24px; text-align: center; font-size: 15px; }
.mic-menu__label { flex: 1 1 auto; min-width: 0; }
.mic-menu__check { flex: 0 0 auto; width: 16px; height: 16px; opacity: 0; transform: scale(.6); transition: .15s; }
.mic-menu__check svg { width: 16px; height: 16px; display: block; }
.mic-menu__item.is-active .mic-menu__check { opacity: 1; transform: scale(1); }
/* Current-dialect value in the tools (+) menu row */
.tools-menu__value { flex: 0 0 auto; font: 600 11.5px var(--font-sans); color: var(--color-text-muted); white-space: nowrap; }
[dir="rtl"] .tools-menu__value { font-family: var(--font-arabic); }
@media (prefers-reduced-motion: reduce) {
  .mic-rec__dot { animation: none; }
  .mic-menu { animation: none; }
  .mic-rec__done:hover { transform: none; }
}
/* Live-dictation variant of the bar: status text instead of the waveform */
.mic-rec.is-live .mic-rec__viz { display: none; }
.mic-rec.is-live .mic-rec__status { text-align: start; }

/* ============================================================================
   VOICE CALL — composer call button + full-screen call overlay
   ========================================================================== */
/* The call button is a Firas-AI-only feature — hide it on Agent/Code. */
body.product-agent .composer__call,
body.product-code .composer__call { display: none; }
.composer__call svg { width: 20px; height: 20px; }

.call {
  position: fixed; inset: 0; z-index: 1200;
  display: grid; place-items: center;
  opacity: 0; visibility: hidden; transition: opacity .26s ease, visibility .26s ease;
}
.call[hidden] { display: none; }
.call.is-open { opacity: 1; visibility: visible; }
.call__bg {
  position: absolute; inset: 0;
  background:
    radial-gradient(120% 80% at 50% 12%, color-mix(in srgb, var(--color-accent) 26%, transparent), transparent 60%),
    var(--color-bg);
  backdrop-filter: blur(6px);
}
:root[data-theme="dark"] .call__bg { background: radial-gradient(120% 80% at 50% 12%, color-mix(in srgb, var(--color-accent) 30%, transparent), transparent 60%), #14201d; }
.call__inner {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center;
  gap: 14px; padding: 32px; text-align: center; max-width: 460px; width: 92%;
}
/* Orb */
.call__orb { position: relative; width: 176px; height: 176px; display: grid; place-items: center; cursor: pointer; margin-bottom: 6px; }
.call__core {
  position: relative; z-index: 3; width: 108px; height: 108px; border-radius: 50%;
  display: grid; place-items: center;
  background: radial-gradient(circle at 32% 28%, color-mix(in srgb, var(--color-accent) 70%, #fff), var(--color-accent) 70%);
  box-shadow: 0 10px 40px color-mix(in srgb, var(--color-accent) 45%, transparent), inset 0 2px 10px rgba(255,255,255,.25);
  transition: transform .25s ease;
}
.call__core .nib { transform: scale(1.25); }
.call__ring {
  position: absolute; inset: 0; margin: auto; width: 108px; height: 108px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--color-accent) 55%, transparent);
  opacity: 0; pointer-events: none;
}
/* Idle: a gentle breathing core */
.call__orb .call__core { animation: callBreathe 3.4s ease-in-out infinite; }
@keyframes callBreathe { 50% { transform: scale(1.05); } }
/* Listening: expanding sonar rings */
.call__orb.is-listening .call__ring { animation: callSonar 1.8s ease-out infinite; }
.call__orb.is-listening .call__ring:nth-child(2) { animation-delay: .6s; }
.call__orb.is-listening .call__ring:nth-child(3) { animation-delay: 1.2s; }
@keyframes callSonar { 0% { opacity: .5; transform: scale(1); } 100% { opacity: 0; transform: scale(1.7); } }
/* Thinking: slow shimmer */
.call__orb.is-thinking .call__core { animation: callThink 1.1s ease-in-out infinite; }
@keyframes callThink { 50% { transform: scale(.94); filter: brightness(.9); } }
/* Speaking: quick pulse */
.call__orb.is-speaking .call__core { animation: callTalk .5s ease-in-out infinite; }
@keyframes callTalk { 50% { transform: scale(1.12); } }
.call__name { font: 700 26px var(--font-serif, var(--font-sans)); color: var(--color-text-primary); margin: 0; }
.call__status { font: 600 15px var(--font-sans); color: var(--color-accent); margin: 0; min-height: 20px; }
[dir="rtl"] .call__status { font-family: var(--font-arabic); }
.call__caption {
  font: 400 15px/1.6 var(--font-sans); color: var(--color-text-secondary);
  margin: 2px 0 0; min-height: 44px; max-height: 30vh; overflow: auto;
  max-width: 44ch; overscroll-behavior: contain;
}
[dir="rtl"] .call__caption { font-family: var(--font-arabic); }
.call__controls { display: flex; align-items: center; gap: 22px; margin-top: 16px; }
.call__btn {
  width: 60px; height: 60px; border-radius: 50%; border: 0; cursor: pointer;
  display: grid; place-items: center; transition: transform .15s ease, background .15s ease, color .15s ease;
}
.call__btn svg { width: 24px; height: 24px; }
.call__btn:hover { transform: translateY(-2px); }
.call__btn--mute { background: var(--color-surface); color: var(--color-text-secondary); border: 1px solid var(--color-border); }
.call__btn--mute.is-muted { background: color-mix(in srgb, #e5484d 15%, transparent); color: #e5484d; border-color: color-mix(in srgb, #e5484d 40%, transparent); }
.call__btn--end { background: #e5484d; color: #fff; box-shadow: 0 8px 24px color-mix(in srgb, #e5484d 45%, transparent); }
.call__btn--end:hover { background: #d13b40; }
@media (prefers-reduced-motion: reduce) {
  .call__orb .call__core, .call__orb.is-listening .call__ring,
  .call__orb.is-thinking .call__core, .call__orb.is-speaking .call__core { animation: none; }
  .call { transition: opacity .1s; }
}
/* ---- Call overlay: fit every device (small phones, notches, landscape) ---- */
.call__inner {
  max-height: 100dvh; overflow-y: auto; overscroll-behavior: contain;
  padding-top: calc(24px + env(safe-area-inset-top, 0px));
  padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px));
}
@media (max-width: 480px) {
  .call__inner { padding-inline: 16px; gap: 12px; }
  .call__orb { width: 148px; height: 148px; }
  .call__core, .call__ring { width: 92px; height: 92px; }
  .call__core .nib { transform: scale(1.1); }
  .call__name { font-size: 22px; }
  .call__status { font-size: 14px; }
  .call__caption { font-size: 14px; max-height: 26vh; max-width: 92vw; }
  .call__controls { gap: 20px; margin-top: 12px; }
  .call__btn { width: 58px; height: 58px; }
}
/* Short screens (landscape phones): compress vertically so controls never clip */
@media (max-height: 560px) {
  .call__inner { gap: 8px; padding-block: 12px; }
  .call__orb { width: 104px; height: 104px; margin-bottom: 0; }
  .call__core, .call__ring { width: 72px; height: 72px; }
  .call__core .nib { transform: scale(0.95); }
  .call__name { font-size: 19px; }
  .call__caption { max-height: 18vh; min-height: 0; }
  .call__controls { margin-top: 6px; }
  .call__btn { width: 52px; height: 52px; }
}

/* Call: microphone consent gate (a real tap grants mic + unlocks audio for Safari/Chrome) */
.call__consent { display: flex; flex-direction: column; align-items: center; gap: 14px; margin: 4px 0 2px; }
.call__consent[hidden] { display: none; }
.call__consent-text { font: 500 15px/1.6 var(--font-sans); color: var(--color-text-secondary); text-align: center; max-width: 30ch; margin: 0; }
[dir="rtl"] .call__consent-text { font-family: var(--font-arabic); }
.call__consent-btn {
  display: inline-flex; align-items: center; gap: 10px; padding: 13px 24px; border: 0; border-radius: var(--radius-pill);
  background: var(--color-accent); color: #fff; font: 600 15px var(--font-sans); cursor: pointer;
  box-shadow: 0 8px 24px color-mix(in srgb, var(--color-accent) 40%, transparent); transition: transform .15s ease, background .15s ease;
}
[dir="rtl"] .call__consent-btn { font-family: var(--font-arabic); }
.call__consent-btn:hover { transform: translateY(-2px); }
.call__consent-btn:active { transform: translateY(0); }
.call__consent-btn svg { width: 20px; height: 20px; }

/* ═══════════════════════════════════════════════════════════════════════════
   GUEST MODE — the sidebar sign-up pill + the members-only upsell dialog.
   The pill is deliberately BILINGUAL (active language large, the other language
   underneath) so "Sign up now / سجّل الآن" reads for every visitor.
   ═══════════════════════════════════════════════════════════════════════════ */
.guest-slot {
  margin: var(--space-2) var(--space-3) 0;
  padding-block-start: var(--space-3);
  border-block-start: var(--border-hairline) solid var(--color-border-hairline);
}
.guest-slot__note {
  margin: 0 0 var(--space-2);
  color: var(--color-text-muted);
  font-size: 11.5px; line-height: 1.5; text-align: center;
}
[dir="rtl"] .guest-slot__note { font-family: var(--font-arabic); line-height: 1.7; }

.guest-cta {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-block-size: 46px;                    /* comfortable touch target on phones */
  padding-inline: var(--space-4);
  border: 0; border-radius: var(--radius-pill);
  background: var(--color-accent); color: var(--color-on-accent);
  cursor: pointer; text-align: center;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--color-accent) 32%, transparent);
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.guest-cta--block { display: flex; inline-size: 100%; }
.guest-cta:hover { background: var(--color-accent-hover); box-shadow: 0 10px 24px color-mix(in srgb, var(--color-accent) 38%, transparent); }
/* Real-hover only — a tapped pill must not stay lifted (sticky :hover on touch). */
@media (hover: hover) and (pointer: fine) {
  .guest-cta:hover { transform: translateY(-1px); }
}
.guest-cta:active { transform: translateY(0); }
.guest-cta:focus-visible { outline: 2px solid var(--color-accent-ring); outline-offset: 2px; }
.guest-cta__ico { display: inline-flex; flex: 0 0 auto; }
.guest-cta__ico svg { inline-size: 17px; block-size: 17px; }
.guest-cta__tx { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.15; }
[dir="rtl"] .guest-cta__tx { align-items: flex-end; }
.guest-cta__main { font: var(--weight-semibold) 14px/1.2 var(--font-sans); }
[dir="rtl"] .guest-cta__main { font-family: var(--font-arabic); }
.guest-cta__alt  { font: var(--weight-medium) 10.5px/1.3 var(--font-sans); opacity: .78; }
[dir="rtl"] .guest-cta__alt { font-family: var(--font-sans); }

/* ---- Upsell dialog (image generation & other members-only features) ---- */
.guest-ov {
  position: fixed; inset: 0; z-index: 1400;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-5);
  background: rgba(0,0,0,.55);
  opacity: 0; transition: opacity var(--dur-fast) var(--ease-out);
}
.guest-ov.is-open { opacity: 1; }
.guest-card {
  inline-size: min(420px, 100%);
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  padding: var(--space-8) var(--space-6) var(--space-6);
  border-radius: var(--radius-xl);
  background: var(--color-surface);
  box-shadow: var(--shadow-lg, 0 24px 60px rgba(0,0,0,.28));
  text-align: center;
  transform: translateY(14px) scale(.97);
  transition: transform var(--dur-base, .22s) var(--ease-out);
}
.guest-ov.is-open .guest-card { transform: none; }
.guest-card__mark { margin-block-end: var(--space-1); }
.guest-card__title {
  margin: 0; color: var(--color-text-primary);
  font: var(--weight-semibold) 19px/1.35 var(--font-sans);
}
[dir="rtl"] .guest-card__title { font-family: var(--font-arabic); line-height: 1.6; }
.guest-card__body {
  margin: 0; color: var(--color-text-secondary);
  font-size: var(--text-sm); line-height: var(--leading-md);
}
[dir="rtl"] .guest-card__body { font-family: var(--font-arabic); line-height: 1.9; }
.guest-card__actions { display: flex; flex-direction: column; gap: var(--space-2); inline-size: 100%; margin-block-start: var(--space-2); }
.guest-card__cta {
  min-block-size: 48px; inline-size: 100%;
  border: 0; border-radius: var(--radius-pill);
  background: var(--color-accent); color: var(--color-on-accent);
  font: var(--weight-semibold) 15px var(--font-sans); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .guest-card__cta { font-family: var(--font-arabic); }
.guest-card__cta:hover { background: var(--color-accent-hover); }
.guest-card__cta:focus-visible,
.guest-card__later:focus-visible { outline: 2px solid var(--color-accent-ring); outline-offset: 2px; }
.guest-card__later {
  min-block-size: 44px; inline-size: 100%;
  border: 0; border-radius: var(--radius-pill); background: transparent;
  color: var(--color-text-muted); font: var(--weight-medium) 14px var(--font-sans); cursor: pointer;
}
[dir="rtl"] .guest-card__later { font-family: var(--font-arabic); }
.guest-card__later:hover { color: var(--color-text-primary); background: var(--color-bg-subtle); }

@media (max-width: 480px) {
  .guest-ov { align-items: flex-end; padding: 0; }
  .guest-card { inline-size: 100%; border-end-start-radius: 0; border-end-end-radius: 0; padding-block-end: calc(var(--space-6) + env(safe-area-inset-bottom, 0px)); }
}
@media (prefers-reduced-motion: reduce) {
  .guest-ov, .guest-card { transition: none !important; }
  .landing__cta.is-busy::after { animation: none !important; }
}

/* ============================================================================
   MOBILE HARDENING — touch targets · tier dropdown · safe areas · focus · RTL
   Appended last on purpose. Every rule below overrides an EARLIER one at the
   same specificity, so plain source order does the work and no !important is
   needed. Rewriting the originals in place would have meant editing rules the
   desktop layout, Firas Code and the native shell all sit on top of.
   Transitions added here inherit the global `prefers-reduced-motion` and
   `:root[data-motion="off"]` kill-switches (both zero out transition-duration
   for `*`), so nothing here animates when motion is off.
   ========================================================================== */

/* ----------------------------------------------------------------------------
   A. TOUCH TARGETS — 44x44 minimum wherever a finger is the pointer

   A1. The one existing bump ("Comfortable touch targets", section 9) reached
   five selectors, and the topbar compaction rules then UNDID it:
   `.topbar .icon-btn{inline-size:34px;block-size:34px}` at <=480px and
   `{31px}` at <=360px. Those never reclaimed a single pixel of width — they
   left `min-inline-size:44px` standing, so the button stayed 44px wide and only
   lost 10-13px of HEIGHT. The space they were chasing comes from the GLYPH
   instead (A2), so the hit area can stay 44px on the smallest phones.
---------------------------------------------------------------------------- */
@media (pointer: coarse), (max-width: 560px) {
  /* Topbar chrome — hamburger, share, bell */
  .topbar .icon-btn {
    inline-size: 44px; block-size: 44px;
    min-inline-size: 44px; min-block-size: 44px;
  }
  /* New-chat is not an .icon-btn, so no bump ever reached it: with the label
     hidden below 480px it was a 24x36 target — the smallest in the topbar.
     Height only here; the square icon-only form is below 768px, where the label
     is hidden. A coarse-pointer TABLET still shows the label and must stay
     auto-width or the text clips. */
  .topbar__newchat { block-size: 44px; min-block-size: 44px; }

  /* Tier strip (tablets / coarse pointers ABOVE 600px — below that the strip is
     replaced by the dropdown in section B). 26px tall segments in a scroller
     were the hardest thing in the app to hit accurately. */
  .tier-switch { block-size: auto; }
  .tier-switch__btn { block-size: 44px; min-block-size: 44px; }

  /* Composer toolbar — the "+" tools trigger was omitted from the original bump
     even though its neighbour .mode-select__trigger was included. */
  .tools-menu__trigger { inline-size: 44px; block-size: 44px; min-inline-size: 44px; }
  .mode-select__trigger { block-size: 44px; min-block-size: 44px; }
  .think-toggle { block-size: 44px; min-block-size: 44px; }

  /* Per-message actions (copy / regenerate / export) — the most-tapped controls
     in a thread after send. */
  .msg-action { block-size: 44px; min-block-size: 44px; padding-inline: var(--space-3); }

  /* Drawer account row: two 30px buttons side by side in a 288px panel. */
  .account-pill { gap: var(--space-2); padding-inline: var(--space-2); }
  .account-pill__logout,
  .account-pill__settings { inline-size: 44px; block-size: 44px; }

  /* Scroll-to-latest floats free above the composer — nothing competes for the
     space, so 36px was a pure loss. */
  .scroll-bottom { inline-size: 44px; block-size: 44px; }

  /* Voice dictation bar: a touch-ONLY surface that shipped 34px buttons. The bar
     is `inset:0` over a composer row that is already >=44px tall, so the buttons
     fit; the gap/padding come down to pay for the extra 20px of width. */
  .mic-rec { gap: 6px; padding-inline: 6px; }
  .mic-rec__cancel, .mic-rec__done { width: 44px; height: 44px; }
  .mic-rec__lang {
    min-block-size: 44px; min-inline-size: 44px;
    flex: 0 1 auto; overflow: hidden;   /* may give width back before it clips the bar */
  }

  /* The three escape hatches from full-screen overlays, all just under the line. */
  .img-lightbox__close,
  .share-sheet__x,
  .ann-icon-btn { inline-size: 44px; block-size: 44px; width: 44px; height: 44px; }

  /* Sign-in screen: text-only buttons at 18-30px tall, no padding to speak of. */
  .auth__forgot,
  .auth__back,
  .auth__switch-btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-block-size: 44px; padding-inline: var(--space-2);
  }
  .auth__switch-btn { vertical-align: middle; }

  /* First control a new mobile visitor ever touches. */
  .cookie-consent__btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-block-size: 44px;
  }
}

/* A2. Reclaim the topbar width the compaction rules were after — from the icon,
   not from the button. 20px -> 19px -> 18px glyphs inside a hit area that never
   drops below 44px. */
@media (max-width: 768px) {
  /* Label is hidden at this width, so the button is a square icon target. */
  .topbar__newchat {
    inline-size: 44px; min-inline-size: 44px;
    justify-content: center; padding-inline: 0;
  }
}
@media (max-width: 480px) {
  .topbar .icon-btn svg { inline-size: 19px; block-size: 19px; }
}
@media (max-width: 360px) {
  .topbar .icon-btn svg { inline-size: 18px; block-size: 18px; }
  .topbar__newchat svg { inline-size: 15px; block-size: 15px; }
  /* Elapsed time is the one droppable item in the recording bar on a 280-360px
     phone; the pulsing dot and the waveform already say "recording". */
  .mic-rec__time { display: none; }
}

/* A3. Controls that genuinely cannot be 44px visually get a transparent hit slug
   instead. Only safe where nothing interactive sits inside the expanded area. */
@media (pointer: coarse), (max-width: 560px) {
  /* Remove-attachment: 18px with an 11px glyph, in the corner of a 56px thumb —
     the worst target in the app, and the only way to undo a wrong attachment.
     The frame clipped its own children, so the slug needs the clip lifted; the
     rounding moves onto the image, which is all the clip was really doing. */
  .attach-tray { gap: var(--space-3); }
  .attach-thumb { overflow: visible; }
  .attach-thumb img { border-radius: inherit; }
  .attach-thumb__remove::after {
    content: ""; position: absolute; inset: -13px;   /* 18 + 13 + 13 = 44 */
    border-radius: var(--radius-pill);
  }

  /* Conversation row actions: NOT slugged. Three 26px buttons 2px apart would
     need overlapping -9px slugs, and in an overlap the later sibling takes the
     tap — rename would end up with a 12px live strip, worse than the 26px it
     started with. Real 44px buttons, gap removed, row grown to fit. */
  .chat-item { block-size: auto; min-block-size: 48px; padding-inline: var(--space-2); }
  .chat-item__actions { gap: 0; }
  .chat-item__actions button { inline-size: 44px; block-size: 44px; }
}

/* ----------------------------------------------------------------------------
   B. TIER SWITCHER BECOMES A DROPDOWN ON PHONES

   The segmented strip holds four tiers and, below ~365px, silently turns into a
   scroller with no scrollbar, no fade and no arrow — "Max", the newest tier,
   simply is not there. A single trigger + panel makes every tier reachable and
   frees ~90px of topbar.

   360px budget (the <=360px block: 4px padding each side, 3px gaps):
     8 padding + 44 drawer + 94 trigger + 44 new-chat + 44 share + 44 bell
     + 5 gaps x 3 = 293px of 360 -> 67px of slack in .topbar__spacer.
   320px: trigger clamps to 83px -> 282px used.  280px: 80px -> 279px of 280.
   The trigger is `flex: 0 1 auto` with an ellipsised name, so anything that
   grows later (a longer tier name, a bigger safe-area inset) shrinks the
   trigger instead of bursting the row.
---------------------------------------------------------------------------- */
.tier-select { display: none; }

@media (max-width: 600px) {
  .tier-switch { display: none; }
  .tier-select {
    display: block; position: relative;
    flex: 0 1 auto; min-inline-size: 0;
  }
}
/* Firas Agent runs on one permanent engine — no tier choice anywhere, matching
   the existing `.product-agent .tier-switch{display:none}` rule. */
.product-agent .tier-select { display: none !important; }

.tier-select__trigger {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  inline-size: clamp(80px, 26vw, 96px); min-inline-size: 0;
  block-size: 44px; min-block-size: 44px;
  padding-inline: 10px;
  border-radius: var(--radius-pill);
  border: var(--border-hairline) solid var(--color-border-hairline);
  background: var(--color-surface);
  color: var(--color-text-secondary);
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .tier-select__trigger { font-family: var(--font-arabic); }
.tier-select__trigger:hover { color: var(--color-text-primary); border-color: var(--color-border-strong); }
.tier-select.is-open .tier-select__trigger,
.tier-select__trigger[aria-expanded="true"] {
  color: var(--color-accent);
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}
/* A real ring, not a box-shadow — see section D for why. */
.tier-select__trigger:focus-visible {
  outline: 2px solid var(--color-accent-ring);
  outline-offset: 2px;
}
.tier-select__ico { display: inline-flex; flex: 0 0 auto; }
.tier-select__ico, .tier-select__ico svg { inline-size: 16px; block-size: 16px; }
.tier-select__name {
  flex: 0 1 auto; min-inline-size: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tier-select__chev {
  display: inline-flex; flex: 0 0 auto; opacity: .7;
  transition: transform var(--dur-fast) var(--ease-out);
}
.tier-select__chev, .tier-select__chev svg { inline-size: 14px; block-size: 14px; }
.tier-select.is-open .tier-select__chev { transform: rotate(180deg); }

/* The panel is FIXED, not absolute-to-the-trigger. The trigger sits three
   buttons in from the inline-end edge, so a 260px panel anchored to it would
   hang ~45px off the opposite edge of a 360px screen — where `body{overflow
   :hidden}` clips it dead. Fixed + inline-end gutter cannot overflow either
   edge in either direction. (Under the native shell the topbar carries a
   backdrop-filter and becomes the containing block; it spans the full width, so
   the same offsets still land the panel just under the bar.) */
.tier-select__menu {
  position: fixed;
  inset-block-start: calc(var(--topbar-h) + var(--safe-top) + 6px);
  inset-inline-end: 12px;
  z-index: var(--z-dropdown);
  inline-size: min(260px, calc(100vw - 24px));
  max-inline-size: calc(100vw - 24px);
  max-block-size: calc(100dvh - var(--topbar-h) - var(--safe-top) - 24px);
  overflow-y: auto; overscroll-behavior: contain;
  padding: var(--space-1);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; gap: 2px;
  /* `seat`, like its sibling menus (tools/mode/product): the panel opens BELOW its
     trigger, so it settles DOWN from it — `msg-in` rose upward, away from the
     trigger. The keyframes live later in the file; @keyframes are order-free. */
  animation: seat var(--dur-base) var(--ease-out) both;
  transform-origin: top center;
}
.tier-select__menu[hidden] { display: none; }

/* One row per tier. Laid out so it works whether the markup keeps name + hint
   flat inside the row or nests them in a `.tier-select__opt-text` column. */
.tier-select__opt {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: var(--space-3); row-gap: 2px;
  inline-size: 100%; min-block-size: 48px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  text-align: start;
  color: var(--color-text-primary);
  transition: background var(--dur-fast) var(--ease-out);
}
.tier-select__opt:hover { background: var(--color-bg-subtle); }
.tier-select__opt:focus-visible { outline: 2px solid var(--color-accent-ring); outline-offset: -2px; }
.tier-select__opt.is-active { background: var(--color-accent-soft); }
.tier-select__opt-ico { display: inline-flex; flex: 0 0 auto; color: var(--color-text-secondary); }
.tier-select__opt-ico, .tier-select__opt-ico svg { inline-size: 18px; block-size: 18px; }
.tier-select__opt.is-active .tier-select__opt-ico { color: var(--color-accent); }
/* The model name ran straight into its description on phones — "Firas MiniFast for everyday
   questions" — because this rule was written for `.tier-select__opt-text` while the markup
   has always emitted `.tier-select__opt-tx` (app.js, renderTierSelect). The selector matched
   nothing, so the wrapper stayed an ordinary inline span, and `flex` / `order` /
   `inline-size:100%` on its children are inert outside a flex container: both spans simply
   laid out as adjacent inline text with no gap and no line break.
   Both names are matched now so neither side can silently drift again. */
.tier-select__opt-tx,
.tier-select__opt-text {
  display: flex; flex-direction: column;
  gap: 3px;
  flex: 1 1 auto; min-inline-size: 0;
}
.tier-select__opt-name {
  flex: 1 1 auto; min-inline-size: 0;
  font-family: var(--font-sans);
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
[dir="rtl"] .tier-select__opt-name { font-family: var(--font-arabic); }
.tier-select__opt.is-active .tier-select__opt-name { color: var(--color-accent); }
.tier-select__opt-hint {
  flex: 0 0 auto; inline-size: 100%;   /* forces its own row when the markup is flat */
  order: 2;                            /* ...and stays BELOW the badge whatever order the markup emits */
  font-size: var(--text-xs); line-height: var(--leading-xs);
  color: var(--color-text-muted);
}
[dir="rtl"] .tier-select__opt-hint { font-family: var(--font-arabic); }
/* Only indent the tagline when it is a direct child (flat markup); inside a
   text column the column itself is already aligned past the icon. */
.tier-select__opt > .tier-select__opt-hint { padding-inline-start: calc(18px + var(--space-3)); }
.tier-select__opt-tx .tier-select__opt-hint,
.tier-select__opt-text .tier-select__opt-hint { inline-size: auto; padding-inline-start: 0; }
/* The name row holds the label plus an optional badge, so it is its own flex row inside the
   column — otherwise the badge would be pushed onto a line of its own. */
.tier-select__opt-tx .tier-select__opt-name,
.tier-select__opt-text .tier-select__opt-name {
  display: flex; align-items: center; gap: var(--space-2);
  white-space: normal;              /* let a long Arabic label wrap instead of ellipsing */
  overflow: visible; text-overflow: clip;
}

.tier-select__badge {
  flex: 0 0 auto; align-self: center; order: 1;
  padding: 2px 7px;
  border-radius: var(--radius-pill);
  border: var(--border-hairline) solid var(--color-accent-ring);
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font-family: var(--font-sans);
  font-size: 10px; font-weight: var(--weight-semibold); line-height: 1.6;
  letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap;
}

/* ----------------------------------------------------------------------------
   C. OVERFLOW + SAFE AREAS

   C1. index.html ships viewport-fit=cover, so on a notched phone in landscape
   the insets are real — and `--safe-left`/`--safe-right` were declared in the
   token block and then consumed nowhere outside Firas Code. The hamburger, the
   bell and the send button sat under the cutout. Physical padding-left/right on
   purpose: safe-area insets are physical, so mapping them through logical
   properties would put the notch inset on the wrong side in Arabic. Each
   breakpoint is restated because the originals compact the padding as the
   viewport narrows and a single blanket rule would undo that.
---------------------------------------------------------------------------- */
.topbar {
  padding-left:  max(var(--space-4), var(--safe-left));
  padding-right: max(var(--space-4), var(--safe-right));
}
.composer-wrap {
  padding-left:  max(var(--space-6), var(--safe-left));
  padding-right: max(var(--space-6), var(--safe-right));
}
.sidebar {
  padding-left:  max(var(--space-3), var(--safe-left));
  padding-right: max(var(--space-3), var(--safe-right));
}
@media (max-width: 768px) {
  .topbar {
    padding-left:  max(var(--space-3), var(--safe-left));
    padding-right: max(var(--space-3), var(--safe-right));
  }
  .composer-wrap {
    padding-left:  max(var(--space-4), var(--safe-left));
    padding-right: max(var(--space-4), var(--safe-right));
  }
}
@media (max-width: 480px) {
  .topbar {
    padding-left:  max(var(--space-2), var(--safe-left));
    padding-right: max(var(--space-2), var(--safe-right));
  }
}
@media (max-width: 360px) {
  .topbar {
    padding-left:  max(var(--space-1), var(--safe-left));
    padding-right: max(var(--space-1), var(--safe-right));
  }
}

/* C2. Two popovers with a physical min-width and no ceiling. The dialect picker
   floors at 240px inside a composer that is 248px wide at 280px viewport, and
   the product menu floors at 230px with a tagline that can push its end edge
   off-screen — both then clipped by `body{overflow:hidden}` rather than
   scrolled. Clamp to the viewport and let long labels wrap. */
.mic-menu {
  min-inline-size: min(240px, calc(100vw - 24px));   /* keeps the 240px floor until the screen is smaller than it */
  max-inline-size: calc(100vw - 24px);
}
.mic-menu__label { overflow-wrap: anywhere; }
.product-menu {
  min-inline-size: min(230px, calc(100vw - 16px));
  max-inline-size: min(320px, calc(100vw - 16px));
}
.product-menu__tag { overflow-wrap: anywhere; }

/* C3. `vh` on iOS Safari is the URL-bar-HIDDEN viewport, so these sheets
   overshoot the visible area by 8-12% whenever the bar is showing. The codebase
   already fixed .share-page / .ann-reader / #codeWorkspace this way; these five
   were missed. The vh line stays first as the fallback for engines without dvh. */
.mem-card      { max-height: 82vh; max-height: 82dvh; }
.ann-card      { max-height: 86vh; max-height: 86dvh; }
.ann-edit-card { max-height: 88vh; max-height: 88dvh; }
.settings-card { max-height: 88vh; max-height: 88dvh; }
.cw-pal        { padding-block-start: 12vh; padding-block-start: 12dvh; }
.cw-pal__card  { max-height: 66vh; max-height: 66dvh; }
@media (max-width: 560px) {
  /* Same widths as the bottom-sheet block that sets these — 560px, not 860px. */
  .ann-card      { max-height: 90vh; max-height: 90dvh; }
  .ann-edit-card { max-height: 92vh; max-height: 92dvh; }
  .settings-card { max-height: 92vh; max-height: 92dvh; }
}

/* C4. Firas Code console: the v3 pass reset `.cw-conrow` padding to `3px 8px`
   and left the `::before` prefix glyph parked at left:2px, so the marker now
   overlaps the first characters of every log line. Restore the 24px gutter the
   glyph is positioned inside. */
.cw--v3 .cw-conrow { padding: 3px 8px 3px 24px; }
/* …EXCEPT the app's own localized messages. Their marker flows inline instead of being parked
   in that gutter (see .cw-conrow.is-msg above), so the 24px would be blank space pushing the
   text out of line with the log rows beneath it. Scoped to .cw--v3 and placed AFTER the rule
   above on purpose: at equal specificity the later declaration wins, and an is-msg rule in the
   earlier block silently loses this exact argument.
   The marker's gap is a SYMMETRIC physical margin, not margin-inline-end. `direction` stays
   ltr here — is-msg only sets unicode-bidi:plaintext — so every inline-logical property still
   resolves to the left/right it would in English, which is the trap this file keeps setting.
   5px on both sides is correct in either direction with nothing to get wrong. */
.cw--v3 .cw-conrow.is-msg { padding: 3px 8px; }
.cw--v3 .cw-conrow.is-msg::before { position: static; margin: 0 5px; }

/* ----------------------------------------------------------------------------
   D. FOCUS VISIBILITY

   The app's only focus indicator was `box-shadow: var(--shadow-focus)` at
   specificity 0,1,0 — so every component that declares its own box-shadow at
   equal-or-higher specificity silently ate the keyboard ring. The active tier
   button, the plan CTA, the scroll-to-latest button, the landing CTA, the
   end-call button and half a dozen others had literally no focus state.
   `outline` is a separate property no component overrides, it paints over any
   box-shadow and it follows border-radius in every current engine.
---------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--color-accent-ring);
  outline-offset: 2px;
}
/* Menu rows that killed the outline at 0,2,0 and replaced it with a hover
   background — which is not a focus indicator, it is the hover state. */
.mode-select__item:focus-visible,
.tools-menu__item:focus-visible,
.export-menu__item:focus-visible,
.file-card:focus-visible,
.file-card__dl:focus-visible,
.ann-item:focus-visible,
.firas-ask__extra:focus-visible {
  outline: 2px solid var(--color-accent-ring);
  outline-offset: 2px;
}
/* Two inputs that set `outline:none` with no replacement at all. Inset offset so
   the ring hugs the field inside its rounded container instead of straddling it. */
.cw-pal__in:focus-visible,
.cw-ed__ta:focus-visible {
  outline: 2px solid var(--color-accent-ring);
  outline-offset: -2px;
}

/* Conversation actions were hover-gated with no touch fallback: pin, rename and
   delete only existed on the row you were already hovering, so on a phone they
   were reachable ONLY on the currently-open conversation. There is no
   long-press handler in app.js either. Same guard the deck presenter already
   uses for this exact class of bug. */
@media (hover: none) {
  .chat-item__actions { display: flex; }
}

/* The closed mobile drawer was hidden with `transform: translateX(-100%)` alone —
   nothing set display, visibility, inert or aria-hidden — so tabbing forward from
   the topbar walked through the logo, the product switch, new-chat, the search
   field, every history row and the logout button, all of them off-screen.
   `visibility` removes it from the tab order and the a11y tree; the 0s-duration /
   --dur-base-delay pairing means it only flips to hidden AFTER the slide-out
   finishes, so the closing animation is untouched. (If app.js later sets `inert`
   on the drawer instead, this rule is harmless duplication and can go.) */
@media (max-width: 768px) {
  .sidebar {
    visibility: hidden;
    transition: transform var(--dur-base) var(--ease-out),
                visibility 0s linear var(--dur-base);
  }
  .sidebar.is-open {
    visibility: visible;
    transition: transform var(--dur-base) var(--ease-out), visibility 0s;
  }
}

/* ----------------------------------------------------------------------------
   E. RTL — Arabic is the default language, so these are the DEFAULT rendering

   E1. Two elements mixed a LOGICAL inset with a PHYSICAL transform. In RTL
   `inset-inline-start:50%` resolves to `right:50%`, putting the element's right
   edge on the midpoint, and `translateX(-50%)` then pushes it a further half-
   width the wrong way: the scroll-to-latest button lands 54px left of centre
   and the "current plan" badge hangs off the card. The `.toast` rule already
   uses the correct idiom (`inset-inline:0; margin-inline:auto`).
---------------------------------------------------------------------------- */
.scroll-bottom {
  inset-inline: 0;
  margin-inline: auto;
  transform: translateY(6px);
}
.scroll-bottom.is-visible { transform: translateY(0); }
.plan-current {
  inset-inline: 0; margin-inline: auto;
  inline-size: max-content;
  transform: none;
}

/* E2. Firas Code bubbles: alignment is direction-aware (`align-self:flex-end`)
   but the tail corner was physical, so in Arabic the notch pointed away from
   the speaker. The main thread deliberately avoided this ("uniform — no
   asymmetric corner (RTL-safe)"); the newer Code surface regressed it. Reset the
   physical corner, then set the logical one so it follows the bubble. */
.cw-thread__turn--me .cw-thread__bubble {
  border-bottom-right-radius: 14px;
  border-end-end-radius: 5px;
}
.cw-thread__turn--ai .cw-thread__bubble {
  border-bottom-left-radius: 14px;
  border-end-start-radius: 5px;
}

/* E3. `.cw-file__p` flushes file paths left but never sets `direction:ltr`, so a
   path containing any Arabic character reorders bidirectionally while staying
   left-aligned — a jumbled path. Every other code surface pairs the two
   (.cw-conrow, .cw-diff__lines, .cw-status__seg); this one lost its partner. */
.cw-file__p { direction: ltr; unicode-bidi: isolate; }

/* E4. The tools-menu toggle knob rests on a LOGICAL inset and travels on a
   PHYSICAL transform, negated again for RTL — three places to keep in sync, and
   the knob leaves the track in one direction the moment the 34px track or the
   16px knob changes. Animate the logical inset instead: 34 - 16 - 2 - 2 = 14px
   of travel from a 2px rest, so the on position is 16px, in both directions. */
.tools-menu__switch::after { transition: inset-inline-start var(--dur-fast) var(--ease-out); }
.tools-menu__item.is-on .tools-menu__switch::after,
[dir="rtl"] .tools-menu__item.is-on .tools-menu__switch::after {
  transform: none;
  inset-inline-start: 16px;
}

/* Two sidebar controls the touch-target sweep missed because they live in the
   mobile DRAWER (off-screen at audit time, but fully tappable once it opens):
   the product switcher (34px) and New chat (40px). Both are primary actions —
   switching product and starting a chat — so they get the same 44px floor. */
@media (pointer: coarse), (max-width: 560px) {
  .product-switch { min-block-size: 44px; }
  .btn-newchat    { min-block-size: 44px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   WELCOME — the cinematic home screen.
   The mark sits inside a soft aurora halo that breathes; the greeting reveals
   word by word (spans injected by splitWords()). JS drives the entrance via the
   Motion library; these rules are the STATIC fallback, so if the CDN is blocked
   everything is simply visible with no half-faded text left on screen.
   ═══════════════════════════════════════════════════════════════════════════ */
.welcome { position: relative; }

/* A CSS radial-gradient interpolates alpha LINEARLY between stops. That is fine in the
   middle and wrong at the end: the falloff stops dead at the final stop, and the break in
   its slope reads as a visible circular edge — on a cream ground it looked like a green disc
   floating on white, which is exactly what was reported.

   Fixed by feathering: seven stops approximating a gaussian tail instead of two stops
   approximating a cone. The alpha never lands on a hard endpoint, so there is no edge to see.

   The `filter: blur(38px)` that used to hide this is GONE. It was papering over the gradient
   while costing a full-size offscreen buffer on every frame of an infinite breathing
   animation — the most expensive way possible to soften an edge that should not exist.
   `border-radius` went too: it clipped the paint area to a circle, adding a second hard edge
   underneath the first. */
.welcome__halo {
  position: absolute;
  inset-block-start: -26%;
  inset-inline-start: 50%;
  translate: -50% 0;
  /* Wider than before: the same falloff spread over more pixels is a gentler gradient. */
  inline-size: min(720px, 116vw);
  aspect-ratio: 1;
  pointer-events: none;
  z-index: 0;
  /* `closest-side`, and CENTRED. This is the whole fix.
     A radial-gradient defaults to `farthest-corner`: the ramp is scaled to the far corner,
     so at the NEAR edges it is only ~45% through and still carrying colour. The element then
     just stops — and that abrupt end is a straight, perfectly visible line on all four sides.
     Measured on the previous version at a 375px viewport: 7.66% alpha still painting at the
     top edge, 1.52% at the bottom. That is the rectangle with a horizontal band through it.

     With `closest-side` the ramp completes at the nearest edge, and every pixel beyond the
     inscribed circle takes the final stop — transparent. There is no boundary left to see,
     because the gradient is already zero everywhere it meets the box.
     The last colour stop lands at 88% too, so it is fully faded even before that. */
  background:
    radial-gradient(closest-side circle at 50% 50%,
      color-mix(in srgb, var(--color-accent) 18%, transparent) 0%,
      color-mix(in srgb, var(--color-accent) 13%, transparent) 20%,
      color-mix(in srgb, var(--color-accent) 8%,  transparent) 38%,
      color-mix(in srgb, var(--color-accent) 4%,  transparent) 54%,
      color-mix(in srgb, var(--color-accent) 1.6%,transparent) 68%,
      color-mix(in srgb, var(--color-accent) 0.4%,transparent) 80%,
      transparent 88%),
    radial-gradient(closest-side circle at 62% 62%,
      color-mix(in srgb, var(--color-accent) 9%,  transparent) 0%,
      color-mix(in srgb, var(--color-accent) 5%,  transparent) 28%,
      color-mix(in srgb, var(--color-accent) 2%,  transparent) 50%,
      color-mix(in srgb, var(--color-accent) 0.5%,transparent) 70%,
      transparent 84%);
  opacity: 1;
  animation: welcome-breathe 9s var(--ease-out, ease-in-out) infinite;
}
/* Light needs LESS, not more. The old values had it backwards — .85 on cream and .7 on
   near-black — so the theme where a green tint is most conspicuous was also the strongest.
   These feed the keyframes above, which is the only place opacity is actually decided
   while the halo is breathing. */
[data-theme="light"] .welcome__halo { --halo-lo: .30; --halo-hi: .46; }
[data-theme="dark"]  .welcome__halo { --halo-lo: .62; --halo-hi: .88; }
/* The opacity here is a VARIABLE, not a literal. Keyframe values beat the element's own
   `opacity` declaration for as long as the animation runs, so hard-coding .62/.9 made the
   per-theme opacity below unreachable — the light theme silently kept the dark theme's
   strength, which is half of why the glow read as a disc on cream.
   Scale eased off too: 1.09 on an element that is now 720px wide is a lot of travel. */
@keyframes welcome-breathe {
  0%, 100% { transform: scale(1);    opacity: var(--halo-lo, .62); }
  50%      { transform: scale(1.05); opacity: var(--halo-hi, .9);  }
}

/* Keep the mark and greeting above the halo. */
.welcome__mark, .welcome__title { position: relative; z-index: 1; }

/* `will-change: transform, opacity` was here. It is a hint for an animation that is ABOUT to
   run, not a property — left on at rest it holds a compositor layer for the whole session in
   exchange for nothing, because the entrance has already finished. A running CSS animation
   is promoted automatically, so the hint bought nothing even while it was useful. */
.welcome__mark { position: relative; z-index: 1; }

/* Per-word reveal. inline-block is required for transform to apply to a span,
   and the padding stops descenders/diacritics being clipped by the blur filter
   mid-animation — Arabic (تشكيل) shows this immediately without it. */
.welcome__title .word {
  display: inline-block;
  padding-block-end: 0.06em;
  /* No will-change. This selector matches EVERY word of the greeting, so the hint promoted
     one compositor layer per word — permanently, long after the one-shot reveal finished —
     and `filter` is the most expensive thing that list can contain. */
}

@media (prefers-reduced-motion: reduce) {
  .welcome__halo { animation: none !important; }
  .welcome__title .word { opacity: 1 !important; filter: none !important; transform: none !important; }
}
[data-motion="off"] .welcome__halo { animation: none !important; }
[data-motion="off"] .welcome__title .word { opacity: 1 !important; filter: none !important; transform: none !important; }

/* ── Micro-interaction polish ────────────────────────────────────────────────
   Small, consistent responses that make the whole shell feel built rather than
   assembled. Press feedback itself is JS (initPressFeedback) so it can spring;
   these are the hover/focus halves that belong in CSS. */
.chat-item { transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out); }
.chat-item:hover { transform: translateX(2px); }
[dir="rtl"] .chat-item:hover { transform: translateX(-2px); }

/* The composer is the primary surface — give focus a real, calm emphasis. */
.composer { transition: border-color var(--dur-base, .22s) var(--ease-out), box-shadow var(--dur-base, .22s) var(--ease-out); }
.composer.is-focused {
  border-color: color-mix(in srgb, var(--color-accent) 55%, var(--color-border-strong));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 12%, transparent);
}

/* Send button: reads as "armed" the moment there is something to send.
   The lift is gated to real hover — on touch, :hover sticks after the tap. */
.composer__send:not(:disabled) { transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
  /* :not(:active) — this rule is LATER in source than the :active press scale, so
     without it the hover lift masks the CSS-fallback press while the mouse is down. */
  .composer__send:not(:disabled):not(:active):hover { transform: translateY(-1px); }
}

@media (prefers-reduced-motion: reduce) {
  .chat-item, .composer, .composer__send { transition: none !important; }
  .chat-item:hover, .composer__send:not(:disabled):hover,
  .landing__cta:hover, .guest-cta:hover { transform: none !important; }
}
[data-motion="off"] .chat-item:hover,
[data-motion="off"] .composer__send:not(:disabled):hover,
[data-motion="off"] .landing__cta:hover,
[data-motion="off"] .guest-cta:hover { transform: none !important; }


/* ═══════════════════════════════════════════════════════════════════════════
   FIRAS CODE — PROFESSIONAL PASS (v4, appended after cw--v3).
   The owner's note: the workspace "feels childish". The cause is the cw--v3
   skin's consumer-app vocabulary rather than the layout:
     · a blue→green GRADIENT blob for the project logo, and a gradient Run pill
     · glow box-shadows tinted with the accent on hover
     · translateY(-1px) lift + scale(.94/.97) bounce on nearly every control
     · pill radii (999px) on toolbar buttons, tabs and chips
     · a 34px SERIF display heading inside a code editor
     · emoji (▶ ⬇ 🕑) standing in for icons
   An IDE wants the opposite: flat surfaces, tight radii, one accent used once,
   and motion you notice only when something actually happens.

   Scoped to .cw--v3 so it beats that layer on specificity+order. NO class names
   change — the JS binds to every one of them.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Retune the v3 design tokens: tighter geometry, calmer elevation. */
.cw--v3 {
  --cwv-radius-chip: 6px;
  --cwv-radius-panel: 8px;
  --cwv-radius-card: 10px;
  --cwv-pad-bar: 7px 12px;
  --cwv-gap: 8px;
  --cwv-shadow-panel: 0 1px 2px rgba(0,0,0,.05), 0 4px 14px rgba(0,0,0,.05);
}
[data-theme="dark"] .cw--v3 {
  --cwv-shadow-panel: 0 1px 2px rgba(0,0,0,.35), 0 4px 14px rgba(0,0,0,.3);
}

/* ---- Toolbar ---- */
.cw--v3 .cw-bar { backdrop-filter: none; -webkit-backdrop-filter: none; background: var(--color-sidebar-bg, var(--color-surface)); }
/* The gradient logo tile was the loudest toy element in the workspace. */
.cw--v3 .cw-bar__logo {
  inline-size: 26px; block-size: 26px;
  border-radius: var(--cwv-radius-chip);
  background: var(--color-accent-soft);
  border: var(--border-hairline) solid color-mix(in srgb, var(--color-accent) 26%, transparent);
  color: var(--color-accent);
  font: var(--weight-semibold) 10px var(--font-mono, monospace);
  box-shadow: none;
}
.cw--v3 .cw-bar__home { inline-size: 30px; block-size: 30px; border-radius: var(--cwv-radius-chip); }
.cw--v3 .cw-bar__home:hover { transform: none; }
.cw--v3 .cw-bar__home:active { transform: none; }
.cw--v3 .cw-bar__name { font-weight: var(--weight-semibold); font-size: 13px; }
.cw--v3 .cw-bar__count { border-radius: var(--cwv-radius-chip); padding: 2px 7px; font-size: 10.5px; }

.cw--v3 .cw-bar__btn {
  display: inline-flex; align-items: center; gap: 6px;
  min-block-size: 30px;
  padding: 0 var(--space-3);
  border-radius: var(--cwv-radius-chip);
  background: transparent;
  /* Explicit width+style, not just a colour: these buttons inherited a dead
     `border:1px solid var(--color-border)`, so border-style was `none` and
     setting only border-color could never bring the edge back.
     Resolved straight from the theme token — --cwv-hair adds a level of
     indirection that measurably did NOT re-resolve on a theme switch here. */
  border: 1px solid var(--color-border-hairline);
  color: var(--color-text-secondary);
  font: var(--weight-medium) 12.5px var(--font-sans);
  transition: background var(--cwv-motion), border-color var(--cwv-motion), color var(--cwv-motion);
}
[dir="rtl"] .cw--v3 .cw-bar__btn { font-family: var(--font-arabic); }
.cw--v3 .cw-bar__btn:hover {
  background: var(--color-bg-subtle);
  border-color: var(--color-border-strong);
  color: var(--color-text-primary);
  transform: none;            /* no lift */
  box-shadow: none;           /* no accent glow */
}
.cw--v3 .cw-bar__btn:active { transform: none; }
.cw--v3 .cw-bar__btn:focus-visible { outline: 2px solid var(--color-accent-ring); outline-offset: 2px; }
.cw--v3 .cw-bar__btn-ico { display: inline-flex; flex: 0 0 auto; }
.cw--v3 .cw-bar__btn-ico svg { inline-size: 14px; block-size: 14px; }

/* Run loses its gradient — it is a utility, not the hero action. */
.cw--v3 .cw-bar__btn.cw-run {
  background: transparent;
  border-color: var(--color-border-hairline);
  color: var(--color-text-secondary);
  box-shadow: none;
}
.cw--v3 .cw-bar__btn.cw-run:hover { background: var(--color-bg-subtle); color: var(--color-text-primary); box-shadow: none; }
/* RUN-A-.PY MODE — the button already reads "تشغيل / Run" instead of "تحديث / Refresh"; this
   makes the change of meaning visible and not merely readable. Deliberately a tint and not a
   fill: the rule above demoted Run to a utility precisely so "Improve" stays the one
   emphasised control in the bar, and executing a script must not take that slot back. Placed
   AFTER that rule at the same (0,3,0) weight — anywhere earlier and it silently loses. */
.cw--v3 .cw-bar__btn.cw-run--py {
  color: var(--color-accent);
  border-color: color-mix(in srgb, var(--color-accent) 42%, var(--color-border-hairline));
}
.cw--v3 .cw-bar__btn.cw-run--py:hover { color: var(--color-accent); background: var(--color-accent-soft); }
.cw--v3 .cw-bar__btn.cw-run[disabled] { opacity: .62; pointer-events: none; }

/* EXACTLY ONE emphasised control in the bar: "Improve" — the action that
   actually re-runs, inspects and develops the project. */
.cw--v3 .cw-bar__btn--primary,
.cw-bar__btn--primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
  font-weight: var(--weight-semibold);
  box-shadow: none;
}
.cw--v3 .cw-bar__btn--primary:hover,
.cw-bar__btn--primary:hover {
  background: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
  color: var(--color-on-accent);
  box-shadow: none;
}
.cw--v3 .cw-bar__btn--primary:disabled,
.cw--v3 .cw-bar__btn--primary.is-busy,
.cw-bar__btn--primary.is-busy { opacity: .6; cursor: progress; }
.cw--v3 .cw-bar__btn--primary.is-busy .cw-bar__btn-ico,
.cw-bar__btn--primary.is-busy .cw-bar__btn-ico { animation: cw-spin 1s linear infinite; }
@keyframes cw-spin { to { transform: rotate(360deg); } }

/* ---- Tabs + tree: square-ish, quiet ---- */
.cw--v3 .cw-tab { border-radius: var(--cwv-radius-chip); padding: 6px 12px; font-size: 12px; }
.cw--v3 .cw-tab.is-active { box-shadow: none; background: var(--color-bg-subtle); color: var(--color-text-primary); font-weight: var(--weight-semibold); }
.cw--v3 .cw-tree__newfolder:hover { transform: none; }
.cw--v3 .cw-file { padding-block: 7px; }

/* ---- Home screen: an editor start screen, not a marketing hero ---- */
.cw-home__mark {
  inline-size: 50px; block-size: 50px;
  border-radius: var(--radius-lg);
  border: var(--border-hairline) solid color-mix(in srgb, var(--color-accent) 28%, var(--color-border-hairline));
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font: var(--weight-semibold) 19px/1 var(--font-mono, monospace);
  box-shadow: none;
}
.cw-home__hero h1 { font: var(--weight-semibold) 24px/1.2 var(--font-sans); letter-spacing: -0.01em; }
.cw-home__hero p { font-size: var(--text-sm); max-inline-size: 420px; }
.cw-home__card { border-radius: var(--radius-lg); }
.cw-home__tpl { border-width: var(--border-hairline); border-radius: var(--radius-md); min-block-size: 40px; }
.cw-home__create { border-radius: var(--radius-md); min-block-size: 44px; }
.cw-home__tpl:focus-visible,
.cw-home__name:focus-visible,
.cw-home__create:focus-visible { outline: 2px solid var(--color-accent-ring); outline-offset: 2px; }

/* ---- Editor surface follows the theme instead of a hardcoded white ---- */
.cw-ed { background: var(--color-surface); }

@media (prefers-reduced-motion: reduce) {
  .cw--v3 .cw-bar__btn { transition: none !important; }
  .cw--v3 .cw-bar__btn--primary.is-busy .cw-bar__btn-ico,
  .cw-bar__btn--primary.is-busy .cw-bar__btn-ico { animation: none !important; }
}
[data-motion="off"] .cw--v3 .cw-bar__btn--primary.is-busy .cw-bar__btn-ico { animation: none !important; }

/* ---- Responsive: the 3-column grid is unreadable before the mobile nav ---- */
/* The desktop ladder is bounded at BOTH ends so no two rules ever fight.
   Below 861px the IDE is single-pane, driven by the mobile bottom nav — an unbounded
   `max-width: 1100px` here leaks into that range and tiles three panes behind the nav,
   which is the same class of bug as the 900px rule below, just less visible. */
@media (min-width: 901px) and (max-width: 1100px) {
  .cw-main { grid-template-columns: 168px minmax(0, 1.15fr) minmax(0, 1fr); }
}

/* P0 — Firas Code was unusable on every tablet and half-screen window.
   This rule used to read `@media (max-width: 900px) { grid-template-columns: 150px
   minmax(0,1fr) }` and it broke the IDE in two separate ways:

   1. It also matched everything BELOW it. At ≤860px the app switches to one pane at a
      time (line 4087, `grid-template-columns:1fr`) driven by the mobile nav — but both
      selectors are plain `.cw-main`, media queries add no specificity, and this rule is
      ~1400 lines later, so source order handed it the win. From 641px to 860px the
      editor rendered as a 150px sliver — roughly fifteen characters per line — beside
      500-700px of empty background. Below 641px an `!important` flex reset rescued it,
      and above 900px it was fine, so the failure sat exactly on iPad portrait (768px).
   2. Even in its own band it declared TWO columns for THREE children (tree, editor,
      preview), so the preview wrapped into an implicit 150px-tall row.

   Fixed by bounding the band at both ends and keeping all three columns — which is what
   the 1100px rule directly above already does. Only the tree narrows. */
@media (min-width: 861px) and (max-width: 900px) {
  .cw-main { grid-template-columns: 138px minmax(0, 1.05fr) minmax(0, 1fr); }
}
/* The button padding was never part of the bug and still wants the full ≤900px range. */
@media (max-width: 900px) { .cw--v3 .cw-bar__btn { padding: 0 var(--space-2); } }

/* ---- Firas Code home: remove the splash-screen vocabulary ----------------
   A 72px teal→#5b8def gradient tile that BOUNCES forever (cwFloat, 5s infinite)
   under a 40px accent glow and a white inset bevel, beneath a 34px serif
   headline whose text is a clipped gradient. That is a kids'-app splash screen,
   not a developer tool — and the gradient text has no fallback, so if
   background-clip fails the product name renders invisible. */
.cw-home { background: none; }                     /* dotted-paper backdrop + glow */
.cw-home__mark {
  inline-size: 50px; block-size: 50px;
  border-radius: var(--radius-lg);
  background: var(--color-accent-soft);
  border: var(--border-hairline) solid color-mix(in srgb, var(--color-accent) 28%, var(--color-border-hairline));
  box-shadow: none;
  animation: none;                                  /* no idle bobbing */
}
.cw-home__mark-glyph { font: var(--weight-semibold) 19px/1 var(--font-mono, monospace); color: var(--color-accent); letter-spacing: 0; }
.cw-home__hero h1 {
  background: none;
  -webkit-background-clip: border-box;
  background-clip: border-box;
  -webkit-text-fill-color: currentColor;            /* restore a real, always-visible fill */
  color: var(--color-text-primary);
  font: var(--weight-semibold) 24px/1.2 var(--font-sans);
  letter-spacing: -0.01em;
}
.cw-home__card { border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); backdrop-filter: none; }
/* The same gradient blob followed you into the IDE toolbar. */
.cw-bar__logo {
  inline-size: 26px; block-size: 26px;
  border-radius: var(--cwv-radius-chip, var(--radius-sm));
  background: var(--color-accent-soft);
  border: var(--border-hairline) solid color-mix(in srgb, var(--color-accent) 26%, transparent);
  color: var(--color-accent);
  font: var(--weight-semibold) 10px var(--font-mono, monospace);
  box-shadow: none;
}
@media (prefers-reduced-motion: reduce) { .cw-home__mark { animation: none !important; } }

/* ════════════════════════════════════════════════════════════════════════════
   FIRAS BRAIN (.fb-*) — grounded answers over the user's OWN documents
   4th product alongside Firas AI / Agent / Code. Appended LAST on purpose:
   this file has ~six override layers that win on SOURCE ORDER at equal
   specificity, so anything inserted mid-file would silently lose. Additive
   only — no existing line is touched, no !important anywhere.

   Laws obeyed (each verified against the precedent named):
     · tokens only — no literal colours outside the --fb-* block + rgba() scrims
     · flex-fence law (.cw/.cw-bar/.cw-main, 3924-3946): one full-height flex
       column, fixed 0-0-auto fences top+bottom, exactly ONE 1-1-auto;min-height:0
     · body{overflow:hidden} (170) — every scroller declares its own overflow
     · `font:` shorthand always paired with a [dir="rtl"] font-family rule (223)
     · dvh restated after vh on every modal cap (4962-4967)
     · safe-area insets applied as PHYSICAL padding-left/right (4898-4938)
     · z-index picked inside the hand-maintained 1200-1400 band (840/886/904)
   The view root carries its own dir attribute (app.js, as renderCodeHome does),
   so [dir="rtl"] .fb-* matches the component root — NOT <html>, which is
   permanently ltr.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── Component-local tokens (marker block, .cw--v3 precedent at 4042-4056) ──
   Declared on the workspace root so every .fb-* descendant inherits them; the
   reader overlay also carries var() fallbacks in case it is ever portaled out
   of this subtree. Values are derived from the global tokens so they follow
   the theme automatically; only the shadow/scrim opacities need a dark tweak. */
/* .fb-reader is in this selector list because app.js appends it to document.body (it is a
   modal, so it must escape the workspace's stacking/overflow context). Declaring the tokens
   ONLY on #brainWorkspace left every --fb-* var unresolved there — and an unresolvable var()
   invalidates the whole `border:` shorthand at computed-value time, so the panel lost its
   outline and its header divider entirely. */
#brainWorkspace, .fb-reader {
  --fb-hair:          var(--color-border-hairline);
  --fb-rail-w:        272px;
  --fb-gap:           10px;
  --fb-radius-row:    10px;
  --fb-ic:            36px;   /* rail tile — denser sibling of .file-card__icon */
  --fb-ic-lg:         40px;   /* answer tile — exactly .file-card__icon (2527) */
  --fb-motion:        160ms var(--ease-out);
  --fb-shadow-float:  0 20px 60px color-mix(in srgb, var(--color-text-primary) 22%, transparent);
  --fb-scrim:         rgba(0,0,0,0.46);      /* same weight as .preview-overlay */
  --fb-scrim-soft:    rgba(0,0,0,0.28);      /* drag-over veil */
  --fb-accent-line:   color-mix(in srgb, var(--color-accent) 30%, transparent);
  --fb-danger-soft:   color-mix(in srgb, var(--color-error) 12%, transparent);
}
[data-theme="dark"] #brainWorkspace, [data-theme="dark"] .fb-reader {
  --fb-shadow-float:  0 20px 60px rgba(0,0,0,0.55);
  --fb-scrim:         rgba(0,0,0,0.62);
  --fb-scrim-soft:    rgba(0,0,0,0.42);
}

/* ── View gate ────────────────────────────────────────────────────────────
   BOTH halves are required: the root is created at runtime by app.js, so it
   must be display:none until .product-brain is on the body.
   #tierSwitch AND #tierSelect are hidden. .product-code only hides the first
   (3272), so on phones — where 4767 swaps the segmented strip for the compact
   dropdown — the tier control reappears over the workspace. Not repeating that
   bug: `.product-brain #tierSelect` is (1,1,0) and beats the (0,1,0) media-query
   rule that reveals it, without needing !important. */
#brainWorkspace { display: none; }
.product-brain #brainWorkspace {
  display: flex; flex: 1; min-height: 0;
  flex-direction: column;
  background: var(--color-bg);
  position: relative;
}
.product-brain .chat-scroll,
.product-brain .composer-wrap { display: none; }
.product-brain #tierSwitch,
.product-brain #tierSelect { display: none; }
.product-brain .composer__call { display: none; }

/* ── Root: the one full-height flex column ────────────────────────────────
   overflow-x is clipped here so nothing in the tree can ever make the page
   scroll sideways (body{overflow:hidden} would clip it instead of scrolling). */
.fb {
  position: relative;
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
  inline-size: 100%; max-inline-size: 100%;
  overflow-x: hidden;
  background: var(--color-bg);
  color: var(--color-text-primary);
  box-sizing: border-box;
}
.fb *, .fb *::before, .fb *::after { box-sizing: border-box; }

/* ── Top fence (flex:0 0 auto) ────────────────────────────────────────────── */
.fb-bar {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: var(--fb-gap);
  padding-block: 10px;
  padding-left:  max(var(--space-4), var(--safe-left));
  padding-right: max(var(--space-4), var(--safe-right));
  border-block-end: var(--border-hairline) solid var(--fb-hair);
  background: var(--color-surface);
}
.fb-bar__title {
  min-inline-size: 0;
  font: var(--weight-semibold) 14px var(--font-sans);
  letter-spacing: var(--tracking-tight);
  color: var(--color-text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
[dir="rtl"] .fb-bar__title { font-family: var(--font-arabic); }
/* Counts are LTR runs inside Arabic chrome — longhands (not the `font:`
   shorthand) so the mono family is a deliberate declaration, and isolated so
   "3/48" cannot reorder. Same recipe as .cw--v3 .cw-bar__count (4065). */
.fb-bar__count {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 11px; font-weight: var(--weight-semibold); line-height: 1.6;
  color: var(--color-text-muted);
  background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--fb-hair);
  border-radius: var(--radius-pill);
  padding: 2px 9px;
  direction: ltr; unicode-bidi: isolate; font-variant-numeric: tabular-nums;
}
.fb-bar__btn {
  flex: 0 0 auto;
  padding: 7px 13px;
  border: var(--border-hairline) solid var(--color-border);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text-primary);
  font: var(--weight-medium) 12.5px var(--font-sans);
  white-space: nowrap;
  transition: border-color var(--fb-motion), color var(--fb-motion),
              background var(--fb-motion);
}
[dir="rtl"] .fb-bar__btn { font-family: var(--font-arabic); }
.fb-bar__btn:hover { border-color: var(--color-accent); color: var(--color-accent); }
.fb-bar__btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.fb-bar__spacer { flex: 1 1 auto; min-inline-size: 0; }

/* ── The single growing region (flex:1 1 auto; min-height:0) ──────────────── */
.fb-main {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: row; align-items: stretch;
  min-inline-size: 0;
  overflow: hidden;
}

/* ── Sources rail — bordered scrolling column, .cw-tree/.cw-file pattern
   (3283-3292, overrides 4071-4082): .is-active rows carry a leading accent
   border. Its own nested fence set: head / list(grow) / foot. ─────────────── */
.fb-rail {
  flex: 0 0 var(--fb-rail-w);
  inline-size: var(--fb-rail-w); max-inline-size: 100%;
  display: flex; flex-direction: column; min-height: 0; min-inline-size: 0;
  border-inline-end: var(--border-hairline) solid var(--fb-hair);
  background: var(--color-surface);
}
.fb-rail__head {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: var(--space-2);
  padding: 12px 14px 9px;
  font: var(--weight-semibold) 10.5px var(--font-sans);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--color-text-muted);
}
[dir="rtl"] .fb-rail__head { font-family: var(--font-arabic); letter-spacing: 0.02em; }
.fb-rail__count {
  margin-inline-start: auto;
  font-family: var(--font-mono);
  font-size: 10px; font-weight: var(--weight-semibold); line-height: 1.6;
  color: var(--color-text-muted);
  background: var(--color-bg-subtle);
  border-radius: var(--radius-pill);
  padding: 2px 8px;
  direction: ltr; unicode-bidi: isolate; font-variant-numeric: tabular-nums;
}
/* The library has a real ceiling (20 documents, 3 for a guest). One slot left is a warning;
   full is a refusal waiting to happen — both are worth seeing before the upload is rejected.
   --color-error is defined in BOTH the light and dark blocks, so this reads correctly in
   either theme without a second rule. */
.fb-rail__count.is-near { color: var(--color-text-primary); }
.fb-rail__count.is-full {
  color: var(--color-error);
  background: color-mix(in srgb, var(--color-error) 12%, transparent);
}
.fb-rail__list {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  padding-block: 2px 6px;
}
.fb-rail__list[hidden] { display: none; }
.fb-rail__foot {
  flex: 0 0 auto;
  padding: 10px 12px;
  padding-block-end: max(10px, var(--safe-bottom));
  border-block-start: var(--border-hairline) solid var(--fb-hair);
  background: var(--color-surface);
}

/* ── One source row ───────────────────────────────────────────────────────── */
.fb-src {
  position: relative;
  display: flex; align-items: center; gap: var(--space-2);
  margin: 2px 6px;
  padding: 8px 10px;
  border-radius: var(--fb-radius-row);
  border-inline-start: var(--border-strong) solid transparent;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background var(--fb-motion), color var(--fb-motion),
              opacity var(--fb-motion), border-color var(--fb-motion);
}
.fb-src:hover { background: var(--color-bg-subtle); }
.fb-src:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.fb-src.is-active {
  background: var(--color-accent-soft);
  color: var(--color-accent);
  border-inline-start-color: var(--color-accent);
}
/* Deselected — excluded from retrieval: dimmed, and the accent is withdrawn. */
.fb-src.is-off {
  opacity: 0.45;
  background: transparent;
  color: var(--color-text-muted);
  border-inline-start-color: transparent;
}
.fb-src.is-off .fb-src__ic { background: var(--color-bg-subtle); color: var(--color-text-muted); }
.fb-src.is-busy { cursor: progress; }
/* 40px --color-accent-soft type tile from .file-card__icon (2527), one step
   denser because it repeats down a 272px rail. */
.fb-src__ic {
  display: inline-flex; align-items: center; justify-content: center;
  inline-size: var(--fb-ic); block-size: var(--fb-ic); flex: 0 0 auto;
  border-radius: var(--radius-md);
  background: var(--color-accent-soft);
  color: var(--color-accent);
}
.fb-src__ic svg { inline-size: 19px; block-size: 19px; }
.fb-src__main {
  flex: 1 1 auto; min-inline-size: 0;
  display: flex; flex-direction: column; gap: 2px;
}
.fb-src__name {
  font: var(--weight-medium) 12.5px var(--font-sans);
  color: inherit;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-align: start;
}
[dir="rtl"] .fb-src__name { font-family: var(--font-arabic); }
/* "24 صفحة · 1.2MB" — page counts must not reorder inside Arabic. */
.fb-src__meta {
  font-family: var(--font-mono);
  font-size: 10.5px; line-height: 1.5;
  color: var(--color-text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  direction: ltr; unicode-bidi: isolate; font-variant-numeric: tabular-nums;
}
.fb-src__x {
  flex: 0 0 auto;
  display: grid; place-items: center;
  inline-size: 22px; block-size: 22px;
  border-radius: 7px;
  color: var(--color-text-muted);
  font-size: 11px;
  opacity: 0;
  transition: opacity var(--fb-motion), background var(--fb-motion), color var(--fb-motion);
}
.fb-src:hover .fb-src__x, .fb-src__x:focus-visible { opacity: 1; }
.fb-src__x:hover { background: var(--fb-danger-soft); color: var(--color-error); }
@media (pointer: coarse) { .fb-src__x { opacity: 0.85; inline-size: 30px; block-size: 30px; } }
/* Ingest progress: absolutely positioned so appearing/disappearing can never
   change the row height (the rail sits next to a content-visibility:auto
   thread — no reflow allowed after first paint). */
.fb-src__bar {
  position: absolute; inset-inline: 10px; inset-block-end: 3px;
  block-size: 3px;
  border-radius: var(--radius-pill);
  background: var(--color-bg-subtle);
  overflow: hidden;
  display: none;
}
.fb-src.is-busy .fb-src__bar { display: block; }
.fb-src__bar-fill {
  display: block;
  block-size: 100%; inline-size: 0;
  border-radius: inherit;
  background: var(--color-accent);
  transition: inline-size var(--fb-motion);
}

/* ── Upload control (a <label> + a visually hidden <input type=file>) ─────── */
.fb-up { display: flex; flex-direction: column; gap: 6px; }
.fb-up input[type="file"] {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  padding: 0; margin: -1px; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.fb-up__btn {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  inline-size: 100%;
  padding: 10px 14px;
  border: var(--border-hairline) dashed var(--fb-accent-line);
  border-radius: var(--radius-md);
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font: var(--weight-semibold) 12.5px var(--font-sans);
  cursor: pointer;
  transition: border-color var(--fb-motion), background var(--fb-motion);
}
[dir="rtl"] .fb-up__btn { font-family: var(--font-arabic); }
.fb-up__btn:hover { border-color: var(--color-accent); }
.fb-up__btn:focus-within, .fb-up__btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.fb-up__btn svg { inline-size: 16px; block-size: 16px; }
/* Reserved height so status text arriving mid-upload cannot nudge the rail. */
.fb-up__status {
  min-block-size: 16px;
  font: var(--weight-regular) 11px/16px var(--font-sans);
  color: var(--color-text-muted);
  text-align: center;
  overflow-wrap: anywhere;
}
[dir="rtl"] .fb-up__status { font-family: var(--font-arabic); }

/* ── Answer thread — the scroller on the other side of .fb-main ──────────── */
.fb-thread {
  flex: 1 1 auto;
  min-inline-size: 0; min-block-size: 0; min-height: 0;
  overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  display: flex; flex-direction: column; gap: var(--space-6);
  padding: var(--space-6) var(--space-5);
  padding-left:  max(var(--space-5), var(--safe-left));
  padding-right: max(var(--space-5), var(--safe-right));
}

/* ── Empty state ──────────────────────────────────────────────────────────── */
.fb-empty {
  margin: auto;
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  max-inline-size: 380px;
  text-align: center;
}
.fb-empty__ic {
  display: grid; place-items: center;
  inline-size: 56px; block-size: 56px;
  border-radius: var(--radius-lg);
  background: var(--color-accent-soft);
  border: var(--border-hairline) solid var(--fb-accent-line);
  color: var(--color-accent);
}
.fb-empty__ic svg { inline-size: 26px; block-size: 26px; }
.fb-empty__title {
  font: var(--weight-semibold) 17px/1.4 var(--font-sans);
  letter-spacing: var(--tracking-tight);
  color: var(--color-text-primary);
}
[dir="rtl"] .fb-empty__title { font-family: var(--font-arabic); }
.fb-empty__hint {
  font: var(--weight-regular) 13px/1.65 var(--font-sans);
  color: var(--color-text-muted);
}
[dir="rtl"] .fb-empty__hint { font-family: var(--font-arabic); }

/* ── Drag-over veil — absolute inside .fb, so it never escapes the workspace.
   Kept out of flow and out of the flex fence maths entirely. ─────────────── */
/* Visibility is driven by the `hidden` ATTRIBUTE (app.js toggles drop.hidden on dragenter /
   dragleave / drop). A base `display:none` + an `.is-open` reveal would never fire, since
   nothing adds that class — clearing `hidden` alone would leave the base rule matching. */
.fb-drop {
  position: absolute; inset: 0; z-index: 1200;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-6);
  background: var(--fb-scrim-soft);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.fb-drop[hidden] { display: none; }
.fb-drop__in {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  padding: var(--space-8) var(--space-10);
  border: var(--border-strong) dashed var(--color-accent);
  border-radius: var(--radius-xl);
  background: var(--color-surface);
  box-shadow: var(--fb-shadow-float);
  color: var(--color-accent);
  font: var(--weight-semibold) 14px/1.5 var(--font-sans);
  text-align: center;
  max-inline-size: 100%;
}
[dir="rtl"] .fb-drop__in { font-family: var(--font-arabic); }

/* ── Bottom fence composer (flex:0 0 auto) ───────────────────────────────── */
.fb-ask {
  flex: 0 0 auto;
  display: flex; align-items: flex-end; gap: var(--space-2);
  padding-block-start: 12px;
  padding-block-end: max(12px, var(--safe-bottom));
  padding-left:  max(var(--space-4), var(--safe-left));
  padding-right: max(var(--space-4), var(--safe-right));
  border-block-start: var(--border-hairline) solid var(--fb-hair);
  background: var(--color-surface);
}
.fb-ask__in {
  flex: 1 1 auto; min-inline-size: 0;
  max-block-size: 160px;
  overflow-y: auto;
  resize: none;
  padding: 10px 14px;
  border: var(--border-hairline) solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  background: var(--color-bg-subtle);
  color: var(--color-text-primary);
  font: var(--weight-regular) 14px/1.55 var(--font-sans);
  transition: border-color var(--fb-motion), background var(--fb-motion);
}
[dir="rtl"] .fb-ask__in { font-family: var(--font-arabic); }
.fb-ask__in::placeholder { color: var(--color-text-muted); }
.fb-ask__in:focus { outline: none; border-color: var(--color-accent); background: var(--color-surface); }
/* Text-shaped, not an icon square: app.js writes the word ("إرسال" / "Send") into this button.
   A fixed 38x38 box would overflow on both sides, since the button reset sets font-family but
   not font-size, so the label renders at the UA default. */
.fb-ask__send {
  flex: 0 0 auto;
  /* flex, not grid: the .is-stop state adds a ::before square that must sit BESIDE the label,
     and grid would place both in the same cell. */
  display: inline-flex; align-items: center; justify-content: center;
  min-inline-size: 38px; block-size: 38px;
  padding-inline: 14px;
  white-space: nowrap;
  font: var(--weight-semibold) 12.5px var(--font-sans);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-on-accent);
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .fb-ask__send { font-family: var(--font-arabic); }
.fb-ask__send svg { inline-size: 18px; block-size: 18px; }
.fb-ask__send:hover:not(:disabled) { background: var(--color-accent-hover); }
.fb-ask__send:active:not(:disabled) { transform: scale(0.96); }
.fb-ask__send:disabled {
  background: var(--color-border-strong);
  color: var(--color-text-muted);
  cursor: not-allowed;
}
/* Stop state — the SAME button while an answer is streaming. Reads as an interrupt (neutral
   surface + hairline) rather than the accent-filled primary action, so it is obvious the click
   cancels rather than sends. */
.fb-ask__send.is-stop {
  background: var(--color-surface);
  color: var(--color-text-primary);
  border: var(--border-hairline) solid var(--fb-hair);
}
.fb-ask__send.is-stop:hover:not(:disabled) { background: var(--color-bg-subtle); }
.fb-ask__send.is-stop::before {
  content: ""; inline-size: 9px; block-size: 9px;
  border-radius: 2px; background: currentColor;
  margin-inline-end: 7px;
}

/* ── Inline citation chip [S1] — the .cw-thread__chip recipe (4181) ────────
   Mono longhands rather than the `font:` shorthand: the run is a bare index,
   it must stay mono + LTR-isolated in Arabic prose (same call as .wordmark 269
   and .msg-ai__badge 694). */
.fb-cite {
  display: inline-flex; align-items: center;
  vertical-align: baseline;
  margin-inline: 2px;
  padding: 0; border: none; background: none;
  line-height: inherit;
  cursor: pointer;
}
.fb-cite:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.fb-cite__n {
  font-family: var(--font-mono);
  font-size: 10px; font-weight: var(--weight-semibold); line-height: 1.6;
  color: var(--color-accent);
  background: var(--color-accent-soft);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
  white-space: nowrap;
  direction: ltr; unicode-bidi: isolate; font-variant-numeric: tabular-nums;
  transition: background var(--fb-motion), color var(--fb-motion);
}
.fb-cite:hover .fb-cite__n { background: var(--color-accent); color: var(--color-on-accent); }

/* ── "المصادر" block under an answer ──────────────────────────────────────
   Appended as a SIBLING of .md, so the 3740-3750 measure clamp (direct
   children of .msg-ai__body > .md only) does not reach it — it must state the
   reading measure itself or it spans wider than the prose column.
   .turn is content-visibility:auto (626): NO height transitions and no
   late-loading media in here, so nothing reflows after first paint. */
.fb-sources {
  max-inline-size: var(--reading-w);
  margin-block-start: var(--space-4);
  border: var(--border-hairline) solid var(--fb-hair);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  overflow: hidden;
}
.fb-sources__head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 10px 14px;
  background: var(--color-sidebar-bg);
  border-block-end: var(--border-hairline) solid var(--fb-hair);
  font: var(--weight-semibold) 10.5px/1.6 var(--font-sans);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--color-text-muted);
}
[dir="rtl"] .fb-sources__head { font-family: var(--font-arabic); letter-spacing: 0.02em; }
.fb-sources__list { display: flex; flex-direction: column; }

/* ── One cited source (file + exact page + the passage) ───────────────────── */
.fb-sourced {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: 11px 14px;
  border-block-start: var(--border-hairline) solid var(--fb-hair);
  border-inline-start: var(--border-strong) solid transparent;
  text-align: start;
  cursor: pointer;
  transition: background var(--fb-motion), border-color var(--fb-motion);
}
.fb-sourced:first-child { border-block-start: none; }
.fb-sourced:hover { background: var(--color-accent-soft); border-inline-start-color: var(--color-accent); }
.fb-sourced:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
/* Exactly .file-card__icon (2527) — 40px --color-accent-soft type tile. */
.fb-sourced__ic {
  display: inline-flex; align-items: center; justify-content: center;
  inline-size: var(--fb-ic-lg); block-size: var(--fb-ic-lg); flex: 0 0 auto;
  border-radius: var(--radius-md);
  background: var(--color-accent-soft);
  color: var(--color-accent);
}
.fb-sourced__ic svg { inline-size: 22px; block-size: 22px; }
.fb-sourced__main {
  flex: 1 1 auto; min-inline-size: 0;
  display: flex; flex-direction: column; gap: 3px;
}
.fb-sourced__name {
  font: var(--weight-semibold) 13px/1.4 var(--font-sans);
  color: var(--color-text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
[dir="rtl"] .fb-sourced__name { font-family: var(--font-arabic); }
/* "صفحة 12" — the page number is the whole point of this product; isolate it. */
/* P0 — this pill was eating the entire citation row on every phone.
   It is a flex item with no `flex` declaration (so flex:0 1 auto) and `white-space: nowrap`.
   A flex item's automatic minimum size floors it at its min-content width, and for unbroken
   text that is the WHOLE string — so it could not shrink and pushed everything else to zero.
   Its label carries a document heading sliced to 80 characters (app.js, brainAddFiles), so
   on a .docx the string is long. Measured at 375px: the filename and the snippet both
   computed to 0px wide while the pill rendered 275px and overflowed the card; at 320px the
   pill sat 67px outside it and was clipped away by .fb-sources{overflow:hidden}. The citation
   is the entire point of Firas Brain, and on a phone it showed nothing readable at all.

   `min-inline-size: 0` removes the automatic floor so the pill can shrink; the ellipsis keeps
   it legible when it does. On narrow screens it drops to its own line instead (below). */
.fb-sourced__pg {
  align-self: flex-start;
  min-inline-size: 0;
  max-inline-size: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--font-mono);
  font-size: 10px; font-weight: var(--weight-semibold); line-height: 1.6;
  color: var(--color-accent);
  background: var(--color-accent-soft);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
  white-space: nowrap;
  direction: ltr; unicode-bidi: isolate; font-variant-numeric: tabular-nums;
}
/* Below 640px the row wraps: the name and snippet take the full width and the page label
   sits under them, which is far more readable than three items fighting for 320px. */
@media (max-width: 640px) {
  .fb-sourced { flex-wrap: wrap; }
  .fb-sourced__main { flex: 1 1 100%; min-inline-size: 0; }
  .fb-sourced__pg { white-space: normal; margin-block-start: 4px; }
}
/* Two deterministic lines — a fixed max-block-size backs the clamp so the
   block's height is known before the text lands. */
.fb-sourced__snip {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  max-block-size: 2.9em;
  font: var(--weight-regular) 12px/1.45 var(--font-sans);
  color: var(--color-text-secondary);
  overflow-wrap: anywhere;
}
[dir="rtl"] .fb-sourced__snip { font-family: var(--font-arabic); }

/* ── Passage reader — .preview-overlay / .preview-panel grammar (2743-2799),
   same .is-open open/close vocabulary. z-index 1360 sits in the hand-kept band
   between .ann-edit-overlay (1350) and .dev-modal (1400). ─────────────────── */
.fb-reader {
  position: fixed; inset: 0; z-index: 1360;
  display: grid; place-items: center;
  padding: var(--space-4);
  background: var(--fb-scrim, rgba(0,0,0,0.46));
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out),
              visibility 0s linear var(--dur-base);
}
.fb-reader.is-open {
  opacity: 1; visibility: visible; pointer-events: auto;
  transition: opacity var(--dur-base) var(--ease-out), visibility 0s;
}
.fb-reader[hidden] { display: none; }
.fb-reader__panel {
  inline-size: 100%; max-inline-size: 720px;
  max-height: 86vh;                 /* fallback first — engines without dvh */
  max-height: 86dvh;                /* iOS Safari: vh is the URL-bar-hidden box */
  display: flex; flex-direction: column; min-height: 0;
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--fb-hair);
  border-radius: var(--radius-xl);
  box-shadow: var(--fb-shadow-float, var(--shadow-lg));
  overflow: hidden;
  transform: translateY(8px) scale(0.99);
  transition: transform var(--dur-base) var(--ease-out);
}
.fb-reader.is-open .fb-reader__panel { transform: none; }
.fb-reader__head {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  border-block-end: var(--border-hairline) solid var(--fb-hair);
  background: var(--color-sidebar-bg);
}
.fb-reader__title {
  flex: 1 1 auto; min-inline-size: 0;
  font: var(--weight-semibold) 13px/1.5 var(--font-sans);
  color: var(--color-text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
[dir="rtl"] .fb-reader__title { font-family: var(--font-arabic); }
.fb-reader__pg {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: var(--weight-semibold); line-height: 1.6;
  color: var(--color-accent);
  background: var(--color-accent-soft);
  border-radius: var(--radius-pill);
  padding: 2px 9px;
  white-space: nowrap;
  direction: ltr; unicode-bidi: isolate; font-variant-numeric: tabular-nums;
}
.fb-reader__x {
  flex: 0 0 auto;
  display: grid; place-items: center;
  inline-size: 34px; block-size: 34px;
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  transition: background var(--dur-fast), color var(--dur-fast);
}
.fb-reader__x:hover { background: var(--color-accent-soft); color: var(--color-accent); }
.fb-reader__x svg { inline-size: 17px; block-size: 17px; }
/* The ONE growing child of the panel column. */
.fb-reader__body {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  padding: var(--space-5);
  padding-block-end: max(var(--space-5), var(--safe-bottom));
}
/* Surrounding context — deliberately dimmer than the hit. */
.fb-reader__ctx {
  font: var(--weight-regular) 14px/1.9 var(--font-sans);
  color: var(--color-text-muted);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
[dir="rtl"] .fb-reader__ctx { font-family: var(--font-arabic); }
/* The cited passage itself — accent-soft field, leading accent border. */
.fb-reader__hit {
  display: block;
  margin-block: var(--space-3);
  padding: var(--space-3) var(--space-4);
  /* The cited passage is marked by a hairline rule and a tinted ground, not a 3px slab.
     A thick coloured edge on a callout is the most recognizable generated-UI tell there is,
     and the tint already carries the "this is the quoted passage" signal on its own. */
  border-inline-start: 1px solid var(--color-accent);
  border-radius: var(--radius-sm);
  background: var(--color-accent-soft);
  color: var(--color-text-primary);
  font: var(--weight-medium) 14px/1.9 var(--font-sans);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
[dir="rtl"] .fb-reader__hit { font-family: var(--font-arabic); }

/* ── Numeric isolation utility — attach to any run holding a page number,
   a count or a ratio that would otherwise reorder inside Arabic. ─────────── */
.fb-num { direction: ltr; unicode-bidi: isolate; font-variant-numeric: tabular-nums; }

/* ── Responsive ───────────────────────────────────────────────────────────
   ≤900px: .fb-main stacks to a column and the rail becomes a horizontal strip
   that scrolls sideways INSIDE ITSELF (never the page). Collapsing is driven
   by [hidden] on .fb-rail__list — an attribute, so no new structural class. */
@media (max-width: 900px) {
  .fb-main { flex-direction: column; }
  .fb-rail {
    flex: 0 0 auto;
    inline-size: 100%; max-inline-size: 100%;
    border-inline-end: none;
    border-block-end: var(--border-hairline) solid var(--fb-hair);
  }
  .fb-rail__head { cursor: pointer; padding-block: 10px 8px; }
  .fb-rail__list {
    flex: 0 0 auto;
    display: flex; flex-direction: row; align-items: stretch; gap: 6px;
    max-block-size: none;
    overflow-x: auto; overflow-y: hidden;
    scrollbar-width: none;
    padding: 2px 8px 8px;
  }
  .fb-rail__list::-webkit-scrollbar { display: none; }
  .fb-src { flex: 0 0 auto; inline-size: 216px; margin: 0; }
  .fb-rail__foot { padding-block: 8px; }
  .fb-up { flex-direction: row; align-items: center; gap: var(--space-2); }
  .fb-up__btn { inline-size: auto; flex: 1 1 auto; }
  .fb-up__status { flex: 0 1 auto; text-align: start; }
  .fb-thread { padding-block: var(--space-5); }
}
@media (max-width: 640px) {
  .fb-bar { padding-block: 8px; gap: var(--space-2); }
  .fb-bar__count { display: none; }
  .fb-thread {
    padding-left:  max(var(--space-4), var(--safe-left));
    padding-right: max(var(--space-4), var(--safe-right));
  }
  .fb-sources { max-inline-size: 100%; }
  /* Reader goes near-full-screen. dvh restated after vh, as everywhere else. */
  .fb-reader { padding: var(--space-2); }
  .fb-reader__panel {
    inline-size: 100%; max-inline-size: 100%;
    max-height: 94vh;
    max-height: 94dvh;
    border-radius: var(--radius-lg);
  }
  .fb-reader__body { padding: var(--space-4); }
  /* 4212 pins every input to 16px on phones to stop the iOS focus-zoom; the
     `font:` shorthand above is (0,1,0) and would out-specify that (0,0,1)
     rule no matter the source order, so restate it here on purpose. */
  .fb-ask__in { font-size: 16px; }
}

/* ── Firas Brain — bidi for genuinely mixed answers ───────────────────────────
   A Brain answer is the hardest bidi case this app has: Arabic prose that quotes an
   English document, so one reply carries English headings ("Slide 19 – Blood – CBC
   baseline"), Arabic sentences, Latin abbreviations (Hb, HCT, AFI, previa), lab values,
   ratios and dates — often inside a single paragraph.

   Setting one base direction for the whole answer cannot work: whichever one is chosen,
   the other script's runs get reordered against it, which is what made trailing terms
   and numbers land in the wrong place. `unicode-bidi: plaintext` is the CSS form of
   dir="auto" applied PER BLOCK — every paragraph, heading, list item and cell resolves
   its own base direction from its first strong character. An English heading then lays
   out LTR and the Arabic paragraph under it lays out RTL, in the same answer, with
   neither disturbing the other.

   Scoped to #brainWorkspace so the main chat's rendering is untouched. */
#brainWorkspace .md > p,
#brainWorkspace .md > h1, #brainWorkspace .md > h2, #brainWorkspace .md > h3,
#brainWorkspace .md > h4, #brainWorkspace .md > h5, #brainWorkspace .md > h6,
#brainWorkspace .md > ul > li, #brainWorkspace .md > ol > li,
#brainWorkspace .md > blockquote,
#brainWorkspace .md td, #brainWorkspace .md th {
  unicode-bidi: plaintext;
  text-align: start;
}
/* A heading that resolves LTR inside an RTL answer must keep the Latin face; the global
   [dir="rtl"] rule at 223 would otherwise force the Arabic family onto pure-Latin text. */
/* brainAutoDir() stamps dir="auto" on every block, so these attribute selectors resolve per
   block. A pure-Latin heading in an Arabic answer gets the Latin face; an Arabic heading in
   an English answer gets the Arabic one. [dir="auto"] alone is not enough — the browser
   resolves it, so match on :dir() and keep the attribute form as the fallback. */
#brainWorkspace .md :is(h1,h2,h3,h4,h5,h6,p,li):dir(ltr) { font-family: var(--font-sans); }
#brainWorkspace .md :is(h1,h2,h3,h4,h5,h6,p,li):dir(rtl) { font-family: var(--font-arabic); }

/* Isolate every atomic mixed-script run so it can never be split or reordered by the
   surrounding paragraph: inline code, and the citation chip itself (its inner number is
   already isolated, but the chip is what sits in the text flow). */
#brainWorkspace .md code { unicode-bidi: isolate; }
.fb-cite { unicode-bidi: isolate; }

/* Bare Latin/number runs inside Arabic prose — "AFI 5.3", "16/8/2026", "7.73×10³/µL" —
   keep tabular figures so columns of lab values do not jitter between lines. */
#brainWorkspace .md { font-variant-numeric: tabular-nums; }

/* The passage reader has the same mixed-script problem: a cited passage is usually in the
   document's language while the chrome around it follows the UI. Each paragraph already
   carries its own dir from JS; plaintext makes the fallback correct too. */
.fb-reader__body p { unicode-bidi: plaintext; text-align: start; }

/* Arabic tables and preformatted blocks. A source table frequently arrives as an Arabic
   markdown table (the prompt now demands a real one) or, when extraction flattened it, as a
   `pre` block. brainAutoDir() stamps a direction on both; these rules make the typography
   follow, so Arabic table cells are not rendered in a Latin monospace face and the header row
   reads from the correct side. */
#brainWorkspace .md table { inline-size: 100%; border-collapse: collapse; }
#brainWorkspace .md :is(td, th):dir(rtl) { font-family: var(--font-arabic); text-align: right; }
#brainWorkspace .md :is(td, th):dir(ltr) { font-family: var(--font-sans);   text-align: left; }
#brainWorkspace .md pre:dir(rtl) { direction: rtl; }
#brainWorkspace .md pre:dir(rtl) code { font-family: var(--font-arabic); unicode-bidi: plaintext; }
/* A wide table must scroll inside its own box, never widen the answer column. */
#brainWorkspace .md table { display: block; max-inline-size: 100%; overflow-x: auto; }

/* Force-vision toggle in the sources rail. Sits under the Add-files button because it changes
   how the NEXT upload is read, not anything already indexed. */
.fb-up__ocr {
  display: flex; align-items: flex-start; gap: 8px;
  margin-block-start: var(--space-2);
  cursor: pointer;
  font: 400 11.5px/1.45 var(--font-sans);
  color: var(--color-text-muted);
}
[dir="rtl"] .fb-up__ocr { font-family: var(--font-arabic); }
.fb-up__ocr input { flex: 0 0 auto; margin-block-start: 2px; accent-color: var(--color-accent); cursor: pointer; }
.fb-up__ocr:hover { color: var(--color-text-secondary); }

/* ── Firas Brain — layout corrections ─────────────────────────────────────────
   Measured problems this fixes, at 1440x900 and 375x812:

   DESKTOP: the thread was 900px wide but its content clamped to a 704px reading
   measure and left-aligned inside it, leaving ~156px of dead space down the right
   and three different content edges (bubble 544, prose 704, sources 704, container
   860). Everything now shares ONE centred column, so question, answer and sources
   line up on the same two edges.

   MOBILE: the sources rail was permanently expanded and ate 176px — 27% of the
   usable height — leaving 474px for the actual conversation. It now starts
   collapsed to its header strip and opens on tap (app.js sets the attribute).  */
#brainWorkspace { --fb-measure: 740px; }

/* One centred column for every turn, the pending row, and the empty state. */
.fb-thread > * {
  inline-size: 100%;
  max-inline-size: var(--fb-measure);
  margin-inline: auto;
}
/* The global prose clamp (styles.css:3740) narrows .md children to --reading-w, which
   left the answer text visibly narrower than the Sources card directly beneath it.
   Inside Brain the column IS the measure, so let the prose fill it. */
#brainWorkspace .msg-ai__body > .md > * { max-inline-size: none; }
#brainWorkspace .fb-sources { max-inline-size: none; }
.fb-thread { gap: var(--space-5); }

/* The question should read as a question — a quiet lead-in to the answer, not a
   competing block. Full-width in the column, so both edges agree with the answer. */
#brainWorkspace .msg-user__bubble {
  inline-size: 100%; max-inline-size: none;
  background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--fb-hair);
  border-radius: var(--radius-lg);
  padding: 10px 14px;
}

@media (max-width: 900px) {
  /* Collapsed rail: the header strip stays as the control, and the SOURCE LIST folds
     away to reclaim the 176px the conversation needed.

     What must NOT fold away is the uploader. This rule used to read
     `.fb-rail__list[hidden] + .fb-rail__foot { display: none }`, and .fb-rail__foot is
     the container holding the entire "Add files" control. The rail starts collapsed on
     every narrow viewport, so a first-time user opening Firas Brain on a phone saw an
     empty thread, an ask box, and a 31px strip — with no way to add the documents the
     whole product exists to read. It was not merely off-screen: display:none also put
     it out of reach of keyboard and screen-reader users entirely.

     The uploader is one ~60px row. It stays. */
  #brainWorkspace .fb-rail { flex: 0 0 auto; }
  #brainWorkspace .fb-rail__list[hidden] + .fb-rail__foot { display: block; }
  #brainWorkspace .fb-rail__head::after {
    content: "";
    inline-size: 7px; block-size: 7px;
    margin-inline-start: auto;
    border-inline-end: 2px solid currentColor;
    border-block-end: 2px solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px);
    opacity: .55;
    transition: transform var(--dur-fast) var(--ease-out);
  }
  #brainWorkspace .fb-rail:has(.fb-rail__list[hidden]) .fb-rail__head::after {
    transform: rotate(-45deg) translate(-1px, 1px);
  }
  .fb-thread { padding-block: var(--space-4); gap: var(--space-4); }
}

/* Answer actions (copy). Quiet until hovered — the answer is the content, these are tools. */
.fb-acts {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-block-start: var(--space-3);
}
.fb-act {
  font: var(--weight-medium) 12px var(--font-sans);
  color: var(--color-text-muted);
  background: transparent;
  border: var(--border-hairline) solid var(--fb-hair);
  border-radius: var(--radius-pill);
  padding: 4px 12px;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
[dir="rtl"] .fb-act { font-family: var(--font-arabic); }
.fb-act:hover {
  color: var(--color-text-primary);
  background: var(--color-bg-subtle);
  border-color: var(--color-border-strong);
}
.fb-act:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.fb-act:active { transform: scale(0.97); }

/* The PDF action reads as the primary export — gold, matching the document it produces.
   Both colours used to be hard-coded (#C9A227 at rest, #E4C468 on hover) with no light
   variant, so in the light theme the label sat at ~2.4:1 and then VANISHED on hover at
   ~1.6:1 — the button appeared to empty itself under the pointer. --plan-gold is the
   project's existing themed gold (#B8862A light / #D8B45A dark) and already solves this
   for the subscription chips; the hover state now comes from the theme, not a second literal. */
.fb-act--gold {
  color: var(--plan-gold);
  border-color: color-mix(in srgb, var(--plan-gold) 45%, transparent);
}
.fb-act--gold:hover:not(:disabled) {
  color: var(--plan-gold);
  background: color-mix(in srgb, var(--plan-gold) 14%, transparent);
  border-color: var(--plan-gold);
}
.fb-act:disabled { opacity: .55; cursor: default; transform: none; }

/* ══════════════════════════════════════════════════════════════════════════════
   COPY YOUR OWN QUESTION (Firas Brain)

   The answer has always had a copy bar; the question had nothing. In Brain the question is
   often the laboriously typed part, and re-running a sweep means copying it, changing a word
   and sending it again. `.msg-user__bubble *` sets user-select:none, so on a phone there was
   no way to get the text back at all.

   Hidden until the turn is hovered or the button is focused, so it never competes with the
   answer — but always reachable by keyboard, which is why it fades rather than being display:none.
   ══════════════════════════════════════════════════════════════════════════════ */
/* IN FLOW, NOT FLOATED OVER THE BUBBLE. Absolute positioning was the first instinct and it was
   wrong: the user bubble spans the full column width (measured 600px of a 600px turn), so there
   is no margin to sit in — the button landed on top of the text. A row underneath costs 26px,
   overlaps nothing, needs no direction-specific rule, and matches the action row the ANSWER
   already has. */
.fb-umsg-acts {
  display: flex;
  justify-content: flex-end;
  margin-block-start: 4px;
  block-size: 26px;
}
.fb-umsg-copy {
  display: inline-flex; align-items: center; justify-content: center;
  inline-size: 26px; block-size: 26px;
  padding: 0;
  color: var(--color-text-muted);
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-md);
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
/* :focus as well as :focus-visible — a keyboard Tab matches both, but a programmatic .focus()
   matches only :focus, and a control that cannot be revealed under test cannot be verified. */
.fb-thread .turn--user:hover .fb-umsg-copy,
.fb-umsg-copy:focus,
.fb-umsg-copy:focus-visible { opacity: 1; }
.fb-umsg-copy:hover { color: var(--color-text-primary); border-color: var(--color-border-strong); }
.fb-umsg-copy:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.fb-umsg-copy:active { transform: translateY(1px); }
/* Touch has no hover, so the affordance would never appear — show it permanently, quietly. */
@media (hover: none) {
  .fb-umsg-copy { opacity: .55; }
}

/* The identity control sits beside the export button and carries a live swatch of the
   chosen paper. It is deliberately quieter than --gold: it configures the export, it is
   not the export. */
.fb-act--theme { display: inline-flex; align-items: center; gap: 7px; }
.fb-act__swatch {
  inline-size: 13px; block-size: 13px; flex: 0 0 auto;
  border-radius: 3px;
  border: 1.5px solid currentColor;   /* overridden inline with the identity's accent */
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
}

/* ══════════════════════════════════════════════════════════════════════════════
   PDF IDENTITY PICKER

   Each card is a miniature of the sheet it produces, not a colour chip: the thing being
   chosen is a document, so the chooser shows a document. The preview is built from the
   identity's own paper/ink/accent, which is why almost every colour here arrives inline.
   ══════════════════════════════════════════════════════════════════════════════ */
.bpk-card-wrap { max-inline-size: 720px; }
.bpk-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 12px;
  max-block-size: 62vh;
  overflow-y: auto;
  padding: 2px;                       /* so a focus ring is not clipped by the scroll box */
}
.bpk-card {
  display: flex; flex-direction: column; align-items: stretch; gap: 7px;
  padding: 8px; text-align: start;
  background: var(--color-surface);
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.bpk-card:hover { border-color: var(--color-border-strong); box-shadow: var(--shadow-md, 0 6px 18px rgba(0,0,0,.10)); transform: translateY(-1px); }
.bpk-card:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.bpk-card.is-on { border-color: var(--color-accent); box-shadow: 0 0 0 1px var(--color-accent); }

/* The miniature sheet. A4 is 1:1.414, so the preview keeps that ratio — a preview in the
   wrong proportion misrepresents how much of the page the title actually occupies. */
.bpk-sheet {
  display: block; position: relative;
  aspect-ratio: 1 / 1.414;
  border-radius: 3px;
  padding: 13px 11px;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(128, 128, 128, .22);
}
.bpk-kick { display: block; inline-size: 26px; block-size: 3px; border-radius: 2px; opacity: .9; }
.bpk-ttl  { display: block; margin-block-start: 9px; font-size: 13px; font-weight: 700; line-height: 1.1; }
.bpk-rule { display: block; inline-size: 22px; block-size: 2px; margin-block: 6px 8px; }
.bpk-ln   { display: block; block-size: 2px; border-radius: 2px; opacity: .34; margin-block-end: 5px; }
.bpk-ln--s { inline-size: 62%; }
.bpk-name { font: var(--weight-medium) 12.5px var(--font-sans); color: var(--color-text-primary); }
[dir="rtl"] .bpk-name, [dir="rtl"] .bpk-desc { font-family: var(--font-arabic); }
.bpk-desc { font: var(--weight-regular) 10.5px/1.45 var(--font-sans); color: var(--color-text-muted); }

@media (max-width: 560px) {
  .bpk-grid { grid-template-columns: repeat(auto-fill, minmax(124px, 1fr)); max-block-size: 56vh; }
}

/* Touch sizing for the Brain action row.
   `.fb-act` used to compute to ~24px tall: the `font:` shorthand omits line-height, so
   it resets to `normal` (~14.4px) and 4px padding either side plus hairlines lands at
   half the 44px minimum. These three buttons — Copy, Copy with page references, Export
   PDF — are how a Brain answer leaves the app, and a mis-tap fires a real PDF render.
   The row already wraps, so extra height costs nothing horizontally. */
/* `(pointer: coarse), (max-width: 560px)` — BOTH conditions, matching the rule the main
   chat composer already uses. Coarse-pointer alone misses a narrow desktop window, and
   width alone misses a tablet; the two products must not disagree about how big a thumb is. */
@media (pointer: coarse), (max-width: 560px) {
  .fb-act {
    min-block-size: 44px;
    display: inline-flex; align-items: center;
    padding-inline: var(--space-4);
  }
  /* Same bump for the composer's send/stop control, which is also the ONLY way to
     cancel a long answer. It was a hard 38px `block-size` — not a min — while the
     identical control on the main chat composer was deliberately raised to 44px. */
  .fb-ask__send { min-inline-size: 44px; min-block-size: 44px; block-size: auto; }
  .fb-src__x    { inline-size: 36px; block-size: 36px; }
  .fb-reader__x { inline-size: 40px; block-size: 40px; }
}

/* ── Firas Code — live progress inside the conversation ───────────────────────
   The engine narrates itself here instead of in the .cw-diff overlay: a headline for the
   current phase, then one line per file carrying its own state. It is appended to the
   transcript and STAYS there when the run ends, so the conversation holds the record of
   what was actually done. Appended last, per the .cw source-order convention. */
.cw-live {
  border: var(--border-hairline) solid var(--cwv-hair, var(--color-border-hairline));
  border-radius: var(--radius-lg);
  background: var(--color-bg-subtle);
  padding: 10px 12px;
  margin-block: 8px;
  font: 400 12.5px/1.6 var(--font-sans);
}
[dir="rtl"] .cw-live { font-family: var(--font-arabic); }
.cw-live.is-fail { border-color: color-mix(in srgb, var(--color-error) 40%, transparent); }
.cw-live.is-done { opacity: .92; }

.cw-live__head { display: flex; align-items: center; gap: 8px; min-inline-size: 0; }
.cw-live__head strong {
  font: var(--weight-semibold) 12.5px var(--font-sans);
  color: var(--color-text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
[dir="rtl"] .cw-live__head strong { font-family: var(--font-arabic); }
.cw-live__t {
  font-size: 11.5px; color: var(--color-text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-inline-size: 0;
}
.cw-live__sp { flex: 1 1 auto; }
.cw-live__btn {
  flex: 0 0 auto;
  font: var(--weight-medium) 11px var(--font-sans);
  color: var(--color-text-muted);
  background: transparent;
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-pill);
  padding: 2px 10px; cursor: pointer;
}
[dir="rtl"] .cw-live__btn { font-family: var(--font-arabic); }
.cw-live__btn:hover { color: var(--color-text-primary); border-color: var(--color-border-strong); }

.cw-live__rows { margin-block-start: 8px; display: flex; flex-direction: column; gap: 3px; }
.cw-live__row {
  display: flex; align-items: baseline; gap: 8px;
  padding-inline-start: 6px;
  min-inline-size: 0;
}
.cw-live__g { flex: 0 0 auto; inline-size: 12px; text-align: center; font-size: 11px; line-height: 1.6; }
.cw-live__row--done  .cw-live__g { color: var(--color-accent); }
.cw-live__row--fail  .cw-live__g { color: var(--color-error); }
.cw-live__row--run   .cw-live__g { color: var(--color-text-primary); }
.cw-live__row--wait  .cw-live__g { color: var(--color-text-muted); opacity: .6; }
.cw-live__g--ok   { color: var(--color-accent); }
.cw-live__g--fail { color: var(--color-error); }
/* File paths are code: always LTR, never reordered by the surrounding Arabic. */
.cw-live__f {
  font-family: var(--font-mono); font-size: 11.5px;
  color: var(--color-text-primary);
  direction: ltr; unicode-bidi: isolate;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cw-live__row--wait .cw-live__f { color: var(--color-text-muted); }
.cw-live__d {
  font-size: 11.5px; color: var(--color-text-muted);
  min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
[dir="rtl"] .cw-live__d { font-family: var(--font-arabic); }
.cw-live__spin {
  flex: 0 0 auto;
  inline-size: 11px; block-size: 11px; border-radius: 50%;
  border: 1.6px solid var(--color-border-strong);
  border-top-color: var(--color-accent);
  animation: cwLiveSpin .7s linear infinite;
}
@keyframes cwLiveSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cw-live__spin { animation: none; } }
/* A stopped run keeps its record and offers to continue from where it got to. */
.cw-live.is-stopped { border-color: color-mix(in srgb, var(--color-accent) 34%, transparent); }
.cw-live__resume {
  color: var(--color-accent);
  border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);
}
.cw-live__resume:hover {
  color: var(--color-accent);
  background: var(--color-accent-soft);
  border-color: var(--color-accent);
}

/* ════════════════════════════════════════════════════════════════════════════
   MOTION — the instrument reading a value
   Appended last on purpose: this file wins on source order at equal specificity.

   One authored idea, applied consistently, instead of an effect per component: an
   instrument reports a value by a GRADUATION arriving at it. So selection across the whole
   app — a conversation, a product, a model tier — is a brass tick that grows into place on
   the reading edge, and hover is that same tick previewed faintly. Nothing travels, nothing
   scales, nothing bounces; the only thing that moves is the thing being measured.

   Cost discipline:
   · Only `transform` and `opacity` are animated — both composite off the main thread, so
     none of this competes with streaming, KaTeX or highlight.js for frame time.
   · Every element animates FROM a visible resting state. If a stylesheet or a CDN fails,
     the UI is simply static — never invisible.
   · No `will-change` anywhere: it is a targeted hint for a known-expensive animation, not a
     baseline, and left on at rest it costs memory on every row of a long list.
   · Durations stay inside 120–320ms. Anything slower reads as lag on a control you clicked.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── The graduation tick ───────────────────────────────────────────────────── */
.chat-item, .product-switch, .tier-switch__btn { position: relative; }

.chat-item::before,
.product-switch::before {
  content: "";
  position: absolute;
  inset-block: 6px;
  inset-inline-start: 0;
  inline-size: 1px;                 /* a hairline graduation — never a slab */
  background: var(--color-line);
  transform: scaleY(0);
  transform-origin: 50% 50%;
  opacity: 0;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out);
  pointer-events: none;
}
.chat-item:hover::before,
.product-switch:hover::before { transform: scaleY(0.45); opacity: 0.5; }
.chat-item.is-active::before   { transform: scaleY(1);    opacity: 1; }

/* The tier strip reads along the bottom edge — it is a horizontal scale. */
.tier-switch__btn::after {
  content: "";
  position: absolute;
  inset-inline: 22%;
  inset-block-end: 3px;
  block-size: 1px;
  background: var(--color-line);
  transform: scaleX(0);
  opacity: 0;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out);
  pointer-events: none;
}
.tier-switch__btn:hover::after      { transform: scaleX(0.5); opacity: 0.45; }
.tier-switch__btn.is-active::after,
.tier-switch__btn[aria-selected="true"]::after { transform: scaleX(1); opacity: 1; }

/* ── Chrome that catches light ─────────────────────────────────────────────── */
/* Engraved metal brightens at its edge before it changes colour. One inset hairline is the
   whole effect — cheaper than a shadow and it never blurs text underneath. */
.icon-btn, .btn-newchat, .landing__feature, .fb-act {
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.icon-btn:hover, .btn-newchat:hover {
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--color-line) 34%, transparent);
}
/* The old rule nudged every conversation row 2px sideways on hover. Rows are a LIST — when
   one shifts, the eye reads the whole column as unstable. The tick reports hover instead. */
.chat-item:hover, [dir="rtl"] .chat-item:hover { transform: none; }

/* Feature cards lift by their shadow, not by moving. */
.landing__feature:hover {
  border-color: color-mix(in srgb, var(--color-line) 40%, var(--color-border-hairline));
  box-shadow: 0 2px 10px rgba(0,0,0,0.30);
  transform: none;
}

/* ── Focus: always visible, never animated away ────────────────────────────── */
/* A focus ring that fades in is a focus ring a keyboard user out-runs. It appears at once;
   only its colour eases. */
:where(button, a, input, textarea, select, [tabindex]):focus-visible {
  transition: outline-color var(--dur-fast) var(--ease-out);
}

/* ── Menus and overlays ────────────────────────────────────────────────────── */
/* Panels resolve from slightly compressed to true size — the way a mechanism seats — rather
   than sliding in from an edge. 160ms: long enough to read as deliberate, short enough that
   a second click is never queued behind it. */
.tools-menu__panel:not([hidden]),
.mode-select__menu:not([hidden]),   /* was .mode-select__panel — a dead selector; the JS emits __menu */
.product-menu:not([hidden]) {
  animation: seat var(--dur-base) var(--ease-out) both;
  transform-origin: top center;
}
@keyframes seat {
  from { opacity: 0; transform: scale(0.985) translateY(-2px); }
  to   { opacity: 1; transform: none; }
}

/* ── Streaming caret ───────────────────────────────────────────────────────── */
/* Deliberately NOT restyled here. The caret already exists further up this file, already
   animates opacity only, and already reads --color-accent, which now resolves to brass.
   Re-declaring it would have added a seventh styling era to a file whose stacked eras are
   the exact defect the audit flagged — a duplicate that looks like an improvement and is
   really just one more rule for the next reader to reconcile. Nothing to do. */

/* ════════════════════════════════════════════════════════════════════════════
   THE BEAM — the chosen mark, and its streaming animation
   Appended last: this file resolves equal specificity by source order.
   ══════════════════════════════════════════════════════════════════════════ */

/* The Beam is a SOLID green letterform, so it needs no tile behind it. The old marks were
   drawn in --color-on-accent on a filled accent square; painting a solid mark on a solid
   square of its own colour would erase it. Tile removed, mark drawn in the accent itself. */
/* `.nib.is-aurora` is specificity (0,2,0) and re-declares the gradient tile, so a plain
   `.nib` reset loses to it and the landing mark rendered as a green Beam on a green tile —
   invisible. Both selectors are listed, at matching specificity.

   `.product-agent .nib` is the SAME trap, found later: it is also (0,2,0), so inside Firas
   Agent the plain `.nib` reset (0,1,0) lost and the green→blue gradient tile survived, with
   the accent-coloured Beam drawn on top of it. Green mark on a green-blue tile is the
   reported "الشعار ما مبين زين" — the mark was there, just with almost no contrast against
   its own background. Listing it here removes the tile in Agent like everywhere else.
   The product-switch DOT keeps its gradient: it is a status dot, not a logo. */
.nib,
.nib.is-aurora,
.product-agent .nib,
.msg-ai__avatar,
.msg-ai__avatar--agent {
  background: none;
  box-shadow: none;
  border-radius: 0;
  overflow: visible;
}
.nib .fmark,
.msg-ai__avatar .fmark {
  inline-size: 100%; block-size: 100%;
  display: block;
  color: var(--color-accent);          /* the stem + upper arm inherit this */
}
.fmark__stem,
.fmark__arm--1 { fill: currentColor; }
.fmark__arm--2 { fill: var(--color-accent-deep); }

/* The wordmark sits beside a mark that is now taller than it is wide, so give the lockup a
   touch more breathing room than the old square tile needed. */
.logo-lockup { gap: var(--space-3); }

/* ── Streaming: the mark emits while the answer arrives ──────────────────────
   The behaviour the owner asked for: the mark beside a reply is alive while the model is
   generating, then settles when it stops.

   Built from the letterform's own logic rather than a generic spinner — the arms TAPER, so
   they animate as light travelling outward along each arm, staggered, while the stem holds
   steady. Nothing spins, nothing jumps.

   Cost: `transform` and `opacity` only, both composited off the main thread, so this never
   competes with the markdown/KaTeX/highlight work happening on the same frames.
   `transform-box: fill-box` is required — without it an SVG transform-origin resolves
   against the whole viewBox and the arms would swing instead of scale. */
.fmark__arm {
  transform-box: fill-box;
  transform-origin: left center;
}
[dir="rtl"] .fmark__arm { transform-origin: right center; }

.msg-ai.is-generating .fmark__arm--1 { animation: beam-emit 1.5s var(--ease-inout) infinite; }
.msg-ai.is-generating .fmark__arm--2 { animation: beam-emit 1.5s var(--ease-inout) 0.22s infinite; }
.msg-ai.is-generating .fmark__stem   { animation: beam-hold 1.5s var(--ease-inout) infinite; }

@keyframes beam-emit {
  0%, 100% { opacity: 0.38; transform: scaleX(0.82); }
  45%      { opacity: 1;    transform: scaleX(1); }
}
@keyframes beam-hold {
  0%, 100% { opacity: 0.72; }
  45%      { opacity: 1; }
}

/* Settling. When generation ends the class comes off and the arms return to rest through
   the same easing — no snap, and no lingering animation burning frames on a finished reply. */
.fmark__arm, .fmark__stem {
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}

/* Reduced motion: the mark still says "working", but by breathing rather than travelling.
   Freezing it entirely would remove the only signal that a long answer is still coming. */
@media (prefers-reduced-motion: reduce) {
  .msg-ai.is-generating .fmark__arm--1,
  .msg-ai.is-generating .fmark__arm--2,
  .msg-ai.is-generating .fmark__stem {
    animation-name: rm-working !important;
    animation-duration: 1.6s !important;
    animation-iteration-count: infinite !important;
    transform: none !important;
  }
}
:root[data-motion="off"] .msg-ai.is-generating .fmark__arm--1,
:root[data-motion="off"] .msg-ai.is-generating .fmark__arm--2,
:root[data-motion="off"] .msg-ai.is-generating .fmark__stem {
  animation-name: rm-working !important;
  animation-duration: 1.6s !important;
  animation-iteration-count: infinite !important;
  transform: none !important;
}

/* ── Product transition ─────────────────────────────────────────────────────
   Tunes the cross-fade the View Transitions API performs when setProduct() swaps the shell.
   Both snapshots animate on the compositor, so this costs no layout and no paint.
   The old view leaves slightly faster than the new one arrives — an even cross-fade dips
   through a visibly empty middle, while an offset one reads as one thing becoming another. */
::view-transition-old(root) {
  animation: vt-out 170ms var(--ease-out) both;
}
::view-transition-new(root) {
  animation: vt-in 260ms var(--ease-out) both;
}
@keyframes vt-out { to   { opacity: 0; transform: scale(0.994); } }
@keyframes vt-in  { from { opacity: 0; transform: scale(1.006); } }

/* The setProduct() call is already gated on motionEnabled(), so a reduced-motion visitor
   never starts a transition. This is belt-and-braces for a transition begun elsewhere. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none !important; }
}
:root[data-motion="off"] ::view-transition-old(root),
:root[data-motion="off"] ::view-transition-new(root) { animation: none !important; }

/* ── Drawn status icons (were typed glyphs) ─────────────────────────────────
   The Firas Code progress column and the product-menu tick used "✓ ✗ ▸ ·" as characters.
   A character inherits the UI font, so the column jumped between Latin and Arabic metrics
   and each mark landed on a different baseline at a different weight. These are SVG now and
   need a box rather than a font-size. */
.cw-live__g {
  flex: 0 0 auto;
  inline-size: 13px; block-size: 13px;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 0;
}
.cw-live__g svg { inline-size: 100%; block-size: 100%; display: block; }

.product-menu__check {
  inline-size: 16px; block-size: 16px;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 0;
}
.product-menu__check svg { inline-size: 100%; block-size: 100%; display: block; }

/* The "running" row's marker breathes so an in-flight step is distinguishable from a
   finished one at a glance — opacity only, and it stops the moment the row changes state. */
.cw-live__row--run .cw-live__g { animation: rm-working 1.4s var(--ease-inout) infinite; }
@media (prefers-reduced-motion: reduce) { .cw-live__row--run .cw-live__g { animation-duration: 2s !important; } }

/* ── Overlays: one seating motion for all nine ──────────────────────────────
   Every modal in the app (memory, plans, redeem, admin, settings, Brain reader…) is built
   from the same .mem-overlay / .mem-card scaffold, copy-pasted nine times. The scrim already
   fades; the card only ever had `transform:none` applied on open, with no FROM state — so it
   appeared instantly inside a fading scrim, which reads as a jump.

   Giving the card a resting transform means the existing `.is-open` rule finally has
   something to animate away from — no JS change, all nine inherit it at once. */
.mem-card {
  transform: translateY(8px) scale(0.985);
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out);
  opacity: 0;
}
.mem-overlay.is-open .mem-card { transform: none; opacity: 1; }

/* The scrim resolves a touch slower than the card so the card leads and the background
   settles behind it, rather than both arriving flat together. */
.mem-overlay { transition: opacity var(--dur-base) var(--ease-out); }

/* Reduced motion: the card still arrives, it just does not travel. Removing the opacity
   change too would make a modal appear with no transition at all, which is more jarring,
   not less. */
@media (prefers-reduced-motion: reduce) {
  .mem-card { transform: none !important; }
}
:root[data-motion="off"] .mem-card { transform: none !important; }

/* ── iOS: never zoom the page just because someone tapped a field ───────────
   Mobile Safari zooms the viewport when a focused input computes below 16px. It is not a
   preference and there is no way to opt out per-element — the ONLY reliable fix is to keep
   every field at 16px or larger.

   Measured before this rule, at 375px: 5 of 7 reachable fields were under 16px, and all four
   sign-in fields were exactly 15px — which is why the login screen was the worst offender.

   Deliberately NOT solved with `maximum-scale=1` / `user-scalable=no` in the viewport meta.
   That does stop the zoom, but it also removes pinch-to-zoom entirely, which is a real
   accessibility loss for anyone who needs to magnify text — trading a small annoyance for a
   barrier. 16px costs nothing and leaves manual zoom working.

   Scoped to `(pointer: coarse)` so it reaches phones AND tablets (an iPad is ~768-1024px
   wide and still auto-zooms), plus a narrow-width branch, matching the convention already
   used by the touch-target rules in this file. A bare element selector is intentional: it
   catches fields in surfaces that do not exist until their product renders — Brain's ask
   box, the Code AI bar, settings, admin, redeem — without having to enumerate them. */
@media (pointer: coarse), (max-width: 560px) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
  textarea,
  select {
    font-size: 16px;
  }
  /* The search field was 13px and sets its own height from that size; keep it visually
     compact now that the text is larger. */
  .sidebar__search input { padding-block: 9px; }
}

/* ── Buttons are buttons, not text ──────────────────────────────────────────
   On iOS a press-and-hold anywhere selectable raises the native selection / Copy callout.
   The action row under a reply (Copy · Regenerate · Retry with Firas Max) was being caught
   by it, so a normal tap selected the label instead of firing the button.

   Applied to EVERY control in the app rather than to that one row: any button, any product,
   any surface added later. Text the user might legitimately want to copy — message bodies,
   code, cited passages — is deliberately untouched. */
button,
[role="button"],
.icon-btn,
.msg-actions button,
.fb-act,
.cw-bar__btn,
.cw-live__btn,
.tier-select__opt,
.tier-switch__btn,
.product-menu__item,
.mode-select__item,
.tools-menu__item,
.plan-cta,
.chat-item__actions button {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;   /* suppresses the long-press Copy/Look Up sheet on iOS */
}
/* …but never gag the content itself. */
.md, .msg-user__bubble, .fb-reader__hit, .code-card, pre, code {
  -webkit-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

/* ── The mark, animating harder ─────────────────────────────────────────────
   The first pass was too polite to notice: arms moving between 0.38 and 1.0 opacity across
   1.5s reads as "static" at the 24px the avatar actually renders at.

   Now the WHOLE mark breathes on a circular arc while the arms emit inside it — the mark
   swells, eases back, and the two arms travel through it out of phase. Still transform and
   opacity only, still compositor-only, but the amplitude is now large enough to read at
   thumbnail size. */


.msg-ai.is-generating .fmark__arm--1 { animation: beam-emit 1.25s var(--ease-inout) infinite; }
.msg-ai.is-generating .fmark__arm--2 { animation: beam-emit 1.25s var(--ease-inout) 0.30s infinite; }
.msg-ai.is-generating .fmark__stem   { animation: beam-hold 1.25s var(--ease-inout) infinite; }
@keyframes beam-emit {
  0%, 100% { opacity: 0.22; transform: scaleX(0.62); }
  46%      { opacity: 1;    transform: scaleX(1); }
}
@keyframes beam-hold {
  0%, 100% { opacity: 0.55; }
  46%      { opacity: 1; }
}

/* ── "Thinking…" before the first character ─────────────────────────────────
   Between pressing send and the first text arriving there was nothing on screen at all —
   and on this server that gap is the WHOLE wait, because replies arrive in one chunk.
   The label is driven by a data attribute so it stays localized. */
.msg-ai.is-thinking .msg-ai__body::before {
  content: attr(data-thinking);
  display: inline-flex; align-items: center;
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  animation: rm-working 1.5s var(--ease-inout) infinite;
}
[dir="rtl"] .msg-ai.is-thinking .msg-ai__body::before { font-family: var(--font-arabic); }

@media (prefers-reduced-motion: reduce) {
  .msg-ai.is-generating .fmark { animation: none !important; transform: none !important; }
}
:root[data-motion="off"] .msg-ai.is-generating .fmark { animation: none !important; transform: none !important; }

/* ════════════════════════════════════════════════════════════════════════════
   GENERATION INDICATOR — radiating, in place, trailing the text
   Replaces the earlier orbit. Three corrections, all requested:
     · NO rotation and no swing. The mark never turns.
     · The motion travels OUTWARD FROM THE CENTRE — a radial emit, not a wobble.
     · It rides at the END OF THE TEXT and moves as the text grows, then stops.
   ══════════════════════════════════════════════════════════════════════════ */

/* The avatar beside the reply is now completely still — the living part belongs at the
   text tip, where the writing is actually happening. */
.msg-ai.is-generating .fmark { animation: none; transform: none; }

/* The tip itself: the Beam, drawn as a MASK so it takes `background-color` and therefore
   follows the theme's accent — a plain SVG data-URI would freeze one colour into the file. */
.beam-tip {
  display: inline-block;
  inline-size: 0.92em; block-size: 0.92em;
  margin-inline-start: 0.28em;
  vertical-align: -0.14em;
  background-color: var(--color-accent);
  -webkit-mask: var(--beam-mask) center / contain no-repeat;
          mask: var(--beam-mask) center / contain no-repeat;
  /* Scale about the centre = the shape grows outward in every direction at once. */
  transform-origin: 50% 50%;
  animation: beam-radiate 1.15s var(--ease-inout) infinite;
  pointer-events: none;
}
:root {
  --beam-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath d='M18 10h6.5v44H18Z'/%3E%3Cpath d='M24.5 10H50l-5 8.5H24.5Z'/%3E%3Cpath d='M24.5 27H42l-4.5 8.5H24.5Z'/%3E%3C/svg%3E");
}
@keyframes beam-radiate {
  0%, 100% { transform: scale(0.68); opacity: 0.30; }
  48%      { transform: scale(1.10); opacity: 1; }
}

/* A second, softer ring pushing outward behind it — this is what reads as "radiating"
   rather than "throbbing". Pure transform+opacity, no blur, no shadow. */
.beam-tip::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--color-accent);
  opacity: 0;
  animation: beam-halo 1.15s var(--ease-out) infinite;
}
.beam-tip { position: relative; }
@keyframes beam-halo {
  0%   { transform: scale(0.5); opacity: 0.28; }
  70%  { transform: scale(1.9); opacity: 0; }
  100% { transform: scale(1.9); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .beam-tip { animation: rm-working 1.6s var(--ease-inout) infinite; transform: none !important; }
  .beam-tip::after { display: none; }
}
:root[data-motion="off"] .beam-tip { animation: rm-working 1.6s var(--ease-inout) infinite; transform: none !important; }
:root[data-motion="off"] .beam-tip::after { display: none; }

/* The avatar is STILL. Two earlier layers animate its arms and stem during generation; those
   selectors target the arm elements, so a rule on the parent .fmark cannot override them.
   Stopped explicitly here, last in source order.
   Rationale: the generation indicator now rides at the END OF THE TEXT, where the writing is
   happening and where it can follow the text as it grows. An avatar pinned at the top of the
   reply cannot do that, and running both puts two competing pulses on screen for one event. */
.msg-ai.is-generating .fmark__arm--1,
.msg-ai.is-generating .fmark__arm--2,
.msg-ai.is-generating .fmark__stem,
.msg-ai.is-generating .fmark {
  animation: none !important;
  transform: none !important;
  opacity: 1 !important;
}

/* ── The mark sits BELOW the text, and descends with it ─────────────────────
   Correction: it was riding inline at the end of the last sentence. The owner asked for
   Claude Code's placement — the mark on its OWN LINE beneath the answer, moving down the
   page as the answer grows, then stopping when generation ends.

   The tip is now a SIBLING of .md inside .msg-ai__body (see placeBeamTip), not a node
   inside the rendered markdown. That is what makes the position stable: its indent comes
   from .md's box, which does not change, instead of from whichever paragraph or list item
   happened to be last that frame. It is also created once per turn and never replaced, so
   the animation below actually runs to completion rather than restarting every paint.

   Motion is deliberate: a pure centre-out radiate. No rotation, no left/right travel. */
/* TWO ANIMATIONS MUST NOT SHARE A PROPERTY.
   This carried `beam-radiate` (infinite, animates transform) and `beam-tip-in` (260ms,
   `both`, also animated transform). Later name wins on a conflicting property, and `both`
   retains the end state forever — so 260ms after the mark appeared, `transform: scale(1)`
   was pinned permanently and the radiate pulse stopped dead. The mark looked completely
   static, which is exactly what was reported: "مابيها انميشن حالياً".

   The entrance now touches OPACITY ONLY. `beam-radiate` owns `transform` alone, with
   nothing competing for it, so the pulse runs for as long as the answer is being written.

   Size: 1.4em, up from 1.05em — Claude's writing indicator is a clearly-visible mark, not
   a speck. At the assistant body's 16px this renders ~22px. */
.beam-tip {
  display: block;
  inline-size: 1.4em; block-size: 1.4em;
  margin-inline: 0;
  margin-block-start: 0.5em;
  vertical-align: baseline;
  /* ONE animation, deliberately. beam-radiate already animates BOTH transform and opacity,
     so any second animation here would collide on one of them and — with a `both` fill —
     freeze it permanently. That is the bug this replaced, and a separate entrance buys
     nothing: the cycle begins at scale(0.68)/opacity(0.30), which IS a soft appearance. */
  animation: beam-radiate 1.15s var(--ease-inout) infinite;
}
[dir="rtl"] .beam-tip { margin-inline: 0; }

/* The halo ring reads more strongly at this size, so it is scaled to match. */
.beam-tip::after { animation: beam-halo 1.25s var(--ease-out) infinite; }

/* Reduced motion: keep the mark (it carries real state — "still writing") but drop the
   travel, leaving a calm opacity breathe. Gentler, not zero. */
@media (prefers-reduced-motion: reduce) {
  .beam-tip { animation: rm-working 1.6s var(--ease-inout) infinite; transform: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   HELD IMAGES — a cross-origin image inside a reply does not fetch itself.
   See installPurifyHooks() in app.js: the src is parked in data-src so a URL that
   smuggles conversation text out cannot fire on paint. This makes the parked state
   look like an offer rather than a failure, and names the host so the choice is real.
   ══════════════════════════════════════════════════════════════════════════════ */
img.md-img-held {
  display: block;
  inline-size: min(100%, 320px);
  min-block-size: 92px;
  border: 1px dashed var(--color-border-strong, rgba(0,0,0,.18));
  border-radius: var(--radius-md, 10px);
  background:
    radial-gradient(closest-side, var(--color-accent-soft) 0%, transparent 100%),
    var(--color-surface-sunken, rgba(0,0,0,.03));
  cursor: pointer;
  position: relative;
  transition: border-color .18s var(--ease-out), background-color .18s var(--ease-out);
}
img.md-img-held::after {
  content: "🖼  اضغط لعرض الصورة — " attr(data-host);
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-3, 12px);
  font: var(--weight-medium, 500) 0.82rem/1.45 var(--font-arabic, system-ui);
  color: var(--color-text-muted);
  text-align: center;
  word-break: break-all;
}
img.md-img-held:hover,
img.md-img-held:focus-visible {
  border-color: var(--color-accent);
  background-color: var(--color-accent-soft);
}
img.md-img-held:hover::after { color: var(--color-accent); }
@media (prefers-reduced-motion: reduce) { img.md-img-held { transition: none; } }

/* ══════════════════════════════════════════════════════════════════════════════
   WELCOME — OPTICAL CENTRING

   The greeting was pinned to the top of the scroll area: on a 812px phone it sat at
   y≈56 with 408px of nothing between it and the composer. Two things went wrong at once,
   and they share a cause.

   · The empty state read as "the page failed to load the rest", because the eye expects
     content to be balanced in the space it is given, not stacked against the ceiling.
   · The halo is positioned relative to .welcome, so with .welcome only 148px tall and
     glued to the top, the glow spilled downward and ended up centred roughly 250px BELOW
     the mark it exists to sit behind. That detached green blob floating in dead space is
     the "نشاز" that was reported twice.

   Centring the block in its scroll area fixes both: the greeting lands in the optical
   centre above the composer, and the halo — being anchored to it — travels with it and
   sits behind the mark again, which is the only place it ever made sense.

   `min-block-size` rather than `block-size`, so a taller empty state (starter chips, a
   notice) still grows and scrolls instead of being clipped. `.hidden` carries !important
   so the thread view is unaffected.
   ══════════════════════════════════════════════════════════════════════════════ */
#welcome {
  min-block-size: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* The 64px top padding existed only to push the greeting off the ceiling. Centring does
   that now, and keeping it would just bias the block downward. A small remainder is left
   so the mark never kisses the header on a short viewport. */
#welcome > .welcome {
  padding-block-start: var(--space-6);
  padding-block-end: var(--space-6);
  inline-size: 100%;
}
@media (max-width: 560px) {
  #welcome > .welcome { padding-block-start: var(--space-4); }
}

/* HALO ANCHORING.
   `inset-block-start: -26%` resolves against .welcome's own HEIGHT — a box whose height is
   whatever the greeting happens to be, and which changed the moment the block was centred.
   The result was a 435px glow whose centre sat ~136px BELOW the mark it is supposed to be
   radiating from: a detached green blob sitting in empty space, reported twice as "نشاز".

   Centring it on the block instead removes the dependency entirely. The glow now sits
   behind the mark and the greeting at every viewport and every string length, because
   50%/-50% is self-correcting in a way that a tuned negative percentage never is. */
.welcome__halo {
  inset-block-start: 50%;
  translate: -50% -50%;
}

/* RTL — THE HALO WAS OFF-SCREEN IN ARABIC.

   `inset-inline-start: 50%` + `translate: -50%` centres correctly in LTR only. In RTL
   `inset-inline-start` maps to `right`, so the box is measured from the opposite edge and
   the -50% translate then pushes it further the SAME way instead of back. Measured on a
   375px viewport with dir="rtl": the halo's centre sat at x = -248 while the mark sat at
   x = 337 — the glow was almost entirely off the left edge of the screen, in the app's
   primary language. This predates the centring work; it has been wrong the whole time,
   which is the likeliest reason the greeting kept reading as "نشاز" no matter what the
   gradient itself was doing.

   `inset-inline: 0` + `margin-inline: auto` centres an absolutely-positioned box with a
   definite width in BOTH directions, with no translate to get the sign wrong. */
.welcome__halo {
  inset-inline: 0;
  margin-inline: auto;
  translate: 0 -50%;
}

/* The secondary highlight sat at a PHYSICAL `62% 62%`, so it stayed on the same side of
   the screen when the layout mirrored — the light appeared to come from the far side of
   the text in Arabic. Driving its x through a variable lets it follow the reading
   direction, so the glow always falls on the side the greeting starts from. */
.welcome__halo { --halo-hx: 62%; }
[dir="rtl"] .welcome__halo { --halo-hx: 38%; }
.welcome__halo {
  background:
    radial-gradient(closest-side circle at 50% 50%,
      color-mix(in srgb, var(--color-accent) 18%, transparent) 0%,
      color-mix(in srgb, var(--color-accent) 13%, transparent) 20%,
      color-mix(in srgb, var(--color-accent) 8%,  transparent) 38%,
      color-mix(in srgb, var(--color-accent) 4%,  transparent) 54%,
      color-mix(in srgb, var(--color-accent) 1.6%,transparent) 68%,
      color-mix(in srgb, var(--color-accent) 0.4%,transparent) 80%,
      transparent 88%),
    radial-gradient(closest-side circle at var(--halo-hx) 62%,
      color-mix(in srgb, var(--color-accent) 9%,  transparent) 0%,
      color-mix(in srgb, var(--color-accent) 5%,  transparent) 28%,
      color-mix(in srgb, var(--color-accent) 2%,  transparent) 50%,
      color-mix(in srgb, var(--color-accent) 0.5%,transparent) 70%,
      transparent 84%);
}

/* The glow is deliberately WIDER than its column (435px in a 327px content box), and for an
   over-constrained absolutely-positioned box `margin-inline: auto` cannot centre — the
   used value collapses toward the inline start, which is why LTR and RTL each sat 30px off
   centre in opposite directions. `left` + a -50% translate are PHYSICAL, so they centre
   identically regardless of direction. That is the right choice here precisely because a
   decorative wash should NOT mirror; only its internal highlight follows the text, and
   that is handled by --halo-hx above. */
.welcome__halo {
  inset-inline: auto;
  left: 50%;
  margin-inline: 0;
  translate: -50% -50%;
}

/* ══════════════════════════════════════════════════════════════════════════════
   ONE GENERATION INDICATOR, NOT TWO.

   `.stream-caret::after` painted a 2px blinking bar at the end of the answer while the
   Beam pulsed on the line below it. Both said the same thing — "still writing" — so the
   pair competed for the same glance, and because the caret is `display:inline-block` on a
   block container it also opened its own line box: a stray green tick floating in the gap
   between the last sentence and the mark. Measured: 53px of dead space inside .md that had
   no content in it.

   The Beam is the chosen indicator, so the caret goes. The class itself is left in place —
   setStreamingUI still toggles it and it remains a useful state hook — only its paint is
   dropped, so nothing that reads the class breaks.
   ══════════════════════════════════════════════════════════════════════════════ */
.stream-caret::after { content: none; }

/* ══════════════════════════════════════════════════════════════════════════════
   TOGGLE KNOB — LOGICAL *AND* ON THE COMPOSITOR.

   An earlier pass moved this knob from `transform` to `inset-inline-start` for a good
   reason: the physical transform needed a hand-negated RTL twin, so the travel distance
   lived in three places and drifted whenever the track or knob size changed.

   But `inset-inline-start` is a layout property. Animating it makes the browser reflow on
   every frame of a control people tap constantly — the one place motion must be free.

   Both goals are reachable at once: keep ONE travel constant, take the direction from a
   variable rather than a duplicated selector, and animate `translate`, which is
   composited. The RTL twin below only flips a sign; it never restates the distance.
   ══════════════════════════════════════════════════════════════════════════════ */
.tools-menu__switch::after {
  --knob-travel: 14px;          /* 34 track - 16 knob - 2 rest - 2 end */
  --knob-dir: 1;
  inset-inline-start: 2px;
  transition: translate var(--dur-fast) var(--ease-out);
  will-change: translate;
}
[dir="rtl"] .tools-menu__switch::after { --knob-dir: -1; }
.tools-menu__item.is-on .tools-menu__switch::after,
[dir="rtl"] .tools-menu__item.is-on .tools-menu__switch::after {
  inset-inline-start: 2px;      /* the rest position never changes — only the translate */
  transform: none;
  translate: calc(var(--knob-travel) * var(--knob-dir));
}
@media (prefers-reduced-motion: reduce) {
  .tools-menu__switch::after { transition-duration: 1ms; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   HISTORY — "COULD NOT LOAD" IS A DIFFERENT STATE FROM "NOTHING HERE".

   The failed-load state reuses the empty-state layout but must not look like it: an empty
   history is neutral, a failed one is a problem the user can act on. So the icon takes a
   warning tint and the retry button is a real, reachable control rather than a hint.
   ══════════════════════════════════════════════════════════════════════════════ */
.history__empty--error .history__empty-ic {
  opacity: 1;
  color: var(--color-warn, #C08A2E);
}
.history__retry {
  margin-block-start: var(--space-2);
  padding: 7px 14px;
  min-block-size: 34px;
  border: var(--border-hairline) solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text-primary);
  font: var(--weight-medium) var(--text-sm) / 1 var(--font-sans);
  cursor: pointer;
  /* Composited only, and inside the 300ms UI budget. */
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out);
}
.history__retry:hover:not(:disabled) {
  background: var(--color-accent-soft);
  border-color: var(--color-accent);
}
.history__retry:focus-visible {
  outline: 2px solid var(--color-accent-ring);
  outline-offset: 2px;
}
.history__retry:disabled { opacity: 0.55; cursor: default; }
/* Touch target floor — this is the only way out of the state, so it must be easy to hit. */
@media (pointer: coarse), (max-width: 560px) {
  .history__retry { min-block-size: 44px; padding-inline: 18px; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   THE GENERATION MARK — REDESIGNED

   Three changes, all requested directly:

   1. NOT A SCALE PULSE. The mark used to bounce 0.68→1.10 every 1.15s. On an element
      that is now permanent that reads as fidgeting, and per the motion rules a
      CONSTANT, ongoing animation is `linear` — an ease curve implies a start and an end,
      which a "still working" indicator does not have. It is now a highlight that sweeps
      across the glyph at constant speed: the mark holds its size and position, and only
      the light moves. Calmer, and unmistakably "in progress" rather than "throbbing".

   2. IT STAYS WHEN GENERATION ENDS. The animation is gated on `.is-generating`, which is
      already toggled per turn, so the shimmer simply stops and the mark remains as a
      signature under the finished answer. No element is created or destroyed.

   3. A DIVIDER ABOVE IT. The mark now sits under a hairline rule spanning the text column,
      which is what turns it from a loose floating glyph into a deliberate end-of-answer
      mark. The rule is on the tip itself (full width), the glyph is drawn as its ::before.
   ══════════════════════════════════════════════════════════════════════════════ */
.beam-tip {
  display: block;
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: auto;
  margin-block-start: 1.15em;
  padding-block-start: 0.85em;
  border-block-start: var(--border-hairline) solid var(--color-border-hairline);
  background: none;
  -webkit-mask: none;
          mask: none;
  animation: none;                 /* the glyph animates, not the container */
  transform: none;
  position: relative;
  pointer-events: none;
}
[dir="rtl"] .beam-tip { margin-inline: 0; }

/* The glyph. Same Beam mask as everywhere else, so it stays one logo. */
.beam-tip::before {
  content: "";
  display: block;
  inline-size: 1.25em;
  block-size: 1.25em;
  background-color: var(--color-accent);
  -webkit-mask: var(--beam-mask) center / contain no-repeat;
          mask: var(--beam-mask) center / contain no-repeat;
}

/* The old halo ring belonged to the pulse; with a sweep it just muddies the glyph. */
.beam-tip::after { content: none; }

/* THE SWEEP. A light band travels across the glyph while — and only while — the turn is
   generating. `linear`, because constant motion has no start or end to ease. 1.6s is slow
   enough to read as breathing rather than blinking. */
.msg-ai.is-generating .beam-tip::before {
  background-image: linear-gradient(100deg,
    transparent 24%,
    color-mix(in srgb, var(--color-text-primary) 65%, transparent) 46%,
    transparent 68%);
  background-size: 280% 100%;
  background-repeat: no-repeat;
  animation: beam-sweep 1.6s linear infinite;
}
@keyframes beam-sweep {
  from { background-position: 175% 0; }
  to   { background-position: -75% 0; }
}

/* Reduced motion: the mark still reports work, without travel. */
@media (prefers-reduced-motion: reduce) {
  .msg-ai.is-generating .beam-tip::before {
    background-image: none;
    animation: rm-working 1.8s var(--ease-inout) infinite;
  }
}
:root[data-motion="off"] .msg-ai.is-generating .beam-tip::before {
  background-image: none;
  animation: rm-working 1.8s var(--ease-inout) infinite;
}

/* ══════════════════════════════════════════════════════════════════════════════
   PER-BLOCK DIRECTION IN THE MAIN CHAT — equations stop fighting the prose.

   #brainWorkspace has carried `unicode-bidi: plaintext` for a while and it works; the main
   chat was deliberately left out. That exemption is what produced the scrambled answers:
   a numbered list where one item is an English sentence, the next is Arabic, and several
   carry inline LaTeX was laid out with ONE forced RTL base direction for every line. The
   Latin runs and their parentheses were then reordered against that base, so a step read
   as "(Euler's totient function for a prime modulus) .12" with the closing clause leading,
   and Arabic lines interleaved their equations mid-sentence.

   `plaintext` is the CSS form of dir="auto" applied PER BLOCK: each paragraph, heading,
   list item and cell resolves its own base direction from its first strong character. An
   English step lays out LTR, the Arabic step under it lays out RTL, and neither disturbs
   the other — which is exactly the "المعادلات تنفصل عن الشرح" that was asked for, achieved
   at the layout level rather than by hoping the model formats around the bug.

   Display math is additionally forced to its own line and centred, so an equation is never
   a participant in a bidi run at all — the strongest form of separation available.
   ══════════════════════════════════════════════════════════════════════════════ */
.msg-ai__body .md > p,
.msg-ai__body .md > h1, .msg-ai__body .md > h2, .msg-ai__body .md > h3,
.msg-ai__body .md > h4, .msg-ai__body .md > h5, .msg-ai__body .md > h6,
.msg-ai__body .md > ul > li, .msg-ai__body .md > ol > li,
.msg-ai__body .md > blockquote,
.msg-ai__body .md td, .msg-ai__body .md th {
  unicode-bidi: plaintext;
  text-align: start;
}

/* An equation on its own line is never part of a mixed run, so it can never be reordered
   against the sentence around it. This is the separation the owner asked for, enforced in
   layout rather than left to the model's formatting. */
.msg-ai__body .md .katex-display {
  display: block;
  direction: ltr;
  unicode-bidi: isolate;
  text-align: center;
  margin-block: var(--space-4);
  overflow-x: auto;
  overflow-y: hidden;
}
/* Inline math keeps its own LTR run and cannot leak its direction into the Arabic around
   it — `isolate` (not `embed`) so the surrounding neutrals resolve against the paragraph,
   not against the formula. */
.msg-ai__body .md :not(.katex-display) > .katex {
  direction: ltr;
  unicode-bidi: isolate;
}

/* ══════════════════════════════════════════════════════════════════════════════
   ANNOUNCEMENT VIDEO

   When an announcement carries a video it IS the announcement and the text is its caption,
   so it leads the panel at full width. The trailer is 1920x1080, and `aspect-ratio` reserves
   its box BEFORE the metadata arrives — without it the panel reflows and the caption jumps
   the moment the first frame loads.
   ══════════════════════════════════════════════════════════════════════════════ */
.ann-reader-video {
  display: block;
  inline-size: 100%;
  aspect-ratio: 16 / 9;
  block-size: auto;
  margin-block-end: var(--space-4);
  border-radius: var(--radius-md, 10px);
  background: #000;                    /* letterbox, not the page ground, while it loads */
  outline: var(--border-hairline) solid var(--color-border-hairline);
  outline-offset: -1px;
}
/* A tall phone gives a 16:9 video very little height; capping by viewport keeps the caption
   visible without the user having to scroll past a full-bleed player to reach the words. */
@media (max-width: 560px) {
  .ann-reader-video { max-block-size: 42vh; object-fit: contain; }
}

/* PINNED — the badge says why this item is first. Without it a pinned announcement just
   looks like the newest one, and the ordering reads as a bug the first time something newer
   appears below it. */
.ann-pin {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px;
  margin-inline-end: var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-accent-soft);
  color: var(--color-accent);
  font: var(--weight-medium) 11px/1.6 var(--font-sans);
  vertical-align: middle;
}
/* At 11px this is small text, so it owes 4.5:1. The accent on its own 14% wash measures
   3.97:1 against the dark card — the lighter accent takes it to 4.9:1. Light theme already
   clears it (4.7:1) because the wash sits on a near-white card, so it is left alone. */
[data-theme="dark"] .ann-pin { color: var(--color-accent-hover); }

/* The same pill in a quieter register: a row that opens a 52-second video should not look
   identical to a row of text, but "Video" is a fact about the row, not a call to action —
   the accent belongs to Pinned alone.
   Two class selectors, not one: the dark-theme accent rule above is (0,2,0) and would
   otherwise repaint this pill teal. Equal specificity, and this comes later in the file. */
.ann-pin.ann-pin--vid {
  background: var(--color-bg-subtle);
  color: var(--color-text-secondary);
}
/* Own row, above the title. `gap` owns the spacing here, so the pill's own inline margin —
   which it needs when it sits inline in the reader — is cancelled. */
.ann-item-badges { display: flex; align-items: center; gap: 6px; margin-block-end: 1px; }
.ann-item-badges .ann-pin { margin-inline-end: 0; }

/* ══════════════════════════════════════════════════════════════════════════════
   ANNOUNCEMENT COMPOSER — the added controls.
   ══════════════════════════════════════════════════════════════════════════════ */
.ann-pin-lbl {
  display: inline-flex; align-items: center; gap: 6px;
  font: var(--weight-regular) var(--text-sm) / 1 var(--font-sans);
  color: var(--color-text-secondary);
  cursor: pointer; user-select: none;
}
.ann-pin-lbl input { accent-color: var(--color-accent); cursor: pointer; }

/* DESTRUCTIVE, so it does not look like the other buttons. Quiet until hover: a red button
   sitting permanently next to Publish invites the mis-click it is meant to prevent. */
.ann-clear-all {
  display: block;
  inline-size: 100%;
  margin-block-start: var(--space-3);
  padding: 9px 14px;
  min-block-size: 38px;
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-md, 10px);
  background: transparent;
  color: var(--color-text-muted);
  font: var(--weight-medium) var(--text-sm) / 1 var(--font-sans);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.ann-clear-all:hover:not(:disabled) {
  color: var(--color-danger, #C0483A);
  border-color: var(--color-danger, #C0483A);
  background: color-mix(in srgb, var(--color-danger, #C0483A) 8%, transparent);
}
.ann-clear-all:focus-visible { outline: 2px solid var(--color-accent-ring); outline-offset: 2px; }
.ann-clear-all:disabled { opacity: .55; cursor: default; }

/* ============================================================================
   TCLAMP — collapse-with-"عرض المزيد" for a very long USER message.

   NO ABSOLUTE POSITIONING ANYWHERE. The fade is a mask-image on the clamped
   element itself and the gradient axis is VERTICAL (to bottom), so there is
   nothing to mirror; the button is a normal-flow inline-flex sibling inside the
   bubble, which sets `text-align: start`. Both render identically under
   dir="ltr" and dir="rtl" — which matters because applyShellLang hard-codes
   html.dir="ltr" and only per-element dir carries RTL. Measured in Chrome: the
   button's offset from the bubble's START edge is 17px in BOTH directions.

   SPECIFICITY: `.msg-user__text.is-tclamped` is (0,2,0) and this block sits at
   the end of the file. Nothing anywhere in styles.css sets max-height,
   max-block-size or overflow on .msg-user__bubble or its text child, and there
   is no `#id *` rule in the file, so these rules win.

   --tclamp-cut is written by JS as an exact pixel boundary snapped to a whole
   line, and never lands inside a .katex-display block.
============================================================================ */
.msg-user__text.is-tclamped {
  max-block-size: var(--tclamp-cut, 60vh);
  overflow: hidden;
  /* Alpha ramp over the last ~2.6 lines. Vertical only — RTL-safe by construction. */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 2.6em), rgba(0,0,0,.3) calc(100% - 1em), transparent 100%);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 2.6em), rgba(0,0,0,.3) calc(100% - 1em), transparent 100%);
}
/* No mask support (very old engines): a clean hard cut. The button still works. */
@supports not (mask-image: linear-gradient(#000, #000)) {
  .msg-user__text.is-tclamped { -webkit-mask-image: none; mask-image: none; }
}

.msg-user__bubble .tclamp__more {
  display: inline-flex; align-items: center; gap: 6px;
  margin-block-start: var(--space-2);
  padding: 5px 11px;
  border: var(--border-hairline) solid var(--color-border-hairline);
  border-radius: var(--radius-pill);
  background: transparent;              /* the bubble is already a tinted panel */
  color: var(--color-text-secondary);
  font: var(--weight-medium) var(--text-sm) / 1.2 var(--font-sans);
  white-space: nowrap;                  /* the bubble sets pre-wrap; the pill must not wrap */
  cursor: pointer;
  -webkit-user-select: none; user-select: none;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.msg-user__bubble .tclamp__more[dir="rtl"] { font-family: var(--font-arabic); }
.msg-user__bubble .tclamp__more:hover {
  color: var(--color-text-primary);
  border-color: var(--color-border-strong);
  background: var(--color-surface);
}
.msg-user__bubble .tclamp__more:focus-visible { outline: 2px solid var(--color-accent-ring); outline-offset: 2px; }
/* ICONS.caret points RIGHT; rotation has no notion of writing direction, so this
   is correct in Arabic and English alike. */
.msg-user__bubble .tclamp__chev {
  display: inline-flex; inline-size: 13px; block-size: 13px;
  transition: transform var(--dur-base) var(--ease-out);
  transform: rotate(90deg);
}
.msg-user__bubble .tclamp__chev svg { inline-size: 100%; block-size: 100%; }
.msg-user__bubble .tclamp__more[aria-expanded="true"] .tclamp__chev { transform: rotate(-90deg); }
@media (prefers-reduced-motion: reduce) {
  .msg-user__bubble .tclamp__more, .msg-user__bubble .tclamp__chev { transition: none; }
}
@media (pointer: coarse), (max-width: 560px) {
  .msg-user__bubble .tclamp__more { min-block-size: 40px; padding-inline: 14px; }
}

/* The English fields read as a secondary pair, so the Arabic ones stay the primary input. */
.ann-title-en, .ann-body-en { opacity: .92; }
@media (pointer: coarse), (max-width: 560px) {
  .ann-clear-all { min-block-size: 44px; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   THE TIER PICKER DOES NOT BELONG TO FIRAS CODE

   Firas Code picks its own engine — the workspace routes builds to the coder tier
   itself (requestTier = "ultra" on a code turn), so the topbar's Mini/Pro/Ultra/Max
   control was offering a choice that Code does not honour. It leaked into BOTH Code
   screens: the "Build with AI" home and the IDE.

   ONLY HALF OF IT WAS HIDDEN. `.product-code #tierSwitch{display:none}` has been in this
   file since Deck v2 — but the tier control has TWO presentations of the same state:
   #tierSwitch, the desktop strip, and #tierSelect, the phone dropdown. Code hid the strip
   and never the dropdown, so the control was invisible on a laptop and fully present on a
   phone. That is why it took a phone screenshot to surface, and why it appeared on BOTH
   Code screens (home and IDE) — nothing about the screen was ever the variable.
   The .product-brain block below already hides the pair and its comment names this exact
   asymmetry as an open bug; this closes it.

   Layout is safe: `.topbar__spacer` is the flex growth element that positions this
   group, so removing the control lets the remaining buttons close up normally —
   no gap, no reflow of the wordmark.
   ══════════════════════════════════════════════════════════════════════════════ */
body.product-code #tierSwitch,
body.product-code #tierSelect { display: none !important; }

/* ----------------------------------------------------------------------------
   Video card — a generated clip. Deliberately the same visual language as
   .image-card (frame, glow, shimmer, action bar) so the two read as one family;
   only the aspect ratio and the player differ.
---------------------------------------------------------------------------- */
.video-card {
  inline-size: 100%; max-inline-size: 520px;
  margin-block: var(--space-3) var(--space-1);
}
.video-card__frame {
  position: relative; overflow: hidden;
  inline-size: 100%; aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  background: var(--color-bg-subtle);
  border: var(--border-hairline) solid var(--color-border-hairline);
}
.video-card.is-loading .video-card__frame {
  border-color: transparent;
  box-shadow: 0 0 0 1.5px var(--color-accent-soft), 0 10px 34px -12px var(--color-accent);
  animation: imgGlow 1.7s ease-in-out infinite;
}
.video-card.is-loading .video-card__frame::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(110deg, transparent 35%, color-mix(in srgb, var(--color-accent) 22%, transparent) 50%, transparent 65%);
  background-size: 220% 100%;
  animation: imgShimmer 1.5s linear infinite;
}
.video-card__loader {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2);
  color: var(--color-text-secondary);
  font-family: var(--font-sans); font-size: var(--text-sm);
  text-align: center; padding-inline: var(--space-4);
}
[dir="rtl"] .video-card__loader { font-family: var(--font-arabic); }
.video-card.is-done .video-card__loader { display: none; }
.video-card__spin {
  inline-size: 28px; block-size: 28px; border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--color-accent) 22%, transparent);
  border-top-color: var(--color-accent);
  /* auth-spin is the file's existing rotation keyframe. There is no `spin` keyframe in this
     stylesheet; naming one would have failed silently. The image card used to share this
     spinner and no longer does — it has the aperture loader above. */
  animation: auth-spin 0.7s linear infinite;
}
.video-card.is-error .video-card__spin { display: none; }
.video-card__vid {
  position: absolute; inset: 0;
  inline-size: 100%; block-size: 100%;
  object-fit: contain; background: #000;
  opacity: 0; transition: opacity var(--dur-base) var(--ease-out);
}
.video-card.is-done .video-card__vid { opacity: 1; }
.video-card.is-error .video-card__frame { box-shadow: none; animation: none; border-color: var(--color-border-hairline); }
.video-card.is-error .video-card__frame::after { content: none; }
.video-card.is-error .video-card__loadtext { color: var(--color-text-muted); }
.video-card__bar {
  display: flex; align-items: center; gap: var(--space-2);
  margin-block-start: var(--space-2);
}
.video-card__cap {
  flex: 1 1 auto; min-inline-size: 0;
  font-family: var(--font-sans); font-size: var(--text-xs); color: var(--color-text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
[dir="rtl"] .video-card__cap { font-family: var(--font-arabic); }
.video-card__dl {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 6px 10px; border-radius: var(--radius-pill);
  border: var(--border-hairline) solid var(--color-border-hairline);
  background: var(--color-surface); color: var(--color-text-secondary);
  font-family: var(--font-sans); font-size: var(--text-xs);
  transition: background var(--dur-fast), border-color var(--dur-fast), color var(--dur-fast);
}
[dir="rtl"] .video-card__dl { font-family: var(--font-arabic); }
.video-card__dl:hover { background: var(--color-bg-subtle); border-color: var(--color-border-strong); color: var(--color-text); }
.video-card__dl svg { inline-size: 15px; block-size: 15px; }
@media (prefers-reduced-motion: reduce) {
  .video-card.is-loading .video-card__frame,
  .video-card.is-loading .video-card__frame::after,
  .video-card__spin { animation: none; }
  .video-card__vid { transition: none; }
}
