/* ============================================================================
   XUConseil — prototype motion layer
   Loaded by every page after its own <style>. It adds nothing to the static
   design: only the sticky nav, the cursor, entrance motion and hover.
   Nothing here can hide content if the JS fails, and the system cursor is
   never hidden. Everything is inert under prefers-reduced-motion (block 10).
   ========================================================================== */
:root{--xc-ease:cubic-bezier(.22,.61,.36,1);
      --xc-expo:cubic-bezier(.16,1,.3,1);   /* Clay's easing, for entrances */
      --xc-punch:cubic-bezier(.34,1.56,.64,1)}  /* slight overshoot, for landings */

/* --- 1. page transition -------------------------------------------------- */
/* a CSS animation, not a JS-set class, so a script error can never leave the
   page invisible */
body{animation:xcIn .42s var(--xc-ease) both}
@keyframes xcIn{from{opacity:0}to{opacity:1}}
body.xc-out{animation:none;opacity:0;transition:opacity .26s ease-in}

/* --- 1b. placeholder overlays -------------------------------------------- */
/* .hero-slot on 01-home is position:absolute;inset:0 and paints over the whole
   hero, including the nav — it was swallowing every click on the home page's
   navigation. It is only a shoot-placeholder caption, so it takes no pointer. */
.hero-slot,.ab-open .slot{pointer-events:none}

/* --- 2. sticky nav ------------------------------------------------------- */
.xc-nav{position:fixed;top:0;left:0;right:0;z-index:120;
  display:flex;align-items:center;justify-content:space-between;
  padding:16px var(--gut);width:100%;
  background:var(--ivoire);
  border-bottom:1px solid #23201C14;
  transform:translateY(-103%);transition:transform .48s var(--xc-ease)}
.xc-nav.show{transform:none}
.xc-nav .mark{font-family:var(--display);font-size:19px;font-weight:400;
  letter-spacing:.16em;color:var(--plume);text-decoration:none}
.xc-nav .xc-links{display:flex;gap:34px;align-items:center}
.xc-nav a{font-size:14px;font-weight:500;color:var(--plume);text-decoration:none;
  padding-bottom:3px;border-bottom:1px solid transparent;transition:border-color .3s}
.xc-nav a.here,.xc-nav a:hover{border-bottom-color:var(--plume)}

/* --- 3. cursor ----------------------------------------------------------- */
/* the system cursor is never hidden — no body{cursor:none} anywhere */
.xc-dot{position:fixed;top:0;left:0;z-index:200;pointer-events:none;
  width:0;height:0;opacity:0;transition:opacity .28s}
.xc-dot.live{opacity:1}
.xc-disc{position:absolute;left:0;top:0;transform:translate(-50%,-50%);
  display:flex;align-items:center;justify-content:center;text-align:center;
  width:15px;height:15px;border-radius:50%;border:0;
  background:rgba(144,65,77,.55);
  transition:width .32s var(--xc-ease),height .32s var(--xc-ease),background .3s}
