/*
 * chordloop.org.
 *
 * The page around the app, written in the app's own language: the ink ramp,
 * the ember accent, Archivo carried locally, and the four note hues used only
 * where they mean what they mean on a diagram. No build step and no framework
 * — a landing page that needs a toolchain is a landing page nobody edits.
 */

/* Carried locally for the same reason the app does it: chordloop.org is one
   origin, and a stylesheet fetched from another is one more thing to fail. */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('fonts/archivo-latin-wght-normal.woff2') format('woff2-variations');
}
@font-face {
  font-family: 'Archivo';
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url('fonts/archivo-latin-wght-italic.woff2') format('woff2-variations');
}

:root {
  color-scheme: dark;

  --ink-100: #f2efe9;
  --ink-200: #ded8ce;
  --ink-300: #c4bdb2;
  --ink-400: #948d82;
  --ink-500: #868079;
  --ink-600: #4f4942;
  --ink-700: #332e29;
  --ink-800: #221f1c;
  --ink-900: #121110;
  --ink-950: #0b0a09;

  --ember-200: #f5cba6;
  --ember-300: #eaa971;
  --ember-400: #e08c47;
  --ember-500: #d2762f;
  --ember-600: #b25f22;
  --onaccent: #140f0a;

  /* The four note roles. Information, not decoration — they appear once, as
     the key to the diagrams, and nothing else on the page may use them. */
  --note-root: #f97316;
  --note-chord: #38bdf8;
  --note-colour: #c084fc;
  --note-scale: #53c186;

  --measure: 68ch;
  --radius: 4px;
}

/* The same ramp read the other way up, exactly as the app does it. Roles are
   kept rather than numbers: 950 is still the ground, 100 still the strongest
   text, so every rule below comes out right without being touched. */
@media (prefers-color-scheme: light) {
  :root {
    color-scheme: light;

    --ink-100: #17140f;
    --ink-200: #2a261f;
    --ink-300: #3e3931;
    --ink-400: #5f594f;
    --ink-500: #6e675e;
    --ink-600: #bdb6aa;
    --ink-700: #d8d3ca;
    --ink-800: #eceae4;
    --ink-900: #fffdfa;
    --ink-950: #f7f4ef;

    --ember-200: #8a4415;
    --ember-300: #a8541a;
    --ember-400: #ab551c;
    --ember-500: #c06d28;
    --ember-600: #8a4415;
  }
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ink-950);
  color: var(--ink-300);
  font-family: 'Archivo', system-ui, 'Segoe UI', Roboto, sans-serif;
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { color: var(--ink-100); line-height: 1.12; letter-spacing: -0.022em; margin: 0; }
h1 { font-size: clamp(2.4rem, 6.2vw, 4rem); font-weight: 800; }
h2 { font-size: clamp(1.65rem, 3.4vw, 2.3rem); font-weight: 700; }
h3 { font-size: 1.12rem; font-weight: 650; letter-spacing: -0.012em; }
p { margin: 0; }
/* height:auto is load-bearing, not tidiness. Every screenshot carries explicit
   width/height attributes so the layout does not jump while they load — and
   without this the attribute height wins once max-width scales the width down,
   so a 1500x1050 capture rendered at 1058px wide kept all 1050px of height and
   came out squashed. Every image on the page was distorted. */
img { max-width: 100%; height: auto; display: block; }

a { color: var(--ember-400); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--ember-300); }

:focus-visible { outline: 2px solid var(--ember-400); outline-offset: 3px; border-radius: 2px; }

.wrap { width: min(1140px, 100% - 3rem); margin-inline: auto; }
@media (max-width: 560px) { .wrap { width: min(1140px, 100% - 2rem); } }

.eyebrow {
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-500); margin-bottom: 0.9rem;
}

/* ---------------------------------------------------------------- header */

.top {
  position: sticky; top: 0; z-index: 10;
  background: color-mix(in srgb, var(--ink-950) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--ink-800);
}
.top > .wrap { display: flex; align-items: center; gap: 1.4rem; height: 62px; }
.top nav { margin-left: auto; display: flex; align-items: center; gap: 1.4rem; }
.top nav a { color: var(--ink-300); text-decoration: none; font-size: 0.94rem; }
.top nav a:hover { color: var(--ink-100); }
@media (max-width: 720px) { .top nav a.hide-sm { display: none; } }

.brand { display: flex; align-items: center; gap: 0.6rem; text-decoration: none; }
.brand svg { width: 30px; height: 30px; flex: none; }
.brand span { font-size: 1.32rem; font-weight: 800; letter-spacing: -0.03em; color: var(--ink-100); }
/* Loop is the part that moves. */
.brand em { font-style: italic; font-weight: 600; color: var(--ember-400); }

