/* ---------------------------------------------------------------------------
   The marketing site: doorcut.app, /providers and /customers.

   Separate from legal.css on purpose. That file styles four documents somebody
   reads top to bottom, three of which are plain-language stand-ins waiting for
   a lawyer-reviewed replacement -- so it stays a simple measure and a left
   edge, and swapping the text under it stays a paste job.

   Every colour here is lifted from src/theme/tokens.ts rather than picked to
   match by eye, so the site and the app are the same brand rather than two
   attempts at it.
   ------------------------------------------------------------------------ */

:root {
  /* Brand ground and figure. [tokens] colorsDark.background / colorsLight.background */
  --ink: #17150f;
  --ink-raised: #221e15;
  --ink-high: #2c271c;
  --bone: #f0ede6;
  --white: #ffffff;

  /* The gold family. [tokens] star / primary(dark) / accent(light) */
  --brass: #c08a2e;
  --brass-soft: #e8c88a;
  --gold-deep: #8a6218;

  /* Lines and quiet text, per ground. */
  --line-dark: #3b3629;
  --line-light: #d9d3c6;
  --muted-dark: #9b9484;
  --muted-light: #6b655a;

  --shell: 1080px;
  --gutter: 20px;
  --radius: 14px;
  --radius-lg: 22px;

  /* One easing for the whole site, so nothing moves in a different voice. */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--bone);
  font-family: "Schibsted Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI",
    Roboto, Helvetica, Arial, sans-serif;
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

/* Somebody tabbing through the form must be able to see where they are. */
:focus-visible {
  outline: 3px solid var(--brass);
  outline-offset: 3px;
  border-radius: 4px;
}

/* --- Shell and bands -----------------------------------------------------
   The page is a stack of full-width bands, each holding one centred shell.
   Alternating ink and bone is what gives the page its rhythm; a band never
   sits next to another of the same ground. */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.band { padding: clamp(56px, 11vw, 108px) 0; }
.band--tight { padding: clamp(40px, 7vw, 68px) 0; }

.band--bone {
  background: var(--bone);
  color: #17150f;
}
.band--bone .muted { color: var(--muted-light); }

.band--raised { background: var(--ink-raised); }

/* --- Type ----------------------------------------------------------------
   Display sizes are clamped rather than stepped at breakpoints: a headline on
   a 360px phone and one on a laptop are the same sentence and should break in
   the same places, not jump a size at an arbitrary width. */

h1, h2, h3 {
  margin: 0;
  letter-spacing: -0.03em;
  line-height: 1.08;
  font-weight: 800;
}

h1 { font-size: clamp(2.35rem, 8.2vw, 4.15rem); }
h2 { font-size: clamp(1.85rem, 5.6vw, 2.85rem); }
h3 { font-size: clamp(1.2rem, 3.6vw, 1.45rem); line-height: 1.22; letter-spacing: -0.02em; }

p { margin: 0 0 1.1rem; }
p:last-child { margin-bottom: 0; }

.lede {
  font-size: clamp(1.08rem, 3.4vw, 1.3rem);
  line-height: 1.55;
  color: var(--muted-dark);
  max-width: 34ch;
}
.band--bone .lede { color: var(--muted-light); }

/* A lede introducing a whole band, rather than one sharing a row with a
   picture.

   34ch is a column measure -- it was set for the hero, where the paragraph
   sits beside a phone and has something to be narrow against. Used at the top
   of a full-width band it reads as stranded: the heading beside it runs the
   whole 1080 shell and the paragraph stops at 432px, which looks like a
   mistake rather than a measure. Wider here, so the intro block relates to its
   own heading and to the figures or cards underneath it.

   Only ledes that are a direct child of the shell, so the ones inside
   .row__text keep the column measure they actually need. Anything with an
   inline max-width -- the centred closing paragraphs, the form -- still wins
   over this, which is why those stay deliberately narrow. */
.band > .shell > .lede { max-width: 54ch; }

