/* ============================================================
   SKYHAND — styling for the subpages (contact, privacy).

   The first section is index.html's stylesheet verbatim: the subpages
   carry the home page's outro, so they need the same tokens, the same
   form and the same scene rules. The subpage-only styles follow it.
   ============================================================ */

/* ============================================================
   SKYHAND — monochrome. black / white / grey only.
   Boxes are square. Only buttons are pills.
   ============================================================ */

:root{
  --bg:#090909;
  --panel:#0c0c0c;
  --panel-2:#151515;
  --fg:#fff;
  --fg-2:#8e8e8e;
  --fg-3:#5f5f5f;
  --line:#1c1c1c;
  --line-2:#282828;
  /* the one colour on the page — reserved for confirming a submission */
  --ok:#3ddc84;
  --ok-fg:#04160c;
  --ok-dim:rgba(61,220,132,.13);
  --ok-line:rgba(61,220,132,.42);
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --gut:clamp(20px,4vw,60px);
  --sans:"Instrument Sans",ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
}
:root[data-theme="light"]{
  --bg:#fff;
  --panel:#f5f5f5;
  --panel-2:#ececec;
  --fg:#000;
  --fg-2:#6b6b6b;
  --fg-3:#909090;
  --line:#e8e8e8;
  --line-2:#dcdcdc;
  --ok:#0a7d3e;
  --ok-fg:#fff;
  --ok-dim:rgba(10,125,62,.09);
  --ok-line:rgba(10,125,62,.34);
}

