/*
 * Говорящий портрет вместо сферы голосового режима (hermes-voice-portrait.js).
 * Персонаж сидит за столом: стол и световая полоса фазы на его заднем крае — псевдоэлементы портрета; голова и волосы
 * ничем не обрезаны, снизу бока бюста сходят на нет. Полоса берёт цвета фаз прежней сферы, ореол за персонажем дышит
 * голосом агента. Слои снизу вверх (z-index): ореол, предплечье руки взмаха, персонаж, позы рук, стол, предмет облика
 * (картинка из пакета сцены), полоса фазы, кисть руки взмаха, «z» и эффекты у головы.
 * Слои персонажа снаружи внутрь: body — прыжок и вздох, breath — дыхание, pose — поза фазы и сна, mood — посадка
 * настроения, sway — блуждание корпуса, face — блуждание головы, look — к курсору, head — занятия тишины и
 * пробуждение, speak — посадка фразы и кивки, gesture ×2 — жесты, talk — лицо; база лица — картинка или холст WebGL
 * (hermes-portrait-warp.js), который возвращает плечи к позе корпуса. Дыхание, блуждание, кивки и жесты ведёт
 * hermes-portrait-motion.js (Web Animations, data-motion="js"); без них — прежние CSS-петли (data-motion="css").
 * Здесь — позы, переходы и рука взмаха. Эффекты у головы (сердечки, искорки…) — отдельный слой поверх всего, без маски.
 * У каждого слоя своё свойство transform, поэтому анимации и переходы друг другу не мешают.
 */

.hermes-dashboard .voice-mode__orb--portrait {
    width: clamp(15rem, 50vh, 27rem);
}

/* Посадка (--seat-y) выносит макушку над коробкой портрета на эту долю её высоты. Рядом с панелью портрет стоит у самого верха
   прокручиваемой сцены, и она срезала бы макушку: место сверху — та же доля ширины портрета (4.5 rem в hermes-shell.css). */
.hermes-dashboard .voice-mode.voice-mode--with-panel .voice-mode__orb--portrait {
    margin-top: calc(4.5rem * 0.075);
}

/* Громкость пишется на круг голоса 20 раз в секунду, а в CSS её никто не читает: ненаследуемая переменная
   не пересчитывает стиль всего портрета при каждой записи. */
@property --voice-level {
    syntax: "<number>";
    inherits: false;
    initial-value: 0;
}

@property --voice-assistant-level {
    syntax: "<number>";
    inherits: false;
    initial-value: 0;
}

@property --voice-assistant-open {
    syntax: "<number>";
    inherits: false;
    initial-value: 0;
}

@property --voice-user-level {
    syntax: "<number>";
    inherits: false;
    initial-value: 0;
}

@property --voice-assistant-vowel {
    syntax: "*";
    inherits: false;
}

/* Пружины (перелёт ~4 %, как у livliv): голова к курсору и посадка фразы — быстрые, поза и настроение — медленные.
   Без linear() — прежняя кривая. */
.voice-portrait {
    --spring-fast: cubic-bezier(0.3, 0.7, 0.3, 1);
    --spring-slow: cubic-bezier(0.3, 0.7, 0.3, 1);
}

@supports (transition-timing-function: linear(0, 1)) {
    .voice-portrait {
        --spring-fast: linear(0, 0.07, 0.226, 0.411, 0.587, 0.737, 0.853, 0.935, 0.989, 1.02, 1.035, 1.038, 1.036, 1.029, 1.022, 1.016, 1);
        --spring-slow: linear(0, 0.057 5%, 0.19 10%, 0.353 15%, 0.517 20%, 0.664 25%, 0.786 30%, 0.947 40%, 1.02 50%, 1.038 60%, 1.032 70%, 1.02 80%, 1.009 90%, 1);
    }
}

/* Сцена: персонаж сидит за столом. Стол — псевдоэлемент ::after на всю ширину от --desk-y (доля высоты коробки, задний
   край столешницы) до низа коробки, световая полоса фазы — ::before вдоль этого края. По --desk-y модуль портрета узнаёт
   стили этой сцены, а сервис сборки аватаров читает его из этого файла регулярным выражением: значение — число с
   процентом в одной строке, без перепостановок. Если у облика есть пакет сцены, модуль пишет на портрет свой --desk-y. */
