/* Stage 2 baseline styling: readable and not broken on mobile. Real
   visual design is deliberately deferred to Этап 9 ("полировка"). */

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
    color: #2b2620;
    background: #faf8f4;
    line-height: 1.5;
}

a {
    color: #6b4a2f;
}

.container {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 1rem;
}

/* Header / nav */

.site-header {
    background: #fff;
    border-bottom: 1px solid #e6ddd0;
}

.header-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 1rem;
}

/* Временный тестовый шрифт логотипа — см. DECISIONS.md ("Логотип в шапке:
   тестовый шрифт ChinaCyr"). Лицензия не до конца прояснена, поэтому
   область применения намеренно ограничена одним .brand ниже. */
@font-face {
    font-family: "ChinaCyr";
    src: url("/static/fonts/chinacyr.woff2") format("woff2");
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}

.brand {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-family: "ChinaCyr", system-ui, -apple-system, "Segoe UI", sans-serif;
    font-weight: bold;
    font-size: 1.6rem;
    text-decoration: none;
}

/* Временная тестовая иконка-кот — см. DECISIONS.md ("Иконка-кот в шапке").
   Тот же .brand__icon путь/имя файла заменится на финальный логотип
   позже — код трогать не придётся. */
.brand__icon {
    height: 44px;
    width: auto;
}

/* Content nav (Каталог/Контакты/Афиша) — sits right after the brand,
   left-aligned. .header-actions (below) is the second, unrelated group
   that gets margin-left: auto now; this one no longer needs it. */
.main-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
}

.main-nav > a {
    text-decoration: none;
}

/* "Каталог" — hybrid open behavior: hover on desktop, tap/click on
   touch, same pattern already built and fixed in internal/admin's own
   "Каталог" nav item (see DECISIONS.md — both the sibling-not-child
   structure and the margin: 0 fix below come from there, not
   rediscovered here). The menu <ul> is a SIBLING of <details>, not its
   child — reacting to details[open]/:hover on the shared .nav-catalog
   wrapper via the ~ / descendant combinators, not by nesting the menu
   inside <details> and toggling its display (a closed <details>
   suppresses non-summary content regardless of any author `display`
   override placed on it — see the admin fix for how that was
   confirmed). */
.nav-catalog {
    position: relative;
}

.nav-catalog summary {
    cursor: pointer;
    list-style: none;
}

.nav-catalog summary::-webkit-details-marker {
    display: none;
}

.catalog-menu {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 10;
    /* margin: 0 is load-bearing, not cosmetic — see the admin "Каталог"
       fix in DECISIONS.md. <ul> gets a ~1em block-start margin from the
       UA stylesheet; top: 100% only positions the margin edge, so an
       unreset margin here reopens exactly the dead zone between the
       trigger's bottom edge and the menu's own top edge that this
       structure exists to avoid, and the mouse loses :hover crossing
       it on the way to the menu. The extra padding-top below (1rem
       instead of the old 0.5rem) keeps the same visual gap the removed
       margin used to provide — just inside the hoverable box instead
       of outside it. */
    margin: 0;
    padding: 1rem 0 0.5rem;
    background: #fff;
    border: 1px solid #e6ddd0;
    border-radius: 6px;
    min-width: 200px;
    list-style: none;
}

.nav-catalog details[open] ~ .catalog-menu {
    display: block;
}

/* Desktop/real-pointer only: hovering anywhere in .nav-catalog (the
   trigger or the menu itself, once shown) reveals the menu without
   opening <details> at all. Touch (hover: none) gets no rule here, so
   it falls back entirely to the native tap-to-toggle-[open] behavior
   above, untouched. */
@media (hover: hover) {
    .nav-catalog:hover .catalog-menu {
        display: block;
    }
}

.catalog-menu li {
    padding: 0.15rem 1rem;
}

.catalog-menu ul {
    list-style: none;
    padding-left: 1rem;
    margin: 0.15rem 0;
}

/* Actions group (Поиск/Избранное/Профиль/[Админка]/Корзина) — icons
   instead of text, pushed to the far right; see DECISIONS.md. */
.header-actions {
    display: flex;
    align-items: center;
    gap: 1.1rem;
    margin-left: auto;
}

.header-actions a {
    text-decoration: none;
}

/* Shared footprint for every icon-only control in the group — same
   32px circular touch target .favorite-heart already uses on product
   cards, reused here for consistency rather than a new size. Applies
   to .nav-search's own <summary> too (see below): it's the same kind
   of icon-only control, just not an <a>. */
.header-actions__icon,
.nav-search summary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
}

/* "Служебная" — set apart from the neutral action icons with the same
   soft accent background/text pairing already used for .badge/.notice
   elsewhere on the site, not a new color invented for this one spot. */
.header-actions__admin {
    padding: 0.3rem 0.7rem;
    background: #f1e2d2;
    color: #6b4a2f;
    border-radius: 6px;
    font-weight: 500;
    font-size: 0.9rem;
}

.nav-search summary {
    cursor: pointer;
    list-style: none;
}

.nav-search summary::-webkit-details-marker {
    display: none;
}

/* .nav-search needs its own position: relative too (like .nav-catalog
   above, for the same reason: an absolutely positioned dropdown needs
   a positioned ancestor to resolve top/left against) — this one
   right-aligns instead of left-aligning (so it doesn't run under the
   icons to its right), via right: 0 rather than left: 0. */
.nav-search {
    position: relative;
}

/* Base (mobile-first): drops below the icon, same trick .catalog-menu
   uses (position: absolute, no page push). Always rendered (display:
   flex, set unconditionally) rather than left to <details>'s own
   default of not rendering closed content at all: author CSS overrides
   that default regardless of specificity (it's only ever a
   UA-stylesheet rule), and something that isn't rendered has no
   width/opacity to transition from. Collapsed to width: 0 + opacity: 0
   + pointer-events: none instead — visually and interactively
   equivalent to unrendered, but animatable.

   max-width guards the one case width: 240px alone can't: a phone
   narrow enough that even the right-anchored, below-the-icon flyout
   would run past the left edge of the screen. 100vw minus 2rem isn't
   enough margin on its own — the flyout is anchored to .nav-search's
   own right edge, not the viewport's, and .nav-search isn't the
   rightmost thing in .header-actions (Избранное/Профиль/[Админка]/
   Корзина sit between it and the actual edge). Measured on a 500px
   viewport with every icon present (staff view, "Админка" included —
   the widest that row gets): .nav-search's right edge sits ~268px in
   from the viewport's own right edge, so 18rem (288px) leaves a
   deliberate margin over that, not just the bare 2rem page gutter
   every other absolutely-positioned dropdown in this file uses. See
   DECISIONS.md. */
