/* SEVEN design layer — generated by tools/build.sh from src/. Do not edit. */
/* src/tokens.css — SEVEN design tokens.
   Values sampled from production seven.club CSS and reference_brand_tokens.md.
   Never invent a colour here. Derive by lightening/darkening, never hue-shifting. */
/* Each token reads from the matching Webflow variable, with the sampled
   literal as the fallback. Webflow emits its variables under its own
   namespace (`--_seven---seven-bg`), which no rule here would otherwise
   reach; aliasing them once, here, makes every rule in this stylesheet
   follow whatever the Designer's variable panel says. Where those variables
   are absent — the local static build, or a Webflow page that has not
   published since the collection was created — the fallback is the value
   that was always there, so nothing depends on Webflow being present.
   Change a colour in the Designer and the whole site follows; change it
   here and the local build follows. The two stay in step because the
   fallback IS the sampled production value. */
:root {
  /* Ground — neutral, production-backed */
  --seven-bg:        var(--_seven---seven-bg,        #000000);
  --seven-raised:    var(--_seven---seven-raised,    #0a0a0a);
  --seven-raised-2:  var(--_seven---seven-raised-2,  #141414);

  /* Ink */
  --seven-fg:        var(--_seven---seven-fg,        #fafafa);  /* off-white, never #fff */
  --seven-muted:     var(--_seven---seven-muted,     #adaaaa);

  /* Accent — champagne, not gold */
  --seven-accent:    var(--_seven---seven-accent,    #dbc8b6);
  --seven-accent-2:  var(--_seven---seven-accent-2,  #c5b9ac);

  /* Webflow stores this as 8-digit hex; rgba() is the same colour. */
  --seven-rule:      var(--_seven---seven-rule,      rgba(219, 200, 182, 0.28));

  /* Semantic aliases — components reference these, so the brand
     can be re-skinned from the block above alone. */
  --ink:    var(--seven-fg);
  --shell:  var(--seven-bg);
  --accent: var(--seven-accent);
  --rule:   var(--seven-rule);

  /* Layering scale */
  --z-base: 10; --z-nav: 30; --z-overlay: 50;
}

/* Warm-dark alternative — REQUIRES BRAND DEPT SIGN-OFF before enabling.
   Derived from assets/site.css. Enabling this hue-shifts the production
   neutral ground, which the brand-tokens rule otherwise forbids.
:root {
  --seven-bg:       #17140F;
  --seven-raised:   #1C1815;
  --seven-raised-2: #241A13;
  --seven-fg:       #EFE9DE;
  --seven-muted:    #B4A99B;
}
*/
/* src/type.css — Acumin Pro only. Contrast comes from weight, not a second family. */
:root {
  --font-seven: "acumin-pro", "Acumin Pro", "Helvetica Neue", Inter, system-ui, sans-serif;
  --step--1: clamp(0.78rem, 0.75rem + 0.15vw, 0.86rem);
  --step-0:  clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  --step-1:  clamp(1.33rem, 1.2rem + 0.6vw, 1.75rem);
  --step-2:  clamp(1.9rem, 1.5rem + 1.8vw, 3rem);
  --step-3:  clamp(2.6rem, 1.7rem + 4.2vw, 5.5rem);
  --step-4:  clamp(3.2rem, 1.6rem + 7vw, 8rem);
}

body { font-family: var(--font-seven); color: var(--ink); background: var(--shell); }
/* Bold (700), not UltraBlack (900), per Frederico's design review of 16 Sep
   2026 — pins 1 (REDEFINED) and 10 (DELIBERATELY), the only two elements in the
   site that carry this class. Acumin Pro is served by Webflow as four ranged
   faces (100 300 / 400 500 / 600 700 / 800 900), so 700 resolves to a real Bold
   file rather than a synthesised one; verified loading on the published page. */
.s-display--black { font-weight: 700; letter-spacing: -0.018em; display: block; }
/* Acumin Thin loses its hairlines below ~48px. .s-display never gets there:
   --step-4 clamps at a 3.2rem (51.2px) floor and the 11vw cap holds 41px at
   375px, so this step-up is a margin of safety on small screens rather than a
   size-derived requirement. The rule bites for real on .s-lede below, which
   sits at 21-28px. */
@media (max-width: 640px) { .s-display--thin { font-weight: 400; } }
/* Weight floor: the hairline rule above applies here. .s-lede is 21.45px at
   375px, 23.81px at 768px and 27.84px at 1440px — never near 48px at any
   viewport — so it takes Book rather than Thin at every size. */

/* Italics are avoided per brand spec. */
em, i { font-style: normal; letter-spacing: 0.04em; }
/* src/motion.css — one easing token, used everywhere. */
:root {
  --ease: cubic-bezier(.22, 1, .36, 1);
  --dur-ui: 500ms;      /* interface transitions, 400-600ms band */
  --dur-reveal: 1150ms; /* entrance reveals */
}

/* Reveals start hidden ONLY when the script that un-hides them is present.
   The `js` class is set by the inline snippet in the document head, which
   also removes it again if `window.SEVEN` never turns up (CDN 404, a stale
   SRI hash, a blocker). Without that gate an unhidden-only-by-JS rule turns
   every failure of seven.js into a page with no copy on it at all. Anything
   here that hides content must therefore stay behind `.js`. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--dur-reveal) var(--ease),
              transform var(--dur-reveal) var(--ease);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

[data-reveal-delay="1"] { transition-delay: 80ms; }
[data-reveal-delay="2"] { transition-delay: 220ms; }
[data-reveal-delay="3"] { transition-delay: 360ms; }
[data-reveal-delay="4"] { transition-delay: 520ms; }
[data-reveal-delay="5"] { transition-delay: 700ms; }

a, button { transition: color var(--dur-ui) var(--ease),
                        background-color var(--dur-ui) var(--ease),
                        border-color var(--dur-ui) var(--ease); }
a:focus-visible, button:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
}
button, [role="button"], .s-cta { min-height: 44px; min-width: 44px; cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  /* Same specificity as the .js rule above and later in the file, so it wins. */
  .js [data-reveal] { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}
/* src/home.css */
html, body { margin: 0; overflow-x: hidden; }

/* Skip link — visually hidden until focused. */
.s-skip {
  position: absolute; top: -100px; left: 0; z-index: var(--z-overlay);
  background: var(--shell); color: var(--ink); padding: 0.85rem 1.4rem;
  text-decoration: none; font-size: var(--step--1); letter-spacing: 0.1em;
  text-transform: uppercase; border: 1px solid var(--accent);
}
.s-skip:focus { top: 0.75rem; left: 0.75rem; }

/* ---------- Nav ---------- */
/* row-gap is load-bearing, not decorative. The 44px hit areas below are
   centred on links whose own boxes are only ~15px tall, so each one reaches
   ~15px above and below its row. Two wrapped rows therefore need at least
   44px between their centres or the lower row's hit area covers the upper
   row's glyphs: at 375px the wordmark wraps above the links and 8px of
   row-gap put the centres 23px apart, so the bottom half of SEVEN was
   navigating to /houses. The arithmetic minimum here is a 29px gap; 3rem is
   deliberately well past it, putting the centres 63px apart, so a different
   line height — Acumin in production rather than the fallback — cannot close
   the margin. Where the bar does not wrap, which is every width tested from
   768px up, row-gap has no effect at all. Use column-gap, never the `gap`
   shorthand, in the rules below: the shorthand silently resets row-gap. */

/* Language switcher. It sits in the header rather than the footer: a visitor
   who needs another language needs it before they read the page, not after.
   Two-letter codes keep the bar short — each link carries the language's own
   name as its accessible name, plus its own lang/hreflang. .s-nav sets
   mix-blend-mode: difference, which inverts against whatever is behind, so
   the current locale is marked by opacity rather than by the accent. */
.s-lang { display: flex; gap: .1rem; }
.s-lang a { padding: 0 .45rem; min-height: 44px;
  display: inline-flex; align-items: center; letter-spacing: .18em;
  opacity: .55; transition: opacity var(--dur-ui) var(--ease); }
.s-lang a:hover { opacity: 1; }
.s-lang a[aria-current="true"] { opacity: 1; }
.s-lang a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Nav dropdown. Opens on hover and on keyboard focus-within, so it needs no
   JavaScript and stays reachable by tab. Deliberately unfilled: .s-nav sets
   mix-blend-mode: difference, which blends the bar as a group — a solid panel
   inside it would invert against whatever image sits behind, so the submenu
   is a plain stack of links under a hairline and blends like the rest. */

.s-nav__group:hover .s-nav__menu,
.s-nav__group:focus-within .s-nav__menu { opacity: 1; visibility: visible; transform: none; }
/* The last group sits hard against the right edge of the bar, so its menu
   hangs from the right rather than the left — otherwise the longest entry
   runs past the page padding. */
.s-nav__list > .s-nav__group:last-child .s-nav__menu { left: auto; right: 0;
  justify-items: end; text-align: right; }
/* Below 640px the submenus close entirely rather than flattening into the bar.
   Flattening worked with one submenu of three; with two submenus totalling eight
   entries the header runs to eleven links at 375px, which is not a navigation.
   The four top-level destinations stay, and each landing page carries its own
   links to the pages beneath it — see the row on club/membership.html. */
@media (max-width: 640px) {
  .s-nav__menu { display: none; }
  /* The header has three children now, and at this width each one claimed its
     own row — a 268px fixed bar over an 800px-tall phone. Giving the primary
     nav the full width pushes it onto its own line and leaves the wordmark
     and the language switcher sharing the first, so it is two rows again
     rather than three. The 3rem row-gap stays: each link carries a 44px hit
     area centred on ~10px of text, so rows closer than that overlap — exactly
     the mis-tap the gap was added to fix. */
  .s-nav__list { order: 1; flex: 1 0 100%; }
}
@media (max-width: 640px) {
  .s-nav__list { column-gap: 0.7rem; }
  .s-nav__link { letter-spacing: 0.1em; }
}

/* Touch targets: an invisible, centred hit area at least 44x44px on every
   nav/footer link (this also covers .s-mark, which is itself an `a`
   descendant of .s-nav / .s-footer). Absolutely positioned and out of flow,
   so it cannot change the link's visual size or the bar's layout/height,
   and — being sized from the link's own box — cannot widen the nav past
   the viewport either. */
.s-nav__link, .s-footer__a { position: relative; }
.s-nav__link::before, .s-footer__a::before {
  content: ''; position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 100%; min-width: 44px; height: 44px;
}
/* Because it is 44px tall and centred, this box overhangs its own row. Any
   container that can wrap these links must therefore keep its rows at least
   44px apart centre-to-centre — see the row-gap note on .s-nav above. That
   applies to .s-footer too: at 375px its rows sat 31px apart, which is closer
   than it looks, and hit-testing found the bottom pixel row of the footer
   wordmark resolving to /houses. One row rather than the nav's seven, but the
   same defect, so it takes the same row-gap. */

/* ---------- Hero ---------- */

/* Short viewports are where the fixed bar and the bottom-anchored hero copy
   collide. The hero is min-height: 100svh with align-items: flex-end and no top
   padding, so when the viewport is shorter than the bar plus the copy, the copy
   rides up behind the bar rather than pushing the hero taller.

   Measured on an iPhone SE in landscape (568x320): the bar wraps to 113px and
   the eyebrow started at y=13, a hundred pixels behind it. Portrait phones never
   hit this - at 375x700 the copy clears the bar by 105px - which is why the
   media query is on height, not width. Below 640px the bar wraps to a second
   row and needs more room again. */
@media (max-height: 34rem) {
  .s-hero { min-height: auto; padding-top: 8rem; padding-bottom: 2rem; }
}
@media (max-height: 34rem) and (max-width: 640px) {
  .s-hero { padding-top: 12rem; }
}
/* Motion-system reduced-motion block (src/motion.css) only neutralises
   [data-reveal] transitions — it never touches the looping hero video.
   A muted, looping background video is exactly what WCAG 2.2.2 targets,
   so swap it here, CSS-only, for its own still poster frame. This works
   even with JS disabled, and does not duplicate motion.css's transition
   rules, only adds the (distinct) video/poster visibility swap.
   Note what this rule can and cannot do: `display: none` stops the video
   being shown, not being fetched and decoded, so on its own it made a
   reduced-motion visitor pay for a frame they never see. The media query on
   the <source> in index.html is what prevents the download; this stays as
   the visible fallback for engines that ignore it. */
@media (prefers-reduced-motion: reduce) {
  .s-hero__film { display: none; }
  .s-hero__poster { display: block; }
}
/* The scrim is champagne over the ground colour, so it must follow the
   palette rather than restate it: hardcoding rgba(219,200,182,...) and
   rgba(0,0,0,...) here made the hero the one surface a token change could
   not reach. color-mix with `transparent` premultiplies, so each of these
   resolves to exactly the literal it replaced while the current tokens are
   in force. */

/* Two calls to action, not one: Apply leads, Book a Visit stays beside it as
   the lower-commitment option. There is deliberately no wrapper element around
   the pair - Webflow's builder inserts a link as a plain sibling and a wrapper
   would exist in the repo but not there, so the rule has to work on three flat
   children. Pushing the label with margin-right keeps the two buttons adjacent
   instead of space-between spreading them to opposite ends. */
.s-hero__foot > .s-label { margin-right: auto; }
/* Used on both <a> and <button>. A native button brings its own background,
   font and border, so those are reset here explicitly — without this the
   submit button renders as a pale box with 1.41:1 contrast, in Arial. */

/* Placed AFTER .s-cta on purpose. Both selectors are one class, so the cascade
   is decided by source order, and while this lived up in the nav section the
   later .s-cta { display: inline-flex } beat the media query's display: none
   and the button stayed visible at 375px. The repo keeps selectors flat (see
   tools/flatten-selectors.py), so raising specificity to .s-nav .s-nav__cta
   would have fought that convention to fix an ordering problem. */
/* The apply CTA in the header. Smaller than the page's .s-cta - it sits in a
   1.25rem bar, so the full 0.85rem/1.6rem padding would set the bar height.
   .s-nav is mix-blend-mode: difference and a child cannot opt out of its
   parent's blend, so this inverts against whatever is behind it exactly as the
   wordmark and the links do. That is why it is an outline and not a filled
   button: a solid fill would invert to a dark slab over a dark hero. */
.s-nav__cta { padding: .5rem 1rem; letter-spacing: .2em; }
/* Never let it wrap mid-label; it is two words and a break reads as two links. */
.s-nav__cta { white-space: nowrap; }
@media (max-width: 640px) {
  /* Hidden, not shrunk. The bar is already tight enough at 375px that the nav
     row overlaps the hero copy - that predates this button, but a third child
     makes it worse, and no type size small enough to fit was still legible.
     The hero carries its own Apply at this width instead (.s-hero__actions),
     so the call to action is still above the fold where Rebecca asked for it. */
  .s-nav__cta { padding: .45rem .75rem; letter-spacing: .14em; }
}

/* ---------- Intro / manifesto ---------- */
/* Tightened 8 Oct 2026, Georgia's Figma pin #8: 128px top and bottom read as
   a gap rather than a pause. */

/* Light (300) at 18px, per Frederico's review pins 2 and 3. Both properties
   override .s-lede, which this element also carries; .s-lede and .s-intro__lede
   have equal specificity, so it is build order (type.css before home.css in
   tools/build.sh) that decides this, not the selectors. Moving either file in
   that list silently reverts these two lines. 1.125rem is a literal 18px at
   every width. --step-0 was used first because it tops out at 18px, but it
   only reaches that at a wide desktop — measured 17.61px on a 1200px window,
   which is not the number Frederico asked for. He gave an exact value, so it
   is set exactly; 18px is comfortable on a phone too, where body text is 16. */
/* 8 Oct 2026, Georgia's pin #6: larger, ~40px light on desktop. Acumin has no
   Light face on this site (Thin 100-300 is the nearest), and Thin holds at
   this size; below 640px it steps up to Book like every other Thin line. */
.s-intro__lede { margin: 0; color: var(--ink);
  font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.5rem); line-height: 1.2; font-weight: 300; }
@media (max-width: 640px) { .s-intro__lede { font-weight: 400; } }

/* ---------- Footer ---------- */
/* The first-generation footer (.s-footer__inner, a single row of uppercase
   links) is gone — every page now carries the same __top/__cols/__base
   footer defined further down. Its .s-footer padding is removed with it:
   .s-footer__top and .s-footer__base each own their own padding, so an
   outer pad on the element was doubling the inset on every page. */
.s-footer__label { margin: 0; }

/* ====================================================================
   Extended sections — added when Demo 2 was chosen as the direction.
   Content is sourced from the original hand-built page (demo-1.html):
   real class names, real stats, real community programming. Nothing here
   asserts a fact that page did not already assert.
   Every colour is a token; the only easing is --ease.
   ==================================================================== */

.s-band + .s-band { border-top: 1px solid var(--rule); }
.s-band__eyebrow { margin: 0; }
/* Light (300) at 18px, per Frederico's review pins 4 and 5. Pinned on the
   pillars lede, which is the one band lede carrying .s-lede (28px); the rest
   already sat at --step-0 via .s-body and only gain the weight. Same equal
   specificity against .s-lede as .s-intro__lede above — build order decides. */
.s-band__lede { max-width: 52ch; margin: 0 0 clamp(2rem, 6vh, 3.5rem);
  color: var(--seven-muted); font-size: 1.125rem; font-weight: 300; }

/* ---- pillars ---- */
/* Tightened 8 Oct 2026 (Georgia #9): the section read too big. */

.s-pillars__band { padding-block: 0 clamp(3rem, 8vh, 5rem); }
@media (min-width: 40rem) { .s-pillars__list { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .s-pillars__list { grid-template-columns: repeat(4, 1fr); } }
.s-pillar__t { font-size: var(--step-1); font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; }
.s-pillar__d { margin: 0; font-size: var(--step--1); line-height: 1.65;
  color: var(--seven-muted); }

/* ---- list rows (classes, app) ---- */
.s-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px;
  background: var(--rule); border-block: 1px solid var(--rule); }
/* The number column's width and the row's column gap are shared with the
   rich-row body in page.css, which has to indent by exactly their sum to line
   up with the title. Kept as variables so the two cannot drift apart. */
.s-rows { --row-n: 3.5rem; --row-gap: 2rem; }

/* ---- stats ---- */

@media (min-width: 52rem) { .s-stats__list { grid-template-columns: repeat(4, 1fr); } }
/* Centred per Frederico's review pin 7. Set on the cell rather than the grid:
   the columns already divide the row evenly, so what he marked was the copy
   sitting left inside each one. */
.s-stat { text-align: center; }
.s-stat__n { font-size: min(var(--step-3), 14vw, 4rem); font-weight: 100;
  line-height: 1; letter-spacing: -.01em; }
.s-stat__l { margin: .6rem 0 0; color: var(--seven-muted); }

/* ---- two-up / three-up ---- */
.s-two { display: grid; gap: clamp(1.75rem, 5vw, 3.5rem); }
/* Community is down to one card since Run Club and Talks moved to Beyond the
   floor, and a three-up holding a single item reads as two that failed to
   load. Keyed on the child count rather than a second class: Webflow will not
   accept a class its own style registry does not know, so .s-one was rejected
   there and the repo would have drifted from the site. :has() reads the
   structure, which both share, and it self-corrects if a card is added back. */
.s-three:has(> :only-child) { grid-template-columns: minmax(0, 34rem); }

/* An offer with no photograph is a choice, not a broken image. The library is
   interiors: there is no frame for a run club or a workshop, which is the same
   reason .s-tile--text exists on Classes and why hiking.webp was not pressed
   into service here — it is one person on a clifftop, not a run, and 750px
   wide against a 1200px slot. Raised ground and a hairline so these read as a
   deliberate treatment beside the two that do carry pictures.
   Matched by id as well as class: Webflow drops the class from Blocks, and
   :has(img) keys off the structure, which it cannot drop. */
.s-offer--text,
#dose-padel .s-two > article:not(:has(img)) {
  background: var(--seven-raised-2); border: 1px solid var(--rule);
  border-radius: 10px; align-content: start;
  padding: clamp(1.4rem, 4vh, 2.1rem) clamp(1rem, 3vw, 1.6rem); }
/* The rule above the name belongs under a photograph, not inside a bordered
   card that already has one across its top. */
.s-offer--text .s-house__row,
#dose-padel .s-two > article:not(:has(img)) .s-house__row {
  border-top: 0; padding-top: 0; }

/* Dose and Padel. Two source frames of different shape - dose is 3:2, the padel
   crop is closer to 2.2:1 - so a fixed aspect-ratio normalises them and the two
   read as a pair rather than as two unrelated pictures.

   height: auto is load-bearing. The <img> tags carry width/height attributes for
   layout stability, and those map to a presentational height that wins over
   aspect-ratio while height is otherwise unset - so the cards rendered at the
   attribute's 800px and 550px and the pair did not match. */
/* Matched two ways, for the same reason the footer social row is. Webflow's
   builder keeps classes on Link elements and drops them from Blocks, Lists and
   Images - the <article> lost s-offer and the <img> lost s-offer__img on insert.
   The id survives (it is an attribute), so the second arm matches the section by
   that instead and the cards style correctly in both places. */
.s-offer,
#dose-padel .s-two > article,
#practice .s-two > article { display: grid; gap: 1rem; }
/* The campaign set on /membership is 3:2 at source and every frame is a
   standing figure, so it keeps 3:2 rather than taking .s-offer__img's 16/9 —
   a 16/9 crop of a 3:2 frame takes 11% off the height, which on a full-body
   shot comes off the head and the feet. Matched by id as well as class because
   Webflow drops the class from Images and Blocks; #dose-padel is here for the
   same reason. height: auto is not decoration: without it the img's height
   attribute wins as a presentational hint and beats aspect-ratio outright. */
#practice .s-two img { display: block; width: 100%; height: auto;
  aspect-ratio: 3 / 2; object-fit: cover; border-radius: 10px;
  background: var(--seven-raised-2); }

.s-offer__img,
#dose-padel .s-two img { display: block; width: 100%; height: auto;
  aspect-ratio: 16 / 9; object-fit: cover; border-radius: 10px;
  background: var(--seven-raised-2); }
@media (min-width: 52rem) { .s-two { grid-template-columns: repeat(2, 1fr); } }
.s-three { display: grid; gap: 1px; background: var(--rule);
  border-block: 1px solid var(--rule); }
@media (min-width: 52rem) { .s-three { grid-template-columns: repeat(3, 1fr); } }
.s-card { background: var(--shell); padding: clamp(1.5rem, 4vh, 2.25rem) clamp(1rem, 3vw, 1.75rem);
  display: flex; flex-direction: column; gap: .7rem; }
.s-card__when { color: var(--accent); }
.s-card__t, .s-house__t { margin: 0; font-size: var(--step-1); font-weight: 100;
  text-transform: uppercase; letter-spacing: .03em; }
.s-house__row { display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: .5rem 1rem;
  /* Frederico's review pin 8 asked for more space between the rule and the
     name under it. .9rem -> a clamp that roughly doubles it at desktop while
     staying tighter on small screens, where the two Houses stack and the extra
     air would otherwise read as a gap between unrelated blocks. */
  border-top: 1px solid var(--rule); padding-top: clamp(1.25rem, 3vh, 2rem);
  margin-bottom: .9rem; }
.s-house__t { font-size: var(--step-2); }

/* ---- retreats strip ---- */
.s-strip { background: var(--seven-raised-2); }
.s-strip__in { max-width: 52rem; }
.s-strip__t { margin: 0 0 1rem; font-size: min(var(--step-3), 11vw, 4.5rem);
  /* Bold, not UltraBlack, per Frederico's review pin 9 — the same move made on
     .s-display--black in type.css, kept in step so the two display weights on
     the page do not drift apart. */
  text-transform: uppercase; line-height: .95; font-weight: 700; letter-spacing: -.015em; }
.s-strip__cta { margin-top: 1.75rem; }

/* ---- ticks ---- */
.s-ticks { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem;
  align-content: start; }
.s-ticks__item { position: relative; padding-inline-start: 1.6rem; font-size: var(--step-0);
  line-height: 1.55; color: var(--seven-muted); }
.s-ticks__item::before { content: ""; position: absolute; left: 0; top: .62em;
  width: .6rem; height: 1px; background: var(--accent); }

/* ---- logo ----------------------------------------------------------- */
/* The wordmark ships at 560x76; these are the only two places it appears,
   so it is sized here rather than given a utility class. */
.s-mark__img { display: block; width: clamp(72px, 7vw, 104px); height: auto; }

/* ---- collage ---------------------------------------------------------
   Uniform portrait cards, each carrying its own call to action, with a
   solid panel that cross-fades in on hover or keyboard focus to reveal a
   description. Layout pattern follows the reference site; the images, the
   copy and the palette are SEVEN's.
   The description is always in the DOM (visually hidden, not display:none)
   so it is not hover-only for assistive tech.
   -------------------------------------------------------------------- */
.s-collage { padding: clamp(4rem, 12vh, 8rem) clamp(1rem, 4vw, 3rem);
  border-top: 1px solid var(--rule); }
.s-collage__intro { max-width: 48rem; margin: 0 auto clamp(2.5rem, 7vh, 4.5rem);
  text-align: center; }
.s-collage__h { margin: 0; font-weight: 100; text-transform: uppercase;
  font-size: min(var(--step-2), 7vw, 2.75rem); line-height: 1.15;
  letter-spacing: .04em; }

.s-collage__grid { list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(.75rem, 1.6vw, 1.25rem);
  grid-template-columns: 1fr; }
@media (min-width: 34rem) { .s-collage__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 62rem) { .s-collage__grid { grid-template-columns: repeat(3, 1fr); } }

.s-tile { position: relative; overflow: hidden; border-radius: 10px;
  aspect-ratio: 3 / 4; background: var(--seven-raised-2); isolation: isolate; }

.s-tile__img { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block; z-index: 0;
  transition: transform 1.2s var(--ease); }
/* Bottom-weighted scrim so the overlaid copy holds contrast whatever the
   frame does. Built from the token, so it follows a palette change. */
.s-tile::after { content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--seven-bg) 10%, transparent) 0%,
    color-mix(in srgb, var(--seven-bg) 34%, transparent) 48%,
    color-mix(in srgb, var(--seven-bg) 90%, transparent) 100%);
  transition: opacity var(--dur-ui) var(--ease); }

