/* ============================================================
   base — reset, typography primitives, layout helpers
   ============================================================ */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-padding-top:calc(var(--bar-h) + 24px)}
@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}
/* the eased driver animates anchors itself; two of them would fight */
html.has-lenis{scroll-behavior:auto}
body{
  margin:0;
  background:var(--milk);
  color:var(--ink);
  font-family:var(--ui);
  font-size:var(--t-body);
  line-height:1.6;
  font-weight:400;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  /* clip, not hidden: hidden would make the body a scrollport and break
     the sticky footer underneath the page */
  overflow-x:clip;
}
body.is-locked{overflow:hidden}
/* A page with little on it — checkout, 404, a thin legal page — used to end
   wherever its content ended, leaving the body's own cream showing under the
   dark footer. The page is a column instead and main takes up the slack, so
   the footer always closes the screen. The salon lives in this sheet too and
   lays itself out its own way, so it is left alone. */
body:not(.salon){min-height:100svh;display:flex;flex-direction:column}
body:not(.salon) > main{flex:1 0 auto}
body:not(.salon) > .foot{flex:none}
img{max-width:100%;display:block;height:auto;background:var(--stone)}
h1,h2,h3,h4,p,figure,blockquote,ul,ol{margin:0}
ul,ol{padding:0;list-style:none}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}
input{font:inherit;color:inherit}
svg{display:block}
[hidden]{display:none !important}
::selection{background:var(--accent);color:#fff}

:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

.skip{
  position:fixed;left:12px;top:-60px;z-index:200;
  background:var(--ink);color:var(--milk);padding:10px 16px;font-size:var(--t-meta);
  transition:top .2s var(--ease);
}
.skip:focus{top:12px}

/* ---------- layout ---------- */
.wrap{width:100%;max-width:var(--max);margin-inline:auto;padding-inline:var(--gutter)}
.wrap--wide{max-width:1760px}
.bleed{width:100%}
/* ---------- the three section sizes ----------
   Everything that is a page section wears one of these and nothing else. */
.sec{padding-block:var(--space-lg) 0}
/* Every section pays the same half-gap, tinted or not, so the distance between
   any two is identical. A tinted band used to add two thirds of a gap on top,
   on the reasoning that colour reads as band rather than as air — but that made
   two transitions out of ten wider than the rest, which is what showed. */
.sec--sm{padding-block:calc(var(--section-compact) / 2)}
.sec--lg{padding-block:calc(var(--section-large) / 2)}
/* a page that opens straight on a section heading, with no page title above
   it: the heading needs the air a title would have carried, measured from the
   breadcrumbs rather than from the section's own rhythm */
.sec--first{padding-top:calc(var(--section-default) / 2)}
.sec--pb{padding-bottom:calc(var(--section-default) / 2)}

/* ---------- transitions on a phone ----------
   A tinted block keeps the smaller, coloured share on both of its edges, and
   whatever sits against it carries the larger, light one. The result reads the
   same way at every seam: the content ends, a little colour, more air, then the
   next heading — never a wall of tint with a heading pinned to it.
   .edi is left out on purpose: it runs straight into the tinted block below it,
   and a light share there would open the white strip that pairing avoids. */
/* qualified by the element: the component sheets load after this one and set
   the same properties at plain-class weight, so a bare .concern loses */
/* ---------- one gap, paid once ----------
   An ordinary section carries its air at the top and nothing at the bottom, so
   two neighbours never stack two paddings into a double gap. Two blocks break
   that pattern and get their own rule:
     · a tinted block keeps both edges: it has to close its own colour, and the
       block after it still needs light air above its heading, so that one
       junction is the only wider one on the page;
     · a full-bleed band has no padding at all, so the block above it pays. */
section.concern,section.routine{padding-block:var(--space-lg)}
section:has(+ .edi){padding-bottom:var(--space-lg)}

/* parallax layer: the container stays put, only this moves */
[data-parallax]>.pl,[data-parallax]>picture{
  position:absolute;inset:-4% 0;display:block;will-change:transform;
}
[data-parallax]>.pl img,[data-parallax]>picture img{width:100%;height:100%;object-fit:cover}
@media (prefers-reduced-motion:reduce){
  [data-parallax]>.pl,[data-parallax]>picture{inset:0;transform:none !important}
}

/* a calm full-width hairline between two light sections */
.rule{height:1px;background:var(--line-soft);border:0;margin:0}

/* ---------- type primitives ---------- */
.label{
  font-size:var(--t-label);font-weight:500;letter-spacing:var(--ls-label);text-transform:uppercase;
  color:var(--ink-45);
}
.label--ink{color:var(--ink)}
.label--rule{display:inline-flex;align-items:center;gap:12px}
.label--rule::before{content:"";width:34px;height:1px;background:currentColor;opacity:.45}

.display{
  font-family:var(--display);font-weight:300;line-height:.94;letter-spacing:-.022em;
  font-size:var(--t-display);text-wrap:balance;
}
.display em{font-style:italic;font-weight:300}

.sec-head{
  display:flex;align-items:flex-end;justify-content:space-between;gap:32px;flex-wrap:wrap;
  margin-bottom:var(--space-head);
}
/* the heading container was capped in body-text ch, which forced short titles
   onto three lines — it now follows the grid, and the sub-line keeps the measure */
.sec-head__t{max-width:min(100%,820px)}
.sec-title{
  font-family:var(--display);font-weight:300;font-size:var(--d2);line-height:1.02;
  letter-spacing:-.018em;text-wrap:balance;
}
.sec-title em{font-style:italic}
.sec-sub{margin-top:var(--space-sub);color:var(--ink-70);max-width:min(100%,640px);font-size:var(--t-body);text-wrap:pretty}
/* On a phone the heading row wraps into a column, and its 32px column gap then
   becomes a row gap stacked on top of the sub's own margin-top — one gap paid
   for twice, about 70px of it. The column owns the spacing instead, at the
   token meant for exactly this pair. Heads holding a link, not a description,
   keep the wider wrapped gap. The salon page shares this sheet but runs its
   own heading scale, so it is left alone. */
@media (max-width:700px){
  .sec-head:not(.salon .sec-head):has(> .sec-sub){
    flex-direction:column;align-items:flex-start;row-gap:var(--space-sub);
  }
  .sec-head:not(.salon .sec-head) > .sec-sub{margin-top:0}
}

/* ---------- links & buttons ---------- */
.link{
  display:inline-flex;align-items:center;gap:9px;
  font-size:var(--t-meta);font-weight:500;letter-spacing:.06em;text-transform:uppercase;
  padding-bottom:5px;position:relative;white-space:nowrap;
}
.link::after{
  content:"";position:absolute;left:0;bottom:0;height:1px;width:100%;
  background:currentColor;transform-origin:right;
  transition:transform .5s var(--slow);
}
.link:hover::after{transform:scaleX(0);transform-origin:left}
.link svg{width:13px;height:13px;transition:transform .45s var(--slow)}
.link:hover svg{transform:translateX(5px)}

/* A "see everything" link belonging to a whole section is the one control of its
   kind on the screen. On a phone it stops being a short line of underlined text
   and becomes a full-width row with a thumb-sized box, its own underline running
   the whole width as that section's rule. Every link sitting in a section head
   qualifies; anywhere else it is opt-in by the modifier, because a link that
   shares its row with the rail arrows or stands beside a .btn must stay inline. */
@media (max-width:700px){
  .link--row,
  .sec-head > .link{
    width:100%;align-self:stretch;justify-content:space-between;
    min-height:60px;padding-bottom:0;
  }
}

/* On a phone a section's "see everything" link belongs after the thing it opens,
   not above it: the reader meets the goods first and the link is the step out.
   display:contents lifts the heading and the link out of .sec-head so they can
   be ordered around the content — CSS cannot move a child between parents. That
   leaves .sec-head with no box, so its margin-bottom goes with it and the
   heading carries the gap itself.
   The salon page shares this sheet and has a .rev of its own, so it is excluded. */
@media (max-width:700px){
  :is(.cats,.landed,.trend,.rev):not(.salon *){display:flex;flex-direction:column}
  :is(.cats,.landed,.trend,.rev):not(.salon *) > *{order:2}
  :is(.cats,.landed,.trend,.rev):not(.salon *) .sec-head{display:contents}
  :is(.cats,.landed,.trend,.rev):not(.salon *) .sec-head__t{
    order:1;margin-bottom:var(--space-head);
  }
  :is(.cats,.landed,.trend,.rev):not(.salon *) .sec-head > .link{
    order:3;margin-top:clamp(22px,5vw,30px);
  }
}

.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  height:52px;padding:0 30px;
  background:var(--ink);color:var(--milk);
  font-size:var(--t-meta);font-weight:500;letter-spacing:var(--ls-ui);text-transform:uppercase;
  position:relative;overflow:hidden;isolation:isolate;border-radius:var(--radius-sm);
  transition:color .4s var(--ease);
}
.btn::after{
  content:"";position:absolute;inset:0;z-index:-1;background:var(--accent);
  transform:translateY(101%);transition:transform .55s var(--slow);
}
.btn:hover::after{transform:translateY(0)}
.btn svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:1.3;flex:none}
.btn--line{background:transparent;color:var(--ink);box-shadow:inset 0 0 0 1px var(--line)}
.btn--line:hover{color:var(--milk)}
.btn--line::after{background:var(--ink)}
.btn--light{background:var(--on-dark);color:var(--ink)}
.btn--light:hover{color:var(--milk)}

