/* Self-hosted so the site makes no third-party requests.
   SIL Open Font License 1.1 — see public/fonts/ATTRIBUTION.md */
@font-face {
  font-family: 'Fraunces';
  src: url('./fonts/fraunces-latin-var.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Karla';
  src: url('./fonts/karla-latin-var.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
    U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Lotus Den palette — see lotus-den-palette.html */
  --bone: #f4efe7;
  --linen: #e7dfd2;
  --rose: #a0786b;
  --rose-deep: #7c5b4e;
  --walnut: #2e211a;
  --walnut-deep: #1e1510;
  --candle: #c87f2e;
  --candle-lift: #e5a048;
  --euc: #6c7a63;

  /* Roles — daylight */
  --bg: var(--bone);
  --surface: var(--linen);
  --ink: var(--walnut);
  --ink-soft: #6b584c; /* 5.87:1 on bone */
  --accent: var(--candle); /* button grounds only, never text */
  --on-accent: var(--walnut-deep); /* 5.58:1 on candle */
  --link: var(--rose-deep); /* 5.30:1 on bone — candle would be 2.81:1 */
  --brand-mark: var(--rose-deep); /* 5.30:1 on bone — candle would be 2.81:1 */
  --euc-text: #5e6b56; /* 4.94:1 on bone — euc itself is 3.99:1, fails AA */
  --focus: var(
    --ink
  ); /* 13.60:1 on bone — candle would be 2.81:1, below the 3:1 non-text minimum */
  --line: rgba(46, 33, 26, 0.14);
  --danger: #8f2f22;

  --space: 1rem;
  --measure: 66ch;
  --radius: 2px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: var(--walnut-deep);
    --surface: #2c2018;
    --ink: #efe7db;
    --ink-soft: #b8a794; /* 7.69:1 on walnut-deep */
    --accent: var(--candle-lift);
    --on-accent: var(--walnut-deep);
    --link: var(--candle-lift); /* 8.08:1 on walnut-deep */
    --brand-mark: var(--candle-lift); /* 8.08:1 on walnut-deep */
    --euc-text: #9daf90; /* 6.76:1 on the dim surface, 7.67:1 on walnut-deep */
    --focus: var(--ink); /* 14.64:1 on walnut-deep */
    --rose: #b08b7c;
    --euc: #8a9a7e;
    --line: rgba(239, 231, 219, 0.14);
    --danger: #d9aa9b;
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: Karla, system-ui, sans-serif;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.6;
}

h1,
h2,
h3,
.disp {
  font-family: Fraunces, Georgia, serif;
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

p {
  max-width: var(--measure);
}

a {
  color: var(--link);
  text-underline-offset: 0.18em;
}
a:hover {
  text-decoration-thickness: 2px;
}

.eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 500;
  margin: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  left: var(--space);
  top: var(--space);
  z-index: 10;
  padding: 0.5rem 0.75rem;
  background: var(--surface);
  color: var(--ink);
}

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

.header {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space);
  align-items: baseline;
  justify-content: space-between;
  padding: 18px 26px;
  border-bottom: 1px solid var(--line);
}

.header__brand {
  font-family: Fraunces, Georgia, serif;
  font-size: 1.15rem;
  letter-spacing: 0.02em;
  text-decoration: none;
  /* All three words in one brown — the colour "Den" already had. The two-tone
     mark split the name in half for no reason once the poster carried the
     brand. */
  color: var(--brand-mark);
}

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
}
.nav__link {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
}
.nav__link:hover {
  color: var(--ink);
}
.nav__link[aria-current='page'] {
  color: var(--ink);
  text-decoration: underline;
}

.main {
  max-width: 62rem;
  margin: 0 auto;
  /* Extra bottom padding: the footer used to supply the page's closing space
     via its own margin, and there is no footer now. */
  padding: calc(var(--space) * 3) 26px calc(var(--space) * 5);
}

/* Hero: text beside the room, stacking on narrow screens */
.hero {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: 40px;
  align-items: center;
}
.hero h1 {
  font-size: clamp(1.9rem, 4vw, 3rem);
  margin: 0.24em 0 0.5em;
}
.hero__lede {
  font-size: 1.05rem;
  color: var(--ink-soft);
}