/* the panel that replaces the image on hover */
.s-tile__face--back { position: absolute; inset: 0; z-index: 2;
  background: var(--seven-raised-2);
  display: flex; align-items: center; justify-content: center;
  padding: clamp(1.25rem, 3vw, 2.25rem);
  opacity: 0; transition: opacity var(--dur-ui) var(--ease); }
.s-tile__desc { margin: 0; text-align: center; color: var(--seven-fg);
  font-size: var(--step-0); line-height: 1.6; max-width: 28ch; }

.s-tile__body { position: absolute; z-index: 3; left: 0; right: 0; bottom: 0;
  padding: clamp(1rem, 2.4vw, 1.6rem);
  display: flex; flex-direction: column; gap: .3rem; }
.s-tile__t { margin: 0; font-size: var(--step-1); font-weight: 100;
  text-transform: uppercase; letter-spacing: .03em; color: var(--seven-fg); }
.s-tile__label { color: var(--accent); margin: 0 0 .9rem; }

/* ---- tiles with no photograph ----
   Padel, running, walking and dance happen outside the club, and the library
   is interiors — there is no frame for them and inventing one is worse than
   admitting it. Six empty image boxes read as broken, so these tiles are
   typographic BY DESIGN: a raised ground, a hairline, the pillar at the top
   and the name at the foot. They are meant to look chosen, not failed. */
