/* ═══════════════════════════════════════════════════════════════════════════
   Системне повідомлення (тост) — DS §9.22
   ═══════════════════════════════════════════════════════════════════════════

   МЕЖА ЗАСТОСУВАННЯ — головне в цьому компоненті. Тост підтверджує, що дія
   завершилась, і нічого більше. Якщо повідомлення зникне непоміченим,
   користувач нічого не втрачає: результат уже видно в самому інтерфейсі
   (адреса з'явилась у списку, форма закрилась). Усе, що вимагає реакції,
   читання чи повернення, тостом бути НЕ може — його неможливо ані закрити,
   ані відкрити повторно.

   | Ситуація                              | Компонент                       |
   |---------------------------------------|---------------------------------|
   | Дія вдалась, результат уже на екрані  | тост                            |
   | Помилка валідації поля                | інлайн під полем                |
   | Дія не вдалась, є що виправити тут    | .inline-error (нижче), не тост  |
   | Незворотна дія (видалення)            | модалка підтвердження ДО дії    |
   | Постійний стан (порожній кошик)       | empty state / інлайн-банер      |
   | Замовлення оформлено                  | окремий екран підтвердження     |
   | Товар додано в кошик                  | бейдж кількості в хедері §9.12   |

   Перевірка одним питанням: чи є на екрані елемент, який користувач натисне
   повторно, щоб виправити ситуацію? Є — текст іде інлайном поруч із ним.
   Немає — тоді тост.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Кліп-контейнер. Смуга лежить у ньому з overflow:hidden заввишки рівно в неї,
   тому translateY(-100%) ховає її ЗА межу контейнера і візуально вона виїжджає
   з-під хедера. Без цього кліпу під час анімації смуга накривала б хедер —
   вона вище за нього по z-index. */
.toast-clip {
	position: fixed;
	left: 0;
	right: 0;
	/* Fallback без JS. Реальний top рахується при показі:
	   top = Math.max(0, header.getBoundingClientRect().bottom), з перерахунком
	   на scroll/resize через rAF. Хедер storefront не sticky (§9.12) — при
	   скролі він їде вгору, тож жорсткий top лишив би смугу висіти посеред
	   контенту. */
	top: var(--header-h);
	overflow: hidden;
	z-index: var(--z-toast);
	/* Контейнер прозорий для миші — інакше він ловив би кліки по всій ширині
	   навіть коли тоста немає. Сама смуга кліки приймає (пауза на hover). */
	pointer-events: none;
}
.toast-clip[hidden] { display: none; }

.toast {
	pointer-events: auto;
	box-sizing: border-box;
	/* Анатомія — два елементи, більше нічого: іконка 24×24 і текст.
	   Текст притиснутий до іконки, вільне місце лишається праворуч.
	   Центрування ЗАБОРОНЕНЕ: на широкому екрані центрований текст
	   відривається від іконки. */
	display: flex;
	align-items: center;
	gap: var(--space-md);            /* 12px */
	min-height: var(--toast-min-h);  /* 52px, ≥670px → 56px */
	/* Висота фактична — авто: росте під текст, ніколи не обрізає. */
	/* Горизонтальний padding — ПРАВИЛО, а не константа: дорівнює --page-gutter
	   на поточній ширині. Смуга лишається full-bleed — розтягується фон, а не
	   текст. Якір ОДИН — поле контенту; згадка про лого хедера була помилкою
	   формулювання §9.22 і знята 24.08.2026 разом із відкритим пунктом §9.0a(3):
	   хедер стоїть на 80px, контент на ~42px, тож двох якорів бути не може. */
	padding: 14px var(--page-gutter);
	font-family: var(--font-body);
	font-weight: 700;
	font-size: 16px;
	line-height: 1.4;
	/* Радіус 0 — смуга не має бічних меж, заокруглювати нічого; заокруглені
	   кути читались би як «картка, що не долізла до країв». Драбина
	   --radius-* (§5) описує поверхні з видимими межами, тост у неї не
	   входить і нового щабля не потребує. */
	border-radius: 0;
	box-shadow: var(--shadow-2);
	/* Рух — §9.22. Поява 180ms, зникнення 140ms. */
	transform: translateY(-100%);
	transition: transform var(--motion-exit);
	will-change: transform;
}
.toast.is-open {
	transform: translateY(0);
	transition: transform var(--motion-enter);
}

