/* Slidivo — landing site.
 *
 * The palette is the app icon's, not a separate decision: the icon is a cool white tile carrying a
 * coral -> rose -> fuchsia -> indigo gradient, bordered in #1E1B4B. Those colours are the brand, so
 * the site wears them. Every one was measured against the ground it actually sits on:
 *
 *   #1E1B4B  the icon's border, and this site's ink        15.99:1 on white
 *   #5B63F0  the icon's indigo end                          4.68:1 on white, 3.86:1 on #E6E9F5
 *   #4A4FDE  that indigo, deep enough to be text anywhere   6.06 / 5.61 / 5.00
 *   #FF6A5E  the icon's coral end                           2.81:1 on white
 *
 * Two rules follow from those numbers. The icon's own indigo is a ground, never text, because it
 * fails on the deepest panel. And the gradient never touches text at all: its coral end reads
 * 2.81:1, which misses even the large-text threshold of 3.0, so a gradient headline would be
 * unreadable at one end however large it got. The gradient lives on fills.
 *
 * Motion follows Apple's fluid-interface rules: springs rather than keyframes, because a spring
 * animates from wherever the element currently is and can be re-targeted mid-flight without a jump.
 * See app.js. Nothing here animates a property the compositor cannot handle cheaply.
 */

:root {
  color-scheme: light dark;

  /* --- the icon's tile --- */
  --page: #ffffff;
  --panel: #f4f6fc;
  --panel-2: #e6e9f5;

  /* --- ink, from the icon's border colour --- */
  --ink: #1e1b4b;
  --ink-soft: #4e5575;    /* 7.29 on white, 6.75 on --panel, 6.02 on --panel-2 */
  --ink-faint: #5a628a;   /* 5.92 / 5.48 / 4.89 — still AA on the deepest ground */

  /* --- the icon's indigo --- */
  --accent: #4a4fde;      /* text-safe everywhere: 6.06 / 5.61 / 5.00 */
  --accent-vivid: #5b63f0;/* the icon's exact indigo. Fills only — 3.86:1 on --panel-2 */
  --accent-wash: #eef0fe;

  /* --- the icon's fuchsia -> indigo arc, used at exactly the values the icon uses ---
   * These are two adjacent stops of the icon's own gradient, unmodified, and they are the only
   * pair in it that clears every threshold at once: white sits on them at 4.71 and 4.68, so the
   * label passes at any size, and they read 3.10 or better against every band on this site,
   * including the deep one, so the button is always plainly an object.
   * The icon's warm end is not used anywhere on this site. */
  /* The one red on this site, and the only thing wearing it is the button that takes money.
     #F23645 was the colour asked for; it carries a white label at 3.90:1, which is under AA for
     anything but large bold text, so it was deepened until the label passes at any size. */
  --cta: #e01f2e;         /* white on it: 4.77:1 */

  /* --- decoration only, never text. Same arc, with one stop between. --- */
  --ramp: linear-gradient(118deg, #c026d3, #8e3fe4 55%, #5b63f0);

  /* Lines and shadows inherit the icon's own values: it borders itself in #1E1B4B at 10% and
     casts a #312E81 shadow. */
  --line: rgb(30 27 75 / 10%);
  --line-strong: rgb(30 27 75 / 16%);
  --shadow-tint: 49 46 129;

  /* A bigger surface reads as a thicker material: more blur, a deeper shadow. */
  --shadow-sm: 0 1px 2px rgb(var(--shadow-tint) / 8%);
  --shadow-md: 0 2px 6px rgb(var(--shadow-tint) / 8%), 0 10px 28px rgb(var(--shadow-tint) / 8%);
  --shadow-lg: 0 4px 10px rgb(var(--shadow-tint) / 10%), 0 24px 60px rgb(var(--shadow-tint) / 12%);

  --chrome: rgb(255 255 255 / 68%);
  --chrome-edge: rgb(255 255 255 / 60%);

  --radius-sm: .625rem;
  --radius: 1rem;
  --radius-lg: 1.5rem;
  --gutter: 1rem;
  --measure: 70rem;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* The icon's border colour becomes the raised surface here, and the page goes deeper than it. */
    --page: #0d0b20;
    --panel: #171538;
    --panel-2: #1e1b4b;

    --ink: #edeef7;        /* 16.74:1 on the page */
    --ink-soft: #b7bbd6;   /* 10.22 / 9.24 / 8.44 */
    --ink-faint: #9ca1c4;  /* 7.65 / 6.92 / 6.33 */

    --accent: #8e93ff;     /* 7.15 / 6.47 / 5.91 — the icon's indigo lifted until it is text-safe */
    --accent-vivid: #7c83ff;
    --accent-wash: #1c1a45;

    /* The CTA keeps both stops here too: they read 4.11:1 and 4.13:1 against this page. */
    --line: rgb(237 238 247 / 12%);
    --line-strong: rgb(237 238 247 / 20%);
    --shadow-tint: 0 0 0;
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 40%);
    --shadow-md: 0 2px 6px rgb(0 0 0 / 35%), 0 10px 28px rgb(0 0 0 / 35%);
    --shadow-lg: 0 4px 10px rgb(0 0 0 / 40%), 0 24px 60px rgb(0 0 0 / 45%);

    --chrome: rgb(13 11 32 / 66%);
    --chrome-edge: rgb(237 238 247 / 10%);
  }
}

