/* Global admin UI fixes — loaded on every Backpack page via config/backpack/ui.php `styles`. */

/* Dashboard KPI tiles: a long value (e.g. revenue "26875,50 грн") must not break mid-number in the
   narrow column. Smaller than the default .h2 with a tight line-height so the number fits on one
   line; `overflow-wrap: normal` keeps the digits together and wraps only at the space before the
   currency (e.g. "26875,50" / "грн") instead of splitting the number. */
.dash-tile-value {
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1.2;
    overflow-wrap: normal;
}

/* Sidebar menu group labels: long localized titles (e.g. "Керування замовленнями",
   "Керування користувачами") were clipped on a single line in the narrow vertical sidebar.
   Let the label wrap onto a second line instead of being cut off, and top-align the row so the
   icon, badge and chevron sit next to the first line rather than floating between the two lines.
   `!important` overrides the `white-space: nowrap` from the Tabler core bundle (CDN, not editable
   here); the other rules win on selector specificity alone. */
aside .navbar-nav .nav-item .nav-link {
    white-space: normal !important;
    align-items: flex-start;
}

aside .navbar-nav .nav-item .nav-link > span:not(.badge) {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Optically center the 16px icon on the 20px first line (align-items: flex-start would sit it
   slightly high otherwise). */
aside .navbar-nav .nav-item .nav-link > i.nav-icon {
    margin-top: 2px;
}

/* Dropdown chevron alignment. The Tabler `.nav-link.dropdown-toggle::after` inherits the nav-link's
   `align-items: flex-start`, so the ~14px chevron sits ~3px above the first text line — and on a
   two-line item ("Керування замовленнями") it floats above the count badge it sits next to. Nudge it
   down onto the first line, the same optical-centering trick the icon uses: (20px line − 14px
   chevron) / 2 ≈ 3px. NOT `align-self: center`, which would drop it to the middle of the whole
   (56px) two-line block, below the icon and badge. */
aside .navbar-nav .nav-item.dropdown > .nav-link.dropdown-toggle::after {
    margin-top: 3px;
}

/* Space the count badge off the dropdown chevron — they touched otherwise. */
aside .navbar-nav .nav-item.dropdown .nav-link span.badge {
    margin-right: 8px;
}

/* Single-item (non-dropdown) count badge, e.g. "Звернення". The Tabler core bundle positions a
   nav-link badge as `position: absolute; top/right: 6px` with a translateY, which parks it in the
   top-right corner (meant for topbar icon badges). Put it back in the flex flow, push it to the right
   edge, and centre it vertically on the row. */
aside .navbar-nav .nav-item:not(.dropdown) .nav-link span.badge {
    position: static;
    transform: none;
    top: auto;
    right: auto;
    margin-left: auto;
    align-self: center;
    margin-right: 8px;
}

/* Noty toast overflowed the right edge on narrow screens; keep it inside the viewport. */
@media (max-width: 575.98px) {
    .noty_layout {
        max-width: 100% !important;
        width: auto !important;
        left: 8px !important;
        right: 8px !important;
    }

    .noty_layout .noty_bar,
    .noty_layout .noty_body {
        max-width: 100% !important;
        word-break: break-word;
    }
}

/* Softer look: the Tabler inputs ship with square (0 radius) corners, which read as harsh. Round the
   form controls and cards across the whole admin for a gentler, more consistent feel. `!important`
   is required here: the theme pins these to 0 with an `!important` of its own (theme-tabler
   style.css, "Border radius inputs"). */
.form-control,
textarea.form-control,
select.form-control,
.input-group-text,
.card,
.well {
    border-radius: 5px !important;
}

/* Buttons take the same radius, but through the theme's own custom property rather than a blanket
   `border-radius: … !important`. Nothing squares buttons off here — Tabler already rounds them
   (`--tblr-border-radius: 4px`) — so a flat override bought 1px and cost every button group: the
   rules that square the inner edges inside a `.btn-group`
   (`.btn-group > .btn:not(:last-child):not(.dropdown-toggle)` and its left-side twin) carry no
   `!important` and lost to it. The save split button and its dropdown caret then rendered as two
   separate pills with a notch where they meet, and so did the image/file field controls, the
   array-field table controls and the Show page's language dropdown. Setting the variable leaves
   those grouping rules in force. */
.btn {
    --tblr-btn-border-radius: 5px;
}

/* `.btn-file` (the "choose file" control of the image and file fields) is pinned by the theme to its
   own radius with `!important` ("Fix border btn-file"), which outranks the grouping rules above and
   re-rounds the edge it presents inside the field's `.btn-group`. Keep the admin radius on its outer
   corners and square the seam by hand. */
.btn-file {
    border-radius: 5px !important;
}

.btn-group > .btn-file:not(:last-child) {
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

/* select2 boxes need a higher-specificity rule: the theme forces square corners with
   `span.select2-selection.select2-selection--single { border-radius:0 !important }` (and an even more
   specific `[data-bs-theme="dark"] …` in dark mode), so match it with a stronger selector. */
.select2.select2-container--bootstrap span.select2-selection.select2-selection--single,
.select2.select2-container--bootstrap span.select2-selection.select2-selection--multiple {
    border-radius: 5px !important;
}

/* Keep an input group (prefix + field, e.g. "грн" + price) visually joined. */
.input-group > .form-control:not(:first-child) {
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
}

.input-group > *:first-child {
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

/* Order "Товари замовлення" (and "Платежі") tables on the order Show page carry many columns
   (товар, фасовка, помел, price, qty, discount type/size, discounted price, total, action). With the
   theme's 12px cell padding the 11-column table is ~90px wider than its card, so `.table-responsive`
   pushed the last column — the "Редагувати" action button — into a horizontal scroll where it was
   clipped at the card edge. Tighten the horizontal cell padding on these order widgets only; the
   full table then fits on standard admin widths and the action button is fully visible. */
.order-show-widget table.card-table > :not(caption) > * > * {
    padding-left: 6px;
    padding-right: 6px;
}

/* A Show screen prints a value whole (spec 2026-10-show-values-whole), and a whole value can be one long word — a
   banner link, an e-mail with dots in it. With no place to break, such a word widened the table past the screen:
   measured at 375 px, a banner link pushed the page 34 px sideways and a dotted e-mail 162 px. `anywhere` lets the value
   cell break inside a word when nothing else fits (unlike `break-word`, it also lets the table narrow to the screen);
   ordinary text still wraps at its spaces. A table drawn inside a value cell (a product's variants, discounts and
   characteristics, a category's discounts, a size chart) keeps its own layout; the next rule makes it scroll.
   Guarded by Tests\Feature\Admin\ShowScreensPrintValuesWholeTest. */
.show-values > tbody > tr > td:last-child {
    overflow-wrap: anywhere;
}

.show-values > tbody > tr > td:last-child table {
    overflow-wrap: normal;
}

/* No Show screen scrolls the page sideways (status M4, spec 2026-10-show-screens-fit-a-phone).

   The Show table lays its columns out by their content, and a cell is at least as wide as its content can be narrow. A
   table drawn inside a value cannot be narrower than its widest row, so it widened its cell and the page with it:
   measured at 375 px, the product's «Ціни та варіанти» drew a 1089 px page, «Знижки» 941, «Характеристики» 583, a
   product category 686 — and the first two overflowed a 1024 px window too. Bootstrap's `.table-responsive` scrolls only
   once its width is set from outside; inside such a cell it grew with the table instead.

   A one-track grid sets that width. Its zero minimum asks nothing of the cell when the table is measured at its narrowest,
   and the whole table when there is room: on a narrow screen the wrapper takes the cell's width and the table scrolls
   inside its card, on a wide one the columns come out exactly as before (measured at 1440 px). `justify-items: start`
   keeps a table with a width of its own (a size chart) at that width instead of stretching it across the value. Screen
   only: on paper nothing scrolls, so a printed Show screen keeps its old layout.
   Guarded by Tests\Feature\Admin\ShowScreensFitAPhoneTest. */
@media screen {
    .show-values > tbody > tr > td:last-child .table-responsive {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        justify-items: start;
    }
}

/* A button or a badge keeps its text on one line, so a long one in a Show value widened the page the same way: the
   static page's «Попередній перегляд на сайті» by 28 px at 375 px, a long category name on a product by 98. Here it
   wraps instead; one that fits looks as before. A badge marked `text-nowrap` (On/Off) keeps its single line, and so does
   one inside a table drawn in the value («В наявності» in the variants): that table scrolls instead.
   Guarded by Tests\Feature\Admin\ShowScreensFitAPhoneTest. */
.show-values > tbody > tr > td:last-child .btn,
.show-values > tbody > tr > td:last-child .badge {
    white-space: normal;
}

.show-values > tbody > tr > td:last-child table .btn,
.show-values > tbody > tr > td:last-child table .badge {
    white-space: nowrap;
}

/* One vertical rhythm for every form field.

   The theme gives `div.row div.form-group` a 10px bottom padding, and Backpack adds `mb-3` (16px) to
   any field the controller did not give a width to — so a form alternated 10px and 26px gaps between
   its rows and stopped reading as rows at all. One gap, set here, for both kinds; `!important`
   because `mb-3` is a Bootstrap utility and carries its own.
   Guarded by Tests\Feature\Admin\AdminFormRhythmTest. */
form [bp-field-wrapper] {
    padding-bottom: 0 !important;
    margin-bottom: 1.25rem !important;
}

/* A hidden input occupies no row, so it spends no gap — and, sitting full width, it used to break
   the row it fell into: the order form's status/payment/customer trio was split across two lines by
   one of them. */
form [bp-field-wrapper][bp-field-type="hidden"] {
    margin-bottom: 0 !important;
    display: none !important;
}

/* A switch now labels above its toggle like every other field (see the field view), so the toggle
   needs the height of an input to sit on the same line as its neighbours. */
form [bp-field-type="switch"] .switch-row {
    min-height: 2.375rem; /* the theme's .form-control height */
    display: flex;
    align-items: center;
}

/* Admin footer credits. The agency mark is the one thing down there that should read as a brand:
   the wordmark (inline SVG, see inc/brand_radevs.blade.php) inherits the footer's text colour but
   sits at full body contrast against the muted credit line around it, and picks up the RaDevs green
   on hover. `.9em` lines the wordmark's cap height up with the text it sits next to. */
/* The footer's own divider, drawn here rather than with Bootstrap's `border-top` utility: that
   utility paints `--tblr-border-color` with `!important`, and in the dark theme that variable is a
   near-black blue at 14% alpha which vanishes against the background. */
.app-footer {
    border-top: 1px solid rgba(0, 0, 0, .1);
}

[data-bs-theme="dark"] .app-footer {
    border-top-color: rgba(255, 255, 255, .08);
}

.app-footer .footer-credit-brand {
    color: var(--tblr-body-color);
    text-decoration: none;
    transition: color .15s ease-in-out;
}

.app-footer .footer-credit-brand:hover,
.app-footer .footer-credit-brand:focus {
    color: #71f049; /* the green of the RaDevs mark */
}

.app-footer .footer-credit-mark {
    height: .9em;
    width: auto;
    vertical-align: -.06em;
}

/* Product badges: one column for the badge, one track per choice.

   Laid out with flexbox, each row's radio group started where that badge's name and description
   happened to end — «Новинка» at one x, «Останні розміри» at another — so the choices could not be
   read down the column and the editor had to check every row separately. The tracks are fixed here
   instead, and a badge with two choices (one with no rule of its own) still lines its first two up
   with everybody else's. Guarded by Tests\Feature\Admin\ProductCardLayoutTest. */
.badge-row {
    display: grid;
    grid-template-columns: minmax(10rem, 20rem) 1fr;
    gap: .5rem 1.5rem;
    align-items: center;
}

.badge-choices {
    display: grid;
    grid-template-columns: repeat(3, minmax(7rem, 12rem));
    gap: .25rem 1rem;
    align-items: center;
}

@media (max-width: 767.98px) {
    .badge-row,
    .badge-choices {
        grid-template-columns: 1fr;
    }
}

/* The permissions as a table (PermissionCatalogue, crud::fields.inc.permission_matrix): a row per
   screen under the menu's sections, a column per action, in the role form and the user form alike.

   It replaced a three-column flow of one flat run of ticks — 104 on the user form, «Товари: список»
   beside «Способи оплати: видалення» — that the owner could not read (2026-09-28). A screen's
   abilities now sit on one line, so granting a screen is one row, and comparing two roles is
   comparing two tables. The section headings and the notes under a tick are quiet text, in the
   panel's readable quiet colour. Guarded by Tests\Feature\Admin\AdminLegibilityTest. */
.permission-matrix-table th,
.permission-matrix-table td {
    vertical-align: middle;
}

.permission-matrix-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--tblr-bg-surface);
}

.permission-matrix-section th {
    padding-top: 1rem;
    font-size: .75rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--admin-muted);
}

