/* ==========================================================================
   SHOON — one stylesheet for the whole site.
   ==========================================================================

   Every page links this. Before it existed each page carried its own copy of
   the palette and the type scale, which is how two pages end up disagreeing
   about what the brand looks like — and why a change to the logo had to be
   made three times.

   Three things it establishes:

     1. Light and dark, following the visitor's own device by default, with a
        toggle that overrides it. Everything is expressed as a token so a
        component never has to know which theme it is in.

     2. One responsive site rather than a separate mobile one. Separate mobile
        sites double the work forever, split the search ranking across two
        addresses, and break when somebody sends a link from their phone to a
        laptop. This reshapes instead.

     3. The mark, drawn correctly. See .logo.
   ========================================================================== */

/* --------------------------------------------------------------- tokens -- */

:root {
  /* The aura, identical to the values in the app's tokens.dart. If these ever
     drift the website stops being the same product as the thing it sells. */
  --rose:   #f43f5e;
  --pink:   #ec4899;
  --violet: #a855f7;
  --indigo: #6366f1;
  --blue:   #2d6cf0;
  --aura: linear-gradient(100deg, var(--rose), var(--pink) 34%, var(--violet) 68%, var(--indigo));

  /* Neutrals carry a slight violet bias rather than being pure grey, so they
     sit with the aura instead of beside it. */
  --ground:   #ffffff;
  --raise:    #faf8fc;
  --sink:     #f4f1f7;
  --ink:      #0d0b11;
  --ink-2:    #3f3949;
  --muted:    #6b6478;
  --faint:    #9a93a6;
  --line:     #e8e3ee;
  --line-2:   #d5cee0;
  --veil:     rgba(255, 255, 255, 0.82);

  --shadow-1: 0 1px 2px rgba(13, 11, 17, .05), 0 10px 30px -14px rgba(13, 11, 17, .16);
  --shadow-2: 0 2px 6px rgba(13, 11, 17, .06), 0 26px 60px -22px rgba(13, 11, 17, .28);

  --gutter: 1.35rem;
  --maxw: 68rem;
  --measure: 38rem;

  --display: "Archivo", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --body: "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --mono: "Geist Mono", ui-monospace, "Cascadia Mono", monospace;
}

/* The un-stamped default: most visitors never touch a toggle, so the device's
   own setting has to be what decides. Guarded so an explicit light choice
   still wins on a dark OS. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #0b0910;
    --raise:  #141019;
    --sink:   #1b1624;
    --ink:    #f4f1f8;
    --ink-2:  #cfc7db;
    --muted:  #9f96ae;
    --faint:  #756c85;
    --line:   #251f30;
    --line-2: #362e44;
    --veil:   rgba(11, 9, 16, 0.78);

    --rose:   #ff6b90;
    --pink:   #f472b6;
    --violet: #c084fc;
    --indigo: #8b7cff;
    --blue:   #6f9dff;

    --shadow-1: 0 1px 2px rgba(0,0,0,.5), 0 10px 30px -14px rgba(0,0,0,.7);
    --shadow-2: 0 2px 6px rgba(0,0,0,.55), 0 26px 60px -22px rgba(0,0,0,.8);
  }
}

/* And again for the explicit choice, so the toggle wins in both directions. */
:root[data-theme="dark"] {
  --ground: #0b0910;
  --raise:  #141019;
  --sink:   #1b1624;
  --ink:    #f4f1f8;
  --ink-2:  #cfc7db;
  --muted:  #9f96ae;
  --faint:  #756c85;
  --line:   #251f30;
  --line-2: #362e44;
  --veil:   rgba(11, 9, 16, 0.78);

  --rose:   #ff6b90;
  --pink:   #f472b6;
  --violet: #c084fc;
  --indigo: #8b7cff;
  --blue:   #6f9dff;

  --shadow-1: 0 1px 2px rgba(0,0,0,.5), 0 10px 30px -14px rgba(0,0,0,.7);
  --shadow-2: 0 2px 6px rgba(0,0,0,.55), 0 26px 60px -22px rgba(0,0,0,.8);
}

/* ----------------------------------------------------------------- base -- */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.62;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

.wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 var(--gutter); }
.measure { max-width: var(--measure); }

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 900;
  text-transform: lowercase;
  text-wrap: balance;
  margin: 0;
}
h1 { font-size: clamp(2.9rem, 9vw, 6.2rem); line-height: .92; letter-spacing: -0.045em; }
h2 { font-size: clamp(2rem, 5.2vw, 3.4rem); line-height: .98; letter-spacing: -0.038em; }
h3 { font-size: clamp(1.25rem, 2.6vw, 1.7rem); line-height: 1.1; letter-spacing: -0.028em; font-weight: 800; }
h4 { font-size: 1.03rem; line-height: 1.25; letter-spacing: -0.02em; font-weight: 700; text-transform: none; }

