/* =============================================================================
   UIKit — components
   -----------------------------------------------------------------------------
   The look of every named component. Markup lives in app/Views/uikit/, is
   rendered through ui(), and is the only thing allowed to emit these classes.

   Naming: ui-<block>__<element>--<modifier>. The ui- prefix makes membership in
   the system visible at a glance and keeps these rules from colliding with the
   Bootstrap and legacy classes that still run the nine untranslated zones.

   Rules of this file:
   - no raw hex, no raw color: every value comes from tokens.css as var(--ui-*);
   - no !important, ever — a component that needs one is a component built wrong;
   - no theme block: the tokens already carry the dark theme, so the components
     follow it without knowing it exists;
   - where a component sits on a Bootstrap class (.btn), the framework paints it
     through the token bridge and these rules own only shape and spacing.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   Utility

   One, and it earns its place: text that exists for screen readers only. The
   count on a chip is a bare number visually; a listener needs the noun too.
   -------------------------------------------------------------------------- */

.ui-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* -----------------------------------------------------------------------------
   Badge — difficulty

   Replaces four hand-rolled implementations and two conflicting colour triples.
   The label is always present: the colour is a second signal, never the only
   one. The base rule is deliberately the neutral (unknown) state, so bad data
   degrades into something readable instead of something invisible.
   -------------------------------------------------------------------------- */

.ui-badge-difficulty {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-3xs);
    padding: var(--ui-space-3xs) var(--ui-space-xs);
    border-radius: var(--ui-radius-pill);
    background-color: var(--ui-color-surface-raised);
    color: var(--ui-color-text-muted);
    font-size: var(--ui-font-size-xs);
    font-weight: var(--ui-font-weight-semibold);
    line-height: var(--ui-line-height-snug);
    white-space: nowrap;
}

.ui-badge-difficulty--easy {
    background-color: var(--ui-color-difficulty-easy-surface);
    color: var(--ui-color-difficulty-easy-text);
}

.ui-badge-difficulty--medium {
    background-color: var(--ui-color-difficulty-medium-surface);
    color: var(--ui-color-difficulty-medium-text);
}

.ui-badge-difficulty--hard {
    background-color: var(--ui-color-difficulty-hard-surface);
    color: var(--ui-color-difficulty-hard-text);
}

/* Иконка — второй, необязательный сигнал; подпись рядом остаётся всегда. */
.ui-badge-difficulty__icon {
    font-size: 1em;
    line-height: 1;
}

/* -----------------------------------------------------------------------------
   Chip — tag

   Replaces .tag-badge, declared twice with different colours and hovers. A chip
   with an href is a target for a finger, so it carries the 44px minimum; a
   static chip is only a label and stays compact.
   -------------------------------------------------------------------------- */

.ui-chip-tag {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-3xs);
    padding: var(--ui-space-3xs) var(--ui-space-xs);
    border-radius: var(--ui-radius-pill);
    background-color: var(--ui-color-surface-raised);
    color: var(--ui-color-text);
    font-size: var(--ui-font-size-sm);
    line-height: var(--ui-line-height-snug);
    text-decoration: none;
    transition: var(--ui-transition-color);
}

a.ui-chip-tag {
    min-height: 2.75rem; /* 44px — touch target */
    padding-inline: var(--ui-space-sm);
}

a.ui-chip-tag:hover {
    background-color: var(--ui-color-accent-soft);
    color: var(--ui-color-accent-text);
}

/* Заливка под подписью — accent-solid, а не accent: подпись чипа 14px, порог
   контраста для неё 4.5:1, которого фирменная бирюза с белым не даёт. */
.ui-chip-tag--active {
    background-color: var(--ui-color-accent-solid);
    color: var(--ui-color-text-on-accent);
}

a.ui-chip-tag--active:hover {
    background-color: var(--ui-color-accent-solid-hover);
    color: var(--ui-color-text-on-accent);
}

.ui-chip-tag__count {
    font-size: var(--ui-font-size-xs);
    font-weight: var(--ui-font-weight-semibold);
    color: var(--ui-color-text-muted);
}

a.ui-chip-tag:hover .ui-chip-tag__count,
.ui-chip-tag--active .ui-chip-tag__count {
    color: inherit;
}