.voice-portrait {
    --desk-y: 85%;
    /* Посадка: на сколько персонаж поднят над канонической посадкой рига (доля высоты коробки). Сдвинуты рамки персонажа,
       ореол, «z» и эффекты; стол, предмет облика и полоса фазы стоят. Модуль портрета считает по ней линию стола в
       координатах холста рига (стол плюс посадка); сервис сборки читает её из этого файла, как и --desk-y: число с
       процентом в одной строке. */
    --seat-y: 7.5%;
    /* Канон рига: место руки взмаха (прежний медальон — круг радиуса --medal-r с центром на высоте --medal-y; проценты
       одинаковы по обеим осям, коробка квадратная) и зона касаний прежних клиентов. На вид больше не влияют; их читает
       сервис сборки аватаров. */
    --medal-r: 37%;
    --medal-y: 54%;
    /* Ход руки взмаха при появлении и исчезновении. По этой переменной hermes-voice-portrait.js узнаёт стили,
       знающие кисть поверх стола: с прежними рука поднималась издалека — на виду. */
    --hand-rise: 18%;
    /* Тень руки позы — непрозрачность размытой тёмной копии спрайта. По этой переменной hermes-voice-portrait.js узнаёт
       стили, знающие руки поз (с прежними узел руки лёг бы на страницу без размеров, и вместо позы идёт запасной жест). */
    --arm-shade: 0.1;
    position: absolute;
    inset: 0;
    /* Слои сцены раскладывает z-index, независимо от порядка узлов; наружу он не выходит. */
    isolation: isolate;
    /* «z» сна растут с портретом (cqw). */
    container-type: size;
    /* По персонажу кликают и гладят: без выделения, подсветки касания и зума двойным тапом; ведение пальцем по
       голове — поглаживание, а не прокрутка (иначе браузер отменит касание). */
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    touch-action: none;
}

/* Ореол — подсветка за персонажем, голос агента: цвет речи в любой фазе, --halo (0–10) пишет hermes-voice-portrait.js
   по громкости. Без перехода и на своём слое: каждый переход стоил главному потоку лишних кадров, а смена слоя — только
   композитору. Плавность даёт огибающая в JS. */
.voice-portrait__halo {
    --halo: 0;
    position: absolute;
    left: 12%;
    top: calc(6% - var(--seat-y, 0%));
    width: 76%;
    height: 78%;
    z-index: 0;
    border-radius: 50%;
    pointer-events: none;
    background: radial-gradient(closest-side, rgb(var(--voice-orb-speak) / 0.42), rgb(var(--voice-orb-speak) / 0.16) 62%, transparent);
    opacity: calc(0.45 + 0.05 * var(--halo));
    transform: scale(calc(1.02 + 0.006 * var(--halo)));
    will-change: opacity, transform;
}

.voice-portrait[data-phase="connect"] .voice-portrait__halo {
    opacity: 0.35;
}

/* Диск, кольца и дуга прежней сцены: их создаёт только модуль старой версии (вебка обновилась не целиком), а круга
   больше нет — без своих правил они лежали бы поверх персонажа. */
.voice-portrait__disc,
.voice-portrait__rings,
.voice-portrait__arc {
    display: none;
}

/* Нейтральный стол: доска на всю ширину от --desk-y до низа коробки, непрозрачная, тёмная с лёгким оттенком акцента;
   светлее полоска столешницы сверху. Тень чуть выше столешницы сажает персонажа за стол. Размеры тени — в rem: единицы
   контейнера в самом контейнере не опора. С предметом облика (data-scene) стол рисует картинка, нейтральный скрыт;
   на мелком портрете (data-compact) предмета нет, и виден нейтральный. */
.voice-portrait::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: var(--desk-y);
    bottom: 0;
    z-index: 4;
    pointer-events: none;
    background:
        linear-gradient(rgb(255 255 255 / 0.14) 0 12%, transparent 12%),
        linear-gradient(rgb(var(--hermes-accent-rgb) / 0.16), rgb(var(--hermes-accent-rgb) / 0.08)),
        #1b1a22;
    box-shadow: 0 -0.5rem 0.8rem -0.4rem rgb(0 0 0 / 0.35);
}

.voice-portrait[data-scene]:not([data-compact])::after {
    display: none;
}

/* Предмет облика (картинка из пакета сцены): место — в процентах холста, его пишет hermes-voice-portrait.js. */
.voice-portrait__prop {
    position: absolute;
    z-index: 5;
    display: block;
    pointer-events: none;
    user-select: none;
}

.voice-portrait[data-compact] .voice-portrait__prop {
    display: none;
}