p { margin: 0 0 1.1rem; color: var(--ink-2); }
p.lead { font-size: clamp(1.1rem, 2.2vw, 1.32rem); line-height: 1.5; color: var(--muted); }
strong { color: var(--ink); font-weight: 600; }
em { font-style: italic; color: var(--ink); }

a { color: inherit; }
a:focus-visible, button:focus-visible {
  outline: 2px solid var(--violet);
  outline-offset: 3px;
  border-radius: 4px;
}

.caps {
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .17em;
  text-transform: uppercase;
  color: var(--faint);
}

.grad {
  background: var(--aura);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ----------------------------------------------------------------- mark -- */

/*
  The wordmark: s h ∞ n.

  It used to read with an obvious hole either side of the infinity. Two causes
  stacked. The flex `gap` put 0.3em between the mark and each letter — and the
  mark's own artwork only occupies about 140 of its 200-unit viewBox, so a
  further 0.15 of the element's width was empty on each side before the ink
  started.

  Fixing only the gap would have left the second half of the problem, which is
  why it looked wrong even at a gap of zero. The negative margin pulls the
  letters across that built-in padding; the viewBox is left alone so the round
  stroke cap is never clipped.
*/
.logo {
  display: inline-flex;
  align-items: center;
  gap: 0;
  font-family: var(--display);
  font-weight: 900;
  font-size: 1.2rem;
  letter-spacing: -0.03em;
  color: var(--ink);
  text-decoration: none;
  line-height: 1;
}
.logo svg {
  width: 2em;
  height: 1em;
  margin: 0 -0.26em;
  flex: 0 0 auto;
}

/* ------------------------------------------------------------- chrome -- */

.site-head {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--veil);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .2s ease;
}
.site-head.scrolled { border-bottom-color: var(--line); }

.site-head .wrap {
  display: flex;
  align-items: center;
  gap: 1rem;
  height: 4.1rem;
}

.nav {
  display: flex;
  align-items: center;
  gap: .3rem;
  margin-left: auto;
}
.nav a {
  font-family: var(--display);
  font-weight: 700;
  font-size: .88rem;
  text-transform: lowercase;
  letter-spacing: -0.015em;
  color: var(--muted);
  text-decoration: none;
  padding: .45rem .7rem;
  border-radius: 99px;
  transition: color .15s ease, background .15s ease;
  white-space: nowrap;
}
.nav a:hover { color: var(--ink); background: var(--sink); }
.nav a[aria-current="page"] { color: var(--ink); }

/* Below this the labels stop fitting beside the mark and the toggle. */
@media (max-width: 40rem) {
  .nav a.opt { display: none; }
}

.theme-toggle {
  width: 2.2rem;
  height: 2.2rem;
  display: grid;
  place-items: center;
  border-radius: 99px;
  border: 1px solid var(--line);
  background: var(--ground);
  color: var(--muted);
  cursor: pointer;
  flex: 0 0 auto;
  transition: color .15s ease, border-color .15s ease;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--line-2); }
.theme-toggle svg { width: 1rem; height: 1rem; }
/* One button, two glyphs: whichever theme is active, the icon shows the one
   you would get by pressing it. */
.theme-toggle .moon { display: none; }
:root[data-theme="dark"] .theme-toggle .sun { display: none; }
:root[data-theme="dark"] .theme-toggle .moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .sun { display: none; }
  :root:not([data-theme="light"]) .theme-toggle .moon { display: block; }
}

/* ---------------------------------------------------------------- atoms -- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  font-family: var(--display);
  font-weight: 800;
  font-size: .95rem;
  text-transform: lowercase;
  letter-spacing: -0.02em;
  text-decoration: none;
  padding: .82rem 1.5rem;
  border-radius: 99px;
  border: 0;
  cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease, opacity .15s ease;
  white-space: nowrap;
}
.btn-primary { background: var(--aura); color: #fff; box-shadow: var(--shadow-1); }
.btn-primary:hover { transform: translateY(-1px); box-shadow: var(--shadow-2); }
.btn-quiet { background: transparent; color: var(--ink); border: 1px solid var(--line-2); }
.btn-quiet:hover { border-color: var(--ink); }

.rule-aura {
  height: 4px;
  width: 3.4rem;
  border-radius: 99px;
  background: var(--aura);
}

.card {
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 1.5rem;
  box-shadow: var(--shadow-1);
}

section { padding: clamp(3.5rem, 9vw, 7rem) 0; }
.tight { padding-top: 0; }

/* -------------------------------------------------------------- ambient -- */

/*
  The blooms behind everything. Fixed rather than scrolled, so the page moves
  over a still atmosphere instead of dragging it along — and heavily blurred,
  so they read as light rather than as shapes.
*/
.ambient { position: fixed; inset: 0; z-index: -2; overflow: hidden; pointer-events: none; }
.bloom { position: absolute; border-radius: 50%; filter: blur(72px); opacity: .5; will-change: auto; }
@media (max-width: 51.99rem) {
  .bloom { filter: blur(44px); }
  .bloom-3 { display: none; }
}
:root[data-theme="dark"] .bloom { opacity: .34; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .bloom { opacity: .34; }
}
.bloom-1 { width: 44vw; height: 44vw; background: var(--rose);   top: -12vw; left: -8vw; }
.bloom-2 { width: 38vw; height: 38vw; background: var(--violet); top: 40vw;  right: -12vw; }
.bloom-3 { width: 36vw; height: 36vw; background: var(--indigo); bottom: -10vw; left: 16vw; }

