/* ═══════════════════════════════════════════════════════════════════════════
   Zawaya Voice — voice mode (js/voice/*) and its entry control
   ═══════════════════════════════════════════════════════════════════════════
   Full-bleed navy, almost no chrome, one focal element: the voice element.
   • Palette and faces are fixed by the brand. Colour is meaning, never
     decoration: mint = the required action and settled truth, amber = in
     progress, violet = awaiting review, critical = at risk. Every coloured
     line also carries an icon, and is written in the page's language.
   • Matte surfaces. Static light gradients exist only inside the four L bars in SVG,
     plus one static pool of light behind the voice element (voice-corner.js).
   • Only transform and opacity ever move (voice-mode.js `animate`, plus two
     CSS transitions on transform: the turn, and a press).
   • Font stacks put DM Sans first: it has no Arabic, so Arabic falls through
     to Cairo (chrome) or Tajawal (reading) glyph by glyph — one stack serves
     a line that mixes both languages.
   ═══════════════════════════════════════════════════════════════════════════ */

.zv,
.zv-waiting,
.action-btn.zv-entry {
    --zv-navy: #071216;
    --zv-surface: #0D2024;
    --zv-surface-2: #132A2E;
    --zv-sunken: #0A1B1F;
    --zv-emerald: #159B78;
    --zv-mint: #76E0BD;
    --zv-cream: #ECE3D6;
    --zv-mist: #F3F7F6;
    --zv-amber: #F2C066;
    --zv-critical: #FF8080;
    --zv-violet: #C4B5FD;
    --zv-line: rgba(243, 247, 246, 0.1);
    --zv-muted: rgba(243, 247, 246, 0.64);
    --zv-quiet: rgba(243, 247, 246, 0.8);
    --zv-face-ui: 'DM Sans', 'Cairo', system-ui, sans-serif;
    --zv-face-read: 'DM Sans', 'Tajawal', system-ui, sans-serif;
    --zv-control-radius: 18px;
    --zv-ease-arrive: cubic-bezier(.16, 1, .3, 1);
    --zv-ease-turn: cubic-bezier(.65, 0, .25, 1);
    --zv-turn-ms: 720ms;
    --zv-press-ms: 140ms;
}

/* Composer: 48px target, free-standing pinwheel / mint arrow / solid stop.
   No housing, fill, ring or squircle in any state. Existing handlers remain. */
.action-btn.zv-entry {
    --zv-entry: 48px;
    width: 48px !important; height: 48px !important;
    min-width: 48px !important; min-height: 48px !important;
    margin-block: -3px;
    border: 0 !important; border-radius: 0 !important;
    background: transparent !important; background-image: none !important;
    box-shadow: none !important; animation: none !important; transition: none !important;
}
.action-btn.zv-entry .voice-icon {
    width: 40px !important; height: 40px !important;
    fill: none; stroke: none !important; filter: none !important;
}
.action-btn.zv-entry[data-mode="send"] .send-icon svg {
    width: 30px !important; height: 30px !important;
    stroke: var(--zv-mint) !important; stroke-width: 2.8 !important; filter: none !important;
}
.action-btn.zv-entry .send-icon.stop-icon { width: 24px !important; height: 24px !important; }
.action-btn.zv-entry .send-icon.stop-icon svg { fill: var(--zv-mist) !important; filter: none !important; }
.action-btn.zv-entry:focus-visible { outline: 2px solid var(--zv-mist); outline-offset: 3px; }

/* ═══ Voice mode ═════════════════════════════════════════════════════════ */
.zv {
    --zv-gutter: 22px;
    --zv-ctl: 52px;
    --zv-voice: 88px;
    --zv-glyph: 72px;                         /* voice-corner.js GLYPH_PX */
    --zv-gap: 12px;
    --zv-cap-h: 18px;
    --zv-status-h: 48px;
    --zv-head-h: 64px;
    --zv-frame-max: 800px;
    --zv-park-h: 64px;                        /* the conversation, parked while the menu is up */
    --zv-park-voice: 56px;                    /* the voice element in that bar */
    --zv-park-glyph: 40px;
    --zv-park-inset: calc((var(--zv-park-h) - var(--zv-park-voice)) / 2);
    --zv-park-line: 18px;                     /* the bar's second line: the spoken way back */
    --zv-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    position: fixed;
    inset: 0;
    z-index: 15000;
    color: var(--zv-mist);
    font-family: var(--zv-face-ui);
    line-height: 1.45;
    -webkit-tap-highlight-color: transparent;
    -webkit-font-smoothing: antialiased;
}
.zv[hidden] { display: none; }
/* Voice motion belongs to the controller; inherited page CSS cannot add loops. */
/* Only the existing central SVG and collapse body may acquire motion rules.
   Text, cards, controls, borrowed menu descendants and the page stay protected. */
.zv, .zv * { transition: none !important; }
.zv, .zv *:not(.zv-object):not(.zv-layer-body) { animation: none !important; }
.zv-object, .zv-layer-body { animation: none; }

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