.nav-search form {
    position: absolute;
    z-index: 10;
    top: 100%;
    right: 0;
    margin-top: 0.5rem;
    display: flex;
    align-items: stretch;
    gap: 0.3rem;
    width: 0;
    max-width: calc(100vw - 18rem);
    padding: 0.3rem;
    overflow: hidden;
    opacity: 0;
    pointer-events: none;
    background: #fff;
    border: 1px solid #e6ddd0;
    border-radius: 6px;
    transition: width 0.18s ease, opacity 0.18s ease;
}

/* <details>'s own open/close toggle (click the icon, no JS) drives
   this — see layout.html. */
.nav-search[open] form {
    width: 240px;
    opacity: 1;
    pointer-events: auto;
}

/* Above ~860px, swap the drop-below for sliding out left in the same
   row instead (top: 50%/transform recenters on the icon's own row
   rather than sitting under it) — what was actually asked for, but
   only where it's measured to fit: at 240px wide, right-anchored on
   the header's own icon, this starts running into "Афиша" somewhere
   around 800–820px (measured — see DECISIONS.md), so 860px keeps a
   real margin rather than sitting exactly on that edge. Below it, the
   base rule above (drops below, no horizontal neighbor to run into)
   is the safer fallback the task itself allows for narrow screens,
   not a compromise unique to this build. */
@media (min-width: 860px) {
    .nav-search form {
        top: 50%;
        margin-top: 0;
        transform: translateY(-50%);
    }
}

.nav-search input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.35rem 0.5rem;
    border: 1px solid #e6ddd0;
    border-radius: 4px;
}

.nav-search button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    padding: 0 0.6rem;
    background: #6b4a2f;
    color: #fff;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

/* Cart item count — total qty across all lines, not the number of
   distinct lines. A new small badge, not a reuse of .badge (that one's
   shaped for stacking under a product card, margin-top and all) or
   .disclaimer-badge (that one's red specifically because it flags a
   legal notice — the wrong color association for an ordinary cart
   count). Brand brown instead, same as the site's primary-action
   color. Pinned to the cart icon's corner the same way
   .disclaimer-badge pins to .social-icon-wrap's — position: relative
   on .header-actions__cart below is that same anchor, not a new
   technique. A future favorites count (not built now — see
   DECISIONS.md) would want the same corner-badge treatment on
   .header-actions__icon rather than this exact class: two badges on
   two visibly different icons (heart vs. cart) can't be confused for
   each other regardless, but the classes shouldn't conflate "cart
   badge" with "any icon badge" either. */
.header-actions__cart {
    position: relative;
}

.cart-count {
    position: absolute;
    top: -0.3rem;
    right: -0.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.1rem;
    height: 1.1rem;
    padding: 0 0.3rem;
    border-radius: 999px;
    background: #6b4a2f;
    color: #fff;
    font-size: 0.75rem;
    font-weight: 600;
}

/* #cart-count is always in the markup now, even at 0 (see
   templates/layout.html) — an htmx out-of-band swap (cartQuickAdd,
   cart.go) needs an existing element with that id to land in,
   including the very first item added to an empty cart. Hidden via
   CSS instead of the earlier {{if .CartCount}} template omission. */
.cart-count--hidden {
    display: none;
}

/* Bonus balance badge, right of the cart icon in .header-actions (part
   2 — see DECISIONS.md). Same visual language as .cart-count above
   (brand-brown pill, white text) but its own standalone element rather
   than a corner-overlay: unlike the cart count, this one shows an
   amount ("150 ₽"), not a one-or-two-digit tally, so it needs real
   width and its own flow position instead of sitting pinned to an
   icon's corner. Entirely omitted from the markup at a zero balance
   (see layout.html) rather than hidden via CSS — there's no
   #cart-count-style htmx out-of-band target that would need an
   always-present element to swap into. */
.header-actions__bonus {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.25rem 0.55rem;
    border-radius: 999px;
    background: #6b4a2f;
    color: #fff;
    font-size: 0.8rem;
    font-weight: 600;
    white-space: nowrap;
}

.header-actions__bonus svg {
    flex-shrink: 0;
}

/* Layout basics */

main {
    padding: 1.5rem 1rem 3rem;
}

.hero {
    padding: 1rem 0 1.5rem;
}

/* background — same soft tan surface .notice/status-pills already use
   elsewhere on the site (see DECISIONS.md), not a new color: enough of
   a step down from body's #faf8f4 that the footer reads as its own
   zone instead of blending into the page. padding-top is generous on
   purpose — .container's own bottom padding above (main, 3rem) already
   separates the footer from the last page content, this is the space
   *inside* the footer band, between its top border and the columns,
   which is what actually read as "flush" before. */
.site-footer {
    background: #f1e2d2;
    border-top: 1px solid #e6ddd0;
    padding: 2.5rem 1rem 2rem;
    font-size: 0.9rem;
    color: #6b6355;
}

/* Logo + brand name, same file/text as the header .brand — see the
   comment on it in layout.html. Small (24-28px) since this is a
   secondary mark, not the page's primary logo placement. */
.footer-brand {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.6rem;
}

.footer-brand__icon {
    height: 28px;
    width: auto;
}

.footer-brand__name {
    font-weight: bold;
    font-size: 1.05rem;
    color: #201c17;
}

/* Three columns (описание+документы / контакты / соцсети) on desktop,
   one column stacked in source order (1 → 2 → 3) below 700px — same
   mobile-first grid + breakpoint .catalog-layout/.profile-layout
   already use elsewhere in this file, not a new responsive approach
   invented for the footer specifically. minmax(0, 1fr), not a bare
   1fr, for the same reason as those: a track's content (e.g. a long
   address) must not be allowed to overflow and stretch the column
   past its share of the row. gap is 2.5rem on every axis, including
   the row-gap used once columns stack on mobile — the extra room
   between stacked sections reads as separation there too, not just
   unwanted whitespace. */
