/* ============================================================ product page */
/* White page. Only the rail is a surface — it is the one block a buyer acts on,
   so it is the one block that gets a fill. Everything on the left is separated
   by rules instead of boxed, which stops the page reading as a stack of cards. */
.single-machine #dg-main{background:var(--white)}
/* No bottom padding: .dg-footer's margin is the clearance before the dark band,
   and this was a second one stacked under it. */
.dg-pdp{padding-bottom:0}
/* Media gets the larger share: it is the only thing that tells a buyer what the
   machine physically is. 1fr/1fr gave a half-page column of mostly whitespace. */
.dg-pdp__top{display:grid;grid-template-columns:minmax(0,1.34fr) minmax(0,.66fr);
  /* The rail is a separate object, not a second column of the same text block.
     A 48px gutter is what makes it read that way. */
  /* Nothing follows this grid inside .dg-pdp, so its bottom padding was a third
     clearance under the other two. */
  gap:var(--sp-7);align-items:start;padding:0}
/* Explicit placement, so DOM order can stay the mobile reading order: the
   gallery and the long detail share the left column as two rows, and the rail
   occupies the right column across both and sticks there. */
/* Media and the facts that belong beside it, as two columns inside the left
   column. The rail keeps only what a buyer acts on. */
.dg-pdp__intro{grid-column:1;grid-row:2;min-width:0;display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--sp-6);align-items:start}
.dg-pdp__intro > .dg-gal{min-width:0}
.dg-pdp__brief{min-width:0}
.dg-pdp__brief .dg-pdp__short{margin:0 0 var(--sp-5);max-width:none}
.dg-pdp__brief .dg-keyspecs{margin:0}
@media (max-width:900px){
  .dg-pdp__intro{grid-template-columns:minmax(0,1fr);gap:var(--sp-5)}
}
.dg-pdp__main            {grid-column:1;grid-row:3;min-width:0;display:flex;flex-direction:column}
/* Spans all three rows so it begins level with the title — the rail starting at
   row 2 left a column of dead space beside the heading. */
.dg-pdp__rail{grid-column:2;grid-row:1 / span 3;align-self:start;
  background:var(--grey-100);border:0;border-radius:var(--radius-lg);
  position:sticky;top:calc(var(--header-h) + var(--sp-4));
  max-height:calc(100vh - var(--header-h) - var(--sp-6));overflow-y:auto;
  /* NO overscroll-behavior:contain. On any viewport shorter than ~900px the rail
     overflows, and containment turns the right-hand 41% of the page into a dead
     zone where the wheel stops the page entirely. Chaining is correct here. */
  scrollbar-width:thin}

@media (max-width:1023px){
  .dg-pdp__top{grid-template-columns:minmax(0,1fr);gap:var(--sp-5)}
  /* One column: the source order already reads photo, then price, then detail.
     No `order` needed — and a sticky panel on a phone is just a wall. */
  .dg-pdp__head,.dg-pdp__top > .dg-gal,.dg-pdp__main,.dg-pdp__rail{grid-column:1;grid-row:auto}
  .dg-pdp__rail{position:static;max-height:none;overflow:visible}
}

/* gallery — an annotated feature tour, not a hero reel */
/* No wrapper: white ground, no padding, no border. The supplier shots are already
   cut out on white, so a grey tile only boxed them in. */
.dg-gal__main{background:transparent;border:0;border-radius:0;
  aspect-ratio:4/3;overflow:hidden;position:relative}
/* Fills whichever axis binds. Nothing is cropped — contain letterboxes the
   other axis — but the machine is as large as the box allows. */
.dg-gal__main img{width:100%;height:100%;object-fit:contain;padding:0;display:block}
.dg-gal__counter{position:absolute;bottom:var(--sp-3);inset-inline-end:var(--sp-3);
  font-family:var(--font-mono);font-size:var(--fs-meta);background:var(--white);
  border:1px solid var(--rule);border-radius:100px;padding:2px 10px;color:var(--text-2)}
/* overflow-x:auto computes overflow-y to auto as well, so a focus ring or the
   current-thumb ring is clipped top and bottom. Pad the scroll box and pull the
   padding back so nothing moves. */
.dg-gal__thumbs{display:flex;gap:var(--sp-2);margin-top:var(--sp-3);overflow-x:auto;
  padding-block:4px;margin-block:calc(var(--sp-3) - 4px) -4px;scrollbar-width:none}