.site-foot {
  border-top: 1px solid var(--line);
  padding: 2.6rem 0 3.4rem;
  margin-top: 2rem;
}
.site-foot .wrap { display: flex; flex-wrap: wrap; gap: 1rem 2rem; align-items: center; }
.site-foot .links { display: flex; gap: 1.2rem; margin-left: auto; flex-wrap: wrap; }
.site-foot a { color: var(--muted); text-decoration: none; font-size: .9rem; }
.site-foot a:hover { color: var(--ink); }
.site-foot small { color: var(--faint); font-size: .85rem; }

/* -------------------------------------------------------------- reveal -- */

/*
  Only armed by JavaScript, never in the stylesheet alone.

  If the class were hidden by default, a visitor whose script failed to load
  would get a blank page rather than an unanimated one. The failure mode of a
  flourish must never be "no content".
*/
[data-reveal].armed {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .7s cubic-bezier(.22,.61,.36,1),
              transform .7s cubic-bezier(.22,.61,.36,1);
}
[data-reveal].armed.shown { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal].armed { opacity: 1; transform: none; }
}

/* ----------------------------------------------------------- long form -- */

/*
  Shared by privacy, the philosophy page and the circle page — anything that is
  mostly words. They used to each carry their own copy of this, which is how
  three pages end up with three different ideas of what a heading looks like.
*/
.prose { max-width: 44rem; margin: 0 auto; padding: 0 var(--gutter) 6rem; }
.prose header { padding-top: 3.6rem; }

.aurabar {
  height: 5px;
  width: 100%;
  border-radius: 99px;
  background: var(--aura);
  margin-bottom: 2.4rem;
}

.prose h1 { font-size: clamp(2.6rem, 8vw, 4.4rem); }
.prose h2 { margin: 3rem 0 .9rem; font-size: clamp(1.7rem, 4vw, 2.4rem); }
.prose h3 {
  margin: 1.9rem 0 .4rem;
  font-size: 1.05rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: -0.015em;
}

.lede { font-size: clamp(1.08rem, 2.2vw, 1.25rem); line-height: 1.5; color: var(--muted); margin-top: 1.3rem; }
.stamp { font-size: .86rem; color: var(--faint); margin-top: .5rem; }

.prose ul, .prose ol { margin: 0 0 1.1rem; padding-left: 1.2rem; }
.prose li { margin-bottom: .55rem; color: var(--ink-2); }
.prose li::marker { color: var(--faint); }

.scroller { overflow-x: auto; margin: 1.4rem 0; -webkit-overflow-scrolling: touch; }
.prose table { border-collapse: collapse; width: 100%; min-width: 32rem; font-size: .95rem; }
.prose th, .prose td {
  text-align: left;
  padding: .7rem .85rem;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.prose th {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--faint);
  font-weight: 500;
  border-bottom-color: var(--line-2);
}
.prose td { color: var(--ink-2); }
.prose td:first-child { color: var(--ink); font-weight: 500; }

.prose a:not(.btn):not(.logo) {
  color: var(--indigo);
  font-weight: 500;
  text-underline-offset: 3px;
  text-decoration-thickness: 1.5px;
}
.prose a:not(.btn):not(.logo):hover { color: var(--rose); }

.prose > footer {
  margin-top: 3.4rem;
  padding-top: 1.6rem;
  border-top: 1px solid var(--line);
  font-size: .88rem;
  color: var(--faint);
}

/* A line that has to be read slowly. Used sparingly — one per page at most,
   or it stops meaning anything. */
.pull {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(1.5rem, 4vw, 2.3rem);
  line-height: 1.14;
  letter-spacing: -0.03em;
  text-transform: lowercase;
  color: var(--ink);
  margin: 2.4rem 0;
  padding-left: 1.2rem;
  border-left: 4px solid transparent;
  border-image: var(--aura) 1;
}

/* ------------------------------------------------------- a day, playing -- */

