/* `tokens` is declared here too. It was missing, and only lost to the other
   layers because tokens.css happens to be linked first: reorder the <link>
   tags and an unordered layer becomes the HIGHEST priority one. Only custom
   properties live in it today, so nothing broke; the omission was the bug. */
@layer tokens, reset, base, components, utilities;

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }
  * { margin: 0; }
  html { -webkit-text-size-adjust: 100%; }
  body { min-height: 100svh; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
  img, video, svg { display: block; max-width: 100%; }
  img { height: auto; }
  button, input, select, textarea { font: inherit; color: inherit; }
  h1, h2, h3 { text-wrap: balance; }
  p { text-wrap: pretty; }
}

@layer base {
  body {
    background: var(--ink-050);
    color: var(--fog-700);
    font-family: var(--font-body);
    font-variation-settings: 'wdth' var(--wdth-body);
    font-size: var(--step-body);
    line-height: var(--lh-body);
    overflow-x: hidden;
  }

  h1, h2, h3 {
    font-family: var(--font-display);
    font-variation-settings: 'wdth' var(--wdth-display);
    font-weight: 800;
    color: var(--fog-900);
    letter-spacing: var(--ls-display);
    line-height: var(--lh-display);
  }

  a { color: inherit; text-decoration: none; }

  :where(a, button, [tabindex]):focus-visible {
    outline: 2px solid var(--cure-500);
    outline-offset: 3px;
    border-radius: 2px;
  }

  .skip {
    position: absolute; left: var(--s-4); top: -4rem; z-index: 999;
    background: var(--cure-500); color: var(--cure-ink);
    padding: var(--s-3) var(--s-5); border-radius: var(--r-input);
    font-weight: 600; transition: top var(--dur-hover) var(--ease-out-quart);
  }
  .skip:focus { top: var(--s-4); }

  /* SplitText word wrappers. Needs a block box to take a transform, and
     inline-block keeps it inside the line rather than breaking it. Nothing
     here hides anything: with JS off or GSAP missing no .wd is ever created
     and the copy renders as ordinary text. */
  .wd { display: inline-block; }

  /* the ONLY uppercase on the site: 10-13px tracked labels */
  .eyebrow {
    font-family: var(--font-mono);
    font-size: var(--step-mono);
    letter-spacing: var(--ls-mono);
    line-height: var(--lh-mono);
    text-transform: uppercase;
    color: var(--fog-500);
  }
}

