/* Ownfold coming-soon page, Aperture direction.
 *
 * Copy on paper at left; at right, the painting in a round window with asset
 * tiles orbiting it. The hairline frame and the header placement follow
 * Wireframe 7. Shared by index.html and 404.html.
 *
 * The page is served with `style-src 'self'`, so there are no inline styles:
 * every angle and crop lives in a class below. Mobile first: phone (orbit
 * still), then 641 px (stacked and centered, larger type), 900 px (two
 * columns, a 260 px globe with tiles only), 1140 px (320 px, badges back),
 * 1281 px (352 px, filenames back), 1440 px (the full 432 px globe).
 *
 * After editing this file, run `npm run fingerprint` in workers/ownfold-landing
 * so its name carries its new hash. */

/* Latin subsets only, self-hosted (SIL OFL 1.1, see licenses/). */
@font-face {
  font-family: 'Faculty Glyphic';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/faculty-glyphic-latin.7bc971b9.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/inter-latin.c9407645.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --page: #faf9f5;
  --surface: #ffffff;
  --ink: #090b0b;
  --muted: #484848;
  --rule: #dfe2de;
  --field: #858b85;
  --action-hover: #262828;
  --err: #9b4338;
  --painting: #c9d8d6;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-press: cubic-bezier(0.2, 0, 0, 1);
  --font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-display: 'Faculty Glyphic', Georgia, 'Times New Roman', serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
  /* The content column's edge: 24 px inside a 1248 px column, centered. */
  --edge: max(24px, calc((100% - 1248px) / 2 + 24px));
  color-scheme: light;
}

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

html,
body {
  margin: 0;
}

body {
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

p,
h1 {
  margin: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

/* The outline follows each control's own corners; only links get a radius. */
a:focus-visible,
input:focus-visible,
button:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}
a:focus-visible { border-radius: 4px; }

/* Normal document flow: at least the small viewport tall, and it scrolls. */
.page {
  position: relative;
  isolation: isolate;
  /* A guard: the orbit's ring box sits within 2 px of the right edge between
     900 and 1139 px, and clip (unlike hidden) makes no scroll container. */
  overflow-x: clip;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
}

/* ── Hairline frame ──────────────────────────────────────────────────────
   Wireframe 7: thin rules just outside the content column, one under the
   header and one above the footer, with a node where they cross. On phones
   only the header rule and its two nodes remain. */
.frame {
  --gx: max(12px, calc((100% - 1248px) / 2 - 16px));
  --top: 64px;
  --bot: 64px;
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}
.frame__line {
  position: absolute;
  background: rgb(9 11 11 / 10%);
}
.frame__line--left,
.frame__line--right {
  top: 0;
  bottom: 0;
  width: 1px;
}
.frame__line--left { left: var(--gx); }
.frame__line--right { right: var(--gx); }
.frame__line--top,
.frame__line--bottom {
  left: 0;
  right: 0;
  height: 1px;
}
.frame__line--top { top: var(--top); }
.frame__line--bottom { bottom: var(--bot); }
.frame__node {
  position: absolute;
  width: 7px;
  height: 7px;
  margin: -3px 0 0 -3px;
  border: 1px solid rgb(9 11 11 / 38%);
  border-radius: 50%;
  background: var(--page);
}
.frame__node::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 6px;
  width: 14px;
  height: 1px;
  background: rgb(9 11 11 / 38%);
}
.frame__node--tl { top: var(--top); left: var(--gx); }
.frame__node--tr { top: var(--top); left: calc(100% - var(--gx)); }
.frame__node--bl { top: calc(100% - var(--bot)); left: var(--gx); }
.frame__node--br { top: calc(100% - var(--bot)); left: calc(100% - var(--gx)); }
.frame__node--tr::after,
.frame__node--br::after {
  left: auto;
  right: 6px;
}
.frame__line--left,
.frame__line--right,
.frame__line--bottom,
.frame__node--bl,
.frame__node--br {
  display: none;
}

/* ── Header ──────────────────────────────────────────────────────────────
   The wordmark sits where it sits on the sign-in page and in the app, moved
   onto the content column's edge on wide screens. Sign in is the only
   action. */