.toast__icon {
	flex: 0 0 auto;
	width: var(--icon-md);
	height: var(--icon-md);
	display: block;
	/* Колір через currentColor — окремий stroke/fill не задається (те саме
	   правило, що в §9.18). Виняток лише в danger-варіанті нижче. */
}
.toast__text {
	flex: 1 1 auto;
	min-width: 0;
}

/* ── Кольори й семантика — §9.22 ─────────────────────────────────────────────
   Три варіанти з чотирьох — суцільна заливка var(--status-*), текст та іконка
   --text-on-dark (#ffffff, ЧИСТИЙ білий, не теплий --text-inverted #fffaf2:
   на кольоровій заливці тепла домішка читається як забруднений білий).

   Це задокументований ВИНЯТОК із правила статус-піла (§15): піл фарбує текст
   у чорний, лишаючи колір тільки в -soft і -ink. Тост цим не керується, і
   причина в різних задачах: піл сидить у щільному списку серед десятків інших
   і не повинен кричати; тост існує 4 секунди й мусить бути помічений
   периферійним зором. -soft і -ink у тості не використовуються взагалі. */
.toast--success { background: var(--status-success); color: var(--text-on-dark); }
.toast--info    { background: var(--status-info);    color: var(--text-on-dark); }
.toast--warning { background: var(--status-warning); color: var(--text-on-dark); }

/* danger влаштований ІНАКШЕ — єдиний варіант без статусної заливки.
   Причини дві, обидві конкретні:
   1. Перцептивна вага. Зелена, синьо-сіра й вохриста площини на всю ширину
      читаються як тло; червона читається як тривога. Компонент за визначенням
      обмежений фоновими збоями, яким ніде дати інлайн-текст — рівень голосу
      має відповідати рівню події.
   2. Колізія з брендом Oponytomy, і вона важливіша. --status-danger (#b3261e)
      і --accent-primary OP (#B41522) — практично один відтінок. Суцільна
      червона смуга на всю ширину читалась би не як помилка, а як Primary CTA
      завширшки з екран, і прямо порушувала б §2.2. Для Inforeifen колізії
      немає (акцент teal), але варіант лишається спільним: у системі геометрія
      й будова компонента ніде не розходяться між брендами, розходяться тільки
      значення токенів. */
.toast--danger {
	background: var(--bg-notice);
	color: var(--text-inverted);
}
/* Статусний колір зведений до однієї іконки. Чистий #b3261e на #323744 дає
   1.8:1 і фізично зникає — тому окремий освітлений токен. */
.toast--danger .toast__icon { color: var(--status-danger-on-dark); }

@media (min-width: 670px) {
	.toast {
		min-height: 56px;
		padding: var(--space-lg) var(--page-gutter);
		/* 18px на ≥670px збігається з --btn-cta-font-size — та сама вага
		   голосу, що в головній дії. 16px — нижня межа для тоста: §7 задає
		   загальний мінімум 14px, але це підлога службового шару, а тост —
		   речення, а не мітка. */
		font-size: 18px;
	}
}
/* prefers-reduced-motion — ВИНЯТОК із §12.3. §12.3 не поширює обмеження на
   короткі 120–150ms transition, і для акордеона чи тогла це правильно. Тут
   інший випадок: рухається великий об'єкт у верхній частині поля зору — саме
   та геометрія, що провокує вестибулярний дискомфорт. Під reduce зсув
   вимикається ПОВНІСТЮ, лишається opacity. Тривалість показу не змінюється. */
