/* ZandGo v0.2 — tokens from BRANDBOOK v1.1 §4/§5. System font stacks only (no font files loaded). */
:root{
  --ink:#1E1B16;--ink-muted:#5C5349;--action:#A8330A;--action-ink:#FFFFFF;
  --white:#FFFFFF;--sand:#F6F1E7;--sand-deep:#EBE3D3;
  --border:#CFC6B4;--border-strong:#857A6B;
  --success:#14633C;--error:#9B1C1C;
  --r-md:6px;--r-lg:12px;
  --display:Archivo,"Helvetica Neue",Arial,sans-serif;
  --body:"Source Sans 3","Segoe UI",system-ui,sans-serif;
}
*{box-sizing:border-box}
[hidden]{display:none!important}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*{transition:none!important;animation:none!important}}
body{margin:0;background:var(--white);color:var(--ink);font-family:var(--body);font-size:16px;line-height:1.6}
img{max-width:100%;height:auto}
a{color:var(--action)}
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,summary:focus-visible{outline:3px solid var(--action);outline-offset:2px}
h1,h2,h3{font-family:var(--display);color:var(--ink);margin:0 0 12px}
h1{font-size:clamp(28px,5vw,39px);line-height:1.12;font-weight:700;font-stretch:90%}
h2{font-size:clamp(24px,4vw,31px);line-height:1.18;font-weight:600;font-stretch:92%}
h3{font-size:20px;line-height:1.25;font-weight:600}
p{margin:0 0 16px}
ul{margin:0 0 16px;padding-left:22px}
.small{font-size:14px;line-height:1.5;color:var(--ink-muted)}
.legal{font-size:12px;line-height:1.45;color:var(--ink-muted)}
.lead{font-size:20px;line-height:1.45}
.wrap{max-width:1120px;margin:0 auto;padding:0 20px}
.narrow{max-width:760px}
.skip{position:absolute;left:-9999px;top:0;background:var(--white);padding:8px 12px;z-index:100}
.skip:focus{left:8px;top:8px}

/* header */
.hdr{border-bottom:1px solid var(--border);background:var(--white)}
.hdr-row{display:flex;align-items:center;justify-content:space-between;gap:12px 24px;flex-wrap:wrap;padding-top:12px;padding-bottom:12px}
.brand{display:inline-flex}
.brand img{display:block;width:170px;height:auto}
.nav{display:flex;gap:6px 18px;flex-wrap:wrap}
.nav a{color:var(--ink);text-decoration:none;font-size:16px;padding:4px 0}
.nav a:hover{text-decoration:underline}

/* buttons */
.btn{display:inline-block;font-family:var(--display);font-size:17px;font-weight:600;line-height:1.2;
  background:var(--action);color:var(--action-ink);border:0;border-radius:var(--r-md);
  padding:13px 22px;text-decoration:none;cursor:pointer;text-align:center}
.btn:hover{text-decoration:underline}
.btn[disabled]{opacity:.65;cursor:progress}
.btn-quiet{background:var(--white);color:var(--action);border:1px solid var(--border-strong)}

/* sections */
.hero{background:var(--sand);padding:44px 0 36px;border-bottom:1px solid var(--border)}
.hero p{max-width:40em}
.hero-cta{display:flex;gap:12px;flex-wrap:wrap}
.hero-price{font-weight:700;font-size:1.15rem;margin:0 0 12px}
.sec{padding:44px 0}
.sec-sand{background:var(--sand)}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:16px}
.card{background:var(--white);border:1px solid var(--border);border-radius:var(--r-lg);padding:18px;display:flex;flex-direction:column}
.card h3{margin-bottom:4px}
.card .unit{font-size:14px;color:var(--ink-muted);margin-bottom:8px}
.card .desc{flex:1}
.card .price{font-family:var(--display);font-weight:700;font-size:20px;color:var(--action);font-variant-numeric:tabular-nums;margin-bottom:12px}
.card .price.none{color:var(--ink);font-weight:600;font-size:17px}
.card .btn{width:100%}
.pimg{margin:-18px -18px 14px}
.pimg>img{display:block;width:100%;height:auto;aspect-ratio:1/1;object-fit:cover;border-radius:var(--r-lg) var(--r-lg) 0 0;background:var(--sand-deep)}
.pimg figcaption{font-size:13px;color:var(--ink-muted);padding:6px 18px 0}
.pimg-more{padding:6px 18px 0;font-size:14px}
.pimg-more summary{cursor:pointer}
.pimg-row{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-top:6px}
.pimg-row img{display:block;width:100%;height:auto;aspect-ratio:1/1;object-fit:cover;border-radius:var(--r-md);background:var(--sand-deep)}
/* Skeleton cards: a FLOOR under the product list's height, never a match for it.
   Measured live 2026-10-16 at 1387x732: the served #product-list is a 21px placeholder
   that app.js replaces with a 836.8px row of four cards, and the list starts 859px down
   the document - below a 732px fold, which is why mobile CLS is a legitimate 0 and the
   agency's desktop run reports 0.096. The row's height is content-driven (the tallest
   card: straatzand, whose h3 wraps to two lines) and it REFLOWS with the column count,
   so no typed min-height and no height-matched skeleton can be right at every width;
   either would have to be re-tuned on every catalogue text edit, and an overshoot shifts
   the page upward just as a miss shifts it down.
   So this reserves only what every card must have: one 1/1 image box, a heading line, a
   unit line and the button. Undershooting is monotone - the page settles downward by the
   remainder once and never by more than before. The bars carry no text, no price and no
   product name: the cards are catalogue-built on purpose so a withdrawn product vanishes
   from the site without a deploy, and a skeleton that named anything would outlive it. */