.site-header {
  position: relative;
  flex: none;
  height: 64px;
}
.brand {
  position: absolute;
  top: 14.65px;
  left: var(--edge);
  display: flex;
  align-items: center;
  gap: 12px;
}
.brand__wordmark {
  display: block;
  width: auto;
  height: 26px;
}
.brand__beta {
  padding: 2px 7px;
  border: 1px solid currentColor;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  opacity: 0.7;
}
.signin {
  position: absolute;
  top: 10px;
  right: max(20px, calc((100% - 1248px) / 2 + 20px));
  display: inline-flex;
  align-items: center;
  height: 44px;
  padding: 0 4px;
  font-size: 14px;
  font-weight: 500;
}
.signin:hover {
  text-decoration: underline;
  text-underline-offset: 4px;
}

/* ── Copy ─────────────────────────────────────────────────────────────── */
.hero {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  align-items: center;
  width: 100%;
  max-width: 1248px;
  margin: 0 auto;
  padding: 32px 24px 0;
}
.hero__copy {
  display: grid;
  justify-items: stretch;
  min-width: 0;
}
.eyebrow {
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
  line-height: 18px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-wrap: balance;
}
/* The measure of the 1440 px design (a 640 px column at 70 px), in em, so
   every width keeps its three lines: THE OPEN / HOME FOR YOUR / CREATIVE
   WORK. Wider lines would run into the orbit on narrower desktops. */
.headline {
  max-width: 9.2em;
  margin-top: 14px;
  font-family: var(--font-display);
  /* 34/36 px on phones; below 354 px it shrinks so the lines still fit. */
  font-size: min(34px, 9.6vw);
  font-weight: 400;
  line-height: 1.06;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-wrap: balance;
}
.lede {
  max-width: 500px;
  margin-top: 18px;
  color: var(--muted);
  font-size: 16px;
  line-height: 24px;
  text-wrap: pretty;
}

/* ── Waitlist ────────────────────────────────────────────────────────────
   A real form: POST /waitlist to this Worker, which answers with the joined
   page or this page again around the form and its error. No script. */
.w {
  display: grid;
  gap: 12px;
  width: 100%;
  margin-top: 28px;
}
/* Result pages open at #waitlist (the form's action carries the fragment).
   The margins scroll only as far as each result needs to fit above the
   bottom of the viewport, so a page that already fits doesn't move. */
.w { scroll-margin-top: max(24px, calc(100svh - 290px)); }
.w-joined { scroll-margin-top: max(24px, calc(100svh - 80px)); }
.w-label {
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
}
.w-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.w-input {
  min-width: 0;
  height: 48px;
  padding: 0 14px;
  border: 1px solid var(--field);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 16px;
}
.w-input::placeholder {
  color: #667068;
  opacity: 1;
}
.w-input[aria-invalid='true'] { border-color: var(--err); }
.w-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  padding: 0 26px;
  border: 0;
  border-radius: 999px;
  background: var(--ink);
  color: var(--surface);
  font: inherit;
  font-size: 16px;
  font-weight: 500;
  line-height: 20px;
  cursor: pointer;
  transition: transform 140ms var(--ease-press), background 140ms var(--ease-press);
}
@media (hover: hover) {
  .w-submit:hover {
    transform: translateY(-1px);
    background: var(--action-hover);
  }
}
.w-submit:active {
  transform: translateY(1px);
  transition-duration: 80ms;
}
.w-error {
  color: var(--err);
  font-size: 14px;
  line-height: 20px;
}
.w-notice {
  color: var(--muted);
  font-size: 14px;
  line-height: 20px;
}
.w-notice a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* The honeypot: visually hidden in place, inert, and hidden from assistive
   technology, so only a bot filling every field fills it. */
.w-trap {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.w-joined {
  display: grid;
  width: 100%;
  margin-top: 28px;
  padding: 13px 16px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--surface);
}
/* Focused on load when the URL has no fragment; it isn't a control. */
.w-joined__title:focus { outline: none; }
.w-joined__title {
  margin: 0;
  font-size: 16px;
  font-weight: 500;
  line-height: 24px;
}

