/* web/app/app.css — layout only for the sign-in shell and the minimal desktop. Every colour, size and
   duration comes from design.css tokens; nothing here invents one. */

.app { position: relative; min-height: 100vh; min-height: 100dvh; }
.app > .signin, .app > .desktop { position: relative; z-index: 1; }

/* ---- sign in: glass over the live desktop, centred, one column ---- */
/* `place-items: center` alone leaves the implicit column an `auto` track, which sizes to the card's
   max-content: the English shelf row («Real-estate lead + listing machine (being built)») and the English
   voice label pushed it to 415 px, `inline-size: 100%` then resolved against THAT and the 400 px card was
   laid out 16 px from the left edge of a 390 px phone, with `body { overflow-x: hidden }` clipping the rest
   so the language chip and the «n / 10» counter could not even be scrolled to (R1-5-S4a-7 / R2-5-6 /
   R4-5-S5). A `minmax(0, 1fr)` track cannot exceed the container, so the shelf label ellipsises at 390 px
   exactly as it already did at 1440. */
.signin { min-height: 100vh; min-height: 100dvh; display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; align-content: center; padding: 16px; }
.signin-card { inline-size: min(100%, 400px); border: 1px solid var(--hair-strong); border-radius: var(--r-card); padding: 20px; display: flex; flex-direction: column; gap: var(--gap-section); animation: window-enter var(--dur-window-enter) var(--ease-window-enter) backwards; }
.signin-head { display: flex; align-items: center; gap: 8px; block-size: var(--row-28); }
.signin-head .lang { margin-inline-start: auto; }
.screen { display: flex; flex-direction: column; gap: var(--gap-grid-lg); }
.screen h1 { font-weight: 500; }
.lang { display: inline-flex; gap: 4px; }
/* drawn at 24 px; design.css derives its 44 px coarse-pointer hit area from --chip-h */
.lang .chip { --chip-h: 24px; padding-inline: 8px; font-size: var(--fs-meta); }
.center-text { text-align: center; }
/* `#ident` is `.num` — `direction: ltr; unicode-bidi: isolate` — which is right for the E.164 number a client
   types. The bilingual placeholder it used to carry («name@company.com أو 05x xxx xxxx») was reordered by that
   same LTR paragraph into «name@company.com 05أوx xxx xxxx» (R3-5-S4a-1 / R1-5-S4a-5 / R2-5-3 / R4-5-S4a-12).
   There is deliberately NO `#ident::placeholder { direction: rtl }` rule here, which is what three of the four
   reviewers proposed: `::placeholder` only accepts the `::first-line` property set, and `direction` is not in
   it — measured in Chromium, the rule changes not one pixel of the painting and `getComputedStyle(el,
   '::placeholder').direction` still answers `ltr`. The fix is in the string instead: the placeholder is now
   two examples and the house separator, with no strong-RTL character in it at all, so no reordering is
   possible in either direction. The «أو» it used to carry is in the field's own label above it. */
.signin-alt:empty, .signin-card .line[data-state="none"] { display: none; }

