/*
 * Atmosphere: texture, gradients, movement.
 *
 * A layer over `styles.css` rather than mixed into it, because the two answer
 * different questions. That file decides what the page *is* — scale, colour,
 * the shape of a panel — and this one decides how it arrives. Kept apart, the
 * whole of the decoration can be read, argued with, or deleted in one place.
 *
 * Three rules hold everywhere below. Nothing moves fast. Nothing moves more
 * than once unless it is ambient. And everything stops for a reader who has
 * asked it to, at the bottom of the file.
 */

/* A grain over the whole page. Inline SVG turbulence rather than an image file,
   so there is nothing to fetch: a flat expanse reads as a dead panel and a
   little noise makes it a surface.

   Much lighter than it was. The grain is dark, and dark grain on a near-black
   page is barely there at 15% — on off-white the same value is visible dirt.
   4% is the point where it is texture rather than a smudge. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

body > * {
  position: relative;
  z-index: 1;
}

/* Two slow lights behind the hero, drifting on different periods so the pair
   never repeats a shape. Blurred past recognition — what should register is
   that the black is not flat, not that there are shapes in it. */
.hero {
  position: relative;
}

/*
 * Taken out of the layout entirely, not just emptied.
 *
 * `inset: -22% -8% auto` put this box 8% of the hero's width past each edge.
 * `overflow: hidden` on it clips its *children*, never itself — so once the
 * lights inside were hidden what remained was an invisible element hanging off
 * both sides of the page, and the horizontal scrollbar it caused had nothing
 * visible in it to explain itself.
 *
 * The div stays in the markup so a dark variant has somewhere to put the lights
 * back; `display: none` is what stops it being a box at all.
 */
.aurora {
  display: none;
}

/*
 * Two drifting blurs used to sit inside it — an orange one and a violet one.
 *
 * They worked on a near-black page, where a blurred colour behind the type
 * reads as light coming from somewhere. On a warm off-white the same thing
 * reads as a stain on the paper: there is nothing for it to be light *against*,
 * so it is just tinted background.
 */

@keyframes drift-a {
  from {
    transform: translate3d(0, 0, 0) scale(1);
  }
  to {
    transform: translate3d(90px, 60px, 0) scale(1.16);
  }
}

@keyframes drift-b {
  from {
    transform: translate3d(0, 20px, 0) scale(1.1);
  }
  to {
    transform: translate3d(-110px, -40px, 0) scale(0.94);
  }
}

/*
 * The one word that carries the claim, in one colour.
 *
 * It was gradient type — orange fading to a pale peach. On a dark page the
 * pale end is the bright end and it reads as emphasis; on off-white the pale
 * end is nearly the background, so the word faded out exactly where it should
 * have landed. Solid brand orange instead, which is also what "agree" deserves:
 * it is the point of the sentence, not a decoration in it.
 */
.accent-grad {
  color: var(--accent-display);
}

/* ---------------------------------------------------------------------------
   The header, given a surface of its own
   --------------------------------------------------------------------------- */

/*
 * The header, given a surface of its own by being flat white against a canvas
 * that is not — which on a light page is enough, and needs no gradient and no
 * shadow until it is scrolled past.
 */
.nav {
  background: var(--bg-panel);
  border-bottom: 1px solid var(--line);
}

.nav.is-scrolled {
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 1px 12px rgba(23, 23, 26, 0.06);
}

/* The only orange button on the page, which is what makes it the one thing a
   first-time reader is asked to find.
 *
 * Black text, not white. White on #ff6c37 is 2.6:1 and fails at this size;
 * black is 8.1:1 and is what the orange was always going to want. It reads as
 * a marker rather than as a link that happens to have a box round it.
 */
/*
 * Black, with white words.
 *
 * This button was orange through three revisions and each one traded something
 * away: the brand orange cannot carry white text (2.82:1), and an orange dark
 * enough to carry it is no longer the brand orange. The way out is to stop
 * asking a button to be the brand.
 *
 * Near-black at 17.89:1 needs no compromise, and it frees the orange to mean
 * one thing everywhere else on the page — the mark, the word a heading turns
 * on, a verdict, the rule under a card. A colour that is also every button is
 * a colour that has stopped signifying anything.
 *
 * It matches `.btn-primary` deliberately. Two names for one appearance, kept
 * apart because they sit in different places — this one in the header, that
 * one in the body — and a header button that grew its own colour later should
 * not have to be untangled from the hero's.
 */
.btn-accent,
.btn-accent:hover {
  color: #ffffff;
}

.btn-accent {
  background: #17171a;
  font-weight: 600;
}

.btn-accent:hover {
  background: #2e2e34;
}

/* ---------------------------------------------------------------------------
   The band of marks
   --------------------------------------------------------------------------- */

.band {
  padding: 36px 0 38px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: var(--bg-raised);
  overflow: hidden;
}