.muted { color: var(--muted-dark); }
.small { font-size: 0.95rem; }

/* The brass eyebrow above a section heading. Letterspaced caps, which is the
   one place uppercase earns its keep -- it labels rather than speaks. */
.eyebrow {
  display: block;
  color: var(--brass);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 0 0 0.85rem;
}
.band--bone .eyebrow { color: var(--gold-deep); }

/* A hairline that stops short of the full width. The mark is a door frame and
   this is the same gesture: a line that implies an edge without drawing a box. */
.rule {
  width: 56px;
  height: 2px;
  background: var(--brass);
  border: 0;
  margin: 0 0 1.6rem;
}

a { color: inherit; }

/* --- Buttons -------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 52px;
  padding: 0.85rem 1.5rem;
  border: 1px solid transparent;
  border-radius: 999px;
  font: inherit;
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: -0.01em;
  text-decoration: none;
  cursor: pointer;
  transition: transform 160ms var(--ease), background-color 160ms var(--ease),
    border-color 160ms var(--ease), color 160ms var(--ease);
}
.btn:active { transform: translateY(1px); }

.btn--primary { background: var(--brass-soft); color: var(--ink); }
.btn--primary:hover { background: #f2dcac; }

.btn--ghost {
  background: transparent;
  color: var(--bone);
  border-color: rgba(240, 237, 230, 0.28);
}
.btn--ghost:hover { border-color: rgba(240, 237, 230, 0.6); }

.band--bone .btn--primary { background: var(--ink); color: var(--bone); }
.band--bone .btn--primary:hover { background: #3b3629; }
.band--bone .btn--ghost { color: var(--ink); border-color: rgba(23, 21, 15, 0.25); }
.band--bone .btn--ghost:hover { border-color: rgba(23, 21, 15, 0.55); }

.btn--wide { width: 100%; }

/* --- Site header ---------------------------------------------------------
   Sticky, and transparent until the page moves under it. A bar that is opaque
   from the first pixel puts a lid on the hero for no reason. */

.site-head {
  position: sticky;
  top: 0;
  z-index: 40;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: background-color 220ms var(--ease), border-color 220ms var(--ease);
}
.site-head.is-stuck {
  background: rgba(23, 21, 15, 0.92);
  border-bottom-color: var(--line-dark);
  backdrop-filter: saturate(150%) blur(10px);
  -webkit-backdrop-filter: saturate(150%) blur(10px);
}

.site-head__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 64px;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  text-decoration: none;
  font-weight: 800;
  font-size: 1.12rem;
  letter-spacing: -0.02em;
  color: var(--bone);
}
.wordmark svg { width: 21px; height: auto; display: block; }

.site-nav {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
/* :not(.btn) is load-bearing, not tidiness. ".site-nav a" is specificity
   0-1-1 and ".btn--primary" is 0-1-0, so without it the nav's muted grey WINS
   over the button's own colour -- and the header's one call to action renders
   grey-on-brass at 1.88:1. It looked merely a bit washed out on screen and was
   only caught by measuring. Excluding buttons here fixes the cause; setting a
   colour on .site-nav .btn would only have patched this one case, and the next
   button added to the header would arrive broken in the same way. */
.site-nav a:not(.btn) {
  padding: 0.45rem 0.7rem;
  border-radius: 999px;
  text-decoration: none;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--muted-dark);
  transition: color 160ms var(--ease);
}
.site-nav a:not(.btn):hover { color: var(--bone); }
.site-nav a:not(.btn)[aria-current="page"] { color: var(--bone); }
.site-nav .btn { min-height: 42px; padding: 0.4rem 1.05rem; font-size: 0.94rem; }

/* On a narrow phone the two text links are dropped and only the action stays.
   Both destinations are one tap away in the footer and in the page body. */
@media (max-width: 560px) {
  .site-nav a:not(.btn) { display: none; }
}

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

.hero { padding: clamp(40px, 9vw, 76px) 0 clamp(56px, 10vw, 96px); }