.zv-ic {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Digits: Western, and each run an LTR island (voice-words.js). */
.zv-num {
    direction: ltr;
    unicode-bidi: isolate;
}

.zv-field {
    position: absolute;
    inset: 0;
    background: var(--zv-navy);
}
/* Entry: the free-standing marks grow; the matte navy field fades. */
.zv-frame {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    padding: env(safe-area-inset-top, 0px) var(--zv-gutter) 0;
    background: transparent;
    width: 100%;
    max-width: var(--zv-frame-max);
    margin-inline: auto;
    box-sizing: border-box;
}
/* Waiting for its beat in the entry (voice-mode.js animateIn). */
.zv .zv-pending { opacity: 0; }

/* ── Header: the way back, and the mark. Nothing else. ─────────────────── */
.zv-head {
    flex: none;
    height: var(--zv-head-h);
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.zv-back {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    margin-inline-start: -8px;
    padding: 0 12px 0 10px;
    border: 0;
    border-radius: 14px;
    background: transparent;
    color: var(--zv-mist);
    font-family: var(--zv-face-ui);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
}
.zv-back .zv-ic { width: 22px; height: 22px; }
.zv-brand { width: 24px; height: 24px; opacity: 0.86; }
/* Which engine hears: a fact about the frame, not the conversation. It sits
   under the header as one quiet line — no mint edge (mint is the action to
   take), so the first thing the eye meets is the turn, not the plumbing. A
   degraded engine earns a hairline well, never a colour of its own. */
.zv-engine-status {
    flex: none;
    display: flex;
    align-items: center;
    gap: 8px;
    padding-block: 0;
    padding-inline: 10px 4px;
    margin-block-end: 6px;
    border: 1px solid transparent;
    border-radius: 12px;
    background: transparent;
    color: var(--zv-muted);
    font: 500 12px/1.5 var(--zv-face-ui);
}
.zv-engine-line {
    flex: 1 1 auto;
    min-width: 0;
    padding-block: 4px;
    overflow-wrap: anywhere;
}
.zv-engine-ic { color: var(--zv-emerald); }
.zv-engine-status[data-degraded="true"] {
    background: var(--zv-sunken);
    border-color: var(--zv-line);
    color: var(--zv-quiet);
}
.zv-engine-status[data-degraded="true"] .zv-engine-ic { color: var(--zv-muted); }
/* The recognition language: a quiet chip, louder when it is not the page's. */
.zv-engine-lang {
    flex: none;
    min-height: 44px;
    padding: 0 10px;
    border: 1px solid var(--zv-line);
    border-radius: 10px;
    background: transparent;
    color: inherit;
    font: 600 12px/1 var(--zv-face-ui);
    white-space: nowrap;
    cursor: pointer;
}
.zv-engine-lang[data-mismatch="true"] {
    border-color: var(--zv-mint);
    background: var(--zv-navy);
    color: var(--zv-mint);
}
.zv-engine-lang:focus-visible {
    outline: 2px solid var(--zv-mint);
    outline-offset: 2px;
}

/* ── Stage: one reading column, centred in the available field ───────────
   The live turn owns this space. Details scroll inside bounded cards, while
   their actions and the voice controls stay in sight. The menu layer keeps
   the newest line in its existing lower band. */
.zv-stage {
    flex: 1 1 auto;
    min-height: 0;
    margin: 0 calc(-1 * var(--zv-gutter));
    padding: 0 var(--zv-gutter);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: none;
}
.zv-stage::-webkit-scrollbar { display: none; }
.zv-flow {
    min-height: 100%;
    height: auto;
    display: flex;
    flex-direction: column;
    justify-content: safe center;
    gap: 16px;
    padding: 28px 0;
    width: 100%;
    max-width: 680px;
    margin-inline: auto;
    box-sizing: border-box;
}

.zv-answer {
    flex: none;
    max-width: 34em;
    margin: 0;
    font-family: var(--zv-face-read);
    font-size: 22px;
    line-height: 1.6;
    font-weight: 400;
    color: var(--zv-cream);
    text-align: start;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
.zv-answer:empty { display: none; }

/* A reading column, with the complete response available on demand. */
.zv-title { margin: 0; font: 500 13px/1.4 var(--zv-face-ui); color: var(--zv-muted); }
.zv-engine-ic { flex: none; width: 16px; height: 16px; }
.zv-stage { position: relative; container-type: size; }
/* A card's height: what the stage has left once the words around it are in.
   With a reply on screen that is the stage less the reply and the guest's
   line; with no reply (an order, a menu) the card may take the room the reply
   would have had — a receipt shows its lines instead of a sliver of one. */
.zv-flow { --zv-card-max: min(560px, max(240px, calc(100svh - 440px))); }
@supports (height: 1cqh) {
    .zv-flow { --zv-card-max: min(560px, max(240px, calc(100cqh - 220px))); }
    .zv-flow:not(:has(> .zv-reply .zv-answer:not(:empty))) {
        --zv-card-max: min(640px, max(240px, calc(100cqh - 132px)));
    }
}
.zv-reply { flex: none; min-width: 0; }
.zv-reply:has(.zv-answer:empty) { display: none; }
/* Who is speaking: one label, the same for both voices — an icon and the
   name, in the page's language. The guest's carries the mic (their channel); Zawaya's the
   speaker, in the mark's own emerald: the voice of the mark below. */
.zv-speaker, .zv-card-preparing {
    display: flex; align-items: center; gap: 8px;
    color: var(--zv-muted); font: 600 12px/1.5 var(--zv-face-ui);
    margin-block-end: 6px;
}
.zv-speaker .zv-ic, .zv-card-preparing .zv-ic { width: 16px; height: 16px; flex: none; }
.zv-answer-label .zv-ic { color: var(--zv-emerald); }
.zv-speaker-t, .zv-card-preparing-t { display: flex; flex-wrap: wrap; gap: 4px 8px; }
/* Between the two voices, a breath more than within one answer. */
.zv-said + .zv-reply, .zv-heard + .zv-said { margin-block-start: 8px; }
.zv .zv-answer {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
    overflow: hidden; line-height: 1.6;
}
.zv-reply[data-expanded="true"] .zv-answer {
    display: block; max-height: min(260px, 28svh); overflow-y: auto;
    overscroll-behavior: contain; scrollbar-width: thin;
    scrollbar-color: var(--zv-muted) transparent;
}
.zv-answer-toggle {
    display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
    padding: 4px 0; margin-top: 4px; border: 0; background: transparent;
    color: var(--zv-mist); font: 600 13px/1.5 var(--zv-face-ui); cursor: pointer;
}
.zv-answer-toggle[hidden] { display: none; }
.zv-answer-toggle .zv-ic { width: 16px; height: 16px; }
.zv-reply[data-expanded="true"] .zv-answer-toggle .zv-ic { transform: rotate(180deg); }
.zv-answer-toggle-t { display: flex; flex-wrap: wrap; gap: 2px 10px; }
/* The server's correction after a misspoken number: one quiet amber line. */
.zv-correction {
    display: flex; align-items: flex-start; gap: 8px; margin: 6px 0 0;
    padding-inline-start: 10px; border-inline-start: 2px solid var(--zv-amber);
    color: var(--zv-mist); font: 500 13px/1.6 var(--zv-face-ui);
}
.zv-correction[hidden] { display: none; }
.zv-correction-ic { width: 16px; height: 16px; flex: none; margin-top: 2px; color: var(--zv-amber); }
.zv-welcome {
    position: absolute; inset: 0; display: flex; flex-direction: column;
    justify-content: center; align-items: center; padding: 24px; text-align: center;
    pointer-events: none;
}
.zv-welcome[hidden] { display: none; }
.zv-welcome-title { margin: 0 0 12px; color: var(--zv-cream); font: 500 32px/1.4 var(--zv-face-read); }
.zv .zv-welcome-note { margin: 0; font: 400 16px/1.6 var(--zv-face-read); color: var(--zv-quiet); }
.zv-welcome-note-t { display: flex; flex-direction: column; gap: 6px; }
/* The guest's live words while they speak: present, never the loudest thing.
   They stay a size under Zawaya's reply at every width (the owner's rule —
   his own speech reads smaller), and quieter in tone. */
.zv[data-phase="listening"] .zv-said { --zv-said-size: 19px; color: var(--zv-quiet); font-weight: 400; }
.zv[data-phase="listening"] .zv-said[data-long="1"] { --zv-said-size: 17px; }
.zv-heard-q { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.zv[data-phase="correcting"] .zv-stale { display: none; }
.zv[data-phase="correcting"] .zv-reply { display: none; }
.zv-answer-toggle:focus-visible, .zv-flow [role="region"]:focus-visible {
    outline: 2px solid var(--zv-mist); outline-offset: 3px; border-radius: 4px;
}

@media (min-width: 700px) {
    .zv { --zv-gutter: 32px; }
    .zv .zv-answer { font-size: 26px; }
    .zv[data-phase="listening"] .zv-said { --zv-said-size: 21px; }
    .zv[data-phase="listening"] .zv-said[data-long="1"] { --zv-said-size: 19px; }
}
@media (max-width: 480px) {
    .zv-title { font-size: 12px; }
    .zv-back { font-size: 14px; gap: 6px; }
    .zv-engine-status { align-items: flex-start; gap: 6px; padding-inline-start: 10px; }
    .zv-engine-ic { margin-top: 6px; }
    .zv-engine-line { line-height: 1.4; padding-block: 3px; }
    .zv-engine-lang { align-self: center; padding-inline: 8px; }
    .zv-flow .fc-lines li { padding-block: 8px; }
    .zv-flow { gap: 12px; padding-block: 12px; }
    .zv .zv-answer { font-size: 21px; -webkit-line-clamp: 3; }
    .zv-flow:has(.concierge-approval-card, .fc-receipt) .zv-answer { -webkit-line-clamp: 1; }
    .zv-flow:has(.zv-card) .zv-reply[data-expanded="true"] .zv-answer { max-height: min(100px, 15cqh); }
    .zv-flow:has(.zv-card):has(.zv-reply[data-expanded="true"]) .zv-said { display: none; }
    .zv .zv-flow .concierge-brief-review-row { padding-block: 6px; }
    .zv-welcome-title { font-size: 28px; }
    .zv-status-t { gap: 6px; }
    .zv-status-t-a { font-size: 12px; }
}

/* ── The guest's words (voice-words.js) ───────────────────────────────────
   Generous while listening, a quieter single correctable line once sent.
   A custom line-height property avoids the page's global Arabic p rule. */
.zv-said {
    --zv-said-size: 16px;
    --zv-said-lh: 1.55;
    flex: none;
    margin: 0 -6px;
    padding: 2px 6px;
    border-radius: 14px;
    font-family: var(--zv-face-read);
    font-size: var(--zv-said-size);
    line-height: var(--zv-said-lh);
    font-weight: 400;
    color: var(--zv-quiet);
    transform-origin: 50% 0;                  /* it folds away about its top edge */
}
.zv-said[data-long="1"] { --zv-said-size: 16px; }
.zv-said .zv-said-label { margin-block-end: 4px; }
.zv-said[data-state="sent"] { cursor: pointer; color: var(--zv-muted); }
.zv-said[data-state="sent"] .zv-said-line {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden;
}
.zv-said-line {
    text-align: start;
    overflow-wrap: anywhere;
}
.zv-said[data-state="live"] .zv-said-line {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

/* The marks: logo.svg's first L at text scale, emerald. Used by the settled
   receipt only (voice-mode.js `settle`); the guest's words carry none. */
.zv-qw {
    display: inline-block;
    width: 0.9em;
    height: 0.424em;                          /* two L bars, with the same open joint */
}
.zv-qw[hidden] { display: none; }
.zv-q {
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
    fill: var(--zv-emerald);
}
.zv-qw-open  { vertical-align: 0.58em; margin-inline-end: 0.22em; }
.zv-qw-close { vertical-align: -0.06em; margin-inline-start: 0.22em; }
/* ⌝ … ⌞ in Arabic; ⌜ … ⌟ in English — the reading direction of the words. */
[dir="rtl"] > .zv-qw-close .zv-q { transform: rotate(180deg); }
[dir="ltr"] > .zv-qw-open .zv-q  { transform: scaleX(-1); }
[dir="ltr"] > .zv-qw-close .zv-q { transform: scaleY(-1); }

/* Folded: one small line under the result — what was heard, one tap to fix. */
.zv-heard {
    flex: none;
    display: flex;
    align-items: baseline;
    gap: 8px;
    width: 100%;
    min-height: 44px;
    margin: -6px 0 0;
    padding: 9px 4px;
    border: 0;
    border-radius: 12px;
    background: none;
    color: var(--zv-quiet);
    font-family: var(--zv-face-read);
    font-size: 14px;
    line-height: 1.55;
    font-weight: 400;
    text-align: start;
    cursor: pointer;
    transform-origin: 50% 0;
}
.zv-heard-k {
    flex: none;
    font-family: var(--zv-face-ui);
    font-size: 12px;
    font-weight: 600;
    color: var(--zv-muted);
}
.zv-heard-q {
    flex: 1 1 auto;
    min-width: 0;
    text-align: start;
    overflow-wrap: anywhere;
}
.zv-heard-ic {
    flex: none;
    align-self: center;
    width: 18px;
    height: 18px;
    color: var(--zv-emerald);
}

/* The correction: the heard line, opened. */
.zv-fix {
    flex: none;
    padding: 16px 16px 10px;
    border-radius: 22px;
    background: var(--zv-surface);
    border: 1px solid var(--zv-line);
    transform-origin: 50% 0;
}
.zv-fix-title {
    margin: 0 0 10px;
    font-size: 15px;
    line-height: 1.4;
    font-weight: 700;
    color: var(--zv-mist);
}
.zv-fix-input {
    display: block;
    width: 100%;
    box-sizing: border-box;
    min-height: 92px;
    margin: 0;
    padding: 12px 14px;
    border: 1px solid rgba(243, 247, 246, 0.18);
    border-radius: 14px;
    background: var(--zv-sunken);
    color: var(--zv-mist);
    font-family: var(--zv-face-read);
    font-size: 19px;                          /* ≥ 16px: no zoom on iOS */
    line-height: 1.55;
    resize: none;
    outline: none;
}
.zv-fix-input:focus { border-color: var(--zv-mist); }
.zv-fix-note {
    margin: 10px 2px 0;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--zv-quiet);
}
.zv-fix-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 14px;
}
.zv-fix-actions .zv-btn:disabled { opacity: 0.45; cursor: default; }
.zv-fix-keep {
    min-height: 44px;
    border: 0;
    border-radius: 14px;
    background: none;
    color: var(--zv-quiet);
    font-family: var(--zv-face-ui);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
}

/* ── One status line, centred above the voice controls ───────────────────
   Every state has an icon, in the page's language. Its small caption never takes
   the reading tier occupied by the answer or the guest's live words. */
.zv-status {
    flex: none;
    display: flex;
    align-items: center;
    min-height: var(--zv-status-h);
    align-self: center;
    justify-content: center;
    color: var(--zv-mist);
}
.zv-status-in {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.zv-status[data-tone="progress"] { color: var(--zv-amber); }
.zv-status[data-tone="review"]   { color: var(--zv-violet); }
.zv-status[data-tone="done"]     { color: var(--zv-mint); }
.zv-status[data-tone="risk"]     { color: var(--zv-critical); }
.zv-status-ic { flex: none; width: 16px; height: 16px; }
.zv-status-t {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
    white-space: nowrap;
}
.zv-status-t-a { font-size: 13px; font-weight: 600; }

/* ── Bottom: mute · the voice element · end ───────────────────────────── */
.zv-controls {
    flex: none;
    display: grid;
    grid-template-columns: var(--zv-ctl) 1fr var(--zv-ctl);
    gap: var(--zv-gap);
    align-items: start;
    padding: 8px 0 var(--zv-bottom);
    width: 100%;
    max-width: 420px;
    align-self: center;
}
.zv-slot { height: calc(var(--zv-ctl) + 8px + var(--zv-cap-h)); }
.zv-ctl {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--zv-mist);
    font-family: var(--zv-face-ui);
    cursor: pointer;
}
.zv-ctl-face {
    display: grid;
    place-items: center;
    width: var(--zv-ctl);
    height: var(--zv-ctl);
    border-radius: var(--zv-control-radius);
    background: var(--zv-sunken);
    border: 1px solid var(--zv-line);
}
.zv-ctl-face .zv-ic { width: 24px; height: 24px; }
.zv-ctl-cap {
    height: var(--zv-cap-h);
    font-size: 13px;
    font-weight: 600;
    line-height: var(--zv-cap-h);
    color: var(--zv-quiet);
    white-space: nowrap;
}
.zv-mute[aria-pressed="true"] .zv-ctl-face {
    background: var(--zv-mist);
    border-color: var(--zv-mist);
    color: var(--zv-navy);
}
.zv-mute:disabled { opacity: 0.4; cursor: default; }

/* The four bars have no enclosing surface. Closed mic = still, outlined —
   unless the hotel's own voice is live through them (data-source="hotel").
   Their static depth paint supplies softness; no filter or blur is used. */
.zv-corner {
    position: absolute; left: 50%;
    bottom: calc(var(--zv-bottom) + var(--zv-cap-h) + 8px + var(--zv-ctl) / 2 - var(--zv-voice) / 2);
    width: var(--zv-voice); height: var(--zv-voice);
    margin-left: calc(var(--zv-voice) / -2);
    padding: 0; border: 0; border-radius: 0; background: transparent;
    cursor: pointer;
}
.zv-corner-face { position: absolute; inset: 0; display: grid; place-items: center; }
.zv-turn { display: block; width: var(--zv-glyph); height: var(--zv-glyph); }
.zv-object { display: block; width: 100%; height: 100%; overflow: visible; }
.zv-corner[data-mic="closed"]:not([data-source="hotel"]) .zv-metal { fill: none; stroke: #159B78; stroke-width: 4; stroke-opacity: 1; }
.zv-corner[data-mic="closed"]:not([data-source="hotel"]) .zv-depth,
.zv-corner[data-mic="closed"]:not([data-source="hotel"]) .zv-light { opacity: 0; }
.zv-corner[data-mic="closed"]:not([data-source="hotel"]) .zv-rim { opacity: 0.3; }
.zv-corner[data-mic="closed"]:not([data-source="hotel"]) .zv-pool { opacity: 0.5; }
/* The hotel at work: the outline stays (the mic is closed) and the mark's own
   light rises and falls inside it — one WAAPI on the light (voice-mode.js
   `syncWork`). With no animation running, that light rests at opacity 0:
   reduced motion and a hidden page show the still outline. */
.zv[data-phase="thinking"] .zv-corner[data-mic="closed"]:not([data-source="hotel"]) .zv-light { opacity: 1; }

.zv button:focus-visible,
.zv [role="button"]:focus-visible,
.zv-waiting button:focus-visible {
    outline: 2px solid var(--zv-mist);
    outline-offset: 3px;
}

/* ── Cards, presented for voice. The renderers are the thread's own;
      these rules only restyle them while they are here. ─────────────── */
.zv-flow .zv-card {
    flex: none;
    min-height: 180px;
    position: relative;
    transform-origin: 50% 0;                  /* a sent card folds about its top edge */
}
.zv-flow .zv-folded { display: none !important; }
/* A row (or the action) waiting for its beat — voice-mode.js `arrive`. */
.zv-flow .zv-pending { opacity: 0 !important; }
/* Inside voice mode only transform and opacity move, and only ours (WAAPI,
   rationed in voice-mode.js). A card's own motion — the approval card's
   max-height reveals and hover fades, a food card's loading pulse — is
   stilled while it is here; it moves again once it is back in the chat. */
.zv-flow .zv-card,
.zv-flow .zv-card * {
    transition: none !important;
    animation: none !important;
    background-image: none !important;
}
.zv-flow .concierge-approval-card {
    flex: none;
    margin: 0 !important;
    max-width: none !important;
    border-radius: 22px !important;
    background: var(--zv-surface) !important;
    border: 1px solid var(--zv-line) !important;
    display: flex;
    flex-direction: column;
    max-height: var(--zv-card-max);
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
}
.zv-flow .concierge-approval-card [style*="text-transform:uppercase"] {
    text-transform: none !important;
    letter-spacing: 0 !important;
    font-size: 13.5px !important;
    color: var(--zv-quiet) !important;
}
/* The question — the summary — is the largest line on the card. */
.zv-flow .concierge-approval-card > div:nth-child(2) {
    padding-top: 4px !important;
    font-size: 21px !important;
    line-height: 1.4 !important;
    color: var(--zv-mist) !important;
}
.zv-flow .concierge-approval-card > div:first-child,
.zv-flow .concierge-approval-card > div:nth-child(2),
.zv-flow .concierge-approval-actions { flex: none; }
/* Never clip a card's actions (owner, 13 Oct: «بعض البطاقات مقصوصه»). The
   card is capped so the words around it stay readable; inside the cap its
   words give way and its controls never do. Every part keeps its natural
   height except the two text regions — the summary (a complaint's own long
   sentence) and the details — which shrink in proportion and scroll in place.
   One rule for every card this renderer draws: complaint, request, «طلب خاص»,
   a request the team reviews. (A food card has no cap: its lines scroll.) */
.zv-flow .concierge-approval-card > * { flex-shrink: 0; }
.zv-flow .concierge-approval-card > div:nth-child(2),
.zv-flow .concierge-approval-card > .concierge-brief-form {
    flex: 0 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--zv-muted) transparent;
}
/* What is being confirmed gives way last: the details shrink first, so the
   summary keeps its lines for as long as the cap leaves room for them. */
.zv-flow .concierge-approval-card > .concierge-brief-form { flex-shrink: 8; }
.zv-flow .concierge-approval-card > div:first-child > span:first-child {
    background: transparent !important; color: var(--zv-mint) !important;
}
.zv-flow .zw-ac-statusdot { display: none; }
.zv-flow .concierge-approval-chat-hint { display: none !important; }
/* The details: one quiet list on the card itself — no box in a box in a box,
   no tiny tracked capitals. Each row is one thing that was understood. */
.zv-flow .concierge-brief-form {
    margin: 2px 17px 12px;
    padding: 0;
    border: 0;
    background: none;
    gap: 0;
    min-height: 64px;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--zv-muted) transparent;
}
.zv-flow .concierge-final-review {
    padding: 0;
    border: 0;
    background: none;
}
.zv-flow .concierge-brief-review-title { display: none; }
.zv-flow .concierge-brief-review-list { gap: 0; }
.zv-flow .concierge-brief-review-row {
    grid-template-columns: minmax(84px, auto) minmax(0, 1fr);
    padding: 10px 0;
    border-top: 1px solid rgba(243, 247, 246, 0.07);
}
.zv-flow .concierge-brief-review-key { font-size: 14px; color: var(--zv-muted); }
.zv-flow .concierge-brief-review-value { font-size: 16.5px; color: var(--zv-mist); }
.zv-flow .concierge-brief-status { font-size: 12px; padding: 4px 10px; }
.zv-flow .concierge-brief-status.is-ready {
    background: var(--zv-surface-2) !important;
    color: var(--zv-violet) !important;
    border-color: rgba(196, 181, 253, 0.3) !important;
}
.zv-flow .concierge-approval-card[data-resolution-status="confirmed"] .concierge-brief-status {
    color: var(--zv-mint) !important;
    border-color: rgba(118, 224, 189, 0.3) !important;
}
.zv-flow .concierge-approval-actions {
    flex-direction: row !important;
    gap: 8px !important;
    padding: 12px 17px 17px !important;
}
.zv-flow .concierge-approval-actions > button { font-family: var(--zv-face-ui) !important; }
.zv-flow .concierge-approval-actions > [data-zv-role="confirm"] {
    flex: 1 1 auto !important;
    order: -1;
    min-height: 54px !important;
    border-radius: 18px !important;
    background: var(--zv-mint) !important;
    color: var(--zv-navy) !important;
    font-size: 18px !important;
    font-weight: 700 !important;
}
.zv-flow .concierge-approval-actions > [data-zv-role="cancel"] {
    flex: none !important;
    min-height: 44px !important;
    border: 0 !important;
    background: transparent !important;
    color: var(--zv-quiet) !important;
    font-size: 15px !important;
    padding-inline: 12px !important;
}
/* One reading column: every card, request or food, spans it. (The thread's
   own ≥769px rule seats a food card at 70% beside an avatar gutter — a chat
   bubble's geometry, which made the voice column change width card to card.) */
.zv-flow .dining-chat-card {
    margin: 0; display: flex; flex-direction: column;
    align-self: stretch; width: auto; max-width: none;
    min-height: 64px; max-height: none;
}
/* A no-action empty menu contributes no decision beside an actionable request,
   nor beside a request just sent. Keep its node for return to chat; give this
   screen to the request to review — or to its receipt. */
.zv-flow:has(.concierge-approval-card:not(.zv-folded)) .dining-chat-card:has(.fc-collapsed.is-static:not(.is-loading)),
.zv-flow:has(.zv-receipt) .dining-chat-card:has(.fc-collapsed.is-static:not(.is-loading)) { display: none; }
.zv-card-scroll {
    flex: none; display: flex; align-items: center; justify-content: center; gap: 6px;
    padding: 6px 16px; color: var(--zv-muted); font: 500 11px/1.4 var(--zv-face-ui);
}
.zv-card-scroll[hidden] { display: none; }
.zv-card-scroll .zv-ic { flex: none; width: 14px; height: 14px; }
.zv-card-scroll-t { display: flex; gap: 6px; flex-wrap: wrap; }
.zv-flow .fc {
    --fc-card: var(--zv-surface);
    --fc-paper: var(--zv-surface);
    --fc-em: var(--zv-mint);
    --fc-em-ink: var(--zv-navy);
    --fc-em-deep: var(--zv-sunken);
    --fc-em-edge: var(--zv-emerald);
    --fc-amber: var(--zv-amber);
    --fc-amber-deep: var(--zv-surface-2);
    --fc-amber-edge: var(--zv-amber);
    --fc-amber-text: var(--zv-amber);
    --fc-t1: var(--zv-mist);
    --fc-t2: var(--zv-quiet);
    --fc-t3: var(--zv-muted);
    --fc-r-card: 22px;
    --fc-r-ctl: 14px;
    font-family: var(--zv-face-read);
    min-height: 0; flex: 0 1 auto;
}
.zv-flow .fc-name { font-size: 19px; line-height: 1.4; }
.zv-flow .fc-meta { white-space: normal; font-size: 14px; line-height: 1.5; }
.zv-flow .fc-collapsed { height: auto; min-height: 64px; padding: 12px 16px; }
.zv-flow .fc-expanded { display: flex; flex-direction: column; padding: 12px 16px; overflow: hidden; }
.zv-flow .fc-head, .zv-flow .fc-tray { flex: none; }
.zv-flow .fc-list, .zv-flow .fc-lines {
    min-height: 48px; overflow-y: auto; overscroll-behavior: contain;
    scrollbar-width: thin; scrollbar-color: var(--zv-muted) transparent;
    border-top-style: solid;
}
.zv-flow .fc-open { min-height: 64px; }
.zv-flow .fc-dish { font-size: 17px; line-height: 1.45; font-weight: 500; }
.zv-flow .fc-lead { border: 0; }
/* Prices and totals are read, not pressed: mist, set in the line's own weight.
   On a voice card the one mint thing is what to press (Review · Send). */
.zv-flow .fc-price, .zv-flow .fc-amt { font-size: 16px; font-weight: 600; color: var(--zv-mist); }
.zv-flow .fc-tray { margin: 12px -16px -12px; padding: 12px 16px; border-radius: 0 0 22px 22px; }
.zv-flow .fc-plate.is-mono { background: var(--zv-surface-2) !important; color: var(--zv-cream) !important; }
.zv-flow .fc-receipt { display: flex; flex-direction: column; overflow: visible; flex: none; }
.zv-flow .fc-paper {
    min-height: 0; padding: 16px; border-radius: 22px; border: 1px solid var(--zv-line);
}
.zv-flow .fc-paper > * { flex: none; }
/* Bound only the scrollable lines. Titles, notices and controls keep their
   natural height, so a wrapped notice can never escape its paper container. */
.zv-flow .fc-paper > .fc-lines {
    flex: none; max-height: max(96px, calc(var(--zv-card-max) - 260px));
    gap: 0; margin-block: 4px; padding-top: 0;
}
.zv-flow .fc-lines li { padding: 10px 0; border-bottom: 1px solid var(--zv-line); }
.zv-flow .fc-tray-total { color: var(--zv-mist); }
.zv-flow .fc-over { font-size: 13px; }
.zv-flow .fc-room { transform: none; color: var(--zv-mist); border-style: solid; }
.zv-flow .fc-total { border-top-style: solid; font-size: 14px; }
.zv-flow .fc-total b { font-size: 24px; font-weight: 600; color: var(--zv-mist); }
.zv-flow .fc-send { min-height: 54px; font-size: 17px; font-weight: 700; }
.zv-flow .fc-tear { display: none; }
.zv-card-preparing { color: var(--zv-amber); margin: 0; padding: 10px 16px; }
.zv-flow .dining-chat-card:has(.is-loading) {
    border: 1px solid var(--zv-line); border-radius: 22px; background: var(--zv-sunken);
}
.zv-flow .is-loading { border: 0; background: transparent; }
/* Being corrected: it recedes, and its Confirm is no longer the mint one. */
.zv-flow .zv-card.zv-stale { opacity: 0.42; }
.zv-flow .zv-stale .concierge-approval-actions > [data-zv-role="confirm"] {
    background: transparent !important;
    color: var(--zv-quiet) !important;
    border: 1px solid var(--zv-line) !important;
}

/* ── Settled: the folded card's receipt ────────────────────────────────── */
.zv-receipt {
    flex: none;
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 12px;
    padding: 12px 12px 12px 14px;
    border-radius: 18px;
    background: var(--zv-sunken);
    border: 1px solid rgba(118, 224, 189, 0.24);
}
.zv-receipt-ic { width: 26px; height: 26px; color: var(--zv-mint); }
.zv-receipt-text { min-width: 0; }
.zv-receipt-summary {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 15.5px;
    font-weight: 650;
    color: var(--zv-mist);
}
.zv-receipt-summary { display: flex; align-items: baseline; gap: 4px; }
.zv-receipt-summary-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.zv-receipt-summary .zv-qw { flex: none; }
.zv-receipt-summary .zv-metal { fill: var(--zv-mint); }
.zv-receipt-summary .zv-qw { width: 18px; height: 9px; }
.zv-receipt-t {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 8px;
    margin-top: 2px;
    color: var(--zv-mint);
}
.zv-receipt-t-a { font-size: 13.5px; font-weight: 700; }
.zv-receipt-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 44px;
    padding: 0 12px;
    border-radius: 14px;
    border: 1px solid var(--zv-line);
    background: transparent;
    color: var(--zv-mist);
    font-family: var(--zv-face-ui);
    font-size: 13.5px;
    font-weight: 600;
    cursor: pointer;
}
.zv-receipt-arrow { width: 16px; height: 16px; }
.zv[dir="rtl"] .zv-receipt-arrow { transform: scaleX(-1); }

/* ── Refused: plain words, a fix, and a way forward ────────────────────── */
.zv-refusal {
    flex: none;
    padding: 20px 18px 18px;
    border-radius: 22px;
    background: var(--zv-surface);
    border: 1px solid var(--zv-line);
}
.zv-refusal-head { display: flex; align-items: flex-start; gap: 12px; }
.zv-refusal-ic { flex: none; width: 26px; height: 26px; margin-top: 3px; color: var(--zv-cream); }
.zv-refusal-title {
    margin: 0;
    font-size: 20px;
    line-height: 1.4;
    font-weight: 700;
    color: var(--zv-mist);
}
.zv-refusal-body {
    margin: 10px 0 18px;
    font-family: var(--zv-face-read);
    font-size: 16.5px;
    line-height: 1.65;
    color: var(--zv-cream);
}
.zv-refusal-actions { display: flex; flex-direction: column; gap: 8px; }
.zv-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 56px;
    border: 0;
    border-radius: 18px;
    font-family: var(--zv-face-ui);
    font-size: 17px;
    font-weight: 700;
    cursor: pointer;
}
.zv-btn .zv-ic { width: 20px; height: 20px; }
.zv-btn-primary { background: var(--zv-mint); color: var(--zv-navy); }
.zv-btn-quiet {
    min-height: 52px;
    background: transparent;
    border: 1px solid var(--zv-line);
    color: var(--zv-mist);
    font-size: 15.5px;
    font-weight: 600;
}