/*
  The one on the landing page that moves. Four moments of a day fade through
  in sequence with their times, which shows what using SHOON is actually like
  far faster than a sentence can.

  Pure CSS with no JavaScript, so it runs before the page has finished loading
  and cannot break. It stops entirely for anybody who has asked for less
  motion — the global reduced-motion rule kills the animation and the first
  frame stays put, which still reads correctly.
*/
.play { position: relative; aspect-ratio: 4 / 3; }
.play .pic {
  position: absolute; inset: 0 0 auto 0;
  aspect-ratio: 4 / 3;
  opacity: 0;
  animation: cycle 12s infinite;
}
.play .pic:nth-child(1) { animation-delay: 0s; }
.play .pic:nth-child(2) { animation-delay: 3s; }
.play .pic:nth-child(3) { animation-delay: 6s; }
.play .pic:nth-child(4) { animation-delay: 9s; }
@keyframes cycle {
  0%   { opacity: 0; transform: scale(1.03) translateY(6px); }
  4%   { opacity: 1; transform: none; }
  25%  { opacity: 1; transform: none; }
  29%  { opacity: 0; transform: scale(.99) translateY(-6px); }
  100% { opacity: 0; }
}
.play-meta {
  position: absolute; left: 0; right: 0; bottom: 0;
  font-size: .6rem; color: var(--faint); text-align: center;
}

/* ------------------------------------------------------------ fragments -- */

/* The month grid, with the marks the app actually draws. */
.cal { display: grid; grid-template-columns: repeat(7,1fr); gap: .22rem; }
.cal i {
  aspect-ratio: 1; border-radius: 50%;
  display: grid; place-items: center;
  font-style: normal; font-size: .5rem; color: var(--faint);
  position: relative;
}
.cal i::after {
  content: ""; position: absolute; bottom: 1px;
  width: 3.5px; height: 3.5px; border-radius: 50%; opacity: 0;
}
.cal i.g::after { background:#10b981; opacity:1; }
.cal i.a::after { background:#d97706; opacity:1; }
.cal i.n::after { background: var(--faint); opacity:.5; }
.cal i.on { background: var(--aura); color:#fff; font-weight:600; }
.cal i.on::after { opacity: 0; }

/* Chat. */
.msg {
  border-radius: 11px; padding: .35rem .55rem; font-size: .62rem;
  line-height: 1.35; margin-bottom: .3rem; max-width: 82%; color: var(--ink-2);
}
.msg.them { background: var(--sink); border: 1px solid var(--line); }
.msg.me {
  margin-left: auto;
  background: linear-gradient(120deg, rgba(244,63,94,.16), rgba(99,102,241,.16));
  border: 1px solid rgba(139,124,255,.34);
}

/* The lock a private moment wears. */
.lock {
  position: absolute; top: .4rem; right: .4rem; z-index: 2;
  display: inline-flex; align-items: center; gap: 3px;
  background: rgba(0,0,0,.5); color: #fff;
  font-size: .55rem; font-weight: 600;
  padding: .18rem .4rem; border-radius: 99px;
}
.lock svg { width: 8px; height: 8px; }

/* A bar chart, for the insights screen. */
.bars { display: flex; align-items: flex-end; gap: 3px; height: 46px; margin-top: .4rem; }
.bars i { flex: 1; border-radius: 2px 2px 0 0; background: var(--aura); opacity: .8; }

/* ----------------------------------------------------------- feature row -- */

/*
  A phone beside a short line of text. Alternating sides on wide screens so the
  page has a rhythm; stacked with the phone first on narrow ones, because the
  picture is the part that carries the meaning.
*/
.row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.8rem;
  align-items: center;
  justify-items: center;
  margin-bottom: clamp(3rem, 7vw, 5rem);
}
.row .say { max-width: 26rem; }
.row .say h3 { font-size: clamp(1.5rem, 3.6vw, 2.15rem); margin-bottom: .7rem; }
.row .say p { font-size: 1.02rem; margin: 0; color: var(--muted); }
.row .say .caps { display: block; margin-bottom: .6rem; }

@media (min-width: 52rem) {
  .row { grid-template-columns: 1fr 1fr; gap: 3rem; justify-items: start; }
  .row .ph { justify-self: center; }
  .row.flip .ph { order: 2; }
  .row.flip .say { order: 1; }
}

/* ==========================================================================
   Corrections after seeing it on an actual phone.
   ========================================================================== */

/*
  1. A drawn phone shown on a phone is redundant.

  Most visitors arrive on the device the mock-up is imitating. Drawing a little
  phone inside their real one wastes the whole viewport, reads as a coloured
  rectangle rather than an app, and asks them to imagine a thing they are
  already holding.

  So below the fold-width, the frame comes off and the screen content is shown
  directly as a card. Their device is the frame. On a laptop, where there is no
  phone in the visitor's hand, the frame comes back and does its job.
*/
@media (max-width: 51.99rem) {
  .ph {
    width: 15rem;
  }
  /* A little smaller in the hand, so the words beside it still fit. */
}

/*
  2. Reduced motion left the hero empty.

  The global rule collapses every animation to almost nothing, which runs the
  cycle straight to its last keyframe — and the last keyframe of a crossfade is
  invisible. Somebody who asked their phone for less movement was getting a
  blank rectangle where the app should be.

  The first moment is now simply shown, still, and the rest are removed. A
  still photograph is a perfectly good answer to "what does this look like".
*/
@media (prefers-reduced-motion: reduce) {
  .play .pic { animation: none; opacity: 0; }
  .play .pic:first-child { opacity: 1; }
}

/*
  3. The day did not read as a screen.

  A single field of colour filling a frame looks like a placeholder, not an
  app. A strip of the day underneath — the moments either side of the one being
  shown — is what makes it legible as a photo journal at a glance.
*/
.strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: .3rem; margin-top: .5rem; }
.strip .pic { aspect-ratio: 1; border-radius: 7px; }
.strip .pic::after { display: none; }
.strip .pic.dim { opacity: .38; }

/* 4. The hero was two and a half screens tall on a phone. */
@media (max-width: 51.99rem) {
  h1 { font-size: clamp(2.5rem, 11vw, 3.4rem); }
  .row { gap: 1.4rem; margin-bottom: 2.6rem; }
  section { padding: clamp(2.4rem, 7vw, 4rem) 0; }
}

/* ------------------------------------------------------------- the test -- */

/*
  The question that does the work.

  Sympathy for a general problem changes nobody. Failing a small test about
  your own life does — the blank you hit when you try to answer is the entire
  argument, and you make it to yourself, which is the only way an argument of
  this kind ever lands.
*/
.test {
  border-radius: 18px;
  padding: clamp(1.8rem, 5vw, 3rem);
  background: var(--sink);
  border: 1px solid var(--line);
  text-align: center;
}
.test .q {
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(1.7rem, 5vw, 3rem);
  line-height: 1.02;
  letter-spacing: -0.035em;
  text-transform: lowercase;
  color: var(--ink);
  margin: 0;
}
.test .a {
  margin: 1.2rem auto 0;
  max-width: 26rem;
  color: var(--muted);
  font-size: 1.02rem;
}

/* ==========================================================================
   One phone, and you drive it.
   ==========================================================================

   Three static phones down the page asked the visitor to scroll past two
   things they had not chosen in order to reach the one they cared about. A
   single device they operate themselves inverts that: the hand moves, so the
   attention follows, and each screen is arrived at rather than delivered.
   ========================================================================== */

.demo {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.6rem;
  align-items: start;
  justify-items: center;
}
@media (min-width: 52rem) {
  .demo { grid-template-columns: 17rem 1fr; gap: 3.2rem; align-items: center; }
  .demo .ph { position: sticky; top: 6rem; }
}

.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin-bottom: 1.4rem;
  padding: 0;
  list-style: none;
}
.tabs button {
  font-family: var(--display);
  font-weight: 700;
  font-size: .84rem;
  text-transform: lowercase;
  letter-spacing: -0.015em;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 99px;
  padding: .48rem .95rem;
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
  white-space: nowrap;
}
.tabs button:hover { color: var(--ink); border-color: var(--line-2); }
.tabs button[aria-selected="true"] {
  color: #fff;
  background: var(--aura);
  border-color: transparent;
}

