/* Hand-authored, no build step, no framework, no external fonts. Every internal
   url() stays relative so the page is identical at the domain root and at
   /bektek/ on the platform.

   The page is a full-screen deck: a title screen that resolves out of blur,
   then one snapping slide per announced product, then a quieter closing slide.
   Palette is paper and red throughout. */

:root {
  --paper: #fbfbf9;
  --ink: #16181a;
  --dim: #5c6166;
  --red: #c3251c;

  --display-face: "Helvetica Neue", Helvetica, Arial, system-ui, sans-serif;
  --mono-face: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* Paper grain, generated rather than fetched: an inline SVG turbulence tile. */
  --paper-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)' opacity='0.055'/%3E%3C/svg%3E");

  /* Text over the darkened product artwork. */
  --on-art: #f4f2ee;
  --on-art-dim: rgba(244, 242, 238, 0.68);
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-snap-type: y mandatory;
}

body {
  margin: 0;
  background-color: var(--paper);
  background-image: var(--paper-grain);
  color: var(--ink);
  font: 16px/1.6 var(--display-face);
}

h1, h2, h3, p, ul, dl, dd { margin: 0; }
ul { list-style: none; padding: 0; }

/* The frame sits on the viewport, so it stays put while the deck moves past. */
.vignette {
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background:
    radial-gradient(118% 88% at 50% 50%, rgba(12, 13, 14, 0) 48%, rgba(12, 13, 14, 0.31) 100%),
    linear-gradient(0deg, rgba(12, 13, 14, 0.11), rgba(12, 13, 14, 0) 26%);
  opacity: 0;
  animation: frame-settles-in 5s cubic-bezier(0.62, 0.02, 0.38, 1) 0.2s both;
}

/* Slides -------------------------------------------------------------------- */

.slide {
  position: relative;
  display: flex;
  min-height: 100svh;
  overflow: hidden;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

/* Product slides sit on a darkened, heavily blurred copy of their own art, so
   each one carries its product's colour while every slide reads at the same
   depth. Scaled past the slide's edge so the blur's feathered rim never shows. */
.slide--product { background: var(--ink); color: var(--on-art); }

.slide-art {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--slide-art) center / cover no-repeat;
  filter: blur(28px) saturate(1.15) brightness(0.5);
  transform: scale(1.15);
}

/* A flat graphic pattern carries more contrast than a photograph, so it takes
   a heavier hand to settle into a backdrop. */
#bubble-notes .slide-art { filter: blur(44px) saturate(0.6) brightness(0.45); transform: scale(1.25); }

.slide-content {
  position: relative;
  z-index: 2;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(1.25rem, 3.5vh, 2.5rem);
  padding: clamp(2.75rem, 7vh, 5rem) 16px;
  text-align: center;
}

/* The <picture> wrappers step aside so the image itself is the flex item. */
.slide-content > picture { display: contents; }

/* Stacked, the image shares the screen with the whole body, so it is capped by
   height as well as width. max-* rather than width keeps the box on the
   image's own aspect, so the corners and shadow stay on the picture. */
.slide-shot {
  display: block;
  width: auto;
  height: auto;
  max-width: min(100%, 40rem);
  max-height: 38svh;
  border-radius: 6px;
  box-shadow: 0 1.5rem 3rem -1rem rgba(0, 0, 0, 0.6), 0 0.5rem 1rem -0.5rem rgba(0, 0, 0, 0.45);
}

/* The app screenshot is a whole phone screen, so it is drawn as one. */
.slide-shot--phone { border-radius: 1.4rem; max-height: 38svh; }

.slide-body { width: 100%; max-width: 30rem; }

.slide-name { line-height: 0; }

/* Logos differ in shape, so each is bounded by height as well as width. */
.slide-name-logo {
  display: inline-block;
  width: auto;
  max-width: min(100%, 22rem);
  height: auto;
  max-height: 5.5rem;
}
.slide-name-logo--wide { max-width: min(100%, 24rem); }

.slide-desc {
  margin-top: 1.25rem;
  font-size: clamp(1.2rem, 1.6vw, 1.45rem);
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.slide-detail {
  margin-top: 0.4rem;
  font-size: 0.95rem;
  color: var(--on-art-dim);
}

/* The status and the link are one family of small pills: red for where a
   product stands, paper for where to go. */
.slide-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.5rem;
}

.slide-status,
.slide-link {
  padding: 0.6rem 1rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.02em;
}

.slide-status { background: var(--red); color: var(--paper); }

/* Pills stacked, both as wide as the wider one, so the pair reads as one unit. */
.slide-actions--stack {
  flex-direction: column;
  align-items: stretch;
  width: fit-content;
  margin-inline: auto;
  text-align: center;
}
.slide-status { display: flex; align-items: center; justify-content: center; gap: 0.4rem; }
.status-icon { width: 0.95rem; height: 0.95rem; flex: none; fill: currentColor; }