.footer-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 2.5rem;
}

@media (min-width: 700px) {
    .footer-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

/* Shorthand `margin` here resets all four sides — a more specific rule
   trying to set only margin-top on a <p> inside .footer-col (e.g.
   .footer-disclaimer, below) loses to this one on specificity alone if
   it isn't made more specific itself (class+tag beats a single class —
   see .footer-col p.footer-disclaimer). This bit the disclaimer's
   margin-top for three edits in a row before it was caught via
   DevTools; see DECISIONS.md. Renamed from .footer-info when the
   footer went from one column to three — same rule, every column now,
   not just the first. */
.footer-col p {
    margin: 0 0 0.5rem;
}

.footer-info__title {
    margin: 0.5rem 0 0.5rem;
    font-size: 0.95rem;
    color: #201c17;
}

/* Документы list — one link per line (not the old inline " · "
   row), own column now that the footer is three-wide instead of one
   long stack, so it no longer needs to compete with the tagline
   paragraph above it for width. */
.footer-legal-links {
    list-style: none;
    margin: 0 0 0.5rem;
    padding: 0;
    font-size: 0.8rem;
}

.footer-legal-links li {
    margin: 0 0 0.4rem;
}

/* Copyright row — under a divider, below all three columns. Reuses
   #cbb9a4 (already the site's muted border/disabled tone — see
   .button--outline, button:disabled) rather than the header border's
   #e6ddd0, which sits too close to .site-footer's own #f1e2d2
   background to read clearly as a line. */
.footer-bottom {
    margin-top: 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid #cbb9a4;
}

.footer-copyright {
    margin: 0;
    font-size: 0.8rem;
    color: #7a6b55;
}

/* One phone/address/hours line — icon, then the value. Shared shape
   for the footer and /contacts; see templates/partials.html,
   "contact-phone"/"contact-address"/"contact-hours". */
.contact-line {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0 0 0.3rem;
}

.contact-line svg {
    flex-shrink: 0;
    color: #6b4a2f;
}

.copy-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.15rem;
    border: none;
    border-radius: 4px;
    background: none;
    color: #6b4a2f;
    cursor: pointer;
}

.copy-icon:hover {
    background: #e6ddd0;
}

.copy-icon__done {
    display: none;
}

.copy-icon.copied .copy-icon__default {
    display: none;
}

.copy-icon.copied .copy-icon__done {
    display: inline-flex;
    color: #2f7a3f;
}

.footer-social {
    display: flex;
    gap: 0.6rem;
}

/* Extra breathing room above the icon row specifically where it now
   sits — under the "Мы в соцсетях" heading in its own footer column
   (see layout.html) — without adding it to the bare .footer-social
   used standalone on /contacts, whose spacing wasn't part of this fix. */
.footer-col .footer-social {
    margin-top: 0.5rem;
}

.social-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: #e6ddd0;
    color: #6b4a2f;
}

.social-icon svg {
    width: 22px;
    height: 22px;
}

/* Wraps one icon so its disclaimer badge (if any) can be positioned
   absolutely in the corner without affecting the row's flex layout —
   the wrapper itself takes up exactly the icon's own footprint. */
.social-icon-wrap {
    position: relative;
    display: inline-flex;
}

.disclaimer-badge {
    position: absolute;
    top: -5px;
    right: -5px;
    width: 17px;
    height: 17px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: #a3342f;
    color: #fff;
    font-size: 0.75rem;
    font-weight: 700;
    line-height: 1;
    cursor: help;
}

/* The footnote/inline-text counterpart of .disclaimer-badge — same
   red, same "*", so the pairing between an icon and its explanation
   reads as one unit rather than two unrelated things. */
.disclaimer-mark {
    color: #a3342f;
    font-weight: 700;
}

/* .footer-col p.footer-disclaimer, not just .footer-disclaimer:
   specificity 0-2-1 vs. .footer-col p's 0-1-1 — has to unambiguously
   outrank that rule's `margin` shorthand (which resets margin-top to 0
   for every <p> in .footer-col, this one included), regardless of
   source order. A plain .footer-disclaimer (0-1-0) loses that fight
   every time. See the comment on .footer-col p above and DECISIONS.md. */
.footer-col p.footer-disclaimer {
    margin: 0.8rem 0 0;
    font-size: 0.8rem;
}

/* No equivalent conflict here — nothing else in this file targets a
   <p> via a "parent-class + p" combinator the way .footer-col p does,
   and .disclaimer-note isn't inside .footer-col on /contacts, so its
   own class selector alone already wins. Checked, not assumed — see
   DECISIONS.md. */
.disclaimer-note {
    max-width: 480px;
    margin-top: 0.8rem;
    font-size: 0.85rem;
    color: #7a6b55;
}

.contacts-map {
    margin: 1rem 0;
    max-width: 640px;
}

.contacts-map iframe {
    display: block;
    border-radius: 6px;
}

/* Category tiles */

.category-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 0.75rem;
}

.category-tile {
    display: block;
    padding: 1rem;
    background: #fff;
    border: 1px solid #e6ddd0;
    border-radius: 8px;
    text-align: center;
    text-decoration: none;
}

/* Breadcrumbs (/catalog, /product) */

.breadcrumbs {
    margin: 1rem 0 1.5rem;
    font-size: 0.9rem;
    color: #7a6b55;
}

.breadcrumbs a {
    color: #7a6b55;
}

.breadcrumbs__current {
    color: #2b2620;
}

/* Catalog filters — sidebar (categories/price/availability), same
   left-column-desktop / <details>-drawer-mobile pattern as
   .profile-layout (below), same 700px breakpoint and 240px sidebar
   width reused directly rather than picked separately. #catalog-results
   isn't given an explicit column count for the desktop 2-column split
   below — it doesn't need one: .product-grid's existing auto-fill
   sizing (minmax(180px, 1fr)) already lands on exactly 4 columns once
   the sidebar's 240px + gap come out of the shared 1100px container
   (was 5 at the full container width) — verified by the grid-track
   math, not just eyeballed. See DECISIONS.md. */
