/*
 * Forms modifiers — additive on top of existing .input-label + .form-control system.
 *
 * Главная архитектурная правка (2026-05-19):
 *   4 лейбы (TL/TR/BL/BR) embedded в рамку input'а.
 *   Title — top-left, top-right — опц. подсказка, bottom-left — hint/error, bottom-right — счётчик.
 *   Все на overlapping границе, фон --input-label-surface (бесшовно с рамкой).
 *
 * Merge target: после Block 2 — объединить с forms.css.
 */

/* ============ Position presets для 4 лейб ============ */

/* Глобально — текст лейб и обёрток не выделяется при клике (раздражает).
   Значения inputs/textarea остаются selectable. */
.input-label {
    user-select: none;
    -webkit-user-select: none;
}

.input-label .form-control,
.input-label .form-input,
.input-label .form-select,
.input-label textarea {
    user-select: text;
    -webkit-user-select: text;
}

/* All 4 corner labels — same baseline (tight line-height for precise positioning on border line) */
.input-label-text--tl,
.input-label-text--tr,
.input-label-text--bl,
.input-label-text--br {
    position: absolute;
    background: var(--input-label-surface);
    padding: 0 0.5rem;
    font-size: 0.78rem;
    font-weight: 500;
    line-height: 1;
    color: var(--color-text-secondary);
    pointer-events: none;
    z-index: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* WebKit (WKWebView) computed shrink-to-fit width as ~0 for these absolutely
       positioned, right-anchored, nowrap labels — collapsing them to just padding
       (~16px) in the .app. `width: max-content` forces the used width to the content
       width (breaks the circular shrink-to-fit that WebKit mis-resolved), so labels
       show their full text. `max-width: calc(100% - 1.5rem)` then caps them to their
       own field's box so a right-anchored label can't overflow leftward across the
       grid gap into the adjacent column (e.g. the LLM-model coef bleeding onto the
       reserve block). The % now resolves correctly because the width is no longer
       shrink-to-fit. Cap kept non-collapsing: it only clamps, width drives sizing. */
    width: max-content;
    max-width: calc(100% - 1.5rem);
    /* Закругление — чтобы при отличающемся фоне (surface=panel)
       лейбы не торчали острыми прямоугольниками поверх скруглённой рамки. */
    border-radius: var(--radius-sm);
}

.input-label-text--tl {
    top: 0;
    left: 0.75rem;
    right: auto;
    bottom: auto;
    transform: translateY(calc(-50% - 1px));
}

.input-label-text--tr {
    top: 0;
    right: 0.75rem;
    left: auto;
    bottom: auto;
    transform: translateY(calc(-50% - 1px));
    color: var(--color-text-tertiary);
    font-weight: 400;
    font-variant-numeric: tabular-nums;
}

.input-label-text--bl {
    bottom: 0;
    left: 0.75rem;
    right: auto;
    top: auto;
    transform: translateY(calc(50% - 1px));
}

.input-label-text--br {
    bottom: 0;
    right: 0.75rem;
    left: auto;
    top: auto;
    transform: translateY(calc(50% - 1px));
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
}

/* ============ WKWebView (.app) corner-label collapse fix ============
 *
 * SYMPTOM (desktop .app only, Chrome fine): the absolutely-positioned corner
 * labels (coef «×4» top-right, provider bottom-right on the model selects in the
 * VideoLab / project-settings modals) collapse to ~16px (just the 0.5rem padding)
 * and pile up at the modal's left edge.
 *
 * ROOT CAUSE (box-model reasoning): the base rule above sets `overflow:hidden`
 * (needed for `text-overflow:ellipsis`). In WebKit the intrinsic / shrink-to-fit
 * width of an absolutely-positioned box whose overflow is clipped resolves to ~0,
 * so `width:max-content` never gets content to measure and the box collapses to
 * padding. That already-shipped `width:max-content; max-width:calc(100% - 1.5rem)`
 * fix therefore had no effect in the .app. Removing the overflow clip lets the
 * content define the width again. We also re-assert the per-corner offsets so a
 * zero-width right-anchored box can't fall back to a left static position.
 *
 * Scoped to `html.tb-desktop` so the web build keeps its ellipsis truncation.
 * NOTE: these labels are short (coef / provider), so dropping ellipsis is cosmetic
 * only. See playbook §8 / §9. */
html.tb-desktop .input-label-text--tl,
html.tb-desktop .input-label-text--tr,
html.tb-desktop .input-label-text--bl,
html.tb-desktop .input-label-text--br {
    overflow: visible;
    text-overflow: clip;
    width: max-content;
    max-width: none;
}