/* Световая полоса фазы — вдоль заднего края столешницы, цвета фаз прежней сферы; смена цвета — переходом. Соединение —
   цвет слушания, приглушённо. Работает: по полосе бежит блик (анимация фона). Ждёт подтверждения: полоса медленно
   мерцает — «твой ход». Анимации — только когда движение разрешено и профиль не лёгкий; во сне они на паузе (ниже).
   Длительность и кривая — в самой полосе, имя анимации ставят правила фаз: пауза сна их не сбрасывает. */
.voice-portrait::before {
    content: "";
    position: absolute;
    left: 2%;
    right: 2%;
    top: var(--desk-y);
    height: max(2px, 0.8%);
    z-index: 6;
    --tone: var(--voice-orb-listen);
    border-radius: 999px;
    pointer-events: none;
    background-color: rgb(var(--tone));
    background-repeat: no-repeat;
    box-shadow: 0 0 0.5rem rgb(var(--tone) / 0.65);
    transition: background-color 300ms ease, box-shadow 300ms ease, opacity 300ms ease;
    animation-duration: 1.6s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
}

.voice-portrait[data-phase="hear"]::before { --tone: var(--voice-orb-hear); }
.voice-portrait[data-phase="work"]::before { --tone: var(--voice-orb-work); }
.voice-portrait[data-phase="speak"]::before { --tone: var(--voice-orb-speak); }
.voice-portrait[data-phase="mute"]::before { --tone: var(--voice-orb-mute); }

.voice-portrait[data-phase="connect"]::before {
    opacity: 0.55;
}

/* Мелкий портрет (круг 3 rem шторки): полоса толще, иначе её не видно. data-compact пишет hermes-voice-portrait.js. */
.voice-portrait[data-compact]::before {
    height: max(3px, 2.4%);
}

@media (prefers-reduced-motion: no-preference) {
    .voice-portrait[data-phase="work"]:not([data-lite])::before {
        background-image: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.9), transparent);
        background-size: 30% 100%;
        animation-name: portrait-glint;
    }

    .voice-mode[data-agent-state="wait"] .voice-portrait:not([data-lite])::before {
        background-image: none;
        animation-name: portrait-strip-wait;
        animation-duration: 2.4s;
        animation-timing-function: ease-in-out;
    }
}

/* Персонаж, предплечье руки взмаха и позы рук: бока сходят на нет (6 % ширины с каждой стороны) только внизу — срез
   бюста по краю коробки не виден, шляпа и волосы наверху не тронуты. Маска из двух слоёв (они складываются): горизонтальный
   градиент на всю высоту и вертикальный — непрозрачный сверху и гаснущий между 50 и 70 % высоты. Низ рамок не режется:
   его закрывает стол. Ореол и стол видны сразу, лицо проявляется, когда его части загрузились (is-ready). */
.voice-portrait__frame {
    position: absolute;
    inset: 0;
    z-index: 2;
    opacity: 0;
    transition: opacity 420ms ease;
    -webkit-mask:
        linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent),
        linear-gradient(#000 50%, transparent 70%);
    mask:
        linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent),
        linear-gradient(#000 50%, transparent 70%);
}

.voice-portrait.is-ready .voice-portrait__frame {
    opacity: 1;
}

/* Посадка (--seat-y): все рамки персонажа (он сам, рука взмаха, позы рук, кисть поверх стола) и слой эффектов у головы подняты
   вместе. Они на всю коробку, так что проценты translate — доли её высоты; ореол и «z» поднимает их top (у них свои размеры).
   translate не мешает transform слоёв и анимациям внутри рамок, а сами рамки вложены только в портрет — сдвиг один. Маска боков
   едет с рамкой. */
.voice-portrait__frame,
.voice-portrait__fx {
    translate: 0 calc(var(--seat-y, 0%) * -1);
}

/* Смена облика: новый портрет собирается невидимым поверх прежнего (и не ловит касаний), прежний живёт до его
   готовности — круг голосового режима не пустеет. Проявление нового — hermes-voice-portrait.js. */
.voice-portrait.is-pending {
    visibility: hidden;
}

.voice-portrait.is-pending .voice-portrait__frame {
    transition: none;
}

/* Рамки спрайта руки взмаха (--hand, --hand-over) за столом пусты: взмах там — поза wave пакета поз в рамке --pose, спрайт
   остался прежней сцене с кругом (hermes-voice-portrait.js). Предплечье руки взмаха — под персонажем: оно выходит из-за плеча, а не лежит обрывком поверх одежды; нижний конец
   плавно гаснет (маску копий пишет hermes-portrait-rig.js). Маска боков — общая, от рамки персонажа. */