/* Photographs ----------------------------------------------------------------
   The three pictures on the site that are not screenshots: one per page, in
   the place each page had nothing. Deliberately not rotated -- a visitor sees
   one page, so rotation would only make it a lottery which face they met
   rather than showing any of them more.

   The crop changes with the space. On a phone it is a 4:3 band, because on
   every one of these pages something below the picture is the point -- the two
   path cards, the steps, the categories -- and a full 4:5 block pushes them off
   the first screen. On a wide screen it takes the whole 4:5 it was composed in.
   object-position holds the faces through the change; they sit high in all
   three frames. */
.photo {
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--line-dark);
  box-shadow: 0 30px 60px -24px rgba(0, 0, 0, 0.7);
}
/* On the bone bands the dark hairline reads as a smudge, and the heavy shadow
   belongs to a dark ground. */
.band--bone .photo {
  border-color: var(--line-light);
  box-shadow: 0 24px 48px -28px rgba(23, 21, 15, 0.45);
}
.photo img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: 50% 40%;
}
@media (min-width: 880px) {
  .photo img { aspect-ratio: 4 / 5; }
}

.hero__grid { display: grid; gap: clamp(36px, 7vw, 56px); }

.hero h1 + .lede { margin-top: 1.25rem; }

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2rem;
}

/* Short reassurances under the buttons. Not a feature list -- the three things
   somebody wants confirmed before they will read any further. */
.hero__points {
  list-style: none;
  margin: 1.85rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  font-size: 0.95rem;
  color: var(--muted-dark);
}
.hero__points li { display: flex; align-items: center; gap: 0.45rem; }
.hero__points li::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--brass);
  flex: none;
}

@media (min-width: 880px) {
  .hero__grid {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    align-items: center;
  }
  .hero .lede { max-width: 38ch; }
}

/* --- Phone frame ---------------------------------------------------------
   A real bezel rather than a rounded rectangle with a shadow. The frame takes
   its height from the screenshot inside it, so a shot of any aspect fits with
   nothing cropped -- which matters, because the bottom of most of these is a
   tab bar and cropping it would remove the part that says this is an app. */

.phone {
  position: relative;
  width: 100%;
  max-width: 288px;
  margin: 0 auto;
  padding: 9px;
  background: linear-gradient(160deg, #3c3628, #1a1811 42%, #2b2619);
  border-radius: 38px;
  box-shadow:
    0 0 0 1px rgba(240, 237, 230, 0.09),
    0 30px 60px -20px rgba(0, 0, 0, 0.75),
    0 8px 20px -8px rgba(0, 0, 0, 0.5);
}
.phone::after {
  /* The highlight along the top-left edge that makes it read as an object. */
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.16), transparent 34%);
  pointer-events: none;
}
.phone img {
  width: 100%;
  height: auto;
  border-radius: 30px;
  background: var(--ink-high);
}

.phone--lg { max-width: 320px; }

/* A bezel only once there is something inside it.

   Most of these screenshots are loading="lazy", so one that is missing cannot
   fail until the reader scrolls to it -- and until then the frame would sit
   there as a tall empty slab. This also removes the flash of empty bezels
   between the page painting and the first load failing.

   Guarded on html.js, which site.js sets immediately. With JavaScript off the
   frames simply draw as they always did, because an unstyled frame around a
   picture that IS there would be the worse failure of the two. */
.js .phone:not(.is-ready) {
  background: none;
  box-shadow: none;
  padding: 0;
}
.js .phone:not(.is-ready)::after { display: none; }

/* Bigger where there is room, which is desktop only.

   A screenshot 1080px wide shown in a 306px frame renders the app's 16px body
   text at 4.8px -- these read as SHAPE rather than as words, and no realistic
   frame size changes that. But the column here is 540px and the frame was
   using 306 of it for no reason, so the shape at least gets to be clear.
   Mobile is untouched: 288px of a 390px screen is already near the limit. */
@media (min-width: 880px) {
  .phone { max-width: 360px; }
  .phone--lg { max-width: 400px; }
}