.slide-link {
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  transition: background-color 0.2s ease;
}
.slide-link:hover { background: #ffffff; }
.slide-link:focus-visible { outline: 2px solid var(--paper); outline-offset: 3px; }

/* Supported platforms read at full strength; planned ones sit back. */
.slide-platforms {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.9rem;
  margin-top: 1.4rem;
}
.platform-item { display: flex; }
.platform-icon { width: 20px; height: 20px; fill: currentColor; }
.platform-item.is-planned { opacity: 0.45; }

/* Visually hidden but still announced: the icon's title gives the tooltip,
   this gives screen readers the same text. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Wide: the body on the left, the image on the right. */
@media (min-width: 75rem) {
  .slide-content {
    flex-direction: row;
    gap: clamp(3rem, 5vw, 6rem);
    padding: 3rem clamp(3rem, 6vw, 7rem);
    text-align: left;
  }
  .slide-shot { order: 2; min-width: 0; max-width: min(52vw, 52rem); max-height: 72svh; }
  .slide-shot--phone { max-height: 76svh; }
  .slide-body { order: 1; flex: 0 1 28rem; }
  .slide-actions,
  .slide-platforms { justify-content: flex-start; }
  .slide-name-logo { max-width: 100%; max-height: 7rem; }
}

/* Bubble Notes: the landing page's light/dark phone pair. Frame, overlap, offset
   and shadow are copied from bubblenotes/story.css (.device, .chapter--pair);
   only the size is fitted to the slide. --bn-w is one phone's width, tied to
   the viewport height so the whole pair (about 2.4x that tall) fits the slide. */
.bn-pair {
  --bn-w: min(40vw, 17svh);
  display: flex;
  justify-content: center;
  /* The front phone is shifted down 19% of its own height (about 0.39 x --bn-w)
     by a transform, which takes no layout space; clear that plus a real gap. */
  padding-bottom: calc(var(--bn-w) * 0.4 + 2rem);
}
.bn-device {
  display: block;
  flex: none;
  width: var(--bn-w);
  padding: calc(var(--bn-w) * 0.032);
  border-radius: calc(var(--bn-w) * 0.176);
  background: #0d0a12;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14), inset 0 0 0 1px rgba(255, 255, 255, 0.06),
    0 40px 80px -24px rgba(0, 0, 0, 0.7);
}
.bn-device img { display: block; width: 100%; height: auto; border-radius: calc(var(--bn-w) * 0.144); }
.bn-device--back { transform: translateX(14%); }
.bn-device--front { margin-left: -18%; transform: translate(-14%, 19%); }
@media (min-width: 75rem) {
  .bn-pair { --bn-w: min(250px, 29svh); order: 2; padding-bottom: calc(var(--bn-w) * 0.19); }
  .slide-actions--stack { margin-inline: 0; }
}

/* Title screen -------------------------------------------------------------- */

.slide--title { align-items: center; justify-content: center; text-align: center; }

.title-block { padding: 0 clamp(1.5rem, 6vw, 5rem); }

/* The name is the wordmark image, not live text; the h1 just carries the
   choreography and the accessible name (via the img's alt). */
.studio-name { line-height: 0; }

.studio-wordmark {
  display: block;
  margin: 0 auto;
  width: clamp(16rem, 70vw, 34rem);
  height: auto;
}

.studio-tagline {
  margin-top: clamp(0.75rem, 2vh, 1.5rem);
  font-size: clamp(1rem, 3.2vw, 1.75rem);
  font-weight: 400;
  color: var(--ink);
}

/* Sized off the wordmark so the pair scales together. */
.studio-tagline-image {
  display: block;
  margin: 0 auto;
  width: clamp(12rem, 50vw, 24rem);
  height: auto;
}

/* The choreography. Both lines run a slow five second curve that surfaces
   enough of the mark for the eye to lock on and start anticipating where
   it is headed, then breaks that expectation with a fast pop to full
   clarity. The wordmark pops first, the tagline pops in on the same shape
   of curve but held back further, and the vignette is still arriving
   behind both of them.

   The wordmark is a solid raster mark rather than fine text strokes, so it
   needs a more visible ramp than live text does to read as "resolving"
   rather than just being absent; the tagline keeps that same shape,
   shifted later so it reads as a second, distinct beat rather than the
   wordmark's echo. Both hold their pop to a tight 3% window so it lands as
   a snap, not a fade. */
.studio-name {
  opacity: 0;
  animation: wordmark-resolves-then-snaps 5s linear 0.45s both;
  will-change: opacity, filter;
}
.studio-tagline {
  opacity: 0;
  animation: tagline-resolves-then-snaps 5s linear 1.05s both;
  will-change: opacity, filter;
}

@keyframes wordmark-resolves-then-snaps {
  0%   { opacity: 0;    filter: blur(30px); }
  4%   { opacity: 0.32; filter: blur(21px); }
  8%   { opacity: 0.55; filter: blur(13px); }
  10%  { opacity: 1;    filter: blur(0);    }
  100% { opacity: 1;    filter: blur(0);    }
}

@keyframes tagline-resolves-then-snaps {
  0%   { opacity: 0;    filter: blur(30px); }
  8%   { opacity: 0.32; filter: blur(21px); }
  12%  { opacity: 0.55; filter: blur(13px); }
  14%  { opacity: 1;    filter: blur(0);    }
  100% { opacity: 1;    filter: blur(0);    }
}

@keyframes frame-settles-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Deck indicators. Two viewport-anchored chevrons, each shown only while the
   move it stands for is possible: onward at the foot of the screen, back at the
   head. They fade on the vignette's own easing so they belong to the frame
   rather than to the slide underneath, though over a much shorter duration
   since these toggle on every move instead of settling in once. */
.deck-hint {
  position: fixed;
  /* Below the slide bodies and their framed shots (z-index 2), above the slide
     backdrops (z-index 1), so an indicator passes under the artwork rather
     than over it. */
  z-index: 1;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s cubic-bezier(0.62, 0.02, 0.38, 1);
}
.deck-hint--onward { bottom: clamp(2rem, 6vh, 4rem); }
.deck-hint--back { top: clamp(2rem, 6vh, 4rem); }
/* Arriving takes its time, 5s like the vignette, so the
   indicator is never what pulls the eye on a fresh slide. Leaving does not:
   a chevron for a move that is no longer possible has to go promptly. */
.deck-hint.is-possible {
  opacity: 0.2;
  transition-duration: 5s;
}

.deck-hint__arrow {
  width: 12px;
  height: 12px;
  border-left: 2px solid var(--ink);
  border-top: 2px solid var(--ink);
  /* Matches the indicators' fade-out, so a colour change over a slide boundary
     reads as the same gesture as an indicator leaving. */
  transition: border-color 0.6s cubic-bezier(0.62, 0.02, 0.38, 1);
}

/* A slide dark enough to swallow a charcoal chevron asks for a light one. */
.deck-hints-light .deck-hint__arrow { border-color: var(--paper); }

/* The chevron is one corner rotated into place, so each direction carries its
   rotation through every frame of its beckon or the arrow spins as it bumps. */
/* Each chevron points the way the deck moves, not the way the finger swipes:
   onward is down through the content, back is up. */
.deck-hint--onward .deck-hint__arrow {
  transform: rotate(225deg);
  animation: onward-beckons 2.2s ease-in-out infinite;
}
.deck-hint--back .deck-hint__arrow {
  transform: rotate(45deg);
  animation: back-beckons 2.2s ease-in-out infinite;
}

@keyframes onward-beckons {
  0%, 60%, 100% { transform: translateY(0) rotate(225deg); }
  30%           { transform: translateY(7px) rotate(225deg); }
}
@keyframes back-beckons {
  0%, 60%, 100% { transform: translateY(0) rotate(45deg); }
  30%           { transform: translateY(-7px) rotate(45deg); }
}

/* Closing slide ------------------------------------------------------------- */

.slide--closing { align-items: center; justify-content: center; }

.closing-body {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 60rem;
  margin: 0 auto;
  padding: clamp(4rem, 10vh, 7rem) clamp(1.5rem, 6vw, 5rem);
  /* A sign-off that bookends the title screen: the same lockup, held still. */
  text-align: center;
}

.closing-heading { line-height: 0; }

.closing-tagline { margin-top: clamp(0.75rem, 2vh, 1.5rem); }

.closing-contact {
  margin-top: clamp(2rem, 6vh, 3.5rem);
  font: 400 15px/1.5 var(--mono-face);
  color: var(--ink);
}

.closing-footer {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: clamp(1.5rem, 4vh, 2.5rem);
  text-align: center;
  font-size: 12px;
  line-height: 1.5;
  color: var(--dim);
}

/* Reduced motion: present everything at once, no blur, no beckoning. */
@media (prefers-reduced-motion: reduce) {
  .studio-name,
  .studio-tagline,
  .vignette {
    animation: none;
    opacity: 1;
    filter: none;
  }
  /* The indicators still appear and disappear with availability, they just
     stop beckoning and stop easing. */
  .deck-hint { transition: none; }
  .deck-hint__arrow { animation: none; transition: none; }
}