.permission-matrix-screen {
    font-weight: 400;
    white-space: nowrap;
}

.permission-matrix-tick {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: .125rem;
    margin: 0;
    cursor: pointer;
}

.permission-matrix-note {
    max-width: 10rem;
    font-size: .75rem;
    line-height: 1.2;
    color: var(--admin-muted);
}

/* Toasts clear the header.

   Noty lands its top-right layout at the very top of the window, which is exactly where the
   language switch and the account menu are — a success message covered both for as long as it was
   on screen. Drop it below the header bar. Guarded by Tests\Feature\Admin\AdminPolishTest. */
#noty_layout__topRight {
    top: 4.5rem;
}

/* Quiet text stays readable — one colour, and it follows the theme.

   Two hardcoded greys sat behind everything the panel says quietly, each blind to the theme and each
   failing AA in one of them. Measured in a browser on /admin/product/1/edit, against the white card
   and the dark one (#221e26):

     - `form .help-block` is #73818f, pinned in Backpack's own crud/…/common.css and therefore not
       editable in place. 3.99:1 light, 4.11:1 dark. Eight per form;
     - `.text-muted` resolves to --tblr-secondary (#66626c), the SAME value in both themes.
       5.95:1 light — and 2.75:1 dark. Fourteen per form.

   The theme does own a dark muted hue, #a09bbe, but serves it as `rgba(160,155,190,.75)`, and the
   75% is exactly what drops it under the line. At full opacity the pair clears 4.5:1 everywhere:
   5.95:1 and 6.18:1 on a card, 5.43:1 and 6.70:1 on the page ground — against body text at 16:1, so
   a hint still reads as quieter than what it explains.

   Not done by redefining --tblr-secondary: that variable also paints .btn-secondary and the borders
   that go with it, so moving it to repair some text would repaint buttons nobody complained about.
   `!important` because Tabler's own .text-muted carries one.
   Guarded by Tests\Feature\Admin\AdminLegibilityTest. */