/* --- Feature rows --------------------------------------------------------
   One claim, one screenshot, alternating sides on a wide screen and stacking
   in reading order on a phone. */

.row { display: grid; gap: clamp(28px, 6vw, 52px); align-items: center; }
.row + .row { margin-top: clamp(56px, 10vw, 96px); }

@media (min-width: 880px) {
  .row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .row--flip .row__media { order: -1; }
  .row__text { max-width: 42ch; }
}

/* A section whose screenshot is not there ------------------------------------
   Set by site.js on the individual grid it removed a phone frame from, once
   that frame is already out of the page.

   It matters because this site is what Apple's developer enrolment is judged
   on, and a reviewer may look on any day -- including one before the
   screenshots have been taken. A two-column layout with one column missing
   reads as a page that failed to load; the same page as a single measured
   column reads as a page that was written that way.

   PER SECTION, not per page. It was a class on <html> first, and that was
   wrong the moment one real screenshot existed: dropping any frame anywhere
   collapsed the hero too, even though the hero had its picture. Six shots
   arrive one at a time, so every partial state has to look deliberate. */
.hero__grid.is-textonly,
.row.is-textonly {
  grid-template-columns: minmax(0, 1fr);
}
.hero__grid.is-textonly .lede,
.row.is-textonly .row__text {
  max-width: 54ch;
}

/* --- Cards ---------------------------------------------------------------- */

.grid {
  display: grid;
  gap: 14px;
  grid-template-columns: 1fr;
}
@media (min-width: 620px) { .grid--2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 620px) { .grid--3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .grid--3 { grid-template-columns: repeat(3, 1fr); } }

.card {
  background: var(--ink-raised);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius-lg);
  padding: clamp(20px, 4vw, 28px);
}
.card h3 { margin-bottom: 0.5rem; }
.card p { color: var(--muted-dark); font-size: 0.98rem; margin-bottom: 0; }

.band--bone .card {
  background: var(--white);
  border-color: var(--line-light);
}
.band--bone .card p { color: var(--muted-light); }

/* The two paths off the front page. Bigger than a card, and a whole tap
   target rather than a card with a link somewhere inside it. */
.path {
  display: block;
  text-decoration: none;
  color: inherit;
  background: var(--ink-raised);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius-lg);
  padding: clamp(24px, 5vw, 36px);
  transition: border-color 200ms var(--ease), transform 200ms var(--ease),
    background-color 200ms var(--ease);
}
.path:hover {
  border-color: var(--brass);
  background: var(--ink-high);
  transform: translateY(-2px);
}
.path__eyebrow {
  display: block;
  color: var(--brass);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: 0.9rem;
}
.path h2 { font-size: clamp(1.5rem, 4.6vw, 1.95rem); margin-bottom: 0.7rem; }
.path p { color: var(--muted-dark); margin-bottom: 1.4rem; }
.path__go {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-weight: 700;
  color: var(--brass-soft);
}
.path__go svg { width: 15px; height: 15px; transition: transform 200ms var(--ease); }
.path:hover .path__go svg { transform: translateX(4px); }

/* --- The money figures ---------------------------------------------------
   Set as figures rather than as bullet points, because the number is the
   argument and burying it in a sentence wastes it. */