/* -----------------------------------------------------------------------------
   Button

   One shape for the whole site: a link and a form submit are the same
   affordance to the user and must not look different. Bootstrap's .btn supplies
   colour through the token bridge; everything here is geometry.

   There is deliberately no size prop. A second size is how a vocabulary starts
   drifting, and the 44px minimum would swallow the difference anyway.
   -------------------------------------------------------------------------- */

.ui-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ui-space-2xs);
    min-height: 2.75rem; /* 44px — touch target */
    padding: var(--ui-space-2xs) var(--ui-space-md);
    font-size: var(--ui-font-size-md);
    font-weight: var(--ui-font-weight-medium);
    line-height: var(--ui-line-height-snug);
    text-decoration: none;
}

.ui-button--ghost {
    padding-inline: var(--ui-space-2xs);
}

.ui-button--icon-only {
    min-width: 2.75rem;
    padding-inline: var(--ui-space-2xs);
}

.ui-button--block {
    display: flex;
    width: 100%;
}

.ui-button--disabled {
    cursor: not-allowed;
}

/* A disabled <a> has no `disabled` attribute to obey — it is stopped here and
   announced through aria-disabled in the markup. */
a.ui-button--disabled {
    pointer-events: none;
    opacity: 0.65;
}

.ui-button__icon {
    font-size: 1.125em;
    line-height: 1;
}

/* -----------------------------------------------------------------------------
   Alert

   Status message: what happened, in Russian, with nothing technical leaking
   out. The icon repeats the meaning for people who do not read colour; it is
   aria-hidden because the text already says it.
   -------------------------------------------------------------------------- */

.ui-alert {
    display: flex;
    gap: var(--ui-space-2xs);
    padding: var(--ui-space-sm);
    border: 1px solid rgba(var(--ui-color-info-rgb), 0.35);
    border-radius: var(--ui-radius-md);
    background-color: var(--ui-color-info-surface);
    color: var(--ui-color-info-text);
}

.ui-alert__icon {
    flex: none;
    font-size: 1.125em;
    line-height: var(--ui-line-height-base);
}

.ui-alert__body {
    min-width: 0;
}

.ui-alert__title {
    margin: 0 0 var(--ui-space-3xs);
    font-weight: var(--ui-font-weight-semibold);
    line-height: var(--ui-line-height-snug);
}

.ui-alert__text {
    max-width: var(--ui-measure);
    margin: 0;
}

.ui-alert--success {
    border-color: rgba(var(--ui-color-success-rgb), 0.35);
    background-color: var(--ui-color-success-surface);
    color: var(--ui-color-success-text);
}

.ui-alert--warning {
    border-color: rgba(var(--ui-color-warning-rgb), 0.35);
    background-color: var(--ui-color-warning-surface);
    color: var(--ui-color-warning-text);
}

.ui-alert--danger {
    border-color: rgba(var(--ui-color-danger-rgb), 0.35);
    background-color: var(--ui-color-danger-surface);
    color: var(--ui-color-danger-text);
}

/* -----------------------------------------------------------------------------
   Empty state

   One component for both "ничего не найдено" and "здесь пока пусто": the two
   differ in wording, which the caller passes, not in structure. It teaches the
   next step instead of announcing a void, so the action slot is part of it.
   -------------------------------------------------------------------------- */

.ui-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ui-space-2xs);
    padding: var(--ui-space-xl) var(--ui-space-sm);
    text-align: center;
}

.ui-empty-state__icon {
    font-size: var(--ui-font-size-3xl);
    line-height: 1;
    color: var(--ui-color-text-muted);
}

.ui-empty-state__title {
    margin: 0;
    font-size: var(--ui-font-size-lg);
    font-weight: var(--ui-font-weight-semibold);
    line-height: var(--ui-line-height-tight);
    text-wrap: balance;
}

.ui-empty-state__text {
    max-width: 42ch;
    margin: 0;
    color: var(--ui-color-text-muted);
}

.ui-empty-state__action {
    margin-top: var(--ui-space-2xs);
}

/* -----------------------------------------------------------------------------
   Page header

   The block every screen opens with, so that section titles stop being
   re-invented per page. The title is the page's single h1; actions sit beside
   it on wide screens and wrap under it on narrow ones.
   -------------------------------------------------------------------------- */

.ui-page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--ui-space-sm);
    margin-bottom: var(--ui-space-md);
    padding-bottom: var(--ui-space-sm);
    border-bottom: 1px solid var(--ui-color-border-subtle);
}