.roomshot {
  margin: 0;
}
.roomshot img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: var(--radius);
}

.section {
  margin-top: calc(var(--space) * 4);
}

.callout {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: var(--space) calc(var(--space) * 1.25);
}

.pricing {
  list-style: none;
  margin: 0 0 var(--space);
  padding: 0;
  max-width: 34rem;
}
.session,
.session-line {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: space-between;
  align-items: baseline;
  max-width: 34rem;
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--line);
}
/* One session length, so this reads as a single stated fact, not a table. */
.session-line {
  margin: 0 0 var(--space);
  font-size: 1.15rem;
}
.session__length {
  letter-spacing: 0.04em;
}
.session__price {
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}
.pricing__note {
  color: var(--ink-soft);
}

.prose > * + * {
  margin-top: var(--space);
}
.pull-quote {
  font-family: Fraunces, Georgia, serif;
  font-size: 1.35rem;
  color: var(--ink);
  border-left: 3px solid var(--euc);
  padding-left: var(--space);
  margin-left: 0;
}
.signature {
  color: var(--ink-soft);
}

.button {
  display: inline-block;
  background: var(--accent);
  color: var(--on-accent);
  padding: 13px 26px;
  border: 0;
  border-radius: var(--radius);
  font: inherit;
  font-weight: 700;
  font-size: 0.86rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition:
    transform 150ms ease-out,
    filter 150ms ease-out;
}
.button:hover {
  filter: brightness(1.06);
}
.button:active {
  transform: scale(0.97);
}

.field {
  display: grid;
  gap: 0.35rem;
  margin-bottom: var(--space);
  max-width: 34rem;
}
.field input,
.field textarea {
  font: inherit;
  padding: 0.7rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  width: 100%;
}

.note {
  color: var(--ink-soft);
  font-size: 0.92rem;
}

.notice {
  padding: 0.75rem var(--space);
  border-radius: var(--radius);
  border: 1px solid var(--line);
}
.notice--error {
  border-color: var(--danger);
  color: var(--danger);
}
.notice--success {
  border-color: var(--euc);
  color: var(--euc-text);
}

/* ── Jennie's portrait on /about ────────────────────────────────────────
   Small on purpose: it puts a face to the name without becoming a feature.
   Floated right so the prose wraps to its left and her raised hand gestures
   into the text. Capped in px as well as %, so it cannot grow into a
   centrepiece on a wide screen. */

/* The paragraphs cap at --measure while the article is wider, so a float at
   the article's right edge lands well clear of the text and the gesture points
   at nothing. Pull the article in to the measure plus the portrait. */
.prose {
  max-width: calc(var(--measure) + 190px);
}

/* Referrals-only notice, above the h1 on /about.

   It is a sibling of .prose rather than its first child, and that is what makes
   the centring right: .prose is widened past the measure to clear the floated
   portrait, so centring inside it puts the line off the page's axis by enough
   to look accidental. Out in .main it centres on the page. The max-width here
   caps the line length; margin-inline:auto is what actually centres the box.

   Bigger than body copy and smaller than the h1 it precedes: it has to be seen
   before the heading without competing with it for the top of the page.

   The italic is SYNTHESISED. Karla is self-hosted as one upright variable face
   (font-style: normal in its @font-face), so there is no real italic to reach
   for and the browser slants the upright. That is fine on a sans at one short
   line and nothing to "fix" by adding a font file for six words — but it is
   why this does not use Fraunces, where a faux slant on a serif looks broken.
   The only genuine italic in public/fonts is Cormorant, and that belongs to
   the poster on the landing page, not here. */
.referral-note {
  font-style: italic;
  font-size: clamp(1.1rem, 1.02rem + 0.4vw, 1.3rem);
  line-height: 1.45;
  text-align: center;
  text-wrap: balance;
  color: var(--ink);
  max-width: var(--measure);
  margin: 0 auto calc(var(--space) * 2);
}