/* ----------------------------------------------------------------- buttons

   The accent is spent once per region: one filled ember button on the page,
   and everything else outlined in ink. Three filled accents in a screenful
   and the eye stops believing any of them. */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  min-height: 46px; padding: 0 1.15rem;
  border-radius: var(--radius); border: 1px solid transparent;
  font: inherit; font-weight: 650; font-size: 1rem;
  text-decoration: none; cursor: pointer; white-space: nowrap;
}
.btn-accent { background: var(--ember-500); color: var(--onaccent); }
.btn-accent:hover { background: var(--ember-400); color: var(--onaccent); }
.btn-quiet { border-color: var(--ink-600); color: var(--ink-200); background: none; }
.btn-quiet:hover { border-color: var(--ink-400); color: var(--ink-100); }
.btn-sm { min-height: 38px; font-size: 0.92rem; padding: 0 0.85rem; }

/* -------------------------------------------------------------------- hero */

.hero { padding: clamp(3rem, 8vw, 6rem) 0 clamp(2.5rem, 5vw, 4rem); }
.hero p.lede {
  font-size: clamp(1.06rem, 2.1vw, 1.3rem);
  max-width: var(--measure); margin-top: 1.4rem; color: var(--ink-300);
}
.hero h1 .accent { color: var(--ember-400); }
.cta { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 2rem; align-items: center; }
.cta .note { font-size: 0.9rem; color: var(--ink-500); }

.facts {
  display: flex; flex-wrap: wrap; gap: 0.45rem 1.4rem;
  margin-top: 1.6rem; font-size: 0.9rem; color: var(--ink-400);
}
.facts span { display: inline-flex; align-items: center; gap: 0.45rem; }
.facts span::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: var(--ink-500); flex: none;
}

.shot {
  margin-top: clamp(2.2rem, 5vw, 3.4rem);
  border: 1px solid var(--ink-700); border-radius: 6px;
  background: var(--ink-900); overflow: hidden;
}
.shot img { width: 100%; }
/* The phone capture is 440px wide; letting it fill a wide card would blow it
   up to four times its own resolution. */
@media (max-width: 700px) { .shot img { max-width: 300px; margin-inline: auto; } .shot { padding-top: 1rem; } }
.shot figcaption {
  border-top: 1px solid var(--ink-800);
  padding: 0.7rem 1rem; font-size: 0.85rem; color: var(--ink-500);
}

/* The key to the four hues, generated nowhere but stated once, in the same
   order and the same words the app's own legend uses. */
.hues { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; margin-top: 1.5rem; font-size: 0.86rem; color: var(--ink-400); }
.hues span { display: inline-flex; align-items: center; gap: 0.45rem; }
.hues i { width: 11px; height: 11px; border-radius: 50%; flex: none; }

/* ---------------------------------------------------------------- sections */

section { padding: clamp(2.4rem, 5vw, 3.6rem) 0; }
section > .wrap > .eyebrow + h2 { margin-bottom: 1rem; }
section p.intro { max-width: var(--measure); color: var(--ink-300); font-size: 1.06rem; }

.split { display: grid; gap: clamp(1.8rem, 4vw, 3.4rem); align-items: center; }
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; }
  .split.flip > figure { order: -1; }
}
.split figure { margin: 0; border: 1px solid var(--ink-700); border-radius: 6px; overflow: hidden; background: var(--ink-900); }
/* A phone shot at its own size would be taller than the prose beside it. */
.split figure.phone { max-width: 290px; margin-inline: auto; border-radius: 10px; }

.split h3 { font-size: clamp(1.3rem, 2.6vw, 1.7rem); font-weight: 700; margin-bottom: 0.9rem; }
.split ul { margin: 1.2rem 0 0; padding: 0; list-style: none; display: grid; gap: 0.7rem; }
.split li { padding-left: 1.35rem; position: relative; font-size: 0.98rem; }
.split li::before {
  content: ''; position: absolute; left: 0; top: 0.66em;
  width: 7px; height: 1px; background: var(--ember-500);
}
.split li b { color: var(--ink-100); font-weight: 650; }

.stack { display: grid; gap: clamp(2.2rem, 5vw, 3.4rem); }

/* ----------------------------------------------------------------- grid */

.cards { display: grid; gap: 1px; margin-top: 2.4rem; background: var(--ink-800); border: 1px solid var(--ink-800); border-radius: 6px; overflow: hidden; }
@media (min-width: 700px) { .cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .cards.three { grid-template-columns: repeat(3, 1fr); } }
.card { background: var(--ink-950); padding: 1.6rem 1.5rem; }
.card h3 { margin-bottom: 0.55rem; }
.card p { font-size: 0.96rem; color: var(--ink-400); }

/* ------------------------------------------------------------------- free */