/* ── The summoned layer (voice-mode.js `lendLayer`) ────────────────────────
   The menu, the guidebook or the room requests — one at a time, each the
   page's own surface. It takes the whole frame under the header, and the
   conversation is parked, not squeezed. The stage, status and controls keep
   their exact boxes underneath (hidden, never re-laid out), so the way back
   lands on the same turn, scroll and card sizes. What stays of the voice is
   one bar at the bottom: the voice element at its start, the way back (the
   layer's own close control) and the word that does the same by voice.
   Nothing here moves; the only motion is the body's own rise and fall.
   Stack: sheets 15050 (guidebook detail 20000) > voice 15000; inside voice: corner 3 > layer 2. */
.zv-layer {
    position: absolute;
    z-index: 2;
    top: calc(env(safe-area-inset-top, 0px) + var(--zv-head-h));
    bottom: 0;
    inset-inline: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--zv-navy);               /* the lent surface's own ground: no box in a box */
}
.zv-layer[hidden] { display: none; }

/* Parked: the voice's parts stay where they are, unseen and unreachable. */
.zv[data-layer] .zv-engine-status,
.zv[data-layer] .zv-stage,
.zv[data-layer] .zv-mute,
.zv[data-layer] .zv-end { visibility: hidden; }
/* The voice element, small, at the bar's start. Size and place only (no
   transform: the exit motion owns the corner's transform). A tap on it is
   a tap on the bar. */