.portrait {
  float: right;
  /* The photograph uncropped, at its own 1122:1402, so it is the frame Jennie
     chose rather than one cut for the slot.

     135px, not the 170 the old portrait used. That one was a circle; matching
     its WIDTH would have made this noticeably heavier on the page, because a
     rectangle fills its box and a circle only fills π/4 of one. Matching AREA
     instead: the circle covered 22,700px², and 135 x 169 covers 22,800. It
     reads as the same size on the page, which is what "the same size" means
     here.

     No shape-outside. The circular wrap it used to have followed the outline of
     a cut-out on transparency; against a rectangle it would bend the text past
     corners that are visibly there. */
  width: min(28%, 135px);
  height: auto;
  margin: 0 0 var(--space) var(--space);
}

@media (max-width: 32rem) {
  /* At phone width a float this size squeezes the measure to a few words a
     line, so let it sit on its own. */
  .portrait {
    float: none;
    display: block;
    width: 40%;
    margin: 0 auto var(--space);
  }
}

/* ── The phone reveal on /about ──────────────────────────────────────────
   The number appears only once the reader confirms they have read the page.
   Done with :checked and a sibling selector so it needs no JavaScript. */

.confirm {
  max-width: 34rem;
  /* Left-aligned with the prose above it, not centred — a centred panel under
     left-aligned body text reads as a mistake. */
  margin: calc(var(--space) * 3) auto 0 0;
  padding: calc(var(--space) * 1.5);
  /* No panel and no accent rail. This section sits on the page rather than on
     top of it: the surface tint and the candle-coloured left border made it a
     callout box, which framed the number as an aside. It is the destination of
     the whole page, so it gets the page's own ground and a hairline only. */
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* Clearance above it when scrolled to. Not merely breathing room: on a phone
     the browser's URL bar overlays the top of the layout viewport, so a heading
     aligned to "the top" lands underneath it. This is what keeps it clear, and
     it is why the figure below is generous rather than tidy. */
  scroll-margin-top: calc(var(--space) * 2);
}

@media (max-width: 40rem) {
  .confirm {
    /* 3.5rem, tuned against the shortest phone viewport worth supporting
       (390x600, which is roughly an iPhone SE with its URL bar showing).

       It is a balance in both directions. Too little and the heading lands
       under the URL bar, which is the fault this fixes. Too much and the
       section is pushed so far down that the foot of the photograph falls off
       the bottom — measured, 5rem left 19px of it below the fold on that
       viewport, and 4rem left 3px. At 3.5rem the heading sits 81px down, clear
       of any URL bar, and the whole photograph fits. */
    scroll-margin-top: 3.5rem;
  }
}
.confirm h2 {
  margin-top: 0;
}

.confirm__row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: var(--space);
}
.confirm__row label {
  cursor: pointer;
}
.confirm__input {
  width: 1.15rem;
  height: 1.15rem;
  accent-color: var(--accent);
  cursor: pointer;
  flex: 0 0 auto;
}

/* Clipped rather than display:none, so the number stays in the accessibility
   tree and the live region can announce it. */
.reveal {
  height: 0;
  overflow: hidden;
  opacity: 0;
  /* Slower than a UI fade, to match the doors below it — the number and the
     room arrive as one gesture rather than a control responding. */
  transition: opacity 700ms ease-out;
}
/* :has(), not a sibling combinator — the checkbox is nested inside
   .confirm__row, so it is not a sibling of .reveal and `~` never matches. */
.confirm:has(.confirm__input:checked) .reveal {
  height: auto;
  opacity: 1;
  margin-top: calc(var(--space) * 1.25);
}

/* ── The studio, revealed with the number ───────────────────────────────
   The page opens onto the room you are being invited into, so the reveal is
   literal: two doors part from the centre.

   Done with clip-path on the image rather than by animating a pair of covering
   panels. A panel would have to be painted the exact colour of whatever sits
   behind it, and this page has a photograph, a gradient and a page ground in
   play; clipping needs no such agreement — there is simply nothing there yet.

   The opening is square-cornered, which was not the original intent — it was an
   arch, echoing Jennie's portrait and the poster's curve — but an animated
   corner radius is exactly what Chrome refuses to interpolate. The rounded
   outline survives on .studio itself, where nothing has to animate for it. */
