/* ============================================================
   ducioo × ool — TUNABLES

   Every value worth changing to alter the page's look lives here.
   Nothing in site.css or the JS hardcodes these; change a line
   below and the whole page follows.

   Loaded before site.css. Read by JS too — reveal.js and
   carousel.js pull their timings from these properties rather than
   keeping their own copies, so the timing of the page is described
   in exactly one place.

   Three of these carry warnings. They are marked MEASURED and
   explain what to re-check if you move them.
   ============================================================ */
:root {

  /* ----------------------------------------------------------
     1. COLOUR
     The palette is deliberately achromatic: the diamonds are the
     only brightness on the page and the art direction depends on
     the surround staying neutral.
     ---------------------------------------------------------- */
  --void:      #000000;   /* page ground. Every asset is cut for pure black */
  --surface:   #0D0D0C;   /* raised panels. Barely used — assets sit on --void */
  --ink:       #F2F2F0;   /* primary text */
  --ink-dim:   #8A8A88;   /* secondary text, captions, labels */
  --ink-faint: #4A4A48;   /* hairlines, disabled, the craft thread */
  --edge:      #1C1C1A;   /* borders, dividers, rules */

  /* ACCENT
     Drives the states that mean "this one, now": focus rings,
     selected swatch and tile, progress fills, the Add to bag
     button. Defaults to --ink, so the page looks today exactly as
     it did before this knob existed.

     Setting it to a real colour is the single biggest look change
     available here — and it is a deliberate break with the brief,
     which says no accent colour anywhere. Try it, but check it
     against the pavé: anything warm competes with the stones.
     Keep --accent-ink legible on top of --accent. */
  --accent:       var(--ink);
  --accent-ink:   var(--void);       /* text/icon colour on top of --accent */
  --accent-faint: var(--ink-faint);  /* the quiet selected states — the §7
                                        tile hairline is deliberately faint,
                                        so it takes this rather than --accent */

  /* TONAL ZONES
     Chapter separation without introducing colour. Only the L
     channel moves, and only by a few points — hue stays out of it,
     because colour on this page belongs to the diamonds alone.

     A hard edge between two near-black values reads as a rendering
     fault rather than a decision, so every change of tone happens
     across a .section-seam gradient and never at a line. */
  --surface-hero:     #000000;
  --surface-straps:   #000000;   /* carries its own wall vignette */
  --surface-buy:      #000000;
  --surface-about:    #0A0A0C;   /* cools very slightly */
  --surface-assembly: #000000;
  --surface-facts:    #08080A;
  --surface-carousel: #000000;
  --surface-arrives:  #0D0B0A;   /* warms very slightly */
  --surface-footer:   #000000;

  --seam-height: 12vh;

  /* §4 3D stage (revision 5 §5). Black and navy straps vanished against
     pure black, so the model sits in a warm charcoal that falls to true
     black at the section's edge. Warm, not grey: it should read as
     light, not as a different mode of the site. Shown only while the 3D
     view is live — the photos have black baked in, and a black photo on
     charcoal would be a hard-edged panel. */
  --stage-3d-centre: #2E2C28;
  --stage-3d-edge:   #000000;

  /* Moss tone (revision 5 §6) — the visitor's second dark tone,
     applied through html[data-tone="moss"] below. A very low-saturation
     green-grey (h90 s11 l15), chosen by the client from rendered
     comparisons.

     How it got here: navy (#12141C) failed first — the §3 frame
     moulding's mid tone (#171310) measured 1.00:1 against it and the
     frame's edge dissolved into the wall. Graphite (#2A2A2C) fixed the
     frames but was a cool blue-grey on a page whose materials are all
     warm (frame wood, mat, stage glow, What Arrives). Moss keeps the
     frame reading as a darker object on a lighter wall (1.26:1), the
     black strap at 1.26:1, and sits with the warm materials and the
     olive strap rather than against them. The mat never touches the
     wall; the moulding sits between.

     Matte, not metallic: shine on this page belongs to the diamonds.
     revision_5.md lists --surface-graphite as #34343700, which is fully
     transparent; nothing needs a raised panel, so none is defined. */
  --moss-bg:          #262A22;
  --moss-ink:         #F0F0EE;
  --moss-ink-dim:     #979A91;   /* 5.1:1 on --moss-bg */
  --moss-stage-centre: #33372D;  /* the glow's warm charcoal matched the
                                    wall (1.05:1) and vanished; a lifted
                                    moss reads at 1.20:1 and stays subtle */

  /* §3 → §4 reveal. Total pinned height; the travel is this minus
     one viewport, so 240vh gives 140vh of scroll for a 100vh slide.
     Tune by feel. */
  --reveal-pin-height: 240vh;

  /* §3's frames. The mat is the one pale surface on the page and is
     meant to be — it is what makes the tiles read as framed art
     rather than images on black. */
  --frame-wood: linear-gradient(155deg, #2a2118, #171310 55%, #0f0c09);
  --frame-mat: #e8e4da;
  --frame-moulding: 14px;

  /* ----------------------------------------------------------
     2. BACKDROPS  (the glass)
     Used in exactly two places — the nav and the §10 swatch row.
     Over black with nothing behind it, glass is a grey rectangle,
     so do not extend this to other components.

     "Ambient" is the nav: a wide, permanent surface with video and
     the drifting carousel passing under it.
     "Accent" is the swatch row: a small panel deliberately
     overlapping the product shot.
     ---------------------------------------------------------- */
  --backdrop-blur:     20px;
  --backdrop-saturate: 120%;  /* what stops glass reading as grey haze */

  --backdrop-ambient:      rgba(255, 255, 255, 0.04);  /* nav wash */
  --backdrop-ambient-edge: rgba(255, 255, 255, 0.08);  /* 1px top-lit hairline */
  --backdrop-accent:       rgba(255, 255, 255, 0.04);  /* swatch row wash */
  --backdrop-accent-edge:  rgba(255, 255, 255, 0.08);

  /* Solid fallbacks for browsers without backdrop-filter. A 4%
     white wash over black is invisible without the blur, so these
     have to be opaque enough to read. */
  --backdrop-ambient-solid: rgba(0, 0, 0, 0.88);
  --backdrop-accent-solid:  rgba(0, 0, 0, 0.70);

  /* Behind the custom finish request dialog. Dark enough that the
     page reads as set aside, not so dark it looks like navigation. */
  --scrim-modal: rgba(0, 0, 0, 0.72);

  /* HERO SCRIM — MEASURED. Do not eyeball these.
     The stops are set from per-frame luminance sampling across the
     whole hero clip so the mark, tagline and scroll cue clear WCAG
     against the brightest pixel behind them. Current worst case:
     mark 21:1, tagline 18.7:1, cue 18.7:1.

     This multiplier scales all of them at once. Below 1 you are
     spending contrast the type needs; above 1 you are dimming the
     watch for no legibility gain. If you move the hero type at all,
     the stops themselves need re-measuring, not just this number. */
  --hero-scrim: 1;

  /* ----------------------------------------------------------
     3. MOTION
     Read by CSS and by JS. Nothing on the page should animate for
     longer than --dur-slow.
     ---------------------------------------------------------- */
  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);    /* reveals, entrances */
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);   /* state changes */

  --dur-fast: 200ms;   /* hover, focus, small state */
  --dur-base: 340ms;   /* crossfades, most transitions */
  --dur-slow: 700ms;   /* section reveals */

  --reveal-y:       24px;   /* how far reveal elements travel */
  --reveal-stagger:  80ms;  /* between siblings in a row (read by reveal.js) */
  --reveal-max:         4;  /* most elements to stagger before it drags */

  /* §3 PIN MODE — set from device testing.
     GSAP picks "transform" on touch by default, which makes JS
     responsible for rewriting the pinned element's position on every
     scroll event. On iOS those events run behind the compositor
     during momentum, so the whole pinned frame lags and springs —
     reported on a real phone as jumpy pixels. "fixed" hands the job
     to the compositor and no JS is in the loop.

     Historically "fixed" was the one that broke on iOS, so if this
     ever misbehaves it will be at the pin boundaries or when the
     address bar collapses, not mid-sequence. Paired with
     ScrollTrigger's ignoreMobileResize. ?pintype= overrides. */
  --seq-pin-touch: fixed;

  /* §3 scrub — how hard the assembly sequence smooths the scroll
     signal before driving the canvas.

     Wheel under Lenis needs none: Lenis has already eased the
     position, and scrubbing again stacks two lags.

     Touch takes a light catch-up. 1.2 was preferred at first, but
     that was measured while the pin was still juddering on
     "transform" and heavy smoothing was partly masking it. With the
     pin on "fixed" there is no difference between 0.4 and 1.2 on
     the device, so it sits at 0.4: same feel, without trailing the
     thumb by over a second. */
  --seq-scrub-wheel: 0;     /* 0 means direct, no smoothing */
  --seq-scrub-touch: 0.4;

  /* Carousel drift. Seconds to traverse one slide width.

     Pinned to an absolute rate rather than to the motion spec's
     25–35s band: 20s against the current 300px slide gives ~15.8
     px/s, which is the speed signed off on screen. The band was
     written for 3:2 landscape slides roughly twice this wide, so
     it no longer maps — holding 25s here would have drifted 23%
     slower than the approved version.

     Coupled to the slide clamp in site.css through step(): narrow
     the slides again and the absolute speed drops with them, so
     re-measure px/s if that ceiling moves. Read by carousel.js. */
  --car-seconds-per-slide: 20;
  --car-resume: 1500ms;   /* how long drag momentum takes to blend back */

  /* ----------------------------------------------------------
     4. LAYOUT
     ---------------------------------------------------------- */
  /* Width caps are expressions, not one flat number. A single
     max-width on every section is what makes a page read as a
     predetermined block with identical dead space at every size:
     min() lets a wide screen actually use its width up to a
     readable ceiling, which a fixed px cap cannot do.
       content — prose and panels (About, Buy, What arrives)
       grid    — multi-column rows that can take more (Straps)
       canvas  — §6's frame, capped near source clarity        */
  --maxw-content: 1600px;
  --maxw-grid:    1800px;
  --maxw-canvas:  1600px;   /* ~1.45x the 1100px source; past this it softens */
  --gutter-desktop: 80px;
  --gutter-mobile:  20px;
  --section-pad-desktop: 160px;
  --section-pad-mobile:   80px;
  --nav-h: 64px;

  /* ----------------------------------------------------------
     5. TYPE
     Two families, three roles. Display serif for statements, sans
     for information. Nothing sits between 16px and the head min —
     that gap is what makes the hierarchy read, so if you raise
     --fs-body, raise the head floor with it.
     ---------------------------------------------------------- */
  --font: "Söhne", "Neue Haas Grotesk", "Inter", -apple-system,
          BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  --font-display: "Playfair Display", "Bodoni Moda", Georgia,
          "Times New Roman", serif;

  /* Section headers (Styles, About us, What arrives) — Cinzel
     Decorative, the client's choice. SIL Open Font License, so free for
     this commercial site; it replaced Trevoire Joaquin, whose licence
     was personal use only. Swash capitals with small-capital lowercase,
     so it is set in mixed case rather than forced to capitals. 18px on
     phones to ~22px wide, larger than the 11px labels at the client's
     request — inside the 16px-to-head gap above on purpose. Bold and
     Black weights exist under the same licence if it needs presence. */
  --font-section: "Cinzel Decorative", var(--font-display);
  --fs-section: clamp(18px, 1.4vw, 22px);
  --track-section: 0.08em;

  --fs-head: clamp(28px, 4vw, 56px);
  --fs-body: 16px;
  --fs-label: 11px;
  --track-label: 0.14em;
}

/* Moss tone. Seams read these same tokens, so every toned section
   fades in and out of moss through the ordinary seams, with no
   per-section fade rules. The stage glow falls to moss instead of
   black, so it blends into Buy's wall. */
html[data-tone="moss"] {
  --surface-straps:  var(--moss-bg);
  --surface-buy:     var(--moss-bg);
  --surface-about:   var(--moss-bg);
  --stage-3d-centre: var(--moss-stage-centre);
  --stage-3d-edge:   var(--moss-bg);
}
