/* ==========================================================================
   Fiiberlahendused - design system
   One hand-written stylesheet. No framework, no build step, nothing to update.

   Brand colours sampled from fiiberlahendused_logo_.png:
     orange #E94C0A   grey #575656

   Accessibility note: #E94C0A on white is only 3.8:1, which fails WCAG AA for
   body text (needs 4.5:1). So the raw logo orange is used for fills, borders,
   icons and large display type, while --brand-ink (5.7:1) and --brand-strong
   (5.2:1 against white text) carry anything at reading size.
   ========================================================================== */

/* --- tokens: light is the base, dark only overrides ---------------------- */
:root {
  --brand:         #E94C0A;   /* logo orange - fills, accents, large type    */
  --brand-strong:  #C13F06;   /* button fill; white on this = 5.2:1          */
  --brand-hover:   #A63505;   /* primary button hover - darker in light mode */
  --brand-ink:     #B83A05;   /* orange text at reading size = 5.7:1         */
  --brand-wash:    #FEF1EA;   /* tinted background for selected states       */
  --brand-line:    #F7C9B2;

  --ink:      #17181A;        /* headings                                    */
  --ink-2:    #575656;        /* body - the logo grey, 7.1:1                 */
  --ink-3:    #757577;        /* muted, 4.6:1                                */
  --ink-4:    #9A9A9C;        /* decorative only, never load-bearing         */

  --bg:       #FFFFFF;
  --bg-2:     #F7F7F8;        /* page background behind cards                */
  --surface:  #FFFFFF;
  --surface-2:#FAFAFB;
  --surface-3:#ECECEF;        /* a well: a card sits ON this, not beside it  */
  --line:     #E4E4E7;
  --line-2:   #D3D3D8;

  --ok:       #15803D;
  --ok-wash:  #E8F6EC;
  --warn:     #A16207;
  --warn-wash:#FDF6E3;
  --sale:     #B4200E;
  --sale-wash:#FDECEA;

  --radius:    10px;
  --radius-sm:  6px;
  --radius-lg: 16px;

  --shadow-1: 0 1px 2px rgb(23 24 26 / .06);
  --shadow-2: 0 4px 12px rgb(23 24 26 / .09);
  --shadow-3: 0 12px 32px rgb(23 24 26 / .16);

  --gap:      1rem;
  --pad:      clamp(1rem, 4vw, 2rem);
  --maxw:     1440px;

  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
          Arial, "Noto Sans", sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
          "Liberation Mono", monospace;

  /* fluid type scale */
  --t-xs:   0.75rem;
  --t-sm:   0.8125rem;
  --t-base: 0.9375rem;
  --t-md:   1.0625rem;
  --t-lg:   clamp(1.125rem, 0.9rem + 0.8vw, 1.375rem);
  --t-xl:   clamp(1.375rem, 1.05rem + 1.4vw, 1.875rem);
  --t-2xl:  clamp(1.75rem, 1.2rem + 2.6vw, 2.75rem);
  --t-3xl:  clamp(2.125rem, 1.3rem + 4vw, 3.5rem);

  color-scheme: light;
}

/* Light is the DEFAULT view - the OS preference does not switch it.
   A visitor arriving in the dark gets our light page; dark is a choice made
   here, by the header toggle (which writes localStorage) or by ?theme=dark.
   There is therefore only ONE dark block below, not the twin pair this file
   used to carry - and no way left for the two to drift apart. */

/* The one dark palette, reached only by an explicit choice. */
:root[data-theme="dark"] {
  --brand-strong: #E9550F;
  --brand-hover:  #F5701F;
  --brand-ink:    #FF8B4D;
  --brand-wash:   #2A1810;
  --brand-line:   #5A2E14;
  --ink:      #F2F2F3;
  --ink-2:    #C3C3C6;
  --ink-3:    #97979B;
  --ink-4:    #6E6E73;
  --bg:       #17181A;
  --bg-2:     #121314;
  --surface:  #1C1D20;
  --surface-2:#212226;
  --surface-3:#0E0F11;        /* the well goes DOWN in the dark, not up      */
  --line:     #2E2F33;
  --line-2:   #3C3D42;
  --ok:       #4ADE80;
  --ok-wash:  #14251A;
  --warn:     #FCD34D;
  --warn-wash:#241E0D;
  --sale:     #FF7A6B;
  --sale-wash:#2A1512;
  --shadow-1: 0 1px 2px rgb(0 0 0 / .4);
  --shadow-2: 0 4px 12px rgb(0 0 0 / .5);
  --shadow-3: 0 12px 32px rgb(0 0 0 / .6);
  color-scheme: dark;
}

/* --- reset --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

body {
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: 1.55;
  color: var(--ink-2);
  background: var(--bg-2);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100dvh;
}

img, picture, svg, video { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; padding: 0; }
a { color: var(--brand-ink); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--brand-strong); }
h1, h2, h3, h4 { color: var(--ink); line-height: 1.2; text-wrap: balance; font-weight: 650; }
p { text-wrap: pretty; }
:focus-visible { outline: 2.5px solid var(--brand); outline-offset: 2px; border-radius: 3px; }
:where(:focus:not(:focus-visible)) { outline: none; }
::selection { background: var(--brand); color: #fff; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
[hidden] { display: none !important; }

/* --- layout -------------------------------------------------------------- */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--pad); }
.stack > * + * { margin-top: var(--flow, 1rem); }

/* --- skip link ----------------------------------------------------------- */
.skip {
  position: absolute; left: 8px; top: -100px; z-index: 200;
  background: var(--brand-strong); color: #fff; padding: .6rem 1rem;
  border-radius: var(--radius-sm); transition: top .15s;
}
.skip:focus { top: 8px; color: #fff; }

/* --- header -------------------------------------------------------------- */
.site-head {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--line);
}
.site-head__inner {
  position: relative;                 /* the mega panel and the collapsed nav
                                         both hang off this, not off the item
                                         they belong to - see .mega below */
  display: flex; align-items: center; gap: clamp(.75rem, 2vw, 2rem);
  min-height: 62px;
}
/* --- the wordmarks --------------------------------------------------------
   Real logo files, not type set to look like one. The header carries F-top
   (the brand the shop sells under, the same in every language); the footer
   and the product sheet carry the company - fiiberlahendused in Estonian,
   fibersolutions in English, swapped by site.js on <img data-logo>.

   The marks are dark-grey-on-transparent artwork, so on a dark ground the
   grey half of the wordmark would sink into the page. A single filter -
   nothing per-logo, nothing baked into a second file - lifts it. */
.logo { display: flex; align-items: center; gap: .55rem; text-decoration: none; flex: none; }
.logo img { display: block; width: auto; }
.logo--head img { height: 34px; }
.logo--foot img { height: 30px; }
:root[data-theme="dark"] .logo img { filter: brightness(1.9) saturate(1.05); }
@media (max-width: 480px) { .logo--head img { height: 28px; } }

.nav { display: flex; gap: .25rem; margin-inline-start: auto; align-items: center; }
.nav a {
  color: var(--ink-2); text-decoration: none; font-weight: 550;
  /* a tenth off the base size: the top menu is four words that never change,
     so it can sit quieter than the body text it hangs above */
  padding: .5rem .7rem; border-radius: var(--radius-sm);
  font-size: calc(var(--t-base) * .9);
  white-space: nowrap;
}
.nav a:hover { background: var(--surface-2); color: var(--ink); }
.nav a[aria-current="page"] { color: var(--brand-ink); background: var(--brand-wash); }

/* The menu was saying two different things in one flat row: where you are
   (Esileht, Kontakt) and what there is to buy. The three catalogues sit in a
   box of their own now, so it reads as one group rather than five peers, and
   „Esileht" is a house - the one item whose meaning needs no word (Rein,
   06.09.2026). The word is still there for a screen reader, and it comes back
   on a phone where the menu is a stacked list and an icon alone would be a
   riddle. */
.nav__home { display: grid; place-items: center; }
.nav__cats {
  display: flex; align-items: center; gap: .1rem;
  margin-inline: .3rem; padding: .2rem;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: calc(var(--radius-sm) + 5px);
}
/* inside the box the hover has to go the other way - lighter, not darker,
   or it lands on the same colour the box already is */
.nav__cats a:hover,
.nav__cats .nav__wrap[data-open="true"] > a { background: var(--surface); color: var(--ink); }

.head-tools { display: flex; align-items: center; gap: .4rem; flex: none; }

/* Below 860px the inline nav's nowrap links force the page wider than the
   viewport, which scrolls the WHOLE layout sideways. Collapse to a menu. */
/* .icon-btn also sets display, so match its specificity or it wins */
.icon-btn.nav-toggle { display: none; }
@media (max-width: 860px) {
  .icon-btn.nav-toggle { display: grid; }
  .nav {
    position: absolute; top: calc(100% + 1px); left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: .1rem;
    background: var(--surface); border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-2); padding: .5rem; margin: 0;
  }
  .nav:not([data-open="true"]) { display: none; }
  .nav a { padding: .65rem .8rem; }
  /* stacked, the box is a section of the list and the house gets its word
     back - an icon on its own in a vertical menu is a riddle */
  .nav__cats { flex-direction: column; align-items: stretch; margin-inline: 0; }
  .nav__home { grid-auto-flow: column; justify-content: start; gap: .55rem; }
  .nav__home .visually-hidden {
    position: static; width: auto; height: auto; margin: 0;
    overflow: visible; clip: auto; white-space: nowrap;
  }
}

/* --- the pick-up mega menu -------------------------------------------------
   A panel the full width of the page, hung under a sticky header. It is a
   shortcut into filters that already exist; the trigger stays a plain link,
   so with no JS - and on a phone, where the whole nav collapses - the
   catalogue is still one tap away. */
.nav__wrap { display: flex; align-items: center; }
.nav__wrap > a { display: inline-flex; align-items: center; gap: .3rem; }
.nav__caret { transition: rotate .16s; }
.nav__wrap[data-open="true"] > a { background: var(--surface-2); color: var(--ink); }
.nav__wrap[data-open="true"] .nav__caret { rotate: 180deg; }

/* Positioned against the HEADER, not against the menu item. A panel hung off
   a nav item that is itself near the right edge runs off one side or the
   other at every width; hung off the header's own content box it is inside
   the page by construction, at 900px and at 1900px alike. */
.mega {
  position: absolute; top: 100%; right: 0; left: auto;
  z-index: 60; width: min(620px, 100%);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-3);
  padding: 1.15rem 1.25rem 1rem;
}
.nav__wrap:not([data-open="true"]) .mega { display: none; }
/* The bridge over the dead strip: the trigger sits mid-header, the panel
   starts below the header, and the pointer has to cross what is between.
   This is inside the panel's own subtree, so crossing it is never leaving
   the menu. Invisible, and only there while the menu is open. */
.mega::before {
  content: ""; position: absolute; left: 0; right: 0;
  top: -22px; height: 22px;
}
.mega .wrap { padding: 0; max-width: none; }
.mega__cols { display: grid; gap: 1.25rem 2rem; grid-template-columns: 1fr 1fr; }
.mega__title {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-3);
  padding-bottom: .4rem; margin-bottom: .3rem; border-bottom: 1px solid var(--line);
}
.mega__list { list-style: none; padding: 0; display: grid; gap: .1rem; }
.mega__list a {
  display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
  padding: .38rem .5rem; border-radius: var(--radius-sm);
  color: var(--ink-2); text-decoration: none; font-size: var(--t-base); font-weight: 550;
}
.mega__list a:hover { background: var(--brand-wash); color: var(--brand-ink); }
.mega__list em {
  font-style: normal; font-size: var(--t-xs); color: var(--ink-4);
  font-variant-numeric: tabular-nums;
}
.mega__col--all { grid-column: 1 / -1; border-top: 1px solid var(--line); padding-top: .7rem; }
.mega__all { font-weight: 650; font-size: var(--t-sm); color: var(--brand-ink); text-decoration: none; }
.mega__all:hover { text-decoration: underline; }

/* Collapsed nav: the panel is not a floating layer any more, it is the next
   thing in the list. Absolute positioning inside a column that is itself a
   dropdown would put it off-screen. */
@media (max-width: 860px) {
  .nav__wrap { display: block; }
  .nav__wrap > a { width: 100%; justify-content: space-between; }
  .mega::before { content: none; }
  .mega {
    position: static; width: auto; box-shadow: none;
    border-radius: var(--radius); padding: .75rem .6rem; margin: .1rem 0 .3rem;
    background: var(--surface-2);
  }
  .mega__cols { grid-template-columns: 1fr; gap: .9rem; }
}

.lang {
  display: inline-flex; border: 1px solid var(--line-2); border-radius: 999px;
  overflow: hidden; font-size: var(--t-xs); font-weight: 650;
}
.lang button { padding: .28rem .6rem; color: var(--ink-3); }
.lang button[aria-pressed="true"] { background: var(--brand); color: #fff; }

.icon-btn {
  width: 34px; height: 34px; border-radius: var(--radius-sm);
  display: grid; place-items: center; color: var(--ink-2);
  border: 1px solid transparent;
}
.icon-btn:hover { background: var(--surface-2); border-color: var(--line); color: var(--ink); }

/* --- buttons ------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  padding: .55rem 1rem; border-radius: var(--radius-sm);
  font-weight: 600; font-size: var(--t-base); line-height: 1.3;
  border: 1px solid transparent; text-decoration: none; white-space: nowrap;
  transition: background-color .13s, border-color .13s, color .13s;
}
.btn--primary { background: var(--brand-strong); color: #fff; }
.btn--primary:hover { background: var(--brand-hover, #A63505); color: #fff; }
.btn--ghost { border-color: var(--line-2); color: var(--ink); background: var(--surface); }
.btn--ghost:hover { background: var(--surface-2); border-color: var(--ink-4); color: var(--ink); }
.btn--quiet { color: var(--ink-2); padding-inline: .6rem; }
.btn--quiet:hover { background: var(--surface-2); color: var(--ink); }
.btn--sm { padding: .34rem .7rem; font-size: var(--t-sm); }
.btn[disabled] { opacity: .45; pointer-events: none; }

/* --- form controls ------------------------------------------------------- */
.field {
  width: 100%; padding: .6rem .8rem;
  border: 1px solid var(--line-2); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink);
}
.field::placeholder { color: var(--ink-4); }
.field:focus-visible { border-color: var(--brand); outline-offset: 0; }

/* Numbers are typed, not stepped. The native spinner arrows put a price one
   mis-click - or one stray scroll over a focused field - away from changing,
   which is not a risk worth two tiny buttons. Money fields go further and are
   plain text boxes (see MONEY in admin.js), so a comma decimal is accepted. */
.field[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.field[type="number"]::-webkit-outer-spin-button,
.field[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.field--num { font-variant-numeric: tabular-nums; }

/* --- badges / pills ------------------------------------------------------ */
.badge {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .16rem .48rem; border-radius: 999px;
  font-size: var(--t-xs); font-weight: 650; line-height: 1.45;
  white-space: nowrap;
}
.badge--sale { background: var(--sale-wash); color: var(--sale); }
.badge--new  { background: var(--brand-wash); color: var(--brand-ink); }
/* „Soovitatud". NOT var(--warn), which is why this is four lines and not
   one: --warn is a TEXT colour in the light theme (#A16207, a dark amber) and
   a light one in the dark theme (#FCD34D), so using it as a background gave a
   muddy brown chip on the light side and a yellow one on the dark. An
   explicit pair instead - light, yellow and a touch smaller than a plain
   badge (Rein, 07.09.2026). .ribbon--rec on the add-on tiles still uses
   --warn; it is a saturated corner flag rather than an inline chip, and
   nothing carries it today (031 cleared them). */
.badge--rec {
  background: #FDE68A; color: #713F12;
  padding: .12rem .42rem; font-size: 11px;
}
[data-theme="dark"] .badge--rec { background: #FCD34D; color: #402A05; }

.dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.dot--in   { background: var(--ok); }
.dot--soon { background: var(--warn); }
.dot--lead { background: var(--ink-4); }

/* ==========================================================================
   CATALOGUE
   ========================================================================== */
.cat {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr);
  gap: clamp(1rem, 2vw, 2rem);
  align-items: start;
  padding-block: 1.25rem 4rem;
}
@media (max-width: 940px) { .cat { grid-template-columns: 1fr; } }

/* --- facets -------------------------------------------------------------- */
.facets {
  position: sticky; top: 78px;
  max-height: calc(100dvh - 96px); overflow-y: auto; overscroll-behavior: contain;
  padding-right: .25rem;
}
@media (max-width: 940px) {
  .facets {
    position: fixed; inset: 0; top: auto; z-index: 90;
    max-height: 82dvh; background: var(--surface);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: var(--shadow-3); padding: 1rem 1rem 5rem;
    transform: translateY(101%); transition: transform .22s ease;
  }
  .facets[data-open="true"] { transform: none; }
}

.facets__head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: .75rem;
}
.facets__head h2 { font-size: var(--t-md); }

.fgroup { border-top: 1px solid var(--line); padding-block: .3rem; }
.fgroup:first-of-type { border-top: 0; }
.fgroup__btn {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  width: 100%; padding: .55rem 0; font-weight: 650; color: var(--ink);
  font-size: var(--t-base);
}
.fgroup__btn svg { transition: transform .18s; color: var(--ink-4); flex: none; }
.fgroup[data-open="false"] .fgroup__btn svg { transform: rotate(-90deg); }
.fgroup[data-open="false"] .fgroup__body { display: none; }
.fgroup__body { padding-bottom: .6rem; display: grid; gap: .12rem; }

.tag {
  display: grid; grid-template-columns: 16px 1fr auto; align-items: center; gap: .5rem;
  width: 100%; padding: .32rem .4rem; border-radius: var(--radius-sm);
  font-size: var(--t-sm); color: var(--ink-2); text-align: left;
}
.tag:hover:not([disabled]) { background: var(--surface-2); color: var(--ink); }
.tag__box {
  width: 16px; height: 16px; border: 1.5px solid var(--line-2);
  border-radius: 4px; display: grid; place-items: center; color: transparent;
}
.tag[aria-pressed="true"] { color: var(--ink); font-weight: 600; }
.tag[aria-pressed="true"] .tag__box {
  background: var(--brand); border-color: var(--brand); color: #fff;
}
.tag__n { font-variant-numeric: tabular-nums; color: var(--ink-4); font-size: var(--t-xs); }
.tag[disabled] { opacity: .38; cursor: default; }

/* --- results toolbar ----------------------------------------------------- */
/* Sticky under the site header: search, the ONE vat note of the list and the
   result count stay visible while scrolling; availability/sale/sort fold
   away behind the "Valikud" toggle. 63px = header min-height + its border. */
.toolbar {
  position: sticky; top: 63px; z-index: 40;
  display: grid; gap: .5rem; margin-bottom: .9rem;
  background: var(--bg-2); border-bottom: 1px solid var(--line);
  padding-block: .55rem .45rem;
}
/* the ONE vat note of the list rides the result-count line, right side */
.resultbar .vat-note { margin: 0; padding: 0; white-space: nowrap; }
/* .searchrow's display:flex would override the hidden attribute's UA rule */
.searchrow--more[hidden] { display: none; }
/* Edit mode's own look lives with the switch itself - see .swbtn. It used to
   be here, as a solid orange fill on aria-pressed, and the fill was the
   problem: a filled brand button reads as "press me to do something", not as
   "this is currently on". */

/* search + availability + on-sale + sort, one row on desktop */
.searchrow { display: flex; gap: .5rem; align-items: stretch; flex-wrap: wrap; }
.searchrow .searchbar { flex: 1 1 260px; min-width: 0; }
.sortwrap { display: inline-flex; align-items: stretch; }
.sortwrap .field { width: auto; padding-block: .32rem; }
@media (max-width: 720px) {
  .searchrow .searchbar { flex: 1 1 100%; }
  .searchrow .dropwrap, .searchrow #saleToggle, .sortwrap { flex: 1 1 auto; }
  .sortwrap .field { width: 100%; }
}

/* Match the search field's height. Do NOT use height:100% here - once the row
   wraps, a percentage height resolves against the whole multi-line flex
   container, not the item's own line, and the buttons balloon. */
.dropwrap { display: flex; }
.searchrow .dropbtn,
.searchrow .sortwrap .field { min-height: 36px; }
.toolbar .searchbar .field { min-height: 36px; padding-block: .3rem; font-size: var(--t-base); }
.dropbtn { gap: .45rem; white-space: nowrap; }
.dropbtn[aria-pressed="true"] {
  border-color: var(--brand); background: var(--brand-wash); color: var(--brand-ink);
}
.dot--sale { background: var(--sale); }

.dropwrap { position: relative; }
.dropmenu {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: 60;
  min-width: 236px; max-height: 60dvh; overflow-y: auto; overscroll-behavior: contain;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--radius); box-shadow: var(--shadow-2); padding: .3rem;
  display: none;
}
.dropwrap[data-open="true"] .dropmenu { display: block; }