.voice-portrait__frame--hand {
    z-index: 1;
    pointer-events: none;
}

/* Руки поз — над персонажем, под столом и предметом. Слои корпуса те же (прыжок, дыхание, поза, настроение, блуждание), слоёв
   головы нет: руки идут с бюстом, а не с головой. Поднимаются из-за стола и уходят обратно (hermes-voice-portrait.js). */
.voice-portrait__frame--pose {
    z-index: 3;
    pointer-events: none;
}

/* Кисть руки взмаха — над столом, без маски. Те же стили пишет hermes-voice-portrait.js прямо в рамку: в CSS из кэша
   (вебка обновилась не целиком) её правил нет, и рамка взяла бы маску персонажа и перехватила бы поглаживания. */
.voice-portrait__frame--hand-over {
    z-index: 7;
    pointer-events: none;
    -webkit-mask: none;
    mask: none;
}

/* По position: absolute у всех слоёв модуль портрета узнаёт стили своей версии (stylesFit в
   js/hermes-voice-portrait.js): слой с другим position модуль примет за старые стили. */
.voice-portrait__body,
.voice-portrait__breath,
.voice-portrait__pose,
.voice-portrait__mood,
.voice-portrait__sway,
.voice-portrait__face,
.voice-portrait__look,
.voice-portrait__head,
.voice-portrait__speak,
.voice-portrait__gesture,
.voice-portrait__talk {
    position: absolute;
    inset: 0;
}

/* Курсор: сдвиг и поворот пишет hermes-voice-portrait.js; голова догоняет глаза пружиной, чуть позже них. */
.voice-portrait__look {
    transform-origin: 50% 82%;
    transition: transform 420ms var(--spring-fast) 60ms;
}

/* Прыжок, вздох и дыхание — от нижнего края: грудь и голова чуть поднимаются. */
.voice-portrait__body,
.voice-portrait__breath {
    transform-origin: 50% 100%;
}

.voice-portrait[data-motion="css"] .voice-portrait__breath {
    animation: portrait-breathe 4.2s ease-in-out infinite;
}

/* Поза фазы: голова поворачивается вокруг шеи, к новой позе — с лёгким перелётом. */
.voice-portrait__pose {
    transform-origin: 50% 82%;
    transition: transform 1000ms var(--spring-slow), opacity 420ms ease, filter 420ms ease;
}

/* Настроение телом: посадку пишет hermes-voice-portrait.js из сглаженных баллов (радость выше, грусть ниже). */
.voice-portrait__mood {
    transform-origin: 50% 82%;
    transition: transform 900ms var(--spring-slow);
}

/* Посадка фразы (поворот головы на каждую фразу) — переход; кивки речи и слушания добавляются к ней. */
.voice-portrait__speak {
    transform-origin: 50% 82%;
    transition: transform 420ms var(--spring-fast);
}

.voice-portrait__gesture {
    transform-origin: 50% 82%;
}


.voice-portrait[data-phase="listen"] .voice-portrait__pose {
    transform: translateY(0.6%) scale(1.01);
}

.voice-portrait[data-phase="hear"] .voice-portrait__pose {
    transform: translateY(1%) rotate(-2.6deg) scale(1.015);
}

.voice-portrait[data-phase="work"] .voice-portrait__pose {
    transform: translateX(1.2%) rotate(2.2deg);
}

.voice-portrait[data-phase="mute"] .voice-portrait__pose {
    filter: saturate(0.6);
    transform: translateY(1.5%) scale(0.985);
}

.voice-mode[data-agent-state="wait"] .voice-portrait__pose {
    transform: translateY(-0.8%) rotate(-3.4deg) scale(1.02);
}

.voice-mode[data-agent-state="error"] .voice-portrait__pose {
    transform: translateY(2.4%) rotate(1.4deg) scale(0.985);
}

/* Корпус качается от пояса, голова — на шее (блуждание покоя, JS). */
.voice-portrait__sway {
    transform-origin: 50% 100%;
}

/* Блуждание покоя (JS) или, без Web Animations, прежнее покачивание; слушая — кивки, работая — раздумье. */
.voice-portrait__face {
    transform-origin: 50% 82%;
}

.voice-portrait[data-motion="css"] .voice-portrait__face {
    animation: portrait-sway 7.4s ease-in-out infinite;
}

.voice-portrait__head {
    transform-origin: 50% 82%;
    transition: transform 700ms var(--spring-slow);
}

.voice-portrait[data-motion="css"][data-phase="hear"] .voice-portrait__head {
    animation: portrait-listen 2.6s ease-in-out infinite;
}