.zv[data-layer] .zv-corner {
    --zv-voice: var(--zv-park-voice);
    --zv-glyph: var(--zv-park-glyph);
    z-index: 3;
    left: auto;
    right: auto;
    margin: 0;
    inset-inline-start: calc(max(0px, (100% - var(--zv-frame-max)) / 2) + var(--zv-gutter) + var(--zv-park-inset));
    bottom: calc(var(--zv-bottom) + var(--zv-park-inset));
    pointer-events: none;
}
/* Falling (closeLayer has already handed the frame back): the conversation is
   in place again and only the body's own fade is left on top of it. */
.zv:not([data-layer]) .zv-layer { background: none; pointer-events: none; }
.zv:not([data-layer]) .zv-layer-head { visibility: hidden; }

/* The bar: the parked conversation. One tap anywhere on it is the way back. */
.zv-layer-head {
    order: 2;
    position: relative;
    flex: none;
    display: flex;
    min-height: var(--zv-park-h);
    margin: 0 var(--zv-gutter) var(--zv-bottom);
    box-sizing: border-box;
    border: 1px solid var(--zv-line);
    border-radius: var(--zv-control-radius);
    background: var(--zv-surface);
}
.zv-layer-close {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-width: 0;
    padding-block: 0;
    padding-inline: calc(var(--zv-park-voice) + 2 * var(--zv-park-inset)) 14px;
    border: 0;
    border-radius: inherit;
    background: transparent;
    color: var(--zv-mist);
    font-family: var(--zv-face-ui);
    font-size: 15px;
    font-weight: 700;
    line-height: 20px;
    text-align: start;
    cursor: pointer;
}
/* Two lines: the way back, and under it (`.zv-layer-hint`) the spoken way.
   The label's line (20px) is the hint's (18px) plus their 2px gap, so the
   bar's middle is exactly between them: the label ends there, the hint
   starts 2px under it, and the chevron sits centred on both. */