/* back to top */
.totop {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 70;
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface); color: var(--ink-2);
  border: 1px solid var(--line-2); box-shadow: var(--shadow-2);
}
.totop:hover { color: var(--brand-ink); border-color: var(--brand-line); }
@media (max-width: 940px) { .totop { bottom: 4.2rem; } }

.searchbar { position: relative; display: flex; gap: .5rem; }
.searchbar svg {
  position: absolute; left: .7rem; top: 50%; translate: 0 -50%;
  color: var(--ink-4); pointer-events: none;
}
.searchbar .field { padding-left: 2.3rem; font-size: var(--t-md); }
.searchbar__clear {
  position: absolute; right: .55rem; top: 50%; translate: 0 -50%;
  width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
  color: var(--ink-3); background: var(--surface-2);
}
.searchbar__clear:hover { color: var(--ink); }

.chips { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; }
.chip {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .22rem .4rem .22rem .6rem; border-radius: 999px;
  background: var(--brand-wash); color: var(--brand-ink);
  border: 1px solid var(--brand-line); font-size: var(--t-xs); font-weight: 600;
}
.chip button { display: grid; place-items: center; color: inherit; opacity: .65; }
.chip button:hover { opacity: 1; }

.resultbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: .75rem; flex-wrap: wrap;
  font-size: var(--t-sm); color: var(--ink-3);
}
.resultbar strong { color: var(--ink); font-variant-numeric: tabular-nums; }

/* --- product row card ---------------------------------------------------- */
.rows { display: grid; gap: .5rem; }

/* No separate stock column: availability now sits on each variant row, so the
   width it used to take goes to the options column instead.

   The last track is a FIXED width, not `auto`, for the reason .opt gives one
   paragraph down: every .row is its own grid, so an auto track is sized by
   THAT row's buttons and no two rows agree. It also made the caption strip
   impossible to line up - .rowhead has no buttons at all, so its auto track
   collapsed to zero and handed the width to the flexible title column, which
   is what pushed every caption ~120px right of the thing it names. */
.row {
  position: relative;
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr) 424px var(--w-cta);
  gap: 0 1rem; align-items: stretch;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .75rem;
  transition: border-color .13s, box-shadow .13s;
}
.row:hover { border-color: var(--line-2); box-shadow: var(--shadow-1); }

/* Highlighted products get a slightly heavier edge - noticeable when scanning,
   but not shouting. */
/* corner ribbon - the top-left of the row is dead space anyway */
.ribbon {
  position: absolute; top: 0; left: 0; width: 78px; height: 78px;
  overflow: hidden; pointer-events: none; z-index: 2;
  border-top-left-radius: var(--radius);
}
.ribbon span {
  position: absolute; top: 14px; left: -30px; width: 116px; padding: 2px 0;
  transform: rotate(-45deg); text-align: center;
  background: var(--sale); color: #fff;
  font-size: 8.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  box-shadow: 0 1px 3px rgb(0 0 0 / .3);
}
.ribbon--outlet span { background: var(--warn); color: #1b1400; }

/* align-self:start matters: the row is a stretch grid, and a stretched
   aspect-ratio box grows WIDER to keep its ratio, overflowing its track. */
.row__media {
  position: relative; aspect-ratio: 4 / 3; width: 100%; align-self: start;
  border-radius: var(--radius-sm);
  overflow: hidden; background: var(--surface-2); border: 1px solid var(--line);
}
.row__media img { width: 100%; height: 100%; object-fit: contain; }
.row__media button { position: absolute; inset: 0; width: 100%; }
.row__media:hover .row__zoom { opacity: 1; }
.row__zoom {
  position: absolute; right: .3rem; bottom: .3rem; opacity: 0; transition: opacity .15s;
  background: color-mix(in srgb, var(--ink) 78%, transparent); color: #fff;
  border-radius: 999px; padding: .2rem .45rem;
  font-size: var(--t-xs); font-weight: 650; pointer-events: none;
}
.row__noimg { display: grid; place-items: center; height: 100%; color: var(--ink-4); font-size: var(--t-xs); }

.row__main { display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
.row__title {
  font-size: var(--t-md); font-weight: 700; color: var(--ink);
  text-decoration: none; line-height: 1.3;
}
.row__title:hover { color: var(--brand-ink); }
.row__rule {
  border: 0; border-top: 1.5px solid color-mix(in srgb, var(--brand) 55%, transparent);
  margin: .28rem 0 .12rem; width: 100%;
}
/* Four lines, not three. Still a cap - one runaway description must not push
   every other card off the screen - but measured against the catalogue as it
   is: the longest short description is 176 characters, which is four lines in
   this column and was three lines plus a cut-off one. */
.row__desc {
  font-size: var(--t-sm); color: var(--ink-2);
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  line-clamp: 4; overflow: hidden;
}
/* tags repeated on the row, clickable - they drive the same selection the
   sidebar does, so clicking one ticks its checkbox over there too */
.row__tags {
  display: flex; flex-wrap: wrap; gap: .25rem; align-items: center;
  margin-top: auto; padding-top: .4rem;
}
.rowtag {
  font-size: 11px; font-weight: 600; line-height: 1.5;
  padding: .1rem .45rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--ink-3); background: var(--surface);
}
.rowtag:hover { border-color: var(--brand); color: var(--brand-ink); background: var(--brand-wash); }
.rowtag[aria-pressed="true"] {
  border-color: var(--brand); color: var(--brand-ink); background: var(--brand-wash);
}

/* variants column - min-width:0 everywhere, or long dealer price lines blow
   the grid track past its max and push the title column out of the card */
.row__opts { display: grid; gap: .18rem; align-content: start; min-width: 0; }
/* radio | name+sku | price | link (admin) | availability or stepper
   Fixed tracks, not auto: each .opt is its own grid, so sibling rows do NOT
   share column widths - with auto tracks every row sized itself and nothing
   lined up. Widths are set from the worst case: "10 000,00 €" for the price
   (with the dealer line under it), "Saabub 14. okt" for availability. */
.opt {
  display: grid;
  grid-template-columns: 15px minmax(0, 1fr) var(--w-price) var(--w-avail);
  gap: .4rem;
  align-items: center; padding: .34rem .4rem; border-radius: var(--radius-sm);
  text-align: left; width: 100%; border: 1px solid transparent;
}
/* defaults live on :root so .opt resolves anywhere it is reused - the product
   page uses the same class without a .row__opts wrapper */
:root { --w-price: 112px; --w-avail: 112px; --w-link: 22px; --w-cta: 120px; }
/* product page: no availability or link column, just radio, name and price */
.buybox .opt { grid-template-columns: 15px minmax(0, 1fr) auto; }
/* the link cell only exists for admins, so the template gains a track */
.row__opts--adm .opt {
  grid-template-columns: 15px minmax(0, 1fr) var(--w-price) var(--w-link) var(--w-avail);
}
.opt__link {
  display: grid; place-items: center; width: 22px; height: 22px;
  border-radius: 4px; color: var(--ink-3); justify-self: center;
}
a.opt__link:hover { color: var(--brand-ink); background: var(--brand-wash); }
.opt__link.is-empty { color: var(--line-2); pointer-events: none; }
/* left-aligned in a fixed track, so the dots form a straight line down the row */
/* Also wrapped, and for the same reason: the track is 112px and „Saabub 2
   nädala jooksul" measures 121px, so the longest arrival window - the one
   worth reading - was the one being cut. The dot is nudged onto the first
   line rather than centred against a two-line label. */
.opt__avail { font-size: 11px; font-weight: 600; display: flex;
              align-items: start; gap: .28rem; justify-self: start; }
.opt__avail .dot { margin-top: .35em; }
.opt__avail.stock--in   { color: var(--ok); }
.opt__avail.stock--soon { color: var(--warn); }
.opt__avail.stock--lead { color: var(--ink-3); font-weight: 550; }
.opt:hover { background: var(--surface-2); }
.opt[aria-checked="true"] { background: var(--brand-wash); border-color: var(--brand-line); }
.opt__radio {
  width: 15px; height: 15px; border-radius: 50%; border: 1.5px solid var(--line-2);
  display: grid; place-items: center; align-self: center;
}
.opt[aria-checked="true"] .opt__radio { border-color: var(--brand); border-width: 4.5px; }
/* single unnamed variant: no radio, no em-dash - but keep the tinted box so
   the price block still reads as one unit against the rest of the row */
.opt--bare {
  grid-template-columns: minmax(0, 1fr) var(--w-price) var(--w-avail); cursor: default;
  background: var(--brand-wash); border-color: var(--brand-line);
}
.row__opts--adm .opt--bare {
  grid-template-columns: minmax(0, 1fr) var(--w-price) var(--w-link) var(--w-avail);
}
.opt--bare:hover { background: var(--brand-wash); }

.opt__label { min-width: 0; display: grid; gap: 0; }
/* These two WRAP rather than end in an ellipsis. The name track is whatever is
   left of the options column after the fixed price and availability cells -
   123px on the widest desktop - and real variant names are far longer than
   that: „Tagaluuk kesklukuta + kaks küljeluuki" measures 222px, and its
   English twin 300px. An ellipsis there hides the very words that tell two
   variants apart, which is the one thing this cell exists to do. A taller row
   is the cheaper price. `anywhere` because a SKU is one unbroken token and
   would otherwise push the track wider than the card. */
.opt__name {
  display: block; font-size: var(--t-sm); color: var(--ink); font-weight: 550;
  line-height: 1.35; overflow-wrap: anywhere;
}
.opt__sku {
  display: block; font-family: var(--mono); font-size: 10px; color: var(--ink-4);
  letter-spacing: -.02em; line-height: 1.3; overflow-wrap: anywhere;
}
.opt__price {
  font-size: var(--t-base); font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; align-self: center;
  min-width: 0; text-align: right; line-height: 1.35;
}
.opt__was {
  display: block; font-size: 11px; font-weight: 500; color: var(--ink-4);
  text-decoration: line-through; text-align: right; white-space: nowrap;
}
.opt__price--sale { color: var(--sale); }
.opt__dealer {
  display: block; font-size: 10.5px; font-weight: 600; color: var(--brand-ink);
  text-align: right; text-wrap: balance;
}
.vat-note {
  font-size: 10.5px; color: var(--ink-4); padding-inline: .4rem;
  text-align: right; margin-top: .1rem;
}

/* status column */
.row__stock { display: flex; flex-direction: column; gap: .2rem; align-content: start; padding-top: .3rem; }
.stock { display: inline-flex; align-items: center; gap: .4rem; font-size: var(--t-sm); font-weight: 600; }
.stock--in   { color: var(--ok); }
.stock--soon { color: var(--warn); }
.stock--lead { color: var(--ink-3); font-weight: 550; }
.row__stock small { font-size: var(--t-xs); color: var(--ink-4); }

.row__cta { display: flex; flex-direction: column; gap: .35rem; justify-content: center; }

/* --- responsive card -----------------------------------------------------
   1400px, not 1080. The four-column row carries 150px of photo, a 424px
   options column and the buttons - about 660px that does not shrink - so
   every pixel the window loses comes out of the title column alone. Measured
   down the range: 358px of title at 1440, 174px at 1280, and 17px at 1120,
   where the description was six words on twenty lines. The page itself stops
   growing at --maxw (1440px), so the side-by-side row now runs only where it
   was ever comfortable, and everything below it gets the stacked card - which
   hands the same content a 500px-wide column instead of a sliver. */
@media (max-width: 1400px) {
  .row {
    grid-template-columns: 108px minmax(0, 1fr);
    grid-template-areas: "media main" "opts opts" "cta cta";
    gap: .6rem .75rem;
  }
  .row__media { grid-area: media; }
  .row__main  { grid-area: main; }
  .row__opts  { grid-area: opts; border-top: 1px solid var(--line); padding-top: .5rem; }
  .row__cta   { grid-area: cta; flex-direction: row; justify-content: flex-end; align-items: center; }
}
@media (max-width: 420px) {
  .row { padding: .6rem; grid-template-columns: 84px minmax(0, 1fr); }
}

/* --- sentinel / empty ---------------------------------------------------- */
.sentinel { height: 1px; }
.loading, .empty { text-align: center; padding: 2.5rem 1rem; color: var(--ink-3); }
.empty h3 { font-size: var(--t-lg); margin-bottom: .4rem; }
.empty .btn { margin-top: .9rem; }

/* --- mobile facet trigger ------------------------------------------------ */
.facet-fab {
  display: none;
  position: fixed; left: 50%; translate: -50% 0; bottom: 1rem; z-index: 80;
  background: var(--ink); color: var(--bg);
  padding: .6rem 1.1rem; border-radius: 999px; box-shadow: var(--shadow-3);
  font-weight: 650; font-size: var(--t-sm); gap: .45rem; align-items: center;
}
@media (max-width: 940px) { .facet-fab { display: inline-flex; } }
.scrim {
  position: fixed; inset: 0; z-index: 85; background: rgb(0 0 0 / .45);
  backdrop-filter: blur(2px);
}

/* ==========================================================================
   LIGHTBOX (native <dialog>)
   ========================================================================== */
dialog.lb {
  border: 0; padding: 0; background: transparent; max-width: 100vw; max-height: 100dvh;
  width: 100%; height: 100%; color: var(--ink);
}
dialog.lb::backdrop { background: rgb(0 0 0 / .84); backdrop-filter: blur(3px); }
.lb__inner { display: grid; grid-template-rows: auto 1fr auto; height: 100%; gap: .5rem; padding: .75rem; }
.lb__bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; color: #fff; }
.lb__bar h3 { color: #fff; font-size: var(--t-md); }
.lb__close { color: #fff; width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center; }
.lb__close:hover { background: rgb(255 255 255 / .16); }
.lb__stage { display: grid; place-items: center; min-height: 0; position: relative; }
.lb__stage img { max-height: 100%; max-width: 100%; object-fit: contain; border-radius: var(--radius-sm); }
.lb__nav {
  position: absolute; top: 50%; translate: 0 -50%;
  width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
  background: rgb(255 255 255 / .14); color: #fff;
}
.lb__nav:hover { background: rgb(255 255 255 / .3); }
.lb__nav--prev { left: .25rem; } .lb__nav--next { right: .25rem; }
.lb__thumbs { display: flex; gap: .4rem; justify-content: center; flex-wrap: wrap; }
.lb__thumbs button {
  width: 56px; height: 44px; border-radius: var(--radius-sm); overflow: hidden;
  border: 2px solid transparent; background: rgb(255 255 255 / .1);
}
.lb__thumbs button[aria-current="true"] { border-color: var(--brand); }
.lb__thumbs img { width: 100%; height: 100%; object-fit: contain; }

/* ==========================================================================
   ADMIN - no separate admin area. These controls appear inside the catalogue
   once you are logged in as admin and flip edit mode on.
   ========================================================================== */
.adminbar {
  display: flex; flex-wrap: wrap; gap: .3rem .4rem; align-items: center;
  background: var(--surface); border: 1px solid var(--brand-line);
  border-radius: var(--radius); padding: .4rem .55rem; margin-bottom: .75rem;
}
.adminbar__note { font-size: var(--t-xs); color: var(--ink-4); margin-inline-start: auto; }

/* --- inline stock stepper, in the catalogue row -------------------------- */
.stepper {
  display: inline-flex; align-items: center; gap: 2px; margin-top: .3rem;
  position: relative;
}
/* The stepper occupies the same cell the availability text does when not
   editing - it replaces it rather than adding a row. */
.opt .stepper { margin-top: 0; }
.opt .stepper button { width: 26px; height: 26px; font-size: 15px; }
.opt .stepper input  { width: 38px; height: 26px; font-size: var(--t-sm); }

/* Below ~620px the fixed tracks leave nothing for the option name, so the
   availability cell drops to its own line under the name and price. */
@media (max-width: 620px) {
  .row__opts .opt,
  .row__opts--adm .opt {
    grid-template-columns: 15px minmax(0, 1fr) auto;
    grid-template-areas: "r l p" "a a k";
    row-gap: .3rem;
  }
  .row__opts .opt--bare,
  .row__opts--adm .opt--bare {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "l p" "a k";
  }
  /* scoped to the catalogue: the product page uses .opt too, with a plain
     three-column template that has no named areas to place into */
  .row__opts .opt > .opt__radio { grid-area: r; }
  .row__opts .opt > .opt__label { grid-area: l; }
  .row__opts .opt > .opt__price { grid-area: p; }
  .row__opts .opt > .opt__link  { grid-area: k; justify-self: end; }
  .row__opts .opt > .opt__avail,
  .row__opts .opt > .stepper    { grid-area: a; justify-self: start; }
}

/* badges beside the product name */
.row__badges { display: inline-flex; gap: .3rem; vertical-align: middle; margin-inline-start: .45rem; }
.stepper button {
  width: 32px; height: 32px; flex: none; border-radius: var(--radius-sm);
  border: 1px solid var(--line-2); background: var(--surface); color: var(--ink-2);
  font-size: 17px; font-weight: 700; line-height: 1;
  display: grid; place-items: center; touch-action: manipulation;
}
.stepper button:hover { border-color: var(--brand); color: var(--brand-ink); background: var(--brand-wash); }
.stepper button:active { scale: .94; }
.stepper input {
  width: 50px; height: 32px; text-align: center; font-weight: 700;
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--line-2); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink); padding: 0 .2rem;
  -moz-appearance: textfield;
}
.stepper input::-webkit-outer-spin-button,
.stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper input:focus-visible { border-color: var(--brand); outline: none; }
.stepper.is-zero input { color: var(--ink-4); }

.stepper__saved {
  position: absolute; left: calc(100% + .4rem); top: 50%; translate: 0 -50%;
  display: inline-flex; align-items: center; gap: .2rem; white-space: nowrap;
  font-size: var(--t-xs); font-weight: 700; color: var(--ok);
  background: var(--ok-wash); border-radius: 999px; padding: .12rem .45rem;
  animation: savedIn .18s ease;
}
@keyframes savedIn { from { opacity: 0; translate: -4px -50%; } to { opacity: 1; translate: 0 -50%; } }
@media (max-width: 1080px) { .stepper__saved { position: static; translate: none; animation: none; } }

/* note button is always present so rows stay the same width; it just dims
   when there is nothing written yet */
.rownote { color: var(--ink-4); }
.rownote svg { opacity: .5; }
.rownote.has-note { color: var(--brand-ink); }
.rownote.has-note svg { opacity: 1; }
.rownote:hover { color: var(--brand-ink); background: var(--brand-wash); }

/* editing affordances on each row */
.row--edit { border-color: var(--brand-line); }
.row__admin { display: flex; gap: .25rem; align-items: center; margin-top: .4rem; }
.row__admin .icon-btn { width: 28px; height: 28px; border-color: var(--line); }
.row__admin .icon-btn:hover { border-color: var(--brand-line); color: var(--brand-ink); background: var(--brand-wash); }
.row--archived { opacity: .55; }
.row--archived .row__media { filter: grayscale(1); }

/* ---- the sheet: one window for every admin dialog ------------------------
   The product editor used to be a drawer pinned to the right edge and the
   rest were dialogs of three different widths. They are one shell now, in
   the middle of the screen at about 80% of it, so the page stays visible
   behind the dim - you can still see WHAT you are editing.

   Three rows: head, body, foot. Only the BODY scrolls, so the head - which
   carries Save and the close button - is reachable at any scroll position
   in a form as long as the product editor. That is the whole point of
   putting Save up there: a long form should never make you scroll to the
   bottom to keep your work.

   The height follows the content up to 80dvh rather than always filling it:
   a two-field dialog in a window sized for the product editor is mostly
   empty space. The frame is the same; only what it needs is taken. */
dialog.sheet {
  border: 0; padding: 0; background: transparent;
  /* the reset above zeroes every margin, and a dialog centres itself with
     `margin: auto` - without this the sheet sits in the top-left corner */
  margin: auto;
  width: min(94vw, 1600px); max-width: 96vw;
  /* HEIGHT, NOT MAX-HEIGHT. Every admin dialog is the same window, and a
     window that resized itself to whatever was in it made the screen jump
     every time a list was filtered, a form was opened over it, or a panel
     unfolded - the close button moving under the pointer between one click
     and the next. Fixed, the frame holds still and the content scrolls
     inside it, which is what .sheet--fill already did for the description
     editor and what made that one the calm screen of the set. */
  height: min(92dvh, 1100px);
}
dialog.sheet::backdrop { background: rgb(0 0 0 / .5); backdrop-filter: blur(2px); }
/* AND THE PAGE UNDERNEATH HOLDS STILL. A modal <dialog> makes the document
   inert to clicks but not to the wheel: pointing at the dim strip beside the
   sheet and scrolling turned the catalogue behind it, so closing the dialog
   landed somewhere nobody had chosen to go. `scrollbar-gutter` on the root
   keeps the reserved space, or the whole page would jump sideways by a
   scrollbar's width each time a dialog opened. */
html { scrollbar-gutter: stable; }
html:has(dialog[open]) { overflow: hidden; }
.sheet__panel {
  background: var(--bg-2); height: 100%;
  border-radius: var(--radius-lg); overflow: hidden;
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  box-shadow: var(--shadow-3);
}
/* .drawer__head / .drawer__foot were these two under their old names. */
.sheet__head, .sheet__foot {
  display: flex; align-items: center; gap: .6rem; padding: .8rem 1rem;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.sheet__foot { border-bottom: 0; border-top: 1px solid var(--line); }
.sheet__foot:empty { display: none; }        /* a dialog with no second row */
.sheet__head h2 { font-size: var(--t-md); margin-inline-end: auto; }
/* the generic dialog's Save is written into a slot; `contents` keeps the
   button itself a flex item of the head, so it spaces like the fixed ones */
.sheet__act { display: contents; }
/* the close button sits last, after the save, and is separated from it */
.sheet__head .icon-btn { margin-inline-start: .15rem; }
/* align-content: start, now that the panel has a fixed height. A grid's
   auto rows stretch to fill their container by default, so the moment the
   frame stopped shrinking to its contents, the contents started spreading to
   the frame - a two-row list drifting into the middle of the window with a
   hand's width of nothing above it. Content sits at the top; the space left
   over is simply left over. */
.sheet__body {
  min-height: 0; overflow-y: auto; padding: 1rem;
  display: grid; gap: 1rem; align-content: start;
  /* EVERY ROW AT ITS CONTENT HEIGHT, and this is the rule that makes a long
     screen reachable at all.

     A grid item is normally allowed to refuse to shrink below its content -
     but only while its own `overflow` is `visible`. The sales board sets
     `overflow: hidden` (it rounds its corners with it), which drops that
     protection to zero, so the board was squashed to whatever was left in the
     window and CLIPPED the deals underneath. The body then measured itself as
     exactly full, decided there was nothing to scroll to, and the rows below
     an unfolded deal simply could not be reached - no scrollbar, no wheel, no
     keyboard. Every panel with rounded corners in here is one line of CSS away
     from the same trap, so it is answered once, for the container. */
  grid-auto-rows: max-content;
  /* SCROLL STOPS HERE. Without this the wheel, on reaching the end of a long
     form, went on turning the CATALOGUE behind the dim - so scrolling down to
     the last line of an offer quietly walked the page underneath instead, and
     the way back up was somebody else's list. Every inner scroller in this
     file that sits over something else says the same thing. */
  overscroll-behavior: contain;
}
/* Below this the window is the screen: at phone width 80% of a small screen
   is not a window, it is a cramped one. */
@media (max-width: 760px) {
  dialog.sheet { width: 100vw; max-width: 100vw; max-height: 100dvh; height: 100dvh; }
  .sheet__panel { border-radius: 0; max-height: 100dvh; height: 100%; }
}

/* ---- .sheet--screen: the ones that stopped being windows ------------------
   THE SALES DESK, THE PRODUCT EDITOR AND TARNED ARE NOT DIALOGS. A dialog is
   for a question you answer and dismiss - a tag to tick, a price to confirm.
   These three are places you WORK: a board of every deal with one of them
   unfolded, a product form ten fieldsets long, a list of deliveries you mark
   off one by one. Sitting them in a window over a dimmed catalogue cost twice
   over - the catalogue was a distraction nobody was reading, and the window
   was the reason the last product line was below the fold.

   So at this size they take the whole screen. Same <dialog>, deliberately:
   the top layer, the focus trap and the Escape key are all correct behaviour
   we would have had to rebuild by hand on a plain <div>, and rebuilding them
   is how a screen ends up half-modal. What changes is only that there is
   nothing left around the edges - no dim, no gutter, nothing behind to
   scroll, and one thing on screen at a time.

   The exit is a labelled button rather than a ✕, because at full width there
   is no page showing round the outside to tell you what you would be going
   back to. */
dialog.sheet--screen {
  width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh;
}
dialog.sheet--screen::backdrop { background: var(--bg); backdrop-filter: none; }
.sheet--screen .sheet__panel { border-radius: 0; box-shadow: none; }
/* the head is the screen's own bar now, so it earns a little more room and
   sticks to the top edge rather than floating inside a frame */
.sheet--screen .sheet__head { padding: .7rem clamp(.8rem, 2vw, 1.4rem); }
.sheet--screen .sheet__body { padding: 1rem clamp(.8rem, 2vw, 1.4rem) 2rem; }
/* Content that was drawn for an 80vw window would run to a 2560px line here,
   which is unreadable. The board and the forms get the width they need and
   the rest is margin. */
.sheet--screen .sheet__body > * { max-width: 1800px; width: 100%; }
.sheet--screen .sheet__body { justify-items: center; }
.sheet--screen .sheet__body > * { justify-self: center; }

/* The back control: only a screen has one. A screen also hides the ✕ - the
   labelled way out is the point, because at full width there is no page
   showing around the edges to say what is behind.

   THE PRODUCT EDITOR KEEPS BOTH, and the reason is the Salvesta beside it. It
   is the only screen here with a save: on the others (the sales desk, Tarned)
   every press writes as it goes, so leaving is only ever "go back" and the
   labelled control says that better than a glyph. In a form, leaving WITHOUT
   saving is its own act, and the place a hand goes to do it is the corner
   beside the save button - not a "← Kataloog" at the far end of the bar,
   which reads as navigation. They do the same thing today (both go through
   the same dirty guard); they do not mean the same thing. */
.sheet__back { display: none; }
.sheet--screen .sheet__back { display: inline-flex; align-items: center; gap: .35rem; }
.sheet--screen .sheet__head > .icon-btn[data-close] { display: none; }
/* …and it is separated from Salvesta, so "close" is never mis-hit for "save" */
.sheet--screen .sheet__head > .icon-btn[data-drawer-close] { margin-inline-start: .45rem; }
/* a screen's title sits next to the way out rather than starting the row */
.sheet--screen .sheet__head h2 { font-size: var(--t-lg); }

.fieldset {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: .9rem 1rem;
}
.fieldset > h3 {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-3); margin-bottom: .7rem;
}

/* ---- the product editor's shape -----------------------------------------
   ELEVEN BOXES IN ONE COLUMN IS NOT A FORM, IT IS A LIST. Every section wore
   the same border and the same small grey heading, so nothing said that the
   name mattered more than the video links - and the two things you fill first
   and check most, the price and the description, were at the bottom of it.

   Two columns, and the side is the argument. LEFT is what you WRITE - who the
   product is, what it says, what it costs, how many are on the shelf. RIGHT
   is what you ATTACH - pictures, files, the tags that decide where it turns
   up, the products that hang off it. The left is the wider of the two because
   it is the one with sentences in it.

   Under 1100px it is one column in the same order, which is also the order a
   product actually gets filled in, so nothing about the narrow view is a
   compromise. ------------------------------------------------------------ */
.pedit { display: grid; gap: 1rem; align-items: start; }
.pedit__col { display: grid; gap: 1rem; align-content: start; min-width: 0; }
.pedit__banners:empty { display: none; }
.pedit__banners > :last-child { margin-bottom: 0; }
@media (min-width: 1100px) {
  .pedit { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
  .pedit__banners { grid-column: 1 / -1; }
}
.pedit__prices { display: grid; gap: 1rem; align-content: start; min-width: 0; }
.pedit__grouph {
  padding-inline: .2rem; font-size: var(--t-xs); text-transform: uppercase;
  letter-spacing: .06em; color: var(--ink-3); margin: .2rem 0 -.3rem;
}
/* the dealer discount: one number that moves every preview below it, so it
   rides at the top of the prices rather than in a section four screens up */
.pedit__dd { padding: .6rem .7rem; background: var(--surface-2); }
.pedit__add { display: grid; gap: .3rem; justify-items: start; padding-inline: .2rem; }

/* ---- a section within a section -----------------------------------------
   Pictures, documents and video are one question - what the product looks
   like - and were three fieldsets. A .mblock is the same content without the
   second border, so grouping them costs no extra frame. */
.mblock + .mblock, .more + .mblock { margin-top: .9rem; }
.mblock > .flabel { margin-bottom: .4rem; }
.mblock__sep { height: 1px; background: var(--line); margin: .8rem 0; }

/* ---- the fields that are real but rarely touched -------------------------
   A <details>, deliberately: the controls stay in the DOM when it is shut, so
   a value that arrived by import or by copy is never hidden from the code
   that saves it, and admin.js opens the one that already holds something. */
.more { margin-top: .7rem; }
.more > summary {
  cursor: pointer; font-size: var(--t-xs); font-weight: 650; color: var(--ink-3);
  padding: .35rem .5rem; border-radius: var(--radius-sm);
  list-style: none; display: flex; align-items: center; gap: .4rem;
}
.more > summary::-webkit-details-marker { display: none; }
.more > summary::before {
  content: '▸'; font-size: .8em; color: var(--ink-4);
  transition: transform .12s ease;
}
.more[open] > summary::before { transform: rotate(90deg); }
.more > summary:hover { background: var(--surface-2); color: var(--ink); }
.more__body { padding: .6rem .5rem 0; }
.more--quiet > summary { font-weight: 500; color: var(--ink-4); }

/* ---- variants, as tabs ---------------------------------------------------
   Several variants were several full cards stacked down the page, and by the
   third one there was no telling where one ended and the next began - nor,
   more expensively, which of the fields on screen belonged to the variant and
   which to the whole product. Somebody sets a price on „Variant 2“ believing
   it is the product's, or hunts for the description inside a variant card.

   So: a DARKER SHELL, and the boundary is a thing you can see. Everything
   inside it belongs to the one variant whose tab is down; everything above it
   - the name, the description, the dealer discount - is the product's. The
   strip is also the overview the stack never gave: every variant at once, by
   the name the person typing gave it, and a mark on any whose SKU will refuse
   to save. Every panel stays in the DOM, hidden rather than unbuilt. */
.vtabs {
  background: var(--surface-3); border: 1px solid var(--line-2);
  border-radius: var(--radius); padding: .5rem;
}
.vtabs__strip { display: flex; flex-wrap: wrap; gap: .3rem; }
.vtab {
  display: inline-flex; align-items: center; gap: .4rem;
  max-width: 15rem; padding: .35rem .6rem;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  font-size: var(--t-sm); font-weight: 600; color: var(--ink-3);
  background: transparent; cursor: pointer;
}
.vtab:hover { background: var(--surface-2); color: var(--ink); }
.vtab.is-on {
  background: var(--surface); color: var(--brand-ink);
  border-color: var(--line); box-shadow: var(--shadow-1);
}
/* the ordinal, so „Variant 2“ in an error message finds its tab */
.vtab__n {
  flex: none; min-width: 1.15rem; height: 1.15rem; border-radius: 50%;
  display: grid; place-items: center;
  font-size: var(--t-xs); font-weight: 700;
  background: var(--line); color: var(--ink-2);
}
.vtab.is-on .vtab__n { background: var(--brand); color: #fff; }
.vtab__t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vtab--bad { color: var(--sale); }
.vtab__bad {
  flex: none; width: 1.1rem; height: 1.1rem; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--sale); color: #fff; font-size: var(--t-xs); font-weight: 800;
}
.vtab--add { font-size: var(--t-md); font-weight: 700; padding-inline: .7rem; color: var(--ink-3); }
.vtab--add:hover { color: var(--brand-ink); }
.vtabs__hint {
  font-size: var(--t-xs); color: var(--ink-3);
  padding: .5rem .25rem .1rem;
}
.vtabs__hint b { color: var(--ink-2); font-weight: 650; }
/* the open panel, raised off the darker shell */
.vpanel { margin-top: .5rem; background: var(--surface); box-shadow: var(--shadow-1); }
.vpanel[hidden] { display: none; }
/* the tab names the variant, so the panel's own heading is for screen readers
   only - what is left of the head is the one action it offers, and it takes
   no height when there is none (the first variant cannot be removed) */
.vpanel__h { margin-bottom: 0; min-height: 0; }
.vpanel__h:has(button) { margin-bottom: .5rem; }
/* …and the stock fence keeps its contrast against the lighter panel */
.vpanel .vstock { background: var(--surface-2); }

/* stock and its substitutes, fenced off together: „how many are there“ and
   „what do we do when there are none“ are one question, and they used to sit
   at opposite ends of the variant card */
.vstock {
  margin-top: .8rem; padding: .7rem .75rem;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface);
}
.frow { display: grid; gap: .5rem; margin-bottom: .6rem; }
.frow:last-child { margin-bottom: 0; }
.frow--2 { grid-template-columns: 1fr 1fr; }
@media (max-width: 560px) { .frow--2 { grid-template-columns: 1fr; } }
.flabel { font-size: var(--t-xs); color: var(--ink-3); font-weight: 650; display: block; margin-bottom: .2rem; }
.flabel span { color: var(--ink-4); font-weight: 500; text-transform: none; }
textarea.field { min-height: 78px; resize: vertical; font-size: var(--t-sm); }
/* A textarea that is exactly as tall as what is in it (admin.js sizes it on
   every keystroke). No drag handle: the height is never wrong, so there is
   nothing to drag, and no minimum beyond the two rows it opens at - a short
   description should not leave a 78px hole under one line of text. */
textarea.field--grow {
  min-height: 0; resize: none; overflow: hidden;
  /* the size of the input it replaced, not the smaller one the big
     description boxes use - the same words should not shrink */
  font-size: var(--t-base);
}

/* --- translation --------------------------------------------------------- */
.trbanner {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .6rem .8rem; border-radius: var(--radius-sm); font-size: var(--t-sm);
  background: var(--warn-wash); color: var(--warn); border: 1px solid currentColor;
}
.trbanner b { color: inherit; }
.trbanner .btn { margin-inline-start: auto; }
.trbanner--ok { background: var(--ok-wash); color: var(--ok); }

.trbadge {
  display: inline-flex; align-items: center; gap: .25rem; font-size: 10px;
  font-weight: 800; letter-spacing: .04em; padding: .1rem .35rem; border-radius: 4px;
  background: var(--warn-wash); color: var(--warn); text-transform: uppercase;
}

/* the Estonian original shown above each English input */
.tr-src {
  font-size: var(--t-sm); color: var(--ink-3); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .45rem .6rem; margin-bottom: .3rem;
  max-height: 8.5rem; overflow-y: auto; overscroll-behavior: contain; white-space: pre-wrap;
}
.tr-src:empty::after { content: '—'; color: var(--ink-4); }

/* --- small html editor --------------------------------------------------- */
.ed { border: 1px solid var(--line-2); border-radius: var(--radius-sm); overflow: hidden; }
.ed__bar {
  display: flex; gap: .15rem; align-items: center; flex-wrap: wrap;
  padding: .3rem; background: var(--surface-2); border-bottom: 1px solid var(--line);
}
.ed__bar button {
  min-width: 28px; height: 26px; padding: 0 .4rem; border-radius: 4px;
  font-size: var(--t-xs); font-weight: 700; color: var(--ink-2);
}
.ed__bar button:hover { background: var(--surface); color: var(--brand-ink); }
.ed__bar .sp { margin-inline-start: auto; }
.ed textarea {
  width: 100%; border: 0; padding: .6rem .7rem; resize: vertical;
  font-family: var(--mono); font-size: 11.5px; line-height: 1.55;
  background: var(--surface); color: var(--ink); display: block;
}
.ed textarea:focus-visible { outline: none; }
.ed textarea[hidden] { display: none; }

/* --- rich text surface --------------------------------------------------- */
.ed__rich {
  min-height: 9rem; max-height: 26rem; overflow-y: auto; overscroll-behavior: contain;
  padding: .7rem .8rem; background: var(--surface); color: var(--ink);
  font-size: var(--t-sm); line-height: 1.6;
}
.ed--small .ed__rich { min-height: 4.5rem; }
.ed__rich:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--brand); }
.ed__rich:empty::before {
  content: attr(data-placeholder); color: var(--ink-4);
}
.ed__rich ul, .ed__rich ol { padding-left: 1.2rem; margin: .4rem 0; }
.ed__rich li { margin: .12rem 0; }
.ed__rich li::marker { color: var(--brand); }
.ed__rich h3, .ed__rich h4 { font-size: var(--t-md); margin: .5rem 0 .3rem; }
.ed__rich a { color: var(--brand-ink); }
.ed__rich .desc-head { font-weight: 600; color: var(--ink); }
.ed__rich .desc-head i { color: var(--ink-3); font-weight: 400; }
.ed__bar button[data-src][aria-pressed="true"] { background: var(--brand); color: #fff; }
/* the caret's current formatting, reflected on the toolbar */
.ed__bar button[data-cmd][aria-pressed="true"] {
  background: var(--brand-wash); color: var(--brand-ink);
  box-shadow: inset 0 0 0 1px var(--brand-line);
}
/* The description editor is a sheet like the rest; all it needs of its own is
   for the editor to fill the window instead of sitting at its natural height,
   which is why this body is told to be exactly as tall as the sheet allows. */
/* every panel is this height now (see dialog.sheet), so --fill has nothing
   left to say about it */
.edfull__body { padding: .75rem; background: var(--surface-2); display: block; }
.edfull__body .ed { height: 100%; display: grid; grid-template-rows: auto 1fr; }
.edfull__body .ed__rich { max-height: none; height: 100%; font-size: var(--t-base); }
.edfull__body .ed textarea { height: 100%; }

/* --- sister products ----------------------------------------------------- */
.sister {
  display: grid; gap: .5rem; padding: .6rem; border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--line); margin-bottom: .4rem;
}
.sister__head { display: flex; align-items: center; gap: .5rem; font-size: var(--t-sm); }
.sister__head b { color: var(--ink); }
.sister__head button { margin-inline-start: auto; color: var(--sale); font-size: var(--t-xs); font-weight: 650; }
.sister__why {
  font-size: var(--t-xs); color: var(--ink-3); background: var(--surface-2);
  border-radius: 4px; padding: .4rem .55rem; line-height: 1.5;
}
.sister__why b { color: var(--ink); }