.voice-portrait[data-motion="css"][data-phase="work"] .voice-portrait__head {
    animation: portrait-ponder 3.8s ease-in-out infinite;
}

.voice-portrait__talk {
    transform-origin: 50% 82%;
}

.voice-portrait__base,
.voice-portrait__layer {
    position: absolute;
    display: block;
    user-select: none;
    pointer-events: none;
}

.voice-portrait__layer[hidden] {
    display: none;
}

.voice-portrait__base {
    inset: 0;
    width: 100%;
    height: 100%;
}

/* Холст базы: на месте картинки с запасом по краям (размер и сдвиг пишет hermes-portrait-warp.js). */
.voice-portrait__warp {
    position: absolute;
    display: block;
    pointer-events: none;
}

/* Риг (портрет из частей): глаз — рамка с белком, радужкой под обрезкой белком и линией век; брови сдвигаются
   к нейтральному месту при слабом выражении; румянец — прозрачность. Всё — transform и opacity. */
.voice-portrait__eye,
.voice-portrait__eye-whites,
.voice-portrait__eye-clip,
.voice-portrait__eye-lines {
    position: absolute;
    pointer-events: none;
}

.voice-portrait__eye-whites,
.voice-portrait__eye-clip,
.voice-portrait__eye-lines {
    inset: 0;
}

.voice-portrait__eye-clip[hidden] {
    display: none;
}

/* Бровь: место ведёт пружина в кадре портрета (60 мс, в лёгком профиле 125 мс); линейный переход длиной в кадр
   сливает его ступени, а не перезапускает торможение на каждом кадре. */
.voice-portrait__brow {
    transition: transform 70ms linear;
}

.voice-portrait[data-lite] .voice-portrait__brow {
    transition-duration: 130ms;
}

/* Радужка: положение пишет кадр портрета (60 мс, в лёгком профиле 125 мс), промежуток доводит композитор. Саккада —
   почти мгновенный перевод; слежение за курсором (data-gaze="follow") — переход длиной в кадр, ступени сливаются. */
.voice-portrait__iris {
    transition: transform 40ms ease-out;
}

.voice-portrait[data-gaze="follow"] .voice-portrait__iris {
    transition: transform 70ms linear;
}

.voice-portrait[data-lite] .voice-portrait__iris {
    transition-duration: 60ms;
}

.voice-portrait[data-lite][data-gaze="follow"] .voice-portrait__iris {
    transition-duration: 130ms;
}

.voice-portrait__cheeks {
    opacity: 0;
    transition: opacity 400ms ease;
}

/* Рука взмаха: проявляется, чуть поднимаясь, машет вокруг оси у запястья и гаснет. Ход короткий: кисть лежит поверх
   стола, и длинный подъём шёл бы на виду. */
.voice-portrait__hand {
    position: absolute;
    display: block;
    user-select: none;
    pointer-events: none;
    opacity: 0;
    transform: translateY(var(--hand-rise)) rotate(14deg);
}

.voice-portrait[data-action="wave"] .voice-portrait__hand {
    animation: portrait-wave 1.8s ease-in-out 1;
}

/* Руки поз (hermes-voice-portrait.js кладёт их в свою рамку над персонажем и под столом). Рука — контейнер по рамке спрайта:
   подъём из-за стола, удержание и уход ведут Web Animations (transform-origin контейнера — корень руки, у кисти — запястье,
   оба пишет модуль). В контейнере тень (размытая тёмная копия спрайта), предплечье и кисть: кисть — тот же спрайт, разделённый
   с предплечьем маской по запястью. Картинки лежат во всю рамку руки. */
.voice-portrait__arms {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.voice-portrait__arm {
    position: absolute;
    pointer-events: none;
    user-select: none;
}

.voice-portrait__arm-hand,
.voice-portrait__arm-shade {
    position: absolute;
    inset: 0;
}

.voice-portrait__arm-img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    pointer-events: none;
    user-select: none;
}

/* Тень — узкая и слабая, вплотную к руке: она сажает руку на корпус. Широкий тёмный ореол поднимал руку над персонажем, как
   наклейку. Размытие и сдвиг растут с портретом (единицы контейнера). */
.voice-portrait__arm-shade {
    opacity: var(--arm-shade);
    filter: blur(0.45cqw) brightness(0);
    transform: translateY(0.35cqh);
}

/* Лёгкий профиль: тени нет (размытие стоит композитору слоя на каждом кадре хода). */
.voice-portrait[data-lite] .voice-portrait__arm-shade {
    display: none;
}

