/* ============================================================
   Shared shell for every page: tokens, reset, header, footer,
   buttons, layout helpers, scroll reveal.

   Page-specific styling goes in that page's own <style> block.

   DESIGN TOKENS — change these per client. Never ship a new
   client with the previous client's palette or type.
   ============================================================ */
/* Madabay Lodge — revision 3, see DESIGN.md */
@font-face{
  font-family:'Satoshi'; font-style:normal; font-weight:300 900; font-display:swap;
  src:url(../fonts/satoshi-variable.woff2) format('woff2');
}
/* Her signature, and only that. Never body, never a heading, never the header mark:
   a script at small sizes is illegible, and it is the third face in the system. */
@font-face{
  font-family:'Italianno'; font-style:normal; font-weight:400; font-display:swap;
  src:url(../fonts/italianno-latin.woff2) format('woff2');
}
@font-face{
  font-family:'Red Rose'; font-style:normal; font-weight:300 700; font-display:swap;
  src:url(../fonts/redrose-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:'Red Rose'; font-style:normal; font-weight:300 700; font-display:swap;
  src:url(../fonts/redrose-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
/* The logo's lettering, for every page's headline in capitals (her notes and JP, 17 Oct: "a
   font similar to the logo, capital letters", then "all hero fonts"). Chosen by setting
   MADABAY in ten free faces at the logo's own cap height: Gilda Display has its upright M
   with the V down to the baseline and its evenly bowled B; Cinzel, the obvious pick, splays
   the M and pinches the B. One weight only - the logo looks heavier because of its gold
   texture, not its strokes. Latin only: the headlines in all three languages need nothing more. */
@font-face{
  font-family:'Gilda Display'; font-style:normal; font-weight:400; font-display:swap;
  src:url(../fonts/gilda-display-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
:root{
  /* Warm, light, coastal. The richness comes from the photographs, not from dark blocks.
     Dark is reserved for type. Sampled from her own footage and the lodge's own materials. */
  --ink:        #2C261E;   /* warm near-black: type, never a large block */
  --ink-soft:   #6A6154;   /* driftwood: secondary type. Darkened from #7C7162 on 18 Sept:
                              the original measured 4.04:1 on --surface, under the 4.5 minimum
                              for body text. This clears both grounds — 4.68 and 5.24. */
  --paper:      #FCF8F1;   /* warm ivory: the page ground and the photography section */
  --sand:       #F3EBDD;   /* soft sand: The Lodge */
  --surface:    #F3EBDD;   /* alias, kept for the shared shell */
  --shell:      #EBDFCB;   /* deeper sand: the footer */
  --seafoam:    #B8CFC8;   /* the shallow water, used only as a hairline */
  --panel:      #DCE4DE;   /* pale seafoam: the panel half of a split, sampled from her shallows */
  --gold:       #A98A55;   /* champagne: the one accent, used sparingly */
  --gold-deep:  #816941;   /* the same hue, darker. Needed because the Book button fills
                              with gold and its label is ink on a pale bar but white over a
                              photograph: champagne carries ink at 4.60:1 but white at only
                              3.25, and this carries white at 5.21. Both read as gold. */
  --accent:     #A98A55;
  --light:      #FCF8F1;   /* type on photographs */
  --light-soft: rgba(252,248,241,.78);
  --line:       #E6DAC7;   /* hairlines on light ground */
  --line-dark:  rgba(252,248,241,.22);   /* hairlines on photographs */
  --scrim:      rgba(23,26,24,.5);

  --font-head: 'Red Rose', Georgia, 'Times New Roman', serif;
  --font-body: 'Satoshi', -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --font-sig:  'Italianno', 'Snell Roundhand', cursive;
  --font-display: 'Gilda Display', 'Trajan Pro', Georgia, serif;

  /* 84px was sized for a 34px drawn mark beside a wordmark. The brand is now her full lockup,
     which needs real height to be legible at all - at 84px it overflowed the bar. Everything
     that offsets against the header (anchor scroll-margin, the drawer, page heads) follows
     this token, so they all move with it. */
  --header-h:  118px;
  --section-y: clamp(80px, 9vw, 128px);
  --maxw:      1240px;
}

*{box-sizing:border-box;}
html{scroll-behavior:smooth;}
/* The body is dark: Safari paints its toolbars in the body colour, and this page opens and
   closes dark. Light sections carry their own ground on <main>'s children. */
body{
  margin:0; background:var(--paper); color:var(--ink);
  font-family:var(--font-body); font-size:16.5px; line-height:1.75; font-weight:400;
  -webkit-font-smoothing:antialiased;
}
img,video{max-width:100%; display:block;}
[hidden]{display:none !important;}
h1,h2,h3{font-family:var(--font-head); font-weight:300; letter-spacing:0.01em; margin:0 0 20px; text-wrap:balance;}
/* Three tiers, so the page has peaks. Nine of ten headings used to be the same 38.4px, which
   made a logistics note, the signature section and the only commercial ask indistinguishable.
   Lead is for the hero and the one closing ask; minor is for sections that support rather than
   carry. Phone floors are up a step: at 390px the old h1 sat at 32px against 27px h2s. */
:root{
  --h-lead:  clamp(2.1rem, 3.6vw, 3.3rem);
  --h-major: clamp(1.7rem, 2.7vw, 2.4rem);
  --h-minor: clamp(1.4rem, 2vw, 1.85rem);
}
h1{font-size:var(--h-lead); line-height:1.15;}
h2{font-size:var(--h-major); line-height:1.2;}
.h-lead{font-size:var(--h-lead); line-height:1.15;}
.h-minor{font-size:var(--h-minor); line-height:1.25;}
h3{font-size:1.25rem; font-weight:400; line-height:1.35;}
/* `pretty` lets the browser avoid leaving a single word alone on the last line. The say
   section's lede was ending on "door." by itself, which is the most conspicuous typographic
   error there is, and hardcoding a measure to fix it would only break again in French. */
p{margin:0 0 18px; max-width:60ch; text-wrap:pretty;}
/* An eyebrow is a <p>, so it inherited the 60ch measure: a 478px box sitting flush left inside
   a centred parent, with its centred text landing left of the section's real centre - 147px out
   on the hero. The measure is for prose; a two-word label does not need one. */
.eyebrow{max-width:none;}
/* The small label above a heading. It was written only into the homepage's own style, so on
   every other page the labels fell back to 16.5px body text and the site had two kinds of
   label without anyone choosing that (JP, 4 Oct: make them match). One rule, every page. */
.eyebrow{font:500 11.5px/1 var(--font-body); letter-spacing:.26em; text-transform:uppercase;
         color:var(--ink-soft); margin:0 0 22px;}
a{color:inherit;}
@media (max-width:640px){
  body{font-size:16.5px;}
}

.wrap{max-width:var(--maxw); margin:0 auto; padding:0 32px;}
.section{padding:var(--section-y) 0;}
.section--surface{background:var(--surface);}
/* A tighter top on a section that follows something already doing the spacing. Was declared
   identically in contact.html and lodge.html; excursions.html is the third page to want it. */
.section--tight{padding-top:clamp(36px,4vw,56px);}
/* Heading on the left, the content it introduces on the right. Declared identically in
   about.html and lodge.html and now wanted by excursions.html too, so it moves here rather
   than being written a third time. The 860 breakpoint is the one .split already uses. */
/* The site's one motion idea: a picture drifts a little inside its own frame as it crosses
   the window. Declared identically in index, about and lodge, and excursions.html is the
   fourth page to want it. The frame must clip and the image must be 112% tall, or the drift
   shows the edge.
   The travel runs from -10.4% to 0, not -5.2% to +5.2% as it did until 20 Oct. Every one of
   these pictures sits at the top of its frame (inset:0, or in flow), 12% taller than it, so it
   can only ever move UP and still cover. The old range moved it down by up to 5.8% of the frame
   in the second half of each crossing and uncovered a strip at the top - up to 8px, the white
   line JP saw between two stacked photographs. Same distance, same direction, never uncovers. */
@keyframes in-frame{from{translate:0 -10.4%} to{translate:0 0}}
/* Touch screens: no picture moves with the scroll. JP's thumb test, 20 Oct: "some photos move
   glitchy when scrolling", on the homepage, the lodge and about. The likely cause is that a
   scroll-linked drift on an iPhone re-times itself whenever Safari's toolbar grows or shrinks,
   which reads as a jump; it cannot be reproduced off a real iPhone, so the next thumb test is
   the check. On a phone the pictures simply sit still; a mouse or trackpad keeps the drift. The
   hero's own slow drift is not in this list. */
@media (hover:none) and (pointer:coarse){
  .split__media img, .pair img, .band__media,
  .tiles img, .trip-shot img, .intro-shot img, .details img{animation:none !important;}
}
/* Back on touch screens since the same day (JP: the food and "the water in front" photos "were
   a little animated, it was nice and flowy"): the table's plates and the archipelago's two
   columns. They move as whole pictures, a few pixels, with nothing behind them to uncover -
   unlike the pictures above, which drift inside a frame. */
.two-col{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.25fr);
         gap:clamp(28px,5vw,80px); align-items:start;}
@media (max-width:860px){ .two-col{grid-template-columns:1fr;} }
/* Anchor targets must clear the fixed header, or the heading lands underneath it. */
section[id]{scroll-margin-top:calc(var(--header-h) + 14px);}
@media (max-width:640px){
  /* The bar is 80px on a phone, not 118 (JP, 4 Oct). 118 was sized for the lockup at 90px on
     a desktop; on a 375px phone it took a seventh of the screen before any content. The lockup
     stays at the 60px it already was here. Everything that offsets against the bar - anchor
     scroll-margin, the drawer's top padding, the page heads - reads this token and follows. */
  :root{--section-y:64px; --header-h:80px;}
  .wrap{padding:0 24px;}
  /* A lighter type step on phones (JP, 5 Oct: the hero text felt too big). Measured at 390: every
     hero headline was 28.8px over two or three lines, a full sentence where other lodge sites
     put two or three words. The hero comes down to 26.4px, and the section headings (27.2) and
     the closing headings (33.6) come down with it, so the hero is still the largest text on the
     page. Body text is unchanged. The `body` in the hero rule out-ranks each page's own
     `.hero h1` size, which sits later in the cascade. */
  :root{--h-lead:1.65rem; --h-major:1.5rem;}
  /* The hero headline was 1.65rem with a 12em measure (the French had run two long lines to
     the edge). In the logo's capitals (17 Oct) it runs about a third wider: 1.32rem, and
     no measure cap, so text-wrap:balance sets the breaks. */
  body .hero h1{font-size:1.32rem; max-width:none;}
  /* The same size as a photo hero's (JP, 20 Oct: every page's opening headline the same). */
  body .page-head h1, body .legal h1, body .notfound h1{font-size:1.32rem;}
}

/* ---------- header ---------- */
header{
  position:fixed; top:0; left:0; right:0; z-index:100; height:var(--header-h);
  display:flex; align-items:center; background:var(--paper);
  border-bottom:1px solid var(--line);
}
.header-inner{
  width:100%; max-width:var(--maxw); margin:0 auto; padding:0 32px;
  display:flex; align-items:center; justify-content:space-between; gap:24px;
}
@media (max-width:640px){.header-inner{padding:0 24px;}}
.brand{font-family:var(--font-head); font-weight:300; font-size:24px; text-decoration:none; letter-spacing:0.08em; flex-shrink:0;}
.header-right{display:flex; align-items:center; gap:12px;}
/* The starter's horizontal-nav rules for nav.links used to sit here. Every property in them
   was overridden by the drawer rules further down, so they did nothing for the menu links -
   and once the drawer grew a foot (languages, WhatsApp) their `nav.links a` reached into it
   and out-ranked `.lang a`. Removed 4 Oct; the drawer rules are the only ones. */
.menu-toggle{
  display:none; background:none; border:none; font-size:22px; cursor:pointer;
  color:var(--ink); padding:4px 6px; line-height:1;
}

/* ---- language switcher --------------------------------------------------------------
   Two links with a middot between them, not a dropdown. Three tries at a <details> panel
   and it was never right: the panel needs an open animation a <details> cannot do on its
   own, clicking the summary put a text caret in the middle of the word, and the whole thing
   is machinery for choosing between two things.
   The footer already writes "Mentions légales · Confidentialité" exactly like this, so this
   is the site's own idiom rather than a new one. Both languages are visible without
   clicking, which is the point of a language switcher, and there is nothing left to animate
   badly. It takes a third item without changing.
   The current language is not a link: it is where you already are. */
.lang{display:flex; align-items:center; gap:9px; min-height:44px; margin:0;
      font:500 14px/1 var(--font-body); letter-spacing:.14em; text-transform:uppercase;}
.lang__sep{opacity:.32;}
.lang__on{color:inherit; opacity:1;}
/* 13px of vertical padding takes the link to a 44px tap target without a flex box, which
   matters: the underline is positioned against the CONTENT box, so if the link were a 44px
   flex row with the text centred the rule would land eleven pixels under the word. */
/* 15px of padding takes a 14px line to a 44px target. Three things move on hover and they
   are deliberately small: the word comes up to full strength, the rule draws in from the
   left the way every other link on the site does, and the whole thing lifts a single pixel.
   One pixel is the same amount .btn:active drops by, so the vocabulary is the site's. */
.lang a{color:inherit; opacity:.5; text-decoration:none; padding:15px 10px; margin:0 -4px;
        background-image:linear-gradient(currentColor, currentColor);
        background-repeat:no-repeat; background-origin:content-box;
        background-size:0 1px; background-position:0 100%;
        transition:opacity .34s ease, background-size .34s cubic-bezier(.3,.7,.2,1),
                   translate .2s ease;}
.lang a:hover, .lang a:focus-visible{opacity:1; background-size:100% 1px; translate:0 -1px;}
@media (prefers-reduced-motion:reduce){ .lang a{transition:opacity .2s ease;}
                                        .lang a:hover{translate:none;} }
/* On a phone the switcher is not in the bar at all (see "the bar on a phone" below): three
   languages, Book and the menu beside her lockup came to more than a 375px screen holds, and
   in French and Italian the menu button was pushed off the right edge - measured 4 Oct at
   x 344-388 on a 375 screen. The squeeze that used to live here (no separators, 12.5px, below
   400) was the previous attempt at fitting it and is gone with it.
   The same three links now also sit in the drawer foot and the footer, at every width. There
   they are on a plain ground, so the faded-link idiom of the bar (opacity .5, which measures
   3.0:1 on ivory) gives way to --ink-soft at full strength, 5.75:1 on ivory and 4.62 on the
   footer's sand, with the current language
   in --ink. The current one gets the links' own side room so the row starts on the text. */
.lang--drawer .lang__on, .lang--footer .lang__on{padding:0 6px; color:var(--ink);}
.lang--drawer a, .lang--footer a{opacity:1; color:var(--ink-soft);}
.lang--drawer a:hover, .lang--drawer a:focus-visible,
.lang--footer a:hover, .lang--footer a:focus-visible{color:var(--ink);}
.lang--drawer, .lang--footer{margin-left:-6px;}

/* The old phone menu block that lived here (a full-width panel under the bar) was replaced
   by the drawer further down, which overrides it everywhere except one property: its
   `inset` set left:0, and with right:0 and a width as well the browser drops `right`, so
   from 641 to 860px the drawer opened on the LEFT, over the logo, away from the button
   that opened it. Measured 4 Oct (drawer at x0-384 on a 768 screen). Removed; its tap
   padding moved down to the drawer rules. */


/* ---- the button ------------------------------------------------------------------------
   Measured off the two reference sites rather than invented. Tsara Komba: 15px/300 uppercase,
   padding 15px 40px, 53px tall, zero radius, no tracking. Constance: 13px sentence case,
   1px border, 54px tall, zero radius, tracking 0.13px. Neither uses tracked uppercase for a
   call to action, which is what .rule-link does - that idiom stays for FR and Book in the bar.
   Sentence case follows Constance, the quieter of the two. Zero radius was already the rule. */
/* The fill rises rather than switching on. Same grammar as the rest of the page: the frame
   stays exactly where it is and only what is inside it moves. Nothing scales, nothing bounces.
   It is an animated background-size rather than a pseudo-element - a `::before` at z-index:-1
   painted OVER the label and the button came up blank, and a background always paints behind
   text with no stacking to reason about. */

/* ---------- buttons ----------
   One button, two weights, and the fill is LIGHT.

   It used to fill with ink and flip the label to cream, which is the gesture the header's BOOK
   had too. Measured on the real button: the label holds --ink until 245ms while the fill covers
   it from about 150ms, then crosses mid-greys until 480ms - so roughly 300ms where the label is
   ink on ink and simply gone. JP saw it without being told to look for it, which is the whole
   argument. Crossfading the label in step with the fill was tried before that and is worse, for
   the same reason: a bottom-up fill means the label straddles both grounds at once, so no single
   label colour works during the crossing.

   So the fill stops being dark. --shell is a deeper sand, the label never changes colour at all,
   and ink sits at 12.5:1 on paper and 10.9:1 on shell - legible at every frame of the
   transition, by construction rather than by timing. Zero radius, like everything else. */
.btn{
  display:inline-flex; align-items:center; justify-content:center;
  min-height:52px; padding:0 30px; border-radius:0; cursor:pointer;
  background-color:transparent; color:var(--ink); border:1px solid var(--line);
  font-family:inherit; font-size:14.5px; font-weight:500; letter-spacing:0.03em;
  line-height:1.3; text-decoration:none;
  text-align:center;   /* a label that wraps on a phone stays centred, not ragged left (24 Oct) */
  box-shadow:inset 0 0 0 0 var(--shell);
  /* No `color` in here on purpose: the label is a fixed colour now, and a transition on it is
     what the old version needed only because the ground went dark underneath it.
     `translate`, not `transform`, for the press: the reveal animation owns transform on
     anything inside a .reveal-groupe and the two would fight. */
  transition:box-shadow .5s cubic-bezier(.2,.6,.2,1),
             border-color .4s ease, translate .14s ease;
}
/* One fill for every button on the site, Book included: the ground rises from the bottom
   edge. Same colour, same curve, same duration everywhere, so two buttons side by side
   behave identically - which they did not before. */
.btn:hover, .btn:focus-visible{box-shadow:inset 0 -80px 0 0 var(--shell); border-color:var(--ink);}
.btn:active{translate:0 1px;}

/* The only difference between the two weights is how loud the outline is at rest: ink for the
   action a page is actually asking for, the pale rule for the one standing beside it. Gold was
   measured for this job and fails - #A98A55 on --paper is 2.95:1, under the 3:1 a UI boundary
   needs, so it cannot be the only thing marking a button's edge. */
.btn-primary{border-color:var(--ink);}
/* --line is the hairline colour and measures 1.05-1.30:1 against every ground on the site.
   That is not a quiet outline, it is an invisible one, and JP could not see these buttons at
   all. --ink-soft is 4.62:1 on the worst ground and 5.75 on the best: clearly a button, still
   quieter than the primary. Gold was measured for this job too and fails at 2.47-3.07. */
.btn-secondary{border-color:var(--ink-soft);}

/* Over a photograph the label is white, so the fill is a dark wash rather than white - filling
   with white would hide a white label exactly the way the ink fill hid the ink one. */
.btn--light{color:#fff; border-color:rgba(255,255,255,.85);
            box-shadow:inset 0 0 0 0 rgba(20,24,22,.62);}
.btn--light:hover, .btn--light:focus-visible{box-shadow:inset 0 -80px 0 0 rgba(20,24,22,.62);
            color:#fff; border-color:#fff;}
/* Gold is 2.47:1 on a photograph; the focus ring has to be white to be seen. */
.btn--light:focus-visible{outline:2px solid #fff; outline-offset:3px;}

/* Phones have no hover, so without :active a tap gives no feedback at all.
   Keep these after the :hover rules - same specificity, so order decides. */
.btn:active{translate:0 1px; box-shadow:inset 0 -80px 0 0 var(--shell);}
.btn--light:active{box-shadow:inset 0 -80px 0 0 rgba(20,24,22,.62);}
.btn:disabled{opacity:.55; cursor:default; translate:none;}
.btn:disabled:hover{box-shadow:none; border-color:var(--line);}

/* ---------- text links in copy ----------
   Underlined at rest, and on hover the rule leaves to the right and a new one arrives from the
   left. Two background layers rather than a border, because each end has to animate
   independently; the staged delays are what make it read as one line travelling rather than
   two lines crossfading. It runs the same way back out, so the gesture is symmetrical.

   :where() holds the specificity at zero so any page can still override it, and :not([class])
   is what keeps it off the buttons - every button on this site carries a class, and every link
   in running copy carries none. */
/* The underline travels left to right: the line that is there retracts to the right, and a
   new one draws in from the left behind it. Two gradients, opposite anchors, staggered.
   background-origin:content-box is what keeps it under the TEXT. Without it the gradient is
   positioned against the padding box, and any link given vertical padding to reach a 44px
   tap target - "Good to know" on the contact page - drew its underline 32px below the word,
   measured, which is why it read as detached. */
:where(main) :where(p, li, dd, td) a:not([class]), .u-link{
  color:inherit; text-decoration:none;
  /* padding-bottom is not cosmetic: a background is clipped to the border box, so drawing
     the rule below the content box with no padding there puts it outside the paint area and
     it vanishes entirely - which is exactly what happened when the padding came out. The
     padding makes room inside the box; background-origin:content-box is what keeps the rule
     under the TEXT when a link has more padding than this for a tap target. */
  padding-bottom:3px;
  background-image:linear-gradient(currentColor, currentColor),
                   linear-gradient(currentColor, currentColor);
  background-repeat:no-repeat;
  background-origin:content-box;
  background-size:0 1px, 100% 1px;
  background-position:0 100%, 100% 100%;
  transition:background-size .3s cubic-bezier(.3,.7,.2,1);
  transition-delay:.26s, 0s;
}
:where(main) :where(p, li, dd, td) a:not([class]):hover,
:where(main) :where(p, li, dd, td) a:not([class]):focus-visible,
.u-link:hover, .u-link:focus-visible{
  background-size:100% 1px, 0 1px;
  transition-delay:.26s, 0s;
}
@media (prefers-reduced-motion:reduce){
  .btn{transition:none;}
  .btn:active{translate:none;}
  :where(main) :where(p, li, dd, td) a:not([class]), .u-link{transition:none;}
}

/* ---------- the shared chrome: header, brand, Book, the drawer ----------
   This lived in eight private copies, one per page, ~116 identical lines each. Two defects came
   straight out of that in one week: the Book button's hover was fixed on one page and stayed
   broken on four, and the footer tap-target rule existed on four pages and was missing from the
   four where those links are the only ones. Every change was eight edits and the eighth was the
   one that got forgotten. One copy now. A page adds only what is unique to its own sections. */

/* Rules Lenis expects when it is driving the wheel. Inert unless it starts, because the
   .lenis class is only ever added by the library itself. */
html.lenis, html.lenis body{height:auto;}
.lenis.lenis-smooth{scroll-behavior:auto !important;}
.lenis.lenis-smooth [data-lenis-prevent]{overscroll-behavior:contain;}
.lenis.lenis-stopped{overflow:hidden;}
.lenis.lenis-smooth iframe{pointer-events:none;}

/* ---- header: menu left, wordmark centred, language and Book right ---------------------
   Alfajiri's composition, locked in DIRECTION.md. The eight tabs live in the overlay
   behind the toggle rather than in a row — a horizontal nav cannot hold eight and stay
   quiet, and the overlay was already the approved pattern. */
header{position:fixed; top:0; left:0; right:0; z-index:100; height:var(--header-h);
       background:rgba(252,248,241,.88); border-bottom:1px solid var(--line); color:var(--ink);
       backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
       transition:background .6s cubic-bezier(.2,.6,.2,1), border-color .6s ease, color .6s ease,
                  transform .55s cubic-bezier(.3,0,.2,1);}
header.is-hidden{transform:translateY(-101%);}
/* No scrim of its own. It used to paint one - 40% at the top, fading out by the foot of the
   bar - because white nav over a bright sky measures 1.2-1.8:1 on the bare photograph. But
   every hero on this site ALREADY carries a dark wash at its top, so the bar's gradient was a
   SECOND one stacked on the first: 62% black at the top of index and excursions, 77% on lodge
   and about, with a visible edge 84px down where the shorter of the two ended. JP, 22 Sept:
   the band is what he was seeing.
   Measured on the brightest 5% of the strip the bar actually sits over, white text:
     lodge 5.86:1 and about 5.83:1 on their hero wash ALONE - the bar's gradient was doing
     nothing there at all, on a still photograph that never changes.
     index and excursions are FILM, and that is the trap: measured against their poster they
     needed 44% and 55%, but the poster is one frame. Sampling both films at 6fps and taking
     the brightest frame - a white sky at f085 and f044 - the bare film is 1.47:1 and 1.17:1
     and they need 50% and 56%. Shipped at 53% and 59%, three points of headroom for frames
     between samples, giving 4.94:1 and 5.05:1.
   So: one soft ramp over a quarter of the hero instead of two with different falloffs, every
   page lighter at the top than it was, every page over 4.5:1.
   If a new page gets a hero, give it its own top wash and measure it - and if the hero is a
   film, measure the FILM, not the poster. Do not bring this rule's gradient back. */
header.is-clear{background:none;
                /* width:0, not just colour:transparent. A transparent 1px border over a
                   gradient background still rendered as a visible dark hairline right
                   across the photograph - proved by isolation: removing EITHER the border
                   or the gradient made it vanish. Zero width removes it for good. */
                border-bottom-width:0; border-bottom-color:transparent; color:#fff;
                backdrop-filter:none; -webkit-backdrop-filter:none;}
/* The panel runs the full height, so it passes behind the bar. Rather than flipping the
   whole bar to ivory - which hid the film and was the thing JP objected to - only the close
   button, which always sits over the panel, goes dark. Language and Book are hidden while
   the drawer is open: once IT joined EN and FR the group grew wider than the panel, so EN
   sat over the film while FR, IT and Book sat over ivory, and no one colour reads on both
   grounds (JP, 4 Oct). The drawer is the menu; nothing in it needs them. */
header.is-menu-open .header-left{color:var(--ink);}
.header-right{transition:opacity .3s ease, visibility 0s;}
header.is-menu-open .header-right{opacity:0; visibility:hidden; pointer-events:none;
                                  transition:opacity .2s ease, visibility 0s .2s;}
/* Logo left, everything else right - the reference site's composition. Flex rather than the
   old three-column grid, because the brand is no longer the centred item: margin-right:auto
   on it pushes the controls to the far edge, and `order` puts the menu last without touching
   the markup, which keeps the toggle's z-index trick below intact. */
.header-inner{display:flex; align-items:center; gap:clamp(16px,2.5vw,34px);
              height:100%; max-width:var(--maxw); margin:0 auto; padding:0 clamp(20px,3vw,32px);}
/* The overlay menu is a child of .header-inner, so its z-index:90 paints it above its own
   siblings — including the toggle that closes it. Lifting the controls above the overlay is
   what keeps the menu escapable. Caught by elementFromPoint on the toggle returning the nav. */
.header-left, .brand, .header-right{position:relative; z-index:95;}
.header-left{display:flex; align-items:center; order:3;}
.header-right{display:flex; align-items:center; gap:28px; order:2; margin-left:auto;}
.brand{order:1; color:inherit; text-decoration:none; display:inline-flex; align-items:center;}

.menu-toggle{display:block; font-size:0; width:44px; height:44px; position:relative;
             margin-left:-9px; color:inherit; background:none; border:0; cursor:pointer;}
.menu-toggle span{position:absolute; left:9px; width:26px; height:1px; background:currentColor;
                  transition:transform .45s cubic-bezier(.2,.6,.2,1), opacity .3s ease, top .45s ease;}
.menu-toggle span:nth-child(1){top:15px;} .menu-toggle span:nth-child(2){top:22px;} .menu-toggle span:nth-child(3){top:29px;}
.menu-toggle[aria-expanded="true"] span:nth-child(1){top:22px; transform:rotate(45deg);}
.menu-toggle[aria-expanded="true"] span:nth-child(2){opacity:0;}
.menu-toggle[aria-expanded="true"] span:nth-child(3){top:22px; transform:rotate(-45deg);}

/* ---- the bar on a phone ----------------------------------------------------------------
   80px tall (the token is overridden up top), her lockup at 60px, Book and the menu, and
   nothing else: the languages leave the bar below 640 and live in the drawer foot and the
   footer instead. The gutter is 24px like every other section on a phone - the bar was at
   20, because the clamp() on .header-inner came later than the old 24px rule and won.
   The menu button's lines sit 9px inside its 44px box. With the button on the left that was
   cancelled by margin-left:-9px; it is on the right now, so the -9px moves to the right and
   the lines end on the gutter, level with the page's text. The box itself stays 15px clear
   of the screen edge. */
@media (max-width:640px){
  .header-inner{padding:0 24px;}
  header .lang{display:none;}
  .menu-toggle{margin-left:0; margin-right:-9px;}
}

.rule-link{font:500 12.5px/1 var(--font-body); letter-spacing:.2em; text-transform:uppercase;
           text-decoration:none; color:inherit; display:inline-flex; align-items:center;
           min-height:44px; padding:0 4px; transition:opacity .4s ease;}
/* Fills with --ink, not --gold, and matches .btn-secondary in base.css, which is the
   site's other outlined button. Gold-on-gold with cream type was muddy - JP's word was
   ugly - and it measured 4.6:1; ink with cream type is 12.5:1 and reads as a deliberate
   state rather than a wash. The transition has to name box-shadow or the fill snaps,
   which is what it was doing on every page but the homepage. */
/* Same light fill as .btn in base.css, and for the same measured reason: filling with ink
   under an ink label left the word BOOK invisible for about 300ms. The label now never
   changes colour at all. */
/* Book is the primary action and the only gold on the chrome, so on hover it goes gold:
   the accent wipes in from the LEFT, the same travel the language links and every text link
   on the site use, rather than the ground rising from the bottom like the quieter .btn does.
   The label never changes colour - on the solid bar it is ink, and ink on champagne
   measures 4.60:1.
   Over a hero header.is-clear turns the whole bar white, and white on champagne is only
   3.25, so there the same wipe uses --gold-deep (5.21) and the two rules are white too. */
.book{padding:0 18px; border-top:1px solid var(--gold); border-bottom:1px solid var(--gold);
      background-image:linear-gradient(var(--gold), var(--gold));
      background-repeat:no-repeat; background-size:0 100%; background-position:0 0;
      transition:background-size .8s cubic-bezier(.22,.7,.2,1),
                 border-color .8s ease, translate .14s ease;}
.book:hover, .book:focus-visible{background-size:100% 100%;}
.book:active{translate:0 1px;}
@media (prefers-reduced-motion:reduce){ .book{transition:background-size .01s, border-color .2s;} }
/* Over a hero the label is white, so the fill is the deeper gold rather than the dark
   wash it used to be - JP saw that wash and read it as the button not going gold at all.
   Same gesture, same direction, same duration; only the shade changes, so the white label
   keeps 5.21:1 instead of 3.25. Measured hovering in a real browser, not headless: the bar
   transitions `color` over .6s and headless virtual time never advances a transition, so a
   computed-style read there reports the label as ink and is simply wrong.
   The rules are white at rest here, not gold: over the film every other mark in the bar -
   the logo, the nav, EN/FR/IT, the label inside this button - is #fff, and a gold outline
   among them read as an odd one out rather than as the accent. JP, 22 Sept. So the frame
   stays put on hover and only the ground moves, which is the cleaner gesture anyway. The
   border still transitions gold <-> white over .8s as you scroll past the hero. */
header.is-clear .book{background-image:linear-gradient(var(--gold-deep), var(--gold-deep));
                      border-top-color:#fff; border-bottom-color:#fff;}
header.is-clear .book:hover, header.is-clear .book:focus-visible{background-size:100% 100%;}
.rule-link:hover{opacity:.7;}

/* ---- the menu: a drawer from the right, not a full overlay ----------------------------
   JP, 18 Sept: it should slide in and cover about half the page, so the site stays visible
   behind it. With four links a full-screen takeover was far heavier than the job needed. */
/* The panel comes from the side its button is on. The toggle moved to the right when the
   logo took the left, so the drawer follows it - sliding in from the opposite edge to the
   control that opened it reads as two unrelated things. */
/* Centred when the links fit, scrolling from the top when they do not. A grid with
   align-content:center overflows at BOTH ends: with seven links on a 548px-tall phone the
   first sat behind the bar and the last below the screen, with nothing to scroll (measured
   4 Oct, the day the seventh link arrived). Auto margins on the first and last link centre
   them when there is room and collapse to nothing when there is not.
   The margins are on the first and last LINK, not the first and last child, because the
   panel ends in .drawer-foot (languages, WhatsApp). With the auto margin on the last link,
   the links centre in the room above the foot and the foot sits on the bottom of the panel;
   on a short screen every auto margin is zero and the whole column scrolls, foot included. */
nav.links{position:fixed; top:0; right:0; bottom:0; width:min(50%, 420px); z-index:90;
          background:var(--paper); border-left:1px solid var(--line);
          display:flex; flex-direction:column; align-items:flex-start;
          gap:clamp(4px, calc((100svh - 644px) / 9), 36px);
          overflow-y:auto; overscroll-behavior:contain;
          padding:var(--header-h) clamp(28px,4vw,56px) 40px;
          transform:translateX(100%); visibility:hidden;
          transition:transform .5s cubic-bezier(.2,.6,.2,1), visibility 0s .5s;}
nav.links > a:first-of-type{margin-top:auto;}
nav.links > a:last-of-type{margin-bottom:auto;}
/* The gap between the links is whatever the screen has room for, up to the spacing as
   designed. Nine links since 24 Oct (Prices), and at a fixed 26px they no longer fitted: the
   foot (languages, WhatsApp) ended 72px below a 1440x900 screen, 172px below 1440x800 and 44px
   below a 390x844 phone. In (100svh - 644px) / 9, 644 is the rest of the panel on a desktop
   as measured that day - the bar, nine rows at line-height 1.4, the foot, the bottom padding
   (a phone's is 20px less, so phones fit with room over) - and 9 is the number of gaps. From
   968px tall it gives the full 36px, which with the tighter line height is the same space
   between words as before (48px); below that the rhythm closes just enough to fit, down to
   4px, under which the panel scrolls. svh, not vh: on an iPhone it is measured with the
   toolbars showing. Add a link and re-measure the 644. */
@media (max-height:700px){
  nav.links{padding-bottom:16px;}
}
nav.links.open{transform:none; visibility:visible; transition:transform .5s cubic-bezier(.2,.6,.2,1);}
/* `> a`, never `nav.links a`: the big serif is for the menu links only. Written as a
   descendant rule it reached the language links and the WhatsApp line in the foot as well,
   and out-ranked .lang's own type. */
nav.links > a{font-family:var(--font-head); font-weight:300; font-size:clamp(1.4rem,2.6vw,1.9rem);
              line-height:1.4; letter-spacing:.04em; color:var(--ink); text-decoration:none;
              transition:color .3s ease;}
nav.links > a:hover{color:var(--gold);}
/* Marks the page you're on. site.js sets this from the URL - don't hand-write class="active"
   in the markup, it gets forgotten when a page is added later. It is a rule, not a colour:
   gold on --paper measures 3.07:1, fine for a graphic and under 4.5 for words, so the rule
   carries the colour and the word stays ink. */
nav.links > a.active{color:var(--ink); position:relative;}
nav.links > a.active::after{content:""; position:absolute; left:0; right:0; bottom:-6px;
                            height:1px; background:var(--gold);}
/* Below 860 the links are 22px type, which makes a row shorter than the 44px a thumb wants;
   the padding makes up the difference without moving the text. */
@media (max-width:860px){ nav.links > a{padding:6px 0;} }

/* The foot of the drawer: the languages, which leave the bar on a phone, and the WhatsApp
   number (JP, 4 Oct). Every page and every width, so the drawer is the same thing everywhere. A hairline across the panel separates it from the
   menu; nothing in it is set in the serif, because it is not the menu. */
.drawer-foot{align-self:stretch; display:flex; flex-direction:column; align-items:flex-start;
             padding-top:12px; border-top:1px solid var(--line);}
.drawer-foot__wa{display:inline-flex; align-items:center; min-height:44px;
                 font:400 15px/1.3 var(--font-body); color:var(--ink-soft); text-decoration:none;
                 transition:color .3s ease;}
.drawer-foot__wa:hover, .drawer-foot__wa:focus-visible{color:var(--ink);}

/* On a phone the panel is full width, because at 430px a 360px drawer would sit under the
   logo as well as the controls - measured, they overlap from x77. Full width means the whole
   bar is over ivory, so all of it goes dark including the mark. Its side padding is the
   phone gutter, so the menu starts on the same line as the lockup above it. The 12px over the
   bar's height is for the screens where the column has to scroll (320x568: 52px too tall,
   measured) - without it the first link sat flush against the bar's hairline. */
@media (max-width:640px){
  nav.links{width:100%; border-left:0; padding:calc(var(--header-h) + 12px) 24px 40px;}
  header.is-menu-open{color:var(--ink);}
  header.is-clear.is-menu-open .brand-logo{filter:none;}
}
/* When that column scrolls (320x568 by 64px, 360x740 by 33px, measured), the links ran up
   behind the lockup and the close button: the panel is painted under them, and the bar over a
   hero has no ground of its own, so "Accueil" sat on top of MADABAY. While the menu is open the
   bar gets an ivory ground with its hairline - the solid bar every inner page already has - in
   a layer between the panel (z 90) and the lockup and close button (z 95). It fades in with the
   panel's slide rather than snapping, the same way the bar turns solid when you scroll past a
   hero. Phones only: on a desktop the drawer is half the screen and the bar stays clear over
   the film (JP's call, see "No scrim of its own" above). */
@media (max-width:640px){
  header::before{content:""; position:absolute; top:0; left:0; right:0; z-index:92;
                 height:var(--header-h); box-sizing:border-box;
                 background:var(--paper); border-bottom:1px solid var(--line);
                 opacity:0; visibility:hidden; transition:opacity .45s ease, visibility 0s .45s;}
  header.is-menu-open::before{opacity:1; visibility:visible; transition:opacity .45s ease;}
}

/* ---------- layout helpers ---------- */
.grid-3{display:grid; grid-template-columns:repeat(3,1fr); gap:36px; margin-top:44px;}
@media (max-width:860px){.grid-3{grid-template-columns:1fr; gap:30px;}}
/* ---------- the split ----------
   The site's other full-bleed device: no gutter, no gap, align-items:stretch, a coloured block
   on one side and the photograph running clean off the window edge on the other. What used to
   sit here was a gapped two-column row that no page ever used - four pages each shadowed it
   with this, and the dead one underneath was a trap waiting for someone to trust it. */
.split{display:grid; grid-template-columns:1.12fr 0.88fr; align-items:stretch;
       background:var(--panel);}
.split__media{position:relative; overflow:clip; min-height:clamp(480px, 58svh, 640px);}
.split__media img{width:100%; height:100%; object-fit:cover; display:block;}
.split__body{display:grid; align-content:center; padding:clamp(44px, 6.5vw, 108px);}
.split__body > *{max-width:46ch;}
.split__body .btn{justify-self:start;}
.split h2{margin-bottom:20px;}
.split p{color:var(--ink-soft);}
.split--reversed{grid-template-columns:0.88fr 1.12fr;}
.split--reversed .split__media{order:2;}
/* Below 860 it stacks. min-height back to 0, or it drives the WIDTH through the aspect ratio -
   that bug has already cost this project a 653px-wide section inside a 390px viewport. */
@media (max-width:860px){
  .split{grid-template-columns:1fr;}
  .split__media{min-height:0; aspect-ratio:8/5;}
  .split--reversed .split__media{order:0;}
  .split__body{padding:clamp(40px,8vw,64px) 24px;}
}
@media (max-width:860px){.split{grid-template-columns:1fr; gap:36px;}}
.card h3{margin-bottom:8px;}
.card p{color:var(--ink-soft); font-size:16px; margin:0;}

.page-head{max-width:var(--maxw); margin:0 auto; padding:calc(var(--header-h) + 72px) 32px 20px;}
@media (max-width:640px){.page-head{padding:calc(var(--header-h) + 48px) 24px 16px;}}
.page-head p{color:var(--ink-soft);}
/* Every page's headline - the hero's, the page head's, or the own heads of legal, privacy
   and the 404 - in the logo's lettering, in capitals (17 Oct). Capitals come from CSS: the
   markup stays in sentence case, so screen readers and search engines read words. `body`
   out-ranks each page's own .hero h1. */
body .hero h1, body .page-head h1, body .legal h1, body .notfound h1{
  font-family:var(--font-display); font-weight:400;
  text-transform:uppercase; letter-spacing:.08em; line-height:1.2;}
/* Capitals carry more weight than the same size in lower case, and run about a third wider:
   at the old sizes the inside heroes set 44px over three or four lines and the page heads
   52px. Smaller, with a wider measure, so most settle on two lines.
   Above 640 only: the phone sizes are set in the phone block near the top of this file,
   which comes earlier in the cascade and would otherwise lose to these. */
@media (min-width:641px){
  body .hero h1{font-size:clamp(1.5rem,2.4vw,2.25rem); max-width:21em;}
  /* One size for every page's opening headline, photo hero or not (JP, 20 Oct). The page heads
     used to be a step bigger, 38px against 35 at 1440. */
  body .page-head h1, body .legal h1, body .notfound h1{font-size:clamp(1.5rem,2.4vw,2.25rem);}
}

/* ---------- the photo hero ----------
   One full-screen photograph with the page's headline at its foot: The lodge, About, Our
   causes and Travel. Until 20 Oct each of those pages carried its own identical copy of
   these rules (six copies, counting the two film heroes), so a change to one hero had to be
   made in all of them. A page now writes class="hero hero--photo" and sets only what is its
   own: the wash over its photograph (measured per photograph - see each page's comment),
   where the photograph is anchored, and any line of its own (the lodge's facts, the causes
   ask). The homepage and excursions heroes are films with their own entrance and are NOT
   on this class.
   `.hero` stays on the element as well: site.js clears the header over any `.hero`. */
.hero--photo{position:relative; min-height:100svh; display:grid; place-items:end center;
             text-align:left; isolation:isolate; background:#2E4A52; color:#fff;
             overflow:clip;}   /* clip, never hidden: hidden would make the hero its own scroll
                                  container and the drift's scroll timeline would never move */
.hero--photo .hero__media{position:absolute; inset:0; width:100%; height:100%;
                          object-fit:cover; z-index:-2;}
/* The wash itself is the page's: `.hero::after{background:...}`. */
.hero--photo::after{content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;}
.hero--photo .hero__inner{width:100%; max-width:var(--maxw); margin:0 auto;
                          padding:0 clamp(20px,3vw,32px) clamp(54px,9vh,104px);}
.hero--photo h1{margin:0; color:#fff;}
.hero--photo .hero__sub{margin:20px 0 0; max-width:46ch; color:rgba(255,255,255,.92);
                        text-shadow:0 1px 16px rgba(0,0,0,.4);}
/* The frame holds, the picture drifts. Declared as a `to` only, so the resting state at
   scroll 0 is transform:none and the hero is complete with no script and under reduced motion. */
@supports (animation-timeline: scroll()){
  @media (prefers-reduced-motion: no-preference){
    .hero--photo .hero__media{animation:hero-drift linear both;
                              animation-timeline:scroll(root); animation-range:0 100svh;}
  }
}
@keyframes hero-drift{ to{ transform:scale(1.06) translateY(3%); } }

/* ---------- the load-in, and the load-out ----------
   One panel doing two jobs. On arrival it fades away; on leaving for another page on this site
   it fades back in, and the next page's own fade-out picks up where it left off - so a click
   between pages reads as one continuous move rather than a white flash.

   It lives in base.css because every page needs it. It began on the homepage only, which is
   why navigating anywhere else used to snap.

   The exit half is script; the arrival half is not. That ordering matters: with no script the
   curtain still clears itself on load, and links still navigate normally - they just cut
   rather than fade. Nothing here can leave a visitor looking at a blank panel. */
.curtain{position:fixed; inset:0; z-index:300; background:var(--paper); display:grid;
         place-items:center; pointer-events:none;
         animation:curtain 1.3s cubic-bezier(.4,0,.2,1) forwards;}
/* Her mark, not the name set in type. The panel is --paper, so this is the gold version at
   its own colour - no filter, unlike the header where it sits over the film.
   The width is 184px, not a round 230: the PNG is cropped tight to the artwork, so the CSS
   width IS the visible width. Change the crop and this number has to move with it. */
.curtain__logo{width:min(184px, 41.5vw); height:auto; display:block;
               animation:curtain-name 1.3s ease forwards;}
@keyframes curtain{ 0%,45%{opacity:1; visibility:visible} 100%{opacity:0; visibility:hidden} }
@keyframes curtain-name{ 0%{opacity:0; transform:translateY(6px)} 30%,60%{opacity:1; transform:none} 100%{opacity:0} }
/* Leaving. The class overrides the arrival animation rather than racing it. */
.curtain.is-leaving{animation:none; opacity:1; visibility:visible;
                    transition:opacity .42s cubic-bezier(.4,0,.2,1);}
.curtain.is-leaving .curtain__logo{animation:none; opacity:1; transform:none;}
@media (prefers-reduced-motion:reduce){
  .curtain{display:none;}
}

/* ---------- the brand logo ----------
   Her full lockup - chameleon, MADABAY, LODGE - left-aligned and given real size, which is how
   the reference site does it: the logo alone, no wordmark set beside it. Hers already contains
   the name, so unlike theirs nothing is lost by dropping the text.

   An <img> rather than a CSS mask. A mask is the tidier mechanism and recolours with
   currentColor, but it is fetched by the stylesheet, and a stylesheet-fetched file is blocked
   when the page is opened straight off disk - which is how this gets looked at. Inlining the
   lockup would have put 87KB of base64 into a render-blocking stylesheet. An image element is
   not blocked, and the one state that needs a different colour is handled by a filter. */
.brand-logo{display:block; width:auto; height:90px;
            transition:filter .45s cubic-bezier(.2,.6,.2,1), opacity .45s ease;}
/* Over the film everything in the bar is white. brightness(0) flattens the gold to black and
   invert(1) lifts it to pure white, which is exactly the state the drawn mark used to take. */
header.is-clear .brand-logo{filter:brightness(0) invert(1);}
@media (max-width:860px){ .brand-logo{height:74px;} }
@media (max-width:640px){ .brand-logo{height:60px;} }

/* ---------- footer ---------- */
/* Sand, not near-black. --ink's own note in the LOCK reads "type, never a large block", and
   --shell is commented "deeper sand: the footer" but was used nowhere. Measured on --shell:
   --ink 11.37:1, --ink-soft 4.62:1, both clear. It also shrinks the iPhone Safari toolbar
   mismatch: the bars take the body colour, so a near-black footer under an ivory body left a
   pale band that no CSS on the page can close. Sand against ivory is 20 levels, not 200. */
footer{background:var(--shell); color:var(--ink-soft); padding:56px 0 28px;}
.footer-grid{
  max-width:var(--maxw); margin:0 auto; padding:0 32px 34px;
  display:grid; grid-template-columns:1.4fr 1fr 1fr; gap:40px;
  /* --line is 1.05:1 on shell, i.e. invisible. Ink at 14% instead. */
  border-bottom:1px solid rgba(44,38,30,0.14);
}
@media (max-width:760px){.footer-grid{grid-template-columns:1fr; gap:32px;}}
/* The column heads were 15px Red Rose 300 against 14px body - a 1.07x step in the lighter of
   the two faces, so "On this site" read as the first item in its own list. Eyebrow style gives
   them a job without making them loud. */
footer h2{color:var(--ink); margin:0 0 18px;
          font:500 11.5px/1 var(--font-body); letter-spacing:.26em; text-transform:uppercase;}
footer p, footer a{font-size:15px; line-height:1.9; color:var(--ink-soft); text-decoration:none;}
footer a{transition:color .3s ease;}
footer a:hover{color:var(--ink);}
/* Three items since 4 Oct: the copyright, the languages, the legal links. align-items:center
   rather than the default stretch, because the items are not the same height - the language
   links are 44px targets on a touch screen - and stretch put each one's text at a different
   height. It also settles the copyright line, which sat 4px above the legal links. */
.footer-bottom{
  max-width:var(--maxw); margin:0 auto; padding:22px 32px 0; font-size:13px;
  display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:10px;
  color:var(--ink-soft);
}
/* Photo and film credit, same tier as the copyright line. --ink-soft measures 4.62:1 on
   --shell, which clears 4.5. */
.footer-credit{
  max-width:var(--maxw); margin:0 auto; padding:8px 32px 0; font-size:13px;
  color:var(--ink-soft);
}

/* .footer-grid, .footer-bottom and .footer-credit each carry their own max-width and 32px
   gutter, from when they were direct children of <footer>. Every page now wraps them in
   footer > .wrap, which already supplies both - so the gutter was applied twice and the whole
   footer sat 32px to the right of the page's own content. Measured at 1400 across all eight
   pages: content started at x=112, footer content at x=144.
   Neutralised here rather than by unwrapping in eight files, and scoped to the nested case so
   a page that still uses the old flat markup is untouched. */
footer > .wrap > .footer-grid,
footer > .wrap > .footer-bottom,
footer > .wrap > .footer-credit{max-width:none; padding-left:0; padding-right:0;}

/* Footer links are 17px tall, under the 24px short-side minimum for a tap target, and on the
   legal pages and the 404 they are the only links on the page. This lived in four page <style>
   blocks and was therefore missing from the other four - about, legal, privacy and 404, which
   are exactly the pages where it matters most. It is chrome, so it belongs here. */
.footer-bottom a, footer p a{display:inline-block; padding:4px 0;}
/* ---- touch targets in the footer ---------------------------------------------------------
   Measured at 375 and 320: every footer link came out 37px tall against the 44px a thumb
   wants. Padding on its own does not fix it here. The links are inline-block separated by
   <br>, so their padding bleeds into the neighbouring line box rather than pushing it away,
   and at 8px two adjacent targets would overlap - a tap near the join would open the wrong
   page, which is worse than a small target. Opening the line-height is what makes the room.
   Mobile only: on desktop there is a pointer and 37px was the approved spacing. */
@media (max-width:860px){
  footer .footer-grid p{line-height:3;}
  footer .footer-grid p a{padding:8px 0;}
  .footer-bottom p{line-height:2.9;}
  .footer-bottom a{padding:8px 6px;}
}
/* The tall line is for rows of links. The address is a paragraph with no link in it, and it
   was getting the same 45px line, so its two lines read as two separate items. It keeps the
   footer's own 1.9. Written as :not(:has(a)) so that a browser without :has() drops only this
   rule and keeps the tall rows for the links - the safe way round. */
@media (max-width:860px){
  footer .footer-grid p:not(:has(a)){line-height:1.9;}
}

/* The language row in the footer: the footer's small type, not the bar's. Same idiom as the
   bar - three words and two middots - at the 13px of the line it sits in, in the footer's
   own --ink-soft with the current language in --ink. Its bottom margin matches the other
   two paragraphs, so align-items:center lines all three up by their middles. The links are
   24px tall with a pointer and 44px wherever a thumb might use them (860 and below, the
   same line every other footer tap target uses). The side padding stays .lang's 10px less 4,
   so the row's first word sits on its edge (see .lang--footer's margin above). */
.footer-bottom .lang{font-size:13px; line-height:16px; min-height:0; margin-bottom:18px;}
.footer-bottom .lang a{padding:4px 10px; font-size:inherit; line-height:inherit;}   /* not footer a's 15/1.9 */
@media (max-width:860px){ .footer-bottom .lang a{padding:14px 10px;} }
/* On a phone the three no longer fit one line (about 410px of them in 327), and wrapped they
   landed two at the ends of one row and the third alone under the first. Stacked instead,
   one per row, in reading order, each row its own 44px. */
@media (max-width:640px){
  .footer-bottom{flex-direction:column; align-items:flex-start; gap:0;}
  .footer-bottom p, .footer-bottom .lang{margin-bottom:0;}
  /* The legal links carry 6px of side padding for their tap area, which in a left-aligned
     stack put "Legal" 6px right of the copyright above it. Pulled back onto the edge. */
  .footer-bottom p:not(.lang) a:first-child{margin-left:-6px;}
}
/* On a desktop the language row sits on the true centre of the footer, under the centred band
   above it. With space-between it sat wherever the widths of the two outer items put it: 11px
   right of centre at 1440 in English, measured. minmax(max-content,1fr) means an outer item
   never wraps - if one outgrows half the room, the middle moves over instead of "Mentions
   légales · Confidentialité" breaking onto two lines. Above 860 only: narrower than that, the
   centred row ended up 6px from the legal links in French at 641 (measured), and the evenly
   spaced row is the better answer there. */
@media (min-width:861px){
  .footer-bottom{display:grid; grid-template-columns:minmax(max-content,1fr) auto minmax(max-content,1fr);}
  .footer-bottom > :last-child{justify-self:end;}
}

/* ---------- the phone bar, and the WhatsApp button ----------
   Phones only (JP, 4 Oct): WhatsApp, and "send your dates" to the form on the contact page,
   in reach without scrolling back up. On index, lodge, excursions, travel, about and causes
   - not on contact, which is the form, nor on legal, privacy or the 404.
   Ivory with a hairline, not ink: iPhone Safari tints its toolbar from what sits at the
   bottom edge, and the body is ivory, so an ink bar would turn the toolbar dark with it.
   site.js adds .is-shown once the hero (or the page head) has scrolled away and takes it off
   again when the footer arrives, so the bar never sits over the end of the page, and while
   the menu is open. With no script it simply never appears; nothing on the page needs it.
   The two buttons are the site's own: zero radius, 1px border, sentence case, no tracking -
   the WhatsApp one filled with ink (ivory on ink is 14.1:1), the form one in the quiet
   outline of .btn-secondary. The contact page's full-width WhatsApp button is the same
   button, so it is styled here once rather than twice. */
.phone-bar{display:none;}
@media (max-width:640px){
  .phone-bar{position:fixed; left:0; right:0; bottom:0; z-index:80;
             display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:8px;
             padding:8px 24px calc(8px + env(safe-area-inset-bottom, 0px));
             background:var(--paper); border-top:1px solid var(--line);
             transform:translateY(100%); visibility:hidden;
             transition:transform .45s cubic-bezier(.2,.6,.2,1), visibility 0s .45s;}
  .phone-bar.is-shown{transform:none; visibility:visible;
                      transition:transform .45s cubic-bezier(.2,.6,.2,1);}
}
/* The buttons sit on the 24px phone gutter, so their outer edges line up with the text, the
   pictures and the lockup above - at 12px they stood 12px proud of everything on the page.
   Below 360 the gutter gives way to 12px: at 320 a 24px gutter leaves each button 132px, and
   "Envoyez vos dates" needs 138 to stay on one line (measured). */
@media (max-width:359px){
  .phone-bar{padding-left:12px; padding-right:12px;}
}
/* 8px of side padding, not more: at 320 each button is 144px, and "Envoyez vos dates" is
   122px of type - at 10px a side it broke onto two lines. Should a longer label ever arrive,
   it wraps balanced inside the 48px rather than overflowing. */
.phone-bar a, .wa-first__btn{
  display:flex; align-items:center; justify-content:center; gap:9px;
  min-height:48px; padding:0 8px; border:1px solid var(--ink); border-radius:0;
  font:500 15px/1.2 var(--font-body); letter-spacing:0; text-align:center; text-wrap:balance;
  text-decoration:none;
  transition:background-color .3s ease, border-color .3s ease, translate .14s ease;}
.phone-bar__wa, .wa-first__btn{background:var(--ink); color:var(--paper);}
.phone-bar a.phone-bar__dates{background:transparent; color:var(--ink); border-color:var(--ink-soft);}
.wa-first__btn{min-height:52px;}   /* the height of every other .btn on the site */
.phone-bar svg{width:18px; height:18px; flex:none;}
.wa-first__btn svg{width:20px; height:20px; flex:none;}
/* No hover on a phone, so the press is the only feedback: the same 1px drop as .btn, the
   outline filling with --shell as .btn does, and the ink easing to --ink-soft (ivory on it
   is still 5.75:1). */
.phone-bar a:active, .wa-first__btn:active{translate:0 1px;}
.phone-bar__wa:active, .phone-bar__wa:hover,
.wa-first__btn:active, .wa-first__btn:hover{background:var(--ink-soft); border-color:var(--ink-soft);}
.phone-bar a.phone-bar__dates:active, .phone-bar a.phone-bar__dates:hover{background:var(--shell);
                                                                        border-color:var(--ink);}
@media (prefers-reduced-motion:reduce){
  .phone-bar, .phone-bar.is-shown{transition:none;}
  .phone-bar a, .wa-first__btn{transition:none;}
  .phone-bar a:active, .wa-first__btn:active{translate:none;}
}


/* ---------- arriving on scroll ----------
   `.reveal` is one element arriving on its own. `.reveal-groupe` is a container
   whose own children stagger in, 70ms apart - right for a heading and its line,
   or a row of items. Long stacks want `.reveal` on each item instead: a stagger
   that starts at the first of eight finishes long before you scroll to the last.

   Everything is gated on `.anime`, which the page's head script grants and takes
   back if site.js never claims it. Without that gate a script that fails to load
   leaves the page permanently blank - content must never depend on an animation
   running to be readable.

   This arrives by ANIMATION, not by transition, and that is deliberate. The transition
   version declared `transition:opacity,transform` on `.anime .reveal-groupe.in-view > *`,
   which is three classes - so it silently replaced the `transition` of every element inside
   a reveal group, permanently. A button's own hover transition was wiped and its fill
   switched on instead of rising, with no transitionrun event to show why. An animation
   cannot collide with an element's transitions, so any child is free to have its own. */
/* OFF since 20 Oct, on every page (JP, thumb test: the text under the lodge hero should "be
   there already"). Scrolling into a section showed blank space and then the words fading up,
   and on a phone that read as the page arriving late. Nothing waits to appear now: text and
   pictures are simply there. The classes stay in the markup and site.js still marks each one
   in-view, so turning this back on is these rules and nothing else - the old version was
   `.anime .reveal, .anime .reveal-groupe > *{opacity:0}` plus a 1.1s rise on `.in-view`,
   staggered .12s per child (see git history before 20 Oct). */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto;}
}

/* ---------- focus visibility (keyboard users) ---------- */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible{
  outline:2px solid var(--accent); outline-offset:2px;
}

/* ---- the lightbox: tap a photograph to see it large --------------------------------------
   The starter's snippets/lightbox.js. Moved here from gallery.html on 24 Oct, when the lodge's
   3x3 grid got it too (JP): any container marked data-lightbox. The cue is the gallery's -
   a zoom cursor and, with a mouse, the picture easing in 3% inside its frame. */
[data-lightbox] figure{cursor:zoom-in; overflow:clip;}
[data-lightbox] figure:focus-visible{outline:2px solid var(--gold); outline-offset:3px;}
/* The homepage islets open only as the phone grid, never while pinned (see site.js). */
[data-lightbox="unpinned"]:not(.is-live) picture{cursor:zoom-in;}
[data-lightbox="unpinned"]:not(.is-live) picture:focus-visible{outline:2px solid var(--gold); outline-offset:3px;}
@media (hover:hover){
  [data-lightbox] img{transition:transform .9s cubic-bezier(.2,.6,.2,1);}
  [data-lightbox] figure:hover img{transform:scale(1.03);}
}
html.lightbox-open{overflow:hidden;}
.lightbox{position:fixed; inset:0; z-index:300; display:flex; align-items:center;
          justify-content:center; padding:24px; background:rgba(20,24,22,.95);}
.lightbox-inner{width:100%; max-width:1400px;}
.lightbox img{display:block; margin:0 auto; max-width:100%; max-height:86vh; object-fit:contain;}
.lightbox-close, .lightbox-prev, .lightbox-next{position:fixed; min-width:44px; min-height:44px;
          padding:10px; border:0; background:none; color:rgba(252,248,241,.86); cursor:pointer;
          font:300 34px/1 var(--font-body); transition:color .2s ease;}
.lightbox-close:hover, .lightbox-prev:hover, .lightbox-next:hover{color:#fff;}
.lightbox-close{top:16px; right:20px;}
.lightbox-prev{left:10px; top:50%; transform:translateY(-50%); font-size:44px;}
.lightbox-next{right:10px; top:50%; transform:translateY(-50%); font-size:44px;}
@media (max-width:640px){
  .lightbox{padding:12px;}
  .lightbox img{max-height:74vh;}
  .lightbox-prev, .lightbox-next{top:auto; bottom:18px; transform:none;}
  .lightbox-prev{left:calc(50% - 64px);} .lightbox-next{right:calc(50% - 64px);}
}