/* ── Globe ───────────────────────────────────────────────────────────────
   The painted window with asset tiles orbiting it, like a plasma globe. Each
   tile is a crop of the same painting, so the circle reads as made of the
   library's assets. Decorative: hidden from assistive technology, takes no
   pointer events. Motion is transform-only, 160 s a turn, from 641 px up:
   the ring of filaments rotates, and each tile travels its own circular path
   (it stays upright, so its box is exact and adds no phantom scroll area).
   On phones the orbit holds still, as the old page's background did; only
   the painting's one 18 s settle runs. All of it stops under reduced
   motion. */
.globe {
  position: relative;
  width: 100%;
  max-width: 200px;
  aspect-ratio: 1;
  margin: 80px auto 56px;
  container-type: inline-size;
  pointer-events: none;
}
.globe__window {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 50%;
  background: var(--painting);
}
.globe__window picture,
.globe__window img {
  display: block;
  width: 100%;
  height: 100%;
}
.globe__window img { object-fit: cover; }
/* No fill mode: the keyframes have only a `from`, so the end state is the
   element's own, and the layers are released when the settle ends. */
.globe__settle { animation: settle 18s var(--ease-out); }
@keyframes settle {
  from { transform: scale(1.025); }
}

.globe__rings {
  position: absolute;
  inset: -24%;
  width: 148%;
  height: 148%;
  overflow: visible;
}
.globe__rings circle,
.globe__rings ellipse {
  fill: none;
  stroke: rgb(9 11 11 / 22%);
  stroke-width: 1;
  stroke-dasharray: 1 5;
  vector-effect: non-scaling-stroke;
}

.globe__rays {
  position: absolute;
  inset: 0;
  z-index: 2;
}
.globe__ray,
.globe__asset {
  position: absolute;
  top: 50%;
  left: 50%;
}
/* Short gold filaments from the window's edge toward each tile. */
.globe__ray {
  width: 4cqw;
  height: 1px;
  transform-origin: 0 50%;
  transform: rotate(var(--a)) translateX(50cqw);
  background: linear-gradient(90deg, rgb(214 170 92 / 15%), rgb(214 170 92 / 85%));
}
/* The static pose (phones, reduced motion) is the tile at its angle; the
   animation starts each tile part-way round, at that same angle. */
.globe__asset {
  z-index: 2;
  width: 13cqw;
  height: 13cqw;
  margin: -6.5cqw 0 0 -6.5cqw;
  transform: rotate(var(--a)) translateX(60cqw) rotate(calc(-1 * var(--a)));
}
.globe__tile {
  display: block;
  width: 100%;
  height: 100%;
  border: 2px solid var(--surface);
  border-radius: 9px;
  background: var(--painting) url('/assets/aperture-416.587bd091.webp') no-repeat;
  background-position: var(--bp);
  background-size: 340%;
  box-shadow: 0 6px 16px rgb(9 11 11 / 18%);
  transform: rotate(var(--t));
}
.globe__name {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 400;
  line-height: 16px;
  white-space: nowrap;
}
/* Angle (--a), its start time on the 160 s turn (--start = -a / 360 × 160 s),
   the crop (--bp) and the tilt (--t). The six tiles are the globe's only
   span children; the six filaments are the ring's. */
.globe__ray:nth-child(1),
.globe__asset:nth-of-type(1) { --a: 18deg; --start: -8s; }
.globe__ray:nth-child(2),
.globe__asset:nth-of-type(2) { --a: 78deg; --start: -34.667s; }
.globe__ray:nth-child(3),
.globe__asset:nth-of-type(3) { --a: 136deg; --start: -60.444s; }
.globe__ray:nth-child(4),
.globe__asset:nth-of-type(4) { --a: 196deg; --start: -87.111s; }
.globe__ray:nth-child(5),
.globe__asset:nth-of-type(5) { --a: 252deg; --start: -112s; }
.globe__ray:nth-child(6),
.globe__asset:nth-of-type(6) { --a: 312deg; --start: -138.667s; }
.globe__asset:nth-of-type(1) { --bp: 12% 34%; --t: -6deg; }
.globe__asset:nth-of-type(2) { --bp: 62% 72%; --t: 5deg; }
.globe__asset:nth-of-type(3) { --bp: 96% 38%; --t: -3deg; }
.globe__asset:nth-of-type(4) { --bp: 34% 92%; --t: 7deg; }
.globe__asset:nth-of-type(5) { --bp: 82% 8%; --t: -8deg; }
.globe__asset:nth-of-type(6) { --bp: 48% 56%; --t: 4deg; }
@keyframes spin {
  to { transform: rotate(1turn); }
}
@keyframes orbit {
  from { transform: rotate(0turn) translateX(60cqw) rotate(0turn); }
  to { transform: rotate(1turn) translateX(60cqw) rotate(-1turn); }
}