.zv-layer-close-t {
    min-width: 0;
    margin-block-end: calc(var(--zv-park-line) + 2px);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.zv-layer-close .zv-ic { flex: none; width: 20px; height: 20px; stroke-width: 2.2; }
.zv-layer-close[aria-expanded="true"] .zv-ic { transform: rotate(180deg); }
.zv-layer-hint {
    position: absolute;
    inset-inline: calc(var(--zv-park-voice) + 2 * var(--zv-park-inset)) 48px;
    top: calc(50% + 2px);
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    height: var(--zv-park-line);
    color: var(--zv-muted);
    pointer-events: none;                     /* part of the bar's one tap */
}
/* Shown only while saying «ارجع» closes the layer (voice-mode.js `spokenCommands`). */
.zv-layer-hint[hidden] { display: none; }
.zv-layer-close.is-alone .zv-layer-close-t { margin-block-end: 0; }
.zv-layer-hint-ic { flex: none; width: 14px; height: 14px; color: var(--zv-mint); }
.zv-layer-hint-t { display: flex; min-width: 0; }
.zv-layer-hint-t-a {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
    font-weight: 600;
    line-height: var(--zv-park-line);
    color: var(--zv-quiet);
}
.zv-layer-body {
    order: 1;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    overflow: hidden;
    overscroll-behavior: contain;
    padding: 4px var(--zv-gutter) 12px;
    scrollbar-width: none;
}
.zv-layer-body::-webkit-scrollbar { display: none; }

/* The lent surface: no box in a box, no tracked capitals over its name. */
.zv-layer .dining-surface { color: var(--zv-mist); }
.zv-layer .dining-shell { width: 100%; padding: 0 0 16px; }
.zv-layer .dining-surface { overflow-y: auto; overscroll-behavior: contain; }
.zv-layer .dining-cart-bar { position: relative; bottom: auto; }
.zv-layer .dining-hero {
    margin: 0 0 8px;
    padding: 0 4px 4px;
    border: 0;
    border-radius: 0;
    background: none;
}
.zv-layer .dining-eyebrow { display: none; }
.zv-layer .dining-hero h1 { font-size: 22px; color: var(--zv-mist); }
/* The food card's own tokens, mixed from the voice palette while it is here. */
.zv-layer .fc {
    --fc-ground: var(--zv-navy);
    --fc-card: var(--zv-sunken);
    --fc-paper: var(--zv-surface-2);
    --fc-line: rgba(243, 247, 246, 0.07);
    --fc-line-2: var(--zv-line);
    --fc-em: var(--zv-mint);
    --fc-em-ink: var(--zv-navy);
    --fc-em-deep: color-mix(in srgb, var(--zv-emerald) 18%, var(--zv-navy));
    --fc-em-edge: color-mix(in srgb, var(--zv-emerald) 60%, var(--zv-navy));
    --fc-em-text: var(--zv-mint);
    --fc-amber: var(--zv-amber);
    --fc-amber-deep: color-mix(in srgb, var(--zv-amber) 14%, var(--zv-navy));
    --fc-amber-edge: color-mix(in srgb, var(--zv-amber) 40%, var(--zv-navy));
    --fc-amber-text: var(--zv-amber);
    --fc-t1: var(--zv-mist);
    --fc-t2: var(--zv-quiet);
    --fc-t3: var(--zv-muted);
}
/* Without color-mix() these four stay raw text, and every var() that reads
   them fails at computed-value time: the add button loses its disc, the tray
   its fill. A plain value declared first cannot rescue a custom property —
   the later one always wins — so the same mixes, precomputed in sRGB, stand
   in only where the function is unknown. */
@supports not (color: color-mix(in srgb, red, red)) {
    .zv-layer .fc {
        --fc-em-deep: #0A2B28;                /* emerald 18% in navy */
        --fc-em-edge: #0F6451;                /* emerald 60% */
        --fc-amber-deep: #282A21;             /* amber 14% */
        --fc-amber-edge: #655836;             /* amber 40% */
    }
}
.zv-layer .fc-tile-art { background: var(--zv-surface-2); }
.zv-layer .fc-tile-art.is-mono { background: var(--zv-surface-2); color: var(--zv-cream); }

/* A sheet the menu opens (a dish, the cart, the priced review) is the
   module's own and stands above voice mode — never hidden beneath it. */
html.zv-open .dining-sheet-overlay { z-index: 15050; }

/* The guidebook and the room requests, lent: each scrolls inside the layer on
   the layer's own ground, as the menu does. Their own transitions move only
   transform and opacity while they are here. */
.zv-layer .guidebook-surface,
.zv-layer #roomServiceContent {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    color: var(--zv-mist);
}
.zv-layer #roomServiceContent { padding: 4px 4px 16px; }
.zv-layer .guidebook-surface *,
.zv-layer #roomServiceContent * { transition-property: transform, opacity !important; }
/* The hotel facilities, lent the same way (the Facilities tab's own body). */
.zv-layer #hotelFacilitiesContent {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 4px 4px 16px;
    color: var(--zv-mist);
}
.zv-layer #hotelFacilitiesContent * { transition-property: transform, opacity !important; }