.s-tile--text { background: var(--seven-raised);
  border: 1px solid var(--rule); }
.s-tile--text::after { opacity: 0; }
.s-tile__body--text { top: 0; justify-content: space-between; }
.s-tile__body--text .s-tile__t { margin-top: auto; }
.s-tile__out { margin: 0; font-size: var(--step--1); letter-spacing: .24em;
  text-transform: uppercase; color: var(--accent); }

.s-tile__cta { display: flex; align-items: center; justify-content: center;
  min-height: 44px; padding: .7rem 1rem; text-decoration: none;
  border: 1px solid color-mix(in srgb, var(--seven-fg) 55%, transparent);
  color: var(--seven-fg); font-size: var(--step--1);
  letter-spacing: .24em; text-transform: uppercase;
  transition: background-color var(--dur-ui) var(--ease),
              color var(--dur-ui) var(--ease),
              border-color var(--dur-ui) var(--ease); }
.s-tile__cta:hover { background: var(--seven-fg); color: var(--seven-bg);
  border-color: var(--seven-fg); }
.s-tile__cta:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Reveal the panel on pointer hover and on keyboard focus alike. */
@media (hover: hover) {
  .s-tile:hover img { transform: scale(1.04); }
  .s-tile:hover .s-tile__face--back { opacity: 1; }
  .s-tile:hover::after { opacity: .25; }
}
.s-tile:focus-within .s-tile__face--back { opacity: 1; }
.s-tile:focus-within::after { opacity: .25; }

@media (prefers-reduced-motion: reduce) {
  .s-tile__img, .s-tile__face--back, .s-tile::after { transition: none; }
  .s-tile:hover img { transform: none; }
}

/* visually hidden, still announced */
.s-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/* An absolutely positioned child is clipped by an ancestor's overflow ONLY if
   that ancestor is its containing block. .s-sr is position: absolute, and
   schedule.js injects one into every day button and every Book link — neither
   of which was positioned — so on /timetable the hidden text resolved against
   a far ancestor, landed at the day strip's UNSCROLLED x of ~600px, and made
   the whole document 600px wide inside a 360px screen. The page could be
   panned 240px sideways, dragging the heading off screen to reveal nothing:
   .s-tt__cal's own overflow-x: auto never got the chance to clip it.
   Giving any parent of an .s-sr a containing block fixes it wherever the
   pattern is used, rather than one page at a time. position: relative with no
   offsets moves nothing and creates no stacking context on its own. */
:has(> .s-sr) { position: relative; }

/* ---- Instagram ------------------------------------------------------- */
/* The grid itself is SEVEN's own Elfsight widget — third-party markup we do
   not author and cannot restyle — so everything here frames it and then
   leaves it alone. min-height reserves the space it will fill, so the footer
   does not jump when the widget finally loads. */
.s-ig { border-top: 1px solid var(--rule);
  padding: clamp(3.5rem, 10vh, 6rem) clamp(1rem, 4vw, 3rem) clamp(2.5rem, 7vh, 4rem); }
.s-ig__head { max-width: 72rem; margin: 0 auto clamp(1.75rem, 5vh, 2.75rem);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 2rem; }
.s-ig__eyebrow { flex: 1 0 100%; margin: 0 0 .5rem; }
/* 38px per Frederico's review pin 11 — a literal 38px, not a cap. The cap
   form held 38px only above a ~778px viewport and shrank below it, which is
   not what he asked for. "@sevenwellness" measures ~266px at 38px, so it
   clears a 375px screen with the side padding on. The line-height is set here because Webflow's own
   stylesheet gives h2 a flat line-height: 36px, which this heading was
   inheriting — a 36px line box under a 38px glyph, and not ours to rely on. */
.s-ig__h { margin: 0; font-size: 2.375rem; font-weight: 100;
  line-height: 1.1; }
.s-ig__link { margin-left: auto; color: var(--accent); text-decoration: none;
  font-size: var(--step--1); letter-spacing: .24em; text-transform: uppercase;
  min-height: 44px; display: inline-flex; align-items: center;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-ui) var(--ease); }
.s-ig__link:hover { border-bottom-color: var(--accent); }
.s-ig__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.s-ig > [class^="elfsight-app-"] { max-width: 72rem; margin-inline: auto;
  min-height: 240px; }

/* ---- footer --------------------------------------------------------- */
.s-footer { border-top: 1px solid var(--rule); background: var(--seven-raised); }
.s-footer__top { padding: clamp(3rem, 9vh, 5rem) clamp(1rem, 4vw, 3rem);
  display: grid; gap: clamp(2rem, 6vw, 4rem); }