.free { border: 1px solid var(--ink-700); border-radius: 6px; padding: clamp(1.8rem, 4vw, 2.8rem); background: var(--ink-900); }
.free h2 { margin-bottom: 1rem; }
.free p { max-width: var(--measure); }
.free p + p { margin-top: 1rem; }

/* Two tiers side by side. The paid one is outlined rather than filled: the
   accent is spent once per screenful, and on this page it belongs to the
   button rather than to a panel behind it. */
.tiers { display: grid; gap: 1rem; margin: 2rem 0 0; }
@media (min-width: 760px) { .tiers { grid-template-columns: 1fr 1fr; } }

.tier { border: 1px solid var(--ink-700); border-radius: 6px; padding: 1.5rem 1.4rem 1.6rem; background: var(--ink-950); }
.tier.paid { border-color: var(--ember-600); }
.tier h3 { font-size: 1.05rem; font-weight: 700; margin-bottom: 0.15rem; }
.tier .price { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.55rem; margin: 0.5rem 0 0.2rem; }
.tier .price b { font-size: 2rem; font-weight: 800; letter-spacing: -0.03em; color: var(--ink-100); font-variant-numeric: tabular-nums; }
.tier.paid .price b { color: var(--ember-400); }
.tier .price s { color: var(--ink-500); text-decoration-thickness: 1px; font-size: 1.05rem; }
.tier .price em { font-style: normal; color: var(--ink-400); font-size: 0.92rem; }
.tier .terms { font-size: 0.9rem; color: var(--ink-500); margin-top: 0.35rem; }
.tier ul { margin: 1.1rem 0 0; padding: 0; list-style: none; display: grid; gap: 0.55rem; }
.tier li { padding-left: 1.35rem; position: relative; font-size: 0.95rem; color: var(--ink-300); }
.tier li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink-600);
}
.tier.paid li::before { background: var(--ember-500); }
.tier li b { color: var(--ink-100); font-weight: 650; }

/* The launch offer is a date, not a countdown: a page with no build step
   cannot tick, and a fake urgency timer is the one thing that would make the
   price look untrustworthy. */
.launch {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.1rem;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--ember-600);
  border-radius: 999px;
  font-size: 0.86rem;
  color: var(--ember-300);
}

/* -------------------------------------------------------------------- faq */

.faq { margin-top: 2.2rem; border-top: 1px solid var(--ink-800); }
details { border-bottom: 1px solid var(--ink-800); }
summary {
  cursor: pointer; list-style: none;
  padding: 1.15rem 2rem 1.15rem 0; position: relative;
  color: var(--ink-100); font-weight: 650; font-size: 1.02rem;
}
summary::-webkit-details-marker { display: none; }
summary::after {
  content: '+'; position: absolute; right: 0.3rem; top: 50%; translate: 0 -50%;
  color: var(--ink-500); font-weight: 400; font-size: 1.3rem; line-height: 1;
}
details[open] summary::after { content: '−'; }
details > p { padding: 0 0 1.3rem; max-width: var(--measure); font-size: 0.99rem; }
details > p + p { padding-top: 0; margin-top: -0.5rem; padding-bottom: 1.3rem; }

/* ----------------------------------------------------------------- footer */

footer { border-top: 1px solid var(--ink-800); padding: 3rem 0 4rem; font-size: 0.92rem; color: var(--ink-500); }
footer .rows { display: flex; flex-wrap: wrap; gap: 1rem 2rem; align-items: baseline; }
footer nav { display: flex; flex-wrap: wrap; gap: 1rem 1.5rem; margin-left: auto; }
footer a { color: var(--ink-400); text-decoration: none; }
footer a:hover { color: var(--ink-100); }
footer .fine { margin-top: 2rem; max-width: var(--measure); font-size: 0.85rem; color: var(--ink-500); }

/* --------------------------------------------------------------- prose page */

.prose { padding: clamp(2.5rem, 6vw, 4rem) 0 5rem; }
.prose h1 { font-size: clamp(2rem, 5vw, 2.8rem); margin-bottom: 0.6rem; }
.prose .updated { color: var(--ink-500); font-size: 0.9rem; }
.prose h2 { font-size: 1.3rem; margin-top: 2.6rem; margin-bottom: 0.7rem; }
.prose p, .prose li { max-width: var(--measure); }
.prose p + p { margin-top: 0.9rem; }
.prose ul { padding-left: 1.2rem; display: grid; gap: 0.5rem; margin-top: 0.9rem; }
.prose strong { color: var(--ink-100); font-weight: 650; }