.catalog-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
}

@media (min-width: 700px) {
    .catalog-layout {
        grid-template-columns: 240px minmax(0, 1fr);
        align-items: start;
    }
}

/* Dual-rendered filter form — an always-visible desktop copy and a
   mobile <details> drawer, exactly one ever shown (display: none on
   the other), same reasoning as .profile-nav-desktop/.profile-nav-drawer:
   a closed <details>'s content isn't reliably forceable back to visible
   through a plain CSS override across breakpoints — see DECISIONS.md. */
.catalog-sidebar-desktop {
    display: none;
}

@media (min-width: 700px) {
    .catalog-sidebar-desktop {
        display: block;
    }

    .catalog-sidebar-drawer {
        display: none;
    }
}

.catalog-sidebar-drawer summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    cursor: pointer;
    list-style: none;
    padding: 0.75rem 1rem;
    margin-bottom: 0.75rem;
    background: #fff;
    border: 1px solid #e6ddd0;
    border-radius: 8px;
    font-weight: 500;
}

.catalog-sidebar-drawer summary::-webkit-details-marker {
    display: none;
}

.catalog-sidebar-drawer__indicator {
    display: flex;
    color: #a99880;
    transition: transform 0.15s ease;
}

.catalog-sidebar-drawer[open] .catalog-sidebar-drawer__indicator {
    transform: rotate(90deg);
}

.catalog-filter-form {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    padding: 1rem;
    background: #fff;
    border: 1px solid #e6ddd0;
    border-radius: 8px;
}

.filter-group__title {
    margin: 0 0 0.6rem;
    font-size: 0.95rem;
}

.filter-category-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Visual grouping for a parent's subcategories (e.g. Чёрный/Зелёный
   чай under Чай) — indentation, same idea as .catalog-menu's nested
   <ul> in the header dropdown. */
.filter-category-list--nested {
    padding-left: 1.25rem;
    margin-top: 0.3rem;
}

.filter-category-list li + li {
    margin-top: 0.4rem;
}

/* Checkbox + label text in one row — same align-items: center + gap
   idiom as .checkout-form__checkbox and .product-card__variant-label. */
.filter-checkbox {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
}

.price-range__inputs {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0 0 0.6rem;
}

.price-range__field {
    display: flex;
    align-items: center;
    flex: 1 1 0;
    min-width: 0;
    gap: 0.35rem;
    font-size: 0.85rem;
    color: #7a6b55;
}

.price-range__field input[type="number"] {
    width: 100%;
    min-width: 0;
    padding: 0.35rem;
    font-size: 0.9rem;
}

.price-range__inputs-dash {
    color: #a99880;
}

/* Two native <input type="range"> stacked exactly on top of each other
   (standard no-library dual-range technique — see DECISIONS.md and
   static/price-range.js, which only handles the two handles' clamping
   and the live label, nothing about the overlay itself): each input's
   own track is fully transparent so only the thumbs are visible,
   sitting on one shared background bar drawn by this wrapper's own
   ::before. pointer-events: none on the inputs (re-enabled only on
   their thumbs, both vendor pseudo-elements) is what lets clicks reach
   whichever handle is actually under the pointer instead of always
   hitting whichever <input> happens to be on top. */
.price-range__sliders {
    position: relative;
    height: 1.5rem;
}

.price-range__sliders::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 2px;
    background: #e6ddd0;
    transform: translateY(-50%);
}

.price-range__sliders input[type="range"] {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    background: transparent;
    pointer-events: none;
    -webkit-appearance: none;
    appearance: none;
}

.price-range__sliders input[type="range"]::-webkit-slider-runnable-track {
    background: transparent;
}

.price-range__sliders input[type="range"]::-moz-range-track {
    background: transparent;
}

.price-range__sliders input[type="range"]::-webkit-slider-thumb {
    pointer-events: auto;
    -webkit-appearance: none;
    appearance: none;
    width: 1rem;
    height: 1rem;
    border-radius: 999px;
    background: #6b4a2f;
    cursor: pointer;
    margin-top: 0;
}

.price-range__sliders input[type="range"]::-moz-range-thumb {
    pointer-events: auto;
    width: 1rem;
    height: 1rem;
    border: none;
    border-radius: 999px;
    background: #6b4a2f;
    cursor: pointer;
}

.catalog-filter-reset {
    display: block;
    margin-top: 0.75rem;
    text-align: center;
    text-decoration: none;
}

/* Product grid / cards */

.product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 1rem;
}

.product-card {
    position: relative;
    display: flex;
    flex-direction: column;
    background: #fff;
    border: 1px solid #e6ddd0;
    border-radius: 8px;
    overflow: hidden;
}

.product-card--unavailable {
    opacity: 0.6;
}

.product-card__link {
    display: block;
    text-decoration: none;
    color: inherit;
}

.product-card__photo img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    display: block;
    background: #eee6d8;
}

.product-card__body {
    padding: 0.75rem;
}

.product-card__body h3 {
    margin: 0 0 0.35rem;
    font-size: 1rem;
}

.product-card__price {
    margin: 0;
    font-weight: bold;
}

.badge {
    display: inline-block;
    margin: 0.35rem 0 0;
    padding: 0.15rem 0.5rem;
    font-size: 0.8rem;
    background: #f1e2d2;
    border-radius: 4px;
}

/* Variant picker + price/buy row, below the name/badge — a sibling of
   .product-card__link, not inside it, same reason as favorite-heart
   (a submit button can't nest inside the card's own link).
   .product-card__purchase IS the <form> on the available branch (see
   partials.html) and reused as a plain <div>'s class on the
   unavailable one — one rule either way. margin-top: auto (with
   .product-card as a flex column, above) pins it to the bottom of the
   card regardless of how many lines the name/badge take above it, so
   a row of cards lines up along the bottom — see DECISIONS.md. */
.product-card__purchase {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-top: auto;
    padding: 0 0.75rem 0.75rem;
}