.dg-gal__thumbs::-webkit-scrollbar{display:none}
.dg-gal__thumbs button{flex:0 0 64px;height:64px;padding:4px;background:transparent;
  border:1px solid var(--grey-200);border-radius:var(--radius);cursor:pointer;
  transition:border-color var(--dur-instant) linear}
.dg-gal__thumbs button:hover{border-color:var(--grey-400)}
.dg-gal__thumbs button[aria-current="true"]{border-color:var(--accent-text);
  box-shadow:0 0 0 1px var(--accent)}
.dg-gal__thumbs img{width:100%;height:100%;object-fit:contain}
.dg-gal__cap{font-size:var(--fs-meta);color:var(--text-3);margin-top:var(--sp-2)}

/* --- page header ---------------------------------------------------------
   Manufacturer mark, then the machine name at full page width, then the model
   code. Identity is settled before the eye reaches the split below. */
/* The header sits in the grid's first row, left column, so the title takes
   exactly the gallery's width and wraps with it at every breakpoint. */
.dg-pdp__head{grid-column:1;grid-row:1;min-width:0;padding:0 0 var(--sp-5)}
/* Condition and availability head the rail. The manufacturer's mark moved into
   the breadcrumb, where the brand belongs. */
/* The rail's top row: what state this machine is in, and the one control that
   asks about it against other machines. The divider moved up to the row so the
   compare button sits above it rather than beside a rule that stops short. */
.dg-rail__top{display:flex;align-items:flex-start;justify-content:space-between;
  gap:var(--sp-3);margin:0 0 var(--sp-5);
  padding:0 0 var(--sp-5);border-bottom:1px solid var(--grey-300)}
.dg-rail__top .dg-status{margin:0;padding:0;border:0}
/* Bigger than the card's, because here it is one of two controls on the page
   rather than one of twenty-four on a grid. */
.dg-rail__top .dg-cmpadd{width:40px;height:40px;flex:none;
  background:var(--white);border-color:var(--grey-300);
  -webkit-backdrop-filter:none;backdrop-filter:none}
.dg-rail__top .dg-cmpadd:hover{background:var(--grey-100);border-color:var(--grey-400)}
.dg-rail__top .dg-cmpadd.is-cmp{background:var(--accent);border-color:var(--accent);
  color:var(--on-accent)}
.dg-rail__top .dg-cmpadd.is-cmp:hover{background:var(--amber-600);border-color:var(--amber-600)}

.dg-status{list-style:none;display:flex;flex-wrap:wrap;gap:var(--sp-2);margin:0 0 var(--sp-5);
  padding:0 0 var(--sp-5);border-bottom:1px solid var(--grey-300)}
.dg-status__tag{display:inline-flex;align-items:center;gap:6px;min-height:28px;
  padding:0 10px 0 8px;border-radius:100px;background:var(--white);
  border:1px solid var(--grey-300);font-size:var(--fs-meta);line-height:1;
  font-weight:600;color:var(--text-2);white-space:nowrap}
.dg-status__tag svg{color:var(--text-3)}
.dg-status__tag.is-new{border-color:var(--success-600);color:var(--success-600)}
.dg-status__tag.is-new svg{color:var(--success-600)}
.dg-status__tag.is-used,.dg-status__tag.is-refurbished{
  border-color:var(--warning-700);color:var(--warning-700)}
.dg-status__tag.is-used svg,.dg-status__tag.is-refurbished svg{color:var(--warning-700)}
.dg-brandmark{display:inline-block;line-height:0}
.dg-brandmark img{display:block;height:26px;width:auto;max-width:180px;object-fit:contain}
/* No logo file for this brand: fall back to the name, set as a mark would be. */
.dg-brandmark--text{font-family:var(--font-mono);font-size:var(--fs-micro);line-height:1;
  font-weight:700;color:var(--text-3);
  text-decoration:none}
.dg-brandmark--text:hover{color:var(--accent-text)}
.dg-pdp__head h1{font-size:var(--fs-h1);line-height:var(--lh-h1);font-weight:700;
  margin:0;text-wrap:balance}

/* Model code and the category path it sits in, as one quiet row of tokens. */
.dg-pdp__meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2);
  margin:var(--sp-4) 0 0}