/* 🎙️ Driven by voice (voice-layer-nav.js). The item a question or answer
   names is marked while it is talked about: a still outline, never an
   animated shadow; its one settle is transform/opacity (WAAPI, rationed). */
/* A lent surface's own stacking (the guidebook's sticky toolbar, its card
   layers) stays inside the body, under the photo. No visual change. */
.zv-layer-body { isolation: isolate; }
/* The guidebook's selected chip is a gradient, which the layer removes (no
   gradients here): its own dark text was left on navy. The flat mint nearest
   to that gradient, so the pane voice just chose can be read. */
.zv-layer .guidebook-category-chip.is-active { background-color: var(--zv-mint); }
.zv-layer .zv-spot {
    outline: 2px solid var(--zv-mint);
    outline-offset: 2px;
}
.zv-layer .guidebook-card.zv-spot,
.zv-layer .hs-card.zv-spot,
.zv-layer .hs-ticket.zv-spot { outline-offset: 0; }

/* An item's photo, inside the layer: over the lent surface, never over the
   bar (the way back stays where it is). Navy ground, the photo on sunken. */
.zv-photo {
    position: absolute;
    z-index: 1;
    inset: 0 0 calc(var(--zv-park-h) + var(--zv-bottom)) 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 8px var(--zv-gutter) 12px;
    background: var(--zv-navy);
}
.zv-photo[hidden] { display: none; }
.zv-photo-frame {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin: 0;
    padding: 10px;
    border: 1px solid var(--zv-line);
    border-radius: 16px;
    background: var(--zv-sunken);
}
.zv-photo-img {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    object-fit: contain;
    border-radius: 12px;
}
.zv-photo-cap {
    flex: none;
    color: var(--zv-cream);
    font-family: var(--zv-face-ui);
    font-size: 17px;
    font-weight: 700;
    line-height: 1.4;
    text-align: center;
}
.zv-photo-bar { flex: none; display: flex; align-items: center; gap: 12px; }
.zv-photo-close {
    flex: none;
    min-width: 96px;
    min-height: 44px;
    padding: 0 18px;
    border: 1px solid var(--zv-line);
    border-radius: 12px;
    background: var(--zv-surface-2);
    color: var(--zv-mist);
    font-family: var(--zv-face-ui);
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
}
.zv-photo-close:focus-visible { outline: 2px solid var(--zv-mint); outline-offset: 2px; }
.zv-photo-hint { min-width: 0; color: var(--zv-muted); font-family: var(--zv-face-ui); font-size: 13px; font-weight: 600; }
/* The guidebook's detail sheet opens over voice mode (it already stands at
   20000): flat while voice is open — no blur behind it. */
