/* ============================================================
   COMING SOON — standalone holding page.

   Self-contained on purpose: it does NOT load assets/styles.css, so nothing
   here can be broken by (or break) the main site while that is still being
   built. It only borrows two things from the real site, byte-identical:

     · the deck   — the cream knit of the homepage start slider (body.home)
     · the logo   — the two-line black logo, centred, same width formula as
                    the hero logo (assets/styles.css → .world--hero / .claim--logo)

   All paths are RELATIVE and point at this folder's OWN assets/ copy, so the
   whole page ships as one self-contained directory — upload /coming-soon/ and
   nothing outside it is needed. Works over HTTP and from disk (file://).

   Everything tunable sits in the :root block below.
   ============================================================ */

:root {
  /* deck (mirrors body.home in assets/styles.css) */
  --cs-deck: #efece4;

  /* Everything that is NOT the deck: the overscroll canvas, and the iOS/Android
     browser bars top and bottom. theme-color in index.html asks for those bars
     to be black, but it is only ever a request — Safari in particular falls back
     to sampling the page's own background colour. So this colour is painted as
     the real background-color of BOTH <html> and <body> — the knit stays on the
     body as the image on top of it, where it always was, and covers it whole.
     The bars then have nothing but black to sample, whatever they do with the
     meta. */
  --cs-canvas: #000;

  /* logo — a 320px cap, pulled back from the 400px maximum so it sits calmly in
     the middle of the page. All three terms scale together, so the mark shrinks
     proportionally on smaller viewports instead of just hitting a lower ceiling.
     Width-driven, but the height term binds on short / landscape viewports so it
     never overflows. svh with a vh fallback. */
  --cs-logo-w: min(61vw, 320px, 40vh);
  --cs-logo-w: min(61vw, 320px, 40svh);
  --cs-logo-ar: 1.1755;                                  /* 710 / 604 */

  /* How long one logo cut takes to fade into another. There are only two cuts,
     and both of the states that leave the resting black bring up the same one:
     black ⇄ black/white, on CONTACT hover and on the IMPRINT click alike. */
  --cs-swap-fade: .55s;

  /* The one margin the whole page is built on. It is the stage's inset top AND
     bottom, so the field the logo lives in is vertically symmetric — which is
     what puts the logo dead centre at rest. Open, it is both the logo's distance
     to the top edge and the marks' distance to the bottom one: the same number
     twice, so the two ends can never drift apart.

     It is also what makes the pair close up on short viewports without a second
     rule: the 7vh term shrinks the margin as the page gets shorter, and the logo
     and the marks are sized on svh terms of their own, so everything converges
     together instead of colliding. */
  --cs-top: clamp(24px, 7vh, 72px);
  --cs-gap: clamp(18px, 4vh, 44px);                      /* logo bottom → copy */
  --cs-ink: #0a0a0a;

  /* The pull-handle: one black bullet in the bottom-right corner, and nothing
     behind it. There is no plate to size any more, so what is left is the mark
     itself, its distance to the two edges it sits in, and the invisible box
     around it that catches the tap. The names still read `tab` — that is what
     this control is in the markup and the stylesheet, whatever shape it takes. */
  --cs-tab-dot: clamp(13px, 2.2vh, 18px);                /* the black bullet */
  --cs-tab-inset: clamp(18px, 3.4vh, 34px);              /* bullet → bottom / right edges */
  --cs-tab-hit: 44px;                                    /* tap target, never seen */
  --cs-tab-scale: 1.35;                                  /* hover grow, the bullet */
  --cs-drawer: .62s;                                     /* the marks' travel — and the logo's */
  --cs-tab-in: .38s;                                     /* how long the bullet takes to slide back in */

  /* the knit CONTACT mark at the foot — its own width, deliberately NOT tied to
     the logo, so the two can be sized against each other by eye. */
  --cs-mail-w: min(44vw, 230px, 29vh);
  --cs-mail-w: min(44vw, 230px, 29svh);
  --cs-mail-scale: 1.07;                                 /* hover grow, the marks */

  /* Only three things grow under the cursor: the two marks at the foot and the
     bullet. The logo is not one of them — it keeps one size in all three states,
     and only its cut and its position ever change. There is no logo scale factor
     here because there is no logo scale.

     --cs-hover is what all three of those grows are timed on. The logo's own
     crossfade is --cs-swap-fade and its travel is --cs-drawer; neither is this. */
  --cs-hover: .45s;
  --cs-ease: cubic-bezier(.22, .61, .36, 1);

  /* The address: three long lines, a shade narrower than the logo above it, with
     a height term of its own so that it shrinks on short / landscape viewports
     the way the logo and the marks do. Ratio is 1046/664 ≈ 1.575, so 40svh of
     width caps its height at ~25svh. All three terms are set against each other
     by eye and move together — pulling only the px cap would change the address
     on a desktop and nowhere else. */
  --cs-addr-w: min(68vw, 360px, 40vh);
  --cs-addr-w: min(68vw, 360px, 40svh);
  --cs-addr-rise: 10px;                                  /* how far it drifts up into place */
  --cs-imprint-gap: 5px;                                 /* CONTACT bottom → IMPRINT top */
  --cs-swap: .7s;
  --cs-swap-in: .98;                                     /* starts a hair small */
}

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