.ui-page-header__main {
    min-width: 0;
}

.ui-page-header__title {
    display: flex;
    align-items: center;
    gap: var(--ui-space-2xs);
    margin: 0;
    font-size: var(--ui-font-size-2xl);
}

.ui-page-header__icon {
    flex: none;
    font-size: 0.9em;
    color: var(--ui-color-accent);
}

.ui-page-header__subtitle {
    max-width: var(--ui-measure);
    margin: var(--ui-space-3xs) 0 0;
    color: var(--ui-color-text-muted);
}

.ui-page-header__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ui-space-2xs);
}

/* 40px is a page title on a desktop and a shout on a phone. */
@media (min-width: 768px) {
    .ui-page-header__title {
        font-size: var(--ui-font-size-3xl);
    }
}

/* -----------------------------------------------------------------------------
   Card — question

   Settles a three-way argument: the same card lifted by -5px with shadow-lg,
   by -2px with a 12px shadow, and by -5px with a 25px one, depending on which
   file won the cascade. It lifts by 2px now, everywhere. A card in a list is
   scanned, not read, so the title clamps; the standalone card does not clamp,
   because there the question IS the page.
   -------------------------------------------------------------------------- */

.ui-card-question {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--ui-space-2xs);
    height: 100%;
    padding: var(--ui-space-md);
    border: 1px solid var(--ui-color-border-subtle);
    border-radius: var(--ui-radius-md);
    background-color: var(--ui-color-surface);
    box-shadow: var(--ui-shadow-sm);
    transition: transform var(--ui-duration-base) var(--ui-easing-standard),
                box-shadow var(--ui-duration-base) var(--ui-easing-standard);
}

.ui-card-question--linked:hover {
    transform: translateY(-2px);
    box-shadow: var(--ui-shadow-md);
}

/* The lift must not hide the ring of a keyboard-focused link inside the card. */
.ui-card-question:focus-within {
    box-shadow: var(--ui-shadow-md);
}

.ui-card-question__title {
    margin: 0;
    font-size: var(--ui-font-size-lg);
    font-weight: var(--ui-font-weight-semibold);
    line-height: var(--ui-line-height-snug);
    text-wrap: balance;
}

.ui-card-question__link {
    color: var(--ui-color-text);
    text-decoration: none;
}

.ui-card-question__link:hover {
    color: var(--ui-color-link);
    text-decoration: underline;
}

/* Three lines is a whole question or the promise of one; four is a paragraph. */
.ui-card-question--linked .ui-card-question__title {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
}

.ui-card-question__tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ui-space-3xs);
}

/* The body of a standalone question — answers, the like control, the
   explanation — sits between the title and the meta, which is the order that
   page reads in. In a list card the slot is simply empty. */
.ui-card-question__body {
    width: 100%;
}

.ui-card-question__meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ui-space-3xs) var(--ui-space-sm);
    margin: auto 0 0;
    padding: var(--ui-space-2xs) 0 0;
    border-top: 1px solid var(--ui-color-border-subtle);
    width: 100%;
    list-style: none;
    font-size: var(--ui-font-size-xs);
    color: var(--ui-color-text-muted);
}

.ui-card-question__meta-item {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-3xs);
    font-variant-numeric: tabular-nums;
}

.ui-card-question__footer {
    width: 100%;
}

/* -----------------------------------------------------------------------------
   Stat tile

   One naming scheme instead of .stat-card, .stat-item and .stats-card. The
   number is large but not coloured: an accent here would compete with the one
   thing on the page the user is meant to press.
   -------------------------------------------------------------------------- */

.ui-stat-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ui-space-3xs);
    height: 100%;
    padding: var(--ui-space-md) var(--ui-space-sm);
    border: 1px solid var(--ui-color-border-subtle);
    border-radius: var(--ui-radius-md);
    background-color: var(--ui-color-surface);
    text-align: center;
    text-decoration: none;
    color: var(--ui-color-text);
    transition: var(--ui-transition-color);
}

a.ui-stat-tile:hover {
    border-color: var(--ui-color-accent-border);
    background-color: var(--ui-color-surface-sunken);
    color: var(--ui-color-text);
}

.ui-stat-tile__icon {
    font-size: var(--ui-font-size-lg);
    line-height: 1;
    color: var(--ui-color-accent);
}