/* Сон — до подключения и после долгой тишины: глаза закрыты (JS), голова опущена, дыхание глубже и реже (JS), над
   головой «z». Пробуждение — голова поднимается с лёгким потягиванием. */
.voice-portrait[data-sleep] .voice-portrait__pose {
    filter: saturate(0.85) brightness(0.94);
    transform: translateY(2.2%) rotate(2.4deg) scale(0.985);
}

.voice-portrait[data-motion="css"][data-sleep] .voice-portrait__breath {
    animation: portrait-sleep-breathe 5.4s ease-in-out infinite;
}

.voice-portrait[data-wake] .voice-portrait__head {
    animation: portrait-wake 0.9s ease-out 1;
}

/* Лёгкий профиль (сенсорный экран, круг-кнопка шторки, «без движения»): без ореола, бегущего блика и мерцания полосы
   фазы — они стоят композитору слоёв на каждый кадр (сами анимации полосы — в правилах выше, только вне data-lite).
   Кадры реже ведёт hermes-voice-portrait.js. */
.voice-portrait[data-lite] .voice-portrait__halo {
    display: none;
}

/* Сон: полоса фазы и прежнее покачивание — на паузе, пока не разбудят; дыхание сна и «z» идут. */
.voice-portrait[data-sleep]::before,
.voice-portrait[data-motion="css"][data-sleep] :is(.voice-portrait__face, .voice-portrait__head) {
    animation-play-state: paused;
}

.voice-portrait__zzz {
    position: absolute;
    z-index: 8;
    left: 80%;
    top: calc(-2% - var(--seat-y, 0%));
    width: 16%;
    height: 22%;
    pointer-events: none;
    opacity: 0;
    transition: opacity 600ms ease;
}

.voice-portrait[data-sleep] .voice-portrait__zzz {
    opacity: 1;
}

.voice-portrait__zzz span {
    position: absolute;
    left: 0;
    bottom: 0;
    font: 800 clamp(0.75rem, 7cqw, 2rem)/1 system-ui, sans-serif;
    color: rgb(var(--voice-orb-listen));
    text-shadow: 0 0 0.4em rgb(var(--voice-orb-listen) / 0.45);
    opacity: 0;
    animation: portrait-zzz 3.6s ease-in infinite;
}

.voice-portrait__zzz span::before {
    content: "z";
}

/* С листом эффектов «z» — значок персонажа. */
.voice-portrait__zzz--icon span {
    width: 42%;
    background-repeat: no-repeat;
}

.voice-portrait__zzz--icon span::before {
    content: none;
}

.voice-portrait__zzz span:nth-child(2) { animation-delay: 1.2s; }
.voice-portrait__zzz span:nth-child(3) { animation-delay: 2.4s; }

/* Тишина: голова следит за взглядом (глаза — слои из manifest.idle), мурлычет, зевает, клюёт носом. */
.voice-portrait[data-idle="look-left"] .voice-portrait__head { transform: translateX(-1.2%) rotate(-2deg); }
.voice-portrait[data-idle="look-right"] .voice-portrait__head { transform: translateX(1.2%) rotate(2deg); }
.voice-portrait[data-idle="look-up"] .voice-portrait__head { transform: translateY(-0.9%) rotate(-0.6deg) scale(1.01); }
.voice-portrait[data-idle="content"] .voice-portrait__head { animation: portrait-hum 1.8s ease-in-out 1; }
.voice-portrait[data-idle="yawn"] .voice-portrait__head { animation: portrait-yawn 2s ease-in-out 1; }
.voice-portrait[data-motion="css"][data-idle="drowsy"] .voice-portrait__head { transform: translateY(1.2%) rotate(1.6deg); }

/* Эффекты у головы: значок из листа (фон) или знак шрифта; всплывает и тает, пот стекает, досада пульсирует у виска.
   Сдвиги — в cqw/cqh портрета: проценты transform считались бы от самого значка. */
.voice-portrait__fx {
    position: absolute;
    inset: 0;
    z-index: 8;
    pointer-events: none;
}

/* Листы значков нарисованы в палитре персонажа: тёмный «?» пропадал на тёмном фоне, «!» — на шляпе, рыжая капля —
   на рыжих волосах. Светлый контур-наклейка читается на тёмном (три сдвига без размытия под 120° друг к другу; каждый
   следующий применяется к уже обведённому — суммы дают шесть точек шестиугольника, сплошную обводку), мягкая
   тень снизу — на светлом и на самом персонаже. Фильтр стоит на месте, анимируется только transform — композитор его
   не перерисовывает. */
