/* LumaWall — the page is a desktop.
 *
 * The wallpapers are the design. Everything else is a dark room built to let
 * them carry: one ground, one ink, one accent, no chrome competing for the
 * eye. Where a scene plays, the page stops being a layout and becomes a place
 * you are standing in.
 *
 * Surfaces separate by tone step. Text over a scene sits in a wash — a ramp
 * from the ground colour, not a panel — so nothing is laid on top of a
 * headline and nothing reads as a box floating over a picture. */

:root {
  --ground: #06070a;
  --ground-up: #121319;
  --ground-up-2: #1c1d25;
  --ink: #f5f5f2;
  --ink-soft: #a3a4ac;
  --accent: #5c57f5;
  --accent-ink: #ffffff;

  --gap-side: clamp(22px, 5vw, 92px);
  --gap-band: clamp(104px, 11vh, 176px);
  --page: 1600px;
  --measure: 58ch;

  --ease: cubic-bezier(0.22, 0.75, 0.3, 1);

  /* The bar is fixed and content-sized; measured at 81.25px at 390, 768,
     1440 and 2000, so it does not move with width. Anchored sections have
     to clear it or every nav link lands its own heading underneath. */
  --bar-h: 82px;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  background: var(--ground);
  scroll-padding-top: calc(var(--bar-h) + 28px);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: "Instrument Sans", ui-sans-serif, system-ui, sans-serif;
  font-size: 17px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, video { display: block; max-width: 100%; height: auto; }

::selection { background: var(--accent); color: var(--accent-ink); }

.skip { position: absolute; left: -9999px; top: 0; background: var(--ink); color: var(--ground); padding: 12px 18px; z-index: 60; }
.skip:focus { left: 12px; top: 12px; }

:where(a, button, [tabindex]):focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ───────────────────────────── type ───────────────────────────── */

.display {
  margin: 0;
  font-size: clamp(42px, 7.4vw, 124px);
  line-height: 0.9;
  letter-spacing: -0.048em;
  font-weight: 700;
  /* Three shadows, and the tight one is the one doing the work. A 60px blur
     spreads its ink so thinly that over a bright wallpaper it contributes
     almost nothing — which is why this used to need the picture dimmed behind
     it. The 2px and 12px terms put the darkness immediately against the glyph,
     where it separates the letter from whatever it lands on and stays
     invisible as a "drop shadow". The 60px one stays because it is what makes
     the type feel lit on the dark wallpapers. */
  text-shadow:
    0 0 3px rgba(0, 0, 0, 0.6),
    0 1px 3px rgba(0, 0, 0, 0.6),
    0 2px 18px rgba(0, 0, 0, 0.5),
    0 2px 60px rgba(0, 0, 0, 0.7);
}

.h2 {
  margin: 0;
  font-size: clamp(30px, 4.2vw, 62px);
  line-height: 1;
  letter-spacing: -0.035em;
  font-weight: 700;
  max-width: 19ch;
}

.scene .h2 {
  text-shadow:
    0 0 3px rgba(0, 0, 0, 0.6),
    0 1px 3px rgba(0, 0, 0, 0.6),
    0 2px 18px rgba(0, 0, 0, 0.5),
    0 2px 50px rgba(0, 0, 0, 0.7);
}

h3 { margin: 0 0 10px; font-size: 19px; line-height: 1.3; letter-spacing: -0.015em; font-weight: 600; }

.lede {
  margin: 28px 0 0;
  font-size: clamp(17px, 1.4vw, 21px);
  line-height: 1.5;
  max-width: 44ch;
  color: var(--ink);
  /* Running text is where a bright wallpaper bites hardest: it is small, it is
     not bold, and there is a lot of it. The tight pair carries it. */
  text-shadow:
    0 0 4px rgba(0, 0, 0, 0.95),
    0 0 2px rgba(0, 0, 0, 0.9),
    0 1px 3px rgba(0, 0, 0, 0.9),
    0 1px 18px rgba(0, 0, 0, 0.75);
}

.say { margin: 0; font-size: 17px; line-height: 1.58; max-width: var(--measure); }

/* Over a scene the type carries a little more weight and a shadow so it holds
   against whatever the wallpaper is doing underneath. */
/* Running text keeps the full ink over a scene. Dropping it to grey is what
   makes copy on a photograph look like a caption nobody is meant to read, and
   the shadow is doing the separation work anyway. */
.say--on {
  color: var(--ink);
  font-size: clamp(17px, 1.15vw, 19px);
  text-shadow:
    0 0 4px rgba(0, 0, 0, 0.95),
    0 0 2px rgba(0, 0, 0, 0.9),
    0 1px 3px rgba(0, 0, 0, 0.9),
    0 1px 18px rgba(0, 0, 0, 0.75);
}

/* ─────────────────────────── top bar ─────────────────────────── */

.bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 28px;
  padding: 20px var(--gap-side);
  transition: background-color 0.4s var(--ease);
}