.s-footer__cols { display: grid; gap: 2rem clamp(1.25rem, 3vw, 2.5rem);
  /* minmax(0,1fr), not 1fr: a plain fr track refuses to shrink below its
     min-content, and hello@seven.club is one unbreakable string — that
     pushed the footer 6px past the viewport at 375px. */
  grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 44rem) { .s-footer__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* Two link columns and a wider third for the newsletter, addresses and
   socials. The history is worth keeping: this footer ran five ragged columns
   of 7/4/6/5/2, then three padded columns of seven. Padding them to seven
   squared the bottom edge and made it DENSER — 21 links, 16 of which the
   primary nav already carries on every page as real anchors. The footer now
   carries only what the header cannot: the shop, the corporate tail, contact,
   and the two links people scroll to the bottom to find. Nine in total.
   The guard is exact — three children and no fourth — so a footer that ships
   with a different count falls back to the two-column stack rather than
   rendering an empty track. */
@media (min-width: 64rem) {
  .s-footer__cols:has(> :nth-child(3)):not(:has(> :nth-child(4))) {
    grid-template-columns: repeat(2, minmax(0, 1fr)) 1.3fr; }
}

/* Below that breakpoint the grid is two equal columns, and the block lands in
   one of them: 161px at a 390px viewport, where "Read what we send" breaks
   mid-phrase, the Dubai address breaks after "Sheikh", and the four icons wrap
   3 + 1. It carries a heading, a sentence, a button, two addresses, two
   contacts and an icon row — more than a list of links — so it takes the full
   width rather than half of it. Both arms again: the class for the static
   build, the position for Webflow. */
@media (max-width: 63.999rem) {
  .s-footer__sign,
  .s-footer__cols > :nth-child(3) { grid-column: 1 / -1; }
}

/* No border-top. Five short rules stacked above five headings read as a row of
   blanks rather than as structure, and the .s-footer__top border already
   separates the footer from the page. The padding stays: it is what keeps the
   column heads off the rule above them when the grid wraps to two rows. */
.s-footer__col { padding-top: clamp(.9rem, 2vh, 1.25rem); }

/* ---- the close ------------------------------------------------------
   A footer that is only a link list has nothing to say, which is what was
   actually wrong with this one — not, as first diagnosed, that it repeated
   the header. Four bands now: the close, the columns, the pillars, the legal
   rule. Every rule below is written twice, once on its own class and once on
   a structural selector, because every class here is new and Webflow keeps
   only names its own style registry already knows. */
.s-close2, .s-footer > div:first-child:not(.s-footer__top) {
  padding: clamp(2.5rem, 6vh, 3.5rem) clamp(1rem, 4vw, 3rem) clamp(1.5rem, 4vh, 2.25rem); }
.s-close2__top, .s-footer > div:first-child:not(.s-footer__top) > div:first-child {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 2rem; flex-wrap: wrap; }
/* The V7 square, not the wordmark — the header already carries the wordmark,
   and running it twice on one page made the footer read as a second header.
   Sized on HEIGHT because it is a square-ish mark: driving a 251x192 glyph off
   a width clamp tuned for a 1400x191 lockup rendered it 178x24.
   aspect-ratio carries the file's own ratio rather than leaving it to the
   width/height attributes, because Webflow drops both when it renders an Image
   element — verified on the published page — and without an intrinsic ratio the
   row reserves no height until the mark arrives, then jumps. The structural arm
   is "the img inside the first band"; the only other marks in this footer are
   the social <svg>s, a different element name in a different band. */
.s-footer__mark,
.s-footer > div:first-child:not(.s-footer__top) img {
  height: clamp(44px, 4.8vw, 60px); width: auto; display: block;
  aspect-ratio: 1163 / 1471; }
.s-close2__acts, .s-footer > div:first-child:not(.s-footer__top) > div:first-child > div {
  display: flex; gap: .75rem; flex-wrap: wrap; }
/* The one filled button on the page. The footer is where the argument ends,
   so the membership action stops being an outline here. */
.s-cta--solid { background: var(--seven-fg); color: var(--seven-raised);
  border-color: var(--seven-fg); }
.s-cta--solid:hover { background: var(--accent); border-color: var(--accent);
  color: var(--seven-raised); }
/* Two paragraphs, not one line with two spans inside it. Webflow's HTML
   builder turned bare <span> children here into ##INLINE0##/##INLINE1##
   placeholder tokens and published them literally; block elements survive.
   The lines are addressed by document order, which is why they carry no
   margin of their own between them. The same 11vw guard .s-display carries:
   DELIBERATELY is the longest word in the set and overruns a narrow column
   without it. */
.s-close2__line,
.s-footer > div:first-child:not(.s-footer__top) > p:nth-of-type(1),
.s-footer > div:first-child:not(.s-footer__top) > p:nth-of-type(2) {
  margin: 0; line-height: .92;
  font-size: min(var(--step-3), 11vw); text-transform: uppercase; }
.s-close2__line--a, .s-footer > div:first-child:not(.s-footer__top) > p:nth-of-type(1) {
  margin-top: clamp(1.25rem, 3.5vh, 2rem); font-weight: 100; letter-spacing: .02em; }
.s-close2__line--b, .s-footer > div:first-child:not(.s-footer__top) > p:nth-of-type(2) {
  font-weight: 700; letter-spacing: -.018em; }

/* No rule between the close and the columns. Four bands each fenced by a
   hairline read as four stacked blocks rather than one footer; the close and
   the links belong to each other, so only the pillars and the legal bar keep
   a division. Nancy, 17 Sep: "why is footer 4 sections so ugly". */
.s-footer__top { padding-top: clamp(1.5rem, 4vh, 2.25rem); }

/* Socials are an icon row, not list rows - see tools/stamp-chrome.py. The mark
   inherits currentColor so it follows the same hover as every other footer
   link, and the 44px box is the touch target, not the 20px glyph.

   Matched two ways on purpose. The static build carries the class; Webflow
   does NOT — inserting this list through the component builder silently drops
   the class from a List element, because Webflow renders a list's class from
   its own styleNames and rejects a hand-set class attribute outright ("the
   operation could not be applied to the component map"). Link elements keep
   their classes, which is why only the <ul> lost its own and the icons stacked
   into a column on the first publish. The :has() arm matches the list by what
   is inside it, so the row survives either way.

   On Webflow the row is an HtmlEmbed carrying this exact markup, because the
   builder strips <use> AND <path> from an ordinary SVG element — the icons
   published as empty 20x20 boxes twice before that. An embed is the only
   element whose raw markup survives. Both arms below still apply to it: the
   embed's own <ul> keeps its class, so do not delete either one. */
.s-footer__social,
ul:has(> li > .s-footer__social-link) {
  list-style: none; margin: 1.1rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: .35rem; margin-left: -.6rem; }
.s-footer__social-link { display: inline-flex; align-items: center;
  justify-content: center; min-width: 44px; min-height: 44px;
  color: var(--seven-muted); text-decoration: none;
  transition: color .25s var(--ease); }
.s-footer__social-link:hover { color: var(--accent); }
.s-footer__social-link:focus-visible { outline: 2px solid var(--accent);
  outline-offset: 2px; border-radius: 4px; }
.s-footer__icon { width: 20px; height: 20px; display: block; fill: currentColor; }
@media (prefers-reduced-motion: reduce) { .s-footer__social-link { transition: none; } }
/* Webflow keeps a class only if its own style registry already knows the name.
   .s-footer__sign, __note, __sub-cta and __contact are new, so they are
   stripped on publish and every rule below would match nothing there. These
   arms key on position instead — the subscribe block is the third child of
   .s-footer__cols, which Webflow does keep — so the block holds its shape
   whether or not the classes survive. Same approach as #classes .s-rows a. */
.s-footer__cols > :nth-child(3) { display: grid; align-content: start; gap: 0; }
.s-footer__cols > :nth-child(3) > p:first-of-type { margin: 0 0 1.1rem;
  font-size: 0.75rem; line-height: 1.7; color: var(--seven-muted); max-width: 30ch; }
.s-footer__cols > :nth-child(3) > .s-cta { justify-self: start; margin-bottom: 1.6rem; }
/* The two House addresses lived here until 17 Sep. They are gone at Nancy's
   ask; the Houses page carries them, and the footer has the email and the
   phone. Their rules went with them rather than staying as dead arms. */
.s-footer__cols > :nth-child(3) > p:last-of-type { margin: 0; display: grid; gap: 0; }
.s-footer__cols > :nth-child(3) a { overflow-wrap: anywhere; }

/* ---- the fourth track: newsletter, addresses, socials ---- */
.s-footer__sign { display: grid; align-content: start; gap: 0; }
.s-footer__note { margin: 0 0 1.1rem; font-size: 0.75rem; line-height: 1.7;
  color: var(--seven-muted); max-width: 30ch; }
.s-footer__sub-cta { justify-self: start; margin-bottom: 1.6rem; }
.s-footer__contact { margin: 0; display: grid; gap: 0; }
/* overflow-wrap because hello@seven.club is one unbreakable string and this
   track is the narrowest thing holding it. */
.s-footer__contact a { overflow-wrap: anywhere; }

/* ---- the footer on a phone -------------------------------------------
   PLACEMENT MATTERS: this block must stay BELOW every base footer rule it
   overrides. A media query adds no specificity of its own, so while it sat
   higher up the file the base .s-footer__cols > :nth-child(3) > p:last-of-type
   matched at equal specificity, won on source order, and none of this applied.

   Measured at 320/360/375/390/414/430/480px on the published page. Nothing
   overflowed and nothing wrapped wrongly, but two things were wrong anyway:
   the two actions stacked at different widths — 262px against 184px, because
   flex lets each button be its own content width — which reads as a mistake
   rather than a stack; and the whole footer ran about 1400px, so a phone
   scrolls through more footer than most of the pages above it.

   The row rhythm is NOT what got tightened. Each link is min-height 44px
   because that is the touch target, and eight of them are 380px however they
   are spaced. What comes out is the padding around and between the bands,
   which was tuned by eye at desktop widths and simply carried down. */
@media (max-width: 34rem) {
  /* One column, so both actions are the same width and read as a pair. */
  .s-close2__acts, .s-footer > div:first-child:not(.s-footer__top) > div:first-child > div {
    display: grid; grid-template-columns: 1fr; gap: .6rem; width: 100%; }
  .s-close2, .s-footer > div:first-child:not(.s-footer__top) { padding-top: 2rem; padding-bottom: 1rem; }
  .s-close2__top, .s-footer > div:first-child:not(.s-footer__top) > div:first-child { gap: 1.25rem; }
  .s-close2__line--a, .s-footer > div:first-child:not(.s-footer__top) > p:nth-of-type(1) {
    margin-top: 1.25rem; }
  .s-footer__top { padding-top: 1rem; padding-bottom: 1rem; }
  .s-footer__cols { gap: 1.5rem 1.25rem; }
  .s-footer__col { padding-top: .5rem; }
  /* The subscribe block is the full width down here, so its own head does
     not need the clearance a column head needs beside a neighbour. */
  .s-footer__cols > :nth-child(3) { padding-top: .5rem; }

  /* Email and phone sit side by side rather than stacked. They were a grid of
     two rows, and at 44px each that is 88px spent on two short strings that
     fit on one line together at 320px. Still 44px tall, so still tappable. */
  .s-footer__cols > :nth-child(3) > p:last-of-type {
    display: flex; flex-wrap: wrap; gap: 0 1.25rem; }
  /* The close reads at a smaller size down here. min() already caps it at
     11vw, which is 41px at 375 — this brings the two lines closer together
     rather than shrinking them, since the cap is doing the sizing. */
  .s-close2__line, .s-footer > div:first-child:not(.s-footer__top) > p:nth-of-type(1),
  .s-footer > div:first-child:not(.s-footer__top) > p:nth-of-type(2) { line-height: .88; }
  .s-footer__list { gap: 0; }
  /* The mark and the actions are the close; the space between them was set
     for a row, and they are a stack here. */
  .s-close2__top, .s-footer > div:first-child:not(.s-footer__top) > div:first-child { gap: .9rem; }
  .s-footer__cols > :nth-child(3) > .s-cta { margin-bottom: 1rem; }
  .s-footer__cols > :nth-child(3) > p:first-of-type { margin-bottom: .8rem; }
  .s-footer__base { padding-top: 1rem; padding-bottom: 1rem; }

  /* ---- the folded columns ----
     One track, not two. Folded, the columns are a 44px header each, and two
     44px headers side by side read as a broken layout rather than as a list
     you can open. Full width makes them rows. The subscribe block takes the
     same track and keeps its own shape. */
  .s-footer__cols { grid-template-columns: minmax(0, 1fr); }
  /* The control is the heading. all: unset strips the button chrome, then
     the .s-footer__h rules above it supply the type again — so the folded and
     unfolded headings are the same object, not two things kept in sync. */
  .s-footer__toggle {
    all: unset; box-sizing: border-box;
    display: flex; align-items: center; justify-content: space-between;
    width: 100%; min-height: 44px; cursor: pointer;
    font: inherit; color: inherit; letter-spacing: inherit;
    text-transform: inherit; }
  .s-footer__toggle:focus-visible { outline: 2px solid var(--accent);
    outline-offset: 3px; }
  /* A chevron from two borders rather than a glyph: it has no font to load,
     it inherits currentColor, and it rotates. */
  .s-footer__toggle::after {
    content: ""; flex: 0 0 auto; width: 7px; height: 7px; margin-left: 1rem;
    border-right: 1px solid currentColor; border-bottom: 1px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
    transition: transform var(--dur-ui) var(--ease); }
  .s-footer__toggle[aria-expanded="true"]::after {
    transform: translateY(2px) rotate(-135deg); }
  /* Keyed on the attribute the button already owns, so the state lives in one
     place and the CSS reads it rather than a class shadowing it. */
  .s-footer__col:has(.s-footer__toggle[aria-expanded="false"]) .s-footer__list {
    display: none; }
  /* The heading carries the fold, so it stops needing the clearance it had
     as a column head. */
  .s-footer__col:has(.s-footer__toggle) { padding-top: 0; }
  .s-footer__col:has(.s-footer__toggle) .s-footer__h { margin-bottom: 0; }
  .s-footer__col:has(.s-footer__toggle[aria-expanded="true"]) .s-footer__h {
    margin-bottom: .5rem; }
}
@media (max-width: 34rem) and (prefers-reduced-motion: reduce) {
  .s-footer__toggle::after { transition: none; }
}

.s-footer__h { margin: 0 0 1rem; font-size: var(--step--1); letter-spacing: .24em;
  text-transform: uppercase; color: var(--seven-muted); font-weight: 400; }
.s-footer__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .2rem; }
/* 12px per Frederico's review pin 12 ("all the links"), down from --step-0's
   18px. Literal rather than a token: 12px sits below --step--1's 12.48px floor,
   so no step on the scale reaches it. The min-height rules below are untouched
   — the touch target stays 44px (32px for fine pointers) regardless of how
   small the label gets, which is the measurement that actually matters here. */
