/* ============================================================ base */
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; background:var(--ground); color:var(--text);
  font-family:var(--font-ka); font-size:var(--fs-body); line-height:var(--lh-body);
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;height:auto;display:block}
a{color:inherit}
:focus-visible{outline:3px solid var(--focus);outline-offset:2px;border-radius:2px}
.dg-skip{position:absolute;left:-9999px}
.dg-skip:focus{left:var(--sp-4);top:var(--sp-4);z-index:100;background:var(--white);padding:var(--sp-3) var(--sp-4);box-shadow:var(--shadow-2)}
.dg-wrap{max-width:1440px;margin:0 auto;padding:0 var(--sp-5)}
@media (max-width:767px){.dg-wrap{padding:0 var(--sp-4)}}
.dg-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* Smooth scrolling belongs on the scroll container, which is the root element —
   set on body it simply does not apply. Declared on both so the anchored jump
   is smooth whichever element the UA treats as the scroller, and the padding
   keeps the target clear of the sticky header. */
html{scroll-behavior:smooth;scroll-padding-top:calc(var(--header-h) + var(--sp-4))}
@media (prefers-reduced-motion:reduce){ html{scroll-behavior:auto} }

h1,h2,h3{margin:0 0 var(--sp-3);font-weight:700;letter-spacing:normal}
h1{font-size:var(--fs-h1);line-height:var(--lh-h1)}
h2{font-size:var(--fs-h2);line-height:var(--lh-h2)}
h3{font-size:var(--fs-h3);line-height:var(--lh-h3);font-weight:600}
p{margin:0 0 var(--sp-4)}
.dg-mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;letter-spacing:.01em}

/* ============================================================ buttons
   Sentence case only. No uppercase — Mkhedruli has none, and a global
   uppercase rule would transform only the Latin model codes. */
.dg-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  min-height:var(--touch);padding:0 var(--sp-5);border:1px solid transparent;
  border-radius:var(--radius);text-decoration:none;
  /* Longhands, not the `font` shorthand. `font:800 16px/1.5 inherit` is invalid —
     a CSS-wide keyword is only legal as an entire declaration value, never as the
     family inside a shorthand — and because the line also carried var() it failed
     at computed-value time, dropping weight, size AND line-height together. Every
     button on the site was rendering at the inherited 400. */
  font-family:inherit;font-weight:800;font-size:var(--fs-body);line-height:1.5;
  cursor:pointer;transition:background-color var(--dur-instant) linear,
    border-color var(--dur-instant) linear,color var(--dur-instant) linear;
}
/* Dark ink on the accent, not white: #FF8A3C is 2.35:1 against white and
   7.84:1 against near-black. This is the whole reason --on-accent exists. */
/* An icon in a button is never the part that gives way. Without this the svg is
   just another flex item, so inside any button that is itself allowed to shrink
   it collapsed to 0 wide and left an empty outline. */
.dg-btn > svg{flex:none}
.dg-btn--primary{background:var(--accent);color:var(--on-accent)}
.dg-btn--primary:hover{background:var(--accent-hover)}
.dg-btn--primary:active{background:var(--accent-active)}
.dg-btn--ghost{background:transparent;color:var(--text);border-color:var(--rule-fn)}
.dg-btn--ghost:hover{background:var(--panel);border-color:var(--text-3)}
.dg-btn--block{width:100%}
.dg-btn[disabled]{opacity:.5;cursor:not-allowed}
/* Used by the 404 and consumable pages too, neither of which loads home.css. */
.dg-btn--lg{min-height:60px;padding:0 var(--sp-6);font-size:var(--fs-card)}

/* ============================================================ breadcrumb */
.dg-crumb{font-size:var(--fs-meta);line-height:var(--lh-meta);color:var(--text-3);
  padding:var(--sp-4) 0;display:flex;flex-wrap:wrap;gap:var(--sp-1) var(--sp-2)}
.dg-crumb a{color:var(--text-3);text-decoration:none}
.dg-crumb a:hover{color:var(--text)}
.dg-crumb [aria-current]{color:var(--text)}
.dg-crumb li{list-style:none}
.dg-crumb ol{display:contents;margin:0;padding:0}
.dg-crumb .sep{color:var(--grey-400)}

/* ============================================================ archive layout */
/* minmax(0,1fr), never a bare 1fr: "1fr" is minmax(auto,1fr), and that auto
   floor is the track's min-content width — long unbreakable content then
   refuses to shrink and pushes the page sideways (measured: 948px of
   horizontal scroll on a 375px phone). Grid children also need min-width:0. */
/* The two columns are two different jobs — where you are in the tree, and the
   machines themselves — so they get real air between them rather than the 32px
   that made the facet rail read as the first column of the grid. */
.dg-archive{display:grid;grid-template-columns:var(--sidebar) minmax(0,1fr);
  gap:var(--sp-8);align-items:start;padding-bottom:var(--sp-6)}
@media (max-width:1279px){.dg-archive{gap:var(--sp-7)}}
@media (max-width:1023px){.dg-archive{grid-template-columns:minmax(0,1fr);gap:var(--sp-4)}}

/* ===================================================== archive page head
   Title and count on the left, sort and view on the right, one hairline under
   the pair — the shape every catalogue uses, because it puts the two controls
   that change what you are looking at on the same line as the thing you are
   looking at. */
.dg-pagehead{display:flex;align-items:flex-end;justify-content:space-between;
  gap:var(--sp-5);flex-wrap:wrap;
  padding-bottom:var(--sp-4);margin:0 0 var(--sp-6);
  border-bottom:1px solid var(--rule)}
.dg-pagehead__id{min-width:0;display:flex;flex-direction:column;gap:2px}
.dg-pagehead__id h1{margin:0}
/* Small, quiet, and under the title — not a row of its own competing with it. */
.dg-pagehead__n{margin:0;font-size:var(--fs-meta);line-height:var(--lh-meta);
  color:var(--text-3);font-variant-numeric:tabular-nums}
.dg-pagehead__tools{display:flex;align-items:center;gap:var(--sp-2);flex:none}

/* The lede sits under the rule and spans the page, so it is prose the reader can
   skip rather than a wall wedged between the title and the machines. */
.dg-lede{margin:0 0 var(--sp-6);max-width:78ch}
/* The toggle belongs to the paragraph above it, not to the white space below. */
.dg-lede .dg-intro__more{margin-top:var(--sp-1)}

/* ------------------------------------------------------------ sort menu
   A <details> of real links. No <select>: sorting is navigation, so as links it
   works with scripting off, it takes the site's own type and colour instead of
   the operating system's, and on a phone four options stop being a full-screen
   wheel picker. */
.dg-sortm{position:relative}
.dg-sortm__btn{display:inline-flex;align-items:center;gap:var(--sp-2);
  min-height:44px;padding:0 var(--sp-3) 0 var(--sp-4);
  border:1px solid var(--rule);border-radius:var(--radius);background:var(--white);
  font-size:var(--fs-sm);color:var(--text);cursor:pointer;white-space:nowrap;
  list-style:none;user-select:none;
  transition:border-color var(--dur-instant) linear,background-color var(--dur-instant) linear}
.dg-sortm__btn::-webkit-details-marker{display:none}
.dg-sortm__btn::marker{content:""}
@media (hover:hover){.dg-sortm__btn:hover{border-color:var(--rule-fn)}}
.dg-sortm__btn:focus-visible{outline:3px solid var(--focus);outline-offset:2px}
.dg-sortm__lbl{color:var(--text-3)}
.dg-sortm__cur{font-weight:600}
.dg-sortm__chev{flex:none;color:var(--text-3);
  transition:transform var(--dur-base) var(--ease-standard)}
.dg-sortm[open] .dg-sortm__btn{border-color:var(--ink);background:var(--white)}
.dg-sortm[open] .dg-sortm__chev{transform:rotate(180deg)}

/* max-content, so the menu is as wide as its longest option. It was pinned to
   232px and "სიმძლავრე: კლებადი" does not fit in that, so half the sort choices
   were cut off in the control that exists to show them. Capped against the
   viewport so it can never run off the side of a phone. */
.dg-sortm__pop{position:absolute;z-index:30;top:calc(100% + 6px);inset-inline-end:0;
  width:max-content;
  min-width:max(100%,232px);
  max-width:min(320px,calc(100vw - var(--sp-5) * 2));
  background:var(--white);border:1px solid var(--grey-200);border-radius:var(--radius-lg);
  box-shadow:var(--shadow-2);padding:var(--sp-1);
  /* Grows out of the button rather than appearing. transform-origin at the
     corner it hangs from, so the motion reads as the button opening. */
  transform-origin:100% 0;
  animation:dg-sortm-in var(--dur-base) var(--ease-enter) both}
@keyframes dg-sortm-in{from{opacity:0;transform:translateY(-6px) scale(.97)}
  to{opacity:1;transform:none}}
.dg-sortm__pop ul{list-style:none;margin:0;padding:0}
.dg-sortm__pop a{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  white-space:nowrap;
  min-height:44px;padding:0 var(--sp-3);border-radius:var(--radius-sm);
  font-size:var(--fs-sm);color:var(--text-2);text-decoration:none;white-space:nowrap;
  transition:background-color var(--dur-instant) linear,color var(--dur-instant) linear}