.demo .say { max-width: 30rem; }
.demo .say h3 {
  font-size: clamp(1.55rem, 4vw, 2.35rem);
  margin-bottom: .8rem;
}
.demo .say p { font-size: 1.05rem; color: var(--muted); margin: 0 0 .9rem; }
.demo .say p:last-child { margin-bottom: 0; }
.demo .say p.close { color: var(--ink-2); }

/* Only the chosen screen is in the frame. Hidden rather than faded, so a
   screen reader is never offered five overlapping descriptions of one phone. */
.screen[hidden] { display: none; }
.panel[hidden] { display: none; }

/* The swap is a short settle rather than a slide — the phone should feel like
   it changed page, not like the page moved. */
@media (prefers-reduced-motion: no-preference) {
  .screen, .panel { animation: settle .32s cubic-bezier(.22,.61,.36,1); }
  @keyframes settle {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
  }
}

/* ==========================================================================
   Zero turning into infinity.
   ==========================================================================

   The concept, shown rather than asserted. A nought rotates a quarter turn and
   becomes the lemniscate — which is the entire idea in one movement, and needs
   no sentence to explain it.

   Still for anybody who has asked for less motion: the mark simply sits there,
   which still reads correctly.
   ========================================================================== */

.turn {
  display: grid;
  place-items: center;
  width: 8.5rem;
  height: 8.5rem;
  margin: 0 auto 1.6rem;
  position: relative;
}
.turn > * { grid-area: 1 / 1; }

.turn .nought {
  font-family: var(--display);
  font-weight: 900;
  font-size: 6.5rem;
  line-height: 1;
  letter-spacing: -0.05em;
  color: var(--ink);
  animation: becoming 7s ease-in-out infinite;
}
.turn .mark {
  width: 8rem;
  height: 4rem;
  opacity: 0;
  animation: arriving 7s ease-in-out infinite;
}