/* Asset states on three tiles: a rights alert, a workflow status, and
   comments. Static, so they read as facts about assets, not live activity. */
.globe__badge,
.globe__bubble {
  position: absolute;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 500;
  line-height: 16px;
  white-space: nowrap;
  box-shadow: 0 4px 10px rgb(9 11 11 / 12%);
}
.globe__badge {
  top: calc(100% + 8px);
  left: 50%;
  translate: -50% 0;
  padding: 3px 9px;
  border-radius: 999px;
}
.globe__badge--alert {
  background: var(--err);
  color: var(--surface);
}
.globe__badge--alert svg {
  width: 12px;
  height: 12px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.globe__asset--alert .globe__tile { border-color: var(--err); }
.globe__badge--status {
  border: 1px solid var(--rule);
  background: var(--surface);
  color: var(--ink);
}
.globe__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #b7791f;
}
.globe__bubble {
  padding: 3px 8px;
  border: 1px solid var(--rule);
  border-radius: 10px 10px 10px 3px;
  background: var(--surface);
  color: var(--ink);
}
.globe__bubble--1 { bottom: calc(100% + 8px); left: 50%; translate: -50% 0; }
.globe__bubble--2 { top: calc(100% + 8px); left: 50%; translate: -50% 0; }
.globe__bubble--3 {
  top: -10px;
  right: -14px;
  padding: 1px 6px;
  border-radius: 999px;
  font-size: 11px;
}
/* Phones: tiles only, no labels. */
.globe__name,
.globe__badge,
.globe__bubble {
  display: none;
}

/* ── Footer ───────────────────────────────────────────────────────────── */
.site-footer {
  display: flex;
  flex: none;
  flex-direction: column;
  gap: 14px;
  padding: 36px var(--edge) 24px;
  color: var(--muted);
  font-size: 12px;
  line-height: 18px;
}
.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}
/* 44 px tall hit areas that don't grow the 18 px line, drawn by a
   pseudo-element so the focus ring still hugs the text. */
.site-footer__links a {
  position: relative;
}
.site-footer__links a::before {
  content: '';
  position: absolute;
  inset: -13px 0;
}
.site-footer__links a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ── 404 ──────────────────────────────────────────────────────────────── */
.notfound {
  flex: 1;
  display: grid;
  align-content: center;
  justify-items: start;
  width: 100%;
  max-width: 1248px;
  margin: 0 auto;
  padding: 48px 24px;
}
.notfound .lede a {
  color: var(--ink);
  white-space: nowrap;
  text-decoration: underline;
  text-underline-offset: 3px;
}

@media (min-width: 361px) {
  .brand__wordmark { height: 32.7px; }
}