@media (hover:hover){.dg-sortm__pop a:hover{background:var(--panel);color:var(--ink)}}
.dg-sortm__pop a[aria-current="true"]{color:var(--ink);font-weight:700}
.dg-sortm__tick{flex:none;color:var(--accent-text)}
@media (prefers-reduced-motion:reduce){
  .dg-sortm__pop{animation:none}.dg-sortm__chev{transition:none}
}

/* --------------------------------------------------------- view switcher */
.dg-view{display:inline-flex;flex:none;padding:3px;gap:2px;
  border:1px solid var(--rule);border-radius:var(--radius);background:var(--white)}
.dg-view__b{display:inline-flex;align-items:center;justify-content:center;
  width:38px;height:36px;padding:0;cursor:pointer;
  background:none;border:0;border-radius:var(--radius-sm);color:var(--grey-400);
  transition:background-color var(--dur-instant) linear,color var(--dur-instant) linear}
.dg-view__b svg{fill:currentColor}
@media (hover:hover){.dg-view__b:hover{color:var(--text-2);background:var(--panel)}}
.dg-view__b:focus-visible{outline:3px solid var(--focus);outline-offset:2px}
.dg-view__b.is-on{background:var(--ink);color:var(--white)}

/* ------------------------------------------------------------- list view
   The same cards, laid on their side: photo left, everything else in a column
   beside it. It exists for scanning specs down a page, so the spec rows are the
   part that gains room. */
.dg-grid[data-view="list"]{grid-template-columns:minmax(0,1fr);row-gap:var(--sp-3)}
.dg-grid[data-view="list"] .dg-card{display:grid;
  grid-template-columns:minmax(0,220px) minmax(0,1fr);align-items:stretch}
/* No rule beside the photograph either, for the same reason there is none under
   it in the grid view: the picture already ends where the body begins.

   aspect-ratio:auto, and this is the whole bug in the list view. The card is a
   grid with a 220px media track, and a grid item stretches to the row height by
   default — so with aspect-ratio:1/1 the media took its WIDTH from that height:
   302px tall body, 302px wide picture, in a 220px track. The 82px of overflow
   pushed the body left and the card's overflow:hidden sliced the first few
   characters off the price, the title, the category and every spec label.
   minmax(0,220px) does not help; a definite height beats a track maximum.
   With auto, the height follows the stretch and the width stays in its track. */
.dg-grid[data-view="list"] .dg-card__media{aspect-ratio:auto;min-height:180px;border:0}
.dg-grid[data-view="list"] .dg-card__body{padding:var(--sp-5)}
@media (max-width:640px){
  .dg-grid[data-view="list"] .dg-card{grid-template-columns:minmax(0,116px) minmax(0,1fr)}
  .dg-grid[data-view="list"] .dg-card__body{padding:var(--sp-3)}
}

/* 32px, not 24: these are two distinct controls — where you are in the tree and
   how you are narrowing it — and at 24 the filter heading read as part of the
   category list it sits under. */
/* Scrolls with the page, not inside itself. It was sticky with its own
   max-height and overflow, which put a second scrollbar on the screen and meant
   the wheel did different things a few pixels apart. Sticky is also wrong here
   on its own terms: the rail runs ~1030px against an ~850px viewport, so it
   pinned the moment it reached the header and the bottom filter groups became
   permanently unreachable. */
.dg-aside{display:flex;flex-direction:column;gap:var(--sp-6);
  align-self:start;
  order:-1}   /* first column visually, second in the DOM — see taxonomy-machine_cat.php */
@media (max-width:1023px){.dg-aside{display:none}}

/* --- archive header ------------------------------------------------------
   Title, a clamped lede, then one quiet meta line. The lede used to run the
   full width as an unbroken wall of text under a 40px Georgian headline. */
.dg-intro{margin:0 0 var(--sp-5);max-width:none}
.dg-intro h1{font-size:var(--fs-h1);line-height:var(--lh-h1);font-weight:700;
  margin:0 0 var(--sp-3);text-wrap:balance;max-width:24em}
.dg-intro p{color:var(--text-2);font-size:var(--fs-body);line-height:var(--lh-body);
  margin:0;max-width:68ch}
.dg-intro__lede{position:relative}
/* The clamp is the closed state; app.js animates max-height across the change,
   so the three-to-twenty-line jump stops throwing the chips, the toolbar and the
   whole grid 494px down the page in a single frame. */
.dg-intro__lede p{transition:max-height var(--dur-panel) var(--ease-standard)}
.dg-intro__lede:not(.is-open) p{display:-webkit-box;-webkit-line-clamp:3;
  -webkit-box-orient:vertical;overflow:hidden}
.dg-intro__lede.is-measuring p{overflow:hidden}
@media (prefers-reduced-motion:reduce){ .dg-intro__lede p{transition:none} }
.dg-intro__more{margin-top:0;min-height:44px;display:inline-flex;align-items:center;
  background:none;border:0;padding:0;cursor:pointer;
  font:inherit;font-size:var(--fs-sm);font-weight:600;color:var(--accent-text)}
/* --accent-hover is a FILL colour: #FFB43C on the grey phone ground is 1.8:1,
   and on a phone :hover sticks after the tap, so the label stayed unreadable.
   Guarded, and pointed at the text ramp. */
@media (hover:hover){ .dg-intro__more:hover{color:var(--accent-text-hover)} }
.dg-updated{font-size:var(--fs-meta);color:var(--text-3);margin:0}

/* ============================================================ category nav */
/* --- category nav --------------------------------------------------------
   No panel, no rounded box: the sidebar sits on the page ground and is held
   together by hairlines and a small-caps label. Two stacked grey cards was the
   cheapest-looking thing on the page. */
.dg-catnav{padding:0 0 var(--sp-5);border-bottom:1px solid var(--rule)}

.dg-catnav ul{list-style:none;margin:0;padding:0}
.dg-catnav li{margin:0}
.dg-catnav a,.dg-catnav span.current{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-3);
  padding:6px var(--sp-3);border-inline-start:var(--rail) solid transparent;
  font-size:var(--fs-sm);line-height:var(--lh-sm);color:var(--text-2);text-decoration:none;
  transition:color var(--dur-instant) linear,border-color var(--dur-instant) linear,
             background-color var(--dur-instant) linear}
.dg-catnav a:hover{color:var(--ink);border-inline-start-color:var(--grey-300);background:var(--grey-100)}
.dg-catnav span.current{font-weight:700;color:var(--ink);
  border-inline-start-color:var(--accent-text);background:var(--accent-soft)}
.dg-catnav .count{font-family:var(--font-mono);font-size:var(--fs-micro);color:var(--text-3);
  font-variant-numeric:tabular-nums;flex:none}
.dg-catnav .up{display:flex;align-items:center;gap:var(--sp-2);
  font-size:var(--fs-meta);color:var(--text-3);padding:0 0 var(--sp-3);text-decoration:none}
.dg-catnav .up:hover{color:var(--accent-text)}

/* mobile: sidebar collapses to a scrolling chip row */
/* mobile: the sidebar's category list collapses to a scrolling chip row.
   On the brand archive there is no sidebar at ANY width, so there this row is
   the navigation and it stays on — which is why the item styling lives out here
   and only the display gate is inside the media query. */