/* Transparent over the hero, solid once the page is under it. */
.bar.is-landed { background: rgba(6, 7, 10, 0.92); }

/* While it is transparent it is sitting directly on the wallpaper, so the
   wordmark and the links carry their own separation the same way the headline
   does. Dropped once the bar lands, where it has a background of its own and a
   shadow would just look like dirt on the type.

   Heavier here than anywhere else on the page, and it has to be. This is 19px
   and 15px type at the very top of the picture, which is where a sky is, and
   over "Coastal View" the wordmark is white sitting on a white cloud. Rings
   rather than a drop: concentric 0-offset blurs put the darkness evenly around
   the glyph, which is what you end up reading against. A single soft shadow at
   this size just greys the letter. */
.bar:not(.is-landed) .bar__name,
.bar:not(.is-landed) .bar__nav a {
  text-shadow:
    0 0 2px rgba(0, 0, 0, 0.95),
    0 0 5px rgba(0, 0, 0, 0.9),
    0 0 10px rgba(0, 0, 0, 0.75),
    0 1px 3px rgba(0, 0, 0, 0.9);
}

/* And full ink while it is over a wallpaper, for the reason .say--on gives
   two screens up: grey on a photograph reads as something not meant to be
   read. It goes back to soft once the bar has landed on the page colour and
   the hierarchy means something again. */
.bar:not(.is-landed) .bar__nav a { color: var(--ink); }

.bar__inner { display: contents; }

.bar__name { font-size: 19px; font-weight: 700; letter-spacing: -0.03em; color: var(--ink); text-decoration: none; }

.bar__nav { display: flex; gap: 26px; margin-left: auto; font-size: 15px; }
.bar__nav a { color: var(--ink-soft); text-decoration: none; transition: color 0.2s var(--ease); }
.bar__nav a:hover { color: var(--ink); }

.bar__buy {
  padding: 9px 18px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--ground);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  transition: transform 0.2s var(--ease);
}
.bar__buy:hover { transform: translateY(-1px); }
.bar__buy:active { transform: translateY(0); }

/* ───────────────────────────── acts ───────────────────────────── */

.acts { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 36px; }

.act {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 15px 26px;
  border: 0;
  border-radius: 999px;
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.18s var(--ease), background-color 0.2s var(--ease);
}