@keyframes becoming {
  0%, 26%   { transform: rotate(0deg)  scale(1);   opacity: 1; }
  46%, 100% { transform: rotate(90deg) scale(1.1); opacity: 0; }
}
@keyframes arriving {
  0%, 30%   { transform: rotate(-90deg) scale(.92); opacity: 0; }
  50%, 92%  { transform: rotate(0deg)   scale(1);   opacity: 1; }
  100%      { transform: rotate(0deg)   scale(1);   opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .turn .nought { display: none; }
  .turn .mark { animation: none; opacity: 1; }
}

/* The concept section sits between the problem and the solution, so it reads
   as the bridge it is rather than as an afterword. */
.idea { text-align: center; max-width: 40rem; margin: 0 auto; }
.idea h2 { margin-bottom: 1.2rem; }
.idea p { font-size: clamp(1.05rem, 2.3vw, 1.25rem); line-height: 1.55; color: var(--muted); }
.idea p.turnline {
  color: var(--ink);
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(1.25rem, 3.2vw, 1.75rem);
  line-height: 1.2;
  letter-spacing: -0.028em;
  text-transform: lowercase;
  margin: 1.8rem 0;
}

/* ==========================================================================
   One Tuesday.
   ==========================================================================

   Six moments of an ordinary day, laid out as an editorial spread.

   Each frame takes a real photograph through a CSS custom property, and falls
   back to a composed gradient when there is not one yet. That fallback is
   deliberate rather than a placeholder: the page has to be presentable before
   the photographs exist, and it must never show a broken frame.

   Drop a file into /assets/day/ and set --img on the figure. Nothing else
   changes.
   ========================================================================== */

.tuesday {
  display: grid;
  grid-template-columns: 1fr;
  gap: .9rem;
}
@media (min-width: 40rem) {
  .tuesday { grid-template-columns: repeat(6, 1fr); gap: 1rem; }
  /* An editorial rhythm rather than a grid of equal tiles: the run and the
     dinner carry the day, so they are given the width. */
  .tuesday .m1 { grid-column: span 4; }
  .tuesday .m2 { grid-column: span 2; }
  .tuesday .m3 { grid-column: span 2; }
  .tuesday .m4 { grid-column: span 2; }
  .tuesday .m5 { grid-column: span 2; }
  .tuesday .m6 { grid-column: span 6; }
}

.moment {
  position: relative;
  margin: 0;
  border-radius: 14px;
  overflow: hidden;
  background-color: var(--sink);
  /* Two layers, always. The photograph sits on top of the composed light.

     The obvious version — var(--img, var(--fallback)) — is wrong, and wrong
     in the way that matters: once --img is set to a URL, the fallback is
     never reached, so a file that is missing or still uploading leaves an
     empty frame rather than the thing the fallback existed to prevent. As a
     second layer the light is always painted, and the photograph covers it
     when and only when it actually loads. */
  background-image: var(--img, none), var(--fallback);
  background-size: cover, cover;
  background-position: center, center;
  aspect-ratio: 4 / 5;
  isolation: isolate;
  box-shadow: var(--shadow-1);
}
.moment.wide { aspect-ratio: 16 / 9; }
.moment.tall { aspect-ratio: 3 / 4; }

/* Legibility for whatever is written over the picture, without dimming the
   whole image the way a flat scrim does. */
.moment::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,.72) 0%, rgba(0,0,0,.28) 34%, transparent 62%);
  z-index: 1;
}

.moment figcaption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 2;
  padding: 1rem 1.1rem;
  color: #fff;
}
.moment .when {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: .82;
  display: block;
  margin-bottom: .25rem;
}
.moment .what {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(1rem, 2.4vw, 1.35rem);
  letter-spacing: -0.025em;
  line-height: 1.14;
  text-transform: lowercase;
}

/* Composed fallbacks. Each one is a different light rather than a different
   hue, so six of them together read as one day passing rather than as a
   colour chart. */