.dg-catchips{display:none;gap:var(--sp-2);overflow-x:auto;padding:0 0 var(--sp-3);
  scrollbar-width:none;-webkit-overflow-scrolling:touch;
  scroll-padding-inline:var(--sp-4);scroll-snap-type:x proximity;
  -webkit-mask-image:linear-gradient(90deg,#000 88%,transparent);
  mask-image:linear-gradient(90deg,#000 88%,transparent)}
.dg-catchips::-webkit-scrollbar{display:none}
/* This row IS the subcategory navigation on a phone. It was one uncapped nowrap
   pill 420px wide inside a 358px box, so the first screen showed a single chip
   cut off at the edge with nothing after it: it read as a text field, not as a
   row you can push sideways. The cap plus the fade says "there is more";
   initChips() in app.js scrolls the current one into view. */
.dg-catchips a,.dg-catchips span{
  flex:0 0 auto;max-width:76%;scroll-snap-align:start;
  padding:var(--sp-2) var(--sp-4);min-height:44px;display:inline-flex;align-items:center;
  border:1px solid var(--rule-fn);border-radius:100px;background:var(--white);
  font-size:var(--fs-sm);text-decoration:none;color:var(--text-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transition:border-color var(--dur-instant) linear,color var(--dur-instant) linear}
.dg-catchips span.current{background:var(--ink);color:var(--white);border-color:var(--ink);font-weight:600}
.dg-catchips__n{font-family:var(--font-mono);font-size:var(--fs-micro);font-weight:700;
  color:var(--text-3);font-variant-numeric:tabular-nums;margin-inline-start:6px}
.tax-machine_brand .dg-catchips{display:flex;padding-block:var(--sp-2) var(--sp-4)}
@media (max-width:1023px){
  /* The subcategory strip is gone from the page on category archives: it is a
     second navigation above the grid that the reader has to notice and scroll
     sideways, and it duplicates what the filter drawer is for. The drawer
     carries it now — see dg_drawer_categories(). Brand archives keep theirs:
     they have no drawer group of their own, and the chips there are the only
     way to narrow 188 machines on a phone. */
  .tax-machine_cat .dg-catchips,
  .post-type-archive-machine .dg-catchips{display:none}
}

/* ============================================================ toolbar */
/* Phone only. The same sort menu and view switcher live in the page head on a
   laptop, where the filters are already in the sidebar beside the grid — so this
   bar has nothing left to do there and was rendering a second, duplicate set of
   controls above the first row of cards. */
.dg-toolbar{display:none}
.dg-count{font-size:var(--fs-sm);color:var(--text-3);margin-right:auto;
  display:flex;align-items:baseline;gap:var(--sp-2)}
.dg-count strong{color:var(--text);font-family:var(--font-mono);font-weight:700;
  font-size:var(--fs-h3);line-height:1;font-variant-numeric:tabular-nums}
.dg-count.is-updating{opacity:.45;transition:opacity var(--dur-base) linear}
.dg-sortform__label{font-size:var(--fs-meta);color:var(--text-3);white-space:nowrap}
.dg-sort{min-height:40px;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--white);padding:0 var(--sp-6) 0 var(--sp-3);font:inherit;
  font-size:var(--fs-sm);font-weight:500;color:var(--text);cursor:pointer;
  appearance:none;-webkit-appearance:none;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' stroke='%235C5C63' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right var(--sp-3) center;
  transition:border-color var(--dur-base) var(--ease-standard)}
.dg-sort:hover{border-color:var(--rule-fn)}
.dg-filter-open{display:none}

/* The page head owns these controls on a laptop; the sticky bar owns them on a
   phone. Exactly one of the two pairs is ever on screen — the markup renders the
   same components twice under different ids rather than trying to move one set
   between two places in the layout. */
@media (max-width:1023px){
  .dg-pagehead__tools{display:none}
  .dg-pagehead{align-items:flex-start;padding-bottom:var(--sp-3);margin-bottom:var(--sp-4)}
  .dg-filter-open{display:inline-flex;gap:var(--sp-2)}
  /* Sticky, and now one 48px row instead of the three it used to wrap into:
     count, filter and sort each took a line of their own, 189px pinned under the
     header for the whole length of the listing. */
  .dg-toolbar{position:sticky;top:var(--header-h);z-index:20;background:var(--ground);
    display:flex;flex-wrap:nowrap;align-items:center;gap:var(--sp-2);
    padding:var(--sp-2) 0;margin:0 0 var(--sp-3);border-top:0;border-bottom:0;
    box-shadow:0 1px 0 var(--grey-200)}
  .dg-toolbar .dg-filter-open{flex:1 1 auto;min-width:0;justify-content:center;min-height:44px}
  .dg-toolbar .dg-sortm{flex:1 1 auto;min-width:0}
  .dg-toolbar .dg-sortm__btn{width:100%;justify-content:center;padding-inline:var(--sp-3)}
  /* "დალაგება: რეკომენდებული" is two long Georgian words in half a 320 screen;
     the value alone still says what it is, next to a button labelled filter. */
  /* "დალაგება", not the current value. The three controls in this bar are a
     filter, a sort and a view switch; labelling this one "რეკომენდებული" named
     the state instead of the control, so the row read as one button and two
     mystery words. The selected option is ticked inside the menu. */
  .dg-toolbar .dg-sortm__lbl{display:inline;color:var(--text)}
  .dg-toolbar .dg-sortm__cur{display:none}
  .dg-toolbar .dg-view{flex:none}
  .dg-toolbar .dg-view__b{width:34px;height:36px}
  /* The popover is inside a sticky bar, so it hangs from the bar and must clear
     the cards under it. */
  /* Anchored to the button's end edge and sized to its content. inset-inline:0
     tied it to the button's own third-of-a-row width, which is what cut the
     option labels off. */
  .dg-toolbar .dg-sortm__pop{inset-inline:auto 0;width:max-content;
    min-width:max(100%,232px);max-width:calc(100vw - var(--sp-4) * 2)}
}
/* Icon only, and only once the label genuinely stops fitting. padding:0 is
   load-bearing: .dg-btn carries 24px of inline padding each side, so a 48px box
   left the content area exactly 0 wide and the icon collapsed to nothing — an
   empty outlined square where the filter button should be. */
@media (max-width:359px){
  .dg-toolbar .dg-filter-open span{display:none}
  .dg-toolbar .dg-filter-open{flex:none;width:48px;padding:0;justify-content:center}
}

/* ============================================================ chips */
.dg-chips{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin:0 0 var(--sp-4);padding:0;list-style:none}
/* Applied filters read as removable tokens, not as alerts — a red-bordered pink
   pill per active facet made a two-filter view look like an error state. */
.dg-chip{display:inline-flex;align-items:center;gap:var(--sp-1);min-height:34px;
  padding:0 var(--sp-2) 0 var(--sp-3);border:1px solid var(--rule);background:var(--white);
  border-radius:100px;font-size:var(--fs-meta);font-weight:500;color:var(--text);
  text-decoration:none;transition:border-color var(--dur-base) var(--ease-standard),
                                  background-color var(--dur-base) var(--ease-standard)}
.dg-chip .x{display:inline-flex;align-items:center;justify-content:center;
  width:20px;height:20px;border-radius:50%;color:var(--text-3);font-size:15px;line-height:1;
  transition:background-color var(--dur-instant) linear,color var(--dur-instant) linear}
.dg-chip:hover{border-color:var(--ink)}
.dg-chip:hover .x{background:var(--ink);color:var(--white)}
.dg-chip--clear{border-color:transparent;background:transparent;color:var(--accent-text);
  padding:0 var(--sp-2);font-weight:600}
.dg-chip--clear:hover{border-color:var(--accent-text);color:var(--accent-text)}

/* ============================================================ grid + card */
 /* Rows breathe more than columns. A uniform gap made the grid read as a
    lattice; with the cards this tall, the eye needs a clearer break between one
    row of machines and the next than between two machines side by side. */
.dg-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  column-gap:var(--sp-4);row-gap:var(--sp-6);margin:0;padding:0;list-style:none}
@media (max-width:1279px){.dg-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:640px){.dg-grid{grid-template-columns:minmax(0,1fr);row-gap:var(--sp-5)}}

/* --- card ---------------------------------------------------------------
   A technical catalogue card, not a fashion one: the substance is the spec
   rows, so the photograph is a calm tile and the four numbers get the
   typographic attention. No per-card CTA — the whole card is the link. */
/* No border. A white card is separated from the page by the page being a tone
   darker than it, which is how a catalogue grid is meant to read — a hairline
   around every tile turns a grid of machines into a grid of boxes. The archive
   pages and the featured rail set that grey ground; the resting shadow covers
   the few places a card still sits on white. */
.dg-card{position:relative;display:flex;flex-direction:column;background:var(--white);
  border:0;border-radius:var(--radius-lg);overflow:hidden;
  box-shadow:var(--shadow-card);
  transition:box-shadow var(--dur-base) var(--ease-standard),
             transform var(--dur-base) var(--ease-standard)}
/* Every one of these was unguarded, so on a phone a tap left the card lifted
   3px, its photo scaled 1.04 and its arrow amber — permanently, because nothing
   takes :hover away from a touch until you tap something else. A card that
   stays lifted reads as selected. */
@media (hover:hover){
  .dg-card:hover{box-shadow:var(--shadow-lift);transform:translateY(-3px)}
}
/* 2px, and only for the card's own link. Tabbing to a quick action used to ring
   the entire card as well as the button — two focus indicators for one focus. */
/* :focus-VISIBLE, not :focus-within. A tap on a quick action is focus but not
   focus-visible, so the old rule drew a 2px ink frame around the whole card
   every time someone saved a machine on a phone — and the :has() escape below
   never fired, for exactly the same reason. */
.dg-card:has(.dg-card__link:focus-visible){outline:2px solid var(--focus);outline-offset:2px}

/* No rule under the photograph. The picture already ends where the body begins —
   a hairline there drew a second edge inside a bordered card. */
.dg-card__media{aspect-ratio:4/3;background:var(--white);position:relative;overflow:hidden}
/* The fade-in is a progressive enhancement, so it starts from opacity:1 and is
   only armed once JS is running (html.js). Starting at 0 meant every catalogue
   image was invisible without JavaScript — and invisible to the LCP measurement
   the preload hints exist to win. */
/* No padding: contain already letterboxes, so the inset was a second margin on
   top of the one the aspect ratio creates. Supplier shots vary from wide panel
   saws to tall dust collectors — contain fits whichever edge binds, so each
   photo fills the box on one axis instead of floating in the middle of it. */
/* Direct child only. The brand mark now lives inside this box too, and without
   the combinator it inherited width:100%/height:100%/padding and rendered as a
   24px square. */
/* Extra padding at the top, not all round: the maker's mark floats in that band
   and a machine photographed edge-to-edge ran straight underneath it, so the
   logo landed on sheet metal instead of on white. */
.dg-card__media > img{width:100%;height:100%;object-fit:contain;
  padding:38px var(--sp-3) var(--sp-3);
  transition:transform var(--dur-panel) var(--ease-standard)}
@media (hover:hover){ .dg-card:hover .dg-card__media > img{transform:scale(1.04)} }
.js .dg-card__media > img{opacity:0;transition:opacity var(--dur-base) linear,
                                             transform var(--dur-panel) var(--ease-standard)}
.js .dg-card__media > img.is-loaded{opacity:1}

/* No top padding: the price row is the first thing in here and it sits directly
   under the photograph, which is what closes the gap the divider used to fill. */
.dg-card__body{display:flex;flex-direction:column;flex:1;
  padding:0 var(--sp-4) var(--sp-4);gap:6px}

/* Price and arrow, the first row of the body. */
.dg-card__lead{display:flex;align-items:center;justify-content:space-between;
  gap:var(--sp-3);padding-top:var(--sp-3)}

/* Brand sits above the title in letterspaced caps rather than floating over the
   photograph in a bordered box. */
.dg-card__brand{display:block;line-height:0;font-size:var(--fs-micro);
  color:var(--text-3)}

/* The display face, not the body one: at 14px beside a 19px price the title is
   supporting text, and the condensed cut fits more of a long Georgian machine
   name on each of its two lines. */
.dg-card__title{font-family:var(--font-display);
  font-size:18px;line-height:23px;font-weight:700;margin:0;letter-spacing:.005em;
  /* reserve exactly two lines so every card in a row aligns */
  min-height:46px;text-wrap:pretty;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.dg-card__link{text-decoration:none;color:var(--text)}
/* Stretched link: the whole card is clickable, but only one link is announced. */
.dg-card__link::after{content:"";position:absolute;inset:0;z-index:1}
/* The title does not recolour on hover — the card already lifts, and the
   revealed actions are the hover affordance. */
.dg-card__link:focus-visible{outline:none}

/* The manufacturer's mark, capped so a wide logo and a tall one occupy the same
   band and the titles below them still line up across a row. */
.dg-card__logo{display:inline-flex;align-items:center;height:18px}
.dg-card__logo img{height:100%;width:auto;max-width:120px;object-fit:contain;display:block}
.dg-card__logo--text{font-size:var(--fs-micro);letter-spacing:.02em;
  text-transform:uppercase;color:var(--text-3);text-decoration:none;line-height:1.4}

/* What kind of machine this is, above the title, coloured by its top-level
   family so a mixed grid groups at a glance. Text, not a chip: the card already
   has a photograph, a spec block and a footer rule, and a fourth bordered box
   would be one border too many. Colours are taken from the logo ramp. */
/* Under the title, and quiet: it labels the machine rather than announcing it,
   so it drops to 10px at a normal weight instead of bold caps above the name. */
.dg-card__cat{display:block;margin:0;
  font-size:10px;line-height:1.4;font-weight:500;color:var(--text-3)}
/* One grey, whatever family the machine belongs to. Six colours across a grid
   of twenty-four made the page read as colour-coded when the colour carried no
   information the buyer could act on — it is a label, not a status. The is-*
   classes stay in the markup; nothing colours them any more. */

/* The maker's mark, floated over the top-left of the photograph. */
.dg-card__media .dg-card__brand{position:absolute;top:16px;inset-inline-start:16px;z-index:2;
  display:inline-flex;pointer-events:none}
.dg-card__brand--bare{display:block;padding:var(--sp-4) var(--sp-4) 0}

/* Machine count on the category cards of the catalogue landing page. */
.dg-card__count{display:inline-flex;align-self:flex-start;align-items:center;
  min-height:24px;padding:0 10px;border-radius:var(--radius-sm);
  font-size:var(--fs-micro);line-height:1;font-weight:700;
  color:var(--text-2);background:var(--grey-100);
  font-variant-numeric:tabular-nums}
.dg-card__kids{list-style:none;margin:var(--sp-2) 0 0;padding:0}

/* Three rows are reserved whether or not the machine has three. Supplier spec
   coverage is uneven, and letting the block collapse made every card in a row a
   different height with the price line floating at a different place. */
/* The reserve lives on the wrapper so specs and the fallback blurb share it. */
/* Longhand, and after: `margin:` shorthand immediately after `margin-top:auto`
   resets the auto back to the shorthand's own top value. The auto is what pins
   the spec block to the bottom so every card in a row ends level. */
.dg-card__facts{margin:var(--sp-2) 0 0;margin-top:auto;
  min-height:calc(var(--lh-meta) * 3 + 14px);
  padding-top:var(--sp-2);
  display:flex;flex-direction:column;gap:var(--sp-2)}
.dg-card__specs{margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.dg-card__blurb{margin:0;font-size:var(--fs-meta);line-height:var(--lh-meta);color:var(--text-3);
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.dg-card__specs + .dg-card__blurb{-webkit-line-clamp:2}
.dg-card__spec{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-3)}
.dg-card__spec dt{font-size:var(--fs-meta);line-height:var(--lh-meta);color:var(--text-3);
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dg-card__spec dd{margin:0;font-size:var(--fs-meta);line-height:var(--lh-meta);
  font-family:var(--font-mono);font-variant-numeric:tabular-nums;color:var(--text);
  font-weight:500;white-space:nowrap;flex:none}
/* No dotted leader: over the short gaps these labels leave, it rendered as one
   stray dot that reads as a typo. The baseline alignment does the work. */

/* .dg-card__foot is gone — the price and the arrow it held are now the body's
   first row. The specs take the space it used to sit under. */
/* .dg-card__go is gone too: the whole card is one stretched link, and an arrow
   in the corner reads as "the target is here" when the target is everywhere. */

/* The price slot is never empty: with most machines quote-only, an empty slot
   reads as a bug, so the enquiry string occupies it in a muted grey. */
/* Both states take the price face, not just the figure: on a Georgian page
   "შეკვეთით" sits in the same slot, at the same size, answering the same
   question, and a different family there reads as a different kind of value.
   700 is the only weight shipped — see fonts.css — so nothing here asks for
   another, or the browser draws a synthetic one. */
.dg-price{font-family:var(--font-price);font-weight:700;font-variant-numeric:tabular-nums}
.dg-price.is-price{color:var(--text)}
.dg-price.is-enquiry{color:var(--text-3)}

@media (max-width:640px){
  /* The card stays vertical on phones. It used to become a row with a 120px
     square thumbnail, which is too small to read a machine from — and the same
     media query hid half the spec rows, which are the reason the card exists. */
  .dg-card__body{padding:var(--sp-4)}
}

/* ============================================================ empty + skeleton */
.dg-empty{border:1px solid var(--rule);border-radius:var(--radius-lg);background:var(--panel);
  padding:var(--sp-6);text-align:start;max-width:60ch}
.dg-empty h2{margin-bottom:var(--sp-2)}
.dg-empty ul{margin:var(--sp-3) 0;padding-inline-start:var(--sp-5)}

/* ======================================================== the blank state
   Every place with nothing to show uses this: an illustration, what happened,
   why, and a way onward. Centred rather than left-aligned, because there is no
   column of content for it to align to — it IS the content, and a left-aligned
   paragraph floating in an empty region reads as something that failed to load.

   No card, no border, no panel fill. A box drawn around emptiness makes the
   emptiness look deliberate in the wrong way; the illustration carries it. */
.dg-blank{display:flex;flex-direction:column;align-items:center;text-align:center;
  gap:var(--sp-3);max-width:46ch;margin-inline:auto;
  padding:var(--sp-7) var(--sp-4);
  /* Held above the optical centre. Dead-centring a short block in a tall region
     leaves it looking low, because the eye weights the top of the space. */
  justify-content:center}
.dg-blank__art{display:block;width:clamp(132px,34vw,196px);height:auto;
  margin-bottom:var(--sp-1);
  /* The renders carry their own contact shadow, so nothing is added here. */
  animation:dg-blank-in var(--dur-panel) var(--ease-enter) both}
/* h2 by default (h1 on the 404) — the size here is the component's, not the
   element's, so promoting it from a <p> changed nothing visually. */
.dg-blank__h{margin:0;font-family:var(--font-display);font-weight:700;
  font-size:var(--fs-h3);line-height:var(--lh-h3);color:var(--ink);text-wrap:balance}
.dg-blank__p{margin:0;font-size:var(--fs-sm);line-height:var(--lh-sm);color:var(--text-3);
  max-width:40ch;text-wrap:pretty}
.dg-blank__acts{display:flex;flex-wrap:wrap;gap:var(--sp-2);justify-content:center;
  margin-top:var(--sp-3)}
.dg-blank__acts .dg-btn{min-height:var(--touch)}
/* Anything a caller appends — applied-filter chips, a category list — sits under
   the actions with a rule above it, so it reads as a second offer and not as
   part of the sentence. */
.dg-blank__more{margin-top:var(--sp-6);padding-top:var(--sp-5);
  border-top:1px solid var(--rule);width:100%}
.dg-blank__more h3{margin:0 0 var(--sp-3);font-size:var(--fs-meta);font-weight:700;
  color:var(--text-3)}
.dg-blank__more .dg-chips{justify-content:center}
/* Any bare link list a caller appends becomes chips. Underlined links stacked
   one per line under a centred illustration read as a sitemap the page fell
   back to; chips read as the offer the state is making. */
.dg-blank__more ul{list-style:none;margin:0;padding:0;
  display:flex;flex-wrap:wrap;gap:var(--sp-2);justify-content:center}
.dg-blank__more ul a{display:inline-flex;align-items:center;min-height:var(--touch);
  padding:0 var(--sp-4);border:1px solid var(--rule);border-radius:100px;
  background:var(--white);color:var(--text-2);text-decoration:none;
  font-size:var(--fs-sm);line-height:1.2;
  transition:border-color var(--dur-instant) linear,color var(--dur-instant) linear}
@media (hover:hover){
  .dg-blank__more ul a:hover{border-color:var(--ink);color:var(--ink)}
}

/* It arrives rather than appears: these states usually replace something, and a
   hard swap reads as a glitch. */
@keyframes dg-blank-in{from{opacity:0;transform:translateY(8px) scale(.96)}
  to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.dg-blank__art{animation:none}}

/* Inside a drawer or the search palette there is no page to fill, and the panel
   is already narrow — the illustration steps down so the copy stays the point. */
/* No max-width here. In a drawer the panel's own padding already sets the
   measure, and a 34ch cap squeezed the primary button until its label wrapped
   with the arrow stranded beside a two-line word. The paragraph keeps its own
   cap below, which is the only part that actually needs one. */
.dg-blank--tight{padding:var(--sp-5) var(--sp-3);gap:var(--sp-2);max-width:none;width:100%}
.dg-blank--tight .dg-blank__p{max-width:32ch}
.dg-blank--tight .dg-blank__art{width:clamp(104px,26vw,140px)}
.dg-blank--tight .dg-blank__h{font-size:var(--fs-card);line-height:var(--lh-card)}
.dg-blank--tight .dg-blank__p{font-size:var(--fs-meta);line-height:var(--lh-meta)}
.dg-blank--tight .dg-blank__acts{margin-top:var(--sp-2);width:100%}
.dg-blank--tight .dg-blank__acts .dg-btn{width:100%}

@media (max-width:640px){
  .dg-blank{padding:var(--sp-6) var(--sp-3)}
  .dg-blank__acts{width:100%;flex-direction:column}
  .dg-blank__acts .dg-btn{width:100%}
}
.dg-skeleton{background:var(--grey-200);border-radius:var(--radius)}

/* ============================================================ pagination */
/* Wrapping, not a single row. At 48px an item and eight pages this overflowed
   BOTH ends of a 358px box — and the left overflow sits at negative x, where no
   amount of scrolling reaches it, while the right overflow widens the document
   and pans the whole page sideways on iOS. */
.dg-pager{display:flex;flex-wrap:wrap;gap:var(--sp-2);justify-content:center;
  margin:var(--sp-7) 0 0;padding:0;list-style:none}
.dg-pager a,.dg-pager span{display:inline-flex;align-items:center;justify-content:center;
  min-width:var(--touch);min-height:var(--touch);padding:0 var(--sp-3);
  border:1px solid var(--rule-fn);border-radius:var(--radius);text-decoration:none;
  font-family:var(--font-mono);font-size:var(--fs-sm);color:var(--text-2);background:var(--white)}
.dg-pager .current{background:var(--ink);color:var(--white);border-color:var(--ink);font-weight:700}
.dg-pager a:hover{border-color:var(--text-3);color:var(--text)}

/* ============================================================ header / footer */

/* ============================================================ sort form */
.dg-sortform{display:flex;align-items:center;gap:var(--sp-2)}

/* --- static pages -------------------------------------------------------- */
.dg-page{padding-block:var(--sp-7) var(--sp-6);max-width:760px}
.dg-page__title{font-size:var(--fs-h1);line-height:var(--lh-h1);font-weight:700;
  margin:0 0 var(--sp-5);text-wrap:balance}
/* The blocks under the results grid are separate documents — a guide, a
   how-to-choose list, a FAQ — and they were running into each other at 16px and
   at nothing at all, so the FAQ heading read as the last line of the list above
   it. Set on the column so every section keeps the same rhythm whichever ones a
   category happens to have. */
.dg-results > .dg-sec{margin-top:var(--sp-8)}
.dg-results > .dg-sec:first-child{margin-top:0}
@media (max-width:767px){.dg-results > .dg-sec{margin-top:var(--sp-7)}}

.dg-prose{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-2)}
.dg-prose>*+*{margin-top:var(--sp-4)}
.dg-prose h2{font-size:var(--fs-h2);line-height:var(--lh-h2);font-weight:700;color:var(--text);
  margin-top:var(--sp-7);text-wrap:balance}
.dg-prose h3{font-size:var(--fs-h3);line-height:var(--lh-h3);font-weight:600;color:var(--text);
  margin-top:var(--sp-6)}
.dg-prose a{color:var(--accent-text);text-decoration:underline;text-underline-offset:2px}
/* List styling lives in single.css, which draws its own markers. Setting
   padding-left here as well put the two rules in conflict on product pages. */
.dg-prose ol{padding-left:var(--sp-5)}
.dg-prose li+li{margin-top:var(--sp-2)}
.dg-prose strong{color:var(--text);font-weight:600}
.dg-prose table{width:100%;border-collapse:collapse;font-size:var(--fs-sm)}
.dg-prose th,.dg-prose td{text-align:left;padding:var(--sp-2) var(--sp-3);
  border-bottom:1px solid var(--rule)}
.dg-prose th{color:var(--text);font-weight:600}

.dg-404__lede{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-2);
  margin:0 0 var(--sp-5)}
.dg-404__h2{font-size:var(--fs-h3);font-weight:600;margin:var(--sp-7) 0 var(--sp-3)}
.dg-404__list{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:var(--sp-2) var(--sp-4)}
.dg-404__list a{color:var(--accent-text);font-size:var(--fs-sm)}

/* Until supplier photography lands, a card with no image collapses the media box
   rather than reserving an empty 4:3 well. Cards that do have one are unaffected,
   so the two can coexist as images arrive brand by brand. */
.dg-card--noimg .dg-card__body{padding-top:var(--sp-4)}
.dg-card--noimg{border-top:3px solid var(--ink)}

/* --- contact -------------------------------------------------------------- */
.dg-contact{padding-block:var(--sp-7) var(--sp-6);max-width:none}
/* The form is the wider column. It is the thing the page exists to get filled
   in, and the channels beside it are a short list of labelled rows. */
.dg-contact__grid{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1fr);
  gap:var(--sp-8);align-items:start}
.dg-contact .dg-page__title{font-size:var(--fs-display);line-height:var(--lh-display);
  margin-bottom:var(--sp-3)}
.dg-contact__details .dg-prose{font-size:var(--fs-card);line-height:var(--lh-body);
  max-width:46ch}

/* Label left, value right, a hairline between each — the shape every contact
   page worth copying uses. It was a two-column auto/1fr grid, which on a page
   where only the phone number is filled in collapsed to one short line floating
   in half a screen of nothing. */
.dg-contact__list{margin:var(--sp-6) 0 0;display:block;
  border-top:1px solid var(--grey-200)}
.dg-contact__list dt{
  float:inline-start;width:9.5em;padding:var(--sp-3) 0;
  font-size:var(--fs-meta);line-height:var(--lh-body);font-weight:700;color:var(--text-3)}
.dg-contact__list dd{margin:0;padding:var(--sp-3) 0 var(--sp-3) 9.5em;
  border-bottom:1px solid var(--grey-200);
  font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text)}
.dg-contact__list dd a{color:var(--text);text-decoration:none;
  border-bottom:1px solid var(--grey-300);
  transition:border-color var(--dur-instant) linear,color var(--dur-instant) linear}
.dg-contact__list dd a:hover{color:var(--accent-text);border-bottom-color:var(--accent)}
/* No rule of its own. The agent card carries a border-top for the buy box it
   was drawn for; here it lands 20px under the channel list's own closing
   hairline and the two read as one doubled line. */
.dg-contact .dg-agent{margin-top:var(--sp-6);border-top:0;padding-top:0}

/* White, like every other card on the site now. A grey slab with a border was
   the heaviest thing on a page that is otherwise type. */
.dg-contact .dg-quote{background:var(--white);border:0;box-shadow:var(--shadow-card);
  padding:var(--sp-6);max-width:none}
.dg-contact .dg-quote-sec{padding:0}

@media (max-width:900px){
  .dg-contact__grid{grid-template-columns:minmax(0,1fr);gap:var(--sp-6)}
  /* Form first: the channels are a reference list, the form is the action. */
  .dg-contact__form{order:-1}
}
@media (max-width:520px){
  .dg-contact__list dt{float:none;width:auto;padding-bottom:0}
  .dg-contact__list dd{padding-inline-start:0}
  .dg-contact .dg-quote{padding:var(--sp-4)}
}
.dg-contact__list a{color:var(--accent-text);text-decoration:none}
.dg-contact__list a:hover{color:var(--accent-hover)}
.dg-contact__empty{margin-top:var(--sp-5);padding:var(--sp-4);background:var(--panel);
  border-left:3px solid var(--rule-fn);font-size:var(--fs-sm);color:var(--text-2)}
.dg-contact .dg-quote{max-width:none}
@media (max-width:900px){
  .dg-contact__grid{grid-template-columns:1fr;gap:var(--sp-6)}
}

/* The focus ring is --ink, which is also the background of every dark section —
   an outline the same colour as its ground is no outline at all. Re-declare the
   token on those containers so :focus-visible needs no per-section rule. */
/* Dark sections: the focus ring must not be --ink, and accented TEXT flips too.
   On white, accent text needs the darker --orange-700 to clear 4.5:1; on
   near-black that same colour is the unreadable one and the bright fill is the
   legible one (7.84:1). Re-declaring the token here means every component can
   say color:var(--accent-text) and be right in both places. */
/* .dg-hero is NOT in this list: the home hero is a white field now, and while it
   was here every focus ring inside it was white drawn on white — the category
   rail, the slider dots and both promo blocks had no visible focus at all. */
.dg-footer,.dg-cta,.dg-util,.dg-sub{--focus:var(--white);--accent-text:var(--accent)}
/* Light islands inside a dark section take the light tokens back. The enquiry
   card sits on white inside .dg-cta, so it was inheriting a white focus ring and
   a white required-marker from its own container. */
.dg-cta .dg-form,.dg-cta .dg-quote{--focus:var(--ink);--accent-text:var(--amber-800)}

/* Every grid child opts out of the min-content floor. */
.dg-archive>*,.dg-grid>*,.dg-rail__track>*,.dg-consgrid>*{min-width:0}

/* A brand archive is a single column — no facet sidebar. */
.dg-archive--brand{grid-template-columns:minmax(0,1fr)}

/* In a card the price is one line among several, not the headline it is on a
   product page. Almost every machine is quote-only, so 24px "price on request"
   x24 shouted; a real price still gets weight when there is one. */
/* Both states are the card's headline now — a real figure and "შეკვეთით" answer
   the same question, so they are set the same size and only the colour differs. */
.dg-card .dg-price{font-size:var(--fs-h3);line-height:1.15;
  color:var(--ink);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dg-card .dg-price.is-enquiry{color:var(--text-2)}
.dg-count__sep{color:var(--grey-300)}
.dg-count .dg-updated{font-size:var(--fs-meta)}

/* FAQ ---------------------------------------------------------------------
   Each row is a card rather than a rule-separated line, so an open answer reads
   as one object instead of text loose between two borders. The answer animates
   on grid-template-rows 0fr -> 1fr, the only way to transition to a height the
   content decides; the inner wrapper owns the overflow. */
.dg-faq__list{display:flex;flex-direction:column;gap:var(--sp-2)}
.dg-faq__item{background:var(--white);border:1px solid var(--grey-200);
  border-radius:var(--radius-lg);overflow:hidden;
  transition:border-color var(--dur-base) var(--ease-standard),
             box-shadow var(--dur-base) var(--ease-standard)}
@media (hover:hover){.dg-faq__item:hover{border-color:var(--grey-300)}}
.dg-faq__item[open]{border-color:var(--grey-300);box-shadow:var(--shadow-1)}
.dg-faq__item summary{list-style:none;cursor:pointer;min-height:var(--touch);display:flex;
  align-items:center;justify-content:space-between;gap:var(--sp-4);
  padding:var(--sp-4) var(--sp-5);
  font-size:var(--fs-card);line-height:var(--lh-card);font-weight:600;color:var(--ink);
  transition:background-color var(--dur-instant) linear,color var(--dur-instant) linear}
.dg-faq__item summary::-webkit-details-marker{display:none}
.dg-faq__item summary::marker{content:""}
@media (hover:hover){.dg-faq__item summary:hover{background:var(--grey-100)}}
.dg-faq__chev{flex:none;color:var(--text-3);
  transition:transform var(--dur-panel) var(--ease-standard),color var(--dur-instant) linear}
.dg-faq__item summary:hover .dg-faq__chev{color:var(--accent-text)}
.dg-faq__item[open] .dg-faq__chev{transform:rotate(180deg);color:var(--accent-text)}
.dg-faq__wrap{display:grid;grid-template-rows:0fr;
  transition:grid-template-rows var(--dur-panel) var(--ease-standard)}
.dg-faq__item.is-open .dg-faq__wrap{grid-template-rows:1fr}
.dg-faq__a{overflow:hidden;color:var(--text-2);max-width:70ch;
  padding:0 var(--sp-5);opacity:0;
  transition:opacity var(--dur-base) var(--ease-standard),padding var(--dur-panel) var(--ease-standard)}
.dg-faq__item.is-open .dg-faq__a{opacity:1;padding:0 var(--sp-5) var(--sp-5)}
/* Without JS the element still opens; it just does not animate. */
.no-js .dg-faq__wrap,html:not(.js) .dg-faq__wrap{grid-template-rows:1fr}
.no-js .dg-faq__a,html:not(.js) .dg-faq__a{opacity:1;padding:0 var(--sp-5) var(--sp-5)}

/* How to choose -----------------------------------------------------------
   Decision criteria, not specifications. These were rendering through the spec
   table's styles with an inline font override to cancel the tabular figures,
   which is precisely why the section read as indented plain text. Numbered,
   because the order is the order the reader should decide in. */
/* Eight situations, read across rather than down. As a stack these were 8 cards
   of full-width prose — about 1,800px the reader scrolls past on the way to the
   FAQ. As a row you can see that there ARE eight, and read the one that matches
   the problem you came with. Native scroll: momentum, trackpad, snap and
   keyboard all come free, and with no JS it is still a row you can push. */
.dg-choose__list{list-style:none;margin:0;padding:0 0 var(--sp-3);
  display:grid;grid-auto-flow:column;grid-auto-columns:minmax(272px,304px);
  gap:var(--sp-3);
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x proximity;scroll-padding-inline:0;
  scrollbar-width:thin;
  /* The row runs to the page gutter and fades, so it reads as continuing rather
     than as ending in a hard edge. */
  -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 56px),transparent);
  mask-image:linear-gradient(90deg,#000 calc(100% - 56px),transparent)}
.dg-choose__list::-webkit-scrollbar{height:8px}
.dg-choose__list::-webkit-scrollbar-thumb{background:var(--grey-300);border-radius:4px}
.dg-choose__item{display:flex;flex-direction:column;gap:var(--sp-3);align-items:flex-start;
  scroll-snap-align:start;
  background:var(--white);border:1px solid var(--grey-200);border-radius:var(--radius-lg);
  padding:var(--sp-5);
  transition:border-color var(--dur-base) var(--ease-standard),
             box-shadow var(--dur-base) var(--ease-standard),
             transform var(--dur-base) var(--ease-standard)}
@media (hover:hover){
  .dg-choose__item:hover{border-color:var(--grey-300);box-shadow:var(--shadow-lift);
    transform:translateY(-2px)}
}
.dg-choose__n{flex:none;width:30px;height:30px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--accent);color:var(--on-accent);
  font-size:var(--fs-meta);font-weight:700;font-variant-numeric:tabular-nums}
.dg-choose__body{min-width:0}
.dg-choose__crit{margin:0 0 6px;font-family:var(--font-ka);
  font-size:var(--fs-card);line-height:var(--lh-card);font-weight:700;color:var(--ink);
  text-wrap:balance}
.dg-choose__guide{margin:0;font-size:var(--fs-sm);line-height:var(--lh-sm);
  color:var(--text-2)}
@media (max-width:560px){
  .dg-choose__list{grid-auto-columns:minmax(244px,264px);gap:var(--sp-2)}
  .dg-choose__item{padding:var(--sp-4)}
}
@media (prefers-reduced-motion:reduce){
  .dg-choose__list{scroll-behavior:auto}
  .dg-choose__item{transition:none}
}

/* --------------------------------------------------------- the buying guide */
.dg-guide__lead{margin:0 0 var(--sp-5);max-width:78ch}
/* Inside an accordion row the prose keeps its rhythm but loses the h2 scale —
   the row's own summary is the heading now. */
.dg-guide .dg-faq__a > *:first-child{margin-top:0}
.dg-guide .dg-faq__a h3,.dg-guide .dg-faq__a h4{
  font-size:var(--fs-card);line-height:var(--lh-card);margin:var(--sp-4) 0 var(--sp-2)}
.dg-about{max-width:78ch}

/* --- archive ground ------------------------------------------------------
   The facet rail sits ON the page, not in a grey card. A panel around the
   controls made them one object the eye has to enter, and it fought the gutter:
   at 32px of gap the card edge sat close enough to the first column of cards to
   read as part of the grid. Hairlines between groups do the same separating job
   with nothing drawn around the outside. */
.tax-machine_cat .dg-aside,
.post-type-archive-machine .dg-aside,
.tax-machine_brand .dg-aside{background:none;border:0;border-radius:0;padding:0}
/* The page stays white; each card is held by its own resting shadow rather than
   by a hairline or by a tone change behind it. */
.tax-machine_cat .dg-card__media,
.post-type-archive-machine .dg-card__media,
.tax-machine_brand .dg-card__media{background:var(--white)}
.tax-machine_cat .dg-catnav,
.post-type-archive-machine .dg-catnav,
.tax-machine_brand .dg-catnav,
.tax-machine_cat .dg-fgroup{border-bottom-color:var(--grey-200)}
.tax-machine_cat .dg-toolbar{border-color:var(--grey-200)}

/* ============================================================ reach & agent
   Two ways to skip a form, and the person who answers it. Shared: the product
   page's buy box and the home page's closing CTA both use them, so they live
   here rather than in single.css. The .dg-agent divider is the caller's job —
   it means something different in a buy box than under a heading. */
/* --- reach ---------------------------------------------------------------
   Two ways to skip the form. WhatsApp keeps its own green because a brand mark
   recoloured is a brand mark misused. */
.dg-reach{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-2);margin-top:2px}
.dg-reach > a{display:inline-flex;align-items:center;justify-content:center;gap:7px;
  min-height:48px;padding:0 var(--sp-2);border-radius:var(--radius);
  border:1px solid transparent;
  font-size:var(--fs-meta);font-weight:800;color:var(--ink);text-decoration:none;
  white-space:nowrap;
  transition:border-color var(--dur-instant) linear,color var(--dur-instant) linear,
             background-color var(--dur-instant) linear}