.dg-pdp__meta .dg-pdp__model{margin:0}
.dg-pdp__tag{display:inline-flex;align-items:center;min-height:28px;padding:0 var(--sp-3);
  border:1px solid var(--grey-300);border-radius:100px;background:var(--white);
  font-size:var(--fs-meta);line-height:1;color:var(--text-2);text-decoration:none;
  transition:border-color var(--dur-instant) linear,color var(--dur-instant) linear}
.dg-pdp__tag:hover{border-color:var(--ink);color:var(--ink)}
@media (max-width:640px){
  .dg-brandmark img{height:22px}
  .dg-pdp__head{padding:var(--sp-4) 0}
}

/* Rail: the decision content — description, the headline numbers, the enquiry. */
.dg-pdp__rail{padding:var(--sp-6)}
@media (max-width:640px){.dg-pdp__rail{padding:var(--sp-5) var(--sp-4)}}

/* No .dg-pdp h1 rule here: it dates from when the title lived in the rail, and
   at equal specificity it silently beat .dg-pdp__head h1 on source order —
   the relocated title rendered at 32px instead of the intended 40px. */
.dg-pdp__model{display:inline-flex;align-items:center;font-family:var(--font-mono);
  font-size:var(--fs-meta);line-height:1;color:var(--text-2);background:var(--grey-100);
  border-radius:3px;padding:6px 9px;margin:0 0 var(--sp-4);letter-spacing:.02em}
.dg-pdp__short{font-size:var(--fs-body);line-height:var(--lh-body);color:var(--text-2);
  max-width:56ch;margin:0 0 var(--sp-5)}

/* Headline specs — the four numbers the sidebar filters on, in a strip. */
.dg-keyspecs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1px;
  margin:0 0 var(--sp-5);background:var(--grey-200);
  border-radius:var(--radius);overflow:hidden}
/* The column count follows the spec count, so no cell is ever left empty:
   three specs read as one row of three, four as a 2x2. */
.dg-keyspecs--n1{grid-template-columns:minmax(0,1fr)}
.dg-keyspecs--n3{grid-template-columns:repeat(3,minmax(0,1fr))}
.dg-keyspecs > div{background:var(--white);padding:var(--sp-3) var(--sp-4);min-width:0;
  display:flex;align-items:center;gap:var(--sp-3)}
/* Three across leaves less width, so the icon stacks above rather than the
   value being squeezed or truncated. */
.dg-keyspecs--n3 > div{flex-direction:column;align-items:flex-start;gap:var(--sp-2);
  padding:var(--sp-4) var(--sp-3)}
.dg-keyspecs--n3 .dg-keyspecs__icon{width:32px;height:32px}
.dg-keyspecs__icon{flex:none;width:40px;height:40px;object-fit:contain;
  mix-blend-mode:multiply}   /* the renders are on white; multiply drops the box */