.skel{pointer-events:none}
.skel>*{background:var(--sand-deep);border-radius:var(--r-md)}
.skel .s-img{margin:-18px -18px 14px;aspect-ratio:1/1;border-radius:var(--r-lg) var(--r-lg) 0 0}
.skel .s-h3{height:25px;margin-bottom:4px;width:70%}
.skel .s-unit{height:22px;margin-bottom:8px;width:45%}
.skel .s-btn{height:46px;margin-top:auto}
@media (prefers-reduced-motion:no-preference){.skel{animation:skelpulse 1.6s ease-in-out infinite}}
@keyframes skelpulse{0%,100%{opacity:1}50%{opacity:.55}}
.steps{list-style:none;padding:0;margin:0 0 16px;counter-reset:s;display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.steps li{counter-increment:s;background:var(--white);border:1px solid var(--border);border-radius:var(--r-lg);padding:18px 18px 4px}
.steps li::before{content:counter(s);display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;
  border:2px solid var(--ink);font-family:var(--display);font-weight:700;margin-bottom:10px}
.model{background:var(--white);border:1px solid var(--border);border-radius:var(--r-md);padding:10px 14px;margin:0 0 16px}
.model summary{cursor:pointer;font-weight:600}
.model p{margin:10px 0 0}
.ftr{background:var(--sand-deep);padding:28px 0 calc(28px + env(safe-area-inset-bottom,0px))}
.ftr p{margin:0 0 8px}

/* order dialog */
.dlg{border:0;border-radius:var(--r-lg);padding:0;width:min(560px,calc(100% - 24px));max-height:calc(100% - 24px);color:var(--ink);background:var(--white)}
.dlg::backdrop{background:rgba(30,27,22,.55)}
.dlg form{padding:20px}
.dlg-head{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:8px}
.dlg-head h2{margin:0;font-size:25px}
.x{background:none;border:0;font-size:30px;line-height:1;cursor:pointer;color:var(--ink);padding:4px 10px;border-radius:var(--r-md)}
.field{margin:0 0 14px}
.field label{display:block;font-weight:600;margin-bottom:4px}
.field input[type=text],.field input[type=tel],.field input[type=email],.field input[type=number],.field select{
  width:100%;font:inherit;font-size:17px;color:var(--ink);background:var(--white);
  border:1px solid var(--border-strong);border-radius:var(--r-md);padding:10px 12px}
.field input[aria-invalid=true]{border:2px solid var(--error)}
.field .small{margin:4px 0 0}
.check{display:flex;gap:10px;align-items:flex-start}
.check input{width:22px;height:22px;margin:2px 0 0;flex:0 0 auto;accent-color:var(--action)}
.check label{font-weight:400;margin:0}
.err{color:var(--error);font-size:14px;margin:4px 0 8px}
.ok{color:var(--success);font-size:18px;font-weight:600}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.dlg-foot{display:flex;gap:12px;flex-wrap:wrap;margin-top:8px}

/* chat widget */
.zc-bubble{position:fixed;right:16px;bottom:calc(16px + env(safe-area-inset-bottom,0px));z-index:50;
  display:flex;align-items:center;gap:8px;font-family:var(--display);font-weight:600;font-size:16px;
  background:var(--action);color:var(--action-ink);border:0;border-radius:999px;padding:12px 18px;cursor:pointer;
  box-shadow:0 2px 10px rgba(30,27,22,.25)}
.zc-bubble svg{width:20px;height:20px;flex:0 0 auto}
.zc-unread{width:10px;height:10px;border-radius:50%;background:var(--white);display:inline-block}
.zc-panel{position:fixed;right:16px;bottom:16px;z-index:60;width:360px;max-width:calc(100% - 32px);height:min(540px,calc(100% - 32px));
  display:flex;flex-direction:column;background:var(--white);border:1px solid var(--border-strong);border-radius:var(--r-lg);
  box-shadow:0 6px 24px rgba(30,27,22,.25);overflow:hidden}
.zc-head{display:flex;justify-content:space-between;align-items:flex-start;gap:8px;padding:12px 14px;background:var(--sand);border-bottom:1px solid var(--border)}
.zc-title{font-family:var(--display);font-weight:700;font-size:18px;margin:0}
.zc-status{display:flex;align-items:center;gap:6px;font-size:14px;color:var(--ink-muted);margin:2px 0 0}
.zc-dot{width:10px;height:10px;border-radius:50%;background:#8A8278;flex:0 0 auto}
.zc-dot.on{background:var(--success)}
/* The art. 50 disclosure. Sized to be read, not to be dismissed: same colour as body text,
   not the muted grey of the status line - "clear and distinguishable" is a legal test and a
   greyed-out sentence is the first thing an eye skips. */
.zc-ai{margin:0;padding:10px 14px;font-size:14px;line-height:1.4;color:var(--ink);background:var(--white);border-bottom:1px solid var(--border)}
.zc-log{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:8px;background:var(--white)}
.zc-msg{max-width:85%;padding:8px 12px;border-radius:12px;white-space:pre-wrap;overflow-wrap:anywhere;font-size:15px;line-height:1.45}
.zc-msg.me{align-self:flex-end;background:var(--sand-deep);border-bottom-right-radius:4px}
.zc-msg.zg{align-self:flex-start;background:var(--sand);border:1px solid var(--border);border-bottom-left-radius:4px}
.zc-msg.pending{opacity:.6}
.zc-msg.fail{border:1px solid var(--error)}
.zc-note{font-size:14px;color:var(--ink-muted)}
.zc-note.err{color:var(--error)}
.zc-typing{font-size:14px;color:var(--ink-muted);padding:0 14px 6px;min-height:1.5em}
.zc-form{display:flex;gap:8px;padding:10px;border-top:1px solid var(--border)}
.zc-form textarea{flex:1;font:inherit;font-size:16px;resize:none;border:1px solid var(--border-strong);border-radius:var(--r-md);padding:8px 10px;min-height:44px;max-height:120px}
.zc-form .btn{padding:10px 14px}
@media (max-width:600px){
  .zc-panel{right:0;bottom:0;width:100%;max-width:100%;height:100%;border-radius:0;border:0;
    padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}
  .zc-bubble span.lbl{display:inline}
  .hero{padding-top:28px}
}

.form-intro{margin:0 0 .75rem;font-size:.9rem;color:#555}

/* Static links to the per-product pages, in #producten above the JS-rendered
   cards: a crawler and a visitor without JavaScript both need a real link. */
.prodlinks { list-style: none; display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; padding: 0; margin: 0 0 1rem; }
.prodlinks a { font-weight: 600; }

/* "Reken het uit" — the volume calculator on a product page. A visitor who does
   not know how many bags to order orders none, or orders one when two cost less
   per bag. Estimate only; it never shows a price. */
.calc { border: 1px solid rgba(0,0,0,.14); border-radius: 10px; padding: 1rem 1.1rem; margin: 1rem 0; }
.calc h4 { margin: 0 0 .25rem; }
.calc-row { display: flex; flex-wrap: wrap; gap: .75rem 1rem; margin: .75rem 0; }
.calc-row label { display: flex; flex-direction: column; gap: .2rem; font-weight: 600; }
.calc-row input { width: 7.5rem; padding: .5rem .6rem; border: 1px solid rgba(0,0,0,.3); border-radius: 6px; font: inherit; }
.calc-out { font-weight: 600; margin: .5rem 0; }

/* The optional half of the order form, folded away (cycle 276).
   The dialog showed NINE input fields and about two hundred words of small print
   above the submit button, at the exact moment a visitor decides whether to
   order. Three of the nine are optional - afleveradres, a week preference and a
   remark - so they are now behind one disclosure and the required six are what a
   visitor meets. Nothing required is hidden and the sentence that LIMITS what we
   promise (the grain-size one) deliberately stays outside this block: a
   disclaimer a reader has to open is not a disclaimer. */
/* THIS RULE IS LOAD-BEARING. Chrome hides a closed <details>'s content with a UA
   stylesheet rule, and ANY author rule beats the UA stylesheet - our own
   `.field { display: block }` un-hid every field inside the closed disclosure, so
   the three optional fields rendered at full height with the arrow still pointing
   right. The form looked exactly as long as before and I had already shipped it as
   shorter. Author-level rule, specificity above .field, so the collapse holds. */
details.opt:not([open]) > *:not(summary) { display: none; }
details.opt { margin: 0 0 1rem; border: 1px solid var(--line, #d9d9d9); border-radius: 6px; padding: .5rem .75rem; }
details.opt > summary { cursor: pointer; font-weight: 600; }
details.opt > summary:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
details.opt[open] > summary { margin-bottom: .5rem; }
details.opt .field:last-child { margin-bottom: 0; }
/* The order total, immediately above the submit button. Deliberately heavier than .small:
   it is the one number that decides the purchase, and "clear and conspicuous" is the test
   it has to meet, not just legibility. Empty until app.js fills it, so it collapses to
   nothing for a visitor without JavaScript rather than leaving a labelled blank. */
.total:empty { display: none; }
.total {
  margin: .9rem 0 .2rem;
  padding: .6rem .75rem;
  border: 1px solid var(--line, #d8d8d8);
  border-radius: 6px;
  background: var(--tint, #f6f6f4);
  font-size: 1.02rem;
  font-weight: 600;
  line-height: 1.35;
}