/* Each carries its own tint rather than a hairline: at this size a 10% wash
   reads as a button where a 1px border reads as a table cell. */
.dg-reach__wa{background:var(--tint-wa)}
.dg-reach__wa svg{color:#1EA855;flex:none}
.dg-reach__wa:hover{border-color:#25D366}
.dg-reach__tel{background:var(--tint-saved)}
.dg-reach__tel svg{color:var(--accent-text);flex:none}
.dg-reach__tel:hover{border-color:var(--accent-hover)}
.dg-reach__tel span{font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis}
/* One button alone should not sit in half the rail. */
.dg-reach:has(> a:only-child){grid-template-columns:1fr}

/* --- the person answering -------------------------------------------------
   Divider first: this is a different kind of thing from the buttons above it. */
.dg-agent{display:flex;align-items:center;gap:var(--sp-3);
  padding-top:var(--sp-4);border-top:1px solid var(--grey-300);flex-wrap:wrap}
.dg-agent__ph{position:relative;flex:none;width:44px;height:44px;border-radius:50%;
  overflow:visible;background:var(--grey-200)}
.dg-agent__ph img{width:44px;height:44px;border-radius:50%;object-fit:cover;display:block}
.dg-agent__ph.is-initial{display:inline-flex;align-items:center;justify-content:center;
  background:var(--accent);color:var(--on-accent);font-size:var(--fs-card);font-weight:700}
/* Presence, not a promise: the ring keeps the dot legible on any photo. */
.dg-agent__dot{position:absolute;right:0;bottom:1px;width:12px;height:12px;border-radius:50%;
  background:var(--success-600);border:2px solid var(--panel);display:block}
.dg-agent__who{display:flex;flex-direction:column;min-width:0}
.dg-agent__who strong{font-size:var(--fs-sm);line-height:1.3;color:var(--ink);font-weight:700}
.dg-agent__who em{font-style:normal;font-size:var(--fs-micro);line-height:1.4;color:var(--text-3)}
.dg-agent__hrs{flex:1 1 100%;font-size:var(--fs-micro);color:var(--text-3)}

/* ============================================================ card actions
   A row along the bottom of the card: compare as a square, shortlist as the
   wide button beside it. They used to hover-reveal over the photograph, which
   meant that on every touch device — most of this catalogue's traffic — the two
   controls on the card did not exist until something was tapped. */
.dg-card__acts{position:relative;z-index:3;
  display:flex;align-items:stretch;gap:var(--sp-2);
  margin-top:var(--sp-4);padding-top:var(--sp-1)}
.dg-cact{display:inline-flex;align-items:center;justify-content:center;gap:7px;
  width:40px;height:40px;padding:0;flex:none;cursor:pointer;
  border:1px solid transparent;border-radius:var(--radius);
  /* Neutral, so the square does not have to be re-tinted every time the fill
     beside it changes — and so the one coloured thing in the footer is the
     action most people want. */
  background:var(--grey-100);color:var(--text-2);
  font-family:inherit;font-size:var(--fs-label);line-height:1;font-weight:700;
  transition:background-color var(--dur-instant) linear,
             border-color var(--dur-instant) linear,
             color var(--dur-instant) linear,
             transform var(--dur-instant) var(--ease-standard)}
.dg-cact:hover{background:var(--grey-200);color:var(--ink)}
/* A ring that follows the button's own corners instead of a 3px slab bolted to
   its bounding box. Still visible, still 2px, no longer the loudest thing on
   the card. */
.dg-cact:focus-visible{outline:none;
  border-color:var(--ink);box-shadow:0 0 0 2px var(--white),0 0 0 4px var(--ink)}
.dg-cact:active{transform:scale(.96)}
.dg-cact > svg{flex:none}

/* The wide one. Takes the rest of the row, carries the word, and is the only
   filled control on the card. */
.dg-cact--save{flex:1 1 auto;width:auto;padding:0 var(--sp-3);
  background:var(--save-fill);color:var(--ink)}
.dg-cact--save:hover{background:var(--save-fill-hover);color:var(--ink)}
.dg-cact__t{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* Two glyphs in one grid cell: the off state fades out under the on state
   rather than being swapped, which would pop at the moment of the click. Only
   the bookmark does this — outline to solid is still a bookmark. Compare keeps
   its scales throughout: swapping the icon for a tick threw away the one thing
   that said what the button was for. */
.dg-cact__i{display:grid;place-items:center;width:17px;height:17px;flex:none}
.dg-cact__i > svg{grid-area:1/1;
  transition:opacity var(--dur-base) var(--ease-standard),
             transform var(--dur-base) var(--ease-standard)}
.dg-cact__on{opacity:0;transform:scale(.5)}
.dg-cact.is-saved .dg-cact__off{opacity:0;transform:scale(.5)}
.dg-cact.is-saved .dg-cact__on{opacity:1;transform:none}

/* One ON state for both: ink means "this machine is in one of your lists".
   They sit next to each other on the same card, so two different ON colours
   would read as two different kinds of state rather than the same machine
   being in two lists. */
.dg-cact.is-saved,.dg-cact.is-cmp{
  background:var(--ink);color:var(--white);border-color:var(--ink)}
.dg-cact.is-saved:hover,.dg-cact.is-cmp:hover{
  background:var(--grey-900);color:var(--white)}

/* The tray is full and this machine is not in it: the control stays where it
   is, dimmed, rather than disappearing and leaving a hole under the pointer. */
.dg-cact.is-full{cursor:not-allowed;opacity:.45}
.dg-cact.is-full:hover{transform:none;background:var(--grey-100);color:var(--text-2)}
/* Dimmed is a state, not an excuse: at 45% the focus ring on every non-selected
   compare button was barely visible once the tray filled up. */
.dg-cact.is-full:focus-visible{opacity:1}

/* Touch targets. The square grows to the 44px minimum; the wide button is
   already taller than that. */
@media (hover:none){
  .dg-cact{width:44px;height:44px}
  .dg-cact--save{width:auto}
}
@media (prefers-reduced-motion:reduce){
  .dg-cact,.dg-cact__i > svg{transition-duration:1ms}
  .dg-cact:active{transform:none}
}

/* The same control on the product page rail, where it sits on its own rather
   than in a card footer — it keeps the grow and the press the footer copies
   have, and nothing needs cancelling now that nothing slides in. */
.dg-cact.dg-cmpadd:hover{transform:scale(1.06)}
.dg-cact.dg-cmpadd:active{transform:scale(.94)}
.dg-cact.dg-cmpadd.is-full{opacity:.45}

/* ============================================================ compare tray */
/* Holds the picks themselves, not a number: four slots that are visible whether
   or not they are filled, so one machine in the tray already says what the tray
   is for and how many more it takes. The primary stays inert below two, with the
   reason spelled out beside the count rather than in a tooltip nobody opens.

   Same frosted glass as the product page's section nav, not a black slab: this
   is a layer floating over the catalogue, and an opaque bar read as a second
   footer that had appeared out of nowhere. */
/* No opacity transition. An element at opacity < 1 becomes a backdrop root, so
   fading this in would switch the blur off for the length of the animation and
   switch it back on at the end — a visible snap. It slides in from off-screen,
   which needs no fade. */
/* --sticky-h is .dg-sticky's box: two --sp-3 of padding around a --touch-high
   row. Below 1024px the product page pins its enquiry bar to the bottom, and the
   tray was landing on top of the one control the page exists for. */
:root{--sticky-h:calc(var(--sp-3) * 2 + var(--touch))}
.dg-cmp{position:fixed;inset-inline:0;bottom:0;z-index:55;
  padding:0 var(--sp-5) calc(var(--sp-4) + env(safe-area-inset-bottom));pointer-events:none;
  transform:translateY(calc(100% + 8px));
  transition:transform var(--dur-panel) var(--ease-enter)}
.dg-cmp.is-open{transform:none}
.dg-cmp.is-closing{transition-timing-function:var(--ease-exit)}
.dg-cmp__in{pointer-events:auto;display:flex;align-items:center;gap:var(--sp-4);
  max-width:calc(1440px - var(--sp-5) * 2);margin:0 auto;padding:var(--sp-3) var(--sp-4);
  color:var(--text);border:1px solid var(--grey-200);border-radius:var(--radius-lg);
  /* Opaque fallback first: backdrop-filter is off by default in Firefox, and a
     translucent bar there would have catalogue cards showing through the text. */
  background:var(--white);
  box-shadow:0 8px 32px -10px rgba(20,20,22,.28);
  transition:padding var(--dur-base) var(--ease-standard)}
@supports (backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px)){
  .dg-cmp__in{background:rgba(255,255,255,.72);
    -webkit-backdrop-filter:saturate(180%) blur(14px);
    backdrop-filter:saturate(180%) blur(14px)}
}

.dg-cmp__lead{display:flex;align-items:center;gap:10px;flex:none}
.dg-cmp__mark{display:inline-flex;align-items:center;justify-content:center;
  width:34px;height:34px;flex:none;border-radius:var(--radius);
  background:var(--accent);color:var(--on-accent)}
.dg-cmp__copy{display:flex;flex-direction:column;gap:1px;line-height:1.25}
.dg-cmp__n{font-size:var(--fs-sm);font-weight:700;font-variant-numeric:tabular-nums;
  color:var(--ink)}
.dg-cmp__hint{font-size:var(--fs-micro);color:var(--text-3)}

.dg-cmp__slots{display:flex;align-items:center;gap:var(--sp-2);
  list-style:none;margin:0;padding:0;flex:1;min-width:0}
.dg-cmp__slot{position:relative;width:48px;height:48px;flex:none;border-radius:var(--radius);
  background:var(--white);border:1px solid var(--grey-200);overflow:visible}
/* Only the tile that just arrived — app.js adds .is-new by diffing ids. */
.dg-cmp__slot.is-new{animation:dg-cmp-in var(--dur-panel) var(--ease-enter) both}
.dg-cmp__slot img{width:100%;height:100%;object-fit:contain;padding:3px;
  border-radius:var(--radius);display:block}
.dg-cmp__slot--empty{background:none;border:1px dashed var(--grey-300);animation:none}
/* Ink on white: the badge sits on a product photograph, which is usually pale,
   and a white chip on a white machine disappears. */
.dg-cmp__x{position:absolute;top:-7px;inset-inline-end:-7px;
  width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center;
  padding:0;border:0;border-radius:50%;cursor:pointer;
  background:var(--ink);color:var(--white);box-shadow:0 0 0 2px var(--white);
  transition:background-color var(--dur-instant) linear,color var(--dur-instant) linear,
             transform var(--dur-base) var(--ease-standard)}
.dg-cmp__x:hover{background:var(--amber-500);color:var(--ink);transform:scale(1.12)}
.dg-cmp__x svg{width:12px;height:12px}
@keyframes dg-cmp-in{from{opacity:0;transform:translateY(6px) scale(.86)}to{opacity:1;transform:none}}

.dg-cmp__acts{display:flex;align-items:center;gap:var(--sp-3);flex:none}
.dg-cmp__clear{background:none;border:0;padding:var(--sp-2);cursor:pointer;
  font-family:inherit;font-size:var(--fs-meta);font-weight:600;color:var(--text-3);
  transition:color var(--dur-instant) linear}
.dg-cmp__clear:hover{color:var(--ink)}
.dg-cmp__go{min-height:44px;gap:8px}
/* aria-disabled, not disabled: an <a> has no disabled state, and swapping the
   href out would lose the link the moment a second machine arrives. */
.dg-cmp__go[aria-disabled="true"]{opacity:.4;pointer-events:none}

/* --------------------------------------------------------------- collapse */
/* The bar covers the last row of cards, so it has to be dismissible — but
   dismissing it must not lose the selection. It shrinks to a pill that still
   carries the count, and the header's compare button brings it back. */
.dg-cmp__toggle{display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;flex:none;padding:0;cursor:pointer;
  border:1px solid var(--grey-200);border-radius:var(--radius);background:var(--white);
  color:var(--text-3);
  transition:color var(--dur-instant) linear,border-color var(--dur-instant) linear,
             background-color var(--dur-instant) linear}
.dg-cmp__toggle:hover{color:var(--ink);border-color:var(--grey-400)}
.dg-cmp__toggle svg{transition:transform var(--dur-base) var(--ease-standard)}
.dg-cmp.is-min .dg-cmp__toggle svg{transform:rotate(180deg)}

.dg-cmp.is-min .dg-cmp__in{width:fit-content;margin-inline-start:auto;margin-inline-end:0;
  padding:8px 10px 8px var(--sp-3);gap:var(--sp-3)}
.dg-cmp.is-min .dg-cmp__slots,
.dg-cmp.is-min .dg-cmp__acts,
.dg-cmp.is-min .dg-cmp__hint{display:none}
.dg-cmp.is-min .dg-cmp__mark{width:28px;height:28px}

.dg-cmp__x--all{display:none}

@media (max-width:1023px){
  /* Only while the enquiry bar is actually showing. The bar is driven by an
     IntersectionObserver on the buy box, so for the whole time the reader is
     INSIDE the buy box it slides away — and the tray stayed lifted anyway,
     leaving a 72px strip of page content moving under a bar that was not there. */
  :root.has-sticky .single-machine .dg-cmp{bottom:var(--sticky-h)}
  /* Room for the enquiry bar itself. It was only reserved when a compare
     selection existed, so for most visitors the fixed bar sat over the © line and
     the privacy and terms links at the very bottom of every product page — the
     one place those links are. Placed before the has-cmp rule, which still wins
     when both bars stack. */
  .single-machine .dg-footer{padding-bottom:calc(var(--sticky-h) + env(safe-area-inset-bottom))}
  /* And the footer holds room for both bars stacked, not just one. */
  .single-machine:root.has-cmp .dg-footer,
  :root.has-cmp .single-machine .dg-footer{padding-bottom:calc(96px + var(--sticky-h))}
}
@media (max-width:900px){
  .dg-cmp{padding:0 var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom))}
  .dg-cmp__in{gap:var(--sp-3);padding:10px var(--sp-3)}
  /* overflow-x:auto forces overflow-y:auto, which clipped every slot's remove
     badge — it hangs 7px above the tile. The padding gives it somewhere to be. */
  .dg-cmp__slots{overflow-x:auto;overflow-y:hidden;scrollbar-width:none;
    padding-block:9px;margin-block:-9px}
  .dg-cmp__slots::-webkit-scrollbar{display:none}
  .dg-cmp__slot--empty{display:none}
  .dg-cmp__clear{display:none}
  .dg-cmp__x--all{display:inline-flex;align-items:center;justify-content:center;
    position:static;width:34px;height:34px;flex:none;border-radius:var(--radius);
    background:var(--grey-100);color:var(--text-2);box-shadow:none}
  .dg-cmp__x--all:hover{background:var(--grey-200);color:var(--ink);transform:none}
  .dg-cmp__x--all svg{width:18px;height:18px}
  .dg-cmp.is-min .dg-cmp__x--all{display:none}
  /* One tap from the collapse chevron, 12px away, and it wipes the whole
     selection with no undo. Clearing already exists per slot and on the compare
     page itself; it does not need to be the easiest thing in the bar to hit. */
  .dg-cmp__x--all{display:none}
  /* The tray is 82px plus the safe-area inset the bar's own padding adds — on a
     Face ID phone that is 116px, against a flat 80px of reserved footer, so the
     legal row sat under the frosted bar. */
  :root.has-cmp .dg-footer{padding-bottom:calc(96px + env(safe-area-inset-bottom))}
  :root.has-cmp-min .dg-footer{padding-bottom:calc(72px + env(safe-area-inset-bottom))}
}
/* Below this the count, the 105px compare button and the collapse chevron leave
   the picks a 90px strip at 390 and 23px at 320 — the second thumbnail cut in
   half, the third invisible, and the per-item remove badge unreachable. They are
   the contents of the tray; they get the row. */