@layer components {
  .btn {
    --btn-bg: transparent;
    --btn-fg: var(--fog-900);
    --btn-bd: var(--ink-300);
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--s-2);
    min-height: 3rem; padding: 0 var(--s-5);
    background: var(--btn-bg); color: var(--btn-fg);
    border: 1px solid var(--btn-bd);
    border-radius: var(--r-input);
    font-weight: 600; font-size: var(--step-sm);
    letter-spacing: -0.005em;
    cursor: pointer;
    transition: background-color var(--dur-hover) var(--ease-out-quart),
                border-color var(--dur-hover) var(--ease-out-quart),
                color var(--dur-hover) var(--ease-out-quart),
                transform var(--dur-hover) var(--ease-out-quart);
  }
  .btn--accent { --btn-bg: var(--cure-500); --btn-fg: var(--cure-ink); --btn-bd: var(--cure-500); }
  .btn--accent:hover { --btn-bg: var(--cure-600); --btn-bd: var(--cure-600); }
  .btn--ghost { --btn-bd: color-mix(in oklch, var(--fog-900) 26%, transparent); }
  .btn--ghost:hover { --btn-bd: var(--fog-900); background: color-mix(in oklch, var(--fog-900) 8%, transparent); }
  .btn:active { transform: translateY(1px); }

  /* ── the pour rail, one continuous line, fills as you descend ── */
  .pour-rail {
    position: fixed; inset: 0 auto 0 0; width: 2px; z-index: var(--z-nav);
    background: color-mix(in oklch, var(--fog-900) 8%, transparent);
    pointer-events: none;
  }
  .pour-rail__fill {
    /* scaleY, not height. Animating height relayouts this element on every
       scroll frame; a transform is compositor-only. --pour is now a unitless
       0-1 progress written straight onto this element rather than a percentage
       written onto :root, which invalidated the style of the whole document
       once per frame for the benefit of one 2px line. */
    display: block; width: 100%; height: 100%;
    transform: scaleY(var(--pour, 0)); transform-origin: 0 0;
    background: linear-gradient(180deg, var(--cure-500), var(--cure-600));
    transition: height 120ms linear;
  }

  /* ── nav ── */
  .nav {
    position: fixed; inset: 0 0 auto 0; z-index: var(--z-nav);
    /* three tracks, outer two equal, so the links sit on the true centre of
       the hero rather than wherever the wordmark and the button leave room */
    display: grid; align-items: center;
    grid-template-columns: 1fr auto 1fr;
    gap: var(--s-6);
    /* tighter than the page gutter on purpose: the mark should sit at the
       edge of the screen and the button at the other edge, so the centred
       links read as centred against the viewport and not against a box */
    padding: var(--s-5) clamp(1rem, 2vw, 2rem);
    /* No blend mode. difference inverts the accent button's dark label into
       mud. Legibility comes from the hero's top radial scrim plus a shadow
       on the glyphs themselves, which survives any frame of the footage. */
    transition: background-color var(--dur-base) var(--ease-out-quart),
                border-color     var(--dur-base) var(--ease-out-quart);
  }
  /* off the hero it needs its own ground, or it floats over photography */
  .nav[data-landed='true'] {
    background: color-mix(in oklch, var(--ink-000) 82%, transparent);
    backdrop-filter: blur(14px) saturate(1.1);
    border-block-end: 1px solid var(--ink-200);
  }

  .nav__mark { justify-self: start; display: flex; align-items: center; gap: var(--s-3); color: var(--fog-max);
    text-shadow: 0 1px 16px oklch(0.07 0.01 250 / 0.9); }
  .nav__logo { width: 42px; height: auto; display: block; }
  .nav__word {
    font-family: var(--font-display); font-variation-settings: 'wdth' 118;
    font-weight: 800; font-size: 1.05rem; letter-spacing: -0.02em; color: var(--fog-max);
  }
  .nav__word span {
    font-family: var(--font-mono); font-weight: 400;
    font-size: 0.6rem; letter-spacing: var(--ls-mono);
    text-transform: uppercase; margin-left: 0.55em; opacity: 0.75;
  }

  .nav__links { justify-self: center; display: flex; gap: var(--s-4); align-items: center; }
  /* masked vertical roll, the label is stacked twice and slides */
  /* The hit area is the anchor (>=44px). The roll happens inside .roll, which
     is the only thing clipped. Keeping the clip on the anchor is what broke
     this: the label rolled out and nothing rolled back in. */
  /* A rule under every label, not just on hover: it reads as navigation
     rather than as plain text, and it is the thing initSpy colours to show
     which section you are actually in. scaleX, not width, so it composites. */
  .nav__links a::after {
    content: ''; position: absolute;
    left: var(--s-2); right: var(--s-2); bottom: 6px; height: 1px;
    background: currentColor; opacity: 0.28;
    transform: scaleX(1); transform-origin: left center;
    transition: opacity var(--dur-hover) var(--ease-out-quart),
                background-color var(--dur-hover) var(--ease-out-quart);
  }
  /* Hover takes the rule to the accent, not just to a brighter white. It used
     to only lift the opacity, so the only feedback was a slightly whiter line
     against a white label, which on a photograph reads as nothing. The footer
     links already answer the pointer in the accent; the nav now agrees with
     them. focus-visible gets the same treatment so the keyboard sees what the
     pointer sees. */
  .nav__links a:hover::after,
  .nav__links a:focus-visible::after { background: var(--cure-500); opacity: 1; }
  .nav__links a.is-active::after { background: var(--cure-500); opacity: 1; }

  .nav__links a {
    position: relative; display: flex; align-items: center;
    min-height: 44px; padding-inline: var(--s-2);
    color: var(--fog-max);
    font-size: var(--step-sm); font-weight: 500;
    text-shadow: 0 1px 14px oklch(0.07 0.01 250 / 0.95),
                 0 0 2px oklch(0.07 0.01 250 / 0.55);
  }
  /* The clip height and the line box are ONE number. They used to be set
     independently: an em height here, and a line-height of 1.6 inherited from
     the body, so every label was clipped by the difference. Measured 3.7px off
     the nav, 5.9px off the finish tabs and 6.6px off the buttons, which is
     exactly where descenders live, so the g in "aggregate" and the p in
     "Stamped" lost their tails. Tying the two together also removes the reason
     the footer and buttons carried their own shorter heights: the two stacked
     copies now sit exactly one roll apart by construction, so no gap can open
     between them as they travel. */
  .roll { --roll-h: 1.3em; display: block; overflow: clip; height: var(--roll-h); }
  .roll > span {
    display: block;
    line-height: var(--roll-h);
    transition: translate var(--dur-hover) var(--ease-out-quart);
  }
  .nav__links a:hover .roll > span,
  .nav__links a:focus-visible .roll > span,
  .btn:hover .roll > span,
  .btn:focus-visible .roll > span,
  .deck__btn:hover .roll > span,
  .foot__nav a:hover .roll > span,
  .foot__nav a:focus-visible .roll > span,
  .ftab:hover .roll > span,
  .ftab:focus-visible .roll > span,
  .menu__nav a:hover .roll > span,
  .menu__nav a:focus-visible .roll > span,
  /* the mobile bar has no hover input, so the tap itself (:active) is what
     shows the roll, alongside hover/focus for anything that does have one */
  .callbar:hover .roll > span,
  .callbar:active .roll > span,
  .callbar:focus-visible .roll > span { translate: 0 -100%; }
  /* a button is a fixed box, so the roll must not add leading to it */
  .btn .roll { --roll-h: 1.2em; }
  .nav__cta { justify-self: end; }

  @media (max-width: 860px) {
    .nav__links { display: none; }
    .nav { gap: var(--s-4); }
  }
  /* Below 760 the sticky call bar already carries the primary action, so the
     nav CTA is a duplicate that crowds the wordmark. One label per intent. */
  @media (max-width: 760px) {
    .nav__cta { display: none; }
    .nav { padding-block: var(--s-4); }
    /* the 42px mark plus the full wordmark ran under the Menu button */
    .nav__logo { width: 32px; }
    .nav__word span { display: none; }        /* "CONCRETE" drops, "M3D" stays */
    /* the links and the desktop CTA are both gone here, so the three-track
       grid left the Menu button stranded in the middle column */
    /* mark | CTA | menu, outer tracks equal so the CTA sits on the true
       centre of the bar rather than wherever the mark leaves room */
    .nav { grid-template-columns: 1fr auto 1fr; }
    .nav__toggle { justify-self: end; }
  }
  /* The panel is z-index 200 and the bar is 100, so the open menu covered its
     own close button: on a phone, with no Escape key, the only way out was to
     navigate somewhere. Lift the whole bar over the panel while it is open. */
  body.menu-open .nav { z-index: calc(var(--z-nav) + 150); }

  @media (max-width: 380px) {
    /* the mark alone still identifies the site; the number does not survive
       being cropped, so the wordmark is what gives way */
    .nav__word { display: none; }
  }

  /* ── the claim strip ── */
  .claim {
    background: var(--ink-000);
    padding: var(--s-9) var(--gutter);
    border-block: 1px solid var(--ink-200);
  }
  .claim p {
    max-width: 34ch;
    font-family: var(--font-display);
    font-variation-settings: 'wdth' 110;
    font-weight: 700;
    font-size: var(--step-h3);
    line-height: var(--lh-heading);
    letter-spacing: var(--ls-heading);
    color: var(--fog-900);
  }
  .claim em { font-style: normal; color: var(--cure-500); display: block; }
}