/* Label text + <select> in one row, same align-items: center + gap
   idiom as .checkout-form__checkbox (label + control side by side)
   rather than the stacked label-above-input look most of the site's
   other form labels use — see DECISIONS.md. */
.product-card__variant-label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8rem;
    color: #7a6b55;
}

.product-card__variant {
    display: block;
    flex: 1;
    min-width: 0;
    padding: 0.4rem;
    border: 1px solid #e6ddd0;
    border-radius: 6px;
    background: #fff;
    color: inherit;
    font-size: 0.85rem;
}

.product-card__buy-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

/* Compact icon button, not the wide text button this used to be —
   .button's base padding/font-size don't fit a square icon button, so
   this doesn't extend .button the way the old one did. Disabled look
   (unavailable products) still comes from the existing global
   button:disabled rule — that selector (0-1-1) already outranks this
   one (0-1-0), so no extra override is needed here for it to win. */
.product-card__add-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border: none;
    border-radius: 6px;
    background: #6b4a2f;
    color: #fff;
    cursor: pointer;
}

/* Both icon states are always in the markup (see partials.html) —
   .added just swaps which one shows, same technique as
   copy-to-clipboard.js's .copied. */
.product-card__add-button__done {
    display: none;
}

.product-card__add-button.added .product-card__add-button__icon {
    display: none;
}

.product-card__add-button.added .product-card__add-button__done {
    display: inline-flex;
}

/* Favorite heart — corner-positioned over .product-card's photo by
   default; .product-detail__info below resets that back to normal
   inline flow for the detail page, where there's no card to float
   over. Filled vs outline is a plain CSS fill swap on the shared
   icon-heart path (see partials.html), not a second icon. */
.favorite-heart,
.favorite-heart-prompt {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 1;
}

.favorite-heart {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: none;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.85);
    color: #6b4a2f;
    cursor: pointer;
}

/* Red here is the near-universal "liked" heart convention, not this
   project's usual reservation of red for destructive actions (see
   DECISIONS.md, кнопка «Выйти») — a filled heart isn't a warning, and
   the shape alone already disambiguates it from that other meaning. */
.favorite-heart--active {
    color: #a3342f;
}

.favorite-heart--active svg {
    fill: currentColor;
}

.favorite-heart-prompt {
    max-width: 8rem;
    padding: 0.3rem 0.5rem;
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.95);
    font-size: 0.75rem;
    color: #6b4a2f;
    text-align: right;
}

/* Product detail */

.product-detail {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
}

@media (min-width: 700px) {
    .product-detail {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

/* Same top-right-of-the-photo placement as .product-card (see above,
   favorite-heart/.favorite-heart-prompt's own position/top/right come
   from there unchanged) — .product-detail__photo just needs to be the
   positioning context, same role .product-card plays for the card
   version. Sized up from the card version for this much larger photo;
   still the exact same component/classes, not a second variant. */
.product-detail__photo {
    position: relative;
}

.product-detail__photo .favorite-heart,
.product-detail__photo .favorite-heart-prompt {
    top: 0.75rem;
    right: 0.75rem;
}

.product-detail__photo .favorite-heart {
    width: 2.75rem;
    height: 2.75rem;
}

.product-detail__photo .favorite-heart svg {
    width: 1.5rem;
    height: 1.5rem;
}

.product-detail__photo img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: 8px;
    background: #eee6d8;
}

.product-detail__gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 0.5rem;
}

.product-detail__gallery img:first-child {
    grid-column: 1 / -1;
}

.variant-list {
    list-style: none;
    padding: 0;
}

.variant-list__item {
    display: flex;
    justify-content: space-between;
    padding: 0.4rem 0;
    border-bottom: 1px solid #e6ddd0;
}

button, .button {
    padding: 0.6rem 1.2rem;
    border: none;
    border-radius: 6px;
    background: #6b4a2f;
    color: #fff;
    font-size: 1rem;
    cursor: pointer;
}

button:disabled {
    background: #cbb9a4;
    cursor: not-allowed;
}

/* Search */

.search-form {
    display: flex;
    gap: 0.5rem;
    max-width: 400px;
    margin-bottom: 1.5rem;
}

.search-form input {
    flex: 1;
    padding: 0.5rem;
}

/* Pagination */

.pagination {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 1.5rem;
}

/* Auth / profile */

.auth-form {
    display: flex;
    gap: 0.5rem;
    max-width: 400px;
    margin-bottom: 0.75rem;
}

.auth-form input {
    flex: 1;
    padding: 0.5rem;
}

.auth-form--secondary button {
    background: transparent;
    border: 1px solid #cbb9a4;
    color: #6b4a2f;
}

.notice {
    padding: 0.6rem 0.9rem;
    border-radius: 6px;
    background: #f1e2d2;
    max-width: 400px;
}

.notice--error {
    background: #f6dede;
    color: #7a2c2c;
}

/* Draft-document banner (see partials.html's draft-document-notice) —
   .notice/.notice--error's own padding/background/color as-is, just
   wider (a legal-document page needs more than a form error's 400px)
   and with its own bottom margin/weight instead of .notice's bare
   inline placement. */
.draft-notice {
    max-width: 640px;
    margin: 0 0 1.5rem;
    font-weight: 500;
}

/* /returns' "Отдельные правила для продовольственных товаров" callout
   — a real open question flagged inside one clause, not the whole
   page being a draft, so it reuses plain .notice (neutral amber, not
   .notice--error's red) to read as a distinct, lower-key note next to
   the page-top .draft-notice banner rather than a second alarm. */
.legal-caveat {
    display: inline-block;
    margin: 0.5rem 0;
}

.muted {
    color: #7a6b55;
    font-size: 0.85rem;
}

/* /profile — just the login-status line now (logout moved to the
   bottom of the sidebar, see .profile-nav-footer below) — margin set
   directly rather than left to <p>'s own default so it doesn't stack
   with <h1>'s into visible dead air above the layout. */

.profile-header {
    margin: 0.5rem 0 1rem;
}

.profile-header p {
    margin: 0;
}

/* /profile — thin rule between the header/contact-form/link-list
   blocks. Plain <hr>, not a bare-tag rule: unlike most of this file's
   other one-off elements, <hr> already carries browser default
   styling (an inset 3D groove, its own margin) that has to be reset
   explicitly rather than just added to. */