.m1 { --fallback: radial-gradient(120% 90% at 20% 10%, #ffd9a8 0%, transparent 55%), linear-gradient(150deg, #7dd3fc, #6366f1 70%, #312e81); }
.m2 { --fallback: radial-gradient(110% 80% at 70% 15%, #fde68a 0%, transparent 58%), linear-gradient(160deg, #fb923c, #b45309 65%, #4c1d95); }
.m3 { --fallback: linear-gradient(150deg, #94a3b8, #475569 60%, #1e293b); }
.m4 { --fallback: radial-gradient(100% 80% at 30% 20%, #e0f2fe 0%, transparent 60%), linear-gradient(155deg, #cbd5e1, #64748b 70%, #334155); }
.m5 { --fallback: radial-gradient(120% 90% at 75% 80%, #f9a8d4 0%, transparent 55%), linear-gradient(150deg, #f43f5e, #7c2d12 70%, #1c1917); }
.m6 { --fallback: radial-gradient(120% 100% at 50% 100%, #fcd34d 0%, transparent 52%), linear-gradient(180deg, #a855f7, #4c1d95 70%, #171033); }

/* -------------------------------------------------------- the run card -- */

/*
  The workout, shown the way a watch shows it.

  This is the one place the site can be specific about what SHOON pulls in from
  a watch, and a number people recognise does more than a sentence describing
  the feature.
*/
.stats {
  position: absolute;
  top: .8rem; left: .8rem; right: .8rem;
  z-index: 2;
  display: flex;
  gap: .9rem;
  align-items: flex-start;
  padding: .7rem .85rem;
  border-radius: 11px;
  background: rgba(10, 8, 14, .48);
  backdrop-filter: blur(9px);
  -webkit-backdrop-filter: blur(9px);
  border: 1px solid rgba(255,255,255,.14);
  color: #fff;
}
.stats dl { margin: 0; display: flex; gap: .9rem; flex: 1 1 auto; flex-wrap: wrap; }
.stats div { min-width: 0; }
.stats dt {
  font-family: var(--mono);
  font-size: .55rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  opacity: .68;
}
.stats dd {
  margin: .1rem 0 0;
  font-family: var(--display);
  font-weight: 800;
  font-size: .95rem;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.stats .trace { width: 46px; height: 34px; flex: 0 0 auto; opacity: .9; }
.stats .trace path { stroke: #fff; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; }

@media (max-width: 26rem) {
  .stats { gap: .5rem; padding: .55rem .65rem; }
  .stats .trace { display: none; }
  .stats dd { font-size: .82rem; }
}

.shotlist {
  font-family: var(--mono);
  font-size: .72rem;
  color: var(--faint);
  text-align: center;
  margin-top: 1.2rem;
}

/* ==========================================================================
   One Tuesday, on one screen.
   ==========================================================================

   The six moments were a spread you scrolled through, which made the page long
   and asked nobody to do anything. One frame with the hours underneath is
   shorter, and stepping through a day yourself is closer to what the app is
   than reading six captions in a row.
   ========================================================================== */

.day-stage {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
  /* Square, because the photographs are. A 16:10 stage would have cropped
     the top and bottom off every one of them — including the temple, where
     the building is the point, and the Garmin card, where the numbers sit
     in the upper corner. */
  aspect-ratio: 1 / 1;
  background-color: var(--sink);
  box-shadow: var(--shadow-2);
  isolation: isolate;
}
@media (max-width: 40rem) { .day-stage { aspect-ratio: 1 / 1; } }

.day-stage .moment {
  background-position: center top;
  position: absolute;
  inset: 0;
  aspect-ratio: auto;
  border-radius: 0;
  box-shadow: none;
  margin: 0;
}
.day-stage .moment[hidden] { display: none; }

@media (prefers-reduced-motion: no-preference) {
  .day-stage .moment { animation: settle .4s cubic-bezier(.22,.61,.36,1); }
}

/* The hours, as the way through the day. */
.hours {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  justify-content: center;
  margin-top: 1.1rem;
}
.hours button {
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .06em;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 99px;
  padding: .42rem .8rem;
  cursor: pointer;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
  font-variant-numeric: tabular-nums;
}
.hours button:hover { color: var(--ink); border-color: var(--line-2); }
.hours button[aria-selected="true"] {
  color: #fff;
  background: var(--aura);
  border-color: transparent;
}

/* The line under the stage that names what you are looking at. */
.day-say {
  text-align: center;
  max-width: 32rem;
  margin: 1.4rem auto 0;
  min-height: 3.2rem;
}
.day-say h3 {
  font-size: clamp(1.2rem, 3vw, 1.7rem);
  margin-bottom: .4rem;
}
.day-say p { color: var(--muted); margin: 0; font-size: .98rem; }
.day-say [hidden] { display: none; }

/* ==========================================================================
   The phone, drawn as a phone.
   ==========================================================================

   The first version was a rounded rectangle with a small card floating inside
   it and a lot of empty space underneath. It did not read as a device and it
   did not read as SHOON — it read as a placeholder.

   What makes a drawing read as a running app is not the outline. It is the
   furniture: a status bar at the top, content that goes edge to edge, and a
   navigation bar carrying the app's own icons in the app's own order. Those
   three are what a person recognises before they have read anything.
   ========================================================================== */

.ph {
  /* Definite, not percentage. In an auto-sized grid track a
     percentage width resolves against a column that has already shrunk to
     fit, which is how this ended up a third of the size of the same
     component in the hero. */
  width: 17rem;
  max-width: 100%;
  aspect-ratio: 9 / 19.5;
  border-radius: 2.4rem;
  padding: .28rem;
  background: linear-gradient(160deg, #3a3546, #14111c 40%, #0a0810);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.07) inset,
    var(--shadow-2);
  flex: 0 0 auto;
  display: flex;
}

/* The glass. Everything the app draws lives in here, edge to edge. */
.ph-screen {
  flex: 1 1 auto;
  border-radius: 2.15rem;
  overflow: hidden;
  background: var(--ground);
  display: flex;
  flex-direction: column;
  position: relative;
  min-width: 0;
}

.ph-status {
  flex: 0 0 auto;
  height: 1.55rem;
  padding: 0 .85rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--body);
  font-size: .52rem;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: .01em;
}
.ph-status .sig { display: flex; align-items: center; gap: 3px; }
.ph-status svg { width: 8px; height: 8px; display: block; }

.ph-app {
  flex: 0 0 auto;
  height: 1.9rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid var(--line);
}
.ph-app .logo { font-size: .68rem; }

/* The screen itself. No padding — an app draws to its own edges, and the
   inset card was most of why this looked like a website in a frame. */
.ph-view { flex: 1 1 auto; position: relative; overflow: hidden; }

/* The app's own navigation, in the app's own order:
   community · calendar · capture · day · insights · profile */
.ph-nav {
  flex: 0 0 auto;
  height: 2.7rem;
  border-top: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding: 0 .3rem;
}
.ph-nav svg { width: 13px; height: 13px; color: var(--faint); display: block; }
.ph-nav .shoot {
  width: 27px; height: 27px;
  border-radius: 50%;
  background: var(--aura);
  display: grid;
  place-items: center;
  box-shadow: 0 0 14px -3px var(--violet);
}
.ph-nav .shoot svg { width: 13px; height: 13px; color: #fff; }
.ph-nav .on svg { color: var(--ink); }
/* The lighter phone used on What It Does — one feature per screen, no
   status bar or real nav needed. .ph-top/.ph-body/.ph-bar were never
   given rules at all: .ph lays its children out in a row by default,
   so the three sections were sitting side by side fighting for width
   instead of stacked as a screen, which is why the photograph inside
   read as a fraction of the phone rather than filling it. */
.ph:has(> .ph-top) { flex-direction: column; }
.ph-top {
  flex: 0 0 auto;
  height: 1.9rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ph-top .logo { font-size: .68rem; }
.ph-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 .65rem;
}
.ph-bar {
  flex: 0 0 auto;
  height: 1.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .3rem;
}
.ph-bar i {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--faint);
  opacity: .5;
  display: block;
}
.ph-bar i.cap {
  width: 15px;
  border-radius: 2px;
  background: var(--ink);
  opacity: 1;
}

/* ---------------------------------------------------------- app screens -- */

/* A photograph filling the view, the way the day feed shows one. */
.sc-photo { position: absolute; inset: 0; }
.sc-photo .frame {
  position: absolute; inset: 0;
  background-size: cover;
  background-position: center;
}
.sc-photo .grad {
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,.66) 0%, transparent 46%);
  -webkit-background-clip: initial;
  background-clip: initial;
  color: initial;
}
.sc-photo .meta {
  position: absolute; left: .6rem; right: .6rem; bottom: .55rem;
  color: #fff;
}
.sc-photo .meta .t {
  font-size: .58rem; font-weight: 700; letter-spacing: -.01em; display: block;
}
.sc-photo .meta .s { font-size: .5rem; opacity: .8; }

/* The three-dot menu the app puts on a moment. */
.sc-photo .dots {
  position: absolute; top: .5rem; right: .5rem;
  display: flex; flex-direction: column; gap: 2px;
}
.sc-photo .dots i { width: 3px; height: 3px; border-radius: 50%; background: rgba(255,255,255,.9); }

.sc-pad { position: absolute; inset: 0; padding: .6rem; overflow: hidden; }
.sc-pad .head {
  font-family: var(--display); font-weight: 800; font-size: .68rem;
  color: var(--ink); text-transform: lowercase; margin: 0 0 .45rem;
}

/* ------------------------------------------------------- a photograph -- */

/*
  A stand-in for an image, until the real ones exist. Abstract on purpose —
  inventing a stranger's lunch would be worse than saying nothing.

  These were lost when the abstract phone was replaced; the screens referenced
  them and rendered as unstyled text over nothing, which is the sort of break
  that only shows up on the page rather than in the source.
*/
.pic {
  border-radius: 10px;
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background-size: cover;
  background-position: center;
}
.pic::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,.6), transparent 55%);
}
.pic b {
  position: absolute;
  left: .5rem;
  bottom: .38rem;
  z-index: 1;
  color: #fff;
  font-size: .58rem;
  font-weight: 600;
  letter-spacing: -.01em;
  line-height: 1.2;
  max-width: calc(100% - 1rem);
}

.p1 { background-image: linear-gradient(135deg,#fbcfe8,#a78bfa 55%,#60a5fa); }
.p2 { background-image: linear-gradient(135deg,#fed7aa,#fb7185 60%,#c084fc); }
.p3 { background-image: linear-gradient(135deg,#bbf7d0,#38bdf8 55%,#818cf8); }
.p4 { background-image: linear-gradient(135deg,#ddd6fe,#f0abfc 50%,#fecaca); }
.p5 { background-image: linear-gradient(135deg,#a5b4fc,#f9a8d4 60%,#fda4af); }

/* Inside the phone the screens go edge to edge, so a photograph filling the
   view has no corners of its own to round. */
.ph-view .play .pic { border-radius: 0; }