.band-label {
  margin-bottom: 24px;
  text-align: center;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* Two identical tracks side by side, translated by exactly half the pair, so
   the loop has no seam. Masked at the edges rather than cut, so marks arrive
   and leave instead of appearing. */
.marquee-mask {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}

.marquee {
  display: flex;
  gap: 58px;
  width: max-content;
  animation: marquee 48s linear infinite;
}

.marquee:hover {
  animation-play-state: paused;
}

@keyframes marquee {
  to {
    transform: translateX(-50%);
  }
}

.mark {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  flex: none;
  color: var(--text-dim);
  font-size: 0.95rem;
  white-space: nowrap;
  transition: color 220ms ease;
}

/* Grey until looked at: a row of full-colour vendor logos is somebody else's
   brand parade, and this band is about coverage rather than about them. */
.mark svg {
  flex: none;
  filter: grayscale(1);
  opacity: 0.6;
  transition:
    filter 260ms ease,
    opacity 260ms ease;
}

.mark:hover {
  color: var(--text);
}

.mark:hover svg {
  filter: none;
  opacity: 1;
}

/* ---------------------------------------------------------------------------
   Arriving on scroll
   --------------------------------------------------------------------------- */

/* Applied by script only where the browser can observe intersections, so a page
   with no JavaScript shows everything rather than nothing. */
.js .reveal {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity 720ms cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 720ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.js .reveal.is-in {
  opacity: 1;
  transform: none;
}

/* A group whose children arrive one after another. The delay is small: this
   should read as the page settling, not as a sequence being performed. */
.js .stagger > * {
  opacity: 0;
  transform: translateY(20px);
  transition:
    opacity 640ms cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 640ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.js .stagger.is-in > * {
  opacity: 1;
  transform: none;
}

.js .stagger.is-in > *:nth-child(2) {
  transition-delay: 90ms;
}
.js .stagger.is-in > *:nth-child(3) {
  transition-delay: 180ms;
}
.js .stagger.is-in > *:nth-child(4) {
  transition-delay: 270ms;
}
.js .stagger.is-in > *:nth-child(5) {
  transition-delay: 360ms;
}

/* The exhibit lifts as it arrives — the one flourish on the page. */
.js .exhibit.reveal {
  transform: translateY(34px) scale(0.986);
}

.js .exhibit.reveal.is-in {
  transform: none;
}

/* Each row draws itself a beat after the table lands, so the eye reads down it
   in the order the engine produced it. */
.js .exhibit.is-in tbody tr {
  animation: row-in 520ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
}

.js .exhibit.is-in tbody tr:nth-child(2) {
  animation-delay: 100ms;
}
.js .exhibit.is-in tbody tr:nth-child(3) {
  animation-delay: 200ms;
}
.js .exhibit.is-in tbody tr:nth-child(4) {
  animation-delay: 300ms;
}
.js .exhibit.is-in tbody tr:nth-child(5) {
  animation-delay: 400ms;
}

@keyframes row-in {
  from {
    opacity: 0;
    transform: translateX(-12px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* The two verdicts that carry the argument keep a slow pulse, so an eye landing
   anywhere on the table is drawn to the pair that disagree. */
.js .exhibit.is-in .verdict.diff,
.js .exhibit.is-in .verdict.unreachable {
  animation: attention 3.6s ease-in-out 1.6s infinite;
}

@keyframes attention {
  0%,
  100% {
    box-shadow: 0 0 0 0 rgba(242, 104, 95, 0);
  }
  50% {
    box-shadow: 0 0 0 4px rgba(242, 104, 95, 0.14);
  }
}

/* ---------------------------------------------------------------------------
   Lit edges
   --------------------------------------------------------------------------- */

.card,
.panel,
.stack-item {
  position: relative;
}

/*
 * A hairline along the top edge, which on a dark page was a highlight — the
 * cheapest way to make a flat panel look lit rather than drawn.
 *
 * On white there is no light for it to catch. What is left is the useful half:
 * a solid accent rule that appears on hover, so a card still answers when the
 * pointer is on it without anything fading anywhere.
 */
.card::before,
.panel::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: transparent;
  transition: background 200ms ease;
}

.card:hover::before {
  background: var(--accent-brand);
}

.stack-item.is-open {
  background: var(--accent-wash);
}

/* ---------------------------------------------------------------------------
   The closing band, the one large area of colour on the page
   --------------------------------------------------------------------------- */

.cta-panel {
  position: relative;
  padding: clamp(52px, 7vw, 92px) 32px;
  border: 1px solid rgba(199, 73, 28, 0.22);
  border-radius: 28px;
  overflow: hidden;
  /*
   * One flat tint, where there were two radial washes and a drifting blur on
   * top. This is the last thing on the page and the only large area of colour;
   * on white it needs to be quiet enough that the words on it are the loudest
   * part, which three overlapping gradients were not.
   */
  background: var(--accent-wash);
}

.cta-panel > * {
  position: relative;
  z-index: 1;
}

/* ---------------------------------------------------------------------------
   Asked to stop
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .aurora span,
  .marquee,
  .cta-panel::after,
  .js .exhibit.is-in tbody tr,
  .js .exhibit.is-in .verdict.diff,
  .js .exhibit.is-in .verdict.unreachable {
    animation: none;
  }

  .js .reveal,
  .js .stagger > * {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