html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}

/* Makes the artwork awkward to lift: nothing is selectable, nothing is
   draggable, and iOS raises no long-press "save image" sheet. Combined with the
   fact that every image is a CSS background rather than an <img>, the casual
   routes are closed. The files are of course still in the network tab — that is
   unavoidable, the browser has to receive them to draw them. */
.cs, .cs * {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
}

/* ---- the ground ------------------------------------------------------------
   One deck, the cream knit, painted straight onto the body — as it always was.

   In two sizes: a phone never needs the wide plate, so it is only fetched where
   it can actually be seen — the small tier is a quarter of the bytes and
   indistinguishable on a handset.

   Black on <html>, and it has to be on <html>: with nothing here the browser
   propagates the BODY's background to the canvas, which is what painted the
   cream out past the viewport and into the bars. Once html carries a background
   of its own, propagation stops — body then paints the knit inside its own box
   only (one viewport, since both are height:100%), and everything beyond it is
   this black. */
html {
  background-color: var(--cs-canvas);
}

/* The body's own background-COLOUR is black too, and that is the bit that makes
   the browser bars go black: theme-color in index.html is only ever a hint —
   Safari falls back to sampling the document's background colour, and as long as
   that was cream the bars came out cream with it. It is never seen: the knit
   covers the whole box.

   Which is also why the cream doesn't just move underneath the image as the
   fallback any more. It sits ABOVE the black instead, as a flat gradient — a
   gradient is an image layer, not the background-color, so if the webp ever
   fails to arrive the deck still comes out cream while the colour the bars read
   stays black. */
body.cs {
  background:
    url('assets/backgrounds/bg-cream.webp') center / cover no-repeat,
    linear-gradient(var(--cs-deck), var(--cs-deck)),
    var(--cs-canvas);
  color: var(--cs-ink);
  font-family: 'Anton', system-ui, sans-serif;
  overflow: hidden;                                      /* the site never scrolls the body */
}

@media (max-width: 820px) {
  body.cs {
    background-image:
      url('assets/backgrounds/bg-cream-sm.webp'),
      linear-gradient(var(--cs-deck), var(--cs-deck));
  }
}


/* full-viewport stage: the logo centred in the field, the marks at the foot.

   The vertical padding is the SAME on both sides now — --cs-top top and bottom.
   There used to be a reserved band at the foot instead, big enough to keep the
   logo clear of the drawer and the address; it is gone because the logo no
   longer has to be kept clear of anything. It moves out of the way itself the
   moment the marks come up. Symmetric padding is what makes the field
   vertically centred on the page, and therefore the logo with it.

   The drawer and the address are positioned against the stage's padding BOX, so
   this padding never moved them and still doesn't — their distance to the bottom
   edge is written into their own rules. */
.cs__stage {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  padding: calc(env(safe-area-inset-top) + var(--cs-top))
           env(safe-area-inset-right)
           calc(env(safe-area-inset-bottom) + var(--cs-top))
           env(safe-area-inset-left);
}

/* The field the logo sits in: the flex child that grows, so with the body empty
   it claims the whole stage between the two paddings — a box whose top edge is
   --cs-top below the top of the page and whose middle IS the middle of the page.
   Both of the logo's two positions are read off it. */
.cs__field {
  position: relative;
  flex: 1 1 auto;
  align-self: stretch;
}

