/* andrewcfi.com — runway 30R, KBJC
   Markings drawn to FAA AC 150/5340-1 proportions for a
   100 ft wide precision instrument runway. 1 ft = var(--ft). */

:root{
  --asphalt:#17181a;
  --paint:#e8e6e0;
  --amber:#e9a13c;
  --green:#3fd07c;
  --type:#eceae6;
  --rw:clamp(100px,9vw,122px);
  --ft:calc(var(--rw)/100);
}

*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{
  background:radial-gradient(120% 100% at 18% 12%, #1f2124 0%, var(--asphalt) 55%, #0d0e0f 100%);
  color:var(--type);
  font-family:Archivo,Helvetica,Arial,sans-serif;
  overflow:hidden;
}

/* ---------- runway ---------- */
.field{position:fixed;inset:0;overflow:hidden;z-index:0}
.runway{
  position:absolute;
  right:17vw;bottom:7vh;
  width:var(--rw);height:165vh;
  transform:rotate(7deg);transform-origin:50% 100%;
  background:linear-gradient(90deg,#1e2124,#262a2e 40%,#1d2023);
}
.runway > *{position:absolute}

/* side stripes, 36 in */
.side{top:0;bottom:0;width:calc(3 * var(--ft));background:var(--paint);opacity:.75}
.side.left{left:0}
.side.right{right:0}

/* centerline, 120 ft stripe / 80 ft gap, 36 in wide */
.centerline{
  left:50%;top:0;bottom:calc(350 * var(--ft));
  width:calc(3 * var(--ft));margin-left:calc(-1.5 * var(--ft));
  background:repeating-linear-gradient(180deg,
    rgba(232,230,224,.9) 0 calc(120 * var(--ft)),
    transparent calc(120 * var(--ft)) calc(200 * var(--ft)));
}

/* threshold bars: 8 stripes for 100 ft width, 150 ft long,
   5.75 ft wide, 5.75 ft apart, 11.5 ft across the centerline,
   starting 20 ft in from the threshold */
.threshold{
  left:0;right:0;bottom:calc(20 * var(--ft));height:calc(150 * var(--ft));
  display:flex;justify-content:center;align-items:stretch;
  gap:calc(5.75 * var(--ft));
}
.threshold i{width:calc(5.75 * var(--ft));background:var(--paint);opacity:.88}
.threshold .ctr{width:0}

/* designation: 60 ft characters, letter below the numbers */
.numbers,.letter{
  left:0;right:0;text-align:center;
  color:var(--paint);opacity:.9;
  font-weight:600;line-height:.72;
  font-size:calc(83 * var(--ft));
  transform:scaleX(.54);
}
.numbers{bottom:calc(270 * var(--ft));letter-spacing:calc(6 * var(--ft))}
.letter{bottom:calc(200 * var(--ft))}

/* touchdown zone, first group at 500 ft: three bars each side,
   75 ft long, inner edges 48 ft apart */
.tdz{left:0;right:0;bottom:calc(500 * var(--ft));height:calc(75 * var(--ft))}
.grp{position:absolute;top:0;bottom:0;display:flex;gap:calc(3.3 * var(--ft))}
.grp.l{right:calc(50% + 24 * var(--ft))}
.grp.r{left:calc(50% + 24 * var(--ft))}
.grp i{width:calc(4 * var(--ft));background:var(--paint);opacity:.85}

@keyframes shimmer{
  0%,100%{opacity:1}
  28%{opacity:.62}
  54%{opacity:.92}
  76%{opacity:.5}
}

/* green threshold lights */
.thlights{
  left:calc(4 * var(--ft));right:calc(4 * var(--ft));bottom:calc(-1.6 * var(--ft));
  display:flex;justify-content:space-between;
}
.thlights i{
  width:calc(2.6 * var(--ft));height:calc(2.6 * var(--ft));border-radius:50%;
  background:var(--green);box-shadow:0 0 calc(7 * var(--ft)) calc(1.6 * var(--ft)) rgba(63,208,124,.45);
  animation:shimmer 5.2s ease-in-out infinite;
}
.thlights i:nth-child(1){animation-duration:4.6s;animation-delay:-.2s}
.thlights i:nth-child(2){animation-duration:6.1s;animation-delay:-1.4s}
.thlights i:nth-child(3){animation-duration:5.4s;animation-delay:-2.9s}
.thlights i:nth-child(4){animation-duration:4.2s;animation-delay:-.7s}
.thlights i:nth-child(5){animation-duration:6.8s;animation-delay:-3.6s}
.thlights i:nth-child(6){animation-duration:5.0s;animation-delay:-2.1s}
.thlights i:nth-child(7){animation-duration:4.4s;animation-delay:-4.3s}
.thlights i:nth-child(8){animation-duration:6.4s;animation-delay:-1.1s}
.thlights i:nth-child(9){animation-duration:5.7s;animation-delay:-3.1s}
.thlights i:nth-child(10){animation-duration:4.9s;animation-delay:-2.5s}

/* PAPI, four boxes on the left, 20 ft apart with the nearest unit
   50 ft off the pavement edge. The unit nearest the runway carries
   the highest angle setting, so the reds fill in from that end.
   The indication wanders, weighted toward on glide path, and is
   driven by the script at the foot of the page. Without it the
   boxes sit on two white two red. */
.papi{
  right:calc(100% + 47 * var(--ft));
  bottom:calc(350 * var(--ft));
  display:flex;gap:calc(14 * var(--ft));
}
.papi i{
  width:calc(6 * var(--ft));height:calc(4.4 * var(--ft));
  border-radius:calc(1 * var(--ft));
  background:#fbf8f1;box-shadow:0 0 calc(9 * var(--ft)) calc(1.8 * var(--ft)) rgba(251,248,241,.5);
  transition:background-color .38s ease, box-shadow .38s ease;
}
.papi .p3,.papi .p4{background:#ff4b3c;box-shadow:0 0 calc(9 * var(--ft)) calc(1.8 * var(--ft)) rgba(255,75,60,.45)}
.papi i.w{background:#fbf8f1;box-shadow:0 0 calc(9 * var(--ft)) calc(1.8 * var(--ft)) rgba(251,248,241,.5)}
.papi i.r{background:#ff4b3c;box-shadow:0 0 calc(9 * var(--ft)) calc(1.8 * var(--ft)) rgba(255,75,60,.45)}

/* high intensity edge lights, 200 ft spacing */
.rwlight{
  width:calc(2.4 * var(--ft));height:calc(2.4 * var(--ft));border-radius:50%;
  background:#f6f3ec;box-shadow:0 0 calc(8 * var(--ft)) calc(1.4 * var(--ft)) rgba(246,243,236,.4);
  animation:shimmer 5.5s ease-in-out infinite;
}
.rwlight.le.l0{animation-duration:4.8s;animation-delay:-.4s}
.rwlight.ri.l0{animation-duration:6.2s;animation-delay:-2.2s}
.rwlight.le.l1{animation-duration:5.6s;animation-delay:-3.4s}
.rwlight.ri.l1{animation-duration:4.3s;animation-delay:-1.0s}
.rwlight.le.l2{animation-duration:6.6s;animation-delay:-2.8s}
.rwlight.ri.l2{animation-duration:5.1s;animation-delay:-4.1s}
.rwlight.le.l3{animation-duration:4.6s;animation-delay:-1.7s}
.rwlight.ri.l3{animation-duration:6.0s;animation-delay:-3.9s}
.rwlight.le{left:calc(-3 * var(--ft))}
.rwlight.ri{right:calc(-3 * var(--ft))}
.l0{bottom:0}
.l1{bottom:calc(200 * var(--ft))}
.l2{bottom:calc(400 * var(--ft))}
.l3{bottom:calc(600 * var(--ft))}


/* ---------- N9908K, a Piper Archer II landing on 30R ----------
   35 ft span, 24 ft long, drawn to the same scale as everything
   else. The cycle runs once every 45 s: short final at about
   65 kt, flare, touchdown roughly 200 ft in, then a decelerating
   rollout off the top of the frame. */
.plane{
  position:absolute;left:50%;
  width:calc(35 * var(--ft));
  margin-left:calc(-17.5 * var(--ft));
  bottom:calc(-140 * var(--ft));
  transform-origin:50% 60%;
  opacity:0;
  animation:landing 45s linear infinite;
}
.plane svg{width:100%;height:auto;display:block;filter:drop-shadow(0 0 calc(3 * var(--ft)) rgba(0,0,0,.65))}

.nav{opacity:.95}
.nav.red{fill:#ff4b3c;filter:drop-shadow(0 0 calc(2.4 * var(--ft)) rgba(255,75,60,.9))}
.nav.grn{fill:#46e08a;filter:drop-shadow(0 0 calc(2.4 * var(--ft)) rgba(70,224,138,.9))}
.nav.wht{fill:#fdfbf6;filter:drop-shadow(0 0 calc(2 * var(--ft)) rgba(253,251,246,.85))}
.beacon{fill:#ff4b3c;animation:acbeacon 1.5s steps(1,end) infinite}
@keyframes acbeacon{0%,22%{opacity:.95}23%,100%{opacity:.08}}

/* landing light spilling up the runway, off after touchdown */
.lightcone{
  position:absolute;left:50%;bottom:100%;
  width:calc(46 * var(--ft));height:calc(150 * var(--ft));
  margin-left:calc(-23 * var(--ft));
  background:radial-gradient(ellipse 60% 100% at 50% 100%,
    rgba(255,248,226,.20) 0%, rgba(255,248,226,.07) 45%, transparent 75%);
  opacity:0;
  animation:ldglight 45s linear infinite;
  pointer-events:none;
}

/* the tail number changes each time round: six aircraft, one per
   45 s pass, so the whole set cycles every 4 and a half minutes */
.tail{
  position:absolute;right:calc(100% + 40 * var(--ft));
  top:calc(8 * var(--ft));
  width:0;height:0;
}
.tail i{
  position:absolute;right:0;top:0;
  font-family:Newsreader,Georgia,serif;
  font-style:italic;
  font-size:calc(13 * var(--ft));
  white-space:nowrap;
  color:rgba(236,234,230,.5);
  opacity:0;
  animation:tailcycle 270s steps(1,end) infinite;
}
.tail i:nth-child(1){animation-delay:0s}
.tail i:nth-child(2){animation-delay:-225s}
.tail i:nth-child(3){animation-delay:-180s}
.tail i:nth-child(4){animation-delay:-135s}
.tail i:nth-child(5){animation-delay:-90s}
.tail i:nth-child(6){animation-delay:-45s}
@keyframes tailcycle{
  0%{opacity:1}
  16.6667%,100%{opacity:0}
}

@keyframes landing{
  0%,70.9%   {opacity:0;bottom:calc(-140 * var(--ft));transform:scale(1.12)}
  71.1%      {opacity:1;bottom:calc(-110 * var(--ft));transform:scale(1.11)}
  72.2%      {bottom:calc(-55 * var(--ft));transform:scale(1.09)}
  73.3%      {bottom:calc(0 * var(--ft));transform:scale(1.07)}
  74.4%      {bottom:calc(52 * var(--ft));transform:scale(1.05)}
  75.6%      {bottom:calc(100 * var(--ft));transform:scale(1.03)}
  76.7%      {bottom:calc(147 * var(--ft));transform:scale(1.01)}
  77.8%      {bottom:calc(190 * var(--ft));transform:scale(1)}
  79.4%      {bottom:calc(252 * var(--ft));transform:scale(1)}
  81.1%      {bottom:calc(310 * var(--ft));transform:scale(1)}
  83.3%      {bottom:calc(382 * var(--ft));transform:scale(1)}
  85.6%      {bottom:calc(450 * var(--ft));transform:scale(1)}
  88.3%      {bottom:calc(518 * var(--ft));transform:scale(1)}
  91.1%      {bottom:calc(580 * var(--ft));transform:scale(1)}
  94.4%      {bottom:calc(645 * var(--ft));transform:scale(1)}
  97.8%      {opacity:1;bottom:calc(700 * var(--ft));transform:scale(1)}
  98.9%,100% {opacity:0;bottom:calc(740 * var(--ft));transform:scale(1)}
}

@keyframes ldglight{
  0%,70.9%   {opacity:0}
  71.3%      {opacity:.85}
  76.7%      {opacity:.8}
  78.9%      {opacity:.25}
  81.1%,100% {opacity:0}
}

/* ---------- tower ---------- */
.tower{
  position:absolute;
  /* clear of the PAPI, which reaches about 113 ft off the pavement */
  right:calc(17vw + var(--rw) + 178px);
  top:11vh;
  width:clamp(76px,7.2vw,104px);
  opacity:1;
}
.tower svg{width:100%;height:auto;display:block}
.t-metal path{stroke:#8b949d;stroke-width:3;fill:none}
.obst{fill:#ff5a4d;opacity:.9;filter:drop-shadow(0 0 6px rgba(255,90,77,.8));animation:obst 3s ease-in-out infinite}
.obst.d2{animation-delay:.4s}
@keyframes obst{0%,45%{opacity:.9}55%,100%{opacity:.16}}

.grain{
  position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.45;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='.22'/%3E%3C/svg%3E");
  mix-blend-mode:overlay;
}

/* ---------- content ---------- */
.page{
  position:relative;z-index:2;height:100%;
  display:flex;flex-direction:column;
  padding:clamp(20px,3.4vw,40px);
}
.edges{display:flex;justify-content:space-between;gap:20px}
footer.edges{justify-content:flex-end}
.edges span{
  font-family:Newsreader,Georgia,serif;
  font-style:italic;font-size:16px;
  color:rgba(236,234,230,.5);
}
main{
  flex:1;display:flex;flex-direction:column;justify-content:center;
  max-width:560px;
}
h1{
  font-weight:600;
  font-size:clamp(46px,6.6vw,86px);
  line-height:.95;letter-spacing:-.03em;
  margin:0 0 24px;
}
h1 .future{
  font-family:Newsreader,Georgia,serif;
  font-style:italic;font-weight:300;
  font-size:.26em;letter-spacing:0;
  color:var(--amber);
  position:relative;top:-1.35em;
  margin:0 .08em 0 .14em;
}
.copy{
  font-family:Newsreader,Georgia,serif;
  font-size:clamp(17px,1.9vw,20px);line-height:1.66;
  color:rgba(236,234,230,.8);
  max-width:40ch;margin:0 0 20px;
}
.soon{
  font-family:Newsreader,Georgia,serif;
  font-style:italic;font-size:17px;
  color:var(--amber);margin:0;
}
.soon a{
  color:var(--amber);text-decoration:none;
  border-bottom:1px solid rgba(233,161,60,.4);padding-bottom:2px;
  transition:border-color .18s ease;
}
.soon a:hover{border-bottom-color:var(--amber)}

@media (max-width:820px){
  :root{--rw:86px}
  body{overflow:hidden}
  .runway{right:9vw;bottom:8vh;width:var(--rw);height:210vh;transform:rotate(4deg);opacity:1}
  .page{height:100%}
  .edges{flex-direction:column;gap:2px}
  .edges span{font-size:13.5px}
  header.edges,footer.edges{text-align:left}
  footer.edges{justify-content:flex-start}
  main{max-width:calc(100% - 150px);padding:0 0 6vh}
  h1{font-size:clamp(34px,10.5vw,46px);margin-bottom:18px}
  .copy{font-size:15.5px;line-height:1.62;margin-bottom:16px}
  .soon{font-size:14.5px}
  .tower{right:auto;left:2vw;top:auto;bottom:13vh;width:54px;opacity:.7}
  /* tucked in beside the pavement so it clears the copy column and
     still shows on short viewports */
  .papi{bottom:calc(330 * var(--ft));right:calc(100% + 12 * var(--ft));gap:calc(6 * var(--ft))}
  .papi i{width:calc(5 * var(--ft));height:calc(3.6 * var(--ft))}
  .tail{right:calc(100% + 20 * var(--ft))}
  .tail i{font-size:calc(14 * var(--ft))}
}

@media (prefers-reduced-motion:reduce){
  