.act--solid { background: var(--accent); color: var(--accent-ink); }
.act--solid:hover { background: #6e69ff; transform: translateY(-2px); }

.act--quiet { background: var(--ground-up-2); color: var(--ink); }
.act--quiet:hover { background: #292933; transform: translateY(-2px); }

/* On a scene the second action is the wallpaper itself, dimmed. */
.act--ghost { background: rgba(255, 255, 255, 0.14); color: var(--ink); }
.act--ghost:hover { background: rgba(255, 255, 255, 0.22); transform: translateY(-2px); }

.act:active { transform: translateY(0); }
.act--wide { width: 100%; }

.nub {
  width: 46px; height: 46px;
  border: 0; border-radius: 999px;
  background: var(--ground-up-2); color: var(--ink);
  font-size: 17px; cursor: pointer;
  transition: transform 0.18s var(--ease), background-color 0.2s var(--ease);
}
.nub:hover { background: #30303c; transform: translateY(-2px); }
.nub:active { transform: translateY(0); }
.nub[disabled] { opacity: 0.3; cursor: default; transform: none; }

/* ───────────────────────────── scenes ─────────────────────────────
   A room. The wallpaper fills it, the words stand in it. */

.scene {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 86svh;
  padding: clamp(110px, 14vh, 190px) var(--gap-side) clamp(74px, 9vh, 120px);
  overflow: hidden;
  isolation: isolate;
}

.scene--tall { min-height: 100svh; }

.scene--close { align-items: flex-end; min-height: 82svh; }

.scene__media { position: absolute; inset: 0; z-index: -2; background: var(--ground-up); }

.scene__media video,
.scene__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.1s var(--ease);
}

.scene__media .is-live { opacity: 1; }

/* The seam, and nothing else.

   This used to be two heavy ramps: one up the whole height and one out of the
   bottom-left corner, together dark enough to put a wallpaper's foreground at
   94% black so that display type could be laid over it without thinking. It
   worked, and it meant every visitor saw the wallpapers through a filter —
   on a page whose entire argument is how the wallpapers look. The owner's
   call, and the right one: the wallpaper is the product, so it plays as it is.

   The contrast the words need now lives on the words, in .display, .lede,
   .say--on and .bar (text-shadow with a tight term that sits against the
   glyph). What is left here is the bottom 13%, handing the picture back to the
   page colour so a scene joins the next section without a hard line. */
.scene__wash {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to top, var(--ground) 0%, rgba(6, 7, 10, 0.72) 4%,
    rgba(6, 7, 10, 0.22) 9%, rgba(6, 7, 10, 0) 13%);
}

/* --left was the same idea aimed at the left half, for the two scenes whose
   words run down that side. It is gone for the same reason, and the class now
   inherits the seam above — kept rather than deleted so the markup does not
   have to care which scene it is. */

.scene__say { position: relative; width: 100%; max-width: var(--page); margin: 0 auto; }

/* Narrower than a full scene, but still anchored to the gutter. Centring it
   put these two headings 21px inside every other heading on the page, which is
   exactly the kind of drift nobody can name and everybody can feel. */
.scene__say--mid { max-width: min(var(--page), 1240px); margin-inline: 0 auto; }

/* Running text in a scene had no spacing at all: .say is margin:0, which is
   right inside .band__head because that is a grid with a gap, and wrong here
   where the paragraphs are in normal flow. The headline and the paragraph
   under it were touching at 0px, and the two paragraphs ran together as one
   block — in "Looking is not downloading" and "Quit the app" both.

   It had been there a while. The wash those scenes used to carry was dark
   enough that the text read as one soft mass and nobody saw where the gaps
   were missing; taking the wash off is what showed it. The hero never had the
   problem because .lede carries its own margin-top. */
.scene__say .h2 + .say--on { margin-top: 24px; }
.scene__say .say--on + .say--on { margin-top: 16px; }

/* The gallery card: what you are looking at, named, the way a wallpaper app
   names things. */
/* All that is left of the gallery card: the control, where the card was. */
.plate {
  position: absolute;
  right: var(--gap-side);
  bottom: clamp(74px, 9vh, 120px);
  z-index: 2;
  margin: 0;
}

.plate__dots { display: flex; gap: 10px; }

.plate__dot {
  width: 34px; height: 4px;
  padding: 0; border: 0; border-radius: 999px;
  background: rgba(255, 255, 255, 0.28);
  cursor: pointer;
  /* A dark ring so the control does not depend on the wallpaper behind it.
     The selected dot is white, and on a phone the dots sit low in the scene —
     which over "Coastal View" is open surf, where white on white put it at
     2.3:1 and the one thing telling you where you are in five slides
     disappeared. The ring gives it a dark edge whatever is underneath, and on
     the dark wallpapers it is a dark line on a dark picture: invisible. */
  box-shadow: 0 0 0 1px rgba(6, 7, 10, 0.45), 0 1px 4px rgba(6, 7, 10, 0.45);
  transition: background-color 0.3s var(--ease), transform 0.2s var(--ease);
}
.plate__dot:hover { transform: scaleY(1.8); }
.plate__dot[aria-selected="true"] { background: var(--ink); }

/* ───────────────────────────── bands ───────────────────────────── */

.band { padding: var(--gap-band) 0; }
.band--tight { padding-bottom: var(--gap-band); }

.band__head {
  display: grid;
  gap: 20px;
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--gap-side) 48px;
}