/* The logo box holds its own aspect ratio, so both cuts (whose source files
   differ in size) can sit absolutely inside it and resolve into the same box —
   nothing shifts when one fades into the other.

   It has two resting places, and it travels between them:

     rest    dead centre of the page — `top: 50%` of a field that is symmetric
             about the middle, pulled back by half its own height.
     open /
     imprint its top edge on the field's top edge, which is --cs-top below the
             top of the page. The marks come up to exactly --cs-top above the
             bottom. Same number at both ends, and neither end has to know how
             tall the other's art is.

   Two properties carry the move — `top` and `translate` — over one duration and
   one curve, so it reads as a single slide. That duration is --cs-drawer, the
   drawer's own: the logo rising and the marks coming up are one gesture, and
   they have to arrive together.

   Size is not one of the things that change. The logo used to grow on CONTACT
   hover and again on IMPRINT; both are gone. It keeps one size in all three
   states, which is also what keeps its parked position honest — a grow about the
   box's centre would have pushed its top edge up into the margin it is supposed
   to be holding. The only thing left that changes up here is the cut. */
.cs__logo {
  margin: 0;
  width: var(--cs-logo-w);
  aspect-ratio: var(--cs-logo-ar);
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  transition: top var(--cs-drawer) var(--cs-ease),
              translate var(--cs-drawer) var(--cs-ease);
}

/* Up top, and it stays there for both of the states that leave rest: IMPRINT is
   reached from an open drawer, so the logo is already up when it is clicked and
   has no reason to move again. Only the cut changes from here. */
body.is-foot-open .cs__logo,
body.is-imprint .cs__logo {
  top: 0;
  translate: -50% 0;
}

/* Both cuts share one box and one fade. Painted as BACKGROUNDS rather
   than <img>: a background offers no "save image as", cannot be dragged out and
   raises no long-press menu on a phone. The meaning is carried by role="img" +
   aria-label on the parent, so nothing is lost to a screen reader. */
.cs__logo span {
  position: absolute;
  inset: 0;
  display: block;
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
  opacity: 0;
  transition: opacity var(--cs-swap-fade) var(--cs-ease);
}
.cs__logo-rest { background-image: url('assets/logo-2line/logo-black.webp'); }
.cs__logo-bw   { background-image: url('assets/logo-2line/logo-bw.webp'); }
/* Two cuts, and that is the whole set: both states that leave the resting black
   bring up the same .cs__logo-bw — CONTACT hover and the IMPRINT click. The
   coloured cut that used to be a third state is gone, art and all; re-adding one
   means a file in assets/logo-2line/, a span, and a rule here. */

/* At rest: black, and nothing else. Scoped through .cs__logo on purpose — a bare
   .cs__logo-rest would lose to the `.cs__logo span` rule above on specificity
   and the logo would never show at all. */
.cs__logo .cs__logo-rest { opacity: 1; }

/* Hovering CONTACT swaps the resting black for the black-and-white cut. The
   logo sits BEFORE the mark in the DOM and in a different parent, so :has() on
   the stage is what reaches it. Unsupported browsers just keep the black logo —
   nothing breaks. Gated on real hover so a tap can't strand it.

   The cut is the whole of it: the mark under the cursor grows, the logo answers
   by changing what it is made of and nothing else. It used to grow along with
   the mark; that is gone, so there is no transform here at all. */
@media (hover: hover) {
  .cs__stage:has(.cs__mail:hover) .cs__logo-rest { opacity: 0; }
  .cs__stage:has(.cs__mail:hover) .cs__logo-bw { opacity: 1; }
}
.cs__stage:has(.cs__mail:focus-visible) .cs__logo-rest { opacity: 0; }
.cs__stage:has(.cs__mail:focus-visible) .cs__logo-bw { opacity: 1; }

/* Copy / countdown / form land here, under the logo. Empty → zero height. */
.cs__body {
  margin-top: var(--cs-gap);
  width: min(90vw, 640px);
  text-align: center;
}
.cs__body:empty { margin-top: 0; }

/* ---- the drawer -----------------------------------------------------------
   The two marks travel as ONE element, parked below the bottom edge: `top: 100%`
   puts its box start exactly on the stage's bottom edge, so everything in it is
   off-screen to begin with. One transform then decides how much of it is
   showing, and it has two resting positions:

     rest / address    left alone at 0 — entirely out of the picture. The handle
                       is a bullet in the corner, not a tab clinging to this
                       edge, so there is nothing here that has to peek in at rest.
     open              pulled in by its own full height (-100%), marks and all

   Expressing "open" as a percentage of ITSELF is what keeps this honest: the
   marks' height never has to be known, measured or kept in sync. It also means
   the drawer is bottom-anchored while open — its own bottom lands on the stage's
   — so the padding below the marks is their distance to the bottom edge no
   matter what else is or isn't in the box.

   Nothing here is in the flow, so the field the logo is positioned against keeps
   the same box whether the drawer is in or out. The logo does travel when the
   drawer does — that is the point — but because it is told to, not because the
   drawer pushed it. */
