/* Forget The Forms — marketing site.

   The palette, fonts, radii and brand mark are LIFTED FROM `dashboard.py`'s STYLE block so the
   site and the app read as one product. If you recolour the app, recolour here to match — the
   dashboard's own comment calls its accent block "change these to recolor everything", and this
   file is the second place those values live.
     accent  #0f5f8f petrol blue   (brand / primary)      dark: #4aa3d8
     warn    #9a6a12 ochre         ("needs you" ONLY)     dark: #d8a648
     ok      #1f7a4d green         (proven / matched)     dark: #4fbb84
   Self-contained: no webfonts, no CDN, no external asset of any kind. */

:root {
  --accent:       #0f5f8f;
  --accent-strong:#0b4a70;
  --ok:           #1f7a4d;
  --warn:         #9a6a12;

  --paper:        #f4f1ea;
  --surface:      #fffdf8;
  --surface-2:    #f6f2e9;
  --ink:          #1b1e26;
  --ink-soft:     #565b66;
  --muted:        #8a8f9a;
  --line:         #e6dece;
  --line-strong:  #d8cdb7;
  --on-accent:    #ffffff;

  --serif: "Palatino Linotype", "Book Antiqua", Palatino, "Iowan Old Style", Georgia, serif;
  --sans:  "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --mono:  "Cascadia Code", Consolas, ui-monospace, monospace;

  --r: 11px;
  --r-sm: 7px;
  --shadow: 0 1px 2px rgba(20,22,28,.05), 0 10px 26px -14px rgba(20,22,28,.30);

  --measure: 33rem;
  --gutter: clamp(1.25rem, 5vw, 4rem);
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:       #14161b;
    --surface:     #1c1f27;
    --surface-2:   #232732;
    --ink:         #ece9e1;
    --ink-soft:    #b6bac3;
    --muted:       #868b96;
    --line:        #2c313c;
    --line-strong: #3b414e;
    --accent:      #4aa3d8;
    --accent-strong:#78bde6;
    --ok:          #4fbb84;
    --warn:        #d8a648;
    --on-accent:   #10141a;
    --shadow: 0 1px 2px rgba(0,0,0,.3), 0 10px 26px -14px rgba(0,0,0,.6);
  }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: clamp(1rem, 0.4vw + 0.92rem, 1.09rem);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }
::selection { background: color-mix(in srgb, var(--accent) 22%, var(--surface)); }

.wrap { max-width: 60rem; margin: 0 auto; padding-inline: var(--gutter); }
.rule { border: 0; border-top: 1px solid var(--line); margin: 0; }
section { padding-block: clamp(2.75rem, 6vw, 4.75rem); }

/* ---------- masthead ---------- */

.masthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1.15rem;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}

.brand { display: flex; align-items: center; gap: 0.5rem; text-decoration: none; color: var(--ink); }
.brand-mark { color: var(--accent); font-size: 0.95rem; line-height: 1; }
.brand-name {
  font-family: var(--serif);
  font-size: 1.16rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.brand-name .thin { color: var(--muted); font-weight: 400; }

.masthead nav { display: flex; gap: 1.4rem; font-size: 0.88rem; }
.masthead nav a {
  text-decoration: none;
  color: var(--ink-soft);
  padding-bottom: 2px;
  border-bottom: 1.5px solid transparent;
  /* No colour transition here either, for the reason spelled out in full on `.btn` below: a
     transition toward a value that a media query just changed never completes and pins the old
     scheme's colour. Found 2026-08-10 by the forced-fail run of the test written for `.btn` --
     the nav had the same defect and nobody had looked. Nav links have nothing scheme-independent
     worth animating, so they simply snap, which is what nav links normally do. */
}
.masthead nav a:hover, .masthead nav a[aria-current] { color: var(--ink); border-color: var(--accent); }

/* ---------- hero ---------- */

.hero { padding-block: clamp(3rem, 8vw, 5.5rem) clamp(2.25rem, 5vw, 3.5rem); }

h1 {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(2.3rem, 6vw, 4rem);
  line-height: 1.06;
  letter-spacing: -0.022em;
  margin: 0 0 1.15rem;
  max-width: 15ch;
  text-wrap: balance;
}
h1 em { font-style: normal; color: var(--accent); }

.standfirst {
  font-size: clamp(1.06rem, 0.8vw + 0.92rem, 1.25rem);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: var(--measure);
  margin: 0 0 2rem;
  text-wrap: pretty;
}

/* ---------- hero graphic ----------
   Real field LABELS, abstract field VALUES. That split is the whole design rule here.

   Ethan cut the very first version because it used a worked example naming his own licences, so
   nothing in this picture may assert a fact about a person — hence the values stay as bars. But
   the version after that went fully wordless, and a card of grey bars reads as a LOADING
   SKELETON rather than a form (2026-08-10: "looks like AI bullshit"). Generic labels every
   application on earth asks for are the middle ground: they make the picture legible while
   asserting nothing about anybody. They are gated by tests/test_site_copy.py like all copy.

   Shape still carries the idea: four rows fill themselves, one stays empty and gets flagged. */

.hero-grid { display: grid; gap: clamp(2rem, 5vw, 3.5rem); align-items: center; }
@media (min-width: 62rem) { .hero-grid { grid-template-columns: 1fr 0.86fr; } }

.formcard {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
  padding: 1.15rem 1.25rem 1.3rem;
  min-width: 0;
}

.fc-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.7rem;
  padding-bottom: 0.8rem;
  margin-bottom: 1.05rem;
  border-bottom: 1px solid var(--line);
}
.fc-name { font-family: var(--serif); font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; }
.fc-step {
  font-family: var(--mono);
  font-size: 0.63rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}