.s-footer__link { color: var(--seven-fg); text-decoration: none;
  overflow-wrap: anywhere;
  font-size: 0.75rem; display: inline-flex; align-items: center; min-height: 44px;
  transition: color var(--dur-ui) var(--ease); }
/* 44px per link is a touch target, not a reading rhythm: eight of them made
   the footer ~500px tall with a ragged void under the shorter columns. Where
   the pointer is fine, tighten to a typographic rhythm; coarse pointers keep
   the full 44px. */
@media (pointer: fine) {
  .s-footer__link { min-height: 32px; }
  .s-footer__list { gap: .1rem; }
}
.s-footer__link:hover { color: var(--accent); }
.s-footer__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.s-footer__base { border-top: 1px solid var(--rule);
  padding: 1.25rem clamp(1rem, 4vw, 3rem);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .75rem 1.5rem; }
/* No legal nav here any more: Legal, Privacy Policy and House Rules moved
   into The House column on 17 Sep. The bar carries the copyright and the two
   Houses, and space-between puts one at each end without further help. */

/* Spaces are shown, not sold — the tiles carry no call to action, so the
   description panel is revealed by hover, by keyboard focus on the tile
   itself, and by tap. Without tabindex the tile would be unreachable by
   keyboard now that the link inside it is gone. */
.s-tile { cursor: default; }
.s-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.s-tile:focus-visible .s-tile__face--back,
.s-tile.is-open .s-tile__face--back { opacity: 1; }
.s-tile:focus-visible::after, .s-tile.is-open::after { opacity: .25; }

/* ====================================================================
   Home v2 — Georgia Parker's Figma review, 8 Oct 2026 (26 pins).
   Every class below is new, so none is owned by a Webflow style and the
   hosted sheet decides them outright. Images are styled structurally
   (".s-cat img") because Webflow drops the class from Image elements.
   Ground: Nancy's call later the same day, the old site's warm brown across
   the whole home page and NO white blocks (Figma frame 269:2, "Home v8").
   These are the html.theme-dark values from assets/site.css, the palette
   the live seven.club already uses. Done by re-pointing the tokens, scoped
   to the home page by its one unique section, so every rule above follows
   and no inner page changes.
   ==================================================================== */
:root:has(.s-cats) {
  --seven-bg: #17140F;
  --seven-raised: #17140F;
  /* Placeholder tiles and image wells: the ground lifted toward the ink,
     never a new hue. */
  --seven-raised-2: color-mix(in srgb, #17140F 90%, #EFE9DE);
  --seven-fg: #EFE9DE;
  --seven-muted: #B4A99B;
  --seven-accent: #C5B9AC;
  --seven-rule: rgba(239, 233, 222, .17);
}
/* The bar inverts against whatever is behind it; over a warm ground that
   tints it, so on this page it draws normally. */
:root:has(.s-cats) .s-nav { mix-blend-mode: normal; }
/* Georgia #23: the close is kept, cut shorter and condensed. */
:root:has(.s-cats) .s-footer > div:first-child:not(.s-footer__top) {
  padding-top: clamp(1.75rem, 4vh, 2.5rem); padding-bottom: clamp(1rem, 2.5vh, 1.5rem); }
:root:has(.s-cats) .s-footer > div:first-child:not(.s-footer__top) > p {
  font-size: min(var(--step-2), 9vw, 3rem); }
:root:has(.s-cats) .s-footer > div:first-child:not(.s-footer__top) > p:nth-of-type(1) {
  margin-top: clamp(.75rem, 2vh, 1.25rem); }

/* ---- nav: links sit beside the wordmark, the one action on the right ---- */
/* Keyed on the CTA's presence so the inner pages, whose header has no CTA,
   keep their space-between layout untouched. */
.s-nav__list:has(~ .s-nav__cta) { margin-right: auto;
  margin-left: clamp(1.25rem, 4vw, 3.5rem); }
@media (max-width: 640px) { .s-nav__list:has(~ .s-nav__cta) { margin-left: 0; } }

/* ---- placeholder tile: an honest gap, never a borrowed photograph ---- */
.s-ph { position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; background: var(--seven-raised-2);
  border: 1px dashed color-mix(in srgb, var(--seven-muted) 45%, transparent); }
.s-ph__t { margin: 0; font-size: var(--step--1); letter-spacing: .24em;
  text-transform: uppercase; color: var(--seven-muted); }

/* ---- one membership: a single sideways row ---- */
.s-cats { padding: clamp(3.5rem, 9vh, 6rem) 0;
  border-top: 1px solid var(--rule); }
/* Georgia's exact wording and case, one line on a desktop. */
.s-cats__h { margin: 0 clamp(1rem, 4vw, 3rem) clamp(2rem, 5vh, 3rem);
  font-weight: 900; color: var(--seven-fg);
  font-size: clamp(1.9rem, 4.4vw, 4.25rem); line-height: 1;
  letter-spacing: -.01em; }
.s-cats__row { list-style: none; margin: 0; padding: 0 clamp(1rem, 4vw, 3rem) .75rem;
  display: grid; grid-auto-flow: column; gap: clamp(.6rem, 1.2vw, 1rem);
  /* 5.5 tiles across on a desktop: the half tile says "there is more". */
  grid-auto-columns: calc((100% - 4.5 * clamp(.6rem, 1.2vw, 1rem)) / 5.5);
  overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: clamp(1rem, 4vw, 3rem);
  scrollbar-width: thin; scrollbar-color: var(--rule) transparent; }
.s-cats__row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
@media (max-width: 62rem) { .s-cats__row { grid-auto-columns: 30%; } }
@media (max-width: 34rem) { .s-cats__row { grid-auto-columns: 62%; } }
.s-cat { position: relative; overflow: hidden; border-radius: 10px;
  aspect-ratio: 3 / 4; background: var(--seven-raised-2); scroll-snap-align: start; }
.s-cat img { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block; }
.s-cat::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 45%,
    color-mix(in srgb, var(--seven-bg) 85%, transparent)); }
.s-cat__t { position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; margin: 0;
  padding: clamp(.8rem, 1.6vw, 1.2rem); color: var(--seven-fg);
  font-size: var(--step--1); font-weight: 700; letter-spacing: .2em;
  text-transform: uppercase; }

/* ---- stats ---- */
.s-facts { background: var(--seven-bg); color: var(--seven-fg);
  border-top: 1px solid var(--rule);
  padding: clamp(3rem, 8vh, 5rem) clamp(1rem, 4vw, 3rem); }
.s-facts__list { margin: 0; display: grid; gap: clamp(1.75rem, 5vw, 3rem);
  grid-template-columns: repeat(2, 1fr); }
@media (min-width: 52rem) { .s-facts__list { grid-template-columns: repeat(5, 1fr); } }
@media (max-width: 51.999rem) { .s-fact:last-child:nth-child(odd) { grid-column: 1 / -1; } }
.s-fact { text-align: center; }
.s-fact__n { font-size: min(var(--step-3), 14vw, 4rem); font-weight: 100;
  line-height: 1; letter-spacing: -.01em; color: var(--seven-fg); }
.s-fact__l { margin: .6rem 0 0; font-size: var(--step--1); letter-spacing: .34em;
  text-transform: uppercase; color: var(--seven-muted); }

/* ---- timetable: photo collage ---- */
.s-tt2 { background: var(--seven-bg); color: var(--seven-fg);
  padding: clamp(3rem, 8vh, 5rem) clamp(1rem, 4vw, 3rem) clamp(3.5rem, 9vh, 6rem);
  border-top: 1px solid var(--rule); }
.s-tt2__head { max-width: 60rem; margin: 0 0 clamp(2rem, 5vh, 3rem); }
.s-tt2__h { margin: 0 0 1rem; font-weight: 900; text-transform: uppercase;
  font-size: min(var(--step-3), 10vw, 5rem); line-height: .95; letter-spacing: -.01em;
  color: var(--seven-fg); }
.s-tt2__lede { margin: 0; font-size: var(--step-1); line-height: 1.35; max-width: 40ch;
  color: var(--seven-muted); }
.s-mosaic { display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(.6rem, 1.2vw, 1rem); align-items: start; }
.s-mosaic__col { display: grid; gap: clamp(.6rem, 1.2vw, 1rem); }
.s-shot { position: relative; overflow: hidden; border-radius: 10px;
  background: var(--seven-raised-2); aspect-ratio: 1 / 1; }
/* Mixed heights, but every column sums to the same height
   (1 + 9/16 + 3/4), so the bottoms line up. */