.figures {
  display: grid;
  gap: 0;
  grid-template-columns: 1fr;
  border-top: 1px solid var(--line-light);
}
.figure {
  padding: 1.6rem 0;
  border-bottom: 1px solid var(--line-light);
}
.figure__n {
  display: block;
  font-size: clamp(2.6rem, 11vw, 4rem);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.figure__l {
  display: block;
  margin-top: 0.6rem;
  font-weight: 700;
  font-size: 1.02rem;
}
.figure__s {
  display: block;
  margin-top: 0.3rem;
  color: var(--muted-light);
  font-size: 0.95rem;
  max-width: 38ch;
}
@media (min-width: 760px) {
  .figures { grid-template-columns: repeat(3, 1fr); gap: 0 clamp(24px, 4vw, 44px); }
  .figure { border-bottom: 0; padding-bottom: 0.4rem; }
}

/* A plain-spoken caveat under a claim. Deliberately not styled as a warning:
   it is the honest footnote that makes the claim above it believable.

   Base is the dark ground with a bone override, the same way round as every
   other component here. It was written light-ground-first when it only ever
   appeared in the bone money band; the demand note on the marketplace section
   is on a dark one, and one component with two grounds beats two components. */
.footnote {
  margin-top: 1.8rem;
  padding: 1.05rem 1.25rem;
  border-left: 2px solid var(--brass);
  background: rgba(192, 138, 46, 0.09);
  border-radius: 0 var(--radius) var(--radius) 0;
  color: var(--muted-dark);
  font-size: 0.97rem;
}
.footnote strong { color: var(--bone); }
.band--bone .footnote { color: var(--muted-light); }
.band--bone .footnote strong { color: #17150f; }

/* --- The booking link, shown as the thing itself ------------------------- */

.linkbar {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.05rem;
  margin: 0.3rem 0 1.3rem;
  padding: 0.85rem 1.15rem;
  background: var(--ink-high);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius);
  font-size: clamp(1.02rem, 4.2vw, 1.35rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  word-break: break-all;
}
.linkbar .host { color: var(--muted-dark); }
.linkbar .you { color: var(--brass-soft); }

/* --- Category chips ------------------------------------------------------- */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.chips li {
  padding: 0.6rem 1.15rem;
  border: 1px solid var(--line-dark);
  border-radius: 999px;
  font-weight: 600;
  font-size: 1rem;
}
.band--bone .chips li { border-color: var(--line-light); }

/* --- Numbered steps ------------------------------------------------------- */

.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }
.steps li {
  counter-increment: step;
  position: relative;
  padding-left: 3.1rem;
  min-height: 2.2rem;
}
.steps li + li { margin-top: 1.6rem; }
.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: -1px;
  width: 2.2rem;
  height: 2.2rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--brass);
  color: var(--brass);
  font-weight: 700;
  font-size: 0.95rem;
}
.steps h3 { margin-bottom: 0.3rem; }
.steps p { color: var(--muted-dark); margin-bottom: 0; }
.band--bone .steps p { color: var(--muted-light); }

/* --- Interest form -------------------------------------------------------- */

.form-band { background: var(--ink-raised); }

.form {
  margin-top: 2rem;
  display: grid;
  gap: 1.1rem;
}
@media (min-width: 700px) {
  .form { grid-template-columns: repeat(2, 1fr); }
  .form .field--full, .form .fieldset, .form .form__foot { grid-column: 1 / -1; }
}

.field { display: grid; gap: 0.45rem; }
.field label { font-weight: 600; font-size: 0.97rem; }
.field .hint { color: var(--muted-dark); font-size: 0.88rem; font-weight: 400; }