.fc-row { margin-bottom: 0.85rem; }
.fc-label {
  display: block;
  font-size: 0.715rem;
  line-height: 1.2;
  color: var(--muted);
  margin-bottom: 0.34rem;
}
.fc-field { display: grid; grid-template-columns: minmax(0, 1fr) 1rem; gap: 0.6rem; align-items: center; }

/* a filled control: an input-shaped box, a soft "proven" wash, and a bar standing in for the
   text that was typed into it. The bar is what stops this reading as an empty placeholder. */
.fc-val {
  height: 30px;
  border-radius: var(--r-sm);
  position: relative;
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--line);
}
.fc-val::after {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ok) 12%, var(--surface));
  border-left: 2px solid color-mix(in srgb, var(--ok) 60%, var(--surface));
  transform-origin: left center;
}
.fc-val::before {
  content: "";
  position: absolute;
  z-index: 2;
  left: 0.66rem;
  top: 50%;
  height: 6px;
  border-radius: 3px;
  transform: translateY(-50%);
  background: color-mix(in srgb, var(--ink) 50%, var(--surface-2));
  transform-origin: left center;
}
/* uneven on purpose: four identical bars look generated, four different lengths look typed */
.fc-val.w1::before { width: 47%; }
.fc-val.w2::before { width: 64%; }
.fc-val.w3::before { width: 19%; }
.fc-val.w4::before { width: 36%; }

.fc-tick { color: var(--ok); font-size: 0.86rem; line-height: 1; text-align: center; }

/* the one that abstains */
.fc-val.blank { background: transparent; border: 1.5px dashed var(--warn); }
.fc-val.blank::after,
.fc-val.blank::before { content: none; }
.fc-flag { color: var(--warn); font-size: 0.86rem; line-height: 1; text-align: center; }

.fc-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 1rem;
  margin-top: 1.15rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
  font-size: 0.72rem;
  color: var(--muted);
}
.fc-key { display: inline-flex; align-items: center; gap: 0.42rem; }
.fc-key i { width: 9px; height: 9px; border-radius: 2px; font-style: normal; flex: none; }
.fc-key.ok i   { background: color-mix(in srgb, var(--ok) 32%, var(--surface)); border: 1px solid var(--ok); }
.fc-key.you i  { background: transparent; border: 1.5px dashed var(--warn); }
.fc-key.you { color: var(--warn); margin-left: auto; }

/* Entrance animations use `backwards` fill and animate TOWARDS the resting state, never away
   from it. The resting state is the finished state, so if animations never run — reduced-motion,
   an extension that disables them, a renderer that isn't compositing — the graphic still shows
   filled bars instead of an empty skeleton. The first version had `transform: scaleX(0)` as the
   base and relied on the animation to reveal it, which renders a blank hero whenever the
   animation doesn't play. Both pseudo-elements obey this: the wash AND the typed-text bar rest
   in their finished state. */