.dg-keyspecs__text{min-width:0}
.dg-keyspecs dt{font-size:var(--fs-micro);line-height:var(--lh-micro);color:var(--text-3);
  margin:0 0 2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dg-keyspecs dd{margin:0;font-family:var(--font-mono);font-size:var(--fs-card);
  line-height:var(--lh-card);font-weight:700;color:var(--ink);
  font-variant-numeric:tabular-nums;white-space:nowrap}

/* The enquiry block is the only red on the page below the header. */
.dg-buybox{display:flex;flex-direction:column;gap:var(--sp-4)}

/* --- price ---------------------------------------------------------------
   A quote-only price is a sentence, not a number, so the block has to carry the
   reason with it. White card on the grey rail so it reads as the answer to the
   one question every visitor arrives with. */
.dg-pricebox{background:var(--white);border:1px solid var(--grey-200);
  border-radius:var(--radius-lg);padding:var(--sp-4) var(--sp-4) var(--sp-3);
  display:flex;flex-direction:column;gap:2px;
  border-left:var(--rail) solid var(--accent)}
.dg-pricebox__label{font-size:var(--fs-micro);line-height:1;color:var(--text-3);font-weight:700}
.dg-pricebox .dg-price{font-size:var(--fs-price);line-height:var(--lh-price);
  font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
.dg-pricebox .dg-price.is-enquiry{font-size:var(--fs-h3);line-height:var(--lh-h3);
  font-weight:700;color:var(--ink)}
.dg-pricebox__drivers{font-size:var(--fs-meta);line-height:var(--lh-meta);
  color:var(--text-3);margin:var(--sp-2) 0 0}

/* --- actions -------------------------------------------------------------- */
.dg-buybox__actions{display:flex;flex-direction:column;gap:var(--sp-2)}
.dg-save{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  background:var(--white);
  transition:background-color var(--dur-instant) linear,border-color var(--dur-instant) linear,
             color var(--dur-instant) linear}
.dg-save__ico{transition:transform var(--dur-base) var(--ease-enter),fill var(--dur-instant) linear}
.dg-save:hover{border-color:var(--accent-text);color:var(--accent-text)}
.dg-save:hover .dg-save__ico{transform:scale(1.12)}
.dg-save.is-saved{border-color:var(--accent-text);color:var(--accent-text);background:var(--accent-soft)}
/* Filled heart is the state; the outline is the invitation. */
.dg-save.is-saved .dg-save__ico{fill:currentColor}

/* .dg-reach and .dg-agent moved to main.css — the closing CTA on the home page
   uses the same two components, and a copy in each stylesheet is two things to
   keep in step. */

.dg-buybox__trust{list-style:none;margin:0;padding:var(--sp-4) 0 0;display:flex;
  flex-direction:column;gap:var(--sp-2);border-top:1px solid var(--grey-300)}
/* Real icons (Lucide) rather than a rotated border trick — they scale, they
   inherit colour, and each one now says which promise it is. */
.dg-buybox__trust li{display:flex;align-items:flex-start;gap:10px;
  font-size:var(--fs-meta);line-height:var(--lh-meta);color:var(--text-2)}
.dg-buybox__trust li svg{flex:none;color:var(--accent-text);margin-top:1px}

/* variant selector — separate crawlable pages, never a dropdown */
.dg-variants{display:flex;gap:var(--sp-2);flex-wrap:wrap}
.dg-variants a,.dg-variants span{display:inline-flex;align-items:center;min-height:44px;
  padding:0 var(--sp-4);border:1px solid var(--rule-fn);border-radius:var(--radius);
  font-family:var(--font-mono);font-size:var(--fs-sm);text-decoration:none;
  color:var(--text-2);background:var(--white)}
.dg-variants span[aria-current]{background:var(--ink);color:var(--white);border-color:var(--ink);font-weight:600}

/* section nav for a 9,000px page */
/* Below the sticky site header, not behind it: the header is z-index 50 and
   owns the top 65px, so a top:0 sub-nav was painted underneath it. */
.dg-anchors{position:sticky;top:var(--header-h);z-index:15;
  /* Reads as a layer over the page rather than a second white slab butting into
     it. The opaque colour stays as the fallback: backdrop-filter is off by
     default in Firefox, and a transparent bar there would be unreadable. */
  background:var(--white);
  border-bottom:1px solid var(--grey-200);display:flex;gap:var(--sp-2);overflow-x:auto;
  padding-block:4px;scrollbar-width:none;margin:var(--sp-6) 0 0}
@supports (backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px)){
  .dg-anchors{background:rgba(255,255,255,.72);
    -webkit-backdrop-filter:saturate(180%) blur(14px);
    backdrop-filter:saturate(180%) blur(14px)}
}
.dg-anchors::-webkit-scrollbar{display:none}
.dg-anchors a{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;
  padding:var(--sp-4) 2px;margin-inline-end:var(--sp-5);
  font-size:var(--fs-sm);font-weight:500;color:var(--text-3);text-decoration:none;
  border-bottom:var(--rail) solid transparent;white-space:nowrap;
  /* One shorthand. Two `transition` declarations in the same block is not a
     merge — the second replaces the first, and the padding animation this bar
     needs when it shrinks on scroll was being silently dropped. */
  transition:padding var(--dur-base) var(--ease-standard),
             color var(--dur-instant) linear,
             border-color var(--dur-instant) linear}
.dg-anchors a:hover{color:var(--ink)}
.dg-anchors a svg{color:var(--grey-400);transition:color var(--dur-instant) linear}
.dg-anchors a:hover svg{color:var(--accent-text)}
.dg-anchors a[aria-current="true"]{color:var(--ink);border-bottom-color:var(--accent-text);font-weight:700}
.dg-anchors a[aria-current="true"] svg{color:var(--accent-text)}
/* Stuck to the header it is navigation, not a section of the page, so it gives
   back the height it only needed while it was sitting in the flow. */
.is-scrolled .dg-anchors a{padding-block:10px}
/* No shadow: the blur already separates it from what passes underneath. */

/* Each section is a panel, not a run of text between hairlines. */
.dg-pdp .dg-sec{background:none;border:0;border-top:1px solid var(--grey-200);
  border-radius:0;padding:var(--sp-7) 0 var(--sp-2);margin-top:0;
  /* The header is ALREADY in html's scroll-padding-top (main.css:22) and again
     in .single-machine's own (below). Counting it a third time here landed every
     anchor jump ~130px low: the reader arrived with the tail of the previous
     section filling the band under the anchors bar, and the heading they tapped
     off the top of the screen. Only the anchors bar's own height belongs here. */
  scroll-margin-top:var(--sp-2)}
.dg-pdp .dg-sec:first-of-type{border-top:0}
@media (max-width:640px){.dg-pdp .dg-sec{padding:var(--sp-6) 0 var(--sp-2)}}
.dg-sec > h2{font-size:var(--fs-h2);line-height:var(--lh-h2);font-weight:700;
  margin:0 0 var(--sp-5);padding-bottom:var(--sp-3);position:relative}
/* No accent bar under the heading. A coloured rule under every section title is
   decoration standing in for hierarchy the type already carries. */
.dg-prose{max-width:70ch}
.dg-prose h2{font-size:var(--fs-h2);margin-top:var(--sp-6)}
.dg-prose h3{font-size:var(--fs-h3);margin-top:var(--sp-5)}
/* Browser bullets hang the list off in its own indented column, which on a spec
   page reads as a stray block of text. These sit on the same left edge as every
   other paragraph, with a drawn marker instead of a glyph — so the list lines up
   with the prose above it and the marker is a brand colour, not a font artefact. */
/* Two shapes carry prose here: <div class="dg-prose"> wrapping the editor's
   content, and <ul class="dg-prose"> for the key-features list. Both need this. */
.dg-prose ul,ul.dg-prose{list-style:none;padding-inline-start:0;margin:0 0 var(--sp-4)}
.dg-prose ul > li,ul.dg-prose > li{position:relative;
  padding-inline-start:calc(var(--sp-5) + 2px);
  margin-bottom:var(--sp-3);line-height:var(--lh-body)}
.dg-prose ul > li::before,ul.dg-prose > li::before{content:"";position:absolute;
  inset-inline-start:0;top:.6em;width:7px;height:7px;border-radius:2px;
  background:var(--accent);transform:rotate(45deg)}
.dg-prose ol{padding-inline-start:var(--sp-5);margin:0 0 var(--sp-4)}
.dg-prose ol li{margin-bottom:var(--sp-3)}

/* spec table: divs not <table>, so it reflows to stacked pairs without a scrollbar */
.dg-specs{display:flex;flex-direction:column;gap:var(--sp-5)}
.dg-specs__group > h3{margin-bottom:var(--sp-2);padding-bottom:var(--sp-2);
  border-bottom:2px solid var(--ink)}
.dg-specs__row{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);
  gap:var(--sp-4);padding:11px var(--sp-4);font-size:var(--fs-sm);line-height:var(--lh-sm);
  border-radius:var(--radius)}