:root {
    --admin-muted: #66626c;
}

[data-bs-theme="dark"] {
    --admin-muted: #a09bbe;
}

form .help-block,
.help-block,
.text-muted {
    color: var(--admin-muted) !important;
}

/* What stands in for an empty field.

   Some fields show in their placeholder what the shop uses while they stay empty: a catalogue
   position's title shows the template it falls back to, the title template shows the default
   (S2 §3.6 T1). Such a stand-in must not read as a value someone typed. In the dark theme the colour
   is the general rule's work (see «An empty field reads as empty in the dark theme» at the end of
   this file: the theme's placeholder colour names a variable it never defines, so every placeholder
   there inherited the value colour). This rule adds what only a stand-in needs: the slant, so a
   value the shop will use is not taken for an ordinary hint, and the panel's quiet colour in the
   light theme as well. `!important` because the theme's dark-theme rule carries one.
   Guarded by Tests\Feature\Admin\PositionTextsFormTest. */
.form-control.fallback-placeholder::placeholder {
    color: var(--admin-muted) !important;
    opacity: 1;
    font-style: italic;
}

/* The delete confirmation.

   Backpack Pro's trash button hands SweetAlert a confirm styled `bg-warning` — #ffc107 under white
   text, which is 1.9:1 and unreadable, and yellow that appears nowhere else in this panel: every
   other control that removes something (the collection screen's two, the log manager's) is red.
   Repaint it rather than dim the whole dialog, and lean the overlay a little darker: on this panel's
   near-white ground the stock 40% left the table behind the question fully legible, so the dialog
   read as one more element of the page rather than as the one thing to answer.

   Guarded by Tests\Feature\Admin\AdminDeleteDialogTest. */