html.tb-desktop .input-label-text--tr {
    left: auto;
    right: 0.75rem;
}

html.tb-desktop .input-label-text--br {
    left: auto;
    right: 0.75rem;
}

html.tb-desktop .input-label-text--tl,
html.tb-desktop .input-label-text--bl {
    right: auto;
    left: 0.75rem;
}

/* ============ WKWebView (.app) corner-label WRONG-FIELD fix ============
 *
 * SYMPTOM (desktop .app only, Chrome fine): a corner label that belongs to a
 * model SELECT (e.g. the coef «x0.5» on «LLM модель») renders over the NEIGHBOUR
 * field («Резервные LLM модели») instead of its own field. This is distinct from
 * the width-collapse fix above (labels no longer collapse; they land wrong).
 *
 * ROOT CAUSE (containing block, NOT DOM insertion): select.ts appends the corner
 * label to `trigger.closest('.input-label')`, which correctly resolves to the
 * per-field wrapper the select macro renders as
 * `<label class="input-label input-label--select">` (select.html). But a <label>
 * is `display:inline` by default and nothing blockifies it, so `position:relative`
 * on that wrapper forms only a DEGENERATE (line-box) containing block. Blink still
 * anchors the absolutely-positioned corner label to it; WebKit does NOT - the
 * label escapes to the nearest BLOCK-level positioned ancestor, which spans the
 * settings-grid row, so it lands on the adjacent field. (The block
 * `<div class="input-label range-input-label">` wrappers - e.g. the «3» range
 * badge - work in both engines precisely because a <div> is already block and
 * forms a real containing block.)
 *
 * FIX: on desktop make the select wrapper a real block box and pin it as a hard
 * containing block. `display:block` is load-bearing - it both lets
 * `position:relative` form a proper containing block AND makes `contain:layout`
 * effective (containment is a no-op on a non-atomic inline box). The select
 * popover is portaled to document.body (position:fixed, outside this wrapper), so
 * `contain:layout` does not trap it; and containment does not clip, so labels that
 * straddle the border stay visible. Scoped `html.tb-desktop` so the web build
 * keeps the macro's default inline label. See playbook §8/§9. */
html.tb-desktop .input-label--select {
    display: block;
    position: relative;
    contain: layout;
}

/* Counter bullet separator — CSS-кружочек вместо символа `•`,
   чтобы не плыл по baseline при разном font-size. */
.input-counter-sep {
    display: inline-block;
    width: 4px;
    height: 4px;
    margin: 0 0.5em;
    border-radius: 50%;
    background: currentColor;
    vertical-align: 0.2em;
    opacity: 0.6;
    /* Скрываем bullet-символ внутри, оставляя только div-точку */
    overflow: hidden;
    text-indent: -9999px;
    line-height: 4px;
}

/* ============ Цвет всех лейб следует state (universal — любая лейба в state-цвете) ============ */

.input-label.is-error .input-label-text--tl,
.input-label.is-error .input-label-text--tr,
.input-label.is-error .input-label-text--bl,
.input-label.is-error .input-label-text--br {
    color: var(--color-error);
}

.input-label.is-warning .input-label-text--tl,
.input-label.is-warning .input-label-text--tr,
.input-label.is-warning .input-label-text--bl,
.input-label.is-warning .input-label-text--br {
    color: var(--color-warning);
}

.input-label.is-success .input-label-text--tl,
.input-label.is-success .input-label-text--tr,
.input-label.is-success .input-label-text--bl,
.input-label.is-success .input-label-text--br {
    color: var(--color-success);
}

/* ============ Icon inside input ============ */
/* Иконка внутри рамки поля (не отдельной кнопкой). Слева или справа.
   Параметры макроса: icon_left='icon-search' и/или icon_right='icon-eye'. */

.input-label-icon {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 18px;
    height: 18px;
    color: var(--color-text-secondary);
    pointer-events: none;
    fill: currentColor;
    z-index: 1;
}

.input-label-icon--left {
    left: 0.85rem;
}

.input-label-icon--right {
    right: 0.85rem;
}

/* Сдвигаем текст input'а чтобы не наезжал на иконку */
.input-label--with-icon-left .form-control,
.input-label--with-icon-left .form-select,
.input-label--with-icon-left .form-input {
    padding-left: 2.6rem;
}

.input-label--with-icon-right .form-control,
.input-label--with-icon-right .form-select,
.input-label--with-icon-right .form-input {
    padding-right: 2.6rem;
}