.cs__drawer {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* The marks keep the same bottom rhythm they always had: this padding sits
     below them inside the drawer, so pulling it fully in leaves exactly
     --cs-top between the last mark and the bottom edge. */
  padding-bottom: calc(env(safe-area-inset-bottom) + var(--cs-top));
  transform: translateY(0);
  transition: transform var(--cs-drawer) var(--cs-ease);
  will-change: transform;
}
body.is-foot-open .cs__drawer { transform: translateY(-100%); }

/* The handle. The black tab is gone: there is no plate on the edge any more,
   only the bullet — bigger than it was, black instead of white, sitting in the
   bottom-right corner of the page. It is still its own element rather than the
   drawer's first child, because the drawer is centred at the bottom and this is
   pinned to the corner.

   The button box is the tap target and nothing else — 44px of transparency, so
   the mark can stay as small as it looks and still be comfortably hittable. The
   bullet is the ::before, centred in it. Which is also why the two offsets
   subtract half the slack between box and mark: what should be --cs-tab-inset
   from the edges is the BULLET, not the invisible box around it.

   The env() fallbacks matter here in a way they don't inside the calc()s
   elsewhere: a bare env() the browser doesn't know makes the whole declaration
   invalid, and `right` would fall back to auto — which would leave the handle
   wherever its static position happened to be. */
.cs__tab {
  position: absolute;
  right: calc(env(safe-area-inset-right, 0px) + var(--cs-tab-inset)
              - (var(--cs-tab-hit) - var(--cs-tab-dot)) / 2);
  bottom: calc(env(safe-area-inset-bottom, 0px) + var(--cs-tab-inset)
               - (var(--cs-tab-hit) - var(--cs-tab-dot)) / 2);
  z-index: 5;
  width: var(--cs-tab-hit);
  height: var(--cs-tab-hit);
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  display: grid;
  place-items: center;
}

/* The mark itself. Its scale is deliberately kept off the button: the button's
   own transform is spoken for by the slide-out, and that one carries a delay as
   long as the drawer's travel — put the hover on it too and the grow would sit
   there waiting out a delay that has nothing to do with it. Two elements, two
   transforms, two transitions, no interference. */
.cs__tab::before {
  content: "";
  width: var(--cs-tab-dot);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--cs-ink);
  transition: transform var(--cs-hover) var(--cs-ease);
  will-change: transform;
}
@media (hover: hover) {
  .cs__tab:hover::before { transform: scale(var(--cs-tab-scale)); }
}

/* The tab is only ever the thing you pull, so it has two very different exits:

     leaving   it goes at once — no fade, no travel. There is nothing left to
               pull, and watching the handle slide away alongside the marks
               coming up would say otherwise.
     coming back
               it waits out the drawer's own travel (both delays are
               --cs-drawer) and only then slides back in from off the right
               edge, arriving as the page settles into rest.

   Both states that hide the marks' handle are listed: with the marks up there is
   nothing to pull, and while the address is up a click anywhere is the way back,
   so a handle would only be a second, contradictory answer.

   visibility rather than display, and the translate is what takes it off-screen:
   the handle keeps its box either way, so nothing in the layout can react to it
   leaving. It goes out the RIGHT edge, the nearer of the two it sits in: 100% is
   its own box, and the whole inset it is standing off — safe area and gap both —
   has to come with it, or the mark would stop just short of the edge and sit
   there in plain sight. */
.cs__tab {
  transform: translateX(0);
  transition: transform var(--cs-tab-in) var(--cs-ease) var(--cs-drawer),
              visibility 0s linear var(--cs-drawer);
}
body.is-foot-open .cs__tab,
body.is-imprint .cs__tab {
  transform: translateX(calc(100% + env(safe-area-inset-right, 0px) + var(--cs-tab-inset)));
  visibility: hidden;
  transition-delay: 0s;
}
/* The ring belongs on the mark, not on the tap target: outlined at the button's
   size it would draw a 44px square around a bullet a third of that. */
.cs__tab:focus-visible { outline: none; }
.cs__tab:focus-visible::before {
  outline: 2px solid var(--cs-ink);
  outline-offset: 4px;
}

/* The marks themselves, the drawer's only content now. Hidden at rest — off-screen is
   not enough on its own: without this they stay in the tab order, and a
   keyboard user would be focusing controls that are nowhere to be seen. The
   delay holds them visible until the slide-out has finished. */