@layer components {
  /* ── the bar ── */
  .nav__links { --gap: clamp(0.5rem, 0.9vw, 1.1rem); gap: var(--gap); }
  /* Every label stays white, including the current section. The accent and
     the underline were doing the same job twice and made the bar flicker
     green as the page scrolled through sections. aria-current still marks
     the current one for a screen reader; it just is not painted. */

  .nav__toggle { justify-self: end;
  
    display: none; align-items: center; gap: var(--s-3);
    min-height: 44px; padding: 0 var(--s-3); margin-left: auto;
    background: none; border: 0; cursor: pointer; color: var(--fog-max);
    font-size: var(--step-sm); font-weight: 600;
    text-shadow: 0 1px 14px oklch(0.07 0.01 250 / 0.95);
  }
  .nav__toggle-bars { display: grid; gap: 5px; width: 22px; }
  .nav__toggle-bars i {
    display: block; height: 2px; background: currentColor;
    transition: translate var(--dur-hover) var(--ease-out-quart),
                rotate  var(--dur-hover) var(--ease-out-quart);
  }
  .nav__toggle[aria-expanded='true'] .nav__toggle-bars i:first-child { translate: 0 3.5px; rotate: 45deg; }
  .nav__toggle[aria-expanded='true'] .nav__toggle-bars i:last-child  { translate: 0 -3.5px; rotate: -45deg; }

  @media (max-width: 1180px) {
    .nav__links { display: none; }
    .nav__toggle { display: flex; }
    .nav__cta { display: none; }
  }

  /* ── the menu ── */
  .menu {
    position: fixed; inset: 0; z-index: var(--z-overlay);
    display: grid; grid-template-rows: 1fr auto;
    gap: var(--s-6);
    padding: calc(var(--s-10) + 2vh) var(--gutter) calc(var(--s-7) + env(safe-area-inset-bottom));
    background: var(--ink-000);
    overflow-y: auto; overscroll-behavior: contain;
  }
  .menu[hidden] { display: none; }
  .menu__nav { display: grid; align-content: center; gap: clamp(0.1rem, 1vh, 0.6rem); }
  .menu__nav a {
    display: flex; align-items: baseline; gap: var(--s-4);
    padding: var(--s-2) 0;
    font-family: var(--font-display);
    font-variation-settings: 'wdth' 118;
    font-weight: 800;
    font-size: clamp(1.9rem, 1rem + 5.4vw, 3.6rem);
    line-height: 1.04;
    letter-spacing: var(--ls-heading);
    color: var(--fog-900);
    transition: color var(--dur-hover) var(--ease-out-quart);
  }
  .menu__nav a:hover { color: var(--cure-500); }
  .menu__n {
    flex: none;
    font-family: var(--font-mono); font-weight: 500;
    font-size: var(--step-mono); letter-spacing: var(--ls-mono);
    color: var(--fog-500);
  }
  .menu__foot {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4) var(--s-5);
    padding-top: var(--s-5); border-top: 1px solid var(--ink-200);
  }
  .menu__tel { font-family: var(--font-mono); font-size: var(--step-body); color: var(--fog-900); }
  .menu__lic {
    flex-basis: 100%;
    font-family: var(--font-mono); font-size: var(--step-mono);
    letter-spacing: var(--ls-mono); color: var(--fog-500);
  }
  body.menu-open { overflow: hidden; }
}