/* parts of a sister combination */
.parts { display: grid; gap: .25rem; margin-top: .15rem; }
.parts__row {
  display: grid; grid-template-columns: auto minmax(0,1fr) 62px 26px;
  gap: .45rem; align-items: center;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: .25rem .3rem .25rem .5rem;
  font-size: var(--t-xs);
}
.parts__row b { font-family: var(--mono); font-size: 10.5px; color: var(--ink); }
.parts__row > span { color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.parts__row input { height: 26px; padding: 0 .3rem; text-align: center; font-size: var(--t-xs); }
.parts__row button { color: var(--ink-4); font-size: 15px; line-height: 1; }
.parts__row button:hover { color: var(--sale); }

/* inherited / invalid field states */
.field[data-inherited="true"] {
  background: var(--surface-2); color: var(--ink-3); cursor: not-allowed;
}
.field.is-bad { border-color: var(--sale); background: var(--sale-wash); }
.fhint { font-size: var(--t-xs); color: var(--ink-4); margin-top: .2rem; display: block; }
.fhint--bad { color: var(--sale); font-weight: 600; }

/* live price preview */
.preview {
  background: var(--surface-2); border: 1px dashed var(--line-2);
  border-radius: var(--radius-sm); padding: .6rem .75rem; font-size: var(--t-sm);
  display: grid; gap: .25rem; margin-top: .6rem;
}
.preview b { color: var(--ink); font-variant-numeric: tabular-nums; }
.preview s { color: var(--ink-4); }
.preview__row { display: flex; gap: .5rem; align-items: baseline; }
.preview__row span:first-child { color: var(--ink-3); min-width: 8.5rem; }

/* uploads */
.drop {
  border: 1.5px dashed var(--line-2); border-radius: var(--radius);
  padding: 1rem; text-align: center; background: var(--surface-2);
}
.drop.is-over { border-color: var(--brand); background: var(--brand-wash); }
.drop p { font-size: var(--t-sm); color: var(--ink-3); margin-bottom: .5rem; }
.drop__actions { display: flex; gap: .4rem; justify-content: center; flex-wrap: wrap; }

/* one image field: add, star the main one, edit alt text, remove */
.imggrid {
  display: grid; gap: .5rem; margin-top: .6rem;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
}
.imgcard {
  position: relative; margin: 0; aspect-ratio: 4/3; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2);
}
/* Order is the model: the first card is the main picture, the next two are
   the extras on the PDF sheet. The cards say which is which, because the only
   other clue is their position. */