/* A cross-document fade between pages. The site is many documents, so this is
   the only way to do it without a router; browsers that do not know the rule
   ignore it and navigate as before — no blank flash either way. */
@view-transition{navigation:auto}
/* the browser's own cross-fade is a quarter of a second, which on pages this
   quiet reads as a flicker rather than as a change of place */
::view-transition-old(root),::view-transition-new(root){
  animation-duration:.42s;
  animation-timing-function:var(--ease);
}
@media (prefers-reduced-motion:reduce){
  @view-transition{navigation:none}
}

/* ---------- the opening overlay ----------
   The wordmark, a thin line, then the site. It is shown on the first page of
   a visit and on a hard refresh only; every internal navigation already has
   the cross-document fade above, and a second overlay on top of that would
   read as the site reloading itself. A script in the head sets .is-booting
   before the first paint, so the overlay is never shown and then taken back.
   ---------------------------------------- */
.boot{
  position:fixed;inset:0;z-index:300;display:none;
  align-items:center;justify-content:center;background:var(--milk);
}
/* the lock also reserves the scrollbar's gutter, so nothing shifts sideways
   when the page is handed back its own scrolling */
html.is-booting{overflow:hidden;scrollbar-gutter:stable}
html.is-booting .boot{
  display:flex;
  /* should the module never run at all, the overlay still lets go */
  animation:boot-bail 0s linear 3.2s forwards;
}
@keyframes boot-bail{to{opacity:0;visibility:hidden;pointer-events:none}}