.xc-disc b{font-family:var(--body);font-size:10.5px;font-weight:500;letter-spacing:.12em;
  text-transform:uppercase;color:#F7F3EE;opacity:0;line-height:1.25;transition:opacity .18s}
/* always a true circle — a two-word label sets on two lines rather than
   stretching the disc into a lozenge */
.xc-dot.big .xc-disc{width:104px;height:104px;background:rgba(144,65,77,.80)}
.xc-dot.big .xc-disc b{opacity:1;transition:opacity .26s .1s}
.xc-dot.big.soon .xc-disc{background:rgba(35,32,28,.80)}

/* --- 3b. CTAs -------------------------------------------------------------
   Deliberately placed BEFORE the entrance rules in block 4/5. A CTA that sits
   inside an entrance group carries two classes at once (e.g. "xb xc-btn"),
   and the `transition` shorthand does not merge between rules of equal
   specificity — the one that comes LAST in the file wins outright, including
   for properties the other rule declared and it didn't. Entrance is meant to
   win first (so the button actually fades/rises in), then release() strips
   the entrance class 2.6s later so this rule's own hover transition takes
   over cleanly. Keeping this block earlier in the file is what makes that
   order hold; moving it below block 4 silently drops the entrance's opacity
   transition on every CTA that sits inside a group (found while adding the
   contact-band pill's own settle timing — .hero-cta had the same bug). ---- */
.xc-btn{transition:transform .4s var(--xc-ease),background .4s var(--xc-ease),
  color .4s var(--xc-ease),border-color .4s var(--xc-ease),box-shadow .4s var(--xc-ease)}
.xc-btn:hover{transform:translateY(-3px);box-shadow:0 11px 26px -14px rgba(35,32,28,.55)}
.xa{display:inline-block;transition:transform .4s var(--xc-ease)}
.xc-btn:hover .xa,.xc-link:hover .xa{transform:translateX(6px)}
/* solid plume pills */
.hero-cta.xc-btn:hover,.ab-cta.xc-btn:hover,.today .cta.xc-btn:hover,
.copy .cta.xc-btn:hover{background:#6F373E}
/* the ivoire pill on the plume contact band */
.cta-pill.xc-btn:hover{background:#fff}
/* outline pills fill in */
.right.xc-btn:hover,.ins-row .more.xc-btn:hover{background:var(--plume);color:#fff}
/* text links: no lift, the rule thickens and the arrow travels */
.xc-link{transition:color .35s var(--xc-ease),border-color .35s var(--xc-ease)}
.cta-line.xc-link:hover{border-bottom-color:var(--plume)}
/* insight/about "more" links: underline is hidden at rest, grows in from the
   left on hover instead of sitting there statically the whole time */
.ins-wide .more.xc-link,.ab-content .more.xc-link{
  position:relative;border-bottom-color:transparent;transition:color .35s var(--xc-ease)}
.ins-wide .more.xc-link::after,.ab-content .more.xc-link::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--plume);
  transform:scaleX(0);transform-origin:left;transition:transform .4s var(--xc-expo)}
.ins-wide .more.xc-link:hover::after,.ab-content .more.xc-link:hover::after{transform:scaleX(1)}

/* --- 4. entrance ---------------------------------------------------------- */
/* Every heading on the site is split into words and grouped into the lines the
   browser actually laid out; each line starts 40ms after the one above it.
   Same mechanism as clay.global. The page header fires on load, every other
   heading fires on scroll, and non-heading content rises as a block behind it.
   Headings revealed on scroll also carry .sig — a touch of letter-spacing and
   scale so a one-line title doesn't read as just another fading block; the
   page header (H1) keeps its plain treatment since its multi-line cascade
   already reads as a deliberate, distinct entrance. */
.xw{display:inline-block;opacity:0;transform:translateY(.5em);
  transition:opacity .9s var(--xc-expo),transform 1.2s var(--xc-expo)}
.xb{opacity:0;transform:translateY(18px);
  transition:opacity .9s var(--xc-expo),transform 1.1s var(--xc-expo)}
.xw.in,.xb.in{opacity:1;transform:none}

.xw.sig{transform:translateY(.4em) scale(1.03);letter-spacing:.05em;
  transition:opacity .8s var(--xc-expo),transform 1.05s var(--xc-expo),letter-spacing .95s var(--xc-expo)}
.xw.sig.in{transform:none;letter-spacing:0}

/* --- 4b. differentiated vocabularies for specific content types ---------- */
/* outcome metrics (case pages): a short scale "punch" instead of a rise, so
   the number reads as landing rather than just fading in like body text */
.xc-punch{opacity:0;transform:scale(.78) translateY(6px);
  transition:opacity .5s var(--xc-ease),transform .65s var(--xc-punch)}
.xc-punch.in{opacity:1;transform:none}

/* contact band: the block breathes in as one unit (transform only — opacity
   stays with each child so the two don't compound), the CTA pill settles a
   half-beat after everything else instead of arriving in the same batch */
.xc-breathe{transition:transform 1.1s var(--xc-expo)}
.xc-breathe:not(.in){transform:scale(.985)}
.xc-pillsettle{opacity:0;transform:translateY(10px);
  transition:opacity .6s var(--xc-ease),transform .6s var(--xc-punch),box-shadow .3s}
.xc-pillsettle.in{opacity:1;transform:none}

/* card grids (work/services): scale in like an object landing, not a rise */
.workcard.xc-rise:not(.in){transform:scale(.94)}
/* network rail (service page): follows its own horizontal scroll direction */
.pcard.xc-rise:not(.in){transform:translateX(28px)}
/* next-work card: slides from the right — a spatial cue for "next" */
.next .xw:not(.in){transform:translateX(18px)}
.next .xb:not(.in){transform:translateX(26px)}

/* case-page pull quote: a rail grows in beside the text instead of the text
   just rising on its own — no extra markup, it's a pseudo-element */
.pull.xb{position:relative;padding-left:20px}
.pull.xb::before{content:"";position:absolute;left:0;top:2px;bottom:2px;width:3px;
  background:var(--plume);transform:scaleY(0);transform-origin:top;
  transition:transform .75s var(--xc-expo) .12s}
.pull.xb.in::before{transform:scaleY(1)}

/* --- 5. scroll entrance, images and text alike --------------------------- */
.xc-rise{opacity:0;transform:translateY(30px);
  transition:opacity .9s var(--xc-expo),transform 1.1s var(--xc-expo)}
.xc-rise.in{opacity:1;transform:none}

/* The WebP pass wraps each image in <picture class="fmt">. That wrapper is a
   format switch, not a box: display:contents keeps it out of layout entirely,
   so every image stays the layout child its parent already sized. Without it a
   <picture> generates an inline box, and a frame that took its height from the
   image -- .film-frame on the Ageless page is the clear case -- collapses to a
   line box until the file arrives. Art-directed pictures (biocodex, nestle)
   carry no `fmt` and are left alone. */
picture.fmt{display:contents}

/* full-bleed imagery: a settle-into-focus zoom rather than a rise, so
   photography and typography read as two different languages entering */
.bleed.xc-rise:not(.in),.art-hero.xc-rise:not(.in){transform:scale(1.05)}
.bleed.xc-rise,.art-hero.xc-rise{transition:opacity 1.1s var(--xc-expo),transform 1.15s var(--xc-expo)}

/* --- 6. parallax + hover zoom on full-bleed imagery ---------------------- */
/* two elements so the two motions never fight: the wrapper carries the hover
   zoom (transitioned), the img carries the scroll parallax (per-frame) */
.xc-px{overflow:hidden}
.xc-zoom{display:block;width:100%;height:100%;transform:scale(1);
  transition:transform .75s var(--xc-ease)}
.xc-px:hover .xc-zoom{transform:scale(1.05)}
.xc-px img{transform:translate3d(0,var(--xc-py,0px),0) scale(1.1);will-change:transform}

/* --- 7. hover on clickable blocks ---------------------------------------- */
.xc-hot{position:relative}
.xc-hot>*{position:relative;z-index:1}
.xc-hot::before{content:"";position:absolute;inset:-20px -22px;z-index:0;
  background:rgba(144,65,77,.038);opacity:0;pointer-events:none;
  transition:opacity .4s var(--xc-ease)}
.xc-hot:hover::before{opacity:1}
.xc-hot .brand,.xc-hot h2,.xc-hot h3{transition:color .4s var(--xc-ease)}
.xc-hot:hover .brand,.xc-hot:hover h2,.xc-hot:hover h3{color:var(--plume)}
.xc-hot .fr img,.xc-hot .next-thumb img{transition:transform .75s var(--xc-ease)}
.xc-hot:hover .fr img,.xc-hot:hover .next-thumb img{transform:scale(1.06)}
a.next.xc-hot::before{inset:1px -22px -14px}
.endband .xc-hot::before{background:rgba(255,255,255,.05)}

/* --- 9. in-page rails ---------------------------------------------------- */
html{scroll-behavior:smooth}
[id^="sec-"],#healthcare,#beauty,#contact,#film{scroll-margin-top:104px}

/* --- 10. reduced motion: everything off ---------------------------------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  body{animation:none}
  .xc-nav{display:none}
  .xc-dot{display:none}
  .xw,.xb,.xc-rise,.xc-punch,.xc-pillsettle{opacity:1;transform:none;transition:none}
  .xc-breathe{transform:none;transition:none}
  .pull.xb::before{transform:scaleY(1);transition:none}
  .xc-px img,.xc-zoom{transform:none}
  .xc-hot::before{display:none}
  .xc-hot:hover .fr img,.xc-hot:hover .next-thumb img{transform:none}
  .xc-btn:hover{transform:none;box-shadow:none}
  .xc-btn:hover .xa,.xc-link:hover .xa{transform:none}
  .ins-wide .more.xc-link::after,.ab-content .more.xc-link::after{transition:none}
}
/* --- 10b. share rail: copy-link feedback --------------------------------- */
/* the copy button has no destination, so without this it is the one control
   on the page that can be pressed and appear to do nothing */
.share-rail a.copied{border-color:var(--plume);color:var(--plume)}
.share-rail a.copied svg{fill:var(--plume)}
.share-rail a.copied::after{content:"Copied";position:absolute;left:50%;
  transform:translateX(-50%);top:calc(100% + 7px);
  font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--plume);white-space:nowrap}
.share-rail a{position:relative}
/* the rail is a row under the byline on mobile, where a label below the last
   circle would sit on the hairline; it goes above instead */
@media (max-width:1023px){
  .share-rail a.copied::after{top:auto;bottom:calc(100% + 7px)}
}

/* --- 10c. zoom overlay --------------------------------------------------- */
/* an image with real detail in it — the deck-tools table — opens at 1400px in
   a scroller. Not a pinch handler: a scroll container is what both a
   trackpad and a thumb already know how to drive, and it cannot get stuck at
   a zoom level with no way back. */
/* The affordance rides on the caption line, not on the image and not on a
   line of its own. It was a sentence under the caption for half a build
   ("Tap the table to open it full size.") and read as a stray paragraph
   hanging off the figure; moved onto the image it covered either the
   headline (top right) or the wordmark (bottom right). The caption row
   already runs the full width with empty space on the right, so the pill
   goes there — adjacent to the thing it acts on, over nothing. */
.xc-zoomable{cursor:zoom-in}
div.table-breakout:has(.xc-zoomable) + p.cap{
  display:flex;align-items:center;justify-content:space-between;gap:14px}
div.table-breakout:has(.xc-zoomable) + p.cap::after{
  content:"Full size";flex:none;
  background:var(--plume);color:var(--ivoire);
  font-family:var(--body);font-size:9px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;
  padding:6px 11px;border-radius:999px}

.xc-zoomov{position:fixed;inset:0;z-index:400;background:#23201Cf2;
  opacity:0;transition:opacity .3s var(--xc-ease)}
.xc-zoomov.open{opacity:1}
.xc-zoomov[hidden]{display:none}
body.xc-zoomlock{overflow:hidden}
.xc-zoomscroll{position:absolute;inset:0;overflow:auto;
  -webkit-overflow-scrolling:touch;display:flex;align-items:center;
  padding:64px 0}
/* desktop: the whole table at once, which is the point of opening it.
   mobile: twice the screen width, so the type lands around 11.5px and the
   reader starts at column 1 and drags right. Anything larger opens on a
   fragment with no way to tell what was cut. The page never sets
   user-scalable=no, so a pinch on top of this still works natively. */
.xc-zoomscroll img{display:block;width:min(1400px,96vw);max-width:none;
  height:auto;margin:auto}
@media (max-width:1023px){
  .xc-zoomscroll{padding:0}
  .xc-zoomscroll img{width:200vw}
}
.xc-zoomclose{position:absolute;top:14px;right:14px;z-index:2;
  appearance:none;background:none;border:0;cursor:pointer;
  width:46px;height:46px;padding:0}
.xc-zoomclose::before,.xc-zoomclose::after{content:"";position:absolute;
  left:12px;top:22px;width:22px;height:1.5px;background:#fff}
.xc-zoomclose::before{transform:rotate(45deg)}
.xc-zoomclose::after{transform:rotate(-45deg)}
/* the line under the table that says the image can be opened. It is a
   <p class="cap table-zoom-hint">, so it inherits the caption's colour. */
.table-zoom-hint{font-size:12px;letter-spacing:.02em}

/* --- 11. touch: no cursor, no parallax ----------------------------------- */
@media (hover:none),(pointer:coarse){
  .xc-dot{display:none}
  .xc-px img{transform:none}
  .xc-zoom{transform:none}
}

/* the two legal links in the end footer; same underline as .addr a, which is
   the established treatment for a link on the plume band */
.endfoot a{border-bottom:1px solid #ffffff40;padding-bottom:1px}