.swal-button--delete.bg-warning,
.swal-button--delete.bg-warning:not([disabled]):hover {
    background-color: var(--tblr-danger) !important;
}

.swal-overlay {
    background-color: rgba(0, 0, 0, .55);
}

/* Headings and breadcrumbs, as the language files already write them.

   Backpack marks both with `.text-capitalize`, which upper-cases the first letter of EVERY word.
   English tolerates it; Ukrainian does not — the panel showed «Категорії Товарів», «Статичні
   Сторінки», «Обмін З 1С», capitalising a preposition. The names arrive from the language files
   already cased correctly («Категорії товарів»), so the transform has nothing left to add and
   plenty to spoil.

   Guarded by Tests\Feature\Admin\AdminButtonGrammarTest. */
[bp-section="page-heading"],
[bp-section="page-subheading"],
.breadcrumb .breadcrumb-item {
    text-transform: none !important;
}

/* An icon inside a button needs air before the word.

   Both Line Awesome and the markup around it leave the gap to a single collapsed space, which at
   this font size reads as one glued token: «Скопіювати понеділок на всі дні» with the sheet icon
   stuck to the «С», «Створити сторінку», «Очистити». Some call sites already paste an `me-1` on the
   icon by hand; this makes that the rule rather than the exception — a hand-written `me-1` is more
   specific and still wins where someone wanted a different gap.

   `.btn-icon` is Tabler's class for a button that is only an icon; there a gap would push the glyph
   off-centre. Guarded by Tests\Feature\Admin\AdminButtonGrammarTest. */