.dg-specs__row:nth-child(odd){background:var(--grey-100)}
.dg-specs__row dt{color:var(--text-3);margin:0}
.dg-specs__row dd{margin:0;color:var(--ink);font-family:var(--font-mono);font-weight:500;
  font-variant-numeric:tabular-nums;text-align:end}
@media (max-width:640px){
  .dg-specs__row{grid-template-columns:1fr;gap:2px;padding:var(--sp-3)}
  .dg-specs__row dd{text-align:start;font-size:var(--fs-sm)}
}

.dg-proscons{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--sp-4)}
.dg-proscons > div{border:1px solid var(--grey-200);border-radius:var(--radius);
  padding:var(--sp-5)}
.dg-proscons > div:first-child{background:var(--success-050);border-color:transparent}
.dg-proscons > div:last-child{background:var(--warning-050);border-color:transparent}
.dg-proscons h2{font-size:var(--fs-card);line-height:var(--lh-card);margin:0 0 var(--sp-3);
  padding:0}
.dg-proscons h2::after{display:none}
@media (max-width:767px){.dg-proscons{grid-template-columns:1fr}}
.dg-proscons ul{list-style:none;margin:0;padding:0}
.dg-proscons li{padding:var(--sp-2) 0 var(--sp-2) var(--sp-5);position:relative;
  font-size:var(--fs-sm);line-height:var(--lh-sm);color:var(--text-2)}