.s-mosaic__col:nth-child(1) > :nth-child(1),
.s-mosaic__col:nth-child(2) > :nth-child(2),
.s-mosaic__col:nth-child(3) > :nth-child(2) { aspect-ratio: 1 / 1; }
.s-mosaic__col:nth-child(1) > :nth-child(2),
.s-mosaic__col:nth-child(2) > :nth-child(1),
.s-mosaic__col:nth-child(3) > :nth-child(3) { aspect-ratio: 16 / 9; }
.s-mosaic__col:nth-child(1) > :nth-child(3),
.s-mosaic__col:nth-child(2) > :nth-child(3),
.s-mosaic__col:nth-child(3) > :nth-child(1) { aspect-ratio: 4 / 3; }
.s-shot img { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block; }
.s-shot::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 50%,
    color-mix(in srgb, var(--seven-bg) 80%, transparent)); }
.s-shot__t { position: absolute; z-index: 1; left: 0; bottom: 0; margin: 0;
  padding: clamp(.8rem, 1.6vw, 1.2rem); color: var(--seven-fg);
  font-size: var(--step-0); font-weight: 700; letter-spacing: .02em; }
/* A phone gets two columns and one shape; three columns of 105px tiles carry no picture. */
@media (max-width: 40rem) {
  .s-mosaic { grid-template-columns: repeat(2, 1fr); }
  .s-mosaic__col { display: contents; }
  .s-mosaic .s-mosaic__col > .s-shot { aspect-ratio: 4 / 5; }
}

/* ---- partners: dark, one slow strip of wordmarks ---- */
.s-partners { padding: clamp(3rem, 8vh, 5rem) 0; border-top: 1px solid var(--rule); }
.s-partners__head { display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 1rem 2rem;
  margin: 0 clamp(1rem, 4vw, 3rem) clamp(1.75rem, 4vh, 2.5rem); }
.s-partners__h { margin: 0; font-weight: 900; text-transform: uppercase;
  font-size: min(var(--step-2), 9vw, 3rem); line-height: 1; color: var(--seven-fg); }
.s-marquee { overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent); }
.s-marquee__track { display: flex; width: max-content;
  animation: s-marquee 60s linear infinite; }
.s-marquee:hover .s-marquee__track { animation-play-state: paused; }
.s-marquee__set { display: flex; align-items: center; flex: none;
  gap: clamp(2.5rem, 5vw, 4.5rem); padding-right: clamp(2.5rem, 5vw, 4.5rem); }
.s-wordmark { margin: 0; white-space: nowrap; font-size: var(--step-1);
  font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--seven-muted); }
@keyframes s-marquee { to { transform: translateX(-50%); } }
/* Reduced motion: no movement at all, the duplicate set goes, and the one
   real set wraps into a still, centred block. */
@media (prefers-reduced-motion: reduce) {
  .s-marquee { mask-image: none; -webkit-mask-image: none;
    padding-inline: clamp(1rem, 4vw, 3rem); }
  .s-marquee__track { animation: none; width: auto; }
  .s-marquee__set { flex-wrap: wrap; justify-content: center; row-gap: 1rem;
    flex: 1 1 auto; padding-right: 0; }
  .s-marquee__set[aria-hidden="true"] { display: none; }
}

/* ---- the two actions ---- */
.s-ctaband { display: flex; flex-wrap: wrap; justify-content: center; gap: .75rem 1rem;
  padding: clamp(2.5rem, 7vh, 4rem) clamp(1rem, 4vw, 3rem);
  border-top: 1px solid var(--rule); }
@media (max-width: 34rem) { .s-ctaband > .s-cta { flex: 1 1 100%; justify-content: center; } }

/* ---- instagram banner ---- */
.s-ig2 { border-top: 1px solid var(--rule);
  padding: clamp(3rem, 8vh, 5rem) clamp(1rem, 4vw, 3rem); }
.s-ig2__head { display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: .5rem 2rem; margin-bottom: clamp(1.5rem, 4vh, 2.25rem); }
.s-ig2__h { margin: 0; font-weight: 900; text-transform: uppercase; color: var(--seven-fg);
  font-size: min(var(--step-3), 10vw, 4.5rem); line-height: .95; }
.s-ig2__handle { margin: 0; color: var(--accent); font-size: var(--step-0); letter-spacing: .12em; }
.s-ig2__row { list-style: none; margin: 0; padding: 0; display: grid;
  grid-template-columns: repeat(6, 1fr); gap: clamp(.4rem, 1vw, .75rem); }
@media (max-width: 52rem) { .s-ig2__row { grid-template-columns: repeat(3, 1fr); } }
.s-ig2__row li { aspect-ratio: 1 / 1; overflow: hidden; border-radius: 6px;
  background: var(--seven-raised-2); }
.s-ig2__row a { display: block; width: 100%; height: 100%; }
.s-ig2__row img { width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 1.2s var(--ease), opacity var(--dur-ui) var(--ease); }
@media (hover: hover) { .s-ig2__row a:hover img { transform: scale(1.04); opacity: .85; } }

/* ---- footer v2: four columns, legal in the bar, the mark across the foot ---- */
@media (min-width: 64rem) {
  .s-footer__cols:has(> :nth-child(4)):not(:has(> :nth-child(5))) {
    grid-template-columns: repeat(2, minmax(0, 1fr)) 1.4fr minmax(0, 1fr); }
}
@media (max-width: 63.999rem) {
  .s-footer__cols:has(> :nth-child(4)) > :nth-child(4) { grid-column: 1 / -1; }
}
.s-footer__line { margin: 0; display: flex; flex-wrap: wrap; gap: 0 1.5rem; }
/* The contact line moved to its own column, so the note is now the last
   paragraph in this one and would lose its gap above Subscribe. */
.s-footer__cols > :nth-child(3) > p:first-of-type:last-of-type { margin-bottom: 1.1rem; }
.s-footer__big { padding: clamp(1rem, 3vw, 2rem) clamp(1rem, 4vw, 3rem) clamp(1.5rem, 4vw, 3rem); }
.s-footer__big img { display: block; width: 100%; height: auto; aspect-ratio: 1400 / 191; opacity: .95; }
/* src/page.css — shared chrome for the inner pages under club/.
   Composes with tokens.css, type.css, motion.css and home.css; adds only
   what a subpage needs that the home page does not. Every colour is a
   token and the only easing is --ease. */

/* ---- page hero ------------------------------------------------------ */
.p-hero { position: relative; padding: clamp(7rem, 18vh, 11rem) clamp(1rem, 4vw, 3rem)
  clamp(3rem, 9vh, 5rem); border-bottom: 1px solid var(--rule); overflow: hidden; }
.p-hero--image { min-height: 62svh; display: flex; align-items: flex-end; }
.p-hero__img { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0; }
.p-hero::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.p-hero--image::after { background: linear-gradient(180deg,
    color-mix(in srgb, var(--seven-bg) 60%, transparent) 0%,
    color-mix(in srgb, var(--seven-bg) 30%, transparent) 45%,
    color-mix(in srgb, var(--seven-bg) 92%, transparent) 100%); }
.p-hero__in { position: relative; z-index: 2; width: 100%; max-width: 72rem; margin-inline: auto; }
/* The hero eyebrow stays --seven-muted. It was briefly changed to --seven-fg on
   a measurement that said 2.98-3.06:1 against a 4.5:1 floor; that measurement was
   wrong. .s-eyebrow is a full-width block whose text fills ~200px of 1152, so
   sampling its BOUNDING BOX reads ~80% backdrop the glyphs never touch — and the
   99th percentile of that is the bright part of the photograph, nowhere near the
   words. Measured at the glyph rects (Range.getClientRects) it is 7.23:1 on
   classes and 4.81:1 on seven-plus. It passes. Measure glyphs, not blocks. */
.p-hero__h { margin: .75rem 0 0; font-size: min(var(--step-3), 12vw, 5rem);
  font-weight: 100; text-transform: uppercase; letter-spacing: .01em; line-height: 1; }
.p-hero__lede { margin: clamp(1.25rem, 4vh, 2rem) 0 0; max-width: 46ch;
  color: var(--seven-muted); }

/* ---- generic page section ------------------------------------------- */
.p-sec { padding: clamp(3.5rem, 10vh, 7rem) clamp(1rem, 4vw, 3rem); }
.p-sec + .p-sec { border-top: 1px solid var(--rule); }
.p-in { max-width: 72rem; margin-inline: auto; }
.p-head { display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: .75rem 2rem;
  margin-bottom: clamp(1.75rem, 5vh, 3rem); }
.p-head__eyebrow { margin: 0; }

/* ---- detail rows ----------------------------------------------------- */
.p-detail { display: grid; gap: 1px; background: var(--rule);
  border-block: 1px solid var(--rule); margin: 0; }
@media (min-width: 46rem) { .p-detail { grid-template-columns: repeat(2, 1fr); } }
.p-detail__row { background: var(--shell); padding: clamp(1.25rem, 3.5vh, 1.9rem) 0; }
@media (min-width: 46rem) { .p-detail__row { padding-inline: 0 clamp(1rem, 3vw, 2rem); } }
.p-detail__t { font-size: var(--step--1); letter-spacing: .24em; text-transform: uppercase;
  color: var(--seven-muted); margin-bottom: .5rem; }
.p-detail__d { margin: 0; font-size: var(--step-0); line-height: 1.6; }

/* ---- back link ------------------------------------------------------- */
.p-back { display: inline-flex; align-items: center; gap: .6rem; min-height: 44px;
  color: var(--seven-muted); text-decoration: none; font-size: var(--step--1);
  letter-spacing: .24em; text-transform: uppercase;
  transition: color var(--dur-ui) var(--ease); }
.p-back:hover { color: var(--seven-fg); }
.p-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---- form ------------------------------------------------------------ */
.p-form { display: grid; gap: clamp(1.1rem, 3vh, 1.6rem); max-width: 34rem; }
.p-field { display: grid; gap: .15rem; }
.p-field__label { font-size: var(--step--1); letter-spacing: .18em;
  text-transform: uppercase; color: var(--seven-muted); }
/* Underline fields, not boxes — matching members.seven.club, which is the
   product surface this form hands over to. Keeping the two consistent means
   a member moving from the marketing site to the portal sees one system. */
.p-field__input, .p-field__select, .p-field__textarea {
  font: inherit; font-size: var(--step-0); color: var(--seven-fg);
  background: transparent; border: 0; border-bottom: 1px solid var(--rule);
  /* Tight to the label: with no box, generous padding reads as dead space
     rather than as a field. 44px min-height still holds the touch target. */
  border-radius: 0; padding: .45rem .25rem; min-height: 44px; width: 100%;
  transition: border-color var(--dur-ui) var(--ease); }
.p-field__select { padding-inline-start: 0; }
.p-field__textarea { min-height: 8rem; resize: vertical;
  border: 1px solid var(--rule); padding: .85rem 1rem; }
.p-field__input:hover, .p-field__select:hover, .p-field__textarea:hover { border-bottom-color: var(--accent); }
.p-field__input:focus-visible, .p-field__select:focus-visible, .p-field__textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 4px; border-bottom-color: var(--accent); }
.p-field__input:user-invalid, .p-field__textarea:user-invalid { border-bottom-color: var(--seven-accent-2); }
.p-hint { font-size: var(--step--1); color: var(--seven-muted); line-height: 1.5; margin: 0; }
.p-note { margin: clamp(1.25rem, 4vh, 2rem) 0 0; padding: 1rem 1.1rem;
  border: 1px solid var(--rule); border-inline-start: 2px solid var(--accent);
  background: var(--seven-raised); color: var(--seven-muted);
  font-size: var(--step--1); line-height: 1.6; max-width: 34rem; }