.boot__in{display:grid;justify-items:center;gap:20px}
.boot__logo{
  height:30px;width:auto;color:var(--ink);fill:currentColor;
  opacity:0;transform:translateY(4px);
  animation:boot-in .3s var(--ease) .05s forwards;
}
.boot__bar{
  display:block;width:100px;height:1px;background:var(--line);overflow:hidden;
  opacity:0;animation:boot-in .28s var(--ease) .18s forwards;
}
.boot__bar i{
  display:block;height:100%;background:var(--ink-45);
  transform:scaleX(0);transform-origin:left;
  animation:boot-fill .6s var(--ease) .18s forwards;
}
@keyframes boot-in{to{opacity:1;transform:none}}
@keyframes boot-fill{to{transform:scaleX(1)}}

/* the page is ready: fade off, and stop taking clicks on the way out */
.boot.is-done{opacity:0;pointer-events:none;transition:opacity .34s var(--ease)}

@media (max-width:700px){
  .boot__logo{height:24px}
  .boot__bar{width:84px}
}
@media (prefers-reduced-motion:reduce){
  .boot__logo,.boot__bar{animation:none;opacity:1;transform:none}
  .boot__bar i{animation:none;transform:scaleX(1)}
  .boot.is-done{transition-duration:.15s}
}