:root[data-theme="dark"] {
  --page: #0d0b20; --panel: #171538; --panel-2: #1e1b4b;
  --ink: #edeef7; --ink-soft: #b7bbd6; --ink-faint: #9ca1c4;
  --accent: #8e93ff; --accent-vivid: #7c83ff; --accent-wash: #1c1a45;
  --line: rgb(237 238 247 / 12%); --line-strong: rgb(237 238 247 / 20%);
  --shadow-tint: 0 0 0;
  --shadow-sm: 0 1px 2px rgb(0 0 0 / 40%);
  --shadow-md: 0 2px 6px rgb(0 0 0 / 35%), 0 10px 28px rgb(0 0 0 / 35%);
  --shadow-lg: 0 4px 10px rgb(0 0 0 / 40%), 0 24px 60px rgb(0 0 0 / 45%);
  --chrome: rgb(13 11 32 / 66%); --chrome-edge: rgb(237 238 247 / 10%);
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 5rem;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  /* The system face already ships optical sizing and tracking tables; there is no reason to load
     a webfont and undo that work. Sizes are in rem so the reader's own text size scales the layout
     with the type instead of bursting it. */
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

/* Tracking is size-specific. Large type reads too loose as it grows, so it tightens; body sits at
   zero; small uppercase needs the opposite treatment and opens up. */
h1, h2, h3 { margin: 0 0 .5em; font-weight: 640; color: var(--ink); }
h1 { font-size: clamp(2.25rem, 5.6vw, 4rem);  line-height: 1.04; letter-spacing: -0.035em; font-weight: 680; }
h2 { font-size: clamp(1.65rem, 3.6vw, 2.5rem); line-height: 1.12; letter-spacing: -0.025em; }
h3 { font-size: 1.0625rem; line-height: 1.35; letter-spacing: -0.008em; font-weight: 640; }
p  { margin: 0 0 1rem; color: var(--ink-soft); }
a  { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }

.lede { font-size: clamp(1.0625rem, 2.1vw, 1.3125rem); line-height: 1.55; letter-spacing: -0.012em; color: var(--ink-soft); max-width: 60ch; }

code {
  font-family: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
  font-size: .86em; background: var(--panel-2); border-radius: .3em; padding: .1em .35em;
  letter-spacing: 0;
}
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) code { background: var(--panel-2); } }

/* ============================ chrome ============================
   A translucent layer the page scrolls under, rather than an opaque strip that eats the top of the
   window. There is no 1px rule under it: the separation appears as a soft edge only once content is
   actually beneath it (see app.js), which is the only moment the separation means anything. */

.bar {
  position: sticky; top: 0; z-index: 30;
  background: var(--chrome);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border-bottom: 1px solid transparent;
  transition: border-color .25s ease, box-shadow .25s ease;
}
.bar.is-edged { border-bottom-color: var(--line); box-shadow: 0 1px 12px rgb(var(--shadow-tint) / 6%); }
.bar .wrap { display: flex; align-items: center; gap: .75rem; height: 3.875rem; }

/* Vibrancy: text on a translucent layer needs more weight and a touch more tracking than the same
   text on a solid ground, or it dissolves into whatever scrolls behind it. */
.bar a { font-weight: 560; letter-spacing: 0.004em; }