/* SM-вариант — иконки меньше и ближе к краю */
.input-label--sm .input-label-icon {
    width: 14px;
    height: 14px;
}

.input-label--sm .input-label-icon--left {
    left: 0.7rem;
}

.input-label--sm .input-label-icon--right {
    right: 0.7rem;
}

.input-label--sm.input-label--with-icon-left .form-control {
    padding-left: 2.1rem;
}

.input-label--sm.input-label--with-icon-right .form-control {
    padding-right: 2.1rem;
}

/* State-цвет на иконке */
.input-label.is-error .input-label-icon { color: var(--color-error); }
.input-label.is-warning .input-label-icon { color: var(--color-warning); }
.input-label.is-success .input-label-icon { color: var(--color-success); }

/* Hover/focus — иконка тоже подсвечивается */
.input-label:not(.is-disabled):not(.is-readonly):hover .input-label-icon,
.input-label:not(.is-disabled):not(.is-readonly):focus-within .input-label-icon {
    color: var(--color-text-primary);
}

.input-label.is-error:not(.is-disabled):not(.is-readonly):hover .input-label-icon,
.input-label.is-error:not(.is-disabled):not(.is-readonly):focus-within .input-label-icon {
    color: var(--color-error);
}

.input-label.is-warning:not(.is-disabled):not(.is-readonly):hover .input-label-icon,
.input-label.is-warning:not(.is-disabled):not(.is-readonly):focus-within .input-label-icon {
    color: var(--color-warning);
}

.input-label.is-success:not(.is-disabled):not(.is-readonly):hover .input-label-icon,
.input-label.is-success:not(.is-disabled):not(.is-readonly):focus-within .input-label-icon {
    color: var(--color-success);
}

/* ============ Textarea variant ============ */
/* Textarea наследует всё от .input-label/.form-control. Отличия:
   - переменная высота через rows attr
   - resize: none — никаких ручек растяжки (раздражает пользователя)
   - padding верх/низ адаптирован под лейбы */

.input-label--textarea .form-control {
    height: auto;
    min-height: 144px;
    padding: 1rem;
    resize: none;
    line-height: 1.4;
}

.input-label--textarea.input-label--sm .form-control {
    min-height: 96px;
    padding: 0.65rem 0.85rem;
}

/* ============ Size: SM compact (36px) ============ */

.input-label--sm .form-control,
.input-label--sm .form-select,
.input-label--sm .form-input {
    height: 36px;
    padding: 0.4rem 0.85rem;
    font-size: 0.85rem;
}

.input-label--sm .input-label-text--tl,
.input-label--sm .input-label-text--tr,
.input-label--sm .input-label-text--bl,
.input-label--sm .input-label-text--br {
    font-size: 0.7rem;
}

/* ============ Success state добавили (пара к existing is-error / is-warning) ============ */

.form-control.is-success,
.form-select.is-success,
.form-input.is-success {
    border-color: var(--color-success);
}

.input-label:has(.form-control.is-success) .input-label-text--tl,
.input-label:has(.form-select.is-success) .input-label-text--tl,
.input-label.is-success .input-label-text--tl {
    color: var(--color-success);
}

/* ============ Базовые правки (фиксят баги ревью) ============ */

/* Bug 1: первый input в строке съезжал вниз из-за :first-child margin-top.
   Убираем — теперь все одной высоты. */
.input-label:first-child {
    margin-top: 0;
}

/* ============ Date / Time native pickers ============
   Native <input type="date|time|datetime-local|month|week">. Stylим только сам field
   (popup-календарь — браузерное окно, customize нельзя кроме color-scheme).
   `color-scheme: dark` подсказывает браузеру использовать тёмную палитру popup. */
.form-control[type="date"],
.form-control[type="time"],
.form-control[type="datetime-local"],
.form-control[type="month"],
.form-control[type="week"] {
    color-scheme: dark;
    font-variant-numeric: tabular-nums;
}

/* WebKit calendar icon — окрашиваем под secondary text */
.form-control[type="date"]::-webkit-calendar-picker-indicator,
.form-control[type="time"]::-webkit-calendar-picker-indicator,
.form-control[type="datetime-local"]::-webkit-calendar-picker-indicator,
.form-control[type="month"]::-webkit-calendar-picker-indicator,
.form-control[type="week"]::-webkit-calendar-picker-indicator {
    cursor: pointer;
    /* SVG в браузерах обычно чёрный — инвертируем, чтобы был светлый на тёмном фоне */
    filter: invert(0.7);
    opacity: 0.6;
    transition: opacity var(--transition-fast);
}