.btn > i[class*="la-"] {
    margin-right: .35em;
}

.btn-icon > i[class*="la-"] {
    margin-right: 0;
}

/* The page head on a phone.

   Backpack lays the heading, the operation name and the "back to all …" link in one flex row. Below
   roughly 600px the three share the width and each wraps inside its own column, so the order card
   opened on a phone with three stacks of one word each: «Замовлення» / «Попередній перегляд
   Замовлення» / «Назад до всіх Замовлення». Stacked, they read as three lines of a title.

   Guarded by Tests\Feature\Admin\AdminPageHeaderTest. */
@media (max-width: 600px) {
    [bp-section="page-header"] {
        flex-direction: column;
        align-items: flex-start !important;
    }

    [bp-section="page-subheading"],
    [bp-section="page-subheading-back-button"] {
        margin-left: 0 !important;
    }
}

/* The main button of a form is the panel's own colour.

   Backpack paints the save button `btn-success`, so the primary action of every form was green
   while the primary action of every list — «Додати…», «Створити замовлення», the quick actions on
   the dashboard — was the panel's purple. Two colours for the same rank of action, decided by which
   screen you happen to be on. The save button is scoped by `#saveActions`, so nothing else that is
   green (a genuinely successful state, a pill) is touched.

   Guarded by Tests\Feature\Admin\AdminButtonColourTest. */
#saveActions .btn-success {
    --tblr-btn-bg: var(--tblr-primary);
    --tblr-btn-border-color: var(--tblr-primary);
    --tblr-btn-hover-bg: var(--tblr-primary-darken, var(--tblr-primary));
    --tblr-btn-hover-border-color: var(--tblr-primary-darken, var(--tblr-primary));
    --tblr-btn-active-bg: var(--tblr-primary-darken, var(--tblr-primary));
    --tblr-btn-active-border-color: var(--tblr-primary-darken, var(--tblr-primary));
}