.voice-portrait__fx-icon {
    position: absolute;
    translate: -50% -50%;
    background-repeat: no-repeat;
    opacity: 0;
    filter:
        drop-shadow(0.45cqw 0 0 rgb(255 255 255 / 0.92))
        drop-shadow(-0.225cqw 0.39cqw 0 rgb(255 255 255 / 0.92))
        drop-shadow(-0.225cqw -0.39cqw 0 rgb(255 255 255 / 0.92))
        drop-shadow(0 0.5cqw 0.9cqw rgb(0 0 0 / 0.4));
    animation: portrait-fx-rise var(--fx-ms, 1.6s) ease-out var(--fx-delay, 0s) 1 both;
}

.voice-portrait__fx-icon[data-motion="pop"] { animation-name: portrait-fx-pop; }
.voice-portrait__fx-icon[data-motion="drip"] { animation-name: portrait-fx-drip; }
.voice-portrait__fx-icon[data-motion="pulse"] { animation-name: portrait-fx-pulse; }
/* Значок над головой по центру лица вращается на месте (при «без движения» стоит — общее правило ниже). */
.voice-portrait__fx-icon[data-motion="spin"] {
    animation-name: portrait-fx-spin;
    animation-timing-function: linear;
}

.voice-portrait__fx-icon[data-glyph] {
    aspect-ratio: 1;
    font: 900 8cqw/1 system-ui, sans-serif;
    text-align: center;
    color: rgb(var(--voice-orb-listen));
    text-shadow: 0 0 0.25em rgb(0 0 0 / 0.45);
}