/* ------------------------------------------------------------- transitions

   Sections used to butt straight up against one another, which on a long page
   reads as one undifferentiated scroll. Two things fix it and neither needs a
   framework.

   The reveal class is added by script, never written into the markup: if the
   script does not run — an old browser, a blocked file, a crawler — nothing is
   ever hidden, because the rule that hides it was never applied. Content
   behind JavaScript is content nobody can be sure they will see.

   Reduced motion gets the page with no movement at all rather than a faster
   version of the same slide: the setting is a request to stop things moving,
   not to hurry them. */

@media (prefers-reduced-motion: no-preference) {
  /* Three things move, not one: the block rises, its own edge wipes in from
     the left, and anything inside it that carries a picture settles from a
     slight scale. A single fade-and-rise on everything is the effect every
     template ships with. */
  .reveal {
    opacity: 0;
    transform: translateY(22px);
    transition:
      opacity 0.7s cubic-bezier(0.16, 0.8, 0.24, 1),
      transform 0.7s cubic-bezier(0.16, 0.8, 0.24, 1);
    will-change: opacity, transform;
  }
  .reveal.is-in { opacity: 1; transform: none; }

  /* The image inside a revealed block comes up from slightly further and a
     touch larger, so the picture arrives after the words rather than with
     them — which is the order you read them in anyway. */
  .reveal figure { transition: inherit; transition-delay: 0.1s; }
  .reveal:not(.is-in) figure { opacity: 0; transform: translateY(14px) scale(1.015); }
  .reveal.is-in figure { opacity: 1; transform: none; }

  /* An eyebrow gets a rule that draws itself in under the heading it labels. */
  .reveal .eyebrow { position: relative; }
  .reveal .eyebrow::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: -6px;
    height: 2px;
    width: 0;
    background: linear-gradient(90deg, var(--ember-500), transparent);
    transition: width 0.85s cubic-bezier(0.16, 0.8, 0.24, 1) 0.16s;
  }
  .reveal.is-in .eyebrow::after { width: 56px; }

  /* Lists arrive item by item rather than as a slab. */
  .reveal li {
    opacity: 0;
    transform: translateY(9px);
    transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.16, 0.8, 0.24, 1);
  }
  .reveal.is-in li { opacity: 1; transform: none; }
  .reveal.is-in li:nth-child(1) { transition-delay: 0.16s; }
  .reveal.is-in li:nth-child(2) { transition-delay: 0.24s; }
  .reveal.is-in li:nth-child(3) { transition-delay: 0.32s; }
  .reveal.is-in li:nth-child(4) { transition-delay: 0.40s; }

  .reveal[data-delay='1'] { transition-delay: 0.06s; }
  .reveal[data-delay='2'] { transition-delay: 0.12s; }
  .reveal[data-delay='3'] { transition-delay: 0.18s; }

  /* The seam draws across as its section arrives, instead of already being
     there waiting. */
  main > section + section::before {
    transform: scaleX(0.2);
    opacity: 0;
    transition: transform 1s cubic-bezier(0.16, 0.8, 0.24, 1), opacity 0.6s ease;
  }
  main > section.seam-in::before { transform: scaleX(1); opacity: 1; }
}

/* A hairline that fades out at both ends, so the join reads as a seam rather
   than as a full-width rule drawn across the page. */
main > section + section { position: relative; }
main > section + section::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 1px;
  transform-origin: center;
  background: linear-gradient(90deg, transparent, var(--ink-700) 18%, var(--ink-700) 82%, transparent);
}

main > section:first-of-type { padding-top: clamp(1.6rem, 4vw, 2.4rem); }


/* --------------------------------------------------- screenshots, contained

   A landing page is scrolled, not studied. Full-height captures stacked one
   per section made the phone layout nineteen screens long, and nobody reaches
   the price at the bottom of that — so the tall ones are capped and anchored
   to the top, which is where the part being described always is. The cap is
   generous enough that the shot still reads as a screenshot rather than as a
   strip of one.

   Desktop keeps more height because the splits sit side by side there and the
   image is already sharing the row with its own explanation. */
.split figure img { max-height: 520px; object-fit: cover; object-position: top center; }

@media (max-width: 860px) {
  .split figure img { max-height: 300px; }
  /* The phone capture is portrait by nature; cropping it to a letterbox would
     lose the thing it exists to show. */
  .split figure.phone img { max-height: 420px; object-fit: contain; }
}

/* The hero shot is the one image worth its full height: it is the whole app,
   above the fold, and it is what a visitor is deciding on. */
.shot img { max-height: none; }


/* ------------------------------------------------------------------- film */
.film { margin: 2rem 0 0; border: 1px solid var(--ink-700); border-radius: 8px; overflow: hidden; background: var(--ink-900); }
.film video { display: block; width: 100%; height: auto; background: #000; }
.film figcaption { border-top: 1px solid var(--ink-800); padding: 0.7rem 1rem; font-size: 0.85rem; color: var(--ink-500); }