/* Two colours that belong to somebody else's palette.

   The filter dropdown highlights its hovered option in the select2-bootstrap blue (#337ab7) and the
   reorder screen outlines a branch in the AdminLTE cyan (#00acd6) — both inherited from libraries
   this panel otherwise overrides, and neither appears anywhere else here. A person reading the
   screen has no way to know the colour means nothing.

   Guarded by Tests\Feature\Admin\AdminButtonColourTest. */
.select2-container--bootstrap .select2-results__option--highlighted[aria-selected],
.select2-container .select2-results__option--highlighted[aria-selected] {
    background-color: var(--tblr-primary) !important;
}

.sortable li.mjs-nestedSortable-branch > div {
    border-color: var(--tblr-border-color, #d7dbe0);
}

/* The two entries of the row menu that open a sub-menu.

   Backpack renders «Попередній перегляд» and «Редагувати» as a `.btn-group` of two links — the label
   and a caret that opens the language list — and its datatables JS turns both halves into
   `dropdown-item`s. The group is `inline-flex`, so it shrinks to its own text: the caret of
   «Редагувати» sat 9px left of the caret of «Попередній перегляд», at a position that changed with
   the label's length. Give the group the menu's full width and push the caret to the edge, so the
   two carets line up with each other and with the right edge of the menu.

   Guarded by Tests\Feature\Admin\AdminRowButtonShapeTest. */
.dropdown-menu .btn-group {
    display: flex;
    width: 100%;
}

.dropdown-menu .btn-group > .dropdown-item:not(.dropdown-toggle) {
    flex: 1 1 auto;
    width: auto;
}

.dropdown-menu .btn-group > .dropdown-item.dropdown-toggle {
    flex: 0 0 auto;
    width: auto;
}

/* The disclosure chevron of a `<summary>` laid out with flex.

   A `<summary>` gets the browser's triangle only while it is `display: list-item`; the combinations
   screen lays its summary out with `d-flex` to put the heading and its explanation on one line, and
   the marker disappears with it. The closed card then reads as an empty one: a heading, a sentence,
   and no hint that the colour and size pickers are behind it. The chevron is in the markup now, and
   this turns it when the section opens.

   Guarded by Tests\Feature\Admin\CombinationsScreenTest. */
.combinations-chevron {
    margin-right: .5rem;
    transition: transform .15s ease-in-out;
}

details[open] > summary .combinations-chevron {
    transform: rotate(90deg);
}

/* The 1C directory tables: one control line per row.

   Each row of «Каталоги 1С» / «Склади 1С» carries controls of three different heights — a text
   field, pairs of number fields, bare checkboxes — and the name cell carries a GUID caption UNDER
   its field. With the cells centred vertically (`table-vcenter`), that caption made the name cell
   taller than the rest, and centring a taller cell lifted its field ~11px above every other field
   in the row: the eye read the name box as belonging to a different line than the numbers beside it.

   So the cells align to the top, and every control sits inside one `.onec-control` strip of the
   same height. A checkbox (16px) and a field (23px) then share a centre line, and the caption
   hangs below the strip instead of pushing it around. 2rem leaves air around the tallest control.

   Guarded by Tests\Feature\Admin\OneCDirectoryAlignmentTest. */
.onec-directory > :not(caption) > * > * {
    vertical-align: top;
}

.onec-directory .onec-control {
    display: flex;
    align-items: center;
    min-height: 2rem;
}

/* …and a hairline between its columns.

   The row is a settings form laid out as a table: six controls of four kinds, side by side with
   nothing marking where one setting ends and the next begins. The head read as a run of words
   («Основний Як основний Торгова точка Активний Порядок») and the ticks under it as a row of loose
   boxes. A rule on the leading edge of every column but the first draws the boundary the reader was
   supplying by guesswork — lighter than `table-bordered`, which would box each cell in.

   Guarded by Tests\Feature\Admin\OneCDirectoryAlignmentTest. */
.onec-directory > :not(caption) > * > *:not(:first-child) {
    border-left: 1px solid rgba(0, 0, 0, .1);
}

/* NOT `--tblr-border-color`: in the dark theme that variable is a near-black blue at 14% alpha and
   the line disappears into the card, which is the same trap the footer divider above fell into. */
[data-bs-theme="dark"] .onec-directory > :not(caption) > * > *:not(:first-child) {
    border-left-color: rgba(255, 255, 255, .08);
}

/* The save button stands under the first field, not under the card's edge.

   Backpack puts `#saveActions` outside the card — a sibling of it — so the button lines up with the
   card's outer edge while the fields inside start one card padding further in. Measured on
   /admin/payment-method/create: card 356, first field 381, button 356. The eye reads the button as
   belonging to the form, so a 25px step between it and the column of fields it saves looks like a
   misalignment rather than a margin.

   The offset is the card's own padding plus its 1px border, taken from the theme's variable rather
   than typed as a number, so a theme that changes its card padding moves the button with it.

   Guarded by Tests\Feature\Admin\AdminSaveButtonAlignmentTest. */
#saveActions {
    padding-left: calc(var(--tblr-card-padding-x, 1.5rem) + 1px);
}