.logo {
  display: inline-flex; align-items: center; gap: .625rem;
  font-weight: 650; letter-spacing: -0.017em; color: var(--ink); text-decoration: none;
  white-space: nowrap;   /* the bar is one line tall; a wrapped wordmark would burst it */
}
@media (max-width: 26rem) { .bar .logo { font-size: .9375rem; } }
.logo img { width: 1.875rem; height: 1.875rem; border-radius: .4375rem; box-shadow: var(--shadow-sm); }
.bar nav { margin-left: auto; display: flex; align-items: center; gap: 1.375rem; }
.bar nav a:not(.btn) { color: var(--ink-soft); text-decoration: none; font-size: .9375rem; }
.bar nav a:not(.btn):hover { color: var(--ink); }
@media (max-width: 45rem) { .hide-sm { display: none; } }
/* At 375px the header measured 417px wide against 375 of room. Pricing goes first: the Buy button
   beside it leads to the same money, and the announcement bar links to the section as well.
   Below 24rem the wordmark follows, because the icon alone still says whose site this is. */
@media (max-width: 30rem) { .hide-xs { display: none; } .bar nav { gap: 1rem; } }
@media (max-width: 24rem) { .bar .logo .wordmark { display: none; } }

/* ============================ buttons ============================
   Feedback lands on pointer-down, not on release — :active fires the moment the press begins, and
   touch-action removes the tap delay that would otherwise sit in front of it. */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .75rem 1.5rem; border: 1px solid transparent; border-radius: 999px;
  font: inherit; font-size: 1rem; font-weight: 600; letter-spacing: -0.01em;
  text-decoration: none; cursor: pointer; touch-action: manipulation;
  transform: translateZ(0);
  transition: transform 110ms cubic-bezier(.2,.8,.3,1), filter 160ms ease, background-color 160ms ease, border-color 160ms ease;
}
.btn:active { transform: scale(.968); }
.btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

/* The label is large and bold because a primary action should be, not because it has to be:
   white clears 4.5:1 on this red at any size. */
.btn-buy {
  background: var(--cta); color: #fff;
  font-size: 1.1875rem; font-weight: 700; padding: .875rem 1.75rem;
  box-shadow: var(--shadow-md);
}
.btn-buy:hover { filter: brightness(1.07); }
.btn-buy.btn-sm { font-size: .9375rem; font-weight: 640; padding: .5rem 1.125rem; box-shadow: var(--shadow-sm); }
.bar nav a.btn-buy, .bar nav a.btn-buy:hover { color: #fff; }

/* At the top of the page the button has to say what it is, so the nav one carries a label, a second
   line and an icon rather than the word "Buy" on its own.

   The gradient is three stops and each was measured against a white label: #dc2626 is 4.83:1,
   #e11d48 is 4.70:1, #cf3a12 is 4.92:1. The orange the same button ends on elsewhere, #f97316, is
   2.80:1 — a white label on it fails AA at every size, so it is not used. This is the one control on
   the site that takes money, and it is the last one that may be hard to read. */
.btn-buy.btn-nav {
  position: relative; isolation: isolate; overflow: hidden;
  gap: .5625rem;
  padding: .3125rem .8125rem .3125rem .3125rem;
  border-radius: 1rem;
  border-color: rgb(255 255 255 / 28%);
  background: linear-gradient(100deg, #dc2626 0%, #e11d48 52%, #cf3a12 100%);
  box-shadow: 0 6px 18px rgb(224 31 46 / 28%);
}
.btn-buy.btn-nav:hover { filter: none; transform: translateY(-1px); box-shadow: 0 10px 24px rgb(224 31 46 / 36%); }
.btn-buy.btn-nav:active { transform: translateY(0) scale(.98); }

/* A sheen crossing the button on hover, as a pseudo-element so the markup stays three spans. */
.btn-buy.btn-nav::before {
  content: ""; position: absolute; inset: -1px;
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / 22%), transparent);
  transform: translateX(-100%);
  transition: transform 700ms ease;
}
.btn-buy.btn-nav:hover::before { transform: translateX(100%); }

.btn-nav-icon, .btn-nav-text { position: relative; }
.btn-nav-icon {
  display: flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; flex: none; border-radius: .75rem;
  background: rgb(255 255 255 / 18%);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 26%);
}
.btn-nav-text { display: flex; flex-direction: column; align-items: flex-start; text-align: left; line-height: 1.15; }
.btn-nav-title { font-size: .875rem; font-weight: 700; }
.btn-nav-note { font-size: .6875rem; font-weight: 600; letter-spacing: .02em; opacity: .88; }