/* ───────────────────────── collection rail ───────────────────────── */

.rail { position: relative; }

.rail__track {
  display: flex;
  gap: 18px;
  padding: 0 max(var(--gap-side), calc((100% - var(--page)) / 2 + var(--gap-side))) 10px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-padding-inline: max(var(--gap-side), calc((100% - var(--page)) / 2 + var(--gap-side)));
  scrollbar-width: none;
}
.rail__track::-webkit-scrollbar { display: none; }

.tile {
  flex: 0 0 clamp(260px, 29vw, 460px);
  scroll-snap-align: start;
  margin: 0;
  transition: transform 0.4s var(--ease);
}
.tile:hover { transform: translateY(-8px); }

/* The tile is the picture. Nothing under it, nothing over it. */
.tile {
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: 16px;
  overflow: hidden;
  background: var(--ground-up);
}
.tile img,
.tile__clip {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.7s var(--ease), opacity 0.6s var(--ease);
}

/* The still holds the frame until the clip has one of its own. */
.tile__clip { opacity: 0; }
.tile__clip.is-live { opacity: 1; }

.tile:hover img,
.tile:hover .tile__clip { transform: scale(1.04); }

.rail__acts {
  display: flex; gap: 10px;
  max-width: var(--page); margin: 0 auto;
  padding: 30px var(--gap-side) 0;
}

/* ───────────────────────────── facts ───────────────────────────── */

.facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 24px;
  margin: 40px 0 0;
  max-width: 720px;
}

.facts div { display: grid; gap: 4px; }
.facts dt {
  font-size: 14px;
  color: #c9cad2;
  text-shadow: 0 0 4px rgba(0, 0, 0, 0.95), 0 1px 3px rgba(0, 0, 0, 0.9), 0 1px 16px rgba(0, 0, 0, 0.8);
}
.facts dd {
  margin: 0;
  font-size: clamp(24px, 2.6vw, 34px);
  font-weight: 700;
  letter-spacing: -0.035em;
  text-shadow: 0 0 3px rgba(0, 0, 0, 0.7), 0 1px 3px rgba(0, 0, 0, 0.7), 0 1px 24px rgba(0, 0, 0, 0.5);
}

/* ─────────────────────────── app deck ─────────────────────────── */

.deck { max-width: min(var(--page), 1500px); margin: 0 auto; padding: 0 var(--gap-side); }

.deck__pane {
  position: relative;
  aspect-ratio: 2200 / 1441;
  border-radius: 18px;
  overflow: hidden;
  background: var(--ground-up);
}

.deck__pane img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.012);
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}
.deck__pane img.is-live { opacity: 1; transform: scale(1); }

.deck__acts { display: flex; align-items: center; gap: 16px; margin-top: 22px; }
.deck__count { margin: 0; font-size: 15px; color: var(--ink-soft); }
.deck__said { margin: 0 0 0 auto; font-size: 15px; text-align: right; max-width: 44ch; }

/* ───────────────────────────── price ───────────────────────────── */

.price {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 440px);
  gap: clamp(32px, 5vw, 76px);
  align-items: start;
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--gap-side) var(--gap-band);
}

.price__say { display: grid; gap: 22px; }

.card { padding: 38px; border-radius: 22px; background: var(--ground-up); }
.card__kind { margin: 0; font-size: 16px; color: var(--ink-soft); }

.card__cost {
  display: flex; align-items: flex-start; gap: 4px;
  margin: 10px 0 0;
  font-size: clamp(58px, 7vw, 84px);
  line-height: 1; letter-spacing: -0.048em; font-weight: 700;
}
.card__cur { margin-top: 0.28em; font-size: 0.42em; letter-spacing: -0.02em; }
.card__once { margin: 12px 0 30px; font-size: 16px; color: var(--ink-soft); }