.imgcard--main { border-color: var(--brand); border-width: 2px; }
.imgcard--pdf { border-color: var(--brand-line); border-width: 2px; }
.imgcard[draggable="true"] { cursor: grab; }
.imgcard.is-drag { opacity: .4; }
.imgcard.is-over { outline: 2px dashed var(--brand); outline-offset: -2px; }
.imgcard img { width: 100%; height: 100%; object-fit: contain; }
.imgcard figcaption {
  position: absolute; top: 0; left: 0; right: 0; text-align: center;
  background: var(--brand); color: #fff; font-size: 9px; font-weight: 800;
  letter-spacing: .05em; padding: 1px;
}
.imgcard .cap--pdf { background: var(--brand-wash); color: var(--brand-ink);
  border-bottom: 1px solid var(--brand-line); }
.imgcard__bar {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; justify-content: center; gap: 1px; padding: 2px;
  background: linear-gradient(transparent, rgb(0 0 0 / .55));
}
.imgcard__bar button {
  width: 24px; height: 22px; border-radius: 4px; color: #fff;
  display: grid; place-items: center; font-size: 13px; line-height: 1;
}
.imgcard__bar button:hover { background: rgb(255 255 255 / .25); }
.imgcard__bar button[disabled] { color: var(--brand); opacity: 1; pointer-events: none; }
.imgcard__bar .has-alt { color: #7ee2a0; }

.filelist { list-style: none; padding: 0; display: grid; gap: .35rem; margin-top: .5rem; }
.filelist li {
  display: flex; align-items: center; gap: .5rem; font-size: var(--t-sm);
  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: .45rem .6rem;
  background: var(--surface);
}
.filelist svg { color: var(--sale); flex: none; }
.filelist .x { margin-inline-start: auto; color: var(--ink-4); }
.filelist .x:hover { color: var(--sale); }

/* document / video rows in the editor */
.filelist li.doc { display: grid; gap: .35rem; align-items: stretch; }
.doc__top { display: flex; align-items: center; gap: .5rem; }
.doc__top b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.doc__top select { width: auto; height: 26px; padding: 0 .4rem; font-size: var(--t-xs);
                   margin-inline-start: auto; }
.doc__top .icon-btn { width: 26px; height: 26px; }
.doc__desc {
  text-align: left; font-size: var(--t-xs); color: var(--ink-2);
  border: 1px dashed var(--line-2); border-radius: 4px; padding: .3rem .45rem; width: 100%;
}
.doc__desc:hover { border-color: var(--brand); color: var(--brand-ink); }
.doc__desc.is-empty { color: var(--ink-4); }
.vidkind {
  flex: none; font-size: 10px; font-weight: 800; letter-spacing: .04em;
  background: var(--brand-wash); color: var(--brand-ink);
  border-radius: 4px; padding: .1rem .35rem;
}

.parts__row--rel { grid-template-columns: auto minmax(0,1fr) auto 26px; }
.relchk {
  display: inline-flex; align-items: center; gap: .3rem; font-size: var(--t-xs);
  font-weight: 650; color: var(--ink-3); white-space: nowrap;
}
.relchk .tag__box { width: 15px; height: 15px; }
.relchk[aria-pressed="true"] { color: var(--warn); }
.relchk[aria-pressed="true"] .tag__box { background: var(--warn); border-color: var(--warn); color: #1b1400; }

/* tag picker */
.tagpick { display: flex; flex-wrap: wrap; gap: .3rem; }
.tagpick__chip {
  display: inline-flex; align-items: center; gap: .3rem; padding: .2rem .5rem;
  border-radius: 999px; background: var(--brand-wash); border: 1px solid var(--brand-line);
  color: var(--brand-ink); font-size: var(--t-xs); font-weight: 650;
}
.tagpick__chip button { color: inherit; opacity: .6; display: grid; place-items: center; }
.tagpick__chip button:hover { opacity: 1; }
.tagpick__add {
  border: 1px dashed var(--line-2); border-radius: 999px; padding: .2rem .6rem;
  font-size: var(--t-xs); font-weight: 650; color: var(--ink-3);
}
.tagpick__add:hover { border-color: var(--brand); color: var(--brand-ink); }
.tagpick__group { margin-bottom: .7rem; }
.tagpick__group:last-child { margin-bottom: 0; }

/* --- catalogue membership (only rendered when there are two or more) ------ */
/* the "add an existing product" rows add on click, so they get a plus rather
   than a tick box - there is no selection to confirm */
.picker__plus {
  display: grid; place-items: center; width: 16px; height: 16px;
  border-radius: 50%; background: var(--brand-wash); color: var(--brand-ink);
}
.picker__opt--add:hover .picker__plus { background: var(--brand); color: #fff; }

/* a picked product from another catalogue says so - cross-catalogue cross-sell
   is intended, but it should never be a surprise */
.othercat {
  font-size: var(--t-xs); font-weight: 650; color: var(--brand-ink);
  background: var(--brand-wash); border-radius: 999px; padding: .05rem .4rem;
}
.adminbar__cat { display: inline-flex; align-items: center; gap: .35rem; }
.adminbar__cat .field { width: auto; padding: .28rem .5rem; font-size: var(--t-sm); }

.catrow {
  display: flex; align-items: flex-start; gap: .55rem; padding: .45rem .1rem;
  border-top: 1px solid var(--line); cursor: pointer;
}
.catrow:first-of-type { border-top: 0; }
.catrow input { margin-top: .15rem; accent-color: var(--brand); }
.catrow span { display: block; font-size: var(--t-sm); }
.catrow .fhint { display: block; }

/* one block of tag groups per catalogue the product is listed in, so it is
   never ambiguous which catalogue a tag is being added to */
.catblock { border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .6rem .65rem; margin-bottom: .6rem; }
.catblock:last-child { margin-bottom: 0; }
.catblock--here { border-color: var(--brand-line); background: var(--brand-wash); }
/* the hint after the name can be a full sentence: it wraps, it does not push
   the drawer sideways */
.catblock__head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .1rem .5rem;
  margin-bottom: .5rem; }
.catblock__head b { font-size: var(--t-sm); }
.catblock__head .fhint { min-width: 0; }

.picker { display: grid; gap: .4rem; max-height: 46dvh; overflow-y: auto;
         overscroll-behavior: contain; }
.picker__opt {
  display: grid; grid-template-columns: 16px 1fr auto; gap: .5rem; align-items: center;
  padding: .38rem .5rem; border-radius: var(--radius-sm); text-align: left; width: 100%;
  font-size: var(--t-sm); color: var(--ink-2);
}
.picker__opt:hover { background: var(--surface-2); }
.picker__opt[aria-pressed="true"] { color: var(--ink); font-weight: 600; }
.picker__opt[aria-pressed="true"] .tag__box { background: var(--brand); border-color: var(--brand); color: #fff; }

/* --- tag manager --------------------------------------------------------- */
.tm { display: grid; gap: .6rem; }
.tm__hint { font-size: var(--t-sm); color: var(--ink-3); }
.tmg {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2); overflow: hidden;
}
.tmg.is-drag { opacity: .45; }
.tmg.is-over { outline: 2px dashed var(--brand); outline-offset: -2px; }
.tmg__head {
  display: grid; grid-template-columns: 22px minmax(0,1fr) auto auto;
  gap: .4rem; align-items: center; padding: .5rem .6rem;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.tmg__head b { color: var(--ink); font-size: var(--t-base); }
.tmg__head .tag__n { font-weight: 500; }
.tmg__body { padding: .35rem .6rem .5rem; display: grid; gap: .2rem; }

.tmt {
  display: grid; grid-template-columns: 22px minmax(0,1fr) auto auto auto auto;
  gap: .4rem; align-items: center;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: .3rem .45rem; font-size: var(--t-sm);
}
.tmt.is-drag { opacity: .45; }
.tmt.is-over { outline: 2px dashed var(--brand); outline-offset: -2px; }
.tmt__names { min-width: 0; }
.tmt__et { color: var(--ink); font-weight: 600; display: block;
           overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tmt__en { color: var(--ink-3); font-size: var(--t-xs); display: block;
           overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tmt__en--missing { color: var(--warn); font-weight: 700; }

.grip {
  cursor: grab; color: var(--ink-4); display: grid; place-items: center;
  width: 22px; height: 26px; touch-action: none;
}
.grip:active { cursor: grabbing; }
.tm .icon-btn { width: 26px; height: 26px; }
.tm__move { display: inline-flex; gap: 1px; }
.tm__move button {
  width: 22px; height: 26px; border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-3); font-size: 10px; line-height: 1;
}
.tm__move button:first-child { border-radius: 4px 0 0 4px; }
.tm__move button:last-child  { border-radius: 0 4px 4px 0; }
.tm__move button:hover { color: var(--brand-ink); border-color: var(--brand); }
.tm__move button[disabled] { opacity: .3; pointer-events: none; }

.tmform {
  display: grid; grid-template-columns: 1fr 1fr auto auto; gap: .35rem;
  align-items: end; padding: .45rem; margin-top: .3rem;
  background: var(--brand-wash); border: 1px dashed var(--brand-line);
  border-radius: var(--radius-sm);
}
@media (max-width: 620px) { .tmform { grid-template-columns: 1fr 1fr; } }
.tmform .field { height: 32px; padding: 0 .5rem; font-size: var(--t-sm); }

/* --- price calculator ---------------------------------------------------- */
.calcrules { display: grid; gap: .4rem; }
.calcrule {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); padding: .5rem .6rem;
}
.calcrule__top { display: flex; align-items: center; gap: .5rem; }
.calcrule__top b { color: var(--ink); font-size: var(--t-sm); }
.calcrule__top .icon-btn { width: 24px; height: 24px; }
.calcres { margin-inline-start: auto; font-size: var(--t-md); font-variant-numeric: tabular-nums; }
.calcerr { margin-inline-start: auto; font-size: var(--t-xs); color: var(--sale); font-weight: 600; }
.calcrule__f {
  display: block; margin-top: .3rem; font-family: var(--mono); font-size: 10.5px;
  color: var(--ink-3); background: var(--surface-2);
  border-radius: 4px; padding: .25rem .4rem; overflow-x: auto; white-space: nowrap;
}
.calcvars { display: grid; gap: .3rem; }
.calcvar {
  display: grid; grid-template-columns: minmax(0,150px) 100px minmax(0,1fr) 24px;
  gap: .45rem; align-items: center; font-size: var(--t-xs);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: .25rem .4rem;
}
.calcvar b { font-family: var(--mono); font-size: 11px; color: var(--ink);
             overflow: hidden; text-overflow: ellipsis; }
.calcvar input { height: 26px; padding: 0 .35rem; font-size: var(--t-xs); text-align: right; }
.calcvar > span { color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calcvar button { color: var(--ink-4); font-size: 15px; }
.calcvar button:hover { color: var(--sale); }
@media (max-width: 620px) {
  .calcvar { grid-template-columns: minmax(0,1fr) 90px 24px; }
  .calcvar > span { display: none; }
}

/* dialog.modal and its panel lived here: the one-product enquiry dialog's
   frame. That dialog became the basket page (paring.html) and nothing has
   worn the class since, so the frame went with it rather than sitting here
   as a thing the next person has to work out is unused. */
.diff { font-size: var(--t-sm); display: grid; gap: .3rem; }
.diff__row { display: grid; grid-template-columns: 84px 1fr; gap: .6rem; padding: .35rem 0; border-top: 1px solid var(--line); }
.diff__tag { font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; padding: .1rem .35rem; border-radius: 4px; text-align: center; height: fit-content; }
.diff__tag--new { background: var(--ok-wash); color: var(--ok); }
.diff__tag--upd { background: var(--warn-wash); color: var(--warn); }
.diff__tag--del { background: var(--sale-wash); color: var(--sale); }
.stat-row { display: flex; flex-wrap: wrap; gap: 1.25rem; padding: .5rem 0 .9rem; }
.stat-row div b { display: block; font-size: var(--t-lg); color: var(--ink); line-height: 1.1; }
.stat-row div span { font-size: var(--t-xs); color: var(--ink-3); }
/* The toast is a popover (see admin.js), which puts it in the top layer -
   above a modal dialog's backdrop, where every z-index in this file is not.
   The UA stylesheet gives a popover `inset: 0; margin: auto; border; padding`,
   so those are all overridden here rather than fought with. */
.toast, .toast:popover-open {
  position: fixed; inset: auto; left: 50%; translate: -50% 0; bottom: 1.25rem;
  z-index: 300; margin: 0; border: 0; overflow: visible;
  background: var(--ink); color: var(--bg); padding: .6rem 1.1rem; border-radius: 999px;
  font-size: var(--t-sm); font-weight: 600; box-shadow: var(--shadow-3);
  width: max-content; max-width: min(92vw, 40rem);
}
.toast::backdrop { background: transparent; }

/* ==========================================================================
   ROLE SWITCHER (prototype only - not shipped)
   ========================================================================== */
.rolebar {
  background: repeating-linear-gradient(45deg, #1F2937, #1F2937 10px, #263243 10px, #263243 20px);
  color: #E5E7EB; font-size: var(--t-xs);
  /* Hidden until a script says otherwise. The bar is in the MARKUP of three
     pages, and DERIVE.adaptChrome() is what removes it for a customer on the
     served site - which is only safe if that call is reached. A blocked
     localStorage used to throw before it, and the customer was left looking
     at „VAADE · Klient / Edasimüüja / Admin". So the default is off, and the
     prototype (no session) and a signed-in session each switch it on. */
  display: none;
}
.rolebar.is-live { display: block; }
.rolebar__inner { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; padding-block: .45rem; }
.rolebar__hint { opacity: .75; }
@media (max-width: 860px) { .rolebar__hint { display: none; } }
.rolebar b { color: #fff; }
.rolebar__tag {
  background: #F59E0B; color: #111; font-weight: 800; padding: .1rem .4rem;
  border-radius: 4px; letter-spacing: .04em; font-size: 10px;
}
.roles { display: inline-flex; border-radius: 999px; overflow: hidden; border: 1px solid #4B5563; }
.roles button { padding: .22rem .7rem; color: #D1D5DB; font-weight: 600; }
.roles button[aria-pressed="true"] { background: var(--brand); color: #fff; }
.rolebar a { color: #93C5FD; }
/* The switch line - what THIS view does not have, on a row of its own under
   the switcher. derive.js fills it, and only an admin session is sent the
   states it reads, so nobody else can see one. */
.rolebar__flags {
  flex-basis: 100%; display: flex; flex-wrap: wrap; gap: .1rem .5rem;
  border-top: 1px solid #3C4757; padding-top: .4rem; color: #FCD34D;
}
.rolebar__flags[hidden] { display: none; }   /* display:flex outranks [hidden] */
.rolebar__where { color: #9CA3AF; }

/* ==========================================================================
   PRODUCT PAGE
   ========================================================================== */
.crumbs {
  display: flex; flex-wrap: wrap; gap: .35rem; align-items: center;
  font-size: var(--t-xs); color: var(--ink-4); padding-block: 1rem .25rem;
}
.crumbs a { color: var(--ink-3); text-decoration: none; }
.crumbs a:hover { color: var(--brand-ink); text-decoration: underline; }
/* the tag levels are filled in as a group; contents lets each crumb inside it
   take part in the flex row, so the separators get the same gap as the rest */
#crumbTags { display: contents; }
/* four crumbs is a long line on a phone: the row wraps, and the product name
   - the one item that can be arbitrarily long - breaks rather than pushing
   the page sideways */
#crumbName { min-width: 0; overflow-wrap: anywhere; }

/* Crumbs left, pager right, one line at the top of the page. The pager never
   shrinks - it is three fixed controls; the crumbs give way and wrap, which
   they already do. */
.crumbrow { display: flex; align-items: flex-start; gap: 1rem; }
.crumbrow .crumbs { flex: 1 1 auto; min-width: 0; }

/* The pager: previous / back to the list / next, for walking the filtered
   catalogue without returning to it between products. Deliberately small and
   quiet - it is a way THROUGH the page, not part of the product - and it is
   never printed (see the print block below). */
.pager {
  flex: 0 0 auto; display: flex; align-items: center; gap: .3rem;
  padding-block: 1rem .25rem;
}
.pager__at {
  font-size: var(--t-xs); color: var(--ink-4); white-space: nowrap;
  font-variant-numeric: tabular-nums; margin-inline-end: .15rem;
}
.pager__btn {
  display: inline-flex; align-items: center; gap: .3rem;
  min-height: 28px; padding-inline: .45rem;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink-2);
  font-size: var(--t-xs); font-weight: 600; text-decoration: none;
  white-space: nowrap;
}
.pager__btn:hover { border-color: var(--brand); color: var(--brand-ink); background: var(--brand-wash); }
.pager__btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* an end of the list stays in place and stops responding, rather than
   vanishing and sliding the other two under the cursor */
.pager__btn.is-off { opacity: .35; cursor: default; }

/* On a phone the three controls stay; the one word does not. */
@media (max-width: 640px) {
  .pager__lbl { display: none; }
  .pager__at { font-size: 11px; }
}

.pdp {
  display: grid; gap: clamp(1.25rem, 3vw, 2.5rem);
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  align-items: start; padding-block: 1rem 3rem;
}
@media (max-width: 900px) { .pdp { grid-template-columns: 1fr; } }

.gal { position: sticky; top: 78px; display: grid; gap: .5rem; }
@media (max-width: 900px) { .gal { position: static; } }
.gal__main {
  aspect-ratio: 4 / 3; border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; background: var(--surface); position: relative; width: 100%;
}
.gal__main img { width: 100%; height: 100%; object-fit: contain; }
.gal__main button { position: absolute; inset: 0; width: 100%; cursor: zoom-in; }
.gal__hint {
  position: absolute; right: .5rem; bottom: .5rem;
  background: color-mix(in srgb, var(--ink) 76%, transparent); color: #fff;
  border-radius: 999px; padding: .22rem .6rem; font-size: var(--t-xs); font-weight: 600;
  pointer-events: none;
}
.gal__thumbs { display: flex; flex-wrap: wrap; gap: .4rem; }
.gal__thumbs button {
  width: 68px; aspect-ratio: 4/3; border-radius: var(--radius-sm); overflow: hidden;
  border: 2px solid var(--line); background: var(--surface);
}
.gal__thumbs button[aria-current="true"] { border-color: var(--brand); }
.gal__thumbs img { width: 100%; height: 100%; object-fit: contain; }

.pdp__head h1 { font-size: var(--t-2xl); letter-spacing: -.02em; margin-bottom: .35rem; }
/* same rule the catalogue rows carry under the product name */
.pdp__rule { border: 0; border-top: 1.5px solid color-mix(in srgb, var(--brand) 55%, transparent);
  margin: .45rem 0 .55rem; width: 100%; }
/* the product's tags, in tag-manager order, each a link into that filter */
.pdp__tags { display: flex; flex-wrap: wrap; gap: .3rem; align-items: center; margin-bottom: 1.1rem; }
.pdp__tags .rowtag { text-decoration: none; }

.buybox {
  position: relative;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); padding: 1rem; margin-bottom: 1.25rem;
}
/* which prices these are, said on the box rather than only in the role bar */
.tradebadge {
  position: absolute; top: .55rem; right: .6rem;
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .02em;
  color: var(--brand-ink); background: var(--brand-wash);
  border: 1px solid var(--brand-line); border-radius: 999px;
  padding: .12rem .55rem;
}
.tradebadge[hidden] { display: none; }
.buybox h2 { font-size: var(--t-sm); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); margin-bottom: .6rem; }
.buybox .opt { border: 1px solid var(--line); margin-bottom: .35rem; }
.buybox .opt[aria-checked="true"] { border-color: var(--brand); }

.price-now { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; margin: .9rem 0 .2rem; }
.price-now b { font-size: var(--t-2xl); color: var(--ink); letter-spacing: -.02em; line-height: 1; }
.price-now b.is-sale { color: var(--sale); }
.price-now s { color: var(--ink-4); font-size: var(--t-md); }
/* Rides on the price it qualifies, never on a line of its own: it is part of
   what the number means. Inside the <b> (or the .opt__price) so it shares the
   baseline, with its own size and weight so it does not shout. */
.price-note {
  font-size: var(--t-xs); font-weight: 500; color: var(--ink-4);
  margin-inline-start: .3rem; white-space: nowrap;
}
.price-dealer {
  margin-top: .5rem; padding: .5rem .7rem; border-radius: var(--radius-sm);
  background: var(--brand-wash); border: 1px solid var(--brand-line);
  color: var(--brand-ink); font-size: var(--t-sm); font-weight: 600;
}
.pdp__actions { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1rem; }

.spec { list-style: none; padding: 0; display: grid; gap: 0; margin-top: 1.25rem; }
.spec li {
  display: grid; grid-template-columns: minmax(110px, 34%) 1fr; gap: 1rem;
  padding: .55rem 0; border-top: 1px solid var(--line); font-size: var(--t-sm);
}
.spec dt, .spec .k { color: var(--ink-3); }
.spec .v { color: var(--ink); font-weight: 550; }

.prose { margin-top: 1.5rem; font-size: var(--t-base); color: var(--ink-2); }
.prose .desc-head { font-size: var(--t-md); color: var(--ink); margin-bottom: .75rem; }
.prose .desc-head i { color: var(--ink-3); font-size: var(--t-sm); }
.prose ul { padding-left: 1.15rem; display: grid; gap: .3rem; }
.prose li::marker { color: var(--brand); }
.prose a { color: var(--brand-ink); }

/* --- videos live in the gallery strip, after the images ------------------ */
.vid__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: rgb(0 0 0 / .22); color: #fff; pointer-events: none;
}
.vid__play svg { filter: drop-shadow(0 2px 8px rgb(0 0 0 / .6)); }
.vid__none { display: grid; place-items: center; height: 100%; color: var(--ink-4); }

.gal__thumbs button.is-vid { position: relative; }
.thumbplay {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: rgb(0 0 0 / .3); color: #fff; pointer-events: none;
}
.lb__thumbs button.is-vid { position: relative; }

/* lightbox slot holds an image, an iframe or a video element */
.lb__slot { display: grid; place-items: center; width: 100%; height: 100%; min-height: 0; }
.lb__slot img { max-height: 100%; max-width: 100%; object-fit: contain; border-radius: var(--radius-sm); }
.lb__embed { width: min(1200px, 92vw); aspect-ratio: 16/9; max-height: 100%; }
.lb__embed iframe, .lb__embed video {
  width: 100%; height: 100%; border: 0; border-radius: var(--radius-sm); background: #000;
}

.atts { list-style: none; padding: 0; display: grid; gap: .4rem; margin-top: .75rem; }
.att__meta { display: block; font-size: var(--t-xs); color: var(--ink-3); font-weight: 400; margin-top: .1rem; }
.att--admin { border-style: dashed; border-color: var(--brand-line); }

/* --- related products / spare parts tabs --------------------------------- */
.tabs { display: flex; gap: .25rem; border-bottom: 1px solid var(--line); margin-bottom: 1rem; flex-wrap: wrap; }
.tabs button {
  padding: .55rem .9rem; font-weight: 650; font-size: var(--t-base);
  color: var(--ink-3); border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabs button:hover { color: var(--ink); }
.tabs button[aria-selected="true"] { color: var(--brand-ink); border-bottom-color: var(--brand); }
.tabs .tag__n { font-weight: 500; }

.relbar {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin-bottom: .75rem; font-size: var(--t-sm); color: var(--ink-3);
}

/* --- the actions, following you down the page ---------------------------- */
.stickyacts {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--line);
  padding: .5rem 0 max(.5rem, env(safe-area-inset-bottom));
}
.stickyacts[hidden] { display: none; }
.stickyacts__in { display: flex; gap: .5rem; align-items: center; }
.stickyacts .btn { flex: 1 1 auto; justify-content: center; }
@media (min-width: 700px) { .stickyacts .btn { flex: 0 0 auto; } }
/* three labels on a narrow phone: shrink the type rather than wrap or clip */
@media (max-width: 520px) {
  .stickyacts .btn { padding-inline: .5rem; font-size: var(--t-xs); }
}

.relrow { position: relative; }
.relrow .row__media { width: 110px; }
.relrow--rec { border-color: var(--warn); border-width: 2px; padding: calc(.75rem - 1px); }
.relpick {
  width: 17px; height: 17px; flex: none; border: 1.5px solid var(--line-2);
  border-radius: 4px; display: grid; place-items: center; color: transparent;
  align-self: center;
}
.relpick[aria-pressed="true"] { background: var(--brand); border-color: var(--brand); color: #fff; }
.ribbon--rec span { background: var(--warn); color: #1b1400; }
.atts a {
  display: flex; align-items: center; gap: .6rem; text-decoration: none;
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: .6rem .75rem; background: var(--surface); color: var(--ink);
  font-size: var(--t-sm); font-weight: 550;
}
.atts a:hover { border-color: var(--brand-line); background: var(--brand-wash); color: var(--brand-ink); }
.atts svg { color: var(--sale); flex: none; }
.atts small { margin-inline-start: auto; color: var(--ink-4); font-weight: 500; }

/* --- the note under the gallery, and the public extra info --------------- */
.gal__note { margin-top: .5rem; font-size: var(--t-xs); color: var(--ink-4); }
.xinfo {
  margin-top: 1.5rem; border-left: 3px solid var(--brand-line);
  background: var(--brand-wash); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: .8rem 1rem;
}
.xinfo h2 { font-size: var(--t-md); margin-bottom: .3rem; }
.xinfo .prose { margin-top: 0; }

/* Printing the product page itself is not the product sheet - that is its own
   document at pdf.html, opened by the PDF button. What is left here is only
   enough to keep a browser's Ctrl+P from looking broken. */
@media print {
  @page { size: A4 portrait; margin: 12mm; }
  .gal { position: static; }
  .gal__thumbs, .pdp__actions, .crumbrow, .crumbs, .pager,
  .stickyacts, #relWrap { display: none !important; }
  .pdp { grid-template-columns: 1.05fr 1fr; gap: 8mm; }
  .pdp__head h1 { font-size: 16pt; }
  .buybox { border: 1px solid #ccc; }
  .prose { font-size: 9.5pt; }
}

/* ==========================================================================
   HOME
   ========================================================================== */
/* --- the hero band ---------------------------------------------------------
   ONE IMAGE, EDGE TO EDGE, and nothing else with a border on it. The photo
   used to sit in a rounded, shadowed card beside a rounded card of text
   inside a bordered band - three rectangles arguing about which one you were
   meant to look at. Now the band IS the photo, and the page fades into it
   from the left with a scrim in the surface colour, so the heading keeps the
   ordinary ink colours (and the brand orange in the <em>) against solid
   ground rather than being knocked out in white over grass.

   NOT FULL HEIGHT. It is capped by clamp() at a band, not a screen: the car
   cards are the point of the front page and they must be visible without
   scrolling past a poster to reach them.

   THE PHOTO IS NARROWER THAN THE BAND, and pinned right. That is what zooms
   it out: `cover` scales an image to fill the box it is given, so a
   full-width box on a 4:3 photo enlarges it until only a 40% slice of its
   height fits, and the truck arrives cropped and enormous. Given three
   quarters of the width it scales down instead - more sky, more ground, a
   whole vehicle - and it starts further from the words. Its left edge is
   masked away rather than trusted to land under the opaque end of the scrim:
   both are percentages of the same band, so they do track each other at
   every width, but a seam down the middle of a hero is not a thing to leave
   to arithmetic.

   The scrim turns VERTICAL below 880px, where the column is too narrow for a
   left-hand fade to leave the photo anything: bottom-weighted, so the words
   sit on the darkened lower half and the truck keeps the top.
   ------------------------------------------------------------------------- */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; align-items: center;
  min-height: clamp(340px, 34vw, 500px);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  padding-block: clamp(2rem, 5vw, 3.5rem);
}
.hero__bg {
  position: absolute; inset: 0 0 0 auto; z-index: -2;
  width: 70%; height: 100%; object-fit: cover; object-position: 50% 56%;
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 22%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 22%);
}
.hero::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(100deg,
    var(--surface) 0%,
    var(--surface) 30%,
    color-mix(in srgb, var(--surface) 72%, transparent) 45%,
    color-mix(in srgb, var(--surface) 26%, transparent) 64%,
    color-mix(in srgb, var(--surface) 6%, transparent) 82%,
    transparent 100%);
}
.hero__inner { position: relative; max-width: min(var(--maxw), 100%); }
.hero h1 {
  font-size: var(--t-3xl); letter-spacing: -.025em; margin-bottom: 1.1rem;
  max-width: 15ch; text-wrap: balance;
}
.hero h1 em { font-style: normal; color: var(--brand-ink); }
.hero__actions { display: flex; flex-wrap: wrap; gap: .6rem; }
.hero__actions .btn { padding: .7rem 1.25rem; font-size: var(--t-md); }
@media (max-width: 880px) {
  .hero {
    min-height: clamp(380px, 88vw, 520px); align-items: end;
    padding-block: 2rem 1.75rem;
  }
  /* full width again: there is no room beside the words on a phone, so the
     photo takes the whole band and the scrim turns vertical under it */
  .hero__bg {
    inset: 0; width: 100%; object-position: 56% 42%;
    -webkit-mask-image: none; mask-image: none;
  }
  .hero::after {
    background: linear-gradient(180deg,
      transparent 0%,
      transparent 24%,
      color-mix(in srgb, var(--surface) 50%, transparent) 44%,
      color-mix(in srgb, var(--surface) 90%, transparent) 62%,
      var(--surface) 78%);
  }
  .hero h1 { max-width: none; }
}

.section { padding-block: clamp(2.25rem, 5vw, 3.75rem); }
.section__head { max-width: 62ch; margin-bottom: 1.75rem; }
.section__head h2 { font-size: var(--t-2xl); letter-spacing: -.02em; margin-bottom: .5rem; }
.section__head p { color: var(--ink-3); }

/* --- the car blocks --------------------------------------------------------
   One card per make: the photo, the make, and every model generation under
   it, each a link straight into that filter. The makes and the models are
   read from the catalogue's own model tags - a new generation appears here
   the moment its tag exists, and a make with no photo of its own falls
   through into "Muud autod" rather than showing an empty frame. */
.carcards {
  display: grid; gap: 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
}
@media (min-width: 1080px) { .carcards { grid-template-columns: repeat(4, 1fr); } }

.carcard {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
  transition: border-color .14s, box-shadow .14s, transform .14s;
}
.carcard:hover { border-color: var(--brand-line); box-shadow: var(--shadow-2); transform: translateY(-2px); }
.carcard__img { aspect-ratio: 16 / 10; background: var(--surface-2); overflow: hidden; }
.carcard__img img { width: 100%; height: 100%; object-fit: cover; transition: scale .35s; }
.carcard:hover .carcard__img img { scale: 1.04; }
.carcard__body { padding: 1rem 1.1rem 1.1rem; display: flex; flex-direction: column; gap: .55rem; flex: 1; }
.carcard__make {
  font-size: var(--t-md); letter-spacing: -.015em;
}
.carcard__models { list-style: none; padding: 0; display: grid; gap: .1rem; }
.carcard__models a {
  display: flex; align-items: baseline; justify-content: space-between; gap: .6rem;
  padding: .34rem .45rem; margin-inline: -.45rem; border-radius: var(--radius-sm);
  font-size: var(--t-base); font-weight: 550; color: var(--ink-2); text-decoration: none;
}
.carcard__models a:hover { background: var(--brand-wash); color: var(--brand-ink); }
.carcard__models em {
  font-style: normal; font-size: var(--t-xs); color: var(--ink-4);
  font-variant-numeric: tabular-nums;
}

/* The two cards with no photo: same frame, different weight. They sit under
   the makes, side by side, and say what is here and what is on the way. */
.carcards--plain { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); margin-top: 1.25rem; }
@media (min-width: 1080px) { .carcards--plain { grid-template-columns: 1fr 1fr; } }
.carcard--plain { background: var(--surface-2); }
.carcard--plain .carcard__body { padding: 1.25rem 1.35rem 1.35rem; }
.carcard__soon { font-size: var(--t-sm); color: var(--ink-4); }

/* --- the brands ------------------------------------------------------------
   Same shape as the makes above: a brand we have written a page about gets a
   picture and two sentences, and the rest are a list in one plain card. It
   was a strip of ten chips, then ten small cards, and neither could point at
   one brand harder than at the other nine (Rein, 06.09.2026).

   The section carries a TINT so it reads as a different question - "who makes
   this" rather than "what fits my car". Mixed off --ink rather than hard-coded
   so it steps AWAY from the page in both themes: 4% black over the light
   page, 4% white over the dark one. */
.section--tint {
  background: color-mix(in srgb, var(--ink) 4%, var(--bg-2));
  border-block: 1px solid var(--line);
}

.brandbands { display: grid; gap: 1.25rem; }
.brandband {
  display: grid; grid-template-columns: minmax(0, 1fr);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
  transition: border-color .14s, box-shadow .14s;
}
/* the picture keeps its own column once there is room for one */
@media (min-width: 860px) { .brandband { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); } }
.brandband:hover { border-color: var(--brand-line); box-shadow: var(--shadow-2); }
/* Stacked, the picture keeps a shape of its own. Side by side, the TEXT sets
   the height and the picture covers whatever that turns out to be - the other
   way round left a 16/10 image dictating a band half of which was empty. */
.brandband__img { display: block; aspect-ratio: 16 / 10; background: var(--surface-2); overflow: hidden; }
.brandband__img img { width: 100%; height: 100%; object-fit: cover; transition: scale .35s; }
@media (min-width: 860px) {
  /* `height: 100%` is not enough on its own: the <img> still contributes its
     intrinsic 16/9 to the row, so a 675px-wide column asked for 380px of band
     and left half the text side empty. Taking the picture out of flow
     entirely - absolute inside a relative frame - lets the words decide. */
  .brandband__img { aspect-ratio: auto; position: relative; min-height: 250px; }
  .brandband__img img { position: absolute; inset: 0; }
}
.brandband:hover .brandband__img img { scale: 1.03; }
.brandband__body {
  padding: clamp(1.1rem, 3vw, 1.65rem); display: flex; flex-direction: column; gap: .5rem;
}
.brandband__name { font-size: var(--t-lg); letter-spacing: -.015em; }
.brandband__lede { color: var(--ink-3); font-size: var(--t-sm); line-height: 1.6; }
.brandband__cta { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: auto; padding-top: .7rem; }
/* The CANOPY is the subject, not the truck (Rein, 06.09.2026). A short wide
   crop off a 16/9 photo centres on the doors and cuts the roof off, so the
   focal point moves up and slightly right - which is where the canopy sits in
   every one of these press shots. */
.brandband__img img { object-position: 56% 10%; }

/* --- one small card per brand ----------------------------------------------
   „Muud brändid" as a single list card did not belong there (Rein), so every
   brand that has no band of its own gets a card, inline.

   The two-letter square is gone (Rein, 08.09.2026) and the card is centred.
   It stood in for a logo that never arrived, and at card size „EG" next to
   „EGR" was the same word twice. When real logos come they belong ABOVE the
   name in this centred column, not beside it. */
.bcards {
  display: grid; gap: .7rem; margin-top: 1.25rem;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
.bcard {
  display: flex; align-items: center; justify-content: center;
  padding: .7rem .75rem; text-decoration: none; text-align: center;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color .14s, box-shadow .14s, transform .14s;
}
.bcard:hover { border-color: var(--brand-line); box-shadow: var(--shadow-2); transform: translateY(-1px); }
.bcard__text { display: grid; gap: .05rem; min-width: 0; justify-items: center; }
.bcard__name {
  font-size: var(--t-base); font-weight: 650; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bcard em {
  font-style: normal; font-size: var(--t-xs); color: var(--ink-4);
  font-variant-numeric: tabular-nums;
}

.brands { display: flex; flex-wrap: wrap; gap: .5rem; }
.brands a {
  border: 1px solid var(--line); border-radius: 999px; padding: .45rem 1rem;
  font-weight: 650; color: var(--ink-2); text-decoration: none; background: var(--surface);
  font-size: var(--t-sm);
}
.brands a:hover { border-color: var(--brand-line); color: var(--brand-ink); background: var(--brand-wash); }

.band {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: clamp(1.5rem, 4vw, 2.5rem);
  display: grid; gap: clamp(1.5rem, 4vw, 2.75rem);
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  align-items: start;
}
@media (max-width: 780px) { .band { grid-template-columns: 1fr; } }
.band h2 { font-size: var(--t-xl); margin-bottom: .4rem; }
.band p { color: var(--ink-3); font-size: var(--t-sm); }
.contact-list { list-style: none; padding: 0; display: grid; gap: .5rem; font-size: var(--t-sm); }
.contact-list li { display: flex; gap: .55rem; align-items: flex-start; }
.contact-list svg { color: var(--brand); flex: none; margin-top: .15rem; }
.contact-list a { color: var(--ink-2); text-decoration: none; }
.contact-list a:hover { color: var(--brand-ink); }
/* two addresses, so each one says which it is before it says where */
.contact-list b { display: block; font-size: var(--t-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-4); }
.contact-list span { color: var(--ink-2); }

/* Visiting is by arrangement - a workshop is not a shop with opening hours,
   and someone who drives out to a locked door does not come back. */
.callfirst {
  display: flex; gap: .6rem; align-items: flex-start;
  margin-top: .9rem; padding: .7rem .85rem;
  background: var(--warn-wash); border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent);
  border-radius: var(--radius); font-size: var(--t-sm); color: var(--ink-2);
}
.callfirst svg { color: var(--warn); flex: none; margin-top: .1rem; }

/* --- the front page's own enquiry form -------------------------------------
   The same endpoint the product pages post to (api/inquiry.php, honeypot and
   all), laid out inline instead of in a dialog. With the 'inquiry' switch off
   there is no form at all - the mailto buttons stay, which is the honest
   fallback for a catalogue-only site. */
.cform { display: grid; gap: .7rem; margin-top: 1.1rem; }
.cform__row { display: grid; gap: .7rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 560px) { .cform__row { grid-template-columns: 1fr; } }
.cform__foot { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
.cform__hint { font-size: var(--t-xs); color: var(--ink-4); }
.cform__err { font-size: var(--t-sm); color: var(--sale); }
.cform__hp { position: absolute; left: -9999px; }
.cform__done {
  margin-top: 1.1rem; padding: 1rem 1.15rem; border-radius: var(--radius);
  background: var(--ok-wash); border: 1px solid color-mix(in srgb, var(--ok) 35%, transparent);
}
.cform__done h3 { font-size: var(--t-md); margin-bottom: .25rem; color: var(--ink); }
.cform__done p { color: var(--ink-2); }

/* ==========================================================================
   FOOTER
   ========================================================================== */
.site-foot {
  border-top: 1px solid var(--line); background: var(--surface);
  padding-block: 2.5rem 2rem; margin-top: 3rem;
}
.foot-grid {
  display: grid; gap: 2rem;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
.foot-grid h4 { font-size: var(--t-sm); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); margin-bottom: .6rem; }
.foot-grid ul { list-style: none; padding: 0; display: grid; gap: .35rem; font-size: var(--t-sm); }
.foot-grid a { color: var(--ink-2); text-decoration: none; }
.foot-grid a:hover { color: var(--brand-ink); text-decoration: underline; }
.foot-base {
  margin-top: 2rem; padding-top: 1.25rem; border-top: 1px solid var(--line);
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  font-size: var(--t-xs); color: var(--ink-4);
}

/* ==========================================================================
   PRINT
   ========================================================================== */
@media print {
  .site-head, .site-foot, .facets, .toolbar, .rolebar, .facet-fab, .row__cta { display: none !important; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .row { break-inside: avoid; border: 1px solid #ccc; box-shadow: none; }
  /* No URL spelled out after every link: it doubled the length of the sheet
     and a printed address is not a usable one anyway. Links stay links -
     a PDF viewer keeps them clickable - and the product sheet carries one
     link icon next to the SKU for the way back. */
}

/* Coming back from a product, the row you left from is scrolled to and lit
   for a moment - long enough to find it, short enough not to look selected.
   Nothing depends on the class; it is removed on a timer. */
.row--returned { animation: rowReturned 1.6s ease-out; }
@keyframes rowReturned {
  0%, 45% { box-shadow: 0 0 0 2px var(--brand); }
  100%    { box-shadow: 0 0 0 2px transparent; }
}
@media (prefers-reduced-motion: reduce) { .row--returned { animation: none; } }

/* shipment-picker mode: the catalogue is the picker - every row keeps
   its tags and search hit, and shows exactly one control: the quantity
   stepper bound to the shipment being filled */
.rows--shippick .row__cta { display: none; }

/* the admin's incoming-goods line on a catalogue card */
.row__incoming { display: grid; gap: .15rem; border-top: 1px dashed var(--line);
  margin-top: .45rem; padding-top: .4rem; font-size: var(--t-xs); color: var(--ink-3); }
.row__incoming b { color: var(--ink-2); }
.row__incoming-title {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--ink-4);
}
/* the columns are their own grid inside the block, so the make-one line below
   them stays full width instead of becoming a third column */
.row__incoming--1 { display: grid; gap: .15rem; min-width: 0; }
.row__incoming--2 { display: grid; gap: .15rem .8rem;
  grid-template-columns: 1fr 1fr; align-items: start; }
.row__incoming--2 > div { display: grid; gap: .15rem; min-width: 0; }
/* collapsible shipment item table, SKUs copy on click */
.shipitems { margin: .3rem 0; font-size: var(--t-xs); }
.shipitems summary { cursor: pointer; color: var(--ink-3); font-weight: 600; }
.shipitems table { width: 100%; border-collapse: collapse; margin-top: .3rem; }
.shipitems td { padding: .18rem .4rem .18rem 0; border-top: 1px solid var(--line);
  vertical-align: top; }
.shipitems .r { text-align: right; white-space: nowrap; color: var(--ink-4); }
.skucopy { font-family: var(--mono); font-size: 11px; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 4px; padding: .05rem .35rem;
  cursor: copy; color: var(--ink-2); }
.skucopy:hover { border-color: var(--brand-line); color: var(--ink); }

/* ==========================================================================
   LEGAL PAGES - privacy, terms. One column of text, nothing else on it.
   ========================================================================== */
.doc { max-width: 78ch; padding-block: clamp(2rem, 5vw, 3.5rem); }
.doc h1 { font-size: var(--t-2xl); letter-spacing: -.02em; margin-bottom: .4rem; }
.doc__meta { font-size: var(--t-sm); color: var(--ink-4); margin-bottom: 2rem; }
.doc h2 {
  font-size: var(--t-md); margin: 2rem 0 .5rem;
  padding-top: 1.25rem; border-top: 1px solid var(--line);
}
.doc h2:first-of-type { border-top: 0; padding-top: 0; margin-top: 1.5rem; }
.doc p, .doc li { color: var(--ink-2); font-size: var(--t-base); line-height: 1.65; }
.doc p { margin-bottom: .75rem; }
.doc ul, .doc ol { margin: 0 0 .9rem 1.15rem; display: grid; gap: .3rem; }
.doc a { color: var(--brand-ink); }
.doc__note {
  margin-top: 2.5rem; padding: .9rem 1.1rem; border-radius: var(--radius);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: var(--t-sm); color: var(--ink-3);
}

/* The one thing on a brand page that is not prose: the supplier's own full
   catalogue. It is the answer to "is there anything else?" and it is the
   reason the page exists, so it gets the brand wash rather than another
   paragraph. Brand-coloured and not warn-coloured on purpose - .callfirst
   already owns "read this before you drive over". */
.doc__hl {
  margin: 1.4rem 0 1.6rem; padding: 1rem 1.15rem;
  background: var(--brand-wash); border: 1px solid var(--brand-line);
  border-radius: var(--radius); line-height: 1.6;
}
.doc__hl b { color: var(--ink); }
.doc__hl a { font-weight: 650; }
.doc__hl-note { display: inline-block; margin-top: .45rem;
  font-size: var(--t-sm); color: var(--ink-3); }

/* ==========================================================================
   A BRAND PAGE - brand-rhinoman.html, and whatever follows it

   It started as a .doc: one 78ch column of prose with pictures dropped in,
   which is right for the privacy policy and wrong for this. "Try to redesign
   it to be a little bit wider and a little more columns and webdesign
   elements, so it would be kind of nice designed web page not flat article"
   (Rein, 06.09.2026). So it is page sections at the site's full width: a head
   band, prose beside a facts panel, the ranges as three cards, and two
   coloured bands for the things a reader is meant to act on.

   Every piece degrades to one column, and all of it is inside the two
   [data-doc] articles - so a reader with no JavaScript still gets the whole
   Estonian page and a crawler reads it without running anything.
   ========================================================================== */
.bp-head { background: var(--surface); border-bottom: 1px solid var(--line); }
.bp-head__grid {
  display: grid; gap: clamp(1.25rem, 4vw, 2.5rem); align-items: center;
  padding-block: clamp(1.75rem, 5vw, 3rem);
}
@media (min-width: 900px) {
  .bp-head__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); }
}
.bp-kicker {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--brand-ink); margin-bottom: .5rem;
}
.bp-head__text h1 { font-size: var(--t-2xl); letter-spacing: -.02em; margin-bottom: .6rem; }
.bp-lede { color: var(--ink-2); font-size: var(--t-md); line-height: 1.55; max-width: 46ch; }
.bp-cta { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.15rem; }
.bp-head__fig { margin: 0; }
.bp-head__fig img {
  display: block; width: 100%; height: auto; aspect-ratio: 16 / 10;
  object-fit: cover; object-position: 56% 22%;
  border-radius: var(--radius-lg); border: 1px solid var(--line);
}

.bp-body { padding-block: clamp(1.75rem, 5vw, 3rem); }
.bp-h2 { font-size: var(--t-xl); letter-spacing: -.015em; margin: 2.5rem 0 1rem; }
.bp-intro { color: var(--ink-3); max-width: 68ch; margin-bottom: 1.25rem; }

/* prose beside the numbers, because the numbers are what a reader scans for
   and a paragraph is not where they should have to find them */
.bp-split { display: grid; gap: clamp(1.25rem, 3vw, 2.25rem); align-items: start; }
@media (min-width: 900px) {
  .bp-split { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); }
}
.bp-prose h2 { font-size: var(--t-lg); margin-bottom: .6rem; }
.bp-prose p { color: var(--ink-2); line-height: 1.65; margin-bottom: .8rem; max-width: 62ch; }
/* a list in the prose, not inside a range card's <details> - the supplier's
   own spec points, which are a list in the source and read as one here too */
.bp-prose ul {
  color: var(--ink-2); font-size: var(--t-sm); line-height: 1.6;
  max-width: 62ch; margin-bottom: .8rem;
  padding-left: 1.15rem; display: grid; gap: .35rem;
}
.bp-prose li::marker { color: var(--brand); }
.bp-facts {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 1.1rem 1.25rem;
}
.bp-facts h3 {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--ink-4); margin-bottom: .6rem;
}
.bp-facts dl { display: grid; gap: 0; margin: 0; }
.bp-facts dl > div {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: .5rem 0; border-top: 1px solid var(--line);
}
.bp-facts dl > div:first-child { border-top: 0; }
.bp-facts dt { color: var(--ink-3); font-size: var(--t-sm); }
.bp-facts dd { margin: 0; color: var(--ink); font-size: var(--t-sm); font-weight: 650; text-align: right; }

/* the two bands a reader is meant to act on */
.bp-promo, .bp-end {
  display: grid; gap: 1rem; align-items: center; margin-top: 2rem;
  padding: clamp(1.1rem, 3vw, 1.6rem);
  border-radius: var(--radius-lg);
}
@media (min-width: 760px) {
  .bp-promo, .bp-end { grid-template-columns: minmax(0, 1fr) auto; }
}
.bp-promo { background: var(--brand-wash); border: 1px solid var(--brand-line); }
.bp-end { background: var(--surface); border: 1px solid var(--line); }
.bp-promo h3, .bp-end h3 { font-size: var(--t-md); margin-bottom: .35rem; }
.bp-promo p, .bp-end p { color: var(--ink-2); font-size: var(--t-sm); line-height: 1.6; max-width: 64ch; }
.bp-promo__note { color: var(--ink-3) !important; margin-top: .4rem; }

/* the three ranges */
.bp-ranges {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
}
.bp-range {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
  transition: border-color .14s, box-shadow .14s;
}
.bp-range:hover { border-color: var(--brand-line); box-shadow: var(--shadow-2); }
/* the renders are drawn on white, so the frame is white in both themes -
   the page's own grey behind them shows as a paler rectangle */
.bp-range__fig { margin: 0; background: #fff; border-bottom: 1px solid var(--line); }
.bp-range__fig img { display: block; width: 100%; height: auto; }
.bp-range__body { padding: .95rem 1.1rem 1.1rem; display: flex; flex-direction: column; gap: .5rem; flex: 1; }
.bp-range__body h3 { font-size: var(--t-md); }
.bp-range__body h3 span {
  display: block; font-size: var(--t-xs); font-weight: 550; color: var(--ink-4);
  margin-top: .15rem;
}
.bp-range__one { color: var(--ink-3); font-size: var(--t-sm); line-height: 1.55; }
.bp-range__load {
  display: flex; flex-direction: column; gap: .1rem;
  padding: .55rem .65rem; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--line);
}
.bp-range__load span { font-size: var(--t-xs); color: var(--ink-4); }
.bp-range__load b { font-size: var(--t-sm); color: var(--ink); }
.bp-range details { margin-top: auto; }
.bp-range summary {
  cursor: pointer; font-size: var(--t-sm); font-weight: 650; color: var(--brand-ink);
  padding: .35rem 0;
}
.bp-range details ul {
  margin: .3rem 0 0 1.05rem; padding: 0; display: grid; gap: .25rem;
  font-size: var(--t-sm); color: var(--ink-2);
}
.bp-range details li::marker { color: var(--brand); }

.bp-note {
  margin-top: 2rem; padding: clamp(1.1rem, 3vw, 1.5rem);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.bp-note h3 { font-size: var(--t-md); margin-bottom: .4rem; }
.bp-note p { color: var(--ink-2); font-size: var(--t-sm); line-height: 1.65; max-width: 78ch; }

.bp-cars {
  display: grid; gap: .7rem;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
.bp-car {
  display: grid; gap: .1rem; padding: .7rem .85rem; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color .14s, box-shadow .14s, transform .14s;
}
.bp-car:hover { border-color: var(--brand-line); box-shadow: var(--shadow-2); transform: translateY(-1px); }
.bp-car b { font-size: var(--t-base); color: var(--ink); }
.bp-car span { font-size: var(--t-xs); color: var(--ink-4); }
.bp-allbtn { margin-top: 1.1rem; }

/* ==========================================================================
   THE ENQUIRY BASKET - paring.html, and the header button that leads to it

   Two columns on a desk (what you collected, who you are), one on a phone,
   with the basket first in both: the form is worth filling in only once the
   list above it is right.
   ========================================================================== */
/* the header button is gone: the bar (above) replaced it, because a count
   could never answer "did that press do what I meant" */
.toast--cart a { color: var(--bg); text-decoration: underline; margin-inline-start: .5rem; }

.page-h1 { font-size: var(--t-2xl); letter-spacing: -.02em; margin: 1.2rem 0 .3rem; }
.page-lede { color: var(--ink-2); max-width: 62ch; margin-bottom: 1.4rem; }

.enq-grid {
  display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(320px, 1fr);
  gap: clamp(1rem, 3vw, 2.2rem); align-items: start; padding-bottom: 3rem;
}
@media (max-width: 900px) { .enq-grid { grid-template-columns: 1fr; } }

.enq-lines > .flabel, .enq-form > .flabel { display: block; margin-bottom: .5rem; }

/* --- one collected product ------------------------------------------------- */
.enq-row {
  display: grid; align-items: center; gap: .8rem;
  grid-template-columns: 84px minmax(0, 1fr) auto auto 28px 28px;
  padding: .7rem 0; border-bottom: 1px solid var(--line);
}
.enq-row__media {
  width: 84px; aspect-ratio: 4 / 3; border-radius: var(--radius-sm);
  overflow: hidden; background: var(--surface-2); border: 1px solid var(--line);
}
.enq-row__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.enq-row__title { color: var(--ink); font-weight: 600; }
.enq-row__title:hover { color: var(--brand-ink); }
.enq-row__sku {
  font-family: var(--mono); font-size: var(--t-xs); color: var(--ink-3); margin-top: .15rem;
}
.enq-row__price { text-align: right; white-space: nowrap; }
.enq-row__price b { display: block; font-size: var(--t-base); }
.enq-row__price b.is-sale { color: var(--sale); }
.enq-row__x {
  color: var(--ink-4); font-size: 20px; line-height: 1;
  width: 28px; height: 28px; border-radius: var(--radius-sm);
}
.enq-row__x:hover { color: var(--sale); background: var(--sale-wash); }

/* --- the per-line comment --------------------------------------------------
   An icon, not a button labelled "Add comment" and not an open text box:
   most lines never get one, and four empty fields down a basket read as four
   things left undone. It fills in when the line has something on it, which is
   the only state worth noticing from across the screen. The field itself
   spans the row underneath, where it belongs to one line and not to the
   list. */
.enq-row__noteb {
  color: var(--ink-4); width: 28px; height: 28px; border-radius: var(--radius-sm);
  display: grid; place-items: center; justify-self: center;
}
.enq-row__noteb:hover { color: var(--brand-ink); background: var(--brand-wash); }
.enq-row__noteb.has-note { color: var(--brand-ink); }
.enq-row__noteb.has-note svg { fill: var(--brand-wash); }
.enq-row__note {
  grid-column: 1 / -1; display: block; margin-top: -.2rem;
  padding-inline-start: calc(84px + .8rem);
}
.enq-row__note[hidden] { display: none; }
.enq-row__note .field { font-size: var(--t-sm); padding-block: .35rem; }
.enq-row--addon .enq-row__note { padding-inline-start: calc(60px + .8rem); }

/* A PRODUCT AND ITS ADD-ONS ARE ONE GROUP, and the group is drawn as one.
   The nesting is the whole point of the redesign - it is what tells the desk
   "these rails go with THAT canopy", and it survives into the offer and the
   order (db/016) - so it has to be legible before anything is read, not
   inferred from an indent. Hence a tinted, rail-bordered block rather than
   four rows in a row. */
.enq-group { margin-bottom: .8rem; }
.enq-group:last-child { margin-bottom: 0; }
.enq-group > .enq-row { border-bottom: 0; }
.enq-group__addons {
  margin-inline-start: 1.5rem; padding: .1rem .7rem .2rem;
  border-inline-start: 2px solid var(--brand-line);
  background: var(--surface-2); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.enq-group__note {
  display: block; padding-top: .35rem; font-size: var(--t-xs);
  color: var(--ink-3); font-weight: 600; letter-spacing: .01em;
}
.enq-row--addon {
  grid-template-columns: 60px minmax(0, 1fr) auto auto 28px 28px;
  border-bottom: 1px dashed var(--line);
  padding: .45rem 0;
}
.enq-group__addons .enq-row:last-child { border-bottom: 0; }
.enq-row--addon .enq-row__media { width: 60px; }
.enq-row--addon .enq-row__title { font-weight: 500; font-size: var(--t-sm); }
.enq-row--addon .enq-row__price b { font-size: var(--t-sm); font-weight: 600; }
.enq-row--gone { grid-template-columns: minmax(0, 1fr) 28px; color: var(--ink-3); }
.enq-row--gone b { font-family: var(--mono); font-size: var(--t-sm); }

/* On a phone the row is two lines, and every cell is PLACED. It used to lean
   on auto-placement around two grid-column overrides, which was already hard
   to read and stopped being predictable the moment the row grew a sixth cell
   for the comment icon. */
@media (max-width: 620px) {
  .enq-row {
    grid-template-columns: 60px minmax(0, 1fr) 28px 28px;
    grid-template-areas: "media main  note  x"
                         "media qty   price price"
                         "field field field field";
    gap: .5rem .6rem; align-items: center;
  }
  .enq-row__media { width: 60px; grid-area: media; align-self: start; }
  .enq-row__main  { grid-area: main; }
  .enq-row__qty   { grid-area: qty; }
  .enq-row__price { grid-area: price; text-align: right; }
  .enq-row__noteb { grid-area: note; }
  .enq-row__x     { grid-area: x; }
  .enq-row__note  { grid-area: field; padding-inline-start: 0; }
  .enq-group__addons { margin-inline-start: .6rem; padding-inline: .5rem; }
  .enq-row--addon { grid-template-columns: 60px minmax(0, 1fr) 28px 28px; }
  .enq-row--addon .enq-row__note { padding-inline-start: 0; }
  /* a line whose product has left the catalogue has neither price nor comment */
  .enq-row--gone { grid-template-areas: "main x"; }
}

/* --- the totals ------------------------------------------------------------ */
.enq-sum {
  margin-top: 1rem; padding: .9rem 1.1rem; border-radius: var(--radius);
  background: var(--surface-2); border: 1px solid var(--line);
}
.enq-sum__badge {
  display: inline-block; margin-bottom: .5rem; padding: .1rem .45rem;
  border-radius: 4px; background: var(--brand-wash); color: var(--brand-ink);
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .03em;
}
.enq-sum__row {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; padding: .3rem 0; color: var(--ink-2); font-size: var(--t-sm);
}
.enq-sum__row b { color: var(--ink); font-variant-numeric: tabular-nums; }
.enq-sum__row small { color: var(--ink-3); font-weight: 400; }
.enq-sum__row--total {
  margin-top: .35rem; padding-top: .6rem; border-top: 1px solid var(--line-2);
  font-size: var(--t-base);
}
.enq-sum__row--total b { font-size: var(--t-lg); }
/* "the price will be specified" is a real answer, and it has to read as one
   rather than as a missing number - so it is worded and coloured, never 0,00 */
/* Send, and the same list as a document. The second one is not a lesser
   Send - it is a different verb - so it sits beside it rather than under it
   as an afterthought, and in the quiet style so which of the two is the
   point of the page is never in question. The width override is because
   .enq-form makes every button full width, which is right when a button is
   the only thing on its line and wrong the moment two share one. */
.enq-send { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .7rem; }
.enq-form .enq-send .btn { width: auto; flex: 1 1 10rem; margin-top: 0; }
.enq-tbd { color: var(--warn); font-weight: 600; font-size: var(--t-sm); }
/* The delivery line in words, under the totals. Two states, and the one that
   matters is the second: no rate on file for this address, so the price is
   worked out by hand and travels with the offer. That is not a detail to be
   discovered on the quote, so it is said here, in the warning tone. */
.enq-shipnote {
  margin-top: .6rem; padding: .5rem .6rem; border-radius: var(--radius-sm);
  font-size: var(--t-xs); line-height: 1.45;
  color: var(--ink-3); background: var(--surface-2); border: 1px solid var(--line);
}
.enq-shipnote--tbd {
  color: var(--warn); font-weight: 550;
  background: var(--warn-wash);
  border-color: color-mix(in srgb, var(--warn) 35%, transparent);
}

/* --- the form -------------------------------------------------------------- */
.enq-form {
  padding: 1.1rem 1.2rem 1.3rem; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface);
  box-shadow: var(--shadow-1); position: sticky; top: 1rem;
}
@media (max-width: 900px) { .enq-form { position: static; } }
.enq-form textarea.field { resize: vertical; min-height: 6rem; }
.enq-form .btn { width: 100%; justify-content: center; margin-top: .7rem; }
.check { display: flex; align-items: center; gap: .5rem; font-size: var(--t-sm); color: var(--ink-2); }
.check input { accent-color: var(--brand); width: 1rem; height: 1rem; }
/* a tick box sharing a .frow--2 with a labelled field: sit on the input's own
   line rather than on the label above it, and let the box keep its own width */
.check--field { align-items: flex-start; padding-top: 1.55rem; }
.check--field input { flex: none; margin-top: .1rem; }

/* --- which fields matter ----------------------------------------------------
   Said on the form rather than learned by having a save refused (Rein,
   07.09.2026). The asterisk marks the four the system genuinely will not
   accept without - validateImport() already refuses a file missing any of
   them - and the muted chip marks what makes a product look finished in the
   catalogue rather than half-entered. Both are <abbr title>, so the reason is
   one hover away and neither needs a sentence beside it. */
.freq {
  color: var(--sale); font-weight: 800; text-decoration: none;
  margin-inline-start: .1rem; cursor: help;
}
.frec {
  margin-inline-start: .3rem; padding: .02rem .34rem; border-radius: 999px;
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: 10px; font-weight: 600; letter-spacing: .01em;
  color: var(--ink-4); text-decoration: none; cursor: help;
  text-transform: lowercase; vertical-align: 1px;
}
/* the legend in the fieldset heading, so the asterisk is explained once */
.fs__legend {
  float: inline-end; font-size: var(--t-xs); font-weight: 500; color: var(--ink-4);
}
.fs__legend .frec { cursor: default; }

/* the name and its „Soovitatud" tick share a row: the name takes what is
   left, the tick takes what it needs */
.frow--name { display: grid; gap: .75rem; grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
.check--feat { padding-bottom: .55rem; white-space: nowrap; }

.enq-empty {
  padding: clamp(2rem, 6vw, 4rem) 0 4rem; max-width: 52ch;
}
.enq-empty h2 { font-size: var(--t-xl); margin-bottom: .4rem; }
.enq-empty p { color: var(--ink-2); margin-bottom: 1.2rem; }

/* ==========================================================================
   MÜÜGIHALDUS - the sales desk (admin only)

   One board, one row per deal, at the stage it has reached. It replaced
   three lists that showed the same customer three times; the stage is a
   filter chip, never a different screen.
   ========================================================================== */
.sale-chips { display: flex; gap: .3rem; flex-wrap: wrap; margin-bottom: .8rem; }
.sale-chips button {
  padding: .3rem .7rem; border-radius: 999px; border: 1px solid var(--line);
  font-size: var(--t-xs); font-weight: 600; color: var(--ink-3);
  display: inline-flex; align-items: center; gap: .35rem;
}
.sale-chips button[aria-selected="true"] {
  background: var(--brand-wash); border-color: var(--brand-line); color: var(--brand-ink);
}
.sale-chips .tag__n { color: inherit; opacity: .7; }

/* --- the board is a table ---------------------------------------------------
   A deal is five facts - stage, whose, where it stands, when, how much - and
   they belong in columns, because the whole point of a list is comparing down
   it. The detail unfolds under its own row rather than replacing the list. */
.sale-tools {
  display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; margin-bottom: .7rem;
}
.sale-search {
  display: flex; align-items: center; gap: .4rem; flex: 1 1 16rem; min-width: 0;
  margin-inline-start: auto; color: var(--ink-4);
}
.sale-search .field { height: 30px; font-size: var(--t-sm); }
.sale-search input[type="search"]::-webkit-search-cancel-button { cursor: pointer; }

.sale-board { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line);
              border-radius: var(--radius); overflow: hidden; }
.sale-row { background: var(--surface); }
.sale-row.is-open .sale-tr { background: var(--surface-2); }
.sale-tr {
  display: grid; width: 100%; text-align: start; align-items: center; gap: .6rem;
  grid-template-columns:
    92px 104px minmax(0, 1.5fr) 84px minmax(0, 1.4fr) 118px 78px 52px 100px 100px;
  padding: .5rem .8rem; font-size: var(--t-sm);
}
.sale-tr:hover { background: var(--surface-2); }
.sale-tr--head {
  background: var(--surface-2); border-bottom: 1px solid var(--line);
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-4); font-weight: 700; padding-block: .35rem;
}
.sale-td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.sale-td--no { font-family: var(--mono); font-size: var(--t-xs); font-weight: 700; color: var(--ink); }
.sale-tr--head .sale-td--no { font-family: var(--font); font-size: 9.5px; font-weight: 700; }
.sale-td--who b { display: block; color: var(--ink); }
.sale-td--who .fhint { display: block; margin: 0; }
.sale-td--status, .sale-td--when { color: var(--ink-2); }
.sale-td--n, .sale-td--sum, .sale-td--vat { text-align: end; font-variant-numeric: tabular-nums; }
.sale-td--sum { font-weight: 700; color: var(--ink); }
/* every price on this desk is NET, so the gross figure is its own column
   rather than a different way of showing the same one */
.sale-td--vat { color: var(--ink-3); }
.sale-td--ship { color: var(--ink-2); }

/* dealer or customer - it decides what the quote should be priced at */
.sale-who {
  display: inline-block; padding: .05rem .35rem; border-radius: 4px;
  font-size: 10px; font-weight: 700; letter-spacing: .02em;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink-3);
}
.sale-who--dealer { background: #EEF2FF; border-color: #C7D2FE; color: #3730A3; }
[data-theme="dark"] .sale-who--dealer { background: #1E1B4B; border-color: #3730A3; color: #C7D2FE; }

/* A colour per SITUATION, not per word: waiting on us, waiting on them, won,
   finished, dead. The eye sorts the board before the reading does. */
.sale-st {
  display: inline-block; padding: .08rem .4rem; border-radius: 4px;
  font-size: 10.5px; font-weight: 700; white-space: nowrap;
}
.sale-st--hot   { background: #FEE2E2; color: #991B1B; }   /* new, on us */
.sale-st--wait  { background: #FEF3C7; color: #92400E; }   /* on them */
.sale-st--draft { background: var(--surface-2); color: var(--ink-3); border: 1px solid var(--line); }
.sale-st--won   { background: #DCFCE7; color: #166534; }   /* confirmed */
.sale-st--done  { background: #DBEAFE; color: #1E40AF; }   /* invoiced, closed */
.sale-st--dead  { background: var(--surface-2); color: var(--ink-4); text-decoration: line-through; }
[data-theme="dark"] .sale-st--hot  { background: #450A0A; color: #FCA5A5; }
[data-theme="dark"] .sale-st--wait { background: #422006; color: #FCD34D; }
[data-theme="dark"] .sale-st--won  { background: #052E16; color: #86EFAC; }
[data-theme="dark"] .sale-st--done { background: #172554; color: #93C5FD; }
.sale-warn { color: var(--warn); font-weight: 700; }
.sale-row__c {
  display: inline-block; padding: 0 .3rem; border-radius: 999px;
  background: var(--brand-wash); border: 1px solid var(--brand-line);
  font-size: 10px; color: var(--brand-ink); font-weight: 700;
}
.sale-none { padding: 1rem; background: var(--surface); }
@media (max-width: 1200px) {
  .sale-tr { grid-template-columns: 92px minmax(0, 1.4fr) 84px 118px 100px; row-gap: .2rem; }
  .sale-td--no { grid-column: 2; font-size: 10px; }
  .sale-td--when, .sale-td--n, .sale-td--ship, .sale-td--vat { display: none; }
}

/* --- the lines, read-only: the same three figures the editor works in ----- */
.sale-line {
  display: grid; grid-template-columns: minmax(0, 9rem) minmax(0, 1fr) 38px 76px 42px 76px 82px;
  gap: .4rem; align-items: baseline; font-size: var(--t-xs);
  padding: .25rem 0; border-bottom: 1px solid var(--line);
}
.sale-line--head {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-4); font-weight: 700; border-bottom: 1.5px solid var(--line-2);
}
.sale-line__list, .sale-line__disc, .sale-line__unit, .sale-line__sum, .sale-line__qty {
  text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.sale-line__disc { color: var(--sale); font-weight: 600; }
.sale-line__unit { color: var(--ink); font-weight: 600; }
.sale-line__note { display: block; color: var(--ink-3); font-style: italic; }

/* --- the editor's line: qty, list, discount, final, and a total nobody types */
.sl-head {
  display: grid; align-items: center; gap: .5rem;
  grid-template-columns: 22px 26px 56px minmax(0, 1fr) auto auto;
  padding: 0 .5rem .2rem; font-size: 9.5px; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-4); font-weight: 700;
}
.sl-head__nums, .sl-row__nums {
  display: grid; gap: .3rem; align-items: center;
  grid-template-columns: 52px 84px 62px 88px 92px;
}
.sl-head__nums span { text-align: end; }
.sl-head__nums span:first-child { text-align: start; }
.sl-row__nums input { height: 28px; font-size: var(--t-xs); text-align: end; }
.sl-pc { display: flex; align-items: center; gap: .15rem; }
.sl-pc span { color: var(--ink-4); font-size: 10px; }
.sl-row__tot {
  text-align: end; font-variant-numeric: tabular-nums; font-weight: 700;
  font-size: var(--t-xs); color: var(--ink);
}
/* a price that does not follow from the discount is legitimate - it is what a
   special price IS - so it is marked, not flagged as an error */
.sl-row--special .sl-row__tot { color: var(--brand-ink); }
.sl-row--special .sl-row__tot::after {
  content: " ★"; font-size: 9px; color: var(--brand); }


/* THE EXPANDED HALF HANGS OFF THE ROW ABOVE IT, and has to look like it.
   Three things say so together, because indentation alone was too quiet to
   read at a glance: a step to the right, a brand-coloured rail down the left
   edge, and a faint tint. The rail is the one that does the work - it starts
   at the row's own left edge and runs the height of the detail, so the eye
   follows it up to the line it belongs to. */
.sale-row__body {
  padding: .1rem .8rem .8rem 2.8rem;
  border-top: 1px solid var(--line);
  border-inline-start: 3px solid var(--brand);
  background: var(--surface-2);
}
.sale-cols { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 1fr);
             gap: 1.2rem; padding-top: .7rem; }
@media (max-width: 820px) { .sale-cols { grid-template-columns: 1fr; } }

.sale-lines { display: grid; gap: .15rem; margin-top: .3rem; }
/* a wrapped SKU is unreadable and throws the row's alignment out, and these
   run to 20 characters - so the column takes what it needs and the NAME is
   the one that gives way */
.sale-line__sku { white-space: nowrap; }
/* the add-on is indented under the product it was ticked on - the structure
   the customer meant, carried from the enquiry through to the order */
.sale-line--addon { padding-inline-start: 1.1rem; border-bottom-style: dashed; color: var(--ink-3); }
.sale-line--addon .sale-line__sku::before { content: "↳ "; color: var(--ink-4); }
.sale-line__sku { font-family: var(--mono); color: var(--ink); }
.sale-line__name { color: var(--ink-2); }
.sale-line--addon .sale-line__sku { color: var(--ink-3); }
.sale-line__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sale-line__qty, .sale-line__sum { text-align: end; font-variant-numeric: tabular-nums; }

.sale-msg {
  font-size: var(--t-sm); color: var(--ink-2); white-space: pre-wrap;
  margin: .2rem 0 .4rem; line-height: 1.5;
}
.sale-acts { display: flex; gap: .4rem; flex-wrap: wrap; align-items: end; margin-top: .8rem; }
.sale-acts__st { display: grid; gap: .15rem; }
.sale-acts__st .field { min-width: 170px; font-size: var(--t-xs); }

.sale-comments { display: grid; gap: .4rem; margin: .3rem 0 .5rem; }
.sale-comment {
  padding: .4rem .6rem; border-radius: var(--radius-sm);
  background: var(--surface-2); border: 1px solid var(--line);
}
.sale-comment p { font-size: var(--t-sm); color: var(--ink-2); white-space: pre-wrap; margin-top: .15rem; }
.sale-addc { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .4rem; }

/* the line editor's indent toggle: one button per row, because "under which
   product" is always "the last row that was not itself an add-on" */
.sl-row [data-slindent] { color: var(--ink-4); font-size: 13px; }
.sl-row [data-slindent][aria-pressed="true"] { color: var(--brand-ink); font-weight: 700; }
.sl-row--addon { margin-inline-start: 1.2rem; border-style: dashed; }

/* --- the offer's covering letter, composed before it is sent ------------- */
.sale-compose {
  margin-top: .8rem; padding: .8rem; border-radius: var(--radius-sm);
  background: var(--brand-wash); border: 1px solid var(--brand-line);
}
.sale-compose textarea.field {
  resize: vertical; min-height: 7rem; margin-top: .3rem;
  background: var(--surface); font-size: var(--t-sm); line-height: 1.5;
}
.sale-compose__acts { display: flex; gap: .4rem; margin-top: .5rem; flex-wrap: wrap; }

/* ==========================================================================
   THE STORAGE NOTICE — assets/consent.js

   A bar, not a modal. Nothing here needs consent (see the file header), so
   nothing here is allowed to hold the page hostage: it sits at the foot,
   says what is kept, and goes away when acknowledged.
   ========================================================================== */
.sbar {
  position: fixed; inset-inline: 0; bottom: 0; z-index: 250;
  background: var(--surface); border-top: 1px solid var(--line-2);
  box-shadow: 0 -2px 12px rgb(23 24 26 / .10);
  animation: sbarIn .25s ease-out;
}
/* Out of the way while a dialog is open. A modal <dialog> is in the top
   layer, so this bar sat UNDER its backdrop - greyed, half legible, and
   looking broken rather than dismissed. It cannot be raised (only the top
   layer is above a backdrop, and this is not a dialog), and a notice nobody
   can read is worse than one nobody can see. It comes back the moment the
   dialog closes, still waiting to be acknowledged. */
body:has(dialog[open]) .sbar { display: none; }
@keyframes sbarIn { from { transform: translateY(100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) { .sbar { animation: none; } }

.sbar__inner {
  display: flex; align-items: center; gap: 1.2rem;
  padding-block: .8rem; flex-wrap: wrap;
}
.sbar__text { flex: 1 1 22rem; min-width: 0; font-size: var(--t-sm); color: var(--ink-2); }
.sbar__text b { display: block; color: var(--ink); margin-bottom: .15rem; }
.sbar__text span { line-height: 1.5; }
.sbar__acts { display: flex; align-items: center; gap: .9rem; flex: none; margin-inline-start: auto; }
.sbar__more { color: var(--brand-ink); font-size: var(--t-sm); text-decoration: underline; }
@media (max-width: 640px) {
  .sbar__acts { margin-inline-start: 0; width: 100%; justify-content: space-between; }
}
/* The sticky product-page action bar would otherwise sit under it. Through
   the BODY, not a sibling combinator: consent.js appends the notice to
   <body> AFTER #stickyActs, and `~` only reaches later siblings - so the
   old `.sbar ~ #stickyActs` never matched and a first-time visitor on a
   phone had „Saada päring" hidden until the notice was dismissed. */
body:has(.sbar) #stickyActs { bottom: 5.5rem; }

/* --- a table inside a policy document -------------------------------------
   The processors are a table because they are three facts about each name -
   who, what, when - and three bullets per name is a wall. It scrolls inside
   itself on a phone rather than pushing the page sideways. */
.doc .scroller { overflow-x: auto; margin: 0 0 1rem; }
.doc table {
  width: 100%; min-width: 30rem; border-collapse: collapse;
  font-size: var(--t-sm); text-align: start;
}
.doc thead th {
  text-align: start; font-size: var(--t-xs); text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink-3); font-weight: 700;
  padding: .35rem .6rem; border-bottom: 1.5px solid var(--line-2);
}
.doc tbody td {
  padding: .55rem .6rem; border-bottom: 1px solid var(--line);
  color: var(--ink-2); vertical-align: top; line-height: 1.5;
}
.doc tbody td b { color: var(--ink); }
.doc code {
  font-family: var(--mono); font-size: .88em; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 4px; padding: 0 .25em;
  color: var(--ink-2);
}
/* a revision keeps the client's reference and adds a version to it */
.sale-row__v {
  font-family: var(--font); font-weight: 700; font-size: 10px;
  background: var(--warn-wash); color: var(--warn);
  border-radius: 3px; padding: 0 .25rem; margin-inline-start: .2rem;
}

/* --- the admin bar, grouped ------------------------------------------------
   Fourteen controls in a flat row was a wall of buttons you had to read every
   time to find the one you wanted. Same controls, same single row, grouped by
   the job they belong to: the eye lands on a caption first and reads three or
   four words instead of fourteen. The captions are what buy the compression -
   the buttons get smaller, which is where the width comes back from. */
.btn--xs { padding: .2rem .45rem; font-size: var(--t-xs); gap: .25rem; }
.btn--xs .tag__n { font-size: 10px; }

/* The caption sits BESIDE its buttons, not above them. Stacked, the bar grew
   to two rows and half again as tall as the flat one it replaced - which is
   the opposite of the point. Inline, the whole thing is one row and SHORTER
   than the original, because the buttons shrank and nothing was added
   vertically. */
.abgroup {
  display: inline-flex; align-items: center; gap: .3rem;
  padding-inline: .35rem; border-inline-start: 1px solid var(--line);
  align-self: stretch;
}
.abgroup:first-of-type { border-inline-start: 0; padding-inline-start: 0; }
.abgroup__t {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-4); font-weight: 700; white-space: nowrap;
}
.abgroup__b { display: flex; gap: .2rem; align-items: center; }

/* ---- the edit-mode switch ------------------------------------------------
   A BUTTON THAT GOES A SHADE DARKER IS NOT A STATE, IT IS A HINT. Edit mode
   was exactly that: aria-pressed, which a screen reader says out loud and a
   pair of eyes has to infer from a tint - and it is the control that decides
   whether every row carries a stock stepper, which is one mis-tap away from a
   ledger movement. "Is it on" has to be answerable across a desk.

   So it is drawn as what it is: a track with a knob that slides. The label
   beside it says which state it is IN rather than what pressing it would do,
   because a switch showing "off" and reading "turn on" is the oldest way to
   make a control ambiguous. */
.swbtn { gap: .4rem; }
.swbtn[aria-checked="true"] {
  background: var(--brand-wash); border-color: var(--brand-line);
  color: var(--brand-ink); font-weight: 650;
}
.sw {
  flex: none; width: 26px; height: 15px; border-radius: 999px;
  background: var(--line-2); position: relative;
  transition: background .15s ease;
}
.sw__knob {
  position: absolute; inset-block-start: 2px; inset-inline-start: 2px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--surface); box-shadow: var(--shadow-1);
  transition: transform .15s ease;
}
.swbtn[aria-checked="true"] .sw { background: var(--brand); }
.swbtn[aria-checked="true"] .sw__knob { transform: translateX(11px); }
/* the knob travels the other way in a right-to-left layout */
[dir="rtl"] .swbtn[aria-checked="true"] .sw__knob { transform: translateX(-11px); }
@media (prefers-reduced-motion: reduce) {
  .sw, .sw__knob { transition: none; }
}

.adminbar__cat { padding-inline-end: .3rem; }
.adminbar__cat .field { padding: .22rem .4rem; font-size: var(--t-xs); }
/* Narrow: the groups wrap onto their own lines, and the captions are exactly
   what keeps that legible instead of a heap of buttons. */
@media (max-width: 900px) {
  .abgroup { border-inline-start: 0; padding-inline: 0; flex: 1 0 100%; }
  .abgroup__t { min-width: 4.5rem; }
}

/* ==========================================================================
   THE BASKET BAR - one bar, two baskets (cart.js makeBar)

   The customer filling an enquiry and the admin picking for an offer are
   doing the same thing, so it is the same bar: the running list, the total,
   the collapse. What differs is the TONE - calm for a basket you own, loud
   for a mode you are in - and the buttons on the right.

   Both sit at the TOP, and neither exists until there is something in it.
   ========================================================================== */
.fbar { position: sticky; top: 0; z-index: 240; }
.fbar--cart {
  background: var(--brand-wash); color: var(--ink);
  border-bottom: 1px solid var(--brand-line);
}
.fbar--pick {
  background: var(--brand-strong); color: #fff;
  box-shadow: 0 2px 10px rgb(23 24 26 / .18);
}
.fbar__inner {
  display: flex; align-items: center; gap: .7rem;
  padding-block: .45rem; flex-wrap: wrap; font-size: var(--t-sm);
}
.fbar__tag {
  border-radius: 4px; padding: .1rem .4rem; font-size: 10px; font-weight: 800;
  letter-spacing: .05em; text-transform: uppercase;
}
.fbar--cart .fbar__tag { background: var(--brand); color: #fff; }
.fbar--pick .fbar__tag { background: rgb(255 255 255 / .22); color: #fff; }
.fbar__label:empty { display: none; }
.fbar--pick b { color: #fff; }
.fbar__n { font-variant-numeric: tabular-nums; }
.fbar--pick .fbar__n { opacity: .85; }
.fbar--cart .fbar__n { color: var(--ink-2); }
.fbar__acts { display: flex; align-items: center; gap: .5rem; margin-inline-start: auto; }
/* Emptying the basket: an icon on a line that already carries two controls,
   and a two-press question rather than a dialog. Armed, it grows the word -
   a red icon alone is a warning about nothing in particular, and the press
   that follows has to know what it is agreeing to. */
.fbar__clear {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .22rem .4rem; border-radius: var(--radius-sm);
  color: var(--ink-4); font-size: var(--t-xs); font-weight: 600;
}
.fbar__clear:hover { color: var(--sale); background: var(--sale-wash); }
.fbar__clear.is-armed { color: #fff; background: var(--sale); }
.fbar__clear.is-armed:hover { color: #fff; background: var(--sale); opacity: .9; }
.fbar__toggle { font-size: var(--t-xs); text-decoration: underline; }
.fbar--cart .fbar__toggle { color: var(--brand-ink); }
.fbar--pick .fbar__toggle { color: rgb(255 255 255 / .85); }
.fbar--pick .btn--quiet { color: #fff; }
.fbar--pick .btn--quiet:hover { background: rgb(255 255 255 / .16); }
.fbar--pick .btn--primary { background: #fff; color: var(--brand-strong); }
.fbar--pick .btn--primary:hover { background: #fff; opacity: .9; }
/* collapsed, an addition still has to be visible or the press feels like
   nothing happened - so it is said on the header line, briefly */
.fbar__flash { font-weight: 600; font-size: var(--t-xs); }
.fbar--cart .fbar__flash { color: var(--brand-ink); }
.fbar__flash.is-new { animation: fbarFlash 3.5s ease-out; }
@keyframes fbarFlash { 0%,10% { opacity: 1; } 85% { opacity: 1; } 100% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) { .fbar__flash.is-new { animation: none; } }
/* the view-as bar would otherwise sit above the pick bar and steal the top */
body.is-picking .rolebar { display: none; }
@media (max-width: 620px) {
  .fbar__n { width: 100%; order: 5; }
  .fbar__acts { margin-inline-start: 0; }
}

/* --- the running list ------------------------------------------------------
   A count is not enough: "3 toodet" does not say whether the third press
   added a third product or a second of the first, which is exactly what the
   person collecting needs to see. One line each, scrolling inside itself past
   a handful, because it sits above a catalogue somebody is reading. */
.picklist {
  max-height: 9rem; overflow-y: auto; overscroll-behavior: contain;
  margin-bottom: .45rem; border-radius: var(--radius-sm);
}
.fbar--pick .picklist { background: rgb(255 255 255 / .12); }
.fbar--cart .picklist { background: var(--surface); border: 1px solid var(--brand-line); }
.picklist__row {
  display: grid; align-items: center; gap: .5rem;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) 40px 88px 96px 22px;
  padding: .22rem .6rem; font-size: var(--t-xs);
  transition: background .5s ease;
}
.fbar--pick .picklist__row { border-bottom: 1px solid rgb(255 255 255 / .12); }
.fbar--cart .picklist__row { border-bottom: 1px solid var(--line); }
.picklist__row:last-child { border-bottom: 0; }
.picklist__row--addon .picklist__name { padding-inline-start: .9rem; opacity: .85; }
.picklist__row--addon .picklist__name::before { content: "↳ "; opacity: .7; }
.picklist__name, .picklist__var { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picklist__var { opacity: .75; }
.picklist__qty, .picklist__unit, .picklist__sum {
  text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.picklist__sum small { opacity: .7; font-size: 9px; margin-inline-start: .2rem; }
.picklist__row--total { border-top: 1px solid currentColor; border-bottom: 0; }
.fbar--pick .picklist__row--total { border-top-color: rgb(255 255 255 / .3); }
.fbar--cart .picklist__row--total { border-top-color: var(--line-2); }
.picklist__x { font-size: 15px; line-height: 1; }
.fbar--pick .picklist__x { color: rgb(255 255 255 / .6); }
.fbar--pick .picklist__x:hover { color: #fff; }
.fbar--cart .picklist__x { color: var(--ink-4); }
.fbar--cart .picklist__x:hover { color: var(--sale); }
/* a second of green: that press landed, and THIS is what it did - which for a
   repeat press is the quantity going up rather than a new row appearing */
.picklist__row.is-new { background: #16A34A; color: #fff; transition: none; }
.picklist__row.is-new .picklist__var { opacity: .9; }
@media (prefers-reduced-motion: reduce) { .picklist__row { transition: none; } }
@media (max-width: 760px) {
  .picklist__row { grid-template-columns: minmax(0, 1fr) 34px 88px 22px; }
  .picklist__var, .picklist__unit { display: none; }
}

.sl-add { display: flex; gap: .4rem; flex-wrap: wrap; margin-top: .4rem; }
/* While picking, the catalogue is a shop again. The admin's own furniture -
   the toolbar, the stock steppers, the per-row edit controls - is not just
   noise here, it is a trap: a stepper reading 0 beside "+ Lisa" looks like
   the quantity for the offer, and it is the warehouse's stock figure. */
body.is-picking #adminbar,
body.is-picking .row__admin,
body.is-picking .rownote,
body.is-picking .stepper { display: none; }

/* ==========================================================================
   THE OFFER'S LINES - a line looks like the product it is

   It used to be a SKU in a text box, and a code tells you nothing. Now the
   row carries the picture, the name, the variant and the price, the way the
   catalogue row that put it there does.
   ========================================================================== */
.sl-row {
  display: grid; align-items: center; gap: .5rem;
  grid-template-columns: 22px 26px 56px minmax(0, 1fr) auto auto;
  padding: .45rem .5rem; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--surface); margin-bottom: .35rem;
}
.sl-row[data-withfrom] { grid-template-columns: 22px 26px 56px minmax(0, 1fr) auto 150px auto; }
.sl-row--addon { margin-inline-start: 1.4rem; border-inline-start: 2px solid var(--brand-line); }
.sl-row__ord { display: grid; gap: 1px; }
.sl-row__ord button {
  width: 20px; height: 15px; line-height: 1; font-size: 8px; color: var(--ink-4);
  border: 1px solid var(--line); border-radius: 3px; background: var(--surface-2);
}
.sl-row__ord button:hover { color: var(--brand-ink); border-color: var(--brand-line); }
.sl-row__ind { color: var(--ink-4); font-size: 13px; }
.sl-row__ind[aria-pressed="true"] { color: var(--brand-ink); font-weight: 700; }
.sl-row__media {
  width: 56px; aspect-ratio: 4/3; border-radius: 4px; overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line); display: block;
}
.sl-row__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sl-row__noimg { display: grid; place-items: center; height: 100%; color: var(--ink-4); font-size: 10px; }
.sl-row__main { display: grid; gap: .05rem; min-width: 0; }
.sl-row__main b { font-size: var(--t-sm); color: var(--ink); }
.sl-row__main .is-bad { color: var(--sale); }
.sl-row__sku { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); }
.sl-row__desc {
  font-size: var(--t-xs); color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* this line has to be built before it can ship - said on the line, not in a
   modal somebody opens afterwards */
.sl-row__make {
  font-size: var(--t-xs); font-weight: 600; color: var(--warn);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sl-row__from { height: 28px; font-size: var(--t-xs); }
.sl-row__x { color: var(--ink-4); font-size: 17px; line-height: 1; }
.sl-row__x:hover { color: var(--sale); }
/* the note is the row's second line, and only worth space when it is used */
.sl-row__note { grid-column: 3 / -1; display: grid; gap: .1rem; margin-top: .15rem; }
.sl-row__note .field { height: 26px; font-size: var(--t-xs); }
.sl-empty { padding: .8rem; border: 1px dashed var(--line-2); border-radius: var(--radius-sm); }
.sl-save { display: flex; gap: .4rem; flex-wrap: wrap; }
/* delivery price is a number, not a paragraph; the note is the other way round */
#slShip { max-width: 9rem; }

/* --- the running list in the pick bar --------------------------------------
   A count is not enough: "3 toodet valitud" does not say whether the third
   press added a third product or a second of the first, which is exactly what
   the person collecting needs to see. One line each, scrolling inside itself
   past a handful, because it sits above a catalogue somebody is reading. */
.picklist {
  max-height: 8.5rem; overflow-y: auto; overscroll-behavior: contain; margin-bottom: .5rem;
  background: rgb(255 255 255 / .12); border-radius: var(--radius-sm);
}
.picklist__empty { padding: .4rem .6rem; font-size: var(--t-xs); opacity: .8; }
.picklist__row {
  display: grid; align-items: center; gap: .5rem;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) 40px 88px 96px 22px;
  padding: .22rem .6rem; font-size: var(--t-xs);
  border-bottom: 1px solid rgb(255 255 255 / .12);
  transition: background .5s ease;
}
.picklist__row:last-child { border-bottom: 0; }
.picklist__row--addon .picklist__name { padding-inline-start: .9rem; opacity: .85; }
.picklist__row--addon .picklist__name::before { content: "↳ "; opacity: .7; }
.picklist__name, .picklist__var { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picklist__var { opacity: .75; }
.picklist__qty, .picklist__unit, .picklist__sum {
  text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.picklist__sum small { opacity: .7; font-size: 9px; margin-inline-start: .2rem; }
.picklist__row--total { border-top: 1px solid rgb(255 255 255 / .3); border-bottom: 0; }
.picklist__x { color: rgb(255 255 255 / .6); font-size: 15px; line-height: 1; }
.picklist__x:hover { color: #fff; }
/* a second of green: that press landed, and THIS is what it did - which for a
   repeat press is the quantity going up rather than a new row appearing */
.picklist__row.is-new { background: #16A34A; transition: none; }
@media (prefers-reduced-motion: reduce) { .picklist__row { transition: none; } }
@media (max-width: 760px) {
  .picklist__row { grid-template-columns: minmax(0, 1fr) 34px 88px 22px; }
  .picklist__var, .picklist__unit { display: none; }
}

/* --- column captions over the catalogue rows -------------------------------
   Three unlabelled columns sit inside every catalogue row, and the reader is
   left to work out which is which: an admin's stock stepper is a bare number
   beside a price and reads as another price until somebody tells you
   otherwise, and a customer's price and availability are no more labelled
   than that. This tells you, once, and follows you down the list.

   IT LIVES IN THE TOOLBAR, as its last row. The toolbar is the sticky block
   on this page (top: 63px, under the site header); a second sticky element
   below it with top: 0 slid underneath both and was never seen. Being part
   of the toolbar it needs no offset of its own and cannot drift out of step
   when the toolbar grows a chip row.

   IT IS THE SAME GRID AS .row - same tracks, same width variables, same
   inline padding (.75rem of card padding plus its 1px border) - so the
   captions cannot drift out of line with what they name. Each one is also
   aligned the way ITS OWN cell is: the name left, the price right, the
   availability left. */
.rowhead {
  display: grid; grid-template-columns: 150px minmax(0, 1fr) 424px var(--w-cta);
  gap: 0 1rem; padding: .35rem calc(.75rem + 1px) .1rem;
  border-top: 1px solid var(--line);
}
.rowhead__opts {
  display: grid; gap: .4rem; padding-inline: .4rem;
  grid-template-columns: 15px minmax(0, 1fr) var(--w-price) var(--w-avail);
}
/* the supplier-link cell only exists for admins, so the template gains a
   track - the same extra track .row__opts--adm .opt gains */
.rowhead__opts--adm {
  grid-template-columns: 15px minmax(0, 1fr) var(--w-price) var(--w-link) var(--w-avail);
}
.rowhead span {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-4); font-weight: 700;
}
/* .opt__price is right-aligned in its track; a caption over it that is not
   sits under the widest digits instead of over the number. The end padding is
   for the customer's four-column layout, where the price track abuts the
   availability one and HIND SAADAVUS otherwise read as one phrase - the
   prices themselves are wide enough that four pixels off the right edge is
   invisible under them. */
.rowhead__num { text-align: right; padding-inline-end: .3rem; }
/* the pick bar owns the top while a session is open */
body.is-picking .rowhead { display: none; }
/* below 1400px the row stacks (see .row's media query) and these tracks
   describe a layout that is no longer on screen */
@media (max-width: 1400px) { .rowhead { display: none; } }
.picklist__empty { padding: .4rem .6rem; font-size: var(--t-xs); opacity: .8; }

/* --- the opened deal: its lines belong to the row above it ---------------
   Flush left they read as a second list that happens to be below the first;
   a step in says "these are that deal's", which is all the reader needs. */
.sale-side { display: grid; align-content: start; gap: .2rem; padding-top: 1.1rem; }
.sale-side .fhint { margin: 0; }
/* the dates are facts about the offer, not things to press, so they sit away
   from the buttons rather than reading as two more of them */
.sale-dates {
  display: flex; gap: .9rem; margin-inline-start: auto; align-items: baseline;
  font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap;
}
.sale-dates b { color: var(--ink-2); }
.sale-dates--held b { color: var(--brand-ink); }
@media (max-width: 820px) {
  /* the rail still says which row this belongs to; the step is what a narrow
     screen cannot afford */
  .sale-row__body { padding-inline-start: 1rem; }
  .sale-dates { margin-inline-start: 0; width: 100%; }
}

/* --- Broneeri, in the page rather than in a prompt() --------------------- */
/* --- SmartAccounts settings: what a status means, which PDF goes out -------- */

/* A status row. The name is read-only once SmartAccounts has actually sent it,
   because it is then a fact about their system rather than a field of ours. */
/* Code, name, what it does, why, how often, remove. The CODE is what
   SmartAccounts' API speaks and the NAME is what a person reads, so both are
   on the row: one of them is the thing you type into a setting, the other is
   the thing you recognise. */
.sa-strow {
  display: grid;
  grid-template-columns: minmax(7rem, .9fr) minmax(8rem, 1fr) minmax(10rem, 1fr)
                         minmax(7rem, 1fr) auto auto;
  gap: .4rem; align-items: center; margin-bottom: .35rem;
}
.sa-strow--head {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-3); margin-bottom: .2rem;
}
/* Seen, and still meaning nothing. This is the row that is costing something:
   SmartAccounts keeps sending it and our shelf keeps ignoring it. */
.sa-strow--todo [data-sast] { border-color: var(--warn); }
.sa-strow__seen { font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; }
.sa-strow [data-sast][readonly] { background: var(--surface-2); color: var(--ink-2); }
.sa-strow button[data-sadel] {
  background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--ink-3); cursor: pointer; line-height: 1; padding: .3rem .5rem;
}
.sa-strow button[data-sadel]:hover { border-color: var(--sale); color: var(--sale); }

/* What each action does to the shelf, spelled out under the table rather than
   crammed into the dropdown - one of the three moves stock, and that is worth
   more than eight words. */
.sa-actlegend {
  list-style: none; padding: .55rem .7rem; margin: .6rem 0 0;
  display: grid; gap: .25rem;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: var(--t-sm); color: var(--ink-2);
}
.sa-actlegend b { color: var(--ink); }

.sa-tpls { display: grid; gap: .5rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.sa-tpls label { display: grid; gap: .2rem; }

/* --- the mirror: an order lives in SmartAccounts, this screen reports on it -- */

/* How old the picture is, above the board. Nothing syncs on a timer here, so
   this line is what stands between a manual system and one that merely looks
   current - which is why the stale state is coloured rather than just worded. */
.sale-sync {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin: 0 0 .7rem; padding: .5rem .7rem;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: var(--t-sm); color: var(--ink-3);
}
/* the label takes the free space, whatever sits before it */
.sale-sync > span.sale-syncwhen { margin-inline-end: auto; }
.sale-sync--old {
  background: var(--warn-wash); border-color: var(--warn); color: var(--ink-2);
}

/* WHICH SmartAccounts. The desk is identical against the mock and against the
   real books, and only one of them can be undone from here - so the mode is
   stated rather than assumed, and the real one is the loud one. */
.sale-samode {
  flex: 0 0 auto;
  padding: .1rem .45rem; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface);
  font-size: var(--t-xs); font-weight: 600; letter-spacing: .02em;
  color: var(--ink-3); white-space: nowrap;
}
.sale-samode--live {
  border-color: var(--sale); background: var(--sale-wash); color: var(--ink);
}

/* The urgent one. A prepayment document with no offsetting row on the order
   means the invoice made from it charges the whole amount twice, so it does not
   get to look like the buttons beside it. */
.sale-warn {
  flex: 1 1 100%;
  display: grid; gap: .35rem; justify-items: start;
  padding: .7rem .85rem; margin-bottom: .5rem;
  background: var(--warn-wash);
  border: 1px solid var(--warn); border-left-width: 3px;
  border-radius: var(--radius-sm);
  font-size: var(--t-sm);
}
.sale-warn b { color: var(--warn); }
.sale-warn span { color: var(--ink-2); }

/* What the order says now, against what we sent. The table scrolls on its own
   rather than pushing the dialog sideways. */
.sale-mirror { overflow-x: auto; margin: .35rem 0 .2rem; }
.sale-mirror__t {
  width: 100%; border-collapse: collapse; font-size: var(--t-sm);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); overflow: hidden;
}
.sale-mirror__t th, .sale-mirror__t td {
  padding: .4rem .6rem; text-align: left; border-top: 1px solid var(--line);
  vertical-align: top;
}
.sale-mirror__t thead th {
  border-top: 0; background: var(--surface-2);
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-3); white-space: nowrap;
}
.sale-mirror__t .num { text-align: right; font-variant-numeric: tabular-nums; }
.sale-mirror__d { display: block; color: var(--ink-3); font-size: var(--t-xs); }
/* A row somebody typed over there. Marked rather than hidden: "the portal did
   not put this here" is the finding, not an error to tidy away. */
.sale-mirror__t tr.is-foreign { background: var(--brand-wash); }
.sale-mirror__tag {
  display: inline-block; white-space: nowrap;
  font-size: var(--t-xs); padding: .05rem .35rem;
  border: 1px solid var(--brand-line); border-radius: 999px;
  color: var(--brand-ink);
}
.sale-mirror__tag--drift { border-color: var(--warn); color: var(--warn); }
/* what we sent, beside what it became */
.sale-mirror__was {
  display: block; font-size: var(--t-xs); color: var(--ink-3);
  text-decoration: line-through;
}

/* SmartAccounts' single internal-information string. Read-only, and it looks
   read-only: it is their note, not a field of ours. */
.sale-sacomment {
  margin: .2rem 0 0; padding: .5rem .7rem;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: var(--t-sm); color: var(--ink-2); white-space: pre-wrap;
}

/* Appending a line to a live order - the portal's one remaining write. */
/* --- rows waiting to go onto a live order ---------------------------------
   Picked in the catalogue, so the row can say what the product IS rather than
   printing the code somebody had to know to type. Quantity and discount stay
   editable here: they are facts about this order, not about the product. */
.sale-addrow { margin-top: .8rem; display: grid; gap: .4rem; }
.sale-addrow__f { display: flex; gap: .4rem; flex-wrap: wrap; align-items: center; }
.sale-addrow__list {
  display: grid; gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.sale-addrow__r {
  display: grid; grid-template-columns: minmax(0, 1fr) 5.5rem 5.5rem 28px;
  gap: .4rem; align-items: center; padding: .3rem .5rem; background: var(--surface);
}
.sale-addrow__r--head {
  background: var(--surface-2); font-size: 9.5px; text-transform: uppercase;
  letter-spacing: .06em; color: var(--ink-4); font-weight: 700;
}
.sale-addrow__r--head span:nth-child(2), .sale-addrow__r--head span:nth-child(3) {
  text-align: end;
}
.sale-addrow__n { min-width: 0; display: grid; gap: .05rem; }
.sale-addrow__n b { font-size: var(--t-sm); overflow: hidden;
                    text-overflow: ellipsis; white-space: nowrap; }
.sale-addrow__sku { font-family: var(--mono); font-size: 10.5px; color: var(--ink-3); }
.sale-addrow__r .field { height: 28px; font-size: var(--t-xs); text-align: end; }
.sale-addrow__r .icon-btn { width: 24px; height: 24px; color: var(--ink-4); }
.sale-addrow__r .icon-btn:hover { color: var(--sale); }
@media (max-width: 620px) {
  .sale-addrow__r { grid-template-columns: minmax(0, 1fr) 4.4rem 4.4rem 24px; }
}

.sale-book {
  margin-top: .8rem; padding: .8rem; border-radius: var(--radius-sm);
  background: var(--warn-wash); border: 1px solid var(--line);
}
.sale-book .fhint { margin: .2rem 0 .6rem; max-width: 62ch; }
.sale-book__row { display: flex; gap: .5rem; align-items: end; flex-wrap: wrap; }
.sale-book__row .field { height: 30px; font-size: var(--t-sm); }

/* --- can we supply this line? -------------------------------------------
   Three states, and the middle one is the point: units that EXIST but are
   booked are a phone call, units that are not there are a purchase order.
   Sized to sit on the baseline of a monospaced SKU without pushing it. */
.stockdot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-inline-end: .35rem; vertical-align: middle; flex: none;
  border: 1px solid rgb(0 0 0 / .12);
}
.stockdot--ok   { background: #16A34A; }   /* free now */
.stockdot--held { background: #D97706; }   /* there, but spoken for */
.stockdot--none { background: var(--line-2); }   /* not there */
[data-theme="dark"] .stockdot { border-color: rgb(255 255 255 / .18); }
/* the status filter sits with the stage chips: same row, same job, and the
   counts beside each word say what pressing it will leave */
.sale-statusf { display: flex; align-items: center; gap: .35rem; }
.sale-statusf .flabel { margin: 0; white-space: nowrap; }
.sale-statusf .field { width: auto; min-width: 9.5rem; }
@media (max-width: 900px) { .sale-search { flex-basis: 100%; margin-inline-start: 0; } }

/* A .field given a small height must lose the base padding with it. The base
   is .6rem top and bottom, which on a 30px control leaves about 10px for the
   text - an input scrolls its own content and survives, a select simply
   clips, and the status filter was showing the top half of its own word. */
.sale-tools .field,
.sale-book__row .field,
.sale-acts__st .field,
.sale-compose .field,
.sl-row__nums input,
.sl-row__note .field {
  height: 30px; padding-block: 0; font-size: var(--t-sm);
}
.sale-compose textarea.field, .sl-row__note .field { height: auto; }
.sale-compose textarea.field { padding-block: .5rem; }
.sl-row__note .field { height: 26px; }
/* A fourth state, and the one that was missing: stock reserved for THIS
   document. It was amber with everybody else's bookings, which is opposite
   news wearing the same colour - "somebody has these" against "we have
   these". Blue, because it is neither a problem nor a free shelf. */
.stockdot--mine { background: #2563EB; }
[data-theme="dark"] .stockdot--mine { background: #60A5FA; }

/* the booking panel when it already knows the answer is no */
.sale-book--no { background: var(--sale-wash); border-color: var(--sale); }
.sale-book__bad {
  margin: .2rem 0 .6rem 1.1rem; display: grid; gap: .15rem;
  font-size: var(--t-xs); color: var(--ink-2);
}
.sale-book__bad code { font-family: var(--mono); color: var(--ink); }