/* On a narrow screen the second line is the first thing to go: the price is in the page anyway. */
@media (max-width: 30rem) {
  .btn-buy.btn-nav { padding-right: .5rem; }
  .btn-nav-note { display: none; }
}

.btn-ghost { border-color: var(--line-strong); color: var(--ink); background: transparent; }
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }

/* ============================ layout ============================ */

section { padding: clamp(3.5rem, 7vw, 6rem) 0; }
.section-head { max-width: 60ch; margin-bottom: clamp(2rem, 4vw, 3rem); }

.grid { display: grid; gap: 1.25rem; }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 54rem) { .grid-3 { grid-template-columns: 1fr 1fr; } }
@media (max-width: 38rem) { .grid-3 { grid-template-columns: 1fr; } }

.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.5rem;
}
.card p:last-child { margin-bottom: 0; }
.card .num {
  display: grid; place-items: center; width: 1.875rem; height: 1.875rem;
  border-radius: .5625rem; background-image: var(--ramp); color: #fff;
  font-size: .875rem; font-weight: 700; margin-bottom: .875rem;
  box-shadow: var(--shadow-sm);
}

/* ============================ hero ============================ */

.hero { padding-top: clamp(3rem, 8vw, 6rem); position: relative; overflow: clip; }

/* The icon's full ramp, at the size and softness where it is scenery rather than a thing to read. */
.hero::before {
  content: ""; position: absolute; inset: -30% -10% auto; height: 40rem; z-index: -1;
  background-image: var(--ramp); opacity: .1; filter: blur(80px);
  pointer-events: none;
}
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .hero::before { opacity: .22; } }

.kicker {
  display: inline-flex; align-items: center; gap: .5rem;
  font-size: .8125rem; font-weight: 660;
  letter-spacing: 0.07em;   /* small uppercase opens up, the opposite of the display sizes */
  text-transform: uppercase; color: var(--accent);
  background: var(--accent-wash); border: 1px solid var(--line);
  padding: .375rem .75rem; border-radius: 999px; margin-bottom: 1.25rem;
}
.actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.75rem; align-items: center; }
.fineprint { font-size: .9375rem; color: var(--ink-faint); margin: .875rem 0 0; }

/* A large surface reads as a thicker material than a card: more blur behind it, a deeper shadow. */
.shot {
  margin: clamp(2.5rem, 5vw, 3.5rem) 0 0;
  border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--panel); box-shadow: var(--shadow-lg); overflow: hidden;
}
.shot img { display: block; width: 100%; height: auto; }
.shot figcaption { padding: .75rem 1.125rem; font-size: .875rem; color: var(--ink-faint); border-top: 1px solid var(--line); }
.shot-todo {
  display: grid; place-items: center; text-align: center;
  aspect-ratio: 16 / 10; padding: 1.5rem; color: var(--ink-faint); font-size: .9375rem;
  background: repeating-linear-gradient(45deg, transparent, transparent .875rem,
              var(--line) .875rem, var(--line) 1.75rem);
}

/* ============================ callout ============================ */

.callout {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: clamp(1.5rem, 4vw, 2.5rem);
  box-shadow: var(--shadow-md); position: relative; overflow: hidden;
}
.callout::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 3px; background-image: var(--ramp);
}
.callout h2 { color: var(--ink); }

/* ============================ pricing ============================ */

.price-card {
  border: 1px solid var(--line-strong); border-radius: var(--radius-lg);
  padding: clamp(1.5rem, 4vw, 2.5rem); background: var(--page);
  box-shadow: var(--shadow-lg); max-width: 30rem; position: relative; overflow: hidden;
}
.price-card::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 3px; background-image: var(--ramp);
}
.price-amount {
  font-size: clamp(2.25rem, 6vw, 3rem); font-weight: 700;
  letter-spacing: -0.035em; line-height: 1.05; color: var(--ink);
}
.price-amount span { font-size: 1rem; font-weight: 500; letter-spacing: -0.01em; color: var(--ink-faint); }
.price-card ul { list-style: none; padding: 0; margin: 1.5rem 0; }
.price-card li { position: relative; padding-left: 1.75rem; margin-bottom: .75rem; color: var(--ink-soft); }
.price-card li::before {
  content: ""; position: absolute; left: .25rem; top: .55em;
  width: .6875rem; height: .375rem;
  border-left: 2px solid var(--accent); border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}