@layer components {
  /* ── the grain ──────────────────────────────────────────────────────
     One SVG turbulence tile, tiled and held at low opacity over the whole
     viewport. Pointer-events off so it never intercepts anything. */
  .grain {
    position: fixed; inset: 0; z-index: 60;
    pointer-events: none;
    /* NOT mix-blend-mode. A fixed, full-viewport blended layer forces the
       whole page beneath it into a blend group that recomposites on every
       scroll frame: measured 43 of 242 frames over budget with overlay, 9
       with plain alpha, 4 with the layer gone entirely. Neither promoting it
       to its own layer nor `contain: strict` helped, because the cost is the
       blend itself. Opacity retuned so the texture reads the same weight
       over the dark grounds without lifting the blacks. */
    opacity: 0.035;
    background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'>\
<filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/>\
<feColorMatrix type='saturate' values='0'/></filter>\
<rect width='140' height='140' filter='url(%23n)'/></svg>");
    background-size: 140px 140px;
  }
  @media (prefers-reduced-motion: reduce) { .grain { opacity: 0.022; } }

}

@layer components {
  /* ── the cursor follower ────────────────────────────────────────────
     The system pointer is never hidden. This is one ring that trails it. */

  .cur {
    position: fixed; top: 0; left: 0; z-index: 9000;
    pointer-events: none;
    translate: -100px -100px;
    transition: opacity 200ms var(--ease-out-quart);
  }
  .cur--ring { /* trails the real pointer, never replaces it */
    mix-blend-mode: difference;
    width: 34px; height: 34px; margin: -17px 0 0 -17px;
    border-radius: 50%;
    border: 1px solid color-mix(in oklch, var(--fog-max) 55%, transparent);
    display: grid; place-items: center;
    transition: width 320ms var(--ease-out-quart),
                height 320ms var(--ease-out-quart),
                margin 320ms var(--ease-out-quart),
                border-color 320ms var(--ease-out-quart),
                background-color 320ms var(--ease-out-quart),
                opacity 200ms var(--ease-out-quart);
  }
  .cur--ring[data-state='link'] {
    width: 52px; height: 52px; margin: -26px 0 0 -26px;
    border-color: var(--cure-500);
    background: color-mix(in oklch, var(--cure-500) 12%, transparent);
  }
  .cur--ring[data-down='true'] { scale: 0.82; }

  /* rides the pointer exactly. The ring lags; the gap between them is the
     whole effect, so the dot must never be animated toward anything. */
  .cur--dot {
    width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px;
    border-radius: 50%;
    background: var(--cure-500);
    transition: scale 220ms var(--ease-out-quart), opacity 200ms var(--ease-out-quart);
  }
  .cur--dot[data-state='link'] { scale: 2.1; }
  .cur--dot[data-down='true'] { scale: 0.6; }

  /* the light band needs the inverse, or the ring vanishes on bone */
  .finishes .cur--ring { border-color: var(--bone-ink); }
}

@layer utilities {
  /* readable to a screen reader, invisible to everyone else */
  .u-sr {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    clip-path: inset(50%); overflow: hidden; white-space: nowrap;
  }

  /* The quote form honeypot. Off-screen rather than display:none, because a
     bot worth catching skips a field it can see is hidden. Not .u-sr: this
     one is aria-hidden and must stay away from assistive tech entirely. */
  .qform__hp {
    position: absolute; left: -9999px;
    width: 1px; height: 1px; overflow: hidden;
  }
}