.p-submit { justify-self: start; }

/* The newsletter form adds three things the membership form did not need. */
/* A honeypot, positioned off-screen rather than display:none — a bot that
   reads computed styles skips hidden fields, and skipping it defeats the
   trap. The enquiry endpoint requires this field: a non-empty value there
   makes it return a clean 200 and drop the submission on purpose. */
.p-field--trap { position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; }
.p-consent { display: flex; align-items: flex-start; gap: .6rem; margin: 0;
  color: var(--seven-muted); font-size: var(--step--1); line-height: 1.5; }
.p-consent__input { flex: none; width: 18px; height: 18px; margin-top: .2rem;
  accent-color: var(--accent); }
.p-consent__link { color: var(--accent); }
.p-form__note { margin: 0; color: var(--seven-muted);
  font-size: var(--step--1); line-height: 1.6; max-width: 44ch; }
.p-form__note-link { color: var(--accent); }

/* Webflow-only repair. Building the sign-up form headlessly turns each input
   into Webflow's own FormTextInput, and Webflow hard-defaults its placeholder
   to "Example text" — `placeholder` is a reserved attribute its API refuses to
   write, and no placeholder setting is exposed, so it cannot be cleared at
   source. `.w-input` and `.w-checkbox-input` exist only in Webflow's output,
   so these two rules are inert on the static build and do not mask a
   placeholder we authored ourselves. Every field still has a real <label>
   tied by `for`, which is what the field is actually named by. */
.p-field .w-input::placeholder { color: transparent; }
.p-consent .w-checkbox-input { margin: .2rem 0 0; }

/* ---- timetable ------------------------------------------------------- */
.s-tt__bar { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1.25rem;
  margin-bottom: clamp(1.5rem, 4vh, 2.25rem); }
.s-tt__label { font-size: var(--step--1); letter-spacing: .24em;
  text-transform: uppercase; color: var(--seven-muted); }
.s-tt__select { font: inherit; font-size: var(--step-0); color: var(--seven-fg);
  background: var(--seven-raised); border: 1px solid var(--rule); border-radius: 2px;
  padding: .6rem .9rem; min-height: 44px; }
.s-tt__select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- the day strip ----
   A horizontal row of days you pick between, rather than the whole week
   stacked. It scrolls on narrow screens instead of wrapping, so the shape
   stays the same at every width and the eye keeps its place along the row. */
.s-tt__cal { display: flex; gap: .5rem; margin: 0 0 clamp(1.5rem, 4vh, 2.25rem);
  overflow-x: auto; scroll-snap-type: x proximity;
  padding-bottom: .5rem; scrollbar-width: thin; }
.s-tt__cal::-webkit-scrollbar { height: 3px; }
.s-tt__cal::-webkit-scrollbar-thumb { background: var(--rule); }

.s-tt__cday { scroll-snap-align: start; flex: 0 0 auto;
  display: grid; justify-items: center; row-gap: .2rem;
  min-width: 4.4rem; min-height: 44px; padding: .7rem .6rem .6rem;
  font: inherit; cursor: pointer; color: var(--seven-muted);
  background: var(--seven-raised); border: 1px solid var(--rule);
  border-radius: 8px;
  transition: background-color var(--dur-ui) var(--ease),
              border-color var(--dur-ui) var(--ease),
              color var(--dur-ui) var(--ease); }
.s-tt__cday:hover { color: var(--seven-fg); border-color: var(--accent); }
.s-tt__cday:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.s-tt__cdow { font-size: var(--step--1); letter-spacing: .18em;
  text-transform: uppercase; }
.s-tt__cnum { font-size: var(--step-1); font-weight: 100; line-height: 1;
  color: var(--seven-fg); }
.s-tt__ccount { font-size: calc(var(--step--1) * .88); letter-spacing: .08em;
  color: var(--accent); }

/* Today gets a mark, the SELECTED day gets the fill — they are different
   things and on most visits they are different days. */
.s-tt__cday.is-today .s-tt__cdow { color: var(--accent); }
.s-tt__cday.is-on { background: var(--seven-fg); border-color: var(--seven-fg);
  color: var(--seven-bg); }
.s-tt__cday.is-on .s-tt__cnum,
.s-tt__cday.is-on .s-tt__ccount,
.s-tt__cday.is-on .s-tt__cdow { color: var(--seven-bg); }

.s-tt__panel { min-height: 12rem; }

@media (prefers-reduced-motion: reduce) {
  .s-tt__cal { scroll-behavior: auto; }
  .s-tt__cday { transition: none; }
}

.s-tt__days { display: grid; gap: clamp(1.75rem, 5vh, 2.75rem); }
.s-tt__dayname { margin: 0 0 .75rem; font-size: var(--step--1); letter-spacing: .24em;
  text-transform: uppercase; color: var(--accent); font-weight: 400; }
.s-tt__rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px;
  background: var(--rule); border-block: 1px solid var(--rule); }
.s-tt__row { background: var(--shell); display: flex; flex-wrap: wrap;
  align-items: baseline; gap: .35rem 1.5rem; padding: clamp(.9rem, 2.5vh, 1.3rem) 0; }
.s-tt__time { font-size: var(--step--1); letter-spacing: .1em; color: var(--seven-muted);
  min-width: 8.5rem; font-variant-numeric: tabular-nums; }
.s-tt__name { font-size: var(--step-1); font-weight: 100; text-transform: uppercase;
  letter-spacing: .03em; flex: 1 1 13rem; }
.s-tt__cat, .s-tt__house { font-size: var(--step--1); letter-spacing: .12em;
  text-transform: uppercase; color: var(--seven-muted); }
.s-tt__house { min-width: 5rem; }
.s-tt__empty { color: var(--seven-muted); font-size: var(--step-0); margin: 0; }
.s-tt__note { margin: clamp(1.5rem, 4vh, 2.25rem) 0 0; padding: 1rem 1.1rem;
  border: 1px solid var(--rule); border-inline-start: 2px solid var(--accent);
  background: var(--seven-raised); color: var(--seven-muted);
  font-size: var(--step--1); line-height: 1.6; max-width: 52rem; }
.s-tt__coach { font-size: var(--step--1); color: var(--seven-muted);
  letter-spacing: .06em; min-width: 6rem; }
.s-tt__book { font-size: var(--step--1); letter-spacing: .24em; text-transform: uppercase;
  color: var(--accent); text-decoration: none; min-height: 44px;
  display: inline-flex; align-items: center; margin-inline-start: auto;
  transition: color var(--dur-ui) var(--ease); }
.s-tt__book:hover { color: var(--seven-fg); }
.s-tt__book:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.s-tt__yours { display: block; font-size: calc(var(--step--1) * .92); color: var(--accent);
  letter-spacing: .06em; margin-top: .2rem; opacity: .85; }

/* ---- right-to-left (Arabic) ------------------------------------------
   The layout uses logical properties throughout, so most of it mirrors on
   its own. These are the few places that pin to a physical edge and have to
   be flipped by hand. Arabic also needs a slightly larger size and looser
   leading than Acumin's Latin at the same nominal value. */
[dir="rtl"] .s-tt__book { margin-inline-start: auto; margin-inline-end: 0; }
[dir="rtl"] .p-note { border-inline-start: 0; border-inline-end: 2px solid var(--accent); }
[dir="rtl"] .s-ticks__item { padding-inline-start: 1.6rem; }
[dir="rtl"] .s-ticks__item::before { left: auto; right: 0; }
[dir="rtl"] .s-skip:focus { left: auto; right: .75rem; }
[dir="rtl"] .p-back { flex-direction: row-reverse; }

/* Latin display faces set uppercase with tight tracking; neither applies to
   Arabic, where it damages legibility and joins. */
:lang(ar) .s-display, :lang(ar) .p-hero__h, :lang(ar) .s-collage__h,
:lang(ar) .s-eyebrow, :lang(ar) .s-label, :lang(ar) .s-tt__name {
  text-transform: none; letter-spacing: normal; }
:lang(ar) { line-height: 1.8; }
:lang(ar) .s-body, :lang(ar) .s-lede { font-size: calc(var(--step-0) * 1.06); }

/* ---- language switcher ------------------------------------------------ */
/* Rich rows carry a description under the name, so they stack rather than
   sit on one line.
   These are a GRID, not wrapped flex, and the reason is worth keeping. As
   flex the body was `flex: 1 1 100%` with `max-width: 68ch`, and a flex item's
   line-breaking is decided on its hypothetical size AFTER max-width clamps it.
   So the body's size was 68ch, not 100% — and whenever the title was short
   enough to leave 68ch free, the body stayed on line one instead of wrapping.
   The same component rendered two different layouts depending on how long its
   title happened to be: "Wellness services on demand" stacked, "Manage" did
   not. A grid places the body on its own row by declaration, so no length of
   title can change the layout. */
.s-row--rich { display: grid; align-items: baseline;
  grid-template-columns: var(--row-n) 1fr auto;
  column-gap: var(--row-gap); row-gap: .4rem; }
/* Carries its own type rather than relying on a second class. Webflow cannot
   create the .s-row__body.s-body combo (a global style of that name already
   exists), so the second class is dropped there and these paragraphs would
   otherwise fall back to the body defaults. */
/* The body takes a line of its own, which left it sitting under the row
   NUMBER rather than under the title — every rich row on the site broke its
   own column. Indent it by the number column plus the row's column gap so the
   title and its body share one left edge. Below 44rem the indent is dropped:
   at that width it costs more room than the alignment is worth. */
/* Starts in the title's column and runs to the end, so the title and its
   body share one left edge. */
/* A row title can be a link (Treatments points at its two halves). It keeps
   the title's own type and picks up the accent underline on hover. */
.s-row__link { color: inherit; text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-ui) var(--ease), color var(--dur-ui) var(--ease); }
.s-row__link:hover { color: var(--accent); border-bottom-color: var(--accent); }
.s-row__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.s-row__body--rich { grid-column: 2 / -1; margin: .35rem 0 0;
  color: var(--seven-muted); max-width: 68ch;
  font-size: var(--step-0); line-height: 1.6; }
/* Below 44rem the descriptor drops under the title and the body takes the
   full width — the indent costs more room than the alignment is worth. */
@media (max-width: 44rem) {
  .s-row--rich { grid-template-columns: var(--row-n) 1fr; }
  .s-row__d--rich { grid-column: 2; }
  .s-row__body--rich { grid-column: 1 / -1; }
}

/* ---- app store badges ---- */
.p-badges { display: flex; flex-wrap: wrap; align-items: center;
  gap: clamp(.75rem, 2vw, 1.25rem); margin: clamp(1.5rem, 4vh, 2.25rem) 0 0; }
.p-badge__img { display: block; height: 2.9rem; width: auto; }
.p-badge { display: inline-flex; border-radius: 8px;
  transition: opacity var(--dur-ui) var(--ease); }
.p-badge:hover { opacity: .75; }
.p-badge:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* ---- jump rows ----
   These carry the navigation the header drops below 640px, where the two
   submenus close rather than flatten. They are a real part of each page at
   every width, not a mobile-only fallback, so they are not media-queried. */
.p-jumps { display: flex; flex-wrap: wrap; gap: clamp(.6rem, 1.6vw, 1rem);
  margin: clamp(1.5rem, 4vh, 2.25rem) 0 0; }