.price-card .btn { width: 100%; }

/* ============================ faq ============================
   <details> keeps working with no JavaScript at all. app.js upgrades it to a spring that animates
   from the height it is at this instant, so a panel caught halfway reverses from there. */

.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  cursor: pointer; list-style: none; touch-action: manipulation;
  display: flex; align-items: flex-start; gap: .75rem;
  padding: 1.125rem 0; font-weight: 620; letter-spacing: -0.012em; color: var(--ink);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: .25rem; }
.faq .sign {
  position: relative; flex: none; width: .875rem; height: .875rem; margin-top: .45em;
}
.faq .sign::before, .faq .sign::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  background: var(--accent); border-radius: 1px;
  transition: transform .32s cubic-bezier(.2,.8,.3,1), opacity .32s ease;
}
.faq .sign::before { width: .875rem; height: 2px; }
.faq .sign::after  { width: 2px; height: .875rem; }
.faq details[open] .sign::after { transform: rotate(90deg); opacity: 0; }
.faq .answer { overflow: hidden; }
.faq .answer > div { padding: 0 0 1.125rem 1.625rem; }
.faq .answer p:last-child { margin-bottom: 0; }

/* ============================ footer ============================ */

footer { border-top: 1px solid var(--line); padding: 2.5rem 0 3.5rem; font-size: .9375rem; color: var(--ink-faint); }
footer .cols { display: flex; flex-wrap: wrap; gap: 2rem 3rem; justify-content: space-between; }
footer a { color: var(--ink-soft); text-decoration: none; }
footer a:hover { color: var(--accent); text-decoration: underline; }
footer ul { list-style: none; padding: 0; margin: 0; }
footer li { margin-bottom: .5rem; }
footer .legal { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid var(--line); max-width: 70ch; }

/* ============================ prose ============================ */

.prose { max-width: 68ch; padding: clamp(2.5rem, 7vw, 4.5rem) 0; }
.prose h2 { margin-top: 2em; }
.prose h2:first-of-type { margin-top: 1em; }
.prose ol, .prose ul { color: var(--ink-soft); padding-left: 1.3em; }
.prose li { margin-bottom: .6em; }
.prose .step { border-left: 3px solid var(--accent); padding: .25rem 0 .25rem 1.25rem; margin: 0 0 1.5rem; }

/* A caveat the reader must not skim past. Amber rather than the site's red: red here is the colour of
   the button that takes money, and a warning wearing it would read as part of the sale. The text keeps
   the page's own ink, which already clears AA on this ground, so the tint carries no meaning on its own
   for anyone who cannot see it — the word "not" in the sentence does that. */
.notice.warn {
  border-left: 3px solid #b45309;
  background: rgb(180 83 9 / 8%);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: .75rem 1rem;
  margin: 1rem 0 .75rem;
}

/* ============================ reveal ============================
   The starting values live here, in a file, because the page is served under
   `style-src 'self'` and a policy with no `unsafe-inline` refuses styles written from script. The
   first deployment proved it: app.js hid twenty-two blocks with `el.style.opacity = "0"`, the
   writes that would have brought them back were refused, and everything below the hero stayed
   invisible. A stylesheet cannot be refused, so app.js now only adds and removes this class.

   The cost is that the motion is a CSS transition rather than the spring it used to be: the reveal
   is not interruptible any more. It is one fade of half a second that nobody grabs mid-flight, so
   the trade buys a page that cannot go blank.

   will-change is on the hidden state only, so it is held while a block waits to move and dropped
   the moment the class comes off. Left on permanently it is twenty-seven compositor layers kept for
   the life of the page, a cost paid forever for motion that happens once. */

[data-reveal] {
  transition: opacity 500ms cubic-bezier(.2,.8,.3,1), transform 500ms cubic-bezier(.2,.8,.3,1);
}
[data-reveal].is-hidden {
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  will-change: transform, opacity;
}