.card__has { margin: 0 0 30px; padding: 0; list-style: none; display: grid; gap: 12px; font-size: 16px; }
.card__has li { position: relative; padding-left: 26px; line-height: 1.45; }
.card__has li::before {
  content: ""; position: absolute; left: 2px; top: 0.58em;
  width: 9px; height: 9px; border-radius: 999px; background: var(--accent);
}

.card .act + .act { margin-top: 12px; }
.card__note { margin: 16px 0 0; min-height: 1.4em; font-size: 14px; color: var(--ink-soft); }

/* Facts about the file the button hands you. Static rather than scripted: the
   download is a plain link now, and the browser's own download UI is better
   feedback than anything this page could say about it. */
.card__fine { margin: 14px 0 0; font-size: 13px; line-height: 1.55; color: var(--ink-soft); }
.card__fine a { color: var(--ink-soft); text-decoration: underline; text-underline-offset: 2px; }
.card__fine a:hover { color: var(--ink); }

/* ───────────────────────────── specs ───────────────────────────── */

.specs { max-width: var(--page); margin: 0 auto; padding: 0 var(--gap-side) var(--gap-band); }
.specs__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(26px, 3.4vw, 56px);
  margin-top: 46px;
}
.specs__grid p { margin: 0; font-size: 16px; line-height: 1.58; max-width: 52ch; }

/* ───────────────────────────── close ───────────────────────────── */

.foot__title { max-width: 15ch; }
/* The credit line and the recovery link, which sit on the last wallpaper. This
   had no shadow at all — 14px of soft grey straight onto the picture, which
   was survivable only because the wash underneath used to be nearly solid.
   It keeps the quiet colour, because it is fine print and the hierarchy is
   right, and gets the halo so that "Lost your licence key?" is still a link
   somebody can find on a moonlit sea. */
.foot__fine {
  margin: 46px 0 0;
  font-size: 14px;
  color: var(--ink-soft);
  max-width: 58ch;
  text-shadow: 0 0 4px rgba(0, 0, 0, 0.95), 0 1px 3px rgba(0, 0, 0, 0.9), 0 1px 16px rgba(0, 0, 0, 0.8);
}

/* ──────────────────────────── motion ──────────────────────────── */

[data-rise] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.75s var(--ease), transform 0.75s var(--ease);
}
[data-rise].is-in { opacity: 1; transform: none; }
.no-js [data-rise] { opacity: 1; transform: none; }

/* The backstop. An entrance is a nicety; content that never arrives because a
   transition did not run is a broken page, so after a few seconds anything
   still waiting is simply shown, with no transition to depend on. */
[data-rise].is-shown { opacity: 1 !important; transform: none !important; transition: none !important; }

/* ──────────────────────────── widths ──────────────────────────── */

@media (max-width: 1040px) {
  .price { grid-template-columns: minmax(0, 1fr); gap: 44px; }
  .bar__nav { display: none; }
  /* The scene is a row at desktop width, with the words on the left and the
     gallery card on the right. Narrower than this there is no room for two
     columns, so it becomes a stack — without this the card stayed a flex
     sibling and sat on top of the buttons. */
  .scene { flex-direction: column; align-items: flex-start; }
  .scene__say { width: 100%; }
  .plate { position: static; align-items: flex-start; text-align: left; margin-top: 36px; }
  .plate__dots { margin-top: 12px; }
}

@media (max-width: 720px) {
  body { font-size: 16px; }
  /* The line break in the hero is a composition for wide screens. At this
     width it makes three lines out of two, so the text wraps on its own. */
  .display br { display: none; }
  /* A 15ch cap is two lines at desktop size and three down here. */
  .foot__title { max-width: none; }
  .specs__grid { grid-template-columns: minmax(0, 1fr); }
  .facts { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  .facts div { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }
  .card { padding: 26px; }
  .acts .act { flex: 1 1 auto; }
  .scene { min-height: 78svh; }
  .scene--tall { min-height: 94svh; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
  [data-rise] { opacity: 1; transform: none; }
  .scene__media video, .scene__media img, .deck__pane img { transition: none; }
  /* Nothing plays, so the poster frame is what the tile is. */
  .tile__clip { display: none; }
}