.voice-portrait__fx-icon[data-glyph="heart"],
.voice-portrait__fx-icon[data-glyph="anger"],
.voice-portrait__fx-icon[data-glyph="exclaim"] { color: #ff5a6e; }
.voice-portrait__fx-icon[data-glyph="sparkles"] { color: #ffd34d; }
.voice-portrait__fx-icon[data-glyph="notes"] { color: #a78bfa; }
.voice-portrait__fx-icon[data-glyph="star"],
.voice-portrait__fx-icon[data-glyph="bolt"] { color: #ffd34d; }
.voice-portrait__fx-icon[data-glyph="flower"] { color: #ff9bd1; }
.voice-portrait__fx-icon[data-glyph="fire"] { color: #ff7a3d; }

.voice-mode[data-agent-state="done"] .voice-portrait__body {
    animation: portrait-hop 0.7s cubic-bezier(0.3, 0.7, 0.3, 1) 2;
}

@keyframes portrait-breathe {
    0%, 100% { transform: scale(1, 1); }
    45% { transform: scale(1.006, 1.014); }
}

@keyframes portrait-sway {
    0%, 100% { transform: rotate(0deg) translateX(0); }
    30% { transform: rotate(0.9deg) translateX(0.3%); }
    70% { transform: rotate(-0.8deg) translateX(-0.3%); }
}

@keyframes portrait-listen {
    0%, 100% { transform: rotate(0deg) translateY(0); }
    18% { transform: rotate(0.6deg) translateY(1.1%); }
    32% { transform: rotate(0deg) translateY(0); }
    50% { transform: rotate(-0.4deg) translateY(0.8%); }
    64% { transform: rotate(0deg) translateY(0); }
}

@keyframes portrait-ponder {
    0%, 100% { transform: rotate(0deg); }
    40% { transform: rotate(1.6deg) translateY(-0.4%); }
    75% { transform: rotate(-0.6deg); }
}

@keyframes portrait-wave {
    0% { opacity: 0; transform: translateY(var(--hand-rise)) rotate(14deg); }
    16% { opacity: 1; transform: translateY(0) rotate(0deg); }
    32% { transform: translateY(0) rotate(-14deg); }
    48% { transform: translateY(0) rotate(10deg); }
    64% { transform: translateY(0) rotate(-12deg); }
    80% { opacity: 1; transform: translateY(0) rotate(4deg); }
    100% { opacity: 0; transform: translateY(var(--hand-rise)) rotate(14deg); }
}

@keyframes portrait-sleep-breathe {
    0%, 100% { transform: scale(1, 1); }
    45% { transform: scale(1.01, 1.026); }
}

@keyframes portrait-wake {
    0% { transform: translateY(1%) rotate(1.4deg); }
    55% { transform: translateY(-1.4%) rotate(-0.8deg) scale(1.012); }
    100% { transform: none; }
}

@keyframes portrait-zzz {
    0% { opacity: 0; transform: translate(0, 0) scale(0.6); }
    20% { opacity: 1; }
    100% { opacity: 0; transform: translate(60%, -160%) scale(1.15); }
}

@keyframes portrait-hum {
    0%, 100% { transform: rotate(0deg); }
    30% { transform: rotate(2.6deg) translateY(-0.3%); }
    65% { transform: rotate(-1.8deg) translateY(-0.2%); }
}

@keyframes portrait-yawn {
    0%, 100% { transform: none; }
    35%, 70% { transform: translateY(-1.2%) rotate(-1.4deg) scale(1.012); }
}

@keyframes portrait-fx-rise {
    0% { opacity: 0; transform: translate(0, 0) scale(0.6) rotate(var(--fx-rot, 0deg)); }
    15% { opacity: 1; transform: translate(calc(var(--fx-dx, 0cqw) * 0.2), -2cqh) scale(1.08) rotate(0deg); }
    70% { opacity: 1; }
    100% { opacity: 0; transform: translate(var(--fx-dx, 0cqw), -12cqh) scale(1) rotate(calc(var(--fx-rot, 0deg) * -1)); }
}

@keyframes portrait-fx-pop {
    0% { opacity: 0; transform: scale(0.4); }
    18% { opacity: 1; transform: translateY(-1.5cqh) scale(1.15); }
    30%, 75% { opacity: 1; transform: translateY(-2cqh) scale(1); }
    100% { opacity: 0; transform: translateY(-4cqh) scale(0.95); }
}

@keyframes portrait-fx-drip {
    0% { opacity: 0; transform: scale(0.6); }
    15% { opacity: 1; transform: scale(1); }
    70% { opacity: 1; }
    100% { opacity: 0; transform: translateY(5cqh) scale(0.9); }
}

@keyframes portrait-fx-pulse {
    0% { opacity: 0; transform: scale(0.5); }
    15% { opacity: 1; transform: scale(1.15); }
    30% { transform: scale(0.95); }
    45% { transform: scale(1.1); }
    60%, 80% { opacity: 1; transform: scale(1); }
    100% { opacity: 0; transform: scale(0.9); }
}

@keyframes portrait-fx-spin {
    0% { opacity: 0; transform: scale(0.5) rotate(0deg); }
    12% { opacity: 1; transform: scale(1.05) rotate(90deg); }
    82% { opacity: 1; transform: scale(1) rotate(560deg); }
    100% { opacity: 0; transform: scale(0.9) rotate(680deg); }
}

@keyframes portrait-glint {
    from { background-position: -43% 0; }
    to { background-position: 143% 0; }
}

@keyframes portrait-strip-wait {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.4; }
}

@keyframes portrait-hop {
    0%, 100% { transform: translateY(0) scale(1, 1); }
    18% { transform: translateY(1%) scale(1.02, 0.97); }
    50% { transform: translateY(-5%) scale(0.98, 1.03); }
    80% { transform: translateY(0.6%) scale(1.01, 0.99); }
}

@media (prefers-reduced-motion: reduce) {
    .voice-portrait__zzz span {
        animation: none;
        opacity: 0.8;
    }

    .voice-portrait__zzz span:nth-child(2) { transform: translate(40%, -110%); }
    .voice-portrait__zzz span:nth-child(3) { transform: translate(80%, -220%); }

    /* Эффект стоит на месте и исчезает по таймеру. */
    .voice-portrait__fx-icon {
        animation: none;
        opacity: 1;
    }

    .voice-portrait :is(.voice-portrait__look, .voice-portrait__pose, .voice-portrait__mood, .voice-portrait__speak, .voice-portrait__head, .voice-portrait__iris) {
        transition: none;
    }
}

/* Границы те же, что у оболочки (hermes-shell.css): колонка складывается на 900 px, шторка — на 600 px. */
@media (max-width: 900px) {
    .hermes-dashboard .voice-mode__orb--portrait {
        width: clamp(11rem, 34vh, 17rem);
    }
}

@media (max-width: 600px) {
    .hermes-dashboard .voice-mode--sheet-collapsed .voice-mode__orb--portrait {
        width: clamp(13rem, 44vh, 20rem);
    }

    .hermes-dashboard .voice-mode--sheet-half .voice-mode__orb--portrait {
        width: 9.5rem;
    }

    .hermes-dashboard .voice-mode--sheet-full .voice-mode__orb--portrait {
        width: 3rem;
        margin-top: calc(3rem * 0.075);
    }
}