@media (max-width:520px){
  .dg-cmp__in{flex-wrap:wrap;row-gap:var(--sp-2)}
  .dg-cmp__lead{flex:1 1 auto;min-width:0}
  .dg-cmp__acts{flex:none}
  .dg-cmp__slots{order:5;flex:1 0 100%;overflow:visible;padding-block:7px 0;margin-block:0}
  .dg-cmp__hint{display:none}
  .dg-cmp.is-min .dg-cmp__in{flex-wrap:nowrap}
}
/* A phone held sideways gives 390px of height, and the header, the section
   anchors, this tray and the enquiry bar were taking 277 of them. The tray goes
   back to being the pill it collapses to, whatever the stored preference. */
@media (max-height:480px) and (orientation:landscape){
  .dg-cmp .dg-cmp__slots,.dg-cmp .dg-cmp__hint,.dg-cmp .dg-cmp__x--all{display:none}
  .dg-cmp .dg-cmp__in{width:fit-content;margin-inline-start:auto;margin-inline-end:0;
    padding:6px 10px}
}
@media (max-width:520px){
  .dg-cmp__hint{display:none}
  /* The strip stays. Hiding it left no way to drop a single machine — only
     "clear all" — and pushed the collapse toggle off the end of the bar. */
  .dg-cmp__slots{flex:1 1 auto}
  .dg-cmp__lead{gap:8px}
  .dg-cmp__mark{display:none}
  .dg-cmp__go{padding:0 var(--sp-3);min-height:40px}
  .dg-cmp__go svg{display:none}
}
@media (prefers-reduced-motion:reduce){
  .dg-cmp{transition-duration:1ms}
  .dg-cmp__slot{animation:none}
}

