/*
 * Component styles. Reference ONLY semantic tokens (var(--bg), var(--fg), ...).
 * Never use primitive tokens (var(--gray-900), var(--blue-500)) here —
 * those belong to a theme, not a component.
 */

* { box-sizing: border-box; }

html {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* ── Utility: styled <select> ────────────────────────────────────────────────
 * Use class="ui-select" on any <select> element. Add component-specific
 * overrides (font-size, width, flex-shrink…) via a second class or inline.
 */
.ui-select {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--fg);
    font: inherit;
    font-size: 0.88rem;
    padding: 0.3rem 0.5rem;
    cursor: pointer;
    outline: none;
    min-width: 0;
    width: 100%;
    transition: border-color 0.15s;
}
.ui-select:focus { border-color: var(--accent); }
.ui-select option { background: var(--bg-elevated); color: var(--fg); }

/* ── Utility: styled checkbox — replaces the unthemed native checkbox ────────
 * Use class="ui-checkbox" on any <input type="checkbox">. `appearance: none`
 * drops the browser's own (light, OS-styled) box; the tick/dash are drawn as
 * a ::after pseudo-element so :checked and :indeterminate both render with
 * semantic tokens instead of browser chrome.
 */
.ui-checkbox {
    appearance: none;
    -webkit-appearance: none;
    width: 1.1rem;
    height: 1.1rem;
    margin: 0;
    padding: 0;
    border: 1.5px solid var(--muted);
    border-radius: 4px;
    background: var(--bg);
    cursor: pointer;
    position: relative;
    flex-shrink: 0;
    vertical-align: middle;
    transition: background 0.15s, border-color 0.15s;
}
.ui-checkbox:hover { border-color: var(--accent); }
.ui-checkbox:checked,
.ui-checkbox:indeterminate {
    background: var(--accent);
    border-color: var(--accent);
}
.ui-checkbox:checked::after {
    content: "";
    position: absolute;
    left: 0.28rem;
    top: 0.08rem;
    width: 0.28rem;
    height: 0.58rem;
    border-right: 2px solid var(--accent-fg);
    border-bottom: 2px solid var(--accent-fg);
    transform: rotate(45deg);
}
.ui-checkbox:indeterminate::after {
    content: "";
    position: absolute;
    left: 0.2rem;
    top: 0.44rem;
    width: 0.6rem;
    height: 2px;
    background: var(--accent-fg);
}
.ui-checkbox:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ui-checkbox:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Utility: field-help — a small "?" hint next to a label/field ────────────
 * Wrap a trigger + popup in `<span class="field-help">`. Pure CSS (no JS):
 * the popup is a sibling revealed via :hover/:focus-within on the wrapper,
 * so it also works from the keyboard (Tab onto the button reveals it too,
 * no click required — matches "при наведении" without needing a click-away
 * handler like the site's other popups).
 */
.field-help {
    position: relative;
    display: inline-flex;
    vertical-align: middle;
}
.field-help__trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.05rem;
    height: 1.05rem;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: var(--bg-elevated);
    color: var(--muted);
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1;
    padding: 0;
    cursor: help;
    transition: border-color 0.15s, color 0.15s;
}
.field-help__trigger:hover,
.field-help__trigger:focus-visible {
    border-color: var(--accent);
    color: var(--accent);
}
.field-help__popup {
    position: absolute;
    left: 0;
    bottom: calc(100% + 0.4rem);
    z-index: 20;
    width: max-content;
    max-width: min(260px, calc(100vw - 2rem));
    padding: 0.55rem 0.7rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--fg) 18%, transparent);
    color: var(--fg);
    font-size: 0.78rem;
    font-weight: 400;
    font-family: var(--font-base);
    text-transform: none;
    letter-spacing: normal;
    line-height: 1.4;
    opacity: 0;
    visibility: hidden;
    transform: translateY(4px);
    transition: opacity 0.12s ease, transform 0.12s ease, visibility 0.12s;
    pointer-events: none;
}
.field-help:hover .field-help__popup,
.field-help:focus-within .field-help__popup {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}
.field-help__intro {
    margin: 0 0 0.35rem;
}
.field-help__list {
    margin: 0;
    padding-left: 1.1rem;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

/* Variant: sits absolutely in a field's own top-right corner (e.g. over a
 * textarea) instead of inline next to a label. The parent field must be
 * `position: relative` for this to anchor correctly. */
.field-help--corner {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 5;
}
/* Variant: popup opens downward and right-aligned, so it doesn't overflow
 * past the right edge of a corner-anchored trigger. */
.field-help__popup--right {
    left: auto;
    right: 0;
    bottom: auto;
    top: calc(100% + 0.4rem);
    transform: translateY(-4px);
}

/* ── Utility: UIDropdown — custom dropdown (replaces native <select> popup) ──
 * Use UIDropdown.create({ options, value, onChange }) from js/shared/dropdown.js.
 * The menu is appended to document.body (fixed positioning, avoids overflow clip).
 */
.ui-dropdown {
    position: relative;
    width: 100%;
    min-width: 0;
}
.ui-dropdown__trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    width: 100%;
    padding: 0.3rem 0.5rem;
    font: inherit;
    font-size: 0.88rem;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    outline: none;
    text-align: left;
    transition: border-color 0.15s;
}
.ui-dropdown__trigger:hover { border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); }
.ui-dropdown__trigger:focus { border-color: var(--accent); }
.ui-dropdown__trigger[aria-expanded="true"] { border-color: var(--accent); }
.ui-dropdown__label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-dropdown__chevron {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    opacity: 0.5;
    transition: transform 0.15s;
}
.ui-dropdown__trigger[aria-expanded="true"] .ui-dropdown__chevron { transform: rotate(180deg); }
.ui-dropdown__menu {
    position: fixed;
    z-index: 9999;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.25rem;
    margin: 0;
    list-style: none;
    overflow-y: auto;
    box-shadow: 0 4px 16px color-mix(in srgb, var(--fg) 12%, transparent),
                0 1px 4px color-mix(in srgb, var(--fg) 6%, transparent);
}
.ui-dropdown__option {
    padding: 0.4rem 0.6rem;
    border-radius: 5px;
    font-size: 0.88rem;
    color: var(--fg);
    cursor: pointer;
    user-select: none;
    outline: none;
    transition: background 0.1s;
}
.ui-dropdown__option:hover,
.ui-dropdown__option:focus { background: var(--bg-hover, color-mix(in srgb, var(--accent) 10%, var(--bg-elevated))); }
.ui-dropdown__option--selected {
    background: color-mix(in srgb, var(--accent) 15%, var(--bg-elevated));
    color: var(--accent);
    font-weight: 600;
}

/* ── Emoji picker popover (js/shared/emoji_picker.js) ─────────────────── */
.emoji-picker {
    z-index: 9999;
    width: 260px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.6rem;
    box-shadow: 0 4px 16px color-mix(in srgb, var(--fg) 12%, transparent),
                0 1px 4px color-mix(in srgb, var(--fg) 6%, transparent);
}

.emoji-picker__input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.4rem 0.6rem;
    margin-bottom: 0.5rem;
    font: inherit;
    font-size: 1rem;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
}

.emoji-picker__input:focus {
    outline: none;
    border-color: var(--accent);
}

.emoji-picker__grid {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 0.15rem;
    max-height: 9rem;
    overflow-y: auto;
    margin-bottom: 0.5rem;
}

.emoji-picker__item {
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    font-size: 1.15rem;
    line-height: 1;
    padding: 0.35rem 0;
    cursor: pointer;
}

.emoji-picker__item:hover {
    border-color: var(--border);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.emoji-picker__item--active {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 15%, transparent);
}

.emoji-picker__actions {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
}

.emoji-picker__clear,
.emoji-picker__save {
    flex: 1;
    padding: 0.4rem 0.6rem;
    font: inherit;
    font-size: 0.85rem;
    border-radius: 6px;
    cursor: pointer;
}

.emoji-picker__clear {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
}

.emoji-picker__clear:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.emoji-picker__save {
    background: var(--accent-gradient);
    border: 1px solid transparent;
    color: var(--accent-fg);
}

/* The [hidden] attribute is `display: none` in the UA stylesheet, but any
 * component rule with the same specificity (`.foo { display: flex }`) loses
 * to it via cascade order. Force [hidden] to always win. */
[hidden] { display: none !important; }

/* ── Utility: NumberStepper — themed spinner for <input type="number"> ───
 * Use `NumberStepper.attach(inputEl)` from js/shared/number_stepper.js on
 * any `<input type="number">` instead of leaving the browser's native
 * (unthemed) up/down arrows. Reads min/max/step off the input's own
 * attributes. The .number-stepper wrapper owns the border/background —
 * .number-stepper__input sits flush inside it, borderless.
 */
.number-stepper {
    display: inline-flex;
    align-items: stretch;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 5px;
    transition: border-color 0.15s;
}
.number-stepper:focus-within { border-color: var(--accent); }
.number-stepper .number-stepper__input {
    border: none;
    border-radius: 0;
    background: transparent;
    outline: none;
    /* Hide native spinner — the custom arrows replace it. */
    -moz-appearance: textfield;
}
.number-stepper .number-stepper__input::-webkit-inner-spin-button,
.number-stepper .number-stepper__input::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.number-stepper__arrows {
    display: flex;
    flex-direction: column;
    border-left: 1px solid var(--border);
}
.number-stepper__arrow {
    flex: 1 1 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: 0;
    line-height: 0;
}
.number-stepper__arrow:first-child { border-bottom: 1px solid var(--border); }
.number-stepper__arrow svg { width: 8px; height: 8px; }
.number-stepper__arrow:hover {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* ── Utility: field at character limit — js/app.js toggles this class on any
 * <input>/<textarea> with a `maxlength` attribute once its value reaches that
 * length. `!important` because it must win over each field type's own
 * `:focus` border/box-shadow (higher specificity) — the limit is hit while
 * typing, i.e. while focused, so the warning has to show right then.
 */
.field--at-limit {
    border-color: var(--danger) !important;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 22%, transparent) !important;
}

/* ── Themed scrollbars ───────────────────────────────────────────────────────
 * Firefox uses `scrollbar-color`; WebKit/Blink (Chrome, Safari, Edge) need the
 * `::-webkit-scrollbar-*` pseudo-elements. Track is invisible on hover/active
 * so only the thumb shows, matching the minimal look. Token-bound so every
 * theme auto-restyles scrollbars.
 */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}

*::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

*::-webkit-scrollbar-track {
    background: transparent;
}

*::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 6px;
    border: 2px solid var(--bg-elevated);
    background-clip: padding-box;
}

*::-webkit-scrollbar-thumb:hover {
    background: var(--muted);
    background-clip: padding-box;
}

body {
    font-family: var(--font-base);
    background: var(--bg);
    color: var(--fg);
    margin: 0;
    padding: 0;
    line-height: 1.5;
    transition: background-color 0.15s ease, color 0.15s ease, padding-left 0.25s ease;
}

body.nav-open { padding-left: 260px; }

.brand {
    margin: 0;
    font-family: var(--font-heading);
    font-size: 2.25rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.02em;
}

.brand a {
    color: var(--fg);
    text-decoration: none;
}

.brand a:hover { opacity: 0.85; }

main {
    max-width: 640px;
    margin: 0 auto;
    padding: 4rem 1rem 2rem;
}

main.assistant-page {
    max-width: 1120px;
    padding-top: 5rem;
}

main.weight-page {
    max-width: 1120px;
}

/* Wider container for every sphere landing page — any of them can now host
   an area-blocks heatmap (js/shared/heatmap.js), which fills the container
   width; a narrow main leaves the year's 53 week-columns rendered as tiny,
   visually cramped cells. life-page was the original (pre-area-blocks)
   case; the rest were added once every sphere could grow the same kind of
   widget. Each sphere's own inner container class (.sport/.finance/.health/
   ... below) is set to max-width: 100% to match — otherwise it would
   re-clamp back down to its own older, narrower value inside this wider
   main. */
main.life-page,
main.sport-page,
main.finance-page,
main.health-page,
main.beauty-page,
main.psycho-page,
main.effectiveness-page,
main.social-page,
main.hobby-page {
    max-width: 1120px;
}

/* Wider container for the «Расходы» page — the inline add/edit form
   needs enough horizontal room that all five columns (date / category /
   note / amount / button) sit comfortably without the long category
   names being truncated. Pairs with .finance-history { max-width: 960px }
   below. */
main.finance-history-page {
    max-width: 1040px;
}

h1, h2, h3 {
    text-wrap: balance;
}

p {
    text-wrap: pretty;
}

h1 {
    font-family: var(--font-heading);
    margin: 0 0 0.25rem;
    font-size: 2rem;
}

button {
    padding: 0.5rem 1rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 6px;
    font-size: 1rem;
    cursor: pointer;
    transition-property: opacity, scale;
    transition-duration: 0.15s;
    transition-timing-function: ease-out;
}

button:hover { opacity: 0.9; }
button:active:not(:disabled) { scale: 0.96; }
button:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── Page title ───────────────────────────────────────────────────────── */
.page-title {
    font-family: var(--font-heading);
    font-size: 1.75rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 0 0 1.5rem;
    text-align: center;
}


/* ── Assistant ────────────────────────────────────────────────────────── */
.assistant {
    display: grid;
    grid-template-columns: 320px minmax(0, 1fr);
    gap: 1rem;
    align-items: start;
}

.assistant__sidebar,
.assistant__chat {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
}

.assistant__sidebar {
    padding: 0.9rem;
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    height: calc(100vh - 180px);
}

.assistant__sidebar-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.75rem;
}

.assistant__sidebar-title {
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 600;
}

.assistant__new-group {
    display: flex;
    align-items: stretch;
    flex-shrink: 0;
}

.assistant__new-btn {
    border-radius: 6px 0 0 6px;
    border-right: none;
}

.assistant__picker-wrap {
    position: relative;
}

.assistant__picker-btn {
    padding: 0 0.45rem;
    border-radius: 0 6px 6px 0;
    border-left: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
}

.assistant__picker-btn[aria-expanded="true"] {
    background: var(--bg-elevated);
}

.assistant__character-menu {
    position: absolute;
    top: calc(100% + 5px);
    right: 0;
    z-index: 200;
    min-width: 180px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.3rem;
    box-shadow: 0 6px 20px color-mix(in srgb, var(--fg) 18%, transparent);
}

.assistant__character-item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    width: 100%;
    padding: 0.45rem 0.6rem;
    border: none;
    border-radius: 5px;
    background: transparent;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.92rem;
    text-align: left;
    cursor: pointer;
}

.assistant__character-item:hover {
    background: var(--bg);
}

.assistant__character-item-avatar {
    width: 22px;
    height: 22px;
    border-radius: 3px;
    image-rendering: pixelated;
    image-rendering: crisp-edges;
    flex-shrink: 0;
}

.assistant__character-item-name {
    flex: 1;
}

.assistant__character-item-check {
    width: 14px;
    flex-shrink: 0;
    color: var(--accent);
    opacity: 0;
}

.assistant__character-item[aria-checked="true"] .assistant__character-item-check {
    opacity: 1;
}

.assistant__list {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    overflow-y: auto;
}

.assistant__item {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
}

/* The main clickable area (title + time + preview). */
.assistant__item-select {
    flex: 1;
    min-width: 0;
    padding: 0.75rem 0.85rem;
    background: var(--bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 8px;
    text-align: left;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.assistant__item.is-active .assistant__item-select {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
}

/* Trash icon button, anchored at the top-right of each list item. */
.assistant__item-delete {
    flex: none;
    width: 2rem;
    height: 2rem;
    margin-top: 0.25rem;
    padding: 0;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
    border-radius: 6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    cursor: pointer;
}

.assistant__item-top {
    display: flex;
    gap: 0.75rem;
    align-items: baseline;
    justify-content: space-between;
}

.assistant__item-title {
    font-weight: 600;
    line-height: 1.3;
}

.assistant__item-time,
.assistant__item-preview,
.assistant__chat-subtitle,
.assistant__list-empty,
.assistant__thread-empty {
    color: var(--muted);
}

.assistant__item-time {
    font-size: 0.8rem;
    flex: none;
}

.assistant__item-preview {
    font-size: 0.9rem;
    line-height: 1.35;
    overflow-wrap: anywhere;
}

.assistant__list-empty,
.assistant__thread-empty {
    padding: 1rem;
    background: var(--bg);
    border: 1px dashed var(--border);
    border-radius: 8px;
}

.assistant__chat {
    display: flex;
    flex-direction: column;
    height: calc(100vh - 180px);
    padding: 1rem;
    gap: 1rem;
}

.assistant__chat-header {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--border);
}

.assistant__character-badge {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.assistant__character-avatar {
    width: 32px;
    height: 32px;
    border-radius: 6px;
    image-rendering: pixelated;
    image-rendering: crisp-edges;
    flex-shrink: 0;
}

.assistant__character-name {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--accent);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    flex-shrink: 0;
    padding-right: 0.6rem;
    border-right: 1px solid var(--border);
}

.assistant__msg-avatar {
    width: 28px;
    height: 28px;
    border-radius: 4px;
    image-rendering: pixelated;
    image-rendering: crisp-edges;
    flex-shrink: 0;
    align-self: flex-start;
    margin-top: 0.55rem;
    margin-right: 0.5rem;
}

.assistant__chat-title {
    flex: 1;
    min-width: 0;
    font-family: var(--font-heading);
    font-size: 1.25rem;
    font-weight: 600;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.assistant__chat-subtitle {
    margin-top: 0.25rem;
    font-size: 0.92rem;
}

.assistant__thread {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    overflow-y: auto;
    padding-right: 0.25rem;
}

.assistant__message {
    display: flex;
}

.assistant__message--user {
    justify-content: flex-end;
}

.assistant__message--assistant {
    justify-content: flex-start;
}

.assistant__bubble {
    max-width: min(82%, 760px);
    padding: 0.85rem 1rem;
    border-radius: 12px;
    border: 1px solid var(--border);
    overflow-wrap: anywhere;
    font-size: 0.88rem;
    line-height: 1.5;
}

.assistant__bubble--assistant {
    background: var(--bg);
    color: var(--fg);
}

.assistant__bubble--user {
    background: var(--accent);
    color: var(--accent-fg);
    border-color: transparent;
    white-space: pre-wrap;
}

.assistant__bubble.is-streaming {
    position: relative;
}

.assistant__cursor {
    display: inline-block;
    margin-left: 0.2rem;
    color: var(--accent);
    animation: assistant-cursor 0.9s steps(1) infinite;
}

@keyframes assistant-cursor {
    0%, 50% { opacity: 1; }
    50.01%, 100% { opacity: 0; }
}

.assistant__composer {
    display: flex;
    flex-direction: row;
    align-items: flex-end;
    gap: 0.75rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--border);
}

.assistant__send-btn {
    flex: none;
}

.assistant__input {
    width: 100%;
    padding: 0.8rem 0.9rem;
    background: var(--bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 8px;
    font-family: var(--font-base);
    font-size: 1rem;
    line-height: 1.5;
    resize: none;
    min-height: 96px;
    max-height: 220px;
    overflow-y: hidden;
}

.assistant__input:focus {
    outline: none;
    border-color: var(--accent);
}

@media (hover: hover) {
    .assistant__item-select:hover {
        border-color: var(--accent);
        opacity: 1;
    }

    .assistant__item-delete:hover {
        color: var(--danger);
        border-color: var(--danger);
        opacity: 1;
    }
}

@media (max-width: 900px) {
    .assistant {
        grid-template-columns: 1fr;
    }

    .assistant__sidebar,
    .assistant__chat {
        height: auto;
    }

    .assistant__list {
        flex: none;
        max-height: 40vh;
    }

    .assistant__thread {
        min-height: 280px;
        max-height: 50vh;
    }

    .assistant__bubble {
        max-width: 100%;
    }
}

/* ── Research ─────────────────────────────────────────────────────────── */
.research {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.research__form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.research__label {
    color: var(--muted);
    font-size: 0.95rem;
}

.research__topic {
    width: 100%;
    padding: 0.75rem;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-family: var(--font-base);
    font-size: 1rem;
    line-height: 1.5;
    resize: none;
    min-height: 90px;
    max-height: 260px;
    overflow-y: hidden;
}

.research__topic:focus {
    outline: none;
    border-color: var(--accent);
}

.research__agents {
    display: flex;
    gap: 1.25rem;
    flex-wrap: wrap;
}

.research__agent-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    user-select: none;
}

.research__agent-toggle input[type="checkbox"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.research__toggle-track {
    position: relative;
    width: 36px;
    height: 20px;
    background: var(--border);
    border-radius: 10px;
    flex-shrink: 0;
    transition: background 0.2s;
}

.research__agent-toggle input:checked + .research__toggle-track {
    background: var(--accent);
}

.research__toggle-knob {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    background: white;
    border-radius: 50%;
    transition: transform 0.2s;
}

.research__agent-toggle input:checked + .research__toggle-track .research__toggle-knob {
    transform: translateX(16px);
}

.research__agent-toggle input:focus-visible + .research__toggle-track {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.research__toggle-label {
    font-size: 0.9rem;
    color: var(--fg);
}

.research__row {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
}

.research__secondary {
    background: transparent;
    color: var(--fg);
    border: 1px solid var(--border);
}

.research__secondary:hover { background: var(--bg-elevated); opacity: 1; }

.research__loading {
    position: relative; /* anchors the progress bar to the window's bottom edge */
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden; /* clip the bar to the rounded corners */
}

.research__loading-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

/* Track: a thin, full-width line along the bottom of the loading window. */
.research__progress {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3px;
    background: var(--border);
}

/* Fill: grows left→right as steps complete (width set inline by research.js). */
.research__progress-bar {
    width: 0;
    height: 100%;
    background: var(--accent);
    transition: width 0.5s ease;
}

.research__loading-info {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
}

.research__phrase {
    color: var(--muted);
    font-style: italic;
    animation: research-pulse 1.6s ease-in-out infinite;
}

/* The topic being researched, shown under the rotating phrase. */
.research__loading-topic {
    color: var(--fg);
    font-weight: 600;
    overflow-wrap: anywhere;
}

.research__loading-topic:empty {
    display: none;
}

@keyframes research-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.55; }
}

/* Planning questions: shown once the planner pass completes. */
.research__planning {
    padding-top: 0.625rem;
    border-top: 1px solid var(--border);
    font-size: 0.875rem;
    color: var(--muted);
}

.research__planning-label {
    font-weight: 600;
    color: var(--fg);
    margin-bottom: 0.25rem;
}

.research__planning-list {
    margin: 0 0 0 1.25rem;
    padding: 0;
}

.research__planning-list li {
    margin-bottom: 0.2rem;
    line-height: 1.4;
}

.research__planning-refined {
    margin-bottom: 0.5rem;
    font-style: italic;
}

/* List of saved report cards (newest first). */
.research__reports {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.research__result {
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 6px;
}

.research__result-header {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 0.55rem 1rem;
}

.research__result-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-width: 0;
}

/* Divider between summary and body — only when the card is expanded, so a
 * collapsed card reads as a single clean row. */
.research__result.is-expanded .research__result-header {
    border-bottom: 1px solid var(--border);
}

/* Clickable toggle (chevron + title). Fills the top row's free space. */
.research__result-toggle {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    background: transparent;
    border: 0;
    padding: 0;
    margin: 0;
    color: inherit;
    cursor: pointer;
    text-align: left;
    font: inherit;
}

.research__result-chevron {
    flex: none;
    color: var(--muted);
    transition: transform 0.15s ease;
}

.research__result.is-expanded .research__result-chevron {
    transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
    .research__result-chevron { transition: none; }
}

.research__result-title {
    color: var(--fg);
    font-weight: 600;
    line-height: 1.3;
    overflow-wrap: break-word;
    min-width: 0;
}

@media (hover: hover) {
    .research__result-toggle:hover .research__result-title { color: var(--accent); }
    .research__result-toggle:hover .research__result-chevron { color: var(--accent); }
}

.research__result-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem 0.6rem;
}

.research__result-time {
    color: var(--muted);
    font-size: 0.85rem;
    white-space: nowrap;
}

.research__result-category {
    color: var(--accent);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.1rem 0.55rem;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* Source-agent provenance chips (web / youtube / papers), muted next to the
 * accent category chip. */
.research__result-contributors {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

.research__contributor {
    color: var(--muted);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.1rem 0.5rem;
    font-size: 0.7rem;
}

.research__result-cost {
    color: var(--muted);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.1rem 0.5rem;
    font-size: 0.7rem;
    font-variant-numeric: tabular-nums;
    margin-left: auto;
}

.research__result-body {
    padding: 1rem 1.25rem;
    line-height: 1.55;
}

/* Body is rendered Markdown — see the reusable `.markdown` block below.
 * The title (filename) is not rendered here; any heading comes from content. */

.research__result-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.research__copy,
.research__erase {
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
    padding: 0.3rem;
    border-radius: 4px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
}

/* Hover only on hover-capable pointers, so a tap/click doesn't leave the
 * button stuck in its hover look on touch (or after a mouse click). */
@media (hover: hover) {
    .research__copy:hover,
    .research__erase:hover { color: var(--fg); opacity: 1; }
}

/* Confirmation flash on a successful copy — a quick accent flicker + glow that
 * "confirms" the action, then settles back. Triggered by the .is-copied class. */
@keyframes research-copy-flash {
    0%   { color: var(--muted);  transform: scale(1);    box-shadow: 0 0 0 0 transparent; }
    15%  { color: var(--accent); transform: scale(1.3);  box-shadow: 0 0 10px 2px var(--accent); }
    35%  { color: var(--muted);  transform: scale(1.05); box-shadow: 0 0 0 0 transparent; }
    55%  { color: var(--accent); transform: scale(1.2);  box-shadow: 0 0 8px 1px var(--accent); }
    100% { color: var(--muted);  transform: scale(1);    box-shadow: 0 0 0 0 transparent; }
}
.research__copy.is-copied {
    animation: research-copy-flash 0.6s ease-out;
    border-color: var(--accent);
}
@media (prefers-reduced-motion: reduce) {
    .research__copy.is-copied { animation: none; color: var(--accent); }
}

/* The toggle and its dropdown are anchored together so the list opens right
 * under the button (instead of at the bottom of the report body). Shared by the
 * Источники and Допущения disclosures. */
.research__sources-wrap,
.research__assumptions-wrap {
    position: relative;
    display: inline-flex;
}

/* Disclosure toggle — a text + icon button (wider than the icon-only copy /
 * erase buttons), sharing their resting look. */
.research__sources-btn,
.research__assumptions-btn {
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
    padding: 0.3rem 0.55rem;
    border-radius: 4px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.8rem;
    line-height: 1;
}

.research__sources-btn.is-open,
.research__assumptions-btn.is-open {
    color: var(--accent);
    border-color: var(--accent);
}

@media (hover: hover) {
    .research__sources-btn:hover,
    .research__assumptions-btn:hover { color: var(--fg); opacity: 1; }
    .research__sources-btn.is-open:hover,
    .research__assumptions-btn.is-open:hover { color: var(--accent); }
}

/* Dropdown panel — anchored under the toggle (right-aligned to it), floating
 * above the report body. Scrolls when the list is long. */
.research__sources,
.research__assumptions {
    position: absolute;
    top: calc(100% + 0.4rem);
    right: 0;
    z-index: 10;
    min-width: 240px;
    max-width: 360px;
    max-height: 320px;
    overflow-y: auto;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.7rem 0.9rem;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--fg) 22%, transparent);
}

.research__sources-list,
.research__assumptions-list {
    margin: 0;
    padding-left: 1.2rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

/* <ol> numbering needs a touch more room than bullet points. */
.research__sources-list {
    padding-left: 1.5rem;
}

.research__source,
.research__assumption {
    line-height: 1.45;
    word-break: break-word;
}

.research__source a {
    color: var(--accent);
    text-decoration: none;
}

@media (hover: hover) {
    .research__source a:hover { text-decoration: underline; }
}

.research__error {
    padding: 0.75rem 1rem;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 6px;
}

/* ── Explore sources ──────────────────────────────────────────────────── */
.sources {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.sources__form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    align-items: stretch;
}

.sources__list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.sources__row {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

.sources__input {
    flex: 1;
    min-width: 0;
    padding: 0.6rem 0.75rem;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-family: var(--font-base);
    font-size: 1rem;
}

.sources__input:focus {
    outline: none;
    border-color: var(--accent);
}

.sources__remove {
    flex: none;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 1.25rem;
    line-height: 1;
}

.sources__remove:hover { background: var(--bg-elevated); opacity: 1; }

#sources-add { align-self: flex-start; }

/* Container for the per-exploration cards (§ sources-task-template) — several
   can run in parallel, stacked newest-first with a gap between them. */
.sources__tasks {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

/* Sources/agents that couldn't be used this run — a soft warning list. Shared
   by the sources page (unreadable links) and the research page (skipped agents). */
.sources__skipped,
.research__skipped {
    padding: 0.75rem 1rem;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-left: 3px solid var(--danger);
    border-radius: 6px;
}

.sources__skipped-title,
.research__skipped-title {
    font-weight: 600;
    margin-bottom: 0.35rem;
}

.sources__skipped-list,
.research__skipped-list {
    margin: 0;
    padding-left: 1.1rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    overflow-wrap: anywhere;
}

/* Coverage notice: planner sub-questions the final report did not answer.
   Same shape as .research__skipped, but with the accent stripe (not danger)
   — gaps are a quality signal, not a failure. */
.research__coverage {
    padding: 0.75rem 1rem;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 6px;
    margin-top: 0.75rem;
}

.research__coverage-title {
    font-weight: 600;
    margin-bottom: 0.35rem;
}

.research__coverage-summary {
    color: var(--muted);
    font-size: 0.85rem;
    margin-bottom: 0.4rem;
}

.research__coverage-list {
    margin: 0;
    padding-left: 1.1rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    overflow-wrap: anywhere;
}

/* ── Markdown (reusable rendered-content block) ───────────────────────── */
/* Output of markdown.js. Reusable on any page: render into a container, then
 * it gets the `.markdown` class. References only semantic tokens. */
.markdown > :first-child { margin-top: 0; }
.markdown > :last-child { margin-bottom: 0; }

.markdown p { margin: 0 0 0.75rem; }

.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
    font-family: var(--font-heading);
    font-weight: 600;
    line-height: 1.25;
    margin: 1.4rem 0 0.6rem;
}

.markdown h1 { font-size: 1.5rem; }
.markdown h2 { font-size: 1.3rem; }
.markdown h3 { font-size: 1.15rem; }
.markdown h4 { font-size: 1.02rem; }
.markdown h5,
.markdown h6 { font-size: 0.95rem; color: var(--muted); }

.markdown a {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Obsidian-style `[[wiki]]` link — distinct from regular markdown links
 * (solid underline) by a dashed border-bottom. Color inherits from
 * .markdown a (var(--accent)). `currentColor` keeps the dash matching the
 * surrounding text color when a wikilink sits inside a heading. */
.markdown a.md-wikilink {
    text-decoration: none;
    border-bottom: 1px dashed currentColor;
    padding-bottom: 1px;
}

/* Missing target — the wiki resolver couldn't find a .md file with the
 * given basename. Visually quieter so a typo is obvious without being loud. */
.markdown a.md-wikilink--missing {
    color: var(--muted);
    font-style: italic;
    cursor: help;
}

.markdown strong { font-weight: 700; }
.markdown em { font-style: italic; }
.markdown del { opacity: 0.7; }

.markdown ul,
.markdown ol {
    margin: 0 0 0.75rem;
    padding-left: 1.5rem;
}

.markdown li { margin: 0.2rem 0; }
.markdown li > ul,
.markdown li > ol { margin: 0.2rem 0; }

/* Inline code and fenced code blocks share the code surface token. */
.markdown code {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.875em;
    background: var(--code-bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.1em 0.35em;
}

.markdown pre {
    margin: 0 0 0.75rem;
    padding: 0.85rem 1rem;
    background: var(--code-bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow-x: auto;
}

/* Code inside a block resets the inline pill styling. */
.markdown pre code {
    background: none;
    border: none;
    border-radius: 0;
    padding: 0;
    font-size: 0.85rem;
    line-height: 1.5;
}

.markdown blockquote {
    margin: 0 0 0.75rem;
    padding: 0.25rem 0 0.25rem 1rem;
    border-left: 3px solid var(--accent);
    color: var(--muted);
}

.markdown blockquote > :last-child { margin-bottom: 0; }

.markdown hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 1.25rem 0;
}

.markdown img {
    max-width: 100%;
    height: auto;
    border-radius: 6px;
    outline: 1px solid var(--image-outline);
    outline-offset: -1px;
}

/* Tables scroll horizontally on narrow viewports rather than overflowing. */
.markdown table {
    display: block;
    width: max-content;
    max-width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
    margin: 0 0 0.75rem;
    font-size: 0.92rem;
}

.markdown th,
.markdown td {
    border: 1px solid var(--border);
    padding: 0.4rem 0.65rem;
    text-align: left;
}

.markdown thead th {
    background: var(--code-bg);
    font-weight: 600;
}

/* ── Modal (confirmation dialog) ──────────────────────────────────────── */
/* Centered overlay; the backdrop scrim doubles as a click-to-cancel target.
 * Sits above the project/workout/etc. modals (which are at z-index 200) so a
 * "Are you sure?" prompt can layer on top of an open detail view. */
.modal {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
}

.modal__dialog {
    position: relative;
    width: 100%;
    max-width: 380px;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 12px 32px color-mix(in srgb, var(--fg) 25%, transparent);
    padding: 1.25rem;
}

.modal__title {
    font-family: var(--font-heading);
    font-size: 1.15rem;
    font-weight: 600;
    margin: 0 0 0.5rem;
}

.modal__text {
    color: var(--muted);
    margin: 0 0 1.25rem;
}

.modal__actions {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
}

.modal__danger {
    background: var(--danger);
    color: var(--danger-fg);
}

/* Non-destructive confirm (Modal.askConfirm(..., {danger: false})) — an
 * affirming accent button instead of the red delete one, for actions like
 * "перенести"/"продолжить" that aren't removing anything. */
.modal__confirm {
    background: var(--accent);
    color: var(--accent-fg);
}

/* ── Top header bar ───────────────────────────────────────────────────── */
/* Fixed bar: brand centered (links home), theme hamburger on the right. The
 * grid keeps the brand optically centered regardless of the button's width. */
.topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 30;
    height: 56px;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    padding: 0 0.75rem;
    background: var(--bg-elevated);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 1px 4px color-mix(in srgb, var(--fg) 8%, transparent);
    transition: left 0.25s ease;
}

.nav-open .topbar { left: 260px; }

.topbar__brand {
    grid-column: 2;
    justify-self: center;
    font-family: var(--font-heading);
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--fg);
    text-decoration: none;
}

.topbar__brand:hover { opacity: 0.85; }

#menu { grid-column: 3; justify-self: end; display: flex; align-items: center; gap: 0.5rem; }

/* Hamburger button for the theme picker. */
.topbar__btn {
    width: 40px;
    height: 40px;
    padding: 0;
    background: transparent;
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 8px;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    cursor: pointer;
}

.topbar__btn:hover { background: var(--bg); opacity: 1; }

.menu-btn__bar {
    display: block;
    width: 18px;
    height: 2px;
    background: var(--fg);
    border-radius: 1px;
}

/* ── Menu (theme picker, anchored at the header's right) ──────────────── */
.menu { position: relative; }

/* Panel drops down from the header button. */
.menu-panel {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    min-width: 180px;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--fg) 12%, transparent);
    padding: 0.5rem;
    z-index: 10;
}

.menu-section + .menu-section { margin-top: 0.5rem; }

.menu-section__title {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
    padding: 0.25rem 0.5rem;
}

.menu-section__items {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.menu-item {
    background: transparent;
    color: var(--fg);
    border: none;
    border-radius: 6px;
    padding: 0.45rem 0.6rem;
    text-align: left;
    font-size: 0.9rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    width: 100%;
}

.menu-item:hover { background: var(--bg); opacity: 1; }

.menu-item[aria-checked="true"] { color: var(--accent); font-weight: 600; }

.menu-item__label { flex: 1; }

.menu-item__check { visibility: hidden; font-size: 0.85rem; }
.menu-item[aria-checked="true"] .menu-item__check { visibility: visible; }

.menu-item__avatar {
    width: 18px;
    height: 18px;
    border-radius: 3px;
    image-rendering: pixelated;
    image-rendering: crisp-edges;
    flex-shrink: 0;
}

/* ── Nav drawer (push sidebar) ───────────────────────────────────────── */
#nav-btn { grid-column: 1; justify-self: start; }

.nav-drawer__panel {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 260px;
    background: var(--bg-elevated);
    border-right: 1px solid var(--border);
    box-shadow: 2px 0 12px color-mix(in srgb, var(--fg) 6%, transparent);
    display: flex;
    flex-direction: column;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    padding: 0.75rem;
    overflow-y: auto;
    z-index: 25;
}

.nav-drawer--open .nav-drawer__panel {
    transform: translateX(0);
}

.nav-drawer--no-transition .nav-drawer__panel {
    transition: none !important;
}

body.nav-no-transition,
body.nav-no-transition .topbar {
    transition: none !important;
}


.nav-drawer__nav {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.nav-drawer__link {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.7rem 1rem;
    border-radius: 8px;
    color: var(--fg);
    text-decoration: none;
    font-size: 1rem;
    font-weight: 600;
    transition: background 0.1s, color 0.1s;
}

.nav-drawer__link svg {
    flex: none;
    opacity: 0.7;
}

.nav-drawer__link:hover {
    background: var(--bg);
    color: var(--accent);
}

.nav-drawer__link--active {
    background: var(--bg);
    color: var(--accent);
}

.nav-drawer__group {
    display: flex;
    flex-direction: column;
}

.nav-drawer__link-label {
    flex: 1 1 auto;
}

.nav-drawer__caret {
    flex: none;
    opacity: 0.5;
    transition: transform 0.2s ease;
}

/* Sublist is revealed only while the pointer/focus is on the "Задачи"
   group (button or its own links) — not a click-toggle. `:focus-within`
   covers keyboard tabbing into the sublist itself. */
.nav-drawer__group:hover .nav-drawer__caret,
.nav-drawer__group:focus-within .nav-drawer__caret {
    transform: rotate(90deg);
}

.nav-drawer__sublist {
    display: flex;
    flex-direction: column;
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.2s ease;
}

.nav-drawer__group:hover .nav-drawer__sublist,
.nav-drawer__group:focus-within .nav-drawer__sublist {
    max-height: 20rem;
}

.nav-drawer__sublink {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 1rem 0.5rem 2.6rem;
    border-radius: 8px;
    color: var(--fg);
    opacity: 0.85;
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 500;
    transition: background 0.1s, color 0.1s;
}

.nav-drawer__sublink svg {
    flex: none;
    opacity: 0.7;
}

.nav-drawer__sublink:hover {
    background: var(--bg);
    color: var(--accent);
}

.nav-drawer__sublink--active {
    background: var(--bg);
    color: var(--accent);
    opacity: 1;
}

/* ── Welcome widget (home page) ───────────────────────────────────────── */
.welcome {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    width: fit-content;
    margin: 4rem auto;
}

.welcome__avatar-frame {
    flex: none;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.75rem;
    line-height: 0;
}

.welcome__avatar {
    display: block;
    width: 192px;
    height: auto;
    image-rendering: pixelated;
    image-rendering: crisp-edges;
    outline: 1px solid var(--image-outline);
    outline-offset: -1px;
}

.welcome__bubble {
    position: relative;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.85rem 1.25rem;
    font-family: var(--font-heading);
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--fg);
    letter-spacing: 0.01em;
}

.welcome__bubble::before {
    content: '';
    position: absolute;
    left: -9px;
    top: 50%;
    transform: translateY(-50%);
    width: 0;
    height: 0;
    border-top: 8px solid transparent;
    border-bottom: 8px solid transparent;
    border-right: 9px solid var(--border);
}

.welcome__bubble::after {
    content: '';
    position: absolute;
    left: -7px;
    top: 50%;
    transform: translateY(-50%);
    width: 0;
    height: 0;
    border-top: 7px solid transparent;
    border-bottom: 7px solid transparent;
    border-right: 8px solid var(--bg-elevated);
}

.welcome__samurai {
    color: var(--accent);
}

.welcome__bubble--loading {
    color: var(--muted);
    font-weight: 400;
    letter-spacing: 0.15em;
}

/* ── Home links (page-entry buttons under the welcome widget) ─────────── */
.home {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    margin: 0 auto 4rem;
    width: fit-content;
}

.home__link {
    display: block;
    min-width: 18rem;
    max-width: 90vw;
    padding: 0.9rem 1.5rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    color: var(--fg);
    text-decoration: none;
    text-align: center;
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    transition: border-color 0.15s, color 0.15s;
}

.home__link:hover,
.home__link:focus-visible {
    border-color: var(--accent);
    color: var(--accent);
    outline: none;
}

/* ── Home page layout (two-column: left panel + tasks kanban) ───────────── */

/* Как main.calendar-page: страница ограничена высотой вьюпорта и не
   скроллится сама — прокрутка задач происходит внутри каждого списка
   (см. .tasks-landing-main .kanban__cards ниже). max-width = .home-left +
   gap + три компактные колонки задач (см. .tasks-landing-main), плюс сама
   .home-left подогнана вверх настолько, насколько остаётся места без
   пересечения брейкпоинта в 1220px (см. комментарий там) — чтобы после
   ряда списков не оставалось пустого поля до края страницы. */
main.home-page {
    max-width: 1170px;
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
    padding: 4rem 1rem 1rem;
}

.home-layout {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    gap: 1.5rem;
    align-items: stretch;
}

/* Левая колонка (аватар + приветствие + корзина + навигация). Ширина
   подобрана так, чтобы вместе с рядом из трёх списков задач (см.
   .tasks-landing-main) занять всю ширину main.home-page без остатка. */
.home-left {
    flex: 0 0 auto;
    width: 100%;
    max-width: 300px;
}

.home-layout .welcome {
    flex-direction: column;
    align-items: center;
    width: 100%;
    gap: 1rem;
    margin: 2rem 0;
}

/* Аватар подогнан под ширину .home-left (max-width: 300px). */
.home-layout .welcome__avatar {
    width: 180px;
}

.home-layout .welcome__bubble {
    width: 100%;
    text-align: center;
}

/* Hide left-pointing arrow — avatar is above the bubble, not to the left */
.home-layout .welcome__bubble::before,
.home-layout .welcome__bubble::after {
    display: none;
}

.home-layout .home {
    margin: 0 0 2rem;
    width: auto;
}

/* .home__link несёт свой собственный `min-width: 18rem` (288px, см.
   базовое правило ниже) — на любой другой странице это ок, но раньше
   .home-left была уже него (220px) и кнопка вылезала за её границы с обеих
   сторон (flex `align-items: center` центрирует более широкий элемент
   симметрично). Снимаем floor и подгоняем под колонку явно — не полагаемся
   на то, что текущая ширина .home-left (300px) больше 288px. */
.home-layout .home__link {
    min-width: 0;
    width: 100%;
}

/* ── Home basket quick-add ───────────────────────────────────────────────── */

.home__basket-wrap {
    min-width: 0;
    width: 100%;
    max-width: 90vw;
}

.home__basket-btn {
    display: block;
    width: 100%;
    padding: 0.9rem 1.5rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    color: var(--fg);
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}

.home__basket-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
    opacity: 1;
}

.home__basket-input {
    display: block;
    width: 100%;
    padding: 0.9rem 1.5rem;
    background: var(--bg-elevated);
    border: 1.5px solid var(--accent);
    border-radius: 10px;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 15%, transparent);
    color: var(--fg);
    font-family: var(--font-heading);
    font-size: 1.1rem;
    text-align: center;
    outline: none;
    box-sizing: border-box;
}

.home__basket-input::placeholder { color: var(--muted); font-weight: 400; }

.home__basket-status {
    font-size: 0.82rem;
    color: var(--success);
    text-align: center;
}

#home-tasks-kanban {
    flex: 1;
    min-width: 0;
    padding-bottom: 0;
    overflow-x: visible;
}

#home-tasks-kanban .kanban__col {
    flex: 1;
}

/* 1220px — не произвольное число: main.home-page нужно 1168px (.home-left
   300px + компактный ряд из трёх списков, 3×260px + gap'ы, см.
   .tasks-landing-main ниже), чтобы поместиться БЕЗ переноса строк (перенос
   несовместим с внутренним скроллом списков — см. комментарий у
   .tasks-landing-main); 1220 даёт небольшой запас. Ниже порога — полный
   вертикальный стек одной колонкой; выше — компактный ряд без потерянного
   места. */
@media (max-width: 1220px) {
    /* В стеке (одна колонка) высота содержимого больше не бьётся с
       height: 100vh / overflow: hidden основного правила (main.home-page)
       — тот расчёт держится только на компактном ряду из трёх списков.
       Возвращаем страницу к обычному скроллу целиком, как до внедрения
       списков-с-внутренним-скроллом. */
    main.home-page {
        height: auto;
        min-height: 100vh;
        overflow: visible;
    }

    .home-layout {
        flex-direction: column;
    }

    #home-tasks-kanban {
        width: 100%;
    }
}

/* ── Ritual (evening-ritual page) ──────────────────────────────────────── */
.ritual {
    max-width: 48rem;
    margin: 0 auto;
    padding: 0 1rem;
}

.ritual__loading {
    text-align: center;
    color: var(--muted);
    font-family: var(--font-heading);
    font-size: 1.05rem;
    padding: 2rem 0;
}

.ritual__error {
    background: var(--bg-elevated);
    border: 1px solid var(--danger);
    color: var(--danger);
    border-radius: 8px;
    padding: 1rem 1.25rem;
    line-height: 1.45;
}

.ritual__card {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1.25rem 1.5rem 1.5rem;
}

.ritual__title {
    margin: 0 0 0.75rem;
    font-family: var(--font-heading);
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--fg);
}

/* Расходы за день: current-month / next-month toggle, sitting right
   under the title. Same segmented-pill vocabulary as .rd-mode-toggle
   (js/reminders/detail.js) — pill container, two equal segments, active
   segment reads solid accent. Picking "next month" changes only where
   an expense row lands (today vs. the 1st of next month), not the form
   itself. */
.ritual__month-toggle {
    display: flex;
    gap: 0.3rem;
    padding: 0.2rem;
    margin-bottom: 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    width: fit-content;
}
.ritual__month-btn {
    appearance: none;
    background: transparent;
    color: var(--muted);
    border: 1px solid transparent;
    border-radius: 6px;
    padding: 0.4rem 1rem;
    font-family: var(--font-base);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    transition: color 0.12s, background 0.12s, border-color 0.12s, box-shadow 0.12s;
}
.ritual__month-btn:hover:not(.ritual__month-btn--active) {
    color: var(--fg);
}
.ritual__month-btn--active {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border-color: var(--accent);
    box-shadow: 0 1px 4px color-mix(in srgb, var(--accent) 35%, transparent);
    font-weight: 600;
}
.ritual__month-btn--active:hover {
    background: var(--accent-gradient);
    color: var(--accent-fg);
}

.ritual__form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.ritual__form-head {
    display: grid;
    /* Amount column is wider than the category column on mobile so the
       arithmetic expression fits on one line; the remove button gets a
       fixed compact slot. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) 2.25rem;
    gap: 0.5rem;
    align-items: center;
    padding: 0 0.25rem;
    font-family: var(--font-heading);
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
}

.ritual__rows {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.ritual__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) 2.25rem;
    gap: 0.5rem;
    /* stretch so the remove button always matches the row's actual height
       (tallest column — the amount field grows when its hint line appears) */
    align-items: stretch;
}

/* Single-field row (e.g. one inspiration item) — same shell as the
   category+amount expense row, minus the second column. */
.ritual__row--single {
    grid-template-columns: minmax(0, 1fr) 2.25rem;
}

.ritual__field {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

/* Full-width block under the expenses grid: the «Что делал» textarea and
   the day-rating buttons each span the whole form width. */
.ritual__field--full {
    width: 100%;
    margin-top: 0.5rem;
}

.ritual__label {
    font-family: var(--font-heading);
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
    padding: 0 0.25rem;
}

/* «Привычки» block (pages/ritual.js) — one SINGLE-LINE row per in_progress
   habit: title + «Т» trigger tooltip button + two checkboxes, all in one
   flex row (no wrapping sub-rows) — written on submit via
   HabitApi.putRecord, same independent-write stance as every other
   ritual field. */
.ritual__habits-rows {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-top: 0.35rem;
}

.ritual__habit-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-elevated);
    padding: 0.4rem 0.65rem;
}

.ritual__habit-row-title {
    flex: 1 1 auto;
    font-weight: 600;
    font-size: 0.9rem;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ritual__habit-row-trigger-btn {
    flex: 0 0 auto;
    width: 1.6rem;
    height: 1.6rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: transparent;
    color: var(--muted);
    font-size: 0.75rem;
    font-weight: 700;
    cursor: pointer;
    padding: 0;
}

.ritual__habit-row-trigger-btn:hover,
.ritual__habit-row-trigger-btn--active {
    border-color: var(--accent);
    color: var(--accent);
}

.ritual__habit-row-check {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.8rem;
    white-space: nowrap;
    cursor: pointer;
}

.ritual__habits-empty {
    color: var(--muted);
    font-size: 0.85rem;
    margin: 0.35rem 0 0;
}

/* Floating trigger tooltip (pages/ritual.js::_openHabitTooltip) — a
   single instance appended to <body> and positioned under the clicked
   «Т» button, so it never affects the one-line row's height/layout the
   way an inline reveal would. Same z-index tier as the other floating
   popups (dropdown menus, calendar picker). */
.ritual__habit-tooltip {
    position: fixed;
    z-index: 300;
    max-width: min(320px, 80vw);
    padding: 0.5rem 0.65rem;
    border-radius: 8px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    box-shadow: 0 8px 24px color-mix(in srgb, var(--fg) 18%, transparent);
    font-size: 0.82rem;
    color: var(--fg);
}

@media (max-width: 640px) {
    .ritual__habit-row {
        flex-wrap: wrap;
    }
    .ritual__habit-row-title {
        flex-basis: 100%;
    }
}

.ritual__textarea-wrap {
    position: relative;
}
.ritual__textarea-wrap .ritual__textarea {
    padding-right: 1.9rem;
}

.ritual__textarea {
    width: 100%;
    font: inherit;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.55rem 0.7rem;
    font-family: var(--font-base);
    font-size: 1rem;
    line-height: 1.5;
    min-height: 4.5rem;
    resize: none;
    overflow-y: hidden;
}

.ritual__textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

/* КБЖУ + вес + % жира — six fixed number fields feeding the Спорт sphere's
   Питание/Вес diaries (see pages/ritual.js § metrics). Unlike the repeatable
   expense/inspiration rows, this is a fixed small set, so a simple auto-fit
   grid (no add/remove) is enough. */
.ritual__metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(5.5rem, 1fr));
    gap: 0.6rem;
}

.ritual__metric {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

/* «Тревога» block's comment field — spans two auto-fit tracks so the
   free-text input gets meaningfully more room than the rating field next
   to it, instead of the even split every other .ritual__metric gets. */
.ritual__metric--wide {
    grid-column: span 2;
}

.ritual__metric-label {
    font-size: 0.75rem;
    color: var(--muted);
    padding: 0 0.15rem;
}

/* Same treatment as .weight__input / .nutrition__row input[type="number"]
   (the dedicated Спорт → Питание/Вес diary pages this block writes into) —
   no stepper arrows, native spinner hidden so it doesn't reappear unstyled. */
.ritual__metric-input {
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.95rem;
    padding: 0.4rem 0.6rem;
    width: 100%;
    box-sizing: border-box;
    -moz-appearance: textfield;
    appearance: textfield;
}

.ritual__metric-input:focus {
    outline: none;
    border-color: var(--accent);
}

.ritual__metric-input::-webkit-inner-spin-button,
.ritual__metric-input::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.ritual__mood {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.ritual__mood-btn {
    flex: 1 1 0;
    min-width: 2.5rem;
    padding: 0.55rem 0.7rem;
    background: var(--bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 6px;
    font: inherit;
    font-family: var(--font-heading);
    font-size: 1.05rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.1s ease, background-color 0.1s ease, color 0.1s ease;
}

/* Selected state: filled with the accent. The shape mirrors the default
   button so the selected one is unmistakable in any theme. */
.ritual__mood-btn--active {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border-color: var(--accent);
}

@media (hover: hover) {
    .ritual__mood-btn:not(.ritual__mood-btn--active):hover {
        border-color: var(--accent);
        color: var(--accent);
    }
}

.ritual__mood-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* The category field hosts an absolutely-positioned suggestion list, so it
   has to be a positioning context. */
.ritual__field--cat {
    position: relative;
}

/* Shows the picked category's emoji inside the input itself once a value
   matches a known category exactly — the combobox's own suggestion list
   only ever showed the icon next to each option, never on the chosen
   value. Positioned over the input's left padding (see
   .ritual__input--has-icon below), toggled via `hidden`. */
.ritual__field-icon {
    position: absolute;
    left: 0.7rem;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    font-size: 0.95rem;
    line-height: 1;
    z-index: 1;
}

.ritual__field--cat .ritual__input.ritual__input--has-icon {
    padding-left: 1.9rem;
}

.ritual__suggest {
    /* Floating list of matching categories under the input. Hidden by default;
       ``hidden`` attribute is reset via JS when there's at least one match. */
    position: absolute;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    z-index: 10;
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    max-height: 16rem;
    overflow-y: auto;
    /* Wide enough to fit emoji-prefixed category labels without truncation
       (e.g. "🍔 Еда", "🚗 Транспорт") even when the input itself is narrow.
       The ``right: 0`` keeps it aligned to the input's right edge above. */
    min-width: 13rem;
}

.ritual__suggest-item {
    padding: 0.5rem 0.7rem;
    cursor: pointer;
    font-size: 0.95rem;
    color: var(--fg);
}

.ritual__suggest-item--active,
.ritual__suggest-item:hover {
    /* Same translucent-accent hover used elsewhere — picks up the theme. */
    background: color-mix(in srgb, var(--accent) 18%, transparent);
}

.ritual__input {
    width: 100%;
    font: inherit;
    font-size: 0.95rem;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.65rem 0.85rem;
    /* The amount input has to fit an arithmetic expression like
       "(1200 - 300) / 2" comfortably without the layout jumping. */
    min-width: 0;
    font-variant-numeric: tabular-nums;
}

.ritual__input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.ritual__input--amount {
    text-align: right;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.98rem;
}

.ritual__input--invalid {
    border-color: var(--danger);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 22%, transparent);
}

.ritual__hint-amount {
    font-size: 0.8rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    padding-left: 0.1rem;
    /* small line so it doesn't push the next row's alignment around */
    min-height: 1em;
}

.ritual__hint-amount--warn {
    color: var(--danger);
}

.ritual__remove {
    /* same shape as other icon-only action buttons in the app */
    background: transparent;
    border: 1px solid var(--border);
    padding: 0.3rem;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    font-size: 1.1rem;
    line-height: 1;
    /* row is stretched (see .ritual__row), so this always matches the
       row's real height instead of a hardcoded approximation */
    height: 100%;
    cursor: pointer;
    transition: color 0.1s ease, border-color 0.1s ease;
}

.ritual__remove:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.ritual__add {
    align-self: flex-start;
    background: transparent;
    color: var(--accent);
    border: 1px dashed var(--border);
    border-radius: 6px;
    padding: 0.45rem 0.9rem;
    font: inherit;
    font-size: 0.92rem;
    cursor: pointer;
    margin-top: 0.25rem;
    transition: border-color 0.1s ease, background-color 0.1s ease;
}

.ritual__add:hover {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.ritual__total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 0.75rem 0.25rem 0.5rem;
    border-top: 1px solid var(--border);
    margin-top: 0.5rem;
    font-family: var(--font-heading);
}

.ritual__total-label {
    font-size: 0.95rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ritual__total-value {
    font-size: 1.4rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--fg);
}

.ritual__status {
    margin: 0.25rem 0 0;
    padding: 0.6rem 0.8rem;
    border-radius: 6px;
    font-size: 0.92rem;
    line-height: 1.4;
}

.ritual__status--ok {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--fg);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}

.ritual__status--error {
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    color: var(--danger);
    border: 1px solid var(--danger);
}

.ritual__status--pending {
    background: var(--code-bg);
    color: var(--muted);
    border: 1px solid var(--border);
}

.ritual__actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 0.5rem;
}

.ritual__submit {
    /* leans on the global ``button`` style but bumps the size for the
       primary action of the page */
    padding: 0.7rem 1.5rem;
    font-size: 1rem;
    font-weight: 600;
}

@media (max-width: 480px) {
    .ritual__form-head,
    .ritual__row {
        /* On narrow screens the action column collapses to icon-only,
           everything else keeps the same proportions. */
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr) 2rem;
    }

    /* Single-field rows have no second column to shrink — only narrow the
       remove-button slot to match the two-column rows above. */
    .ritual__row--single {
        grid-template-columns: minmax(0, 1fr) 2rem;
    }
}

/* Ritual page layout: fixed-width form (48rem) + fixed-width "done today"
   sidebar (17rem) sitting to its right. Both columns are FIXED widths — the
   form must NOT shrink because of the sidebar; its comfortable width is the
   same `max-width: 48rem` it had before the sidebar existed. Below 1100px
   (≈ 48rem + 17rem + gap + side gutters) the layout collapses into a single
   centered column where both sections sit at `max-width: 48rem`. */
.ritual-layout {
    display: grid;
    grid-template-columns: 48rem 17rem;
    gap: 1.5rem;
    align-items: start;
    justify-content: center;
}

/* `.ritual` keeps `max-width: 48rem; padding: 0 1rem` from the rule above.
   Grid handles horizontal alignment; the explicit `margin: 0` here just
   neutralises `.ritual`'s `margin: 0 auto` so the form snaps flush to its
   grid column instead of being re-centred by the auto margins. */
.ritual-layout > .ritual {
    margin: 0;
}

/* Sidebar column — sticky so the completed-today list stays in view while
   the user scrolls through a long expense form (many rows + big "Что делал"
   textarea). `top: calc(56px + 1rem)` clears the fixed topbar. */
.ritual-done {
    max-width: 17rem;
    width: 100%;
    margin: 0;
    padding: 0 1rem;
    position: sticky;
    top: calc(56px + 1rem);
    /* Caps the sidebar to the viewport and turns it into a flex column so
       only `.ritual-done__list` below scrolls internally (see its own
       flex: 1 1 auto; min-height: 0; overflow-y: auto) — the heading stays
       pinned instead of scrolling away with a long "done today" list. */
    max-height: calc(100vh - 56px - 2rem);
    display: flex;
    flex-direction: column;
}

.ritual-done__heading {
    margin: 0 0 0.75rem;
    font-family: var(--font-heading);
    font-size: 1rem;
    color: var(--muted);
    letter-spacing: 0.02em;
}

.ritual-done__loading,
.ritual-done__empty {
    color: var(--muted);
    font-size: 0.875rem;
    padding: 0.25rem 0;
}

.ritual-done__error {
    background: var(--bg-elevated);
    border: 1px solid var(--danger);
    color: var(--danger);
    border-radius: 6px;
    padding: 0.6rem 0.8rem;
    font-size: 0.875rem;
    line-height: 1.4;
}

/* Renders TaskCube (.cube--task, fixed 220px — see the comment on
   .cube--task) one per row; the sidebar's 17rem max-width minus its
   1rem side padding leaves ~240px, just enough for one column.
   flex: 1 1 auto + min-height: 0 is the same trick as
   .kanban__col--list .kanban__cards — required in a flex column so this
   list shrinks to the remaining space instead of forcing `.ritual-done`
   to grow past its max-height, which is what actually makes overflow-y
   kick in. */
.ritual-done__list {
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

@media (max-width: 1100px) {
    .ritual-layout {
        grid-template-columns: minmax(0, 1fr);
    }
    .ritual-layout > .ritual {
        margin: 0 auto;
    }
    .ritual-done {
        max-width: 48rem;
        margin: 0 auto;
        /* sticky on a single-column flow is meaningless: the sidebar
           already sits below the form, no scrolling required to keep
           it in view. */
        position: static;
    }
}

/* ==========================================================================
   Sport page — records
   ========================================================================== */

.sport {
    max-width: 100%;
}

.sport__header {
    margin-bottom: 1.25rem;
}

.sport__loading,
.sport__error {
    font-size: 0.95rem;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    margin-bottom: 1rem;
}

.sport__loading {
    color: var(--muted);
    background: var(--code-bg);
}

.sport__error {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
    border: 1px solid var(--danger);
}

.sport__records-title {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--fg);
    margin: 0 0 1rem;
}

.sport__records-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 0.75rem;
    margin-bottom: 2rem;
}

.sport__record-card {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.sport__record-exercise {
    font-size: 0.85rem;
    color: var(--muted);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.sport__record-value {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--accent);
    line-height: 1.1;
}

.sport__record-unit {
    font-size: 0.82rem;
    color: var(--muted);
}

.sport__records-empty {
    grid-column: 1 / -1;
    font-size: 0.9rem;
    color: var(--muted);
    font-style: italic;
    margin: 0;
}

.sport__diary-btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s;
}

.sport__diary-btn:hover {
    opacity: 0.85;
}

.sport__actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.sport__diary-btn--outline {
    background: transparent;
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.sport__diary-btn--outline:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    opacity: 1;
}

/* ==========================================================================
   Finance page — landing summary (per-month budget cards)
   ========================================================================== */

.finance {
    max-width: 100%;
}

.finance__header {
    margin-bottom: 1.25rem;
}

.finance__loading,
.finance__error {
    font-size: 0.95rem;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    margin-bottom: 1rem;
}

.finance__loading {
    color: var(--muted);
    background: var(--code-bg);
}

.finance__error {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
    border: 1px solid var(--danger);
}

.finance__summary-title {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--fg);
    margin: 0 0 1rem;
}

.finance__main-progress {
    width: 100%;
    margin: 0 auto 1.5rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 1rem 1.25rem;
}

.finance__main-label {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--muted);
    text-align: center;
}

.finance__summary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 0.5rem;
    margin-bottom: 1.25rem;
}

.finance__card {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.6rem 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.finance__card-name {
    font-size: 0.75rem;
    color: var(--muted);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.finance__card-label {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--fg);
    line-height: 1.1;
}

.finance__bar {
    width: 100%;
    height: 5px;
    background: var(--bg);
    border-radius: 3px;
    overflow: hidden;
}

.finance__bar--main {
    height: 10px;
    border-radius: 5px;
}

.finance__bar-fill {
    height: 100%;
    background: var(--accent);
    transition: width 200ms ease-out;
}

.finance__bar-fill--warn {
    background: #f59e0b;
}

.finance__bar-fill--over {
    background: var(--danger);
}

.finance__records-empty {
    grid-column: 1 / -1;
    font-size: 0.9rem;
    color: var(--muted);
    font-style: italic;
    margin: 0;
}

.finance__diary-btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s;
}

.finance__diary-btn:hover {
    opacity: 0.85;
}

.finance__diary-btn--outline {
    background: transparent;
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.finance__diary-btn--outline:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    opacity: 1;
}

.finance__actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* ==========================================================================
   Health page — placeholder landing (no entity yet; just a «Действия» button
   that mirrors sport/finance's pattern)
   ========================================================================== */

.health {
    max-width: 100%;
}

.health__header {
    margin-bottom: 1.25rem;
}

.health__empty {
    font-size: 0.95rem;
    color: var(--muted);
    font-style: italic;
    margin: 1rem 0 1.5rem;
}

.health__actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.health__diary-btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s;
}

.health__diary-btn:hover {
    opacity: 0.85;
}

.health__diary-btn--outline {
    background: transparent;
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.health__diary-btn--outline:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    opacity: 1;
}

/* ==========================================================================
   Beauty page — placeholder landing (no entity yet; just a «Действия» button
   that mirrors sport/finance's pattern)
   ========================================================================== */

.beauty {
    max-width: 100%;
}

.beauty__header {
    margin-bottom: 1.25rem;
}

.beauty__empty {
    font-size: 0.95rem;
    color: var(--muted);
    font-style: italic;
    margin: 1rem 0 1.5rem;
}

.beauty__actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.beauty__diary-btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s;
}

.beauty__diary-btn:hover {
    opacity: 0.85;
}

.beauty__diary-btn--outline {
    background: transparent;
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.beauty__diary-btn--outline:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    opacity: 1;
}

/* ==========================================================================
   Psycho page — landing with «Вдохновение» + «Действия» buttons (mirrors
   sport/finance's pattern)
   ========================================================================== */

.psycho {
    max-width: 100%;
}

.psycho__header {
    margin-bottom: 1.25rem;
}

.psycho__empty {
    font-size: 0.95rem;
    color: var(--muted);
    font-style: italic;
    margin: 1rem 0 1.5rem;
}

.psycho__actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.psycho__diary-btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s;
}

.psycho__diary-btn:hover {
    opacity: 0.85;
}

.psycho__diary-btn--outline {
    background: transparent;
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.psycho__diary-btn--outline:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    opacity: 1;
}

/* ==========================================================================
   Effectiveness page — placeholder landing (no entity yet; just a «Действия»
   button that mirrors sport/finance's pattern)
   ========================================================================== */

.effectiveness {
    max-width: 100%;
}

.effectiveness__header {
    margin-bottom: 1.25rem;
}

.effectiveness__empty {
    font-size: 0.95rem;
    color: var(--muted);
    font-style: italic;
    margin: 1rem 0 1.5rem;
}

.effectiveness__actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.effectiveness__diary-btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s;
}

.effectiveness__diary-btn:hover {
    opacity: 0.85;
}

.effectiveness__diary-btn--outline {
    background: transparent;
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.effectiveness__diary-btn--outline:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    opacity: 1;
}

/* ==========================================================================
   Social page — placeholder landing (no entity yet; just a «Действия»
   button that mirrors sport/finance's pattern)
   ========================================================================== */

.social {
    max-width: 100%;
}

.social__header {
    margin-bottom: 1.25rem;
}

.social__empty {
    font-size: 0.95rem;
    color: var(--muted);
    font-style: italic;
    margin: 1rem 0 1.5rem;
}

.social__actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.social__diary-btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s;
}

.social__diary-btn:hover {
    opacity: 0.85;
}

.social__diary-btn--outline {
    background: transparent;
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.social__diary-btn--outline:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    opacity: 1;
}

.life {
    max-width: 100%;
}

.life__header {
    margin-bottom: 1.25rem;
}

.life__empty {
    font-size: 0.95rem;
    color: var(--muted);
    font-style: italic;
    margin: 1rem 0 1.5rem;
}

.life__actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* Space between the «Действия»/«Фокусы»/«Дневник» button row and the
   «Оценка дня»/«Насыщенность жизни» heatmap tabs right below it — .rules-tabs
   itself only carries margin-bottom (it's usually the first element in its
   container on rules.html/reminders.html), so life.html needs its own
   top gap here. */
#life-heatmap-tabs {
    margin-top: 1.25rem;
}

.life__diary-btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s;
}

.life__diary-btn:hover {
    opacity: 0.85;
}

.life__diary-btn--outline {
    background: transparent;
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.life__diary-btn--outline:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    opacity: 1;
}

.hobby {
    max-width: 100%;
}

.hobby__header {
    margin-bottom: 1.25rem;
}

.hobby__empty {
    font-size: 0.95rem;
    color: var(--muted);
    font-style: italic;
    margin: 1rem 0 1.5rem;
}

.hobby__actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.hobby__diary-btn {
    display: inline-block;
    padding: 0.75rem 1.75rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 1rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s;
}

.hobby__diary-btn:hover {
    opacity: 0.85;
}

.hobby__diary-btn--outline {
    background: transparent;
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.hobby__diary-btn--outline:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    opacity: 1;
}

/* ==========================================================================
   Budget editor
   ========================================================================== */

.budget {
    max-width: 720px;
}

.budget__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}

.budget__copy-prev {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.45rem 0.85rem;
    color: var(--muted);
    font-family: var(--font-base);
    font-size: 0.85rem;
    cursor: pointer;
}

.budget__copy-prev:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.budget__copy-prev:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.budget__month-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.25rem;
    padding: 0.75rem 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
}

.budget__month-label {
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--fg);
}

.budget__month-btn {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.4rem 0.7rem;
    color: var(--fg);
    font-family: var(--font-base);
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
}

.budget__month-btn:hover {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--accent);
}

.budget__month-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.budget__loading,
.budget__error {
    font-size: 0.95rem;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    margin-bottom: 1rem;
}

.budget__loading {
    color: var(--muted);
    background: var(--code-bg);
}

.budget__error {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
    border: 1px solid var(--danger);
}

.budget__total {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
}

.budget__total-label {
    color: var(--muted);
    font-size: 0.9rem;
}

.budget__total-value {
    font-weight: 700;
    font-size: 1.1rem;
    color: var(--fg);
}

.budget__rows {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.25rem 1rem;
    margin-bottom: 1rem;
}

.budget__row {
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--border);
}

.budget__row:last-child {
    border-bottom: none;
}

.budget__row-main {
    display: grid;
    grid-template-columns: 36px 1fr 140px 36px;
    gap: 12px;
    align-items: center;
}

/* Free-text note per budget category, sitting under the main row — indented
   to line up under the name column (icon width 36px + the row's 12px gap). */
.budget__row-comment {
    margin-top: 0.35rem;
    padding-left: 48px;
    font-size: 0.85rem;
    color: var(--muted);
    overflow-wrap: anywhere;
}

.budget__row-comment:empty::before {
    content: attr(data-placeholder);
    font-style: italic;
}

.budget__row-comment:hover {
    color: var(--fg);
}

/* Override the generic click-to-edit input's bold title weight — a budget
   comment is a small note, not a heading. */
.budget__row-comment .click-to-edit__input {
    font-weight: 400;
    font-size: 0.85rem;
}

.budget__row-icon {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    line-height: 1;
    color: var(--fg);
    cursor: pointer;
}

.budget__row-icon:hover {
    border-color: var(--accent);
}

.budget__row-icon--empty {
    border-style: dashed;
    color: var(--muted);
    font-size: 1rem;
}

.budget__row-name {
    font-weight: 500;
    color: var(--fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Wraps the limit input so a "₽" suffix can sit inside the same grid cell
   without changing .budget__row's column count. */
.budget__row-limit {
    position: relative;
}

.budget__row input[type="number"] {
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.95rem;
    padding: 0.4rem 1.5rem 0.4rem 0.6rem;
    width: 100%;
    box-sizing: border-box;
    /* No stepper arrows on budget inputs — hide the native browser spinner too. */
    -moz-appearance: textfield;
    appearance: textfield;
}

.budget__row input[type="number"]:focus {
    outline: none;
    border-color: var(--accent);
}

.budget__row input[type="number"]::-webkit-inner-spin-button,
.budget__row input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.budget__row-limit-suffix {
    position: absolute;
    right: 0.6rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--muted);
    font-size: 0.85rem;
    pointer-events: none;
}

.budget__row-remove {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.3rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
}

.budget__row-remove:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.budget__add {
    display: inline-block;
    padding: 0.55rem 1rem;
    background: transparent;
    border: 1.5px dashed var(--border);
    border-radius: 8px;
    color: var(--muted);
    font-family: var(--font-base);
    font-size: 0.95rem;
    cursor: pointer;
}

.budget__add:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.budget__add-form {
    display: grid;
    grid-template-columns: 1fr 140px auto auto;
    gap: 12px;
    align-items: center;
    padding: 0.75rem 0;
}

.budget__add-form input {
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.95rem;
}

.budget__add-form input:focus {
    outline: none;
    border-color: var(--accent);
}

.budget__add-form input[type="number"] {
    width: 100%;
    box-sizing: border-box;
    /* No stepper arrows on budget inputs — hide the native browser spinner too. */
    -moz-appearance: textfield;
    appearance: textfield;
}

.budget__add-form input[type="number"]::-webkit-inner-spin-button,
.budget__add-form input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.budget__empty {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--muted);
    font-style: italic;
}

/* ==========================================================================
   Nutrition (Спорт → Питание)
   ========================================================================== */

.nutrition {
    max-width: 820px;
}

.nutrition__header {
    margin-bottom: 1.25rem;
}

.nutrition__labels-row {
    display: grid;
    grid-template-columns: 90px repeat(4, 1fr);
    gap: 12px;
    padding: 0 1rem 0.5rem;
}

.nutrition__label {
    font-size: 0.85rem;
    color: var(--muted);
    text-align: center;
}

.nutrition__days {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.25rem 1rem;
}

.nutrition__row {
    display: grid;
    grid-template-columns: 90px repeat(4, 1fr);
    gap: 12px;
    align-items: center;
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--border);
}

.nutrition__row:last-child {
    border-bottom: none;
}

.nutrition__row-date {
    font-weight: 500;
    color: var(--fg);
    white-space: nowrap;
}

.nutrition__row-weekday {
    font-size: 0.8rem;
    color: var(--muted);
}

.nutrition__row input[type="number"] {
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.95rem;
    padding: 0.4rem 0.6rem;
    width: 100%;
    box-sizing: border-box;
    /* No stepper arrows on this page (see pages/nutrition.js) — hide the
       native browser spinner too so it doesn't reappear unstyled. */
    -moz-appearance: textfield;
    appearance: textfield;
}

.nutrition__row input[type="number"]:focus {
    outline: none;
    border-color: var(--accent);
}

.nutrition__row input[type="number"]::-webkit-inner-spin-button,
.nutrition__row input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* ==========================================================================
   Weight (Спорт → Вес)
   ========================================================================== */

/* Fills the viewport exactly (see main.weight-page below) — no page-level
   scrollbar; only the day-rows list and the chart stack scroll internally
   if their content overflows. Same pattern as main.home-page /
   .tasks-landing-main .kanban__cards. */
main.weight-page {
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
    padding: 3rem 1rem 1rem;
}

.weight {
    max-width: 1120px;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.weight__header {
    flex: 0 0 auto;
    margin-bottom: 1rem;
}

.weight .budget__month-nav {
    flex: 0 0 auto;
}

.weight__layout {
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(260px, 360px) 1fr;
    gap: 1.5rem;
    align-items: stretch;
}

.weight__rows-col,
.weight__chart-col {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.weight__labels-row {
    flex: 0 0 auto;
    display: grid;
    grid-template-columns: 56px 1fr 1fr;
    gap: 10px;
    padding: 0 1rem 0.5rem;
}

.weight__label {
    font-size: 0.85rem;
    color: var(--muted);
    text-align: center;
}

.weight__days {
    flex: 1 1 auto;
    min-height: 0;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.25rem 1rem;
    overflow-y: auto;
}

.weight__row {
    display: grid;
    grid-template-columns: 56px 1fr 1fr;
    gap: 10px;
    align-items: center;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--border);
}

.weight__row:last-child {
    border-bottom: none;
}

.weight__row-date {
    font-weight: 500;
    color: var(--fg);
    white-space: nowrap;
    font-size: 0.9rem;
}

.weight__row-weekday {
    font-size: 0.78rem;
    color: var(--muted);
}

.weight__input {
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.95rem;
    padding: 0.4rem 0.5rem;
    width: 100%;
    box-sizing: border-box;
    /* No stepper arrows on this page (see pages/weight.js) — hide the
       native browser spinner too so it doesn't reappear unstyled. */
    -moz-appearance: textfield;
    appearance: textfield;
}

.weight__input:focus {
    outline: none;
    border-color: var(--accent);
}

.weight__input::-webkit-inner-spin-button,
.weight__input::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.weight__charts {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    overflow-y: auto;
}

/* Both charts split .weight__charts' available height evenly (flex: 1 1 0)
   and grow their svg to fill it (see .weight-chart__svg height:100% below)
   instead of a fixed aspect ratio — that's what lets the pair fit without
   a page scrollbar on typical viewports. */
.weight-chart {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1rem 1.1rem 0.5rem;
}

/* Per-series identity — one accent per measure, consumed via currentColor
   by every colored child (legend dot, line, points) so there's a single
   place that picks the hue. */
.weight-chart--weight { color: var(--chart-line-weight); }
.weight-chart--fat     { color: var(--chart-line-fat); }

.weight-chart__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
    flex-wrap: wrap;
}

.weight-chart__title {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 600;
    color: var(--fg);
    font-size: 0.95rem;
}

.weight-chart__dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.weight-chart__stat {
    font-weight: 700;
    color: var(--fg);
    font-size: 1rem;
}

.weight-chart__delta {
    font-weight: 500;
    color: var(--muted);
    font-size: 0.85rem;
}

.weight-chart__empty {
    color: var(--muted);
    font-style: italic;
    font-size: 0.9rem;
    padding: 1.5rem 0 2rem;
    text-align: center;
}

.weight-chart__svg-wrap {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    margin: 0 -0.1rem;
}

.weight-chart__svg-inner {
    flex: 1 1 auto;
    min-height: 0;
    position: relative;
}

.weight-chart__svg {
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
}

.weight-chart__grid {
    stroke: var(--chart-grid);
    stroke-width: 1;
}

.weight-chart__axis-label {
    fill: var(--muted);
    font-family: var(--font-base);
    font-size: 9px;
}

.weight-chart__axis-label--y {
    text-anchor: end;
    dominant-baseline: middle;
}

.weight-chart__axis-label--x {
    text-anchor: middle;
}

.weight-chart__line {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.weight-chart__point {
    fill: currentColor;
}

.weight-chart__point--last {
    stroke: var(--chart-dot-ring);
    stroke-width: 2;
}

.weight-chart__crosshair {
    stroke: var(--muted);
    stroke-width: 1;
    stroke-dasharray: 3 3;
    pointer-events: none;
}

.weight-chart__crosshair-dot {
    fill: currentColor;
    stroke: var(--chart-dot-ring);
    stroke-width: 2;
    pointer-events: none;
}

.weight-chart__hitrect {
    fill: transparent;
    cursor: crosshair;
}

.weight-chart__tooltip {
    position: absolute;
    transform: translate(-50%, calc(-100% - 8px));
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.3rem 0.55rem;
    font-size: 0.8rem;
    color: var(--fg);
    white-space: nowrap;
    pointer-events: none;
    box-shadow: 0 4px 12px color-mix(in srgb, var(--fg) 22%, transparent);
    z-index: 1;
}

@media (max-width: 900px) {
    /* Below this width the sitewide MobileNotice overlay (js/shared/mobile_notice.js,
       same 900px threshold) already blocks the page, but keep this
       non-broken in case that overlay is ever bypassed: the height:100vh /
       overflow:hidden math only holds for the side-by-side two-column
       layout — in a single-column stack, revert to normal document scroll,
       same as main.home-page's own @media (max-width: 1220px) override. */
    main.weight-page {
        height: auto;
        min-height: 100vh;
        overflow: visible;
    }

    .weight__layout {
        grid-template-columns: 1fr;
    }

    .weight__rows-col,
    .weight__chart-col {
        min-height: auto;
    }

    .weight__days {
        flex: none;
        max-height: 60vh;
    }

    .weight__charts {
        flex: none;
        overflow-y: visible;
    }

    .weight-chart {
        flex: none;
    }

    .weight-chart__svg-wrap {
        flex: none;
        height: 220px;
    }
}

/* ==========================================================================
   Anxiety level («Тревога», сфера «Психика»)
   Same viewport-fill / two-column shell as .weight above, adapted for a
   single 1-10 rating series plus a free-text comment column instead of
   weight's two numeric metrics.
   ========================================================================== */

main.anxiety-level-page {
    /* Wider than main.weight-page's 1120px — this page's left column carries
       an extra free-text comment field, so it needs a bigger overall budget
       to leave the chart column just as roomy. Overrides the generic
       `main { max-width: 640px }` — without this, the inner .anxiety-level
       section's own max-width is moot since its parent <main> is narrower. */
    max-width: 1520px;
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
    /* Top padding must clear the fixed .topbar (56px) — 3rem (48px), copied
       from main.weight-page, undershoots it, so the title's own top few
       pixels render behind the bar. 4rem matches the generic `main`
       selector's own top padding (the spacing anxiety-diary.html and every
       other non-viewport-fill page already uses), clearing the topbar with
       the same ~8px gap those pages get instead of a bespoke wider one. */
    padding: 4rem 1rem 1rem;
}

.anxiety-level {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.anxiety-level__header {
    flex: 0 0 auto;
    margin-bottom: 1rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.anxiety-level__clear-btn {
    border: 1px solid var(--border);
    border-radius: 8px;
    background: transparent;
    color: var(--muted);
    font-family: var(--font-base);
    font-size: 0.85rem;
    padding: 0.4rem 0.75rem;
    cursor: pointer;
}

.anxiety-level__clear-btn:hover {
    border-color: var(--danger);
    color: var(--danger);
}

.anxiety-level .budget__month-nav {
    flex: 0 0 auto;
}

.anxiety-level__layout {
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(300px, 380px) 1fr;
    gap: 1.5rem;
    align-items: stretch;
}

.anxiety-level__rows-col,
.anxiety-level__chart-col {
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
}

.anxiety-level__labels-row {
    flex: 0 0 auto;
    display: grid;
    grid-template-columns: 56px 70px minmax(0, 1fr);
    gap: 10px;
    padding: 0 1rem 0.5rem;
}

.anxiety-level__label {
    font-size: 0.85rem;
    color: var(--muted);
}

.anxiety-level__days {
    flex: 1 1 auto;
    min-height: 0;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.25rem 1rem;
    overflow-y: auto;
}

.anxiety-level__row {
    display: grid;
    /* minmax(0, 1fr) — not a bare 1fr — so the comment input's intrinsic
       content width can't force this column (and therefore the whole rows
       column, capped at 380px above) wider than intended, starving the
       chart of space. Same fix `.ritual__row` already applies. */
    grid-template-columns: 56px 70px minmax(0, 1fr);
    gap: 10px;
    align-items: center;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--border);
}

.anxiety-level__row:last-child {
    border-bottom: none;
}

.anxiety-level__row-date {
    font-weight: 500;
    color: var(--fg);
    white-space: nowrap;
    font-size: 0.9rem;
}

.anxiety-level__row-weekday {
    font-size: 0.78rem;
    color: var(--muted);
}

.anxiety-level__input {
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.95rem;
    padding: 0.4rem 0.5rem;
    width: 100%;
    box-sizing: border-box;
}

.anxiety-level__input:focus {
    outline: none;
    border-color: var(--accent);
}

/* No stepper arrows on the rating input (mirrors .weight__input) — hide
   the native browser spinner too so it doesn't reappear unstyled. */
.anxiety-level__input--level {
    -moz-appearance: textfield;
    appearance: textfield;
    text-align: center;
}

.anxiety-level__input--level::-webkit-inner-spin-button,
.anxiety-level__input--level::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.anxiety-level__chart-wrap {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
}

.anxiety-chart {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1rem 1.1rem 0.5rem;
    color: var(--accent);
}

.anxiety-chart__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
    flex-wrap: wrap;
}

.anxiety-chart__title {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 600;
    color: var(--fg);
    font-size: 0.95rem;
}

.anxiety-chart__dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.anxiety-chart__stat {
    font-weight: 700;
    color: var(--fg);
    font-size: 1rem;
}

.anxiety-chart__delta {
    font-weight: 500;
    color: var(--muted);
    font-size: 0.85rem;
}

.anxiety-chart__empty {
    color: var(--muted);
    font-style: italic;
    font-size: 0.9rem;
    padding: 1.5rem 0 2rem;
    text-align: center;
}

.anxiety-chart__svg-wrap {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    margin: 0 -0.1rem;
}

.anxiety-chart__svg-inner {
    flex: 1 1 auto;
    min-height: 0;
    position: relative;
}

.anxiety-chart__svg {
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
}

.anxiety-chart__grid {
    stroke: var(--chart-grid);
    stroke-width: 1;
}

.anxiety-chart__axis-label {
    fill: var(--muted);
    font-family: var(--font-base);
    font-size: 9px;
}

.anxiety-chart__axis-label--y {
    text-anchor: end;
    dominant-baseline: middle;
}

.anxiety-chart__axis-label--x {
    text-anchor: middle;
}

.anxiety-chart__line {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.anxiety-chart__point {
    fill: currentColor;
}

.anxiety-chart__point--last {
    stroke: var(--chart-dot-ring);
    stroke-width: 2;
}

.anxiety-chart__crosshair {
    stroke: var(--muted);
    stroke-width: 1;
    stroke-dasharray: 3 3;
    pointer-events: none;
}

.anxiety-chart__crosshair-dot {
    fill: currentColor;
    stroke: var(--chart-dot-ring);
    stroke-width: 2;
    pointer-events: none;
}

.anxiety-chart__hitrect {
    fill: transparent;
    cursor: crosshair;
}

.anxiety-chart__tooltip {
    position: absolute;
    transform: translate(-50%, calc(-100% - 8px));
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.3rem 0.55rem;
    font-size: 0.8rem;
    color: var(--fg);
    white-space: nowrap;
    pointer-events: none;
    box-shadow: 0 4px 12px color-mix(in srgb, var(--fg) 22%, transparent);
    z-index: 1;
}

@media (max-width: 900px) {
    /* Same reasoning as .weight's own override above — the sitewide
       MobileNotice overlay already blocks this page below 900px, but
       keep the layout non-broken in case that overlay is ever bypassed. */
    main.anxiety-level-page {
        height: auto;
        min-height: 100vh;
        overflow: visible;
    }

    .anxiety-level__layout {
        grid-template-columns: 1fr;
    }

    .anxiety-level__rows-col,
    .anxiety-level__chart-col {
        min-height: auto;
    }

    .anxiety-level__days {
        flex: none;
        max-height: 60vh;
    }

    .anxiety-chart__svg-wrap {
        flex: none;
        height: 220px;
    }
}

/* ==========================================================================
   Finance history
   ========================================================================== */

.finance-history {
    max-width: 1000px;
}

.finance-history__header {
    margin-bottom: 1.25rem;
}

.finance-history__filters {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
    padding: 0.75rem 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
}

.finance-history__filter {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 180px;
}

.finance-history__filter label {
    font-size: 0.8rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.finance-history__loading,
.finance-history__error {
    font-size: 0.95rem;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    margin-bottom: 1rem;
}

.finance-history__loading {
    color: var(--muted);
    background: var(--code-bg);
}

.finance-history__error {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
    border: 1px solid var(--danger);
}

.finance-history__list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.finance-history__row {
    display: grid;
    grid-template-columns: 150px minmax(0, 1.6fr) minmax(0, 1.6fr) 140px 36px 36px;
    gap: 12px;
    align-items: center;
    padding: 0.75rem 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
}

/* Edit mode swaps the four value cells for inputs (see .ritual__input) —
   the row keeps the same six-column grid, just with taller cell content. */
.finance-history__row--editing {
    align-items: start;
}

.finance-history__date {
    color: var(--muted);
    font-size: 0.9rem;
}

.finance-history__category {
    font-weight: 600;
    color: var(--fg);
}

.finance-history__note {
    font-size: 0.85rem;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.finance-history__amount {
    font-weight: 700;
    color: var(--accent);
    text-align: right;
}

.finance-history__edit,
.finance-history__remove,
.finance-history__save,
.finance-history__cancel {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
}

.finance-history__edit:hover,
.finance-history__save:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.finance-history__remove:hover,
.finance-history__cancel:hover {
    color: var(--danger);
    border-color: var(--danger);
}

/* ── Add-expense form (above the list) ──────────────────────────────── */

.finance-history__add {
    display: grid;
    /* The fr-tracks have no min-width floor — they take whatever space
       remains after the fixed-size date / amount / button columns + gaps.
       At a 960px page max-width that's ~210px per fr-track on desktop, and
       gracefully shrinks to whatever the viewport can spare on mobile.
       Earlier we used minmax(200px, fr) here which forced the row wider
       than its container whenever the viewport was too narrow. */
    grid-template-columns: 150px minmax(0, 1.6fr) minmax(0, 1.6fr) 140px auto;
    gap: 12px;
    align-items: start;
    padding: 0.85rem 1rem;
    margin-bottom: 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
}

.finance-history__add-btn {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    padding: 0.65rem 1.5rem;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: opacity 0.15s;
    align-self: stretch;
}

.finance-history__add-btn:hover {
    opacity: 0.85;
}

.finance-history__add-btn:disabled {
    opacity: 0.6;
    cursor: default;
}

/* Styled date-picker trigger — opens the shared `CalendarPicker` popup
 * (js/tasks/calendar.js) instead of the native `<input type="date">`.
 * Same box shape as `.ritual__input` (border/radius/padding) so it lines
 * up with the row's other fields, laid out as label + calendar icon like
 * `.rd-one-shot-date` in reminders.html. */
.finance-history__date-btn {
    appearance: none;
    width: 100%;
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    font: inherit;
    font-size: 0.95rem;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.65rem 0.85rem;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.12s, color 0.12s;
}

.finance-history__date-btn:hover {
    border-color: var(--accent);
}

.finance-history__date-btn:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.finance-history__date-btn-label {
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.finance-history__date-btn-icon {
    flex: 0 0 auto;
    display: inline-flex;
    color: var(--muted);
    transition: color 0.12s;
}

.finance-history__date-btn:hover .finance-history__date-btn-icon {
    color: var(--accent);
}

.finance-history__empty {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--muted);
    font-style: italic;
    background: var(--bg-elevated);
    border: 1px dashed var(--border);
    border-radius: 10px;
}

/* ==========================================================================
   Inspiration (психика → «Вдохновение») — flat list of things that inspire
   ========================================================================== */

.inspiration {
    max-width: 720px;
}

.inspiration__header {
    margin-bottom: 1.25rem;
}

.inspiration__add-trigger {
    display: inline-block;
    margin-bottom: 1.25rem;
    padding: 0.6rem 1.25rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 0.9rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
}

.inspiration-add__btn {
    display: inline-block;
    margin-bottom: 1.25rem;
    padding: 0.6rem 1.25rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 0.9rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    transition: opacity 0.15s;
}

.inspiration-add__btn:hover {
    opacity: 0.85;
}

.inspiration-add__input {
    display: block;
    width: 100%;
    box-sizing: border-box;
    margin-bottom: 1.25rem;
    padding: 0.65rem 0.9rem;
    background: var(--bg-elevated);
    border: 1px solid var(--accent);
    border-radius: 8px;
    color: var(--fg);
    font-size: 0.95rem;
    font-family: var(--font-base);
}

.inspiration__loading,
.inspiration__error {
    font-size: 0.95rem;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    margin-bottom: 1rem;
}

.inspiration__loading {
    color: var(--muted);
    background: var(--code-bg);
}

.inspiration__error {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
    border: 1px solid var(--danger);
}

.inspiration__list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.inspiration__row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0.75rem 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
}

.inspiration__text {
    flex: 1;
    color: var(--fg);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.inspiration__remove {
    flex-shrink: 0;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.3rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
}

.inspiration__remove:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.inspiration__empty {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--muted);
    font-style: italic;
    background: var(--bg-elevated);
    border: 1px dashed var(--border);
    border-radius: 10px;
}

/* ==========================================================================
   Anxiety diary (психика → «Дневник тревожности»)
   ========================================================================== */

.anxiety-diary {
    max-width: 760px;
}

.anxiety-diary__header {
    margin-bottom: 1.25rem;
}

.anxiety-diary__add-trigger,
.anxiety-diary-add__btn {
    display: inline-block;
    margin-bottom: 1.25rem;
    padding: 0.6rem 1.25rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 0.9rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
}

.anxiety-diary-add__btn {
    transition: opacity 0.15s;
}

.anxiety-diary-add__btn:hover {
    opacity: 0.85;
}

.anxiety-diary-add__input {
    display: block;
    width: 100%;
    box-sizing: border-box;
    margin-bottom: 1.25rem;
    padding: 0.65rem 0.9rem;
    background: var(--bg-elevated);
    border: 1px solid var(--accent);
    border-radius: 8px;
    color: var(--fg);
    font-size: 0.95rem;
    font-family: var(--font-base);
}

.anxiety-diary-add__input::placeholder {
    color: var(--muted);
    opacity: 0.6;
}

.anxiety-diary__loading,
.anxiety-diary__error {
    font-size: 0.95rem;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    margin-bottom: 1rem;
}

.anxiety-diary__loading {
    color: var(--muted);
    background: var(--code-bg);
}

.anxiety-diary__error {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
    border: 1px solid var(--danger);
}

.anxiety-diary__list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.anxiety-diary__row {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
}

.anxiety-diary__toggle {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0.75rem 1rem;
    background: transparent;
    border: none;
    color: var(--fg);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.anxiety-diary__toggle:hover {
    background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.anxiety-diary__icon {
    flex-shrink: 0;
    display: inline-flex;
}

.anxiety-diary__icon--none {
    color: var(--muted);
}

.anxiety-diary__icon--pending {
    color: var(--warn);
}

.anxiety-diary__icon--done {
    color: var(--success);
}

.anxiety-diary__date {
    flex-shrink: 0;
    color: var(--muted);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}

.anxiety-diary__preview {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--fg);
}

.anxiety-diary__chevron {
    flex-shrink: 0;
    display: inline-flex;
    color: var(--muted);
    transition: transform 0.15s;
}

.anxiety-diary__toggle[aria-expanded="true"] .anxiety-diary__chevron {
    transform: rotate(180deg);
}

.anxiety-diary__body {
    padding: 0 1rem 1rem;
    border-top: 1px solid var(--border);
}

.anxiety-diary__text {
    padding: 0.85rem 0;
    color: var(--fg);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    border-bottom: 1px solid var(--border);
}

.anxiety-diary__analysis {
    padding: 0.85rem 0;
}

.anxiety-diary__analysis-empty {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.anxiety-diary__create-analysis-btn {
    padding: 0.5rem 1rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 0.85rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
}

.anxiety-diary__field {
    margin-bottom: 0.85rem;
}

.anxiety-diary__field-label {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.02em;
    margin-bottom: 0.3rem;
}

.anxiety-diary__field-value {
    color: var(--fg);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    min-height: 1.4em;
}

.anxiety-diary__field-value:empty::before {
    content: attr(data-placeholder);
    color: var(--muted);
    font-style: italic;
    opacity: 0.6;
}

/* Эмоции + слайдер интенсивности (anxiety-diary.js::renderEmotionsSection). */

.anxiety-diary__emotions {
    margin-bottom: 1rem;
}

.anxiety-diary__emotion-row {
    display: grid;
    grid-template-columns: minmax(120px, 1fr) minmax(120px, 1.5fr) auto auto;
    gap: 8px;
    align-items: center;
    margin-bottom: 0.4rem;
}

/* Name control: either a UIDropdown (catalog, emoji + name per option)
   or, after picking "Своё…"/for legacy custom names, an emoji + free-text
   input — see anxiety-diary.js::mountEmotionNameControl. */
.anxiety-diary__emotion-name-wrap {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

.anxiety-diary__emotion-emoji {
    flex-shrink: 0;
    font-size: 1rem;
    line-height: 1;
}

.anxiety-diary__emotion-name-input {
    flex: 1;
    min-width: 0;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.4rem 0.6rem;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.9rem;
}

.anxiety-diary__emotion-name-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.anxiety-diary__emotion-name-input::placeholder {
    color: var(--muted);
    opacity: 0.6;
}

.anxiety-diary__emotion-value {
    color: var(--fg);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
    min-width: 3em;
    text-align: right;
}

.anxiety-diary__emotion-remove {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.3rem;
    color: var(--muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.anxiety-diary__emotion-remove:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.anxiety-diary__add-emotion-btn {
    background: transparent;
    border: 1px dashed var(--border);
    border-radius: 6px;
    padding: 0.45rem 0.85rem;
    color: var(--muted);
    font-size: 0.85rem;
    font-family: var(--font-base);
    cursor: pointer;
    width: 100%;
    margin-top: 0.3rem;
}

.anxiety-diary__add-emotion-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}

/* Кастомный range-слайдер — без вендорных префиксов браузер рисует
   серую полосу и круглый бегунок, которые выбиваются из темы. Те же
   семантические токены, что у прогресс-баров и чипов. */
.anxiety-diary__emotion-slider {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 6px;
    background: var(--border);
    border-radius: 3px;
    outline: none;
}

.anxiety-diary__emotion-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--accent);
    cursor: pointer;
    border: 2px solid var(--bg-elevated);
    box-shadow: 0 1px 3px color-mix(in srgb, var(--fg) 30%, transparent);
}

.anxiety-diary__emotion-slider::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--accent);
    cursor: pointer;
    border: 2px solid var(--bg-elevated);
    box-shadow: 0 1px 3px color-mix(in srgb, var(--fg) 30%, transparent);
}

.anxiety-diary__emotion-slider:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
}

/* Когнитивные искажения — мульти-чекбокс из закрытого каталога. */

.anxiety-diary__distortions {
    margin-bottom: 1rem;
}

.anxiety-diary__distortion-catalog {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 0.5rem;
}

.anxiety-diary__distortion-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 0.35rem 0.75rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--muted);
    font-size: 0.85rem;
    font-family: var(--font-base);
    cursor: pointer;
    user-select: none;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.anxiety-diary__distortion-chip:hover {
    border-color: var(--accent);
    color: var(--fg);
}

.anxiety-diary__distortion-chip.is-selected {
    background: color-mix(in srgb, var(--accent) 18%, var(--bg-elevated));
    border-color: var(--accent);
    color: var(--fg);
}

.anxiety-diary__distortion-custom {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 0.5rem;
}

.anxiety-diary__distortion-chip.is-custom {
    padding-right: 0.35rem;
}

.anxiety-diary__distortion-chip-remove {
    background: transparent;
    border: none;
    color: inherit;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    padding: 0;
    opacity: 0.7;
}

.anxiety-diary__distortion-chip-remove:hover {
    opacity: 1;
}

.anxiety-diary__add-distortion-btn {
    background: transparent;
    border: 1px dashed var(--border);
    border-radius: 6px;
    padding: 0.4rem 0.85rem;
    color: var(--muted);
    font-size: 0.85rem;
    font-family: var(--font-base);
    cursor: pointer;
}

.anxiety-diary__add-distortion-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.anxiety-diary__add-distortion-input {
    background: var(--bg);
    border: 1px solid var(--accent);
    border-radius: 6px;
    padding: 0.4rem 0.7rem;
    color: var(--fg);
    font-size: 0.9rem;
    font-family: var(--font-base);
    min-width: 200px;
}

.anxiety-diary__add-distortion-input:focus {
    outline: none;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.anxiety-diary__add-distortion-input::placeholder {
    color: var(--muted);
    opacity: 0.6;
}

/* "?" info button on each catalog distortion chip + its popover. */
.anxiety-diary__distortion-info-btn {
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--muted) 18%, transparent);
    border: none;
    border-radius: 50%;
    color: var(--muted);
    font-size: 0.7rem;
    line-height: 1;
    cursor: pointer;
}

.anxiety-diary__distortion-info-btn:hover {
    background: var(--accent);
    color: var(--accent-fg);
}

.anxiety-diary__distortion-info {
    z-index: 9999;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.7rem 0.85rem;
    box-shadow: 0 4px 16px color-mix(in srgb, var(--fg) 12%, transparent),
                0 1px 4px color-mix(in srgb, var(--fg) 6%, transparent);
}

.anxiety-diary__distortion-info-title {
    font-weight: 600;
    color: var(--fg);
    margin-bottom: 0.35rem;
}

.anxiety-diary__distortion-info-desc {
    color: var(--fg);
    font-size: 0.88rem;
    line-height: 1.4;
    margin-bottom: 0.35rem;
}

.anxiety-diary__distortion-info-example {
    color: var(--muted);
    font-size: 0.85rem;
    font-style: italic;
    line-height: 1.4;
}

.anxiety-diary__actions-done {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 1rem 0;
    color: var(--fg);
    font-size: 0.9rem;
    cursor: pointer;
}

.anxiety-diary__delete-analysis,
.anxiety-diary__delete-entry {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.4rem 0.85rem;
    color: var(--muted);
    font-size: 0.85rem;
    font-family: var(--font-base);
    cursor: pointer;
}

.anxiety-diary__delete-analysis:hover,
.anxiety-diary__delete-entry:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.anxiety-diary__delete-entry {
    margin-top: 0.5rem;
}

.anxiety-diary__empty {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--muted);
    font-style: italic;
    background: var(--bg-elevated);
    border: 1px dashed var(--border);
    border-radius: 10px;
}

/* Fear-analysis sections — same visual language as emotions/distortions
 * so the form reads as one diary, just with a different shape per type.
 */
.anxiety-diary__intensity {
    margin-top: 0.75rem;
}
.anxiety-diary__intensity-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.25rem;
}
.anxiety-diary__intensity-slider {
    flex: 1;
    accent-color: var(--accent);
}
.anxiety-diary__intensity-value {
    min-width: 3rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

.anxiety-diary__sensation-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) auto;
    gap: 0.5rem;
    align-items: center;
    margin-top: 0.4rem;
}
/* Each grid cell hosts a UIDropdown (or a custom-input fallback) — the
 * wrapper fills its cell horizontally and lets the dropdown trigger
 * shrink to its grid track, so a long location / descriptor label still
 * ellipsises instead of pushing the row wider than the column.
 */
.anxiety-diary__sensation-cell {
    min-width: 0;
    width: 100%;
}
.anxiety-diary__sensation-cell .ui-dropdown {
    width: 100%;
}
.anxiety-diary__sensation-cell .ui-dropdown__trigger {
    width: 100%;
    justify-content: flex-start;
}
.anxiety-diary__sensation-input {
    width: 100%;
    padding: 0.4rem 0.6rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--fg);
    font: inherit;
}
.anxiety-diary__sensation-input:focus {
    outline: none;
    border-color: var(--accent);
}
.anxiety-diary__add-sensation-btn,
.anxiety-diary__add-avoidance-btn {
    margin-top: 0.5rem;
    background: transparent;
    border: 1px dashed var(--border);
    color: var(--muted);
    padding: 0.3rem 0.75rem;
    border-radius: 6px;
    cursor: pointer;
    font: inherit;
}
.anxiety-diary__add-sensation-btn:hover,
.anxiety-diary__add-avoidance-btn:hover {
    color: var(--fg);
    border-color: var(--accent);
}

/* ==========================================================================
   Experience diary (эффективность → «Дневник опыта»)
   ========================================================================== */

.experience-diary {
    max-width: 760px;
}

.experience-diary__header {
    margin-bottom: 1.25rem;
}

.experience-diary__add-trigger {
    display: inline-block;
    margin-bottom: 1.25rem;
    padding: 0.6rem 1.25rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 0.9rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
}

.experience-diary__add-panel {
    margin-bottom: 1.25rem;
    padding: 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--accent);
    border-radius: 10px;
}

.experience-diary__add-rows {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.experience-diary__add-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.5rem;
    align-items: stretch;
}

.experience-diary__add-row-input {
    width: 100%;
    box-sizing: border-box;
    resize: vertical;
    min-height: 2.6rem;
    max-height: 12rem;
    font: inherit;
    font-size: 0.95rem;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.65rem 0.85rem;
}

.experience-diary__add-row-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.experience-diary__add-row-input::placeholder {
    color: var(--muted);
    opacity: 0.6;
}

.experience-diary__add-row-remove {
    background: transparent;
    border: 1px solid var(--border);
    padding: 0.3rem;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    height: 100%;
    cursor: pointer;
}

.experience-diary__add-row-remove:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.experience-diary__add-controls {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.75rem;
    margin-top: 0.75rem;
}

.experience-diary__add-actions {
    display: flex;
    gap: 0.5rem;
}

.experience-diary__add-cancel,
.experience-diary__add-save {
    padding: 0.5rem 1rem;
    border-radius: 8px;
    font-size: 0.85rem;
    font-family: var(--font-base);
    cursor: pointer;
}

.experience-diary__add-cancel {
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
}

.experience-diary__add-cancel:hover {
    color: var(--fg);
    border-color: var(--fg);
}

.experience-diary__add-save {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    font-weight: 600;
}

.experience-diary__loading,
.experience-diary__error {
    font-size: 0.95rem;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    margin-bottom: 1rem;
}

.experience-diary__loading {
    color: var(--muted);
    background: var(--code-bg);
}

.experience-diary__error {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
    border: 1px solid var(--danger);
}

.experience-diary__list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.experience-diary__row {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
}

.experience-diary__toggle {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0.75rem 1rem;
    background: transparent;
    border: none;
    color: var(--fg);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.experience-diary__toggle:hover {
    background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.experience-diary__icon {
    flex-shrink: 0;
    display: inline-flex;
}

.experience-diary__icon--pending {
    color: var(--warn);
}

.experience-diary__icon--done {
    color: var(--success);
}

.experience-diary__date {
    flex-shrink: 0;
    color: var(--muted);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}

.experience-diary__preview {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--fg);
}

.experience-diary__chevron {
    flex-shrink: 0;
    display: inline-flex;
    color: var(--muted);
    transition: transform 0.15s;
}

.experience-diary__toggle[aria-expanded="true"] .experience-diary__chevron {
    transform: rotate(180deg);
}

.experience-diary__body {
    padding: 0 1rem 1rem;
    border-top: 1px solid var(--border);
}

.experience-diary__text {
    padding: 0.85rem 0;
    color: var(--fg);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    border-bottom: 1px solid var(--border);
}

.experience-diary__actions-created {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 1rem 0;
    color: var(--fg);
    font-size: 0.9rem;
    cursor: pointer;
}

.experience-diary__delete-entry {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.4rem 0.85rem;
    color: var(--muted);
    font-size: 0.85rem;
    font-family: var(--font-base);
    cursor: pointer;
}

.experience-diary__delete-entry:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.experience-diary__empty {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--muted);
    font-style: italic;
    background: var(--bg-elevated);
    border: 1px dashed var(--border);
    border-radius: 10px;
}

/* ==========================================================================
   Life diary (сфера «Жизнь» → «Дневник») — month-browsable journal,
   several free-text entries per day, grouped under a day heading.
   ========================================================================== */

.life-diary {
    max-width: 760px;
}

.life-diary__header {
    margin-bottom: 1.25rem;
}

.life-diary__add-trigger {
    display: inline-block;
    margin-bottom: 1.25rem;
    padding: 0.6rem 1.25rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 0.9rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
}

.life-diary__add-panel {
    margin-bottom: 1.25rem;
    padding: 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--accent);
    border-radius: 10px;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.life-diary__date-btn {
    align-self: flex-start;
    appearance: none;
    min-width: 180px;
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    font: inherit;
    font-size: 0.9rem;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.5rem 0.75rem;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.12s, color 0.12s;
}

.life-diary__date-btn:hover {
    border-color: var(--accent);
}

.life-diary__date-btn-icon {
    flex: 0 0 auto;
    display: inline-flex;
    color: var(--muted);
}

.life-diary__textarea-wrap {
    position: relative;
}

.life-diary__add-text {
    width: 100%;
    box-sizing: border-box;
    resize: vertical;
    min-height: 4rem;
    max-height: 16rem;
    font: inherit;
    font-size: 0.95rem;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.65rem 0.85rem;
    padding-right: 1.9rem;
}

.life-diary__add-text:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.life-diary__add-controls {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.75rem;
}

.life-diary__add-cancel,
.life-diary__add-save {
    padding: 0.5rem 1rem;
    border-radius: 8px;
    font-size: 0.85rem;
    font-family: var(--font-base);
    cursor: pointer;
}

.life-diary__add-cancel {
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
}

.life-diary__add-cancel:hover {
    color: var(--fg);
    border-color: var(--fg);
}

.life-diary__add-save {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    font-weight: 600;
}

.life-diary__list {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.life-diary__day-group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.life-diary__day-heading {
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--fg);
    text-transform: capitalize;
    padding-left: 0.15rem;
}

.life-diary__row {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
}

.life-diary__toggle {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0.75rem 1rem;
    background: transparent;
    border: none;
    color: var(--fg);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.life-diary__toggle:hover {
    background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.life-diary__time {
    flex-shrink: 0;
    color: var(--muted);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}

.life-diary__preview {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--fg);
}

.life-diary__chevron {
    flex-shrink: 0;
    display: inline-flex;
    color: var(--muted);
    transition: transform 0.15s;
}

.life-diary__toggle[aria-expanded="true"] .life-diary__chevron {
    transform: rotate(180deg);
}

.life-diary__body {
    padding: 0 1rem 1rem;
    border-top: 1px solid var(--border);
}

.life-diary__text {
    padding: 0.85rem 0;
    color: var(--fg);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    border-bottom: 1px solid var(--border);
}

.life-diary__delete-entry {
    margin-top: 1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.4rem;
    color: var(--muted);
    cursor: pointer;
}

.life-diary__delete-entry:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.life-diary__empty {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--muted);
    font-style: italic;
    background: var(--bg-elevated);
    border: 1px dashed var(--border);
    border-radius: 10px;
}

/* ==========================================================================
   Heatmap (shared) — GitHub-contributions-style day/week grid engine, plus
   the stats row / set-clear popover every metric page built on it wires the
   same way (see js/shared/heatmap.js). Cell/legend/level-button coloring is
   set inline via CSS vars (Heatmap.render's / buildLevelPicker's colorVar),
   not per-feature [data-level] selectors here — a new metric needs no new
   CSS, only a palette (theme.css) + config.
   ========================================================================== */

.heatmap__scroll {
    overflow: hidden;
    width: 100%;
    box-sizing: border-box;
}

.heatmap__grid {
    display: grid;
    gap: 2px;
    width: 100%;
    box-sizing: border-box;
    overflow: hidden;
    /* The last row's bottom edge sits flush against this box's own
       boundary (row heights are explicit `grid-template-rows` tracks,
       no slack below them) — a hovered/today cell's `outline-offset`
       pushes its outline past the cell, which `overflow: hidden` above
       then clips clean off. 3px covers the largest protrusion in play:
       `.heatmap__cell--today`'s 2px outline-width + 1px outline-offset
       (plain hover is only 1px + 1px, so already covered). Bottom
       padding alone gives it room without affecting horizontal layout —
       computeCellSize (heatmap.js) sizes columns off the *parent*
       wrapper's clientWidth, not this element's, so adding *horizontal*
       padding here would shrink this box's content width below what
       the columns were sized for and clip the last column instead;
       padding-bottom has no such side effect since every row track is
       already explicit. */
    padding-bottom: 3px;
}

.heatmap__weekday-label {
    display: flex;
    align-items: center;
    font-size: 0.65rem;
    color: var(--muted);
    white-space: nowrap;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
}

.heatmap__month-label {
    display: flex;
    align-items: flex-end;
    font-size: 0.7rem;
    color: var(--muted);
    white-space: nowrap;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
}

.heatmap__cell {
    width: 100%;
    height: 100%;
    min-width: 0;
    min-height: 0;
    padding: 0;
    border: none;
    border-radius: 2px;
    background: var(--heatmap-empty);
    cursor: pointer;
    transition: outline-color 0.1s;
    outline: 1px solid transparent;
    outline-offset: 1px;
}

.heatmap__cell:hover {
    outline-color: var(--accent);
}

.heatmap__cell:disabled {
    cursor: default;
}

.heatmap__cell--future {
    background: transparent;
}

.heatmap__cell--today {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.heatmap__legend {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 0.75rem;
    color: var(--muted);
    font-size: 0.75rem;
}

.heatmap__legend-swatch {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    background: var(--heatmap-empty);
}

/* Stats + the quick-set trigger sit in one row above the grid, all sized to
   the trigger button's height (same padding/border-radius, single-line
   content) rather than a two-line stacked look. */
.heatmap__toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1.5rem;
    margin-bottom: 1.25rem;
}

.heatmap__stats {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.heatmap__stat-tile {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.6rem 1.1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
}

.heatmap__stat-value {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 1rem;
    font-weight: 700;
    color: var(--fg);
    font-variant-numeric: tabular-nums;
}

.heatmap__stat-unit {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--muted);
}

.heatmap__stat-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex-shrink: 0;
}

.heatmap__stat-label {
    font-size: 0.85rem;
    color: var(--muted);
    white-space: nowrap;
}

.heatmap__stats.heatmap__stats--empty {
    display: block;
    padding: 0.6rem 1.1rem;
    color: var(--muted);
    font-size: 0.9rem;
    font-style: italic;
    background: var(--bg-elevated);
    border: 1px dashed var(--border);
    border-radius: 8px;
}

.heatmap__quick-trigger {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--accent);
    background: transparent;
    border: 1.5px solid var(--accent);
    border-radius: 8px;
    padding: 0.6rem 1.1rem;
    cursor: pointer;
    transition: background 0.12s, opacity 0.12s;
}

.heatmap__quick-trigger:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.heatmap__popover {
    position: fixed;
    z-index: 400;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.65rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--shadow-color) 22%, transparent);
}

.heatmap__popover-date {
    font-size: 0.8rem;
    color: var(--muted);
    text-align: center;
}

.heatmap__popover-date--btn {
    appearance: none;
    font: inherit;
    background: transparent;
    border: none;
    border-bottom: 1px dashed var(--muted);
    color: var(--muted);
    cursor: pointer;
    padding: 0;
    transition: color 0.12s, border-color 0.12s;
}

.heatmap__popover-date--btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.heatmap__level-picker {
    display: flex;
    gap: 4px;
}

.heatmap__level-btn {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-base);
    font-weight: 600;
    font-size: 0.85rem;
    cursor: pointer;
}

.heatmap__level-btn:hover {
    border-color: var(--accent);
}

/* .heatmap__level-btn--active carries no color of its own — buildLevelPicker
   sets the active button's background inline via colorVar(level), same
   convention as a grid cell's paintCell(). */

.heatmap__popover-clear {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.35rem;
    color: var(--muted);
    font-family: var(--font-base);
    font-size: 0.8rem;
    cursor: pointer;
}

.heatmap__popover-clear:hover {
    color: var(--danger);
    border-color: var(--danger);
}

/* ==========================================================================
   Area blocks (shared) — the sphere-page "+ Добавить блок" widgets
   (js/shared/area_blocks.js). Heatmap blocks reuse every .heatmap__* class
   above verbatim; this section only adds the block-list chrome and the
   .area-chart__* classes for the chart block type (a standalone extraction
   of .weight-chart__*, see js/shared/chart.js).
   ========================================================================== */

.area-blocks {
    margin-top: 2rem;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.area-blocks__block {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1.1rem 1.25rem;
}

.area-blocks__block-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
}

.area-blocks__block-title {
    font-weight: 700;
    font-size: 1rem;
    color: var(--fg);
}

.area-blocks__delete {
    appearance: none;
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: 0.25rem;
    border-radius: 6px;
    display: inline-flex;
    transition: color 0.12s, background 0.12s;
}

.area-blocks__delete:hover {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, transparent);
}

.area-blocks__add-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.25rem;
}

.area-blocks__add-btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--accent);
    background: transparent;
    border: 1.5px dashed var(--accent);
    border-radius: 8px;
    padding: 0.7rem 1.1rem;
    cursor: pointer;
    transition: background 0.12s;
    width: 100%;
    justify-content: center;
}

.area-blocks__add-btn:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.area-blocks__entry-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.75rem;
    flex-wrap: wrap;
}

.area-blocks__date-btn {
    appearance: none;
    font: inherit;
    font-size: 0.85rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.45rem 0.7rem;
    color: var(--fg);
    cursor: pointer;
}

.area-blocks__date-btn:hover {
    border-color: var(--accent);
}

.area-blocks__value-input {
    width: 6rem;
    font: inherit;
    font-size: 0.9rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.45rem 0.6rem;
    color: var(--fg);
}

.area-blocks__save-btn {
    appearance: none;
    font: inherit;
    font-size: 0.85rem;
    font-weight: 600;
    background: var(--accent);
    color: var(--accent-fg);
    border: none;
    border-radius: 6px;
    padding: 0.45rem 0.9rem;
    cursor: pointer;
    transition: opacity 0.12s;
}

.area-blocks__save-btn:hover {
    opacity: 0.9;
}

/* ── Type-choice popover (reuses .heatmap__popover chrome) ───────────── */

.area-blocks__type-popover {
    min-width: 160px;
}

.area-blocks__type-option {
    appearance: none;
    display: block;
    width: 100%;
    text-align: left;
    font: inherit;
    font-size: 0.9rem;
    background: transparent;
    border: none;
    border-radius: 6px;
    padding: 0.5rem 0.6rem;
    color: var(--fg);
    cursor: pointer;
}

.area-blocks__type-option:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    color: var(--accent);
}

/* ── Create-block form modal (reuses .modal/.modal__dialog chrome) ────── */

/* Deliberately *below* the shared Modal.askConfirm's z-index (300, see the
   comment on .modal above) — the "Далее" button's own confirm-before-create
   step opens that shared singleton on top of this overlay. Without this
   override both sit at the same z-index (.modal's default), and whichever
   DOM node happens to exist first wins the paint order: if Modal's
   singleton element was already created earlier on the page (e.g. a prior
   block delete's confirm), it would render *behind* this still-open form
   overlay instead of on top of it — the confirm dialog silently opens but
   is invisible, making "Далее" look like it does nothing. Same convention
   as .project-modal (z-index 200) sitting below .modal for the same reason. */
.area-blocks__modal {
    z-index: 200;
}

.area-blocks__form-dialog {
    max-width: 440px;
    max-height: calc(100vh - 2rem);
    overflow-y: auto;
}

.area-blocks__form {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    margin-bottom: 1.25rem;
}

.area-blocks__form-row {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.area-blocks__form-row-pair {
    display: flex;
    gap: 0.9rem;
}

.area-blocks__form-row-pair .area-blocks__form-row {
    flex: 1;
    min-width: 0;
}

.area-blocks__form-label {
    font-size: 0.8rem;
    color: var(--muted);
}

.area-blocks__form-input {
    font: inherit;
    font-size: 0.9rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.55rem 0.7rem;
    color: var(--fg);
}

.area-blocks__form-input:focus {
    outline: none;
    border-color: var(--accent);
}

/* Min/max range inputs are plain number fields, no stepper arrows —
   hide the native browser spinner too (same treatment as .budget__row). */
input[type="number"].area-blocks__form-input {
    -moz-appearance: textfield;
    appearance: textfield;
}

input[type="number"].area-blocks__form-input::-webkit-inner-spin-button,
input[type="number"].area-blocks__form-input::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* ── Chart block (js/shared/chart.js) ─────────────────────────────────── */

.area-chart {
    height: 220px;
    display: flex;
    flex-direction: column;
}

.area-chart__header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
    flex-wrap: wrap;
}

.area-chart__title {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 600;
    color: var(--fg);
    font-size: 0.95rem;
}

.area-chart__dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.area-chart__stat {
    font-weight: 700;
    color: var(--fg);
    font-size: 1rem;
}

.area-chart__delta {
    font-weight: 500;
    color: var(--muted);
    font-size: 0.85rem;
}

.area-chart__empty {
    color: var(--muted);
    font-style: italic;
    font-size: 0.9rem;
    padding: 1.5rem 0 2rem;
    text-align: center;
}

.area-chart__svg-wrap {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    margin: 0 -0.1rem;
}

.area-chart__svg-inner {
    flex: 1 1 auto;
    min-height: 0;
    position: relative;
}

.area-chart__svg {
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
}

.area-chart__grid {
    stroke: var(--chart-grid);
    stroke-width: 1;
}

.area-chart__axis-label {
    fill: var(--muted);
    font-family: var(--font-base);
    font-size: 9px;
}

.area-chart__axis-label--y {
    text-anchor: end;
    dominant-baseline: middle;
}

.area-chart__axis-label--x {
    text-anchor: middle;
}

.area-chart__line {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.area-chart__point {
    fill: currentColor;
}

.area-chart__point--last {
    stroke: var(--chart-dot-ring);
    stroke-width: 2;
}

.area-chart__crosshair {
    stroke: var(--muted);
    stroke-width: 1;
    stroke-dasharray: 3 3;
    pointer-events: none;
}

.area-chart__crosshair-dot {
    fill: currentColor;
    stroke: var(--chart-dot-ring);
    stroke-width: 2;
    pointer-events: none;
}

.area-chart__hitrect {
    fill: transparent;
    cursor: crosshair;
}

.area-chart__tooltip {
    position: absolute;
    transform: translate(-50%, calc(-100% - 8px));
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.3rem 0.55rem;
    font-size: 0.8rem;
    color: var(--fg);
    white-space: nowrap;
    pointer-events: none;
    box-shadow: 0 4px 12px color-mix(in srgb, var(--fg) 22%, transparent);
    z-index: 1;
}

/* ==========================================================================
   Templates modal exercise rows
   ========================================================================== */

.template-modal__name-input {
    flex: 1;
    background: transparent;
    border: none;
    border-bottom: 1.5px solid transparent;
    border-radius: 0;
    padding: 0.1rem 0;
    font-size: 1.1rem;
    font-weight: 700;
    font-family: var(--font-heading);
    color: var(--fg);
    min-width: 0;
    transition: border-color 0.15s;
}

.template-modal__name-input:focus {
    outline: none;
    border-bottom-color: var(--accent);
}

.template-modal__name-input::placeholder {
    color: var(--muted);
    font-weight: 400;
}

.template-form__exercises {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.template-form__exercise-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 0.4rem;
    align-items: center;
    border-radius: 6px;
    transition: opacity 0.15s;
}

.template-form__exercise-row--dragging {
    opacity: 0.35;
}

.template-form__exercise-row--drop-top {
    border-top: 2px solid var(--accent);
}

.template-form__exercise-row--drop-bottom {
    border-bottom: 2px solid var(--accent);
}

.template-form__drag-handle {
    background: transparent;
    border: none;
    padding: 0.3rem 0.25rem;
    color: var(--muted);
    cursor: grab;
    display: inline-flex;
    align-items: center;
    border-radius: 4px;
    touch-action: none;
}

.template-form__drag-handle:active {
    cursor: grabbing;
}

.template-form__drag-handle:hover {
    color: var(--fg);
}

.template-form__actions {
    display: flex;
    padding-top: 0.25rem;
}

/* ==========================================================================
   Workouts dashboard
   ========================================================================== */

.workouts {
    max-width: 900px;
}

.workouts__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.5rem;
    flex-wrap: wrap;
}

.workouts__back {
    font-size: 0.9rem;
    color: var(--muted);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    transition: color 0.15s;
}

.workouts__back:hover {
    color: var(--fg);
}

.workouts__new-btn {
    padding: 0.6rem 1.25rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 8px;
    font-size: 0.95rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    transition: opacity 0.15s;
}

.workouts__new-btn:hover {
    opacity: 0.85;
}

.workouts__new-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.workouts__new-split {
    position: relative;
    display: inline-flex;
    align-items: stretch;
}

.workouts__new-split .workouts__new-btn {
    border-radius: 8px 0 0 8px;
}

.workouts__template-toggle {
    padding: 0 0.65rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-left: 1px solid color-mix(in srgb, var(--accent-fg) 25%, transparent);
    border-radius: 0 8px 8px 0;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    transition: opacity 0.15s;
}

.workouts__template-toggle:hover {
    opacity: 0.85;
}

.workouts__template-toggle:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.workouts__template-list {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 30;
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    min-width: 200px;
    max-height: 14rem;
    overflow-y: auto;
    box-shadow: 0 4px 16px color-mix(in srgb, var(--fg) 12%, transparent);
}

.workouts__template-item {
    padding: 0.55rem 1rem;
    cursor: pointer;
    font-size: 0.9rem;
    color: var(--fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.workouts__template-item:hover {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}

.workouts__template-item--hint {
    color: var(--muted);
    cursor: default;
    font-style: italic;
}

.workouts__template-item--hint:hover {
    background: none;
}

.workouts__loading,
.workouts__error,
.workouts__empty {
    font-size: 0.95rem;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    margin-bottom: 1rem;
}

.workouts__loading {
    color: var(--muted);
    background: var(--code-bg);
}

.workouts__error {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
    border: 1px solid var(--danger);
}

.workouts__empty {
    color: var(--muted);
    background: var(--code-bg);
    text-align: center;
    padding: 2rem;
}

.workouts__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 0.875rem;
}

/* --- Workout tile -------------------------------------------------------- */

.workout-tile {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1rem 1.125rem;
    cursor: pointer;
    transition: border-color 0.15s, box-shadow 0.15s;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    text-align: left;
    width: 100%;
    font-family: var(--font-base);
    position: relative;
}

.workout-tile:hover {
    border-color: var(--accent);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 20%, transparent);
}

.workout-tile--active {
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-elevated));
    border-color: var(--accent);
}

.workout-tile--active:hover {
    box-shadow: 0 2px 12px color-mix(in srgb, var(--accent) 30%, transparent);
}

.workout-tile__date {
    font-size: 0.88rem;
    font-weight: 700;
    color: var(--fg);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.workout-tile__badge {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
    border-radius: 4px;
    padding: 0.1rem 0.45rem;
    white-space: nowrap;
}

.workout-tile__exercises {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.workout-tile__exercise-row {
    font-size: 0.82rem;
    color: var(--muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.workout-tile__exercise-name {
    color: var(--fg);
    font-weight: 500;
}

.workout-tile__no-sets {
    font-size: 0.82rem;
    color: var(--muted);
    font-style: italic;
}

.workout-tile__delete {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.3rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, color 0.15s, border-color 0.15s;
}

.workout-tile:hover .workout-tile__delete {
    opacity: 1;
}

.workout-tile__delete:hover {
    color: var(--danger);
    border-color: var(--danger);
}

/* ==========================================================================
   Workout detail modal
   ========================================================================== */

.workout-modal {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 2rem 1rem;
    overflow-y: auto;
}

.workout-modal[hidden] {
    display: none;
}

.workout-modal__backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: -1;
}

.workout-modal__dialog {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 12px;
    width: 100%;
    max-width: 640px;
    padding: 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    position: relative;
}

.workout-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.workout-modal__title {
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--fg);
    margin: 0;
}

.workout-modal__close {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    transition: color 0.15s, border-color 0.15s;
}

.workout-modal__close:hover {
    color: var(--fg);
    border-color: var(--fg);
}

.workout-modal__save-status {
    font-size: 0.8rem;
    color: var(--muted);
    min-height: 1.2em;
    text-align: right;
}

.workout-modal__save-status--saving {
    color: var(--muted);
}

.workout-modal__save-status--saved {
    color: var(--accent);
}

.workout-modal__save-status--error {
    color: var(--danger);
}

/* --- Sets list ---------------------------------------------------------- */

.workout-form__sets {
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
}

.workout-form__ex-wrap {
    position: relative;
    min-width: 0;
}

.workout-form__suggest {
    position: absolute;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    z-index: 10;
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    max-height: 10rem;
    overflow-y: auto;
}

.workout-form__suggest-item {
    padding: 0.4rem 0.7rem;
    cursor: pointer;
    font-size: 0.88rem;
    color: var(--fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.workout-form__suggest-item--active,
.workout-form__suggest-item:hover {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
}

.workout-form__set-row {
    display: grid;
    grid-template-columns: minmax(0, 1.8fr) 5rem 4rem auto;
    gap: 0.4rem;
    align-items: center;
}

.workout-form__set-row--readonly .workout-form__input {
    opacity: 0.8;
}

.workout-form__set-actions {
    display: flex;
    align-items: center;
    gap: 0.2rem;
    justify-content: flex-end;
}

.workout-form__input {
    background: var(--bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.45rem 0.6rem;
    font-size: 0.88rem;
    font-family: var(--font-base);
    width: 100%;
    box-sizing: border-box;
    transition: border-color 0.15s;
}

.workout-form__input:focus {
    outline: none;
    border-color: var(--accent);
}

.workout-form__input[readonly] {
    cursor: default;
    background: var(--code-bg);
}

.workout-form__set-remove {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem;
    border-radius: 4px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: color 0.15s, border-color 0.15s;
}

.workout-form__set-remove:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.workout-form__comment-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem;
    border-radius: 4px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: color 0.15s, border-color 0.15s;
}

.workout-form__comment-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.workout-form__comment-btn--active {
    color: var(--accent);
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.workout-form__comment-wrap {
    grid-column: 1 / -1;
}

.workout-form__comment-wrap[hidden] {
    display: none;
}

.workout-form__comment-input {
    font-size: 0.82rem;
}

.workout-form__col-labels {
    display: grid;
    grid-template-columns: minmax(0, 1.8fr) 5rem 4rem auto;
    gap: 0.4rem;
    margin-bottom: 0.25rem;
}

.workout-form__col-label {
    font-size: 0.75rem;
    color: var(--muted);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding-left: 0.1rem;
}

.workout-form__actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-top: 0.25rem;
}

.workout-form__add-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    border-radius: 6px;
    padding: 0.45rem 0.9rem;
    font-size: 0.88rem;
    font-family: var(--font-base);
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
}

.workout-form__add-btn:hover {
    color: var(--fg);
    border-color: var(--accent);
}

.workout-form__finish-btn {
    margin-left: auto;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border: none;
    border-radius: 6px;
    padding: 0.5rem 1.2rem;
    font-size: 0.92rem;
    font-weight: 600;
    font-family: var(--font-base);
    cursor: pointer;
    transition: opacity 0.15s;
}

.workout-form__finish-btn:hover {
    opacity: 0.85;
}

.workout-form__finish-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.workout-modal__completed-notice {
    font-size: 0.88rem;
    color: var(--muted);
    text-align: center;
    padding: 0.5rem;
    border-top: 1px solid var(--border);
}

@media (max-width: 520px) {
    .workout-form__set-row {
        grid-template-columns: minmax(0, 1fr) 4.5rem 3.5rem auto;
    }
    .workout-form__col-labels {
        grid-template-columns: minmax(0, 1fr) 4.5rem 3.5rem auto;
    }
    .workouts__grid {
        grid-template-columns: 1fr;
    }
}

/* ── Page character widget (fixed bottom-right, shown on most pages) ────── */
.page-char {
    position: fixed;
    bottom: 1.5rem;
    right: 1.5rem;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    z-index: 100;
    max-width: 380px;
}

.page-char__bubble {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.65rem 0.75rem 0.65rem 1rem;
    font-family: var(--font-heading);
    font-size: 0.88rem;
    font-weight: 500;
    color: var(--fg);
    letter-spacing: 0.01em;
    line-height: 1.45;
    max-width: 280px;
}

.page-char__text {
    flex: 1;
    min-width: 0;
}

.page-char__close {
    flex: none;
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 1.1rem;
    line-height: 1;
    padding: 0;
    width: 1.1rem;
    height: 1.1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 4px;
    transition: color 0.15s, background 0.15s;
}

.page-char__close:hover {
    color: var(--fg);
    background: var(--border);
}

.page-char__bubble::before {
    content: '';
    position: absolute;
    right: -9px;
    top: 50%;
    transform: translateY(-50%);
    width: 0;
    height: 0;
    border-top: 8px solid transparent;
    border-bottom: 8px solid transparent;
    border-left: 9px solid var(--border);
}

.page-char__bubble::after {
    content: '';
    position: absolute;
    right: -7px;
    top: 50%;
    transform: translateY(-50%);
    width: 0;
    height: 0;
    border-top: 7px solid transparent;
    border-bottom: 7px solid transparent;
    border-left: 8px solid var(--bg-elevated);
}

.page-char__bubble--loading {
    color: var(--muted);
    font-weight: 400;
    letter-spacing: 0.15em;
}

.page-char__avatar-wrap {
    flex: none;
    appearance: none;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.4rem;
    line-height: 0;
    cursor: pointer;
    transition: border-color 0.15s;
}

.page-char__avatar-wrap:hover {
    border-color: var(--fg);
}

.page-char__avatar {
    display: block;
    width: 56px;
    height: auto;
    image-rendering: pixelated;
    image-rendering: crisp-edges;
}

@media (max-width: 480px) {
    .page-char {
        bottom: 0.75rem;
        right: 0.75rem;
        max-width: calc(100vw - 1.5rem);
    }

    .page-char__bubble {
        font-size: 0.82rem;
        padding: 0.5rem 0.6rem 0.5rem 0.75rem;
        max-width: 220px;
    }
}


/* ── Markdown properties panel (frontmatter) ─────────────────────────── */

.md-props {
    border: 1px solid var(--border);
    border-radius: 6px;
    margin-bottom: 1.75rem;
    overflow: hidden;
    font-size: 0.875rem;
}

.md-props__row {
    display: grid;
    grid-template-columns: 100px 1fr;
    gap: 0.4rem 0.75rem;
    padding: 0.4rem 0.85rem;
    align-items: start;
    border-bottom: 1px solid var(--border);
}

.md-props__row:last-child {
    border-bottom: none;
}

.md-props__key {
    padding-top: 0.15rem;
    color: var(--muted);
    font-size: 0.78rem;
    font-weight: 500;
    user-select: none;
}

.md-props__val {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    align-items: flex-start;
    min-width: 0;
}

/* Plain text value */
.md-props__text {
    color: var(--fg);
    font-size: 0.875rem;
}

/* Chip pills — tags, contributors */
.md-props__chip {
    display: inline-flex;
    align-items: center;
    padding: 0.1rem 0.6rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 100px;
    font-size: 0.78rem;
    color: var(--fg);
    line-height: 1.5;
    white-space: nowrap;
}

/* Source & assumption lists */
.md-props__sources {
    width: 100%;
    min-width: 0;
}

/* Override generic .markdown ul/li rules inside the props panel.
   Use .markdown selector to match or beat the specificity of .markdown ul. */
.markdown .md-props__list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.18rem;
    width: 100%;
}

.markdown .md-props__list li {
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.markdown .md-props__assumptions li {
    white-space: normal;
    overflow: visible;
    text-overflow: unset;
    padding-left: 0.85rem;
    position: relative;
    font-size: 0.83rem;
    line-height: 1.4;
}

.md-props__assumptions li::before {
    content: "·";
    position: absolute;
    left: 0;
    color: var(--muted);
}

/* Source links — .markdown a adds underline by default, suppress here */
.markdown .md-props__link {
    color: var(--accent);
    text-decoration: none;
    font-size: 0.83rem;
}

.markdown .md-props__link:hover {
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* "ещё N" — plain button below the collapsed list; disappears on click */
.md-props__expand {
    display: block;
    background: none;
    border: none;
    padding: 0.15rem 0;
    margin-top: 0.2rem;
    font-size: 0.8rem;
    color: var(--muted);
    font-family: var(--font-base);
    cursor: pointer;
    text-align: left;
}

.md-props__expand:hover {
    color: var(--fg);
}


/* ── Files ───────────────────────────────────────────────────────────── */

.files-toolbar {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.5rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 0.6rem;
}

.files-toolbar__search-input {
    flex: 1;
    max-width: 400px;
    height: 28px;
    padding: 0 0.55rem;
    background: var(--bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 4px;
    font-family: var(--font-base);
    font-size: 0.86rem;
    outline: none;
}

.files-toolbar__search-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--bg-elevated), 0 0 0 4px var(--accent);
}

.files-toolbar__search-input::-webkit-search-cancel-button {
    display: none;
}

main.files-page {
    max-width: 1200px;
    height: 100vh;
    padding: 5rem 1rem 1rem;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.files-page .page-title {
    flex-shrink: 0;
    margin-bottom: 1rem;
}

.files {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 280px minmax(0, 1fr);
    gap: 1rem;
    align-items: stretch;
}

.files__tree-panel,
.files__viewer-panel {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.files__panel-header {
    padding: 0.7rem 0.9rem;
    font-family: var(--font-heading);
    font-size: 0.95rem;
    font-weight: 600;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.files__tree {
    flex: 1;
    overflow-y: auto;
    padding: 0.4rem 0.3rem;
}

.files__tree-loading,
.files__tree-error {
    padding: 0.75rem 0.5rem;
    font-size: 0.88rem;
    color: var(--muted);
}

.files__tree-error { color: var(--danger); }

/* Directory nodes */
.files__dir {
    /* wrapper — no visual styling, just structure */
}

.files__dir-btn {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    width: 100%;
    padding: 0.28rem 0.4rem;
    background: transparent;
    color: var(--fg);
    border: none;
    border-radius: 4px;
    font-size: 0.86rem;
    font-family: var(--font-base);
    text-align: left;
    cursor: pointer;
}

.files__dir-btn:hover {
    background: var(--bg);
    opacity: 1;
}

.files__dir-children {
    padding-left: 1.1rem;
}

.files__dir-arrow {
    flex-shrink: 0;
    color: var(--muted);
    display: flex;
    align-items: center;
    transition: transform 0.15s ease;
}

/* Invisible spacer on file nodes — same width as the dir arrow (11 px)
 * so file icons align with folder icons at the same nesting level. */
.files__file-spacer {
    flex-shrink: 0;
    width: 11px;
}

.files__dir-btn[aria-expanded="true"] .files__dir-arrow {
    transform: rotate(90deg);
}

.files__dir-icon {
    flex-shrink: 0;
    color: var(--muted);
    display: flex;
    align-items: center;
}

/* File nodes */
.files__file-btn {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    width: 100%;
    padding: 0.28rem 0.4rem;
    background: transparent;
    color: var(--fg);
    border: none;
    border-radius: 4px;
    font-size: 0.86rem;
    font-family: var(--font-base);
    text-align: left;
    cursor: pointer;
}

.files__file-btn:hover:not(:disabled) {
    background: var(--bg);
    opacity: 1;
}

.files__file-btn.is-selected {
    background: var(--bg);
    color: var(--accent);
}

/* Directory on the path to the selected file — muted accent so it
 * reads as "active" without competing with the file itself. */
.files__dir-btn.is-ancestor {
    color: var(--accent);
    background: var(--bg);
    opacity: 0.85;
}

.files__dir-btn.is-ancestor .files__dir-icon {
    color: var(--accent);
}

.files__file-btn--dim {
    color: var(--muted);
    opacity: 0.6;
}

.files__file-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    color: var(--muted);
}

.files__file-btn.is-selected .files__file-icon {
    color: var(--accent);
}

.files__node-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
}

/* Viewer panel */
.files__viewer-header {
    display: flex;
    align-items: stretch;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.files__viewer-title {
    padding: 0.7rem 0.9rem;
    font-weight: 600;
    font-size: 0.9rem;
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--muted);
}

.files__viewer-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.6rem;
    flex-shrink: 0;
}

.files__action-btn {
    background: transparent;
    color: var(--fg);
    border: 1px solid var(--border);
    padding: 0.3rem 0.7rem;
    border-radius: 4px;
    font-family: var(--font-base);
    font-size: 0.82rem;
    cursor: pointer;
    line-height: 1.2;
}

.files__action-btn:hover:not(:disabled) {
    background: var(--bg);
}

.files__action-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.files__action-btn--primary {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border-color: var(--accent);
}

.files__action-btn--primary:hover:not(:disabled) {
    opacity: 0.9;
    background: var(--accent-gradient);
}

/* Icon-only action button — same shape as the research copy/erase buttons
 * elsewhere in the app: transparent + bordered square, muted by default, the
 * hover shifts color to the foreground so a touch/click doesn't leave it
 * stuck in the hover state on small screens. */
.files__icon-btn {
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
    padding: 0.3rem;
    border-radius: 4px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
}

@media (hover: hover) {
    .files__icon-btn:hover { color: var(--fg); opacity: 1; }
}

.files__viewer-title.has-file {
    color: var(--fg);
}

.files__viewer-body {
    flex: 1;
    overflow-y: auto;
    padding: 1.25rem 1.5rem;
}

.files__viewer-editor {
    display: block;
    width: 100%;
    min-height: 200px;
    padding: 0.9rem 1rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
    font-size: 0.88rem;
    line-height: 1.55;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    resize: vertical;
    box-sizing: border-box;
}

.files__viewer-editor:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--bg-elevated), 0 0 0 4px var(--accent);
}

.files__viewer-error {
    margin-top: 0.6rem;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--danger);
    border-radius: 4px;
    color: var(--danger);
    font-size: 0.85rem;
    background: var(--bg-elevated);
}

.files__viewer-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: var(--muted);
    font-size: 0.92rem;
}

.files__loading {
    color: var(--muted);
    font-size: 0.9rem;
}

.files__load-error {
    color: var(--danger);
    font-size: 0.9rem;
}

@media (max-width: 760px) {
    main.files-page {
        height: auto;
        overflow: visible;
    }

    .files {
        grid-template-columns: 1fr;
        align-items: start;
    }

    .files__tree-panel {
        max-height: 40vh;
    }

    .files__viewer-panel {
        max-height: 60vh;
    }
}

/* ==========================================================================
   Projects page — tiles, detail modal
   ========================================================================== */

main.projects-page {
    max-width: 1100px;
}

.projects {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

/* ── Status tabs (filter the grid) ─────────────────────────────────────── */

.projects__tabs {
    display: flex;
    gap: 0.25rem;
    border-bottom: 1px solid var(--border);
}

.projects__tab {
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    padding: 0.5rem 0.9rem;
    font-family: var(--font-base);
    font-size: 0.9rem;
    color: var(--muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    transition: color 0.15s, border-color 0.15s;
}

.projects__tab:hover { color: var(--fg); }

.projects__tab--active {
    color: var(--fg);
    border-bottom-color: var(--accent);
    font-weight: 600;
}

.projects__tab-count {
    font-size: 0.78rem;
    font-weight: 400;
    background: var(--code-bg);
    color: var(--muted);
    border-radius: 10px;
    padding: 0.05rem 0.45rem;
    min-width: 1.4em;
    text-align: center;
}

.projects__tab--active .projects__tab-count {
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    color: var(--accent);
}

/* ── Loading / error / empty ───────────────────────────────────────────── */

.projects__loading,
.projects__error,
.projects__empty {
    font-size: 0.95rem;
    padding: 0.75rem 1rem;
    border-radius: 8px;
    text-align: center;
}

.projects__loading { color: var(--muted); background: var(--code-bg); }

.projects__error {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated));
    border: 1px solid var(--danger);
}

.projects__empty { color: var(--muted); background: var(--code-bg); padding: 2rem; }

/* ── Grid + tile ───────────────────────────────────────────────────────── */

.projects__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 0.9rem;
}

.project-tile {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1rem 1.1rem;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    text-align: left;
    width: 100%;
    font-family: var(--font-base);
    position: relative;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.project-tile:hover {
    border-color: var(--accent);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 18%, transparent);
}

/* «Открыть в новой вкладке» — top-right corner icon on the sphere Actions
 * page's project tiles (pages/actions.js::buildProjectTile). Only there —
 * projects.html's own grid tiles don't get this, they're the primary
 * browsing surface, not a reference to a project shown elsewhere. */
.project-tile__open-link {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--muted);
    text-decoration: none;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.project-tile__open-link:hover {
    color: var(--accent);
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}
/* Keeps a long wrapping title from running under the corner icon above.
 * Scoped to tiles that actually carry the icon (projects.html's own grid
 * tiles don't, so they keep the full-width title untouched). */
.project-tile--linkable .project-tile__name {
    padding-right: 1.9rem;
}

.project-tile--completed {
    background: var(--code-bg);
    border-style: dashed;
}

.project-tile--completed .project-tile__name {
    text-decoration: line-through;
    color: var(--muted);
}

/* Название всегда первой строкой карточки, во всю ширину — ничто не делит с
   ним место и не ужимает его. Статус/дедлайн/сфера/вложенность идут ниже,
   в `.project-tile__meta` (см. TODO.md § Проекты). */
.project-tile__name {
    font-family: var(--font-heading);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--fg);
    line-height: 1.3;
    overflow-wrap: anywhere;
}

.project-tile__meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.project-tile__badge {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
    border-radius: 4px;
    padding: 0.1rem 0.45rem;
    white-space: nowrap;
    flex-shrink: 0;
}

/* Бейдж «Внутри: <родитель>» на карточке — тише статус-бейджа: не заявление
   о состоянии проекта, а справочная деталь про его место в иерархии. Имя
   родителя не капсом — оно не влезает в UPPERCASE так же хорошо, как
   короткие статус-слова. */
.project-tile__badge--parent {
    color: var(--muted);
    background: color-mix(in srgb, var(--muted) 12%, transparent);
    border-color: color-mix(in srgb, var(--muted) 30%, transparent);
    text-transform: none;
    letter-spacing: normal;
    flex-shrink: 1;
    min-width: 0;
    max-width: 12rem;
    overflow: hidden;
    text-overflow: ellipsis;
}

.project-tile__deadline {
    font-size: 0.78rem;
    color: var(--muted);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.1rem 0.5rem;
    white-space: nowrap;
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
}

/* Меньше месяца до дедлайна — жёлтое свечение (TODO.md § Проекты). */
.project-tile__deadline--soon {
    color: var(--warn);
    border-color: color-mix(in srgb, var(--warn) 50%, transparent);
    background: color-mix(in srgb, var(--warn) 12%, transparent);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--warn) 25%, transparent);
}

.project-tile__deadline--overdue {
    color: var(--danger);
    border-color: color-mix(in srgb, var(--danger) 40%, transparent);
    background: color-mix(in srgb, var(--danger) 10%, transparent);
}

.project-tile__progress {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.project-tile__progress-bar {
    width: 100%;
    height: 6px;
    background: var(--bg);
    border-radius: 3px;
    overflow: hidden;
}

.project-tile__progress-fill {
    height: 100%;
    background: var(--accent);
    transition: width 0.2s ease;
}

.project-tile__progress-text {
    font-size: 0.78rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.project-tile__created {
    font-size: 0.72rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    opacity: 0.75;
}

.project-tile__delete {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.3rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s, color 0.15s, border-color 0.15s;
}

.project-tile:hover .project-tile__delete { opacity: 1; }
.project-tile__delete:hover { color: var(--danger); border-color: var(--danger); }

/* ── Add-project tile (inline create) ──────────────────────────────────── */

.project-add-tile {
    background: transparent;
    border: 1.5px dashed var(--border);
    border-radius: 10px;
    display: flex;
    align-items: stretch;
    min-height: 68px;
    transition: border-color 0.15s;
}

.project-add-tile:hover,
.project-add-tile--editing {
    border-color: var(--accent);
}

.project-add-tile--editing {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
}

.project-add-tile__btn {
    flex: 1;
    background: transparent;
    border: none;
    color: var(--muted);
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
    text-align: left;
    padding: 1rem 1.1rem;
    transition: color 0.15s;
}

.project-add-tile:hover .project-add-tile__btn { color: var(--accent); }

.project-add-tile__input {
    flex: 1;
    background: transparent;
    border: none;
    color: var(--fg);
    font: inherit;
    font-size: 0.95rem;
    font-weight: 600;
    padding: 1rem 1.1rem;
    outline: none;
    box-sizing: border-box;
}

.project-add-tile__input::placeholder { color: var(--muted); font-weight: 400; }

/* ── Detail modal shell ────────────────────────────────────────────────── */

.project-modal {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 2rem 1rem;
    overflow-y: auto;
}

.project-modal[hidden] { display: none; }

.project-modal__backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: -1;
}

.project-modal__dialog {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 12px;
    width: 100%;
    max-width: 720px;
    padding: 1.5rem 1.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    position: relative;
}

.project-modal__header {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    border-bottom: 1px solid var(--border);
    padding-bottom: 0.85rem;
}

/* Title block: the heading is a click-to-edit affordance — clicking it
 * swaps it for an input. On blur/Enter the value is saved and the
 * heading comes back; on Escape the original value is restored. No
 * auto-focus on open — the title should read as a label, not as
 * something the user is being asked to type into. */
.project-modal__title-block {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}

/* Projects (not focuses) pile a status select + parent badge + sphere
 * select + delete button onto the title row — enough fixed-width content
 * to squeeze a long name down to one character per line. Stack the title
 * above a wrapped `.project-modal__meta` row instead (see TODO.md §
 * Проекты). focuses.html reuses `.project-modal__title-block` too but only
 * ever has a single delete button, so it keeps the plain row layout. */
.project-modal__title-block--stacked {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
}

.project-modal__meta {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.project-modal__title-text-wrap {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
}

.project-modal__title-display {
    flex: 1;
    margin: 0;
    font-family: var(--font-heading);
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--fg);
    line-height: 1.3;
    overflow-wrap: anywhere;
    min-width: 0;
    cursor: pointer;
    border-radius: 6px;
    padding: 0.2rem 0.5rem;
    margin-left: -0.5rem; /* compensate left padding so text stays flush with modal edge */
    transition: background 0.12s;
}

.project-modal__title-display:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.project-modal__title-input {
    flex: 1;
    background: transparent;
    border: none;
    border-bottom: 1.5px solid var(--accent);
    border-radius: 0;
    padding: 0.25rem 0;
    font-family: var(--font-heading);
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--fg);
    min-width: 0;
}

.project-modal__title-input:focus {
    outline: none;
}

.project-modal__title-input::placeholder {
    color: var(--muted);
    font-weight: 400;
}

.project-modal__close {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    flex-shrink: 0;
    transition: color 0.15s, border-color 0.15s;
}

.project-modal__close:hover { color: var(--fg); border-color: var(--fg); }

.project-modal__field {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    flex: 1;
    min-width: 200px;
}

.project-modal__field-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
    font-weight: 600;
}

.project-modal__date {
    background: var(--bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.5rem 0.65rem;
    font-family: var(--font-base);
    font-size: 0.95rem;
    transition: border-color 0.15s;
}

.project-modal__date:focus {
    outline: none;
    border-color: var(--accent);
}

.project-modal__save-status {
    font-size: 0.8rem;
    color: var(--muted);
    min-height: 1.2em;
    text-align: right;
}

/* Idle state (no save in flight, nothing just saved) leaves this element
 * with zero text nodes — collapse it out of the flex flow entirely instead
 * of reserving `min-height` + a `.project-modal__dialog` gap for nothing.
 * Otherwise the first section below it (now «Описание», often empty)
 * reads as having an oversized gap above its heading for no reason. */
.project-modal__save-status:empty {
    display: none;
}

.project-modal__save-status--saving { color: var(--muted); }
.project-modal__save-status--saved { color: var(--accent); }
.project-modal__save-status--error { color: var(--danger); }

/* ── Goal sections (separate sections, not a card grid) ──────────────────── */

/* Shared shell for a single goal section (criterion, why, tasks, progress).
 * Each section is a full-width block with its own title and a border-bottom
 * separator, so the modal reads as a vertical stack of independent groups. */
.project-section,
.project-progress,
.project-actions,
.project-timeline {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding-bottom: 0.85rem;
    border-bottom: 1px solid var(--border);
}

.project-section:last-of-type,
.project-progress:last-of-type,
.project-actions:last-of-type,
.project-timeline:last-of-type {
    border-bottom: none;
    padding-bottom: 0;
}

.project-section__header { display: flex; align-items: center; }

.project-section__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: 1rem;
    font-weight: 600;
    color: var(--fg);
    letter-spacing: 0.01em;
}

/* Click-to-edit pattern. The display is a plain <div> that looks like
 * static text until hovered; the click target is the text itself, not a
 * separate pencil button. The negative horizontal margin compensates for
 * the padding so the text position is identical to display-mode-default
 * while the hover background has room to show. */
.project-section__display {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
    color: var(--fg);
    font-size: 0.95rem;
    line-height: 1.55;
    cursor: pointer;
    border-radius: 6px;
    padding: 0.3rem 0.5rem;
    margin: 0 -0.5rem;
    transition: background 0.12s;
}

.project-section__display:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.project-section__text {
    flex: 1;
    margin: 0;
    word-wrap: break-word;
    overflow-wrap: anywhere;
    white-space: pre-wrap;
    min-width: 0;
}

/* Empty state — italic + muted so the user knows the field is unfilled. */
.project-section__text--empty {
    color: var(--muted);
    font-style: italic;
}

/* «Сколько дней до дедлайна» рядом с датой в секции «Дедлайн» — переиспользует
   плашку кубика (`.project-tile__deadline`, см. buildTile), только с чуть
   бо́льшим шрифтом, чтобы не потеряться рядом с 0.95rem текстом даты. */
.project-section__deadline-relative {
    font-size: 0.8rem;
    margin-top: 0.05rem;
}

/* Description section renders Markdown (real block elements from marked),
 * not raw text — `pre-wrap` from `.project-section__text` would turn the
 * whitespace between block tags into visible blank lines, so switch back
 * to normal collapsing. `.markdown` (added by Markdown.renderInto) owns
 * spacing between its own children. */
.project-section__text--markdown {
    white-space: normal;
}

/* With the placeholder hint removed, an empty description has no text node
 * at all, so the display div collapses toward 0 height instead of reading
 * as an empty field. Floor it at one text line (font-size 0.95rem *
 * line-height 1.55 + the display's own 0.3rem top/bottom padding). */
.project-section[data-section="description"] .project-section__display {
    min-height: 2.1rem;
}

/* Focus modal's description (pages/focuses.js) uses attachClickToEdit
   directly on the `.project-section__display` div (no nested
   `.project-section__text` span) — so it needs the text-wrapping rules
   that span would normally carry, plus a placeholder for the empty state.
   `:empty` matches whenever the div has no text (the widget clears it to
   swap in the edit textarea), so `::before` never fires once a child
   <textarea> is present. */
#focus-section-description-text {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
#focus-section-description-text:empty::before {
    content: attr(data-placeholder);
    color: var(--muted);
    font-style: italic;
}

.project-section__input {
    background: var(--bg);
    border: 1px solid var(--accent);
    border-radius: 8px;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.95rem;
    line-height: 1.55;
    padding: 0.7rem 0.85rem;
    resize: vertical;
    min-height: 4.5rem;
    width: 100%;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
}

.project-section__input:focus {
    outline: none;
}

.project-section__input::placeholder {
    color: var(--muted);
    font-style: italic;
}

/* The deadline input keeps the same shape as the textarea but flat (no
 * inner shadow), and gets the native date-picker affordance. */
.project-section__date-input {
    background: var(--bg);
    border: 1px solid var(--accent);
    border-radius: 8px;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.95rem;
    padding: 0.7rem 0.85rem;
    width: 100%;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
}

.project-section__date-input:focus { outline: none; }

/* ── Status dropdown (project modal) ─────────────────────────────────────
 * Mounted as a UIDropdown (js/shared/dropdown.js, see pages/projects.js's
 * _renderProjectDropdowns) instead of a native <select> — this class only
 * sizes the mount inside the header row; the trigger's look comes from
 * .ui-dropdown__trigger (top of this file). */
.project-modal__status-select {
    flex-shrink: 0;
    width: auto;
}

.project-modal__status-select .ui-dropdown { width: auto; }

.project-modal__status-select .ui-dropdown__trigger {
    width: auto;
    font-size: 0.82rem;
}

/* Completed project: the whole modal softens — title gets struck through,
 * so the project visibly "rests" the same way the tile does. */
.project-modal--completed .project-modal__title-display {
    text-decoration: line-through;
    color: var(--muted);
}

/* ── Progress (M) — checklist with progress bar ──────────────────────────── */

.project-progress__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.project-progress__header .project-section__title { flex-shrink: 0; }

.project-progress__meta {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex: 1;
    min-width: 200px;
}

.project-progress__bar {
    flex: 1;
    min-width: 0;
    height: 8px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    overflow: hidden;
}

.project-progress__fill {
    height: 100%;
    background: var(--accent);
    width: 0;
    transition: width 0.2s ease;
}

.project-progress__count {
    font-family: var(--font-heading);
    font-size: 0.92rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
    white-space: nowrap;
}

.project-progress__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.project-progress__item {
    display: grid;
    /* Six columns: handle | checkbox | title (flex) | deadline | hint | removeBtn.
       minmax(0, 1fr) on the title lets it shrink without overflowing — a
       long row stays one line instead of pushing the removeBtn out of
       the row. The hint column is ``auto`` so it hugs the text
       («5 дн.», «сегодня», «просрочен 2 дн.») and disappears when
       empty; the deadline cell gets a fixed width band; the remove
       cell stays a tight 2rem so the trash icon never moves from the
       far right. */
    grid-template-columns: auto 1.1rem minmax(0, 1fr) 8.5rem auto 2rem;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.55rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    transition: border-color 0.15s, background 0.15s, opacity 0.15s;
}

.project-progress__item:focus-within { border-color: var(--accent); }

.project-progress__item--done { background: var(--code-bg); }
.project-progress__item--done .project-progress__title {
    text-decoration: line-through;
    color: var(--muted);
}

.project-progress__item--dragging { opacity: 0.35; }

.project-progress__item--drop-top { border-top: 2px solid var(--accent); }

.project-progress__item--drop-bottom { border-bottom: 2px solid var(--accent); }

.project-progress__handle {
    background: transparent;
    border: none;
    padding: 0.3rem 0.15rem;
    color: var(--muted);
    cursor: grab;
    display: inline-flex;
    align-items: center;
    border-radius: 4px;
    touch-action: none;
}

.project-progress__handle:active { cursor: grabbing; }

.project-progress__handle:hover { color: var(--fg); }

/* Custom-styled checkbox — `appearance: none` so we drop the browser's
 * default white/light fill that clashed with dark themes. The checkmark
 * is drawn as a `::after` pseudo-element rotated to look like a tick.
 * Centering is done via flex (not hand-tuned top/left offsets) so the
 * tick's own box is always centered regardless of border-box rounding. */
.project-progress__checkbox {
    appearance: none;
    -webkit-appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    height: 1.1rem;
    margin: 0;
    padding: 0;
    border: 1.5px solid var(--muted);
    border-radius: 3px;
    background: transparent;
    cursor: pointer;
    flex-shrink: 0;
    transition: background 0.15s, border-color 0.15s;
}

.project-progress__checkbox:hover {
    border-color: var(--accent);
}

.project-progress__checkbox:checked {
    background: var(--accent);
    border-color: var(--accent);
}

.project-progress__checkbox:checked::after {
    content: "";
    width: 0.28rem;
    height: 0.55rem;
    border-right: 2px solid var(--accent-fg);
    border-bottom: 2px solid var(--accent-fg);
    transform: rotate(45deg);
}

.project-progress__checkbox:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.project-progress__title {
    background: transparent;
    border: none;
    color: var(--fg);
    font: inherit;
    font-size: 0.95rem;
    padding: 0.15rem 0;
    min-width: 0;
    width: 100%;
}

.project-progress__title:focus { outline: none; }

/* Per-checkpoint deadline (TODO.md § Проекты — «У каждого пункта
   прогресса должны быть дедлайны»). Native date input, no border so it
   blends with the row; only the focus ring tells you it's a real
   control.

   Two styling quirks we work around here:

   1. Width — the native control ships with a generous default
      padding (~0.5rem on each side) plus the ~1rem calendar-icon
      button, so even with a 10-char date the field is ~11rem wide
      and visually heavier than the title next to it. We shrink
      min/max width and trim padding so the field is just slightly
      wider than its content, like every other inline control in the
      app.

   2. Calendar icon — the browser draws its own calendar glyph in a
      colour that defaults to ``CanvasText`` and is invisible against
      dark backgrounds. ``color-scheme: light dark`` lets the browser
      pick the right palette per theme; we additionally force the
      icon to track ``currentColor`` via a ``filter`` so the hover /
      overdue / accent colours we apply below actually paint it. */
.project-progress__deadline {
    background: transparent;
    border: none;
    color: var(--muted);
    font: inherit;
    font-size: 0.85rem;
    padding: 0.05rem 1.05rem 0.05rem 0.25rem;
    border-radius: 4px;
    flex-shrink: 0;
    /* The field hugs its value; the grid column (9.5rem in
       .project-progress__item) provides the upper bound. */
    width: auto;
    min-width: 0;
    max-width: 8.5rem;
    cursor: pointer;
    /* Let the browser pick a date-picker palette that matches the
       surrounding background — otherwise the native picker dropdown
       stays light-themed on dark pages and looks pasted in. */
    color-scheme: light dark;
    /* Replace the native calendar-indicator (which is impossible to
       style reliably across browsers — modern Chromium ships it as
       a shadow-DOM node that ignores ``::-webkit-calendar-picker-indicator``)
       with our own SVG. The icon is encoded with ``stroke='%23fff'``
       so it stays white on dark themes; the light-theme override
       below swaps that for a black stroke. ``right 4px`` keeps the
       icon flush with the trailing edge, the 12×12 px size stops it
       from bleeding past the right padding, and ``no-repeat`` +
       ``center`` vertically keeps the icon aligned with the date text. */
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect x='3' y='4' width='18' height='18' rx='2' ry='2'/><line x1='16' y1='2' x2='16' y2='6'/><line x1='8' y1='2' x2='8' y2='6'/><line x1='3' y1='10' x2='21' y2='10'/></svg>");
    background-repeat: no-repeat;
    background-position: right 4px center;
    background-size: 12px 12px;
}
:where([data-theme="light"]) .project-progress__deadline {
    /* On the light theme the original icon needs a black stroke. The
       encoded ``%23fff`` above becomes white on dark; here we
       override with black so the icon is visible on light. */
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect x='3' y='4' width='18' height='18' rx='2' ry='2'/><line x1='16' y1='2' x2='16' y2='6'/><line x1='8' y1='2' x2='8' y2='6'/><line x1='3' y1='10' x2='21' y2='10'/></svg>");
}
/* Hide the native indicator completely — our background SVG replaces
   it. Belt-and-braces for browsers that still respect this pseudo. */
.project-progress__deadline::-webkit-calendar-picker-indicator {
    opacity: 0;
    display: none;
}
.project-progress__deadline::-webkit-inner-spin-button {
    display: none;
}
.project-progress__deadline:focus {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
    color: var(--fg);
}
/* Hover affordance — the native date picker looks inert without
   one, and the user has to discover it's clickable. Light accent
   tint, accent-coloured text, and a subtle border so the control
   pops out of the row without competing with the overdue-red state.

   IMPORTANT: use ``background-color`` (not the ``background`` shorthand)
   so the calendar-icon SVG in ``background-image`` survives the
   hover change. The shorthand resets every background-* property to
   its initial value, which would wipe the icon. */
.project-progress__deadline:hover {
    background-color: color-mix(in srgb, var(--accent) 10%, transparent);
    color: var(--accent);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    /* Re-declare padding inside the border so the visible content
       position doesn't shift when the hover border appears. The
       right padding matches the base rule (1.05rem) minus the 1px
       border thickness so the date text ends at the same x-offset
       whether or not the field is hovered. */
    padding: calc(0.05rem - 1px) calc(1.05rem - 1px) calc(0.05rem - 1px) calc(0.25rem - 1px);
}
/* Highlight the deadline in red when it's in the past AND the item
   isn't done yet — same "overdue" signal the rest of the app uses.
   Done items with a past deadline keep the muted colour so the row
   doesn't shout about history. */
.project-progress__deadline[data-overdue="true"] {
    color: var(--danger);
}
/* Yellow tint when the deadline is within the next two weeks
   (≤14 days, set via SOON_DAYS in pages/projects.js). Mirrors
   the overdue colour but with the warn token — same convention
   the calendar / tasks module uses for "coming up soon". */
.project-progress__deadline[data-soon="true"] {
    color: var(--warn);
}
.project-progress__deadline[data-overdue="true"]:hover {
    color: var(--danger);
    border-color: color-mix(in srgb, var(--danger) 50%, transparent);
}
.project-progress__deadline[data-soon="true"]:hover {
    color: var(--warn);
    border-color: color-mix(in srgb, var(--warn) 50%, transparent);
    background-color: color-mix(in srgb, var(--warn) 8%, transparent);
}
/* Days-remaining hint («5 дн.», «сегодня», «просрочен 2 дн.») sits
   right of the date input. Empty by default; populated by
   ``_refreshDeadlineState()`` in pages/projects.js. The colour
   follows the input state — red if the deadline is past, yellow
   if soon, muted otherwise. The hover rule above for the input
   also bolds it visually, so the hint just reads as a tone
   modifier. */
.project-progress__deadline-hint {
    font-size: 0.8rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    flex-shrink: 0;
}
.project-progress__deadline[data-overdue="true"] + .project-progress__deadline-hint {
    color: var(--danger);
}
.project-progress__deadline[data-soon="true"] + .project-progress__deadline-hint {
    color: var(--warn);
}
/* Done rows: the hint is history, not a signal — dim it. */
.project-progress__item--done .project-progress__deadline-hint {
    opacity: 0.7;
}
.project-progress__item--done .project-progress__deadline {
    color: var(--muted);
    opacity: 0.7;
}

.project-progress__remove {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem;
    border-radius: 4px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    flex-shrink: 0;
    transition: color 0.15s, border-color 0.15s;
}

.project-progress__remove:hover { color: var(--danger); border-color: var(--danger); }

.project-progress__add-row {
    display: flex;
    gap: 0.5rem;
    align-items: stretch;
}

.project-progress__new-input {
    flex: 1;
    background: var(--bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.5rem 0.7rem;
    font-family: var(--font-base);
    font-size: 0.95rem;
    min-width: 0;
}

.project-progress__new-input:focus {
    outline: none;
    border-color: var(--accent);
}

/* Small link-style add button — the click creates a focused new row,
 * the button itself just sits there as a small hint. */
.project-progress__add-btn {
    align-self: flex-start;
    background: transparent;
    color: var(--accent);
    border: 1px dashed var(--border);
    border-radius: 6px;
    padding: 0.5rem 0.9rem;
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
    white-space: nowrap;
    transition: border-color 0.15s, background-color 0.15s;
}

.project-progress__add-btn:hover {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}

/* ── Timeline ──────────────────────────────────────────────────────────── */

/* The single "Add" button sits at the top of the section — clicking it
 * creates a new row with today's date, focused for typing. No more inline
 * date/comment inputs next to the button. */
.project-timeline__add-btn {
    align-self: flex-start;
    background: transparent;
    color: var(--accent);
    border: 1px dashed var(--border);
    border-radius: 6px;
    padding: 0.5rem 0.9rem;
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
    white-space: nowrap;
    transition: border-color 0.15s, background-color 0.15s;
}

.project-timeline__add-btn:hover {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.project-timeline__list {
    list-style: none;
    margin: 0;
    padding: 0;
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.project-timeline__list::before {
    content: "";
    position: absolute;
    top: 0.85rem;
    bottom: 0.85rem;
    left: 0.5rem;
    width: 2px;
    background: var(--border);
    border-radius: 1px;
}

.project-timeline__item {
    position: relative;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.55rem 0.7rem 0.65rem;
    margin-left: 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    transition: border-color 0.15s;
}

.project-timeline__item:focus-within { border-color: var(--accent); }

.project-timeline__item::before {
    content: "";
    position: absolute;
    left: -1.15rem;
    top: 0.85rem;
    width: 0.65rem;
    height: 0.65rem;
    background: var(--accent);
    border-radius: 50%;
    box-shadow: 0 0 0 3px var(--bg-elevated);
}

.project-timeline__row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.project-timeline__date-input {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    color: var(--muted);
    font-family: var(--font-base);
    font-size: 0.8rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    padding: 0.1rem 0.3rem;
    border-radius: 3px;
    width: 7.5rem;
    letter-spacing: 0.01em;
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.project-timeline__date-input:hover {
    background: var(--code-bg);
    border-color: var(--border);
}

.project-timeline__date-input:focus {
    outline: none;
    background: var(--bg-elevated);
    border-color: var(--accent);
    color: var(--fg);
}

/* The comment textarea auto-grows with content. Enter (without shift)
 * blurs the field (= "I'm done editing this row"); Shift+Enter inserts a
 * newline (default textarea behaviour). */
.project-timeline__comment {
    background: transparent;
    border: none;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.95rem;
    line-height: 1.5;
    padding: 0.2rem 0.1rem;
    min-height: 1.5rem;
    resize: none;
    overflow: hidden;
    width: 100%;
}

.project-timeline__comment:focus {
    outline: none;
    background: var(--code-bg);
    border-radius: 3px;
}

.project-timeline__comment::placeholder {
    color: var(--muted);
    font-style: italic;
}

.project-timeline__remove {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem;
    border-radius: 4px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    flex-shrink: 0;
    margin-left: auto;
    transition: color 0.15s, border-color 0.15s;
}

.project-timeline__remove:hover { color: var(--danger); border-color: var(--danger); }

/* Delete project button — icon-only, sits in the title block next to the
 * other icon buttons. Muted by default (it's a destructive action, but
 * the click goes through a confirmation dialog), goes to danger on hover. */
.project-modal__delete-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.4rem;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    flex-shrink: 0;
    transition: color 0.15s, background 0.15s, border-color 0.15s;
}

@media (hover: hover) {
    .project-modal__delete-btn:hover {
        color: var(--danger);
        border-color: var(--danger);
        background: color-mix(in srgb, var(--danger) 8%, transparent);
    }
}

.project-modal__delete-btn:focus-visible {
    outline: none;
    color: var(--danger);
    border-color: var(--danger);
}
}

@media (max-width: 560px) {
    .project-modal__dialog { padding: 1.1rem 1.1rem; }
    .project-modal__title-display,
    .project-modal__title-input { font-size: 1.2rem; }
}

/* ==========================================================================
   Tasks — landing page
   ========================================================================== */

.tasks-nav {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    max-width: 500px;
    margin: 0 auto;
}

.tasks-nav__item {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem 1.25rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    color: var(--fg);
    text-decoration: none;
    transition: border-color 0.15s, color 0.15s, box-shadow 0.15s;
}

.tasks-nav__item:hover {
    border-color: var(--accent);
    color: var(--accent);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 15%, transparent);
}

.tasks-nav__icon {
    flex-shrink: 0;
    color: var(--muted);
    display: flex;
    align-items: center;
    transition: color 0.15s;
}

.tasks-nav__item:hover .tasks-nav__icon { color: var(--accent); }

.tasks-nav__label {
    font-family: var(--font-heading);
    font-size: 1.05rem;
    font-weight: 700;
    flex-shrink: 0;
}

/* Spheres page (spheres.html): .tasks-nav is a direct child of main.tasks-page
   there (not nested in .tasks-layout like on tasks.html's sidebar, which stays
   a single narrow column) — lay it out as a wider two-column grid instead of
   the default single-column stack. */
main.tasks-page > .tasks-nav {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    max-width: 760px;
}

@media (max-width: 620px) {
    main.tasks-page > .tasks-nav {
        grid-template-columns: 1fr;
        max-width: 500px;
    }
}

.tasks-stub-notice {
    color: var(--muted);
    font-style: italic;
    text-align: center;
    padding: 2rem;
    background: var(--code-bg);
    border-radius: 8px;
}

/* Как main.calendar-page: страница ограничена высотой вьюпорта и не
   скроллится сама — прокрутка задач происходит внутри каждого списка (см.
   .tasks-landing-main .kanban__cards ниже). max-width подогнан под реальную
   ширину контента (узкий .tasks-nav + три компактные колонки задач), чтобы
   справа не оставалось пустого поля до края страницы. */
main.tasks-page {
    max-width: 1210px;
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
    padding: 4rem 1rem 1rem;
}

/* ── Tasks landing layout (nav + today sidebar + deadlines panel) ──────── */

.tasks-layout {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    gap: 2rem;
    align-items: stretch;
}

.tasks-layout .tasks-nav {
    flex: 0 0 auto;
    width: 100%;
    max-width: 320px;
    margin: 0;
}

/* Лендинг «Задачи» / «Главная»: три равные колонки (Сегодня / Без даты /
   Ближайшие дедлайны) горизонтально. Общий класс — используется и на
   tasks.html, и на index.html (см. js/tasks/landing.js), чтобы оба лендинга
   рендерили идентичные списки идентичным образом. */
/* flex-wrap НЕ используется намеренно: с ним и высоким «Сегодня» (много
   задач) align-items: stretch не обрезает высоту колонок до высоты
   контейнера — при переносе строк браузер сперва считает высоту строки по
   самому высокому айтему (по контенту), а потом растягивает остальные под
   неё, так что все три колонки раздуваются под самый длинный список вместо
   внутреннего скролла. Вместо переноса строк — .kanban__col--узкие колонки
   пропорционально сжимаются (flex-shrink) до min-width, а полный перенос на
   вертикальный стек (см. media query ниже) происходит до того, как для
   рядка перестаёт хватать места. */
.tasks-landing-main {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    display: flex;
    gap: 1rem;
    align-items: stretch;
}

/* Каждая колонка — обёртка, в которую ListColumn.mount кладёт .kanban__col
   (тот же паттерн, что .calendar-day-wrap на странице календаря): обёртка
   сама является flex-элементом ряда, а .kanban__col внутри неё растягивается
   на все 100% через flex:1 1 auto.
   flex-grow: 0 — намеренно НЕ растягиваем колонки на всё доступное место.
   Кубик задачи (.cube--task) везде в приложении имеет жёстко зафиксированную
   ширину 220px и не умеет тянуться (см. комментарий у .cube--task) — колонка
   шире просто добавляет пустое поле справа от кубика, а не пользу. 260px —
   минимальный запас (padding + border колонки) вокруг кубика без видимого
   зазора. main.home-page/tasks-page max-width подогнан под сумму трёх таких
   колонок + левая панель, чтобы после ряда не оставалось пустого поля. */
.tasks-landing-main > .tasks-today-column,
.tasks-landing-main > .tasks-no-date-column,
.tasks-landing-main > .tasks-deadlines-column {
    flex: 0 1 260px;
    min-width: 260px;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.tasks-landing-main .kanban__col {
    flex: 1 1 auto;
    width: 100%;
    min-height: 0;
}

/* Список тянется на всю высоту колонки (см. .tasks-landing-main .kanban__col
   выше); прокручиваются только карточки — как .calendar-day .kanban__cards
   на странице календаря. Шапка и «+ Задача» остаются на месте, не уезжают
   со скроллом. */
.tasks-landing-main .kanban__cards {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* 1220px — та же граница, что у .home-layout (см. комментарий там): ниже
   неё .tasks-nav (320px) + компактный ряд из трёх списков не помещаются без
   переноса строк, поэтому переключаемся на вертикальный стек целиком. */
@media (max-width: 1220px) {
    /* Как у main.home-page — в стеке высота содержимого больше не бьётся с
       height: 100vh / overflow: hidden, возвращаем обычный скролл страницы. */
    main.tasks-page {
        height: auto;
        min-height: 100vh;
        overflow: visible;
    }

    .tasks-landing-main {
        flex-direction: column;
    }

    .tasks-landing-main > .tasks-today-column,
    .tasks-landing-main > .tasks-no-date-column,
    .tasks-landing-main > .tasks-deadlines-column {
        min-width: 0;
    }

    .tasks-layout {
        flex-direction: column;
    }

    .tasks-layout .tasks-nav {
        max-width: none;
    }
}

#tasks-today-kanban {
    flex: 1 1 auto;
    min-width: 0;
    padding-bottom: 0;
    overflow-x: visible;
}

#tasks-today-kanban .kanban__col {
    flex: 1;
}

@media (max-width: 680px) {
    .deadlines-panel {
        width: 100%;
    }
}

/* ── Tasks landing — боковая панель «Ближайшие дедлайны» ─────────────── */

.deadlines-panel {
    flex: 0 0 280px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1rem 1.1rem;
    align-self: stretch;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    max-height: calc(100vh - 140px);
    overflow-y: auto;
}

.deadlines-panel__title {
    font-family: var(--font-heading);
    font-size: 1rem;
    font-weight: 700;
    margin: 0 0 0.2rem;
    color: var(--fg);
}

.deadlines-panel__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.deadlines-panel__item {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0.55rem 0.65rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
}

.deadlines-panel__item:hover,
.deadlines-panel__item:focus-visible {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 6%, var(--bg));
    outline: none;
}

/* Подсветка «дедлайн наступает раньше назначенного дня в спринте»:
   красная окантовка + мягкая заливка. Текстовое объяснение — внутри
   открытой задачи: .task-detail__deadline-missed-note (см. выше). */
.deadlines-panel__item--urgent {
    border-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, var(--bg));
}
.deadlines-panel__item--urgent:hover,
.deadlines-panel__item--urgent:focus-visible {
    border-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 14%, var(--bg));
}

.deadlines-panel__title {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--fg);
    line-height: 1.25;
    word-break: break-word;
}

.deadlines-panel__item--urgent .deadlines-panel__title::after {
    content: " ⚠";
    color: var(--danger);
    font-weight: 700;
}

.deadlines-panel__meta {
    display: flex;
    gap: 0.4rem;
    align-items: center;
    flex-wrap: wrap;
}

.deadlines-panel__date {
    font-size: 0.78rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.deadlines-panel__dl {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--muted);
    background: color-mix(in srgb, var(--muted) 12%, transparent);
    border-radius: 4px;
    padding: 0.1rem 0.35rem;
    line-height: 1.4;
}

.deadlines-panel__dl--overdue {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 12%, transparent);
}
.deadlines-panel__dl--today {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 15%, transparent);
}
.deadlines-panel__dl--soon {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.deadlines-panel__loc {
    font-size: 0.7rem;
    color: var(--muted);
    line-height: 1.3;
}

.deadlines-panel__empty {
    color: var(--muted);
    font-style: italic;
    font-size: 0.85rem;
    margin: 0;
    text-align: center;
    padding: 1.5rem 0.5rem;
}

.deadlines-panel__note {
    color: var(--muted);
    font-size: 0.75rem;
    margin: 0;
    text-align: center;
    padding-top: 0.4rem;
    border-top: 1px solid var(--border);
}

/* ==========================================================================
   Tasks — «Все задачи» (table view)
   ========================================================================== */

main.tasks-all-page {
    max-width: 1400px;
}

.tasks-all__mobile-notice {
    background: var(--code-bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 2rem;
    text-align: center;
    color: var(--muted);
    font-style: italic;
}

.tasks-all__toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-end;
    justify-content: space-between;
    margin-bottom: 1rem;
    padding: 1rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
}

.tasks-all__filters {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    flex: 1 1 auto;
    align-items: flex-end;
}

.tasks-all__filter {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 130px;
}

.tasks-all__filter--search {
    flex: 1 1 200px;
    min-width: 180px;
}

.tasks-all__filter-label {
    font-size: 0.75rem;
    color: var(--muted);
    font-family: var(--font-base);
    letter-spacing: 0.02em;
}

.tasks-all__status-toggle {
    display: flex;
    gap: 0;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
    background: var(--bg);
}

.tasks-all__status-btn {
    appearance: none;
    background: transparent;
    border: none;
    border-right: 1px solid var(--border);
    padding: 0.4rem 0.75rem;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.85rem;
    cursor: pointer;
    transition: background 0.12s;
}

.tasks-all__status-btn:last-child { border-right: none; }

.tasks-all__status-btn:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }

.tasks-all__status-btn.is-active {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    font-weight: 600;
}

.tasks-all__actions {
    position: relative;
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-shrink: 0;
}

.tasks-all__btn {
    appearance: none;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.5rem 0.85rem;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.88rem;
    cursor: pointer;
    transition: opacity 0.12s, border-color 0.12s;
    position: relative;
}

.tasks-all__btn:hover { border-color: var(--accent); }

.tasks-all__btn--primary {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border-color: var(--accent);
    font-weight: 600;
}

.tasks-all__btn--primary:hover { opacity: 0.85; }

.tasks-all__btn--ghost { background: transparent; }

.tasks-all__btn--danger { color: var(--danger); border-color: var(--danger); }

.tasks-all__btn--danger:hover:not(:disabled) {
    background: var(--danger);
    color: var(--danger-fg);
}

.tasks-all__btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* Icon-only variant (Колонки / Удалить выбранные) — square button, fixed
   42px to match .filters-bar's height so the whole toolbar row lines up. */
.tasks-all__btn--icon {
    width: 42px;
    height: 42px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.tasks-all__btn--icon svg { width: 16px; height: 16px; }

/* Selection-count badge on #bulk-delete-btn — reuses .topbar__badge (see
   NotificationCenter) so both icon-button badges look the same site-wide. */
.tasks-all__btn-badge { pointer-events: none; }

/* Mounted on document.body with position:fixed, positioned by
   openColumnsPanel()/_positionColumnsPanel() in pages/tasks-all.js against
   #columns-btn's getBoundingClientRect(), clamped to the viewport — same
   convention as .filters-bar__menu / UIDropdown (js/shared/dropdown.js).
   #columns-btn sits at the right edge of the toolbar, so anchoring via a
   plain `left: 0` (relative to its parent) ran the panel off the right
   edge of the screen and forced a page-wide horizontal scrollbar. */
.tasks-all__columns-panel {
    position: fixed;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 4px 16px color-mix(in srgb, var(--fg) 8%, transparent);
    padding: 0.5rem 0.75rem;
    z-index: 50;
    min-width: 200px;
    max-height: 400px;
    overflow-y: auto;
}

.tasks-all__columns-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.25rem;
    cursor: pointer;
    font-family: var(--font-base);
    font-size: 0.88rem;
    color: var(--fg);
    user-select: none;
}

.tasks-all__columns-row:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }

/* Virtual table */

.vt-scroll {
    height: calc(100vh - 380px);
    min-height: 320px;
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg-elevated);
}

.vt-stack {
    position: relative;
    min-width: 100%;
}

.vt-spacer {
    padding: 0;
    margin: 0;
    pointer-events: none;
}

.vt-table {
    width: 100%;
    min-width: 1100px;
    border-collapse: separate;
    border-spacing: 0;
    table-layout: fixed;
}

.vt-th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--bg-elevated);
    border-bottom: 1px solid var(--border);
    text-align: left;
    font-weight: 600;
    font-family: var(--font-base);
    font-size: 0.82rem;
    letter-spacing: 0.02em;
    color: var(--muted);
    height: 36px;
    padding: 0 12px;
    box-sizing: border-box;
    white-space: nowrap;
    user-select: none;
}

.vt-th--sortable { cursor: pointer; }
.vt-th--sortable:hover { color: var(--fg); }
.vt-th--sorted { color: var(--fg); }
.vt-th--sorted::after {
    content: attr(data-sort-dir);
    display: inline-block;
    margin-left: 0.4rem;
    font-size: 0.7rem;
    color: var(--accent);
}
.vt-th--sorted[data-sort-dir="asc"]::after  { content: "↑"; }
.vt-th--sorted[data-sort-dir="desc"]::after { content: "↓"; }

.vt-tr { background: transparent; }
.vt-tr:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }

.vt-td {
    height: 36px;
    padding: 0 12px;
    border-bottom: 1px solid var(--border);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: var(--font-base);
    font-size: 0.88rem;
    color: var(--fg);
    vertical-align: middle;
    box-sizing: border-box;
}

.tasks-all__row {
    cursor: pointer;
}

.tasks-all__row--done .vt-td,
.tasks-all__row--done .tasks-all__title {
    opacity: 0.55;
    text-decoration: line-through;
}

/* Колонки фиксированной ширины. .vt-table is table-layout:fixed, which
   takes column widths from the FIRST row of the table — i.e. the <thead>
   row, not <tbody>. renderHeader() below applies these same
   tasks-all__cell--<key> classes to each <th>, so both rows agree; without
   that the browser falls back to splitting the space evenly across
   columns, which is what made the select/title columns balloon to ~136px
   each with the checkbox stranded in the corner. Keys here match
   ALL_COLUMNS' `key` in pages/tasks-all.js exactly — keep them in sync. */
.tasks-all__cell--select,
.tasks-all__cell--actions { width: 36px; padding: 0 4px; text-align: center; }

.tasks-all__cell--title { width: 280px; }
.tasks-all__cell--status { width: 110px; }
.tasks-all__cell--priority { width: 100px; }
.tasks-all__cell--size_minutes { width: 90px; text-align: right; }
.tasks-all__cell--deadline { width: 120px; }
.tasks-all__cell--area,
.tasks-all__cell--project { width: 150px; }
.tasks-all__cell--created_at,
.tasks-all__cell--updated_at { width: 130px; }
.tasks-all__cell--author { width: 110px; }

.tasks-all__title {
    font-weight: 500;
    color: var(--fg);
}
.tasks-all__title--done {
    text-decoration: line-through;
    opacity: 0.6;
}
.tasks-all__title-deadline {
    color: var(--muted);
    font-size: 0.78rem;
    margin-left: 0.5rem;
}

.tasks-all__pill {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    font-size: 0.75rem;
    font-family: var(--font-base);
    font-weight: 500;
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--muted);
}

.tasks-all__pill--done {
    color: var(--success-fg, #ffffff);
    background: var(--success, #2f9e44);
    border-color: var(--success, #2f9e44);
}

.tasks-all__pill--active {
    color: var(--accent-fg);
    background: var(--accent-gradient);
    border-color: var(--accent);
}

.tasks-all__pill--prio-normal { color: var(--muted); }
.tasks-all__pill--prio-high {
    color: var(--accent-fg);
    background: color-mix(in srgb, var(--accent) 30%, transparent);
    border-color: var(--accent);
}
.tasks-all__pill--prio-max {
    color: var(--accent-fg);
    background: var(--accent-gradient);
    border-color: var(--accent);
    font-weight: 700;
}

.tasks-all__date {
    color: var(--muted);
    font-size: 0.82rem;
}

.tasks-all__trash {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    padding: 0.3rem;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
    transition: color 0.12s, border-color 0.12s;
}
.tasks-all__trash:hover {
    color: var(--danger);
    border-color: var(--danger);
}

/* «Проект» column cell — name + a small "open in new tab" icon-link when
 * the task has a project_id (see pages/tasks-all.js::_renderCell). */
.tasks-all__project-cell {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    max-width: 100%;
}
.tasks-all__project-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tasks-all__project-link {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    padding: 0.2rem;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    text-decoration: none;
    flex: 0 0 auto;
    transition: color 0.12s, border-color 0.12s;
}
.tasks-all__project-link:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.tasks-all__select {
    cursor: pointer;
}

.tasks-all__empty {
    text-align: center;
    padding: 2rem;
    color: var(--muted);
    font-style: italic;
}

/* ==========================================================================
   Tasks — backlog page (kanban board)
   ========================================================================== */

main.tasks-backlog-page,
main.tasks-regular-page,
main.calendar-page,
main.rules-page,
main.task-templates-page {
    max-width: 1600px;
}

/* Страница бэклога (Корзина + К работе) — flex-колонка на всю высоту
   вьюпорта, чтобы колонка «Корзина» могла растянуться на всю
   оставшуюся высоту (как колонка-дня в календаре) и прокручивать
   задачи внутри себя, а страница при этом не скроллилась.
   Структура повторяет main.calendar-page (см. блок ниже): общий
   <main> с overflow:hidden, и дочерние блоки (шапка → фильтры →
   #kanban) текут сверху вниз, последний забирает flex: 1 1 auto. */
main.tasks-backlog-page {
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
    /* как у .calendar-page: 4rem сверху (под глобальный топбар), 1rem по
       бокам и снизу — без него контент упирается в край окна */
    padding: 4rem 1rem 1rem;
    /* Равномерный вертикальный зазор между всеми блоками (шапка → фильтры
       → #kanban). Раньше зазор между шапкой и фильтрами был слишком
       тесным (обнулили margin-bottom у .kanban-page-header, gap был 0);
       теперь 1rem снизу у каждого ребёнка, плюс у фильтр-бара отменяем
       собственный margin-bottom (см. ниже), чтобы шапка→фильтры и
       фильтры→доска были симметричны. */
    gap: 1rem;
}

/* Шапка страницы (← Задачи / Бэклог / …) у .kanban-page-header глобально
   имеет margin-bottom: 1.5rem. В flex-колонке родителя зазор уже
   задаётся через gap, иначе суммарно получалось бы ~2rem до фильтров.
   Обнуляем — пусть зазор задаёт только gap родителя (1rem). */
main.tasks-backlog-page > .kanban-page-header {
    margin-bottom: 0;
}

/* .filters's `flex: 1 1 0` (see § Filters row below) is tuned for its OTHER
   host, .tasks-all__toolbar, a flex-ROW where flex-basis:0 stops it from
   claiming full natural width before the row decides to wrap. Here on the
   backlog page the parent (main.tasks-backlog-page) is a flex-COLUMN, so
   that same flex-grow:1 makes .filters stretch tall to match .kanban's
   flex:1 1 auto — the 42px-tall filter bar inside just floats at the top of
   a now-tall box, showing as a big empty gap above the board. Pin it back
   to its content height on this page only. */
main.tasks-backlog-page .filters {
    flex: 0 0 auto;
}

/* Два таба (Бэклог / Идеи, tasks_system.md § Страница бэклог) — общий
   `.rules-tabs` уже несёт свой `margin-bottom: 1rem`; родитель здесь —
   flex-колонка с `gap: 1rem`, так что без обнуления зазор под табами
   удвоится (тот же приём, что и у `.kanban-page-header` выше). */
main.tasks-backlog-page > .rules-tabs {
    margin-bottom: 0;
}

/* Каждый таб — сам flex-колонка, занимающая всю оставшуюся высоту (раньше
   это делал main напрямую): «Бэклог» держит `.filters` + `.kanban`,
   «Идеи» — одну плоскую сетку. `min-height: 0` обязателен, чтобы
   внутренний overflow у `.kanban`/`.kanban__col--list` продолжал работать
   через ещё один уровень вложенности. */
main.tasks-backlog-page .rules-tab {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    gap: 1rem;
}

/* Страница правил (rules.html: Идеи / Бэклог / В работе / Завершено) —
   тот же приём, что и у .tasks-backlog-page выше: <main> занимает ровно
   высоту вьюпорта без собственной прокрутки страницы, а каждый таб решает
   сам, что скроллится внутри него (см. TODO.md § Правила). */
main.tasks-regular-page {
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
    padding: 4rem 1rem 1rem;
    gap: 1rem;
}
main.tasks-regular-page > .kanban-page-header {
    margin-bottom: 0;
}
main.tasks-regular-page > .page-title {
    margin-bottom: 0;
}
main.tasks-regular-page > .rules-tabs {
    margin-bottom: 0;
}
main.tasks-regular-page .rules-tab {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    gap: 0.5rem;
}
/* «Идеи» / «В работе» / «Завершено» — тулбар фиксированной высоты сверху
   (.rules-tab__toolbar), сама сетка кубиков растягивается на остаток
   и скроллится внутри себя. align-content: flex-start — иначе при малом
   числе кубиков grid растягивает единственную строку на всю высоту. */
main.tasks-regular-page .rules-tab__toolbar {
    flex: 0 0 auto;
}
main.tasks-regular-page .rules-grid {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    align-content: flex-start;
}

/* ── Regular tasks page sections ─────────────────────────────────────────── */

.regular__section { margin-bottom: 1rem; }

/* Компактные отступы для колонок бэклога на странице правил —
   правила короче задач, крупный padding выглядит пустовато. */
main.tasks-regular-page .kanban__col {
    padding: 0.5rem 0.4rem;
    gap: 0.4rem;
}

main.tasks-regular-page .kanban__col-body {
    padding: 0.15rem 0 0.4rem;
    gap: 0.35rem;
}


.regular__section-title {
    font-family: var(--font-heading);
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0 0 0.5rem;
}

/* Внутренние табы на rules.html (Бэклог / В работе / Завершено) — статус-фильтры.
   Стилистически едино с .projects__tab на projects.html: underline + accent при active.
   Никаких кнопочных заливок; акцент — только в нижнем бордере. */
.rules-tabs {
    display: flex;
    gap: 0.25rem;
    border-bottom: 1px solid var(--border);
    margin-bottom: 1rem;
}

.rules-tabs__tab {
    appearance: none;
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    border-radius: 0.4rem 0.4rem 0 0;
    margin-bottom: -1px;
    padding: 0.5rem 0.9rem;
    font-family: var(--font-base);
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--muted);
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s, transform 0.15s;
}

.rules-tabs__tab:hover { color: var(--fg); }

.rules-tabs__tab--active {
    color: var(--fg);
    border-bottom-color: var(--accent);
}

/* Count badge next to a tab label — same look as .projects__tab-count,
   reused by every page built on the shared .rules-tabs component
   (rules.html, focuses.html, tasks-backlog.html). */
.rules-tabs__tab-count {
    display: inline-block;
    margin-left: 0.4rem;
    font-size: 0.78rem;
    font-weight: 400;
    background: var(--code-bg);
    color: var(--muted);
    border-radius: 10px;
    padding: 0.05rem 0.45rem;
    min-width: 1.4em;
    text-align: center;
}

.rules-tabs__tab--active .rules-tabs__tab-count {
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    color: var(--accent);
}

/* Drop target while dragging a rule cube onto a tab (cross-tab status
   change — см. tasks_system.md § Страница Правила "между вкладками").
   Much stronger than the active-tab underline — a filled accent pill,
   so it reads unambiguously as "drop here" against the plain-text tabs
   next to it (a background tint alone was too easy to miss mid-drag). */
.rules-tabs__tab--drag-over {
    color: var(--accent-fg);
    background: var(--accent-gradient);
    border-bottom-color: var(--accent);
    border-radius: 0.4rem;
    transform: scale(1.08);
    box-shadow: 0 2px 10px color-mix(in srgb, var(--accent) 50%, transparent);
}

/* Active rules — оформляется как выделенная «колонка»: чуть светлее
   фона в светлой теме, чуть темнее в тёмной (за счёт --bg-elevated),
   со скруглёнными углами и акцентной верхней рамкой. Карточки внутри —
   фиксированной ширины, обтекают строкой. Кнопка «+ Правило» имеет ту же
   ширину, что карточки, чтобы лежать на одной строке с ними. */
.rules__grid {
    padding: 0.5rem;
    min-height: 2.5rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-top: 2px solid var(--accent);
    border-radius: 10px;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: flex-start;
    align-content: flex-start;
}

/* Колонка активных правил участвует в общей DnD-зоне — поэтому
   визуально подсвечивается при dragover, как остальные kanban__col. */
.rules__grid--drag-over {
    background: color-mix(in srgb, var(--accent) 8%, var(--bg-elevated));
    border-color: var(--accent);
}

.rules__cards {
    display: contents;
}

/* В активной flex-сетке карточки фиксированной ширины, обтекают рядами.
   В колонках бэклога (.kanban__col) карточка заполняет ширину колонки
   (как обычные task-карточки) — переопределяется ниже. */
.rules__grid .rule-card {
    width: 230px;
    flex: 0 0 230px;
}

.kanban__col .rule-card {
    width: 100%;
    flex: none;
    max-width: none;
}

/* Add-rule button (inline title input) — используется и в активной сетке,
   и в каждой колонке бэклога (через обёртку .rules__add-btn-wrap в kanban__col) */
.rules__add-btn {
    appearance: none;
    border: 1px dashed var(--border);
    background: transparent;
    color: var(--muted);
    font: inherit;
    font-size: 0.85rem;
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    width: 230px;
    flex: 0 0 230px;
    text-align: left;
    cursor: pointer;
    transition: border-color 120ms, color 120ms, background 120ms;
}

.rules__add-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 4%, transparent);
}

/* Когда обёртка в режиме редактирования — кнопка скрыта, виден только
   инпут, который растягивается на всю ширину обёртки. */
.rules__add-btn-wrap--editing .rules__add-input {
    display: block;
    width: 100%;
}

.rules__add-btn-wrap--editing .rules__add-btn {
    display: none;
}

/* В активной сетке обёртка-add-btn сама становится flex-элементом той же
   ширины, что карточка (а не контейнером-строкой). */
.rules__grid > .rules__add-btn-wrap {
    width: 230px;
    flex: 0 0 230px;
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* В колонках бэклога — живёт внутри kanban__col-body, отступы col-body уже есть */
.kanban__col .rules__add-btn-wrap {
    width: 100%;
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

.kanban__col .rules__add-btn {
    width: 100%;
    flex: none;
}

.rules__add-input {
    width: 100%;
    box-sizing: border-box;
    background: var(--bg-elevated);
    border: 1px solid var(--accent);
    color: var(--fg);
    font: inherit;
    font-size: 0.875rem;
    padding: 0.45rem 0.6rem;
    border-radius: 6px;
    outline: none;
    margin-top: 0.25rem;
}

/* ── Rule detail panel ───────────────────────────────────────────────────── */
/* Reuses .task-detail shell; only rule-specific inner elements are styled here */

.rule-detail__recurrence { margin-top: 0.25rem; }

.rule-detail__status {
    margin-top: 0.6rem;
    font-size: 0.8rem;
    color: var(--muted);
}

.rule-detail__status-value { color: var(--fg); }

/* Property-badges block at the top of the rule panel: «В неделю» + «Следующее».
   Каждая строка — как обычное свойство task-detail (label слева, value справа),
   плюс иконка слева от label. Шрифт — такой же как у остальных полей (0.9rem),
   без акцента: «Следующее» — нейтральная справка, «В неделю» — числовая
   характеристика, читаемая, но не «кричащая». */
.rule-detail__props {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.rule-detail__prop {
    display: grid;
    grid-template-columns: 0.95rem auto 1fr;
    gap: 0.4rem;
    align-items: center;
    font-size: 0.9rem;
    line-height: 1.4;
    min-width: 0;
}

.rule-detail__prop-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    opacity: 0.75;
}

.rule-detail__prop-key {
    color: var(--muted);
    font-weight: 600;
    text-transform: uppercase;
    font-size: 0.74rem;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

.rule-detail__prop-value {
    color: var(--fg);
    font-size: 0.9rem;
    min-width: 0;
}

/* Weekly estimate — единственная числовая характеристика правила, в плашке.
   В property-row подсвечиваем чуть сильнее, чтобы бросалась в глаза, но
   без лишнего акцента — это не «награда», а метрика. */
#rd-weekly-est .rule-detail__prop-value { font-weight: 600; }

.rule-detail__rec-options {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    margin-bottom: 0.5rem;
}

.rule-detail__rec-label {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    font-size: 0.875rem;
    color: var(--fg);
    cursor: pointer;
    user-select: none;
}

.rule-detail__rec-label input[type="radio"] {
    accent-color: var(--accent);
    cursor: pointer;
}

.rule-detail__rec-every-n {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-left: 1.6rem;
    margin-top: 0.1rem;
}

.rule-detail__rec-n-input {
    width: 4.5rem;
    padding: 0.25rem 0.5rem;
    background: var(--code-bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.875rem;
}

.rule-detail__rec-n-input:focus { outline: none; border-color: var(--accent); }

.rule-detail__rec-unit {
    padding: 0.25rem 0.4rem;
    background: var(--code-bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.875rem;
    cursor: pointer;
}

.rule-detail__rec-weekly {
    margin-top: 0.4rem;
    padding-left: 1.6rem;
}

.rule-detail__rec-weekly-label {
    font-size: 0.8rem;
    color: var(--muted);
    margin-bottom: 0.35rem;
}

.rule-detail__rec-weekdays {
    display: flex;
    gap: 0.3rem;
    flex-wrap: wrap;
}

.rule-detail__rec-weekday {
    min-width: 2.1rem;
    padding: 0.3rem 0.55rem;
    background: var(--code-bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.825rem;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s, color 0.12s;
}

.rule-detail__rec-weekday:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.rule-detail__rec-weekday--on {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-fg);
}

.rule-detail__rec-weekday--on:hover {
    /* сохраняем инверсный цвет при наведении на активную кнопку */
    color: var(--accent-fg);
    opacity: 0.88;
}

.rule-detail__rec-weekly-hint {
    margin-top: 0.4rem;
    font-size: 0.78rem;
    color: var(--muted);
    font-style: italic;
}

.rule-detail__task-list {
    padding: 0;
    margin: 0.1rem 0 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.rule-detail__task-empty {
    font-size: 0.85rem;
    color: var(--muted);
    font-style: italic;
    padding: 0.2rem 0;
}

.rule-detail__task-add-area { margin-top: 0.1rem; }

.rule-detail__add-task-btn { font-size: 0.825rem; }

.rule-detail__add-task-input {
    width: 100%;
    padding: 0.35rem 0.5rem;
    background: var(--code-bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.85rem;
}

.rule-detail__add-task-input:focus { outline: none; border-color: var(--accent); }

/* ── Template mini-cards inside rule detail panel ────────────────────────── */

.rule-detail__template-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 0.4rem;
    margin: 0.25rem 0 0.5rem;
}

.rule-detail__template-list > .rule-detail__task-empty {
    grid-column: 1 / -1;
}

.rule-detail__template-mini {
    padding: 0.35rem 0.5rem;
    cursor: pointer;
}

.rule-detail__template-mini .kanban__card-title {
    font-size: 0.82rem;
    line-height: 1.35;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.rule-detail__template-mini .kanban__card-meta {
    margin-top: 0.2rem;
    gap: 0.25rem;
}

.rule-detail__template-mini--missing {
    opacity: 0.5;
    cursor: default;
}

.rule-detail__template-missing-label {
    font-style: italic;
    color: var(--muted);
}

.rule-detail__template-remove-btn {
    position: absolute;
    top: 0.2rem;
    right: 0.2rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    color: var(--muted);
    width: 16px;
    height: 16px;
    padding: 0;
    border-radius: 3px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s, color 0.15s, border-color 0.15s;
    z-index: 1;
}

.rule-detail__template-mini:hover .rule-detail__template-remove-btn {
    opacity: 1;
    pointer-events: auto;
}

.rule-detail__template-remove-btn:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.kanban-page-header {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    margin-bottom: 1.5rem;
    flex-wrap: wrap;
}

.kanban-page-header__title {
    margin: 0;
}

.kanban-page-header__subtitle {
    color: var(--muted);
    font-size: 0.85rem;
    font-weight: 600;
    font-family: var(--font-base);
    letter-spacing: 0.01em;
    white-space: nowrap;
}

.kanban-back {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    color: var(--muted);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 600;
    transition: color 0.15s;
    flex-shrink: 0;
}

.kanban-back:hover { color: var(--fg); }

/* ── Task templates grid ─────────────────────────────────────────────────── */

.task-templates__toolbar {
    padding: 0 1rem;
    margin-bottom: 0.75rem;
}

/* Кнопка «+ Новый шаблон» — по умолчанию `.kanban__add-btn` растягивается
   на всю ширину контейнера (это нужно внутри узкой колонки бэклога), но
   здесь тулбар — это вся ширина страницы, а не колонка. Ограничиваем до
   ширины одного кубика шаблона (тот же 230px, что и `minmax` сетки ниже),
   чтобы кнопка не растягивалась на весь экран. */
.task-templates__toolbar .kanban__add-btn,
.task-templates__toolbar .kanban__add-input {
    width: 230px;
    max-width: 100%;
}

.task-templates__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 0.5rem;
    padding: 0 1rem 1rem;
}

/* ── Board ────────────────────────────────────────────────────────────────── */

.kanban {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    overflow-x: auto;
    /* Внутри main.tasks-backlog-page (flex-колонка, 100vh, overflow:hidden)
       этот блок должен занять всё, что осталось после шапки и фильтров.
       min-height: 0 обязателен: иначе флекс-цепочка не сможет ужать детей
       ниже размера содержимого и внутренний overflow у колонки не сработает.
       padding-bottom: 1.5rem был запасом под вертикальную прокрутку страницы;
       на странице бэклога её больше нет (overflow:hidden у <main>) — обнуляем. */
    flex: 1 1 auto;
    min-height: 0;
    padding-bottom: 0;
    /* allow the board to extend past the right edge on small screens */
    min-width: 0;
    /* Обе колонки (Корзина / К работе) теперь ограничены высотой этого
       ряда и скроллят кубики сами внутри себя (.kanban__col-wrap--list /
       --grid выше) — так что вертикально сам `.kanban` больше не
       скроллится. `overflow-x: auto` остаётся ради узких экранов, где обе
       колонки суммарно не помещаются по ширине. По спеке ось, оставленная
       `visible` рядом с не-`visible` осью, «повышается» до `auto` — так
       что `overflow-y` тоже технически `auto`, хоть и не должен
       срабатывать. `scrollbar-gutter: stable` всё равно держим
       unconditionally: без него появление/исчезновение горизонтального
       скроллбара забирало бы ~15-17px у ширины, доступной
       `.kanban__col--grid .kanban__cards`'s `auto-fill` сетке — этого
       хватает, чтобы уронить число колонок (напр. 4 → 3) и заметно
       расширить каждый кубик. */
    scrollbar-gutter: stable;
}

/* ── Column ───────────────────────────────────────────────────────────────── */

.kanban__col {
    flex: 0 0 230px;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.75rem;
}

.kanban__col-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0 0.1rem 0.25rem;
    border-bottom: 1px solid var(--border);
}

.kanban__col-title {
    font-family: var(--font-heading);
    font-size: 0.82rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
}

.kanban__col-count {
    font-size: 0.75rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.05rem 0.5rem;
    color: var(--muted);
    min-width: 1.5rem;
    text-align: center;
}

/* ── Add-task area (button ↔ inline input) ────────────────────────────────── */

.kanban__add-area {
    min-height: 2.2rem;
}

.kanban__add-btn {
    width: 100%;
    background: transparent;
    border: 1px dashed var(--border);
    border-radius: 6px;
    color: var(--muted);
    font: inherit;
    font-size: 0.82rem;
    padding: 0.45rem 0.6rem;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.kanban__add-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.kanban__add-input {
    width: 100%;
    background: var(--bg);
    border: 1.5px solid var(--accent);
    border-radius: 6px;
    color: var(--fg);
    font: inherit;
    font-size: 0.88rem;
    padding: 0.45rem 0.6rem;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
    box-sizing: border-box;
}

.kanban__add-input:focus { outline: none; }

.kanban__add-input::placeholder { color: var(--muted); }

/* ── Task cards ───────────────────────────────────────────────────────────── */

.kanban__cards {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    min-height: 0.5rem;
}

/* ── Backlog layout (Бэклог) ───────────────────────────────────────────────
   Per `tasks_system.md` § Страница бэклог: «слева — вертикальная колонка
   basket, справа от нее сетка "к работе"». Basket is a single vertical
   list (compact); todo is a multi-column grid. */

/* Wrapper around each ListColumn — сам .kanban__col НЕ flex-айтем, потому
   что ListColumn.mount чистит свой target при каждом вызове, и единственный
   способ держать обе колонки (#kanban → Корзина + К работе) — оборачивать
   каждую в свой блок-флекс-айтем. «Корзина» остаётся узкой (270px — как
   колонка-дня в календаре, чтобы визуально два лендинга были единым
   стилем), «К работе» растягивается на остаток ширины и её внутренняя
   сетка кубиков становится по-настоящему многоколоночной. */
.kanban__col-wrap {
    flex: 0 0 270px;
    min-width: 0;
    display: flex;
    flex-direction: column;
}
/* «К работе» тоже заполняет всю высоту flex-цепочки и прокручивает свои
   кубики внутри себя (см. .kanban__col-wrap--list ниже — тот же приём),
   а не растягивает страницу: раньше эта колонка росла вместе с
   содержимым, из-за чего скроллился весь `.kanban` целиком (и «Корзина»
   уезжала из вида вместе с ним). */
.kanban__col-wrap--grid {
    flex: 1 1 auto;
    min-width: 320px;
    height: 100%;
    min-height: 0;
    overflow: hidden;
}
.kanban__col-wrap--grid > .kanban__col {
    min-height: 0;
    overflow: hidden;
}
.kanban__col-wrap > .kanban__col {
    flex: 1 1 auto;
    width: 100%;
}

/* «Корзина» на странице бэклога — фиксированной ширины 270px (как день в
   календаре) и заполняет всю оставшуюся высоту flex-цепочки
   <main.tasks-backlog-page> → <.kanban> → <.kanban__col-wrap>.
   height: 100% решается благодаря явной высоте предков;
   min-height: 0 + overflow: hidden нужны, чтобы внутренний overflow на
   .kanban__cards (см. ниже) сработал, а колонка не раздувалась под
   содержимое. Тот же приём применяется и к --grid-варианту
   (.kanban__col-wrap--grid выше) — обе колонки одинаково зажаты по
   высоте `.kanban` и скроллят свои кубики сами. */
.kanban__col-wrap--list {
    height: 100%;
    min-height: 0;
    overflow: hidden;
}
.kanban__col-wrap--list > .kanban__col {
    min-height: 0;
    overflow: hidden;
}

.kanban__col--list .kanban__cards {
    display: flex;
    flex-direction: column;
    /* В «Корзине» (layout=list, страница бэклога) карточки сами
       подстраиваются под высоту колонки и скроллятся внутри, если их
       слишком много. Селектор привязан к --list, чтобы не задеть
       calendar-day .kanban__cards (у того своё точечное правило ниже в
       календарном блоке). min-height: 0 обязателен во flex-цепочке,
       иначе контейнер не ужмётся ниже размера содержимого. */
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

.kanban__col--grid .kanban__cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 0.5rem;
    /* Скроллится сама (не вся страница) — см. `grid-template-rows` у
       `.kanban__col--grid` ниже: эта строка сетки — единственная гибкая
       (`minmax(0, 1fr)`), остальные (шапка, add-area) — по содержимому.
       min-height: 0 обязателен, иначе grid-item не ужмётся ниже своего
       содержимого и overflow-y не сработает (тот же приём, что у
       `.kanban__col--list .kanban__cards` выше). Внутри `.kanban__col-wrap
       --grid` (см. выше) высота колонки ограничена высотой `.kanban`, так
       что здесь появляется реальный скролл; в самостоятельном
       использовании (вкладка «Идеи» — без обёртки, без ограничения
       высоты) это правило безвредно: строка просто остаётся по контенту,
       как и раньше. */
    min-height: 0;
    overflow-y: auto;
}

/* rules.html «В работе» / «Завершено» — same multi-column grid as the
   Бэклог page's «К работе» (.kanban__col--grid .kanban__cards above),
   but these two tabs are full-width standalone sections (no narrow
   .kanban__col wrapper to fight), so the class applies directly. */
.rules-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 0.5rem;
}

/* «+ Правило» toolbar sits ABOVE .rules-grid (own row), not as a grid
   cell inside it — as a grid item it would occupy a slot in the cube
   flow, and when the cube count wrapped to a new row the button left a
   stretched empty gap next to the lone leftover cube. Cube-width via
   an explicit max-width (kanban__add-btn is width:100% by default,
   which here would mean 100% of the full-width toolbar row). */
.rules-tab__toolbar {
    margin-bottom: 0.5rem;
}
/* «В работе» tab only — «+ Правило» on the left, the weekly-total badge
   (TODO.md § Правила) on the right. Other tabs' toolbars keep the plain
   block layout above since they have no second child. */
.rules-tab__toolbar--with-total {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.rules-tab__toolbar .kanban__add-area {
    max-width: 220px;
}

/* В grid-колонке кубик живёт в одной ячейке сетки `minmax(220px, 1fr)` и
   расширяется по `1fr`, если места хватает. Чтобы кнопка «+ Задача» была
   такой же по ширине, как один кубик, она должна жить в той же сетке.
   Превращаем сам `.kanban__col--grid` в grid; заголовок растягиваем на
   всю строку (`grid-column: 1 / -1`), add-area становится одной ячейкой,
   cards — строкой во всю ширину с собственной вложенной сеткой того же
   шаблона. Обе сетки считают треки от одной и той же ширины → первая
   ячейка add-area и первая ячейка cards выровнены по ширине. Применяем
   только к grid-лейауту: list-колонка (Корзина) и так во всю ширину,
   кубики там списком на всю колонку — там кнопку менять не надо.
   `grid-template-rows`: шапка и add-area — по содержимому (`auto`),
   последняя строка (cards) — гибкая (`minmax(0, 1fr)`), чтобы забрать
   весь остаток высоты колонки и включить свой internal-скролл (см.
   `.kanban__col--grid .kanban__cards` выше) вместо раздувания страницы.
   Порядок реальных детей (шапка → add-area → cards, либо на вкладке
   «Идеи» без шапки — add-area → cards) через auto-placement сам
   раскладывается по этим трём трекам — лишний трек, если детей меньше,
   просто остаётся нулевой высоты. */
.kanban__col--grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    grid-template-rows: auto auto minmax(0, 1fr);
    gap: 0.5rem;
    min-height: 0;
}
.kanban__col--grid > .kanban__col-head {
    grid-column: 1 / -1;
}
/* `.kanban__cards` в grid-колонке растягиваем на всю ширину родительской
   сетки — без этого он попадает в одну ячейку (~259px в вьюпорте 1400),
   а его собственная внутренняя сетка `minmax(220px, 1fr)` помещает
   внутрь только один столбец кубиков, и весь «grid» превращается в
   обычный список. С `grid-column: 1 / -1` контейнер задач получает
   всю ширину обёртки (≈1086px в вьюпорте 1400) и сетка
   auto-fill создаёт уже 3-4 столбца кубиков. */
.kanban__col--grid > .kanban__cards {
    grid-column: 1 / -1;
}
.kanban__col--grid > .kanban__add-area > .kanban__add-btn,
.kanban__col--grid > .kanban__add-area > .kanban__add-input {
    width: 100%;
}

/* Вкладка «Идеи» на Бэклоге — рисует TaskCube (.cube--task), у которого
   ширина ЖЁСТКО зафиксирована в 220px (см. комментарий у .cube--task) и
   он не умеет растягиваться по `1fr`. На широкой полосе
   (main.tasks-backlog-page — 1600px) с малым числом задач
   `minmax(220px, 1fr)` растягивает КАЖДЫЙ трек на всю лишнюю ширину, и
   фиксированный кубик повисает в нём с огромным пустым полем — визуально
   читается как гигантский зазор между кубиками. Фиксируем трек ровно под
   ширину кубика; неиспользованное место остаётся в конце строки, а не
   раздувает ячейки. Не трогаем общий `.kanban__col--grid` — на Бэклог
   («К работе») треки заполняет более плотный набор задач, где эффект не
   так заметен; на Правилах — RuleCube (без фиксированной ширины,
   растягивается штатно). */
#backlog-tab-idea .kanban__cards {
    grid-template-columns: repeat(auto-fill, 220px);
}

/* Drop indicators used by manual-reorder DnD within the same column. */
.cube--drop-before { box-shadow: 0 -2px 0 var(--accent); }
.cube--drop-after  { box-shadow: 0 2px 0 var(--accent); }
.kanban__col--drag-over { outline: 2px dashed var(--accent); outline-offset: -2px; }

/* В grid-лейауте кубики текут горизонтально внутри строки, поэтому
   индикатор вставки показываем сбоку (слева/справа), а не сверху/снизу.
   Согласовано с логикой _dropTarget в pages/tasks-backlog.js: для grid
   курсор сравнивается по X с центром кубика, для list — по Y. */
.kanban__col--grid .cube--drop-before { box-shadow: -2px 0 0 var(--accent); }
.kanban__col--grid .cube--drop-after  { box-shadow: 2px 0 0 var(--accent); }

/* Колонка-источник текущего drag — приглушаем, чтобы было видно
   «отсюда уезжает задача». Используется календарём (pages/tasks-calendar.js)
   и страницей «Все задачи», если позже подключат тот же контракт.
   Подсветка приоритетнее, чем drop-over: если источник и цель — одна и
   та же колонка (пустой drag), drop-over не ставится, а источник уже
   подсвечен. */
.kanban__col--drag-source { opacity: 0.55; }

/* Сам тащимый кубик — полупрозрачный, чтобы его было видно
   одновременно с ghost-картинкой. */
.cube--dragging { opacity: 0.45; cursor: grabbing; }

.kanban__card {
    position: relative;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.55rem 0.7rem;
    cursor: pointer;
    transition: border-color 0.15s, box-shadow 0.15s;
    user-select: none;
}

.kanban__card:hover {
    border-color: var(--accent);
    box-shadow: 0 1px 6px color-mix(in srgb, var(--accent) 15%, transparent);
}

.kanban__card--active {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
    background: color-mix(in srgb, var(--accent) 6%, var(--bg));
}

.kanban__card--dragging {
    opacity: 0.35;
    box-shadow: none;
}

.kanban__drop-line {
    height: 2px;
    background: var(--accent);
    border-radius: 1px;
    margin: 2px 4px;
    flex-shrink: 0;
}

/* ── Завершение задачи (только в спринте) ─────────────────────────────────── */

/* Кнопка «Завершить» на карточке. По умолчанию скрыта — появляется при
   hover или у завершённой карточки. Внутри корзинных карточек спринта
   (kanban__card--basket-item) стиль переопределяется ниже: кнопка становится
   flex-сиблингом рядом с .kanban__card-backlog-btn, чтобы не накладываться. */
.kanban__card-complete-btn {
    position: absolute;
    top: 0.35rem;
    right: 0.4rem;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.22rem;
    border-radius: 4px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    opacity: 0;
    pointer-events: none;
    transition: color 0.15s, border-color 0.15s, opacity 0.15s;
    z-index: 1;
}

/* Внутри корзинной карточки — кнопка flex-сиблинг (не absolute), скрыта
   по умолчанию, появляется при наведении на карточку. */
.kanban__card--basket-item .kanban__card-complete-btn {
    position: static;
    opacity: 0;
    pointer-events: none;
    padding: 0.28rem;
    flex-shrink: 0;
    margin-top: 1px;
    z-index: auto;
}

/* Кнопка «Удалить» на корзинной карточке — тот же подход: flex-сиблинг,
   появляется на hover. Деструктивное действие — отдельный стиль hover. */
.kanban__card--basket-item .kanban__card-delete-btn {
    position: static;
    opacity: 0;
    pointer-events: none;
    padding: 0.28rem;
    flex-shrink: 0;
    margin-top: 1px;
    z-index: auto;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
    font-size: 0;
    transition: color 0.15s, border-color 0.15s, opacity 0.15s;
}

.kanban__card--basket-item .kanban__card-backlog-btn {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s;
}

.kanban__card--basket-item:hover .kanban__card-backlog-btn,
.kanban__card--basket-item:hover .kanban__card-complete-btn,
.kanban__card--basket-item:hover .kanban__card-delete-btn,
.kanban__card--basket-item.kanban__card--done .kanban__card-complete-btn {
    opacity: 1;
    pointer-events: auto;
}

.kanban__card-delete-btn:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.kanban__card:hover .kanban__card-complete-btn,
.kanban__card--done .kanban__card-complete-btn {
    opacity: 1;
    pointer-events: auto;
}

.kanban__card-complete-btn:hover {
    color: var(--fg);
    border-color: var(--fg);
}

.kanban__card--done {
    opacity: 0.6;
}
.kanban__card--done:hover { opacity: 0.9; }

.kanban__card--done .kanban__card-title {
    text-decoration: line-through;
    color: var(--muted);
}

/* Завершённые карточки НЕ подсвечиваются акцентным цветом при hover/active —
   состояние «готово» передаётся только типографикой (зачёркивание) и
   пониженной прозрачностью. Бордер и тень при наведении/выделении —
   нейтральные (var(--border) / var(--fg)), чтобы доска выглядела ровно. */
.kanban__card--done:hover {
    border-color: var(--border);
    box-shadow: none;
}
.kanban__card--done.kanban__card--active {
    border-color: var(--fg);
    box-shadow: inset 0 0 0 1px var(--fg);
    background: var(--bg);
}

.kanban__card-title {
    font-size: 0.88rem;
    line-height: 1.45;
    color: var(--fg);
    word-break: break-word;
}

/* ==========================================================================
   Task detail — right-side panel
   ========================================================================== */

.task-detail {
    position: fixed;
    inset: 56px 0 0 0;
    /* One tick above `.task-card` (z-index 250) — ReminderDetail is only
       ever a standalone page-level drawer (reminders.html, no .task-card
       around) or opened nested on top of a HabitCard (js/habits/card.js),
       and in the latter case it must always paint above the card that
       opened it regardless of DOM insertion order (unlike the .task-card
       stack, ReminderDetail is a lazily-injected singleton element whose
       DOM position doesn't reliably track "most recently opened"). */
    z-index: 260;
    pointer-events: none;
}

/* Opened from inside a HabitCard (js/habits/card.js) — shift left by one
   card-width, same visual "opens to the left, previous card's edge still
   visible" effect `.task-card:nth-of-type(2)` gives rules/templates for
   free. ReminderDetail isn't part of that stack (see the comment above),
   so this is a manual equivalent toggled by the caller. */
.task-detail--nested .task-detail__panel {
    right: min(420px, 25vw);
}

@media (max-width: 900px) {
    .task-detail--nested .task-detail__panel { right: 0; }
}

.task-detail__backdrop {
    position: absolute;
    inset: 0;
    background: transparent;
    pointer-events: none;
    cursor: default;
    opacity: 0;
    transition: opacity 0.22s ease;
}

.task-detail--open .task-detail__backdrop {
    pointer-events: auto;
    opacity: 1;
}

.task-detail__panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 360px;
    display: flex;
    flex-direction: column;
    background: var(--bg-elevated);
    border-left: 1px solid var(--border);
    box-shadow: -4px 0 20px color-mix(in srgb, var(--fg) 12%, transparent);
    overflow-y: auto;
    pointer-events: none;
    transform: translateX(100%);
    transition: transform 0.22s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.task-detail--open .task-detail__panel {
    transform: translateX(0);
    pointer-events: auto;
}

/* ── Header ─────────────────────────────────────────────────────────────── */

.task-detail__header {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 1rem 1rem 0.85rem;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.task-detail__header-actions {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-shrink: 0;
}

.task-detail__title-wrap {
    flex: 1;
    min-width: 0;
}

.task-detail__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--fg);
    line-height: 1.35;
    overflow-wrap: anywhere;
    cursor: pointer;
}

.task-detail__title:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border-radius: 4px;
}

.task-detail__title-input {
    width: 100%;
    background: transparent;
    border: none;
    border-bottom: 1.5px solid var(--accent);
    border-radius: 0;
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--fg);
    padding: 0.1rem 0;
    min-width: 0;
    box-sizing: border-box;
}

.task-detail__title-input:focus { outline: none; }

.task-detail__delete-icon,
.task-detail__close {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    transition: color 0.15s, border-color 0.15s;
}

.task-detail__delete-icon:hover {
    color: var(--danger);
    border-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 6%, transparent);
}

.task-detail__close:hover { color: var(--fg); border-color: var(--fg); }

/* Завершение задачи в шапке панели — видна только когда canComplete === true
   (т.е. задача открыта из спринта). По умолчанию скрыта (hidden в HTML).
   Кнопка нейтральна и в «Завершить», и в «Возобновить» — состояние «готово»
   передаётся иконкой (CHECK ↔ UNDO) и лейблом, без подкрашивания. */
.task-detail__complete-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem 0.7rem;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.82rem;
    line-height: 1;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.task-detail__complete-btn:hover {
    color: var(--fg);
    border-color: var(--fg);
}

.task-detail__completed-at {
    font-size: 0.78rem;
    color: var(--muted);
    margin-top: 0.25rem;
}

/* ── Body ─────────────────────────────────────────────────────────────────── */

.task-detail__body {
    flex: 1;
    padding: 0.65rem 0.85rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

/* ── Properties ──────────────────────────────────────────────────────────── */

.task-detail__props {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.task-detail__prop {
    display: grid;
    grid-template-columns: 5.5rem 1fr;
    gap: 0.4rem 0.6rem;
    align-items: center;
    font-size: 0.9rem;
}

.task-detail__prop-key {
    color: var(--muted);
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

/* Deadline display: looks like plain text, cursor signals it's clickable */
.task-detail__deadline-view {
    cursor: pointer;
    border-radius: 4px;
    padding: 0.1rem 0.3rem;
    margin-left: -0.3rem;
    transition: background 0.12s;
}

.task-detail__deadline-view:hover,
.task-detail__deadline-view:focus-visible {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    outline: none;
}

.task-detail__deadline-text {
    font-size: 0.9rem;
    color: var(--fg);
}

.task-detail__deadline-text--empty {
    color: var(--muted);
    font-style: italic;
}

/* Inline-плашка «дедлайн раньше назначенного дня в спринте».
   Появляется под дедлайном в детальной панели задачи; служит объяснением
   для красной окантовки .kanban__card--deadline-missed на кубике и
   плашки .deadlines-panel__item--urgent в боковой панели дедлайнов.
   Тот же токен --danger, что и сами подсветки — чтобы визуально
   принадлежать одной группе. */
.task-detail__deadline-missed-note {
    grid-column: 2;
    display: flex;
    align-items: flex-start;
    gap: 0.45rem;
    margin-top: 0.45rem;
    padding: 0.5rem 0.6rem;
    border: 1px solid color-mix(in srgb, var(--danger) 55%, transparent);
    border-radius: 6px;
    background: color-mix(in srgb, var(--danger) 8%, var(--bg));
    color: var(--danger);
    font-size: 0.78rem;
    line-height: 1.4;
}
.task-detail__deadline-missed-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-top: 1px;
    color: var(--danger);
}
.task-detail__deadline-missed-text {
    flex: 1 1 auto;
}

/* Edit mode: date + time inputs side by side */
.task-detail__deadline-edit {
    grid-column: 2;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
}

.task-detail__date-native,
.task-detail__time-input {
    background: var(--bg);
    border: 1px solid var(--accent);
    border-radius: 6px;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.88rem;
    padding: 0.4rem 0.55rem;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 15%, transparent);
}

.task-detail__date-native {
    flex: 1 1 8rem;
    min-width: 0;
}
.task-detail__date-native:focus { outline: none; }
/* Hide native picker icon — we have a dedicated calendar button */
.task-detail__date-native::-webkit-calendar-picker-indicator { display: none; }

.task-detail__date-cal-btn {
    flex-shrink: 0;
    background: var(--bg);
    border: 1px solid var(--accent);
    color: var(--muted);
    padding: 0.38rem;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 15%, transparent);
    transition: color 0.1s;
}
.task-detail__date-cal-btn:hover { color: var(--accent); }

.task-detail__time-input { flex: 0 0 6.5rem; }
.task-detail__time-input:focus { outline: none; }

.task-detail__clear-deadline {
    background: transparent;
    border: none;
    color: var(--muted);
    font: inherit;
    font-size: 0.8rem;
    padding: 0.2rem 0;
    cursor: pointer;
    transition: color 0.12s;
    white-space: nowrap;
}

.task-detail__clear-deadline:hover { color: var(--danger); }

/* ── Description ─────────────────────────────────────────────────────────── */

.task-detail__section {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.task-detail__section-label {
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
}

/* Description in display mode: plain text, click to edit */
.task-detail__desc-view {
    font-size: 0.92rem;
    line-height: 1.6;
    color: var(--fg);
    cursor: pointer;
    border-radius: 6px;
    padding: 0.5rem 0.6rem;
    min-height: 3rem;
    white-space: pre-wrap;
    word-break: break-word;
    transition: background 0.12s;
}

.task-detail__desc-view:hover,
.task-detail__desc-view:focus-visible {
    background: var(--bg);
    outline: none;
}

.task-detail__desc-view--empty {
    color: var(--muted);
    font-style: italic;
}

/* Description textarea (edit mode) */
.task-detail__desc-input {
    width: 100%;
    background: var(--bg);
    border: 1.5px solid var(--accent);
    border-radius: 6px;
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.92rem;
    line-height: 1.6;
    padding: 0.5rem 0.65rem;
    resize: none;
    min-height: 3rem;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
    box-sizing: border-box;
    overflow: hidden;
}

.task-detail__desc-input:focus { outline: none; }
.task-detail__desc-input::placeholder { color: var(--muted); font-style: italic; }

/* ── Divider between props and description ───────────────────────────────── */

.task-detail__divider {
    border: none;
    border-top: 1px solid var(--border);
    margin: 0;
}

/* ── "Add property" button + dropdown ───────────────────────────────────── */

.task-detail__add-prop-wrap {
    display: flex;
    padding-top: 0.1rem;
}

.task-detail__add-prop-btn {
    background: transparent;
    border: 1px dashed var(--border);
    color: var(--muted);
    font: inherit;
    font-size: 0.8rem;
    border-radius: 6px;
    padding: 0.3rem 0.65rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    transition: color 0.12s, border-color 0.12s;
    line-height: 1;
}
.task-detail__add-prop-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.task-detail__add-prop-menu {
    position: fixed;
    z-index: 400;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 4px 20px color-mix(in srgb, var(--fg) 18%, transparent);
    padding: 0.3rem;
    min-width: 160px;
}
.task-detail__add-prop-menu[hidden] { display: none !important; }

.task-detail__add-prop-option {
    display: flex;
    align-items: center;
    width: 100%;
    background: transparent;
    border: none;
    color: var(--fg);
    font: inherit;
    font-size: 0.875rem;
    padding: 0.45rem 0.65rem;
    border-radius: 5px;
    cursor: pointer;
    text-align: left;
    transition: background 0.1s;
}
.task-detail__add-prop-option:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* ── Remove optional prop button ─────────────────────────────────────────── */

.task-detail__prop-key-wrap {
    display: flex;
    align-items: center;
    gap: 0.2rem;
}

.task-detail__prop-remove {
    background: none;
    border: none;
    padding: 0 0.1rem;
    color: var(--muted);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.12s, color 0.12s;
    flex-shrink: 0;
}

.task-detail__prop:hover .task-detail__prop-remove { opacity: 0.55; }
.task-detail__prop-remove:hover { opacity: 1 !important; color: var(--danger); }

/* Ссылка на правило регулярности в TaskDetail (read-only свойство) */
.task-detail__prop-rule-link {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    font: inherit;
    font-size: 0.82rem;
    font-weight: 500;
    padding: 0.25rem 0.55rem;
    border-radius: 4px;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.12s, color 0.12s, background 0.12s;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.task-detail__prop-rule-link:hover {
    border-color: var(--muted);
    color: var(--fg);
    background: var(--code-bg);
}

/* ── Task page tabs nav ───────────────────────────────────────────────────── */

.tasks-tabs {
    display: flex;
    gap: 0.2rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.2rem;
    margin-left: auto;
}

.tasks-tabs__tab {
    padding: 0.3rem 0.75rem;
    border-radius: 6px;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--muted);
    text-decoration: none;
    transition: background 0.15s, color 0.15s;
    white-space: nowrap;
}

.tasks-tabs__tab:hover {
    background: var(--bg-elevated);
    color: var(--fg);
}

.tasks-tabs__tab--active {
    background: var(--accent-gradient);
    color: var(--accent-fg);
}

.tasks-tabs__tab--active:hover {
    color: var(--accent-fg);
    background: var(--accent-gradient);
}

/* ── Archive button on backlog cards ─────────────────────────────────────── */

.kanban__card-archive-btn {
    position: absolute;
    top: 0.35rem;
    right: 0.4rem;
    opacity: 0;
    pointer-events: none;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    padding: 0.25rem;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
    font-size: 0;
    transition: color 0.15s, border-color 0.15s, opacity 0.15s;
}

.kanban__card:hover .kanban__card-archive-btn {
    opacity: 1;
    pointer-events: auto;
}

.kanban__card-archive-btn:hover {
    color: var(--fg);
    border-color: var(--fg);
}

/* ── Calendar picker ─────────────────────────────────────────────────────── */

.cal-picker {
    position: fixed;
    z-index: 500;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 8px 32px color-mix(in srgb, var(--fg) 22%, transparent);
    padding: 0.6rem;
    width: 236px;
    font-family: var(--font-base);
    font-size: 0.88rem;
    user-select: none;
}
.cal-picker[hidden] { display: none !important; }

.cal-picker__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.45rem;
}

.cal-picker__nav {
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 1.4rem;
    line-height: 1;
    padding: 0.1rem 0.3rem;
    cursor: pointer;
    border-radius: 4px;
    transition: color 0.1s, background 0.1s;
}
.cal-picker__nav:hover { color: var(--fg); background: var(--bg); }

.cal-picker__label {
    font-weight: 700;
    color: var(--fg);
    font-size: 0.88rem;
    letter-spacing: 0.01em;
}

.cal-picker__weekdays {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    margin-bottom: 0.2rem;
    text-align: center;
}
.cal-picker__weekdays span {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--muted);
    padding: 0.15rem 0;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.cal-picker__grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 1px;
}

.cal-picker__day {
    background: transparent;
    border: none;
    color: var(--fg);
    font: inherit;
    font-size: 0.83rem;
    padding: 0;
    aspect-ratio: 1;
    border-radius: 6px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.1s, color 0.1s;
    line-height: 1;
}
.cal-picker__day:hover:not(.cal-picker__day--sel) {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.cal-picker__day--pad {
    visibility: hidden;
    pointer-events: none;
}
.cal-picker__day--today {
    font-weight: 700;
    color: var(--accent);
}
.cal-picker__day--sel {
    background: var(--accent-gradient) !important;
    color: white;
    font-weight: 700;
}

/* ── Kanban card description icon (legacy .kanban__card-meta removed in v2) ─ */

.kanban__card-desc-icon {
    color: var(--muted);
    display: inline-flex;
    align-items: center;
    line-height: 0;
    opacity: 0.7;
}

/* Иконка «регулярная задача» — задача порождена правилом регулярности */
.kanban__card-regular-icon {
    color: var(--accent);
    display: inline-flex;
    align-items: center;
    line-height: 0;
    opacity: 0.85;
    cursor: help;
}

.kanban__card-dl {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--muted);
    background: color-mix(in srgb, var(--muted) 12%, transparent);
    border-radius: 4px;
    padding: 0.1rem 0.35rem;
    white-space: nowrap;
    line-height: 1.4;
}
.kanban__card-dl--overdue {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 12%, transparent);
}
.kanban__card-dl--today {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 15%, transparent);
}
.kanban__card-dl--soon {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

/* ── Priority на кубике (через окантовку/заливку, без иконки в мета-строке) ──
   high — тонкая accent-полоса слева (3px), читаемо «выше обычного».
   max  — accent-окантовка со всех сторон + мягкая заливка фона + та же
          полоса слева. Доминирует над high. На --done (завершённых) полоса
          сохраняется — приоритет был задан до выполнения, это всё ещё
          важная информация. */
.kanban__card--prio-high {
    border-left: 3px solid var(--accent);
    padding-left: 0.45rem;
}
.kanban__card--prio-max {
    border: 1.5px solid var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, var(--bg));
}
.kanban__card--prio-max.kanban__card--prio-high {
    /* max сам по себе уже даёт «всё ярче, чем high» — двойной модификатор
       (и то, и то) невозможен, на случай если когда-то появится — побеждает max. */
    border-left: 1.5px solid var(--accent);
}

/* ── Deadline missed (спринт: дедлайн раньше назначенного дня) ─────────────
   Красная окантовка + мягкая заливка тем же тоном. Сервер пересчитывает
   task.deadline_missed на каждом ответе; updateCardMeta синхронизирует
   класс без перерисовки кубика. При completed=True сервер возвращает
   false — модификатор снимается, специального правила для --done не
   нужно (стандартный opacity 0.6 от --done не применяется, потому что
   класс просто отсутствует).
   Source-order: правило стоит после --prio-max — при коллизии красный
   побеждает акцент (более важный сигнал). */
.kanban__card--deadline-missed {
    border: 1.5px solid var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, var(--bg));
}

/* ── Приоритет в бэклоге: верхняя полоса на кубике (интенсивность убывает) ──
   bp-1 = высший → сплошная accent, bp-2 → 60%, bp-3 → 35%.
   Сочетается с prio-high (левая полоса) и prio-max (окантовка) — разные стороны. */

.kanban__card--bp-2 { border-top: 3px solid var(--accent); }
.kanban__card--bp-3 { border: 1.5px solid var(--accent); }

/* ── Приоритет в бэклоге: бейдж #N в мета-строке кубика ─────────────────── */

.kanban__card-bp {
    display: inline-flex;
    align-items: center;
    font-size: 0.68rem;
    font-weight: 700;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    border-radius: 4px;
    padding: 0.1rem 0.35rem;
    white-space: nowrap;
    line-height: 1.4;
}

/* ── Приоритет в бэклоге: пикер [1][2][3] на кубике (по ховеру) ─────────── */

.kanban__card-bp-picker {
    display: flex;
    align-items: center;
    gap: 0.2rem;
    margin-top: 0.35rem;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s;
}

.kanban__card:hover .kanban__card-bp-picker {
    opacity: 1;
    pointer-events: auto;
}

.kanban__card-bp-label {
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--muted);
    margin-right: 0.1rem;
    user-select: none;
}

.kanban__card-bp-btn {
    min-width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--muted);
    font: inherit;
    font-size: 0.72rem;
    font-weight: 700;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.kanban__card-bp-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.kanban__card-bp-btn:focus {
    outline: none;
}

.kanban__card-bp-btn--active {
    background: var(--accent-gradient);
    border-color: var(--accent);
    color: var(--accent-fg);
}

.kanban__card-bp-btn--active:hover {
    color: var(--accent-fg);
}

/* ── Иконки связей между задачами на кубике ─────────────────────────────
   Цветовая схема:
     relates/depends_on = --muted (серый нейтральный),
     blocks              = --danger (красный — блокер бросается в глаза),
     soon                = --warn (оранжевый — эскалация blocked+soon).
   Иконка показывается в meta-строке, рядом с project/area чипами.
   Тултип собирает сводку «Блокирует: A, B · Зависит от: C». */

.kanban__card-link-relates,
.kanban__card-link-blocks,
.kanban__card-link-depends,
.kanban__card-link-soon {
    display: inline-flex;
    align-items: center;
    line-height: 1;
    cursor: help;
}

.kanban__card-link-relates {
    color: var(--muted);
}

.kanban__card-link-blocks {
    color: var(--danger);
}

.kanban__card-link-lock {
    color: var(--warn);
}

.kanban__card-link-soon {
    color: var(--warn);
}

/* ── Тусклость кубика при незавершённом блокере (depends_on → not completed).
   Hover возвращает полную видимость — пользователь может прочитать и кликнуть. */

.kanban__card--blocked {
    opacity: 0.55;
    filter: grayscale(0.45);
    transition: opacity 0.18s, filter 0.18s;
}

.kanban__card--blocked:hover {
    opacity: 1;
    filter: none;
}

/* ── Секция «Связи» в TaskDetail ──────────────────────────────────────────
   Группировка: три заголовка (relates / blocks / depends_on) с иконкой
   того же цвета, что и на кубике. Клик по target-title открывает эту
   задачу в той же панели; × удаляет связь. */

.task-detail__links-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.task-detail__link-group-header {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.55rem;
    margin-bottom: 0.15rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.task-detail__link-group-header:first-child {
    margin-top: 0;
}

.task-detail__link-group-header--relates .task-detail__link-icon,
.task-detail__link-group-header--depends_on .task-detail__link-icon {
    color: var(--muted);
}

.task-detail__link-group-header--blocks .task-detail__link-icon {
    color: var(--danger);
}

.task-detail__link-row {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.25rem 0.4rem;
    border-radius: 4px;
    border: 1px solid transparent;
}

.task-detail__link-row:hover {
    border-color: var(--border);
    background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.task-detail__link-row--missing {
    opacity: 0.6;
    font-style: italic;
}

.task-detail__link-icon {
    display: inline-flex;
    align-items: center;
    line-height: 1;
}

.task-detail__link-icon--relates,
.task-detail__link-icon--depends_on {
    color: var(--muted);
}

.task-detail__link-icon--blocks {
    color: var(--danger);
}

.task-detail__link-title {
    flex: 1;
    background: transparent;
    border: none;
    padding: 0;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.task-detail__link-title:hover {
    color: var(--accent);
    text-decoration: underline;
}

.task-detail__link-title:focus {
    outline: none;
    color: var(--accent);
}

.task-detail__link-remove {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0.1rem 0.35rem;
    border-radius: 4px;
    opacity: 0.35;
    transition: opacity 0.12s, color 0.12s, background 0.12s;
}

.task-detail__link-row:hover .task-detail__link-remove,
.task-detail__link-row:focus-within .task-detail__link-remove {
    opacity: 1;
}

.task-detail__link-remove:hover {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 12%, transparent);
}

.task-detail__links-empty {
    color: var(--muted);
    font-size: 0.85rem;
    font-style: italic;
    margin-bottom: 0.4rem;
}

.task-detail__add-link-btn {
    background: transparent;
    border: 1px dashed var(--border);
    color: var(--muted);
    padding: 0.35rem 0.7rem;
    border-radius: 4px;
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
    margin-top: 0.45rem;
    transition: color 0.12s, border-color 0.12s;
}

.task-detail__add-link-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.task-detail__add-link-form {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-top: 0.45rem;
    padding: 0.55rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: color-mix(in srgb, var(--accent) 4%, transparent);
}

.task-detail__add-link-type {
    align-self: flex-start;
    min-width: 8rem;
}

.task-detail__add-link-target-wrap {
    position: relative;
}

/* Список suggestions под инпутом — absolute-позиционирование относительно
   wrap-контейнера. По умолчанию скрыт; показывается при фокусе/вводе. */
.task-detail__add-link-suggest {
    position: absolute;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    z-index: 5;
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 4px;
    max-height: 14rem;
    overflow-y: auto;
    box-shadow: 0 4px 12px color-mix(in srgb, var(--fg) 22%, transparent);
}

.task-detail__add-link-suggest-item {
    padding: 0.4rem 0.6rem;
    cursor: pointer;
    font-size: 0.88rem;
    color: var(--fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.task-detail__add-link-suggest-item--active,
.task-detail__add-link-suggest-item:hover {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
}

.task-detail__add-link-suggest-item--completed {
    color: var(--muted);
    text-decoration: line-through;
}

.task-detail__add-link-target {
    width: 100%;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--fg);
    border-radius: 4px;
    padding: 0.35rem 0.5rem;
    font: inherit;
    font-size: 0.9rem;
}

.task-detail__add-link-target:focus {
    outline: none;
    border-color: var(--accent);
}

.task-detail__add-link-actions {
    display: flex;
    gap: 0.4rem;
    justify-content: flex-end;
}

.task-detail__add-link-actions button {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--fg);
    padding: 0.3rem 0.75rem;
    border-radius: 4px;
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
}

.task-detail__add-link-actions button:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.task-detail__add-link-actions button:disabled {
    opacity: 0.45;
    cursor: not-allowed;
    color: var(--muted);
    border-color: var(--border);
}

.task-detail__add-link-actions button#td-add-link-submit:not(:disabled) {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border-color: var(--accent);
}

/* ── Плашка «Заблокировано» под секцией «Связи» ─────────────────────────── */

.task-detail__blocked-note {
    margin-top: 0.55rem;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--warn);
    border-radius: 4px;
    background: color-mix(in srgb, var(--warn) 10%, transparent);
    color: var(--fg);
    font-size: 0.85rem;
}

.task-detail__blocker-link {
    background: transparent;
    border: none;
    padding: 0;
    margin: 0;
    color: inherit;
    font: inherit;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.task-detail__blocker-link:hover {
    color: var(--accent);
}

/* ── Быстрый пикер приоритета в корзине спринта ─────────────────────────── */

.kanban__card-prio-picker {
    display: flex;
    align-items: center;
    gap: 0.2rem;
    margin-top: 0.35rem;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s;
}

.kanban__card:hover .kanban__card-prio-picker {
    opacity: 1;
    pointer-events: auto;
}

.kanban__card-prio-btn {
    min-width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--muted);
    font: inherit;
    font-size: 0.72rem;
    font-weight: 700;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
}

.kanban__card-prio-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.kanban__card-prio-btn:focus { outline: none; }

.kanban__card-prio-btn--active {
    color: var(--accent);
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, var(--bg));
}

.kanban__card-prio-btn--active:hover {
    color: var(--accent);
}

/* Estimate badge on card */
.kanban__card-est {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--muted);
    background: color-mix(in srgb, var(--muted) 10%, transparent);
    border-radius: 4px;
    padding: 0.1rem 0.35rem;
    white-space: nowrap;
    line-height: 1.4;
}

/* Points total in collapsible column header */
.kanban__col-head-right {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-shrink: 0;
}

.kanban__col-points {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--muted);
    background: color-mix(in srgb, var(--muted) 10%, transparent);
    border-radius: 4px;
    padding: 0.1rem 0.35rem;
    white-space: nowrap;
    line-height: 1.4;
}

/* Estimate segmented control in task detail */
.td-estimate-btns {
    display: flex;
    gap: 0.25rem;
}

.td-est-btn {
    min-width: 2rem;
    padding: 0.2rem 0.45rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 5px;
    color: var(--muted);
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
    text-align: center;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.td-est-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.td-est-btn--active {
    background: var(--accent-gradient);
    border-color: var(--accent);
    color: var(--accent-fg);
}

.td-est-btn--active:hover {
    color: var(--accent-fg);
}

/* В редакторе приоритетов: иконки в кнопках одинакового размера (14×14),
   high чуть приглушён, max — в бейдж-стиле (заливка accent) даже в
   неактивном состоянии, чтобы сразу бросался в глаза как «очень важно». */
.td-prio-btn--high {
    opacity: 0.75;
}
.td-prio-btn--max {
    background: color-mix(in srgb, var(--accent) 18%, var(--bg));
    border-color: var(--accent);
    color: var(--accent);
}
.td-prio-btn--max.td-est-btn--active {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border-color: var(--accent);
}
.td-prio-btn--max svg {
    /* Немного крупнее в неактивном состоянии — чтобы бейдж читался
       сильнее. В активном — кнопка уже яркая, размер не критичен. */
    width: 14px;
    height: 14px;
}

@media (max-width: 700px) {
    .task-detail__panel { width: 100%; }
    .kanban__col { flex: 0 0 200px; }
}

/* ── Collapsible column ───────────────────────────────────────────────────── */

.kanban__col--collapsible {
    padding: 0; /* padding lives inside head and body */
}

.kanban__col-head--toggle {
    padding: 0.6rem 0.75rem;
    border-bottom: none; /* only visible when expanded */
    cursor: pointer;
    user-select: none;
    gap: 0.5rem;
}

/* Show separator only when body is visible */
.kanban__col--collapsible:not(.kanban__col--collapsed) .kanban__col-head--toggle {
    border-bottom: 1px solid var(--border);
    padding-bottom: 0.5rem;
}

.kanban__col-head-label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    min-width: 0;
    flex: 1;
}

/* Chevron rotates from → to ↓ when column opens */
.kanban__col-toggle-arrow {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    color: var(--muted);
    transition: transform 0.18s ease;
}

.kanban__col--collapsible:not(.kanban__col--collapsed) .kanban__col-toggle-arrow {
    transform: rotate(90deg);
}

/* Body wrapper: provides padding + flex layout for add-area + cards */
.kanban__col-body {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem 0.75rem;
}

/* ── Planning banner (rendered by js/tasks/sprint-banner.js — class shell kept) ── */

.sprint__planning-banner {
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-elevated));
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    border-radius: 8px;
    padding: 0.45rem 1rem;
    margin-bottom: 0.75rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--accent);
    display: flex;
    align-items: center;
    gap: 0.55rem;
}

.sprint__planning-banner::before {
    content: "";
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent);
    animation: sprint-pulse 1.6s ease-in-out infinite;
    flex-shrink: 0;
}

@keyframes sprint-pulse {
    0%, 100% { opacity: 1; }
    50%       { opacity: 0.35; }
}

/* Capacity counter inside the planning banner */
.sprint__banner-capacity {
    margin-left: auto;
}

.sprint__banner-cap {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
    border-radius: 5px;
    padding: 0.1rem 0.45rem;
}

.sprint__banner-cap--over {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    border-color: color-mix(in srgb, var(--danger) 30%, transparent);
}

.sprint__banner-cap-sep {
    font-weight: 400;
    opacity: 0.6;
    font-size: 0.72rem;
}

/* Banner link (for cross-page planning notice) */
.sprint__planning-banner a {
    color: inherit;
    font-weight: 700;
    text-decoration: none;
}

.sprint__planning-banner a:hover {
    text-decoration: underline;
}

/* ── Sprint basket column (full-width row in the kanban grid) ────────────── */

.kanban__col--basket {
    grid-column: 1 / -1;
    background: color-mix(in srgb, var(--accent) 4%, var(--bg-elevated));
    border: 1px dashed color-mix(in srgb, var(--accent) 40%, transparent);
    border-radius: 8px;
    padding: 0;
    margin-bottom: 0.5rem;
}

.kanban__col--basket .kanban__col-head {
    padding: 0.55rem 0.75rem;
    border-bottom: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
}

.kanban__col--basket .kanban__add-area {
    padding: 0.5rem 0.75rem 0;
}

.kanban__col--basket .kanban__add-btn,
.kanban__col--basket .kanban__add-input {
    width: auto;
    min-width: 170px;
    max-width: 240px;
}

.kanban__col--basket .kanban__cards {
    flex-direction: row;
    flex-wrap: wrap;
    padding: 0.5rem 0.75rem 0.75rem;
    min-height: 3rem;
}

/* Basket card: horizontal layout (content left, backlog button right) */
.kanban__card--basket-item {
    display: flex;
    align-items: flex-start;
    gap: 0.45rem;
    min-width: 170px;
    max-width: 240px;
    flex: 0 0 auto;
}

.kanban__card--basket-item .kanban__card-inner {
    flex: 1;
    min-width: 0;
}

/* Во время планирования карточки колонок спринта принимают тот же flex-макет,
   что и корзинные: контент слева, кнопки справа, всегда видны. */
.kanban--planning .kanban__card:not(.kanban__card--basket-item) {
    display: flex;
    align-items: flex-start;
    gap: 0.45rem;
}

.kanban--planning .kanban__card:not(.kanban__card--basket-item) .kanban__card-inner {
    flex: 1;
    min-width: 0;
}

.kanban--planning .kanban__card:not(.kanban__card--basket-item) .kanban__card-complete-btn,
.kanban--planning .kanban__card:not(.kanban__card--basket-item) .kanban__card-col-backlog-btn,
.kanban--planning .kanban__card:not(.kanban__card--basket-item) .kanban__card-postpone-btn,
.kanban--planning .kanban__card:not(.kanban__card--basket-item) .kanban__card-delete-btn {
    position: static;
    opacity: 0;
    pointer-events: none;
    padding: 0.28rem;
    flex-shrink: 0;
    margin-top: 1px;
    z-index: auto;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
    font-size: 0;
    transition: color 0.15s, border-color 0.15s, opacity 0.15s;
}

.kanban--planning .kanban__card:not(.kanban__card--basket-item):hover .kanban__card-complete-btn,
.kanban--planning .kanban__card:not(.kanban__card--basket-item):hover .kanban__card-col-backlog-btn,
.kanban--planning .kanban__card:not(.kanban__card--basket-item):hover .kanban__card-postpone-btn,
.kanban--planning .kanban__card:not(.kanban__card--basket-item):hover .kanban__card-delete-btn,
.kanban--planning .kanban__card--done:not(.kanban__card--basket-item) .kanban__card-complete-btn {
    opacity: 1;
    pointer-events: auto;
}

.kanban--planning .kanban__card:not(.kanban__card--basket-item) .kanban__card-complete-btn:hover,
.kanban--planning .kanban__card:not(.kanban__card--basket-item) .kanban__card-col-backlog-btn:hover,
.kanban--planning .kanban__card:not(.kanban__card--basket-item) .kanban__card-postpone-btn:hover {
    color: var(--fg);
    border-color: var(--fg);
}

/* «Удалить задачу» — деструктивное действие, при наведении подсвечиваем
   опасным цветом (как и другие кнопки-«мусорки» в проекте). */
.kanban--planning .kanban__card:not(.kanban__card--basket-item) .kanban__card-delete-btn:hover {
    color: var(--danger);
    border-color: var(--danger);
}

/* Hover-reveal action buttons on cards (sprint-add on backlog, backlog-send on sprint cols) */
.kanban__card-sprint-btn,
.kanban__card-col-backlog-btn {
    position: absolute;
    bottom: 0.35rem;
    right: 0.4rem;
    opacity: 0;
    pointer-events: none;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    padding: 0.25rem;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
    font-size: 0;
    transition: color 0.15s, border-color 0.15s, opacity 0.15s;
}

.kanban__card:hover .kanban__card-sprint-btn,
.kanban__card:hover .kanban__card-col-backlog-btn {
    opacity: 1;
    pointer-events: auto;
}

.kanban__card-sprint-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.kanban__card-col-backlog-btn:hover {
    color: var(--fg);
    border-color: var(--fg);
}

.kanban__card-backlog-btn {
    background: transparent;
    border: 1px solid var(--border);
    padding: 0.28rem;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
    cursor: pointer;
    flex-shrink: 0;
    font-size: 0;
    transition: color 0.15s, border-color 0.15s;
    margin-top: 1px;
}

.kanban__card-backlog-btn:hover {
    color: var(--fg);
    border-color: var(--fg);
}

/* ── Кнопка «Перенести в следующие дни» (только в спринте) ────────────────── */
/* Та же иконка skip-forward (ICONS.ARROW_RIGHT), что и в TaskDetail; внешний
   вид зеркалит .kanban__card-complete-btn (hover-reveal, square button с
   тонкой обводкой). По умолчанию сидит в правом нижнем углу кубика; в режиме
   планирования перестраивается в flex-сиблинг (см. .kanban--planning ниже). */
.kanban__card-postpone-btn {
    position: absolute;
    bottom: 0.35rem;
    right: 0.4rem;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.22rem;
    border-radius: 4px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    opacity: 0;
    pointer-events: none;
    transition: color 0.15s, border-color 0.15s, opacity 0.15s;
    z-index: 1;
}

.kanban__card:hover .kanban__card-postpone-btn {
    opacity: 1;
    pointer-events: auto;
}

.kanban__card-postpone-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.kanban__card-postpone-btn:focus {
    outline: none;
}

/* В режиме планирования спринта карточка превращается в flex-контейнер;
   кнопка становится flex-сиблингом, чтобы не накладываться на текст.
   Сами правила (border/background/opacity) переопределяются в общей группе
   селекторов .kanban--planning ... .kanban__card-complete-btn / col-backlog-btn
   / postpone-btn выше — там одним блоком задаётся стиль flex-сиблинга. */

/* ==========================================================================
   Project actions sub-section — задачи/правила/вложенные проекты в модалке
   проекта. Три main-tab'а (Проекты/Правила/Задачи), внутри каждого — sub-табы
   по статусам, DnD между ними. Identical markup on both projects.html and
   actions.html (`.project-actions__section[data-action-section]`).
   ========================================================================== */

.project-actions__section {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}

.project-actions__sub-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
}

.project-actions__sub-title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--muted);
    letter-spacing: 0.01em;
}

.project-actions__add-btn {
    background: transparent;
    border: 1px dashed var(--border);
    color: var(--muted);
    font: inherit;
    font-size: 0.82rem;
    padding: 0.22rem 0.65rem;
    border-radius: 6px;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
    flex-shrink: 0;
}

/* Wraps the "Проекты" sub-section's two triggers (attach existing / create
   new) so `.project-actions__sub-header`'s space-between still sees just
   two top-level children (title + this group) instead of spreading three
   buttons apart. */
.project-actions__sub-header-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-shrink: 0;
}

.project-actions__add-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/* «+ Проект» inline-add (attachInlineAdd) в под-заголовке секции «Проекты» —
   тот же визуальный язык что и .project-actions__add-btn (attachInlineAdd
   переиспользует триггер-элемент, но генерирует свои классы, поэтому кнопка
   продублирована здесь), плюс поле ввода вместо браузерного prompt(). */
.project-actions-inline-add__btn {
    background: transparent;
    border: 1px dashed var(--border);
    color: var(--muted);
    font: inherit;
    font-size: 0.82rem;
    padding: 0.22rem 0.65rem;
    border-radius: 6px;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
    flex-shrink: 0;
}

.project-actions-inline-add__btn:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.project-actions-inline-add__input {
    width: 200px;
    max-width: 100%;
    box-sizing: border-box;
    font: inherit;
    font-size: 0.82rem;
    padding: 0.22rem 0.65rem;
    border: 1px solid var(--accent);
    border-radius: 6px;
    background: var(--bg-elevated);
    color: var(--fg);
    flex-shrink: 0;
}

/* Главные вкладки секции «Действия» внутри модалки проекта (три штуки:
   Проекты / Правила / Задачи). Стилистически идентичны .actions__main-tab*
   на actions.html — pill chip, accent border при active. Тот же компонентный
   язык в двух местах: actions.html (вне модалки) и projects.html
   (внутри модалки). */
.project-actions__main-tabs {
    display: flex;
    gap: 0.4rem;
    margin: 0.25rem 0 0.9rem;
}

.project-actions__main-tab {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.45rem 1rem;
    font-family: var(--font-base);
    font-size: 0.88rem;
    color: var(--muted);
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.project-actions__main-tab:hover {
    color: var(--fg);
    border-color: var(--fg);
}

.project-actions__main-tab--active {
    color: var(--fg);
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    font-weight: 600;
}

.project-actions__sub-tabs {
    display: flex;
    gap: 0.25rem;
    border-bottom: 1px solid var(--border);
}

.project-actions__sub-tab {
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    color: var(--muted);
    font-family: var(--font-base);
    font-size: 0.82rem;
    font-weight: 600;
    padding: 0.45rem 0.75rem;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: -1px;
}

.project-actions__sub-tab:hover { color: var(--fg); }

.project-actions__sub-tab--active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

.project-actions__sub-tab-count {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.04rem 0.45rem;
    font-size: 0.7rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    min-width: 1.3rem;
    text-align: center;
}

.project-actions__sub-tab--active .project-actions__sub-tab-count {
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
    color: var(--accent);
}

.project-actions__sub-tab--drag-over {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border-bottom-color: var(--accent);
}

.project-actions__cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(215px, 1fr));
    gap: 0.55rem;
    min-height: 1px; /* keep DnD drop zone valid even when empty */
}

.project-actions__cards .kanban__card {
    cursor: pointer;
}

.project-actions__cards .kanban__card--dragging {
    opacity: 0.5;
}

/* Highlight for any drop target inside an action sub-section (used by the
   nested-projects DnD — container-level drop, not tab-level). */
.project-actions__cards--drag-over {
    outline: 2px dashed var(--accent);
    outline-offset: -4px;
    border-radius: 6px;
}

/* В под-секции «Проекты» кубики рендерятся через тот же buildTile() что и на
   дашборде, поэтому получают класс .project-tile — грид из style.css уже
   подходит, но явно проставим курсор и состояние dragging. */
.project-actions__cards .project-tile {
    cursor: pointer;
}
.project-actions__cards .project-tile--dragging {
    opacity: 0.5;
}

/* Бейдж «Внутри: <родитель>» в шапке модалки проекта. Появляется только когда
   у открытого проекта есть parent_project_id; клик открывает модалку
   родителя. */
.project-modal__parent-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.55rem;
    margin-left: 0.5rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--muted);
    font-size: 0.8rem;
    line-height: 1.2;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
    vertical-align: middle;
}
.project-modal__parent-badge:hover {
    background: var(--surface-3);
    color: var(--fg);
    border-color: var(--accent);
}
.project-modal__parent-badge svg {
    flex: 0 0 auto;
}
.project-modal__parent-badge-label {
    white-space: nowrap;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* «Открыть родителя в новой вкладке» — sibling of .project-modal__parent-badge
 * (can't nest an <a> inside another <a>), same pill height via vertical-align. */
.project-modal__parent-open-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    margin-left: 0.3rem;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--muted);
    text-decoration: none;
    cursor: pointer;
    vertical-align: middle;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.project-modal__parent-open-link:hover {
    background: var(--surface-3);
    color: var(--fg);
    border-color: var(--accent);
}

.project-actions__empty {
    margin: 0;
    padding: 0.5rem 0.75rem;
    color: var(--muted);
    font-size: 0.85rem;
    font-style: italic;
    background: var(--code-bg);
    border: 1px dashed var(--border);
    border-radius: 6px;
    text-align: center;
}

/* project select in TaskDetail */
/* project / area selects in TaskDetail and RuleDetail use .ui-select (top of file) */

/* CalendarPicker-based deadline edit row */
.project-section__deadline-edit {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.project-section__date-cal {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    padding: 0.4rem 0.5rem;
    transition: color 0.15s, border-color 0.15s;
    flex-shrink: 0;
}

.project-section__date-cal:hover {
    color: var(--accent);
    border-color: var(--accent);
}

.project-section__clear-deadline {
    background: transparent;
    border: none;
    color: var(--muted);
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
    padding: 0.3rem 0.5rem;
    transition: color 0.15s;
    flex-shrink: 0;
}

.project-section__clear-deadline:hover { color: var(--danger); }

/* Project chip on kanban cards — задача/правило привязаны к проекту */
.kanban__card-project {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 18%, var(--bg));
    border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
    border-radius: 4px;
    padding: 0.1rem 0.45rem;
    line-height: 1.4;
}

/* Sphere (life area) chip on kanban cards — задача привязана к сфере жизни.
   Нейтральные цвета (muted + фон из border), чтобы визуально отделяться от
   акцентной плашки проекта и не конкурировать с ней за внимание. Иконка
   сферы ставится инлайном рядом с названием. */
.kanban__card-area {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--muted);
    background: color-mix(in srgb, var(--border) 50%, transparent);
    border: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
    border-radius: 4px;
    padding: 0.1rem 0.45rem;
    line-height: 1.4;
}

.kanban__card-area svg {
    flex-shrink: 0;
    opacity: 0.85;
}

/* ── Basket page ─────────────────────────────────────────────────────────── */

main.basket-page {
    max-width: 1100px;
}

.basket-layout {
    display: flex;
    gap: 1.5rem;
    align-items: flex-start;
}

/* ── Records panel (left) ────────────────────────────────────────────────── */

.basket-panel {
    flex: 0 0 340px;
    min-width: 0;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.9rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.basket-panel__header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.basket-panel__title {
    font-family: var(--font-heading);
    font-size: 1rem;
    font-weight: 600;
}

.basket-panel__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.basket-panel__empty {
    font-size: 0.85rem;
    color: var(--muted);
    padding: 0.5rem 0;
}

/* Add area — mirrors .kanban__add-area + .kanban__add-btn/.kanban__add-input */
.basket-panel__add-area {
    min-height: 2.2rem;
}

.basket-panel__add-btn {
    width: 100%;
    background: transparent;
    border: 1px dashed var(--border);
    border-radius: 6px;
    color: var(--muted);
    font: inherit;
    font-size: 0.82rem;
    padding: 0.45rem 0.6rem;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.basket-panel__add-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 6%, transparent);
    opacity: 1;
}

.basket-panel__add-input {
    width: 100%;
    background: var(--bg);
    border: 1.5px solid var(--accent);
    border-radius: 6px;
    color: var(--fg);
    font: inherit;
    font-size: 0.88rem;
    padding: 0.45rem 0.6rem;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
    box-sizing: border-box;
    outline: none;
}

.basket-panel__add-input::placeholder { color: var(--muted); }

/* ── Individual record card ──────────────────────────────────────────────── */

.basket-record {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    padding: 0.55rem 0.7rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 7px;
    cursor: grab;
    user-select: none;
    transition: border-color 0.12s;
}

.basket-record:hover {
    border-color: var(--accent);
}

.basket-record--dragging {
    opacity: 0.45;
}

.basket-record__title {
    flex: 1;
    min-width: 0;
    font-size: 0.9rem;
    word-break: break-word;
    cursor: text;
}

.basket-record__edit-input {
    flex: 1;
    min-width: 0;
    padding: 0;
    background: transparent;
    border: none;
    border-bottom: 1.5px solid var(--accent);
    color: var(--fg);
    font: inherit;
    font-size: 0.9rem;
    outline: none;
}

.basket-record__del {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--muted);
    border-radius: 4px;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.1s, color 0.1s;
}

.basket-record:hover .basket-record__del {
    opacity: 1;
}

.basket-record__del:hover {
    color: var(--danger);
    opacity: 1;
}

/* ── Shortcuts panel (right) ─────────────────────────────────────────────── */

.basket-shortcuts {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.basket-shortcut {
    background: var(--bg-elevated);
    border: 2px dashed var(--border);
    border-radius: 12px;
    padding: 1.25rem 1.5rem;
    display: flex;
    align-items: flex-start;
    gap: 1.1rem;
    transition: border-color 0.15s, background 0.15s;
    min-height: 100px;
}

.basket-shortcut--over {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, var(--bg-elevated));
}

.basket-shortcut--active {
    border-style: solid;
    border-color: var(--accent);
}

.basket-shortcut--sent {
    border-style: solid;
    border-color: var(--success);
}

.basket-shortcut__icon {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    background: color-mix(in srgb, var(--accent) 15%, var(--bg));
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent);
}

.basket-shortcut__body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.basket-shortcut__name {
    font-family: var(--font-heading);
    font-size: 1.05rem;
    font-weight: 600;
}

.basket-shortcut__hint {
    font-size: 0.83rem;
    color: var(--muted);
}

/* Status line shown briefly after a successful shortcut action */
.basket-shortcut__status {
    font-size: 0.83rem;
    color: var(--success);
}

/* Inline input shown after dropping on "Изучить" */
.basket-shortcut__study-input-wrap {
    margin-top: 0.2rem;
}

.basket-shortcut__study-input {
    width: 100%;
    padding: 0.45rem 0.65rem;
    background: var(--bg);
    color: var(--fg);
    border: 1.5px solid var(--accent);
    border-radius: 6px;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
    font-family: var(--font-base);
    font-size: 0.9rem;
    outline: none;
    box-sizing: border-box;
}

.basket-shortcut__study-input::placeholder { color: var(--muted); }

/* Multiline variant — used by the anxiety-diary shortcut. Grows with
   content (autoGrow helper), keeps the same border/focus styling as
   the single-line variant, but reserves enough vertical room for a
   short paragraph out of the box. */
.basket-shortcut__study-input--multiline {
    min-height: 4.5rem;
    resize: vertical;
    line-height: 1.4;
}

/* ── Responsive ──────────────────────────────────────────────────────────── */

@media (max-width: 780px) {
    .basket-layout {
        flex-direction: column;
    }

    .basket-panel {
        flex: none;
        width: 100%;
    }
}

/* ── Spheres page ─────────────────────────────────────────────────────────── */

.spheres-page { padding: 1.5rem; max-width: 960px; margin: 0 auto; }

.spheres__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 1rem;
    margin-top: 1.5rem;
}

.sphere-card {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    transition: border-color 0.15s;
}

.sphere-card:hover { border-color: var(--accent); }

.sphere-card__name {
    font-size: 1rem;
    font-weight: 600;
    color: var(--fg);
}

.sphere-card__desc {
    font-size: 0.82rem;
    color: var(--muted);
    line-height: 1.5;
    flex: 1;
}

.sphere-card__footer {
    display: flex;
    justify-content: flex-end;
    margin-top: 0.25rem;
}

.sphere-card__actions-btn {
    font-size: 0.82rem;
    font-family: var(--font-base);
    font-weight: 600;
    color: var(--accent);
    background: transparent;
    border: 1px solid var(--accent);
    border-radius: 5px;
    padding: 0.3rem 0.75rem;
    cursor: pointer;
    text-decoration: none;
    display: inline-block;
    transition: background 0.12s, color 0.12s;
}

.sphere-card__actions-btn:hover {
    background: var(--accent-gradient);
    color: var(--accent-fg);
}

/* ── Actions page ─────────────────────────────────────────────────────────── */

.actions-page { padding: 4rem 1.5rem 2rem; max-width: 960px; margin: 0 auto; }

.actions__header {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 0.25rem;
}

.actions__title { margin: 0; }

.actions__back {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.85rem;
    color: var(--muted);
    text-decoration: none;
    white-space: nowrap;
    transition: color 0.12s;
}

.actions__back:hover { color: var(--fg); }

/* Main tab bar (Проекты / Правила / Задачи) */
.actions__main-tabs {
    display: flex;
    gap: 0.25rem;
    margin-top: 1.25rem;
    margin-bottom: 1rem;
    border-bottom: 1px solid var(--border);
}

.actions__main-tab {
    font-size: 1rem;
    font-family: var(--font-base);
    font-weight: 600;
    color: var(--muted);
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    padding: 0.7rem 1.4rem;
    cursor: pointer;
    margin-bottom: -1px;
    transition: color 0.12s, border-color 0.12s;
}

.actions__main-tab:hover { color: var(--fg); }

.actions__main-tab--active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* Sub-tab bar (status tabs within each section) */
.actions__sub-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-bottom: 1rem;
}

.actions__sub-tab {
    font-size: 0.9rem;
    font-family: var(--font-base);
    color: var(--muted);
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.45rem 1rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
}

.actions__sub-tab:hover { color: var(--fg); border-color: var(--fg); }

.actions__sub-tab--active {
    color: var(--accent);
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, var(--bg-elevated));
}

.actions__sub-tab-count {
    font-size: 0.72rem;
    background: var(--border);
    border-radius: 10px;
    padding: 0 0.4rem;
    min-width: 1.4em;
    text-align: center;
}

.actions__sub-tab--active .actions__sub-tab-count {
    background: color-mix(in srgb, var(--accent) 20%, transparent);
}

.actions__sub-tab--drag-over {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 18%, var(--bg-elevated));
    color: var(--accent);
    transform: scale(1.04);
}

.project-tile--dragging,
.kanban__card--dragging {
    opacity: 0.35;
    cursor: grabbing;
}

/* Projects grid inside actions */
.actions__projects-grid { margin-top: 0; }

/* Item list (rules + tasks) — same column width as kanban__col (230px).
   RuleCube has no fixed width, so it stretches to fill a `1fr` track
   fine — this stays `minmax(…, 1fr)` for #rules-list. */
.actions__list,
.actions__tasks-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

/* #tasks-cards renders TaskCube (.cube--task), whose width is hard-fixed
   at 220px (see the comment on .cube--task) — it can't stretch to fill a
   `1fr` track. With few tasks on a wide section, `minmax(230px, 1fr)`
   stretches every track to fill the leftover width, so the fixed-width
   cube sits in a much wider cell than itself — same effect documented at
   `#backlog-tab-idea .kanban__cards`. Fix the track to the cube's own
   width so cubes (and the add-card, which fills its cell) pack tightly
   instead of floating in oversized cells. */
#tasks-cards {
    grid-template-columns: repeat(auto-fill, 220px);
}

.action-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.6rem 0.75rem;
    transition: border-color 0.12s;
}

.action-item:hover { border-color: color-mix(in srgb, var(--border) 60%, var(--fg)); }

.action-item__title {
    flex: 1;
    font-size: 0.9rem;
    color: var(--fg);
    cursor: pointer;
    border-radius: 3px;
    padding: 0.1rem 0.25rem;
    min-width: 0;
    word-break: break-word;
}

.action-item__title:hover {
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.action-item__title-input {
    flex: 1;
    font-size: 0.9rem;
    font-family: var(--font-base);
    color: var(--fg);
    background: var(--bg);
    border: 1.5px solid var(--accent);
    border-radius: 3px;
    padding: 0.1rem 0.25rem;
    outline: none;
    min-width: 0;
    width: 100%;
}

.action-item__controls {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    flex-shrink: 0;
}

.action-item__move-btn {
    font-size: 0.85rem;
    color: var(--muted);
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.2rem 0.45rem;
    cursor: pointer;
    line-height: 1;
    transition: color 0.12s, border-color 0.12s;
}

.action-item__move-btn:hover { color: var(--fg); border-color: var(--fg); }

.action-item__delete-btn {
    color: var(--muted);
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.2rem 0.35rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    transition: color 0.12s, border-color 0.12s;
}

.action-item__delete-btn:hover { color: var(--danger); border-color: var(--danger); }

/* Add button + form */
.actions__add-card {
    border: 1.5px dashed color-mix(in srgb, var(--accent) 50%, var(--border));
    border-radius: 6px;
    width: 100%;
    overflow: hidden;
    margin-top: 0.25rem;
    box-sizing: border-box;
    transition: border-color 0.12s, box-shadow 0.12s;
}

.actions__add-card:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 15%, transparent);
}

.actions__add-card--proj { border-radius: 10px; }

.actions__add-card-trigger {
    display: block;
    width: 100%;
    padding: 0.6rem 0.75rem;
    font-size: 0.85rem;
    font-family: var(--font-base);
    font-weight: 600;
    color: var(--accent);
    background: transparent;
    border: none;
    text-align: left;
    cursor: pointer;
}

.actions__add-card--proj .actions__add-card-trigger { padding: 1rem 1.1rem; }

.actions__add-card-input {
    display: block;
    width: 100%;
    padding: 0.6rem 0.75rem;
    font-size: 0.9rem;
    font-family: var(--font-base);
    color: var(--fg);
    background: transparent;
    border: none;
    outline: none;
    box-sizing: border-box;
}

.actions__add-card--proj .actions__add-card-input { padding: 1rem 1.1rem; }

.actions__add-card-input::placeholder { color: var(--muted); }

.actions__loading, .actions__error, .actions__empty {
    font-size: 0.88rem;
    color: var(--muted);
    padding: 0.75rem 0;
}

.actions__error { color: var(--danger); }

/* Area dropdown in project modal — mounted as a UIDropdown, see the
 * .project-modal__status-select comment above for the pattern. */
.project-modal__area-select {
    flex-shrink: 0;
    width: auto;
}

.project-modal__area-select .ui-dropdown { width: auto; }

.project-modal__area-select .ui-dropdown__trigger {
    width: auto;
    font-size: 0.8rem;
    color: var(--muted);
    background: var(--bg-elevated);
}

.project-modal__area-select .ui-dropdown__trigger:focus,
.project-modal__area-select .ui-dropdown__trigger[aria-expanded="true"] {
    color: var(--fg);
}

/* Focus dropdown in project modal — same pattern as the area dropdown
 * above, scoped to the project's current sphere (see pages/projects.js
 * ::_renderProjectDropdowns). */
.project-modal__focus-select {
    flex-shrink: 0;
    width: auto;
}

.project-modal__focus-select .ui-dropdown { width: auto; }

.project-modal__focus-select .ui-dropdown__trigger {
    width: auto;
    font-size: 0.8rem;
    color: var(--muted);
    background: var(--bg-elevated);
}

.project-modal__focus-select .ui-dropdown__trigger:focus,
.project-modal__focus-select .ui-dropdown__trigger[aria-expanded="true"] {
    color: var(--fg);
}

/* ── Toast notifications (notify.error / notify.success / …) ──────────── */
.notify-container {
    position: fixed;
    bottom: 1rem;
    right: 1rem;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    pointer-events: none;
}

.notify {
    pointer-events: auto;
    padding: 0.6rem 1rem;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    color: var(--fg);
    box-shadow: 0 4px 16px color-mix(in srgb, var(--fg) 18%, transparent);
    max-width: 360px;
    cursor: pointer;
    font-size: 0.95rem;
    line-height: 1.35;
    animation: notify-in 0.18s ease-out;
}

.notify--success { border-color: var(--success); color: var(--success); }
.notify--warn    { border-color: var(--danger);  color: var(--danger); }
.notify--error   { border-color: var(--danger);  color: var(--danger); background: color-mix(in srgb, var(--danger) 8%, var(--bg-elevated)); }

@keyframes notify-in {
    from { transform: translateY(8px); opacity: 0; }
    to   { transform: translateY(0);   opacity: 1; }
}

/* Modal kind — ``Modal.alert(message, { kind: "error" })`` adds this to the
   shared modal template so destructive info dialogs stand out from the
   default askConfirm visuals. */
.modal[data-kind="error"]   .modal__title { color: var(--danger); }
.modal[data-kind="warn"]    .modal__title { color: var(--danger); }
.modal[data-kind="success"] .modal__title { color: var(--success); }

/* ── Auth overlay ─────────────────────────────────────────────────────────
   Full-screen blocker shown by `js/auth.js` when the backend rejects every
   request (no token in localStorage, or the stored token no longer matches
   the one in `.env`). The user must paste the bearer token from
   `src/backend/server/.env` and submit; on success we reload so every
   script (including `app.js`'s early `/assistant/characters` fetch) runs
   with the token in place. Mirrors the modal/notify visuals so it feels
   like the rest of the app across all three themes. */
.auth-overlay {
    position: fixed;
    inset: 0;
    z-index: 400;            /* above .modal (300), below .notify-container (9999) */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(2px);
    animation: notify-in 0.18s ease-out;
}

.auth-overlay__card {
    width: 100%;
    max-width: 460px;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 16px 40px color-mix(in srgb, var(--fg) 32%, transparent);
    padding: 1.5rem 1.5rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.auth-overlay__title {
    font-family: var(--font-heading);
    font-size: 1.2rem;
    font-weight: 600;
    margin: 0;
}

.auth-overlay__text {
    margin: 0;
    color: var(--muted);
    line-height: 1.45;
}
.auth-overlay__text code {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85em;
    background: var(--code-bg, color-mix(in srgb, var(--fg) 8%, transparent));
    border-radius: 4px;
    padding: 0.05rem 0.3rem;
}
.auth-overlay__text--muted { font-size: 0.9rem; }

.auth-overlay__hint {
    margin: 0;
    padding: 0.55rem 0.7rem;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.82rem;
    color: var(--fg);
    background: var(--code-bg, color-mix(in srgb, var(--fg) 8%, transparent));
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow-x: auto;
    white-space: pre-wrap;
    word-break: break-all;
}

.auth-overlay__field {
    display: flex;
    gap: 0.4rem;
    align-items: stretch;
}

.auth-overlay__input {
    flex: 1;
    min-width: 0;
    padding: 0.55rem 0.7rem;
    background: var(--bg);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 6px;
    font: inherit;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.92rem;
    outline: none;
    transition: border-color 0.15s;
}
.auth-overlay__input:focus { border-color: var(--accent); }
.auth-overlay__input::placeholder { color: var(--muted); opacity: 0.7; }

.auth-overlay__toggle {
    padding: 0 0.7rem;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 1rem;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
}
.auth-overlay__toggle:hover { color: var(--fg); border-color: var(--accent); }

.auth-overlay__actions {
    display: flex;
    gap: 0.5rem;
    justify-content: flex-end;
    margin-top: 0.25rem;
}

.auth-overlay__submit {
    padding: 0.55rem 1.25rem;
    background: var(--accent-gradient);
    color: var(--accent-fg, var(--bg));
    border: 1px solid var(--accent);
    border-radius: 6px;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: filter 0.15s;
}
.auth-overlay__submit:hover { filter: brightness(1.08); }
.auth-overlay__submit:active { filter: brightness(0.95); }

.auth-overlay__clear {
    align-self: flex-start;
    background: transparent;
    color: var(--muted);
    border: none;
    padding: 0;
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.auth-overlay__clear:hover { color: var(--danger); }

/* ── Debug toolbar (mounted only when `?debug=1` or localStorage flag) ── */
/* Bottom-left so it doesn't collide with .page-char (bottom-right). z-index
 * 95 keeps it under the topbar (30 + .auth-overlay (400)) but above page
 * content so it stays reachable while debugging. */
.debug-toolbar {
    position: fixed;
    bottom: 1.25rem;
    left: 1.25rem;
    z-index: 95;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
}

.debug-toolbar__btn {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--bg-elevated);
    color: var(--accent);
    border: 1px solid var(--border);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    box-shadow: 0 4px 14px color-mix(in srgb, var(--fg) 18%, transparent);
    transition: transform 0.12s ease, color 0.12s ease;
}
.debug-toolbar__btn:hover {
    transform: translateY(-1px);
    color: var(--fg);
}
.debug-toolbar__btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.debug-panel {
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 12px 32px color-mix(in srgb, var(--fg) 25%, transparent);
    padding: 0.5rem;
    min-width: 220px;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.debug-panel__heading {
    font-family: var(--font-heading);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    padding: 0.25rem 0.5rem 0.4rem;
    border-bottom: 1px solid var(--border);
    margin-bottom: 0.25rem;
}

.debug-panel__action {
    background: transparent;
    border: none;
    color: var(--fg);
    font: inherit;
    text-align: left;
    padding: 0.5rem 0.6rem;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.12s;
}
.debug-panel__action:hover {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.debug-panel__action:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* ── Debug text viewer (modal that shows extracted page text) ─────────── */
.debug-viewer .modal__dialog.debug-viewer__dialog {
    max-width: 720px;
    width: 100%;
    max-height: 85vh;
    display: flex;
    flex-direction: column;
}

.debug-viewer__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}
.debug-viewer__header .modal__title {
    margin: 0;
}

.debug-viewer__copy {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem 0.6rem;
    border-radius: 6px;
    font: inherit;
    font-size: 0.85rem;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    cursor: pointer;
    transition: color 0.12s, border-color 0.12s;
}
.debug-viewer__copy:hover {
    color: var(--fg);
    border-color: var(--accent);
}

.debug-viewer__pre {
    flex: 1 1 auto;
    overflow: auto;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.75rem 1rem;
    margin: 0 0 1rem;
    font-family: ui-monospace, "SFMono-Regular", "Menlo", monospace;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--fg);
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 60vh;
}
.debug-viewer__pre:focus {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

@media (max-width: 480px) {
    .debug-toolbar { bottom: 0.75rem; left: 0.75rem; }
    .debug-toolbar__btn { width: 40px; height: 40px; }
    .debug-panel { min-width: 180px; }
}

/* ── Reminders feature ─────────────────────────────────────────────────── */
/* Cube grid + history list on the dedicated page (reminders.html), and
 * the global bell + dropdown panel mounted by NotificationCenter
 * (injected from app.js). All colours via semantic tokens — no
 * primitives, so themes swap by changing theme.css alone. */

/* Same shell as main.tasks-backlog-page: fixed to the viewport height, own
 * scroll disabled — header/tabs/add-button stay put, only the cube grid
 * below scrolls internally (see .reminders-page__scroll). Without this the
 * whole page would scroll like a plain document instead of behaving like
 * the task/rule list columns it's styled to match. */
.reminders-page {
    max-width: 1200px;
    margin: 0 auto;
    height: 100vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    /* 4rem top clears the fixed global topbar (56px) — same convention as
     * main.tasks-backlog-page etc. Without it this page's own header row
     * (title / «Уведомления») renders hidden underneath it. */
    padding: 4rem 1rem 1rem;
    gap: 0.75rem;
}

/* The one flexible row — takes all remaining height and scrolls its own
 * content (the cube grid), same trick as `.kanban__col--grid .kanban__cards`
 * (min-height: 0 is required in a flex chain for overflow-y to actually
 * kick in instead of the container growing to fit its content). */
.reminders-page__scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* «+ Новое напоминание» — by default `.kanban__add-btn`/`.kanban__add-input`
 * stretch to 100% of their container (needed inside a narrow backlog
 * column), but here the add-area spans the whole page width. Narrow it to
 * one cube's width, same convention as `.task-templates__toolbar`. */
.reminders-page__add-area .kanban__add-btn,
.reminders-page__add-area .kanban__add-input {
    width: 230px;
    max-width: 100%;
}

/* Tiny breadcrumb shown when the user lands on this page via the
   «task-card → Создать напоминание» shortcut (TODO.md § Напоминания).
   Spans the same 230 px width as the inline input below it so it
   lines up. */
.reminders-from-task-banner {
    width: 230px;
    max-width: 100%;
    margin: 0 0 0.4rem 0;
    padding: 0.35rem 0.55rem;
    background: color-mix(in srgb, var(--accent) 8%, transparent);
    border-left: 3px solid var(--accent);
    border-radius: 4px;
    color: var(--muted);
    font-size: 0.82rem;
}
.reminders-from-task-banner a {
    color: var(--accent);
    text-decoration: underline;
}

.reminders__empty {
    padding: 1.5rem 1rem;
    border: 1px dashed var(--border);
    border-radius: 6px;
    color: var(--muted);
    text-align: center;
    font-size: 0.9rem;
}

/* Reminder cube — reuses the generic `.cube`/`.cube__head`/`.cube__planks`/
 * `.cube__actions` shell (same as tasks/rules/templates/focuses), plus a
 * one-shot flavour variant. No priority/deadline/estimate planks — a
 * reminder only has a schedule summary + optional microaction badge +
 * next-fire preview.
 *
 * `.cube__actions` is `position: absolute` (see the generic rule below),
 * so it doesn't contribute to the cube's own height — task/rule cubes
 * never notice because their extra plank/template/priority rows always
 * make the cube tall enough anyway. A reminder cube always shows exactly
 * two stacked action buttons (2 × 1.7rem + gap + top offset), but on the
 * Бэклог/Сделано tabs it has no next-fire plank (only "in_progress" shows
 * one), so without an explicit floor the cube is short enough that the
 * button stack overflows past its bottom edge. */
.cube--reminder {
    min-height: 4.5rem;
}
.cube--reminder-one-shot {
    border-left: 3px solid var(--warn);
}
.cube__plank--one-shot {
    color: var(--warn) !important;
}

/* Page header: title truly centered (like the plain `<h2 class="page-title">`
 * sphere pages use), with the «Уведомления» button anchored to the right —
 * same 3-column grid trick as `.topbar` (1fr auto 1fr), so the title's
 * center doesn't drift toward the button's side the way a plain flex row
 * with one flanking element would. */
.reminders-page__header {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    margin-bottom: 1.5rem;
}
.reminders-page__header-spacer { grid-column: 1; }
.reminders-page__header-title { grid-column: 2; margin: 0; }

/* «Уведомления» — a real filled button (same accent look as the global
 * `button` element), not a plain text link, so it reads as the page's one
 * clear call-to-action rather than blending into the header. */
.reminders-page__notif-btn {
    grid-column: 3;
    justify-self: end;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 1rem;
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border-radius: 6px;
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    transition-property: opacity, scale;
    transition-duration: 0.15s;
    transition-timing-function: ease-out;
}
.reminders-page__notif-btn:hover { opacity: 0.9; }
.reminders-page__notif-btn:active { scale: 0.96; }

.notifications-page__read-all {
    margin-left: auto;
}

/* ── ReminderDetail panel body ─────────────────────────────────────── */
/* Reuses `.task-detail` chrome; only body content is reminder-specific. */

.rd-day-buttons {
    display: flex;
    gap: 0.3rem;
    flex-wrap: wrap;
}

.rd-day-btn {
    width: 36px;
    height: 36px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--muted);
    font-weight: 600;
    font-size: 0.82rem;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}
.rd-day-btn:hover {
    border-color: var(--accent);
    color: var(--fg);
}
.rd-day-btn--active {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border-color: var(--accent);
}
.rd-day-btn--active:hover {
    background: var(--accent-gradient);
    color: var(--accent-fg);
}

.rd-day-hint {
    margin-top: 0.35rem;
    font-size: 0.75rem;
    color: var(--muted);
}

.rd-slots {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-bottom: 0.5rem;
}

.rd-slot {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg);
}
.rd-slot__time {
    font-family: var(--font-base);
    font-size: 0.85rem;
    padding: 0.25rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg-elevated);
    color: var(--fg);
}
.rd-slot__step {
    max-width: 130px;
}
/* Number-input replacement for the old `every_minutes` select.
   Lives in a column with its preset chips below — wraps inside the
   slot row's flex container. */
.rd-slot__step-wrap {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    flex: 1 1 auto;
    min-width: 0;
}
.rd-slot__step-num {
    font-family: var(--font-base);
    font-size: 0.85rem;
    padding: 0.25rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg-elevated);
    color: var(--fg);
    width: 5.5rem;
}
.rd-slot__step-num:focus {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
/* Hide the up/down spinner — the preset chips are the primary
   control, and the native spinner is too easy to nudge off a
   sensible value. Users who want it can re-enable with dev-tools. */
.rd-slot__step-num::-webkit-outer-spin-button,
.rd-slot__step-num::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.rd-slot__step-num[type="number"] {
    -moz-appearance: textfield;
}
.rd-slot__step-presets {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
}
.rd-slot__step-preset {
    border: 1px solid var(--border);
    background: var(--bg-elevated);
    color: var(--muted);
    border-radius: 999px;
    padding: 0.15rem 0.55rem;
    font-size: 0.72rem;
    cursor: pointer;
    font-family: var(--font-base);
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.rd-slot__step-preset:hover {
    color: var(--fg);
    border-color: var(--accent);
}
.rd-slot__step-preset--active {
    color: var(--accent);
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.rd-slot__unit-label {
    color: var(--muted);
    font-size: 0.85rem;
}
.rd-slot__sep {
    color: var(--muted);
    font-weight: 600;
}
.rd-slot__label {
    color: var(--muted);
    font-size: 0.78rem;
    flex: 1 1 auto;
    min-width: 0;
}
.rd-slot__remove {
    width: 20px;
    height: 20px;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--muted);
    border-radius: 50%;
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}
.rd-slot__remove:hover {
    color: var(--danger);
    border-color: var(--danger);
}

.rd-add-slot {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.4rem 0.7rem;
    border: 1px dashed var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--muted);
    font-weight: 600;
    font-size: 0.82rem;
    cursor: pointer;
}
.rd-add-slot:hover {
    color: var(--accent);
    border-color: var(--accent);
}

/* Microaction — themed UIDropdown (js/shared/dropdown.js) mount, same
 * min-height convention as .rd-status-mount, plus a label input row
 * that appears once a non-"none" type is picked (see .rd-microaction-label-row below). */
.rd-microaction-mount {
    min-height: 1.8rem;
}

/* ── NotificationCenter (topbar bell + dropdown) ──────────────────── */

.notif-bell {
    position: relative;
    margin-right: 0.5rem;
}
.notif-bell svg {
    color: inherit;
}
.notif-bell--has-items {
    color: var(--accent);
}

.topbar__badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--accent);
    color: var(--accent-fg);
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    pointer-events: none;
}

.notif-panel {
    position: fixed;
    top: 56px;
    right: 0.75rem;
    width: min(360px, calc(100vw - 1.5rem));
    max-height: 70vh;
    overflow-y: auto;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 6px 24px color-mix(in srgb, var(--fg) 18%, transparent);
    z-index: 200;
    padding: 0.5rem 0;
    display: flex;
    flex-direction: column;
}

.notif-panel__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.4rem 0.85rem 0.5rem;
    border-bottom: 1px solid var(--border);
}
.notif-panel__title {
    font-weight: 600;
    color: var(--muted);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.notif-panel__dismiss-all {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    border-radius: 4px;
    padding: 0.25rem 0.55rem;
    font-size: 0.78rem;
    cursor: pointer;
}
.notif-panel__dismiss-all:hover {
    color: var(--fg);
    border-color: var(--fg);
}

.notif-panel__empty {
    padding: 1rem 0.85rem;
    color: var(--muted);
    font-size: 0.85rem;
    text-align: center;
}

.notif-panel__list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.notif-item {
    display: flex;
    align-items: stretch;
    border-bottom: 1px solid var(--border);
}
.notif-item:last-child { border-bottom: none; }

.notif-item__main {
    flex: 1 1 auto;
    background: transparent;
    border: none;
    padding: 0.6rem 0.85rem;
    text-align: left;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}
.notif-item__main:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }

.notif-item__title {
    font-weight: 600;
    color: var(--fg);
    font-size: 0.88rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.notif-item__desc {
    color: var(--muted);
    font-size: 0.8rem;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.notif-item__time {
    color: var(--muted);
    font-size: 0.78rem;
}

/* Quick "mark read" button — lets the user clear a bell-panel item
 * without opening the full NotificationCard. Sits beside `.notif-item__main`
 * (which still opens the card on click); `stopPropagation` keeps the two
 * click targets independent. */
.notif-item__mark-read {
    width: 32px;
    flex: 0 0 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
}
.notif-item__mark-read:hover { color: var(--success); }

.notif-panel__footer {
    border-top: 1px solid var(--border);
    padding: 0.5rem 0.85rem 0.25rem;
    text-align: center;
}
.notif-panel__all-link {
    color: var(--accent);
    text-decoration: none;
    font-size: 0.82rem;
    font-weight: 600;
}
.notif-panel__all-link:hover { text-decoration: underline; }

/* ── NotificationCard (centered detail modal) ─────────────────────── */
/* Reuses `.project-modal`/`.project-section` wholesale — only the small
 * meta row + action row below the header are card-specific. */

/* Neither field is actually editable in this card (unlike the project/
 * focus modals `.project-modal__title-display`/`.project-section__display`
 * normally come from) — strip the click-to-edit cursor + hover tint those
 * base classes carry so the card doesn't look interactive where it isn't.
 * Combined-class selectors (not a bare `.notif-card__title`) so these win
 * on specificity over the base rules regardless of source order. */
.project-modal__title-display.notif-card__title,
.project-section__display.notif-card__description {
    cursor: default;
}
.project-modal__title-display.notif-card__title:hover,
.project-section__display.notif-card__description:hover {
    background: none;
}
/* The habits digest (and any future multi-line notification body) writes
 * `\n`-joined lines into `description` — `textContent` alone collapses
 * them under the base class's normal white-space, so this override
 * preserves line breaks without switching the whole card to `pre`. */
.project-section__display.notif-card__description {
    white-space: pre-line;
}

/* Read/unread toggle sits in the same row as the title (inside
 * `.project-modal__title-block`, a flex row already) — short label,
 * fixed width so a long title (which can shrink/wrap via the sibling
 * `.project-modal__title-text-wrap`'s `flex: 1; min-width: 0`) never
 * squeezes it. */
.notif-card__read-toggle {
    flex-shrink: 0;
    padding: 0.25rem 0.6rem;
    font-size: 0.78rem;
    font-weight: 600;
    border-radius: 999px;
}
/* `.research__secondary:hover` sets `background: var(--bg-elevated)` —
 * identical to `.project-modal__dialog`'s own background, so the hover
 * was invisible against the modal it sits in. Override with an
 * accent-tinted hover instead; combined-class selector so it wins on
 * specificity over the base rule regardless of source order. */
.research__secondary.notif-card__read-toggle:hover {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    border-color: var(--accent);
    color: var(--fg);
    opacity: 1;
}

.notif-card__meta {
    display: flex;
    align-items: center;
    margin: -0.25rem 0 0.75rem;
}
/* The fired-at time as a small pill/"плашка" badge — same visual recipe
 * as `.cube__plank` (muted-tinted background, small pill radius) rather
 * than bare inline text. */
.notif-card__time-badge {
    display: inline-flex;
    align-items: center;
    font-size: 0.78rem;
    font-weight: 600;
    padding: 0.2rem 0.55rem;
    border-radius: 999px;
    background: color-mix(in srgb, var(--muted) 14%, var(--bg-elevated));
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

/* Microaction gets its own visually separated block — a plain divider
 * line, the label as its own heading row (nothing else shares that row),
 * then the action button on the row below — instead of blending into
 * the bottom actions row with unrelated controls. */
.notif-card__divider {
    height: 1px;
    background: var(--border);
    margin: 1rem 0;
}
.notif-card__microaction-block {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
}
.notif-card__microaction-label {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}
.notif-card__microaction--done {
    opacity: 0.7;
}

.notif-card__microaction-value {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    width: 100%;
    margin-top: 0.5rem;
}
.notif-card__microaction-value-label {
    font-size: 0.85rem;
    color: var(--muted);
}
.notif-card__microaction-levels {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
}
.notif-card__microaction-level {
    appearance: none;
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--text);
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 6px;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.12s ease, border-color 0.12s ease;
}
.notif-card__microaction-level:hover {
    border-color: var(--accent, var(--text));
}
.notif-card__microaction-level--active {
    background: var(--accent, var(--text));
    border-color: var(--accent, var(--text));
    color: var(--bg-elevated);
}
.notif-card__microaction-confirm {
    margin-left: 0.5rem;
}

.notif-card__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 1rem;
}

/* ── Notifications page (notifications.html) ──────────────────────── */

.notifications-list {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg-elevated);
    overflow: hidden;
}

.notifications-list__row {
    display: grid;
    /* Fixed width for the indicators column (room for both the unread
     * dot and the microaction-pending dot side by side) — an `auto`
     * track here sized itself to however many dots a given row
     * actually has, so every column after it visibly shifted left/right
     * depending on whether a row had 0, 1, or 2 dots. */
    grid-template-columns: 22px 90px 1fr auto;
    gap: 0.75rem;
    align-items: center;
    padding: 0.6rem 0.85rem;
    border-bottom: 1px solid var(--border);
    font-size: 0.88rem;
    background: transparent;
    color: var(--fg);
    text-align: left;
    width: 100%;
    cursor: pointer;
    transition: background 0.12s;
}
.notifications-list__row:last-child { border-bottom: none; }
.notifications-list__row:hover {
    background: color-mix(in srgb, var(--accent) 6%, transparent);
}
/* Override the sitewide `button:active:not(:disabled) { scale: 0.96 }`
 * press animation — a shrinking row looks wrong in a list (rows aren't
 * standalone buttons, they're click targets in a stack); pressing one
 * should just deepen the hover tint, not scale down. Needs the `button`
 * element in the selector to match that rule's specificity (both end up
 * at 0 ids / 2 classes / 1 element) — a plain `.notifications-list__row:active`
 * loses the tie-break because it has one fewer element selector. */
button.notifications-list__row:active {
    scale: 1;
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}
/* A clicked row stays focused (it's a <button>) and the browser's default
 * focus outline lingers on it after the click — looks like a stuck
 * highlight frame. Tried a `:focus-visible`-only carve-out first (kept
 * the ring for keyboard nav, dropped it for mouse — the pattern used by
 * `.deadlines-panel__item:focus-visible` elsewhere in this file), but a
 * plain mouse click on a `<button>` still matches `:focus-visible` here
 * (confirmed via `el.matches(':focus-visible')` after a real click), so
 * that carve-out doesn't actually help — drop the outline unconditionally. */
.notifications-list__row:focus,
.notifications-list__row:focus-visible {
    outline: none;
}
.notifications-list__row--read {
    opacity: 0.6;
}

.notifications-list__indicators {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}
.notifications-list__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--accent);
    flex: 0 0 auto;
}
.notifications-list__dot--microaction {
    background: var(--warn);
}

.notifications-list__time {
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    font-size: 0.82rem;
}

.notifications-list__title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.notifications-list__kind {
    color: var(--muted);
    font-size: 0.78rem;
    white-space: nowrap;
}

/* ── ReminderDetail: extensions (v2) ────────────────────────────── */
/* Status dropdown + slot type picker + every_n row. The base styles
 * above are unchanged. */

/* Status — themed UIDropdown (js/shared/dropdown.js), same widget
 * tasks/rules cards use for their own status pickers. min-height avoids
 * a layout jump before the dropdown mounts, same as .task-card__status-mount. */
.rd-status-mount {
    min-height: 1.8rem;
}

/* Mode toggle (Повторяющееся / Одноразовое) — segmented control sitting
 * just under the title. Pill container, two equal segments. The active
 * segment reads as solid accent — the inactive one stays muted. Hover
 * on an inactive segment is just a text-color change (no background
 * tint) so it can't be confused with the active state at a glance.
 * Matches the segmented-button vocabulary used elsewhere on the site
 * (`.rd-day-btn`, `.rd-pending-btn`) for visual consistency. */
.rd-mode-toggle {
    display: flex;
    gap: 0.3rem;
    padding: 0.2rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
}
.rd-mode-btn {
    flex: 1 1 0;
    appearance: none;
    background: transparent;
    color: var(--muted);
    border: 1px solid transparent;
    border-radius: 6px;
    padding: 0.4rem 0.6rem;
    font-family: var(--font-base);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    transition: color 0.12s, background 0.12s, border-color 0.12s,
        box-shadow 0.12s;
}
.rd-mode-btn:hover:not(.rd-mode-btn--active) {
    color: var(--fg);
}
.rd-mode-btn--active {
    background: var(--accent-gradient);
    color: var(--accent-fg);
    border-color: var(--accent);
    box-shadow: 0 1px 4px color-mix(in srgb, var(--accent) 35%, transparent);
    font-weight: 600;
}
.rd-mode-btn--active:hover {
    background: var(--accent-gradient);
    color: var(--accent-fg);
}

/* One-shot fire_at row: a styled date-picker button + native time
 * input side by side. The date button reuses the shared
 * `CalendarPicker` (js/tasks/calendar.js) — see that module for the
 * popup; here we just style the trigger so it sits in a coordinated
 * pair with the time input. The time input stays native (no themed
 * time picker exists) but is colour-scheme-matched via `theme.css`
 * so the dropdown feels at home in each theme. */
.rd-one-shot-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
}
.rd-one-shot-date,
.rd-one-shot-time {
    font-family: var(--font-base);
    font-size: 0.88rem;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg-elevated);
    color: var(--fg);
}
.rd-one-shot-date {
    /* The button is a flex row: label + calendar icon. */
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-width: 9.5rem;
    flex: 0 1 11.5rem;
    cursor: pointer;
    text-align: left;
    font-weight: 500;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.rd-one-shot-date:hover {
    color: var(--fg);
    border-color: var(--accent);
}
.rd-one-shot-date:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.rd-one-shot-date-label {
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.rd-one-shot-date-icon {
    flex: 0 0 auto;
    display: inline-flex;
    color: var(--muted);
    transition: color 0.12s;
}
.rd-one-shot-date:hover .rd-one-shot-date-icon {
    color: var(--accent);
}
.rd-one-shot-time {
    min-width: 5.5rem;
    flex: 0 0 6rem;
}
.rd-one-shot-sep {
    color: var(--muted);
    font-size: 0.85rem;
}

/* Microaction label input — appears below the type dropdown once a
 * non-"none" type is picked. Reuses the same input chrome as the
 * one-shot date/time fields for visual consistency. */
.rd-microaction-label-row {
    margin-top: 0.5rem;
}
.rd-microaction-label-caption {
    display: block;
    margin-bottom: 0.3rem;
    font-size: 0.75rem;
    color: var(--muted);
}
.rd-microaction-label-input {
    width: 100%;
    font-family: var(--font-base);
    font-size: 0.88rem;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg-elevated);
    color: var(--fg);
}
.rd-microaction-label-input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* Quick presets for one-shot fire_at — a row of compact buttons below
 * the date+time inputs. Visual pattern echoes the recurring-section
 * day buttons (`.rd-day-btn`): muted surface, hover lifts to fg, no
 * "active" state — clicking just sets the values and autosaves, the
 * date+time inputs above stay the source of truth. */
.rd-one-shot-presets {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-top: 0.45rem;
}
.rd-one-shot-preset {
    appearance: none;
    background: var(--bg-elevated);
    color: var(--muted);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.25rem 0.55rem;
    font-family: var(--font-base);
    font-size: 0.76rem;
    font-weight: 500;
    cursor: pointer;
    transition: color 0.12s, border-color 0.12s;
}
.rd-one-shot-preset:hover {
    color: var(--fg);
    border-color: var(--fg);
}
.rd-one-shot-preset:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* Add-slot row: button + type picker side by side. */
.rd-add-slot-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.rd-add-slot-type {
    max-width: 180px;
    font-size: 0.78rem;
}

/* every_n row variant — wraps the per-slot type fields. */
.rd-slot__type-select {
    max-width: 150px;
    font-size: 0.78rem;
    flex: 0 0 auto;
}

.rd-slot__n {
    width: 48px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.rd-slot__unit {
    max-width: 80px;
    font-size: 0.78rem;
    flex: 0 0 auto;
}

.rd-slot__fields {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-wrap: wrap;
    flex: 1 1 auto;
    min-width: 0;
}

.rd-slot__lead {
    color: var(--muted);
    font-size: 0.78rem;
    font-weight: 600;
    flex: 0 0 auto;
}

/* ==========================================================================
   Tasks v2 — cube / card / dnd / filters / capacity utility classes
   ==========================================================================
   These are the layout shells for the new task/rule cubes, card side-panel,
   drag-and-drop visuals, filters row, and capacity planks. Component code
   in js/shared/ picks one of these classes (and theme tokens) per element.
   All colors come from semantic tokens — see theme.css. */

/* ── Cube (task / rule) ────────────────────────────────────────────────── */

.cube {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 0.55rem 0.65rem 0.6rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--fg);
    min-width: 0;
    position: relative;
    cursor: pointer;
    transition: border-color 0.15s, background-color 0.15s, box-shadow 0.15s, transform 0.1s;
}

/* Ширина фиксированная, высота — auto с потолком. Только для кубиков задач
   (.cube--task). Ширина 220px совпадает с `minmax(220px, 1fr)` ячейкой
   grid-колонки бэклога (см. .kanban__col--grid) и умещается в flex-колонку
   .kanban__col (230px).

   max-height подобран так, чтобы вмещался полный дефолтный набор с
   3 строками планок: padding (≈18.4px) + title 2 строки (≈38px) + icons
   (≈14px) + planks 3 строки (≈67px c .cube__planks max-height 4.5rem) +
   priority (≈25px) + 5 gap'ов (≈28px) ≈ 190px. 200px — буфер.
   Если контента меньше — кубик автоматически сжимается.

   Кубики правил и шаблонов оставляем на auto-height без потолка — у них
   дополнительная строка .cube__templates, которая сюда не помещается. */
.cube--task {
    width: 220px;
    max-height: 200px;
}

/* Hover affordance: подсветка рамки акцентом + лёгкая подложка + приподнятая
   тень. По `tasks_system.md` кубик целиком кликабельный (открывает карточку
   справа), а не только кнопки действий — поэтому нужно явно показать, что
   «это можно кликнуть», а не только «здесь есть скрытые кнопки». */
.cube:hover {
    border-color: var(--accent);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--fg) 10%, transparent);
    /* ``background`` намеренно НЕ трогаем — он задаётся через
       ``cube--fill-*`` (dim / done / red / yellow) и должен
       оставаться стабильным при hover, иначе состояния заблокированной
       и сделанной задачи «мигают» при наведении. */
}

.cube:active {
    transform: translateY(0.5px);
    box-shadow: 0 1px 3px color-mix(in srgb, var(--fg) 8%, transparent);
}

/* Keyboard focus — для табов и SR-пользователей тот же сигнал, что и hover. */
.cube:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.cube__title {
    font-size: 0.82rem;
    font-weight: 600;
    line-height: 1.35;
    color: var(--fg);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
    /* Заполняет ширину cube__head (flex-родитель) — без этого
       -webkit-box сжимается по содержимому, и заголовок обрезается
       на ~20px. */
    flex: 1 1 0;
    min-width: 0;
}

/* Иконка типа сущности (задача / правило) слева от заголовка.
   Вертикальное центрирование относится к cube__head (align-items: center
   ниже) — раньше стоял flex-start с margin-top-костылём, иконка
   визуально «прилипала» к верхнему краю и расходилась с текстом. */
.cube__kind {
    flex: 0 0 auto;
    line-height: 0;
    color: var(--muted);
    display: inline-flex;
    align-items: center;
}

.cube__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    align-items: center;
}

.cube__icons {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    color: var(--muted);
    line-height: 0;
}

.cube__planks {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    align-items: center;
}

/* Только для кубиков задач: ряд планок ограничен 3 строками. Высота одной
   планки с box-sizing:border-box: padding-top 0.1rem + line-height
   (1.4 × 0.72rem ≈ 1.008rem) + padding-bottom 0.1rem ≈ 1.21rem ≈ 19.3px.
   3 строки + 2 gap'а 0.3rem = 3 × 1.21rem + 2 × 0.3rem ≈ 4.23rem. Берём
   4.5rem с небольшим запасом, чтобы line-height дрожания не обрезали низ.
   Если планок > 3 строк — четвёртая и далее обрезаются; индивидуальные
   планки при этом сжимаются (min-width:0) и обрезают свой текст через
   .cube__plank-text. align-content (а не align-items) нужен для
   multi-row flex-wrap — прижимает ряды к верху, а не растягивает их.
   У правил и шаблонов — оставляем свободный рост по контенту. */
.cube--task .cube__planks {
    max-height: 4.5rem;
    overflow: hidden;
    align-content: flex-start;
}

/* Cube header — icon + title row.
   align-items: center центрирует иконку cube__kind по вертикали
   относительно cube__title (включая случай, когда заголовок
   переносится на 2 строки — иконка остаётся у первой строки,
   потому что сама короче line-height). */

.cube__head {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    min-width: 0;
}

/* Action buttons (📥 / 🗂 / ▶ / ✓ / ×) — hover-revealed overlay on the right.
   `.cube__buttons` is the legacy alias kept so any leftover pages still
   surface the actions on hover; new markup uses `.cube__actions`. */

.cube__actions,
.cube__buttons {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    opacity: 0;
    transition: opacity 0.15s;
}

.cube:hover .cube__actions,
.cube:focus-within .cube__actions,
.cube:hover .cube__buttons,
.cube:focus-within .cube__buttons { opacity: 1; }

.cube__action {
    width: 1.7rem;
    height: 1.7rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--bg-elevated);
    color: var(--muted);
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background-color 0.15s;
    /* Глобальный `button { padding: 0.5rem 1rem }` (style.css:183) иначе
       раздувает кнопку и ломает квадратную форму. */
    padding: 0;
    font: inherit;
}

.cube__action:hover:not(:disabled) {
    border-color: var(--accent);
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-elevated));
}

.cube__action:disabled { opacity: 0.4; cursor: not-allowed; }

.cube__action--delete {
    color: var(--danger);
    border-color: color-mix(in srgb, var(--danger) 35%, var(--border));
}
.cube__action--delete:hover:not(:disabled) {
    color: var(--danger);
    border-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, var(--bg-elevated));
}

.cube__priority {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    /* По умолчанию скрыта — появляется при наведении на кубик (или при
       фокусе внутри, чтобы Tab-навигация оставалась рабочей). Тот же
       приём, что у .cube__actions выше. */
    opacity: 0;
    transition: opacity 0.15s;
}

.cube:hover .cube__priority,
.cube:focus-within .cube__priority { opacity: 1; }

.cube__prio {
    width: 1.55rem;
    height: 1.55rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: transparent;
    color: var(--muted);
    line-height: 0;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background-color 0.15s;
    /* Глобальный `button { padding: 0.5rem 1rem }` (style.css:183) иначе
       раздувает кнопку до ~34×25px. */
    padding: 0;
    font: inherit;
}

.cube__prio:hover:not(:disabled) {
    border-color: var(--accent);
    color: var(--accent);
}

.cube__prio--active {
    border-color: var(--accent);
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 14%, var(--bg-elevated));
}

.cube__prio:disabled { opacity: 0.4; cursor: not-allowed; }

/* Rule cube — template pill row. */

.cube__templates {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    align-items: center;
}

/* Мини-кубик, а не акцентная плашка — те же токены, что у .cube (border +
   bg-elevated + border-radius), просто компактнее по padding/шрифту, чтобы
   в глаз бросалась именно форма «кубика», а не цветной бейдж. */
.cube__template {
    font-size: 0.68rem;
    font-weight: 600;
    padding: 0.1rem 0.4rem;
    border-radius: 6px;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    color: var(--fg);
    white-space: nowrap;
    line-height: 1.4;
    /* Длинное название шаблона иначе выпирает за пределы кубика — flex-item
       по умолчанию не сжимается ниже intrinsic-ширины текста (min-width:auto
       + white-space:nowrap = min-content равен полной ширине текста).
       min-width:0 снимает этот пол, max-width:100% + overflow/ellipsis
       обрезают текст по ширине .cube__templates (= ширина кубика). */
    max-width: 100%;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cube__template--empty {
    color: var(--warn);
    background: color-mix(in srgb, var(--warn) 12%, transparent);
    border: 1px dashed var(--warn);
}

.cube__template--more {
    color: var(--muted);
    background: var(--bg);
    border: 1px solid var(--border);
}

/* Habit trigger/done toggle button — used by the Статистика tab's
   per-day popover (pages/habits.js::_popoverToggleBtn), one habit row
   at a time. The habit cube itself has no quick toggles (removed per
   user feedback — marking a day now lives only in the evening ritual
   block and this stats popover). */

.cube__habit-toggle {
    flex: 1 1 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    padding: 0.3rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--muted);
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background-color 0.15s;
}

.cube__habit-toggle:hover:not(:disabled) {
    border-color: var(--accent);
    color: var(--accent);
}

.cube__habit-toggle--active {
    border-color: var(--success);
    color: var(--success);
    background: color-mix(in srgb, var(--success) 14%, var(--bg-elevated));
}

.cube__habit-toggle:disabled { opacity: 0.4; cursor: not-allowed; }

.cube__habit-toggle-label { white-space: nowrap; }

/* Habit card — reminder chips (js/habits/card.js § Напоминания). Compact,
   not a full `.cube--reminder` — this is a local chip per
   habits-system-plan.md § 7 option (б). */

.habit-card__reminders-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-bottom: 0.5rem;
}

.habit-card__reminder-chip {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-elevated);
    cursor: pointer;
}

.habit-card__reminder-chip--missing {
    cursor: default;
    color: var(--muted);
    border-style: dashed;
}

.habit-card__reminder-chip-icon {
    flex: 0 0 auto;
    line-height: 0;
    color: var(--muted);
}

.habit-card__reminder-chip-title {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.85rem;
}

.habit-card__reminder-chip-status {
    flex: 0 0 auto;
    font-size: 0.7rem;
    color: var(--muted);
}

.habit-card__reminder-chip-remove {
    flex: 0 0 auto;
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: 0.1rem 0.3rem;
    line-height: 1;
}

.habit-card__reminder-chip-remove:hover { color: var(--danger); }

/* Habits page — Статистика tab (habits.html, pages/habits.js). The tab
   itself isn't a `.rules-grid` (heatmap + three ranking lists instead of
   a cube grid), so it needs its own scroll container. */
#habits-tab-stats {
    overflow-y: auto;
}

.habits-stats__lists {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
    margin-top: 1.5rem;
}

.habits-stats__list-block {
    min-width: 0;
}

.habits-stats__list {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.habits-stats__list-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-elevated);
    cursor: pointer;
    font-size: 0.85rem;
}

.habits-stats__list-item:hover {
    border-color: var(--accent);
}

.habits-stats__list-item-title {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.habits-stats__list-item-value {
    flex: 0 0 auto;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.habits-stats__list-empty {
    color: var(--muted);
    font-size: 0.85rem;
}

/* Habits page — day popover (pages/habits.js::_openDayPopover), one row
   per active habit with its own trigger/done toggle pair. */

.habits-stats__popover-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    max-height: 320px;
    overflow-y: auto;
}

.habits-stats__popover-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.habits-stats__popover-row-title {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.82rem;
}

/* Cube icons — fixed per-icon colors so they read on any background. */

.cube__icon { line-height: 0; display: inline-flex; align-items: center; }
.cube__icon--link     { color: var(--icon-link-relates); }
.cube__icon--blocked  { color: var(--icon-blocked); }
.cube__icon--blocks   { color: var(--icon-blocks); }
.cube__icon--miss     { color: var(--icon-deadline-miss); }
.cube__icon--forward  { color: var(--icon-blocker-forward); }
.cube__icon--bang     { color: var(--icon-blocker-forward); }
.cube__icon--robot    { color: var(--icon-author-rule); }
.cube__icon--done     { color: var(--icon-done); }
.cube__icon--desc     { color: var(--muted); }

/* «Приглушённое» состояние иконки блокировки — связь существует, но
   все связанные задачи в статусе done. Иконка остаётся видимой (чтобы
   пользователь не терял сигнал о существовании связи), но с
   пониженной непрозрачностью — отличается от активной блокировки.
   cube.js::_iconRow ставит этот класс рядом с cube__icon--blocked /
   cube__icon--blocks когда есть хоть одна запись, но все они done. */
.cube__icon--dim {
    opacity: 0.4;
}
.cube__icon--dim:hover {
    opacity: 0.7;
}

/* Cube planks (badges inside the cube). */

.cube__plank {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    font-size: 0.68rem;
    font-weight: 600;
    padding: 0.1rem 0.45rem;
    border-radius: 4px;
    line-height: 1.4;
    background: color-mix(in srgb, var(--muted) 14%, var(--bg-elevated));
    color: var(--muted);
    white-space: nowrap;
}

/* Только для кубиков задач: планка должна уметь сжиматься внутри 2-строчного
   контейнера, иначе flex-wrap её игнорирует и она выпирает за пределы cube.
   min-width: 0 разрешает flex-item'у сжаться меньше своего контента,
   max-width: 100% не даёт вылезти за пределы родителя, overflow:hidden скрывает
   невлезающее (текст обрезается через .cube__plank-text). */
.cube--task .cube__plank {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
}

/* Плашка дедлайна — единственный носитель жёлтого/красного сигнала
   (кубик целиком заливкой на дедлайн больше не реагирует, см.
   js/tasks/cube.js::_fillState). Состояния приходят из
   `deadlineState()` — "soon" (≤3 дней) / "missed" (просрочен или
   deadline < assigned_date) / "normal". */
.cube__plank--deadline-soon {
    color: var(--warn);
    background: color-mix(in srgb, var(--warn) 25%, var(--bg-elevated));
}
.cube__plank--deadline-missed {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 22%, var(--bg-elevated));
}

/* Правило без recurrence/trigger — не будет материализовывать задачи.
   Тот же жёлтый сигнал, что у .cube__plank--deadline-soon, чтобы все
   предупреждения на кубике выглядели одинаково (см. rules/cube.js::_planks
   и .cube__template--empty для «нет шаблонов»). */
.cube__plank--warn {
    color: var(--warn);
    background: color-mix(in srgb, var(--warn) 25%, var(--bg-elevated));
}

/* Иконка внутри плашки (используется пока только в
   .cube__plank--size — часы рядом с оценкой). Размер чуть меньше
   текста плашки, чтобы не выпирать по высоте. */
.cube__plank-icon {
    display: inline-flex;
    align-items: center;
    line-height: 0;
    /* Иконка 14px, текст плашки ~11.5px — подтягиваем к одному
       оптическому размеру, чтобы плашка выглядела цельной, а не
       «иконка + текст». */
    transform: scale(0.85);
    transform-origin: center;
    /* Компенсация визуального отступа от scale: при уменьшении
       иконка смещается вправо, даём лёгкий отрицательный margin. */
    margin-right: -0.1rem;
    opacity: 0.85;
}

/* Иконка не сжимается даже если планка сжалась до минимума — иначе при
   длинном тексте иконка бы «съедалась». */
.cube--task .cube__plank-icon,
.cube--template .cube__plank-icon,
.cube--rule .cube__plank-icon {
    flex: 0 0 auto;
}

/* Focus cube's "last updated" date — plain muted text, deliberately NOT a
   `.cube__plank` (no icon, no pill background) per user feedback. */
.cube__updated {
    font-size: 0.72rem;
    color: var(--muted);
}

/* Текстовая часть планки — обёртка над текстом, чтобы text-overflow
   ellipsis корректно работал (text-overflow требует nowrap + overflow:hidden
   на блок-уровне; inline-flex родитель не может сам обрезать текст, если
   рядом с ним есть другие flex-дети — иконка). flex: 1 1 auto забирает всё
   свободное место у иконки и сжимается при переполнении. */
.cube__plank-text {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Cube modifier classes — generated by js/tasks/cube.js and js/rules/cube.js
   from task/rule fields (status, priority, deadlines, dependencies).
   Дедлайн (жёлтый/красный) и «сделано» (зелёный) больше НЕ красят
   заливку кубика — оба сигнала переехали на плашку дедлайна
   (`.cube__plank--deadline-soon`/`-missed`) и на иконку `.cube__icon--done`
   соответственно. Заливка отражает только «заблокировано» (dim). */
.cube--fill-neutral { background: var(--bg-elevated); }
.cube--fill-dim     { background: var(--cube-bg-blocked); opacity: 0.75; }

/* Контур (border) — приоритет. Совпадает с bg-state (border-color
   «тянется» из базового `.cube`). Средний = пунктир, высокий = сплошной
   акцент. */
.cube--outline-solid  { border-color: var(--cube-border-prio-high);   border-style: solid;  border-width: 1.5px; }
.cube--outline-dashed { border-color: var(--cube-border-prio-medium); border-style: dashed; border-width: 1.5px; }
.cube--outline-none   { border-color: var(--border); }

/* Сделано: тусклая заливка + зачёркнутый заголовок. */
.cube--done .cube__title { text-decoration: line-through; opacity: 0.6; }
.cube--done { opacity: 0.85; }

/* ── Card (task / rule side-panel) ────────────────────────────────────── */

/* Общий шелл — соответствует старому .card, оставлен для совместимости с
   любыми кастомными встраиваниями. Сама правая панель теперь .task-card
   (см. ниже). */
.card {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    width: min(420px, 25vw);
    max-width: 100%;
    height: 100%;
    padding: 1rem 1.1rem;
    background: var(--bg-elevated);
    border-left: 1px solid var(--border);
    box-shadow: -4px 0 18px color-mix(in srgb, var(--fg) 18%, transparent);
    overflow-y: auto;
}

.card__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.6rem;
}

.card__warnings {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.6rem 0.75rem;
    background: color-mix(in srgb, var(--warn) 12%, var(--bg-elevated));
    border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
    border-radius: 6px;
    font-size: 0.84rem;
    color: var(--fg);
}

.card__props {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.card__links {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* ── TaskCard — slide-out drawer (js/tasks/card.js) ─────────────────────
   Соответствует спеке `tasks_system.md` § Карточка задачи:
     • выдвигается справа (~1/4 экрана);
     • Esc / клик по затемнению — закрыть только верхнюю;
     • пока открыта — остальной UI затемнён и не кликабелен;
     • в стеке несколько карточек — каждая следующая левее предыдущей. */

.task-card-backdrop {
    position: fixed;
    inset: 0;
    z-index: 240;
    background: color-mix(in srgb, var(--shadow-color) 45%, transparent);
    opacity: 0;
    pointer-events: none;
    /* Синхронно с .task-card — 0.4s, чтобы фон не «обгонял» панель. */
    transition: opacity 0.4s ease;
}

.task-card-backdrop--open {
    opacity: 1;
    pointer-events: auto;
}

.task-card {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 250;
    width: min(420px, 25vw);
    min-width: 320px;
    max-width: 100vw;
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    padding: 1rem 1.1rem 1.5rem;
    background: var(--bg-elevated);
    border-left: 1px solid var(--border);
    box-shadow: -6px 0 24px color-mix(in srgb, var(--fg) 20%, transparent);
    overflow-y: auto;
    overflow-x: hidden;
    transform: translateX(100%);
    /* Чуть медленнее (0.4s) и более «весомый» easing — карточка должна
       читаться как физическая панель, а не мгновенный popup. */
    transition: transform 0.4s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Стек: каждая следующая карточка сдвигается влево, чтобы было видно кромку
   предыдущей. Если открыто 1 — :nth-of-type(2) не существует, так что
   одиночная карточка занимает правый край без сдвига. */
.task-card:nth-of-type(2) { right: min(420px, 25vw); }
.task-card:nth-of-type(3) { right: calc(min(420px, 25vw) * 2); }
.task-card:nth-of-type(4) { right: calc(min(420px, 25vw) * 3); }

/* Когда в стеке больше одной карточки — открываем все видимыми сразу
   (без translateX), чтобы пользователь видел стопку. */
.task-card-backdrop--open ~ .task-card,
.task-card-backdrop--open + .task-card {
    transform: translateX(0);
}

/* Одиночная карточка — анимация выезда как и раньше. */
body.task-card-open .task-card {
    transform: translateX(0);
}

@media (max-width: 900px) {
    .task-card { width: 100vw; }
    .task-card:nth-of-type(2) { right: 0; }
}

/* ── Header ──────────────────────────────────────────────────────────── */

.task-card__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.6rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.task-card__title {
    margin: 0;
    flex: 1;
    min-width: 0;
    font-family: var(--font-heading);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--fg);
    line-height: 1.35;
    overflow-wrap: anywhere;
    cursor: pointer;
    padding: 0.1rem 0.2rem;
    border-radius: 4px;
}

.task-card__title:hover {
    background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.task-card__header-actions {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-shrink: 0;
}

.task-card__more,
.task-card__delete,
.task-card__close {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.3rem;
    border-radius: 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 0;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.task-card__delete:hover {
    color: var(--danger);
    border-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 6%, transparent);
}

.task-card__more:hover,
.task-card__close:hover {
    color: var(--fg);
    border-color: var(--fg);
}

/* ── Warnings ────────────────────────────────────────────────────────── */

.task-card__warnings {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.task-card__warnings-list {
    list-style: none;
    margin: 0;
    padding: 0.55rem 0.7rem;
    background: color-mix(in srgb, var(--warn) 12%, var(--bg-elevated));
    border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
    border-radius: 6px;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    font-size: 0.84rem;
    color: var(--fg);
}

.task-card__warning {
    line-height: 1.4;
}

/* Варианты предупреждений по семантике. Каждый kind из card.js::_warnings
   получает свой фон/границу — сигнал должен читаться одним взглядом. */
.task-card__warning--deadline-missed,
.task-card__warning--cal-miss {
    color: var(--danger);
}
.task-card__warning--deadline-soon {
    color: var(--warn);
}
.task-card__warning--blocked,
.task-card__warning--blocker-forward,
.task-card__warning--robot {
    color: var(--fg);
}

/* ── Status section ──────────────────────────────────────────────────── */

.task-card__status {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.task-card__label {
    font-size: 0.74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
}

.task-card__status-static {
    font-size: 0.92rem;
    color: var(--fg);
    padding: 0.4rem 0.6rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
}

.task-card__status-mount {
    min-height: 1.8rem;
}

.task-card__status-mount-target {
    min-height: 1.8rem;
}

/* Капля цвета статуса слева у выпадающего списка — карточка сразу
 * подсказывает, в каком состоянии задача, без открытия меню.
 * Цвета берутся как CSS-переменные темы (см. theme.css), fallback на
 * нейтральный серый, если переменная не определена. */
.task-card__status-mount-target .ui-dropdown__trigger {
    border-left-width: 3px;
    border-left-style: solid;
}
.task-card__status-mount-target[data-current-status="idea"]        .ui-dropdown__trigger { border-left-color: var(--muted); }
.task-card__status-mount-target[data-current-status="backlog"]     .ui-dropdown__trigger { border-left-color: color-mix(in srgb, var(--muted) 70%, var(--accent)); }
.task-card__status-mount-target[data-current-status="in_progress"] .ui-dropdown__trigger { border-left-color: var(--accent); }
.task-card__status-mount-target[data-current-status="done"]        .ui-dropdown__trigger { border-left-color: var(--success); }

/* ── Properties ──────────────────────────────────────────────────────── */

.task-card__props {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.task-card__prop {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 0.4rem 0.6rem;
    padding: 0.4rem 0.55rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 0.88rem;
}

.task-card__prop-label {
    color: var(--muted);
    font-size: 0.74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    grid-column: 1;
    grid-row: 1;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}
/* Иконка слева от label (пока только для deadline — календарь).
   Совпадает по размеру с .cube__plank-icon (scale 0.85), чтобы
   карточка и кубик выглядели единообразно. */
.task-card__prop-label-icon {
    display: inline-flex;
    align-items: center;
    line-height: 0;
    transform: scale(0.85);
    transform-origin: center;
    margin-right: -0.1rem;
    opacity: 0.85;
}
.task-card__prop-label-text {
    line-height: 1;
}

.task-card__prop-value {
    color: var(--fg);
    grid-column: 1;
    grid-row: 2;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Wraps the remove "×" button and (project rows only) the "open in new
 * tab" link — both live in the grid's 2nd column, spanning both rows,
 * side by side. A plain flex row rather than extra grid columns so rows
 * without the open-link (deadline/area/assigned-date) don't reserve empty
 * grid space next to the remove button. */
.task-card__prop-actions {
    grid-column: 2;
    grid-row: 1 / span 2;
    display: flex;
    align-items: center;
    gap: 0.35rem;
}

.task-card__prop-remove {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    cursor: pointer;
    width: 1.75rem;
    height: 1.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    font-size: 1.15rem;
    line-height: 1;
    transition: color 0.15s, background 0.15s, border-color 0.15s;
}

.task-card__prop-remove:hover {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, transparent);
}

/* «Открыть проект в новой вкладке» — sits next to the remove button on
 * the «Проект» row only (see js/tasks/card.js::_properties). Same square
 * footprint as .task-card__prop-remove, accent hover instead of danger
 * since it isn't destructive. */
.task-card__prop-open {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    cursor: pointer;
    width: 1.75rem;
    height: 1.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    text-decoration: none;
    transition: color 0.15s, background 0.15s, border-color 0.15s;
}

.task-card__prop-open:hover {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}

/* ── Size picker (в карточке задачи) ─────────────────────────────────────
 * Кнопки `[5 мин] [15 мин] [30 мин] [1 ч] [2 ч]` без панели/фона —
 * поле обязательное, поэтому крестик «удалить» не показывается (он
 * только вводил в заблуждение). */
.task-card__size-picker {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 0.4rem 0.6rem;
    padding: 0.2rem 0;
}
.task-card__size-label {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: var(--muted);
    font-size: 0.74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.task-card__size-label svg {
    flex-shrink: 0;
    color: var(--muted);
}
.task-card__size-label-text {
    /* Текст и SVG разнесены в два flex-элемента, чтобы выровнять
     * по центру и не дать иконке съесть место у текста на узких
     * ширинах. */
    line-height: 1;
}
.task-card__size-group {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}
.task-card__size-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--fg);
    border-radius: 5px;
    padding: 0.25rem 0.55rem;
    font-size: 0.82rem;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.task-card__size-btn:hover {
    border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}
.task-card__size-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.task-card__size-btn--active {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    border-color: var(--accent);
    color: var(--accent);
    font-weight: 600;
}

.task-card__add-prop,
.task-card__add-link,
.task-card__create-blocker {
    background: transparent;
    border: 1px dashed var(--border);
    border-radius: 6px;
    color: var(--muted);
    font: inherit;
    font-size: 0.82rem;
    padding: 0.4rem 0.6rem;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.task-card__add-prop:hover,
.task-card__add-link:hover,
.task-card__create-blocker:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 6%, transparent);
}

.task-card__add-prop:disabled,
.task-card__add-link:disabled,
.task-card__create-blocker:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* ── Кликабельное значение в propRow ──────────────────────────────────
 *    Кнопка-«текст»: открывает picker для смены значения (CalendarPicker
 *    для дат, popup-список для area/project). Стилизована под обычный
 *    текст, при hover подсвечивается accent'ом — намёк, что кликабельно. */
.task-card__prop-edit {
    appearance: none;
    display: block;
    width: 100%;
    /* Grid item in a 1fr column — without min-width: 0 its track sizes to
     * the intrinsic content width (an unbreakable long project/area name
     * pushes the whole card wider instead of truncating). */
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    background: transparent;
    border: 0;
    padding: 0.1rem 0;
    margin: 0;
    color: var(--fg);
    font: inherit;
    font-size: 0.88rem;
    text-align: left;
    cursor: pointer;
    border-radius: 3px;
    transition: color 0.12s, background 0.12s;
}
.task-card__prop-edit:hover {
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.task-card__prop-edit:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
/* Пустое значение (только что добавленное через «+ Добавить свойство»,
 * но ещё не заполненное — для area/project). Приглушённый цвет и
 * italic, чтобы визуально отличать от заполненного, но без смены
 * структуры строки (label/value/× остаются одинаковыми). */
.task-card__prop-edit--placeholder {
    color: var(--muted);
    font-style: italic;
}

/* ── «+ Добавить свойство» — выпадающее меню выбора поля ─────────────
 *    position:fixed на document.body, чтобы не клиппилось overflow'ом
 *    карточки. Стилизовано как UIDropdown-меню, но без подвязки к
 *    триггеру (см. card.js::_openAddPropMenu). */
.task-card__add-prop-menu {
    list-style: none;
    margin: 0;
    padding: 0.3rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--fg) 18%, transparent);
    z-index: 9999;
    overflow-y: auto;
    font-size: 0.85rem;
}
.task-card__add-prop-option {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.6rem;
    border-radius: 4px;
    cursor: pointer;
    color: var(--fg);
    user-select: none;
}
/* Иконка свойства в пункте меню «+ Добавить свойство» — тот же SVG и
   тот же .task-card__prop-label-icon, что и на самой строке свойства
   (см. card.js::propRow), чтобы пункт меню и появившаяся после выбора
   строка выглядели как одна и та же сущность. Пункты без иконки в
   PROP_LABEL_ICON просто не получают span — тут ничего резервировать
   не нужно, т.к. текст не выровнен по колонке. */
.task-card__add-prop-option .task-card__prop-label-icon {
    opacity: 0.85;
}
.task-card__add-prop-option:hover {
    background: color-mix(in srgb, var(--accent) 18%, var(--bg-elevated));
    color: var(--accent);
}
/* Текущий выбранный пункт (см. card.js::_openCatalogPicker — synthetic
   «(текущий)»-строка для проекта, скрытого дефолтным статус-фильтром,
   и обычная отметка совпадения с currentVal). */
.task-card__add-prop-option--current {
    font-style: italic;
    opacity: 0.85;
}

/* ── Picker-обёртка (search + list) ──────────────────────────────────
 * Создаётся в card.js::_openCatalogPicker ТОЛЬКО когда нужен поиск
 * (пикер проектов). Содержит sticky-инпут и <ul.task-card__add-prop-menu>;
 * maxHeight ставится inline в JS, внутри — flex-колонка, в которой
 * <ul> занимает оставшееся место и скроллится. Area-пикер идёт по
 * старому пути (без обёртки), старые правила .task-card__add-prop-menu
 * не задеваем — они действуют на _openAddPropMenu и на случай прямого
 * использования <ul> (если кто-то в будущем решит вернуться). */
.task-card__prop-picker {
    position: fixed;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--fg) 18%, transparent);
    font-size: 0.85rem;
    overflow: hidden; /* чтобы скругление border-radius не клипало sticky-header */
}
.task-card__prop-picker .task-card__add-prop-menu {
    /* Внутри обёртки <ul> становится flex-ребёнком: занимает
       оставшееся место, скроллится через overflow-y:auto. */
    flex: 1 1 auto;
    min-height: 0;
    /* Inline-JS ставит menu.style.maxHeight в старом пути — обнуляем
       здесь, обёртка теперь задаёт вертикальный предел. */
    max-height: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
}
.task-card__prop-picker-search {
    position: sticky;
    top: 0;
    background: var(--bg-elevated);
    padding: 0.3rem 0.4rem;
    border-bottom: 1px solid var(--border);
    z-index: 1;
    flex: 0 0 auto;
}
.task-card__prop-picker-search input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg);
    color: var(--fg);
    font-size: 0.85rem;
}
.task-card__prop-picker-separator {
    height: 1px;
    background: var(--border);
    margin: 0.25rem 0;
    padding: 0;
    list-style: none;
    cursor: default;
}

/* ── Picker задачи для «+ Добавить связь» ─────────────────────────────
   Структурно повторяет .task-card__prop-picker (search-input + скролл-
   список), но под собственным именем класса — два независимых сценария
   (project-picker vs task-link-picker) не должны делить правила, иначе
   правка в одном сломает другой. Контракт по виду ровно такой же:
   fixed-попап под кнопкой «+ Добавить связь» карточки задачи. */
.task-card__add-link-picker {
    position: fixed;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 8px 24px color-mix(in srgb, var(--fg) 18%, transparent);
    font-size: 0.85rem;
    overflow: hidden;
}
.task-card__add-link-picker > .task-card__add-prop-menu {
    /* Внутри обёртки <ul> — flex-ребёнок: занимает оставшееся место
       и скроллится через overflow-y:auto (как в .task-card__prop-picker). */
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
}
.task-card__add-link-picker-search {
    position: sticky;
    top: 0;
    background: var(--bg-elevated);
    padding: 0.3rem 0.4rem;
    border-bottom: 1px solid var(--border);
    z-index: 1;
    flex: 0 0 auto;
}
.task-card__add-link-picker-search input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg);
    color: var(--fg);
    font-size: 0.85rem;
}
/* Подсветка строки при клавиатурной навигации (↑/↓ + Enter в picker'е).
   На hover тот же .task-card__add-prop-option:hover — нам не нужен
   дубликат. --active нужен отдельно, потому что hover на мышке и
   подсветка стрелками — разные источники и должны работать одновременно. */
.task-card__add-link-picker-option--active {
    background: color-mix(in srgb, var(--accent) 18%, var(--bg-elevated));
    color: var(--accent);
}
.task-card__add-link-picker-empty {
    color: var(--muted);
    font-style: italic;
    cursor: default;
    text-align: center;
}

/* ── Description ─────────────────────────────────────────────────────── */

.task-card__description-section {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.task-card__section-title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
}

.task-card__description {
    /* Пустая область без подсветки — тот же div и для пустого, и для
       заполненного описания. Никакого «Описание не задано», рамки или
       фона: наводимся → видим лёгкий hover → кликаем → открывается
       textarea (см. js/tasks/card.js::_wireDescriptionEdit). Длинные
       описания не растягивают всю карточку — выше DESCRIPTION_MAX_HEIGHT
       (тот же порог, что у textarea в режиме редактирования) включается
       собственная прокрутка. */
    margin: 0;
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--fg);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    padding: 0.45rem 0.55rem;
    min-height: 1.5em;
    max-height: 360px;
    overflow-y: auto;
    border-radius: 6px;
    cursor: text;
    transition: background-color 0.12s ease;
}

.task-card__description:hover {
    /* Тонкая подсветка при наведении намекает, что область кликабельна.
       Используем color-mix от --fg, чтобы работало на всех темах без
       отдельного примитива. */
    background-color: color-mix(in srgb, var(--fg) 6%, transparent);
}

.task-card__description a {
    /* Те же токены, что у .markdown a — ссылки внутри описания открываются
       в новой вкладке (см. data-click="edit-description" в card.js), не
       запуская режим редактирования. */
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.task-card__description-input {
    /* Растёт вместе с вводом до DESCRIPTION_MAX_HEIGHT (см. autoGrow в
       js/{tasks,rules,templates}/card.js), дальше — своя прокрутка.
       Ручной resize запрещён: высота управляется только контентом. */
    display: block;
    width: 100%;
    box-sizing: border-box;
    margin: 0;
    font: inherit;
    color: var(--fg);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.45rem 0.55rem;
    font-family: var(--font-base);
    line-height: 1.5;
    resize: none;
    overflow-y: hidden;
}

.task-card__description-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

/* ── Links ───────────────────────────────────────────────────────────── */

.task-card__links {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}

.task-card__link-group {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.task-card__link-title {
    margin: 0;
    font-size: 0.74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
}

/* Заголовок группы связей (Связана / Блокирует / Зависит от) — слева
   от текста стоит иконка типа (LINK_RELATES / WARN_BLOCKS / LOCK_BLOCKED).
   WARN_BLOCKS — стрелка, упирающаяся в стену (3 тонких штриха в том
   же Lucide-стиле, что и остальные иконки сайта). Тот же SVG, что на
   планке кубика, цвет через семантический токен
   (--icon-link-relates / --icon-blocks / --icon-blocked). */
.task-card__link-title--group {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    line-height: 1;
}

.task-card__link-list {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.task-card__link-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    padding: 0.35rem 0.55rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 0.85rem;
}

/* Иконка слева от названия связанной задачи — единый визуальный
   язык с планкой кубика (cube.js::__icon). Цвет — через семантические
   токены theme.css (--icon-link-relates / --icon-blocked / --icon-blocks),
   чтобы иконка одинаково читалась во всех трёх темах. */
.task-card__link-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 16px;
    height: 16px;
}
.task-card__link-icon svg {
    width: 14px;
    height: 14px;
    display: block;
}
.task-card__link-icon--relates {
    color: var(--icon-link-relates);
}
.task-card__link-icon--blocks {
    color: var(--icon-blocks);
}
.task-card__link-icon--depends_on {
    color: var(--icon-blocked);
}

.task-card__link-row > .task-card__link-title {
    flex: 1;
    min-width: 0;
    color: var(--fg);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 500;
    overflow-wrap: anywhere;
    cursor: pointer;
}

.task-card__link-row > .task-card__link-title:hover {
    color: var(--accent);
}

/* Связь, чью target-задачу не удалось зарезолвить (теоретически
   возможно если link указывает на удалённую/не существующую в кэше
   задачу). Приглушённый цвет + курсив, чтобы пользователь видел, что
   строка «(удалена)» — это не обычная связанная задача. */
.task-card__link-row > .task-card__link-title--missing {
    color: var(--muted);
    font-style: italic;
    cursor: default;
}

.task-card__link-remove {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    padding: 0.1rem 0.3rem;
    border-radius: 4px;
    font-size: 0.95rem;
    line-height: 1;
    flex-shrink: 0;
    transition: color 0.15s, background 0.15s;
}

.task-card__link-remove:hover {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, transparent);
}

/* ── Click-to-edit input (js/shared/click_to_edit.js) ──────────────────
   Без стилей input получает дефолтный браузерный вид (inset-рамка, белый
   фон, Arial) — резко выбивается из темы. Используем те же семантические
   токены, что и сам заголовок/карточка, чтобы редактирование «сливалось»
   с остальным текстом, а не выглядело как попап. */

.click-to-edit__input {
    width: 100%;
    background: var(--bg);
    color: var(--fg);
    border: 1.5px solid var(--accent);
    border-radius: 4px;
    font: inherit;
    font-weight: 700;
    line-height: 1.35;
    padding: 0.2rem 0.35rem;
    margin: -0.2rem -0.35rem;
    /* Внутри карточки заголовок крупнее (1.05rem) — на других страницах
       поле может быть обычного размера; font:inherit + размер контейнера
       решают это автоматически. */
    box-sizing: border-box;
    outline: none;
}

.click-to-edit__input:focus {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}

.click-to-edit__input::placeholder {
    color: var(--muted);
}

/* Wrapping variant (attachClickToEdit({wrap: true})) — a <textarea> used
   for single-line values (titles) that should still word-wrap across
   several lines like the static text does, instead of scrolling
   horizontally in a single-line <input>. Height is auto-grown in JS. */
textarea.click-to-edit__input {
    display: block;
    resize: none;
    overflow-wrap: anywhere;
}

/* Курсор-«редактируемо» на статичном (не-edit) состоянии — единый
   affordance для всех click-to-edit элементов сайта. */
.click-to-edit--editable { cursor: text; }
.click-to-edit--editable:hover {
    background: color-mix(in srgb, var(--accent) 6%, transparent);
    border-radius: 4px;
}

/* ── Priority buttons inside the card ────────────────────────────────── */

.task-card__prio {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    padding: 0.25rem 0.55rem;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.78rem;
    line-height: 1;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.task-card__prio:hover {
    color: var(--fg);
    border-color: var(--fg);
}

.task-card__prio:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.task-card__prio--active {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    border-color: var(--accent);
    color: var(--accent);
    font-weight: 600;
}

/* Rule.status has a 4th value ("completed") the task status-dot rules
   don't cover — same success token as "done" (both read as "finished"). */
.task-card__status-mount-target[data-current-status="completed"] .ui-dropdown__trigger {
    border-left-color: var(--success);
}

/* ── RuleCard — recurrence editor, templates (js/rules/card.js) ───────────
   Reuses `.task-card__*` for every structural element shared with
   TaskCard (header, props, description, add-prop button) — only the
   recurrence editor and template chips below are rule-specific. */

.rule-card__rec-editor {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.rule-card__rec-types {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.rule-card__rec-type-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    border-radius: 6px;
    padding: 0.25rem 0.55rem;
    font-size: 0.78rem;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.rule-card__rec-type-btn:hover {
    border-color: color-mix(in srgb, var(--accent) 50%, var(--border));
}
.rule-card__rec-type-btn--active {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    border-color: var(--accent);
    color: var(--accent);
    font-weight: 600;
}

.rule-card__rec-weekdays {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}
.rule-card__rec-weekday {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    border-radius: 5px;
    padding: 0.2rem 0.5rem;
    font-size: 0.76rem;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.rule-card__rec-weekday--on {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    border-color: var(--accent);
    color: var(--accent);
    font-weight: 600;
}

.rule-card__rec-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.rule-card__rec-inline-label {
    color: var(--muted);
    font-size: 0.82rem;
}
.rule-card__rec-n-input {
    width: 4rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 5px;
    color: var(--fg);
    padding: 0.2rem 0.4rem;
    font-size: 0.85rem;
}

.rule-card__rec-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    font-size: 0.78rem;
    color: var(--muted);
}
.rule-card__rec-meta-item {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}
.rule-card__rec-meta-item svg { flex-shrink: 0; }

.rule-card__rec-remove {
    align-self: flex-start;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    cursor: pointer;
    width: 1.75rem;
    height: 1.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
    line-height: 1;
    border-radius: 6px;
    transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.rule-card__rec-remove:hover {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 8%, transparent);
}

/* Mount point for the trigger's "Куда положить" UIDropdown (rules/card.js
   ::_wireTriggerDestDropdown) — same min-height reservation as
   .task-card__status-mount, so the row doesn't collapse before dropdown.js
   loads and the menu mounts. */
.rule-card__trigger-dest-mount {
    min-height: 1.8rem;
}

.rule-card__template-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

/* Каждый чип — обёртка вокруг TemplateCube.html, ужатая под ширину
   карточки (обычные .cube на странице шаблонов рассчитаны на сетку). */
.rule-card__template-chip {
    width: 100%;
    cursor: pointer;
}
.rule-card__template-chip .cube {
    width: 100%;
}

.rule-card__template-missing {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.4rem 0.6rem;
    color: var(--muted);
    font-style: italic;
}

/* TaskCard's read-only «Шаблон» chip (js/tasks/card.js::_templateHTML) —
   same TemplateCube shell as .rule-card__template-chip, minus the
   delete button: a task's template_id is immutable, there's nothing to
   detach, so the (otherwise inert) "×" from TemplateCube.html is hidden
   rather than left clickable-but-dead. */
.task-card__template-chip {
    width: 100%;
    cursor: pointer;
}
.task-card__template-chip .cube {
    width: 100%;
}
.task-card__template-chip .cube__action--delete {
    display: none;
}

.rule-card__template-remove {
    background: transparent;
    border: none;
    color: var(--muted);
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0.1rem 0.3rem;
}
.rule-card__template-remove:hover { color: var(--danger); }

/* «+ Шаблон» via attachInlineAdd (js/shared/inline_add.js) — reuse the
   add-prop button look for the trigger, plain input for the editing
   state. */
.rule-card__add-template__btn {
    background: transparent;
    border: 1px dashed var(--border);
    border-radius: 6px;
    color: var(--muted);
    font: inherit;
    font-size: 0.82rem;
    padding: 0.4rem 0.6rem;
    width: 100%;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.rule-card__add-template__btn:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.rule-card__add-template__input {
    width: 100%;
    box-sizing: border-box;
    background: var(--bg);
    border: 1px solid var(--accent);
    border-radius: 6px;
    color: var(--fg);
    padding: 0.4rem 0.6rem;
    font-size: 0.85rem;
}

/* ── Drag and drop visuals ────────────────────────────────────────────── */

.dnd-ghost {
    opacity: 0.4;
    border-style: dashed;
    border-color: var(--accent);
}

.dnd-drop-target {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    background: color-mix(in srgb, var(--accent) 7%, var(--bg-elevated));
}

/* ── Capacity plank (calendar day X/Y badge) ──────────────────────────── */

.capacity-plank {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.78rem;
    font-weight: 600;
    padding: 0.1rem 0.45rem;
    border-radius: 5px;
    line-height: 1.4;
    background: color-mix(in srgb, var(--capacity-full) 14%, var(--bg-elevated));
    color: var(--capacity-full);
}

.capacity-plank__icon {
    display: inline-flex;
    align-items: center;
    line-height: 0;
    opacity: 0.85;
}
.capacity-plank__icon svg { display: block; }

.capacity-plank--overfull {
    color: var(--capacity-overfull);
    background: color-mix(in srgb, var(--capacity-overfull) 18%, var(--bg-elevated));
}

.capacity-plank--full {
    color: var(--capacity-full);
    background: color-mix(in srgb, var(--capacity-full) 14%, var(--bg-elevated));
}

.capacity-plank--underfull {
    color: var(--capacity-underfull);
    background: color-mix(in srgb, var(--capacity-underfull) 18%, var(--bg-elevated));
}

/* Ячейка вместимости: три кнопки в одну линию. Шаговые ± по умолчанию
   скрыты; при входе в режим редактирования (.is-editing на контейнере)
   появляются по бокам плашки и образуют с ней единый inline-контрол. */
.capacity-plank-cell {
    display: inline-flex;
    align-items: stretch;
    gap: 0;             /* шаговые кнопки вплотную к плашке */
    line-height: 0;
}

/* Шаговые ±: скрыты в обычном режиме. При .is-editing на родителе —
   показываются, тянутся по высоте плашки, скругляются с внешних сторон. */
.capacity-plank__step {
    display: none;
    align-items: center;
    justify-content: center;
    min-width: 1.4rem;
    padding: 0 0.35rem;
    font-size: 0.9rem;
    font-weight: 700;
    line-height: 1;
    color: var(--muted);
    background: var(--bg-elevated-2);
    border: 1px solid var(--border);
    cursor: pointer;
    user-select: none;
}
.capacity-plank-cell.is-editing > .capacity-plank__step { display: inline-flex; }
.capacity-plank__step:hover {
    color: var(--fg);
    background: color-mix(in srgb, var(--fg) 6%, var(--bg-elevated-2));
}

/* В режиме редактирования подпись переключается между "Hч" и "H:30ч" на
   каждый клик ±30 мин — без фиксированной ширины число разрядов меняется
   и вся капсула (вместе с кнопкой «+») дёргается по горизонтали под
   курсором. Ширина подобрана под "hh:mmч" (двузначные часы) — самый
   длинный вид, который встречается в практике — и держится постоянной
   на всё время редактирования, а не только на момент клика. */
.capacity-plank-cell.is-editing .capacity-plank__text {
    display: inline-block;
    min-width: 5.5ch;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* Когда вся капсула активна — скругляем внешние углы и убираем внутренние
   границы, чтобы плашка и ± выглядели как один контрол. */
.capacity-plank-cell.is-editing > .capacity-plank {
    border-radius: 5px 0 0 5px;
    border-right: none;
}
.capacity-plank-cell.is-editing > .capacity-plank__step[data-step="-30"] {
    border-radius: 5px 0 0 5px;
}
.capacity-plank-cell.is-editing > .capacity-plank__step[data-step="30"] {
    border-radius: 0 5px 5px 0;
}

/* ── Calendar page (days grid + no-date section) ─────────────────────── */

.calendar-page {
    /* Flex-колонка вместо grid — чтобы ряд дней мог забрать всё свободное
       место после шапки/no-date/навигации, не раздувая страницу по высоте.
       Сама страница ограничена viewport: вертикальной прокрутки у <main>
       нет, прокрутка задач происходит внутри каждой колонки-дня. */
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    height: 100vh;
    overflow: hidden;
    /* match the shared header padding used by tasks-backlog / tasks-all / rules /
       task-templates: top 4rem to clear the global topbar; horizontal/bottom 1rem */
    padding: 4rem 1rem 1rem;
}

/* На календарной странице шапка (← Задачи / Календарь / табы) лежит в
   flex-колонке с `gap: 0.5rem`. У `.kanban-page-header` глобально
   margin-bottom: 1.5rem — он складывается с gap и раздувает зазор
   между шапкой и «Без даты» до ~2rem. Обнуляем — пусть зазор задаёт
   только gap родителя. */
.calendar-page > .kanban-page-header {
    margin-bottom: 0;
}

.calendar-page__nav {
    /* Единая компактная панель: две стрелки + название окна, без года,
       в русском стиле DD.MM. Ширина — по содержимому, чтобы панель не
       растягивалась на всю строку календаря. */
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg-elevated);
    font-size: 0.82rem;
    width: fit-content;
}
.calendar-page__nav strong {
    font-weight: 600;
    color: var(--muted);
    letter-spacing: 0.02em;
    min-width: 9ch;
    text-align: center;
}

/* Стрелки сдвига окна. Переопределяем базовый `button` (большой + акцентный):
   нужен сдержанный вторичный вид внутри панели. */
.calendar-page__nav button {
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.05rem 0.4rem;
    font-size: 0.82rem;
    line-height: 1;
    min-width: 1.5rem;
}
.calendar-page__nav button:hover {
    color: var(--fg);
    border-color: color-mix(in srgb, var(--fg) 25%, var(--border));
    opacity: 1;
}

/* Суммарные вместимость/размер по всему отображаемому окну (сумма по
   дням) — тот же `.capacity-plank` бейдж, что и у каждого дня, просто
   read-only (см. pages/tasks-calendar.js). Разделитель слева отличает
   его от стрелок/даты внутри той же панели. */
.calendar-page__nav-total {
    margin-left: 0.3rem;
    padding-left: 0.5rem;
    border-left: 1px solid var(--border);
}
.calendar-page__nav-total:empty {
    display: none;
}

.calendar-page__days {
    /* По спеке (tasks_system.md § Страница календарь): 7 колонок-дней
       расположены горизонтально в один ряд; если не помещаются в экран —
       горизонтальная прокрутка. Никаких переходов на новый ряд. */
    flex: 1 1 auto;
    min-height: 0; /* critical: allow flex item to shrink below content size */
    display: flex;
    flex-wrap: nowrap;
    gap: 0.75rem;
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 0.5rem;
}

/* Каждый день — отдельная колонка фиксированной ширины (как у бэклога).
   ListColumn.mount рендерит .kanban__col внутрь этой обёртки; на ней самой
   никакая разметка кубиков не нужна — только flex-контейнер, задающий
   ширину дня в общем ряду и заполняющий всю высоту ряда (для внутренней
   прокрутки задач).

   Ширина 270px подобрана так, чтобы кубик задачи (.cube--task, width:
   220px) полностью помещался по горизонтали внутри дня без overflow:
       270 − 2×0.6rem padding колонки − 1×1px рамка − небольшой зазор
   остаётся достаточно места для кубика + его собственный padding.
   Если 7 колонок не помещаются в окно — включается горизонтальная
   прокрутка на .calendar-page__days (см. спеку). */
.calendar-day-wrap {
    flex: 0 0 270px;
    min-width: 270px;
    height: 100%;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
.calendar-day-wrap > .kanban__col {
    flex: 1 1 auto;
    width: 100%;
    min-height: 0;
    overflow: hidden;
}

/* Прокрутка задач внутри дня. Селектор `.calendar-day .kanban__cards` —
   чтобы не задеть .kanban__cards на других страницах (там своё поведение).
   `min-height: 0` обязателен в flex-цепочке, иначе контейнер не сможет
   ужаться ниже размера содержимого и overflow не сработает. */
.calendar-day .kanban__cards {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

.calendar-page__empty {
    color: var(--muted);
    font-style: italic;
    padding: 1.5rem 0;
    text-align: center;
}

.calendar-day {
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-elevated);
    padding: 0.6rem;
    min-height: 160px;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* Текущий день недели — акцентная рамка + лёгкая подсветка заголовка, чтобы
   день сразу бросался в глаза среди 7 колонок. */
.calendar-day--today {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
}

.calendar-day--today .kanban__col-title {
    color: var(--accent);
}

.calendar-day__capacity-toggle {
    /* Не задаём background здесь — заливку определяет .capacity-plank /
       .capacity-plank--{state} (та же специфичность класса, но объявлены
       раньше в файле; `background: inherit` тут раньше перетирал их заливку
       порядком объявления, оставляя от плашки только цветной текст). */
    cursor: pointer;
    border: 1px solid transparent;
    padding: 0.1rem 0.4rem;
    font-weight: 600;
}

.calendar-day__capacity-toggle:hover {
    border-color: color-mix(in srgb, var(--capacity-full) 30%, transparent);
}

/* Старые стили .calendar-day__capacity-editor / __step / __current удалены —
   редактор теперь встроен в .capacity-plank-cell, см. блок выше. */

.calendar-day__capacity-bar {
    height: 4px;
    background: var(--bg-elevated-2);
    border-radius: 2px;
    overflow: hidden;
}

.calendar-day__capacity-fill {
    display: block;
    height: 100%;
    background: var(--accent);
    transition: width 0.2s;
}

.calendar-no-date-section {
    /* Отступ сверху задаёт `gap: 1rem` у .calendar-page (flex-контейнер
       страницы) — добавлять свой margin-top не нужно, иначе отступ между
       шапкой и «Без даты» визуально удваивается (~2.5rem вместо 1rem). */
}

.calendar-no-date__title {
    /* Зазор между подписью «Без даты» и лентой кубиков задаёт row-gap
       самого grid-а (.calendar-no-date { row-gap: 0.15rem }), так что
       свой margin-bottom не нужен. Нулевой margin-top — обязательно:
       дефолтный <h3> margin-top = 1em × font-size (~18px), и внутри
       grid-родителя он НЕ схлопывается с padding-top артикла, добавляя
       ~30px пустого пространства сверху секции. */
    margin-top: 0;
    margin-bottom: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

/* «Без даты» — горизонтальная лента кубиков (страница «Календарь»).
   ListColumn создаёт DOM: <h3.headerReplace>, <.kanban__add-area>,
   <.kanban__cards>. Чтобы кнопка «+ Задача» жила СЛЕВА от ленты
   (а не сверху над ней), превращаем сам .kanban__col в 2-колоночный
   grid: первый ряд — заголовок на всю ширину, второй ряд — слева
   «псевдо-кубик» добавления, справа flex-лента кубиков с горизонтальным
   скроллом. Ширина псевдо-кубика совпадает с .cube--task (220px),
   высота — как у пустого кубика (~6rem ≈ 96px), визуально он отличается
   пунктирной рамкой и приглушённым цветом, чтобы не путать с реальной
   задачей. Когда пользователь кликает — input «+ Задача» того же
   размера появляется на его месте. */
.calendar-no-date {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto 1fr;
    column-gap: 0.5rem;
    row-gap: 0.15rem;
    align-items: start;
    min-width: 0;
}
.calendar-no-date > .calendar-no-date__title {
    grid-column: 1 / -1;
    grid-row: 1;
}
.calendar-no-date > .kanban__add-area {
    grid-column: 1;
    grid-row: 2;
    /* Сам add-area не рисует ничего видимого — только кнопку/инput. */
    width: 220px;
}
.calendar-no-date > .kanban__cards {
    grid-column: 2;
    grid-row: 2;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    gap: 0.5rem;
    /* Минимальная высота как у обычных колонок (1 кубик) — чтобы пустая
       секция не схлопывалась и в неё всё ещё можно было перетаскивать. */
    min-height: 6rem;
}

/* Кубики задач в горизонтальной ленте «Без даты» имеют фиксированную
   ширину 220px (.cube--task). По умолчанию flex-айтемы сжимаются
   (flex-shrink: 1), и при нехватке места кубик бы ужался ниже 220px,
   ломая разметку. Запрещаем shrink — при переполнении лента скроллится
   горизонтально, как и задумано. */
.calendar-no-date .kanban__cards > .cube {
    flex: 0 0 220px;
}

/* Скроллбар у горизонтальной ленты — тонкий, чтобы не съедать высоту
   кубика. На WebKit-браузерах кастомизируем через ::-webkit-scrollbar. */
.calendar-no-date .kanban__cards::-webkit-scrollbar {
    height: 6px;
}
.calendar-no-date .kanban__cards::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 3px;
}
.calendar-no-date .kanban__cards::-webkit-scrollbar-thumb:hover {
    background: var(--muted);
}

/* «+ Задача» в виде псевдо-кубика слева от ленты «Без даты».
   Размеры и форма совпадают с .cube / .cube--task; пунктирная рамка и
   приглушённый цвет отличают его от настоящего кубика задачи, чтобы
   пользователь сразу видел: «это действие, не задача». На hover —
   подсветка акцентом как у настоящего кубика (см. .cube:hover). */
.calendar-no-date .kanban__add-area > .kanban__add-btn {
    width: 100%;
    height: 6rem;
    background: var(--bg-elevated);
    border: 1px dashed var(--border);
    border-radius: 8px;
    color: var(--muted);
    font-size: 0.9rem;
    font-weight: 600;
    padding: 0.55rem 0.65rem 0.6rem;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background-color 0.15s,
                box-shadow 0.15s;
}
.calendar-no-date .kanban__add-area > .kanban__add-btn:hover {
    border-color: var(--accent);
    /* border-style НЕ трогаем — пунктир остаётся пунктиром, чтобы кнопка
       визуально отличалась от настоящего кубика задачи даже в hover. */
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 4%, var(--bg-elevated));
    box-shadow: 0 2px 8px color-mix(in srgb, var(--fg) 10%, transparent);
}

/* Когда пользователь кликнул по «+ Задача» — на месте кнопки появляется
   input того же размера (220×6rem), чтобы высота ленты не «прыгала» при
   переключении button ↔ input. */
.calendar-no-date .kanban__add-area > .kanban__add-input {
    width: 100%;
    height: 6rem;
    padding: 0.55rem 0.65rem 0.6rem;
    background: var(--bg-elevated);
    border: 1px solid var(--accent);
    border-radius: 8px;
    color: var(--fg);
    font-size: 0.9rem;
    font-family: inherit;
    resize: none;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 25%, transparent);
}
.calendar-no-date .kanban__add-area > .kanban__add-input:focus {
    outline: none;
}

/* Счётчик «#N» в шапке «Без даты» — стиль берётся из общего класса
   .kanban__col-count (пилюля), дополнительно — небольшой отступ слева,
   чтобы не лип к подписи «Без даты». */
.calendar-no-date__count {
    margin-left: 0.3rem;
}

/* Контейнер «левого» блока заголовка: подпись «Без даты» + пилюля
   суммарного времени + счётчик #N. Без неё inline-flex у h3
   распихивает дочерние элементы по разным углам из-за space-between. */
.calendar-no-date__title-left {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

/* Суммарный размер задач в «Без даты» — общий бейдж `.capacity-plank`
   (js/shared/capacity_plank.js), тот же что и на «Задачи»/«Главная».
   Буква «ч» — строчная, чтобы не ломать типографскую норму: по умолчанию
   .kanban__col-title (h3) ставит text-transform: uppercase на всё
   внутри, включая вложенный бейдж; без override «ч» рендерился бы «Ч». */
.calendar-no-date__title .capacity-plank__text { text-transform: none; }

/* Кнопка сворачивания «Без даты» — иконка-шеврон прямо рядом с текстом
   «Без даты» (не отдельная текстовая кнопка). Разворот — поворот шеврона
   на 180°, тело секции прячется через `.is-collapsed` ниже (CSS-only, без
   пере-запроса данных). */
.calendar-no-date__collapse-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 4px;
    color: var(--muted);
    cursor: pointer;
    line-height: 0;
}

@media (hover: hover) {
    .calendar-no-date__collapse-btn:hover {
        color: var(--fg);
        background: color-mix(in srgb, var(--fg) 8%, transparent);
    }
}

.calendar-no-date__collapse-icon {
    display: inline-flex;
    transition: transform 0.15s ease;
}
.calendar-no-date__collapse-icon svg { display: block; }

.calendar-no-date__collapse-btn[aria-expanded="true"] .calendar-no-date__collapse-icon {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .calendar-no-date__collapse-icon { transition: none; }
}

/* Свёрнутое состояние — прячем тело (add-area + лента кубиков), заголовок
   остаётся. Строка грид-а схлопывается сама (нет контента), доп. отступов
   убирать не нужно. */
.calendar-no-date.is-collapsed > .kanban__add-area,
.calendar-no-date.is-collapsed > .kanban__cards {
    display: none;
}

.calendar-no-date__actions {
    display: flex;
    gap: 0.3rem;
}

/* Иконка-метёлка, а не текстовая кнопка — та же квадратная icon-button
   форма, что и у `.calendar-no-date__collapse-btn` рядом (единый вид
   мелких действий в шапке колонки). */
.calendar-no-date__clear-done {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    padding: 0;
    cursor: pointer;
    background: none;
    border: 1px solid var(--border);
    border-radius: 5px;
    color: var(--muted);
    line-height: 0;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}

/* Тот же приём, что и у остальных «очистить»-действий на сайте
   (task-detail__clear-deadline, project-section__clear-deadline,
   auth-overlay__clear) — hover уходит в --danger, а не в --fg. */
.calendar-no-date__clear-done:hover {
    color: var(--danger);
    border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
    background: color-mix(in srgb, var(--danger) 8%, transparent);
}

/* ── Filters row (shared module — see BACKLOG / ALL TASKS pages) ──────── */

.filters {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    /* На «Все задачи» этот блок — flex-item в .tasks-all__toolbar (родитель
       flex-row). flex-basis: 0 (а не auto!) — иначе браузер использует
       полную «естественную» ширину нескроллящегося ряда фильтров ещё ДО
       сжатия при решении, помещаются ли элементы в строку, и переносит
       .tasks-all__actions (Колонки / Удалить) на отдельную строку даже
       когда места достаточно. min-width:0 позволяет сжиматься дальше —
       .filters-bar внутри сам скроллится по горизонтали при нехватке места. */
    flex: 1 1 0;
    min-width: 0;
}

.filters__row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}

.filters__chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.7rem;
    font-size: 0.82rem;
    font-weight: 500;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 999px;
    cursor: pointer;
    user-select: none;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.filters__chip:hover { border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); }

.filters__chip--active {
    background: color-mix(in srgb, var(--accent) 18%, var(--bg-elevated));
    color: var(--accent);
    border-color: var(--accent);
    font-weight: 600;
}

/* ── Filters bar (dropdown widgets — see BACKLOG / ALL TASKS pages) ────
   Compact horizontal row of dropdown triggers, one per filter definition.
   Each trigger reads «Label: <summary>». Menu is a small panel below the
   trigger with checkboxes for multi-select and radios for single-select.
*/

/* .filters-bar is a bordered "frame" — purely a visual contour so the row
   reads as its own scrollable widget instead of looking like it's just
   been carelessly clipped. The actual overflow-x:auto element is the
   nested .filters-bar__scroll, which also carries the edge fade as a
   mask-image (see .filters-bar--has-left / --has-right below) toggled by
   _updateFades in js/shared/filters.js. */
.filters-bar {
    position: relative;
    /* Фиксированная высота — совпадает с .tasks-all__btn--icon (Колонки /
       Удалить), чтобы весь ряд тулбара на «Все задачи» был выровнен по
       единой высоте. .filters-bar__scroll стретчится на 100% и центрирует
       чипы через align-items — им не нужен собственный вертикальный отступ.
       42px, не 40 — 40 когда-то было впритык к реальной высоте чипа
       (~35px) и на некоторых движках/шрифтах чип обрезался снизу; 2px
       запаса достаточно раз скроллбар больше не отъедает высоту (см.
       .filters-bar__scroll ниже — он спрятан, скролл работает и без него,
       подсказку про прокрутку даёт mask-fade самого .filters-bar__scroll). */
    height: 42px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg);
    /* Без этого квадратные углы .filters-bar__scroll вылезали за
       скруглённый угол рамки. Безопасно клипать: .filters-bar__menu
       теперь живёт на document.body с position:fixed (см. комментарий в
       .filters-bar__scroll ниже) и не зависит от overflow предков. */
    overflow: hidden;
    /* Свой запас снизу, чтобы визуально отделить фильтры от доски —
       раньше это был margin-bottom: 1.25rem, сейчас эту роль играет
       gap родителя main.tasks-backlog-page (см. блок выше). */
    margin-bottom: 0;
    min-width: 0;
}

.filters-bar__scroll {
    display: flex;
    /* flex-wrap: nowrap — заставляет фильтры жить одной строкой даже
       когда у чипов длинные значения (например, длинные названия
       проектов). Раньше overflow здесь оставался visible, потому что
       .filters-bar__menu был position:absolute внутри .filters-bar__group
       и любой overflow (auto/hidden/scroll) обрезал бы выпадающее меню.
       Теперь .filters-bar__menu монтируется на document.body с
       position:fixed (см. js/shared/filters.js, тот же приём что и
       UIDropdown в js/shared/dropdown.js) — меню больше не зависит от
       overflow предков, поэтому сам бар может честно скроллиться по
       горизонтали вместо того, чтобы обрезать чипы, не поместившиеся
       в строку. */
    flex-wrap: nowrap;
    align-items: center;
    height: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    min-width: 0;
    /* Скроллбар спрятан (а не просто "thin") — на движках с классическим
       (не overlay) скроллбаром он занимает место внутри блока и обрезает
       чипы снизу вместо того, чтобы просто лежать под ними. Подсказка
       "тут можно скроллить" уже даёт mask-fade ниже, отдельный
       скроллбар для этого не нужен; колесо/трекпад/свайп продолжают
       работать как обычно. */
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* Фиксированный gap вместо унаследованного (был 0.5rem), чтобы
       сохранить горизонтальные зазоры между чипами. */
    gap: 0.5rem;
    padding: 0 0.6rem;
    box-sizing: border-box;
    /* Плавный переход маски при появлении/исчезновении подсказки на
       границе (см. правила mask-image ниже). */
    transition: -webkit-mask-image 0.15s, mask-image 0.15s;
}
.filters-bar__scroll::-webkit-scrollbar { display: none; }

.filters-bar__row {
    display: contents;
}

/* Edge fade — hints "there's more, scroll me" instead of a hard clip.
   Implemented as mask-image on the scroll element itself (not a
   background-color gradient overlay on a sibling div, which is what this
   used to be): a background-color overlay only dims the chip pill's own
   *background* colour — it barely dims bright white trigger text, since a
   semi-transparent dark layer needs alpha close to 1 to visually darken
   white but far less to darken an already-dark background. That mismatch
   read as "the darkening doesn't reach the edge" (the text stayed crisp
   while the pill behind it visibly dimmed). A mask instead multiplies the
   alpha of the *actual rendered pixels* — text and background fade at the
   same rate because both are just being made more transparent, revealing
   .filters-bar's own opaque background behind rather than imitating it
   with a second color. Toggled by .filters-bar--has-left/--has-right (see
   _updateFades in js/shared/filters.js); 24px fade width, transparent at
   the outer edge fading in to fully opaque. */
.filters-bar--has-left .filters-bar__scroll {
    -webkit-mask-image: linear-gradient(to right, transparent, black 24px);
            mask-image: linear-gradient(to right, transparent, black 24px);
}
.filters-bar--has-right .filters-bar__scroll {
    -webkit-mask-image: linear-gradient(to left, transparent, black 24px);
            mask-image: linear-gradient(to left, transparent, black 24px);
}
.filters-bar--has-left.filters-bar--has-right .filters-bar__scroll {
    -webkit-mask-image: linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), transparent);
            mask-image: linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), transparent);
}

.filters-bar__group {
    display: inline-flex;
    /* Не сжимаем чип внутри одной строки фильтр-бара: каждая группа
       занимает свою естественную ширину; если не помещается —
       появляется горизонтальный скролл у .filters-bar. */
    flex: 0 0 auto;
}

.filters-bar__trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.7rem;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 8px;
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
    user-select: none;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
    white-space: nowrap;
    /* Cap по ширине чипа, чтобы одно длинное значение (например, имя
       проекта) не растягивало весь фильтр-бар на пол-экрана. 260px —
       вмещает обычные имена проектов («Доход 300кк в наносек» с
       ellipsis'ом, см. ниже). min-width: 0 — обязателен, иначе
       inline-flex не позволит детям сжиматься, и overflow не сработает. */
    max-width: 260px;
    min-width: 0;
    overflow: hidden;
}

.filters-bar__trigger:hover { border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); }
.filters-bar__trigger:focus { outline: none; border-color: var(--accent); }
.filters-bar__trigger[aria-expanded="true"] { border-color: var(--accent); }

.filters-bar__trigger-name {
    color: var(--muted);
    font-weight: 600;
    /* Имя («Тип связи», «Проект») — короткое, всегда должно
       помещаться. Не сжимается, чтобы ellipsis не отрезал подпись
       чипа. */
    flex-shrink: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.filters-bar__trigger[aria-expanded="true"] .filters-bar__trigger-name,
.filters-bar__group--active .filters-bar__trigger-name {
    color: var(--accent);
}

.filters-bar__trigger-value {
    color: var(--fg);
    font-weight: 600;
    /* Длинное значение («Доход 300кк в наносек», «Накачаться
       жестко» и т.п.) — единственная часть чипа, которая может
       вырасти. При max-width родителя в 260px и неизменной ширине
       имени+шеврона у значения остаётся конечное место: всё, что не
       помещается, уходит в ellipsis. min-width: 0 нужен, чтобы
       flex-цепочка inline-flex позволила ребёнку сжаться
       (стандартно flex-айтем не сжимается меньше min-content). */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.filters-bar__group--active .filters-bar__trigger {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-elevated));
}

.filters-bar__group--active .filters-bar__trigger-value { color: var(--accent); }

/* Шеврон справа от значения всегда виден целиком, иначе
   пользователю неясно, что чип открывает меню. */
.filters-bar__chevron {
    flex-shrink: 0;
}

.filters-bar__chevron {
    display: inline-flex;
    align-items: center;
    opacity: 0.6;
    transition: transform 0.15s;
}

.filters-bar__trigger[aria-expanded="true"] .filters-bar__chevron {
    transform: rotate(180deg);
    opacity: 1;
}

.filters-bar__menu {
    /* Mounted on document.body; top/left/bottom/max-height are set inline
       by js/shared/filters.js::_positionMenu against the trigger's
       getBoundingClientRect() — position:fixed here is what lets it escape
       .filters-bar's overflow-x:auto instead of being clipped by it. */
    position: fixed;
    z-index: 50;
    min-width: 180px;
    max-width: 280px;
    max-height: 280px;
    overflow-y: auto;
    margin: 0;
    padding: 0.25rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    list-style: none;
    box-shadow: 0 4px 16px color-mix(in srgb, var(--fg) 12%, transparent),
                0 1px 4px color-mix(in srgb, var(--fg) 6%, transparent);
}

/* Sticky search box for long option lists (see js/shared/filters.js's
   SEARCH_MIN_OPTIONS) — a direct child of .filters-bar__menu, which is
   itself the scrolling element, so `position: sticky` needs no extra
   wrapper (unlike js/tasks/card.js's picker, where the <ul> isn't the
   scroller and a wrapper flex-column is needed instead). Negative margin
   cancels the menu's own padding so the sticky bar's background spans
   edge-to-edge instead of leaving the menu's rounded corners peeking
   through on the sides. */
.filters-bar__menu-search {
    position: sticky;
    top: -0.25rem;
    margin: -0.25rem -0.25rem 0.25rem;
    padding: 0.35rem 0.25rem;
    background: var(--bg-elevated);
    border-bottom: 1px solid var(--border);
    z-index: 1;
    list-style: none;
    cursor: default;
}

.filters-bar__menu-search-input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-base);
    font-size: 0.85rem;
    outline: none;
}

.filters-bar__menu-search-input:focus {
    border-color: var(--accent);
}

.filters-bar__menu-search-input::-webkit-search-cancel-button {
    display: none;
}

.filters-bar__menu-empty {
    padding: 0.5rem 0.6rem;
    color: var(--muted);
    font-size: 0.85rem;
    font-style: italic;
    text-align: center;
    cursor: default;
}

.filters-bar__item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.4rem 0.6rem;
    border-radius: 5px;
    font-size: 0.88rem;
    color: var(--fg);
    cursor: pointer;
    user-select: none;
    transition: background 0.1s;
}

.filters-bar__item:hover {
    background: color-mix(in srgb, var(--accent) 10%, var(--bg-elevated));
}

.filters-bar__item--reset {
    color: var(--muted);
    font-style: italic;
    border-bottom: 1px solid var(--border);
    border-radius: 5px 5px 0 0;
    margin-bottom: 2px;
}

.filters-bar__item--reset .filters-bar__item-label { font-style: italic; }
.filters-bar__item--reset:hover { color: var(--fg); }

.filters-bar__item--active {
    color: var(--accent);
    font-weight: 600;
}

.filters-bar__check {
    flex-shrink: 0;
    width: 1rem;
    height: 1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85rem;
    line-height: 1;
    color: var(--accent);
}

.filters-bar__item-label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.filters-bar__reset-all {
    appearance: none;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--muted);
    font: inherit;
    font-size: 0.82rem;
    padding: 0.35rem 0.7rem;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}

.filters-bar__reset-all:hover { color: var(--accent); border-color: var(--accent); }

/* ── Pagination ──────────────────────────────────────────────────────── */

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin-top: 1rem;
    padding: 0.5rem 0;
    color: var(--muted);
    font-size: 0.88rem;
}

.pagination__btn {
    padding: 0.35rem 0.75rem;
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: 6px;
    font: inherit;
    font-size: 0.82rem;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}

.pagination__btn:hover:not(:disabled) {
    border-color: var(--accent);
    color: var(--accent);
}

.pagination__btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* Network banner (tasks_system.md § Errors) */
.network-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 9999;
    background: var(--danger);
    color: var(--danger-fg);
    padding: 0.6rem 1rem;
    text-align: center;
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
}

.network-banner[hidden] { display: none; }

.network-banner__retry {
    border: 1px solid currentColor;
    background: transparent;
    color: inherit;
    border-radius: 5px;
    padding: 0.2rem 0.6rem;
    font-weight: 600;
    cursor: pointer;
}

/* Mobile <900px notice (tasks_system.md § Mobile) */
.mobile-notice {
    position: fixed;
    inset: 0;
    z-index: 9998;
    background: var(--bg);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 2rem;
}

.mobile-notice[hidden] { display: none; }

.mobile-notice__inner {
    max-width: 28rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    color: var(--fg);
}

.mobile-notice__inner strong {
    font-size: 1.1rem;
    color: var(--fg);
}

.mobile-notice__inner p {
    color: var(--muted);
    margin: 0;
}

/* ── Skeleton placeholders ──────────────────────────────────────────── */
.skeleton-stack {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.skeleton-stack--grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 0.5rem;
}

.skeleton {
    background: linear-gradient(90deg, var(--bg-elevated-2) 25%, var(--bg) 50%, var(--bg-elevated-2) 75%);
    background-size: 400% 100%;
    animation: skeleton-shimmer 1.4s infinite;
    border-radius: 8px;
    border: 1px solid var(--border);
}

@keyframes skeleton-shimmer {
    0%   { background-position: 100% 0; }
    100% { background-position: 0 0; }
}

.skeleton--cube { height: 88px; }
.skeleton--rule { height: 110px; }
.skeleton--row { height: 28px; }

/* Admin — Логи page (admin-logs.html): full-height shell (mirrors
 * main.files-page) with a toolbar row and a scrollable monospace tail
 * view below it. */
main.admin-logs-page {
    max-width: 1200px;
    height: 100vh;
    padding: 5rem 1rem 1rem;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.admin-logs-page .page-title {
    flex-shrink: 0;
    margin-bottom: 1rem;
}

.admin-logs-toolbar {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 0.75rem;
}

.admin-logs-toolbar__status {
    font-size: 0.82rem;
    color: var(--muted);
}

.admin-logs-toolbar__spacer {
    flex: 1;
}

.admin-logs-view {
    flex: 1;
    min-height: 0;
    overflow: auto;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.9rem 1.1rem;
    font-family: ui-monospace, "SFMono-Regular", "Menlo", monospace;
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--fg);
    white-space: pre-wrap;
    word-break: break-word;
}

.admin-logs-view:focus {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

@media (max-width: 480px) {
    main.admin-logs-page { padding: 4.5rem 0.75rem 0.75rem; }
    .admin-logs-toolbar { flex-wrap: wrap; }
}