/* method and profile choices are the same shape: an identity circle, a name, a caption */
.choice { display: flex; align-items: center; gap: 10px; inline-size: 100%; min-block-size: 56px; padding: 10px 12px; border: 1px solid var(--hair); border-radius: var(--r-card); background: var(--card); text-align: start; transition: border-color var(--dur-hover) var(--ease-hover); }
.choice:hover { border-color: var(--hair-strong); }
.choice-text { display: flex; flex-direction: column; min-width: 0; gap: 2px; }
.choice-name { font-size: var(--fs-row); line-height: var(--lh-row); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.choice-sub { font-size: var(--fs-meta); line-height: var(--lh-meta); color: var(--text-2); }
.choice .glyph { inline-size: 32px; block-size: 32px; border-radius: var(--r-circle); border: 1px solid var(--hair); background: var(--input); display: grid; place-items: center; flex: none; }
.choice .glyph svg { inline-size: 16px; block-size: 16px; fill: none; stroke: var(--text); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }

/* ---- desktop ---- */
.desktop { min-height: 100vh; min-height: 100dvh; display: flex; flex-direction: column; }
.menubar .identity .wordmark { font-size: 10px; }
.desktop-area { position: relative; flex: 1; padding: 24px; display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--gap-section); align-content: start; }
.desktop-area .window { max-inline-size: 560px; }
@media (min-width: 1100px) {
  .desktop-area { display: block; }
  .desktop-area .window { position: absolute; inline-size: 520px; }
  #win-package { inset-block-start: 40px; inset-inline-start: 48px; }
  #win-security { inset-block-start: 96px; inset-inline-start: 616px; }
}
@media (max-width: 480px) {
  .desktop-area { padding: 12px 16px; }
  /* `#status-sessions` and the «·» that introduces it go together — the separator used to stay behind and the
     line ended on a dangling dot (R4-5-S4a-1). The whole clause goes when an anonymous desktop has put a
     «تسجيل الدخول» chip in the same 390 px bar: «مختوم» earns no room beside a way in, and a crushed
     `.status-line` painted its glyphs on top of the clock (design.css's `overflow: hidden` is the other half
     of that fix, for a bar that runs out of room some other way). */
  .menubar .status-line #status-sessions, .menubar .status-line .sep { display: none; }
  .menubar:has(#anon-signin) .status-line { display: none; }
  /* And the identity pill yields before anything leaves the phone. Hiding the status line was not enough in
     ARABIC: the six items measured 118 + 64 + 61 + 100 + 22 px with four 8 px gaps and 20 px of padding =
     417 px against a 390 px viewport, because «تسجيل الدخول» is 33 px wider than «Sign in» (English measured
     384 and never showed it). `body { overflow-x: hidden }` then made those 27 px unreachable and
     `k1-quote-ar-390.png` shipped 399 px wide — a picture of the clipping, not of the screen (P1). This is
     the treatment `web/command/command.css`'s own 480 px block already uses on the partners' bar for the same
     defect (R1-5-S5-4): the pill shrinks and ellipsises rather than pushing the profile circle off the edge,
     so the mark, the clock, the language switch and the way in all stay reachable. */
  .menubar .identity { min-inline-size: 0; overflow: hidden; flex: 0 1 auto; }
  /* `direction: ltr` on the wordmark so the ellipsis eats its END: «Empire MAX» is a Latin run, and inside the
     RTL bar its box's inline end is the LEFT, which clipped the head of the name and drew «…RE MAX». */
  .menubar .identity .wordmark { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; direction: ltr; unicode-bidi: isolate; }
}
.window.is-dragging { transition: none; }
.window.is-zoomed { max-inline-size: none; }
@media (min-width: 1100px) { .desktop-area .window.is-zoomed { inline-size: calc(100% - 96px); inset-inline-start: 48px !important; } }
/* biometric success dissolves the sign-in (design §6): 1.05 scale, 12 px blur */
.signin-card.is-dissolving { animation: dissolve var(--dur-env) var(--ease-env) forwards; }
@keyframes dissolve { to { opacity: 0; transform: scale(1.05); filter: blur(12px); } }
/* the profile circle's popover hangs from the menu bar at the inline end */
.profile-menu { position: absolute; inset-block-start: 36px; inset-inline-end: 10px; }
.window-body .rows { border: 1px solid var(--hair); border-radius: var(--r-card); padding-inline: 12px; background: var(--card); }
.window-body .rows:empty { display: none; }
.row-actions { display: inline-flex; align-items: center; gap: 8px; }
.row-taps { display: inline-flex; gap: 4px; }

/* ================= lane K1: the interview, the workspace ================= */

/* ---- the interview: the sign-in card's shape, one question per screen ---- */
.iv-card .screen { gap: var(--gap-grid-lg); }
.iv-progress { display: flex; align-items: center; gap: 10px; }
/* the dim-gold progress rule — this screen's one gold, and it is not text, so it carries no contrast duty */
.iv-rule { flex: 1; block-size: 2px; border-radius: var(--r-pill); background: var(--hair-strong); overflow: hidden; }
.iv-rule i { display: block; block-size: 100%; inline-size: 0; background: var(--accent); transition: inline-size var(--dur-open) var(--ease-open); }
#iv-step { font-variant-numeric: tabular-nums; }
#iv-progress-note:empty { display: none; }
#iv-title { font-weight: 600; font-size: clamp(22px, 3.2vw, 30px); line-height: 1.25; text-wrap: balance; }
/* the interview, 2026-09-23: a wider card, the question as the hero, the options as a two-column grid of keyed rows,
   an endowed progress rule, an intro line on the first question, the picked type as the eyebrow, a slide-in per question */
.iv-card { inline-size: min(100%, 560px); }
.iv-intro { color: var(--text-2); }
.iv-intro[hidden], .iv-context[hidden] { display: none; }
.iv-context { color: var(--accent); letter-spacing: .08em; text-transform: uppercase; }
:lang(ar) .iv-context { letter-spacing: 0; }
#iv-options { display: grid; grid-template-columns: 1fr; gap: 8px; }
@media (min-width: 560px) {
  #iv-options { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #iv-options > li:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
#iv-options > li { list-style: none; min-inline-size: 0; }
.choice-key { flex: none; margin-inline-start: auto; inline-size: 22px; block-size: 22px; border-radius: 6px; border: 1px solid var(--hair); color: var(--text-2); font-size: 11px; line-height: 20px; text-align: center; }
.choice-opt.is-picked { border-color: var(--accent); background: rgba(228, 196, 106, .10); }
.choice-opt.is-picked .dot { background: var(--accent); }
.choice-opt.is-picked .choice-key { border-color: var(--accent); color: var(--accent); }
.iv-enter { animation: iv-in 360ms var(--ease-open) both; --iv-dx: -14px; }
html[dir="rtl"] .iv-enter { --iv-dx: 14px; }
@keyframes iv-in { from { opacity: 0; transform: translateX(var(--iv-dx)); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .iv-enter { animation: none; } .choice-opt { transition: none; } }
#iv-options:empty { display: none; }
/* an answer is a row: a dot and the label, at the 44 px touch height, never an identity circle */
.choice-opt { min-block-size: var(--row-44); gap: 10px; }
.choice-opt .dot { flex: none; }
/* a shelf row carries the type's own line under its name (R3-20): 11 px --text-2, the row grows past the
   44 px minimum to hold it and the dot rides with the first line instead of the middle of two */
.choice-opt-sub { align-items: flex-start; }
.choice-opt-sub .dot { margin-block-start: 6px; }
.choice-opt-sub .choice-sub { white-space: normal; }
.choice-opt-sub .choice-name { white-space: normal; }
.iv-pencil { inline-size: 16px; block-size: 16px; flex: none; color: var(--text-2); }
.iv-pencil svg { inline-size: 16px; block-size: 16px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.iv-card .cluster { gap: var(--gap-grid); }
#iv-voice svg { inline-size: 16px; block-size: 16px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
#iv-voice[aria-pressed="true"] { border-color: var(--amber); }
/* The voice button carries a whole sentence — «Record (transcription comes later — you type it)» measures
   370 px in English — and `.btn` is `white-space: nowrap`, so at 390 px it ran 17 px past the phone: the two
   stills `k1-interview-field-en-390.png` and `k1-interview-material-en-390.png` came out exactly 390 wide
   because `body { overflow-x: hidden }` cut the tail off before the shutter, so the defect was invisible in
   the evidence and only the renderer's own measurement found it (P2). The button never exceeds its card at any
   width, and on a phone it wraps inside it instead of leaving it: the whole sentence is kept, the 38 px btn-lg
   height is a floor rather than a fixed size, and the 44 px touch floor is untouched. The Arabic label is
   shorter and fitted at 390 already, which is why only the English pair overflowed. */
#iv-voice { max-inline-size: 100%; }
@media (max-width: 480px) {
  #iv-voice { inline-size: 100%; white-space: normal; block-size: auto; min-block-size: 38px; padding-block: 8px; line-height: var(--lh-body); }
}

/* ---- the workspace: nine windows tile, they do not float ---- */
.ws-stale, .ws-line { padding: 6px 24px 0; }
.ws-line[data-state="none"] { display: none; }
@media (max-width: 480px) { .ws-stale, .ws-line { padding-inline: 16px; } }
.desktop-area.is-workspace { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--gap-section); align-content: start; align-items: start; }
@media (min-width: 1100px) {
  .desktop-area.is-workspace { display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); }
  .desktop-area.is-workspace .window { position: static; inline-size: auto; max-inline-size: none; inset: auto; }
  .desktop-area.is-workspace .window.is-zoomed { grid-column: 1 / -1; }
}
.desktop-area.is-workspace .window { max-inline-size: none; }
.desktop-area.is-workspace .window-body { max-block-size: 520px; }
/* One BOARD window on the desktop (the quote a client meets before he has anything else) reads as a document.
   `.is-solo .window` matched Package and Security too, so all three stretched to 760 px on the one screen
   where the quote is meant to be the whole subject (R4-5-S4a-2); the two account windows keep their tiles. */
.desktop-area.is-solo #win-quote { grid-column: 1 / -1; max-inline-size: 760px; margin-inline: auto; }
.desktop-area.is-solo #win-quote .window-body { max-block-size: none; }
/* a window's Waiting panels stack: two columns inside a 380 px tile elide both of them */
#win-waiting .panels { grid-template-columns: minmax(0, 1fr); }

/* A row that needs him: a 2 px rule on its inline start and its state word in full text colour. The rule is
   a hairline by default — gold is arbitrated once for the whole BOARD (workspace.js goldOwner()), and only
   that one row adds .is-gold-row, so a screen never shows the signal twice (design §2, spec §6.1). */
.row.is-needed { border-inline-start: 2px solid var(--hair-strong); padding-inline-start: 8px; }
.row.is-needed.is-gold-row { border-inline-start-color: var(--accent); }
.row-state { font-size: var(--fs-meta); white-space: nowrap; }
/* A row whose caption sits on its own line. 44 px is now the MINIMUM, not a cap: the caption used to be
   `nowrap` + `ellipsis`, and inside a 380-520 px window tile it lost its end — which under RTL is the START
   of an LTR isolate, so «الاستحقاق 2026-09-22 14:51» printed as «الاستحقاق 14:51…», a plausible wrong date on
   the panel headed «ما ننتظره منك» (R3-5-S4a-5), and the English Studio row lost the time the cut arrived
   while the Arabic twin kept it (R3-5-S4a-8). A second 11 px line inside the same row is what this row shape
   was built for; the list still scans mechanically because every row still starts at 44 px. */
.row.row-stacked { align-items: center; block-size: auto; min-block-size: var(--row-44); padding-block: 4px; }
.row-col { display: flex; flex-direction: column; min-width: 0; }
.row-col .row-name { line-height: var(--lh-body); }
/* `overflow-wrap: anywhere` stays — it is the net for a pasted URL in a revise note, and it is not what broke
   «Episod / e 3 · / 36:12 · / today / 17:35» on `desktop-en-390.png` and `k1-workspace-en-390.png` (P3). Both
   `anywhere` and `break-word` break inside a word only when the word cannot fit the line, and the line was
   110 px wide because the row's third column had taken the rest. The geometry is the defect; the rule below
   fixes it, and the meta line is now one unbroken line at 390 in both languages. */
.row-sub-line { font-size: var(--fs-meta); line-height: var(--lh-meta); color: var(--text-2); overflow-wrap: anywhere; }
/* A phone gives the row's actions their own line. `.row` is `auto minmax(0, 1fr) auto` and the third column is
   sized by its content, so «Waiting on you» + «Approve» + «Revise» — 33 % wider than «بانتظارك» + «اعتمد» +
   «عدّل» — squeezed the title/meta column to about 110 px at 390: the title ellipsised to nothing readable and
   the meta line broke mid-word, while the Arabic sibling laid the same row out correctly (P3). Below 480 px the
   value cell drops under the name instead of competing with it, so the English row reads the same as the
   Arabic one and nothing is cut. */
@media (max-width: 480px) {
  .row.row-stacked { grid-template-columns: auto minmax(0, 1fr); align-items: start; row-gap: 4px; }
  .row.row-stacked > .row-value { grid-column: 2; text-align: start; white-space: normal; }
  .row.row-stacked .row-actions { flex-wrap: wrap; }
}
.row-link { text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--hair-strong); }
.row-link[aria-pressed="true"] { font-weight: 500; text-decoration-color: var(--text-2); }
.ws-compose { gap: 6px; }
.ws-compose .cluster { flex-wrap: nowrap; }
.ws-compose .input { flex: 1; min-inline-size: 0; }

/* the pipeline: chips in a row, the last one is where he approves */
.ws-flow { align-items: center; }
.ws-step { cursor: default; }
.ws-step-final { border-color: var(--text-2); }
.ws-quote-actions { padding-block-start: var(--u); }
/* the click-to-accept block (R1.4): a real checkbox at the 44 px target, its label beside it, the two
   document versions as links, and one caption that says why the buttons are closed */
.ws-accept { gap: var(--gap-grid); }
.ws-check { display: flex; align-items: flex-start; gap: 10px; min-block-size: var(--row-44); padding-block: 4px; cursor: pointer; }
/* the tick is --text, never --accent: this screen's one gold is the monthly figure, and a checkbox is not
   a demand (design §2). The state is the box itself plus the line under the pair, never colour alone. */
.ws-check-box { inline-size: 20px; block-size: 20px; margin: 12px 0 0; accent-color: var(--text); flex: none; }
.ws-check-text { display: flex; flex-direction: column; gap: 2px; padding-block-start: 10px; min-width: 0; }
.ws-check-label { font-size: var(--fs-body); line-height: var(--lh-body); }
.ws-legal-link { text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--text-2); }
.ws-legal-link:hover { text-decoration-color: currentColor; }
.ws-accept-note:empty { display: none; }
/* the one-line legal footer under the desktop (terms · privacy · refunds · AI disclosure) */
.legal-foot { display: flex; flex-wrap: wrap; gap: 6px 12px; padding: 8px 24px 16px; font-size: var(--fs-meta); line-height: var(--lh-meta); color: var(--text-2); }
.legal-foot a { color: var(--text-2); text-decoration: underline; text-underline-offset: 2px; text-decoration-color: currentColor; }
.legal-foot a:hover { color: var(--text); }
@media (max-width: 480px) { .legal-foot { padding-inline: 16px; } }

/* two amounts on one line: USD primary, SAR beside it (R2.4) */
.amounts { display: inline-flex; align-items: baseline; gap: 8px; justify-content: flex-end; }
.amount { display: inline-flex; align-items: baseline; gap: 4px; font-variant-numeric: tabular-nums; }
.amount .num { direction: ltr; unicode-bidi: isolate; }
.amount .unit { font-size: var(--fs-meta); color: var(--text-2); }
.amount-2 { color: var(--text-2); }
.amounts.is-gold .amount:first-child { color: var(--accent); }

/* the cancellation confirm window floats above the tiles (it out-specifies the workspace's static rule) */
.desktop-area .window.window-confirm { position: fixed; inset-block-start: 20vh; inset-inline-start: 50%; inset-inline-end: auto; transform: translateX(-50%); inline-size: min(420px, calc(100% - 32px)); max-inline-size: none; margin-inline: 0; z-index: 25; }
html[dir="rtl"] .desktop-area .window.window-confirm { transform: translateX(50%); }
.window-confirm .window-body { gap: var(--gap-grid-lg); max-block-size: none; }
