/* ============================================================================
   XUConseil — mobile layer
   Loaded LAST, after each page's own <style> and after proto.css, so equal
   specificity resolves in this file's favour. Nothing here exists above the
   breakpoint: the desktop build is untouched.

   Breakpoint: 1023px (was 820 until 16 Sep). One tier. The desktop grid is fixed (--gut:63px,
   --rail:300px) and starts failing well before that, so 821–1200 remains a
   separate, later pass; it is not addressed here.

   Structure
     0  tokens + shared shell
     1  mobile navigation (bar + full-screen menu)
     2  section shell, headings, rules
     3  contact band + footer      (every page)
     4  01 home       5  02 about      6  03 work list
     7  case pages    7b per-case      8  04 service
     9  05 insights list             10  06 insights article
    11  14 contact
   ========================================================================== */

/* mobile.js builds the burger and the section-action pill at every width, so
   that no resize handler is needed and no state can get out of step. Above
   the breakpoint neither shows and the desktop nav and .sec-head links keep
   their places. These two rules are deliberately OUTSIDE the media block:
   without them an unstyled <button> and <a> appear on the desktop build. */
.xm-tail,.xm-burger,.xm-dots{display:none}

/* Also outside the media block, because it is not a mobile fix: proto.css
   scales a .bleed that has not yet entered to 1.05, and .bleed is the full
   viewport width, so 5% of 1440 is 36px of genuine horizontal scroll on every
   case page until each image enters. overflow-x:clip stops the document
   growing WITHOUT creating a scroll container, so position:sticky keeps
   working — the about rail, the work rail and the case rails all depend on it,
   and overflow-x:hidden here would silently kill all three. */
html{overflow-x:clip}

/* mobile.js turns the closing line's <br>-separated sentences into blocks at
   every width, so the block display has to hold at every width too — as an
   inline span the sentences would run together on the desktop. It reproduces
   exactly what the <br>s did; only the balancing below the breakpoint is new. */
.handoff .cta-line .xm-ln{display:block}