/* A frame drawn with Tabler's border utilities shows in the dark theme too.

   Tabler 1.0.0-beta19 compiles `.border` and its side forms (`.border-top`, `-end`, `-bottom`, `-start`, `-x`, `-y`,
   and the `-wide` of each) with a fixed colour, `rgba(4, 32, 69, .14)`, where Bootstrap reads a variable, and gives
   them no dark-theme rule: no theme variable reaches them, and on the dark card (#221e26) that near-black blue at 14%
   disappears. Measured on /admin/product/1/edit, «Ціни та варіанти» (2026-10-09): the unticked option chips
   («Молочний», «L») had no frame at all and read as loose text with a checkbox beside the framed ticked ones. The
   same utilities frame a home block's collection tiles, a badge's rule note, the order's refund form, the progress
   lines of the image pipeline and the 1C exchange, the blog article's recommendation hits and the colour swatches —
   the footer divider and the 1C hairlines above met the same trap and work around it one element at a time.

   In the dark theme they take the colour the theme frames every input with, --tblr-primary-border-subtle (#383243):
   1.33:1 on the dark card, the contrast the fixed colour has on the white one. The light theme keeps Tabler's own.
   A colour utility beside them keeps its colour (the log viewer's white link circle is `border border-white`): in
   Tabler a colour wins over `.border` only by coming later in the same file, and this sheet comes later still, so the
   rule steps around every border colour Tabler beta19 defines. A ticked chip keeps its purple frame, its own rule
   weighs more. All four sides take the colour, which shows only on an element that draws other borders of its own
   beside a side utility — none does today.

   Guarded by Tests\Feature\Admin\AdminDarkThemeFramesTest. */
[data-bs-theme="dark"] :is(.border, .border-wide, .border-top, .border-top-wide, .border-end, .border-end-wide,
    .border-bottom, .border-bottom-wide, .border-start, .border-start-wide, .border-x, .border-x-wide, .border-y,
    .border-y-wide):not([class*="border-azure"], [class*="border-bitbucket"], [class*="border-black"],
    [class*="border-blue"], [class*="border-cyan"], [class*="border-danger"], [class*="border-dark"],
    [class*="border-dribbble"], [class*="border-facebook"], [class*="border-flickr"], [class*="border-github"],
    [class*="border-google"], [class*="border-green"], [class*="border-indigo"], [class*="border-info"],
    [class*="border-instagram"], [class*="border-light"], [class*="border-lime"], [class*="border-linkedin"],
    [class*="border-muted"], [class*="border-orange"], [class*="border-pink"], [class*="border-pinterest"],
    [class*="border-primary"], [class*="border-purple"], [class*="border-red"], [class*="border-rss"],
    [class*="border-secondary"], [class*="border-success"], [class*="border-tabler"], [class*="border-teal"],
    [class*="border-twitter"], [class*="border-vimeo"], [class*="border-vk"], [class*="border-warning"],
    [class*="border-white"], [class*="border-yellow"], [class*="border-youtube"]) {
    border-color: var(--tblr-primary-border-subtle) !important;
}

