/* =====================================================================
 *  Verso — landing page styles
 *
 *  Numbers that are meant to be tuned are NOT in this file: they arrive as
 *  custom properties from scripts/config.js. The fallbacks in var() here
 *  exist only so the page is still sane if that script fails to load.
 * ===================================================================== */

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

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

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;

  background: var(--page-bg, #0A0A0B);
  color: var(--text-primary, #fff);

  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI',
               Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  overflow-x: hidden;
}


/* =====================================================================
 *  Backdrop: the photo grid, the radial blur, the radial darkening
 * ===================================================================== */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--page-bg, #0A0A0B);
  /* Keeps backdrop-filter sampling the grid and nothing else. */
  isolation: isolate;
}

.backdrop__grid {
  position: absolute;
  inset: 0;
  display: grid;
  gap: var(--grid-gap, 10px);
  /* The last row usually overflows; centring splits it top and bottom so
     neither edge of the screen gets a visibly clipped strip. */
  align-content: center;
  /* grid-template-columns and grid-auto-rows are set by background.js,
     which measures the viewport and works out a whole number of columns. */
}

.tile {
  position: relative;
  overflow: hidden;
  background: var(--tile-empty, rgba(255, 255, 255, 0.028));
  border-radius: var(--tile-radius, 3px);
}

.tile__photo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;                       /* animations take it from here */
  filter: var(--tile-filter, none);
  will-change: opacity, transform;
}

/* The blur. background.js sets backdrop-filter and the radial mask that
   confines it to the middle of the screen. Browsers without backdrop-filter
   ignore all of it and are left with the darkening below, which is enough
   on its own to keep the headline readable. */