.divider {
    border: none;
    border-top: 1px solid #e6ddd0;
    margin: 1.5rem 0;
}

/* /profile — nav (history/email) and the contact form share one grid:
   single column, nav first, form below it, on mobile; two columns
   side by side from 700px up (same breakpoint as .product-detail's
   photo+details split — the one other place this project goes to a
   two-column layout). Nav is first in the DOM either way — on the
   2-column layout that alone is what puts it in the left/narrow
   column, no grid-column/order override needed. */
.profile-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
}

@media (min-width: 700px) {
    .profile-layout {
        grid-template-columns: 240px minmax(0, 1fr);
        align-items: start;
    }
}

/* Nav is rendered twice in profile.html (via the shared
   "profile-nav-list" partial) — an always-visible desktop block and a
   mobile <details> drawer — and exactly one of the two is ever shown,
   switched by the same 700px breakpoint as .profile-layout above. Not
   one copy forced open across breakpoints: a closed <details>'s
   content turned out not to be reliably forceable back to visible
   through a plain CSS override (confirmed by inspecting computed
   styles, not assumed) — see DECISIONS.md. Because each hidden copy
   is `display: none` outright, it also doesn't consume a track in
   .profile-layout's grid — the visible copy plus .profile-contact are
   the only two items the grid ever actually sees, at any width. */
.profile-nav-desktop {
    display: none;
}

@media (min-width: 700px) {
    .profile-nav-desktop {
        display: block;
    }

    .profile-nav-drawer {
        display: none;
    }
}

/* Mobile: a native <details>/<summary> drawer, same mechanism as
   .nav-catalog in the header (plain HTML, no JS) — but its own look,
   not .nav-catalog's floating-dropdown CSS, since this one sits
   in-flow on the page rather than overlaying it. Custom chevron
   instead of the browser's native marker (which .nav-catalog just
   hides outright) because this drawer needs a visible open/closed
   indicator — reuses the same icon-chevron as the list rows below,
   just rotated. */
.profile-nav-drawer summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    cursor: pointer;
    list-style: none;
    padding: 0.75rem 1rem;
    margin-bottom: 0.75rem;
    background: #fff;
    border: 1px solid #e6ddd0;
    border-radius: 8px;
    font-weight: 500;
}

.profile-nav-drawer summary::-webkit-details-marker {
    display: none;
}

.profile-nav-drawer__indicator {
    display: flex;
    color: #a99880;
    transition: transform 0.15s ease;
}

.profile-nav-drawer[open] .profile-nav-drawer__indicator {
    transform: rotate(90deg);
}

/* /profile — compact link-rows to the two rarely-used sub-pages
   (/profile/orders, /profile/email): icon, label, trailing chevron.
   Contact editing is the frequently-used one of the three, so it lives
   inline above this list instead of behind a third row — see
   DECISIONS.md. One bordered group (radius on the group, not each
   row) with a divider between rows via border-bottom, same technique
   as .variant-list__item, just without the last row's border. */
.profile-list {
    list-style: none;
    margin: 0;
    padding: 0;
    background: #fff;
    border: 1px solid #e6ddd0;
    border-radius: 8px;
    overflow: hidden;
}

.profile-list li {
    border-bottom: 1px solid #e6ddd0;
}

.profile-list li:last-child {
    border-bottom: none;
}

.profile-list__row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.75rem 1rem;
    text-decoration: none;
    color: inherit;
}

.profile-list__icon {
    display: flex;
    flex-shrink: 0;
    color: #6b4a2f;
}

.profile-list__label {
    flex: 1;
    font-weight: 500;
}

.profile-list__chevron {
    display: flex;
    flex-shrink: 0;
    color: #a99880;
}

/* Logout — bottom of the sidebar (both the desktop block and inside
   the mobile drawer, since both come from the same profile-nav-list
   partial), visually separated from the nav rows above it: its own
   divider plus a bit of extra room, not just .profile-list's own
   flush row-to-row border-bottom. */
.profile-nav-footer {
    margin-top: 1rem;
}

.profile-nav-footer .divider {
    margin: 0 0 0.75rem;
}

.profile-nav-footer button {
    width: 100%;
}

/* Muted/secondary button — reuses the look .auth-form--secondary
   button already established (transparent background, tan border,
   brown text), not a third "secondary button" style invented here.
   Its own class rather than sharing that selector directly:
   .auth-form--secondary is scoped to the flex auth-form layout, this
   button sits alone in the sidebar — same visual language, unrelated
   markup context. Deliberately not the solid brown .button (that's
   the primary call-to-action, e.g. "Сохранить") and deliberately not
   red — logging out is fully reversible, red is kept in reserve for
   actually destructive actions; see DECISIONS.md. */
.button--outline {
    background: transparent;
    border: 1px solid #cbb9a4;
    color: #6b4a2f;
}

/* Add-to-cart form (product detail) */

.add-to-cart-form fieldset {
    border: none;
    padding: 0;
    margin: 0 0 1rem;
}

.add-to-cart-form legend {
    font-weight: bold;
    padding: 0;
    margin-bottom: 0.35rem;
}

.variant-option {
    display: block;
    padding: 0.25rem 0;
}

.add-to-cart-form__qty {
    display: block;
    margin-bottom: 1rem;
}

.add-to-cart-form__qty input {
    display: block;
    width: 5rem;
    padding: 0.4rem;
    margin-top: 0.25rem;
}

/* Cart table */

.cart-table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 1rem;
}

.cart-table th,
.cart-table td {
    text-align: left;
    padding: 0.6rem 0.5rem;
    border-bottom: 1px solid #e6ddd0;
    vertical-align: middle;
}

.cart-row--unavailable {
    opacity: 0.6;
}

.cart-qty-form {
    display: flex;
    gap: 0.35rem;
}

.cart-qty-form input {
    width: 4rem;
    padding: 0.3rem;
}

.cart-total {
    font-size: 1.1rem;
    font-weight: bold;
}

/* Checkout */

.checkout-summary {
    margin-bottom: 1.5rem;
}

.checkout-summary__list {
    list-style: none;
    padding: 0;
}