.form-control[type="date"]::-webkit-calendar-picker-indicator:hover,
.form-control[type="time"]::-webkit-calendar-picker-indicator:hover,
.form-control[type="datetime-local"]::-webkit-calendar-picker-indicator:hover {
    opacity: 1;
    filter: invert(0.7) sepia(1) saturate(5) hue-rotate(220deg);
}

/* WebKit datetime segments — placeholder-цвет когда нет значения */
.form-control[type="date"]::-webkit-datetime-edit,
.form-control[type="time"]::-webkit-datetime-edit,
.form-control[type="datetime-local"]::-webkit-datetime-edit {
    color: var(--color-text-primary);
}

.form-control[type="date"]:invalid::-webkit-datetime-edit,
.form-control[type="time"]:invalid::-webkit-datetime-edit,
.form-control[type="datetime-local"]:invalid::-webkit-datetime-edit {
    color: var(--color-text-tertiary);
}

/* Bug 2: number-input стрелочки убираем для согласованности с остальными */
.form-control[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}
.form-control[type="number"]::-webkit-outer-spin-button,
.form-control[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    appearance: none;
    margin: 0;
}

/* Bug 3: hover должен реагировать как focus — border-color = brand */
.input-label:not(.is-disabled):not(.is-readonly):hover .form-control:not(.is-error):not(.is-warning):not(.is-success):not(:focus),
.input-label:not(.is-disabled):not(.is-readonly):hover .form-select:not(.is-error):not(.is-warning):not(.is-success):not(:focus),
.input-label:not(.is-disabled):not(.is-readonly):hover .form-input:not(.is-error):not(.is-warning):not(.is-success):not(:focus) {
    border-color: var(--color-border-focus);
}

/* Bug 4: readonly — не реагирует ни на hover ни на focus */
.input-label.is-readonly .form-control,
.input-label.is-readonly .form-select,
.input-label.is-readonly .form-input,
.form-control[readonly],
.form-select[readonly],
.form-input[readonly] {
    cursor: default;
    background: color-mix(in srgb, var(--input-label-surface) 70%, transparent);
}

.input-label.is-readonly .form-control:hover,
.input-label.is-readonly .form-control:focus,
.input-label.is-readonly:hover .form-control,
.form-control[readonly]:hover,
.form-control[readonly]:focus,
.form-input[readonly]:hover,
.form-input[readonly]:focus {
    border-color: var(--color-border);
    outline: none;
    box-shadow: none;
}

/* ============ Состояния hover/focus для labels тоже должны реагировать ============ */

.input-label:not(.is-disabled):not(.is-readonly):hover .input-label-text--tl,
.input-label:not(.is-disabled):not(.is-readonly):focus-within .input-label-text--tl {
    color: var(--color-text-primary);
}

.input-label.is-error:not(.is-disabled):not(.is-readonly):hover .input-label-text--tl,
.input-label.is-error:not(.is-disabled):not(.is-readonly):focus-within .input-label-text--tl {
    color: var(--color-error);
}

.input-label.is-warning:not(.is-disabled):not(.is-readonly):hover .input-label-text--tl,
.input-label.is-warning:not(.is-disabled):not(.is-readonly):focus-within .input-label-text--tl {
    color: var(--color-warning);
}

.input-label.is-success:not(.is-disabled):not(.is-readonly):hover .input-label-text--tl,
.input-label.is-success:not(.is-disabled):not(.is-readonly):focus-within .input-label-text--tl {
    color: var(--color-success);
}

/* ============ Disabled — тоже не реагирует ============ */

.input-label.is-disabled,
.form-control:disabled,
.form-select:disabled,
.form-input:disabled {
    cursor: not-allowed;
    opacity: 0.55;
}

.input-label.is-disabled:hover .form-control,
.form-control:disabled:hover {
    border-color: var(--color-border);
}

/* ARCHITECTURE NOTE — без !important.
   Специфичность модификаторов выше базовой `.input-label-text` за счёт `.input-label`-обёртки в селекторах ниже. */

.input-label .input-label-text--tl,
.input-label .input-label-text--tr,
.input-label .input-label-text--bl,
.input-label .input-label-text--br {
    line-height: 1;
    height: auto;
    width: auto;
    margin: 0;
}

.input-label .input-label-text--br {
    bottom: 0 !important;
    right: 0.75rem !important;
    left: auto !important;
    top: auto !important;
    transform: translateY(calc(50% - 1px)) !important;
}