/* ---------- scroll reveal ----------
   An editorial lift, not an entrance: 8px and 400ms, so a heading has settled
   by the time the eye reaches it. Only opacity and transform move, so nothing
   reflows. A description paired with a heading carries data-rv-step="1", which
   reveal.js turns into a 70ms trail. */
[data-rv]{opacity:0;transform:translateY(8px);transition:opacity .4s var(--slow),transform .4s var(--slow)}
[data-rv].in{opacity:1;transform:none}
/* a phone shows less of the page at once, so the travel and the wait shorten */
@media (max-width:700px){
  [data-rv]{transform:translateY(6px);transition-duration:.35s}
}
/* A picture arrives the way the type does, just a touch softer and slower — it
   used to be a curtain of page colour sliding up over a second, which read as a
   wipe rather than an arrival. position/overflow stay: the images inside are
   absolutely placed and the frame still has to clip them. */
[data-rv-img]{
  position:relative;overflow:hidden;
  opacity:0;transform:translateY(12px);
  transition:opacity .55s var(--slow),transform .55s var(--slow);
}
[data-rv-img].in{opacity:1;transform:none}
@media (max-width:700px){
  [data-rv-img]{transform:translateY(8px);transition-duration:.45s}
}

/* ---------- rails (horizontal scroll) ---------- */
.rail{
  display:flex;gap:clamp(14px,1.6vw,26px);
  overflow-x:auto;overflow-y:hidden;scroll-snap-type:x proximity;
  overscroll-behavior-x:contain;
  /* no touch-action here: the drag handler is mouse-only, so touch relies on the
     browser's own scrolling, and pan-y would forbid the horizontal swipe.
     overflow-y:hidden is what keeps a vertical drag with the page. */
  scrollbar-width:none;-ms-overflow-style:none;
  padding-block:6px;margin-block:-6px;
}
.rail img,.rail a{-webkit-user-drag:none;user-select:none}
/* inside a horizontal track nothing travels vertically — the reveal fades only,
   otherwise staggered cards sit at different heights while it runs */
.rail [data-rv]{transform:none}
.rail.is-drag{user-select:none}
.rail::-webkit-scrollbar{display:none}
.rail>*{scroll-snap-align:start;flex:0 0 auto}
.rail.is-drag{cursor:grabbing;scroll-snap-type:none}
.rail.is-gliding{scroll-snap-type:none}
@media (hover:none){.rail{scroll-snap-type:x mandatory}}
.rail-nav{display:flex;gap:8px}
.rail-nav button{
  width:44px;height:44px;border-radius:50%;box-shadow:inset 0 0 0 1px var(--line);
  display:grid;place-items:center;transition:background .35s var(--ease),color .35s var(--ease),opacity .3s;
}
.rail-nav button:hover{background:var(--ink);color:var(--milk)}
.rail-nav button[disabled]{opacity:.25;pointer-events:none}
.rail-nav svg{width:14px;height:14px}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;transition-duration:.01ms !important}
  [data-rv],[data-rv-img]{opacity:1;transform:none}
  html{scroll-behavior:auto}
}