.dg-proscons li::before{position:absolute;inset-inline-start:0;top:var(--sp-2);font-weight:700}
.dg-pros li::before{content:"+";color:var(--success-600)}
.dg-cons li::before{content:"–";color:var(--warning-700)}

/* FAQ styles live in main.css: the same accordion renders on category pages,
   which never load this file. */

/* downloads — the manual IS the trust content */
.dg-dl{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:var(--sp-3)}
.dg-dl a{display:flex;align-items:center;gap:var(--sp-3);min-height:56px;
  padding:var(--sp-3) var(--sp-4);background:var(--white);color:var(--ink);
  border:1px solid var(--grey-300);border-radius:var(--radius);text-decoration:none;
  font-size:var(--fs-sm);font-weight:500;
  transition:border-color var(--dur-base) var(--ease-standard),
             box-shadow var(--dur-base) var(--ease-standard)}
.dg-dl a svg{flex:none;color:var(--accent-text)}
.dg-dl a:hover{border-color:var(--ink);box-shadow:var(--shadow-1)}
.dg-dl .meta{margin-inline-start:auto;font-family:var(--font-mono);font-size:var(--fs-micro);
  color:var(--text-3)}

/* sticky enquiry bar — the conversion, never collapsed to an icon */
.dg-sticky{position:fixed;inset-inline:0;bottom:0;z-index:40;display:none;
  gap:var(--sp-3);align-items:center;background:var(--white);border-top:1px solid var(--rule);
  padding:var(--sp-3) var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom));
  box-shadow:var(--shadow-panel);transform:translateY(100%);
  transition:transform var(--dur-base) var(--ease-enter)}
.dg-sticky.is-shown{transform:translateY(0)}
@media (max-width:1023px){.dg-sticky{display:flex}}
/* Clear of the phone's bottom bar, which is 60px of opaque chrome pinned to the
   same edge. --tabbar-h is 0 above 900px, so this is inert on a desktop and the
   bar keeps sitting on the viewport floor. The safe-area inset is already inside
   --tabbar-h, so it must come back out of the padding here or it is counted
   twice and the bar grows a second home-indicator gap. */
.dg-sticky{bottom:var(--tabbar-h)}
@media (max-width:900px){
  .dg-sticky{padding-bottom:var(--sp-3)}
}
/* Name over price in one column, CTA fixed beside it — the three items used to
   compete for one row and the model truncated to "FK…". */
.dg-sticky{align-items:stretch}
/* No align-self here. In a COLUMN flexbox align-self:center centres on the
   horizontal axis, so the model floated in the middle of the bar — and it also
   shrank the name to its max-content width, which meant overflow:hidden had
   nothing to overflow and the ellipsis never fired: long codes were cut at both
   ends by the bar instead. .dg-sticky__meta already centres them vertically. */