/* The doors clip the FIGURE and the settle scales the IMAGE inside it, and they
   must stay on separate elements.

   Both were on the image, and in Chrome the doors then never appeared: an
   element with an animating transform is promoted to a compositor layer,
   rasterised once and transformed, so a clip-path animating on that same
   element is applied at its final value instead of per frame. The result was
   the room snapping open and then panning out — the scale animating visibly
   while the doors did not animate at all. Computed styles interpolated
   correctly throughout, which is why this did not show up in measurements;
   only the paint was wrong. */
.studio {
  margin: calc(var(--space) * 1.1) 0 0;
  border-radius: 0.4rem;
  overflow: hidden;
  /* Shut: a hairline at the centre. Square-cornered — see the keyframes. */
  clip-path: inset(0 50% 0 50%);
}

.confirm:has(.confirm__input:checked) .studio {
  animation: studio-doors 1600ms cubic-bezier(0.22, 0.61, 0.36, 1) 420ms
    forwards;
}

/* Keyframes, not a transition, and that is a bug fix rather than a preference.
   .reveal is height:0, so on the FIRST click the image goes from never-rendered
   to rendered — and a transition has no established "from" value to interpolate
   out of, so it snapped open. On the second and later clicks the element
   already had a computed style and it animated properly, which is exactly the
   "first one was faster" you would see. An animation applies when its rule
   starts matching, whether or not the element was rendered a frame earlier. */
/* NO `round` in these. That is the whole fix, and it cost several wrong
   diagnoses to find: Chrome will not interpolate the corner radius inside
   inset() — it jumps to the end value and paints the doors already open, while
   everything else on the page animates normally. Confirmed by running six
   techniques side by side in real Chrome; the only difference between the one
   that worked and the one that did not was this keyword.

   It never showed up in measurement because getComputedStyle interpolates the
   radius quite happily. The failure is in paint, and only in hardware
   compositing — headless software rendering animates it correctly, so it looks
   fine in tests and wrong on a real machine.

   The rounded corners are unaffected: .studio carries border-radius with
   overflow:hidden, which does that job without anything having to animate. */
@keyframes studio-doors {
  from {
    clip-path: inset(0 50% 0 50%);
  }
  to {
    clip-path: inset(0 0 0 0);
  }
}
@keyframes studio-settle {
  from {
    transform: scale(1.04);
  }
  to {
    transform: scale(1);
  }
}

.studio img {
  display: block;
  width: 100%;
  height: auto;
  transform: scale(1.04);
}

.confirm:has(.confirm__input:checked) .studio img {
  /* Longer than the doors, so the room is still settling as they finish — it
     comes to rest rather than arriving already still.

     The 420ms delay, shared with the doors, waits for the scroll. Clicking the
     box also smooth-scrolls this into view, and without the delay the doors
     covered half their distance while the page was still moving under them, so
     a first reveal looked over before it began while later ones, needing no
     scroll, played in full. Where nothing needs scrolling the delay costs
     nothing: the doors are shut throughout it either way. */
  animation: studio-settle 2400ms cubic-bezier(0.22, 0.61, 0.36, 1) 420ms
    forwards;
}

@media (prefers-reduced-motion: reduce) {
  /* The doors are decorative; the room is not. Show it, do not animate it. */
  .studio,
  .confirm:has(.confirm__input:checked) .studio {
    clip-path: none;
    animation: none;
  }
  .studio img,
  .confirm:has(.confirm__input:checked) .studio img {
    transform: none;
    animation: none;
  }
}

.reveal__lead {
  margin: 0 0 0.35rem;
  color: var(--ink-soft);
}
.reveal__phone {
  margin: 0 0 0.5rem;
  font-family: Fraunces, Georgia, serif;
  font-size: 1.6rem;
  font-variant-numeric: lining-nums tabular-nums;
}
.reveal__phone a {
  text-decoration: none;
}
.reveal__phone a:hover {
  text-decoration: underline;
}
.reveal__note {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    transition: none;
  }
}

@media (max-width: 760px) {
  .hero {
    grid-template-columns: 1fr;
    gap: 28px;
  }
  .main {
    padding: calc(var(--space) * 2) 20px;
  }
  .header {
    padding: 16px 20px;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