/* ============================ accessibility =====================
   Reduced motion is not "no feedback" — the press response and the colour changes stay, and only
   the travelling parts are replaced by a plain cross-fade. */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  [data-reveal], [data-reveal].is-hidden { opacity: 1 !important; transform: none !important; }
  .btn { transition: filter 160ms ease, background-color 160ms ease; }
  .btn-buy.btn-nav::before, .btn-buy.btn-nav:hover::before { transition: none; transform: translateX(-100%); }
  .btn-buy.btn-nav:hover { transform: none; }
  .btn:active { transform: none; filter: brightness(.94); }
  .faq .sign::before, .faq .sign::after { transition: none; }
}

@media (prefers-reduced-transparency: reduce) {
  .bar { background: var(--page); backdrop-filter: none; -webkit-backdrop-filter: none; border-bottom-color: var(--line); }
  .hero::before { display: none; }
}

@media (prefers-contrast: more) {
  :root { --line: rgb(30 27 75 / 45%); --line-strong: rgb(30 27 75 / 60%); --ink-soft: #33384f; --ink-faint: #3d4260; }
  :root:not([data-theme="light"]) { --line: rgb(237 238 247 / 45%); --line-strong: rgb(237 238 247 / 65%); --ink-soft: #dcdef0; --ink-faint: #c8cbe2; }
  .bar { background: var(--page); backdrop-filter: none; -webkit-backdrop-filter: none; }
  .card, .callout, .price-card, .shot { border-color: var(--line-strong); }
  .hero::before { display: none; }
}

@media print {
  /* A page being printed does not scroll, so nothing would ever be revealed. */
  [data-reveal], [data-reveal].is-hidden { opacity: 1 !important; transform: none !important; }
  .bar { position: static; background: none; backdrop-filter: none; }
  .hero::before { display: none; }
}

/* ============================ tonal bands ============================
 * TypingMind's landing page gets its rhythm from tone rather than from rules: white, then a black
 * band for the logo wall, then grey, then white again. The eye uses those changes to count
 * sections, so the page needs no dividers at all.
 *
 * A band here is a token scope, not a background colour. Setting --page, --ink and friends inside
 * it means every card, border and label underneath simply works, including on the inverted band.
 *
 * `tone-deep` is the break in the rhythm. In the light theme that is a dramatic inversion; in the
 * dark theme it cannot go darker and stay a break, so it lifts instead. Either way it is light ink
 * on a deep indigo ground, and every value below was measured on the ground it sits on.
 */

.tone-plain { background: var(--page); }

.tone-soft { background: var(--panel); }
.tone-soft .card, .tone-soft .price-card { background: var(--page); }

.tone-deep {
  --page: #1e1b4b;          /* the icon's own border colour, used at full size */
  --panel: #262252;
  --panel-2: #2a2660;
  --ink: #f2f3fb;           /* 14.46:1 */
  --ink-soft: #c3c6e0;      /*  9.50:1 */
  --ink-faint: #a6aacb;     /*  7.03:1 */
  --accent: #a5aaff;        /*  7.46:1 — the icon's indigo, lifted for this ground */
  --accent-wash: rgb(165 170 255 / 12%);
  --line: rgb(242 243 251 / 14%);
  --line-strong: rgb(242 243 251 / 24%);
  --shadow-tint: 0 0 0;
  background: var(--page);
  color: var(--ink);
}
.tone-deep .card { background: var(--panel); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .tone-soft { background: #141130; }
  :root:not([data-theme="light"]) .tone-deep {
    /* Darker would not read as a break here, so the band lifts instead. */
    --page: #251f5e; --panel: #2f2970; --panel-2: #332d78;
    --ink: #f2f3fb;       /* 13.22:1 */
    --ink-soft: #c7cae4;  /*  9.05:1 */
    --ink-faint: #acb0d0; /*  6.88:1 */
    --accent: #b9bdff;    /*  8.21:1 */
  }
}
:root[data-theme="dark"] .tone-soft { background: #141130; }
:root[data-theme="dark"] .tone-deep {
  --page: #251f5e; --panel: #2f2970; --panel-2: #332d78;
  --ink: #f2f3fb; --ink-soft: #c7cae4; --ink-faint: #acb0d0; --accent: #b9bdff;
}

/* ============================ announcement ============================ */

/* Brand, not red: this bar reports what is shipping, and red on it would read as an alert. */
.announce {
  background-image: var(--ramp); color: #fff;
  font-size: .875rem; font-weight: 600; letter-spacing: -0.005em;
  text-align: center; padding: .5rem 1rem;
}
.announce a { color: #fff; text-decoration: none; display: inline-flex; gap: .35rem; align-items: center; }
.announce a:hover { text-decoration: underline; }

/* ============================ section furniture ============================ */

.eyebrow {
  display: inline-flex; align-items: center; gap: .4375rem;
  font-size: .8125rem; font-weight: 640; letter-spacing: 0.055em; text-transform: uppercase;
  color: var(--accent); margin-bottom: .875rem;
}
.eyebrow::before { content: ""; width: .375rem; height: .375rem; border-radius: 50%; background-image: var(--ramp); }

/* The accent phrase inside a headline — TypingMind's one reliable trick. It is a solid colour and
   never the gradient, for the reason stated at the top of this file. */
.hl { color: var(--accent); }

.center { text-align: center; }
.center .section-head { margin-inline: auto; }
.center .actions { justify-content: center; }

/* ============================ providers ============================ */

.providers { display: flex; flex-wrap: wrap; gap: .625rem 1.75rem; justify-content: center; }
.providers li {
  list-style: none; font-size: 1.0625rem; font-weight: 600; letter-spacing: -0.012em;
  color: var(--ink-soft);
}

/* ============================ comparison ============================ */

.compare { width: 100%; border-collapse: collapse; font-size: .9375rem; }
.compare th, .compare td { padding: .875rem 1rem; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top; }
.compare thead th { font-weight: 640; letter-spacing: -0.01em; color: var(--ink-faint); font-size: .8125rem; text-transform: uppercase; letter-spacing: .05em; }
.compare tbody th { font-weight: 600; color: var(--ink); width: 34%; }
.compare td { color: var(--ink-soft); }
.compare .mine { color: var(--ink); font-weight: 560; background: var(--accent-wash); }
.compare-scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); }
@media (max-width: 40rem) { .compare { min-width: 34rem; } }

/* ============================ theme control ============================ */

.theme-toggle {
  display: inline-grid; place-items: center;
  width: 2.125rem; height: 2.125rem; padding: 0;
  border: 1px solid var(--line-strong); border-radius: 50%;
  background: transparent; color: var(--ink-soft); cursor: pointer; touch-action: manipulation;
  transition: transform 110ms cubic-bezier(.2,.8,.3,1), color 160ms ease, border-color 160ms ease;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--accent); }
.theme-toggle:active { transform: scale(.92); }
.theme-toggle:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.theme-toggle svg { width: 1rem; height: 1rem; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.theme-toggle [data-icon] { display: none; }
:root:not([data-theme]) .theme-toggle [data-icon="auto"],
:root[data-theme="light"] .theme-toggle [data-icon="light"],
:root[data-theme="dark"]  .theme-toggle [data-icon="dark"] { display: block; }

@media (prefers-reduced-motion: reduce) { .theme-toggle:active { transform: none; } }

/* The brand line beside the wordmark. Quiet on purpose: it names the promise, it is not a control, so it
   takes --ink-faint and no weight. `nav` already claims the free space with margin-left:auto, so this
   needs no margin of its own; it is hidden with .hide-sm before the bar gets tight. */
.bar .tagline {
  margin: 0;
  padding-left: .9rem;
  border-left: 1px solid var(--line);
  font-size: .82rem;
  letter-spacing: .01em;
  white-space: nowrap;
  color: var(--ink-faint);
}

/* ============================ from the markup ====================
   These were `style="..."` attributes until the site was first deployed. The pages are served under
   `style-src 'self'`, which refuses a style attribute the same way it refuses one written from
   script, so every one of them was silently dropped and the spacing they set never applied. They
   are declarations, not a utility system: each one exists because exactly one place needed it. */

.meta-line     { font-size: .92rem; color: var(--ink-faint); }
.back-link     { margin-top: 40px; }
.flush         { margin: 0; }
.flush-below   { margin-bottom: 0; }
.gap-above     { margin-top: 1.5rem; }
.gap-below     { margin-bottom: .625rem; }
.centred       { text-align: center; }
.centred-block { margin-inline: auto; }
.col-narrow    { max-width: 32ch; }
.small-text    { font-size: .9375rem; }
.push-right    { margin-left: auto; }
.aside-note    { margin-top: 1.5rem; font-size: .9375rem; color: var(--ink-faint); max-width: 52ch; }