.dg-sticky__name{min-width:0;max-width:100%;font-family:var(--font-mono);font-size:var(--fs-micro);
  line-height:1.3;color:var(--text-3);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dg-sticky__price{font-size:var(--fs-meta);line-height:1.3;font-weight:600;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dg-sticky > .dg-btn{flex:0 0 auto;min-height:var(--touch);padding:0 var(--sp-5)}
@media (max-width:420px){
  .dg-sticky__price{display:none}
}

/* quote form */

/* No photography yet for this machine: collapse the frame instead of holding a
   4:3 hole open next to the buy box. */
.dg-gal--empty .dg-gal__main{display:none}

.dg-sticky__meta{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center;gap:1px}

/* A jump to #sec-specs put the target under the sticky header plus the sticky
   section nav — 126px of chrome. scroll-padding on the scrolling element fixes
   it for anchor clicks, keyboard focus and the browser's own restoration alike,
   which per-target scroll-margin does not. */
/* Both terms shrink on scroll — --header-h in chrome.css, the anchor bar above —
   and an anchor jump always lands in the scrolled state, so this uses the
   collapsed heights rather than the ones you see on arrival. */
/* Product pages carry the anchor bar under the header, so the target has to
   clear both. Declared on html too, since that is the real scroll container. */
.single-machine{scroll-padding-top:calc(var(--header-h) + 46px)}
html:has(.single-machine){scroll-padding-top:calc(var(--header-h) + 46px)}

@media (max-width:1023px){
  /* the sticky enquiry bar owns the bottom of the viewport below 1024px */
  .single-machine{scroll-padding-bottom:84px}
}

/* --- breadcrumb ----------------------------------------------------------
   Larger than a footnote: it is the only navigation on the page now that the
   section tabs are gone, and it ends in the manufacturer's mark. */
.dg-pdp .dg-crumb{padding:var(--sp-5) 0 var(--sp-4)}
.dg-pdp .dg-crumb ol{align-items:center;gap:var(--sp-2)}
.dg-pdp .dg-crumb a,.dg-pdp .dg-crumb span{font-size:var(--fs-sm);line-height:var(--lh-sm)}
.dg-crumb__brand{display:inline-flex;align-items:center;gap:var(--sp-2)}
.dg-crumb__mark{display:inline-flex;line-height:0}
.dg-crumb__mark img{height:20px;width:auto;max-width:120px;object-fit:contain;
  display:block;opacity:.9;transition:opacity var(--dur-instant) linear}
.dg-crumb__mark:hover img{opacity:1}

/* --- rhythm --------------------------------------------------------------
   One vertical scale on this page: 32px between sections, 24px inside a block,
   16px between a label and its value. Sections were 48/8 top/bottom, which read
   as uneven gaps that grew and shrank down the page. */
.dg-pdp .dg-sec{padding:var(--sp-6) 0}
.dg-pdp .dg-sec > h2{margin-bottom:var(--sp-5)}
.dg-pdp .dg-sec > h2 + *{margin-top:0}
.dg-pdp__main > :first-child{padding-top:0}
.dg-pdp__main > :last-child{padding-bottom:0}

/* Related machines --------------------------------------------------------
   A scroll-snap track, not a JS slider. It swipes natively on touch, keeps
   keyboard focus working because every card is still in the document order,
   and the arrows only ever call scrollBy — so with scripting off you lose the
   buttons and nothing else. */
.dg-rel__head{display:flex;align-items:center;justify-content:space-between;
  gap:var(--sp-4);margin-bottom:var(--sp-5)}
.dg-rel__head h2{margin:0}
.dg-rel__nav{display:flex;gap:var(--sp-2);flex:none}
.dg-rel__nav[hidden]{display:none}
.dg-rel__btn{width:42px;height:42px;display:inline-flex;align-items:center;justify-content:center;
  background:var(--white);color:var(--text);border:1px solid var(--grey-300);
  border-radius:var(--radius);cursor:pointer;
  transition:border-color var(--dur-instant) linear,color var(--dur-instant) linear,
             background-color var(--dur-instant) linear,opacity var(--dur-base) var(--ease-standard)}
.dg-rel__btn:hover:not([disabled]){border-color:var(--accent-text);color:var(--accent-text);
  background:var(--accent-soft)}
.dg-rel__btn[disabled]{opacity:.35;cursor:default}
[data-rel-prev] svg{transform:rotate(180deg)}

/* The last visible card is faded out rather than cut off at the container edge.
   A hard clip reads as a rendering fault; a fade reads as "there is more", which
   is the thing the arrows are for. The mask is dropped once the track is scrolled
   to the end, so the final card is never dimmed when it is genuinely the last. */
.dg-rel__track{display:flex;gap:var(--sp-4);list-style:none;margin:0;padding:0 0 var(--sp-2);
  overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;
  scrollbar-width:none;overscroll-behavior-x:contain;
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 90px),transparent 100%);
  mask-image:linear-gradient(to right,#000 calc(100% - 90px),transparent 100%)}
.dg-rel__track.is-end{-webkit-mask-image:none;mask-image:none}
.dg-rel__track::-webkit-scrollbar{display:none}
.dg-rel__track > .dg-card{flex:0 0 clamp(240px,26%,300px);scroll-snap-align:start}
@media (prefers-reduced-motion:reduce){ .dg-rel__track{scroll-behavior:auto} }
@media (max-width:600px){ .dg-rel__track > .dg-card{flex-basis:78%} }