@media (max-width:1023px){

/* A standing rule for this file: never hide a <br>. Several headings here are
   written as "word<br>word" with no space around the tag, so display:none on
   the <br> silently joins the two words ("Cross-borderGTM", "campaign.This",
   "parts?Let's"). Let the line break stand and size the type to fit instead. */

/* --- 0. tokens ----------------------------------------------------------- */
:root{
  --gut:22px;
  --rail:0px;
  --colgap:30px;
}
body{line-height:1.58}

/* iOS: stop long unbroken strings (emails, URLs) forcing a horizontal scroll.
   clip, not hidden, for the same sticky reason as the rule above. */
body{overflow-x:clip}
h1,h2,h3,p,a{overflow-wrap:break-word}

/* --- 1. navigation ------------------------------------------------------- */
/* The six desktop links do not fit at 390px. Both the in-page nav and the
   sticky clone proto.js builds keep only the wordmark; mobile.js injects a
   burger into each and builds one full-screen menu shared by both. */
/* env(safe-area-inset-top) is 0 in ordinary Safari, where the browser chrome
   already sits above the page, and non-zero exactly where the notch or the
   Dynamic Island would otherwise cover the wordmark: added to home screen,
   in full-screen playback, and in landscape. Costs nothing when it is 0.
   It has to be added to padding, not applied via inset, because both bars
   also carry a horizontal gutter. */
nav{padding:calc(20px + env(safe-area-inset-top)) var(--gut) 20px}
nav .nav-links,.xc-nav .xc-links{display:none}
.mark{font-size:18px}
.xc-nav{padding:calc(14px + env(safe-area-inset-top)) var(--gut) 14px}
.xc-nav .mark{font-size:17px}

.xm-burger{display:flex;appearance:none;background:none;border:0;padding:0;cursor:pointer;
  width:44px;height:44px;margin-right:-10px;
  flex-direction:column;justify-content:center;align-items:flex-end;gap:6px}
.xm-burger i{display:block;width:22px;height:1.5px;background:var(--plume);
  transition:transform .4s var(--xc-ease),opacity .3s}
.xm-burger i:last-child{width:15px}

/* One motion, and NOT a transform. This block has been rebuilt twice; the
   history matters because the obvious implementations both fail on the
   phone.

   v13: panel transform .58s + six links staggered 55ms apart on their own
        .5s transition, finishing at ~0.94s. Read as sluggish.
   v14: one .xm-sheet counter-translating inside the panel, so the content
        stands still and the panel's bottom edge reveals it. Correct effect,
        but it stacks a transformed absolute layer inside a transformed
        position:fixed layer, and iOS promotes that pair and composites it
        against a stale viewport box: the panel came in partly drawn and
        snapped to full screen seconds later. Adding will-change in v15 made
        it worse, because it pinned the stale layer.

   v16: clip-path. The panel does not move at all — it is laid out full
        screen with its bottom clipped away, and only the clip animates. The
        content is therefore revealed in place, which is the effect the
        reference has, with one element, no second layer, no transform on a
        position:fixed box, and nothing for iOS to promote wrongly. The
        prefix is there for iOS below 13.4. */
.xm-menu{position:fixed;inset:0;z-index:300;background:var(--plume);color:#fff;
  -webkit-clip-path:inset(0 0 100% 0);clip-path:inset(0 0 100% 0);
  /* --xc-ease, not --xc-expo. The expo curve covers 75% of the sweep in the
     first 115ms and then spends 400ms on a tail nobody can see, so it reads
     as a snap followed by nothing. An even ease-out over .46s is the sweep
     the reference actually has. */
  transition:-webkit-clip-path .46s var(--xc-ease),clip-path .46s var(--xc-ease);
  overscroll-behavior:contain}
.xm-menu.open{-webkit-clip-path:inset(0 0 0 0);clip-path:inset(0 0 0 0)}
/* closing: a shorter, flatter curve. An expo out on the way back overshoots
   the top edge and reads as a bounce. */
.xm-menu:not(.open){transition:-webkit-clip-path .4s var(--xc-ease),
  clip-path .4s var(--xc-ease)}
/* .xm-sheet is now only the layout wrapper — no transform, nothing animated
   on it except the close fade */
.xm-sheet{position:absolute;inset:0;
  display:flex;flex-direction:column;justify-content:space-between;
  padding:calc(20px + env(safe-area-inset-top)) var(--gut) calc(30px + env(safe-area-inset-bottom))}
/* beat one of the close: the content drops out before the panel clips away */
.xm-menu.closing .xm-sheet{opacity:0;transition:opacity .15s linear}
/* author styles beat the UA rule for [hidden], so it has to be restated */
.xm-menu[hidden]{display:none}
body.xm-lock{overflow:hidden}

.xm-top{display:flex;align-items:center;justify-content:space-between;height:44px}
.xm-top .mark{font-family:var(--display);font-size:18px;font-weight:400;
  letter-spacing:.16em;color:#fff}
.xm-close{appearance:none;background:none;border:0;padding:0;cursor:pointer;
  width:44px;height:44px;margin-right:-10px;position:relative}
/* focus() is called on open so the panel takes keyboard focus; a programmatic
   focus paints a ring in Chromium even on touch, which is not what a close
   button should look like. Keyboard users still get :focus-visible. */
.xm-close:focus:not(:focus-visible){outline:none}
.xm-close::before,.xm-close::after{content:"";position:absolute;left:11px;top:21px;
  width:22px;height:1.5px;background:#fff}
.xm-close::before{transform:rotate(45deg)}
.xm-close::after{transform:rotate(-45deg)}

.xm-links{display:flex;flex-direction:column;align-items:stretch;
  justify-content:flex-start;padding:26px 0}
/* no per-link opacity or transform any more: the sweep above is the reveal */
.xm-links a{font-family:var(--display);font-weight:300;font-size:34px;line-height:1.1;
  letter-spacing:-.025em;color:#fff;padding:15px 0;border-bottom:1px solid #ffffff2b;
  display:flex;align-items:baseline;justify-content:space-between}
.xm-links a:first-child{border-top:1px solid #ffffff2b}
.xm-links a.here{color:#ffffffa8}
.xm-links a em{font-family:var(--body);font-style:normal;font-size:11px;font-weight:500;
  letter-spacing:.14em;color:#ffffff6b}

/* Layout B, Nina's pick. The v14 block was two 14px lines on the floor and
   read as fine print at the bottom of a form. The strapline drops to an
   eyebrow and the address comes up to 26px Fraunces on a rule of its own, so
   the menu closes on an action carrying the same weight as the nav items
   rather than on a caption. */
.xm-foot{display:flex;flex-direction:column;
  border-top:1px solid #ffffff2b;padding-top:22px}
.xm-tag{font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:#ffffff8a;font-weight:500;line-height:1.8;max-width:26ch}
.xm-foot a{margin-top:12px;align-self:flex-start;
  font-family:var(--display);font-weight:300;font-size:26px;line-height:1.15;
  letter-spacing:-.02em;color:#fff;border-bottom:0;padding-bottom:0}

/* --- 2. section shell ---------------------------------------------------- */
/* only the home page's unclassed <section>s. A bare `section` selector also
   caught .meta and .own on the case pages, which are <section> elements too,
   and gave each of them 76px of bottom padding they never asked for. */
section:not([class]){padding:0 0 76px}
.sec-head{flex-direction:column;align-items:flex-start;gap:24px;
  padding-top:28px;margin-bottom:40px}
.sec-head h2{font-size:31px;line-height:1.08}
.sec-head p{font-size:16.5px;margin-top:14px;max-width:none}
/* the desktop link is replaced by .xm-tail below the grid, not duplicated;
   without this rule both the head pill and the tail pill show */
.sec-head .right{display:none}
.sec-head-col{flex:none;width:100%}
.eyebrow{font-size:10.5px}

/* --- 3. contact band + footer (all pages) -------------------------------- */
.endband .inner{grid-template-columns:1fr;gap:44px;padding:58px var(--gut) 0}
.endband h2{font-size:32px;margin-top:12px}
.endband .lead{font-size:16px;max-width:none}
.cta-pill{margin-top:26px;font-size:14.5px;padding:14px 26px}
.end-meta{gap:18px;padding-top:0}
.end-meta .v{font-size:16px}
.endfoot{flex-direction:column;gap:8px;margin-top:40px;padding:22px var(--gut);
  font-size:12px}

/* --- 4. 01 home ---------------------------------------------------------- */
/* One full screen. svh, not vh or dvh: vh overshoots on iOS (it assumes the
   URL bar is collapsed, so the CTA sits below the fold on load) and dvh
   resizes as the bar collapses, which makes the whole hero jump on the first
   scroll. svh is the height with the bar showing, so the composition is
   complete the moment the page opens and never moves. */
.hero-full{min-height:100vh;min-height:100svh}
.hero-inner{padding:0 var(--gut) 58px;align-items:flex-end}
.hero-copy{max-width:none}
.hero-copy h1{font-size:36px;line-height:1.08;letter-spacing:-.03em;
  margin-top:20px;max-width:none}
.hero-copy p{font-size:16px;line-height:1.62;margin-top:26px;max-width:none}
.hero-cta{margin-top:34px;font-size:14.5px;padding:14px 26px}
/* the shoot placeholder moves out from under the copy, to just below the nav */
.hero-slot{align-items:flex-start;justify-content:flex-end;padding:96px var(--gut) 0}

/* An <img class="hero-img"> inside .hero-full fills the screen, and the scrim
   keeps the type legible over it.

   Every rule below names the image twice: once as a direct child, once through
   the <picture> that the WebP pass wraps it in. Both spellings are needed —
   the bare one because that is how the markup reads when an image is dropped
   straight in, the picture one because that is how the home hero reads now.
   Miss the second and the image loses its position, the scrim never draws and
   the type stays dark on a dark photograph. */
.hero-full>.hero-img,
.hero-full>picture>.hero-img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;z-index:0}
/* the in-page nav is a static child of .hero-full, so an absolutely positioned
   image paints over it unless it is given a stacking context of its own */
.hero-full>nav{position:relative;z-index:3}
.hero-full:has(>.hero-img,>picture>.hero-img)::after{content:"";position:absolute;left:0;right:0;bottom:0;
  height:70%;z-index:1;pointer-events:none;
  background:linear-gradient(to top,rgba(20,17,14,.70),rgba(20,17,14,.34) 46%,transparent)}
.hero-full:has(>.hero-img,>picture>.hero-img) .hero-copy h1,
.hero-full:has(>.hero-img,>picture>.hero-img) .hero-copy p{color:#fff}
.hero-full:has(>.hero-img,>picture>.hero-img) .hero-copy .eyebrow{color:#ffffffcc}
.hero-full:has(>.hero-img,>picture>.hero-img) .mark{color:#fff}
.hero-full:has(>.hero-img,>picture>.hero-img) .xm-burger i{background:#fff}
.hero-full:has(>.hero-img,>picture>.hero-img) .hero-slot{display:none}

/* Section action, moved below the content it belongs to.
   On the desktop the link sits top-right of the section head, in the eye's
   periphery. A single column has no periphery: anything placed there sits in
   the reading path, and "See all work" ahead of the four cases is a skip
   button offered before the work has been seen. So it moves below the grid,
   where it is the next step rather than an exit.
   Full width, not the natural-width pill: the natural-width pill lands
   directly under the keyword tags, which are also outlined plume pills at the
   same left edge, and reads as a third tag on the last card. Full width is
   what separates the two at a glance.
   Centred label, chosen deliberately over left-aligned: it is the one place
   on the site where text is not set to the left edge, and it is what makes
   the element read as a button rather than as another line of content. */
.xm-tail{display:flex;align-items:center;justify-content:center;gap:10px;
  margin-top:44px;padding:16px 26px;
  font-size:14.5px;font-weight:500;color:var(--plume);
  border:1px solid var(--plume);border-radius:999px}
.xm-tail .ar{font-size:16px;line-height:1}

/* one case after another */
.workgrid{grid-template-columns:1fr;gap:46px}
.workcard .meta{margin-top:18px;padding-top:15px}
.workcard .brand{font-size:23px}
.workcard .one{font-size:15px;margin-top:10px;max-width:none}
.kws{margin-top:16px;gap:8px}

.cap-sec{padding:0 0 72px}
.capgrid{grid-template-columns:1fr;gap:38px}
.cap .n{font-size:66px}
.cap h3{font-size:23px;margin-top:16px;min-height:0}
.cap p{font-size:15.5px;margin-top:10px}

.ins-sec{padding:0 0 76px}
.ins-wide{grid-template-columns:1fr;gap:22px}
/* the only 16:9 source on the page — shown whole rather than cropped to 4:3 */
.ins-wide .fr{aspect-ratio:auto}
.ins-wide .fr img{height:auto}
.ins-wide h3{font-size:26px;margin-top:14px}
.ins-wide p{font-size:15.5px;margin-top:14px;max-width:none}
.ins-wide .more{margin-top:20px}

/* --- 5. 02 about --------------------------------------------------------- */
/* opening: same full screen as the home hero, and wired the same way for the
   portrait, so the two pages open with one gesture */
/* aligned to the home hero: the type sits at the bottom and the top of the
   screen is left to the portrait, so the two openings read as one gesture */
.ab-open{min-height:100vh;min-height:100svh;align-items:flex-end;
  padding:110px var(--gut) 62px}
.ab-open h1{font-size:38px;line-height:1.04;letter-spacing:-.03em}
.ab-open .tick{height:38px;margin-top:30px}
.ab-open .stand{font-size:16.5px;line-height:1.62;margin-top:26px;max-width:none}
.ab-cta{margin-top:32px;font-size:14.5px;padding:14px 26px}
/* the shoot placeholder moves off the bottom, where it now collides with the
   CTA, to just under the nav — the same place it sits on the home hero */
.ab-open .slot{top:92px;bottom:auto}
.ab-open>.hero-img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;z-index:0}
.ab-open:has(>.hero-img)::after{content:"";position:absolute;inset:0;z-index:1;
  pointer-events:none;background:linear-gradient(to top,rgba(20,17,14,.62),rgba(20,17,14,.30))}
.ab-open:has(>.hero-img)>div{position:relative;z-index:2}
.ab-open:has(>.hero-img) .slot{display:none}

/* The in-page rail comes back as a chip row directly under the opening: it is
   the first child of .ab-main, so in one column it lands there without any DOM
   move. Not sticky — a second permanent bar under the nav would cost 44px for
   the whole page. It scrolls horizontally, the "On this page" label is dropped
   because seven chips say what they are, and proto.js goes on marking the
   active one, which mobile.js keeps scrolled into view. */
.ab-rail{position:static;display:flex;gap:8px;align-items:center;
  overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;
  padding:30px var(--gut) 4px;margin:0 calc(-1 * var(--bleed))}
.ab-rail::-webkit-scrollbar{display:none}
.ab-rail .lab{display:none}
.ab-rail a{flex:0 0 auto;white-space:nowrap;font-size:12.5px;line-height:1;
  color:var(--quiet);padding:10px 15px;border:1px solid var(--hair);
  border-radius:999px;transition:color .3s,border-color .3s,background .3s}
.ab-rail a.on{color:#fff;background:var(--plume);border-color:var(--plume);font-weight:500}
.ab-main{grid-template-columns:1fr;gap:0}

.ab-row,.ab-tri{grid-template-columns:1fr;gap:30px;padding:54px 0;align-items:start}
/* the first block has no heading, so on the desktop the image beside it does
   the work of announcing a new section. Stacked, it lands straight off the
   hero, which is why it needs more air above it. The standfirst that was here
   is gone: the pull quote that closes the block now carries the weight, and
   two enlarged elements in a row competed rather than building. */
.ab-content>.ab-row:first-child{padding-top:66px}
/* every section reads heading, text, image, whichever side the desktop puts
   the picture on: leading a section with an untitled image gives the reader
   nothing to place it against */
.ab-textright>.fr{order:2}
.ab-tri>.txt{order:0}
.ab-tri>.fr{order:1}
/* One rule for every section on this page: heading, text, one full-width
   image. The third frame of the tri (IMG-12, 1:1) is dropped rather than
   stacked — a second image with no text between it and the first is what made
   the picture rhythm read as random, and it is the only frame on the page
   that carries nothing the others do not. */
.ab-tri>.small{display:none}
h3{font-size:24px;line-height:1.2;margin-bottom:18px}
p.b{font-size:16px;line-height:1.7;max-width:none}
.ab-row p.q{font-size:18px;line-height:1.5;padding-left:18px;margin-top:26px}

.ab-wide{padding:54px 0}
.ab-wide .cap{margin-top:26px}
.ab-wide .cap h3{padding-bottom:18px}
.ab-wide .cols{grid-template-columns:1fr;gap:16px;margin-top:22px}
.ab-wide .cols p.b{margin-bottom:0}
.more{margin-top:22px}

/* references: the arrows become the finger.
   Native overflow scrolling with snap points, not a JS swipe handler: it
   inherits iOS momentum, rubber-banding and the accessibility behaviour for
   free, and it cannot desynchronise from the desktop script. That script goes
   on setting an inline transform on .rtrack, which is why the reset needs
   !important — inline styles outrank a stylesheet otherwise.
   One card per screen, aligned to the page gutter on both sides: a peeking
   sliver of the next card showed a quote cut mid-word, which reads as a layout
   fault rather than as an invitation. The dots below carry the affordance
   instead, and they also say how many there are, which the sliver never did. */
.refs{padding:54px 0 64px}
.refs .head{flex-direction:column;align-items:flex-start;gap:0;padding-bottom:20px}
.refs h3{font-size:23px}
.refs .sub{font-size:14px;max-width:none}
.rnav{display:none}
.rview{overflow-x:auto;overflow-y:hidden;margin-top:24px;
  scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;
  scroll-padding-left:var(--gut);scrollbar-width:none;
  margin-left:calc(-1 * var(--bleed));margin-right:calc(-1 * var(--bleed));
  padding-left:var(--gut);padding-right:var(--gut)}
.rview::-webkit-scrollbar{display:none}
/* gap equal to the gutter: any smaller and the next card's edge shows past
   the right margin, which is what read as a cut-off card */
.rtrack{transform:none!important;transition:none;gap:var(--gut)}
/* 100%, not calc(100vw - gutters): under mobile emulation and on iOS 100vw
   can include the scrollbar/visual-viewport slack and comes out ~17px wider
   than the column, which puts a sliver of the next card on screen. 100%
   resolves against the flex container's content box, which IS the column. */
.rcard{flex:0 0 100%;scroll-snap-align:start;
  padding:26px 24px 24px}
.rcard .said p{font-size:14.5px;line-height:1.65}
.rcard .attr{padding-top:20px}
.rcard .who{font-size:16px}

.xm-dots{display:flex;justify-content:center;align-items:center;gap:9px;
  margin-top:22px}
.xm-dots button{appearance:none;padding:0;border:0;background:none;cursor:pointer;
  width:22px;height:22px;display:grid;place-items:center}
.xm-dots button::before{content:"";width:6px;height:6px;border-radius:50%;
  background:var(--plume);opacity:.22;transition:opacity .3s,transform .3s}
.xm-dots button[aria-current="true"]::before{opacity:1;transform:scale(1.25)}

/* today */
.today{margin-top:28px;padding:64px 0 72px}
.today .statement{font-size:26px;line-height:1.24;margin-top:16px;max-width:none}
.today .cols{grid-template-columns:1fr;gap:30px;margin-top:32px}
.today p.b{max-width:none}
.today .close{font-size:19px;max-width:none}
.today .cta{margin-top:26px;font-size:14.5px;padding:14px 26px}
.today .band{margin-top:48px}

/* --- 6. 03 work list ----------------------------------------------------- */
/* No opening image on this page, so the head is type only and does not take a
   full screen the way the home and about openings do. */
.pagehead{padding:44px var(--gut) 34px}
.pagehead h1{font-size:38px;line-height:1.04;letter-spacing:-.03em}
.pagehead p{font-size:16.5px;line-height:1.6;margin-top:18px;max-width:none}

/* the sector rail becomes the same chip row as on About. Two chips only, but
   the healthcare group runs five cards deep, so jumping to Beauty is a real
   saving. The year ranges are dropped: they double each chip's width and
   would push both off the screen. */
.workshell{grid-template-columns:1fr;gap:0;padding-bottom:76px}
/* One segmented pill, and on mobile it does what a segmented control looks
   like it does: it FILTERS. mobile.js adds an "All" segment and hides the
   group that is not selected, so choosing Beauty no longer jumps five cases
   down the page. proto.js keeps toggling its own .on class from the scroll
   position, which would fight the filter, so the active state is read from
   [data-on] instead and .on is neutralised inside .xm-filter. */
.rail{position:static;display:flex;gap:0;align-items:stretch;
  border:1px solid var(--hair);border-radius:999px;padding:4px;
  margin:0 0 24px;overflow:visible}
.rail .lab{display:none}
.rail a .n{display:none}
.rail a{flex:1 1 0;text-align:center;white-space:nowrap;
  font-family:var(--body);font-size:13.5px;font-weight:500;line-height:1;
  color:var(--quiet);padding:13px 6px;border:0;border-left:0;border-radius:999px;
  transition:color .3s,background .3s}
.rail a.on{color:#fff;background:var(--plume);border:0;border-left:0}
.rail.xm-filter a.on{color:var(--quiet);background:none}
.rail.xm-filter a[data-on]{color:#fff;background:var(--plume)}
.group[hidden]{display:none}
/* with one group filtered out the survivor still carried the 72px that
   separates two groups, leaving a hole under the control */
.group[hidden] + .group{margin-top:0}

.group + .group{margin-top:72px}
.grouphead{flex-direction:column;align-items:flex-start;gap:8px;
  padding-top:22px;margin-bottom:30px}
.grouphead h2{font-size:30px}
.yrmark{margin-bottom:12px}
.nda{padding:0 28px}
.nda .m{font-size:22px;margin-top:10px}

/* --- 7. case pages (07 08 09 12 13 15 16 17) ----------------------------- */
/* All seven share one vocabulary, so this block is the whole template. Every
   text section is `var(--rail) 1fr`; --rail is 0 on mobile but a zero column
   plus a column gap still indents the body, so each grid is restated as a
   single column with its own gap rather than relying on the token. */
.case-head{padding-top:20px}
.case-head h1{font-size:38px;line-height:1.02;letter-spacing:-.03em}
.case-head .one{font-size:19px;line-height:1.4;margin-top:18px;max-width:none}

/* Overview reads as rows, not columns: label left, value right, a hairline
   between each. Two columns put a two-word label above a three-word value in
   a 160px cell and everything wrapped; one label-and-value line per row is
   both wider and quieter. */
.meta{margin-top:26px}
.meta-row{grid-template-columns:1fr;gap:0;padding:0}
.meta-row>div{display:flex;align-items:baseline;justify-content:space-between;
  gap:24px;padding:15px 0;border-bottom:1px solid var(--hair)}
/* the last row's rule would double with .meta-about's own border-top */
.meta-row>div:last-child{border-bottom:0}
.meta-row .k{flex:0 0 auto}
.meta-row .v{text-align:right;font-size:16px;margin-top:0}
.meta-row .v small{font-size:13.5px}
.meta-about{grid-template-columns:1fr;gap:8px;padding:16px 0}
.meta-about .k{padding-top:0}
.meta-about p{font-size:15.5px;max-width:none}

/* Crops, and only where the source has room to lose. A 16:9 frame is 220px
   tall at 390px wide, which is why the photography was reading as small. The
   trim is off the sides only; nothing is scaled down.
     bayer 16:9 → 4:3   220 → 292px, 12.5% off each edge, all seven are event
                        photography with space at the sides
     nestle-01 3:2 → 4:3  260 → 292px
     blackmores-01 2.33:1 → 16:9  149 → 220px, the widest source on the site
   Leven is deliberately absent: its frames carry logos at the edges. */
.bleed img[src*="assets/bayer-0"]{aspect-ratio:4/3;object-fit:cover}
.bleed img[src*="assets/nestle-01"]{aspect-ratio:4/3;object-fit:cover}
.bleed img[src*="assets/blackmores-01"]{aspect-ratio:16/9;object-fit:cover}

/* The caption belongs to the image above it, so it sits close to it and the
   next image is pushed clearly away. At the old spacing the two gaps were
   almost equal and the caption read as a stray line between two pictures. */
.bleed{margin-top:30px}
.bleed.tight{margin-top:24px}
/* Only the two section-break frames that carry an inline margin-top:74px.
   The selector was [style] alone, which is wrong here: proto.js writes its own
   inline styles (--xc-py, transition-delay) onto EVERY .bleed, so the
   !important 44px was landing on all of them and silently overriding .opener,
   .tight and the paired-frames rule below. This is why the gaps between
   images would not move. */
.bleed[style*="margin-top"]{margin-top:44px!important}
/* the opening frame runs straight under the headline */
.bleed.opener{margin-top:22px}
/* Two frames with no caption between them are one exhibit — the Olay pair
   under a single caption, the two ESPGHAN frames — so they sit almost
   touching. Everywhere else a caption separates the frames, and the adjacent
   sibling selector never matches there. */
.bleed + .bleed{margin-top:8px}
.cap{font-size:11.5px;margin-top:8px;line-height:1.45}

/* section markers: at the shared eyebrow size of 10.5px these read as a
   caption, not as the start of a section, and on a page this long that is the
   only thing telling the reader a new part has begun. Sized up and given a
   rule of their own. */
.own .eyebrow,.refline .eyebrow{display:block;font-size:12.5px;letter-spacing:.15em;
  font-weight:600;padding-bottom:12px;border-bottom:1px solid var(--plume-hair)}

.own{margin-top:52px}
.own-in{grid-template-columns:1fr;gap:26px;padding:42px var(--gut) 44px}
.own-lead{font-size:18px;line-height:1.45;margin-top:16px}
.own-grid{grid-template-columns:1fr;gap:26px}
.own-grid p{font-size:16px}
.own-grid b{font-size:18px}

.sec{grid-template-columns:1fr;gap:20px;padding-top:52px}
.sec-rail h2{font-size:28px;line-height:1.06}
.sec-rail .pull{font-size:17px;margin-top:14px;padding-top:14px}
.sec-body{max-width:none}
.sec-body p{font-size:16.5px;line-height:1.68;margin-bottom:16px}
.sec-body h3{font-size:21px;margin:26px 0 10px}
.ledby{padding-left:18px;margin-top:30px}
.ledby p{font-size:16.5px;line-height:1.68}

/* the two metrics stack. Side by side they hold, but the label and the detail
   line under each would be set to about 20 characters, and the numeral is the
   one element on this page that should not have to compete for width. */
.out-metrics{grid-template-columns:1fr;gap:0;padding:30px 0 32px}
.metric + .metric{margin-top:28px;padding-top:28px;border-top:1px solid var(--hair)}
.metric .n{font-size:72px;min-height:0}
.metric .n.word{font-size:42px}
.metric .lab{font-size:14.5px;margin-top:16px}
.metric .det{font-size:15px;margin-top:8px}
.out-body{max-width:none;margin-top:32px}
.out-body h3{font-size:20px}
.out-body h3+p{margin-bottom:24px}
.out-body p{font-size:16.5px;line-height:1.68}

/* .duo (two 16:9 pack visuals) stacks: at full width each is 195px tall, which
   is the right size for what they are.
   .trip is different — three variations of the same campaign visual, 9:16 on
   Blackmores and A4 on Biocodex. Stacked full width they would be 615px and
   490px each, close to 1800px of scroll for three images that say one thing;
   at a third of the screen they would be 108px wide and unreadable. So they
   become a swipe row at two thirds width, the same gesture as References.
   A peeking image edge is a carousel, not a cut sentence, so no dots. */
.duo{grid-template-columns:1fr;gap:12px}
/* align-items must be flex-start: stretched, each figure's height is forced
   to the row's cross size and its aspect-ratio then recomputes the WIDTH,
   which silently overrides the flex-basis and puts two and a bit on screen. */
/* no negative margins here: .trip is a <div class="bleed trip">, already
   full-bleed at body level, so pulling it out again would push it off both
   edges. Only the padding is needed, to line the first figure up with the
   page gutter. */
.trip{display:flex;align-items:flex-start;grid-template-columns:none;
  overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;
  scroll-snap-type:x mandatory;scroll-padding-left:var(--gut);scrollbar-width:none;
  margin-left:0;margin-right:0;
  padding-left:var(--gut);padding-right:var(--gut)}
.trip::-webkit-scrollbar{display:none}
/* one per view, aligned to the gutter on both sides, with the gap equal to
   the gutter so the next figure's edge falls exactly off-screen. These are
   9:16 and A4 verticals: at two thirds width they were still small and the
   neighbour was cut. Dots below carry the swipe, as on References. */
.trip figure{flex:0 0 100%;scroll-snap-align:start}
.trip{gap:var(--gut)}

.refline{grid-template-columns:1fr;gap:10px;margin-top:44px}
.refline .eyebrow{padding-top:0;margin-bottom:4px}
.refline .body{padding:22px 22px 20px}
.refline .said{font-size:15px;max-width:none}
/* the arrow lives inside the link, and at .12em tracking the line broke with
   the arrow alone on the last row */
.refline .a2{letter-spacing:.08em}
.refline .a2 a{white-space:nowrap}

/* the closing line is placed in column 2 on the desktop grid; in one column
   that reference would push it out of the layout entirely */
.handoff{grid-template-columns:1fr;padding-top:52px}
/* 18.5px, not larger: the line carries a <br> and ends in an arrow, and at
   20px or above the arrow is pushed onto a line of its own. Measured, not
   guessed — 19px is the first size that fits, 18.5 leaves a margin. */
/* balance evens the lines out so the closing question does not end on one
   short orphan; the size is the largest that keeps the trailing arrow off a
   line of its own on the longest of the seven (Biocodex). */
.handoff .cta-line{grid-column:1;font-size:18.5px;line-height:1.34;padding-bottom:6px;
  text-wrap:balance}
/* balancing is mobile-only; the block display itself is set outside the media
   block, because mobile.js replaces the <br>s at every width */
.handoff .cta-line .xm-ln{text-wrap:balance}

.next{grid-template-columns:1fr;gap:16px;margin-top:64px;padding-top:26px;
  align-items:start}
/* --- 7b. blocks that belong to a single case page ------------------------ */

/* 09 Olay Ageless — the film. The label is absolutely positioned over the left
   third of the poster with a scrim behind it; at 390px that leaves it about
   130px of usable width over a 220px-tall frame. It drops out of the overlay
   and sets below the poster instead, still on the block's own charcoal, so the
   white type and the play control need no recolouring. The tap target
   (.film-hit) covers the whole block either way. */
/* the label panel sits on the block's own ground; charcoal under a beauty
   film read as a hard cut, so on mobile the block takes plume instead. Solid
   --plume, not a translucent one: the contact band at the foot of every page
   is already a solid #90414D field, so this way the site has one plume rather
   than a second, slightly-off shade nobody can name. */
.film{margin-top:44px;background:var(--plume)}
.film-scrim{display:none}
.film-lab{position:static;transform:none;max-width:none;
  padding:22px var(--gut) 28px}
.film-lab h3{font-size:26px;line-height:1.12;margin-top:10px}
.film-lab h3 .hz{font-size:.66em}
.film-lab p{font-size:15px;margin-top:10px}
.film-play{margin-top:20px;gap:13px}
.film-play .disc{width:46px;height:46px;flex:0 0 46px}

/* 13 Blackmores — the bilingual platform line */
.plat{font-size:19px;margin:24px 0 18px;padding-left:16px}
.plat .cn{font-size:16px;margin-top:6px}

/* 17 L'Oréal — the NDA plate. 16:7 is 171px tall at this width, and the
   headline alone needs more than that, so the ratio is dropped for a
   min-height and real padding. */
.ndahero{aspect-ratio:auto;min-height:240px;margin-top:36px;padding:44px 0}
.ndahero .m{font-size:26px;line-height:1.16;margin-top:14px}
.ndahero .t{font-size:10.5px}

.next-thumb{width:120px}
/* 24/1.1: the longest next-work titles ("Leven (Alliance Nutrition Group)",
   "Biocodex — S. boulardii CNCM I-745") cannot be made to fit one line at any
   size worth reading, so the wrap is treated as a two-line title rather than
   fought with a smaller size. */
.next h3{font-size:24px;line-height:1.1;margin-top:8px}
.next p{font-size:15px}
.endband{margin-top:60px}

/* --- 8. 04 service ------------------------------------------------------- */
/* The offers spine. On the desktop a hairline runs the full height of the
   block with the numeral sitting on it, and the content is indented 128px to
   clear it. At 390px that indent is a third of the screen, and a 1px rule
   with nothing to the left of it is not a spine, it is a stray line. So the
   spine goes and the numeral comes inline above the title, at the same 66px
   the home page's Core services cards already use — one numeral treatment on
   the site, not two. The rule between offers carries the rhythm the spine
   was carrying. */
.offers{padding:0 var(--gut) 8px}
.offer{grid-template-columns:1fr;gap:26px;padding:40px 0 46px}
.offer::before{display:none}
.offer+.offer{border-top:1px solid var(--hair)}
.offer .n{position:static;transform:none;display:block;
  font-size:66px;line-height:.8;background:none;padding:0}
.offer h2{font-size:30px;line-height:1.06;max-width:none}
.offer .lede{font-size:15.5px;margin-top:16px;max-width:none}
.recv{margin-top:28px;max-width:none}
.recv li{font-size:15px;padding:13px 0}

.network{padding:66px 0 74px;margin-top:0}
.net-head{max-width:none}
.net-head h2{font-size:31px;line-height:1.06;margin-top:12px}
.net-head p{font-size:16px;margin-top:16px;max-width:none}
/* Three 500px cards laid side by side under overflow:hidden — at 390px the
   second and third are simply unreachable. Same swipe as References and the
   case-page .trip rows, at 82% so the next card's edge is visibly cut: that
   peeking edge is the affordance, which is why this row needs no dots. The
   "Scroll for more" line the desktop already carries stays, and now means
   something on a touch screen. */
.netrail{display:flex;gap:14px;margin-top:18px;padding:0 var(--gut);
  overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;
  scroll-snap-type:x mandatory;scroll-padding-left:var(--gut);
  scrollbar-width:none}
.netrail::-webkit-scrollbar{display:none}
/* The avatar sits above the text rather than beside it: at 82% width the
   78px column plus its 24px gap leaves under 160px for a three-line role
   description, which wraps to six. */
.pcard{flex:0 0 82%;scroll-snap-align:start;
  grid-template-columns:1fr;gap:16px;padding:26px 24px 28px}
.pcard .av{width:58px;height:58px}
.pcard .nm{font-size:19px}
.pcard .cat{margin-top:9px;padding-top:11px}
.pcard .role{font-size:14px;margin-top:10px}
.railhint{display:block;padding:18px var(--gut) 0;font-size:10.5px}
.net-note{margin-top:20px;padding:0 var(--gut);font-size:9.5px;letter-spacing:.08em;
  text-transform:uppercase;color:#23201C57;text-align:left}
/* visual-only reorder for mobile (DOM/desktop order is unchanged):
   Brynn, Anna, Mina, Simi, Kimberley, Ting */
.netrail .pcard:nth-child(1){order:1}
.netrail .pcard:nth-child(2){order:4}
.netrail .pcard:nth-child(3){order:5}
.netrail .pcard:nth-child(4){order:3}
.netrail .pcard:nth-child(5){order:2}
.netrail .pcard:nth-child(6){order:6}

/* 84px here plus the 60px the shared .endband rule adds was 144px of empty
   ivory under a hard bordered edge. See the note on the desktop rule. */
.how{padding:0 0 4px}
.how .inner{padding:52px var(--gut) 0}
.how h2{font-size:31px;line-height:1.06}
.how .stand{font-size:16.5px;margin-top:14px;max-width:none}
/* min-height on the title and the hook exists to put three columns on one
   baseline. Stacked there is no shared baseline, and the reserved space is
   just a gap under every heading. */
.howgrid{grid-template-columns:1fr;gap:14px;margin-top:36px}
.how .col{padding:28px 24px 30px}
.how .col h3{font-size:24px;min-height:0}
.how .col .hook{font-size:16.5px;margin-top:10px;min-height:0}
.how .col p{font-size:15px;margin-top:16px;padding-top:16px}

/* --- 9. 05 insights list ------------------------------------------------- */
.list-head{padding:44px var(--gut) 0}
.list-head h1{font-size:38px;line-height:1.04;letter-spacing:-.03em}
.list-head .stand{font-size:16.5px;line-height:1.6;margin-top:18px;max-width:none}
.ins-list{padding:40px var(--gut) 78px;gap:56px}
.ins-row{grid-template-columns:1fr;gap:20px}
/* la-rosee-hero is 16:9 and the frame is 4:3, so on the desktop this list
   thumbnail is a crop. The home page already shows the same image whole on
   mobile (.ins-wide); cropping it here would give one article two different
   shapes on the same phone. */
.ins-row .fr{aspect-ratio:auto}
.ins-row .fr img{height:auto}
.ins-row h2{font-size:26px;line-height:1.14;margin-top:14px}
.ins-row p{font-size:15.5px;line-height:1.66;margin-top:14px}
.ins-row .more{margin-top:22px;font-size:14.5px;padding:12px 24px}

/* --- 10. 06 insights article --------------------------------------------- */
.art-head{padding-top:24px}
.art-head h1{font-size:34px;line-height:1.06;letter-spacing:-.03em;margin-top:16px}
.art-head .stand{font-size:17px;line-height:1.5;margin-top:20px;max-width:none}
.art-head .byline{font-size:12.5px;margin-top:20px;line-height:1.75}
.art-head .byline .sep{margin:0 7px}
/* the byline is one line on the desktop and 61 characters long; at 390px it
   wraps, and left alone it breaks inside "5 min to read". The second
   separator becomes a zero-height block instead — a line break without a
   hidden <br> (see the standing rule at the top of this file) and without
   the dot left stranded at the end of the first line. Two deliberate lines:
   author and date, then reading time. */
.art-head .byline .sep:nth-of-type(2){display:block;height:0;margin:0;overflow:hidden}

/* mobile.js moves the share rail up here, under the byline. The desktop rail
   is absolutely positioned 108px to the LEFT of the reading column, which at
   390px is 108px off the screen — a single column has no margin to put it
   in. It stays a row of the same four circles rather than becoming a new
   component: the final visual design of this block is still open, and this
   pass should not pre-empt it. */
.art-head .share-rail{position:static;flex-direction:row;align-items:center;
  gap:9px;margin-top:24px;padding-top:20px;border-top:1px solid var(--hair)}
.art-head .share-rail .lab{margin-bottom:0;margin-right:5px}
.art-head .share-rail a{width:40px;height:40px}

/* 1921/730 is 148px tall at this width — a letterbox strip, not a hero. 3/2
   keeps the same crop centre with enough height to read as an opening image. */
.art-hero{margin-top:28px}
.art-hero .fr{aspect-ratio:3/2}

/* the shell is a direct child of body, not inside .wrap, so it carries no
   gutter of its own */
.content-shell{max-width:none;margin-top:20px;padding:0 var(--gut)}
.italic-note{font-size:15px;margin-top:24px;margin-bottom:30px}
.art-body p.b{font-size:16px;line-height:1.7;margin-bottom:20px}
.art-body h2{font-size:26px;line-height:1.14;margin:44px 0 18px}
.art-body h3{font-size:21px;margin:34px 0 14px}
.art-body .cta-line{font-size:19px;line-height:1.4;margin:38px 0 8px}
.art-body .breakout{margin-top:26px}
.art-body .cap{margin-top:12px;margin-bottom:30px}
.wf-wrap-outer{margin-top:26px}

/* Three variations of one campaign visual — the same case as .trip on the
   case pages, and the same treatment: one per screen with dots, rather than
   three 106px panels. The negative margin is needed here and not on .trip
   because .kv-row sits inside .content-shell's gutter, not at body level. */
/* max-width:none is load-bearing: .art-body .breakout caps this element at
   100% of the reading column, which would clamp the flex container to 346px
   and leave the negative margins shifting a too-narrow box sideways. */
.art-body .kv-row{max-width:none}
.kv-row{display:flex;align-items:flex-start;grid-template-columns:none;
  gap:var(--gut);margin:0 calc(var(--gut) * -1);padding:0 var(--gut);
  overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;
  scroll-snap-type:x mandatory;scroll-padding-left:var(--gut);
  scrollbar-width:none}
.kv-row::-webkit-scrollbar{display:none}
.kv-row .fr{flex:0 0 100%;scroll-snap-align:start}

/* The deck-tools table sits at the reading width like every other image and
   opens in the zoom overlay on tap (proto.js §10) — the horizontal scroll
   container this file carried in v13 is gone. Nina's call: a table you can
   open at full size reads better than one you drag sideways at two-thirds
   legibility. The tap hint under it is what says so. */
/* the hint is a badge on the image now (proto.css §10c), not a line here */

/* --- 11. 14 contact ------------------------------------------------------ */
/* Copy first, portrait last. On the desktop the two are read at once, side
   by side; a single column has to choose an order, and a full-screen
   portrait ahead of the address puts a scroll in front of the one action
   this page exists for. So the portrait becomes the closing band and the
   page opens on the CTA. The nav returns to the flow with it — it is
   absolute over the frame on the desktop, and there is no frame at the top
   any more. */
.page>nav{position:static}
/* align-items:start, not the desktop's stretch. This is the bug behind "14 页
   的人像带我确实没看到": a stretched grid item is given the row's height, the
   row is sized from the item's content, and .shot is an empty div — so in
   WebKit the aspect-ratio is never consulted and the band collapses to zero.
   Chromium resolves the same circle the other way, which is why it rendered
   here and not on the phone. min-height is the belt to that brace: 125vw is
   4:5 of the viewport width, so the band keeps its proportion even if
   aspect-ratio is ignored entirely. */
.stage{grid-template-columns:1fr;min-height:0;align-items:start}
.copy{padding:34px var(--gut) 52px;max-width:none;justify-content:flex-start}
.copy h1{font-size:38px;line-height:1.04;letter-spacing:-.03em;margin-top:14px}
.copy .stand{font-size:16.5px;line-height:1.6;margin-top:20px;max-width:none}
/* scoped to .copy: 02-about also carries a bare class="cta" (its .today
   pill), and an unscoped .cta here would reach it. */
.copy .cta{margin-top:32px;font-size:14.5px;padding:14px 26px}
.copy .addr{margin-top:16px}
.copy .li-link{margin-top:26px}
.avail{margin-top:38px;padding-top:20px;max-width:none}
.avail p{font-size:16px}
/* 4/5, not the desktop's full-height column: the portrait is the band the
   page ends on, and it should close the page rather than become a second
   screen the reader meets after they already have the address. */
.shot{aspect-ratio:4/5;align-self:start;min-height:125vw}
.shot span{left:var(--gut);right:var(--gut);bottom:20px;line-height:1.7}
/* the shared rule in section 3 gives .endfoot a 40px top margin, which on
   every other page separates it from the contact band above. Here it would
   open an ivoire gap between the portrait and the plume footer. */
body:has(.stage) .endfoot{margin-top:0}

}