.ui-stat-tile__value {
    font-size: var(--ui-font-size-2xl);
    font-weight: var(--ui-font-weight-bold);
    line-height: var(--ui-line-height-tight);
    font-variant-numeric: tabular-nums;
}

.ui-stat-tile__label {
    font-size: var(--ui-font-size-sm);
    color: var(--ui-color-text-muted);
    text-wrap: balance;
}

/* -----------------------------------------------------------------------------
   Search field

   One radius for what was 23px, 25px and 30px in three files describing the
   same field. Pill, because the field is a lozenge everywhere it appears and
   the pill is the token that says so.
   -------------------------------------------------------------------------- */

.ui-search-field {
    display: flex;
    gap: var(--ui-space-2xs);
    align-items: center;
    width: 100%;
}

.ui-search-field__control {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
}

.ui-search-field__icon {
    position: absolute;
    top: 50%;
    left: var(--ui-space-sm);
    transform: translateY(-50%);
    color: var(--ui-color-text-muted);
    pointer-events: none;
}

.ui-search-field__input {
    min-height: 2.75rem; /* 44px — touch target */
    padding-left: calc(var(--ui-space-sm) * 2 + 1em);
    padding-right: var(--ui-space-sm);
    border-radius: var(--ui-radius-pill);
}

/* Chrome and Safari draw their own clear button inside type=search. */
.ui-search-field__input::-webkit-search-cancel-button {
    cursor: pointer;
}

/* Filled by JS; empty it must not leave a floating white slab under the field. */
.ui-search-field__results {
    position: absolute;
    z-index: 1000; /* Bootstrap's dropdown layer */
    top: calc(100% + var(--ui-space-3xs));
    right: 0;
    left: 0;
    max-height: 60vh;
    overflow-y: auto;
    border: 1px solid var(--ui-color-border);
    border-radius: var(--ui-radius-md);
    background-color: var(--ui-color-surface);
    box-shadow: var(--ui-shadow-lg);
}

.ui-search-field__results:empty {
    display: none;
}

/* -----------------------------------------------------------------------------
   Chip — filter

   One contract instead of .filter-tag against .active-filter. Unlike a tag
   chip, this one is a control: it has a pressed state, it is reachable by
   keyboard, and its size is a touch target rather than a label's.
   -------------------------------------------------------------------------- */

.ui-chip-filter {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-space-3xs);
    min-height: 2.75rem; /* 44px — touch target */
    padding: var(--ui-space-3xs) var(--ui-space-sm);
    border: 1px solid var(--ui-color-border-strong);
    border-radius: var(--ui-radius-pill);
    background-color: var(--ui-color-surface);
    color: var(--ui-color-text);
    font-size: var(--ui-font-size-sm);
    font-family: inherit;
    line-height: var(--ui-line-height-snug);
    text-decoration: none;
    cursor: pointer;
    transition: var(--ui-transition-color);
}

.ui-chip-filter:hover {
    border-color: var(--ui-color-accent);
    background-color: var(--ui-color-accent-soft);
    color: var(--ui-color-accent-text);
}

/* Applied filter: the fill under a 14px caption, so accent-solid, not accent. */
.ui-chip-filter--active {
    border-color: var(--ui-color-accent-solid);
    background-color: var(--ui-color-accent-solid);
    color: var(--ui-color-text-on-accent);
}

.ui-chip-filter--active:hover {
    border-color: var(--ui-color-accent-solid-hover);
    background-color: var(--ui-color-accent-solid-hover);
    color: var(--ui-color-text-on-accent);
}

.ui-chip-filter__count {
    font-size: var(--ui-font-size-xs);
    font-weight: var(--ui-font-weight-semibold);
    font-variant-numeric: tabular-nums;
    color: var(--ui-color-text-muted);
}

.ui-chip-filter:hover .ui-chip-filter__count,
.ui-chip-filter--active .ui-chip-filter__count {
    color: inherit;
}

/* Fills the right end of the chip: a 12px cross is a target nobody can hit. */
.ui-chip-filter__remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.75rem;
    min-height: 2.75rem;
    margin: calc(var(--ui-space-3xs) * -1) calc(var(--ui-space-sm) * -1) calc(var(--ui-space-3xs) * -1) 0;
    padding: 0;
    border: 0;
    border-radius: var(--ui-radius-pill);
    background-color: transparent;
    color: inherit;
    font-size: var(--ui-font-size-xs);
    cursor: pointer;
    transition: var(--ui-transition-color);
}