/* EXPERIMENT: which scene set is mounted. Set before first paint by the
   inline script below, so neither pair ever flashes. */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;overflow-x:clip}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0;
  background:var(--bg);
  color:var(--fg);
  font-family:var(--sans);
  font-size:16px;line-height:1.5;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  overflow-x:clip;
  transition:background .4s var(--ease),color .4s var(--ease);
}
svg{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
input{font:inherit}
::selection{background:var(--fg);color:var(--bg)}

.wrap{width:100%;max-width:1180px;margin-inline:auto;padding-inline:var(--gut)}
.hair{height:1px;background:var(--line)}

h1,h2,h3,h4{margin:0;font-weight:600;letter-spacing:-.03em;line-height:1.08}
p{margin:0}

.sh{margin-bottom:clamp(30px,4vw,48px)}
.sh h2{font-size:clamp(26px,3.6vw,42px);font-weight:400;letter-spacing:-.03em}
.sh h2 span{display:block;color:var(--fg-2)}
.sh p{margin-top:16px;color:var(--fg-2);font-size:15.5px;max-width:58ch}

/* ---------- small pill buttons ---------- */
.pill{
  display:inline-flex;align-items:center;gap:6px;
  padding:7px 15px;border-radius:999px;
  font-size:13px;font-weight:500;letter-spacing:-.01em;line-height:1.4;
  background:var(--fg);color:var(--bg);
  transition:opacity .25s var(--ease),transform .3s var(--ease-out);
}
.pill:hover{opacity:.85;transform:translateY(-1px)}
.pill .arw{transition:transform .3s var(--ease-out)}
.pill:hover .arw{transform:translateX(3px)}
.pill.quiet{background:var(--panel-2);color:var(--fg)}
.pill.quiet:hover{background:var(--line-2);opacity:1}

/* ---------- hero ---------- */
.hero{
  position:relative;isolation:isolate;overflow:hidden;text-align:center;
  padding:clamp(76px,13vh,140px) 0 clamp(40px,6vh,68px);
  min-height:620px;
  min-height:clamp(600px,92vh,900px);
  min-height:clamp(600px,92svh,900px);
  display:flex;flex-direction:column;justify-content:flex-start;
}
.hero .wrap{position:relative;z-index:2}
.hero .lift{transform:translateY(calc(var(--sp,0) * -40px));opacity:calc(1 - var(--sp,0) * .8)}
.hero h1{font-size:clamp(29px,4.5vw,54px);font-weight:400;letter-spacing:-.03em;line-height:1.06;max-width:15ch;margin-inline:auto;text-wrap:balance}
.hero .cta{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;padding:0;margin-top:clamp(19px,2.7vh,31px)}
/* caption rides the bottom of the room, still above the fold */
.hero .foot{position:absolute;left:0;right:0;bottom:clamp(26px,5vh,52px)}
.hero .cap{margin:0 auto;max-width:52ch;color:var(--fg-2);font-size:14.5px}

.plate{position:relative;display:inline-block}

/* ---------- technical drawing ---------- */
.tech{color:var(--fg)}
.tech .solid{stroke:currentColor;stroke-width:1.1;fill:none;opacity:.92;vector-effect:non-scaling-stroke}
.tech .mid{stroke:currentColor;stroke-width:1;fill:none;opacity:.5;vector-effect:non-scaling-stroke}
.tech .hidden{stroke:currentColor;stroke-width:1;fill:none;opacity:.26;stroke-dasharray:5 4;vector-effect:non-scaling-stroke}
.tech .thin{stroke:currentColor;stroke-width:.75;fill:none;opacity:.34;vector-effect:non-scaling-stroke}
.tech .faint{stroke:currentColor;stroke-width:.75;fill:none;opacity:.16;vector-effect:non-scaling-stroke}
.tech .centre{stroke:currentColor;stroke-width:.75;fill:none;opacity:.22;stroke-dasharray:14 4 2 4;vector-effect:non-scaling-stroke}
.tech .fillsoft{fill:currentColor;opacity:.07}
.tech .dot{fill:currentColor;opacity:.8}
.tech text{
  font-family:var(--sans);
  fill:currentColor;opacity:.42;font-size:10.5px;letter-spacing:.06em;
}
.tech .dim{opacity:.5;font-size:10px}
.draw{stroke-dasharray:var(--len,700);stroke-dashoffset:var(--len,700);transition:stroke-dashoffset 1.6s var(--ease-out)}
.in .draw{stroke-dashoffset:0}
.flow{stroke-dasharray:4 6;animation:flow 1.8s linear infinite}
@keyframes flow{to{stroke-dashoffset:-20}}
.hover-lift{transition:transform .6s var(--ease-out)}
.media:hover .hover-lift{transform:translateY(-6px)}

/* ---------- hero scene: interior, one-point perspective ---------- */
.scene{
  position:absolute;inset:0;z-index:0;pointer-events:none;color:var(--fg);
  opacity:calc(1 - var(--sp,0) * .6);
  -webkit-mask-image:
    radial-gradient(34% 16% at 50% 22%,transparent 0 52%,#000 100%),
    radial-gradient(26% 7% at 50% 92%,rgba(0,0,0,.32) 0 40%,#000 100%),
    linear-gradient(180deg,transparent 0,#000 7%,#000 86%,transparent 100%);
  mask-image:
    radial-gradient(34% 16% at 50% 22%,transparent 0 52%,#000 100%),
    radial-gradient(26% 7% at 50% 92%,rgba(0,0,0,.32) 0 40%,#000 100%),
    linear-gradient(180deg,transparent 0,#000 7%,#000 86%,transparent 100%);
  -webkit-mask-composite:source-in;
  mask-composite:intersect;
}
/* always cover: the room fills the hero, cropped left and right */
.scene svg{width:100%;height:100%;display:block}

/* ---------- intro: the room materialises out of the vanishing point ----------
   one animated mask sweeps outward from the VP while the lines plot themselves
   from the same point, so the drawing builds the way it was constructed. */
.scene-wipe{
  position:absolute;inset:0;
  transform:scale(1.06);transform-origin:50% 42%;
  -webkit-mask-image:radial-gradient(circle,#000 38%,rgba(0,0,0,0) 74%);
  mask-image:radial-gradient(circle,#000 38%,rgba(0,0,0,0) 74%);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:50% 42%;mask-position:50% 42%;
  -webkit-mask-size:.1% .1%;mask-size:.1% .1%;
}
.scene.in .scene-wipe{animation:room-in 2.8s cubic-bezier(.3,.12,.24,1) .06s forwards}
@keyframes room-in{
  from{-webkit-mask-size:.1% .1%;mask-size:.1% .1%;transform:scale(1.06)}
  to{-webkit-mask-size:380% 380%;mask-size:380% 380%;transform:scale(1)}
}

/* fleet, routes and loose objects come online behind the front */
.unit{opacity:0}
.scene.in .unit{animation:unit-in 1.1s var(--ease) forwards;animation-delay:var(--in,1.2s)}
@keyframes unit-in{from{opacity:0}to{opacity:1}}
/* narrow screens are height-constrained, so the crop eats the sides — keep the
   hero shorter there so more of the room survives either side of centre */
@media(max-width:760px){
  .hero{
    min-height:520px;
    min-height:clamp(500px,74vh,700px);
    min-height:clamp(500px,74svh,700px);
    padding:clamp(60px,10vh,104px) 0 clamp(32px,5vh,52px);
  }
}

/* ---------- EXPERIMENT — landscape hero ----------
   Temporarily the hero is the outro's field instead of the room. Everything
   the swap touches is this block plus the .scene markup in the hero; the
   room as it stood is kept whole in index.hero-indoor.html.
   Same intro as the room: one mask sweeps out of the vanishing point on the
   horizon while the lines plot themselves from it. */
.scene.land{
  -webkit-mask-image:
    radial-gradient(38% 17% at 50% 23%,transparent 0 44%,#000 100%),
    radial-gradient(30% 8% at 50% 90%,rgba(0,0,0,.3) 0 36%,#000 100%),
    linear-gradient(180deg,transparent 0,#000 11%,#000 100%);
  mask-image:
    radial-gradient(38% 17% at 50% 23%,transparent 0 44%,#000 100%),
    radial-gradient(30% 8% at 50% 90%,rgba(0,0,0,.3) 0 36%,#000 100%),
    linear-gradient(180deg,transparent 0,#000 11%,#000 100%);
}
/* the vanishing point sits on the horizon, a touch lower than the room's */
.scene.land .scene-wipe{
  transform-origin:50% 44%;
  -webkit-mask-position:50% 44%;mask-position:50% 44%;
}
.scene.land .zoom{transform-origin:50% 44%;transform:scale(calc(1 + var(--sp,0) * .05))}
/* depth: the sky sinks, the ground rides up, the near grass rakes past */
.h-sky,.h-far,.h-mid,.h-near,.h-gb,.h-gf{transform-box:view-box;transform-origin:50% 100%}
.h-sky{transform:translateY(calc(var(--sp,0) * 30px))}
.h-far{transform:translateY(calc(var(--sp,0) * -12px))}
.h-mid{transform:translateY(calc(var(--sp,0) * -44px))}
.h-gb{transform:translateY(calc(var(--sp,0) * -70px))}
.h-near{transform:translateY(calc(var(--sp,0) * -92px))}
.h-gf{transform:translateY(calc(var(--sp,0) * -124px)) skewX(calc(var(--sp,0) * 2.2deg - 1.1deg))}

/* depth: layers separate as the page moves */
.zoom{transform-box:view-box;transform-origin:50% 42%;transform:scale(calc(1 + var(--sp,0) * .075))}
.lyr{transform-box:view-box}
.l-arch{transform:translateY(calc(var(--sp,0) * -18px))}
.l-far{transform:translateY(calc(var(--sp,0) * -32px))}
.l-mid{transform:translateY(calc(var(--sp,0) * -58px))}
.l-near{transform:translateY(calc(var(--sp,0) * -106px))}
.drift{transform-box:view-box;transform:translate(calc(var(--sp,0) * var(--dx,0px)),calc(var(--sp,0) * var(--dy,0px)))}
.scene .draw{transition-duration:2.4s}

/* slow life */
@keyframes bob{
  0%,100%{transform:translate(0,0) rotate(0)}
  22%{transform:translate(1.5px,-4.5px) rotate(.7deg)}
  48%{transform:translate(-1px,-8px) rotate(-.5deg)}
  74%{transform:translate(-2.5px,-3px) rotate(.35deg)}
}
.bob{transform-box:fill-box;transform-origin:50% 55%;animation:bob var(--t,11s) var(--ease) infinite;animation-delay:var(--d,0s)}
@keyframes sweep{0%,100%{transform:rotate(-15deg)}50%{transform:rotate(15deg)}}
.sweep{transform-box:fill-box;transform-origin:50% 0;animation:sweep var(--t,15s) var(--ease) infinite;animation-delay:var(--d,0s)}
@keyframes ping{0%{transform:scale(.3);opacity:.5}70%{opacity:0}100%{transform:scale(1);opacity:0}}
.ping{transform-box:fill-box;transform-origin:50% 50%;animation:ping var(--t,9s) var(--ease-out) infinite;animation-delay:var(--d,0s)}
@keyframes wash{0%,100%{transform:scaleX(.86) scaleY(.7);opacity:.05}50%{transform:scaleX(1.08) scaleY(1.1);opacity:.22}}
.wash{transform-box:fill-box;transform-origin:50% 50%;animation:wash var(--t,10s) var(--ease) infinite;animation-delay:var(--d,0s)}
@keyframes blink{0%,38%{opacity:0}50%,86%{opacity:.9}100%{opacity:0}}
.blink{animation:blink var(--t,12s) var(--ease) infinite;animation-delay:var(--d,0s)}
@keyframes flow-slow{to{stroke-dashoffset:-36}}
.flow-slow{stroke-dasharray:3 9;animation:flow-slow 6s linear infinite}

/* ---------- sections ---------- */
.sec{padding:clamp(56px,7vw,86px) 0}

/* ---------- numbered rows ---------- */
.rows{display:flex;flex-direction:column;gap:clamp(36px,4.5vw,60px)}
.row{display:grid;grid-template-columns:1fr 1fr;gap:clamp(22px,3.5vw,56px);align-items:stretch}
.row .copy{display:flex;flex-direction:column;justify-content:flex-end;position:relative;min-height:250px;padding-bottom:4px}
.row .copy .num{position:absolute;top:0;left:0;font-size:14px;color:var(--fg-2);font-variant-numeric:tabular-nums}
.row .copy h3{font-size:clamp(18px,1.6vw,21px);margin-bottom:11px}
.row .copy p{color:var(--fg-2);font-size:15px;line-height:1.55;max-width:42ch}
.row.flip .copy{order:2}
.row.flip .media{order:1}
@media(max-width:840px){
  .row{grid-template-columns:1fr;gap:18px}
  .row .copy{min-height:0;padding-top:28px;justify-content:flex-start}
  /* single column: always text then graphic, regardless of desktop side */
  .row.flip .copy{order:1}.row.flip .media{order:2}
}
.media{
  background:var(--panel);position:relative;overflow:hidden;
  display:grid;place-items:center;min-height:280px;
  transition:background .4s var(--ease);
}
.media:hover{background:var(--panel-2)}
.media .inner{width:100%;padding:clamp(18px,2.4vw,32px)}

/* ---------- platforms ---------- */
.pair{display:grid;grid-template-columns:1fr 1fr;gap:clamp(14px,1.8vw,24px)}
@media(max-width:880px){.pair{grid-template-columns:1fr}}
.plat{background:var(--panel);transition:background .4s var(--ease)}
.plat:hover{background:var(--panel-2)}
.plat .art{display:grid;place-items:center;padding:clamp(18px,2.4vw,30px) clamp(18px,2.4vw,30px) 0;min-height:250px}
.plat .body{padding:clamp(20px,2.4vw,30px)}
.plat .tag{font-size:12.5px;color:var(--fg-2);margin-bottom:10px}
.plat h3{font-size:clamp(18px,1.7vw,21px);margin-bottom:10px}
.plat p{color:var(--fg-2);font-size:14.5px;line-height:1.55}
.spec{margin-top:18px}
.spec div{display:flex;gap:16px;justify-content:space-between;padding:9px 0;border-top:1px solid var(--line-2);font-size:13px}
.spec div span:first-child{color:var(--fg-3);flex:none}
.spec div span:last-child{text-align:right;color:var(--fg-2)}

/* ---------- faq (2 col) ---------- */
.faqgrid{
  display:grid;
  grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);
  gap:clamp(26px,5vw,72px);
  align-items:start;
}
.faqgrid .sh{margin-bottom:0;position:sticky;top:36px}
@media(max-width:840px){
  .faqgrid{grid-template-columns:1fr;gap:24px}
  .faqgrid .sh{position:static}
}
.faq details{transition:background .4s var(--ease)}
.faq details[open]{background:var(--panel)}
.faq summary{
  list-style:none;cursor:pointer;display:flex;align-items:flex-start;gap:18px;justify-content:space-between;
  padding:17px clamp(14px,1.6vw,20px);font-size:15px;font-weight:500;letter-spacing:-.015em;
  border-bottom:1px solid var(--line);
  transition:color .25s var(--ease),border-color .4s var(--ease);
}
.faq summary::-webkit-details-marker{display:none}
.faq details[open] summary{border-bottom-color:transparent}
.faq details:not([open]) summary:hover{color:var(--fg-2)}

/* smooth open/close: 0fr → 1fr on a grid row */
.faq .reveal{
  display:grid;grid-template-rows:0fr;
  transition:grid-template-rows .42s var(--ease-out);
}
.faq details[open] .reveal{grid-template-rows:1fr}
.faq .reveal > .ans{
  overflow:hidden;
  opacity:0;transition:opacity .3s var(--ease);
}
.faq details[open] .reveal > .ans{opacity:1;transition:opacity .4s var(--ease) .1s}
.faq .sign{position:relative;width:13px;height:13px;flex:none;margin-top:5px}
.faq .sign::before,.faq .sign::after{content:"";position:absolute;left:0;top:6px;width:13px;height:1.3px;background:var(--fg);transition:transform .3s var(--ease-out)}
.faq .sign::after{transform:rotate(90deg)}
.faq details[open] .sign::after{transform:rotate(0)}
.faq .ans{padding:0 clamp(14px,1.6vw,20px) 20px;margin-top:-4px;max-width:74ch}
.faq .ans p{color:var(--fg-2);font-size:14.5px;line-height:1.6}

/* ---------- outro: cta + footer, out on a field ----------
   everything down here moves on scroll only — no idle loops. */
.outro{position:relative;isolation:isolate;overflow:hidden}
.outro > .cta,.outro > footer{position:relative;z-index:3}
/* sky above the headline, so the pass overhead has room to clear the section above */
.outro > .cta{padding-top:clamp(104px,13vw,168px)}
.outro footer{padding:clamp(74px,11vh,132px) 0 clamp(26px,4vh,46px)}
.field,.air{position:absolute;inset:0;pointer-events:none;color:var(--fg)}
.field svg,.air svg{width:100%;height:100%;display:block}
.field{z-index:0}
/* the meadow holds itself back off the footer copy; the city doesn't need to
   — its blocks are solid, and dimming them would only let the far skyline
   bleed through the masses in front of it */
.field.meadow{
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 9%,#000 78%,rgba(0,0,0,.24) 86%,rgba(0,0,0,.24) 96%,#000 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 9%,#000 78%,rgba(0,0,0,.24) 86%,rgba(0,0,0,.24) 96%,#000 100%);
}
.field.city{
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 11%,#000 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 11%,#000 100%);
}
.air{z-index:1}
.f-sky,.f-far,.f-mid,.f-gb,.f-gf{transform-box:view-box;transform-origin:50% 100%}
.f-sky{transform:translateY(calc(var(--fp,0) * 22px))}
.f-far{transform:translateY(calc(var(--fp,0) * 10px))}
.f-mid{transform:translateY(calc(var(--fp,0) * -12px))}
.f-gb{transform:translateY(calc(var(--fp,0) * -26px))}
.f-gf{transform:translateY(calc(var(--fp,0) * -54px))}
/* grass rakes as it passes; architecture must not */
.field.meadow .f-gf{transform:translateY(calc(var(--fp,0) * -54px)) skewX(calc(var(--fp,0) * 2.6deg - 1.3deg))}
.f-drift{transform-box:view-box;transform:translateX(calc(var(--fp,0) * var(--dx,0px)))}
/* --fd is the drone's own progress along the arc: it only flies the back
   half, so the trail behind it starts already half drawn. */
.arc-trail{stroke-dasharray:1000;stroke-dashoffset:calc(1000 - var(--fd,1) * 1000)}
@media (prefers-reduced-motion:reduce){.outro{--fp:1}}

/* ---------- cta ---------- */
.cta{text-align:center;padding:clamp(52px,7vw,88px) 0}
.cta h2{font-size:clamp(24px,3.4vw,40px);font-weight:400;letter-spacing:-.03em;max-width:19ch;margin-inline:auto}
.cta h2 span{display:block;color:var(--fg-2)}
.cta p{margin:18px auto 0;max-width:48ch;color:var(--fg-2);font-size:14.5px}
/* a hairline edge, so the field still reads as a field against the scene
   behind it */
.form{position:relative;display:flex;align-items:center;gap:6px;margin:32px auto 0;max-width:380px;background:var(--panel);border-radius:999px;padding:4px 5px 4px 18px;box-shadow:inset 0 0 0 1px var(--line-2);transition:background .3s var(--ease),box-shadow .3s var(--ease)}
.form:focus-within{background:var(--panel-2)}
.form input{flex:1;min-width:0;border:0;background:none;outline:none;color:var(--fg);font-size:13.5px;padding:8px 0;transition:color .4s var(--ease),opacity .4s var(--ease)}
.form input::placeholder{color:var(--fg-3)}
.form input:disabled{-webkit-text-fill-color:currentColor}
/* label and tick share one grid cell, so the button keeps the label's width
   through the swap and nothing inside the pill reflows */
.form button{flex:none;display:grid;place-items:center;padding:7px 15px;border-radius:999px;background:var(--fg);color:var(--bg);font-size:13px;font-weight:500;transition:opacity .25s var(--ease),background .4s var(--ease),color .4s var(--ease)}
.form button > *{grid-area:1/1}
.form button:hover{opacity:.85}
.form .lbl{transition:opacity .22s var(--ease),transform .38s var(--ease-out)}
.form .tick{
  width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2.2;
  stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:20;stroke-dashoffset:20;opacity:0;
  transform:scale(.7);transform-origin:50% 50%;
}

/* success: the pill goes green, the label lifts out and the tick draws in */
.form.ok{background:var(--ok-dim)}
.form.ok input{color:var(--fg-2);opacity:.7}
.form.ok button{background:var(--ok);color:var(--ok-fg);pointer-events:none}
.form.ok .lbl{opacity:0;transform:translateY(-9px)}
.form.ok .tick{
  opacity:1;transform:none;stroke-dashoffset:0;
  transition:stroke-dashoffset .5s var(--ease-out) .14s,opacity .2s var(--ease) .14s,transform .45s var(--ease-out) .14s;
}
/* a ring that swells once and then stays as a quiet green outline */
.form::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:inset 0 0 0 1px var(--ok-line);opacity:0;
}
.form.ok::after{animation:okring 1.1s var(--ease-out) forwards}
@keyframes okring{
  0%{opacity:0;transform:scale(.985)}
  40%{opacity:1;transform:scale(1.012)}
  100%{opacity:.6;transform:scale(1)}
}

/* error: nudge the pill, never the page */
.form.err{animation:nudge .5s var(--ease-out)}
@keyframes nudge{
  0%,100%{transform:translateX(0)}
  22%{transform:translateX(-4px)}
  55%{transform:translateX(3px)}
  80%{transform:translateX(-1.5px)}
}

/* the note always reserves two lines, so text appearing or changing never
   moves the footer below it */
.form-note{
  margin:11px auto 0;max-width:380px;
  font-size:12.5px;line-height:1.45;color:var(--fg-3);
  min-height:2.9em;
  opacity:0;transform:translateY(-4px);
  transition:opacity .3s var(--ease),transform .4s var(--ease-out),color .3s var(--ease);
}
.form-note.show{opacity:1;transform:none}
.form-note.good{color:var(--ok)}

/* ---------- footer ---------- */
footer{padding:26px 0 32px}
.fbot{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;font-size:12.5px;color:var(--fg-3)}
.fbot .l{display:flex;gap:18px;flex-wrap:wrap}
.fbot a{transition:color .25s var(--ease)}
.fbot a:hover{color:var(--fg)}

/* ---------- EXPERIMENT — the two scene sets, and the switch between them ----------
   A: the room up top, the field down below.  B: the field up top, the city below.
   Both sets sit in the page; only one pair is ever mounted. */
[data-scene="field"] .scene.room,
[data-scene="field"] .field.meadow{display:none}
[data-scene="room"] .scene.land,
[data-scene="room"] .field.city{display:none}

.vswitch,.aswitch{
  position:fixed;z-index:50;
  right:clamp(14px,2.2vw,26px);bottom:clamp(14px,2.2vw,26px);
  display:flex;gap:2px;padding:3px;border-radius:999px;
  background:var(--panel-2);box-shadow:0 0 0 1px var(--line-2);
  opacity:.5;
  transition:opacity .3s var(--ease),background .4s var(--ease),box-shadow .4s var(--ease);
}
.aswitch{bottom:calc(clamp(14px,2.2vw,26px) + 38px)}
.vswitch:hover,.vswitch:focus-within,.aswitch:hover,.aswitch:focus-within{opacity:1}
.vswitch button,.aswitch button{
  padding:6px 13px;border-radius:999px;
  font-size:12.5px;font-weight:500;letter-spacing:-.01em;line-height:1.3;
  color:var(--fg-2);
  transition:color .25s var(--ease),background .3s var(--ease);
}
.vswitch button:hover,.aswitch button:hover{color:var(--fg)}
.vswitch button[aria-pressed="true"],.aswitch button[aria-pressed="true"]{background:var(--fg);color:var(--bg)}

/* ---------- RENDER — the same two scenes, rendered ----------
   Same cameras and the same inventory as the drawings, built out of
   gradients, noise and light instead of line. Hardware carries its own dark
   materials through both themes and only the environments are re-exposed —
   so the headline sits on a dark field in the dark theme and a bright one in
   the light, while a drone reads the same against either. */
:root{
  --r-sky-0:#020202;--r-sky-1:#0c0c0c;--r-sky-2:#212121;--r-sky-3:#525252;--r-sky-4:#a4a4a4;
  --r-hi:#fff;
  --r-ridge-3:#4a4a4a;--r-ridge-2:#303030;--r-ridge-1:#191919;
  --r-gnd-0:#3d3d3d;--r-gnd-1:#212121;--r-gnd-2:#131313;--r-gnd-3:#070707;
  --r-blade:#040404;--r-pad:#3a3a3a;--r-pad-2:#242424;
  --r-ceil-0:#080808;--r-ceil-1:#171717;
  --r-wall-0:#090909;--r-wall-1:#1f1f1f;
  --r-flr-0:#2e2e2e;--r-flr-1:#131313;--r-flr-2:#050505;
  --r-mat-0:#767676;--r-mat-1:#3e3e3e;--r-mat-2:#1d1d1d;--r-mat-3:#0b0b0b;
  /* structural shade — seams, joists, mullions, contact shadows. It is what
     every hard edge in the room is drawn with, so it has to lift with the
     theme or a light room ends up ruled in black. */
  --r-ink:#000;--r-edge:#6e6e6e;
  --r-dia-0:#2a2a2a;--r-dia-1:#141414;--r-dia-2:#080808;
  --r-city-0:#2b2b2b;--r-city-1:#1f1f1f;--r-city-2:#151515;--r-city-3:#0c0c0c;
  --r-vig:#000;--r-vig-o:.6;
}
:root[data-theme="light"]{
  /* The sky is the page: near-white top to bottom, so a scene dissolves into
     the background instead of sitting on a grey plate with a seam at its top
     edge — the same way the dark theme meets #000. Everything else is lifted
     with it; only the silhouettes and the hardware hold enough tone to read. */
  --r-sky-0:#fff;--r-sky-1:#fdfdfd;--r-sky-2:#fafafa;--r-sky-3:#fdfdfd;--r-sky-4:#fff;
  --r-ridge-3:#efefef;--r-ridge-2:#e2e2e2;--r-ridge-1:#d0d0d0;
  --r-gnd-0:#f4f4f4;--r-gnd-1:#e9e9e9;--r-gnd-2:#dcdcdc;--r-gnd-3:#c4c4c4;
  --r-blade:#9a9a9a;--r-pad:#fcfcfc;--r-pad-2:#f0f0f0;
  --r-mat-0:#f8f8f8;--r-mat-1:#e9e9e9;--r-mat-2:#d5d5d5;--r-mat-3:#bebebe;
  --r-ink:#a8a8a8;--r-edge:#9c9c9c;
  --r-dia-0:#fafafa;--r-dia-1:#ebebeb;--r-dia-2:#dcdcdc;
  --r-city-0:#ededed;--r-city-1:#dedede;--r-city-2:#cbcbcb;--r-city-3:#b4b4b4;
  --r-ceil-0:#f0f0f0;--r-ceil-1:#f8f8f8;
  --r-wall-0:#f2f2f2;--r-wall-1:#fbfbfb;
  --r-flr-0:#fdfdfd;--r-flr-1:#f6f6f6;--r-flr-2:#ebebeb;
  --r-vig-o:.05;
}
/* one palette drives both scenes: gradient stops first, then flat fills */
.rr .k0{stop-color:var(--r-sky-0)}
.rr .k1{stop-color:var(--r-sky-1)}
.rr .k2{stop-color:var(--r-sky-2)}
.rr .k3{stop-color:var(--r-sky-3)}
.rr .k4{stop-color:var(--r-sky-4)}
.rr .hi{stop-color:var(--r-hi)}
.rr .r3{stop-color:var(--r-ridge-3)}
.rr .r2{stop-color:var(--r-ridge-2)}
.rr .r1{stop-color:var(--r-ridge-1)}
.rr .n0{stop-color:var(--r-gnd-0)}
.rr .n1{stop-color:var(--r-gnd-1)}
.rr .n2{stop-color:var(--r-gnd-2)}
.rr .n3{stop-color:var(--r-gnd-3)}
.rr .c0{stop-color:var(--r-ceil-0)}
.rr .c1{stop-color:var(--r-ceil-1)}
.rr .a0{stop-color:var(--r-wall-0)}
.rr .a1{stop-color:var(--r-wall-1)}
.rr .p0{stop-color:var(--r-flr-0)}
.rr .p1{stop-color:var(--r-flr-1)}
.rr .p2{stop-color:var(--r-flr-2)}
.rr .bg0{stop-color:var(--bg);stop-opacity:0}
.rr .bg1{stop-color:var(--bg);stop-opacity:.4}
.rr .bg2{stop-color:var(--bg);stop-opacity:.74}
.rr .vg{stop-color:var(--r-vig)}
.rr .vo{stop-color:var(--r-vig);stop-opacity:var(--r-vig-o)}
.rr .fh{fill:var(--r-hi)}
.rr .fs,.rr .fk{fill:var(--r-blade)}
/* stamped tufts are strokes, not fills: even thickness, blunt ends */
.rr .fsl{fill:none;stroke:var(--r-blade);stroke-linecap:round;stroke-linejoin:round}
.rr .bk{stop-color:var(--r-blade)}
.rr .y0{stop-color:var(--r-city-0)}
.rr .y1{stop-color:var(--r-city-1)}
.rr .y2{stop-color:var(--r-city-2)}
.rr .y3{stop-color:var(--r-city-3)}
.rr .fp{fill:var(--r-pad)}
.rr .e0{stop-color:var(--r-mat-0)}
.rr .e1{stop-color:var(--r-mat-1)}
.rr .e2{stop-color:var(--r-mat-2)}
.rr .e3{stop-color:var(--r-mat-3)}
.rr .fmid2{fill:var(--r-mat-1)}
.rr .fmid{fill:var(--r-mat-2)}
.rr .fdark{fill:var(--r-mat-3)}
.rr .ink{fill:var(--r-ink)}
.rr .inks{stroke:var(--r-ink)}
.rr .edge{stroke:var(--r-edge);fill:none}
.rr .pd0{stop-color:var(--r-pad)}
.rr .pd1{stop-color:var(--r-pad-2)}
.rr .st{stroke:var(--r-hi);fill:none}
.rr .dlabel{fill:var(--r-hi);fill-opacity:.42;font-family:var(--sans);font-size:9px;letter-spacing:.06em}
/* a render is an image, not a drawing: it fades rather than un-inks */
.scene.real{
  -webkit-mask-image:
    radial-gradient(52% 29% at 50% 19%,rgba(0,0,0,.16) 0 20%,#000 74%),
    linear-gradient(180deg,transparent 0,#000 6%,#000 100%);
  mask-image:
    radial-gradient(52% 29% at 50% 19%,rgba(0,0,0,.16) 0 20%,#000 74%),
    linear-gradient(180deg,transparent 0,#000 6%,#000 100%);
}
.scene.room.real{
  -webkit-mask-image:
    radial-gradient(48% 26% at 50% 21%,rgba(0,0,0,.15) 0 18%,#000 72%),
    linear-gradient(180deg,transparent 0,#000 5%,#000 94%,rgba(0,0,0,.6) 100%);
  mask-image:
    radial-gradient(48% 26% at 50% 21%,rgba(0,0,0,.15) 0 18%,#000 72%),
    linear-gradient(180deg,transparent 0,#000 5%,#000 94%,rgba(0,0,0,.6) 100%);
}
/* ---------- how a render arrives ----------
   A drawing can plot itself, so the drawings sweep out of the vanishing point
   and ink themselves in. A render has no line to draw, so it is staged
   instead: the light comes up, the land settles under it, the ground grows,
   and the hardware comes online last — each element on its own clock, set by
   --d where it is emitted. */
.scene.real .scene-wipe{
  -webkit-mask-image:none;mask-image:none;
  transform:none;animation:none;
}
.scene.real [class*="ra-"]{opacity:0}
.scene.real.in .ra-fade{animation:ra-fade 1.2s var(--ease) var(--d,0s) both}
.scene.real.in .ra-rise{animation:ra-rise 1.5s var(--ease-out) var(--d,0s) both}
.scene.real.in .ra-drop{animation:ra-drop 1.4s var(--ease-out) var(--d,0s) both}
.scene.real.in .ra-grow{animation:ra-grow 1.3s var(--ease-out) var(--d,0s) both}
.scene.real.in .ra-open{animation:ra-open 1.6s var(--ease-out) var(--d,0s) both}
/* a fluorescent tube does not simply appear */
.scene.real.in .ra-lamp{animation:ra-lamp 1.1s linear var(--d,0s) both}
.scene.real.in .ra-strike{animation:ra-strike 1.6s var(--ease-out) var(--d,0s) both}
@keyframes ra-fade{from{opacity:0}to{opacity:1}}
@keyframes ra-rise{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}
@keyframes ra-drop{from{opacity:0;transform:translateY(-16px)}to{opacity:1;transform:none}}
@keyframes ra-grow{from{opacity:0;transform:scaleY(.15)}to{opacity:1;transform:none}}
@keyframes ra-open{from{opacity:0;transform:scale(.72)}to{opacity:1;transform:none}}
@keyframes ra-lamp{
  0%{opacity:0}9%{opacity:.9}14%{opacity:.08}22%{opacity:.75}
  28%{opacity:.12}40%{opacity:1}52%{opacity:.55}100%{opacity:1}
}
@keyframes ra-strike{0%{opacity:0}26%{opacity:0}34%{opacity:.5}44%{opacity:.1}100%{opacity:1}}
.scene.real .ra-rise,.scene.real .ra-drop{transform-box:view-box}
.scene.real .ra-grow{transform-box:fill-box;transform-origin:50% 100%}
.scene.real .ra-open{transform-box:fill-box;transform-origin:50% 50%}

/* which pair, and which medium, is mounted */
[data-art="render"] .scene.sk,
[data-art="render"] .field.sk,
[data-art="render"] .air.sk,
[data-art="render"] .art-sk{display:none}
[data-art="sketch"] .scene.real,
[data-art="sketch"] .field.real,
[data-art="sketch"] .air.real,
[data-art="sketch"] .art-real{display:none}

/* a rendered city is solid all the way down, so the footer needs the same
   scrim the hero caption gets — the image fades into the page under it */
[data-art="render"] .field.city{
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 11%,#000 76%,rgba(0,0,0,.26) 90%,rgba(0,0,0,.22) 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 11%,#000 76%,rgba(0,0,0,.26) 90%,rgba(0,0,0,.22) 100%);
}
/* A rendered diagram is lit by its panel, not by a plate inside it — the
   ambient lives on .media so it runs to the outer edge, while the drawing
   itself keeps the size it had. */
[data-art="render"] .media,
[data-art="render"] .media:hover{
  background:
    radial-gradient(62% 66% at 50% 38%,rgba(255,255,255,.085),rgba(255,255,255,0) 72%),
    linear-gradient(180deg,var(--r-dia-2) 0%,var(--r-dia-1) 58%,var(--r-dia-0) 100%);
}
[data-art="render"] .media:hover{
  background:
    radial-gradient(62% 66% at 50% 38%,rgba(255,255,255,.12),rgba(255,255,255,0) 72%),
    linear-gradient(180deg,var(--r-dia-2) 0%,var(--r-dia-1) 58%,var(--r-dia-0) 100%);
}

.rr .dg0{stop-color:var(--r-dia-0)}
.rr .dg1{stop-color:var(--r-dia-1)}
.rr .dg2{stop-color:var(--r-dia-2)}

/* ---------- reveal ---------- */
.rv{opacity:0;transform:translateY(12px)}
.rv.in{opacity:1;transform:none;transition:opacity .6s var(--ease) var(--dl,0s),transform .7s var(--ease-out) var(--dl,0s)}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
  .rv{opacity:1;transform:none}
  .draw{stroke-dashoffset:0}
}


/* ============================================================
   SUBPAGES — everything above this line is index.html's stylesheet,
   copied so the two can never drift. Below is what only contact.html
   and privacy.html need. Re-copy the block above if the tokens on the
   home page ever change.
   ============================================================ */

/* ---------- navbar ----------
   Subpages only — the home page has none. Not sticky, and only two things
   in it: the name, which is the way back, and the same call to action the
   home page leads with. */
.nav{padding-block:clamp(18px,2.6vw,26px)}
.navrow{display:flex;align-items:center;justify-content:space-between;gap:20px}
/* set as a word rather than as a lockup: sentence case, normal tracking,
   and the weight left as the only thing marking it out */
.wordmark{
  font-size:15px;font-weight:700;
  color:var(--fg);transition:opacity .25s var(--ease);
}
.wordmark:hover{opacity:.7}

/* ---------- page head ----------
   .sec and .phead set padding with the shorthand, which wipes the inline
   gutter .wrap supplies when both classes sit on the same element. Where they
   do, the gutter has to be restated or the copy hangs left of everything
   else on the page. */
.wrap.phead{padding:clamp(30px,5vw,64px) var(--gut) 0}
.wrap.sec{padding:clamp(48px,6vw,76px) var(--gut)}
/* lighter than the headings on the home page: these are page names, not a
   pitch, and the weight is what says so */
.phead h1{font-size:clamp(34px,5.4vw,62px);font-weight:400;letter-spacing:-.03em}
.phead .meta{margin-top:20px;font-size:12.5px;color:var(--fg-3);font-variant-numeric:tabular-nums}
.phead .lede{margin-top:18px;color:var(--fg-2);font-size:15.5px;max-width:52ch}

/* contact: the name and the address on the left, the form on the right */
.split{
  display:grid;grid-template-columns:minmax(0,.86fr) minmax(0,1fr);
  gap:clamp(40px,9vw,140px);align-items:start;
}
@media(max-width:760px){.split{grid-template-columns:1fr;gap:34px}}
.formside{padding-top:clamp(4px,1.6vw,18px);scroll-margin-top:28px}
.formside h2{font-size:17px;margin-bottom:6px}
.formside > p{color:var(--fg-2);font-size:14.5px;max-width:44ch;margin-bottom:24px}

/* ---------- the contact form ----------
   Square, like every other box on the site; only the button is a pill. Each
   field carries its own label rather than a placeholder, so the form still
   reads once it has been filled in. */
.cform{display:flex;flex-direction:column;gap:16px}
.cform .frow{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:560px){.cform .frow{grid-template-columns:1fr}}
.cform label{display:flex;flex-direction:column;gap:8px}
.cform .lb{font-size:12px;color:var(--fg-3);padding-left:20px}
.cform .opt{opacity:.65;letter-spacing:0}
.cform input,.cform textarea{
  font:inherit;font-size:14.5px;line-height:1.5;color:var(--fg);
  background:var(--panel);border:0;outline:none;
  width:100%;
  box-shadow:inset 0 0 0 1px var(--line);
  transition:background .3s var(--ease),box-shadow .3s var(--ease),color .4s var(--ease);
}
/* the single-line fields are pills, like the access form on the home page;
   the message only takes the corner off, since a pill that tall reads as a
   capsule rather than a field */
.cform input{border-radius:999px;padding:9px 20px}
.cform textarea{border-radius:20px;padding:9px 20px}
.cform input::placeholder,.cform textarea::placeholder{color:var(--fg-3)}
.cform input:focus,.cform textarea:focus{background:var(--panel-2);box-shadow:inset 0 0 0 1px var(--line-2)}
.cform textarea{resize:vertical;min-height:132px;display:block}
/* no colour to spend on errors, so a missed field is marked by its edge */
.cform .bad input,.cform .bad textarea{box-shadow:inset 0 0 0 1px var(--fg-3)}
.cform .bad .lb{color:var(--fg-2)}

/* the button carries .pill for its metrics, so it matches the one in the
   navbar exactly; only the label/tick swap is added on top */
.cform .send{
  align-self:flex-start;margin-top:4px;
  display:grid;place-items:center;
  transition:opacity .25s var(--ease),transform .3s var(--ease-out),background .4s var(--ease),color .4s var(--ease);
}
.cform .send > *{grid-area:1/1}
.cform .lbl{transition:opacity .22s var(--ease),transform .38s var(--ease-out)}
.cform .tick{
  width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2.2;
  stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:20;stroke-dashoffset:20;opacity:0;
  transform:scale(.7);transform-origin:50% 50%;
}
.cform.ok input,.cform.ok textarea{color:var(--fg-2);background:var(--ok-dim)}
.cform.ok .send{background:var(--ok);color:var(--ok-fg);pointer-events:none;transform:none}
.cform.ok .lbl{opacity:0;transform:translateY(-9px)}
.cform.ok .tick{
  opacity:1;transform:none;stroke-dashoffset:0;
  transition:stroke-dashoffset .5s var(--ease-out) .14s,opacity .2s var(--ease) .14s,transform .45s var(--ease-out) .14s;
}
.cform.err{animation:nudge .5s var(--ease-out)}

/* ---------- the legal document ---------- */
.doc{
  display:grid;
  grid-template-columns:minmax(0,.62fr) minmax(0,1.38fr);
  gap:clamp(26px,5vw,72px);
  align-items:start;
}
@media(max-width:840px){.doc{grid-template-columns:1fr;gap:8px}}
.toc{position:sticky;top:28px;font-size:13.5px}
@media(max-width:840px){.toc{position:static;margin-bottom:26px}}
.toc ol{margin:0;padding:0;list-style:none;counter-reset:c}
.toc li{counter-increment:c;border-top:1px solid var(--line)}
.toc li:last-child{border-bottom:1px solid var(--line)}
.toc a{display:flex;gap:12px;padding:10px 2px;color:var(--fg-2);transition:color .25s var(--ease)}
.toc a::before{content:counter(c,decimal-leading-zero);color:var(--fg-3);flex:none}
.toc a:hover{color:var(--fg)}

.clause{padding-bottom:clamp(26px,3.4vw,40px);scroll-margin-top:32px}
.clause + .clause{border-top:1px solid var(--line);padding-top:clamp(26px,3.4vw,40px)}
.clause h2{font-size:clamp(18px,2vw,22px);margin-bottom:14px}
.clause h2 .n{color:var(--fg-3);font-variant-numeric:tabular-nums;margin-right:12px}
.clause p{color:var(--fg-2);font-size:15px;line-height:1.62;max-width:66ch}
.clause p + p{margin-top:12px}
.clause ul{margin:14px 0 0;padding:0;list-style:none;max-width:66ch}
.clause ul + p{margin-top:14px}
.clause li{position:relative;padding-left:20px;color:var(--fg-2);font-size:15px;line-height:1.62}
.clause li + li{margin-top:9px}
.clause li::before{content:"";position:absolute;left:2px;top:.72em;width:7px;height:1px;background:var(--fg-3)}
.clause code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;background:var(--panel);padding:2px 6px;color:var(--fg)}
.clause a:not(.pill){border-bottom:1px solid var(--line-2);transition:border-color .25s var(--ease)}
.clause a:not(.pill):hover{border-bottom-color:var(--fg)}

/* Anything still to be filled in before this is published. Deliberately
   conspicuous — it should be impossible to ship one of these by accident. */
.todo{color:var(--fg-3);border-bottom:1px dashed var(--fg-3);font-style:italic}

/* the form sits left on these pages, not centred under a headline */
.formside .form-note{margin-left:0;max-width:none;text-align:left}

/* honeypot: off-screen for eyes, still reachable to a bot that parses the DOM.
   display:none is the one thing a decent bot checks for, so this hides it the
   long way round. */
.hp{position:absolute!important;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}

/* in flight: the control is inert but must not jump or grey out — the note
   underneath is what tells the reader anything is happening. */
.form.sending button,.cform.sending .send{pointer-events:none;opacity:.62}
.form.sending input,.cform.sending input,.cform.sending textarea{opacity:.72}