.backdrop__blur {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* The darkening. background.js sets `background` to a stack of gradients. */
.backdrop__dim {
  position: absolute;
  inset: 0;
  pointer-events: none;
}


/* =====================================================================
 *  Hero
 * ===================================================================== */

.hero {
  position: relative;
  z-index: 1;
  flex: 1;

  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;

  padding: clamp(3rem, 8vmin, 6rem) clamp(1.25rem, 5vw, 2.5rem);
  padding-bottom: clamp(6rem, 14vmin, 9rem);   /* room for the footer */
}

/* Only hidden when scripts are running, so a reader without JavaScript
   never waits on a fade that will not arrive. */
.js .hero {
  opacity: 0;
}

.js .hero.is-ready {
  opacity: 1;
  transition: opacity var(--hero-fade-duration, 1100ms)
              cubic-bezier(0.22, 0.61, 0.36, 1)
              var(--hero-fade-delay, 150ms);
}

.hero__title {
  margin: clamp(1.4rem, 3.2vmin, 2.2rem) 0 0;
  font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI',
               Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: clamp(2.75rem, 7.4vmin, 4.5rem);
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.05;
  color: var(--text-primary, #fff);
}

.hero__lead {
  margin: clamp(0.7rem, 1.8vmin, 1.1rem) 0 0;
  font-size: clamp(1.0625rem, 2.1vmin, 1.3125rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.4;
  color: var(--text-primary, #fff);
}

.hero__body {
  margin: clamp(0.85rem, 2vmin, 1.25rem) 0 0;
  max-width: 44ch;
  font-size: clamp(0.9375rem, 1.65vmin, 1.0625rem);
  line-height: 1.62;
  text-wrap: pretty;
  color: var(--text-secondary, rgba(255, 255, 255, 0.76));
}

.hero__badge {
  display: inline-block;
  margin-top: clamp(1.6rem, 3.6vmin, 2.4rem);
  border-radius: 9px;
  transition: transform 220ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.hero__badge img {
  display: block;
  height: clamp(44px, 5.4vmin, 52px);
  width: auto;
}

.hero__badge:hover { transform: scale(1.035); }
.hero__badge:active { transform: scale(0.985); }

.hero__soon {
  margin: clamp(1.6rem, 3.6vmin, 2.4rem) 0 0;
  padding: 0.6em 1.15em;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text-secondary, rgba(255, 255, 255, 0.76));
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

[hidden] { display: none !important; }


/* =====================================================================
 *  App icon
 *
 *  Layer geometry is set inline by app-icon.js, in the Icon Composer
 *  document's own 1024-unit coordinate space. Everything here is the
 *  surrounding scaffolding.
 * ===================================================================== */

/* Carries the shadow the icon casts on the page. It sits outside the canvas
   scaling, so --icon-shadow is written in ordinary screen px, and outside the
   perspective, so a filter and a 3D transform never share an element. */
.app-icon {
  position: relative;
  width: var(--icon-size, 180px);
  height: var(--icon-size, 180px);
  filter: var(--icon-shadow, drop-shadow(0 20px 46px rgba(0, 0, 0, 0.55)));
}

.app-icon__fallback {
  width: 100%;
  height: 100%;
}

.app-icon__stage {
  position: absolute;
  inset: 0;
  perspective: var(--icon-perspective, 1400px);
}

/* The 1024 x 1024 working canvas, scaled down to --icon-size and tilted
   towards the pointer by parallax.js. */
.app-icon__canvas {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1024px;
  height: 1024px;
  will-change: transform;
}

/* The rounded square itself. Its superellipse mask, set by app-icon.js,
   clips every layer inside it — which is what makes the cards run off the
   edge of the icon exactly as they do in Icon Composer. */
.app-icon__surface {
  position: absolute;
  inset: 0;
  background: var(--icon-fill, linear-gradient(180deg, #FFA363, #FF8821));
}

@supports (background: linear-gradient(in oklab, red, blue)) {
  .app-icon__surface {
    background: var(--icon-fill-p3, var(--icon-fill));
  }
}

/* One parallax layer. app-icon.js writes --px / --py every frame. */
.app-icon__layer {
  position: absolute;
  inset: 0;
  transform: translate3d(var(--px, 0px), var(--py, 0px), 0);
  will-change: transform;
}

/* A group is a point, not a box: its children hang off its centre exactly
   as the layers of an Icon Composer group hang off the group's origin. */
.icon-group {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
}

/* Wrapper whose only job is to cast the card's drop shadow. The card is
   mask-shaped, so a box-shadow would trace its square border box instead. */
.icon-group__card-shadow {
  position: absolute;
  top: 0;
  left: 0;
}

.icon-group__card,
.icon-group__label {
  position: absolute;
  top: 0;
  left: 0;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
}

.icon-group__card {
  background: var(--icon-card-fill, rgba(255, 253, 250, 0.93));
}

.icon-group__label {
  background: var(--icon-label-color, #2A211A);
}

.icon-group__photo {
  position: absolute;
  top: 0;
  left: 0;
}

/* Light on the icon face, above every layer and inside the same mask. */
.app-icon__gloss {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(
      180deg,
      rgba(255, 255, 255, 1) 0%,
      rgba(255, 255, 255, 0.28) 34%,
      rgba(255, 255, 255, 0) 58%,
      rgba(0, 0, 0, 0.32) 100%
    );
  opacity: var(--icon-gloss, 0.2);
}


/* =====================================================================
 *  Footer
 * ===================================================================== */

.footer {
  position: relative;
  z-index: 1;

  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem clamp(1rem, 4vw, 2rem);
  align-items: baseline;
  justify-content: space-between;

  padding: clamp(1.1rem, 3vmin, 1.75rem) clamp(1.25rem, 5vw, 2.5rem);
  padding-bottom: calc(clamp(1.1rem, 3vmin, 1.75rem) + env(safe-area-inset-bottom, 0px));

  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--text-muted, rgba(255, 255, 255, 0.5));
}

.js .footer {
  opacity: 0;
}

.js .footer.is-ready {
  opacity: 1;
  transition: opacity var(--hero-fade-duration, 1100ms)
              cubic-bezier(0.22, 0.61, 0.36, 1)
              var(--hero-fade-delay, 150ms);
}

.footer__copyright {
  margin: 0;
}

.footer__links {
  display: flex;
  gap: clamp(1rem, 3vw, 1.75rem);
}

/* Legal links and the language switcher travel together, so the footer keeps
   to two groups and wraps into two rows rather than three.

   `flex: 1 1 auto` with the content pushed right does double duty: on one line
   it sits opposite the copyright, and when it wraps to its own line it still
   spans the footer and stays right-aligned. That keeps the globe on the right
   edge at every width, which is what the menu below anchors itself to. */
.footer__end {
  display: flex;
  flex: 1 1 auto;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.6rem clamp(1rem, 3vw, 1.75rem);
}


/* =====================================================================
 *  Language switcher
 *
 *  A <details> disclosure: one globe in the corner rather than a row of
 *  language names, so the footer does not grow as languages are added.
 * ===================================================================== */

.langpick {
  position: relative;
}

.langpick__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding: 0.25em 0.1em;
  cursor: pointer;
  color: var(--text-muted, rgba(255, 255, 255, 0.5));
  transition: color 180ms ease;

  /* Both lines are needed to be rid of the disclosure triangle: the standard
     property for most browsers, the pseudo-element for older WebKit. */
  list-style: none;
}

.langpick__toggle::-webkit-details-marker {
  display: none;
}

.langpick__toggle:hover,
.langpick[open] .langpick__toggle {
  color: var(--text-primary, #fff);
}

.langpick__globe {
  flex: none;
  /* Optically centred against lowercase text rather than sitting on the
     baseline, which leaves it looking like it has slipped. */
  margin-top: -0.08em;
}

.langpick__name {
  border-bottom: 1px solid rgba(255, 255, 255, 0.22);
  padding-bottom: 1px;
  transition: border-color 180ms ease;
}

.langpick__toggle:hover .langpick__name,
.langpick[open] .langpick__name {
  border-bottom-color: currentColor;
}

/* Opens upward, out of the footer, and is pinned to the switcher's right edge
   so it cannot run off the side of a narrow screen. */
.langpick__menu {
  position: absolute;
  right: 0;
  bottom: calc(100% + 0.65rem);
  z-index: 2;

  display: flex;
  flex-direction: column;
  min-width: max(11rem, 100%);
  max-height: min(60vh, 22rem);
  overflow-y: auto;
  overscroll-behavior: contain;

  padding: 0.4rem;
  border: 1px solid rgba(255, 255, 255, 0.13);
  border-radius: 14px;
  /* Opaque enough to read over a bright photograph on its own; the blur is
     polish, not the thing legibility rests on. */
  background: rgba(19, 19, 22, 0.94);
  backdrop-filter: blur(22px) saturate(1.5);
  -webkit-backdrop-filter: blur(22px) saturate(1.5);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55), 0 2px 8px rgba(0, 0, 0, 0.4);
}

/* Wins over `.footer a`, which would otherwise underline every entry. */
.footer .langpick__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5em;
  padding: 0.5em 0.7em;
  border: 0;
  border-radius: 9px;
  white-space: nowrap;
  color: var(--text-secondary, rgba(255, 255, 255, 0.76));
  text-decoration: none;
  transition: background-color 140ms ease, color 140ms ease;
}

.footer a.langpick__item:hover {
  background: rgba(255, 255, 255, 0.09);
  color: var(--text-primary, #fff);
  border: 0;
}

.footer .langpick__item.is-current {
  color: var(--text-primary, #fff);
  cursor: default;
}

/* A tick on the language being read. */
.footer .langpick__item.is-current::after {
  content: '';
  flex: none;
  width: 0.62em;
  height: 0.34em;
  border-left: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-0.12em) rotate(-45deg);
}

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

.footer a,
.legal a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 1px;
  transition: color 180ms ease, border-color 180ms ease;
}

.footer a { border-bottom-color: rgba(255, 255, 255, 0.22); }

.footer a:hover,
.legal a:hover {
  color: var(--text-primary, #fff);
  border-bottom-color: currentColor;
}

:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.85);
  outline-offset: 3px;
  border-radius: 4px;
}


/* =====================================================================
 *  Privacy / Support pages
 * ===================================================================== */

/* Privacy and Support carry no photo grid: they are for reading, and the
   animation would be both a distraction and a needless download. They keep
   the same palette through a static wash. */
.page-legal {
  background:
    radial-gradient(120% 90% at 50% -10%,
                    rgba(255, 163, 99, 0.09) 0%,
                    rgba(255, 136, 33, 0.03) 34%,
                    rgba(10, 10, 11, 0) 68%),
    var(--page-bg, #0A0A0B);
  background-attachment: fixed;
}

.js .legal {
  opacity: 0;
}

.js .legal.is-ready {
  opacity: 1;
  transition: opacity var(--hero-fade-duration, 1100ms)
              cubic-bezier(0.22, 0.61, 0.36, 1);
}

.legal {
  position: relative;
  z-index: 1;
  flex: 1;
  width: 100%;
  max-width: 44rem;
  margin: 0 auto;
  padding: clamp(3.5rem, 11vmin, 7rem) clamp(1.25rem, 5vw, 2.5rem)
           clamp(4rem, 10vmin, 6rem);
  text-align: left;
}

.legal__back {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  margin-bottom: clamp(1.75rem, 5vmin, 2.75rem);
  font-size: 0.875rem;
  color: var(--text-muted, rgba(255, 255, 255, 0.5));
  text-decoration: none;
  border: 0;
}

.legal__back:hover { color: var(--text-primary, #fff); }

.legal h1 {
  margin: 0 0 0.35em;
  font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI',
               Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: clamp(2rem, 5.2vmin, 2.75rem);
  font-weight: 600;
  letter-spacing: -0.022em;
  line-height: 1.1;
}

.legal h2 {
  margin: 2.25em 0 0.6em;
  font-size: clamp(1.0625rem, 2.1vmin, 1.1875rem);
  font-weight: 600;
  letter-spacing: -0.012em;
}

.legal p,
.legal li {
  color: var(--text-secondary, rgba(255, 255, 255, 0.76));
  font-size: clamp(0.9375rem, 1.7vmin, 1.0625rem);
  line-height: 1.68;
  text-wrap: pretty;
}

.legal p { margin: 0 0 1em; }

.legal ul {
  margin: 0 0 1em;
  padding-left: 1.2em;
}

.legal li { margin-bottom: 0.45em; }

.legal__updated {
  margin-top: 0;
  font-size: 0.8125rem;
  color: var(--text-muted, rgba(255, 255, 255, 0.5));
}

.legal__note {
  margin-top: 2.5em;
  padding: 1.1em 1.3em;
  border: 1px dashed rgba(255, 255, 255, 0.2);
  border-radius: 12px;
  font-size: 0.875rem;
  color: var(--text-muted, rgba(255, 255, 255, 0.5));
}


/* =====================================================================
 *  Reduce motion
 *
 *  The scripts already stand down when the setting is on (see
 *  motion.respectReducedMotion in config.js); this catches the transitions
 *  that CSS owns.
 * ===================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