.p-jump { display: inline-flex; align-items: center; min-height: 44px;
  padding: .7rem 1.15rem; text-decoration: none;
  border: 1px solid var(--rule); color: var(--seven-fg);
  font-size: var(--step--1); letter-spacing: .16em; text-transform: uppercase;
  transition: background-color var(--dur-ui) var(--ease),
              border-color var(--dur-ui) var(--ease),
              color var(--dur-ui) var(--ease); }
.p-jump:hover { background: var(--seven-raised-2); border-color: var(--accent);
  color: var(--accent); }
.p-jump:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---- Long-form legal documents -------------------------------------- */
/* club/legal.html, club/privacy.html, club/house-rules.html. The wording is
   SEVEN's own and is reproduced verbatim — the styling does all the reading
   work, the sentences are untouched. That is also why these three pages are
   exempt from the copy rules in tools/brand-lint.mjs (see docs/HANDOVER.md):
   the lint governs copy we write, not a contract we quote. */
.p-doc { max-width: 68ch; }
.p-doc__h { margin: clamp(2.5rem, 7vh, 4rem) 0 0;
  padding-top: clamp(1.25rem, 3vh, 2rem); border-top: 1px solid var(--rule);
  font-size: var(--step--1); font-weight: 400; letter-spacing: .24em;
  text-transform: uppercase; color: var(--accent); }
.p-doc__h:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
/* A run of headings is a heading and its subhead — only the first takes the rule. */
.p-doc__h + .p-doc__h { margin-top: .9rem; padding-top: 0; border-top: 0; }
.p-doc__p { margin: 1.1rem 0 0; color: var(--seven-muted); line-height: 1.75; }
.p-doc__h + .p-doc__p { margin-top: clamp(1rem, 2.5vh, 1.5rem); }
.p-doc + .p-jumps { margin-top: clamp(3rem, 8vh, 5rem);
  border-top: 1px solid var(--rule); padding-top: clamp(1.5rem, 4vh, 2.5rem); }

/* ---- Statement and fine print ---------------------------------------- */
/* A single sentence given a whole band. Used where a page needs to stop and
   say one thing — the Ibiza House's own line about pace. Sized off --step-1
   rather than a heading step so it reads as speech, not as a section title. */
.p-statement { max-width: 24ch; margin: 0; font-size: var(--step-1);
  font-weight: 100; line-height: 1.25; letter-spacing: -0.01em; }
@media (min-width: 44rem) { .p-statement { max-width: 34ch; } }

/* Registered-entity lines and similar. Deliberately quiet, but still 4.5:1
   on the black ground — small print is not an excuse to fail contrast. */
.p-fine { margin: clamp(1.75rem, 5vh, 2.5rem) 0 0; color: var(--seven-muted);
  font-size: var(--step--1); letter-spacing: .04em; max-width: 62ch; }

/* ---- Newsletter archive ----------------------------------------------- */
.s-nl { list-style: none; margin: 0; padding: 0; }
.s-nl__item { border-top: 1px solid var(--rule);
  padding: clamp(1.25rem, 3vh, 1.75rem) 0; }
.s-nl__date { display: block; font-size: var(--step--1); letter-spacing: .24em;
  text-transform: uppercase; color: var(--accent); }
.s-nl__title { margin: .5rem 0 0; font-size: var(--step-1); font-weight: 100; }
.s-nl__link { color: inherit; text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-ui) var(--ease); }
.s-nl__link:hover { border-bottom-color: var(--accent); }
.s-nl__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.s-nl__excerpt { margin: .6rem 0 0; color: var(--seven-muted); max-width: 62ch; }
.s-nl__empty, .s-nl__thanks { margin: 0; color: var(--seven-muted); }

/* ---- Feature blocks (SEVEN+) ------------------------------------------ */
/* An app screen beside its explanation, alternating side to side. The
   screenshots are SEVEN's own, already shot on dark marble, so they are used
   ungraded — they arrive at a mean of 0.04-0.19 and glow against the black
   rather than sitting on it as a rectangle. No panel, no border: the marble
   already fades to near-black at its edges, which does the job a container
   would otherwise have to. */
.p-features { list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(3rem, 9vh, 6rem); }
.p-feature { display: grid; gap: clamp(1.25rem, 4vw, 3.5rem); align-items: center; }
@media (min-width: 52rem) {
  .p-feature { grid-template-columns: 1fr 1fr; }
  /* Alternate which side the screen sits on. Only the figure moves, so the
     reading order in the markup stays image-then-text on every block. */
  .p-feature:nth-child(even) .p-feature__shot { order: 2; }
}
.p-feature__shot { margin: 0; }
.p-feature__img { display: block; width: 100%; height: auto; }
.p-feature__n { display: block; font-size: var(--step--1); letter-spacing: .3em;
  color: var(--accent); }
.p-feature__t { margin: .75rem 0 0; font-size: var(--step-1); font-weight: 100;
  text-transform: uppercase; letter-spacing: .03em; }
.p-feature__k { margin: .4rem 0 0; font-size: var(--step--1); letter-spacing: .12em;
  text-transform: uppercase; color: var(--seven-muted); }
.p-feature__p { margin: 1rem 0 0; color: var(--seven-muted);
  max-width: 46ch; line-height: 1.6; }


/* ---- Webflow compatibility layer -------------------------------------
   Descendant copies of the flattened rules, for markup Webflow has not
   had rebuilt yet. Identical declarations, so either selector gives the
   same result. Generated by tools/compat-layer.py; shrinks as pages are
   swept. Not present in the static build. */
.s-tile--text .s-tile__body h3 { margin-top: auto; }
.s-tile--text .s-tile__body { top: 0; justify-content: space-between; }
.s-rows--rich .s-row__body { grid-column: 2 / -1; margin: .35rem 0 0; color: var(--seven-muted); max-width: 68ch; font-size: var(--step-0); line-height: 1.6; }
.s-band__head .s-eyebrow { margin: 0; }
.s-tile__body .s-label { color: var(--accent); margin: 0 0 .9rem; }
.s-ig__head .s-eyebrow { flex: 1 0 100%; margin: 0 0 .5rem; }
.s-rows--rich .s-row { display: grid; align-items: baseline; grid-template-columns: var(--row-n) 1fr auto; column-gap: var(--row-gap); row-gap: .4rem; }
.p-feature__shot img { display: block; width: 100%; height: auto; }
.s-feature__shot img { display: block; width: 100%; height: auto; }
.s-footer .s-label { margin: 0; }
.s-footer__cols h3 { margin: 0 0 1rem; font-size: var(--step--1); letter-spacing: .24em; text-transform: uppercase; color: var(--seven-muted); font-weight: 400; }
.s-footer__cols ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .2rem; }
.s-pillars .s-band { padding-block: 0 clamp(3rem, 8vh, 5rem); }
.p-head .s-eyebrow { margin: 0; }
.s-footer__cols a { color: var(--seven-fg); text-decoration: none; overflow-wrap: anywhere; font-size: 0.75rem; display: inline-flex; align-items: center; min-height: 44px; transition: color var(--dur-ui) var(--ease); }
.p-field textarea { font: inherit; font-size: var(--step-0); color: var(--seven-fg); background: transparent; border: 0; border-bottom: 1px solid var(--rule); border-radius: 0; padding: .45rem .25rem; min-height: 44px; width: 100%; transition: border-color var(--dur-ui) var(--ease); }
.p-field textarea { min-height: 8rem; resize: vertical; border: 1px solid var(--rule); padding: .85rem 1rem; }
.s-tt__bar select { font: inherit; font-size: var(--step-0); color: var(--seven-fg); background: var(--seven-raised); border: 1px solid var(--rule); border-radius: 2px; padding: .6rem .9rem; min-height: 44px; }
.s-intro .s-lede { margin: 0; color: var(--ink); font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.5rem); line-height: 1.2; font-weight: 300; }
.s-tile__body h3 { margin: 0; font-size: var(--step-1); font-weight: 100; text-transform: uppercase; letter-spacing: .03em; color: var(--seven-fg); }
.p-consent input { flex: none; width: 18px; height: 18px; margin-top: .2rem; accent-color: var(--accent); }
.s-tt__bar label { font-size: var(--step--1); letter-spacing: .24em; text-transform: uppercase; color: var(--seven-muted); }
.p-field select { font: inherit; font-size: var(--step-0); color: var(--seven-fg); background: transparent; border: 0; border-bottom: 1px solid var(--rule); border-radius: 0; padding: .45rem .25rem; min-height: 44px; width: 100%; transition: border-color var(--dur-ui) var(--ease); }
.p-field select { padding-inline-start: 0; }
.p-form__note a { color: var(--accent); }
.s-ig__head h2 { margin: 0; font-size: 2.375rem; font-weight: 100; line-height: 1.1; }
.s-nl__title a { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; transition: border-color var(--dur-ui) var(--ease); }
.p-field label { font-size: var(--step--1); letter-spacing: .18em; text-transform: uppercase; color: var(--seven-muted); }
.p-field input { font: inherit; font-size: var(--step-0); color: var(--seven-fg); background: transparent; border: 0; border-bottom: 1px solid var(--rule); border-radius: 0; padding: .45rem .25rem; min-height: 44px; width: 100%; transition: border-color var(--dur-ui) var(--ease); }
.p-badges img { display: block; height: 2.9rem; width: auto; }
.s-pillar dt { font-size: var(--step-1); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.s-pillar dd { margin: 0; font-size: var(--step--1); line-height: 1.65; color: var(--seven-muted); }
.p-detail dt { font-size: var(--step--1); letter-spacing: .24em; text-transform: uppercase; color: var(--seven-muted); margin-bottom: .5rem; }
.p-detail dd { margin: 0; font-size: var(--step-0); line-height: 1.6; }
.p-consent a { color: var(--accent); }
.s-mark img { display: block; width: clamp(72px, 7vw, 104px); height: auto; }
.s-footer a { position: relative; }
.s-house h3 { margin: 0; font-size: var(--step-1); font-weight: 100; text-transform: uppercase; letter-spacing: .03em; }
.s-house h3 { font-size: var(--step-2); }
.s-ticks li { position: relative; padding-inline-start: 1.6rem; font-size: var(--step-0); line-height: 1.55; color: var(--seven-muted); }
.s-tile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; z-index: 0; transition: transform 1.2s var(--ease); }
.s-row__t a { color: inherit; text-decoration: none; border-bottom: 1px solid transparent; transition: border-color var(--dur-ui) var(--ease), color var(--dur-ui) var(--ease); }
.p-badges a { display: inline-flex; border-radius: 8px; transition: opacity var(--dur-ui) var(--ease); }
.s-stat dt { font-size: min(var(--step-3), 14vw, 4rem); font-weight: 100; line-height: 1; letter-spacing: -.01em; }
.s-stat dd { margin: .6rem 0 0; color: var(--seven-muted); }
.s-card h3 { margin: 0; font-size: var(--step-1); font-weight: 100; text-transform: uppercase; letter-spacing: .03em; }
.p-hero h1 { margin: .75rem 0 0; font-size: min(var(--step-3), 12vw, 5rem); font-weight: 100; text-transform: uppercase; letter-spacing: .01em; line-height: 1; }
.s-nav a { position: relative; }