.checkout-summary__list li {
    padding: 0.3rem 0;
    border-bottom: 1px solid #e6ddd0;
}

.checkout-form {
    max-width: 480px;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.checkout-form label {
    display: block;
}

.checkout-form input[type="text"],
.checkout-form input[type="tel"],
.checkout-form textarea {
    display: block;
    width: 100%;
    padding: 0.5rem;
    margin-top: 0.25rem;
}

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

/* "Списать бонусы" (part 2, see DECISIONS.md) — same label/input shape
   as the rest of .checkout-form, own block only for the hint text
   above the field. */
.checkout-bonus label {
    margin-bottom: 0.3rem;
}

.checkout-bonus input[type="number"] {
    display: block;
    width: 100%;
    max-width: 160px;
    padding: 0.5rem;
    margin-top: 0.25rem;
}

/* The live-recalculated "К оплате" line (static/checkout-bonus.js) —
   bolder than the plain "Сумма заказа" line above it so the number
   that actually changes as you type reads as the important one. */
.cart-total--payable {
    font-weight: 700;
    font-size: 1.1rem;
}

/* Order history (/profile/orders) */

.order-history {
    list-style: none;
    padding: 0;
    margin: 0 0 1.5rem;
}

.order-history__item {
    padding: 0.75rem 0;
    border-bottom: 1px solid #e6ddd0;
}

.order-history__row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
}

.order-history__row + .order-history__row {
    margin-top: 0.3rem;
    color: #7a6b55;
    font-size: 0.9rem;
}

.order-history__number {
    font-weight: bold;
}

/* Order status badge — same small-pill shape as .badge, colored per
   status so /profile/orders reads at a glance. The admin order list
   (Этап 6) only ever shows status as plain table text, so there's no
   existing colored badge to reuse here. */

.order-status {
    display: inline-block;
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    font-size: 0.8rem;
    white-space: nowrap;
}

.order-status--pending {
    background: #f1e2d2;
    color: #6b4a2f;
}

.order-status--confirmed {
    background: #dce8f6;
    color: #2c4a7a;
}

.order-status--ready {
    background: #dff0e0;
    color: #2c6b3a;
}

.order-status--done {
    background: #e6e6e6;
    color: #555;
}

.order-status--cancelled {
    background: #f6dede;
    color: #7a2c2c;
}

/* /profile/bonus — balance/level summary, progress bar to the next
   level, and the ledger history list. Part 2, see DECISIONS.md. */

.bonus-summary {
    margin-bottom: 1.5rem;
}

.bonus-summary__balance {
    margin: 0;
    font-size: 2rem;
    font-weight: 700;
    color: #201c17;
}

.bonus-summary__level {
    margin: 0.2rem 0 0.75rem;
    color: #6b4a2f;
    font-weight: 600;
}

/* Same track/fill shape as the dual-range price slider's own track
   (see .price-range__sliders in this file) isn't reused here — that
   one's an <input type="range">'s own rendering, not a plain div bar;
   this is a much simpler read-only meter, styled fresh. */
.bonus-progress {
    height: 0.6rem;
    border-radius: 999px;
    background: #e6ddd0;
    overflow: hidden;
}

.bonus-progress__fill {
    height: 100%;
    background: #6b4a2f;
    border-radius: 999px;
}

/* .bonus-progress__fill--pct-0 .. --pct-100 — one rule per whole
   percent, picked by class name (see profile_bonus.html:
   class="bonus-progress__fill bonus-progress__fill--pct-{{...}}"),
   NOT an inline style="width: N%" — confirmed root cause, not a
   guess (see DECISIONS.md for the full headless-browser investigation):
   this project's own CSP (internal/platform/httpserver.go,
   contentSecurityPolicy) sends style-src 'self' with no
   'unsafe-inline' — deliberately strict, because an earlier audit
   found zero inline styles anywhere in the project at the time. This
   bar's dynamic inline style="width: N%" was the first one ever
   added (part 2), silently violating that audit's own premise — the
   browser keeps the attribute in the DOM (so raw HTML/curl inspection
   always looked correct) but refuses to apply it as CSS, falling back
   to whatever the stylesheet declares instead. A CSS class only ever
   references a same-origin stylesheet rule — never inline content —
   so it's unaffected by style-src regardless of 'unsafe-inline', with
   no change to the CSP itself needed. 101 rules (one per integer
   percent, matching bonus.LevelProgress.PercentToNext's exact 0-100
   range) keeps full precision instead of trading it away for coarser
   buckets. Mechanically generated (see DECISIONS.md for the exact
   command), not hand-maintained. */