@media (prefers-reduced-motion: reduce) {
	.toast,
	.toast.is-open {
		transform: none;
		transition: opacity var(--motion-fast);
	}
	.toast { opacity: 0; }
	.toast.is-open { opacity: 1; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Суміжний компонент — ЦЕ НЕ ТОСТ: інлайн-помилка дії (§9.22)
   ═══════════════════════════════════════════════════════════════════════════
   Найчастіша помилка застосування тоста — показати ним збій, який мав бути
   інлайном. Користувач заповнює адресу в bottom-sheet, тисне «Zapisz adres»,
   збереження падає. Кнопка, яку тиснути повторно, стоїть унизу шита, а тост
   з'явився б угорі екрана: на мобайлі це ≈630px відстані, поза полем зору
   людини, яка дивиться на кнопку. Через 8 секунд текст зникне, і повернути
   його неможливо — а помилка нікуди не поділась.

   Не плутати з валідацією поля: та живе під конкретним інпутом і з'являється
   ДО натискання кнопки. Ця — над CTA, і зникає не за таймером, а коли
   користувач повторює дію.

   Фону й рамки немає навмисно: -soft/-ink у danger досі TBD (§2.5), а текст
   на білому дає 6.6:1 і без підкладки. */
.inline-error {
	display: flex;
	align-items: flex-start;        /* при переносі на два рядки іконка
	                                   лишається біля першого */
	gap: var(--space-sm);           /* 8px */
	margin-bottom: var(--space-md); /* 12px до кнопки */
	font-family: var(--font-body);
	font-weight: 400;
	font-size: 16px;
	line-height: 1.4;
	color: var(--status-danger);
}
.inline-error[hidden] { display: none; }
.inline-error__icon {
	flex: 0 0 auto;
	width: 20px;   /* §9.22 задає саме 20px для інлайн-помилки — між --icon-sm і --icon-md */
	height: 20px;
	display: block;
}

/* Живі регіони aria-live лежать у DOM постійно й порожні. Ховати їх через
   display:none НЕ можна — скринридер тоді їх не читає. Класичний clip-рецепт. */
.visually-hidden {
	position: absolute !important;
	width: 1px; height: 1px;
	margin: -1px; padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}


/* ═══════════════════════════════════════════════════════════════════════════
   Блокувальний оверлей — «сторінка зайнята» (shared/page_busy.js)
   ═══════════════════════════════════════════════════════════════════════════
   Показується на час довгої дії, яка завершується навігацією — сьогодні це
   оформлення замовлення (/b/checkout/submit, повний POST-редірект форми).
   Доти сторінка лишалась цілком інтерактивною: кнопка сабміту блокувалась, а
   решта екрана ні.

   Візуал свідомо той самий, що в лоадера результатів пошуку: та сама крива
   (shared/curve_loader.js), той самий розмір кадру 96px, той самий колір
   --accent-primary, тож на INFR і OTM він брендується сам. ДС стану
   завантаження не описує — тому орієнтир тут внутрішня консистентність,
   а не окремий §. */
.page-busy {
	position: fixed;
	inset: 0;
	z-index: var(--z-toast);   /* під тостом нічого немає — блокування має бути зверху */
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--bg-scrim, rgba(40, 36, 36, .12));
	/* Розмиття підкреслює «сторінка недоступна», не ховаючи контекст повністю. */
	backdrop-filter: blur(2px);
	color: var(--accent-primary);
	animation: page-busy-in var(--motion-fast) both;
}
.page-busy[hidden] { display: none; }

.page-busy__frame { width: 96px; height: 96px; }
.page-busy__frame svg { width: 100%; height: 100%; overflow: visible; }

/* Скрол блокується на <html>, а не на <body>: body тепер flex-контейнер
   sticky-футера, і overflow на ньому дав би стрибок розкладки. */
.is-page-busy { overflow: hidden; }

@keyframes page-busy-in { from { opacity: 0; } to { opacity: 1; } }

/* §12.3 — під reduce лишається лише поява, без інших рухів оверлея.
   Саму криву не глушимо: це індикатор процесу, а не декоративний рух, і без
   нього стан «щось відбувається» втрачає єдиного носія. */
@media (prefers-reduced-motion: reduce) {
	.page-busy { animation-duration: 1ms; }
}