/* 641 to 899 px: stacked and centered, with the desktop type. */
@media (min-width: 641px) {
  .frame { --top: 76px; }
  .frame__line--left,
  .frame__line--right,
  .frame__line--bottom,
  .frame__node--bl,
  .frame__node--br {
    display: block;
  }
  .site-header { height: 76px; }
  .hero { padding: 48px 24px 0; }
  /* Stacked: the copy is one centered column above the globe, so the two
     share an axis instead of the copy hugging the left edge. */
  .hero__copy {
    justify-items: start;
    width: min(100%, 520px);
    margin-inline: auto;
  }
  .headline {
    margin-top: 20px;
    font-size: clamp(48px, calc(48px + (100vw - 1024px) * 0.0529), 70px);
    line-height: 0.972;
    letter-spacing: -0.015em;
  }
  .lede {
    margin-top: 28px;
    font-size: 18px;
    line-height: 28px;
  }
  .w,
  .w-joined {
    max-width: 480px;
    margin-top: 36px;
  }
  .w { scroll-margin-top: max(24px, calc(100svh - 200px)); }
  .w-row { flex-direction: row; }
  .w-input { flex: 1; }
  .w-submit { flex: none; }
  /* The bubbles above and the badge below a tile reach about 83 px past the
     circle, so these margins keep about 36 px clear of the copy and the
     footer through the whole rotation. */
  .globe {
    max-width: 320px;
    margin: 120px auto 96px;
  }
  .globe__tile { background-image: url('/assets/aperture-640.0a32b5f6.webp'); }
  .globe__rays { animation: spin 160s linear infinite; }
  .globe__asset { animation: orbit 160s linear var(--start) infinite; }
  .globe__name { display: block; }
  .globe__badge,
  .globe__bubble {
    display: inline-flex;
  }
  .site-footer {
    flex-direction: row;
    justify-content: space-between;
    gap: 20px;
    padding: 24px var(--edge) 22px;
  }
  .notfound { padding: 64px 24px; }
}

/* Two columns from 900 px. Below 1140 px the globe is a 260 px circle with
   tiles only; the badges, bubbles and filenames would reach the copy. The
   shift keeps the tiles at least 16 px inside the viewport, and the copy
   narrows to 440 px so the orbit never meets it. */
@media (min-width: 900px) {
  .hero {
    grid-template-columns: minmax(0, 1fr) 260px;
    column-gap: clamp(48px, 9vw, 128px);
    align-content: center;
    /* Block padding, so in a short window the copy, which sets the row
       height here, stays off the frame's header and footer rules. */
    padding: 40px 24px;
  }
  .hero__copy {
    width: auto;
    margin-inline: 0;
  }
  .lede,
  .w,
  .w-joined {
    max-width: 440px;
  }
  .globe {
    max-width: 260px;
    margin: 72px 0;
    translate: calc(-1 * (40px + min(0px, 624px - 50vw))) 0;
  }
  .globe__name,
  .globe__badge,
  .globe__bubble {
    display: none;
  }
}

/* From 1140 px the circle is 320 px and the badges and bubbles come back. */
@media (min-width: 1140px) {
  .hero { grid-template-columns: minmax(0, 1fr) 320px; }
  .globe {
    max-width: 320px;
    margin: 104px 0;
    translate: calc(-1 * (112px + min(0px, 624px - 50vw))) 0;
  }
  .globe__badge,
  .globe__bubble {
    display: inline-flex;
  }
}

/* From 1281 px: the 352 px globe (432 px from 1440). The orbit reaches about
   21 % past the circle, plus the widest badge, so the globe shifts left as the
   window narrows: the alert badge stays at least 16 px inside the viewport
   through the whole rotation. The block margins reserve the overhang above
   and below (about 88 px at 352 px, 101 px at 432 px), so a short window
   scrolls instead of the labels crossing the header or the footer. */
@media (min-width: 1281px) {
  .hero {
    grid-template-columns: minmax(0, 1fr) 352px;
    column-gap: clamp(48px, 9vw, 128px);
    align-content: center;
    padding: 0 24px;
  }
  .lede { max-width: 500px; }
  .w,
  .w-joined {
    max-width: 480px;
  }
  .globe {
    max-width: 352px;
    margin: 112px 0;
    translate: calc(-1 * max(48px, 739px - 50vw)) 0;
  }
  .globe__name { display: none; }
}

@media (min-width: 1440px) {
  .hero { grid-template-columns: minmax(0, 1fr) 432px; }
  .globe {
    max-width: 432px;
    margin: 132px 0;
    translate: calc(-1 * max(12px, 747px - 50vw)) 0;
  }
  .globe__name { display: block; }
}

/* Phones held sideways are wider than 641 px but still phones: the orbit
   holds still there too (the app's compact-shell query). */
@media (max-height: 500px) and (pointer: coarse) {
  .globe__rays,
  .globe__asset {
    animation: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .globe__rays,
  .globe__asset,
  .globe__settle {
    animation: none;
  }
  .w-submit,
  .w-submit:hover,
  .w-submit:active {
    transform: none;
    transition: none;
  }
}