/* An empty field reads as empty in the dark theme.

   The theme paints every dark-theme placeholder `rgba(var(--tblr-light-text), .5) !important`
   (theme-tabler color-adjustments.css), and nothing defines --tblr-light-text: the declaration is
   invalid at computed-value time, so the placeholder inherits the colour of the value. Measured on
   /admin/product/1/edit, /admin/product and /admin/static-page/create: both rgb(201,193,214), so an
   empty «Заголовок» read as a title someone had typed — while the select2 search «Оберіть
   характеристики» fell to #555, 2.2:1. The light theme was never affected (Tabler's own #929dab).

   The panel's quiet colour instead: #a09bbe, 6.18:1 on the dark card — about where Tabler's own
   placeholder grey would have sat (5.95:1) had the theme not overridden it. The same selector as the
   theme's and an `!important` like it, so this sheet loading later is what wins. `opacity: 1` because
   Firefox dims a placeholder to 54% of its own accord, which would undo the contrast measured here.
   A rule that styles a placeholder on purpose is more specific and keeps its look: the red hint of
   an empty «Змінні для текстів» value, the slanted stand-ins of «What stands in for an empty field».

   Guarded by Tests\Feature\Admin\AdminLegibilityTest. */
[data-bs-theme="dark"] ::placeholder {
    color: var(--admin-muted) !important;
    opacity: 1;
}

/* The session window: the admin signs back in over the page, the typed form stays under it
   (public/js/admin-session.js, docs/ai/specs/2026-10-admin-session-resume.md). Above Noty (2000)
   and Backpack's modals, and phone-safe: the card shrinks to the screen with a 16px margin. */
.admin-session-overlay {
    position: fixed;
    inset: 0;
    z-index: 2100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(15, 12, 25, 0.55);
    overflow-y: auto;
}

.admin-session-card {
    position: relative;
    width: 100%;
    max-width: 420px;
    margin: auto;
}

.admin-session-close {
    position: absolute;
    top: 14px;
    right: 14px;
}

.admin-session-card .card-title {
    padding-right: 32px;
}

/* S2 §2.12 — the service notice list (docs/ai/specs/2026-10-service-notices.md). DataTables' responsive mode sizes a
   column by its text on one line, so a notice carrying one long word (a 1C catalogue id) folded every other column
   into the row's details even at 1440 px. Each cell wraps inside a bounded width instead. */
.service-notice-text,
.service-notice-state {
    white-space: normal;
    overflow-wrap: anywhere;
}

.service-notice-text {
    min-width: 14rem;
    max-width: 26rem;
}

.service-notice-state {
    min-width: 9rem;
    max-width: 14rem;
}

/* S2 §3.9 — «Магазини» and «Бронювання». A store's address or a week of hours, or a long product name, on one line
   pushed five of the eight columns into the row's details even at 1440 px (seen 2026-10-15). The long cells wrap
   inside a bounded width instead, so every column stays on the row. */
.store-wrap-cell {
    white-space: normal;
    overflow-wrap: anywhere;
    min-width: 8rem;
    max-width: 18rem;
}

/* The panel explains itself (docs/ai/specs/2026-11-admin-plain-language.md). «Що це / Що зробити» opens every screen
   and folds to one quiet line; a field hint's «Детальніше» opens its longer rules in place. Both sit on Tabler's own
   info alert and link colours, which carry their dark-theme values already. */
.admin-explainer {
    padding: .75rem 1rem;
}

.admin-explainer-body {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
}

.admin-explainer-text {
    flex: 1 1 auto;
    min-width: 0;
}

.admin-explainer-hide {
    flex: 0 0 auto;
}

.admin-explainer.is-folded {
    padding: .35rem 1rem;
}

.admin-hint-more {
    display: inline;
    padding: 0;
    border: 0;
    background: none;
    color: var(--tblr-link-color);
    font: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}

.admin-hint-more:hover,
.admin-hint-more:focus-visible {
    color: var(--tblr-link-hover-color);
}

@media (max-width: 575.98px) {
    .admin-explainer-body {
        flex-direction: column;
        gap: .5rem;
    }
}

.admin-first-steps-list {
    display: grid;
    gap: .35rem;
}
