/* gate login page, tuned for safari on an iphone

   safari keeps a page between its status bar and its toolbar and paints the strips under both with
   the page colour. the document runs on below the viewport, so the picture really continues under
   the toolbar. under the status bar it can't - only scrolled content shows there - so that strip is
   --void, the colour of bg.avif's top rows, and the picture sits --top px higher than the viewport,
   where it was framed for the whole screen

   the page rests at its top and scrolls only to meet the keyboard - up by --rise while the email
   field has the focus, form, picture and brand together, and back. safari blurs what passes under
   the status bar itself, no fade of ours on top, and the field ends up on the fog instead of the
   tower. app.js does the scrolling and says why not safari. nothing here is fixed or sticky -
   safari displaces such a layer for as long as a scroll to a field runs

   dark-mode extensions rewrite stylesheets. keep var() out of any declaration with a url() - noir
   turns that into an invalid value and the picture is gone */

:root {
  --void: #09090c;
  --ink: #e9e9ec;
  --dim: #8a8a91;
  --faint: #5a5a61;
  --rule: #2c2c32;
  --red: #aa4141; /* the light strip's own red - its core in bg.avif, rows 850 to 1300 */
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  /* how far the page scrolls up while the field has the focus - only where a keyboard covers the
     screen. 55svh - 64px puts the field 56 px above 45% of the viewport and the line under it at
     45% - all of the form above every iphone keyboard */
  --rise: 0px;
  color-scheme: dark;
}
@media (pointer: coarse) {
  :root { --rise: calc(55svh - 64px); }
}

* { box-sizing: border-box; margin: 0; }
::-webkit-scrollbar { display: none; }
[hidden] { display: none !important; }

html {
  overflow: hidden; /* the document is longer than the viewport, on purpose, and moves only when app.js scrolls it */
  background: var(--void);
  overscroll-behavior: none;
  scrollbar-width: none; /* only app.js scrolls the page, a scroll bar on the way has nothing to say */
  touch-action: none; /* no pan and no pinch, wherever the finger lands - the caret and its loupe still work */
  -webkit-text-size-adjust: 100%;
}
body {
  position: relative;
  height: 100svh;
  color: var(--ink);
  font: 13px/1.4 var(--mono);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}

/* by default the picture covers the viewport, and the form stands where it stands on a phone - its
   top edge, 196 px above the viewport's bottom, on the row 66.4% down the picture, where the fog
   begins under the tower. --ph is the picture's height once it covers, --py how far its top goes
   above the viewport for that. a window with room below shows the rest of the fog and no more than
   there is, a wide one loses the sky first, never the fog under the form */