@media (prefers-reduced-motion: no-preference) {
  .fc-val::after  { animation: fc-fill 0.5s cubic-bezier(0.2,0.75,0.3,1) backwards; }
  .fc-val::before { animation: fc-type 0.34s cubic-bezier(0.2,0.75,0.3,1) backwards; }
  .fc-row:nth-child(2) .fc-val::after  { animation-delay: 0.30s; }
  .fc-row:nth-child(3) .fc-val::after  { animation-delay: 0.48s; }
  .fc-row:nth-child(5) .fc-val::after  { animation-delay: 0.78s; }
  .fc-row:nth-child(6) .fc-val::after  { animation-delay: 0.96s; }
  .fc-row:nth-child(2) .fc-val::before { animation-delay: 0.46s; }
  .fc-row:nth-child(3) .fc-val::before { animation-delay: 0.64s; }
  .fc-row:nth-child(5) .fc-val::before { animation-delay: 0.94s; }
  .fc-row:nth-child(6) .fc-val::before { animation-delay: 1.12s; }
  .fc-tick { animation: fc-pop 0.3s ease-out backwards; }
  .fc-row:nth-child(2) .fc-tick { animation-delay: 0.70s; }
  .fc-row:nth-child(3) .fc-tick { animation-delay: 0.88s; }
  .fc-row:nth-child(5) .fc-tick { animation-delay: 1.18s; }
  .fc-row:nth-child(6) .fc-tick { animation-delay: 1.36s; }
  /* the blank row is skipped over, then flagged — the beat the whole product is about */
  .fc-row.abstains .fc-val { animation: fc-appear 0.4s ease-out 1.55s backwards; }
  .fc-flag { animation: fc-pop 0.35s ease-out 1.85s backwards; }
}
@keyframes fc-fill   { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes fc-type   { from { opacity: 0; transform: translateY(-50%) scaleX(0.2); }
                       to   { opacity: 1; transform: translateY(-50%) scaleX(1); } }
@keyframes fc-pop    { from { opacity: 0; transform: scale(0.6); } to { opacity: 1; transform: scale(1); } }
@keyframes fc-appear { from { opacity: 0; } to { opacity: 1; } }

h2 {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(1.6rem, 2.6vw, 2.2rem);
  line-height: 1.14;
  letter-spacing: -0.016em;
  margin: 0 0 1.4rem;
  max-width: 20ch;
  text-wrap: balance;
}

.kicker {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 1rem;
}

.lede { color: var(--ink-soft); max-width: var(--measure); margin: 0 0 1.6rem; }

/* ---------- numbered points ---------- */

.points { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.points li {
  display: grid;
  gap: 0.15rem 1.5rem;
  padding-block: 1.25rem;
  border-top: 1px solid var(--line);
  align-items: baseline;
}
@media (min-width: 46rem) {
  .points li { grid-template-columns: 2.5rem minmax(0, 17ch) minmax(0, 1fr); }
}
.points .n { font-family: var(--mono); font-size: 0.78rem; color: var(--accent); }
.points .h {
  font-family: var(--serif);
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.points .d { color: var(--ink-soft); margin: 0; }

/* ---------- fine print (pricing) ----------
   Deliberately NOT `.points`. That numbered 01/02/03 component sitting identically on both pages
   was one of the things that made the site read as machine-made, and numbering only earns its
   place where the items are a sequence. "How it works" is a sequence; three unrelated billing
   facts are not. Keep these two components apart. */

.terms { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.4rem; max-width: 42rem; }
.terms li { position: relative; padding-left: 1.4rem; }
.terms li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.66em;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 2px;
  background: var(--accent);
  opacity: 0.5;
}
.terms .h {
  display: block;
  font-family: var(--serif);
  font-size: 1.14rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 0.15rem;
}
.terms .d { color: var(--ink-soft); margin: 0; }

/* ---------- founder's note ----------
   Set apart from everything else on purpose. Every other block on the site is a bordered card on
   `--surface`, and that sameness is part of what read as generated — this one is bare paper, a
   single hairline down the left, and the serif at body size instead of the sans. It should look
   like a letter dropped into a product page, because that is what it is. */

.note-inner {
  max-width: 38rem;
  border-left: 2px solid var(--line-strong);
  padding-left: clamp(1.1rem, 3vw, 2.1rem);
}
.note .kicker { display: block; }
.note p {
  font-family: var(--serif);
  font-size: clamp(1.01rem, 0.4vw + 0.93rem, 1.13rem);
  line-height: 1.62;
  color: var(--ink);
  margin: 0 0 1.05rem;
  text-wrap: pretty;
}
.note p:first-of-type {
  font-size: clamp(1.2rem, 1.1vw + 0.98rem, 1.5rem);
  line-height: 1.36;
  letter-spacing: -0.012em;
  margin-bottom: 1.25rem;
}
.note .sig {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.6rem 0 0;
  font-size: 1rem;
  font-style: italic;
  color: var(--ink-soft);
}
.note .sig::before {
  content: "";
  width: 2rem;
  height: 1px;
  background: var(--line-strong);
  flex: none;
}

/* ---------- plain feature list ---------- */

.does { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.72rem; max-width: 42rem; }
.does li {
  display: grid;
  grid-template-columns: 1.3rem 1fr;
  gap: 0.7rem;
  align-items: baseline;
  color: var(--ink-soft);
}
.does li::before {
  content: "\2713";
  color: var(--ok);
  font-size: 0.92rem;
}
.does b { color: var(--ink); font-weight: 600; }

/* ---------- pricing ---------- */

.tiers { display: grid; gap: 0.9rem; margin-block: 1.75rem; }
@media (min-width: 48rem) { .tiers { grid-template-columns: repeat(3, 1fr); } }

.tier {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.tier.feature { border-color: var(--accent); box-shadow: var(--shadow); }
.tier .n { font-size: 0.9rem; color: var(--ink-soft); }
.tier .price {
  font-family: var(--serif);
  font-size: 2.5rem;
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.03em;
}
.tier .price sup { font-size: 0.42em; top: -0.9em; position: relative; }
.tier .per { font-family: var(--mono); font-size: 0.76rem; color: var(--accent); }
/* The situation the pack is FOR ("a typical full search"). Added 2026-08-13 with the v2 ladder:
   a pack size is arithmetic the buyer has to do, a situation is a decision they can make at a
   glance. Deliberately quiet - it sits under the price, it does not compete with it. No new
   colour: --ink-soft and --line are both already in the palette above. */
.tier .what {
  margin-top: 0.55rem;
  padding-top: 0.55rem;
  border-top: 1px solid var(--line);
  font-size: 0.82rem;
  color: var(--ink-soft);
}

.freebar {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 1.1rem 1.4rem;
  color: var(--ink-soft);
}
.freebar b { color: var(--ink); }

/* the "needs you" colour, used only where the app uses it */
.flagline {
  border-left: 3px solid var(--warn);
  background: var(--surface-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: 0.85rem 1.1rem;
  margin: 1.5rem 0;
  color: var(--ink-soft);
  max-width: 42rem;
}
.flagline b { color: var(--ink); }

/* ---------- CTA + buttons ---------- */

.cta { background: var(--surface-2); border-block: 1px solid var(--line); }

.status-line {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.06em;
  color: var(--warn);
  margin: 0 0 0.9rem;
}
.status-line::before {
  content: "";
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--warn);
  animation: pulse 2.6s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) { .status-line::before { animation: none; } }

.btn-row { display: flex; flex-wrap: wrap; gap: 0.7rem; align-items: center; }

.btn {
  display: inline-block;
  appearance: none;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.94rem;
  font-weight: 600;
  text-decoration: none;
  padding: 0.72rem 1.35rem;
  border-radius: var(--r-sm);
  border: 1.5px solid var(--accent);
  background-color: var(--accent);
  color: var(--on-accent);
  /* TRANSITION NOTHING THAT CHANGES WITH THE COLOUR SCHEME. Only `transform` is scheme-independent,
     so only `transform` may be transitioned here. Colour snaps on hover, which is what most
     buttons do anyway.

     History, because this has now been got wrong twice. 2026-08-04 the `background` shorthand was
     narrowed to `background-color` on the theory that the shorthand was failing to re-resolve the
     custom property. That was the wrong diagnosis and the bug survived it. Re-measured 2026-08-10
     in a real browser, light mode, after a full reload:

       --accent resolved #0f5f8f, and .brand-mark / .points .n / h1 em ALL painted #0f5f8f
       ...while .btn painted #4aa3d8, the DARK accent, at 2.79:1 against its white label
       btn.getAnimations() -> background-color + all four border-colors stuck in state "running"
       forcing a reflow (display:none, read offsetHeight, restore) snapped it to #0f5f8f

     So the property is declared correctly and the cascade is right; the transition simply never
     completes when its target value comes from a custom property that a media query just changed,
     and it pins the old scheme's colour until something forces a recalc. Longhand vs shorthand was
     never the variable. Anyone re-adding a colour to this transition reintroduces a wrong-brand,
     sub-3:1 button for every user whose OS switches theme on a schedule.
     Guarded by tests/test_site_button_transition.py. Same failure class as ISSUES Z17. */
  transition: transform 0.18s;
}
.btn:hover { background-color: var(--accent-strong); border-color: var(--accent-strong); transform: translateY(-1px); }

.btn.quiet { background-color: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn.quiet:hover { border-color: var(--accent); color: var(--accent); background-color: transparent; }

/* Real work isn't hosted yet for these, so they're a native <button disabled> rather than an
   <a href="#">  a disabled button is honestly inert; a link that goes nowhere looks broken. */
.btn:disabled {
  cursor: default;
  background-color: transparent;
  border-color: var(--line-strong);
  color: var(--muted);
}
.btn:disabled:hover {
  background-color: transparent;
  border-color: var(--line-strong);
  color: var(--muted);
  transform: none;
}

/* ---------- get it (desktop app / extension) ---------- */

.getgrid { display: grid; gap: 0.9rem; margin-block: 1.6rem; }
@media (min-width: 48rem) { .getgrid { grid-template-columns: repeat(2, 1fr); } }

.getcard {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 1.4rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
}
.getcard h3 {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 1.15rem;
  line-height: 1.25;
  margin: 0.3rem 0 0.15rem;
  letter-spacing: -0.01em;
}
.getcard .d { color: var(--ink-soft); margin: 0 0 1rem; }

/* The line under a download button: file size, whether it wants an administrator password, who to
   email about the Mac build.

   --ink-soft, NOT --muted, and that is a contrast fix rather than a preference. Measured at
   13.12px against the CARD surface: --muted lands at 3.19:1 in LIGHT mode (#8a8f9a on #fffdf8),
   under the 4.5:1 AA floor for text this size, while passing at 4.82:1 in dark -- so the failure
   is invisible to anyone working in dark mode, which is how it got written that way. --ink-soft
   measures 6.70:1 light and 8.47:1 dark. Pinned by tests/test_site_contrast.py, which resolves
   both palettes rather than trusting one.

   It also happens to be the sentence that matters most on a page whose whole problem is people
   being nervous about a download. "It never asks you for an administrator password" is the
   reassurance; setting it in the page's faintest grey is the wrong place to save ink. */
.getcard .fine {
  font-size: 0.82rem;
  line-height: 1.55;
  color: var(--ink-soft);
  margin: 0.75rem 0 0;
  max-width: 30rem;
}

/* ---------- footer ---------- */

footer { padding-block: 2.5rem 3.5rem; font-size: 0.86rem; color: var(--muted); }
footer a { color: var(--ink-soft); text-decoration: none; border-bottom: 1px solid var(--line); }
footer a:hover { color: var(--accent); border-color: var(--accent); }
.footer-row { display: flex; flex-wrap: wrap; gap: 0.75rem 1.6rem; align-items: center; }
.disclose { margin-top: 1.4rem; padding-top: 1.1rem; border-top: 1px solid var(--line); max-width: 44rem; line-height: 1.7; }

/* ---------- entrance ---------- */

/* Same `backwards` rule as the hero graphic: the resting state is visible, so content can never
   be stranded at opacity 0 if the animation doesn't play. */
@media (prefers-reduced-motion: no-preference) {
  .rise { animation: rise 0.7s cubic-bezier(0.2,0.7,0.25,1) backwards; }
  .d1 { animation-delay: 0.04s; }
  .d2 { animation-delay: 0.14s; }
  .d3 { animation-delay: 0.26s; }
}
@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