.cs__foot {
  padding-top: var(--cs-gap);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  visibility: hidden;
  transition: visibility 0s linear var(--cs-drawer);
}
body.is-foot-open .cs__foot {
  visibility: visible;
  transition-delay: 0s;
}

.cs__mail {
  width: var(--cs-mail-w);
  display: block;
  position: relative;                                    /* anchors the crop */
  transition: transform var(--cs-hover) var(--cs-ease);
  will-change: transform;
}
.cs__mail { aspect-ratio: 1300 / 410; }         /* the art's ratio, which the <img> used to carry */
.cs__mail-img {
  position: absolute;
  inset: 0;
  display: block;
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
  background-image: url('assets/headlines/headline-contact.webp');
}

/* One crop, and it stays. The rainbow crop that used to fade in over this on
   hover is gone, art and all — hovering CONTACT now only scales the mark (and
   swaps the LOGO to its black/white cut, which is a separate rule up top). */

/* The postal address: centred at the foot of the stage, over everything, and
   never a click target — while it is up, a click anywhere ends the flight.
   It sits on the same bottom rhythm as the CONTACT mark it replaces. */
.cs__addr {
  position: absolute;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom) + var(--cs-top));
  z-index: 3;
  width: var(--cs-addr-w);
  aspect-ratio: 1046 / 664;
  background-image: url('assets/headlines/headline-address.webp');
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, var(--cs-addr-rise)) scale(var(--cs-swap-in));
  transition: opacity var(--cs-swap) var(--cs-ease),
              transform var(--cs-swap) var(--cs-ease);
}

/* Gated on real hover so a tap on touch can't leave the mark stranded mid-grow;
   keyboard focus gets the same treatment. */
@media (hover: hover) {
  .cs__mail:hover { transform: scale(var(--cs-mail-scale)); }
}
.cs__mail:focus-visible {
  transform: scale(var(--cs-mail-scale));
  outline: none;
}

/* The IMPRINT button under CONTACT: same width, its own (taller) ratio carried
   by aspect-ratio. Stripped back to bare art — it is a control, not a chrome. */
.cs__imprint {
  display: block;
  width: var(--cs-mail-w);
  margin: var(--cs-imprint-gap) 0 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: transform var(--cs-hover) var(--cs-ease);
}
/* the button IS the mark now — no child element to speak of */
.cs__imprint {
  aspect-ratio: 1272 / 495;
  background-image: url('assets/headlines/headline-imprint.webp');
  background-position: center;
  background-size: contain;
  background-repeat: no-repeat;
}
@media (hover: hover) {
  .cs__imprint:hover { transform: scale(var(--cs-mail-scale)); }
}
.cs__imprint:focus-visible {
  outline: 2px solid var(--cs-ink);
  outline-offset: 6px;
}

/* ---- the imprint state, switched by the IMPRINT button ---------------------
   Three things at once, on one class: the drawer travels all the way back out,
   the address comes up in its place, and the logo swaps to its black/white cut.
   The logo does not move — it is already parked up top by the state this was
   reached from. The handle stays away too (see .cs__tab). A click anywhere (or
   Escape) takes it all back. */
body.is-imprint .cs__drawer { transform: translateY(0); }
/* The logo does nothing at all here but change its cut. It is already up top —
   this state is only ever reached from an open drawer, and both states park it
   in the same place (see .cs__logo) — and it no longer grows, in this state or
   any other.

   The cut it swaps to is the SAME black/white one CONTACT hover uses, so the two
   gestures speak with one image. That the hover rule up top asks for exactly the
   same thing is why the two can't fight over it; and the foot is hidden by the
   time this is up anyway, so neither hover nor focus can be re-entered while the
   address is showing. */
body.is-imprint .cs__logo .cs__logo-rest { opacity: 0; }
body.is-imprint .cs__logo .cs__logo-bw { opacity: 1; }

body.is-imprint .cs__addr {
  opacity: 1;
  transform: translate(-50%, 0) scale(1);
}

/* Nothing on this page moves of its own accord — every transition is the answer
   to a hover, a focus, the handle or the IMPRINT click. Reduced motion therefore
   only has to collapse the travel and the fades: the state changes still happen,
   the logo and the marks still swap ends, they just arrive instantly. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    /* The delays go too, or the handle and the marks would still wait out a
       travel that no longer happens — the state would be right and look stuck. */
    transition-delay: 0s !important;
  }
}