html.zv-open .guidebook-detail-overlay { backdrop-filter: none; -webkit-backdrop-filter: none; }

/* ── While voice mode covers the page ──────────────────────────────────── */
/* Nothing underneath is seen, so nothing underneath is painted; the mascot's
   loops stop entirely (no mascot in voice mode). Both lift before the exit
   motion reveals the chat again. */
html.zv-covered .app-layout { visibility: hidden; }
html.zv-covered #zawiPet { display: none !important; }
/* From the first frame of the entry to the last of the exit, the chat's own
   loops hold still: the only motion on screen is voice mode's. */
html.zv-open .app-layout *,
html.zv-open .zw-skel { animation-play-state: paused !important; }

/* ── «طلباتي»: a confirmation still waiting (voice-requests.js) ─────────── */
.zv-waiting {
    margin: 0 0 16px;
    padding: 14px 14px 12px;
    border-radius: 16px;
    background: var(--zv-sunken);
    border: 1px solid rgba(196, 181, 253, 0.3);
    font-family: var(--zv-face-ui);
}
.zv-waiting-head { display: flex; align-items: center; gap: 8px; color: var(--zv-violet); }
.zv-waiting-ic {
    flex: none;
    width: 18px;
    height: 18px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.zv-waiting-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; }
.zv-waiting-title-a { font-size: 14px; font-weight: 700; }
.zv-waiting-title-b { font-size: 12.5px; font-weight: 500; opacity: 0.8; }
.zv-waiting-row { margin-top: 10px; padding-top: 10px; border-top: 1px solid rgba(243, 247, 246, 0.08); }
.zv-waiting-summary { font-size: 15px; font-weight: 650; line-height: 1.45; color: var(--zv-mist); }
.zv-waiting-note { margin-top: 3px; font-size: 12.5px; line-height: 1.5; color: var(--zv-muted); }
.zv-waiting-go {
    width: 100%;
    min-height: 44px;
    margin-top: 10px;
    border-radius: 12px;
    border: 1px solid rgba(196, 181, 253, 0.45);
    background: transparent;
    color: var(--zv-mist);
    font-family: var(--zv-face-ui);
    font-size: 14px;
    font-weight: 650;
    cursor: pointer;
}