.bonus-progress__fill--pct-0 { width: 0%; }
.bonus-progress__fill--pct-1 { width: 1%; }
.bonus-progress__fill--pct-2 { width: 2%; }
.bonus-progress__fill--pct-3 { width: 3%; }
.bonus-progress__fill--pct-4 { width: 4%; }
.bonus-progress__fill--pct-5 { width: 5%; }
.bonus-progress__fill--pct-6 { width: 6%; }
.bonus-progress__fill--pct-7 { width: 7%; }
.bonus-progress__fill--pct-8 { width: 8%; }
.bonus-progress__fill--pct-9 { width: 9%; }
.bonus-progress__fill--pct-10 { width: 10%; }
.bonus-progress__fill--pct-11 { width: 11%; }
.bonus-progress__fill--pct-12 { width: 12%; }
.bonus-progress__fill--pct-13 { width: 13%; }
.bonus-progress__fill--pct-14 { width: 14%; }
.bonus-progress__fill--pct-15 { width: 15%; }
.bonus-progress__fill--pct-16 { width: 16%; }
.bonus-progress__fill--pct-17 { width: 17%; }
.bonus-progress__fill--pct-18 { width: 18%; }
.bonus-progress__fill--pct-19 { width: 19%; }
.bonus-progress__fill--pct-20 { width: 20%; }
.bonus-progress__fill--pct-21 { width: 21%; }
.bonus-progress__fill--pct-22 { width: 22%; }
.bonus-progress__fill--pct-23 { width: 23%; }
.bonus-progress__fill--pct-24 { width: 24%; }
.bonus-progress__fill--pct-25 { width: 25%; }
.bonus-progress__fill--pct-26 { width: 26%; }
.bonus-progress__fill--pct-27 { width: 27%; }
.bonus-progress__fill--pct-28 { width: 28%; }
.bonus-progress__fill--pct-29 { width: 29%; }
.bonus-progress__fill--pct-30 { width: 30%; }
.bonus-progress__fill--pct-31 { width: 31%; }
.bonus-progress__fill--pct-32 { width: 32%; }
.bonus-progress__fill--pct-33 { width: 33%; }
.bonus-progress__fill--pct-34 { width: 34%; }
.bonus-progress__fill--pct-35 { width: 35%; }
.bonus-progress__fill--pct-36 { width: 36%; }
.bonus-progress__fill--pct-37 { width: 37%; }
.bonus-progress__fill--pct-38 { width: 38%; }
.bonus-progress__fill--pct-39 { width: 39%; }
.bonus-progress__fill--pct-40 { width: 40%; }
.bonus-progress__fill--pct-41 { width: 41%; }
.bonus-progress__fill--pct-42 { width: 42%; }
.bonus-progress__fill--pct-43 { width: 43%; }
.bonus-progress__fill--pct-44 { width: 44%; }
.bonus-progress__fill--pct-45 { width: 45%; }
.bonus-progress__fill--pct-46 { width: 46%; }
.bonus-progress__fill--pct-47 { width: 47%; }
.bonus-progress__fill--pct-48 { width: 48%; }
.bonus-progress__fill--pct-49 { width: 49%; }
.bonus-progress__fill--pct-50 { width: 50%; }
.bonus-progress__fill--pct-51 { width: 51%; }
.bonus-progress__fill--pct-52 { width: 52%; }
.bonus-progress__fill--pct-53 { width: 53%; }
.bonus-progress__fill--pct-54 { width: 54%; }
.bonus-progress__fill--pct-55 { width: 55%; }
.bonus-progress__fill--pct-56 { width: 56%; }
.bonus-progress__fill--pct-57 { width: 57%; }
.bonus-progress__fill--pct-58 { width: 58%; }
.bonus-progress__fill--pct-59 { width: 59%; }
.bonus-progress__fill--pct-60 { width: 60%; }
.bonus-progress__fill--pct-61 { width: 61%; }
.bonus-progress__fill--pct-62 { width: 62%; }
.bonus-progress__fill--pct-63 { width: 63%; }
.bonus-progress__fill--pct-64 { width: 64%; }
.bonus-progress__fill--pct-65 { width: 65%; }
.bonus-progress__fill--pct-66 { width: 66%; }
.bonus-progress__fill--pct-67 { width: 67%; }
.bonus-progress__fill--pct-68 { width: 68%; }
.bonus-progress__fill--pct-69 { width: 69%; }
.bonus-progress__fill--pct-70 { width: 70%; }
.bonus-progress__fill--pct-71 { width: 71%; }
.bonus-progress__fill--pct-72 { width: 72%; }
.bonus-progress__fill--pct-73 { width: 73%; }
.bonus-progress__fill--pct-74 { width: 74%; }
.bonus-progress__fill--pct-75 { width: 75%; }
.bonus-progress__fill--pct-76 { width: 76%; }
.bonus-progress__fill--pct-77 { width: 77%; }
.bonus-progress__fill--pct-78 { width: 78%; }
.bonus-progress__fill--pct-79 { width: 79%; }
.bonus-progress__fill--pct-80 { width: 80%; }
.bonus-progress__fill--pct-81 { width: 81%; }
.bonus-progress__fill--pct-82 { width: 82%; }
.bonus-progress__fill--pct-83 { width: 83%; }
.bonus-progress__fill--pct-84 { width: 84%; }
.bonus-progress__fill--pct-85 { width: 85%; }
.bonus-progress__fill--pct-86 { width: 86%; }
.bonus-progress__fill--pct-87 { width: 87%; }
.bonus-progress__fill--pct-88 { width: 88%; }
.bonus-progress__fill--pct-89 { width: 89%; }
.bonus-progress__fill--pct-90 { width: 90%; }
.bonus-progress__fill--pct-91 { width: 91%; }
.bonus-progress__fill--pct-92 { width: 92%; }
.bonus-progress__fill--pct-93 { width: 93%; }
.bonus-progress__fill--pct-94 { width: 94%; }
.bonus-progress__fill--pct-95 { width: 95%; }
.bonus-progress__fill--pct-96 { width: 96%; }
.bonus-progress__fill--pct-97 { width: 97%; }
.bonus-progress__fill--pct-98 { width: 98%; }
.bonus-progress__fill--pct-99 { width: 99%; }
.bonus-progress__fill--pct-100 { width: 100%; }

.bonus-history {
    list-style: none;
    padding: 0;
    margin: 0 0 1.5rem;
}

.bonus-history__item {
    padding: 0.75rem 0;
    border-bottom: 1px solid #e6ddd0;
}

.bonus-history__row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
}

.bonus-history__row + .bonus-history__row {
    margin-top: 0.3rem;
    color: #7a6b55;
    font-size: 0.9rem;
}

.bonus-history__type {
    font-weight: bold;
}

/* Credit (accrual, refund, an upward admin correction) stays the
   default text color and keeps formatRubles' own leading "+" (added by
   the template, not this class); a debit (redemption, a downward
   admin correction) gets both formatRubles' own leading "-" (see
   money.go) and this color, so it reads as a deduction at a glance,
   the same "negative reads differently" convention
   .order-status--cancelled's red already sets elsewhere on this page. */
.bonus-history__amount--negative {
    color: #7a2c2c;
}

/* /requisites — a plain field: value list (dl/dt/dd), not prose; see
   requisites.html. Browser defaults already indent dd under dt, this
   just tightens the spacing and de-emphasizes the value slightly so
   the field label reads as the primary thing at a glance. */
.requisites-list {
    max-width: 480px;
}

.requisites-list dt {
    margin-top: 0.75rem;
    font-weight: 600;
}

.requisites-list dt:first-child {
    margin-top: 0;
}

.requisites-list dd {
    margin: 0.15rem 0 0;
    color: #4a4136;
}