/* The tray floats over the page, so the page has to make room for it or the
   last row of cards and the footer's bottom line sit underneath it forever.
   The room is taken inside the footer, not after it: padding on <body> put a
   white strip below a near-black footer, which reads as the page having stopped
   early rather than as space held for the bar. */
:root.has-cmp .dg-footer{padding-bottom:96px}
@media (max-width:900px){:root.has-cmp .dg-footer{padding-bottom:80px}}
/* Collapsed it is a pill in the corner, not a bar, so it stops reserving a
   bar's worth of page. */
:root.has-cmp-min .dg-footer{padding-bottom:64px}

/* Nothing happened, and here is why: the cap is on the tray, so the tray is
   what answers. One shake, no toast to dismiss. */
.dg-cmp.is-shake .dg-cmp__in{animation:dg-cmp-shake 420ms var(--ease-standard)}
@keyframes dg-cmp-shake{
  0%,100%{transform:none}
  20%{transform:translateX(-6px)}
  45%{transform:translateX(5px)}
  70%{transform:translateX(-3px)}
}
/* Fallback tile for a machine with no photograph — its initials, not an empty
   white square that reads as a broken image. */
.dg-cmp__slot-t{display:flex;align-items:center;justify-content:center;
  width:100%;height:100%;border-radius:var(--radius);background:var(--grey-100);
  color:var(--text-2);font-size:var(--fs-meta);font-weight:700;text-transform:uppercase}
@media (prefers-reduced-motion:reduce){
  .dg-cmp.is-shake .dg-cmp__in{animation:none}
}