/* Reduced motion means none. The page-wide rule sets every element's
   transition-duration to 0.01ms, and transition-property defaults to `all`
   — which turns every style change into a one-frame transition. Not here:
   no turn, no press. The screen is complete without them — orientation,
   fill and the status line still say everything the motion said. */
@media (prefers-reduced-motion: reduce) {
    .zv,
    .zv *,
    .zv-waiting,
    .zv-waiting *,
    .action-btn.zv-entry { transition-property: none !important; }
}

/* Owner polish: state lights belong to the approved SVG material. */
.zv-engine-status[hidden], .zv-layer-body[hidden] { display: none; }
.zv[data-phase="speaking"] .zv-lamp { opacity: .32; }
.zv[data-phase="speaking"] .zv-light-drift { opacity: .55; }
.zv[data-phase="speaking"] .zv-light-surge { opacity: .28; transform: scale(1.15); }
.zv[data-phase="thinking"] .zv-light-drift { opacity: .45; }
.zv-object { transform-origin: 50% 50%; }
.zv-layer, .zv-layer * { background-image: none !important; box-shadow: none !important;
    backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }
.zv-layer .dining-panes { display: none !important; }
.zv-layer .dining-layout { grid-template-columns: minmax(0, 1fr); }
.zv-layer .dining-search, .zv-layer .dining-filter-btn { background: var(--zv-sunken); color: var(--zv-mist); }
.zv-layer .dining-outlet-title { font-size: 19px; }
.zv-layer .dining-hero { margin-bottom: 12px; }
@media (prefers-reduced-motion: reduce) {
    .zv, .zv * { animation: none !important; transition: none !important; }
}

.zv-layer-close:focus-visible { outline-offset: -4px; border-radius: 12px; }
