/* ============================================================
   PARC DEWI SANT — DESIGN TOKENS
   Swap the four brand values below to match the official logo.
   ============================================================ */
:root{
  /* Slate, lichen and brass. The estate is a Grade II Victorian asylum of
     1865 in 38 acres of parkland — stone, iron and trees, not a tech park.
     So the greens are deepened out of the "eco brand" register and the
     neutrals are warmed off the old cold mint toward limestone.

     CONTRAST IS LOAD-BEARING HERE. Every value below is measured against
     both grounds it is used on; the ratio is in the comment. Do not nudge
     one without re-measuring — the palette this replaces failed AA on every
     eyebrow, tag, route number and directory index on the site.          */
  --ink:      #0B211A;   /* slate, green cast     16.84:1 white on it     */
  --moss:     #0E5C41;   /* PRIMARY   7.59 on paper · 6.53 on stone       */
  --moss-2:   #17795A;   /* lighter green, hovers + rules · 5.09 on paper */
  --brass:    #7E5A1C;   /* ACCENT, Victorian brass · 5.92 paper·5.09 stone */
  --brass-lt: #C79A4A;   /* brass for DARK grounds ONLY · 6.54 on --ink.
                            2.4:1 on --paper — never put this on a light band */
  --stone:    #EBE8E0;   /* warm weathered-stone panel                    */
  --paper:    #FAF9F5;   /* warm paper                                    */
  --muted:    #52625A;   /* 6.13 on paper · 5.27 on stone                 */
  --line:     rgba(11,33,26,.13);   /* dividers — decorative, no minimum   */
  --line-str: rgba(11,33,26,.26);   /* stronger dividers                    */
  /* Controls whose ONLY affordance is their outline (filter chips, ghost
     buttons) need 3:1 against the page under SC 1.4.11. --line-str gives
     1.74:1; this gives 3.45:1. Use it for the edge of anything pressable. */
  --line-ctl: rgba(11,33,26,.52);

  /* Figtree is the brand typeface and Arial the specified substitute, so it
     keeps the display, body and reading roles.

     --f-mono does NOT. It was Figtree too, which made the token a lie: every
     price, building number, spec strip and letterspaced label on the site
     claims a monospaced register and got a proportional face, so nothing in
     the numeric column ever lined up. It is now a real mono, and that is
     where the site's typographic contrast comes from — one brand face for
     the voice, one machine face for the data.                            */
  --f-disp: 'Figtree', Arial, system-ui, sans-serif;
  --f-body: 'Figtree', Arial, system-ui, sans-serif;
  --f-read: 'Figtree', Arial, system-ui, sans-serif;
  --f-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;

  --wrap: 1220px;
  --gut: clamp(20px, 5vw, 64px);

  --r:     18px;    /* panels, cards, media                        */
  --r-sm:  11px;    /* small surfaces                              */
  --pill:  999px;   /* anything you press                          */
  --bar-h: 80px;    /* the floating bar: its height and the air above */
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--f-body);font-size:16.5px;line-height:1.6;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block}
a{color:inherit}
button{font:inherit;color:inherit}

.wrap{max-width:var(--wrap);margin:0 auto;padding-inline:var(--gut)}

/* ---------- shared type ---------- */
.eyebrow{
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--moss);
  margin:0 0 16px;display:flex;align-items:center;gap:11px;
}
/* the brass rule is the one warm mark on a green page; 22px reads as a tick,
   not a divider, so it sits with the label rather than separating anything */
.eyebrow::before{content:"";width:22px;height:2px;background:var(--brass);flex:none}

/* Sentence case, not caps. Every heading on the site used to be set in 800
   uppercase, which is the one thing that made a 38-acre Victorian estate read
   like a software landing page: caps flatten the ascenders and descenders that
   carry word-shape, so nothing had a silhouette and everything shouted at the
   same volume. Caps are now reserved for the small letterspaced registers that
   earn them — the eyebrow, the spec strip, buttons, the mono data.       */
.h-sec{
  font-family:var(--f-disp);font-weight:700;
  font-size:clamp(2rem,4.4vw,3.4rem);line-height:1.04;
  letter-spacing:-.032em;margin:0 0 20px;text-wrap:balance;
}
.h-sub{
  font-family:var(--f-disp);font-weight:700;
  font-size:clamp(1.18rem,2.2vw,1.5rem);line-height:1.16;
  letter-spacing:-.018em;margin:0 0 9px;text-wrap:balance;
}
.lede{
  font-family:var(--f-read);font-size:clamp(1.06rem,1.75vw,1.28rem);
  font-weight:400;line-height:1.55;letter-spacing:-.011em;
  color:#22302A;max-width:58ch;margin:0 0 20px;text-wrap:pretty;
}
p{margin:0 0 16px}
.muted{color:var(--muted)}

/* Title-block: the architectural spec strip used across the site */
.spec{
  font-family:var(--f-mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);font-variant-numeric:tabular-nums;
  display:flex;flex-wrap:wrap;gap:8px 0;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:12px 0;margin:0 0 34px;
}
.spec span{padding-right:18px;margin-right:18px;border-right:1px solid var(--line)}
.spec span:last-child{border-right:0;margin-right:0;padding-right:0}
.spec b{color:var(--ink);font-weight:500}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;gap:9px;
  font-family:var(--f-body);font-size:12px;font-weight:600;
  letter-spacing:.11em;text-transform:uppercase;text-decoration:none;
  padding:14px 24px;border:1px solid var(--moss);background:var(--moss);color:#fff;
  border-radius:var(--pill);
  cursor:pointer;transition:background .18s,border-color .18s,color .18s,transform .18s;
}
.btn:hover{background:var(--ink);border-color:var(--ink);transform:translateY(-1px)}
.btn:active{transform:none}
.btn--ghost{background:transparent;color:var(--moss);border-color:var(--line-ctl)}
.btn--ghost:hover{background:var(--moss);border-color:var(--moss);color:#fff}
.btn--light{background:transparent;color:#fff;border-color:rgba(255,255,255,.5)}
.btn--light:hover{background:#fff;color:var(--ink);border-color:#fff}
.btn::after{content:"→";font-family:var(--f-body);font-size:14px;line-height:1}

/* --brass, and deliberately: it is the only hue on the site that is neither
   the page nor the brand green, so a focus ring can never be mistaken for a
   hover or an active nav item. 5.92:1 on --paper, 5.09 on --stone, and it
   carries its own outline colour onto the dark bands too.               */
:where(a,button,input,select,summary,details,[tabindex]):focus-visible{
  outline:2px solid var(--brass);outline-offset:3px;border-radius:2px
}
/* Over a photograph or a pane of dark glass a single hairline can land on any
   luminance the picture happens to have, so on those grounds the ring is two
   parts: the brass line plus a dark halo behind it. The halo guarantees the
   line's own contrast (brass-lt measures 4.99:1 against it over sunlit grass)
   instead of gambling on the backdrop.

   The header and the full-screen menu are in this list because neither is a
   descendant of .hero — .site-head floats above it and .navmask is its
   sibling — so both were falling through to the light-ground rule. */
.band--ink :where(a,button):focus-visible,
.band--shot :where(a,button):focus-visible,
.cta-band :where(a,button):focus-visible,
.site-foot :where(a,button):focus-visible,
.site-head.over :where(a,button):focus-visible,
.navmask :where(a,button):focus-visible,
.hero :where(a,button):focus-visible{
  outline-color:var(--brass-lt);
  box-shadow:0 0 0 5px rgba(4,16,12,.72)
}

/* ---------- header ----------
   The bar is the same object on every page: a pill of glass floating clear of
   whatever is beneath it. Over the hero it is dark glass with pale letters,
   everywhere else light glass with ink ones. The shape never changes — only
   the key — so leaving the hero reads as a change of light, not of furniture. */
.site-head{
  position:fixed;top:0;left:0;right:0;z-index:90;
  padding:clamp(10px,1.3vh,16px) clamp(14px,1.8vw,26px) 0
}
.head-in{
  /* brand · nav · language · book. The switch is a fourth cell, not a fifth
     child crammed into three columns — that pushed the CTA onto a row of its
     own and out of the bar. The burger is display:none above 940px, so it
     takes no cell there. */
  display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;gap:18px;
  height:64px;max-width:var(--shell,1500px);margin:0 auto;padding:0 10px 0 20px;
  /* .80 was mixed when this bar only ever floated over --paper. It now floats
     over the .band--shot photographs too, where the glass composites down to
     L=0.62 and drops --muted nav links to 4.18:1 — under AA for 11.5px. At .92
     the bar holds L=0.81 and the links measure 5.27:1, and the backdrop-filter
     still reads as glass. */
  background:rgba(251,252,250,.92);
  border:1px solid rgba(11,33,26,.10);
  border-radius:var(--pill);
  box-shadow:0 12px 34px rgba(11,33,26,.10);
  backdrop-filter:blur(20px) saturate(165%);-webkit-backdrop-filter:blur(20px) saturate(165%);
  transition:background .45s, border-color .45s, box-shadow .45s
}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none}
.brand img{width:34px;height:34px;object-fit:contain}
.brand-tx{display:flex;flex-direction:column;line-height:1}
.brand-tx b{
  font-family:var(--f-disp);font-weight:800;font-size:15.5px;
  letter-spacing:-.01em;text-transform:uppercase
}
.brand-tx i{
  font-family:var(--f-mono);font-style:normal;font-size:9.5px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted);margin-top:4px
}
.nav{display:flex;align-items:center;gap:4px}
.nav a{text-decoration:none;display:inline-block;
  background:none;border:0;cursor:pointer;padding:9px 13px;
  font-family:var(--f-body);font-size:11.5px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  border-bottom:2px solid transparent;transition:color .16s,border-color .16s;
}
.nav a:hover{color:var(--ink)}
.nav a.is-on{color:var(--moss);border-bottom-color:var(--brass)}
.head-cta{margin-left:14px;padding:11px 18px}
.burger{display:none;background:none;border:1px solid var(--line-str);padding:9px 12px;cursor:pointer;
  font-family:var(--f-body);font-size:11px;letter-spacing:.12em;text-transform:uppercase}

@media(max-width:940px){
  .burger{display:block}
  .head-cta{display:none}
  .nav{
    position:absolute;top:70px;left:0;right:0;background:var(--paper);
    border-bottom:1px solid var(--line);flex-direction:column;align-items:stretch;
    padding:8px var(--gut) 20px;gap:0;display:none;
  }
  .nav.open{display:flex}
  .nav a{text-decoration:none;display:inline-block;text-align:left;padding:15px 0;border-bottom:1px solid var(--line);border-left:0}
  .nav a.is-on{border-bottom-color:var(--brass)}
}

/* ---------- page switching ---------- */
@keyframes fade{from{opacity:0;transform:translateY(7px)}to{opacity:1;transform:none}}

/* ---------- generic band ---------- */
.band{padding:clamp(56px,8vw,104px) 0}
.band--stone{background:var(--stone)}
.band--ink{background:var(--ink);color:#fff}
.band--ink .h-sec,.band--ink .lede{color:#fff}
.band--ink .eyebrow{color:rgba(255,255,255,.7)}
.band--ink .spec{border-color:rgba(255,255,255,.2);color:rgba(255,255,255,.62)}
.band--ink .spec span{border-color:rgba(255,255,255,.15)}
.band--ink .spec b{color:#fff}
.band--tight{padding:clamp(40px,5vw,64px) 0}

/* ---------- split ---------- */
.split{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,68px);align-items:center}
@media(max-width:860px){.split{grid-template-columns:1fr}}
/* The 4/3.4 cover crop is for PHOTOGRAPHS, and it is opt-IN via .photo.
   As a blanket `.split figure img` rule it also matched the estate plan —
   a diagram — and silently sliced 16.8% off its sides on two separate
   pages, taking the building directory with it. Scoping it here means the
   next drawing dropped into a .split is correct by default rather than
   needing another override. */
.split figure.photo{margin:0;background:var(--stone);border:1px solid var(--line);
  border-radius:var(--r);overflow:hidden}
.split figure.photo img{width:100%;height:auto;aspect-ratio:4/3.4;object-fit:cover;display:block}

/* The estate is the product, so where a photograph IS the argument the column
   split stops being even: the picture takes the larger half and runs taller
   than the copy beside it. Opt-in, so the plain 1:1 .split is untouched. */
.split--showcase{grid-template-columns:minmax(0,.88fr) minmax(0,1.12fr);align-items:center}
.split--showcase figure.photo img{aspect-ratio:4/3.15}
.split--showcase figure.photo{box-shadow:0 24px 60px rgba(11,33,26,.13)}
@media(max-width:860px){.split--showcase{grid-template-columns:1fr}}

/* ---------- route cards ---------- */
.routes{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(12px,1.4vw,18px)}
@media(max-width:820px){.routes{grid-template-columns:1fr}}
.route{
  background:var(--paper);padding:clamp(24px,3vw,36px);
  border:1px solid var(--line);border-radius:var(--r);
  display:flex;flex-direction:column;text-decoration:none;
  transition:background .2s, box-shadow .3s, border-color .3s;
}
.route:hover{background:#fff;border-color:var(--line-str);
  box-shadow:0 18px 44px rgba(11,33,26,.10);transform:translateY(-3px)}
.route{transition:background .2s, box-shadow .3s, border-color .3s, transform .3s}
/* a category label, not an index — see Home.tsx */
.route .rn{
  font-family:var(--f-mono);font-size:11px;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--brass);margin-bottom:20px
}
.route p{color:var(--muted);font-size:15px;margin-bottom:22px}
.route span.go{
  margin-top:auto;font-family:var(--f-body);font-size:11.5px;font-weight:600;letter-spacing:.1em;
  text-transform:uppercase;color:var(--moss);display:inline-flex;gap:8px;align-items:center
}

/* ---------- unit / space cards ---------- */
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(16px,2.2vw,26px)}
/* four rooms at the foot of a room page: four divides by two and by one, so no
   breakpoint leaves an orphan the way three-across would */
.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(14px,1.8vw,22px)}
@media(max-width:1000px){.grid-4{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.grid-4{grid-template-columns:1fr}}
.grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(16px,2.2vw,26px)}
@media(max-width:900px){.grid-3{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.grid-3,.grid-2{grid-template-columns:1fr}}

/* Every room card ends in the same ruled line carrying the verb. The button is
   gone: a pill repeated once per card was the loudest thing on the page and
   carried no information, so the whole card is the link now (.card-foot::after
   covers it) and the footer says what the click does. Keyboard users get the
   frame via :focus-within, since the anchor's own box is only the footer.

   No lift, no drop shadow on hover either. The state change is precise
   instead: the rule darkens, the photograph opens up, the verb underlines. */
.card{
  position:relative;
  border:1px solid var(--line);background:var(--paper);
  border-radius:var(--r);overflow:hidden;
  display:flex;flex-direction:column;transition:border-color .25s
}
.card:hover{border-color:var(--line-str)}
.card:focus-within{outline:2px solid var(--moss);outline-offset:3px}
.card-img{position:relative;background:var(--stone);overflow:hidden}
/* Six photographs of six rooms have six different brightnesses exactly where
   the label sits. The scrim settles them, so the plate needs no drop shadow of
   its own to stay legible and the picture grounds into the card instead of
   stopping dead at the caption. */
.card-img::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top,rgba(11,33,26,.34),rgba(11,33,26,0) 44%)
}
.card-img img{width:100%;height:auto;aspect-ratio:5/4;object-fit:cover;
  transition:transform .6s cubic-bezier(.2,.6,.2,1)}
/* height:auto is load-bearing, exactly as it is on .mapfig: the derivatives
   carry width/height attributes to reserve the box, and without it the
   browser honours height="608" literally and aspect-ratio never applies. */
.card:hover .card-img img{transform:scale(1.04)}

/* The category names the PHOTOGRAPH, so it sits on it — a specimen label on a
   plate, rather than a third line of type stacked above the room's name. */
/* The category was set at 10px mono and disappeared into the photograph — the
   estate could not see what kind of room it was looking at. Same badge, read
   at a glance: bigger, heavier, in the brand green rather than brass, and on
   an opaque plate so it holds over any part of the image. */
.card-cat{
  position:absolute;left:10px;bottom:13px;z-index:1;margin:0;   /* 10 + 12 of padding = the body's 22 */
  background:var(--paper);border-radius:var(--r-sm);padding:8px 14px 7px;
  font-family:var(--f-mono);font-size:12px;font-weight:600;letter-spacing:.13em;
  text-transform:uppercase;color:var(--moss);
  box-shadow:0 2px 10px rgba(3,15,10,.14)
}
.card-cat--plain{position:static;background:none;box-shadow:none;padding:0;margin-bottom:12px}

.card-body{padding:20px 22px 20px;display:flex;flex-direction:column;flex:1}
.card-body .h-sub{margin-bottom:9px}
.card-body p{font-size:14.8px;line-height:1.6;color:var(--muted);margin:0}

.card-foot{
  display:flex;align-items:center;gap:9px 16px;flex-wrap:wrap;
  margin-top:auto;padding:14px 22px 15px;
  border-top:1px solid var(--line);text-decoration:none;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase
}
.card-foot::after{content:"";position:absolute;inset:0}   /* the card is the link */
.card-go{
  color:var(--moss);display:inline-flex;align-items:center;gap:7px;
  border-bottom:1px solid transparent;padding-bottom:2px;transition:border-color .25s
}
.card-go::after{content:"→";transition:transform .25s}
.card:hover .card-go{border-bottom-color:rgba(14,92,65,.45)}
.card:hover .card-go::after{transform:translateX(3px)}

/* The odd one out, and the section's one field of colour. It has no room to
   show, so an empty tile the colour of the band read as a gap in the grid
   rather than the invitation it is. Moss also puts the brand back into a
   section that lost all of it when the five green buttons went — once, in the
   last cell, where the row ends. White on --moss measures 8.15:1; --brass-lt
   is only 3.16:1 on it, so the label stays white and does not go brass. */
.card--ask{background:var(--moss);border-color:var(--moss);color:#fff}
.card--ask .card-body{justify-content:flex-end;padding:34px 24px 22px}
.card--ask .h-sub{color:#fff;font-size:clamp(1.5rem,2.3vw,1.95rem);line-height:1.07;letter-spacing:-.026em}
.card--ask .card-cat{color:rgba(255,255,255,.74)}
.card--ask .card-body p{color:rgba(255,255,255,.86)}
.card--ask .card-foot{border-top-color:rgba(255,255,255,.24)}
.card--ask .card-go{color:#fff}
.card--ask:hover{border-color:var(--ink);background:var(--ink)}
.card--ask:hover .card-go{border-bottom-color:rgba(255,255,255,.5)}
.card--ask{transition:background .25s,border-color .25s}
@media(prefers-reduced-motion:reduce){
  .card-img img,.card-go,.card-go::after{transition:none}
}

.tag{
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--brass);margin-bottom:11px
}
.price{
  font-family:var(--f-mono);font-size:12.5px;color:var(--ink);font-variant-numeric:tabular-nums;
  border-top:1px solid var(--line);padding-top:14px;margin-top:auto;margin-bottom:16px
}
.price b{color:var(--moss);font-weight:500}
.card .btn{align-self:flex-start}
/* .price used to carry margin-top:auto to pin the card footer; where a listing
   card has no price line the button takes over, so CTAs stay baseline-aligned
   across a row. Scoped to :has(.card-img) because the prompt card ("Talk to us
   first") has no photo and centres its content instead — an auto margin there
   silently defeats its justify-content and strands the button. */
.card:has(.card-img) .card-body:not(:has(.price)) .btn{margin-top:auto}

/* ---------- a room's own page ----------
   The head is the site's usual .band--shot, so a room arrives as the same kind
   of page as everything else. What travels with the click is the plate: the
   little paper label on the card, at page scale, over the same room. One
   device at two sizes is what makes opening a card read as an opening rather
   than a jump.

   The calendar is built like the estate plan and the logo wall — a bordered
   block with a mono strip along its foot — because it is the same kind of
   thing: an artifact the page frames rather than a widget dropped into it.
   The strip is also where the way out lives. Skedda sends no frame-ancestors
   so the embed works, but an embed can always fail, and "open in Skedda" must
   not be something a visitor has to go hunting for.                        */
.room-back{
  display:flex;width:fit-content;align-items:center;gap:9px;margin-bottom:20px;
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;letter-spacing:.15em;
  text-transform:uppercase;color:rgba(255,255,255,.82);text-decoration:none;
  transition:color .2s
}
.room-back::before{content:"\2190"}
.room-back:hover{color:#fff}
.room-plate{
  display:inline-block;margin:0 0 17px;
  background:var(--paper);border-radius:var(--r-sm);padding:7px 13px 6px;
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;letter-spacing:.15em;
  text-transform:uppercase;color:var(--brass)
}
.room-lede{max-width:54ch}
.room-offsite{margin:clamp(26px,3.4vw,38px) 0 0}

.cal{
  margin:clamp(26px,3.4vw,38px) 0 0;background:#fff;
  border:1px solid var(--line-str);border-radius:var(--r);overflow:hidden
}
.cal__frame{
  display:block;width:100%;border:0;background:var(--stone);
  /* a booking grid needs real height; vh so a laptop is not left scrolling a
     tall frame inside a tall page */
  height:clamp(540px,78vh,880px)
}
.cal figcaption{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px 22px;
  border-top:1px solid var(--line);padding:13px 18px;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--muted)
}
.cal__open{margin-left:auto;color:var(--moss);font-weight:500;text-underline-offset:4px}
@media(max-width:620px){.cal__open{margin-left:0}}

/* ============================================================
   SPACE TO BREATHE — a tenant's own page
   ============================================================
   The one page on the site that does not wear the estate's colours. Space to
   Breathe is a tenant with an identity of its own, a sage arch on cream, and
   it lets rooms on how they feel; arriving into slate and moss would have sold
   the wrong thing.

   It is done by re-declaring the tokens on .stb, so every component inside —
   bands, cards, notices, buttons, rules — recolours from the same switch and
   nothing needed a second implementation. --paper is the cream baked into the
   logo file itself, so the mark lands on the page with no visible plate.

   The TYPE does not change. Figtree and IBM Plex Mono as everywhere else: the
   tenant's voice is carried by their mark, their colour and their rooms, and a
   borrowed display serif only made this page look like a different website.

   MEASURED ON #F1EFE7: --ink 14.63:1, --moss 6.95:1, --muted 5.61:1,
   --brass 5.42:1. All four are the estate's own values, kept because all four
   still pass on this ground. --sage is the logo's mark at 2.39:1 and is
   DECORATIVE ONLY — rules, washes, the arch. Never put text in it.        */
.stb{
  --paper:#F1EFE7;
  --stone:#E5E2D2;
  --sage:#9F9D87;
  --line:rgba(74,76,56,.18);
  --line-str:rgba(74,76,56,.32);
  /* The action colour shifts with the ground. Emerald was the one note on this
     page that argued with the sage, and green still means "press" — the button
     keeps its shape, only its hue moves. MEASURED: 7.14:1 on the cream, 6.32:1
     on the panel, and white on it 8.22:1, so it clears AA in both directions
     wherever the estate's --moss did. */
  --moss:#41543A;
  --moss-2:#5A6E4B;   /* 4.84:1 on the cream — rules and hovers */
  background:var(--paper)
}
/* the wordmark's widest gesture is its tracking; the small labels echo it */
.stb .eyebrow{letter-spacing:.26em}
.stb .eyebrow::before{background:var(--sage);width:26px}

/* ---- head ----
   No photograph behind it. The carousel is the picture, and a second image
   washed out under the words would only have competed with it. */
/* ---- head ----
   The hero is composed TO the viewport, not allowed to overflow it. The header
   takes the height it needs and the strip takes the rest, so the rooms are
   whole and on screen the moment the page opens — which is the entire point of
   showing seven of them. Below 900px it goes back to flowing normally: a
   stacked headline, lede, price and button leave nothing worth squeezing a
   photograph into.

   svh, not vh: on a phone vh is the tallest the viewport ever gets, so the
   strip would sit under the browser's own chrome until you scrolled. */
.stb-head{
  background:var(--paper);
  padding-top:calc(var(--bar-h) + clamp(14px,1.8vw,22px));
  padding-bottom:clamp(20px,2.4vw,30px)
}
@media(min-width:901px){
  /* height, not min-height: min-height lets the section grow past the viewport
     to fit its contents, which is exactly what was pushing the rooms under the
     fold. A definite height makes the strip shrink into whatever the header
     leaves. min-height keeps a very short window from crushing it — past that
     the page simply scrolls. */
  /* min() caps it: on a tall desktop window a pure 100svh hero turned the
     strip into a wall of photographs three storeys high. */
  .stb-head{height:min(100svh,940px);min-height:620px;display:flex;flex-direction:column}
  .stb-head>.wrap{flex:0 0 auto}
}
.stb-back{
  display:flex;width:fit-content;align-items:center;gap:9px;
  margin-bottom:clamp(12px,1.6vw,18px);
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;letter-spacing:.2em;
  text-transform:uppercase;color:var(--muted);text-decoration:none;transition:color .2s
}
.stb-back::before{content:"\2190"}
.stb-back:hover{color:var(--ink)}

/* Title on one side, the case for it on the other. */
.stb-hero{
  display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:clamp(24px,4vw,64px);
  margin-top:clamp(10px,1.4vw,18px)
}
@media(max-width:900px){.stb-hero{grid-template-columns:1fr;gap:26px}}
/* ---- the sign ----
   Their mark hangs from a crook, and it is drawn in outlined letterforms that
   need room: at 98px of pale sage on cream it was a watermark, not a logo. So
   it gets a plate, a size worth reading, and a ruled label naming the place —
   a sign on the wall rather than a picture floating on paper.

   The plate is --paper, not white. The logo file bakes its own cream behind
   the wordmark, and on a white card that block shows as a rectangle; matched
   to the page it disappears, and the card is held by its edge and its shadow
   instead. Laid out sideways because stacked it cost 120px of a hero whose
   whole job is to leave room for the rooms. */
.stb-sign{
  display:inline-flex;align-items:center;gap:clamp(13px,1.5vw,18px);
  background:var(--paper);border:1px solid var(--line-str);border-radius:var(--r-sm);
  padding:clamp(11px,1.3vw,15px) clamp(15px,1.7vw,20px);
  box-shadow:0 16px 40px rgba(74,76,56,.13);
  margin-bottom:clamp(14px,1.8vw,22px)
}
.stb-sign img{display:block;width:clamp(74px,6.2vw,92px);height:auto}
/* Two deliberate lines, not a wrapped sentence: at this tracking the label
   broke into five ragged ones. What it is, then where it is. */
.stb-sign span{
  display:flex;flex-direction:column;gap:6px;
  border-left:1px solid var(--line);padding-left:clamp(13px,1.5vw,18px);
  font-family:var(--f-mono);font-size:9.5px;font-weight:500;letter-spacing:.17em;
  line-height:1.2;text-transform:uppercase;color:var(--muted);white-space:nowrap
}
.stb-sign b{font-weight:500;color:var(--ink)}
.stb-h1{margin:0;max-width:13ch}
/* The offer sits as one group, centred against the sign and the headline
   beside it. Pinned to the foot instead it left a well of empty page between
   the lede and the price. */
.stb-hero{align-items:stretch}
.stb-hero__aside{
  display:flex;flex-direction:column;justify-content:center;
  padding-top:clamp(4px,1vw,10px)
}
.stb-lede{
  margin:0;max-width:44ch;color:var(--muted);
  font-family:var(--f-read);font-size:clamp(1.02rem,1.5vw,1.14rem);
  line-height:1.62;letter-spacing:-.009em;text-wrap:pretty
}
/* The rate is the pitch on their own page, so it is set as data: figures in the
   machine face, the words around them small. */
.stb-rate{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:8px;
  margin:clamp(20px,2.4vw,26px) 0 0;
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted)
}
.stb-rate b{
  font-size:23px;font-weight:500;letter-spacing:-.01em;text-transform:none;
  color:var(--ink);font-variant-numeric:tabular-nums
}
.stb-rate i{display:block;width:5px;height:5px;border-radius:50%;background:var(--sage);margin:0 6px}
.stb-cta{margin:clamp(20px,2.6vw,28px) 0 0}

/* ---- the rooms ----
   Bands of whole rooms, scattered down the page: three across the foot of the
   hero, then a pair closing each section after it. They were one strip with a
   single room open and six slivers, which read as a spectrum but meant six of
   the seven were never really seen — and the rooms are the product.

   The hero's band flexes to whatever height the header leaves it; the pairs
   set their own at 3:2, which is generous enough that a room reads as a room
   and not a thumbnail. */
.stb-rooms{
  display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(8px,1vw,14px);
  max-width:min(1640px,94vw);margin:clamp(16px,2vw,24px) auto 0
}
.stb-rooms--pair{
  grid-template-columns:repeat(2,1fr);margin-top:clamp(34px,4.4vw,58px)
}
.stb-rooms figure{
  margin:0;overflow:hidden;border-radius:var(--r);background:var(--stone);
  animation:stbArrive .85s cubic-bezier(.2,.7,.25,1) backwards;
  animation-delay:calc(var(--k,0) * 90ms + 120ms)
}
.stb-rooms img{width:100%;height:100%;object-fit:cover;display:block}
.stb-rooms--pair img{aspect-ratio:3/2;height:auto}
/* The one staged moment on the site: the rooms arrive across the band as the
   page settles. */
@keyframes stbArrive{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:none}}

/* A room shrunk to a third of a phone is a thumbnail, so the hero's band
   becomes a row you push sideways with each room at a size worth looking at.
   The pairs simply stack. */
@media(max-width:760px){
  .stb-head .stb-rooms{
    display:flex;overflow-x:auto;touch-action:pan-x;overscroll-behavior-x:contain;
    padding-inline:var(--gut);max-width:none;scroll-snap-type:x mandatory
  }
  .stb-head .stb-rooms figure{flex:0 0 78%;scroll-snap-align:center}
  .stb-rooms--pair{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
  .stb-rooms figure{animation:none}
}

.stb-strip__note{
  width:min(1640px,94vw);max-width:100%;margin:12px auto 0;
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted)
}
@media(max-width:760px){.stb-strip__note{padding-inline:var(--gut);width:auto}}

/* ---- three reasons ----
   Ruled apart rather than boxed, and each one ends in the same place: the
   argument in prose, the checkable facts under a rule at the foot of the
   column in the machine face. Because the columns are flex, those foot rules
   line up across all three however long the prose runs — the same device the
   space cards use, and the reason this section stopped being three unequal
   slabs of text. */
.stb-why{
  display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(24px,3.4vw,52px);
  margin-top:clamp(30px,4vw,46px)
}
@media(max-width:900px){.stb-why{grid-template-columns:1fr;gap:32px}}
.stb-why>div{display:flex;flex-direction:column;border-top:1px solid var(--line-str);padding-top:20px}
.stb-why h3{
  font-family:var(--f-disp);font-weight:700;margin:0 0 11px;color:var(--ink);
  font-size:clamp(1.12rem,1.8vw,1.3rem);line-height:1.18;letter-spacing:-.018em
}
.stb-why p{margin:0 0 22px;color:var(--muted);font-size:15.2px;line-height:1.66;text-wrap:pretty}
.stb-why ul{
  margin:auto 0 0;padding:14px 0 0;list-style:none;
  border-top:1px solid var(--line);
  font-family:var(--f-mono);font-size:10px;font-weight:500;
  letter-spacing:.13em;line-height:1.9;text-transform:uppercase;color:var(--ink)
}
/* The separator belongs to the item that follows it, not the one before: as a
   trailing character it dangled at the end of a wrapped line. */
.stb-why ul{display:flex;flex-wrap:wrap;gap:3px 11px}
.stb-why li{display:flex;align-items:center;gap:11px}
.stb-why li+li::before{
  content:"";flex:none;width:3px;height:3px;border-radius:50%;background:var(--sage)
}

.stb-roomlede{max-width:56ch}

/* ---- the live plan ----
   Skedda's map view is the floor plan and it is interactive, so it takes the
   place the still drawing had: wider than the page's measure, because the
   building is a corridor and wider than the page.

   The still sits underneath the frame, not instead of it. Skedda takes a while
   to boot and an iframe is transparent until its document paints, so the
   drawing holds the space from the first frame and is covered when the live
   one arrives. */
.stb-plan{
  max-width:min(1640px,94vw);margin:clamp(24px,3vw,36px) auto 0;
  background:#fff;border:1px solid var(--line-str);border-radius:var(--r);overflow:hidden
}
/* Kept here, with the frame it serves: as a free-standing block twice now it
   has been caught by a tidy-up of neighbouring rules, and without it the
   absolutely-positioned iframe has no positioned ancestor and lays itself over
   the whole page. */
.stb-views__stack{
  position:relative;isolation:isolate;background:var(--stone);
  height:clamp(560px,76vh,900px)
}
.stb-views__wait{
  position:absolute;inset:0;margin:0;display:grid;place-items:center;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted)
}
.stb-plan .cal__frame{position:absolute;inset:0;width:100%;height:100%}
.stb-plan__still{
  position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);
  width:100%;height:auto;max-height:100%;object-fit:contain;opacity:.55
}
.stb-plan figcaption{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px 22px;
  border-top:1px solid var(--line);padding:13px 18px;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--muted)
}
@media(max-width:620px){.stb-plan figcaption .cal__open{margin-left:0}}

/* What the plan shows, counted off it. Figures in the machine face so the row
   reads as the building's dimensions rather than a feature list. */
.stb-building{
  display:flex;flex-wrap:wrap;gap:0;margin:clamp(22px,2.6vw,30px) 0 0;padding:0;list-style:none;
  border-top:1px solid var(--line-str)
}
.stb-building li{
  display:flex;align-items:baseline;gap:8px;padding:15px clamp(20px,2.6vw,34px) 15px 0;
  margin-right:clamp(20px,2.6vw,34px);border-right:1px solid var(--line);
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--muted)
}
.stb-building li:last-child{border-right:0;margin-right:0;padding-right:0}
.stb-building b{
  font-size:21px;font-weight:500;letter-spacing:-.01em;text-transform:none;
  color:var(--ink);font-variant-numeric:tabular-nums
}
.stb-building__note{color:var(--moss)}
@media(max-width:620px){
  .stb-building li{border-right:0;margin-right:0;padding:11px 0;width:100%;
    border-bottom:1px solid var(--line)}
  .stb-building li:last-child{border-bottom:0}
}

.stb-ask{
  position:relative;
  margin-top:clamp(34px,4.4vw,54px);padding-top:clamp(26px,3vw,34px);
  border-top:1px solid var(--line-str)
}
/* The one ornament on the page, and the only thing here that is purely a
   picture. It is the brand's own shape at the size of a piece of furniture,
   held at an opacity where it reads as a watermark on the paper rather than
   as an illustration competing with the words. */
.stb-arch{
  position:absolute;right:2%;bottom:calc(-1 * clamp(56px,8vw,104px));width:clamp(96px,12vw,168px);height:auto;
  color:var(--sage);opacity:.5;pointer-events:none
}
@media(max-width:760px){.stb-arch{display:none}}
.stb-ask h3{
  font-family:var(--f-disp);font-weight:700;margin:0 0 8px;color:var(--ink);
  font-size:clamp(1.2rem,2vw,1.5rem);line-height:1.16;letter-spacing:-.02em
}
.stb-ask p{margin:0 0 20px;color:var(--muted);max-width:48ch}

/* ---------- logo wall ---------- */
/* Sixty organisations' logos are sixty different colour systems, sixty
   different aspect ratios and sixty different amounts of built-in padding.
   Two things fix that wall.

   GRID, not flex. `flex:1 1 132px` let the last row stretch its items to fill
   the line, so a row that happened to hold two logos rendered them as two
   enormous cells twice the size of every other tile — which is exactly what
   the bottom of the home page was doing. Grid tracks are the same width on
   every row, and a short last row simply leaves the tail empty.

   GREY AT REST, COLOUR ON HOVER. The wall's job is to show how many there
   are; the individual marks matter on approach, not at a glance. Desaturating
   holds the wall together and returns each logo's own colour when you point
   at it. */
/* ONE BLOCK, NOT SIXTEEN CARDS. Sixteen separately-bordered tiles read as
   sixteen objects floating on the band; the wall's job is the opposite, so
   the tiles now share a hairline lattice — a 1px grid gap over a rule-coloured
   backdrop — inside a single frame. Same construction as the estate plan's
   figure, down to the mono caption strip, so the two blocks on this page read
   as siblings.

   Sixteen divides into four and two, which is every column count here, so no
   breakpoint ever leaves an orphan row. (Eight across fitted the old cards but
   left each mark ~92px of usable width: a 6:1 wordmark came out 15px tall.
   Four across gives it 230px and the wall became legible.) */
.logos{
  margin-top:clamp(26px,3vw,34px);
  display:grid;grid-template-columns:repeat(2,1fr);gap:1px;
  background:var(--line-str);
  border:1px solid var(--line-str);border-radius:var(--r);overflow:hidden
}
@media(min-width:760px){.logos{grid-template-columns:repeat(4,1fr)}}
/* Fixed height, not min-height: a mark carrying --fit grows its box past the
   padding, and a row that stretched to suit one logo would put the whole wall
   out of step. What overflows is the bitmap's own whitespace. */
/* Every cell is set in its own organisation's colour, --brand, given per logo
   in Home.tsx. A mark drawn in dark ink gets that colour as a wash — enough
   for the wall to read as sixty different organisations rather than one grey
   block, and light enough that the ink still carries. The four reversed marks
   take it at full strength below. */
.logos>a,.logos>span{
  background:color-mix(in srgb, var(--brand,transparent) 9%, var(--paper));
  height:clamp(100px,10.4vw,124px);overflow:hidden;
  display:grid;place-items:center;padding:16px 22px;
  transition:background .22s
}
/* Hover deepens the organisation's own colour rather than washing to white —
   the cell answers in its own hue. */
.logos>a:hover{background:color-mix(in srgb, var(--brand,transparent) 20%, var(--paper))}

/* The four that publish only a white mark take the colour at full strength,
   which is how their own guidelines set a reversed logo. These must opt OUT
   of the multiply below: multiply exists to drop a white PLATE through to the
   cell, and run over a white MARK it would drop the mark and leave the panel
   empty. */
.logos>.is-rev{background:var(--brand)}
.logos>a.is-rev:hover{background:color-mix(in srgb, var(--brand) 86%, #000)}
.logos>.is-rev img,.logos>a.is-rev:hover img{
  mix-blend-mode:normal;filter:none;opacity:1
}
/* Every mark is fitted to ONE box rather than one max-height: a square crest
   and a 6:1 wordmark given the same height are nothing like the same size on
   the page. contain inside a fixed box equalises them instead — and --fit,
   set per logo in Home.tsx, grows that box for the files that padded their
   own mark with whitespace. */
.logos img{
  --box:clamp(42px,4.6vw,56px);
  width:100%;max-width:calc(210px * var(--fit,1));
  height:calc(var(--box) * var(--fit,1));object-fit:contain;
  /* The wall used to be desaturated for the sake of one even texture. The
     estate asked for its tenants' own colours back: sixty organisations is
     the point of the section, and in grey it read as one grey block. Only the
     plated files below still need correcting, because their baked-in
     background is not white and no filter can put colour back through it. */
  filter:contrast(1.02);opacity:1;
  /* Most of these files carry their own white plate baked into the bitmap,
     which would otherwise sit on the tile as a visible white rectangle.
     Multiply drops any white in the file straight through to the tile
     beneath, so what is left is the mark itself, in its own colours. */
  mix-blend-mode:multiply;
  transition:filter .28s,opacity .28s
}
.logos img.is-tall{--box:clamp(58px,6.2vw,76px)}
.logos>a:hover img{filter:none;opacity:1}


.logos__note{
  grid-column:1/-1;margin:0;background:var(--paper);
  display:flex;flex-wrap:wrap;align-items:center;gap:8px 22px;padding:14px 20px;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--muted)
}
.logos__note a{
  margin-left:auto;color:var(--moss);font-weight:500;
  text-decoration:underline;text-underline-offset:4px;
  text-decoration-color:rgba(14,92,65,.4);transition:text-decoration-color .2s
}
.logos__note a:hover{text-decoration-color:currentColor}
@media(max-width:520px){.logos__note a{margin-left:0}}
@media(prefers-reduced-motion:reduce){.logos img{transition:none}}

/* ---------- language switch ----------
   English and Cymraeg, as two links rather than a control with state: changing
   language is a page load, so it works with scripting off and the server can
   remember the choice. The current one is not a link — it has nowhere to go —
   and is filled so the pair reads as a switch at a glance.

   In the header it is the compact form, small enough to sit in the bar without
   competing with the nav; in the phone menu it runs full size under the links.
   Moss on the filled half with white letters measures 8:1. */
.lang{
  display:inline-flex;align-items:stretch;flex:none;
  border:1px solid var(--line-ctl);border-radius:var(--pill);overflow:hidden
}
.lang>*{
  display:inline-flex;align-items:center;gap:8px;
  padding:9px 15px;text-decoration:none;
  font-family:var(--f-body);font-size:11.5px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  transition:background .18s,color .18s
}
.lang a:hover{background:rgba(11,33,26,.06);color:var(--ink)}
.lang .is-on{background:var(--moss);color:#fff}
.lang__flag{width:23px;height:23px;flex:none;border-radius:50%;display:block}
.lang--compact>*{padding:6px 12px;gap:8px;font-size:10.5px;letter-spacing:.08em}
.lang--compact .lang__flag{width:21px;height:21px}
/* Over the hero the bar is dark glass, so the switch takes the pale key with it. */
.site-head.over .lang{border-color:rgba(255,255,255,.42)}
.site-head.over .lang a{color:rgba(255,255,255,.82)}
.site-head.over .lang a:hover{background:rgba(255,255,255,.14);color:#fff}
.site-head.over .lang .is-on{background:#fff;color:var(--ink)}
/* Below the nav's own breakpoint the bar has no room for it — the phone menu
   carries it instead, and the bar goes back to three cells. */
@media(max-width:1000px){
  .head-in .lang{display:none}
  .head-in,.site-head.over .head-in{grid-template-columns:auto 1fr auto}
}
/* Inside the phone menu the mask styles every `a` as a display-sized link, so
   the switch has to state its own case — and take the pale key, since the mask
   is near-black. It rides in on the same stagger as the links above it. */
.mask-lang{margin-top:clamp(22px,5vw,34px)}
.navmask .mask-lang .lang{border-color:rgba(255,255,255,.42);width:fit-content}
.navmask .mask-lang .lang>*{
  display:inline-flex;align-items:center;gap:9px;
  font-family:var(--f-body);font-weight:600;font-size:12px;line-height:1;
  letter-spacing:.1em;text-transform:uppercase;padding:12px 17px;
  color:rgba(255,255,255,.82);transform:none
}
.navmask .mask-lang .lang a{opacity:0}
.navmask.open .mask-lang .lang a{animation:maskIn .6s cubic-bezier(.16,1,.3,1) forwards;animation-delay:var(--d,0ms)}
.navmask .mask-lang .lang a:hover{background:rgba(255,255,255,.14);color:#fff}
.navmask .mask-lang .lang .is-on{background:#fff;color:var(--ink)}
.navmask .mask-lang .lang__flag{width:26px;height:26px}

/* ---------- directory ---------- */
.filters{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 30px}
.chip{
  font-family:var(--f-body);font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  padding:12px 17px;border:1px solid var(--line-ctl);background:transparent;
  border-radius:var(--pill);
  cursor:pointer;color:var(--muted);transition:all .16s
}
.chip:hover{color:var(--ink);border-color:var(--ink)}
.chip.on{background:var(--moss);border-color:var(--moss);color:#fff}

/* One green per sector, drawn off the logo rather than invented: the filters
   were five identical outlines and the estate wanted the sectors told apart.
   Each keeps its own hue when selected, and carries it in the text and rule
   when it is not, so the row reads as a key to the directory beneath.

   Every fill is dark enough to hold white type — the lightest here is 5.10:1,
   comfortably past AA — which is why the brand's lighter --moss-2 (3.33:1) is
   not among them. */
.chip[data-cat="health"]    {--chip:#0B6B4E}
.chip[data-cat="wellbeing"] {--chip:#12794A}
.chip[data-cat="education"] {--chip:#2F7D3A}
.chip[data-cat="community"] {--chip:#0E6F63}
.chip[data-cat="services"]  {--chip:#3A6B54}

.chip[data-cat]:not([data-cat="all"]){color:var(--chip,var(--muted))}
.chip[data-cat]:not([data-cat="all"]):hover{
  color:var(--chip);border-color:var(--chip);background:color-mix(in srgb, var(--chip) 8%, transparent)
}
.chip.on[data-cat]{background:var(--chip,var(--moss));border-color:var(--chip,var(--moss));color:#fff}

/* The row of rules under the directory follows the same key. */
.dir-row[data-cat="health"]    {--chip:#0B6B4E}
.dir-row[data-cat="wellbeing"] {--chip:#12794A}
.dir-row[data-cat="education"] {--chip:#2F7D3A}
.dir-row[data-cat="community"] {--chip:#0E6F63}
.dir-row[data-cat="services"]  {--chip:#3A6B54}
.dir-row[data-cat] .idx{color:var(--chip,var(--brass))}

.dir{border-top:1px solid var(--line-str)}
.dir-row{
  display:grid;grid-template-columns:38px 1fr 200px 108px;gap:18px;align-items:baseline;
  padding:17px 4px;border-bottom:1px solid var(--line);transition:background .16s
}
.dir-row:hover{background:var(--stone)}
.dir-row .idx{font-family:var(--f-mono);font-size:11px;color:var(--brass);font-variant-numeric:tabular-nums}
.dir-row .nm{font-family:var(--f-disp);font-weight:600;font-size:16.5px;letter-spacing:-.008em}
.dir-row .ds{font-size:14px;color:var(--muted)}
.dir-row .lk{
  font-family:var(--f-body);font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--moss);text-decoration:none;justify-self:end;white-space:nowrap
}
.dir-row .lk:hover{text-decoration:underline;text-underline-offset:3px}
@media(max-width:820px){
  .dir-row{grid-template-columns:30px 1fr;gap:6px 14px}
  .dir-row .ds{grid-column:2}
  .dir-row .lk{grid-column:2;justify-self:start;margin-top:5px}
}
.dir-count{font-family:var(--f-mono);font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin:22px 0 0}

/* ---------- activities ---------- */
.acts{columns:2;column-gap:clamp(24px,4vw,56px)}
@media(max-width:760px){.acts{columns:1}}
.act{
  break-inside:avoid;padding:13px 0;border-bottom:1px solid var(--line);
  display:flex;justify-content:space-between;gap:16px;align-items:baseline
}
.act b{font-weight:600;font-size:15px}
.act span{font-family:var(--f-mono);font-size:10.5px;letter-spacing:.09em;color:var(--muted);text-align:right;white-space:nowrap}
.act a{color:var(--moss)}

/* ---------- timeline ---------- */
.tl{border-top:1px solid var(--line-str)}
.tl-row{
  display:grid;grid-template-columns:150px 1fr;gap:clamp(20px,4vw,54px);
  padding:clamp(24px,3vw,36px) 0;border-bottom:1px solid var(--line)
}
@media(max-width:700px){.tl-row{grid-template-columns:1fr;gap:10px}}
/* the year is data, so it is set in the machine face and lined — the column
   of years down the left of the timeline now actually forms a column */
.tl-yr{
  font-family:var(--f-mono);font-weight:500;font-size:clamp(1.25rem,2.6vw,1.75rem);
  letter-spacing:-.01em;line-height:1;color:var(--moss);
  font-variant-numeric:tabular-nums
}
.tl-row p{font-family:var(--f-read);font-size:1.03rem;max-width:64ch;margin:0}

/* ---------- info blocks ---------- */
.facts{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}
@media(max-width:700px){.facts{grid-template-columns:1fr}}
.fact{background:var(--paper);border:1px solid var(--line);border-radius:var(--r);padding:26px}
/* "Parking", "EV charging" — a term with its definition under it, so it is a
   heading, not a label, and it is set as one. */
.fact h4{
  font-family:var(--f-disp);font-weight:700;font-size:16px;letter-spacing:-.012em;
  margin:0 0 8px
}
.fact p{font-size:14.8px;color:var(--muted);margin:0}
.fact p+p{margin-top:9px}

/* ---------- notice: a disclosure, not a warning box --------------------
   These four notices are small print the estate volunteers rather than
   hides: two admit which photographs are AI-staged, one is the parking
   rule a visitor must act on, one caveats a timetable.

   So: no panel, no bar, no fill. The title hangs to the left of the text
   like a gloss in a margin, and the text is set LARGER than the copy it
   qualifies (17px against the 16.5px body) — the previous version set it
   at 15px, the smallest type in its section, which is how small print
   hides. It is also the only block on the site in plain sentence case,
   where everything else is letterspaced uppercase, so it reads as someone
   speaking rather than a system warning.

   Do NOT convert .notice__text to --muted in a later tidy-up. Volunteering
   small print and greying it out are contradictory instructions.

   Layout is driven by a container query, not a viewport one: the Info page
   drops a notice into a ~380px .split column at a 900px viewport, which no
   max-width media query would catch. Stacked is the default, so that is
   also the no-container-query fallback.                                 */
.notice{
  container-type:inline-size;
  background:none;border:0;border-radius:0;padding:0;  /* replaces, not appends */
  margin:clamp(38px,5vw,58px) 0 0
}
.notice__in{display:grid;grid-template-columns:minmax(0,1fr);row-gap:6px}

@container (min-width:30rem){
  .notice__in{
    grid-template-columns:minmax(0,clamp(6rem,26%,9.5rem)) minmax(0,46ch);
    column-gap:clamp(18px,2.4vw,32px);
    justify-content:start;
    align-items:baseline;          /* label sits on the first text baseline */
  }
  .notice__label{text-align:right}
}

/* --moss on --stone measures 6.53:1 since the palette was rewarmed, so this
   label now has real headroom. If --stone is ever darkened, re-measure: below
   4.5 the label must go to --ink at 700 and lose its only colour. */
.notice__label{
  margin:0;text-wrap:balance;
  font-size:15.5px;font-weight:700;line-height:1.42;
  letter-spacing:-.004em;color:var(--moss)
}
.notice__text{
  margin:0;
  font-size:1.0625rem;line-height:1.68;letter-spacing:-.006em;
  color:var(--ink);text-wrap:pretty
}

/* An instruction the reader must act on, not merely know about: same voice
   raised. Always full-column so it never competes with a hung label. */
.notice--act .notice__in{grid-template-columns:minmax(0,44ch)}
.notice--act .notice__label{text-align:left;font-size:16px;font-weight:800}
.notice--act .notice__text{font-size:1.1875rem;line-height:1.58;letter-spacing:-.011em}

/* Underlined at rest. A link inside a disclosure that only appears on hover
   is the same sin as hiding the disclosure. */
.notice__text a{
  color:var(--moss);font-weight:600;
  text-decoration:underline;text-decoration-thickness:1px;
  text-underline-offset:.19em;text-decoration-color:rgba(14,92,65,.45);
  transition:color .18s,text-decoration-color .18s
}
.notice__text a:hover{color:var(--ink);text-decoration-color:currentColor}

/* Colour is unavailable in forced-colors, so separate the label by weight. */
@media (forced-colors:active){
  .notice__label{font-weight:900}
  /* colour is discarded in forced-colors, so anything that signalled state with
     it alone needs a shape as well */
  .logos img{filter:none;opacity:1;mix-blend-mode:normal}
  .hero-seek button::before{background:CanvasText;opacity:.45}
  .hero-seek button::after{background:Highlight}
  .hero-tile{border:1px solid CanvasText}
  .hero-tile.is-live{outline:2px solid Highlight;outline-offset:-2px}
  .hero-tile::after{background:Highlight;forced-color-adjust:none}
  :where(a,button):focus-visible{outline:2px solid Highlight;box-shadow:none}
}
@media (prefers-reduced-motion:reduce){.notice__text a{transition:none}}

.mapfig{margin:0;border:1px solid var(--line);background:#fff;
  border-radius:var(--r);overflow:hidden}
.mapfig img{width:100%;height:auto}   /* height:auto is load-bearing: the img carries
  intrinsic width/height attributes to reserve the box against CLS, and without this
  the browser honours height="1667" literally and renders the plan stretched. */
.mapfig figcaption{
  font-family:var(--f-mono);font-size:11px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--muted);padding:13px 16px;border-top:1px solid var(--line)
}

/* ---------- the location: the estate plan at full measure --------------
   The plan is the most useful artifact on the site and it was unusable:
   shown at 510px from a 1200px source, with `.split figure img` slicing
   16.8% off its sides. Nothing crops it now, and it is served at up to
   1730px so the shrunk-to-fit copy stays sharp on a 2x screen.

   Served as the DRAWING ALONE. The source PNG has a tenant directory baked
   into its right-hand third which is illegible at any size this page can
   afford — and the Who's here page already lists every organisation — so
   the crop is done by Wix (x_0,y_63,w_1730,h_1667 of the 2900x2051
   original) rather than in CSS. That crop segment also makes Wix serve
   AVIF unconditionally, without content negotiation: 156KB at 1730px wide
   against 739KB for the old 1200px PNG. Sharper and a fifth of the weight.

   The pins carry a code the artwork never explains: solid #0771C0 is a
   front entrance, pale #38B6FF a rear one. Only buildings 1 and 2 have
   both — exactly the pair the estate directory splits into Front and Rear.
   The legend states it, because nothing else on the estate does.        */
/* Two columns from 901px up: plan on the left, the copy that explains it on
   the right, so the section reads without a scroll between question and
   answer — full measure meant the drawing alone was taller than most
   viewports. The plan is DOM-ordered after the copy (an h2 must precede its
   figure for anything reading linearly) and moved left with `order`.
   The drawing loses width in the trade, so `Open full size` in the caption
   stops being a nicety: it is now the way to read the small labels. */
.loc{display:grid;gap:clamp(28px,4vw,54px)}
@media(min-width:901px){
  .loc{grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);align-items:center}
  .loc .plan{order:-1}
}
.loc__copy>.eyebrow:first-child{margin-top:0}
.loc__h{font-size:clamp(1.85rem,3vw,2.45rem);margin-bottom:0}
.loc__lede{max-width:52ch;margin:22px 0 13px}
.loc__gaps{max-width:54ch;margin:0;font-size:15px;color:var(--muted)}
.loc__seq{
  display:block;margin-top:10px;color:var(--ink);
  font-family:var(--f-mono);font-weight:500;font-size:14.5px;
  font-variant-numeric:tabular-nums;letter-spacing:.02em
}
.loc__copy .notice{margin-top:clamp(24px,2.8vw,34px)}
/* The list fills the narrower column on its own, so the button always wraps
   to a row of its own — pushed right by the default `margin-left:auto` it
   floated away from the copy it belongs to. */
.loc__copy .loc__also{flex-direction:column;gap:7px}
.loc__copy .loc__after{flex-direction:column;align-items:flex-start;gap:20px}
.loc__copy .loc__after .btn{margin-left:0}

.plan{margin:0;border:1px solid var(--line-str);border-radius:var(--r);
  background:#fff;overflow:hidden}
/* width:auto + max-width/max-height, not width:100%: the plan is 1730x1667,
   so at full column width on a laptop it still ran past the fold. Capping
   the height lets it shrink to whatever the viewport actually has and the
   intrinsic ratio does the rest — no object-fit, nothing cropped. */
.plan__scroll{display:flex;justify-content:center;overflow:hidden}
.plan img{display:block;width:auto;height:auto;max-width:100%;max-height:min(62vh,600px)}
.plan__pan{display:none}
/* Below ~900px the plan scaled to the viewport renders its labels at 2-3px,
   which is where a visitor actually reads it — standing in the car park. So
   hold it at a legible 860px and let the reader pan sideways instead.
   touch-action:pan-x hands every vertical swipe straight back to the page,
   so the map is never a scroll trap mid-article. */
@media(max-width:900px){
  .plan__scroll{justify-content:flex-start;   /* centring clips the left edge once it overflows */
    overflow-x:auto;touch-action:pan-x;overscroll-behavior-x:contain}
  .plan__scroll img{width:860px;max-width:none;max-height:none}
  .plan__pan{display:inline;color:var(--ink);font-weight:500}
}
.plan figcaption{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px 22px;
  border-top:1px solid var(--line);padding:13px 18px;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--muted)
}
.plan__key{display:inline-flex;align-items:center;gap:8px}
.plan__sw{width:11px;height:11px;border-radius:50%;flex:none;
  /* the fills match the pins in the drawing and must not change, so the swatch
     gets a boundary instead — #38B6FF alone is 2.26:1 on white */
  box-shadow:0 0 0 1px rgba(11,33,26,.55)}
.plan__sw--front{background:#0771C0}   /* sampled from the artwork itself */
.plan__sw--rear{background:#38B6FF}
.plan__open{margin-left:auto;color:var(--moss);font-weight:500;text-underline-offset:4px}
@media(max-width:620px){.plan__open{margin-left:0}}

/* Arrival, ranked. The four equal boxes this replaces gave "EV charging —
   pending planning permission" the same weight as the parking rule you
   must actually follow, so the one instruction was buried among amenities
   and one of the four does not exist yet. */
.loc__after{
  display:flex;flex-wrap:wrap;align-items:center;gap:18px 34px;
  margin-top:clamp(26px,3.5vw,38px)
}
.loc__also{margin:0;padding:0;list-style:none;
  display:flex;flex-wrap:wrap;gap:6px 26px;font-size:14.6px;color:var(--muted)}
.loc__also b{color:var(--ink);font-weight:600}
.loc__after .btn{margin-left:auto}
@media(max-width:760px){.loc__after .btn{margin-left:0}}

/* ---------- faq ---------- */
.faq{border-top:1px solid var(--line-str)}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{
  cursor:pointer;list-style:none;padding:20px 40px 20px 0;position:relative;
  font-family:var(--f-disp);font-weight:600;font-size:16.5px;letter-spacing:-.005em
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";position:absolute;right:6px;top:15px;font-size:24px;line-height:1;color:var(--brass);font-weight:400;transition:transform .2s}
.faq summary:hover::after{transform:scale(1.15)}
.faq details[open] summary::after{content:"–"}
.faq .ans{padding:0 0 22px;max-width:70ch;font-size:15.4px;color:var(--muted)}
.faq .ans p:last-child{margin-bottom:0}

/* ---------- CTA + footer ---------- */
.cta-band{background:var(--moss);color:#fff;padding:clamp(52px,7vw,86px) 0}
.cta-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(30px,6vw,80px);align-items:end}
@media(max-width:860px){.cta-grid{grid-template-columns:1fr;gap:34px}}
.cta-stats{display:grid;grid-template-columns:repeat(2,1fr);border-top:1px solid rgba(255,255,255,.28)}
.cta-stats div{padding:20px 16px 18px 0;border-bottom:1px solid rgba(255,255,255,.16)}
.cta-stats b{display:block;font-family:var(--f-disp);font-weight:700;font-size:clamp(1.6rem,3vw,2.35rem);
  letter-spacing:-.025em;line-height:1;color:#fff;font-variant-numeric:tabular-nums}
.cta-stats i{display:block;font-family:var(--f-mono);font-style:normal;font-size:10px;
  letter-spacing:.15em;text-transform:uppercase;color:rgba(255,255,255,.72);margin-top:9px}
/* set like every other section head — see .h-sec */
.cta-band h2{
  font-family:var(--f-disp);font-weight:700;font-size:clamp(1.95rem,4.6vw,3.4rem);
  line-height:1.04;letter-spacing:-.032em;margin:0 0 18px;max-width:20ch;text-wrap:balance
}
.cta-band p{color:rgba(255,255,255,.85);max-width:52ch;font-family:var(--f-read);font-size:1.1rem}
.cta-mail{
  font-family:var(--f-mono);font-size:clamp(13px,2.2vw,17px);color:#fff;
  text-decoration:none;border-bottom:1px solid var(--brass-lt);padding-bottom:4px
}

.site-foot{background:var(--ink);color:rgba(255,255,255,.72);padding:clamp(44px,6vw,68px) 0 34px;font-size:14.5px}
.foot-grid{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:32px}
@media(max-width:860px){.foot-grid{grid-template-columns:1fr 1fr}}
@media(max-width:520px){.foot-grid{grid-template-columns:1fr}}
.site-foot h5{
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:rgba(255,255,255,.5);margin:0 0 14px;font-weight:500
}
.site-foot a{color:rgba(255,255,255,.82);text-decoration:none}
.site-foot a:hover{color:#fff;text-decoration:underline;text-underline-offset:3px}
.site-foot ul{list-style:none;margin:0;padding:0}
.site-foot li{margin-bottom:9px}
.foot-brand img{width:64px;margin-bottom:16px;background:#fff;border-radius:50%;padding:5px}
.foot-brand p{font-size:14px;max-width:34ch;color:rgba(255,255,255,.6)}
.foot-btm{
  border-top:1px solid rgba(255,255,255,.13);margin-top:38px;padding-top:22px;
  display:flex;flex-wrap:wrap;gap:12px;justify-content:space-between;
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.11em;text-transform:uppercase;
  color:rgba(255,255,255,.55)
}

/* ============================================================
   HERO — "standing in the grounds"
   One photograph you look into, graded down to forest dark.
   The estate's bookable rooms float over it as frosted panes:
   the tile grid is the booking menu, not decoration.
   ============================================================ */
:root{
  --hero-live:#7FD9A8;   /* 9.94:1 on --ink */
  --head-h:  84px;
  --pale:    #E8F0EC;   /* headline: pale grey-green, never pure white */
  --rail-w:  60px;
}

/* every page runs to the top edge; the bar floats over it */
main{padding-top:0}
/* …so the band that opens a page has to clear the bar */
.band--top{padding-top:calc(var(--bar-h) + clamp(34px,4.4vw,66px))}
.nav{justify-self:center}

/* Over the hero the chrome is a pane of glass rather than a solid band: a
   frosted bar hung off the rail, carrying the mark, the four destinations and
   the booking link. Everywhere else the header stays the solid white one. */
/* The bar was reading as almost nothing over the drone shot — the estate said
   it "gets lost" against the white one it becomes on every other page. It is
   still glass, but now glass with a pane behind it: a dark wash under a
   brighter white film, so the pill has an edge wherever the photograph puts
   sky, grass or roof behind it, without becoming the solid band that would
   defeat the point of a hero. */
.site-head.over .head-in{
  background:
    linear-gradient(rgba(255,255,255,.17), rgba(255,255,255,.13)),
    rgba(4,20,15,.30);
  border-color:rgba(255,255,255,.42);
  box-shadow:
    0 16px 46px rgba(3,15,10,.40),
    inset 0 1px 0 rgba(255,255,255,.34)
}
.site-head.over .brand-tx b{color:var(--pale)}
.site-head.over .brand-tx i{color:rgba(255,255,255,.85);text-shadow:0 1px 3px rgba(3,15,10,.85), 0 1px 12px rgba(3,15,10,.9)}
/* every colour in the bar changes over the same .45s, so crossing out of the
   hero reads as one cross-fade rather than a set of separate switches */
.brand-tx b,.brand-tx i{transition:color .45s, text-shadow .45s}
.nav a{transition:color .32s, border-color .3s}
.burger span{transition:background .45s, transform .35s cubic-bezier(.16,1,.3,1), opacity .2s}
.head-cta{transition:background .45s, border-color .45s, color .45s}
.site-head.over .nav{display:flex}
.site-head.over .nav a{color:rgba(255,255,255,.84);border-bottom-color:transparent}
.site-head.over .nav a:hover{color:#fff}
.site-head.over .nav a.is-on{color:var(--hero-live);border-bottom-color:transparent}
.site-head.over .head-cta{
  display:inline-flex;margin-left:0;padding:12px 22px;border-radius:999px;
  background:var(--moss);border-color:var(--moss);color:#fff
}
.site-head.over .head-cta:hover{background:var(--pale);border-color:var(--pale);color:var(--ink)}
.site-head.over .burger{display:none}

.brand-big{display:none}
.brand-sm{display:flex;align-items:center;gap:11px}
.site-head.over .brand-sm{display:flex}

/* ---- burger ---- */
.burger{
  background:transparent;border:0;padding:10px 4px;cursor:pointer;
  width:42px;height:42px;display:none;position:relative
}
.burger span{
  position:absolute;left:8px;right:8px;height:1.5px;background:#fff;
  border-radius:2px;box-shadow:0 1px 5px rgba(3,15,10,.75);transition:transform .35s cubic-bezier(.16,1,.3,1), opacity .2s
}
.burger span:nth-child(1){top:15px}
.burger span:nth-child(2){top:24px}
.site-head:not(.over) .burger span{background:var(--ink)}
.burger[aria-expanded="true"] span:nth-child(1){transform:translateY(4.5px) rotate(45deg)}
.burger[aria-expanded="true"] span:nth-child(2){transform:translateY(-4.5px) rotate(-45deg)}

/* ---- full-screen menu (keeps every destination reachable) ---- */
.navmask{
  position:fixed;inset:0;z-index:70;display:flex;flex-direction:column;
  justify-content:center;padding:0 clamp(24px,8vw,140px);
  background:rgba(4,20,15,.93);
  backdrop-filter:blur(22px) saturate(140%);-webkit-backdrop-filter:blur(22px) saturate(140%);
  opacity:0;visibility:hidden;transition:opacity .4s, visibility .4s
}
.navmask.open{opacity:1;visibility:visible}
.navmask a{
  display:block;color:var(--pale);text-decoration:none;
  font-family:var(--f-disp);font-weight:300;letter-spacing:-.02em;
  font-size:clamp(1.9rem,5.4vw,3.6rem);line-height:1.5;
  opacity:0;transform:translateY(18px);transition:color .25s
}
.navmask.open a{animation:maskIn .6s cubic-bezier(.16,1,.3,1) forwards;animation-delay:var(--d,0ms)}
@keyframes maskIn{to{opacity:1;transform:none}}
.navmask a:hover{color:var(--hero-live)}
.navmask a.is-on{color:var(--hero-live)}
.navmask .mask-cta{
  margin-top:34px;font-family:var(--f-body);font-size:15px;font-weight:600;
  letter-spacing:.02em;color:var(--ink);background:var(--pale);
  width:max-content;padding:15px 30px;border-radius:999px;line-height:1
}
.navmask .mask-cta:hover{background:var(--moss-2);color:#fff}

/* ---- frame ---- */
.hero{
  position:relative;min-height:100svh;display:flex;overflow:hidden;
  background:#06120E;isolation:isolate
}
/* --- carousel + parallax ---------------------------------------------
   Depth is split three ways so the frosted tiles read as real panes of
   glass standing in front of a view: the backdrop drifts against the
   pointer, the tiles drift with it, and the copy rises on scroll.       */
.hero-stage{position:absolute;inset:0;z-index:0;overflow:hidden}
.hero-parallax{
  position:absolute;inset:-13% -7%;
  transform:translate3d(0, var(--sy,0px), 0) scale(var(--sc,1));
  will-change:transform
}
.hero-slide{
  position:absolute;inset:0;opacity:0;
  transition:opacity 1.4s cubic-bezier(.4,0,.2,1)
}
.hero-slide.is-on{opacity:1}
.hero-slide img{
  width:100%;height:100%;object-fit:cover;transform:scale(1.02);
  filter:saturate(1.04) contrast(1.02) brightness(1.02)
}
/* the still breathes while it is held */
.hero-slide.is-on img{animation:ken 9.5s linear forwards}
@keyframes ken{from{transform:scale(1.02)}to{transform:scale(1.11)}}

/* --- reading scrim ----------------------------------------------------
   The photograph is bright where the words are: the projector screen sits
   straight behind the headline and the sky behind the mark. Two graded
   washes sit between the picture and the type — one down the rail-and-copy
   side, one across the header band — so the pale type keeps its contrast
   whichever slide is showing. Both are transparent by mid-frame, which
   leaves the right of the picture, and the frosted tiles, untouched.     */
.hero::before,
.hero::after{content:"";position:absolute;z-index:1;pointer-events:none}
.hero::before{
  inset:0;
  background:linear-gradient(90deg,
    rgba(3,17,12,.54) 0%,
    rgba(3,17,12,.40) 22%,
    rgba(3,17,12,.18) 44%,
    rgba(3,17,12,0)   62%)
}
.hero::after{
  left:0;right:0;top:0;height:calc(var(--head-h) + 96px);
  background:linear-gradient(180deg,
    rgba(3,17,12,.40) 0%,
    rgba(3,17,12,.18) 45%,
    rgba(3,17,12,0)   100%)
}

/* --- seek rail: one control that both shows progress and selects --- */
.hero-seek{display:flex;align-items:center;gap:6px;margin-top:14px;margin-bottom:-10px;flex-wrap:wrap}
.hero-seek button{
  position:relative;width:44px;height:44px;padding:0;
  background:transparent;border:0;cursor:pointer
}
.hero-seek button::before,
.hero-seek button::after{
  content:"";position:absolute;left:0;right:0;top:21px;height:2px;border-radius:2px
}
.hero-seek button::before{background:rgba(255,255,255,.45);box-shadow:0 1px 6px rgba(3,15,10,.5)}
.hero-seek button::after{background:var(--pale);transform:scaleX(0);transform-origin:left}
.hero-seek button:hover::before{background:rgba(232,240,236,.5)}
.hero-seek button.is-on::after{animation:seek var(--dur,7000ms) linear forwards}
.hero-seek button.is-done::after{transform:scaleX(1)}
@keyframes seek{to{transform:scaleX(1)}}

/* ---- left rail ---- */
.hero-rail{
  position:absolute;left:0;top:0;bottom:0;width:var(--rail-w);z-index:4;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:calc(var(--head-h) + 8px) 0 26px;
  border-right:1px solid rgba(255,255,255,.22)
}
.hero-rail .place{
  writing-mode:vertical-rl;transform:rotate(180deg);
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.28em;text-transform:uppercase;
  color:rgba(255,255,255,.88);white-space:nowrap;text-shadow:0 1px 3px rgba(3,15,10,.8), 0 1px 12px rgba(3,15,10,.9)
}
.hero-rail .rail-social{position:absolute;bottom:20px;left:0;right:0;display:flex;flex-direction:column;gap:2px;align-items:center}
.hero-rail .rail-social a{color:rgba(255,255,255,.9);filter:drop-shadow(0 1px 4px rgba(3,15,10,.85));display:grid;place-items:center;width:32px;height:32px;transition:color .25s, transform .3s}
.hero-rail .rail-social a:hover{color:var(--pale);transform:translateY(-2px)}
.hero-rail .rail-social svg{width:16px;height:16px;fill:currentColor}

/* ---- content ---- */
.hero-inner{
  position:relative;z-index:2;width:100%;max-width:var(--shell,1500px);margin:0 auto;
  /* The right padding was 0, so the booking tiles ran flush into the viewport
     edge and looked clipped at every width above 820px. The rail is only on
     the left, so the two sides are set independently.                    */
  padding:calc(var(--head-h) + clamp(20px,5vh,54px)) clamp(24px,4vw,64px) clamp(26px,5vh,50px)
          calc(var(--rail-w) + clamp(24px,4vw,64px));
  display:grid;grid-template-columns:minmax(0,1fr) auto;
  gap:clamp(24px,4vw,60px);align-items:stretch
}

/* The words stand on their own pane: light at the head where it catches the
   sky, dense at the foot where the reading is. It is the same glass as the
   bar above it and the room cards beside it — one material, three uses.   */
.hero-copy{
  display:flex;flex-direction:column;justify-content:center;align-self:center;
  max-width:min(100%,630px);
  padding:clamp(26px,2.6vw,40px) clamp(24px,2.4vw,38px) clamp(24px,2.4vw,34px);
  border-radius:22px;
  background:linear-gradient(160deg,
    rgba(6,26,19,.30) 0%,
    rgba(5,22,16,.42) 52%,
    rgba(3,16,12,.56) 100%);
  border:1px solid rgba(255,255,255,.20);
  box-shadow:0 24px 70px rgba(3,15,10,.34), inset 0 1px 0 rgba(255,255,255,.18);
  backdrop-filter:blur(15px) saturate(122%);-webkit-backdrop-filter:blur(15px) saturate(122%);
  transform:translate3d(0,var(--cy,0px),0);opacity:var(--cop,1)
}
.hero-h1{
  font-family:var(--f-disp);font-weight:400;color:#fff;
  font-size:clamp(1.95rem,3.9vw,3.5rem);line-height:1.1;letter-spacing:-.028em;
  margin:0;text-transform:none;
  /* three lines are held open whatever the slide says, so the button and the
     note underneath stay put as the headline turns — and the lines sit at the
     FOOT of that box, so a two-line slide banks its spare line as air above
     the words instead of as a hole between them and the button */
  min-height:3.21em;display:flex;flex-direction:column;justify-content:flex-end;
  text-shadow:0 2px 26px rgba(3,15,10,.78), 0 1px 5px rgba(3,15,10,.45)
}
.hero-h1 span{
  display:block;opacity:0;transform:translateY(24px);
  animation:heroRise 1.1s cubic-bezier(.16,1,.3,1) both
}
.hero-h1 span:nth-child(1){animation-delay:.20s}
.hero-h1 span:nth-child(2){animation-delay:.32s}
.hero-h1 span:nth-child(3){animation-delay:.44s}
/* the opening line takes its time; every later turn of the carousel is quick,
   so the words land while the picture behind them is still crossfading */
.hero-h1.is-swap span{animation-duration:.72s}
.hero-h1.is-swap span:nth-child(1){animation-delay:0s}
.hero-h1.is-swap span:nth-child(2){animation-delay:.07s}
.hero-h1.is-swap span:nth-child(3){animation-delay:.14s}
@keyframes heroRise{to{opacity:1;transform:none}}

/* ---- the slide's own call to action, beside the scroll cue ---- */
.hero-act{
  display:flex;align-items:center;gap:clamp(12px,1.5vw,18px);flex-wrap:wrap;
  margin-top:clamp(20px,3.4vh,38px);
  opacity:0;animation:heroRise 1.1s cubic-bezier(.16,1,.3,1) .58s both
}
.hero-cta{
  display:inline-flex;align-items:center;gap:10px;
  font-family:var(--f-body);font-size:12px;font-weight:600;
  letter-spacing:.11em;text-transform:uppercase;text-decoration:none;
  padding:16px 28px;border-radius:999px;
  background:var(--moss);border:1px solid var(--moss);color:#fff;
  box-shadow:0 6px 26px rgba(3,17,12,.42);
  transition:background .24s, border-color .24s, color .24s
}
.hero-cta:hover{background:var(--pale);border-color:var(--pale);color:var(--ink)}
.hero-cta::after{content:"→";font-family:var(--f-body);font-size:14px;line-height:1}
/* the label is swapped with the slide, so it rises in with the headline */
.hero-cta span{
  display:inline-block;opacity:0;transform:translateY(9px);
  animation:heroRise .72s cubic-bezier(.16,1,.3,1) .06s both
}

.hero-scroll{
  width:54px;height:54px;border-radius:50%;border:1.5px solid rgba(255,255,255,.7);
  display:grid;place-items:center;margin:0;color:#fff;flex:none;
  background:rgba(255,255,255,.13);cursor:pointer;
  backdrop-filter:blur(12px) saturate(120%);-webkit-backdrop-filter:blur(12px) saturate(120%);
  box-shadow:0 8px 26px rgba(3,15,10,.28), inset 0 1px 0 rgba(255,255,255,.22);
  transition:background .3s, border-color .3s
}
.hero-scroll:hover{background:rgba(255,255,255,.22);border-color:#fff}
.hero-scroll svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;animation:bob 2.4s ease-in-out infinite}
@keyframes bob{0%,100%{transform:translateY(-2px)}50%{transform:translateY(3px)}}

.hero-note{
  margin-top:auto;padding-top:clamp(24px,5vh,54px);max-width:44ch;
  opacity:0;animation:heroRise 1.1s cubic-bezier(.16,1,.3,1) .70s both
}
.hero-note h2{
  font-family:var(--f-body);font-weight:700;font-size:16px;letter-spacing:-.005em;
  color:#fff;margin:0 0 12px;text-shadow:0 1px 3px rgba(3,15,10,.7), 0 2px 18px rgba(3,15,10,.9)
}
.hero-note p{
  font-family:var(--f-body);font-size:14px;line-height:1.65;
  color:#fff;margin:0 0 10px;text-shadow:0 1px 3px rgba(3,15,10,.7), 0 2px 16px rgba(3,15,10,.9)
}
.hero-note p:last-child{margin-bottom:0}

/* ---- the signature: bookable rooms as frosted panes ----
   Sash windows onto the view, hung on a hairline glazing bar: each pane names
   a room and what it costs, so the grid reads as the price list it is. The
   pane the carousel is showing lights its sill with the same green fill the
   seek dashes use — menu and picture are one control, not two.              */
.hero-tiles-wrap{
  align-self:end;width:clamp(320px,40vw,560px);min-width:0;
  opacity:0;animation:heroRise 1.2s cubic-bezier(.16,1,.3,1) .46s both
}
/* The panes run in a single sliding row, two in view at a time. It is a real
   scroller with snap points, so a trackpad or a thumb moves it natively; the
   scrollbar is hidden because the arrows and dots say the same thing. The
   padding gives the panes' shadows room inside the scroller's clip, and the
   negative margin gives that room back to the layout. */
.hero-tiles{
  --tiles-gap:10px;--in-view:2;
  width:100%;
  display:flex;gap:var(--tiles-gap);
  overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;scrollbar-width:none;
  padding:8px 0 44px;margin:-8px 0 -44px;
  cursor:grab;touch-action:pan-x pan-y
}
.hero-tiles::-webkit-scrollbar{display:none}
.hero-tiles.is-drag{scroll-snap-type:none;cursor:grabbing}
.hero-tiles.is-drag .hero-tile{pointer-events:none}
.hero-tile{
  flex:0 0 calc((100% - var(--tiles-gap) * (var(--in-view) - 1)) / var(--in-view));
  scroll-snap-align:start;-webkit-user-drag:none;user-select:none;
  position:relative;min-height:clamp(128px,17vh,172px);
  display:flex;flex-direction:column;align-items:flex-start;
  padding:16px 18px 15px;border-radius:14px;
  /* each pane is a picture of its room: the photograph fills it, a shade
     sits over the photograph, and the words sit on the shade. The ground
     colour only shows while the picture is still loading. */
  overflow:hidden;isolation:isolate;background:#0B2419;
  border:1px solid rgba(255,255,255,.22);
  box-shadow:0 10px 26px rgba(3,15,10,.3);
  color:var(--pale);text-decoration:none;
  transition:border-color .38s, color .3s
}
.hero-tile-img{
  position:absolute;inset:0;z-index:0;width:100%;height:100%;
  object-fit:cover;pointer-events:none;
  transform:scale(1.01);
  transition:transform 1.1s cubic-bezier(.16,1,.3,1), filter .5s
}
/* The shade, dense at the foot where the words are.
   157deg runs top-left to bottom-RIGHT, so the dense end of this gradient
   landed in the corner opposite the words: .lbl and .price are flex-start,
   i.e. bottom-LEFT, and sat on the brightest part of the tile. Over the
   sunlit slides that measured 2.3-3.6:1. Straightening it to 178deg puts the
   density along the bottom EDGE, where the type actually is. Over a whole
   photograph it has to work harder than it did over frosted glass, so the
   foot is near-solid. */
.hero-tile::before{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(178deg,
    rgba(4,22,15,.10) 0%,
    rgba(5,24,17,.52) 42%,
    rgba(3,15,11,.92) 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.20);
  transition:opacity .38s
}
.hero-tile .lbl,.hero-tile .price{position:relative;z-index:2}
.hero-tile:hover,
.hero-tile.is-live{border-color:rgba(255,255,255,.5);color:#fff}
.hero-tile:hover .hero-tile-img,
.hero-tile.is-live .hero-tile-img{transform:scale(1.07)}
/* the name and the price sit on the sill; the auto margin puts the free space
   above them, so a pane reads the same whatever height the grid gives it */
.hero-tile .lbl{
  margin-top:auto;
  font-family:var(--f-body);font-size:15px;font-weight:500;
  letter-spacing:-.005em;line-height:1.2;color:#fff;
  text-shadow:0 1px 10px rgba(3,17,11,.72)
}
.hero-tile .price{
  border-top:0;padding-top:0;margin:5px 0 0;
  font-family:var(--f-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  /* .74 alpha with no shadow, at 10px, was the least legible type on the site */
  color:rgba(238,245,241,.95);text-shadow:0 1px 10px rgba(3,17,11,.75);
  transition:color .38s
}
.hero-tile:hover .price,
.hero-tile.is-live .price{color:var(--hero-live)}
/* the lit sill */
.hero-tile::after{
  content:"";position:absolute;z-index:3;left:16px;right:16px;bottom:0;height:2px;border-radius:2px;
  background:var(--hero-live);
  transform:scaleX(0);transform-origin:left;
  transition:transform .5s cubic-bezier(.16,1,.3,1)
}
.hero-tile.is-live::after{transform:scaleX(1)}
/* over the track: what it is, and the arrows that turn it */
.hero-tiles-head{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin-bottom:12px
}
.hero-tiles-title{
  font-family:var(--f-mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;
  color:rgba(238,245,241,.95);text-shadow:0 1px 10px rgba(3,17,11,.75)
}
.hero-tiles-nav{display:flex;gap:8px}
.hero-tiles-nav button{
  width:40px;height:40px;border-radius:50%;display:grid;place-items:center;
  margin:0;padding:0;color:#fff;cursor:pointer;
  border:1px solid rgba(255,255,255,.42);background:rgba(255,255,255,.1);
  backdrop-filter:blur(12px) saturate(120%);-webkit-backdrop-filter:blur(12px) saturate(120%);
  box-shadow:0 8px 22px rgba(3,15,10,.24), inset 0 1px 0 rgba(255,255,255,.2);
  transition:background .25s, border-color .25s, transform .25s cubic-bezier(.16,1,.3,1)
}
.hero-tiles-nav button:hover{background:rgba(255,255,255,.22);border-color:#fff}
.hero-tiles-nav button:active{transform:scale(.94)}
.hero-tiles-nav svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}
/* under the track: one dash per stop, the current one long and lit */
.hero-dots{display:flex;gap:6px;margin-top:14px}
.hero-dots button{
  width:16px;height:3px;border-radius:2px;margin:0;padding:0;border:0;cursor:pointer;
  background:rgba(255,255,255,.38);position:relative;
  transition:width .45s cubic-bezier(.16,1,.3,1), background .3s
}
/* a taller hit area than the 3px dash shows */
.hero-dots button::before{content:"";position:absolute;inset:-10px -3px}
.hero-dots button.is-on{width:34px;background:var(--hero-live)}

/* ---- white contact bar + copyright strip ---- */
/* was #fff with #8d8d8d text (3.54:1) and an #a5a5a5 copyright line (2.59:1),
   both under AA for their size. Warmed onto the page ground and darkened. */
.hero-bar{background:var(--paper);border-bottom:1px solid var(--line)}
.hero-bar .in{
  max-width:1340px;margin:0 auto;padding:19px clamp(20px,4vw,54px);
  display:flex;align-items:center;gap:26px;flex-wrap:wrap
}
.hero-bar .item{display:flex;align-items:center;gap:9px;font-size:13px;color:var(--muted)}
.hero-bar .item a{color:var(--muted);text-decoration:none}
.hero-bar .item a:hover{color:var(--moss)}
.hero-bar svg{width:13px;height:15px;fill:var(--brass);flex:none}
.hero-bar .socials{margin-left:auto;display:flex;gap:6px;margin-right:-6px}
.hero-bar .socials a{color:var(--muted);display:grid;place-items:center;width:32px;height:32px}
.hero-bar .socials a:hover{color:var(--moss)}
.hero-bar .socials svg{width:15px;height:15px;fill:currentColor}
.hero-copyright{background:var(--paper);border-bottom:1px solid var(--line)}
.hero-copyright .in{
  max-width:1340px;margin:0 auto;padding:15px clamp(20px,4vw,54px);
  font-size:12px;color:var(--muted)
}

/* ---- responsive ---- */
@media(max-width:1080px){
  .hero-copy{max-width:100%}
  .hero-tiles-wrap{width:clamp(280px,44vw,420px)}
}
@media(max-width:940px){
  .head-in{grid-template-columns:auto auto;justify-content:space-between;height:66px}
  .site-head.over .head-in{grid-template-columns:auto auto;height:58px;padding:0 8px 0 16px}
  .burger{display:block;justify-self:end}
  .head-cta{display:none}
  .nav{display:none}
  .site-head.over .nav{display:none}
  .site-head.over .head-cta{display:none}
  .site-head.over .burger{display:block;justify-self:end}
}
@media(max-width:820px){
  :root{--rail-w:44px}
  .hero::before{
    background:linear-gradient(180deg,
      rgba(3,17,12,.72) 0%,
      rgba(3,17,12,.48) 38%,
      rgba(3,17,12,.60) 100%)
  }
  .hero{min-height:auto}
  .hero-inner{
    grid-template-columns:1fr;padding-right:clamp(18px,5vw,28px);
    padding-bottom:34px;gap:34px
  }
  .hero-rail .place{display:none}
  .hero-tiles-wrap{width:100%;align-self:auto}
  /* a phone shows one and a half panes, so the half says there is more */
  .hero-tiles{--in-view:1.6}
  .hero-note{margin-top:0}
}
@media(max-width:480px){
  .hero-tile{min-height:112px;padding:14px 15px 13px}
  .hero-tile .lbl{font-size:14px}
}

@media(prefers-reduced-motion:reduce){
  *{animation:none!important;transition:none!important;scroll-behavior:auto!important}
  /* .hero-tiles-wrap is opacity:0 until heroRise fills it, and the rule above has
     just killed every animation — so without this the entire booking menu
     renders fully transparent while staying in the layout, focusable and
     clickable. Anything that starts at opacity:0 must be listed here. */
  .hero-h1 span,.hero-note,.hero-act,.hero-cta span,.hero-tiles-wrap{opacity:1;transform:none}
  .hero-copy,.hero-parallax,.hero-tile-img{transform:none}
  .hero-slide.is-on img{transform:scale(1.02)}
  .navmask.open a{opacity:1;transform:none}
}


/* ============================================================
   PAGE HEAD — the estate, not a green rectangle
   ------------------------------------------------------------
   Who's here, Our spaces and General information all opened on a
   flat --ink band with the title in the left half and roughly six
   hundred pixels of empty dark green to the right of it. On a site
   whose entire proposition is thirty-eight acres of Victorian
   parkland, the three biggest surfaces after the hero were showing
   the visitor nothing at all.

   So the band keeps its exact structure — eyebrow, h1, lede, spec
   strip — and gains a photograph behind it. Two washes hold the
   type: one across the page dense on the left where the words are
   and clearing by mid-frame so the picture survives, one down from
   the top so the floating header bar always has something to sit
   on. The image is set per page from the markup with --shot.
   ============================================================ */
.band--shot{
  position:relative;isolation:isolate;background:var(--ink);overflow:hidden;
  padding-top:calc(var(--bar-h) + clamp(46px,6vw,88px));
  padding-bottom:clamp(42px,5.5vw,72px)
}
.band--shot::before{
  content:"";position:absolute;inset:0;z-index:-2;
  background-image:var(--shot);background-size:cover;
  background-position:var(--shot-pos,50% 42%);
  transform:scale(1.03)
}
.band--shot::after{
  content:"";position:absolute;inset:0;z-index:-1;
  background:
    linear-gradient(93deg,
      rgba(11,33,26,.93) 0%,
      rgba(11,33,26,.84) 32%,
      rgba(11,33,26,.56) 60%,
      rgba(11,33,26,.36) 100%),
    linear-gradient(180deg,
      rgba(11,33,26,.70) 0%,
      rgba(11,33,26,.10) 38%,
      rgba(11,33,26,0)   60%)
}
/* the head's own type, pale on the photograph */
.band--shot .h-sec{color:#fff;max-width:16ch}
.band--shot .lede{color:rgba(255,255,255,.90);max-width:52ch}
.band--shot .eyebrow{color:#fff;opacity:.92}
.band--shot .eyebrow::before{background:var(--brass-lt)}
.band--shot .spec{
  border-color:rgba(255,255,255,.30);color:rgba(255,255,255,.80)
}
.band--shot .spec span{border-color:rgba(255,255,255,.20)}
.band--shot .spec b{color:#fff;font-weight:500}

/* Below the fold of a phone the graded wash across the page is the wrong
   axis — there is no right-hand half to protect — so it becomes a straight
   top-to-bottom darkening and the picture keeps the bottom of the band. */
@media(max-width:760px){
  .band--shot::after{
    background:linear-gradient(180deg,
      rgba(11,33,26,.88) 0%,
      rgba(11,33,26,.74) 45%,
      rgba(11,33,26,.62) 100%)
  }
  .band--shot .h-sec{max-width:none}
}

/* ---------- emphasised words in a section heading ----------
   Used by <Emph>, which marks words inside a translatable string. The
   emphasis is carried by weight, colour and a rule under the word rather than
   by italics — the display face is set tight and its italic reads as a
   different voice rather than as stress. The rule is drawn with a gradient
   background instead of text-decoration so it can sit clear of the descenders
   at this size. */
.h-sec .emph{
  font-style:normal;font-weight:700;color:var(--moss);
  background-image:linear-gradient(var(--moss-2),var(--moss-2));
  background-repeat:no-repeat;
  background-size:100% .09em;
  background-position:0 calc(100% - .06em);
  padding-bottom:.10em
}

/* ---------- Rightmove link ----------
   A row, not a card: it sits under the three unit cards and points off the
   site, so it should read as the next line of the page rather than as a
   fourth unit competing with them. */
.rm{
  display:flex;align-items:center;gap:clamp(14px,2vw,22px);
  margin-top:26px;padding:20px clamp(18px,2.4vw,26px);
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-md,14px);
  text-decoration:none;color:inherit;
  transition:border-color .2s, box-shadow .2s, transform .2s
}
.rm:hover{
  border-color:var(--moss);
  box-shadow:0 10px 30px rgba(6,126,72,.13);
  transform:translateY(-2px)
}
.rm__mark{
  flex:none;display:grid;place-items:center;
  width:46px;height:46px;border-radius:50%;
  background:var(--moss);color:#fff
}
.rm__mark svg{width:24px;height:24px}
.rm__copy{display:flex;flex-direction:column;gap:4px;min-width:0}
.rm__copy b{
  font-family:var(--f-disp);font-weight:600;font-size:16.5px;letter-spacing:-.008em;color:var(--ink)
}
.rm__copy span{font-size:14px;color:var(--muted)}
.rm__go{margin-left:auto;flex:none;font-size:20px;color:var(--moss);transition:transform .2s}
.rm:hover .rm__go{transform:translate(3px,-3px)}
@media (max-width:560px){
  .rm{align-items:flex-start}
  .rm__go{display:none}
}

/* The 40px tinted tiles that used to sit here were replaced by the drawings —
   see "illustrations" at the foot of this file. */
@media (prefers-reduced-motion:reduce){
  .rm,.rm__go{transition:none}
  .rm:hover{transform:none}
}

/* ---------- illustrations ----------
   One drawing style across the site: an ink outline with a single accent in
   the lighter brand green, drawn on a 64 grid (see Glyph.tsx). The colour is
   set here once — the artwork inherits it through currentColor — so a card on
   a dark band could invert the whole set by changing one property. */
.glyph{
  display:block;
  width:clamp(52px,5.4vw,64px);
  height:clamp(52px,5.4vw,64px);
  margin-bottom:16px;
  color:var(--ink);
  transition:color .22s, transform .22s;
}
.glyph svg{width:100%;height:100%;display:block;overflow:visible}

/* On the amenity panels the drawing is the thing that carries the section, so
   it sits above the term and the panel lifts as one on hover. */
.facts--marked .fact{padding-top:24px;transition:border-color .2s, box-shadow .2s}
.facts--marked .fact:hover{border-color:var(--moss);box-shadow:0 8px 26px rgba(6,126,72,.10)}
.facts--marked .fact:hover .glyph{color:var(--moss)}

/* On the three "what brings you here" cards the drawing replaces nothing —
   it sits above the category label, which keeps its place as the eyebrow. */
.route .glyph{margin-bottom:18px}
.route:hover .glyph{color:var(--moss);transform:translateY(-2px)}

@media (prefers-reduced-motion:reduce){
  .glyph{transition:none}
  .route:hover .glyph{transform:none}
}

/* ---------- very wide screens ----------
   The shell was capped at 1220px, which on a 2560px monitor left 52% of the
   screen as empty margin and on an ultrawide 56% — the page sat in a strip
   down the middle.

   Widening it is safe because the reading measure is capped separately and in
   ch, not by the shell: .lede at 58ch, .loc__lede at 52, the history at 64.
   So the extra width goes where there is something to see — the estate's
   photographs, the logo wall, the card grids and the plan — and the prose
   keeps the line length it was set at.

   --shell is the header and hero rail, which sit wider than the text bands
   and need to keep that relationship as both grow. */
@media (min-width:1500px){ :root{ --wrap:1340px; --shell:1560px } }
@media (min-width:1800px){ :root{ --wrap:1520px; --shell:1780px } }
@media (min-width:2200px){ :root{ --wrap:1760px; --shell:2040px } }
@media (min-width:2800px){ :root{ --wrap:2000px; --shell:2320px } }

/* With the room to do it, the grids take another column rather than letting
   each cell grow to the size of a poster. */
@media (min-width:1800px){
  .logos{grid-template-columns:repeat(5,1fr)}
  .facts{grid-template-columns:repeat(3,1fr)}
}
@media (min-width:2400px){
  .logos{grid-template-columns:repeat(6,1fr)}
  .routes{gap:clamp(18px,1.4vw,26px)}
}

/* ---------- logo wall: four scrolling rows ----------
   The whole directory, not the sixteen we hold artwork for. Rows alternate
   direction so the wall reads as movement rather than as a conveyor, and each
   row's duration is set from its own tile count in LogoWall.tsx so a longer
   row does not travel faster.

   The track holds every row twice and travels exactly -50%: at that point the
   second copy sits where the first began, so the loop has no seam. */
.wall{
  margin-top:clamp(26px,3vw,34px);
  display:flex;flex-direction:column;gap:1px;
  background:var(--line-str);
  border:1px solid var(--line-str);
  border-radius:var(--r);
  overflow:hidden;
  /* the rows run to the edges, so fade them out rather than cutting them */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent);
}
.wall__row{overflow:hidden;background:var(--paper)}
.wall__track{
  display:flex;width:max-content;
  animation:wallRun var(--dur,80s) linear infinite;
}
.wall__track.is-rev{animation-direction:reverse}
@keyframes wallRun{to{transform:translateX(-50%)}}

/* A moving link is a link you cannot click. Any hover or keyboard focus
   anywhere in the wall stops every row, not just the one under the pointer —
   stopping one row while its neighbours run reads as a fault. */
.wall:hover .wall__track,
.wall:focus-within .wall__track{animation-play-state:paused}

.wall__tile{
  flex:none;
  width:clamp(150px,15vw,196px);
  height:clamp(84px,8.6vw,104px);
  display:grid;place-items:center;
  padding:14px 20px;
  border-right:1px solid var(--line);
  background:color-mix(in srgb, var(--brand,transparent) 9%, var(--paper));
  text-decoration:none;
  transition:background .22s;
}
a.wall__tile:hover{background:color-mix(in srgb, var(--brand,var(--moss)) 20%, var(--paper))}
.wall__tile.is-rev{background:var(--brand)}
a.wall__tile.is-rev:hover{background:color-mix(in srgb, var(--brand) 86%, #000)}

.wall__tile img{
  --box:clamp(38px,4vw,50px);
  width:100%;max-width:calc(150px * var(--fit,1));
  height:calc(var(--box) * var(--fit,1));
  object-fit:contain;
  mix-blend-mode:multiply;
}
.wall__tile img.is-tall{--box:clamp(52px,5.6vw,68px)}
/* A white mark on its own brand colour must not multiply — multiply drops
   white, which would drop the mark and leave an empty tile. */
.wall__tile.is-rev img{mix-blend-mode:normal}

/* No artwork yet: the name, set as a wordmark. */
.wall__name{
  font-family:var(--f-disp);font-weight:600;font-size:13.5px;line-height:1.3;
  letter-spacing:-.008em;text-align:center;color:var(--muted);
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
}
a.wall__tile.is-word:hover .wall__name{color:var(--moss)}

.wall__note{
  grid-column:1/-1;margin:14px 0 0;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  display:flex;flex-wrap:wrap;gap:8px 22px;align-items:center;
}

/* Movement is the whole idea here, so with it switched off the wall becomes a
   strip the reader scrolls themselves rather than a frozen crop. */
@media (prefers-reduced-motion:reduce){
  .wall__row{overflow-x:auto}
  .wall__track{animation:none;width:auto}
  .wall__track > [aria-hidden]{display:none}
}

/* ---------- amenity cards: the photograph behind ----------
   Hovering a card fades in a photograph of the thing it describes. The text
   has to stay readable over whatever the photograph happens to be, so it is
   not tinted — it is inverted onto a scrim dark enough to guarantee contrast
   regardless of the image: white on rgba(7,36,26,.78) clears 7:1 even where
   the photograph underneath is pure white.

   isolation:isolate makes the card its own stacking context, so the two
   negative-z layers stay inside it and behind the text rather than sliding
   under the card itself. */
.facts--marked .fact{position:relative;overflow:hidden;isolation:isolate}
.fact.has-shot::before,
.fact.has-shot::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  opacity:0;transition:opacity .34s ease;
}
.fact.has-shot::before{
  z-index:-2;
  background-image:var(--shot);
  background-size:cover;background-position:center;
  transform:scale(1.05);
  transition:opacity .34s ease, transform .55s ease;
}
.fact.has-shot::after{
  z-index:-1;
  background:linear-gradient(180deg, rgba(7,36,26,.66) 0%, rgba(7,36,26,.84) 100%);
}
.fact.has-shot:hover::before,
.fact.has-shot:focus-within::before{opacity:1;transform:none}
.fact.has-shot:hover::after,
.fact.has-shot:focus-within::after{opacity:1}

/* Everything in the card follows the scrim, so nothing is left dark-on-dark. */
.fact.has-shot{transition:border-color .22s}
.fact.has-shot:hover,
.fact.has-shot:focus-within{border-color:var(--ink)}
.fact.has-shot:hover h4,
.fact.has-shot:focus-within h4{color:#fff}
.fact.has-shot:hover p,
.fact.has-shot:focus-within p{color:rgba(255,255,255,.88)}
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{color:var(--moss-2)}
.fact.has-shot h4,.fact.has-shot p{transition:color .28s}

@media (prefers-reduced-motion:reduce){
  .fact.has-shot::before{transform:none;transition:opacity .01s}
}

/* ---------- illustration movement ----------
   Each drawing carries one class on the part that would actually move, and it
   runs only while the card is hovered or focused. The motion is meant to say
   something about the subject — wheels turn, steam rises, a stopwatch hand
   sweeps — so nothing animates where nothing in the subject moves.

   transform-box:fill-box makes every transform-origin relative to the shape's
   own box rather than the whole 64 grid, which is what lets a clock hand pivot
   on its own end and a canopy lean from its trunk. */
.glyph [class^="g-"]{
  transform-box:fill-box;
  transform-origin:center;
}
/* The hands are drawn outward FROM the pivot, so the pivot is the corner of
   their own box, not its centre. */
.glyph .g-sweep{transform-origin:0 0}
.glyph .g-sway{transform-origin:50% 100%}
.glyph .g-open{transform-origin:50% 100%}
.glyph .g-pop{transform-origin:50% 100%}
.glyph .g-curl{transform-origin:center}

/* Nothing moves until the card is hovered or holds focus. */
.fact:hover .glyph .g-spin,
.fact:focus-within .glyph .g-spin,
.route:hover .glyph .g-spin{animation:gSpin 2.4s linear infinite}

.fact:hover .glyph .g-sweep,
.fact:focus-within .glyph .g-sweep,
.route:hover .glyph .g-sweep{animation:gSweep 2.6s cubic-bezier(.5,0,.5,1) infinite}

.fact:hover .glyph .g-rise,
.fact:focus-within .glyph .g-rise{animation:gRise 1.9s ease-out infinite}

.fact:hover .glyph .g-lift,
.fact:focus-within .glyph .g-lift{animation:gLift 1.5s ease-in-out infinite}

.fact:hover .glyph .g-sway,
.fact:focus-within .glyph .g-sway{animation:gSway 2.6s ease-in-out infinite}

.fact:hover .glyph .g-flash,
.fact:focus-within .glyph .g-flash,
.route:hover .glyph .g-flash{animation:gFlash 1.6s ease-in-out infinite}

.fact:hover .glyph .g-press,
.fact:focus-within .glyph .g-press{animation:gPress 1.4s ease-in-out infinite}

.fact:hover .glyph .g-open,
.fact:focus-within .glyph .g-open{animation:gOpen 2.2s ease-in-out infinite}

.fact:hover .glyph .g-curl,
.fact:focus-within .glyph .g-curl{animation:gCurl 1.8s ease-in-out infinite}

.route:hover .glyph .g-pop{animation:gPop 1.6s ease-in-out infinite}

@keyframes gSpin{to{transform:rotate(360deg)}}
@keyframes gSweep{to{transform:rotate(360deg)}}
@keyframes gRise{
  0%{transform:translateY(3px);opacity:.35}
  55%{opacity:1}
  100%{transform:translateY(-4px);opacity:0}
}
@keyframes gLift{0%,100%{transform:translateY(0)}50%{transform:translateY(-3.5px)}}
@keyframes gSway{0%,100%{transform:rotate(-4deg)}50%{transform:rotate(4deg)}}
@keyframes gFlash{0%,100%{opacity:.35}50%{opacity:1}}
@keyframes gPress{0%,100%{transform:scale(1)}50%{transform:scale(.88)}}
@keyframes gOpen{0%,100%{transform:scaleY(1)}50%{transform:scaleY(.55)}}
@keyframes gCurl{0%,100%{transform:translateY(0) rotate(0)}50%{transform:translateY(-3px) rotate(-7deg)}}
@keyframes gPop{0%,100%{transform:scale(1)}50%{transform:scale(1.12)}}

/* Motion is decoration here, not information — the drawings read the same
   standing still, so switching it off costs nothing. */
@media (prefers-reduced-motion:reduce){
  .glyph [class^="g-"]{animation:none!important}
}

/* ---------- route cards: the photograph behind ----------
   Same treatment as the amenity cards, so the two sections behave alike: the
   photograph fades in on hover under a scrim dark enough that the text can be
   inverted onto it and stay readable whatever the image is. The category
   label, which is brass on paper, goes to the accent green rather than white
   so it keeps reading as a label and not as a second heading. */
.route.has-shot{position:relative;overflow:hidden;isolation:isolate}
.route.has-shot::before,
.route.has-shot::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  opacity:0;transition:opacity .34s ease;
}
.route.has-shot::before{
  z-index:-2;
  background-image:var(--shot);background-size:cover;background-position:center;
  transform:scale(1.05);
  transition:opacity .34s ease, transform .55s ease;
}
.route.has-shot::after{
  z-index:-1;
  background:linear-gradient(180deg, rgba(7,36,26,.68) 0%, rgba(7,36,26,.86) 100%);
}
.route.has-shot:hover::before,
.route.has-shot:focus-visible::before{opacity:1;transform:none}
.route.has-shot:hover::after,
.route.has-shot:focus-visible::after{opacity:1}

.route.has-shot .rn,
.route.has-shot .h-sub,
.route.has-shot p,
.route.has-shot .go{transition:color .28s}
.route.has-shot:hover{background:transparent;border-color:var(--ink)}
.route.has-shot:hover .rn,
.route.has-shot:focus-visible .rn{color:var(--moss-2)}
.route.has-shot:hover .h-sub,
.route.has-shot:focus-visible .h-sub{color:#fff}
.route.has-shot:hover p,
.route.has-shot:focus-visible p{color:rgba(255,255,255,.88)}
.route.has-shot:hover .go,
.route.has-shot:focus-visible .go{color:var(--moss-2)}
.route.has-shot:hover .glyph{color:var(--moss-2)}

@media (prefers-reduced-motion:reduce){
  .route.has-shot::before{transform:none;transition:opacity .01s}
}

/* ---------- accents on a photograph ----------
   --moss-2 is tuned for the pale page, and on the scrim over a photograph it
   drops to 1.74:1 — the category labels on the route cards were effectively
   unreadable. This is the same green lifted for a dark ground: 6.02:1 on the
   scrim at its lightest, measured against a WHITE photograph, which is the
   worst case the image can present. The scrim is deepened to match. */
:root{ --moss-on-shot:#6FD6A2 }

.route.has-shot::after{
  background:linear-gradient(180deg, rgba(7,36,26,.80) 0%, rgba(7,36,26,.90) 100%);
}
.fact.has-shot::after{
  background:linear-gradient(180deg, rgba(7,36,26,.78) 0%, rgba(7,36,26,.88) 100%);
}

.route.has-shot:hover .rn,
.route.has-shot:focus-visible .rn,
.route.has-shot:hover .go,
.route.has-shot:focus-visible .go{color:var(--moss-on-shot)}
.route.has-shot:hover .glyph{color:var(--moss-on-shot)}
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{color:var(--moss-on-shot)}

/* ---------- cards over a photograph: everything goes white ----------
   On the scrim the brand greens are not readable — measured earlier at
   1.74:1 for --moss-2 — and a drawing that is half white outline and half
   green accent reads as a fault rather than as a two-tone mark. So over a
   photograph the whole card resolves to white: the label, the link, the
   drawing's outline and its accent.

   The accent keeps a little transparency rather than going pure white,
   because several drawings are a filled shape sitting inside their own
   outline — a solid canopy, a paw pad, a stacked block — and at the same
   white the fill and the line it sits in become one shape. */
.route.has-shot:hover,
.route.has-shot:focus-visible,
.fact.has-shot:hover,
.fact.has-shot:focus-within{ --glyph-accent:rgba(255,255,255,.72) }

.route.has-shot:hover .rn,/* ---------- the vine ----------
   The client asked for the logo's tree to run through the history, so the
   timeline hangs off a vine drawn in the logo's own language: an almond leaf
   with the vein cut out of it.

   It is drawn, not assembled. The first go was a dead-straight stripe with one
   leaf repeated at a perfect pitch, and no amount of easing was going to
   rescue that — the stiffness was in the artwork. So the stem meanders, on a
   sine whose period is exactly the tile height, which is what lets it still
   repeat seamlessly however tall the history turns out to be; it thickens and
   thins as it goes; the leaves come off it at irregular heights, at varying
   angles off the local tangent, at varying sizes, in three greens; and the
   tile is 300px long so the repeat does not read as a pattern.

   Stem and leaves are separate tiles, and the leaves are split across four of
   them. That is what lets the leaves stir in the wind without dragging the
   stem off the buds and branches that meet it, and lets neighbouring leaves
   move out of step instead of the whole plant sliding about as one piece.

   It is all background and pseudo-elements, so a screen reader walks the years
   and the prose and never meets a leaf. */
:root{
  --vine-stem:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M21.2,0 L21.7,5 L22.1,10 L22.6,15 L23,20 L23.5,25 L23.9,30 L24.3,35 L24.7,40 L25.1,45 L25.4,50 L25.7,55 L25.9,60 L26,65 L26.2,70 L26.2,75 L26.2,80 L26.2,85 L26,90 L25.8,95 L25.6,100 L25.3,105 L25,110 L24.6,115 L24.2,120 L23.7,125 L23.2,130 L22.7,135 L22.2,140 L21.7,145 L21.2,150 L20.7,155 L20.3,160 L19.8,165 L19.4,170 L19,175 L18.6,180 L18.3,185 L18,190 L17.8,195 L17.6,200 L17.4,205 L17.3,210 L17.2,215 L17.2,220 L17.2,225 L17.3,230 L17.3,235 L17.5,240 L17.6,245 L17.8,250 L18,255 L18.3,260 L18.6,265 L18.9,270 L19.2,275 L19.6,280 L20,285 L20.4,290 L20.8,295 L21.2,300 L24.8,300 L24.3,295 L23.8,290 L23.3,285 L22.8,280 L22.3,275 L21.8,270 L21.4,265 L21,260 L20.7,255 L20.4,250 L20.2,245 L20,240 L19.8,235 L19.8,230 L19.8,225 L19.8,220 L20,215 L20.1,210 L20.3,205 L20.6,200 L20.9,195 L21.3,190 L21.7,185 L22.1,180 L22.5,175 L23,170 L23.4,165 L23.9,160 L24.3,155 L24.8,150 L25.2,145 L25.6,140 L26,135 L26.4,130 L26.8,125 L27.1,120 L27.4,115 L27.7,110 L28,105 L28.2,100 L28.4,95 L28.5,90 L28.7,85 L28.7,80 L28.8,75 L28.8,70 L28.8,65 L28.7,60 L28.6,55 L28.4,50 L28.2,45 L28,40 L27.7,35 L27.4,30 L27,25 L26.6,20 L26.2,15 L25.7,10 L25.3,5 L24.8,0 Z' fill='%230E5C41'/%3E%3C/svg%3E");
  --vine-leaf-a:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M25.2,24 Q39.2,26.3 42.9,12.5 Q28.9,10.3 25.2,24 Z' fill='%2317795A'/%3E%3Cpath d='M25.2,24 L35.5,17.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M23.1,149 Q38.9,147.9 39.1,132.1 Q23.3,133.2 23.1,149 Z' fill='%2314684E'/%3E%3Cpath d='M23.1,149 L32.4,139.2' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M20.8,276 Q33.3,278.7 38.4,267 Q25.9,264.3 20.8,276 Z' fill='%231E9268'/%3E%3Cpath d='M20.8,276 L31,270.8' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --vine-leaf-b:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M27.2,58 Q25,45.2 12.1,46 Q14.3,58.8 27.2,58 Z' fill='%2314684E'/%3E%3Cpath d='M27.2,58 L18.4,51.1' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M20.1,183 Q19.1,170.9 6.9,171.4 Q8,183.6 20.1,183 Z' fill='%231E9268'/%3E%3Cpath d='M20.1,183 L12.4,176.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --vine-leaf-c:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M27.4,86 Q42.7,90 45.9,74.5 Q30.6,70.5 27.4,86 Z' fill='%231E9268'/%3E%3Cpath d='M27.4,86 L38.1,79.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M18.6,214 Q19.1,201.9 7,202.1 Q6.5,214.1 18.6,214 Z' fill='%2317795A'/%3E%3Cpath d='M18.6,214 L11.9,207.1' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --vine-leaf-d:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M25.6,121 Q36.2,122.8 40.1,112.8 Q29.5,110.9 25.6,121 Z' fill='%2317795A'/%3E%3Cpath d='M25.6,121 L34,116.2' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M18.9,246 Q17.3,232.3 3.5,232.3 Q5.1,246 18.9,246 Z' fill='%2314684E'/%3E%3Cpath d='M18.9,246 L10,238' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --vine-bud:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18'%3E%3Ccircle cx='9' cy='9' r='8' fill='%23FAF9F5'/%3E%3Ccircle cx='9' cy='9' r='4.4' fill='%230E5C41'/%3E%3C/svg%3E");
  --branch-r:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='54' viewBox='0 0 100 54'%3E%3Cpath d='M0.7,37.5 L6.4,34.8 L12.1,32.2 L17.9,29.8 L23.7,27.5 L29.5,25.3 L35.3,23.3 L41.1,21.5 L47,19.8 L52.9,18.2 L58.9,16.8 L64.8,15.6 L70.8,14.4 L76.8,13.5 L82.9,12.6 L88.9,12 L95,11.4 L95,10.6 L88.8,10.9 L82.7,11.4 L76.6,12.1 L70.6,12.9 L64.5,13.9 L58.5,15 L52.5,16.3 L46.5,17.7 L40.5,19.3 L34.6,21 L28.6,22.9 L22.7,24.9 L16.8,27.1 L10.9,29.4 L5.1,31.8 L-0.7,34.5 Z' fill='%230E5C41'/%3E%3Cpath d='M27.9,24.5 Q37.7,18.9 33.1,8.5 Q23.3,14.2 27.9,24.5 Z' fill='%231E9268'/%3E%3Cpath d='M27.9,24.5 L30.9,15.2' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M51.5,17.5 Q53,28.6 64.2,28 Q62.7,16.9 51.5,17.5 Z' fill='%2314684E'/%3E%3Cpath d='M51.5,17.5 L58.9,23.6' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M73.6,13.2 Q85.1,9.4 81.9,-2.3 Q70.4,1.5 73.6,13.2 Z' fill='%2317795A'/%3E%3Cpath d='M73.6,13.2 L78.4,4.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M88.2,11.5 Q89,25.7 103.2,24.5 Q102.3,10.3 88.2,11.5 Z' fill='%231E9268'/%3E%3Cpath d='M88.2,11.5 L96.9,19.1' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --branch-l:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='54' viewBox='0 0 100 54'%3E%3Cg transform='translate(100,0) scale(-1,1)'%3E%3Cpath d='M0.7,37.5 L6.4,34.8 L12.1,32.2 L17.9,29.8 L23.7,27.5 L29.5,25.3 L35.3,23.3 L41.1,21.5 L47,19.8 L52.9,18.2 L58.9,16.8 L64.8,15.6 L70.8,14.4 L76.8,13.5 L82.9,12.6 L88.9,12 L95,11.4 L95,10.6 L88.8,10.9 L82.7,11.4 L76.6,12.1 L70.6,12.9 L64.5,13.9 L58.5,15 L52.5,16.3 L46.5,17.7 L40.5,19.3 L34.6,21 L28.6,22.9 L22.7,24.9 L16.8,27.1 L10.9,29.4 L5.1,31.8 L-0.7,34.5 Z' fill='%230E5C41'/%3E%3Cpath d='M27.9,24.5 Q37.7,18.9 33.1,8.5 Q23.3,14.2 27.9,24.5 Z' fill='%231E9268'/%3E%3Cpath d='M27.9,24.5 L30.9,15.2' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M51.5,17.5 Q53,28.6 64.2,28 Q62.7,16.9 51.5,17.5 Z' fill='%2314684E'/%3E%3Cpath d='M51.5,17.5 L58.9,23.6' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M73.6,13.2 Q85.1,9.4 81.9,-2.3 Q70.4,1.5 73.6,13.2 Z' fill='%2317795A'/%3E%3Cpath d='M73.6,13.2 L78.4,4.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M88.2,11.5 Q89,25.7 103.2,24.5 Q102.3,10.3 88.2,11.5 Z' fill='%231E9268'/%3E%3Cpath d='M88.2,11.5 L96.9,19.1' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E");
  --fly-1:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cpath d='M2,15 Q15,1 28,13 Q15,27 2,15 Z' fill='%2317795A'/%3E%3Cpath d='M4,15 L18.72,13.8' fill='none' stroke='%23FAF9F5' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
  --fly-2:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cpath d='M2,15 Q15,-5 28,10 Q15,30 2,15 Z' fill='%231E9268'/%3E%3Cpath d='M4,15 L18.72,12' fill='none' stroke='%23FAF9F5' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
  --fly-3:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cpath d='M2,15 Q15,5 28,15 Q15,25 2,15 Z' fill='%2314684E'/%3E%3Cpath d='M4,15 L18.72,15' fill='none' stroke='%23FAF9F5' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* How far the vine has grown, how far a branch has reached and at what angle,
   how open a bud is. Registering them is what lets them be animated — an
   unregistered custom property jumps from one value to the next instead of
   sweeping between them.

   They start at nothing, so the vine is not on the page until the reader has
   scrolled down to it. Anything that would otherwise leave them at nothing —
   a reader who has asked for less motion — sets them to finished explicitly
   further down.

   The first inherits, unlike the rest: it is animated on the vine but read by
   the cover sitting inside it, and a property that does not inherit never
   reaches a pseudo-element child, which left the stem invisible. */
@property --vine-grow{syntax:'<percentage>';inherits:true;initial-value:0%}
@property --branch-grow{syntax:'<percentage>';inherits:false;initial-value:0%}
@property --branch-tilt{syntax:'<angle>';inherits:false;initial-value:0deg}
@property --bud-pop{syntax:'<number>';inherits:false;initial-value:0}

.vine{
  position:absolute;top:0;bottom:0;width:var(--vine-w);pointer-events:none;
  /* The cover is the vine's own height and slides a whole height downwards, so
     once the vine is grown it sits entirely below the timeline — a paper
     coloured stripe straight down the middle of whatever section comes next.
     It has to be kept inside. */
  overflow:hidden;
  background:var(--vine-stem) repeat-y center top / var(--vine-w) calc(var(--vine-w) * 300 / 46)
}
.vine i{
  position:absolute;inset:0;display:block;
  background-repeat:repeat-y;background-position:center top;
  background-size:var(--vine-w) calc(var(--vine-w) * 300 / 46)
}
.vine .vl-a{background-image:var(--vine-leaf-a)}
.vine .vl-b{background-image:var(--vine-leaf-b)}
.vine .vl-c{background-image:var(--vine-leaf-c)}
.vine .vl-d{background-image:var(--vine-leaf-d)}

/* The vine is always drawn in full. What grows is a cover over it, sliding
   down out of the way — and a cover moves on a transform, which the compositor
   handles without repainting anything.

   Animating a mask down the vine instead, which is what this did first, meant
   repainting a two thousand pixel tall element on every frame of every scroll,
   and it flickered. The soft top edge of the cover gives the arriving tip. */
.vine::after{
  content:'';position:absolute;inset:0;z-index:2;
  background:linear-gradient(to bottom,transparent 0,var(--paper) 78px,var(--paper) 100%);
  transform:translateY(var(--vine-grow))
}
/* and a permanent softening where the vine stops */
.vine::before{
  content:'';position:absolute;left:0;right:0;bottom:0;height:74px;z-index:3;
  background:linear-gradient(to top,var(--paper),transparent)
}

/* ---------- the timeline it runs down ----------
   The vine is the spine, down the middle, and the history steps past it — 1863
   to the right, 1865 to the left, and so on down. Each entry is reached by a
   branch off the stem, so the years hang from the vine rather than merely
   sitting beside it. There are no rules between the rows: a line drawn across
   the page would have to cross the vine, and the branches already say where one
   entry ends and the next begins. */
.tl{--vine-w:46px;--branch-w:clamp(58px,7vw,112px);position:relative}
.tl>.vine{left:calc(50% - var(--vine-w) / 2)}

.tl-row{
  position:relative;display:grid;
  grid-template-columns:1fr var(--vine-w) 1fr;
  column-gap:var(--branch-w);
  padding:clamp(26px,3.2vw,42px) 0;border-bottom:0
}
/* first entry to the right of the spine, then alternating. Held to a
   comfortable measure and pushed against the spine, so the two columns read as
   one thread rather than as two loose columns of prose. */
.tl-side{grid-column:3;text-align:left;max-width:46ch;margin-right:auto}
.tl-row:nth-of-type(even) .tl-side{grid-column:1;text-align:right;margin-right:0;margin-left:auto}
.tl-row p{font-family:var(--f-read);font-size:1.03rem;max-width:none;margin:10px 0 0}
/* the picture hugs the spine, like the text beside it */
.tl-row:nth-of-type(even) .tl-fig{margin-left:auto}

/* a bud on the spine beside every year, and the branch that leaves it. The bud
   is wide enough to cover wherever the meandering stem happens to be at that
   height, so the junction always closes. */
.tl-row::before,.tl-row::after{
  content:'';position:absolute;pointer-events:none;
  top:calc(clamp(26px,3.2vw,42px) + clamp(1.25rem,2.6vw,1.75rem) / 2)
}
.tl-row::before{
  width:18px;height:18px;left:calc(50% - 9px);z-index:1;
  transform:translateY(-50%) scale(var(--bud-pop));
  background:var(--vine-bud) no-repeat center / 18px 18px
}
/* Each side has its own drawing rather than one mirrored with a transform:
   the transform is wanted for the swing the branch makes as it opens, and
   transform-origin cannot serve both the mirror and the hinge. The hinge sits
   where the branch meets the stem, and a soft edge uncovers it from that end,
   so it reaches outwards rather than being wiped into view. */
.tl-row::after{
  width:calc(var(--branch-w) * var(--bw-f, 1));
  height:calc(var(--branch-w) * var(--bw-f, 1) * 54 / 100);
  left:calc(50% + 9px);transform-origin:left center;
  transform:translateY(-50%) rotate(calc(var(--tilt-base, 0deg) + var(--branch-tilt)));
  background:var(--branch-r) no-repeat left center / 100% auto;
  -webkit-mask-image:linear-gradient(to right,#000 calc(var(--branch-grow) - 26px),transparent var(--branch-grow));
  mask-image:linear-gradient(to right,#000 calc(var(--branch-grow) - 26px),transparent var(--branch-grow))
}
.tl-row:nth-of-type(even)::after{
  left:auto;right:calc(50% + 9px);transform-origin:right center;
  background-image:var(--branch-l);background-position:right center;
  -webkit-mask-image:linear-gradient(to left,#000 calc(var(--branch-grow) - 26px),transparent var(--branch-grow));
  mask-image:linear-gradient(to left,#000 calc(var(--branch-grow) - 26px),transparent var(--branch-grow))
}
.tl-row:nth-of-type(1)::after{--bw-f:1;--tilt-base:-2.2deg}
.tl-row:nth-of-type(2)::after{--bw-f:0.88;--tilt-base:2.6deg}
.tl-row:nth-of-type(3)::after{--bw-f:1.05;--tilt-base:-3.4deg}
.tl-row:nth-of-type(4)::after{--bw-f:0.93;--tilt-base:1.6deg}
.tl-row:nth-of-type(5)::after{--bw-f:0.99;--tilt-base:-1.4deg}
.tl-row:nth-of-type(6)::after{--bw-f:0.86;--tilt-base:3deg}

/* ---------- the leaves the wind has taken ----------
   Behind the history, drifting across and sinking as they go. They are pure
   decoration and carry nothing, so they sit under the content, take no
   pointer, and are hidden from a screen reader at the markup. They move on
   transform and opacity only, which the compositor can do on its own. */
.hist{position:relative}
.hist>.wrap{position:relative;z-index:1}
.hist>.wind{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0}
.hist>.wind i{
  position:absolute;left:0;display:block;opacity:0;
  background-repeat:no-repeat;background-position:center;background-size:contain
}
.hist>.wind i:nth-child(1){top:6%;width:26px;height:26px;--t:23s;--d:-3s;--dy:210px;--o:0.5;background-image:var(--fly-1)}
.hist>.wind i:nth-child(2){top:22%;width:18px;height:18px;--t:31s;--d:-14s;--dy:150px;--o:0.42;background-image:var(--fly-2)}
.hist>.wind i:nth-child(3){top:38%;width:30px;height:30px;--t:19s;--d:-8s;--dy:260px;--o:0.46;background-image:var(--fly-3)}
.hist>.wind i:nth-child(4){top:12%;width:15px;height:15px;--t:37s;--d:-25s;--dy:120px;--o:0.36;background-image:var(--fly-2)}
.hist>.wind i:nth-child(5){top:55%;width:22px;height:22px;--t:27s;--d:-19s;--dy:190px;--o:0.44;background-image:var(--fly-1)}
.hist>.wind i:nth-child(6){top:68%;width:28px;height:28px;--t:21s;--d:-6s;--dy:230px;--o:0.48;background-image:var(--fly-3)}
.hist>.wind i:nth-child(7){top:44%;width:16px;height:16px;--t:34s;--d:-30s;--dy:130px;--o:0.34;background-image:var(--fly-2)}
.hist>.wind i:nth-child(8){top:80%;width:24px;height:24px;--t:25s;--d:-11s;--dy:170px;--o:0.4;background-image:var(--fly-1)}
.hist>.wind i:nth-child(9){top:30%;width:20px;height:20px;--t:29s;--d:-22s;--dy:200px;--o:0.38;background-image:var(--fly-3)}

/* ---------- movement ----------
   Two ways in, because the feature that does this properly is not everywhere
   yet. Where the browser can tie an animation to scroll position, the vine
   draws itself down as the history passes the viewport and every entry's bud
   opens and swings out its branch as that entry arrives — real scrolling,
   running off the main thread with no scroll handler. Where it cannot, a class
   on the section plays the same thing once as it comes into view. Either way,
   nobody who has asked for less motion gets any.

   The branch opens on one set of keyframes doing two things at once: it
   lengthens, and it lifts through a small overswing into its resting angle. */
@keyframes vine-grow{from{--vine-grow:0%}to{--vine-grow:100%}}
@keyframes bud-pop{from{--bud-pop:0}to{--bud-pop:1}}
@keyframes branch-out{
  from{--branch-grow:0%;--branch-tilt:-13deg}
  55%{--branch-tilt:4deg}
  80%{--branch-tilt:-1.4deg}
  to{--branch-grow:100%;--branch-tilt:0deg}
}
/* The leaves turning in the wind.

   Sliding them sideways is the obvious move and it cannot go far: the leaves
   are attached to the stem, the stem does not slide with them, and past about
   two pixels they visibly come off it — which is why the first attempt could
   not be seen at all. Scaling them across instead, about the line of the stem,
   has no such limit. The base of every leaf sits on the origin and never
   moves, while the blade shortens and lengthens: a leaf turning edge on to the
   wind and back. Four layers, so neighbours are never in step. */
@keyframes stir-a{0%,100%{transform:scaleX(1) translate3d(-.6px,0,0)}47%{transform:scaleX(.8) translate3d(.7px,0,0)}}
@keyframes stir-b{0%,100%{transform:scaleX(.86) translate3d(.5px,0,0)}55%{transform:scaleX(1.05) translate3d(-.5px,0,0)}}
@keyframes stir-c{0%,100%{transform:scaleX(1.03) translate3d(.4px,0,0)}42%{transform:scaleX(.82) translate3d(-.6px,0,0)}}
@keyframes stir-d{0%,100%{transform:scaleX(.87) translate3d(-.4px,0,0)}60%{transform:scaleX(1.04) translate3d(.6px,0,0)}}
/* one leaf, blown across and sinking as it goes */
@keyframes blow{
  0%{opacity:0;transform:translate3d(-8vw,0,0) rotate(0deg)}
  7%{opacity:var(--o)}
  33%{transform:translate3d(26vw,calc(var(--dy) * .3),0) rotate(140deg)}
  64%{transform:translate3d(62vw,calc(var(--dy) * .72),0) rotate(290deg)}
  93%{opacity:var(--o)}
  100%{opacity:0;transform:translate3d(112vw,var(--dy),0) rotate(430deg)}
}

@media(prefers-reduced-motion:no-preference){
  @supports (animation-timeline:view()){
    /* Eighty per cent of cover grows the vine a shade slower than the page
       scrolls, so the tip drifts gently up into view rather than sitting
       pinned at the bottom edge where it cannot be seen. It still only ever
       moves one way.

       Entry was the obvious range and it is wrong here: for a subject taller
       than the screen, entry is over once its top reaches the top of the
       screen, so the whole vine was drawn across 880px of scrolling — two and
       a quarter pixels of vine per pixel scrolled. The tip raced down, then
       stopped dead with a thousand pixels of section still to go, which is
       what read as the vine running back up the page. */
    .hist .vine{animation:vine-grow linear both;animation-timeline:view();animation-range:cover 0% cover 80%}
    .tl-row{view-timeline-name:--tl-row}
    /* Measured against cover rather than entry, because the entry phase for a
       short row is over while that row is still down at the bottom edge — the
       branches were finishing 89% down the screen, where nobody was looking.
       Finishing later in cover means the row has travelled further up by then,
       so the branch opens where it can actually be watched. */
    .tl-row::before{animation:bud-pop cubic-bezier(.34,1.56,.64,1) both;
      animation-timeline:--tl-row;animation-range:cover 14% cover 38%}
    .tl-row::after{animation:branch-out cubic-bezier(.28,.72,.35,1) both;
      animation-timeline:--tl-row;animation-range:cover 18% cover 54%}
  }
  /* the fallback: one growth as the section arrives */
  .hist.vine-go .vine{--vine-grow:100%;transition:--vine-grow 1.7s cubic-bezier(.22,.61,.36,1)}
  .hist.vine-go .tl-row::before{--bud-pop:1;transition:--bud-pop .5s cubic-bezier(.34,1.56,.64,1)}
  .hist.vine-go .tl-row::after{animation:branch-out 1s cubic-bezier(.28,.72,.35,1) both .15s}
  .hist.vine-go .tl-row:nth-of-type(2)::before{transition-delay:.18s}
  .hist.vine-go .tl-row:nth-of-type(2)::after{animation-delay:.33s}
  .hist.vine-go .tl-row:nth-of-type(3)::before{transition-delay:.36s}
  .hist.vine-go .tl-row:nth-of-type(3)::after{animation-delay:.51s}
  .hist.vine-go .tl-row:nth-of-type(4)::before{transition-delay:.54s}
  .hist.vine-go .tl-row:nth-of-type(4)::after{animation-delay:.69s}
  .hist.vine-go .tl-row:nth-of-type(5)::before{transition-delay:.72s}
  .hist.vine-go .tl-row:nth-of-type(5)::after{animation-delay:.87s}
  .hist.vine-go .tl-row:nth-of-type(6)::before{transition-delay:.9s}
  .hist.vine-go .tl-row:nth-of-type(6)::after{animation-delay:1.05s}

  /* the wind: the leaves stir always, and the loose ones start blowing once
     the section has been reached, so nothing is animating off-screen before
     anyone has got there */
  .hist .vine i{transform-origin:50% 50%}
  .hist .vl-a{animation:stir-a 5.3s ease-in-out infinite}
  .hist .vl-b{animation:stir-b 7.1s ease-in-out -1.9s infinite}
  .hist .vl-c{animation:stir-c 6.2s ease-in-out -3.4s infinite}
  .hist .vl-d{animation:stir-d 8.6s ease-in-out -.8s infinite}
  .hist.wind-on>.wind i{animation:blow var(--t) linear var(--d) infinite}
}

/* Asked for less motion: the vine is simply there, whole and still, and
   nothing blows across. */
@media(prefers-reduced-motion:reduce){
  .vine{--vine-grow:100%}
  .tl-row::before{--bud-pop:1}
  .tl-row::after{--branch-grow:100%}
  .hist>.wind{display:none}
}

/* ---------- the two engravings in the timeline ---------- */
.tl-fig{margin:20px 0 0;max-width:min(520px,100%)}
.tl-fig img{
  display:block;width:100%;height:auto;border:1px solid var(--line-str);
  background:var(--stone)
}
.tl-fig figcaption{
  margin-top:9px;font-family:var(--f-read);font-size:.86rem;line-height:1.5;color:var(--muted)
}
.tl-src{display:block;font-family:var(--f-mono);font-size:10.5px;letter-spacing:.05em;
  text-transform:uppercase;color:var(--muted);margin-top:4px}

/* ---------- narrow: the spine goes back to the edge ----------
   Half a column is not enough to read in on a phone, so the entries stop
   alternating, the vine moves to the left of them, and it reads as one list
   again. There is no room for a branch to reach across, so the bud carries the
   year on its own, and the loose leaves are left to wider screens. */
@media(max-width:819px){
  .tl{--vine-w:30px;padding-left:var(--vine-w)}
  .tl>.vine{left:0}
  .tl-row{display:block;padding:clamp(22px,4vw,30px) 0}
  .tl-side,.tl-row:nth-of-type(even) .tl-side{text-align:left;max-width:none;margin:0}
  .tl-row:nth-of-type(even) .tl-fig{margin-left:0}
  .tl-row::after{display:none}
  .tl-row::before{width:14px;height:14px;background-size:14px 14px;
    left:calc(-1 * var(--vine-w) + (var(--vine-w) - 14px) / 2)}
  .tl-row+.tl-row{border-top:1px solid var(--line)}
  .hist>.wind{display:none}
}

/* ---------- the header at the top of the page ----------
   No card until it is needed. At the top the mark, the links and the booking
   button sit straight on the page; the pill forms as the page scrolls under
   it, where it earns its place by keeping the links readable over content.
   The background, edge and shadow fade in together with the colour change the
   bar already makes, so it arrives as one thing. */
.site-head.at-top .head-in{
  background:transparent;border-color:transparent;box-shadow:none;
  backdrop-filter:none;-webkit-backdrop-filter:none
}
/* With no glass behind them, pale letters over a photograph need their own
   shadow to hold up where the picture is bright — sky, a white wall. */
.site-head.over.at-top .brand-tx b,
.site-head.over.at-top .nav a{text-shadow:0 1px 3px rgba(3,15,10,.7), 0 1px 14px rgba(3,15,10,.55)}
.site-head.over.at-top .brand img{filter:drop-shadow(0 1px 6px rgba(3,15,10,.45))}
.site-head.over.at-top .lang{background:rgba(4,20,15,.28)}

/* ---------- the crystal veil over the hero, with the logo cut out of it ----------
   A pane of green crystal covers the whole photograph. On the right the
   estate's mark is subtracted from it, so inside the circle the picture shows
   clear and at full strength and the tree — the transparent part of the mark —
   stays behind the glass. The emblem is drawn out of the estate itself.

   The glass is four things, top to bottom: cut facets (crystal.svg, faces
   catching light from the top left, fine bright lines along the cuts), a soft
   sheen across the pane, the logo's lighter green (#00A55F) as the tint, and a
   frost — the photograph behind is blurred and dimmed a little, so the clear
   picture inside the circle reads as a window cut through solid glass.

   The logo is two mask layers composited: a solid layer the size of the hero,
   and the mark. "exclude" keeps only what is in one and not the other, which
   is the whole hero less the logo. The mask clips the frost too, so nothing
   inside the circle is blurred. The mark is a traced vector of mark.png, so its
   edge stays crisp at hero size on a sharp screen.

   The veil does not move. The slides keep their slow drift and their
   parallax, so the picture moves behind the glass and through the window while
   the mark holds still. */
.hero-veil{
  --mark:min(74vh,50vw);
  --mark-x:clamp(24px,5vw,90px);
  --glass:rgba(0,165,95,.3);
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    url("/images/crystal.svg") center/cover no-repeat,
    linear-gradient(118deg, rgba(255,255,255,.12) 0%, rgba(255,255,255,0) 32%, rgba(255,255,255,.07) 58%, rgba(255,255,255,0) 74%),
    linear-gradient(var(--glass), var(--glass));
  -webkit-backdrop-filter:blur(7px) brightness(.62) saturate(1.25);
  backdrop-filter:blur(7px) brightness(.62) saturate(1.25);
  -webkit-mask-image:linear-gradient(#000 0 0),url("/images/mark-mask.svg");
  mask-image:linear-gradient(#000 0 0),url("/images/mark-mask.svg");
  -webkit-mask-size:100% 100%,var(--mark) var(--mark);
  mask-size:100% 100%,var(--mark) var(--mark);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:0 0,right var(--mark-x) top 44%;
  mask-position:0 0,right var(--mark-x) top 44%;
  -webkit-mask-composite:xor;mask-composite:exclude
}
/* The cut edge of the glass round the circle: a bright bevel line and a soft
   shadow falling outward. Drawn as a ring the same size and place as the
   mark; being inside the veil it is masked with it, so it only shows round
   the outside of the circle. */
.hero-veil::after{
  content:"";position:absolute;
  width:var(--mark);height:var(--mark);
  right:var(--mark-x);top:calc((100% - var(--mark)) * .44);
  border-radius:50%;
  box-shadow:0 0 0 1.5px rgba(200,255,225,.34), 0 0 0 5px rgba(255,255,255,.05), 0 0 80px rgba(2,30,18,.4)
}

/* On a phone the copy fills the width and there is no clear side to cut a
   window into, so the mark sits large in the upper corner and runs off the
   edge — present, but out from under the headline. */
@media(max-width:820px){
  .hero-veil{
    --mark:92vw;--mark-x:-30vw;
    -webkit-mask-position:0 0,right var(--mark-x) top -6vw;
    mask-position:0 0,right var(--mark-x) top -6vw
  }
  .hero-veil::after{top:-6vw}
}
/* No frost where the browser cannot blur behind an element: a deeper tint
   keeps the headline side as quiet as the glass would. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .hero-veil{--glass:rgba(0,95,55,.6)}
}

/* ---------- the two alternative templates ----------
   Same homepage, same photographs, same booking carousel; only the veil over
   the hero differs, and on both of these the mask is the logo's branches
   alone — no circle round them. Served at /template-2 and /template-3 so the
   three can be looked at side by side.

   Both drop the crystal facets and the sheen: the base rule's background is
   replaced outright rather than layered on. */
.hero-veil--white,
.hero-veil--frost{
  -webkit-mask-image:linear-gradient(#000 0 0),url("/images/mark-tree.svg");
  mask-image:linear-gradient(#000 0 0),url("/images/mark-tree.svg")
}
/* the circle belongs to the crystal template alone */
.hero-veil--white::after,
.hero-veil--frost::after{content:none}

/* ---- template 2: a white wash ----
   White over the photograph, and the branches cut out of it in full colour.
   Nothing is blurred: the picture stays sharp and simply pales, so the tree
   reads as the only saturated thing on the page. */
.hero-veil--white{
  --mark:min(78vh,52vw);
  background:rgba(255,255,255,.72);
  -webkit-backdrop-filter:saturate(.88);backdrop-filter:saturate(.88)
}
/* The page's two reading scrims are dark washes for pale type. On a white
   hero they would show as grey smears, and the type they were protecting is
   dark here anyway, so the side wash goes and the header band turns white. */
.hero--white::before{background:none}
.hero--white::after{
  background:linear-gradient(180deg, rgba(255,255,255,.85) 0%, rgba(255,255,255,.4) 55%, rgba(255,255,255,0) 100%)
}
/* Everything that was pale type on a dark picture is dark type on a pale one:
   the rail, the seek dashes, and the carousel's own furniture. The copy pane
   and the room cards keep their dark glass — they are the contrast. */
.hero--white .hero-rail{border-right-color:rgba(9,42,29,.18)}
.hero--white .hero-rail .place{color:rgba(9,42,29,.78);text-shadow:none}
.hero--white .hero-rail .rail-social a{color:rgba(9,42,29,.72);filter:none}
.hero--white .hero-rail .rail-social a:hover{color:var(--moss)}
.hero--white .hero-seek button::before{background:rgba(9,42,29,.3);box-shadow:none}
.hero--white .hero-seek button::after{background:var(--moss)}
.hero--white .hero-tiles-title{color:rgba(9,42,29,.82);text-shadow:none}
.hero--white .hero-tiles-nav button{
  color:var(--moss);border-color:rgba(9,42,29,.28);background:rgba(255,255,255,.6);
  box-shadow:0 8px 22px rgba(9,42,29,.12)
}
.hero--white .hero-tiles-nav button:hover{background:#fff;border-color:var(--moss)}
.hero--white .hero-dots button{background:rgba(9,42,29,.26)}
.hero--white .hero-dots button.is-on{background:var(--moss)}

/* ---- template 3: plain frosted glass ----
   No colour of its own and no texture: the photograph is blurred behind a
   thin white frost, the way a bathroom pane blurs what is behind it, and the
   branches are wiped clear of it. Dimmed a shade so the pale type on the copy
   pane, the rail and the header keeps the contrast it has on the homepage. */
.hero-veil--frost{
  --mark:min(78vh,52vw);
  background:rgba(255,255,255,.14);
  -webkit-backdrop-filter:blur(16px) brightness(.68) saturate(1.05);
  backdrop-filter:blur(16px) brightness(.68) saturate(1.05)
}
/* Without a blur to fall back on the frost is only a pale film, so it carries
   the dimming itself. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .hero-veil--frost{background:rgba(9,30,22,.52)}
}

/* ---- template 4: plain green glass, crystal in the logo's branches ----
   The homepage's own veil — the logo green, the frost, the sheen, the circle
   and its bevel — with one change: the facets come off the pane, which is
   left smooth, and are set into the branches and leaves instead. The picture
   inside the circle is still clear and sharp; the tree is cut crystal. */
.hero-veil--gem{
  background:
    linear-gradient(118deg, rgba(255,255,255,.12) 0%, rgba(255,255,255,0) 32%, rgba(255,255,255,.07) 58%, rgba(255,255,255,0) 74%),
    linear-gradient(var(--glass), var(--glass))
}
/* The facet layer. Sized and placed with the same numbers as the veil's mask,
   and masked by mark-tree.svg — the branches and leaves alone — so it lies
   on the tree and nowhere else: not on the clear picture round it, not on the
   pane. crystal-gem.svg is a square cut of the facets with stronger faces and
   cut lines, so they still read on branches a few dozen pixels wide. */
.hero-gem{
  --mark:min(74vh,50vw);
  --mark-x:clamp(24px,5vw,90px);
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:url("/images/crystal-gem.svg") no-repeat;
  background-size:var(--mark) var(--mark);
  background-position:right var(--mark-x) top 44%;
  -webkit-mask-image:url("/images/mark-tree.svg");mask-image:url("/images/mark-tree.svg");
  -webkit-mask-size:var(--mark) var(--mark);mask-size:var(--mark) var(--mark);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:right var(--mark-x) top 44%;mask-position:right var(--mark-x) top 44%
}
@media(max-width:820px){
  .hero-gem{
    --mark:92vw;--mark-x:-30vw;
    background-position:right var(--mark-x) top -6vw;
    -webkit-mask-position:right var(--mark-x) top -6vw;mask-position:right var(--mark-x) top -6vw
  }
}

/* ---------- picture cards: the History page's way on ----------
   Unlike the route cards on the homepage, the photograph is always there — no
   hover to reveal it and no scrim over the whole of it. The picture stays
   clear at the top, and a gradient deepens only toward the foot, where the
   words sit, so they read whatever the image is. Hover just draws the picture
   in a little. */
.routes--pics{grid-template-columns:repeat(4,1fr)}
@media(max-width:1100px){.routes--pics{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.routes--pics{grid-template-columns:1fr}}

.route.route--pic{
  position:relative;overflow:hidden;isolation:isolate;
  min-height:clamp(340px,34vw,420px);justify-content:flex-end;
  background:var(--ink);border-color:transparent;color:#fff
}
.route.route--pic::before,
.route.route--pic::after{content:"";position:absolute;inset:0;pointer-events:none}
.route.route--pic::before{
  z-index:-2;
  background-image:var(--shot);background-size:cover;background-position:center;
  transition:transform .8s cubic-bezier(.16,1,.3,1)
}
.route.route--pic::after{
  z-index:-1;
  background:linear-gradient(180deg,
    rgba(7,36,26,0)   0%,
    rgba(7,36,26,.08) 22%,
    rgba(7,36,26,.62) 48%,
    rgba(7,36,26,.94) 100%)
}
.route.route--pic:hover{background:var(--ink);border-color:transparent}
.route.route--pic:hover::before,
.route.route--pic:focus-visible::before{transform:scale(1.06)}

.route.route--pic .rn{color:var(--moss-on-shot);margin-bottom:10px;text-shadow:0 1px 8px rgba(3,15,10,.6)}
.route.route--pic .h-sub{color:#fff;font-size:clamp(1.25rem,1.55vw,1.5rem);line-height:1.2;text-shadow:0 1px 12px rgba(3,15,10,.55)}
.route.route--pic p{color:rgba(255,255,255,.9);margin-bottom:18px;text-shadow:0 1px 10px rgba(3,15,10,.6)}
.route.route--pic .go{margin-top:0;color:var(--moss-on-shot)}
@media (prefers-reduced-motion:reduce){
  .route.route--pic::before{transition:none}
  .route.route--pic:hover::before{transform:none}
}

/* ---------- amenity cards: the photograph always showing ----------
   These used to fade their photograph in on hover under a full-card scrim.
   Now, like the History page's cards, the picture is always there and left
   clear at the top; the words sit at the foot of a taller card, and a
   gradient deepens only under them. Hover draws the picture in a little. */
.facts--marked .fact.has-shot{
  min-height:clamp(250px,24vw,300px);
  display:flex;flex-direction:column;justify-content:flex-end;
  background:var(--ink);border-color:transparent
}
.fact.has-shot::before,
.fact.has-shot::after{opacity:1}
.fact.has-shot::before{transform:none;transition:transform .8s cubic-bezier(.16,1,.3,1)}
.fact.has-shot::after{
  background:linear-gradient(180deg,
    rgba(7,36,26,0)   0%,
    rgba(7,36,26,.10) 26%,
    rgba(7,36,26,.66) 55%,
    rgba(7,36,26,.94) 100%)
}
.fact.has-shot:hover::before,
.fact.has-shot:focus-within::before{transform:scale(1.06)}

.fact.has-shot h4{color:#fff;text-shadow:0 1px 10px rgba(3,15,10,.55)}
.fact.has-shot p{color:rgba(255,255,255,.9);text-shadow:0 1px 10px rgba(3,15,10,.6)}
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{color:var(--moss-on-shot)}
.facts--marked .fact.has-shot:hover,
.facts--marked .fact.has-shot:focus-within{border-color:transparent;box-shadow:0 14px 34px rgba(11,33,26,.18)}
@media (prefers-reduced-motion:reduce){
  .fact.has-shot::before{transition:none}
  .fact.has-shot:hover::before,.fact.has-shot:focus-within::before{transform:none}
}

/* ---------- the header, a size up ----------
   The mark was 34px and the links 11.5px — both read small against the bar.
   The bar is 64px tall, so a 44px mark still leaves it air. */
.brand img{width:44px;height:44px}
.brand-tx b{font-size:17px}
.brand-tx i{font-size:10px}
.nav a{font-size:13px;padding:9px 14px}
@media(max-width:1180px){
  .nav a{font-size:12px;padding:9px 10px}
}

/* ---------- amenity icons over their photographs ----------
   Drawn in the accent green straight onto a green gradient, the icons sank
   into it. Each now sits on a small dark glass disc with a pale rim, drawn in
   white with a light-green accent, so it reads whatever the photograph is. */
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{
  --glyph-accent:#9FE8C1;
  color:#fff;
  width:clamp(58px,5.6vw,68px);height:clamp(58px,5.6vw,68px);
  padding:12px;box-sizing:border-box;border-radius:50%;
  background:rgba(4,22,15,.55);
  border:1px solid rgba(255,255,255,.38);
  box-shadow:0 6px 18px rgba(3,15,10,.35), inset 0 1px 0 rgba(255,255,255,.18);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)
}

/* ---------- Who's here: every row carries its organisation's mark ----------
   The mark twice over: large and faint across the whole row as its
   background, and whole in a plate in the middle of the row. The row's own
   brand colour washes in from the left under both. A white (reversed) mark is
   shown on its brand colour in the plate, and as a dark silhouette in the
   background, where white on the pale page would vanish.

   Rows with no artwork yet keep the same shape: the plate carries the
   initials in the sector colour, and there is no watermark. */
.dir-row{
  grid-template-columns:38px minmax(0,1.1fr) 150px minmax(0,1fr) 108px;
  align-items:center;position:relative;isolation:isolate;overflow:hidden;
  padding:14px 12px
}
.dir-row::before,
.dir-row::after{content:"";position:absolute;inset:0;pointer-events:none}
.dir-row::before{
  z-index:-2;
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--brand, var(--chip, var(--moss))) 11%, transparent) 0%,
    color-mix(in srgb, var(--brand, var(--chip, var(--moss))) 4%, transparent) 55%,
    transparent 100%)
}
.dir-row.has-logo::after{
  z-index:-1;
  background:var(--logo) no-repeat 50% 50% / auto 240%;
  opacity:.09;filter:saturate(.8) blur(.5px);
  transition:opacity .3s, transform .6s cubic-bezier(.16,1,.3,1)
}
.dir-row.is-rev::after{filter:brightness(0);opacity:.06}
.dir-row:hover{background:var(--stone)}
.dir-row.has-logo:hover::after{opacity:.15;transform:scale(1.04)}

.dir-row .lg{
  justify-self:center;display:grid;place-items:center;
  width:132px;height:52px;padding:7px 12px;box-sizing:border-box;
  background:#fff;border:1px solid var(--line);border-radius:10px;
  box-shadow:0 4px 14px rgba(11,33,26,.06)
}
.dir-row.is-rev .lg{background:var(--brand);border-color:transparent}
.dir-row .lg img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;display:block}
.dir-row .lg b{
  font-family:var(--f-disp);font-weight:700;font-size:17px;letter-spacing:.02em;
  color:var(--chip, var(--moss))
}
.dir-row:not(.has-logo) .lg{
  background:color-mix(in srgb, var(--chip, var(--moss)) 8%, #fff);
  border-style:dashed
}

@media(max-width:1000px){
  .dir-row{grid-template-columns:38px minmax(0,1fr) 120px minmax(0,1fr) 96px}
  .dir-row .lg{width:112px;height:46px}
}
@media(max-width:820px){
  .dir-row{grid-template-columns:30px 1fr auto;gap:6px 14px}
  .dir-row .nm{grid-column:2}
  .dir-row .lg{grid-column:3;grid-row:1 / span 3;width:96px;height:44px}
  .dir-row .ds{grid-column:2}
  .dir-row .lk{grid-column:2}
  .dir-row.has-logo::after{background-size:auto 160%;background-position:85% 50%}
}

/* ---------- Who's here: logos inside their plates ----------
   The picture gets a fixed box to fit within rather than a percentage of a
   height the grid never settles, which is what let tall and wide marks spill
   out of the plate. */
.dir-row .lg{overflow:hidden}
.dir-row .lg img{max-width:100%;max-height:36px;width:auto;height:auto;object-fit:contain}
@media(max-width:1000px){.dir-row .lg img{max-height:32px}}
@media(max-width:820px){.dir-row .lg img{max-height:30px}}

/* ---------- Who's here: the words kept clear of the watermark ----------
   The faint mark behind a row can land right under the name or the
   description. Rather than boxing the words, a gradient of the row's own
   ground lies over the mark where the words are — dense under the name on
   the left and under the description and link on the right, clearing
   through the middle, where the mark and the logo plate are meant to show.

   That needs a layer above the mark and below the words, so the two layers
   swap places on rows with a logo: the mark moves to ::before (bottom), and
   ::after (above it) carries the brand wash and the gradient. The ground is
   a custom property so the gradient follows the row's hover colour. */
.dir-row{--row-bg:var(--paper)}
.dir-row:hover{--row-bg:var(--stone)}
.dir-row.has-logo::before{
  z-index:-2;
  background:var(--logo) no-repeat 50% 50% / auto 240%;
  opacity:.09;filter:saturate(.8) blur(.5px);
  transition:opacity .3s, transform .6s cubic-bezier(.16,1,.3,1)
}
.dir-row.is-rev::before{filter:brightness(0);opacity:.06}
.dir-row.has-logo:hover::before{opacity:.15;transform:scale(1.04)}
.dir-row.has-logo::after{
  z-index:-1;opacity:1;filter:none;transform:none;
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--brand) 11%, transparent) 0%,
      color-mix(in srgb, var(--brand) 4%, transparent) 55%,
      transparent 100%),
    linear-gradient(90deg,
      color-mix(in srgb, var(--row-bg) 92%, transparent) 0%,
      color-mix(in srgb, var(--row-bg) 80%, transparent) 30%,
      transparent 42%,
      transparent 58%,
      color-mix(in srgb, var(--row-bg) 80%, transparent) 66%,
      color-mix(in srgb, var(--row-bg) 92%, transparent) 100%)
}
.dir-row.has-logo:hover::after{opacity:1;transform:none}
@media(max-width:820px){
  /* stacked, the words run down the left and the plate sits on the right */
  .dir-row.has-logo::after{
    background:
      linear-gradient(90deg,
        color-mix(in srgb, var(--brand) 11%, transparent) 0%,
        transparent 100%),
      linear-gradient(90deg,
        color-mix(in srgb, var(--row-bg) 92%, transparent) 0%,
        color-mix(in srgb, var(--row-bg) 78%, transparent) 62%,
        transparent 80%)
  }
  .dir-row.has-logo::before{background-size:auto 160%;background-position:85% 50%}
}

/* ---------- Who's here: the overlay in the logo's own colour ----------
   The gradient over the words is now drawn in each organisation's brand
   colour rather than the page's ground. It is that colour mixed into the
   row's ground — strong enough to read as the logo's colour, light enough
   that the dark type on it keeps its contrast — and it follows the hover
   colour as before. */
.dir-row.has-logo{--tint:color-mix(in srgb, var(--brand) 36%, var(--row-bg))}
.dir-row.has-logo::after{
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--tint) 100%, transparent) 0%,
    color-mix(in srgb, var(--tint) 92%, transparent) 30%,
    color-mix(in srgb, var(--tint) 40%, transparent) 42%,
    color-mix(in srgb, var(--tint) 40%, transparent) 58%,
    color-mix(in srgb, var(--tint) 92%, transparent) 66%,
    color-mix(in srgb, var(--tint) 100%, transparent) 100%)
}
@media(max-width:820px){
  .dir-row.has-logo::after{
    background:linear-gradient(90deg,
      color-mix(in srgb, var(--tint) 100%, transparent) 0%,
      color-mix(in srgb, var(--tint) 92%, transparent) 62%,
      color-mix(in srgb, var(--tint) 40%, transparent) 80%)
  }
}
/* on the stronger brand tint the muted grey description drops too low, so on
   rows with a logo it is set in the ink the name uses */
.dir-row.has-logo .ds{color:var(--ink)}

/* A reversed mark sits on its plate colour, which is its brand colour unless
   logos.ts names another — for marks drawn in the brand colour with white
   lettering, which would vanish on either white or the brand colour. The row's
   tint still follows the brand. */
.dir-row.is-rev .lg{background:var(--plate, var(--brand))}
.wall__tile.is-rev{background:var(--plate, var(--brand))}
a.wall__tile.is-rev:hover{background:color-mix(in srgb, var(--plate, var(--brand)) 86%, #000)}

/* ---------- Who's here: marks that read in the background ----------
   Two kinds of mark were not reading behind their rows. Marks drawn light on
   a dark square (Eglwys Penuel, Jonathan Davies, R&R, Thrive, Refined Beauty)
   were being turned into a black silhouette, which on their own dark tint
   was just a grey block the size of the file. And marks set small inside a
   big light file (EmpathyWorks, Just Mortgages) showed as a pale box.

   Now the row has a solid ground of its own, so the mark can be multiplied
   into it: anything white in the file falls away and only the drawing is
   left, in its own colours. A reversed mark is inverted first, so its light
   drawing becomes dark and its dark square becomes light — and then falls
   away the same way. With the box gone the mark can be stronger without
   turning into a block, and the gradient over the words is eased so a little
   more of it shows through. */
.dir-row.has-logo{background:var(--row-bg)}
.dir-row.has-logo::before{
  mix-blend-mode:multiply;opacity:.3;filter:saturate(.9);
  background-size:auto 190%
}
.dir-row.is-rev::before{filter:invert(1) grayscale(1) contrast(1.6);opacity:.3}
.dir-row.has-logo:hover::before{opacity:.42}
.dir-row.has-logo::after{
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--tint) 94%, transparent) 0%,
    color-mix(in srgb, var(--tint) 78%, transparent) 30%,
    color-mix(in srgb, var(--tint) 30%, transparent) 42%,
    color-mix(in srgb, var(--tint) 30%, transparent) 58%,
    color-mix(in srgb, var(--tint) 78%, transparent) 66%,
    color-mix(in srgb, var(--tint) 94%, transparent) 100%)
}

/* ---------- reviews ----------
   A dark band, so it reads as a pause between the paper and stone sections
   either side of it on both pages. The cards run in the same sliding track as
   the hero's booking tiles: three in view on a wide screen, one and a bit on
   a phone so the next card says there is more. */
.reviews{background:var(--ink);color:#fff;overflow:hidden}
.reviews .eyebrow{color:rgba(255,255,255,.7)}
.reviews .h-sec{color:#fff;max-width:22ch}
.reviews__head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;margin-bottom:clamp(26px,3.4vw,40px)}
.reviews__nav{display:flex;gap:10px;flex:none}
.reviews__nav button{
  width:48px;height:48px;border-radius:50%;display:grid;place-items:center;margin:0;padding:0;cursor:pointer;
  color:#fff;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.35);
  transition:background .25s,border-color .25s,transform .25s cubic-bezier(.16,1,.3,1)
}
.reviews__nav button:hover{background:rgba(255,255,255,.18);border-color:#fff}
.reviews__nav button:active{transform:scale(.94)}
.reviews__nav svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}

.reviews__track{
  --gap:clamp(12px,1.4vw,18px);--in-view:3;
  display:flex;gap:var(--gap);overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;scrollbar-width:none;overscroll-behavior-x:contain;
  padding:4px 0 10px;cursor:grab
}
.reviews__track::-webkit-scrollbar{display:none}
.reviews__track.is-drag{scroll-snap-type:none;cursor:grabbing}
.reviews__track.is-drag .review{pointer-events:none}
@media(max-width:1000px){.reviews__track{--in-view:2}}
@media(max-width:640px){.reviews__track{--in-view:1.15}}

.review{
  flex:0 0 calc((100% - var(--gap) * (var(--in-view) - 1)) / var(--in-view));
  scroll-snap-align:start;margin:0;user-select:none;
  display:flex;flex-direction:column;gap:18px;
  padding:clamp(24px,2.6vw,34px);border-radius:var(--r);
  background:linear-gradient(160deg, rgba(255,255,255,.09), rgba(255,255,255,.04));
  border:1px solid rgba(255,255,255,.16)
}
.review__mark{width:34px;height:27px;fill:var(--hero-live)}
.review__stars{color:#F2C94C;letter-spacing:2px;font-size:15px}
.review__stars span{color:rgba(255,255,255,.25)}
.review blockquote{
  margin:0;font-family:var(--f-disp);font-size:clamp(1.02rem,1.25vw,1.18rem);line-height:1.55;
  color:rgba(255,255,255,.94)
}
.review figcaption{margin-top:auto;display:flex;flex-direction:column;gap:3px;padding-top:16px;border-top:1px solid rgba(255,255,255,.14)}
.review figcaption b{font-weight:600;color:#fff}
.review figcaption span{font-size:13.5px;color:rgba(255,255,255,.7)}
.review figcaption i{font-style:normal;font-family:var(--f-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--hero-live);margin-top:4px}
/* the local-only stand-ins say so plainly, so nobody mistakes them for copy */
.review.is-placeholder{border-style:dashed;border-color:rgba(255,255,255,.3)}
.review.is-placeholder blockquote{color:rgba(255,255,255,.6);font-style:italic}

.reviews__dots{display:flex;gap:7px;margin-top:22px}
.reviews__dots button{
  width:18px;height:3px;border:0;padding:0;margin:0;border-radius:2px;cursor:pointer;position:relative;
  background:rgba(255,255,255,.3);transition:width .45s cubic-bezier(.16,1,.3,1),background .3s
}
.reviews__dots button::before{content:"";position:absolute;inset:-10px -3px}
.reviews__dots button.is-on{width:36px;background:var(--hero-live)}
@media(max-width:640px){
  .reviews__head{flex-direction:column;align-items:flex-start}
}

/* ---------- social ----------
   The Facebook timeline on the left at the width Facebook allows (500px at
   most), the Instagram grid or its follow card filling the rest. Stacked on a
   narrow screen. */
.social__head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;flex-wrap:wrap;margin-bottom:clamp(26px,3.4vw,40px)}
.social__links{display:flex;gap:10px;flex-wrap:wrap}
.social__btn{
  display:inline-flex;align-items:center;gap:9px;padding:11px 18px;border-radius:999px;text-decoration:none;
  font-family:var(--f-body);font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--moss);background:var(--paper);border:1px solid var(--line-str);transition:background .2s,border-color .2s,color .2s
}
.social__btn:hover{background:var(--moss);border-color:var(--moss);color:#fff}
.social__btn svg{width:16px;height:16px;fill:currentColor}

.social__grid{display:grid;grid-template-columns:minmax(0,500px) minmax(0,1fr);gap:clamp(16px,2.4vw,32px);align-items:start}
@media(max-width:900px){.social__grid{grid-template-columns:1fr}}
.social__fb{
  background:#fff;border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  min-height:640px;display:flex;justify-content:center
}
.social__fb iframe{border:0;display:block;max-width:100%}
.social__hold{width:100%;height:640px;background:linear-gradient(180deg,#fff,var(--paper))}

.ig-grid{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
@media(max-width:1100px){.ig-grid{grid-template-columns:repeat(3,1fr)}}
@media(max-width:560px){.ig-grid{grid-template-columns:repeat(2,1fr)}}
.ig-grid a{position:relative;display:block;aspect-ratio:1;border-radius:12px;overflow:hidden;background:var(--line)}
.ig-grid img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .7s cubic-bezier(.16,1,.3,1)}
.ig-grid a:hover img{transform:scale(1.06)}
.ig-grid__play{position:absolute;top:10px;right:10px;width:22px;height:22px;border-radius:50%;background:rgba(0,0,0,.5)}
.ig-grid__play::after{content:"";position:absolute;left:8px;top:6px;border-left:8px solid #fff;border-top:5px solid transparent;border-bottom:5px solid transparent}

.ig-follow{
  display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-end;gap:10px;
  min-height:clamp(300px,40vw,640px);padding:clamp(26px,3vw,40px);border-radius:var(--r);text-decoration:none;color:#fff;
  background:
    linear-gradient(180deg, rgba(7,36,26,.05) 0%, rgba(7,36,26,.55) 55%, rgba(7,36,26,.92) 100%),
    url("/images/estate/aerial-main-ranges.jpg") center/cover;
  transition:transform .3s, box-shadow .3s
}
.ig-follow:hover{transform:translateY(-3px);box-shadow:0 18px 44px rgba(11,33,26,.18)}
.ig-follow__icon{
  width:56px;height:56px;border-radius:16px;display:grid;place-items:center;color:#fff;margin-bottom:auto;
  background:linear-gradient(45deg,#F58529,#DD2A7B 45%,#8134AF 75%,#515BD4)
}
.ig-follow__icon svg{width:30px;height:30px}
.ig-follow b{font-family:var(--f-disp);font-size:clamp(1.5rem,2.4vw,2rem);letter-spacing:-.01em}
.ig-follow span{max-width:40ch;color:rgba(255,255,255,.9);line-height:1.6}
.ig-follow i{font-style:normal;font-family:var(--f-body);font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--hero-live)}

/* ---------- header: room for a fifth link ----------
   With History added the bar carries five links, and at mid widths the longer
   ones ("General information") broke onto two lines. Each link now stays on
   one line, and the spacing and tracking tighten in steps as the bar narrows
   rather than the words wrapping. */
.nav a{white-space:nowrap}
@media(max-width:1360px){
  .nav a{font-size:12.5px;padding:9px 10px;letter-spacing:.08em}
}
@media(max-width:1180px){
  .nav{gap:0}
  .nav a{font-size:11.5px;padding:9px 7px;letter-spacing:.06em}
  .head-in{gap:12px}
}
@media(max-width:1040px){
  .brand-tx i{display:none}
  .nav a{padding:9px 6px;letter-spacing:.04em}
}
/* Between the phone menu and a full-width bar there is no room for the
   language names beside five links, so the switch keeps just its flags. The
   names stay in the markup, so the links are still announced as "English" and
   "Cymraeg"; they are only drawn at no size. */
.head-in .lang{flex:none}
@media(min-width:941px) and (max-width:1180px){
  .lang--compact>*{font-size:0;letter-spacing:0;gap:0;padding:6px 9px}
}

/* ---------- reviews: the link to the full list ---------- */
.reviews__more{
  display:inline-block;margin-top:22px;text-decoration:none;
  font-family:var(--f-body);font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--hero-live)
}
.reviews__more:hover{text-decoration:underline;text-underline-offset:4px}
/* with fewer reviews than fit across, the cards keep a readable width rather
   than stretching or leaving the row half empty on one side */
.review{max-width:560px}

/* ---------- amenity icons: green crystal ----------
   The little discs behind the icons on "What you'll find here" now use the
   hero's own material: the logo's lighter green as glass, frosted, with the
   cut-crystal facets and a sheen across them, and a bright bevel at the rim. */
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{
  background:
    url("/images/crystal-gem.svg") center / 140% 140% no-repeat,
    linear-gradient(135deg, rgba(255,255,255,.28) 0%, rgba(255,255,255,0) 42%, rgba(255,255,255,.1) 64%, rgba(255,255,255,0) 80%),
    linear-gradient(rgba(0,165,95,.46), rgba(0,120,70,.58));
  border:1px solid rgba(200,255,225,.55);
  box-shadow:0 6px 18px rgba(3,15,10,.35), inset 0 1px 0 rgba(255,255,255,.35), inset 0 -6px 14px rgba(0,60,35,.3);
  -webkit-backdrop-filter:blur(8px) saturate(1.2);backdrop-filter:blur(8px) saturate(1.2)
}

/* ---------- amenity icons: easier to see ----------
   On the crystal disc the thin white line drawings were competing with the
   facets behind them. The disc is a size up, the glass is deeper, and a soft
   dark pool sits in its centre so the facets show round the rim but not under
   the drawing. The drawing itself is heavier, pure white with a brighter green
   accent, and carries a small shadow so it lifts off the glass. */
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{
  --glyph-accent:#C2F7DA;
  width:clamp(68px,6.4vw,80px);height:clamp(68px,6.4vw,80px);
  padding:14px;
  background:
    radial-gradient(circle at 50% 50%, rgba(0,48,28,.62) 0%, rgba(0,48,28,.45) 45%, rgba(0,48,28,0) 72%),
    url("/images/crystal-gem.svg") center / 140% 140% no-repeat,
    linear-gradient(135deg, rgba(255,255,255,.26) 0%, rgba(255,255,255,0) 40%, rgba(255,255,255,.08) 64%, rgba(255,255,255,0) 80%),
    linear-gradient(rgba(0,150,86,.62), rgba(0,96,56,.78));
  border:1.5px solid rgba(210,255,230,.7)
}
.fact.has-shot .glyph svg{filter:drop-shadow(0 1px 2px rgba(0,25,14,.55))}
.fact.has-shot .glyph svg > g{stroke-width:2.3}
/* the drawing fills more of the disc, with a heavier line to match */
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{padding:10px}
.fact.has-shot .glyph svg > g{stroke-width:2.7}

/* ---------- social: Facebook's header replaced ----------
   The plugin's own header cut the page name off, so it is cropped away: the
   frame is pulled up by the header's height inside a box that clips it, and a
   header of the site's own sits above instead. */
.social__fbcard{
  background:#fff;border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  display:flex;flex-direction:column
}
.social__fbhead{
  display:flex;align-items:center;gap:12px;padding:14px 16px;text-decoration:none;color:var(--ink);
  border-bottom:1px solid var(--line)
}
.social__fbhead img{width:44px;height:44px;border-radius:50%;flex:none}
.social__fbhead span{display:flex;flex-direction:column;line-height:1.2;min-width:0}
.social__fbhead b{font-family:var(--f-disp);font-weight:700;font-size:16px}
.social__fbhead i{font-style:normal;font-size:12.5px;color:var(--muted)}
.social__fbhead em{
  margin-left:auto;font-style:normal;font-size:12px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  padding:8px 14px;border-radius:999px;background:#1877F2;color:#fff;transition:background .2s
}
.social__fbhead:hover em{background:#0B5FCC}
.social__fbcard .social__fb{
  border:0;border-radius:0;min-height:0;height:620px;overflow:hidden;position:relative;display:block
}
.social__fbcard .social__fb iframe{position:absolute;top:-80px;left:50%;transform:translateX(-50%)}
.social__fbcard .social__hold{height:620px}
/* on paper, the section after it is paper too, so a rule keeps them apart */
.social--paper + .band{border-top:1px solid var(--line)}

/* ---------- social: the two columns the same height ----------
   Side by side, the Instagram card stretches to the Facebook card's height
   (its header plus the feed), top and bottom aligned. Stacked on a narrow
   screen each keeps its own height. */
@media(min-width:901px){
  .social__grid{align-items:stretch}
  .social__ig{display:flex;flex-direction:column}
  .social__ig > .ig-follow{flex:1;min-height:0}
  .social__ig > .ig-grid{flex:1;align-content:start}
}

/* ---------- hero: the video behind everything ----------
   Fills the stage the photographs used to, cropped to cover, under the same
   veil, scrims and parallax. The still frame is its poster, so the hero is
   never blank while the video loads. */
.hero-vid{position:absolute;inset:0;background:#06120E}
/* Two layers: the one in front is opaque, the one waiting is not, and the
   hand-over between films is a cross-fade rather than a cut. */
.hero-video{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
  pointer-events:none;filter:saturate(1.04) contrast(1.02);
  opacity:0;transition:opacity var(--fade,900ms) linear
}
.hero-video.is-on{opacity:1}
@media(prefers-reduced-motion:reduce){.hero-video{transition:none}}
/* a credit that links to the depository reads as a credit, not as body copy */
.tl-src a{color:var(--muted);text-decoration:none;border-bottom:1px solid var(--line-str)}
.tl-src a:hover{color:var(--moss);border-bottom-color:var(--moss)}

/* ---------- header: the mark is larger before the page moves ----------
   At the very top there is no bar, so the mark has room to be a proper
   signature; once the page scrolls under the header it returns to the size it
   has to be to sit in the pill. Both sizes cross-fade with the bar itself. */
.brand img{transition:width .35s cubic-bezier(.16,1,.3,1), height .35s cubic-bezier(.16,1,.3,1)}
.site-head.at-top .brand img{width:62px;height:62px}
.site-head.at-top .brand-tx b{font-size:19px}
@media(max-width:940px){
  .site-head.at-top .brand img{width:52px;height:52px}
  .site-head.at-top .brand-tx b{font-size:17px}
}

/* ---------- reviews: a shorter band ----------
   Two or three short quotes did not need the full section height: the band,
   the gap under the heading and the cards themselves all close up. */
.reviews{padding-block:clamp(38px,4.6vw,64px)}
.reviews__head{margin-bottom:clamp(18px,2.2vw,26px)}
.reviews .h-sec{max-width:26ch}
.review{gap:12px;padding:clamp(18px,1.9vw,24px)}
.review__mark{width:26px;height:21px}
.review blockquote{font-size:clamp(.98rem,1.1vw,1.08rem);line-height:1.5}
.review figcaption{padding-top:12px}
.reviews__track{padding:3px 0 6px}
.reviews__more{margin-top:16px}
.reviews__nav button{width:42px;height:42px}

/* ============================================================
   PARC DEWI SANT — DESIGN TOKENS
   Swap the four brand values below to match the official logo.
   ============================================================ */
:root{
  /* Slate, lichen and brass. The estate is a Grade II Victorian asylum of
     1865 in 38 acres of parkland — stone, iron and trees, not a tech park.
     So the greens are deepened out of the "eco brand" register and the
     neutrals are warmed off the old cold mint toward limestone.

     CONTRAST IS LOAD-BEARING HERE. Every value below is measured against
     both grounds it is used on; the ratio is in the comment. Do not nudge
     one without re-measuring — the palette this replaces failed AA on every
     eyebrow, tag, route number and directory index on the site.          */
  --ink:      #0B211A;   /* slate, green cast     16.84:1 white on it     */
  --moss:     #0E5C41;   /* PRIMARY   7.59 on paper · 6.53 on stone       */
  --moss-2:   #17795A;   /* lighter green, hovers + rules · 5.09 on paper */
  --brass:    #7E5A1C;   /* ACCENT, Victorian brass · 5.92 paper·5.09 stone */
  --brass-lt: #C79A4A;   /* brass for DARK grounds ONLY · 6.54 on --ink.
                            2.4:1 on --paper — never put this on a light band */
  --stone:    #EBE8E0;   /* warm weathered-stone panel                    */
  --paper:    #FAF9F5;   /* warm paper                                    */
  --muted:    #52625A;   /* 6.13 on paper · 5.27 on stone                 */
  --line:     rgba(11,33,26,.13);   /* dividers — decorative, no minimum   */
  --line-str: rgba(11,33,26,.26);   /* stronger dividers                    */
  /* Controls whose ONLY affordance is their outline (filter chips, ghost
     buttons) need 3:1 against the page under SC 1.4.11. --line-str gives
     1.74:1; this gives 3.45:1. Use it for the edge of anything pressable. */
  --line-ctl: rgba(11,33,26,.52);

  /* Figtree is the brand typeface and Arial the specified substitute, so it
     keeps the display, body and reading roles.

     --f-mono does NOT. It was Figtree too, which made the token a lie: every
     price, building number, spec strip and letterspaced label on the site
     claims a monospaced register and got a proportional face, so nothing in
     the numeric column ever lined up. It is now a real mono, and that is
     where the site's typographic contrast comes from — one brand face for
     the voice, one machine face for the data.                            */
  --f-disp: 'Figtree', Arial, system-ui, sans-serif;
  --f-body: 'Figtree', Arial, system-ui, sans-serif;
  --f-read: 'Figtree', Arial, system-ui, sans-serif;
  --f-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;

  --wrap: 1220px;
  --gut: clamp(20px, 5vw, 64px);

  --r:     18px;    /* panels, cards, media                        */
  --r-sm:  11px;    /* small surfaces                              */
  --pill:  999px;   /* anything you press                          */
  --bar-h: 80px;    /* the floating bar: its height and the air above */
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--f-body);font-size:16.5px;line-height:1.6;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block}
a{color:inherit}
button{font:inherit;color:inherit}

.wrap{max-width:var(--wrap);margin:0 auto;padding-inline:var(--gut)}

/* ---------- shared type ---------- */
.eyebrow{
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--moss);
  margin:0 0 16px;display:flex;align-items:center;gap:11px;
}
/* the brass rule is the one warm mark on a green page; 22px reads as a tick,
   not a divider, so it sits with the label rather than separating anything */
.eyebrow::before{content:"";width:22px;height:2px;background:var(--brass);flex:none}

/* Sentence case, not caps. Every heading on the site used to be set in 800
   uppercase, which is the one thing that made a 38-acre Victorian estate read
   like a software landing page: caps flatten the ascenders and descenders that
   carry word-shape, so nothing had a silhouette and everything shouted at the
   same volume. Caps are now reserved for the small letterspaced registers that
   earn them — the eyebrow, the spec strip, buttons, the mono data.       */
.h-sec{
  font-family:var(--f-disp);font-weight:700;
  font-size:clamp(2rem,4.4vw,3.4rem);line-height:1.04;
  letter-spacing:-.032em;margin:0 0 20px;text-wrap:balance;
}
.h-sub{
  font-family:var(--f-disp);font-weight:700;
  font-size:clamp(1.18rem,2.2vw,1.5rem);line-height:1.16;
  letter-spacing:-.018em;margin:0 0 9px;text-wrap:balance;
}
.lede{
  font-family:var(--f-read);font-size:clamp(1.06rem,1.75vw,1.28rem);
  font-weight:400;line-height:1.55;letter-spacing:-.011em;
  color:#22302A;max-width:58ch;margin:0 0 20px;text-wrap:pretty;
}
p{margin:0 0 16px}
.muted{color:var(--muted)}

/* Title-block: the architectural spec strip used across the site */
.spec{
  font-family:var(--f-mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);font-variant-numeric:tabular-nums;
  display:flex;flex-wrap:wrap;gap:8px 0;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:12px 0;margin:0 0 34px;
}
.spec span{padding-right:18px;margin-right:18px;border-right:1px solid var(--line)}
.spec span:last-child{border-right:0;margin-right:0;padding-right:0}
.spec b{color:var(--ink);font-weight:500}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;gap:9px;
  font-family:var(--f-body);font-size:12px;font-weight:600;
  letter-spacing:.11em;text-transform:uppercase;text-decoration:none;
  padding:14px 24px;border:1px solid var(--moss);background:var(--moss);color:#fff;
  border-radius:var(--pill);
  cursor:pointer;transition:background .18s,border-color .18s,color .18s,transform .18s;
}
.btn:hover{background:var(--ink);border-color:var(--ink);transform:translateY(-1px)}
.btn:active{transform:none}
.btn--ghost{background:transparent;color:var(--moss);border-color:var(--line-ctl)}
.btn--ghost:hover{background:var(--moss);border-color:var(--moss);color:#fff}
.btn--light{background:transparent;color:#fff;border-color:rgba(255,255,255,.5)}
.btn--light:hover{background:#fff;color:var(--ink);border-color:#fff}
.btn::after{content:"→";font-family:var(--f-body);font-size:14px;line-height:1}

/* --brass, and deliberately: it is the only hue on the site that is neither
   the page nor the brand green, so a focus ring can never be mistaken for a
   hover or an active nav item. 5.92:1 on --paper, 5.09 on --stone, and it
   carries its own outline colour onto the dark bands too.               */
:where(a,button,input,select,summary,details,[tabindex]):focus-visible{
  outline:2px solid var(--brass);outline-offset:3px;border-radius:2px
}
/* Over a photograph or a pane of dark glass a single hairline can land on any
   luminance the picture happens to have, so on those grounds the ring is two
   parts: the brass line plus a dark halo behind it. The halo guarantees the
   line's own contrast (brass-lt measures 4.99:1 against it over sunlit grass)
   instead of gambling on the backdrop.

   The header and the full-screen menu are in this list because neither is a
   descendant of .hero — .site-head floats above it and .navmask is its
   sibling — so both were falling through to the light-ground rule. */
.band--ink :where(a,button):focus-visible,
.band--shot :where(a,button):focus-visible,
.cta-band :where(a,button):focus-visible,
.site-foot :where(a,button):focus-visible,
.site-head.over :where(a,button):focus-visible,
.navmask :where(a,button):focus-visible,
.hero :where(a,button):focus-visible{
  outline-color:var(--brass-lt);
  box-shadow:0 0 0 5px rgba(4,16,12,.72)
}

/* ---------- header ----------
   The bar is the same object on every page: a pill of glass floating clear of
   whatever is beneath it. Over the hero it is dark glass with pale letters,
   everywhere else light glass with ink ones. The shape never changes — only
   the key — so leaving the hero reads as a change of light, not of furniture. */
.site-head{
  position:fixed;top:0;left:0;right:0;z-index:90;
  padding:clamp(10px,1.3vh,16px) clamp(14px,1.8vw,26px) 0
}
.head-in{
  /* brand · nav · language · book. The switch is a fourth cell, not a fifth
     child crammed into three columns — that pushed the CTA onto a row of its
     own and out of the bar. The burger is display:none above 940px, so it
     takes no cell there. */
  display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;gap:18px;
  height:64px;max-width:var(--shell,1500px);margin:0 auto;padding:0 10px 0 20px;
  /* .80 was mixed when this bar only ever floated over --paper. It now floats
     over the .band--shot photographs too, where the glass composites down to
     L=0.62 and drops --muted nav links to 4.18:1 — under AA for 11.5px. At .92
     the bar holds L=0.81 and the links measure 5.27:1, and the backdrop-filter
     still reads as glass. */
  background:rgba(251,252,250,.92);
  border:1px solid rgba(11,33,26,.10);
  border-radius:var(--pill);
  box-shadow:0 12px 34px rgba(11,33,26,.10);
  backdrop-filter:blur(20px) saturate(165%);-webkit-backdrop-filter:blur(20px) saturate(165%);
  transition:background .45s, border-color .45s, box-shadow .45s
}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none}
.brand img{width:34px;height:34px;object-fit:contain}
.brand-tx{display:flex;flex-direction:column;line-height:1}
.brand-tx b{
  font-family:var(--f-disp);font-weight:800;font-size:15.5px;
  letter-spacing:-.01em;text-transform:uppercase
}
.brand-tx i{
  font-family:var(--f-mono);font-style:normal;font-size:9.5px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted);margin-top:4px
}
.nav{display:flex;align-items:center;gap:4px}
.nav a{text-decoration:none;display:inline-block;
  background:none;border:0;cursor:pointer;padding:9px 13px;
  font-family:var(--f-body);font-size:11.5px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  border-bottom:2px solid transparent;transition:color .16s,border-color .16s;
}
.nav a:hover{color:var(--ink)}
.nav a.is-on{color:var(--moss);border-bottom-color:var(--brass)}
.head-cta{margin-left:14px;padding:11px 18px}
.burger{display:none;background:none;border:1px solid var(--line-str);padding:9px 12px;cursor:pointer;
  font-family:var(--f-body);font-size:11px;letter-spacing:.12em;text-transform:uppercase}

@media(max-width:940px){
  .burger{display:block}
  .head-cta{display:none}
  .nav{
    position:absolute;top:70px;left:0;right:0;background:var(--paper);
    border-bottom:1px solid var(--line);flex-direction:column;align-items:stretch;
    padding:8px var(--gut) 20px;gap:0;display:none;
  }
  .nav.open{display:flex}
  .nav a{text-decoration:none;display:inline-block;text-align:left;padding:15px 0;border-bottom:1px solid var(--line);border-left:0}
  .nav a.is-on{border-bottom-color:var(--brass)}
}

/* ---------- page switching ---------- */
@keyframes fade{from{opacity:0;transform:translateY(7px)}to{opacity:1;transform:none}}

/* ---------- generic band ---------- */
.band{padding:clamp(56px,8vw,104px) 0}
.band--stone{background:var(--stone)}
.band--ink{background:var(--ink);color:#fff}
.band--ink .h-sec,.band--ink .lede{color:#fff}
.band--ink .eyebrow{color:rgba(255,255,255,.7)}
.band--ink .spec{border-color:rgba(255,255,255,.2);color:rgba(255,255,255,.62)}
.band--ink .spec span{border-color:rgba(255,255,255,.15)}
.band--ink .spec b{color:#fff}
.band--tight{padding:clamp(40px,5vw,64px) 0}

/* ---------- split ---------- */
.split{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,68px);align-items:center}
@media(max-width:860px){.split{grid-template-columns:1fr}}
/* The 4/3.4 cover crop is for PHOTOGRAPHS, and it is opt-IN via .photo.
   As a blanket `.split figure img` rule it also matched the estate plan —
   a diagram — and silently sliced 16.8% off its sides on two separate
   pages, taking the building directory with it. Scoping it here means the
   next drawing dropped into a .split is correct by default rather than
   needing another override. */
.split figure.photo{margin:0;background:var(--stone);border:1px solid var(--line);
  border-radius:var(--r);overflow:hidden}
.split figure.photo img{width:100%;height:auto;aspect-ratio:4/3.4;object-fit:cover;display:block}

/* The estate is the product, so where a photograph IS the argument the column
   split stops being even: the picture takes the larger half and runs taller
   than the copy beside it. Opt-in, so the plain 1:1 .split is untouched. */
.split--showcase{grid-template-columns:minmax(0,.88fr) minmax(0,1.12fr);align-items:center}
.split--showcase figure.photo img{aspect-ratio:4/3.15}
.split--showcase figure.photo{box-shadow:0 24px 60px rgba(11,33,26,.13)}
@media(max-width:860px){.split--showcase{grid-template-columns:1fr}}

/* ---------- route cards ---------- */
.routes{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(12px,1.4vw,18px)}
@media(max-width:820px){.routes{grid-template-columns:1fr}}
.route{
  background:var(--paper);padding:clamp(24px,3vw,36px);
  border:1px solid var(--line);border-radius:var(--r);
  display:flex;flex-direction:column;text-decoration:none;
  transition:background .2s, box-shadow .3s, border-color .3s;
}
.route:hover{background:#fff;border-color:var(--line-str);
  box-shadow:0 18px 44px rgba(11,33,26,.10);transform:translateY(-3px)}
.route{transition:background .2s, box-shadow .3s, border-color .3s, transform .3s}
/* a category label, not an index — see Home.tsx */
.route .rn{
  font-family:var(--f-mono);font-size:11px;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--brass);margin-bottom:20px
}
.route p{color:var(--muted);font-size:15px;margin-bottom:22px}
.route span.go{
  margin-top:auto;font-family:var(--f-body);font-size:11.5px;font-weight:600;letter-spacing:.1em;
  text-transform:uppercase;color:var(--moss);display:inline-flex;gap:8px;align-items:center
}

/* ---------- unit / space cards ---------- */
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(16px,2.2vw,26px)}
/* four rooms at the foot of a room page: four divides by two and by one, so no
   breakpoint leaves an orphan the way three-across would */
.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(14px,1.8vw,22px)}
@media(max-width:1000px){.grid-4{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.grid-4{grid-template-columns:1fr}}
.grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(16px,2.2vw,26px)}
@media(max-width:900px){.grid-3{grid-template-columns:repeat(2,1fr)}}
@media(max-width:640px){.grid-3,.grid-2{grid-template-columns:1fr}}

/* Every room card ends in the same ruled line carrying the verb. The button is
   gone: a pill repeated once per card was the loudest thing on the page and
   carried no information, so the whole card is the link now (.card-foot::after
   covers it) and the footer says what the click does. Keyboard users get the
   frame via :focus-within, since the anchor's own box is only the footer.

   No lift, no drop shadow on hover either. The state change is precise
   instead: the rule darkens, the photograph opens up, the verb underlines. */
.card{
  position:relative;
  border:1px solid var(--line);background:var(--paper);
  border-radius:var(--r);overflow:hidden;
  display:flex;flex-direction:column;transition:border-color .25s
}
.card:hover{border-color:var(--line-str)}
.card:focus-within{outline:2px solid var(--moss);outline-offset:3px}
.card-img{position:relative;background:var(--stone);overflow:hidden}
/* Six photographs of six rooms have six different brightnesses exactly where
   the label sits. The scrim settles them, so the plate needs no drop shadow of
   its own to stay legible and the picture grounds into the card instead of
   stopping dead at the caption. */
.card-img::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top,rgba(11,33,26,.34),rgba(11,33,26,0) 44%)
}
.card-img img{width:100%;height:auto;aspect-ratio:5/4;object-fit:cover;
  transition:transform .6s cubic-bezier(.2,.6,.2,1)}
/* height:auto is load-bearing, exactly as it is on .mapfig: the derivatives
   carry width/height attributes to reserve the box, and without it the
   browser honours height="608" literally and aspect-ratio never applies. */
.card:hover .card-img img{transform:scale(1.04)}

/* The category names the PHOTOGRAPH, so it sits on it — a specimen label on a
   plate, rather than a third line of type stacked above the room's name. */
/* The category was set at 10px mono and disappeared into the photograph — the
   estate could not see what kind of room it was looking at. Same badge, read
   at a glance: bigger, heavier, in the brand green rather than brass, and on
   an opaque plate so it holds over any part of the image. */
.card-cat{
  position:absolute;left:10px;bottom:13px;z-index:1;margin:0;   /* 10 + 12 of padding = the body's 22 */
  background:var(--paper);border-radius:var(--r-sm);padding:8px 14px 7px;
  font-family:var(--f-mono);font-size:12px;font-weight:600;letter-spacing:.13em;
  text-transform:uppercase;color:var(--moss);
  box-shadow:0 2px 10px rgba(3,15,10,.14)
}
.card-cat--plain{position:static;background:none;box-shadow:none;padding:0;margin-bottom:12px}

.card-body{padding:20px 22px 20px;display:flex;flex-direction:column;flex:1}
.card-body .h-sub{margin-bottom:9px}
.card-body p{font-size:14.8px;line-height:1.6;color:var(--muted);margin:0}

.card-foot{
  display:flex;align-items:center;gap:9px 16px;flex-wrap:wrap;
  margin-top:auto;padding:14px 22px 15px;
  border-top:1px solid var(--line);text-decoration:none;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase
}
.card-foot::after{content:"";position:absolute;inset:0}   /* the card is the link */
.card-go{
  color:var(--moss);display:inline-flex;align-items:center;gap:7px;
  border-bottom:1px solid transparent;padding-bottom:2px;transition:border-color .25s
}
.card-go::after{content:"→";transition:transform .25s}
.card:hover .card-go{border-bottom-color:rgba(14,92,65,.45)}
.card:hover .card-go::after{transform:translateX(3px)}

/* The odd one out, and the section's one field of colour. It has no room to
   show, so an empty tile the colour of the band read as a gap in the grid
   rather than the invitation it is. Moss also puts the brand back into a
   section that lost all of it when the five green buttons went — once, in the
   last cell, where the row ends. White on --moss measures 8.15:1; --brass-lt
   is only 3.16:1 on it, so the label stays white and does not go brass. */
.card--ask{background:var(--moss);border-color:var(--moss);color:#fff}
.card--ask .card-body{justify-content:flex-end;padding:34px 24px 22px}
.card--ask .h-sub{color:#fff;font-size:clamp(1.5rem,2.3vw,1.95rem);line-height:1.07;letter-spacing:-.026em}
.card--ask .card-cat{color:rgba(255,255,255,.74)}
.card--ask .card-body p{color:rgba(255,255,255,.86)}
.card--ask .card-foot{border-top-color:rgba(255,255,255,.24)}
.card--ask .card-go{color:#fff}
.card--ask:hover{border-color:var(--ink);background:var(--ink)}
.card--ask:hover .card-go{border-bottom-color:rgba(255,255,255,.5)}
.card--ask{transition:background .25s,border-color .25s}
@media(prefers-reduced-motion:reduce){
  .card-img img,.card-go,.card-go::after{transition:none}
}

.tag{
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--brass);margin-bottom:11px
}
.price{
  font-family:var(--f-mono);font-size:12.5px;color:var(--ink);font-variant-numeric:tabular-nums;
  border-top:1px solid var(--line);padding-top:14px;margin-top:auto;margin-bottom:16px
}
.price b{color:var(--moss);font-weight:500}
.card .btn{align-self:flex-start}
/* .price used to carry margin-top:auto to pin the card footer; where a listing
   card has no price line the button takes over, so CTAs stay baseline-aligned
   across a row. Scoped to :has(.card-img) because the prompt card ("Talk to us
   first") has no photo and centres its content instead — an auto margin there
   silently defeats its justify-content and strands the button. */
.card:has(.card-img) .card-body:not(:has(.price)) .btn{margin-top:auto}

/* ---------- a room's own page ----------
   The head is the site's usual .band--shot, so a room arrives as the same kind
   of page as everything else. What travels with the click is the plate: the
   little paper label on the card, at page scale, over the same room. One
   device at two sizes is what makes opening a card read as an opening rather
   than a jump.

   The calendar is built like the estate plan and the logo wall — a bordered
   block with a mono strip along its foot — because it is the same kind of
   thing: an artifact the page frames rather than a widget dropped into it.
   The strip is also where the way out lives. Skedda sends no frame-ancestors
   so the embed works, but an embed can always fail, and "open in Skedda" must
   not be something a visitor has to go hunting for.                        */
.room-back{
  display:flex;width:fit-content;align-items:center;gap:9px;margin-bottom:20px;
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;letter-spacing:.15em;
  text-transform:uppercase;color:rgba(255,255,255,.82);text-decoration:none;
  transition:color .2s
}
.room-back::before{content:"\2190"}
.room-back:hover{color:#fff}
.room-plate{
  display:inline-block;margin:0 0 17px;
  background:var(--paper);border-radius:var(--r-sm);padding:7px 13px 6px;
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;letter-spacing:.15em;
  text-transform:uppercase;color:var(--brass)
}
.room-lede{max-width:54ch}
.room-offsite{margin:clamp(26px,3.4vw,38px) 0 0}

.cal{
  margin:clamp(26px,3.4vw,38px) 0 0;background:#fff;
  border:1px solid var(--line-str);border-radius:var(--r);overflow:hidden
}
.cal__frame{
  display:block;width:100%;border:0;background:var(--stone);
  /* a booking grid needs real height; vh so a laptop is not left scrolling a
     tall frame inside a tall page */
  height:clamp(540px,78vh,880px)
}
.cal figcaption{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px 22px;
  border-top:1px solid var(--line);padding:13px 18px;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--muted)
}
.cal__open{margin-left:auto;color:var(--moss);font-weight:500;text-underline-offset:4px}
@media(max-width:620px){.cal__open{margin-left:0}}

/* ============================================================
   SPACE TO BREATHE — a tenant's own page
   ============================================================
   The one page on the site that does not wear the estate's colours. Space to
   Breathe is a tenant with an identity of its own, a sage arch on cream, and
   it lets rooms on how they feel; arriving into slate and moss would have sold
   the wrong thing.

   It is done by re-declaring the tokens on .stb, so every component inside —
   bands, cards, notices, buttons, rules — recolours from the same switch and
   nothing needed a second implementation. --paper is the cream baked into the
   logo file itself, so the mark lands on the page with no visible plate.

   The TYPE does not change. Figtree and IBM Plex Mono as everywhere else: the
   tenant's voice is carried by their mark, their colour and their rooms, and a
   borrowed display serif only made this page look like a different website.

   MEASURED ON #F1EFE7: --ink 14.63:1, --moss 6.95:1, --muted 5.61:1,
   --brass 5.42:1. All four are the estate's own values, kept because all four
   still pass on this ground. --sage is the logo's mark at 2.39:1 and is
   DECORATIVE ONLY — rules, washes, the arch. Never put text in it.        */
.stb{
  --paper:#F1EFE7;
  --stone:#E5E2D2;
  --sage:#9F9D87;
  --line:rgba(74,76,56,.18);
  --line-str:rgba(74,76,56,.32);
  /* The action colour shifts with the ground. Emerald was the one note on this
     page that argued with the sage, and green still means "press" — the button
     keeps its shape, only its hue moves. MEASURED: 7.14:1 on the cream, 6.32:1
     on the panel, and white on it 8.22:1, so it clears AA in both directions
     wherever the estate's --moss did. */
  --moss:#41543A;
  --moss-2:#5A6E4B;   /* 4.84:1 on the cream — rules and hovers */
  background:var(--paper)
}
/* the wordmark's widest gesture is its tracking; the small labels echo it */
.stb .eyebrow{letter-spacing:.26em}
.stb .eyebrow::before{background:var(--sage);width:26px}

/* ---- head ----
   No photograph behind it. The carousel is the picture, and a second image
   washed out under the words would only have competed with it. */
/* ---- head ----
   The hero is composed TO the viewport, not allowed to overflow it. The header
   takes the height it needs and the strip takes the rest, so the rooms are
   whole and on screen the moment the page opens — which is the entire point of
   showing seven of them. Below 900px it goes back to flowing normally: a
   stacked headline, lede, price and button leave nothing worth squeezing a
   photograph into.

   svh, not vh: on a phone vh is the tallest the viewport ever gets, so the
   strip would sit under the browser's own chrome until you scrolled. */
.stb-head{
  background:var(--paper);
  padding-top:calc(var(--bar-h) + clamp(14px,1.8vw,22px));
  padding-bottom:clamp(20px,2.4vw,30px)
}
@media(min-width:901px){
  /* height, not min-height: min-height lets the section grow past the viewport
     to fit its contents, which is exactly what was pushing the rooms under the
     fold. A definite height makes the strip shrink into whatever the header
     leaves. min-height keeps a very short window from crushing it — past that
     the page simply scrolls. */
  /* min() caps it: on a tall desktop window a pure 100svh hero turned the
     strip into a wall of photographs three storeys high. */
  .stb-head{height:min(100svh,940px);min-height:620px;display:flex;flex-direction:column}
  .stb-head>.wrap{flex:0 0 auto}
}
.stb-back{
  display:flex;width:fit-content;align-items:center;gap:9px;
  margin-bottom:clamp(12px,1.6vw,18px);
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;letter-spacing:.2em;
  text-transform:uppercase;color:var(--muted);text-decoration:none;transition:color .2s
}
.stb-back::before{content:"\2190"}
.stb-back:hover{color:var(--ink)}

/* Title on one side, the case for it on the other. */
.stb-hero{
  display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:clamp(24px,4vw,64px);
  margin-top:clamp(10px,1.4vw,18px)
}
@media(max-width:900px){.stb-hero{grid-template-columns:1fr;gap:26px}}
/* ---- the sign ----
   Their mark hangs from a crook, and it is drawn in outlined letterforms that
   need room: at 98px of pale sage on cream it was a watermark, not a logo. So
   it gets a plate, a size worth reading, and a ruled label naming the place —
   a sign on the wall rather than a picture floating on paper.

   The plate is --paper, not white. The logo file bakes its own cream behind
   the wordmark, and on a white card that block shows as a rectangle; matched
   to the page it disappears, and the card is held by its edge and its shadow
   instead. Laid out sideways because stacked it cost 120px of a hero whose
   whole job is to leave room for the rooms. */
.stb-sign{
  display:inline-flex;align-items:center;gap:clamp(13px,1.5vw,18px);
  background:var(--paper);border:1px solid var(--line-str);border-radius:var(--r-sm);
  padding:clamp(11px,1.3vw,15px) clamp(15px,1.7vw,20px);
  box-shadow:0 16px 40px rgba(74,76,56,.13);
  margin-bottom:clamp(14px,1.8vw,22px)
}
.stb-sign img{display:block;width:clamp(74px,6.2vw,92px);height:auto}
/* Two deliberate lines, not a wrapped sentence: at this tracking the label
   broke into five ragged ones. What it is, then where it is. */
.stb-sign span{
  display:flex;flex-direction:column;gap:6px;
  border-left:1px solid var(--line);padding-left:clamp(13px,1.5vw,18px);
  font-family:var(--f-mono);font-size:9.5px;font-weight:500;letter-spacing:.17em;
  line-height:1.2;text-transform:uppercase;color:var(--muted);white-space:nowrap
}
.stb-sign b{font-weight:500;color:var(--ink)}
.stb-h1{margin:0;max-width:13ch}
/* The offer sits as one group, centred against the sign and the headline
   beside it. Pinned to the foot instead it left a well of empty page between
   the lede and the price. */
.stb-hero{align-items:stretch}
.stb-hero__aside{
  display:flex;flex-direction:column;justify-content:center;
  padding-top:clamp(4px,1vw,10px)
}
.stb-lede{
  margin:0;max-width:44ch;color:var(--muted);
  font-family:var(--f-read);font-size:clamp(1.02rem,1.5vw,1.14rem);
  line-height:1.62;letter-spacing:-.009em;text-wrap:pretty
}
/* The rate is the pitch on their own page, so it is set as data: figures in the
   machine face, the words around them small. */
.stb-rate{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:8px;
  margin:clamp(20px,2.4vw,26px) 0 0;
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted)
}
.stb-rate b{
  font-size:23px;font-weight:500;letter-spacing:-.01em;text-transform:none;
  color:var(--ink);font-variant-numeric:tabular-nums
}
.stb-rate i{display:block;width:5px;height:5px;border-radius:50%;background:var(--sage);margin:0 6px}
.stb-cta{margin:clamp(20px,2.6vw,28px) 0 0}

/* ---- the rooms ----
   Bands of whole rooms, scattered down the page: three across the foot of the
   hero, then a pair closing each section after it. They were one strip with a
   single room open and six slivers, which read as a spectrum but meant six of
   the seven were never really seen — and the rooms are the product.

   The hero's band flexes to whatever height the header leaves it; the pairs
   set their own at 3:2, which is generous enough that a room reads as a room
   and not a thumbnail. */
.stb-rooms{
  display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(8px,1vw,14px);
  max-width:min(1640px,94vw);margin:clamp(16px,2vw,24px) auto 0
}
.stb-rooms--pair{
  grid-template-columns:repeat(2,1fr);margin-top:clamp(34px,4.4vw,58px)
}
.stb-rooms figure{
  margin:0;overflow:hidden;border-radius:var(--r);background:var(--stone);
  animation:stbArrive .85s cubic-bezier(.2,.7,.25,1) backwards;
  animation-delay:calc(var(--k,0) * 90ms + 120ms)
}
.stb-rooms img{width:100%;height:100%;object-fit:cover;display:block}
.stb-rooms--pair img{aspect-ratio:3/2;height:auto}
/* The one staged moment on the site: the rooms arrive across the band as the
   page settles. */
@keyframes stbArrive{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:none}}

/* A room shrunk to a third of a phone is a thumbnail, so the hero's band
   becomes a row you push sideways with each room at a size worth looking at.
   The pairs simply stack. */
@media(max-width:760px){
  .stb-head .stb-rooms{
    display:flex;overflow-x:auto;touch-action:pan-x;overscroll-behavior-x:contain;
    padding-inline:var(--gut);max-width:none;scroll-snap-type:x mandatory
  }
  .stb-head .stb-rooms figure{flex:0 0 78%;scroll-snap-align:center}
  .stb-rooms--pair{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
  .stb-rooms figure{animation:none}
}

.stb-strip__note{
  width:min(1640px,94vw);max-width:100%;margin:12px auto 0;
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted)
}
@media(max-width:760px){.stb-strip__note{padding-inline:var(--gut);width:auto}}

/* ---- three reasons ----
   Ruled apart rather than boxed, and each one ends in the same place: the
   argument in prose, the checkable facts under a rule at the foot of the
   column in the machine face. Because the columns are flex, those foot rules
   line up across all three however long the prose runs — the same device the
   space cards use, and the reason this section stopped being three unequal
   slabs of text. */
.stb-why{
  display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(24px,3.4vw,52px);
  margin-top:clamp(30px,4vw,46px)
}
@media(max-width:900px){.stb-why{grid-template-columns:1fr;gap:32px}}
.stb-why>div{display:flex;flex-direction:column;border-top:1px solid var(--line-str);padding-top:20px}
.stb-why h3{
  font-family:var(--f-disp);font-weight:700;margin:0 0 11px;color:var(--ink);
  font-size:clamp(1.12rem,1.8vw,1.3rem);line-height:1.18;letter-spacing:-.018em
}
.stb-why p{margin:0 0 22px;color:var(--muted);font-size:15.2px;line-height:1.66;text-wrap:pretty}
.stb-why ul{
  margin:auto 0 0;padding:14px 0 0;list-style:none;
  border-top:1px solid var(--line);
  font-family:var(--f-mono);font-size:10px;font-weight:500;
  letter-spacing:.13em;line-height:1.9;text-transform:uppercase;color:var(--ink)
}
/* The separator belongs to the item that follows it, not the one before: as a
   trailing character it dangled at the end of a wrapped line. */
.stb-why ul{display:flex;flex-wrap:wrap;gap:3px 11px}
.stb-why li{display:flex;align-items:center;gap:11px}
.stb-why li+li::before{
  content:"";flex:none;width:3px;height:3px;border-radius:50%;background:var(--sage)
}

.stb-roomlede{max-width:56ch}

/* ---- the live plan ----
   Skedda's map view is the floor plan and it is interactive, so it takes the
   place the still drawing had: wider than the page's measure, because the
   building is a corridor and wider than the page.

   The still sits underneath the frame, not instead of it. Skedda takes a while
   to boot and an iframe is transparent until its document paints, so the
   drawing holds the space from the first frame and is covered when the live
   one arrives. */
.stb-plan{
  max-width:min(1640px,94vw);margin:clamp(24px,3vw,36px) auto 0;
  background:#fff;border:1px solid var(--line-str);border-radius:var(--r);overflow:hidden
}
/* Kept here, with the frame it serves: as a free-standing block twice now it
   has been caught by a tidy-up of neighbouring rules, and without it the
   absolutely-positioned iframe has no positioned ancestor and lays itself over
   the whole page. */
.stb-views__stack{
  position:relative;isolation:isolate;background:var(--stone);
  height:clamp(560px,76vh,900px)
}
.stb-views__wait{
  position:absolute;inset:0;margin:0;display:grid;place-items:center;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--muted)
}
.stb-plan .cal__frame{position:absolute;inset:0;width:100%;height:100%}
.stb-plan__still{
  position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);
  width:100%;height:auto;max-height:100%;object-fit:contain;opacity:.55
}
.stb-plan figcaption{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px 22px;
  border-top:1px solid var(--line);padding:13px 18px;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--muted)
}
@media(max-width:620px){.stb-plan figcaption .cal__open{margin-left:0}}

/* What the plan shows, counted off it. Figures in the machine face so the row
   reads as the building's dimensions rather than a feature list. */
.stb-building{
  display:flex;flex-wrap:wrap;gap:0;margin:clamp(22px,2.6vw,30px) 0 0;padding:0;list-style:none;
  border-top:1px solid var(--line-str)
}
.stb-building li{
  display:flex;align-items:baseline;gap:8px;padding:15px clamp(20px,2.6vw,34px) 15px 0;
  margin-right:clamp(20px,2.6vw,34px);border-right:1px solid var(--line);
  font-family:var(--f-mono);font-size:10.5px;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--muted)
}
.stb-building li:last-child{border-right:0;margin-right:0;padding-right:0}
.stb-building b{
  font-size:21px;font-weight:500;letter-spacing:-.01em;text-transform:none;
  color:var(--ink);font-variant-numeric:tabular-nums
}
.stb-building__note{color:var(--moss)}
@media(max-width:620px){
  .stb-building li{border-right:0;margin-right:0;padding:11px 0;width:100%;
    border-bottom:1px solid var(--line)}
  .stb-building li:last-child{border-bottom:0}
}

.stb-ask{
  position:relative;
  margin-top:clamp(34px,4.4vw,54px);padding-top:clamp(26px,3vw,34px);
  border-top:1px solid var(--line-str)
}
/* The one ornament on the page, and the only thing here that is purely a
   picture. It is the brand's own shape at the size of a piece of furniture,
   held at an opacity where it reads as a watermark on the paper rather than
   as an illustration competing with the words. */
.stb-arch{
  position:absolute;right:2%;bottom:calc(-1 * clamp(56px,8vw,104px));width:clamp(96px,12vw,168px);height:auto;
  color:var(--sage);opacity:.5;pointer-events:none
}
@media(max-width:760px){.stb-arch{display:none}}
.stb-ask h3{
  font-family:var(--f-disp);font-weight:700;margin:0 0 8px;color:var(--ink);
  font-size:clamp(1.2rem,2vw,1.5rem);line-height:1.16;letter-spacing:-.02em
}
.stb-ask p{margin:0 0 20px;color:var(--muted);max-width:48ch}

/* ---------- logo wall ---------- */
/* Sixty organisations' logos are sixty different colour systems, sixty
   different aspect ratios and sixty different amounts of built-in padding.
   Two things fix that wall.

   GRID, not flex. `flex:1 1 132px` let the last row stretch its items to fill
   the line, so a row that happened to hold two logos rendered them as two
   enormous cells twice the size of every other tile — which is exactly what
   the bottom of the home page was doing. Grid tracks are the same width on
   every row, and a short last row simply leaves the tail empty.

   GREY AT REST, COLOUR ON HOVER. The wall's job is to show how many there
   are; the individual marks matter on approach, not at a glance. Desaturating
   holds the wall together and returns each logo's own colour when you point
   at it. */
/* ONE BLOCK, NOT SIXTEEN CARDS. Sixteen separately-bordered tiles read as
   sixteen objects floating on the band; the wall's job is the opposite, so
   the tiles now share a hairline lattice — a 1px grid gap over a rule-coloured
   backdrop — inside a single frame. Same construction as the estate plan's
   figure, down to the mono caption strip, so the two blocks on this page read
   as siblings.

   Sixteen divides into four and two, which is every column count here, so no
   breakpoint ever leaves an orphan row. (Eight across fitted the old cards but
   left each mark ~92px of usable width: a 6:1 wordmark came out 15px tall.
   Four across gives it 230px and the wall became legible.) */
.logos{
  margin-top:clamp(26px,3vw,34px);
  display:grid;grid-template-columns:repeat(2,1fr);gap:1px;
  background:var(--line-str);
  border:1px solid var(--line-str);border-radius:var(--r);overflow:hidden
}
@media(min-width:760px){.logos{grid-template-columns:repeat(4,1fr)}}
/* Fixed height, not min-height: a mark carrying --fit grows its box past the
   padding, and a row that stretched to suit one logo would put the whole wall
   out of step. What overflows is the bitmap's own whitespace. */
/* Every cell is set in its own organisation's colour, --brand, given per logo
   in Home.tsx. A mark drawn in dark ink gets that colour as a wash — enough
   for the wall to read as sixty different organisations rather than one grey
   block, and light enough that the ink still carries. The four reversed marks
   take it at full strength below. */
.logos>a,.logos>span{
  background:color-mix(in srgb, var(--brand,transparent) 9%, var(--paper));
  height:clamp(100px,10.4vw,124px);overflow:hidden;
  display:grid;place-items:center;padding:16px 22px;
  transition:background .22s
}
/* Hover deepens the organisation's own colour rather than washing to white —
   the cell answers in its own hue. */
.logos>a:hover{background:color-mix(in srgb, var(--brand,transparent) 20%, var(--paper))}

/* The four that publish only a white mark take the colour at full strength,
   which is how their own guidelines set a reversed logo. These must opt OUT
   of the multiply below: multiply exists to drop a white PLATE through to the
   cell, and run over a white MARK it would drop the mark and leave the panel
   empty. */
.logos>.is-rev{background:var(--brand)}
.logos>a.is-rev:hover{background:color-mix(in srgb, var(--brand) 86%, #000)}
.logos>.is-rev img,.logos>a.is-rev:hover img{
  mix-blend-mode:normal;filter:none;opacity:1
}
/* Every mark is fitted to ONE box rather than one max-height: a square crest
   and a 6:1 wordmark given the same height are nothing like the same size on
   the page. contain inside a fixed box equalises them instead — and --fit,
   set per logo in Home.tsx, grows that box for the files that padded their
   own mark with whitespace. */
.logos img{
  --box:clamp(42px,4.6vw,56px);
  width:100%;max-width:calc(210px * var(--fit,1));
  height:calc(var(--box) * var(--fit,1));object-fit:contain;
  /* The wall used to be desaturated for the sake of one even texture. The
     estate asked for its tenants' own colours back: sixty organisations is
     the point of the section, and in grey it read as one grey block. Only the
     plated files below still need correcting, because their baked-in
     background is not white and no filter can put colour back through it. */
  filter:contrast(1.02);opacity:1;
  /* Most of these files carry their own white plate baked into the bitmap,
     which would otherwise sit on the tile as a visible white rectangle.
     Multiply drops any white in the file straight through to the tile
     beneath, so what is left is the mark itself, in its own colours. */
  mix-blend-mode:multiply;
  transition:filter .28s,opacity .28s
}
.logos img.is-tall{--box:clamp(58px,6.2vw,76px)}
.logos>a:hover img{filter:none;opacity:1}


.logos__note{
  grid-column:1/-1;margin:0;background:var(--paper);
  display:flex;flex-wrap:wrap;align-items:center;gap:8px 22px;padding:14px 20px;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--muted)
}
.logos__note a{
  margin-left:auto;color:var(--moss);font-weight:500;
  text-decoration:underline;text-underline-offset:4px;
  text-decoration-color:rgba(14,92,65,.4);transition:text-decoration-color .2s
}
.logos__note a:hover{text-decoration-color:currentColor}
@media(max-width:520px){.logos__note a{margin-left:0}}
@media(prefers-reduced-motion:reduce){.logos img{transition:none}}

/* ---------- language switch ----------
   English and Cymraeg, as two links rather than a control with state: changing
   language is a page load, so it works with scripting off and the server can
   remember the choice. The current one is not a link — it has nowhere to go —
   and is filled so the pair reads as a switch at a glance.

   In the header it is the compact form, small enough to sit in the bar without
   competing with the nav; in the phone menu it runs full size under the links.
   Moss on the filled half with white letters measures 8:1. */
.lang{
  display:inline-flex;align-items:stretch;flex:none;
  border:1px solid var(--line-ctl);border-radius:var(--pill);overflow:hidden
}
.lang>*{
  display:inline-flex;align-items:center;gap:8px;
  padding:9px 15px;text-decoration:none;
  font-family:var(--f-body);font-size:11.5px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  transition:background .18s,color .18s
}
.lang a:hover{background:rgba(11,33,26,.06);color:var(--ink)}
.lang .is-on{background:var(--moss);color:#fff}
.lang__flag{width:23px;height:23px;flex:none;border-radius:50%;display:block}
.lang--compact>*{padding:6px 12px;gap:8px;font-size:10.5px;letter-spacing:.08em}
.lang--compact .lang__flag{width:21px;height:21px}
/* Over the hero the bar is dark glass, so the switch takes the pale key with it. */
.site-head.over .lang{border-color:rgba(255,255,255,.42)}
.site-head.over .lang a{color:rgba(255,255,255,.82)}
.site-head.over .lang a:hover{background:rgba(255,255,255,.14);color:#fff}
.site-head.over .lang .is-on{background:#fff;color:var(--ink)}
/* Below the nav's own breakpoint the bar has no room for it — the phone menu
   carries it instead, and the bar goes back to three cells. */
@media(max-width:1000px){
  .head-in .lang{display:none}
  .head-in,.site-head.over .head-in{grid-template-columns:auto 1fr auto}
}
/* Inside the phone menu the mask styles every `a` as a display-sized link, so
   the switch has to state its own case — and take the pale key, since the mask
   is near-black. It rides in on the same stagger as the links above it. */
.mask-lang{margin-top:clamp(22px,5vw,34px)}
.navmask .mask-lang .lang{border-color:rgba(255,255,255,.42);width:fit-content}
.navmask .mask-lang .lang>*{
  display:inline-flex;align-items:center;gap:9px;
  font-family:var(--f-body);font-weight:600;font-size:12px;line-height:1;
  letter-spacing:.1em;text-transform:uppercase;padding:12px 17px;
  color:rgba(255,255,255,.82);transform:none
}
.navmask .mask-lang .lang a{opacity:0}
.navmask.open .mask-lang .lang a{animation:maskIn .6s cubic-bezier(.16,1,.3,1) forwards;animation-delay:var(--d,0ms)}
.navmask .mask-lang .lang a:hover{background:rgba(255,255,255,.14);color:#fff}
.navmask .mask-lang .lang .is-on{background:#fff;color:var(--ink)}
.navmask .mask-lang .lang__flag{width:26px;height:26px}

/* ---------- directory ---------- */
.filters{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 30px}
.chip{
  font-family:var(--f-body);font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  padding:12px 17px;border:1px solid var(--line-ctl);background:transparent;
  border-radius:var(--pill);
  cursor:pointer;color:var(--muted);transition:all .16s
}
.chip:hover{color:var(--ink);border-color:var(--ink)}
.chip.on{background:var(--moss);border-color:var(--moss);color:#fff}

/* One green per sector, drawn off the logo rather than invented: the filters
   were five identical outlines and the estate wanted the sectors told apart.
   Each keeps its own hue when selected, and carries it in the text and rule
   when it is not, so the row reads as a key to the directory beneath.

   Every fill is dark enough to hold white type — the lightest here is 5.10:1,
   comfortably past AA — which is why the brand's lighter --moss-2 (3.33:1) is
   not among them. */
.chip[data-cat="health"]    {--chip:#0B6B4E}
.chip[data-cat="wellbeing"] {--chip:#12794A}
.chip[data-cat="education"] {--chip:#2F7D3A}
.chip[data-cat="community"] {--chip:#0E6F63}
.chip[data-cat="services"]  {--chip:#3A6B54}

.chip[data-cat]:not([data-cat="all"]){color:var(--chip,var(--muted))}
.chip[data-cat]:not([data-cat="all"]):hover{
  color:var(--chip);border-color:var(--chip);background:color-mix(in srgb, var(--chip) 8%, transparent)
}
.chip.on[data-cat]{background:var(--chip,var(--moss));border-color:var(--chip,var(--moss));color:#fff}

/* The row of rules under the directory follows the same key. */
.dir-row[data-cat="health"]    {--chip:#0B6B4E}
.dir-row[data-cat="wellbeing"] {--chip:#12794A}
.dir-row[data-cat="education"] {--chip:#2F7D3A}
.dir-row[data-cat="community"] {--chip:#0E6F63}
.dir-row[data-cat="services"]  {--chip:#3A6B54}
.dir-row[data-cat] .idx{color:var(--chip,var(--brass))}

.dir{border-top:1px solid var(--line-str)}
.dir-row{
  display:grid;grid-template-columns:38px 1fr 200px 108px;gap:18px;align-items:baseline;
  padding:17px 4px;border-bottom:1px solid var(--line);transition:background .16s
}
.dir-row:hover{background:var(--stone)}
.dir-row .idx{font-family:var(--f-mono);font-size:11px;color:var(--brass);font-variant-numeric:tabular-nums}
.dir-row .nm{font-family:var(--f-disp);font-weight:600;font-size:16.5px;letter-spacing:-.008em}
.dir-row .ds{font-size:14px;color:var(--muted)}
.dir-row .lk{
  font-family:var(--f-body);font-size:10.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--moss);text-decoration:none;justify-self:end;white-space:nowrap
}
.dir-row .lk:hover{text-decoration:underline;text-underline-offset:3px}
@media(max-width:820px){
  .dir-row{grid-template-columns:30px 1fr;gap:6px 14px}
  .dir-row .ds{grid-column:2}
  .dir-row .lk{grid-column:2;justify-self:start;margin-top:5px}
}
.dir-count{font-family:var(--f-mono);font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin:22px 0 0}

/* ---------- activities ---------- */
.acts{columns:2;column-gap:clamp(24px,4vw,56px)}
@media(max-width:760px){.acts{columns:1}}
.act{
  break-inside:avoid;padding:13px 0;border-bottom:1px solid var(--line);
  display:flex;justify-content:space-between;gap:16px;align-items:baseline
}
.act b{font-weight:600;font-size:15px}
.act span{font-family:var(--f-mono);font-size:10.5px;letter-spacing:.09em;color:var(--muted);text-align:right;white-space:nowrap}
.act a{color:var(--moss)}

/* ---------- timeline ---------- */
.tl{border-top:1px solid var(--line-str)}
.tl-row{
  display:grid;grid-template-columns:150px 1fr;gap:clamp(20px,4vw,54px);
  padding:clamp(24px,3vw,36px) 0;border-bottom:1px solid var(--line)
}
@media(max-width:700px){.tl-row{grid-template-columns:1fr;gap:10px}}
/* the year is data, so it is set in the machine face and lined — the column
   of years down the left of the timeline now actually forms a column */
.tl-yr{
  font-family:var(--f-mono);font-weight:500;font-size:clamp(1.25rem,2.6vw,1.75rem);
  letter-spacing:-.01em;line-height:1;color:var(--moss);
  font-variant-numeric:tabular-nums
}
.tl-row p{font-family:var(--f-read);font-size:1.03rem;max-width:64ch;margin:0}

/* ---------- info blocks ---------- */
.facts{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}
@media(max-width:700px){.facts{grid-template-columns:1fr}}
.fact{background:var(--paper);border:1px solid var(--line);border-radius:var(--r);padding:26px}
/* "Parking", "EV charging" — a term with its definition under it, so it is a
   heading, not a label, and it is set as one. */
.fact h4{
  font-family:var(--f-disp);font-weight:700;font-size:16px;letter-spacing:-.012em;
  margin:0 0 8px
}
.fact p{font-size:14.8px;color:var(--muted);margin:0}
.fact p+p{margin-top:9px}

/* ---------- notice: a disclosure, not a warning box --------------------
   These four notices are small print the estate volunteers rather than
   hides: two admit which photographs are AI-staged, one is the parking
   rule a visitor must act on, one caveats a timetable.

   So: no panel, no bar, no fill. The title hangs to the left of the text
   like a gloss in a margin, and the text is set LARGER than the copy it
   qualifies (17px against the 16.5px body) — the previous version set it
   at 15px, the smallest type in its section, which is how small print
   hides. It is also the only block on the site in plain sentence case,
   where everything else is letterspaced uppercase, so it reads as someone
   speaking rather than a system warning.

   Do NOT convert .notice__text to --muted in a later tidy-up. Volunteering
   small print and greying it out are contradictory instructions.

   Layout is driven by a container query, not a viewport one: the Info page
   drops a notice into a ~380px .split column at a 900px viewport, which no
   max-width media query would catch. Stacked is the default, so that is
   also the no-container-query fallback.                                 */
.notice{
  container-type:inline-size;
  background:none;border:0;border-radius:0;padding:0;  /* replaces, not appends */
  margin:clamp(38px,5vw,58px) 0 0
}
.notice__in{display:grid;grid-template-columns:minmax(0,1fr);row-gap:6px}

@container (min-width:30rem){
  .notice__in{
    grid-template-columns:minmax(0,clamp(6rem,26%,9.5rem)) minmax(0,46ch);
    column-gap:clamp(18px,2.4vw,32px);
    justify-content:start;
    align-items:baseline;          /* label sits on the first text baseline */
  }
  .notice__label{text-align:right}
}

/* --moss on --stone measures 6.53:1 since the palette was rewarmed, so this
   label now has real headroom. If --stone is ever darkened, re-measure: below
   4.5 the label must go to --ink at 700 and lose its only colour. */
.notice__label{
  margin:0;text-wrap:balance;
  font-size:15.5px;font-weight:700;line-height:1.42;
  letter-spacing:-.004em;color:var(--moss)
}
.notice__text{
  margin:0;
  font-size:1.0625rem;line-height:1.68;letter-spacing:-.006em;
  color:var(--ink);text-wrap:pretty
}

/* An instruction the reader must act on, not merely know about: same voice
   raised. Always full-column so it never competes with a hung label. */
.notice--act .notice__in{grid-template-columns:minmax(0,44ch)}
.notice--act .notice__label{text-align:left;font-size:16px;font-weight:800}
.notice--act .notice__text{font-size:1.1875rem;line-height:1.58;letter-spacing:-.011em}

/* Underlined at rest. A link inside a disclosure that only appears on hover
   is the same sin as hiding the disclosure. */
.notice__text a{
  color:var(--moss);font-weight:600;
  text-decoration:underline;text-decoration-thickness:1px;
  text-underline-offset:.19em;text-decoration-color:rgba(14,92,65,.45);
  transition:color .18s,text-decoration-color .18s
}
.notice__text a:hover{color:var(--ink);text-decoration-color:currentColor}

/* Colour is unavailable in forced-colors, so separate the label by weight. */
@media (forced-colors:active){
  .notice__label{font-weight:900}
  /* colour is discarded in forced-colors, so anything that signalled state with
     it alone needs a shape as well */
  .logos img{filter:none;opacity:1;mix-blend-mode:normal}
  .hero-seek button::before{background:CanvasText;opacity:.45}
  .hero-seek button::after{background:Highlight}
  .hero-tile{border:1px solid CanvasText}
  .hero-tile.is-live{outline:2px solid Highlight;outline-offset:-2px}
  .hero-tile::after{background:Highlight;forced-color-adjust:none}
  :where(a,button):focus-visible{outline:2px solid Highlight;box-shadow:none}
}
@media (prefers-reduced-motion:reduce){.notice__text a{transition:none}}

.mapfig{margin:0;border:1px solid var(--line);background:#fff;
  border-radius:var(--r);overflow:hidden}
.mapfig img{width:100%;height:auto}   /* height:auto is load-bearing: the img carries
  intrinsic width/height attributes to reserve the box against CLS, and without this
  the browser honours height="1667" literally and renders the plan stretched. */
.mapfig figcaption{
  font-family:var(--f-mono);font-size:11px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--muted);padding:13px 16px;border-top:1px solid var(--line)
}

/* ---------- the location: the estate plan at full measure --------------
   The plan is the most useful artifact on the site and it was unusable:
   shown at 510px from a 1200px source, with `.split figure img` slicing
   16.8% off its sides. Nothing crops it now, and it is served at up to
   1730px so the shrunk-to-fit copy stays sharp on a 2x screen.

   Served as the DRAWING ALONE. The source PNG has a tenant directory baked
   into its right-hand third which is illegible at any size this page can
   afford — and the Who's here page already lists every organisation — so
   the crop is done by Wix (x_0,y_63,w_1730,h_1667 of the 2900x2051
   original) rather than in CSS. That crop segment also makes Wix serve
   AVIF unconditionally, without content negotiation: 156KB at 1730px wide
   against 739KB for the old 1200px PNG. Sharper and a fifth of the weight.

   The pins carry a code the artwork never explains: solid #0771C0 is a
   front entrance, pale #38B6FF a rear one. Only buildings 1 and 2 have
   both — exactly the pair the estate directory splits into Front and Rear.
   The legend states it, because nothing else on the estate does.        */
/* Two columns from 901px up: plan on the left, the copy that explains it on
   the right, so the section reads without a scroll between question and
   answer — full measure meant the drawing alone was taller than most
   viewports. The plan is DOM-ordered after the copy (an h2 must precede its
   figure for anything reading linearly) and moved left with `order`.
   The drawing loses width in the trade, so `Open full size` in the caption
   stops being a nicety: it is now the way to read the small labels. */
.loc{display:grid;gap:clamp(28px,4vw,54px)}
@media(min-width:901px){
  .loc{grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);align-items:center}
  .loc .plan{order:-1}
}
.loc__copy>.eyebrow:first-child{margin-top:0}
.loc__h{font-size:clamp(1.85rem,3vw,2.45rem);margin-bottom:0}
.loc__lede{max-width:52ch;margin:22px 0 13px}
.loc__gaps{max-width:54ch;margin:0;font-size:15px;color:var(--muted)}
.loc__seq{
  display:block;margin-top:10px;color:var(--ink);
  font-family:var(--f-mono);font-weight:500;font-size:14.5px;
  font-variant-numeric:tabular-nums;letter-spacing:.02em
}
.loc__copy .notice{margin-top:clamp(24px,2.8vw,34px)}
/* The list fills the narrower column on its own, so the button always wraps
   to a row of its own — pushed right by the default `margin-left:auto` it
   floated away from the copy it belongs to. */
.loc__copy .loc__also{flex-direction:column;gap:7px}
.loc__copy .loc__after{flex-direction:column;align-items:flex-start;gap:20px}
.loc__copy .loc__after .btn{margin-left:0}

.plan{margin:0;border:1px solid var(--line-str);border-radius:var(--r);
  background:#fff;overflow:hidden}
/* width:auto + max-width/max-height, not width:100%: the plan is 1730x1667,
   so at full column width on a laptop it still ran past the fold. Capping
   the height lets it shrink to whatever the viewport actually has and the
   intrinsic ratio does the rest — no object-fit, nothing cropped. */
.plan__scroll{display:flex;justify-content:center;overflow:hidden}
.plan img{display:block;width:auto;height:auto;max-width:100%;max-height:min(62vh,600px)}
.plan__pan{display:none}
/* Below ~900px the plan scaled to the viewport renders its labels at 2-3px,
   which is where a visitor actually reads it — standing in the car park. So
   hold it at a legible 860px and let the reader pan sideways instead.
   touch-action:pan-x hands every vertical swipe straight back to the page,
   so the map is never a scroll trap mid-article. */
@media(max-width:900px){
  .plan__scroll{justify-content:flex-start;   /* centring clips the left edge once it overflows */
    overflow-x:auto;touch-action:pan-x;overscroll-behavior-x:contain}
  .plan__scroll img{width:860px;max-width:none;max-height:none}
  .plan__pan{display:inline;color:var(--ink);font-weight:500}
}
.plan figcaption{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px 22px;
  border-top:1px solid var(--line);padding:13px 18px;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--muted)
}
.plan__key{display:inline-flex;align-items:center;gap:8px}
.plan__sw{width:11px;height:11px;border-radius:50%;flex:none;
  /* the fills match the pins in the drawing and must not change, so the swatch
     gets a boundary instead — #38B6FF alone is 2.26:1 on white */
  box-shadow:0 0 0 1px rgba(11,33,26,.55)}
.plan__sw--front{background:#0771C0}   /* sampled from the artwork itself */
.plan__sw--rear{background:#38B6FF}
.plan__open{margin-left:auto;color:var(--moss);font-weight:500;text-underline-offset:4px}
@media(max-width:620px){.plan__open{margin-left:0}}

/* Arrival, ranked. The four equal boxes this replaces gave "EV charging —
   pending planning permission" the same weight as the parking rule you
   must actually follow, so the one instruction was buried among amenities
   and one of the four does not exist yet. */
.loc__after{
  display:flex;flex-wrap:wrap;align-items:center;gap:18px 34px;
  margin-top:clamp(26px,3.5vw,38px)
}
.loc__also{margin:0;padding:0;list-style:none;
  display:flex;flex-wrap:wrap;gap:6px 26px;font-size:14.6px;color:var(--muted)}
.loc__also b{color:var(--ink);font-weight:600}
.loc__after .btn{margin-left:auto}
@media(max-width:760px){.loc__after .btn{margin-left:0}}

/* ---------- faq ---------- */
.faq{border-top:1px solid var(--line-str)}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{
  cursor:pointer;list-style:none;padding:20px 40px 20px 0;position:relative;
  font-family:var(--f-disp);font-weight:600;font-size:16.5px;letter-spacing:-.005em
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";position:absolute;right:6px;top:15px;font-size:24px;line-height:1;color:var(--brass);font-weight:400;transition:transform .2s}
.faq summary:hover::after{transform:scale(1.15)}
.faq details[open] summary::after{content:"–"}
.faq .ans{padding:0 0 22px;max-width:70ch;font-size:15.4px;color:var(--muted)}
.faq .ans p:last-child{margin-bottom:0}

/* ---------- CTA + footer ---------- */
.cta-band{background:var(--moss);color:#fff;padding:clamp(52px,7vw,86px) 0}
.cta-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(30px,6vw,80px);align-items:end}
@media(max-width:860px){.cta-grid{grid-template-columns:1fr;gap:34px}}
.cta-stats{display:grid;grid-template-columns:repeat(2,1fr);border-top:1px solid rgba(255,255,255,.28)}
.cta-stats div{padding:20px 16px 18px 0;border-bottom:1px solid rgba(255,255,255,.16)}
.cta-stats b{display:block;font-family:var(--f-disp);font-weight:700;font-size:clamp(1.6rem,3vw,2.35rem);
  letter-spacing:-.025em;line-height:1;color:#fff;font-variant-numeric:tabular-nums}
.cta-stats i{display:block;font-family:var(--f-mono);font-style:normal;font-size:10px;
  letter-spacing:.15em;text-transform:uppercase;color:rgba(255,255,255,.72);margin-top:9px}
/* set like every other section head — see .h-sec */
.cta-band h2{
  font-family:var(--f-disp);font-weight:700;font-size:clamp(1.95rem,4.6vw,3.4rem);
  line-height:1.04;letter-spacing:-.032em;margin:0 0 18px;max-width:20ch;text-wrap:balance
}
.cta-band p{color:rgba(255,255,255,.85);max-width:52ch;font-family:var(--f-read);font-size:1.1rem}
.cta-mail{
  font-family:var(--f-mono);font-size:clamp(13px,2.2vw,17px);color:#fff;
  text-decoration:none;border-bottom:1px solid var(--brass-lt);padding-bottom:4px
}

.site-foot{background:var(--ink);color:rgba(255,255,255,.72);padding:clamp(44px,6vw,68px) 0 34px;font-size:14.5px}
.foot-grid{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:32px}
@media(max-width:860px){.foot-grid{grid-template-columns:1fr 1fr}}
@media(max-width:520px){.foot-grid{grid-template-columns:1fr}}
.site-foot h5{
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:rgba(255,255,255,.5);margin:0 0 14px;font-weight:500
}
.site-foot a{color:rgba(255,255,255,.82);text-decoration:none}
.site-foot a:hover{color:#fff;text-decoration:underline;text-underline-offset:3px}
.site-foot ul{list-style:none;margin:0;padding:0}
.site-foot li{margin-bottom:9px}
.foot-brand img{width:64px;margin-bottom:16px;background:#fff;border-radius:50%;padding:5px}
.foot-brand p{font-size:14px;max-width:34ch;color:rgba(255,255,255,.6)}
.foot-btm{
  border-top:1px solid rgba(255,255,255,.13);margin-top:38px;padding-top:22px;
  display:flex;flex-wrap:wrap;gap:12px;justify-content:space-between;
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.11em;text-transform:uppercase;
  color:rgba(255,255,255,.55)
}

/* ============================================================
   HERO — "standing in the grounds"
   One photograph you look into, graded down to forest dark.
   The estate's bookable rooms float over it as frosted panes:
   the tile grid is the booking menu, not decoration.
   ============================================================ */
:root{
  --hero-live:#7FD9A8;   /* 9.94:1 on --ink */
  --head-h:  84px;
  --pale:    #E8F0EC;   /* headline: pale grey-green, never pure white */
  --rail-w:  60px;
}

/* every page runs to the top edge; the bar floats over it */
main{padding-top:0}
/* …so the band that opens a page has to clear the bar */
.band--top{padding-top:calc(var(--bar-h) + clamp(34px,4.4vw,66px))}
.nav{justify-self:center}

/* Over the hero the chrome is a pane of glass rather than a solid band: a
   frosted bar hung off the rail, carrying the mark, the four destinations and
   the booking link. Everywhere else the header stays the solid white one. */
/* The bar was reading as almost nothing over the drone shot — the estate said
   it "gets lost" against the white one it becomes on every other page. It is
   still glass, but now glass with a pane behind it: a dark wash under a
   brighter white film, so the pill has an edge wherever the photograph puts
   sky, grass or roof behind it, without becoming the solid band that would
   defeat the point of a hero. */
.site-head.over .head-in{
  background:
    linear-gradient(rgba(255,255,255,.17), rgba(255,255,255,.13)),
    rgba(4,20,15,.30);
  border-color:rgba(255,255,255,.42);
  box-shadow:
    0 16px 46px rgba(3,15,10,.40),
    inset 0 1px 0 rgba(255,255,255,.34)
}
.site-head.over .brand-tx b{color:var(--pale)}
.site-head.over .brand-tx i{color:rgba(255,255,255,.85);text-shadow:0 1px 3px rgba(3,15,10,.85), 0 1px 12px rgba(3,15,10,.9)}
/* every colour in the bar changes over the same .45s, so crossing out of the
   hero reads as one cross-fade rather than a set of separate switches */
.brand-tx b,.brand-tx i{transition:color .45s, text-shadow .45s}
.nav a{transition:color .32s, border-color .3s}
.burger span{transition:background .45s, transform .35s cubic-bezier(.16,1,.3,1), opacity .2s}
.head-cta{transition:background .45s, border-color .45s, color .45s}
.site-head.over .nav{display:flex}
.site-head.over .nav a{color:rgba(255,255,255,.84);border-bottom-color:transparent}
.site-head.over .nav a:hover{color:#fff}
.site-head.over .nav a.is-on{color:var(--hero-live);border-bottom-color:transparent}
.site-head.over .head-cta{
  display:inline-flex;margin-left:0;padding:12px 22px;border-radius:999px;
  background:var(--moss);border-color:var(--moss);color:#fff
}
.site-head.over .head-cta:hover{background:var(--pale);border-color:var(--pale);color:var(--ink)}
.site-head.over .burger{display:none}

.brand-big{display:none}
.brand-sm{display:flex;align-items:center;gap:11px}
.site-head.over .brand-sm{display:flex}

/* ---- burger ---- */
.burger{
  background:transparent;border:0;padding:10px 4px;cursor:pointer;
  width:42px;height:42px;display:none;position:relative
}
.burger span{
  position:absolute;left:8px;right:8px;height:1.5px;background:#fff;
  border-radius:2px;box-shadow:0 1px 5px rgba(3,15,10,.75);transition:transform .35s cubic-bezier(.16,1,.3,1), opacity .2s
}
.burger span:nth-child(1){top:15px}
.burger span:nth-child(2){top:24px}
.site-head:not(.over) .burger span{background:var(--ink)}
.burger[aria-expanded="true"] span:nth-child(1){transform:translateY(4.5px) rotate(45deg)}
.burger[aria-expanded="true"] span:nth-child(2){transform:translateY(-4.5px) rotate(-45deg)}

/* ---- full-screen menu (keeps every destination reachable) ---- */
.navmask{
  position:fixed;inset:0;z-index:70;display:flex;flex-direction:column;
  justify-content:center;padding:0 clamp(24px,8vw,140px);
  background:rgba(4,20,15,.93);
  backdrop-filter:blur(22px) saturate(140%);-webkit-backdrop-filter:blur(22px) saturate(140%);
  opacity:0;visibility:hidden;transition:opacity .4s, visibility .4s
}
.navmask.open{opacity:1;visibility:visible}
.navmask a{
  display:block;color:var(--pale);text-decoration:none;
  font-family:var(--f-disp);font-weight:300;letter-spacing:-.02em;
  font-size:clamp(1.9rem,5.4vw,3.6rem);line-height:1.5;
  opacity:0;transform:translateY(18px);transition:color .25s
}
.navmask.open a{animation:maskIn .6s cubic-bezier(.16,1,.3,1) forwards;animation-delay:var(--d,0ms)}
@keyframes maskIn{to{opacity:1;transform:none}}
.navmask a:hover{color:var(--hero-live)}
.navmask a.is-on{color:var(--hero-live)}
.navmask .mask-cta{
  margin-top:34px;font-family:var(--f-body);font-size:15px;font-weight:600;
  letter-spacing:.02em;color:var(--ink);background:var(--pale);
  width:max-content;padding:15px 30px;border-radius:999px;line-height:1
}
.navmask .mask-cta:hover{background:var(--moss-2);color:#fff}

/* ---- frame ---- */
.hero{
  position:relative;min-height:100svh;display:flex;overflow:hidden;
  background:#06120E;isolation:isolate
}
/* --- carousel + parallax ---------------------------------------------
   Depth is split three ways so the frosted tiles read as real panes of
   glass standing in front of a view: the backdrop drifts against the
   pointer, the tiles drift with it, and the copy rises on scroll.       */
.hero-stage{position:absolute;inset:0;z-index:0;overflow:hidden}
.hero-parallax{
  position:absolute;inset:-13% -7%;
  transform:translate3d(0, var(--sy,0px), 0) scale(var(--sc,1));
  will-change:transform
}
.hero-slide{
  position:absolute;inset:0;opacity:0;
  transition:opacity 1.4s cubic-bezier(.4,0,.2,1)
}
.hero-slide.is-on{opacity:1}
.hero-slide img{
  width:100%;height:100%;object-fit:cover;transform:scale(1.02);
  filter:saturate(1.04) contrast(1.02) brightness(1.02)
}
/* the still breathes while it is held */
.hero-slide.is-on img{animation:ken 9.5s linear forwards}
@keyframes ken{from{transform:scale(1.02)}to{transform:scale(1.11)}}

/* --- reading scrim ----------------------------------------------------
   The photograph is bright where the words are: the projector screen sits
   straight behind the headline and the sky behind the mark. Two graded
   washes sit between the picture and the type — one down the rail-and-copy
   side, one across the header band — so the pale type keeps its contrast
   whichever slide is showing. Both are transparent by mid-frame, which
   leaves the right of the picture, and the frosted tiles, untouched.     */
.hero::before,
.hero::after{content:"";position:absolute;z-index:1;pointer-events:none}
.hero::before{
  inset:0;
  background:linear-gradient(90deg,
    rgba(3,17,12,.54) 0%,
    rgba(3,17,12,.40) 22%,
    rgba(3,17,12,.18) 44%,
    rgba(3,17,12,0)   62%)
}
.hero::after{
  left:0;right:0;top:0;height:calc(var(--head-h) + 96px);
  background:linear-gradient(180deg,
    rgba(3,17,12,.40) 0%,
    rgba(3,17,12,.18) 45%,
    rgba(3,17,12,0)   100%)
}

/* --- seek rail: one control that both shows progress and selects --- */
.hero-seek{display:flex;align-items:center;gap:6px;margin-top:14px;margin-bottom:-10px;flex-wrap:wrap}
.hero-seek button{
  position:relative;width:44px;height:44px;padding:0;
  background:transparent;border:0;cursor:pointer
}
.hero-seek button::before,
.hero-seek button::after{
  content:"";position:absolute;left:0;right:0;top:21px;height:2px;border-radius:2px
}
.hero-seek button::before{background:rgba(255,255,255,.45);box-shadow:0 1px 6px rgba(3,15,10,.5)}
.hero-seek button::after{background:var(--pale);transform:scaleX(0);transform-origin:left}
.hero-seek button:hover::before{background:rgba(232,240,236,.5)}
.hero-seek button.is-on::after{animation:seek var(--dur,7000ms) linear forwards}
.hero-seek button.is-done::after{transform:scaleX(1)}
@keyframes seek{to{transform:scaleX(1)}}

/* ---- left rail ---- */
.hero-rail{
  position:absolute;left:0;top:0;bottom:0;width:var(--rail-w);z-index:4;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:calc(var(--head-h) + 8px) 0 26px;
  border-right:1px solid rgba(255,255,255,.22)
}
.hero-rail .place{
  writing-mode:vertical-rl;transform:rotate(180deg);
  font-family:var(--f-mono);font-size:10.5px;letter-spacing:.28em;text-transform:uppercase;
  color:rgba(255,255,255,.88);white-space:nowrap;text-shadow:0 1px 3px rgba(3,15,10,.8), 0 1px 12px rgba(3,15,10,.9)
}
.hero-rail .rail-social{position:absolute;bottom:20px;left:0;right:0;display:flex;flex-direction:column;gap:2px;align-items:center}
.hero-rail .rail-social a{color:rgba(255,255,255,.9);filter:drop-shadow(0 1px 4px rgba(3,15,10,.85));display:grid;place-items:center;width:32px;height:32px;transition:color .25s, transform .3s}
.hero-rail .rail-social a:hover{color:var(--pale);transform:translateY(-2px)}
.hero-rail .rail-social svg{width:16px;height:16px;fill:currentColor}

/* ---- content ---- */
.hero-inner{
  position:relative;z-index:2;width:100%;max-width:var(--shell,1500px);margin:0 auto;
  /* The right padding was 0, so the booking tiles ran flush into the viewport
     edge and looked clipped at every width above 820px. The rail is only on
     the left, so the two sides are set independently.                    */
  padding:calc(var(--head-h) + clamp(20px,5vh,54px)) clamp(24px,4vw,64px) clamp(26px,5vh,50px)
          calc(var(--rail-w) + clamp(24px,4vw,64px));
  display:grid;grid-template-columns:minmax(0,1fr) auto;
  gap:clamp(24px,4vw,60px);align-items:stretch
}

/* The words stand on their own pane: light at the head where it catches the
   sky, dense at the foot where the reading is. It is the same glass as the
   bar above it and the room cards beside it — one material, three uses.   */
.hero-copy{
  display:flex;flex-direction:column;justify-content:center;align-self:center;
  max-width:min(100%,630px);
  padding:clamp(26px,2.6vw,40px) clamp(24px,2.4vw,38px) clamp(24px,2.4vw,34px);
  border-radius:22px;
  background:linear-gradient(160deg,
    rgba(6,26,19,.30) 0%,
    rgba(5,22,16,.42) 52%,
    rgba(3,16,12,.56) 100%);
  border:1px solid rgba(255,255,255,.20);
  box-shadow:0 24px 70px rgba(3,15,10,.34), inset 0 1px 0 rgba(255,255,255,.18);
  backdrop-filter:blur(15px) saturate(122%);-webkit-backdrop-filter:blur(15px) saturate(122%);
  transform:translate3d(0,var(--cy,0px),0);opacity:var(--cop,1)
}
.hero-h1{
  font-family:var(--f-disp);font-weight:400;color:#fff;
  font-size:clamp(1.95rem,3.9vw,3.5rem);line-height:1.1;letter-spacing:-.028em;
  margin:0;text-transform:none;
  /* three lines are held open whatever the slide says, so the button and the
     note underneath stay put as the headline turns — and the lines sit at the
     FOOT of that box, so a two-line slide banks its spare line as air above
     the words instead of as a hole between them and the button */
  min-height:3.21em;display:flex;flex-direction:column;justify-content:flex-end;
  text-shadow:0 2px 26px rgba(3,15,10,.78), 0 1px 5px rgba(3,15,10,.45)
}
.hero-h1 span{
  display:block;opacity:0;transform:translateY(24px);
  animation:heroRise 1.1s cubic-bezier(.16,1,.3,1) both
}
.hero-h1 span:nth-child(1){animation-delay:.20s}
.hero-h1 span:nth-child(2){animation-delay:.32s}
.hero-h1 span:nth-child(3){animation-delay:.44s}
/* the opening line takes its time; every later turn of the carousel is quick,
   so the words land while the picture behind them is still crossfading */
.hero-h1.is-swap span{animation-duration:.72s}
.hero-h1.is-swap span:nth-child(1){animation-delay:0s}
.hero-h1.is-swap span:nth-child(2){animation-delay:.07s}
.hero-h1.is-swap span:nth-child(3){animation-delay:.14s}
@keyframes heroRise{to{opacity:1;transform:none}}

/* ---- the slide's own call to action, beside the scroll cue ---- */
.hero-act{
  display:flex;align-items:center;gap:clamp(12px,1.5vw,18px);flex-wrap:wrap;
  margin-top:clamp(20px,3.4vh,38px);
  opacity:0;animation:heroRise 1.1s cubic-bezier(.16,1,.3,1) .58s both
}
.hero-cta{
  display:inline-flex;align-items:center;gap:10px;
  font-family:var(--f-body);font-size:12px;font-weight:600;
  letter-spacing:.11em;text-transform:uppercase;text-decoration:none;
  padding:16px 28px;border-radius:999px;
  background:var(--moss);border:1px solid var(--moss);color:#fff;
  box-shadow:0 6px 26px rgba(3,17,12,.42);
  transition:background .24s, border-color .24s, color .24s
}
.hero-cta:hover{background:var(--pale);border-color:var(--pale);color:var(--ink)}
.hero-cta::after{content:"→";font-family:var(--f-body);font-size:14px;line-height:1}
/* the label is swapped with the slide, so it rises in with the headline */
.hero-cta span{
  display:inline-block;opacity:0;transform:translateY(9px);
  animation:heroRise .72s cubic-bezier(.16,1,.3,1) .06s both
}

.hero-scroll{
  width:54px;height:54px;border-radius:50%;border:1.5px solid rgba(255,255,255,.7);
  display:grid;place-items:center;margin:0;color:#fff;flex:none;
  background:rgba(255,255,255,.13);cursor:pointer;
  backdrop-filter:blur(12px) saturate(120%);-webkit-backdrop-filter:blur(12px) saturate(120%);
  box-shadow:0 8px 26px rgba(3,15,10,.28), inset 0 1px 0 rgba(255,255,255,.22);
  transition:background .3s, border-color .3s
}
.hero-scroll:hover{background:rgba(255,255,255,.22);border-color:#fff}
.hero-scroll svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;animation:bob 2.4s ease-in-out infinite}
@keyframes bob{0%,100%{transform:translateY(-2px)}50%{transform:translateY(3px)}}

.hero-note{
  margin-top:auto;padding-top:clamp(24px,5vh,54px);max-width:44ch;
  opacity:0;animation:heroRise 1.1s cubic-bezier(.16,1,.3,1) .70s both
}
.hero-note h2{
  font-family:var(--f-body);font-weight:700;font-size:16px;letter-spacing:-.005em;
  color:#fff;margin:0 0 12px;text-shadow:0 1px 3px rgba(3,15,10,.7), 0 2px 18px rgba(3,15,10,.9)
}
.hero-note p{
  font-family:var(--f-body);font-size:14px;line-height:1.65;
  color:#fff;margin:0 0 10px;text-shadow:0 1px 3px rgba(3,15,10,.7), 0 2px 16px rgba(3,15,10,.9)
}
.hero-note p:last-child{margin-bottom:0}

/* ---- the signature: bookable rooms as frosted panes ----
   Sash windows onto the view, hung on a hairline glazing bar: each pane names
   a room and what it costs, so the grid reads as the price list it is. The
   pane the carousel is showing lights its sill with the same green fill the
   seek dashes use — menu and picture are one control, not two.              */
.hero-tiles-wrap{
  align-self:end;width:clamp(320px,40vw,560px);min-width:0;
  opacity:0;animation:heroRise 1.2s cubic-bezier(.16,1,.3,1) .46s both
}
/* The panes run in a single sliding row, two in view at a time. It is a real
   scroller with snap points, so a trackpad or a thumb moves it natively; the
   scrollbar is hidden because the arrows and dots say the same thing. The
   padding gives the panes' shadows room inside the scroller's clip, and the
   negative margin gives that room back to the layout. */
.hero-tiles{
  --tiles-gap:10px;--in-view:2;
  width:100%;
  display:flex;gap:var(--tiles-gap);
  overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;scrollbar-width:none;
  padding:8px 0 44px;margin:-8px 0 -44px;
  cursor:grab;touch-action:pan-x pan-y
}
.hero-tiles::-webkit-scrollbar{display:none}
.hero-tiles.is-drag{scroll-snap-type:none;cursor:grabbing}
.hero-tiles.is-drag .hero-tile{pointer-events:none}
.hero-tile{
  flex:0 0 calc((100% - var(--tiles-gap) * (var(--in-view) - 1)) / var(--in-view));
  scroll-snap-align:start;-webkit-user-drag:none;user-select:none;
  position:relative;min-height:clamp(128px,17vh,172px);
  display:flex;flex-direction:column;align-items:flex-start;
  padding:16px 18px 15px;border-radius:14px;
  /* each pane is a picture of its room: the photograph fills it, a shade
     sits over the photograph, and the words sit on the shade. The ground
     colour only shows while the picture is still loading. */
  overflow:hidden;isolation:isolate;background:#0B2419;
  border:1px solid rgba(255,255,255,.22);
  box-shadow:0 10px 26px rgba(3,15,10,.3);
  color:var(--pale);text-decoration:none;
  transition:border-color .38s, color .3s
}
.hero-tile-img{
  position:absolute;inset:0;z-index:0;width:100%;height:100%;
  object-fit:cover;pointer-events:none;
  transform:scale(1.01);
  transition:transform 1.1s cubic-bezier(.16,1,.3,1), filter .5s
}
/* The shade, dense at the foot where the words are.
   157deg runs top-left to bottom-RIGHT, so the dense end of this gradient
   landed in the corner opposite the words: .lbl and .price are flex-start,
   i.e. bottom-LEFT, and sat on the brightest part of the tile. Over the
   sunlit slides that measured 2.3-3.6:1. Straightening it to 178deg puts the
   density along the bottom EDGE, where the type actually is. Over a whole
   photograph it has to work harder than it did over frosted glass, so the
   foot is near-solid. */
.hero-tile::before{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(178deg,
    rgba(4,22,15,.10) 0%,
    rgba(5,24,17,.52) 42%,
    rgba(3,15,11,.92) 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.20);
  transition:opacity .38s
}
.hero-tile .lbl,.hero-tile .price{position:relative;z-index:2}
.hero-tile:hover,
.hero-tile.is-live{border-color:rgba(255,255,255,.5);color:#fff}
.hero-tile:hover .hero-tile-img,
.hero-tile.is-live .hero-tile-img{transform:scale(1.07)}
/* the name and the price sit on the sill; the auto margin puts the free space
   above them, so a pane reads the same whatever height the grid gives it */
.hero-tile .lbl{
  margin-top:auto;
  font-family:var(--f-body);font-size:15px;font-weight:500;
  letter-spacing:-.005em;line-height:1.2;color:#fff;
  text-shadow:0 1px 10px rgba(3,17,11,.72)
}
.hero-tile .price{
  border-top:0;padding-top:0;margin:5px 0 0;
  font-family:var(--f-mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  /* .74 alpha with no shadow, at 10px, was the least legible type on the site */
  color:rgba(238,245,241,.95);text-shadow:0 1px 10px rgba(3,17,11,.75);
  transition:color .38s
}
.hero-tile:hover .price,
.hero-tile.is-live .price{color:var(--hero-live)}
/* the lit sill */
.hero-tile::after{
  content:"";position:absolute;z-index:3;left:16px;right:16px;bottom:0;height:2px;border-radius:2px;
  background:var(--hero-live);
  transform:scaleX(0);transform-origin:left;
  transition:transform .5s cubic-bezier(.16,1,.3,1)
}
.hero-tile.is-live::after{transform:scaleX(1)}
/* over the track: what it is, and the arrows that turn it */
.hero-tiles-head{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin-bottom:12px
}
.hero-tiles-title{
  font-family:var(--f-mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;
  color:rgba(238,245,241,.95);text-shadow:0 1px 10px rgba(3,17,11,.75)
}
.hero-tiles-nav{display:flex;gap:8px}
.hero-tiles-nav button{
  width:40px;height:40px;border-radius:50%;display:grid;place-items:center;
  margin:0;padding:0;color:#fff;cursor:pointer;
  border:1px solid rgba(255,255,255,.42);background:rgba(255,255,255,.1);
  backdrop-filter:blur(12px) saturate(120%);-webkit-backdrop-filter:blur(12px) saturate(120%);
  box-shadow:0 8px 22px rgba(3,15,10,.24), inset 0 1px 0 rgba(255,255,255,.2);
  transition:background .25s, border-color .25s, transform .25s cubic-bezier(.16,1,.3,1)
}
.hero-tiles-nav button:hover{background:rgba(255,255,255,.22);border-color:#fff}
.hero-tiles-nav button:active{transform:scale(.94)}
.hero-tiles-nav svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}
/* under the track: one dash per stop, the current one long and lit */
.hero-dots{display:flex;gap:6px;margin-top:14px}
.hero-dots button{
  width:16px;height:3px;border-radius:2px;margin:0;padding:0;border:0;cursor:pointer;
  background:rgba(255,255,255,.38);position:relative;
  transition:width .45s cubic-bezier(.16,1,.3,1), background .3s
}
/* a taller hit area than the 3px dash shows */
.hero-dots button::before{content:"";position:absolute;inset:-10px -3px}
.hero-dots button.is-on{width:34px;background:var(--hero-live)}

/* ---- white contact bar + copyright strip ---- */
/* was #fff with #8d8d8d text (3.54:1) and an #a5a5a5 copyright line (2.59:1),
   both under AA for their size. Warmed onto the page ground and darkened. */
.hero-bar{background:var(--paper);border-bottom:1px solid var(--line)}
.hero-bar .in{
  max-width:1340px;margin:0 auto;padding:19px clamp(20px,4vw,54px);
  display:flex;align-items:center;gap:26px;flex-wrap:wrap
}
.hero-bar .item{display:flex;align-items:center;gap:9px;font-size:13px;color:var(--muted)}
.hero-bar .item a{color:var(--muted);text-decoration:none}
.hero-bar .item a:hover{color:var(--moss)}
.hero-bar svg{width:13px;height:15px;fill:var(--brass);flex:none}
.hero-bar .socials{margin-left:auto;display:flex;gap:6px;margin-right:-6px}
.hero-bar .socials a{color:var(--muted);display:grid;place-items:center;width:32px;height:32px}
.hero-bar .socials a:hover{color:var(--moss)}
.hero-bar .socials svg{width:15px;height:15px;fill:currentColor}
.hero-copyright{background:var(--paper);border-bottom:1px solid var(--line)}
.hero-copyright .in{
  max-width:1340px;margin:0 auto;padding:15px clamp(20px,4vw,54px);
  font-size:12px;color:var(--muted)
}

/* ---- responsive ---- */
@media(max-width:1080px){
  .hero-copy{max-width:100%}
  .hero-tiles-wrap{width:clamp(280px,44vw,420px)}
}
@media(max-width:940px){
  .head-in{grid-template-columns:auto auto;justify-content:space-between;height:66px}
  .site-head.over .head-in{grid-template-columns:auto auto;height:58px;padding:0 8px 0 16px}
  .burger{display:block;justify-self:end}
  .head-cta{display:none}
  .nav{display:none}
  .site-head.over .nav{display:none}
  .site-head.over .head-cta{display:none}
  .site-head.over .burger{display:block;justify-self:end}
}
@media(max-width:820px){
  :root{--rail-w:44px}
  .hero::before{
    background:linear-gradient(180deg,
      rgba(3,17,12,.72) 0%,
      rgba(3,17,12,.48) 38%,
      rgba(3,17,12,.60) 100%)
  }
  .hero{min-height:auto}
  .hero-inner{
    grid-template-columns:1fr;padding-right:clamp(18px,5vw,28px);
    padding-bottom:34px;gap:34px
  }
  .hero-rail .place{display:none}
  .hero-tiles-wrap{width:100%;align-self:auto}
  /* a phone shows one and a half panes, so the half says there is more */
  .hero-tiles{--in-view:1.6}
  .hero-note{margin-top:0}
}
@media(max-width:480px){
  .hero-tile{min-height:112px;padding:14px 15px 13px}
  .hero-tile .lbl{font-size:14px}
}

@media(prefers-reduced-motion:reduce){
  *{animation:none!important;transition:none!important;scroll-behavior:auto!important}
  /* .hero-tiles-wrap is opacity:0 until heroRise fills it, and the rule above has
     just killed every animation — so without this the entire booking menu
     renders fully transparent while staying in the layout, focusable and
     clickable. Anything that starts at opacity:0 must be listed here. */
  .hero-h1 span,.hero-note,.hero-act,.hero-cta span,.hero-tiles-wrap{opacity:1;transform:none}
  .hero-copy,.hero-parallax,.hero-tile-img{transform:none}
  .hero-slide.is-on img{transform:scale(1.02)}
  .navmask.open a{opacity:1;transform:none}
}


/* ============================================================
   PAGE HEAD — the estate, not a green rectangle
   ------------------------------------------------------------
   Who's here, Our spaces and General information all opened on a
   flat --ink band with the title in the left half and roughly six
   hundred pixels of empty dark green to the right of it. On a site
   whose entire proposition is thirty-eight acres of Victorian
   parkland, the three biggest surfaces after the hero were showing
   the visitor nothing at all.

   So the band keeps its exact structure — eyebrow, h1, lede, spec
   strip — and gains a photograph behind it. Two washes hold the
   type: one across the page dense on the left where the words are
   and clearing by mid-frame so the picture survives, one down from
   the top so the floating header bar always has something to sit
   on. The image is set per page from the markup with --shot.
   ============================================================ */
.band--shot{
  position:relative;isolation:isolate;background:var(--ink);overflow:hidden;
  padding-top:calc(var(--bar-h) + clamp(46px,6vw,88px));
  padding-bottom:clamp(42px,5.5vw,72px)
}
.band--shot::before{
  content:"";position:absolute;inset:0;z-index:-2;
  background-image:var(--shot);background-size:cover;
  background-position:var(--shot-pos,50% 42%);
  transform:scale(1.03)
}
.band--shot::after{
  content:"";position:absolute;inset:0;z-index:-1;
  background:
    linear-gradient(93deg,
      rgba(11,33,26,.93) 0%,
      rgba(11,33,26,.84) 32%,
      rgba(11,33,26,.56) 60%,
      rgba(11,33,26,.36) 100%),
    linear-gradient(180deg,
      rgba(11,33,26,.70) 0%,
      rgba(11,33,26,.10) 38%,
      rgba(11,33,26,0)   60%)
}
/* the head's own type, pale on the photograph */
.band--shot .h-sec{color:#fff;max-width:16ch}
.band--shot .lede{color:rgba(255,255,255,.90);max-width:52ch}
.band--shot .eyebrow{color:#fff;opacity:.92}
.band--shot .eyebrow::before{background:var(--brass-lt)}
.band--shot .spec{
  border-color:rgba(255,255,255,.30);color:rgba(255,255,255,.80)
}
.band--shot .spec span{border-color:rgba(255,255,255,.20)}
.band--shot .spec b{color:#fff;font-weight:500}

/* Below the fold of a phone the graded wash across the page is the wrong
   axis — there is no right-hand half to protect — so it becomes a straight
   top-to-bottom darkening and the picture keeps the bottom of the band. */
@media(max-width:760px){
  .band--shot::after{
    background:linear-gradient(180deg,
      rgba(11,33,26,.88) 0%,
      rgba(11,33,26,.74) 45%,
      rgba(11,33,26,.62) 100%)
  }
  .band--shot .h-sec{max-width:none}
}

/* ---------- emphasised words in a section heading ----------
   Used by <Emph>, which marks words inside a translatable string. The
   emphasis is carried by weight, colour and a rule under the word rather than
   by italics — the display face is set tight and its italic reads as a
   different voice rather than as stress. The rule is drawn with a gradient
   background instead of text-decoration so it can sit clear of the descenders
   at this size. */
.h-sec .emph{
  font-style:normal;font-weight:700;color:var(--moss);
  background-image:linear-gradient(var(--moss-2),var(--moss-2));
  background-repeat:no-repeat;
  background-size:100% .09em;
  background-position:0 calc(100% - .06em);
  padding-bottom:.10em
}

/* ---------- Rightmove link ----------
   A row, not a card: it sits under the three unit cards and points off the
   site, so it should read as the next line of the page rather than as a
   fourth unit competing with them. */
.rm{
  display:flex;align-items:center;gap:clamp(14px,2vw,22px);
  margin-top:26px;padding:20px clamp(18px,2.4vw,26px);
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-md,14px);
  text-decoration:none;color:inherit;
  transition:border-color .2s, box-shadow .2s, transform .2s
}
.rm:hover{
  border-color:var(--moss);
  box-shadow:0 10px 30px rgba(6,126,72,.13);
  transform:translateY(-2px)
}
.rm__mark{
  flex:none;display:grid;place-items:center;
  width:46px;height:46px;border-radius:50%;
  background:var(--moss);color:#fff
}
.rm__mark svg{width:24px;height:24px}
.rm__copy{display:flex;flex-direction:column;gap:4px;min-width:0}
.rm__copy b{
  font-family:var(--f-disp);font-weight:600;font-size:16.5px;letter-spacing:-.008em;color:var(--ink)
}
.rm__copy span{font-size:14px;color:var(--muted)}
.rm__go{margin-left:auto;flex:none;font-size:20px;color:var(--moss);transition:transform .2s}
.rm:hover .rm__go{transform:translate(3px,-3px)}
@media (max-width:560px){
  .rm{align-items:flex-start}
  .rm__go{display:none}
}

/* The 40px tinted tiles that used to sit here were replaced by the drawings —
   see "illustrations" at the foot of this file. */
@media (prefers-reduced-motion:reduce){
  .rm,.rm__go{transition:none}
  .rm:hover{transform:none}
}

/* ---------- illustrations ----------
   One drawing style across the site: an ink outline with a single accent in
   the lighter brand green, drawn on a 64 grid (see Glyph.tsx). The colour is
   set here once — the artwork inherits it through currentColor — so a card on
   a dark band could invert the whole set by changing one property. */
.glyph{
  display:block;
  width:clamp(52px,5.4vw,64px);
  height:clamp(52px,5.4vw,64px);
  margin-bottom:16px;
  color:var(--ink);
  transition:color .22s, transform .22s;
}
.glyph svg{width:100%;height:100%;display:block;overflow:visible}

/* On the amenity panels the drawing is the thing that carries the section, so
   it sits above the term and the panel lifts as one on hover. */
.facts--marked .fact{padding-top:24px;transition:border-color .2s, box-shadow .2s}
.facts--marked .fact:hover{border-color:var(--moss);box-shadow:0 8px 26px rgba(6,126,72,.10)}
.facts--marked .fact:hover .glyph{color:var(--moss)}

/* On the three "what brings you here" cards the drawing replaces nothing —
   it sits above the category label, which keeps its place as the eyebrow. */
.route .glyph{margin-bottom:18px}
.route:hover .glyph{color:var(--moss);transform:translateY(-2px)}

@media (prefers-reduced-motion:reduce){
  .glyph{transition:none}
  .route:hover .glyph{transform:none}
}

/* ---------- very wide screens ----------
   The shell was capped at 1220px, which on a 2560px monitor left 52% of the
   screen as empty margin and on an ultrawide 56% — the page sat in a strip
   down the middle.

   Widening it is safe because the reading measure is capped separately and in
   ch, not by the shell: .lede at 58ch, .loc__lede at 52, the history at 64.
   So the extra width goes where there is something to see — the estate's
   photographs, the logo wall, the card grids and the plan — and the prose
   keeps the line length it was set at.

   --shell is the header and hero rail, which sit wider than the text bands
   and need to keep that relationship as both grow. */
@media (min-width:1500px){ :root{ --wrap:1340px; --shell:1560px } }
@media (min-width:1800px){ :root{ --wrap:1520px; --shell:1780px } }
@media (min-width:2200px){ :root{ --wrap:1760px; --shell:2040px } }
@media (min-width:2800px){ :root{ --wrap:2000px; --shell:2320px } }

/* With the room to do it, the grids take another column rather than letting
   each cell grow to the size of a poster. */
@media (min-width:1800px){
  .logos{grid-template-columns:repeat(5,1fr)}
  .facts{grid-template-columns:repeat(3,1fr)}
}
@media (min-width:2400px){
  .logos{grid-template-columns:repeat(6,1fr)}
  .routes{gap:clamp(18px,1.4vw,26px)}
}

/* ---------- logo wall: four scrolling rows ----------
   The whole directory, not the sixteen we hold artwork for. Rows alternate
   direction so the wall reads as movement rather than as a conveyor, and each
   row's duration is set from its own tile count in LogoWall.tsx so a longer
   row does not travel faster.

   The track holds every row twice and travels exactly -50%: at that point the
   second copy sits where the first began, so the loop has no seam. */
.wall{
  margin-top:clamp(26px,3vw,34px);
  display:flex;flex-direction:column;gap:1px;
  background:var(--line-str);
  border:1px solid var(--line-str);
  border-radius:var(--r);
  overflow:hidden;
  /* the rows run to the edges, so fade them out rather than cutting them */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent);
}
.wall__row{overflow:hidden;background:var(--paper)}
.wall__track{
  display:flex;width:max-content;
  animation:wallRun var(--dur,80s) linear infinite;
}
.wall__track.is-rev{animation-direction:reverse}
@keyframes wallRun{to{transform:translateX(-50%)}}

/* A moving link is a link you cannot click. Any hover or keyboard focus
   anywhere in the wall stops every row, not just the one under the pointer —
   stopping one row while its neighbours run reads as a fault. */
.wall:hover .wall__track,
.wall:focus-within .wall__track{animation-play-state:paused}

.wall__tile{
  flex:none;
  width:clamp(150px,15vw,196px);
  height:clamp(84px,8.6vw,104px);
  display:grid;place-items:center;
  padding:14px 20px;
  border-right:1px solid var(--line);
  background:color-mix(in srgb, var(--brand,transparent) 9%, var(--paper));
  text-decoration:none;
  transition:background .22s;
}
a.wall__tile:hover{background:color-mix(in srgb, var(--brand,var(--moss)) 20%, var(--paper))}
.wall__tile.is-rev{background:var(--brand)}
a.wall__tile.is-rev:hover{background:color-mix(in srgb, var(--brand) 86%, #000)}

.wall__tile img{
  --box:clamp(38px,4vw,50px);
  width:100%;max-width:calc(150px * var(--fit,1));
  height:calc(var(--box) * var(--fit,1));
  object-fit:contain;
  mix-blend-mode:multiply;
}
.wall__tile img.is-tall{--box:clamp(52px,5.6vw,68px)}
/* A white mark on its own brand colour must not multiply — multiply drops
   white, which would drop the mark and leave an empty tile. */
.wall__tile.is-rev img{mix-blend-mode:normal}

/* No artwork yet: the name, set as a wordmark. */
.wall__name{
  font-family:var(--f-disp);font-weight:600;font-size:13.5px;line-height:1.3;
  letter-spacing:-.008em;text-align:center;color:var(--muted);
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
}
a.wall__tile.is-word:hover .wall__name{color:var(--moss)}

.wall__note{
  grid-column:1/-1;margin:14px 0 0;
  font-family:var(--f-mono);font-size:11px;font-weight:500;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  display:flex;flex-wrap:wrap;gap:8px 22px;align-items:center;
}

/* Movement is the whole idea here, so with it switched off the wall becomes a
   strip the reader scrolls themselves rather than a frozen crop. */
@media (prefers-reduced-motion:reduce){
  .wall__row{overflow-x:auto}
  .wall__track{animation:none;width:auto}
  .wall__track > [aria-hidden]{display:none}
}

/* ---------- amenity cards: the photograph behind ----------
   Hovering a card fades in a photograph of the thing it describes. The text
   has to stay readable over whatever the photograph happens to be, so it is
   not tinted — it is inverted onto a scrim dark enough to guarantee contrast
   regardless of the image: white on rgba(7,36,26,.78) clears 7:1 even where
   the photograph underneath is pure white.

   isolation:isolate makes the card its own stacking context, so the two
   negative-z layers stay inside it and behind the text rather than sliding
   under the card itself. */
.facts--marked .fact{position:relative;overflow:hidden;isolation:isolate}
.fact.has-shot::before,
.fact.has-shot::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  opacity:0;transition:opacity .34s ease;
}
.fact.has-shot::before{
  z-index:-2;
  background-image:var(--shot);
  background-size:cover;background-position:center;
  transform:scale(1.05);
  transition:opacity .34s ease, transform .55s ease;
}
.fact.has-shot::after{
  z-index:-1;
  background:linear-gradient(180deg, rgba(7,36,26,.66) 0%, rgba(7,36,26,.84) 100%);
}
.fact.has-shot:hover::before,
.fact.has-shot:focus-within::before{opacity:1;transform:none}
.fact.has-shot:hover::after,
.fact.has-shot:focus-within::after{opacity:1}

/* Everything in the card follows the scrim, so nothing is left dark-on-dark. */
.fact.has-shot{transition:border-color .22s}
.fact.has-shot:hover,
.fact.has-shot:focus-within{border-color:var(--ink)}
.fact.has-shot:hover h4,
.fact.has-shot:focus-within h4{color:#fff}
.fact.has-shot:hover p,
.fact.has-shot:focus-within p{color:rgba(255,255,255,.88)}
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{color:var(--moss-2)}
.fact.has-shot h4,.fact.has-shot p{transition:color .28s}

@media (prefers-reduced-motion:reduce){
  .fact.has-shot::before{transform:none;transition:opacity .01s}
}

/* ---------- illustration movement ----------
   Each drawing carries one class on the part that would actually move, and it
   runs only while the card is hovered or focused. The motion is meant to say
   something about the subject — wheels turn, steam rises, a stopwatch hand
   sweeps — so nothing animates where nothing in the subject moves.

   transform-box:fill-box makes every transform-origin relative to the shape's
   own box rather than the whole 64 grid, which is what lets a clock hand pivot
   on its own end and a canopy lean from its trunk. */
.glyph [class^="g-"]{
  transform-box:fill-box;
  transform-origin:center;
}
/* The hands are drawn outward FROM the pivot, so the pivot is the corner of
   their own box, not its centre. */
.glyph .g-sweep{transform-origin:0 0}
.glyph .g-sway{transform-origin:50% 100%}
.glyph .g-open{transform-origin:50% 100%}
.glyph .g-pop{transform-origin:50% 100%}
.glyph .g-curl{transform-origin:center}

/* Nothing moves until the card is hovered or holds focus. */
.fact:hover .glyph .g-spin,
.fact:focus-within .glyph .g-spin,
.route:hover .glyph .g-spin{animation:gSpin 2.4s linear infinite}

.fact:hover .glyph .g-sweep,
.fact:focus-within .glyph .g-sweep,
.route:hover .glyph .g-sweep{animation:gSweep 2.6s cubic-bezier(.5,0,.5,1) infinite}

.fact:hover .glyph .g-rise,
.fact:focus-within .glyph .g-rise{animation:gRise 1.9s ease-out infinite}

.fact:hover .glyph .g-lift,
.fact:focus-within .glyph .g-lift{animation:gLift 1.5s ease-in-out infinite}

.fact:hover .glyph .g-sway,
.fact:focus-within .glyph .g-sway{animation:gSway 2.6s ease-in-out infinite}

.fact:hover .glyph .g-flash,
.fact:focus-within .glyph .g-flash,
.route:hover .glyph .g-flash{animation:gFlash 1.6s ease-in-out infinite}

.fact:hover .glyph .g-press,
.fact:focus-within .glyph .g-press{animation:gPress 1.4s ease-in-out infinite}

.fact:hover .glyph .g-open,
.fact:focus-within .glyph .g-open{animation:gOpen 2.2s ease-in-out infinite}

.fact:hover .glyph .g-curl,
.fact:focus-within .glyph .g-curl{animation:gCurl 1.8s ease-in-out infinite}

.route:hover .glyph .g-pop{animation:gPop 1.6s ease-in-out infinite}

@keyframes gSpin{to{transform:rotate(360deg)}}
@keyframes gSweep{to{transform:rotate(360deg)}}
@keyframes gRise{
  0%{transform:translateY(3px);opacity:.35}
  55%{opacity:1}
  100%{transform:translateY(-4px);opacity:0}
}
@keyframes gLift{0%,100%{transform:translateY(0)}50%{transform:translateY(-3.5px)}}
@keyframes gSway{0%,100%{transform:rotate(-4deg)}50%{transform:rotate(4deg)}}
@keyframes gFlash{0%,100%{opacity:.35}50%{opacity:1}}
@keyframes gPress{0%,100%{transform:scale(1)}50%{transform:scale(.88)}}
@keyframes gOpen{0%,100%{transform:scaleY(1)}50%{transform:scaleY(.55)}}
@keyframes gCurl{0%,100%{transform:translateY(0) rotate(0)}50%{transform:translateY(-3px) rotate(-7deg)}}
@keyframes gPop{0%,100%{transform:scale(1)}50%{transform:scale(1.12)}}

/* Motion is decoration here, not information — the drawings read the same
   standing still, so switching it off costs nothing. */
@media (prefers-reduced-motion:reduce){
  .glyph [class^="g-"]{animation:none!important}
}

/* ---------- route cards: the photograph behind ----------
   Same treatment as the amenity cards, so the two sections behave alike: the
   photograph fades in on hover under a scrim dark enough that the text can be
   inverted onto it and stay readable whatever the image is. The category
   label, which is brass on paper, goes to the accent green rather than white
   so it keeps reading as a label and not as a second heading. */
.route.has-shot{position:relative;overflow:hidden;isolation:isolate}
.route.has-shot::before,
.route.has-shot::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  opacity:0;transition:opacity .34s ease;
}
.route.has-shot::before{
  z-index:-2;
  background-image:var(--shot);background-size:cover;background-position:center;
  transform:scale(1.05);
  transition:opacity .34s ease, transform .55s ease;
}
.route.has-shot::after{
  z-index:-1;
  background:linear-gradient(180deg, rgba(7,36,26,.68) 0%, rgba(7,36,26,.86) 100%);
}
.route.has-shot:hover::before,
.route.has-shot:focus-visible::before{opacity:1;transform:none}
.route.has-shot:hover::after,
.route.has-shot:focus-visible::after{opacity:1}

.route.has-shot .rn,
.route.has-shot .h-sub,
.route.has-shot p,
.route.has-shot .go{transition:color .28s}
.route.has-shot:hover{background:transparent;border-color:var(--ink)}
.route.has-shot:hover .rn,
.route.has-shot:focus-visible .rn{color:var(--moss-2)}
.route.has-shot:hover .h-sub,
.route.has-shot:focus-visible .h-sub{color:#fff}
.route.has-shot:hover p,
.route.has-shot:focus-visible p{color:rgba(255,255,255,.88)}
.route.has-shot:hover .go,
.route.has-shot:focus-visible .go{color:var(--moss-2)}
.route.has-shot:hover .glyph{color:var(--moss-2)}

@media (prefers-reduced-motion:reduce){
  .route.has-shot::before{transform:none;transition:opacity .01s}
}

/* ---------- accents on a photograph ----------
   --moss-2 is tuned for the pale page, and on the scrim over a photograph it
   drops to 1.74:1 — the category labels on the route cards were effectively
   unreadable. This is the same green lifted for a dark ground: 6.02:1 on the
   scrim at its lightest, measured against a WHITE photograph, which is the
   worst case the image can present. The scrim is deepened to match. */
:root{ --moss-on-shot:#6FD6A2 }

.route.has-shot::after{
  background:linear-gradient(180deg, rgba(7,36,26,.80) 0%, rgba(7,36,26,.90) 100%);
}
.fact.has-shot::after{
  background:linear-gradient(180deg, rgba(7,36,26,.78) 0%, rgba(7,36,26,.88) 100%);
}

.route.has-shot:hover .rn,
.route.has-shot:focus-visible .rn,
.route.has-shot:hover .go,
.route.has-shot:focus-visible .go{color:var(--moss-on-shot)}
.route.has-shot:hover .glyph{color:var(--moss-on-shot)}
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{color:var(--moss-on-shot)}

/* ---------- cards over a photograph: everything goes white ----------
   On the scrim the brand greens are not readable — measured earlier at
   1.74:1 for --moss-2 — and a drawing that is half white outline and half
   green accent reads as a fault rather than as a two-tone mark. So over a
   photograph the whole card resolves to white: the label, the link, the
   drawing's outline and its accent.

   The accent keeps a little transparency rather than going pure white,
   because several drawings are a filled shape sitting inside their own
   outline — a solid canopy, a paw pad, a stacked block — and at the same
   white the fill and the line it sits in become one shape. */
.route.has-shot:hover,
.route.has-shot:focus-visible,
.fact.has-shot:hover,
.fact.has-shot:focus-within{ --glyph-accent:rgba(255,255,255,.72) }

.route.has-shot:hover .rn,/* ---------- the vine ----------
   The client asked for the logo's tree to run through the history, so the
   timeline hangs off a vine drawn in the logo's own language: an almond leaf
   with the vein cut out of it.

   It is drawn, not assembled. The first go was a dead-straight stripe with one
   leaf repeated at a perfect pitch, and no amount of easing was going to
   rescue that — the stiffness was in the artwork. So the stem meanders, on a
   sine whose period is exactly the tile height, which is what lets it still
   repeat seamlessly however tall the history turns out to be; it thickens and
   thins as it goes; the leaves come off it at irregular heights, at varying
   angles off the local tangent, at varying sizes, in three greens; and the
   tile is 300px long so the repeat does not read as a pattern.

   Stem and leaves are separate tiles, and the leaves are split across four of
   them. That is what lets the leaves stir in the wind without dragging the
   stem off the buds and branches that meet it, and lets neighbouring leaves
   move out of step instead of the whole plant sliding about as one piece.

   It is all background and pseudo-elements, so a screen reader walks the years
   and the prose and never meets a leaf. */
:root{
  --vine-stem:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M21.2,0 L21.7,5 L22.1,10 L22.6,15 L23,20 L23.5,25 L23.9,30 L24.3,35 L24.7,40 L25.1,45 L25.4,50 L25.7,55 L25.9,60 L26,65 L26.2,70 L26.2,75 L26.2,80 L26.2,85 L26,90 L25.8,95 L25.6,100 L25.3,105 L25,110 L24.6,115 L24.2,120 L23.7,125 L23.2,130 L22.7,135 L22.2,140 L21.7,145 L21.2,150 L20.7,155 L20.3,160 L19.8,165 L19.4,170 L19,175 L18.6,180 L18.3,185 L18,190 L17.8,195 L17.6,200 L17.4,205 L17.3,210 L17.2,215 L17.2,220 L17.2,225 L17.3,230 L17.3,235 L17.5,240 L17.6,245 L17.8,250 L18,255 L18.3,260 L18.6,265 L18.9,270 L19.2,275 L19.6,280 L20,285 L20.4,290 L20.8,295 L21.2,300 L24.8,300 L24.3,295 L23.8,290 L23.3,285 L22.8,280 L22.3,275 L21.8,270 L21.4,265 L21,260 L20.7,255 L20.4,250 L20.2,245 L20,240 L19.8,235 L19.8,230 L19.8,225 L19.8,220 L20,215 L20.1,210 L20.3,205 L20.6,200 L20.9,195 L21.3,190 L21.7,185 L22.1,180 L22.5,175 L23,170 L23.4,165 L23.9,160 L24.3,155 L24.8,150 L25.2,145 L25.6,140 L26,135 L26.4,130 L26.8,125 L27.1,120 L27.4,115 L27.7,110 L28,105 L28.2,100 L28.4,95 L28.5,90 L28.7,85 L28.7,80 L28.8,75 L28.8,70 L28.8,65 L28.7,60 L28.6,55 L28.4,50 L28.2,45 L28,40 L27.7,35 L27.4,30 L27,25 L26.6,20 L26.2,15 L25.7,10 L25.3,5 L24.8,0 Z' fill='%230E5C41'/%3E%3C/svg%3E");
  --vine-leaf-a:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M25.2,24 Q39.2,26.3 42.9,12.5 Q28.9,10.3 25.2,24 Z' fill='%2317795A'/%3E%3Cpath d='M25.2,24 L35.5,17.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M23.1,149 Q38.9,147.9 39.1,132.1 Q23.3,133.2 23.1,149 Z' fill='%2314684E'/%3E%3Cpath d='M23.1,149 L32.4,139.2' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M20.8,276 Q33.3,278.7 38.4,267 Q25.9,264.3 20.8,276 Z' fill='%231E9268'/%3E%3Cpath d='M20.8,276 L31,270.8' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --vine-leaf-b:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M27.2,58 Q25,45.2 12.1,46 Q14.3,58.8 27.2,58 Z' fill='%2314684E'/%3E%3Cpath d='M27.2,58 L18.4,51.1' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M20.1,183 Q19.1,170.9 6.9,171.4 Q8,183.6 20.1,183 Z' fill='%231E9268'/%3E%3Cpath d='M20.1,183 L12.4,176.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --vine-leaf-c:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M27.4,86 Q42.7,90 45.9,74.5 Q30.6,70.5 27.4,86 Z' fill='%231E9268'/%3E%3Cpath d='M27.4,86 L38.1,79.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M18.6,214 Q19.1,201.9 7,202.1 Q6.5,214.1 18.6,214 Z' fill='%2317795A'/%3E%3Cpath d='M18.6,214 L11.9,207.1' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --vine-leaf-d:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='46' height='300' viewBox='0 0 46 300'%3E%3Cpath d='M25.6,121 Q36.2,122.8 40.1,112.8 Q29.5,110.9 25.6,121 Z' fill='%2317795A'/%3E%3Cpath d='M25.6,121 L34,116.2' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M18.9,246 Q17.3,232.3 3.5,232.3 Q5.1,246 18.9,246 Z' fill='%2314684E'/%3E%3Cpath d='M18.9,246 L10,238' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --vine-bud:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18'%3E%3Ccircle cx='9' cy='9' r='8' fill='%23FAF9F5'/%3E%3Ccircle cx='9' cy='9' r='4.4' fill='%230E5C41'/%3E%3C/svg%3E");
  --branch-r:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='54' viewBox='0 0 100 54'%3E%3Cpath d='M0.7,37.5 L6.4,34.8 L12.1,32.2 L17.9,29.8 L23.7,27.5 L29.5,25.3 L35.3,23.3 L41.1,21.5 L47,19.8 L52.9,18.2 L58.9,16.8 L64.8,15.6 L70.8,14.4 L76.8,13.5 L82.9,12.6 L88.9,12 L95,11.4 L95,10.6 L88.8,10.9 L82.7,11.4 L76.6,12.1 L70.6,12.9 L64.5,13.9 L58.5,15 L52.5,16.3 L46.5,17.7 L40.5,19.3 L34.6,21 L28.6,22.9 L22.7,24.9 L16.8,27.1 L10.9,29.4 L5.1,31.8 L-0.7,34.5 Z' fill='%230E5C41'/%3E%3Cpath d='M27.9,24.5 Q37.7,18.9 33.1,8.5 Q23.3,14.2 27.9,24.5 Z' fill='%231E9268'/%3E%3Cpath d='M27.9,24.5 L30.9,15.2' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M51.5,17.5 Q53,28.6 64.2,28 Q62.7,16.9 51.5,17.5 Z' fill='%2314684E'/%3E%3Cpath d='M51.5,17.5 L58.9,23.6' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M73.6,13.2 Q85.1,9.4 81.9,-2.3 Q70.4,1.5 73.6,13.2 Z' fill='%2317795A'/%3E%3Cpath d='M73.6,13.2 L78.4,4.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M88.2,11.5 Q89,25.7 103.2,24.5 Q102.3,10.3 88.2,11.5 Z' fill='%231E9268'/%3E%3Cpath d='M88.2,11.5 L96.9,19.1' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E");
  --branch-l:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='54' viewBox='0 0 100 54'%3E%3Cg transform='translate(100,0) scale(-1,1)'%3E%3Cpath d='M0.7,37.5 L6.4,34.8 L12.1,32.2 L17.9,29.8 L23.7,27.5 L29.5,25.3 L35.3,23.3 L41.1,21.5 L47,19.8 L52.9,18.2 L58.9,16.8 L64.8,15.6 L70.8,14.4 L76.8,13.5 L82.9,12.6 L88.9,12 L95,11.4 L95,10.6 L88.8,10.9 L82.7,11.4 L76.6,12.1 L70.6,12.9 L64.5,13.9 L58.5,15 L52.5,16.3 L46.5,17.7 L40.5,19.3 L34.6,21 L28.6,22.9 L22.7,24.9 L16.8,27.1 L10.9,29.4 L5.1,31.8 L-0.7,34.5 Z' fill='%230E5C41'/%3E%3Cpath d='M27.9,24.5 Q37.7,18.9 33.1,8.5 Q23.3,14.2 27.9,24.5 Z' fill='%231E9268'/%3E%3Cpath d='M27.9,24.5 L30.9,15.2' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M51.5,17.5 Q53,28.6 64.2,28 Q62.7,16.9 51.5,17.5 Z' fill='%2314684E'/%3E%3Cpath d='M51.5,17.5 L58.9,23.6' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M73.6,13.2 Q85.1,9.4 81.9,-2.3 Q70.4,1.5 73.6,13.2 Z' fill='%2317795A'/%3E%3Cpath d='M73.6,13.2 L78.4,4.3' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3Cpath d='M88.2,11.5 Q89,25.7 103.2,24.5 Q102.3,10.3 88.2,11.5 Z' fill='%231E9268'/%3E%3Cpath d='M88.2,11.5 L96.9,19.1' fill='none' stroke='%23FAF9F5' stroke-width='1' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E");
  --fly-1:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cpath d='M2,15 Q15,1 28,13 Q15,27 2,15 Z' fill='%2317795A'/%3E%3Cpath d='M4,15 L18.72,13.8' fill='none' stroke='%23FAF9F5' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
  --fly-2:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cpath d='M2,15 Q15,-5 28,10 Q15,30 2,15 Z' fill='%231E9268'/%3E%3Cpath d='M4,15 L18.72,12' fill='none' stroke='%23FAF9F5' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
  --fly-3:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3E%3Cpath d='M2,15 Q15,5 28,15 Q15,25 2,15 Z' fill='%2314684E'/%3E%3Cpath d='M4,15 L18.72,15' fill='none' stroke='%23FAF9F5' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* How far the vine has grown, how far a branch has reached and at what angle,
   how open a bud is. Registering them is what lets them be animated — an
   unregistered custom property jumps from one value to the next instead of
   sweeping between them.

   They start at nothing, so the vine is not on the page until the reader has
   scrolled down to it. Anything that would otherwise leave them at nothing —
   a reader who has asked for less motion — sets them to finished explicitly
   further down.

   The first inherits, unlike the rest: it is animated on the vine but read by
   the cover sitting inside it, and a property that does not inherit never
   reaches a pseudo-element child, which left the stem invisible. */
@property --vine-grow{syntax:'<percentage>';inherits:true;initial-value:0%}
@property --branch-grow{syntax:'<percentage>';inherits:false;initial-value:0%}
@property --branch-tilt{syntax:'<angle>';inherits:false;initial-value:0deg}
@property --bud-pop{syntax:'<number>';inherits:false;initial-value:0}

.vine{
  position:absolute;top:0;bottom:0;width:var(--vine-w);pointer-events:none;
  /* The cover is the vine's own height and slides a whole height downwards, so
     once the vine is grown it sits entirely below the timeline — a paper
     coloured stripe straight down the middle of whatever section comes next.
     It has to be kept inside. */
  overflow:hidden;
  background:var(--vine-stem) repeat-y center top / var(--vine-w) calc(var(--vine-w) * 300 / 46)
}
.vine i{
  position:absolute;inset:0;display:block;
  background-repeat:repeat-y;background-position:center top;
  background-size:var(--vine-w) calc(var(--vine-w) * 300 / 46)
}
.vine .vl-a{background-image:var(--vine-leaf-a)}
.vine .vl-b{background-image:var(--vine-leaf-b)}
.vine .vl-c{background-image:var(--vine-leaf-c)}
.vine .vl-d{background-image:var(--vine-leaf-d)}

/* The vine is always drawn in full. What grows is a cover over it, sliding
   down out of the way — and a cover moves on a transform, which the compositor
   handles without repainting anything.

   Animating a mask down the vine instead, which is what this did first, meant
   repainting a two thousand pixel tall element on every frame of every scroll,
   and it flickered. The soft top edge of the cover gives the arriving tip. */
.vine::after{
  content:'';position:absolute;inset:0;z-index:2;
  background:linear-gradient(to bottom,transparent 0,var(--paper) 78px,var(--paper) 100%);
  transform:translateY(var(--vine-grow))
}
/* and a permanent softening where the vine stops */
.vine::before{
  content:'';position:absolute;left:0;right:0;bottom:0;height:74px;z-index:3;
  background:linear-gradient(to top,var(--paper),transparent)
}

/* ---------- the timeline it runs down ----------
   The vine is the spine, down the middle, and the history steps past it — 1863
   to the right, 1865 to the left, and so on down. Each entry is reached by a
   branch off the stem, so the years hang from the vine rather than merely
   sitting beside it. There are no rules between the rows: a line drawn across
   the page would have to cross the vine, and the branches already say where one
   entry ends and the next begins. */
.tl{--vine-w:46px;--branch-w:clamp(58px,7vw,112px);position:relative}
.tl>.vine{left:calc(50% - var(--vine-w) / 2)}

.tl-row{
  position:relative;display:grid;
  grid-template-columns:1fr var(--vine-w) 1fr;
  column-gap:var(--branch-w);
  padding:clamp(26px,3.2vw,42px) 0;border-bottom:0
}
/* first entry to the right of the spine, then alternating. Held to a
   comfortable measure and pushed against the spine, so the two columns read as
   one thread rather than as two loose columns of prose. */
.tl-side{grid-column:3;text-align:left;max-width:46ch;margin-right:auto}
.tl-row:nth-of-type(even) .tl-side{grid-column:1;text-align:right;margin-right:0;margin-left:auto}
.tl-row p{font-family:var(--f-read);font-size:1.03rem;max-width:none;margin:10px 0 0}
/* the picture hugs the spine, like the text beside it */
.tl-row:nth-of-type(even) .tl-fig{margin-left:auto}

/* a bud on the spine beside every year, and the branch that leaves it. The bud
   is wide enough to cover wherever the meandering stem happens to be at that
   height, so the junction always closes. */
.tl-row::before,.tl-row::after{
  content:'';position:absolute;pointer-events:none;
  top:calc(clamp(26px,3.2vw,42px) + clamp(1.25rem,2.6vw,1.75rem) / 2)
}
.tl-row::before{
  width:18px;height:18px;left:calc(50% - 9px);z-index:1;
  transform:translateY(-50%) scale(var(--bud-pop));
  background:var(--vine-bud) no-repeat center / 18px 18px
}
/* Each side has its own drawing rather than one mirrored with a transform:
   the transform is wanted for the swing the branch makes as it opens, and
   transform-origin cannot serve both the mirror and the hinge. The hinge sits
   where the branch meets the stem, and a soft edge uncovers it from that end,
   so it reaches outwards rather than being wiped into view. */
.tl-row::after{
  width:calc(var(--branch-w) * var(--bw-f, 1));
  height:calc(var(--branch-w) * var(--bw-f, 1) * 54 / 100);
  left:calc(50% + 9px);transform-origin:left center;
  transform:translateY(-50%) rotate(calc(var(--tilt-base, 0deg) + var(--branch-tilt)));
  background:var(--branch-r) no-repeat left center / 100% auto;
  -webkit-mask-image:linear-gradient(to right,#000 calc(var(--branch-grow) - 26px),transparent var(--branch-grow));
  mask-image:linear-gradient(to right,#000 calc(var(--branch-grow) - 26px),transparent var(--branch-grow))
}
.tl-row:nth-of-type(even)::after{
  left:auto;right:calc(50% + 9px);transform-origin:right center;
  background-image:var(--branch-l);background-position:right center;
  -webkit-mask-image:linear-gradient(to left,#000 calc(var(--branch-grow) - 26px),transparent var(--branch-grow));
  mask-image:linear-gradient(to left,#000 calc(var(--branch-grow) - 26px),transparent var(--branch-grow))
}
.tl-row:nth-of-type(1)::after{--bw-f:1;--tilt-base:-2.2deg}
.tl-row:nth-of-type(2)::after{--bw-f:0.88;--tilt-base:2.6deg}
.tl-row:nth-of-type(3)::after{--bw-f:1.05;--tilt-base:-3.4deg}
.tl-row:nth-of-type(4)::after{--bw-f:0.93;--tilt-base:1.6deg}
.tl-row:nth-of-type(5)::after{--bw-f:0.99;--tilt-base:-1.4deg}
.tl-row:nth-of-type(6)::after{--bw-f:0.86;--tilt-base:3deg}

/* ---------- the leaves the wind has taken ----------
   Behind the history, drifting across and sinking as they go. They are pure
   decoration and carry nothing, so they sit under the content, take no
   pointer, and are hidden from a screen reader at the markup. They move on
   transform and opacity only, which the compositor can do on its own. */
.hist{position:relative}
.hist>.wrap{position:relative;z-index:1}
.hist>.wind{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0}
.hist>.wind i{
  position:absolute;left:0;display:block;opacity:0;
  background-repeat:no-repeat;background-position:center;background-size:contain
}
.hist>.wind i:nth-child(1){top:6%;width:26px;height:26px;--t:23s;--d:-3s;--dy:210px;--o:0.5;background-image:var(--fly-1)}
.hist>.wind i:nth-child(2){top:22%;width:18px;height:18px;--t:31s;--d:-14s;--dy:150px;--o:0.42;background-image:var(--fly-2)}
.hist>.wind i:nth-child(3){top:38%;width:30px;height:30px;--t:19s;--d:-8s;--dy:260px;--o:0.46;background-image:var(--fly-3)}
.hist>.wind i:nth-child(4){top:12%;width:15px;height:15px;--t:37s;--d:-25s;--dy:120px;--o:0.36;background-image:var(--fly-2)}
.hist>.wind i:nth-child(5){top:55%;width:22px;height:22px;--t:27s;--d:-19s;--dy:190px;--o:0.44;background-image:var(--fly-1)}
.hist>.wind i:nth-child(6){top:68%;width:28px;height:28px;--t:21s;--d:-6s;--dy:230px;--o:0.48;background-image:var(--fly-3)}
.hist>.wind i:nth-child(7){top:44%;width:16px;height:16px;--t:34s;--d:-30s;--dy:130px;--o:0.34;background-image:var(--fly-2)}
.hist>.wind i:nth-child(8){top:80%;width:24px;height:24px;--t:25s;--d:-11s;--dy:170px;--o:0.4;background-image:var(--fly-1)}
.hist>.wind i:nth-child(9){top:30%;width:20px;height:20px;--t:29s;--d:-22s;--dy:200px;--o:0.38;background-image:var(--fly-3)}

/* ---------- movement ----------
   Two ways in, because the feature that does this properly is not everywhere
   yet. Where the browser can tie an animation to scroll position, the vine
   draws itself down as the history passes the viewport and every entry's bud
   opens and swings out its branch as that entry arrives — real scrolling,
   running off the main thread with no scroll handler. Where it cannot, a class
   on the section plays the same thing once as it comes into view. Either way,
   nobody who has asked for less motion gets any.

   The branch opens on one set of keyframes doing two things at once: it
   lengthens, and it lifts through a small overswing into its resting angle. */
@keyframes vine-grow{from{--vine-grow:0%}to{--vine-grow:100%}}
@keyframes bud-pop{from{--bud-pop:0}to{--bud-pop:1}}
@keyframes branch-out{
  from{--branch-grow:0%;--branch-tilt:-13deg}
  55%{--branch-tilt:4deg}
  80%{--branch-tilt:-1.4deg}
  to{--branch-grow:100%;--branch-tilt:0deg}
}
/* The leaves turning in the wind.

   Sliding them sideways is the obvious move and it cannot go far: the leaves
   are attached to the stem, the stem does not slide with them, and past about
   two pixels they visibly come off it — which is why the first attempt could
   not be seen at all. Scaling them across instead, about the line of the stem,
   has no such limit. The base of every leaf sits on the origin and never
   moves, while the blade shortens and lengthens: a leaf turning edge on to the
   wind and back. Four layers, so neighbours are never in step. */
@keyframes stir-a{0%,100%{transform:scaleX(1) translate3d(-.6px,0,0)}47%{transform:scaleX(.8) translate3d(.7px,0,0)}}
@keyframes stir-b{0%,100%{transform:scaleX(.86) translate3d(.5px,0,0)}55%{transform:scaleX(1.05) translate3d(-.5px,0,0)}}
@keyframes stir-c{0%,100%{transform:scaleX(1.03) translate3d(.4px,0,0)}42%{transform:scaleX(.82) translate3d(-.6px,0,0)}}
@keyframes stir-d{0%,100%{transform:scaleX(.87) translate3d(-.4px,0,0)}60%{transform:scaleX(1.04) translate3d(.6px,0,0)}}
/* one leaf, blown across and sinking as it goes */
@keyframes blow{
  0%{opacity:0;transform:translate3d(-8vw,0,0) rotate(0deg)}
  7%{opacity:var(--o)}
  33%{transform:translate3d(26vw,calc(var(--dy) * .3),0) rotate(140deg)}
  64%{transform:translate3d(62vw,calc(var(--dy) * .72),0) rotate(290deg)}
  93%{opacity:var(--o)}
  100%{opacity:0;transform:translate3d(112vw,var(--dy),0) rotate(430deg)}
}

@media(prefers-reduced-motion:no-preference){
  @supports (animation-timeline:view()){
    /* Eighty per cent of cover grows the vine a shade slower than the page
       scrolls, so the tip drifts gently up into view rather than sitting
       pinned at the bottom edge where it cannot be seen. It still only ever
       moves one way.

       Entry was the obvious range and it is wrong here: for a subject taller
       than the screen, entry is over once its top reaches the top of the
       screen, so the whole vine was drawn across 880px of scrolling — two and
       a quarter pixels of vine per pixel scrolled. The tip raced down, then
       stopped dead with a thousand pixels of section still to go, which is
       what read as the vine running back up the page. */
    .hist .vine{animation:vine-grow linear both;animation-timeline:view();animation-range:cover 0% cover 80%}
    .tl-row{view-timeline-name:--tl-row}
    /* Measured against cover rather than entry, because the entry phase for a
       short row is over while that row is still down at the bottom edge — the
       branches were finishing 89% down the screen, where nobody was looking.
       Finishing later in cover means the row has travelled further up by then,
       so the branch opens where it can actually be watched. */
    .tl-row::before{animation:bud-pop cubic-bezier(.34,1.56,.64,1) both;
      animation-timeline:--tl-row;animation-range:cover 14% cover 38%}
    .tl-row::after{animation:branch-out cubic-bezier(.28,.72,.35,1) both;
      animation-timeline:--tl-row;animation-range:cover 18% cover 54%}
  }
  /* the fallback: one growth as the section arrives */
  .hist.vine-go .vine{--vine-grow:100%;transition:--vine-grow 1.7s cubic-bezier(.22,.61,.36,1)}
  .hist.vine-go .tl-row::before{--bud-pop:1;transition:--bud-pop .5s cubic-bezier(.34,1.56,.64,1)}
  .hist.vine-go .tl-row::after{animation:branch-out 1s cubic-bezier(.28,.72,.35,1) both .15s}
  .hist.vine-go .tl-row:nth-of-type(2)::before{transition-delay:.18s}
  .hist.vine-go .tl-row:nth-of-type(2)::after{animation-delay:.33s}
  .hist.vine-go .tl-row:nth-of-type(3)::before{transition-delay:.36s}
  .hist.vine-go .tl-row:nth-of-type(3)::after{animation-delay:.51s}
  .hist.vine-go .tl-row:nth-of-type(4)::before{transition-delay:.54s}
  .hist.vine-go .tl-row:nth-of-type(4)::after{animation-delay:.69s}
  .hist.vine-go .tl-row:nth-of-type(5)::before{transition-delay:.72s}
  .hist.vine-go .tl-row:nth-of-type(5)::after{animation-delay:.87s}
  .hist.vine-go .tl-row:nth-of-type(6)::before{transition-delay:.9s}
  .hist.vine-go .tl-row:nth-of-type(6)::after{animation-delay:1.05s}

  /* the wind: the leaves stir always, and the loose ones start blowing once
     the section has been reached, so nothing is animating off-screen before
     anyone has got there */
  .hist .vine i{transform-origin:50% 50%}
  .hist .vl-a{animation:stir-a 5.3s ease-in-out infinite}
  .hist .vl-b{animation:stir-b 7.1s ease-in-out -1.9s infinite}
  .hist .vl-c{animation:stir-c 6.2s ease-in-out -3.4s infinite}
  .hist .vl-d{animation:stir-d 8.6s ease-in-out -.8s infinite}
  .hist.wind-on>.wind i{animation:blow var(--t) linear var(--d) infinite}
}

/* Asked for less motion: the vine is simply there, whole and still, and
   nothing blows across. */
@media(prefers-reduced-motion:reduce){
  .vine{--vine-grow:100%}
  .tl-row::before{--bud-pop:1}
  .tl-row::after{--branch-grow:100%}
  .hist>.wind{display:none}
}

/* ---------- the two engravings in the timeline ---------- */
.tl-fig{margin:20px 0 0;max-width:min(520px,100%)}
.tl-fig img{
  display:block;width:100%;height:auto;border:1px solid var(--line-str);
  background:var(--stone)
}
.tl-fig figcaption{
  margin-top:9px;font-family:var(--f-read);font-size:.86rem;line-height:1.5;color:var(--muted)
}
.tl-src{display:block;font-family:var(--f-mono);font-size:10.5px;letter-spacing:.05em;
  text-transform:uppercase;color:var(--muted);margin-top:4px}

/* ---------- narrow: the spine goes back to the edge ----------
   Half a column is not enough to read in on a phone, so the entries stop
   alternating, the vine moves to the left of them, and it reads as one list
   again. There is no room for a branch to reach across, so the bud carries the
   year on its own, and the loose leaves are left to wider screens. */
@media(max-width:819px){
  .tl{--vine-w:30px;padding-left:var(--vine-w)}
  .tl>.vine{left:0}
  .tl-row{display:block;padding:clamp(22px,4vw,30px) 0}
  .tl-side,.tl-row:nth-of-type(even) .tl-side{text-align:left;max-width:none;margin:0}
  .tl-row:nth-of-type(even) .tl-fig{margin-left:0}
  .tl-row::after{display:none}
  .tl-row::before{width:14px;height:14px;background-size:14px 14px;
    left:calc(-1 * var(--vine-w) + (var(--vine-w) - 14px) / 2)}
  .tl-row+.tl-row{border-top:1px solid var(--line)}
  .hist>.wind{display:none}
}

/* ---------- the header at the top of the page ----------
   No card until it is needed. At the top the mark, the links and the booking
   button sit straight on the page; the pill forms as the page scrolls under
   it, where it earns its place by keeping the links readable over content.
   The background, edge and shadow fade in together with the colour change the
   bar already makes, so it arrives as one thing. */
.site-head.at-top .head-in{
  background:transparent;border-color:transparent;box-shadow:none;
  backdrop-filter:none;-webkit-backdrop-filter:none
}
/* With no glass behind them, pale letters over a photograph need their own
   shadow to hold up where the picture is bright — sky, a white wall. */
.site-head.over.at-top .brand-tx b,
.site-head.over.at-top .nav a{text-shadow:0 1px 3px rgba(3,15,10,.7), 0 1px 14px rgba(3,15,10,.55)}
.site-head.over.at-top .brand img{filter:drop-shadow(0 1px 6px rgba(3,15,10,.45))}
.site-head.over.at-top .lang{background:rgba(4,20,15,.28)}

/* ---------- the crystal veil over the hero, with the logo cut out of it ----------
   A pane of green crystal covers the whole photograph. On the right the
   estate's mark is subtracted from it, so inside the circle the picture shows
   clear and at full strength and the tree — the transparent part of the mark —
   stays behind the glass. The emblem is drawn out of the estate itself.

   The glass is four things, top to bottom: cut facets (crystal.svg, faces
   catching light from the top left, fine bright lines along the cuts), a soft
   sheen across the pane, the logo's lighter green (#00A55F) as the tint, and a
   frost — the photograph behind is blurred and dimmed a little, so the clear
   picture inside the circle reads as a window cut through solid glass.

   The logo is two mask layers composited: a solid layer the size of the hero,
   and the mark. "exclude" keeps only what is in one and not the other, which
   is the whole hero less the logo. The mask clips the frost too, so nothing
   inside the circle is blurred. The mark is a traced vector of mark.png, so its
   edge stays crisp at hero size on a sharp screen.

   The veil does not move. The slides keep their slow drift and their
   parallax, so the picture moves behind the glass and through the window while
   the mark holds still. */
.hero-veil{
  --mark:min(74vh,50vw);
  --mark-x:clamp(24px,5vw,90px);
  --glass:rgba(0,165,95,.3);
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    url("/images/crystal.svg") center/cover no-repeat,
    linear-gradient(118deg, rgba(255,255,255,.12) 0%, rgba(255,255,255,0) 32%, rgba(255,255,255,.07) 58%, rgba(255,255,255,0) 74%),
    linear-gradient(var(--glass), var(--glass));
  -webkit-backdrop-filter:blur(7px) brightness(.62) saturate(1.25);
  backdrop-filter:blur(7px) brightness(.62) saturate(1.25);
  -webkit-mask-image:linear-gradient(#000 0 0),url("/images/mark-mask.svg");
  mask-image:linear-gradient(#000 0 0),url("/images/mark-mask.svg");
  -webkit-mask-size:100% 100%,var(--mark) var(--mark);
  mask-size:100% 100%,var(--mark) var(--mark);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:0 0,right var(--mark-x) top 44%;
  mask-position:0 0,right var(--mark-x) top 44%;
  -webkit-mask-composite:xor;mask-composite:exclude
}
/* The cut edge of the glass round the circle: a bright bevel line and a soft
   shadow falling outward. Drawn as a ring the same size and place as the
   mark; being inside the veil it is masked with it, so it only shows round
   the outside of the circle. */
.hero-veil::after{
  content:"";position:absolute;
  width:var(--mark);height:var(--mark);
  right:var(--mark-x);top:calc((100% - var(--mark)) * .44);
  border-radius:50%;
  box-shadow:0 0 0 1.5px rgba(200,255,225,.34), 0 0 0 5px rgba(255,255,255,.05), 0 0 80px rgba(2,30,18,.4)
}

/* On a phone the copy fills the width and there is no clear side to cut a
   window into, so the mark sits large in the upper corner and runs off the
   edge — present, but out from under the headline. */
@media(max-width:820px){
  .hero-veil{
    --mark:92vw;--mark-x:-30vw;
    -webkit-mask-position:0 0,right var(--mark-x) top -6vw;
    mask-position:0 0,right var(--mark-x) top -6vw
  }
  .hero-veil::after{top:-6vw}
}
/* No frost where the browser cannot blur behind an element: a deeper tint
   keeps the headline side as quiet as the glass would. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .hero-veil{--glass:rgba(0,95,55,.6)}
}

/* ---------- the two alternative templates ----------
   Same homepage, same photographs, same booking carousel; only the veil over
   the hero differs, and on both of these the mask is the logo's branches
   alone — no circle round them. Served at /template-2 and /template-3 so the
   three can be looked at side by side.

   Both drop the crystal facets and the sheen: the base rule's background is
   replaced outright rather than layered on. */
.hero-veil--white,
.hero-veil--frost{
  -webkit-mask-image:linear-gradient(#000 0 0),url("/images/mark-tree.svg");
  mask-image:linear-gradient(#000 0 0),url("/images/mark-tree.svg")
}
/* the circle belongs to the crystal template alone */
.hero-veil--white::after,
.hero-veil--frost::after{content:none}

/* ---- template 2: a white wash ----
   White over the photograph, and the branches cut out of it in full colour.
   Nothing is blurred: the picture stays sharp and simply pales, so the tree
   reads as the only saturated thing on the page. */
.hero-veil--white{
  --mark:min(78vh,52vw);
  background:rgba(255,255,255,.72);
  -webkit-backdrop-filter:saturate(.88);backdrop-filter:saturate(.88)
}
/* The page's two reading scrims are dark washes for pale type. On a white
   hero they would show as grey smears, and the type they were protecting is
   dark here anyway, so the side wash goes and the header band turns white. */
.hero--white::before{background:none}
.hero--white::after{
  background:linear-gradient(180deg, rgba(255,255,255,.85) 0%, rgba(255,255,255,.4) 55%, rgba(255,255,255,0) 100%)
}
/* Everything that was pale type on a dark picture is dark type on a pale one:
   the rail, the seek dashes, and the carousel's own furniture. The copy pane
   and the room cards keep their dark glass — they are the contrast. */
.hero--white .hero-rail{border-right-color:rgba(9,42,29,.18)}
.hero--white .hero-rail .place{color:rgba(9,42,29,.78);text-shadow:none}
.hero--white .hero-rail .rail-social a{color:rgba(9,42,29,.72);filter:none}
.hero--white .hero-rail .rail-social a:hover{color:var(--moss)}
.hero--white .hero-seek button::before{background:rgba(9,42,29,.3);box-shadow:none}
.hero--white .hero-seek button::after{background:var(--moss)}
.hero--white .hero-tiles-title{color:rgba(9,42,29,.82);text-shadow:none}
.hero--white .hero-tiles-nav button{
  color:var(--moss);border-color:rgba(9,42,29,.28);background:rgba(255,255,255,.6);
  box-shadow:0 8px 22px rgba(9,42,29,.12)
}
.hero--white .hero-tiles-nav button:hover{background:#fff;border-color:var(--moss)}
.hero--white .hero-dots button{background:rgba(9,42,29,.26)}
.hero--white .hero-dots button.is-on{background:var(--moss)}

/* ---- template 3: plain frosted glass ----
   No colour of its own and no texture: the photograph is blurred behind a
   thin white frost, the way a bathroom pane blurs what is behind it, and the
   branches are wiped clear of it. Dimmed a shade so the pale type on the copy
   pane, the rail and the header keeps the contrast it has on the homepage. */
.hero-veil--frost{
  --mark:min(78vh,52vw);
  background:rgba(255,255,255,.14);
  -webkit-backdrop-filter:blur(16px) brightness(.68) saturate(1.05);
  backdrop-filter:blur(16px) brightness(.68) saturate(1.05)
}
/* Without a blur to fall back on the frost is only a pale film, so it carries
   the dimming itself. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .hero-veil--frost{background:rgba(9,30,22,.52)}
}

/* ---- template 4: plain green glass, crystal in the logo's branches ----
   The homepage's own veil — the logo green, the frost, the sheen, the circle
   and its bevel — with one change: the facets come off the pane, which is
   left smooth, and are set into the branches and leaves instead. The picture
   inside the circle is still clear and sharp; the tree is cut crystal. */
.hero-veil--gem{
  background:
    linear-gradient(118deg, rgba(255,255,255,.12) 0%, rgba(255,255,255,0) 32%, rgba(255,255,255,.07) 58%, rgba(255,255,255,0) 74%),
    linear-gradient(var(--glass), var(--glass))
}
/* The facet layer. Sized and placed with the same numbers as the veil's mask,
   and masked by mark-tree.svg — the branches and leaves alone — so it lies
   on the tree and nowhere else: not on the clear picture round it, not on the
   pane. crystal-gem.svg is a square cut of the facets with stronger faces and
   cut lines, so they still read on branches a few dozen pixels wide. */
.hero-gem{
  --mark:min(74vh,50vw);
  --mark-x:clamp(24px,5vw,90px);
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:url("/images/crystal-gem.svg") no-repeat;
  background-size:var(--mark) var(--mark);
  background-position:right var(--mark-x) top 44%;
  -webkit-mask-image:url("/images/mark-tree.svg");mask-image:url("/images/mark-tree.svg");
  -webkit-mask-size:var(--mark) var(--mark);mask-size:var(--mark) var(--mark);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:right var(--mark-x) top 44%;mask-position:right var(--mark-x) top 44%
}
@media(max-width:820px){
  .hero-gem{
    --mark:92vw;--mark-x:-30vw;
    background-position:right var(--mark-x) top -6vw;
    -webkit-mask-position:right var(--mark-x) top -6vw;mask-position:right var(--mark-x) top -6vw
  }
}

/* ---------- picture cards: the History page's way on ----------
   Unlike the route cards on the homepage, the photograph is always there — no
   hover to reveal it and no scrim over the whole of it. The picture stays
   clear at the top, and a gradient deepens only toward the foot, where the
   words sit, so they read whatever the image is. Hover just draws the picture
   in a little. */
.routes--pics{grid-template-columns:repeat(4,1fr)}
@media(max-width:1100px){.routes--pics{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.routes--pics{grid-template-columns:1fr}}

.route.route--pic{
  position:relative;overflow:hidden;isolation:isolate;
  min-height:clamp(340px,34vw,420px);justify-content:flex-end;
  background:var(--ink);border-color:transparent;color:#fff
}
.route.route--pic::before,
.route.route--pic::after{content:"";position:absolute;inset:0;pointer-events:none}
.route.route--pic::before{
  z-index:-2;
  background-image:var(--shot);background-size:cover;background-position:center;
  transition:transform .8s cubic-bezier(.16,1,.3,1)
}
.route.route--pic::after{
  z-index:-1;
  background:linear-gradient(180deg,
    rgba(7,36,26,0)   0%,
    rgba(7,36,26,.08) 22%,
    rgba(7,36,26,.62) 48%,
    rgba(7,36,26,.94) 100%)
}
.route.route--pic:hover{background:var(--ink);border-color:transparent}
.route.route--pic:hover::before,
.route.route--pic:focus-visible::before{transform:scale(1.06)}

.route.route--pic .rn{color:var(--moss-on-shot);margin-bottom:10px;text-shadow:0 1px 8px rgba(3,15,10,.6)}
.route.route--pic .h-sub{color:#fff;font-size:clamp(1.25rem,1.55vw,1.5rem);line-height:1.2;text-shadow:0 1px 12px rgba(3,15,10,.55)}
.route.route--pic p{color:rgba(255,255,255,.9);margin-bottom:18px;text-shadow:0 1px 10px rgba(3,15,10,.6)}
.route.route--pic .go{margin-top:0;color:var(--moss-on-shot)}
@media (prefers-reduced-motion:reduce){
  .route.route--pic::before{transition:none}
  .route.route--pic:hover::before{transform:none}
}

/* ---------- amenity cards: the photograph always showing ----------
   These used to fade their photograph in on hover under a full-card scrim.
   Now, like the History page's cards, the picture is always there and left
   clear at the top; the words sit at the foot of a taller card, and a
   gradient deepens only under them. Hover draws the picture in a little. */
.facts--marked .fact.has-shot{
  min-height:clamp(250px,24vw,300px);
  display:flex;flex-direction:column;justify-content:flex-end;
  background:var(--ink);border-color:transparent
}
.fact.has-shot::before,
.fact.has-shot::after{opacity:1}
.fact.has-shot::before{transform:none;transition:transform .8s cubic-bezier(.16,1,.3,1)}
.fact.has-shot::after{
  background:linear-gradient(180deg,
    rgba(7,36,26,0)   0%,
    rgba(7,36,26,.10) 26%,
    rgba(7,36,26,.66) 55%,
    rgba(7,36,26,.94) 100%)
}
.fact.has-shot:hover::before,
.fact.has-shot:focus-within::before{transform:scale(1.06)}

.fact.has-shot h4{color:#fff;text-shadow:0 1px 10px rgba(3,15,10,.55)}
.fact.has-shot p{color:rgba(255,255,255,.9);text-shadow:0 1px 10px rgba(3,15,10,.6)}
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{color:var(--moss-on-shot)}
.facts--marked .fact.has-shot:hover,
.facts--marked .fact.has-shot:focus-within{border-color:transparent;box-shadow:0 14px 34px rgba(11,33,26,.18)}
@media (prefers-reduced-motion:reduce){
  .fact.has-shot::before{transition:none}
  .fact.has-shot:hover::before,.fact.has-shot:focus-within::before{transform:none}
}

/* ---------- the header, a size up ----------
   The mark was 34px and the links 11.5px — both read small against the bar.
   The bar is 64px tall, so a 44px mark still leaves it air. */
.brand img{width:44px;height:44px}
.brand-tx b{font-size:17px}
.brand-tx i{font-size:10px}
.nav a{font-size:13px;padding:9px 14px}
@media(max-width:1180px){
  .nav a{font-size:12px;padding:9px 10px}
}

/* ---------- amenity icons over their photographs ----------
   Drawn in the accent green straight onto a green gradient, the icons sank
   into it. Each now sits on a small dark glass disc with a pale rim, drawn in
   white with a light-green accent, so it reads whatever the photograph is. */
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{
  --glyph-accent:#9FE8C1;
  color:#fff;
  width:clamp(58px,5.6vw,68px);height:clamp(58px,5.6vw,68px);
  padding:12px;box-sizing:border-box;border-radius:50%;
  background:rgba(4,22,15,.55);
  border:1px solid rgba(255,255,255,.38);
  box-shadow:0 6px 18px rgba(3,15,10,.35), inset 0 1px 0 rgba(255,255,255,.18);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)
}

/* ---------- Who's here: every row carries its organisation's mark ----------
   The mark twice over: large and faint across the whole row as its
   background, and whole in a plate in the middle of the row. The row's own
   brand colour washes in from the left under both. A white (reversed) mark is
   shown on its brand colour in the plate, and as a dark silhouette in the
   background, where white on the pale page would vanish.

   Rows with no artwork yet keep the same shape: the plate carries the
   initials in the sector colour, and there is no watermark. */
.dir-row{
  grid-template-columns:38px minmax(0,1.1fr) 150px minmax(0,1fr) 108px;
  align-items:center;position:relative;isolation:isolate;overflow:hidden;
  padding:14px 12px
}
.dir-row::before,
.dir-row::after{content:"";position:absolute;inset:0;pointer-events:none}
.dir-row::before{
  z-index:-2;
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--brand, var(--chip, var(--moss))) 11%, transparent) 0%,
    color-mix(in srgb, var(--brand, var(--chip, var(--moss))) 4%, transparent) 55%,
    transparent 100%)
}
.dir-row.has-logo::after{
  z-index:-1;
  background:var(--logo) no-repeat 50% 50% / auto 240%;
  opacity:.09;filter:saturate(.8) blur(.5px);
  transition:opacity .3s, transform .6s cubic-bezier(.16,1,.3,1)
}
.dir-row.is-rev::after{filter:brightness(0);opacity:.06}
.dir-row:hover{background:var(--stone)}
.dir-row.has-logo:hover::after{opacity:.15;transform:scale(1.04)}

.dir-row .lg{
  justify-self:center;display:grid;place-items:center;
  width:132px;height:52px;padding:7px 12px;box-sizing:border-box;
  background:#fff;border:1px solid var(--line);border-radius:10px;
  box-shadow:0 4px 14px rgba(11,33,26,.06)
}
.dir-row.is-rev .lg{background:var(--brand);border-color:transparent}
.dir-row .lg img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;display:block}
.dir-row .lg b{
  font-family:var(--f-disp);font-weight:700;font-size:17px;letter-spacing:.02em;
  color:var(--chip, var(--moss))
}
.dir-row:not(.has-logo) .lg{
  background:color-mix(in srgb, var(--chip, var(--moss)) 8%, #fff);
  border-style:dashed
}

@media(max-width:1000px){
  .dir-row{grid-template-columns:38px minmax(0,1fr) 120px minmax(0,1fr) 96px}
  .dir-row .lg{width:112px;height:46px}
}
@media(max-width:820px){
  .dir-row{grid-template-columns:30px 1fr auto;gap:6px 14px}
  .dir-row .nm{grid-column:2}
  .dir-row .lg{grid-column:3;grid-row:1 / span 3;width:96px;height:44px}
  .dir-row .ds{grid-column:2}
  .dir-row .lk{grid-column:2}
  .dir-row.has-logo::after{background-size:auto 160%;background-position:85% 50%}
}

/* ---------- Who's here: logos inside their plates ----------
   The picture gets a fixed box to fit within rather than a percentage of a
   height the grid never settles, which is what let tall and wide marks spill
   out of the plate. */
.dir-row .lg{overflow:hidden}
.dir-row .lg img{max-width:100%;max-height:36px;width:auto;height:auto;object-fit:contain}
@media(max-width:1000px){.dir-row .lg img{max-height:32px}}
@media(max-width:820px){.dir-row .lg img{max-height:30px}}

/* ---------- Who's here: the words kept clear of the watermark ----------
   The faint mark behind a row can land right under the name or the
   description. Rather than boxing the words, a gradient of the row's own
   ground lies over the mark where the words are — dense under the name on
   the left and under the description and link on the right, clearing
   through the middle, where the mark and the logo plate are meant to show.

   That needs a layer above the mark and below the words, so the two layers
   swap places on rows with a logo: the mark moves to ::before (bottom), and
   ::after (above it) carries the brand wash and the gradient. The ground is
   a custom property so the gradient follows the row's hover colour. */
.dir-row{--row-bg:var(--paper)}
.dir-row:hover{--row-bg:var(--stone)}
.dir-row.has-logo::before{
  z-index:-2;
  background:var(--logo) no-repeat 50% 50% / auto 240%;
  opacity:.09;filter:saturate(.8) blur(.5px);
  transition:opacity .3s, transform .6s cubic-bezier(.16,1,.3,1)
}
.dir-row.is-rev::before{filter:brightness(0);opacity:.06}
.dir-row.has-logo:hover::before{opacity:.15;transform:scale(1.04)}
.dir-row.has-logo::after{
  z-index:-1;opacity:1;filter:none;transform:none;
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--brand) 11%, transparent) 0%,
      color-mix(in srgb, var(--brand) 4%, transparent) 55%,
      transparent 100%),
    linear-gradient(90deg,
      color-mix(in srgb, var(--row-bg) 92%, transparent) 0%,
      color-mix(in srgb, var(--row-bg) 80%, transparent) 30%,
      transparent 42%,
      transparent 58%,
      color-mix(in srgb, var(--row-bg) 80%, transparent) 66%,
      color-mix(in srgb, var(--row-bg) 92%, transparent) 100%)
}
.dir-row.has-logo:hover::after{opacity:1;transform:none}
@media(max-width:820px){
  /* stacked, the words run down the left and the plate sits on the right */
  .dir-row.has-logo::after{
    background:
      linear-gradient(90deg,
        color-mix(in srgb, var(--brand) 11%, transparent) 0%,
        transparent 100%),
      linear-gradient(90deg,
        color-mix(in srgb, var(--row-bg) 92%, transparent) 0%,
        color-mix(in srgb, var(--row-bg) 78%, transparent) 62%,
        transparent 80%)
  }
  .dir-row.has-logo::before{background-size:auto 160%;background-position:85% 50%}
}

/* ---------- Who's here: the overlay in the logo's own colour ----------
   The gradient over the words is now drawn in each organisation's brand
   colour rather than the page's ground. It is that colour mixed into the
   row's ground — strong enough to read as the logo's colour, light enough
   that the dark type on it keeps its contrast — and it follows the hover
   colour as before. */
.dir-row.has-logo{--tint:color-mix(in srgb, var(--brand) 36%, var(--row-bg))}
.dir-row.has-logo::after{
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--tint) 100%, transparent) 0%,
    color-mix(in srgb, var(--tint) 92%, transparent) 30%,
    color-mix(in srgb, var(--tint) 40%, transparent) 42%,
    color-mix(in srgb, var(--tint) 40%, transparent) 58%,
    color-mix(in srgb, var(--tint) 92%, transparent) 66%,
    color-mix(in srgb, var(--tint) 100%, transparent) 100%)
}
@media(max-width:820px){
  .dir-row.has-logo::after{
    background:linear-gradient(90deg,
      color-mix(in srgb, var(--tint) 100%, transparent) 0%,
      color-mix(in srgb, var(--tint) 92%, transparent) 62%,
      color-mix(in srgb, var(--tint) 40%, transparent) 80%)
  }
}
/* on the stronger brand tint the muted grey description drops too low, so on
   rows with a logo it is set in the ink the name uses */
.dir-row.has-logo .ds{color:var(--ink)}

/* A reversed mark sits on its plate colour, which is its brand colour unless
   logos.ts names another — for marks drawn in the brand colour with white
   lettering, which would vanish on either white or the brand colour. The row's
   tint still follows the brand. */
.dir-row.is-rev .lg{background:var(--plate, var(--brand))}
.wall__tile.is-rev{background:var(--plate, var(--brand))}
a.wall__tile.is-rev:hover{background:color-mix(in srgb, var(--plate, var(--brand)) 86%, #000)}

/* ---------- Who's here: marks that read in the background ----------
   Two kinds of mark were not reading behind their rows. Marks drawn light on
   a dark square (Eglwys Penuel, Jonathan Davies, R&R, Thrive, Refined Beauty)
   were being turned into a black silhouette, which on their own dark tint
   was just a grey block the size of the file. And marks set small inside a
   big light file (EmpathyWorks, Just Mortgages) showed as a pale box.

   Now the row has a solid ground of its own, so the mark can be multiplied
   into it: anything white in the file falls away and only the drawing is
   left, in its own colours. A reversed mark is inverted first, so its light
   drawing becomes dark and its dark square becomes light — and then falls
   away the same way. With the box gone the mark can be stronger without
   turning into a block, and the gradient over the words is eased so a little
   more of it shows through. */
.dir-row.has-logo{background:var(--row-bg)}
.dir-row.has-logo::before{
  mix-blend-mode:multiply;opacity:.3;filter:saturate(.9);
  background-size:auto 190%
}
.dir-row.is-rev::before{filter:invert(1) grayscale(1) contrast(1.6);opacity:.3}
.dir-row.has-logo:hover::before{opacity:.42}
.dir-row.has-logo::after{
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--tint) 94%, transparent) 0%,
    color-mix(in srgb, var(--tint) 78%, transparent) 30%,
    color-mix(in srgb, var(--tint) 30%, transparent) 42%,
    color-mix(in srgb, var(--tint) 30%, transparent) 58%,
    color-mix(in srgb, var(--tint) 78%, transparent) 66%,
    color-mix(in srgb, var(--tint) 94%, transparent) 100%)
}

/* ---------- reviews ----------
   A dark band, so it reads as a pause between the paper and stone sections
   either side of it on both pages. The cards run in the same sliding track as
   the hero's booking tiles: three in view on a wide screen, one and a bit on
   a phone so the next card says there is more. */
.reviews{background:var(--ink);color:#fff;overflow:hidden}
.reviews .eyebrow{color:rgba(255,255,255,.7)}
.reviews .h-sec{color:#fff;max-width:22ch}
.reviews__head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;margin-bottom:clamp(26px,3.4vw,40px)}
.reviews__nav{display:flex;gap:10px;flex:none}
.reviews__nav button{
  width:48px;height:48px;border-radius:50%;display:grid;place-items:center;margin:0;padding:0;cursor:pointer;
  color:#fff;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.35);
  transition:background .25s,border-color .25s,transform .25s cubic-bezier(.16,1,.3,1)
}
.reviews__nav button:hover{background:rgba(255,255,255,.18);border-color:#fff}
.reviews__nav button:active{transform:scale(.94)}
.reviews__nav svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}

.reviews__track{
  --gap:clamp(12px,1.4vw,18px);--in-view:3;
  display:flex;gap:var(--gap);overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;scrollbar-width:none;overscroll-behavior-x:contain;
  padding:4px 0 10px;cursor:grab
}
.reviews__track::-webkit-scrollbar{display:none}
.reviews__track.is-drag{scroll-snap-type:none;cursor:grabbing}
.reviews__track.is-drag .review{pointer-events:none}
@media(max-width:1000px){.reviews__track{--in-view:2}}
@media(max-width:640px){.reviews__track{--in-view:1.15}}

.review{
  flex:0 0 calc((100% - var(--gap) * (var(--in-view) - 1)) / var(--in-view));
  scroll-snap-align:start;margin:0;user-select:none;
  display:flex;flex-direction:column;gap:18px;
  padding:clamp(24px,2.6vw,34px);border-radius:var(--r);
  background:linear-gradient(160deg, rgba(255,255,255,.09), rgba(255,255,255,.04));
  border:1px solid rgba(255,255,255,.16)
}
.review__mark{width:34px;height:27px;fill:var(--hero-live)}
.review__stars{color:#F2C94C;letter-spacing:2px;font-size:15px}
.review__stars span{color:rgba(255,255,255,.25)}
.review blockquote{
  margin:0;font-family:var(--f-disp);font-size:clamp(1.02rem,1.25vw,1.18rem);line-height:1.55;
  color:rgba(255,255,255,.94)
}
.review figcaption{margin-top:auto;display:flex;flex-direction:column;gap:3px;padding-top:16px;border-top:1px solid rgba(255,255,255,.14)}
.review figcaption b{font-weight:600;color:#fff}
.review figcaption span{font-size:13.5px;color:rgba(255,255,255,.7)}
.review figcaption i{font-style:normal;font-family:var(--f-mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--hero-live);margin-top:4px}
/* the local-only stand-ins say so plainly, so nobody mistakes them for copy */
.review.is-placeholder{border-style:dashed;border-color:rgba(255,255,255,.3)}
.review.is-placeholder blockquote{color:rgba(255,255,255,.6);font-style:italic}

.reviews__dots{display:flex;gap:7px;margin-top:22px}
.reviews__dots button{
  width:18px;height:3px;border:0;padding:0;margin:0;border-radius:2px;cursor:pointer;position:relative;
  background:rgba(255,255,255,.3);transition:width .45s cubic-bezier(.16,1,.3,1),background .3s
}
.reviews__dots button::before{content:"";position:absolute;inset:-10px -3px}
.reviews__dots button.is-on{width:36px;background:var(--hero-live)}
@media(max-width:640px){
  .reviews__head{flex-direction:column;align-items:flex-start}
}

/* ---------- social ----------
   The Facebook timeline on the left at the width Facebook allows (500px at
   most), the Instagram grid or its follow card filling the rest. Stacked on a
   narrow screen. */
.social__head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;flex-wrap:wrap;margin-bottom:clamp(26px,3.4vw,40px)}
.social__links{display:flex;gap:10px;flex-wrap:wrap}
.social__btn{
  display:inline-flex;align-items:center;gap:9px;padding:11px 18px;border-radius:999px;text-decoration:none;
  font-family:var(--f-body);font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--moss);background:var(--paper);border:1px solid var(--line-str);transition:background .2s,border-color .2s,color .2s
}
.social__btn:hover{background:var(--moss);border-color:var(--moss);color:#fff}
.social__btn svg{width:16px;height:16px;fill:currentColor}

.social__grid{display:grid;grid-template-columns:minmax(0,500px) minmax(0,1fr);gap:clamp(16px,2.4vw,32px);align-items:start}
@media(max-width:900px){.social__grid{grid-template-columns:1fr}}
.social__fb{
  background:#fff;border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  min-height:640px;display:flex;justify-content:center
}
.social__fb iframe{border:0;display:block;max-width:100%}
.social__hold{width:100%;height:640px;background:linear-gradient(180deg,#fff,var(--paper))}

.ig-grid{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
@media(max-width:1100px){.ig-grid{grid-template-columns:repeat(3,1fr)}}
@media(max-width:560px){.ig-grid{grid-template-columns:repeat(2,1fr)}}
.ig-grid a{position:relative;display:block;aspect-ratio:1;border-radius:12px;overflow:hidden;background:var(--line)}
.ig-grid img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .7s cubic-bezier(.16,1,.3,1)}
.ig-grid a:hover img{transform:scale(1.06)}
.ig-grid__play{position:absolute;top:10px;right:10px;width:22px;height:22px;border-radius:50%;background:rgba(0,0,0,.5)}
.ig-grid__play::after{content:"";position:absolute;left:8px;top:6px;border-left:8px solid #fff;border-top:5px solid transparent;border-bottom:5px solid transparent}

.ig-follow{
  display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-end;gap:10px;
  min-height:clamp(300px,40vw,640px);padding:clamp(26px,3vw,40px);border-radius:var(--r);text-decoration:none;color:#fff;
  background:
    linear-gradient(180deg, rgba(7,36,26,.05) 0%, rgba(7,36,26,.55) 55%, rgba(7,36,26,.92) 100%),
    url("/images/estate/aerial-main-ranges.jpg") center/cover;
  transition:transform .3s, box-shadow .3s
}
.ig-follow:hover{transform:translateY(-3px);box-shadow:0 18px 44px rgba(11,33,26,.18)}
.ig-follow__icon{
  width:56px;height:56px;border-radius:16px;display:grid;place-items:center;color:#fff;margin-bottom:auto;
  background:linear-gradient(45deg,#F58529,#DD2A7B 45%,#8134AF 75%,#515BD4)
}
.ig-follow__icon svg{width:30px;height:30px}
.ig-follow b{font-family:var(--f-disp);font-size:clamp(1.5rem,2.4vw,2rem);letter-spacing:-.01em}
.ig-follow span{max-width:40ch;color:rgba(255,255,255,.9);line-height:1.6}
.ig-follow i{font-style:normal;font-family:var(--f-body);font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--hero-live)}

/* ---------- header: room for a fifth link ----------
   With History added the bar carries five links, and at mid widths the longer
   ones ("General information") broke onto two lines. Each link now stays on
   one line, and the spacing and tracking tighten in steps as the bar narrows
   rather than the words wrapping. */
.nav a{white-space:nowrap}
@media(max-width:1360px){
  .nav a{font-size:12.5px;padding:9px 10px;letter-spacing:.08em}
}
@media(max-width:1180px){
  .nav{gap:0}
  .nav a{font-size:11.5px;padding:9px 7px;letter-spacing:.06em}
  .head-in{gap:12px}
}
@media(max-width:1040px){
  .brand-tx i{display:none}
  .nav a{padding:9px 6px;letter-spacing:.04em}
}
/* Between the phone menu and a full-width bar there is no room for the
   language names beside five links, so the switch keeps just its flags. The
   names stay in the markup, so the links are still announced as "English" and
   "Cymraeg"; they are only drawn at no size. */
.head-in .lang{flex:none}
@media(min-width:941px) and (max-width:1180px){
  .lang--compact>*{font-size:0;letter-spacing:0;gap:0;padding:6px 9px}
}

/* ---------- reviews: the link to the full list ---------- */
.reviews__more{
  display:inline-block;margin-top:22px;text-decoration:none;
  font-family:var(--f-body);font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--hero-live)
}
.reviews__more:hover{text-decoration:underline;text-underline-offset:4px}
/* with fewer reviews than fit across, the cards keep a readable width rather
   than stretching or leaving the row half empty on one side */
.review{max-width:560px}

/* ---------- amenity icons: green crystal ----------
   The little discs behind the icons on "What you'll find here" now use the
   hero's own material: the logo's lighter green as glass, frosted, with the
   cut-crystal facets and a sheen across them, and a bright bevel at the rim. */
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{
  background:
    url("/images/crystal-gem.svg") center / 140% 140% no-repeat,
    linear-gradient(135deg, rgba(255,255,255,.28) 0%, rgba(255,255,255,0) 42%, rgba(255,255,255,.1) 64%, rgba(255,255,255,0) 80%),
    linear-gradient(rgba(0,165,95,.46), rgba(0,120,70,.58));
  border:1px solid rgba(200,255,225,.55);
  box-shadow:0 6px 18px rgba(3,15,10,.35), inset 0 1px 0 rgba(255,255,255,.35), inset 0 -6px 14px rgba(0,60,35,.3);
  -webkit-backdrop-filter:blur(8px) saturate(1.2);backdrop-filter:blur(8px) saturate(1.2)
}

/* ---------- amenity icons: easier to see ----------
   On the crystal disc the thin white line drawings were competing with the
   facets behind them. The disc is a size up, the glass is deeper, and a soft
   dark pool sits in its centre so the facets show round the rim but not under
   the drawing. The drawing itself is heavier, pure white with a brighter green
   accent, and carries a small shadow so it lifts off the glass. */
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{
  --glyph-accent:#C2F7DA;
  width:clamp(68px,6.4vw,80px);height:clamp(68px,6.4vw,80px);
  padding:14px;
  background:
    radial-gradient(circle at 50% 50%, rgba(0,48,28,.62) 0%, rgba(0,48,28,.45) 45%, rgba(0,48,28,0) 72%),
    url("/images/crystal-gem.svg") center / 140% 140% no-repeat,
    linear-gradient(135deg, rgba(255,255,255,.26) 0%, rgba(255,255,255,0) 40%, rgba(255,255,255,.08) 64%, rgba(255,255,255,0) 80%),
    linear-gradient(rgba(0,150,86,.62), rgba(0,96,56,.78));
  border:1.5px solid rgba(210,255,230,.7)
}
.fact.has-shot .glyph svg{filter:drop-shadow(0 1px 2px rgba(0,25,14,.55))}
.fact.has-shot .glyph svg > g{stroke-width:2.3}
/* the drawing fills more of the disc, with a heavier line to match */
.fact.has-shot .glyph,
.fact.has-shot:hover .glyph,
.fact.has-shot:focus-within .glyph{padding:10px}
.fact.has-shot .glyph svg > g{stroke-width:2.7}

/* ---------- social: Facebook's header replaced ----------
   The plugin's own header cut the page name off, so it is cropped away: the
   frame is pulled up by the header's height inside a box that clips it, and a
   header of the site's own sits above instead. */
.social__fbcard{
  background:#fff;border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  display:flex;flex-direction:column
}
.social__fbhead{
  display:flex;align-items:center;gap:12px;padding:14px 16px;text-decoration:none;color:var(--ink);
  border-bottom:1px solid var(--line)
}
.social__fbhead img{width:44px;height:44px;border-radius:50%;flex:none}
.social__fbhead span{display:flex;flex-direction:column;line-height:1.2;min-width:0}
.social__fbhead b{font-family:var(--f-disp);font-weight:700;font-size:16px}
.social__fbhead i{font-style:normal;font-size:12.5px;color:var(--muted)}
.social__fbhead em{
  margin-left:auto;font-style:normal;font-size:12px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  padding:8px 14px;border-radius:999px;background:#1877F2;color:#fff;transition:background .2s
}
.social__fbhead:hover em{background:#0B5FCC}
.social__fbcard .social__fb{
  border:0;border-radius:0;min-height:0;height:620px;overflow:hidden;position:relative;display:block
}
.social__fbcard .social__fb iframe{position:absolute;top:-80px;left:50%;transform:translateX(-50%)}
.social__fbcard .social__hold{height:620px}
/* on paper, the section after it is paper too, so a rule keeps them apart */
.social--paper + .band{border-top:1px solid var(--line)}

/* ---------- social: the two columns the same height ----------
   Side by side, the Instagram card stretches to the Facebook card's height
   (its header plus the feed), top and bottom aligned. Stacked on a narrow
   screen each keeps its own height. */
@media(min-width:901px){
  .social__grid{align-items:stretch}
  .social__ig{display:flex;flex-direction:column}
  .social__ig > .ig-follow{flex:1;min-height:0}
  .social__ig > .ig-grid{flex:1;align-content:start}
}

/* ---------- hero: the video behind everything ----------
   Fills the stage the photographs used to, cropped to cover, under the same
   veil, scrims and parallax. The still frame is its poster, so the hero is
   never blank while the video loads. */
.hero-vid{position:absolute;inset:0;background:#06120E}
/* Two layers: the one in front is opaque, the one waiting is not, and the
   hand-over between films is a cross-fade rather than a cut. */
.hero-video{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
  pointer-events:none;filter:saturate(1.04) contrast(1.02);
  opacity:0;transition:opacity var(--fade,900ms) linear
}
.hero-video.is-on{opacity:1}
@media(prefers-reduced-motion:reduce){.hero-video{transition:none}}
/* a credit that links to the depository reads as a credit, not as body copy */
.tl-src a{color:var(--muted);text-decoration:none;border-bottom:1px solid var(--line-str)}
.tl-src a:hover{color:var(--moss);border-bottom-color:var(--moss)}

/* ---------- header: the mark is larger before the page moves ----------
   At the very top there is no bar, so the mark has room to be a proper
   signature; once the page scrolls under the header it returns to the size it
   has to be to sit in the pill. Both sizes cross-fade with the bar itself. */
.brand img{transition:width .35s cubic-bezier(.16,1,.3,1), height .35s cubic-bezier(.16,1,.3,1)}
.site-head.at-top .brand img{width:62px;height:62px}
.site-head.at-top .brand-tx b{font-size:19px}
@media(max-width:940px){
  .site-head.at-top .brand img{width:52px;height:52px}
  .site-head.at-top .brand-tx b{font-size:17px}
}

/* ---------- reviews: a shorter band ----------
   Two or three short quotes did not need the full section height: the band,
   the gap under the heading and the cards themselves all close up. */
.reviews{padding-block:clamp(38px,4.6vw,64px)}
.reviews__head{margin-bottom:clamp(18px,2.2vw,26px)}
.reviews .h-sec{max-width:26ch}
.review{gap:12px;padding:clamp(18px,1.9vw,24px)}
.review__mark{width:26px;height:21px}
.review blockquote{font-size:clamp(.98rem,1.1vw,1.08rem);line-height:1.5}
.review figcaption{padding-top:12px}
.reviews__track{padding:3px 0 6px}
.reviews__more{margin-top:16px}
.reviews__nav button{width:42px;height:42px}

/* ---------- activities: a drawing per class ----------
   One icon for what the class is, not for who runs it: a dumbbell for
   strength work, a mat for yoga, a target for the rifle club. Small, in the
   accent green, and the same drawing language as the rest of the site. */
.act{align-items:center;gap:12px}
.act b{flex:1;min-width:0}
.act__icon{
  flex:none;display:block;width:30px;height:30px;margin:0;color:var(--moss);
  --glyph-accent:var(--moss-2)
}
.act__icon svg{width:100%;height:100%;display:block;overflow:visible}
.act .act__by{text-align:right}
@media(max-width:560px){
  .act{flex-wrap:wrap}
  .act__icon{width:26px;height:26px}
  .act .act__by{text-align:left;width:100%;padding-left:38px}
}

/* ---------- what's on: the heading and the week in pictures ----------
   The pictures sit beside the heading on a wide screen and under it on a
   narrow one, two and a bit in view so the next one is always showing. */
.acts-top{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,46%);
  gap:clamp(20px,3vw,44px);align-items:center
}
@media(max-width:900px){.acts-top{grid-template-columns:1fr}}
.acts-top .h-sec{max-width:18ch}
.acts-top .lede{margin-bottom:0}

.actpics__track{
  --gap:10px;--in-view:2;
  display:flex;gap:var(--gap);overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;scrollbar-width:none;overscroll-behavior-x:contain;
  cursor:grab;padding-bottom:2px
}
.actpics__track::-webkit-scrollbar{display:none}
.actpics__track.is-drag{scroll-snap-type:none;cursor:grabbing}
.actpics figure{
  flex:0 0 calc((100% - var(--gap) * (var(--in-view) - 1)) / var(--in-view));
  scroll-snap-align:start;position:relative;margin:0;border-radius:14px;overflow:hidden;
  background:var(--ink);user-select:none
}
.actpics img{
  display:block;width:100%;height:clamp(140px,15vw,190px);object-fit:cover;
  transition:transform .8s cubic-bezier(.16,1,.3,1)
}
.actpics figure:hover img{transform:scale(1.05)}
.actpics figcaption{
  position:absolute;left:0;right:0;bottom:0;padding:26px 14px 12px;color:#fff;
  font-family:var(--f-disp);font-weight:600;font-size:14px;line-height:1.25;
  background:linear-gradient(180deg, rgba(7,36,26,0) 0%, rgba(7,36,26,.72) 55%, rgba(7,36,26,.94) 100%);
  text-shadow:0 1px 10px rgba(3,15,10,.5)
}
.actpics__dots{display:flex;gap:6px;margin-top:10px}
.actpics__dots button{
  width:16px;height:3px;border:0;padding:0;margin:0;border-radius:2px;cursor:pointer;position:relative;
  background:rgba(11,33,26,.22);transition:width .45s cubic-bezier(.16,1,.3,1), background .3s
}
.actpics__dots button::before{content:"";position:absolute;inset:-10px -3px}
.actpics__dots button.is-on{width:30px;background:var(--moss)}
@media(max-width:560px){.actpics__track{--in-view:1}}

/* ---------- what's on: a tighter list, each line a link ----------
   Shorter rows, so the top of the list is still on screen when the foot of it
   is. Each class opens whoever runs it, in a new tab, where the directory has
   a site for them. */
.act{padding:9px 0}
.act b{font-size:14.5px}
.act__icon{width:26px;height:26px}
.act--link{text-decoration:none;color:inherit;transition:color .2s}
.act--link b{transition:color .2s}
.act--link:hover b{color:var(--moss)}
.act--link:hover .act__icon{color:var(--moss-2)}
.act--link::after{content:"↗";font-size:11px;color:var(--muted);opacity:0;transition:opacity .2s;margin-left:2px}
.act--link:hover::after,.act--link:focus-visible::after{opacity:1}
@media(max-width:560px){.act__icon{width:24px;height:24px}}

/* ---------- directory: search beside the sector chips ----------
   One field over the chips: type a name, a service or a sector, then narrow
   by sector if you want to. The two work together. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
.dir-find{margin-bottom:14px}
.dir-search{
  display:flex;align-items:center;gap:10px;max-width:460px;
  padding:11px 14px;border-radius:999px;background:var(--paper);
  border:1px solid var(--line-str);transition:border-color .2s, box-shadow .2s
}
.dir-search:focus-within{border-color:var(--moss);box-shadow:0 0 0 4px rgba(14,92,65,.12)}
.dir-search svg{width:17px;height:17px;flex:none;fill:none;stroke:var(--muted);stroke-width:1.8;stroke-linecap:round}
.dir-search input{
  flex:1;min-width:0;border:0;background:none;padding:0;margin:0;
  font-family:var(--f-body);font-size:15px;color:var(--ink)
}
.dir-search input:focus{outline:none}
.dir-search input::placeholder{color:var(--muted)}
.dir-search input::-webkit-search-cancel-button{display:none}
.dir-search__clear{
  flex:none;border:0;background:none;cursor:pointer;padding:0 2px;margin:0;
  font-size:20px;line-height:1;color:var(--muted)
}
.dir-search__clear:hover{color:var(--ink)}
.dir-none{
  padding:30px 4px 6px;color:var(--muted);font-size:15px
}
.dir-none button{
  border:0;background:none;padding:0;margin:0;cursor:pointer;color:var(--moss);
  font:inherit;text-decoration:underline;text-underline-offset:3px
}
@media(max-width:560px){.dir-search{max-width:none}}

/* ---------- directory: sectors and search on one line ----------
   Chips to the left, the search field to the right, both on the same baseline;
   on a narrow screen the search drops under the chips and fills the width. */
.dir-tools{
  display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;
  margin-bottom:6px
}
.dir-tools .filters{margin-bottom:0;flex:1 1 300px;min-width:0;flex-wrap:wrap}
.dir-tools .dir-find{margin-bottom:0;flex:0 0 clamp(240px,24vw,320px)}
.dir-tools .dir-search{max-width:none;padding:9px 13px}
@media(max-width:900px){
  .dir-tools{align-items:stretch}
  .dir-tools .dir-find{flex:1 1 100%;order:-1}
}

/* ---------- the page at 95% ----------
   Everything a shade smaller, so more of each page is in view at once: the
   whole document is zoomed rather than every size being rewritten. Zoom
   leaves viewport units measuring the real window, so the full-height hero is
   divided back out to keep filling the screen exactly. */
body{zoom:.95}
.hero{min-height:calc(100svh / .95)}