.scene {
  --ph: max(100vw * 2622 / 1206, 100svh);
  --py: calc(-1 * max(0px, min(var(--ph) - 100svh, var(--ph) * .664 + 196px - 100svh)));
  position: absolute;
  inset: 0;
  background: url(bg.avif?v=22e2d624b8c9) 50% 0 / cover no-repeat;
  background-position-y: var(--py);
}
/* room to scroll into below the picture - its last row's fog, thinning out */
.scene::after {
  content: "";
  position: absolute;
  inset: 100% 0 auto;
  height: 60svh;
  background: linear-gradient(to bottom, #0d0d0e, var(--void));
}
/* the tower's light strip alone, on the picture's own canvas and laid out the same way, so the two
   line up at any size. it adds to the light the picture already has, and is drawn three times over
   itself - that lifts its soft edge into a glow, no filter needed.
   the light runs up the strip: .stream is a column of windows, a bright head and a fading tail
   every --run px, that climbs by one --run per cycle, and .strip sinks inside it at the same pace.
   the strip stays where it is and only the light moves - two transforms, nothing is repainted */
.light {
  --run: 120px;
  position: absolute;
  inset: 0;
  overflow: hidden;
  mix-blend-mode: plus-lighter;
  opacity: 0;
}
.stream {
  position: absolute;
  inset: 0 0 calc(-1 * var(--run));
  -webkit-mask-image: repeating-linear-gradient(to bottom, transparent, #000 calc(.06 * var(--run)), transparent calc(.62 * var(--run)), transparent var(--run));
  mask-image: repeating-linear-gradient(to bottom, transparent, #000 calc(.06 * var(--run)), transparent calc(.62 * var(--run)), transparent var(--run));
}
.strip {
  position: absolute;
  inset: 0 0 var(--run);
  background: url(light.avif?v=4b21912c7c9e) 50% 0 / cover no-repeat, url(light.avif?v=4b21912c7c9e) 50% 0 / cover no-repeat, url(light.avif?v=4b21912c7c9e) 50% 0 / cover no-repeat;
  background-position-y: var(--py);
}

/* a phone held upright - the render is one iphone screen, so the scene is one screen tall and starts
   the status bar's height above the viewport. that height can't be measured from a page, --top holds
   it per screen width. in a full-screen web view the safe area inset is the same number and the
   scene starts at the viewport's own top */
@media (pointer: coarse) and (orientation: portrait) and (max-width: 480px) {
  :root { --top: 54px; }
  @media (width: 402px), (width: 420px), (width: 440px) { :root { --top: 62px; } }
  @media (width: 393px), (width: 430px) { :root { --top: 59px; } }
  @media (width: 390px), (width: 428px) { :root { --top: 47px; } }
  @media (width: 375px), (width: 414px) { :root { --top: 46px; } }
  .scene {
    --py: 0px;
    inset: calc(env(safe-area-inset-top, 0px) - var(--top)) 0 auto;
    height: calc(100vw * 2622 / 1206);
  }
}

.brand { position: absolute; left: 42px; top: calc(41px + env(safe-area-inset-top, 0px)); }
.name { font-size: 16px; line-height: 20px; letter-spacing: .38em; }
.tagline {
  max-width: 16em;
  margin-top: 8px;
  color: #7d7d84;
  font-size: 10px;
  line-height: 12.8px;
  letter-spacing: .26em;
  text-transform: uppercase;
  word-spacing: 100vw; /* one word per line */
}

form { position: absolute; inset: 0; }

/* a column that ends 50 px above the toolbar. the slots have fixed heights, so the centre of the
   primary button, or of the email field in its place, is always 120 px above the toolbar - under
   the thumb - whatever else the view shows */
.dock {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 50px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.note {
  max-width: 300px;
  min-height: 16px;
  margin-bottom: 18px;
  color: var(--dim);
  font-size: 11px;
  line-height: 16px;
  letter-spacing: .14em;
  text-align: center;
  text-wrap: balance;
  overflow-wrap: anywhere;
  transition: opacity .2s;
}
.note[data-kind="err"] { color: #cf7f83; }

button {
  border: 0;
  border-radius: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  touch-action: manipulation;
}
button:focus-visible { outline: 1px solid var(--faint); outline-offset: -4px; }

/* the email field, in the primary button's slot - a view has one or the other. the input lets
   touches through to the slot until it has the focus, app.js says why */
.field {
  position: relative;
  width: 262px;
  height: 52px;
  cursor: text;
}
.field::after {
  content: "";
  position: absolute;
  inset: auto 0 7px;
  height: 1px;
  background: var(--rule);
  transition: background .2s;
}
.field:focus-within::after { background: var(--red); }
#email {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  border-radius: 0;
  outline: 0;
  background: transparent;
  color: var(--ink);
  font: 16px/52px var(--mono); /* 16px - anything smaller makes safari zoom in on focus */
  letter-spacing: .06em;
  text-align: center;
  text-overflow: ellipsis;
  caret-color: var(--red);
  pointer-events: none;
  touch-action: none; /* a finger that starts on the focused field must not pan or pinch the page either */
  -webkit-appearance: none;
  appearance: none;
  -webkit-user-select: text;
  user-select: text;
}
#email:focus { pointer-events: auto; }
#email::placeholder { color: var(--faint); opacity: 1; }
#email:-webkit-autofill { -webkit-text-fill-color: var(--ink); transition: background-color 9999s; }

.primary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 100%;
  height: 52px;
  padding: 0 20px;
  font-size: 13px;
  letter-spacing: .45em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: opacity .2s;
}
/* the tracking trails the last letter - the indent centres the word between the rules again */
.primary span { padding-left: .45em; }
.primary::before, .primary::after {
  content: "";
  flex: 0 1 73px;
  min-width: 20px;
  height: 1px;
  background: linear-gradient(to right, rgb(170 65 65 / .25), var(--red) 45%);
}
.primary::after { transform: scaleX(-1); }
.primary:active { opacity: .55; }
.busy .primary { pointer-events: none; }

/* .pulse - the page waits for something. light streams up the tower, swelling and fading in
   breaths of 3.6 s, three runs of the stream each - nothing else on the page moves. app.js takes
   the class off at the end of a breath, never inside one */
.pulse .light { animation: swell 3.6s ease-in-out infinite; }
.pulse .stream { animation: climb 1.2s linear infinite; }
.pulse .strip { animation: sink 1.2s linear infinite; }
@keyframes swell { 30%, 70% { opacity: 1; } }
@keyframes climb { to { transform: translateY(calc(-1 * var(--run))); } }
@keyframes sink { to { transform: translateY(var(--run)); } }

.secondary {
  height: 44px;
  padding: 0 20px;
  color: var(--dim);
  font-size: 11px;
  letter-spacing: .16em;
  transition: opacity .3s;
}
.secondary:empty { visibility: hidden; }
.secondary:active { opacity: .55; }

.noscript {
  position: absolute;
  inset: auto 0 170px;
  color: var(--dim);
  font-size: 11px;
  letter-spacing: .14em;
  text-align: center;
}

/* the page /verify answers a signed-in user without access to the host with */
.denied .note { max-width: 320px; }
.denied a.primary { color: inherit; text-decoration: none; }

@media (prefers-reduced-motion: reduce) {
  .note, .secondary, .primary, .field::after { transition: none; }
  .pulse .light { animation: none; opacity: .6; }
  .pulse .stream, .pulse .strip { animation: none; }
  .stream { -webkit-mask-image: none; mask-image: none; }
}