.field input {
  width: 100%;
  min-height: 52px;
  padding: 0.8rem 1rem;
  background: var(--ink);
  color: var(--bone);
  border: 1px solid var(--line-dark);
  border-radius: var(--radius);
  font: inherit;
  font-size: 1rem;
  transition: border-color 160ms var(--ease);
}
.field input::placeholder { color: #6d675c; }
.field input:hover { border-color: #4d4737; }
.field input:focus { border-color: var(--brass); outline-offset: 1px; }

.fieldset { border: 0; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.fieldset legend { padding: 0; font-weight: 600; font-size: 0.97rem; }

/* Checkboxes drawn as chips: on a phone a row of tick boxes is a column of
   small targets, and these are five short words somebody taps once.

   The SPAN carries the chip, not the label, so every state can be reached with
   an adjacent-sibling selector. Hanging the checked state off the label would
   need :has(), and where that is not supported a tapped chip would look
   exactly like an untapped one -- somebody would tick all five, see nothing
   happen, and give up on the form. Not worth it to save one element. */
.pick { display: flex; flex-wrap: wrap; gap: 10px; }
.pick label {
  position: relative;
  display: inline-flex;
  cursor: pointer;
  user-select: none;
}
.pick input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  margin: 0;
  cursor: pointer;
}
.pick span {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0.62rem 1.15rem;
  border: 1px solid var(--line-dark);
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.97rem;
  transition: border-color 160ms var(--ease), background-color 160ms var(--ease),
    color 160ms var(--ease);
}
.pick input:hover + span { border-color: #4d4737; }
.pick input:checked + span {
  background: var(--brass-soft);
  border-color: var(--brass-soft);
  color: var(--ink);
}
.pick input:focus-visible + span {
  outline: 3px solid var(--brass);
  outline-offset: 3px;
}

/* The honeypot. Off-screen rather than display:none, which some autofillers
   skip and some bots read as a signal in itself. */
.trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form__foot { display: grid; gap: 0.9rem; justify-items: start; }
.form__note { color: var(--muted-dark); font-size: 0.9rem; margin: 0; max-width: 46ch; }

.form__status {
  margin: 0;
  font-size: 0.97rem;
  min-height: 1.4rem;
}
.form__status[data-state="error"] { color: #e8a396; }
.form__status[data-state="sending"] { color: var(--muted-dark); }

/* Replaces the form once it has been sent. */
.thanks {
  border: 1px solid var(--brass);
  background: rgba(192, 138, 46, 0.1);
  border-radius: var(--radius-lg);
  padding: clamp(24px, 5vw, 34px);
  margin-top: 2rem;
}
.thanks h3 { margin-bottom: 0.6rem; }
.thanks p { color: var(--muted-dark); margin-bottom: 0; }

/* --- Footer --------------------------------------------------------------- */

.site-foot {
  background: var(--ink);
  border-top: 1px solid var(--line-dark);
  padding: clamp(40px, 7vw, 64px) 0 clamp(32px, 5vw, 48px);
}
.site-foot__top {
  display: grid;
  gap: 2rem;
  padding-bottom: 2rem;
  border-bottom: 1px solid var(--line-dark);
}
@media (min-width: 720px) {
  .site-foot__top { grid-template-columns: 1.2fr 1fr 1fr; }
}
.site-foot h4 {
  margin: 0 0 0.9rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brass);
}
.site-foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.site-foot a { color: var(--muted-dark); text-decoration: none; }
.site-foot a:hover { color: var(--bone); text-decoration: underline; }
.site-foot__bottom {
  padding-top: 1.6rem;
  color: var(--muted-dark);
  font-size: 0.88rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.2rem;
  justify-content: space-between;
}
.site-foot__blurb { color: var(--muted-dark); font-size: 0.95rem; max-width: 32ch; }

/* Who actually operates this website.

   Apple's developer enrolment checks that the organisation's site is publicly
   available and CLEARLY ASSOCIATED with the organisation on the application;
   a site that never names the company is what "minimal content" means to that
   reviewer, however much else is on it. So the legal entity and a working
   contact address appear on every page of this domain rather than only in the
   terms, and they are ordinary readable text rather than fine print. */
.site-foot__entity {
  padding-top: 1.6rem;
  margin: 0;
  color: var(--muted-dark);
  font-size: 0.92rem;
  max-width: 62ch;
}
.site-foot__entity strong { color: var(--bone); font-weight: 700; }
.site-foot__entity a { color: var(--brass); }
.site-foot__entity + .site-foot__bottom { padding-top: 1.1rem; }

/* --- Motion --------------------------------------------------------------
   One gesture, used everywhere: a short rise as the element arrives. The class
   is only added by JavaScript, so with JS off nothing is ever hidden. */

.rise {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 620ms var(--ease), transform 620ms var(--ease);
  transition-delay: var(--rise-delay, 0ms);
}
.rise.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .rise { opacity: 1; transform: none; transition: none; }
  .path:hover { transform: none; }
  .btn:active { transform: none; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