.ui-chip-filter__remove:hover {
    background-color: rgba(var(--ui-color-text-rgb), 0.12);
}

/* -----------------------------------------------------------------------------
   Progress timer

   The quick test's countdown, in the site's own palette instead of the blue and
   indigo it arrived in. "Running out" is a word, an icon and a hatched bar as
   well as a colour — a red bar alone says nothing to a part of the audience.
   -------------------------------------------------------------------------- */

.ui-progress-timer {
    display: flex;
    flex-direction: column;
    gap: var(--ui-space-3xs);
    width: 100%;
}

.ui-progress-timer__head {
    display: flex;
    align-items: baseline;
    gap: var(--ui-space-2xs);
    font-size: var(--ui-font-size-sm);
    color: var(--ui-color-text-muted);
}

.ui-progress-timer__icon {
    align-self: center;
    line-height: 1;
}

.ui-progress-timer__label {
    min-width: 0;
}

.ui-progress-timer__warning {
    font-weight: var(--ui-font-weight-semibold);
    color: var(--ui-color-warning-text);
}

/* Tabular figures: a countdown that reflows on every tick is unreadable. */
.ui-progress-timer__value {
    margin-left: auto;
    font-size: var(--ui-font-size-lg);
    font-weight: var(--ui-font-weight-semibold);
    font-variant-numeric: tabular-nums;
    color: var(--ui-color-text);
}

.ui-progress-timer--warning .ui-progress-timer__icon,
.ui-progress-timer--warning .ui-progress-timer__value {
    color: var(--ui-color-warning-text);
}

.ui-progress-timer__track {
    overflow: hidden;
    width: 100%;
    height: 0.5rem;
    border-radius: var(--ui-radius-pill);
    background-color: var(--ui-color-surface-sunken);
}

.ui-progress-timer__bar {
    height: 100%;
    border-radius: inherit;
    background-color: var(--ui-color-accent);
    transition: width var(--ui-duration-base) var(--ui-easing-standard),
                background-color var(--ui-duration-base) var(--ui-easing-standard);
}

/* Hatching, so the state survives a greyscale screen and colour blindness. */
.ui-progress-timer--warning .ui-progress-timer__bar {
    background-color: var(--ui-color-warning);
    background-image: repeating-linear-gradient(
        45deg,
        rgba(var(--ui-color-surface-rgb), 0.45) 0,
        rgba(var(--ui-color-surface-rgb), 0.45) 0.25rem,
        transparent 0.25rem,
        transparent 0.5rem
    );
}

/* -----------------------------------------------------------------------------
   Pagination

   Not a component of its own making: the CodeIgniter pager, rendered through
   ui('pagination') and painted by the Bootstrap bridge. What is owned here is
   the touch target — the previous row was pagination-sm, which is 31px tall.
   -------------------------------------------------------------------------- */

.ui-pagination__nav {
    display: flex;
    justify-content: center;
}

.ui-pagination {
    gap: var(--ui-space-3xs);
    margin: 0;
}

.ui-pagination__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.75rem;
    min-height: 2.75rem; /* 44px — touch target */
    border-radius: var(--ui-radius-sm);
    font-variant-numeric: tabular-nums;
    text-decoration: none;
}

/* -----------------------------------------------------------------------------
   Bootstrap form controls

   Not a UIKit component — the framework's own controls, which have no
   component-level --bs-* variables in 5.3 and compile their focus, checked and
   thumb colours literally. Only those literals are handed over here; radius,
   text and background colours are left to Bootstrap, which already derives them
   from the bridge in tokens.css.
   -------------------------------------------------------------------------- */

.form-control,
.form-select {
    border-color: var(--ui-color-border-strong);
}

.form-control:focus,
.form-select:focus {
    border-color: var(--ui-color-accent);
    box-shadow: var(--ui-focus-ring);
}

.form-check-input:checked {
    background-color: var(--ui-color-accent);
    border-color: var(--ui-color-accent);
}

.form-check-input:focus {
    border-color: var(--ui-color-accent);
    box-shadow: var(--ui-focus-ring);
}

.form-range::-webkit-slider-thumb { background-color: var(--ui-color-accent); }
.form-range::-moz-range-thumb     { background-color: var(--ui-color-accent); }
