/* PortifyLead marketing site — direction B.
 *
 * "Dark, but actually designed": the same near-black ground as the product,
 * with structure carrying the design instead of gradients and blur. No Bulma,
 * no framework — a hand-built 12-column grid, flat surfaces, visible hairlines,
 * and the accent used once per viewport, on the action.
 *
 * Every colour, radius and font here resolves through tokens.css, which is
 * generated from packages/design-tokens/tokens.mjs. Add a value there, not
 * here.
 */

/* The two hairlines, the accent's decorative edge and the state colours used
 * to be hand-written here as alphas, pending the reconciliation §13 item 2
 * called for. That is done (2026-09-08) and they come from tokens.css with
 * everything else: the same solid `hairline` and `hairline-strong` the portal,
 * the popup and the console resolve. Layout is what is left here, because it
 * is this page's alone. */
:root {
  --gutter: 72px;
  --measure: 1280px;
}

@media (max-width: 900px) {
  :root { --gutter: 24px; }
}

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

html { color-scheme: dark; }

body {
  margin: 0;
  background: var(--base);
  color: var(--text-primary);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a { color: inherit; text-decoration: none; }

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.035em; text-wrap: pretty; }
p { margin: 0; }

/* Focus. The old site had one designed focus state across roughly nine
 * patterns (§13 item 7). :focus-visible rather than :focus so the ring appears
 * for the keyboard and not on every mouse click. */
:focus { outline: none; }
:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* On focus the ring is the whole indicator, so the field's own edge steps out
   from under it. Two accent-adjacent lines around one control was the doubling
   that made focus read as a heavy border; `transparent` rather than `none` so
   the box does not resize and the control shift by a pixel. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  border-color: transparent;
}

/* Two hover transforms shipped with no reduced-motion block (§13 item 8). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --- Layout ------------------------------------------------------------- */

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

.grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 32px;
}

.span-6 { grid-column: span 6; }

@media (max-width: 900px) {
  .grid { gap: 24px; }
  .span-6 { grid-column: span 12; }
}

/* --- Type --------------------------------------------------------------- */

.eyebrow {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-full);
  background: var(--veil);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* Positive tracking is the one place letterspacing goes out. */
.label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.h1 { font-size: clamp(38px, 5vw, 62px); line-height: 1.03; letter-spacing: -0.04em; max-width: 17em; }
.h2 { font-size: clamp(28px, 3.4vw, 40px); line-height: 1.1; max-width: 20em; }
.lede { font-size: 17px; line-height: 1.62; color: var(--text-secondary); }
.fine { font-size: 13px; color: var(--text-secondary); }

/* --- Buttons ------------------------------------------------------------
 * Three, not the eight classes the old stylesheet carried for three actual
 * behaviours (§13 item 10): a filled primary, an outlined secondary, and the
 * small variant of each. The accent fill appears once per viewport.
 */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* min-height, not height: the plan's CTAs are sentences ("Start 14-day
     Agency trial"), and a fixed height cramps the second line inside the
     border instead of growing the button. */
  min-height: 48px;
  padding: 10px 24px;
  line-height: 1.3;
  /* Balances a two-line CTA instead of leaving "trial" alone on line two. */
  text-wrap: balance;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
}

.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { background: var(--accent-hover); }

.btn--secondary {
  /* Explicit, because a <button> otherwise keeps the browser's grey fill; the
     anchors that used this first never showed it. */
  background: transparent;
  border-color: var(--line-strong);
  color: var(--text-primary);
  font-weight: 500;
}
.btn--secondary:hover { border-color: var(--text-primary); }

.btn--sm { min-height: 36px; padding: 6px 16px; font-size: 13px; }
.btn--block { width: 100%; }

/* --- Nav ---------------------------------------------------------------- */

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-block: 24px;
  border-bottom: 1px solid var(--line);
}

/* The horizontal lockup, to design/design.md §12.4: the wordmark's cap-height
 * matches the solid square, and the gap is 0.3 × the mark's height.
 *
 * Both numbers are derived rather than picked. At a 32px mark the solid square
 * is 38u of 72, so 16.9px; Inter's cap-height is 0.727em, so the wordmark is
 * 16.9 / 0.727 = 23px. The painted mark spans 9u–63u, so 24px, and 0.3 × 24 is
 * a 7.2px gap — but 9u of that (4px) is transparent margin inside the SVG box,
 * so the CSS gap is the remaining 3px. Change the mark size and all three move.
 */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 23px;
  font-weight: 600;
  letter-spacing: -0.045em;
}
.brand__mark { display: block; width: 32px; height: 32px; flex: none; }
.brand__lead { color: var(--accent); }

.nav__links {
  display: flex;
  align-items: center;
  gap: 28px;
  font-size: 14px;
  color: var(--text-secondary);
}
.nav__links a:hover { color: var(--text-primary); }

/* A nav CTA is a label, not a paragraph: without this it stacks one word per
 * line as soon as the row runs short, which is how it ended up 78px wide and
 * 76px tall. `.btn` sets text-wrap: balance for the plan cards' sentence-length
 * CTAs, and that is the right default there and the wrong one here. */
.nav__cta { white-space: nowrap; }

@media (max-width: 700px) {
  .nav { gap: 12px; }
  .nav__links { gap: 16px; }
  .nav__links .nav__hide { display: none; }
  /* The hero's own "Get started free" is the next thing on the page at this
     width, so the nav copy of it is the one thing here worth losing to keep
     Integrations and Pricing reachable. §9 asks for a primary CTA in the nav;
     it is present at every width where it fits without crowding them out. */
  .nav__cta { display: none; }
}

/* --- Sections ----------------------------------------------------------- */

.hero { padding-top: 88px; }
.section { padding-block: 84px; }
/* A tight top, but a full bottom. This follows the hero closely on purpose;
 * it used to have no bottom padding at all and got away with it because the
 * next section's own 84px top padding stood in. A band follows it now, and a
 * band draws a border — which landed exactly on the logo tiles. */
.section--tight { padding-top: 60px; padding-bottom: 84px; }

.panel {
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--surface-1);
}
.panel--accent { border-color: var(--accent-line); }

/* --- Field rows ----------------------------------------------------------
 * Shared by the hero mockup's extension panel. */

.field {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  gap: 20px;
  padding: 15px 22px;
  border-bottom: 1px solid var(--line);
  font-size: 13px;
  line-height: 1.5;
}
.field__name { font-size: 11px; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-secondary); }
@media (max-width: 620px) {
  .field { grid-template-columns: minmax(0, 1fr); gap: 6px; }
}

/* An absent value is marked absent. It is never filled in. */
.tag {
  display: inline-flex;
  align-self: flex-start;
  padding: 3px 8px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-sm);
  font-size: 12px;
  color: var(--text-secondary);
}
.tag--warn {
  border: 1px solid var(--warn-line);
  border-radius: var(--radius-full);
  background: var(--warn-tint);
  color: var(--warn);
  font-size: 11px;
  font-weight: 600;
  padding: 2px 8px;
}

/* --- Cards -------------------------------------------------------------- */

.cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; margin-top: 40px; }

@media (max-width: 900px) { .cards { grid-template-columns: minmax(0, 1fr); } }

.card { display: flex; flex-direction: column; gap: 12px; padding: 26px; }
.card__title { font-size: 19px; font-weight: 600; }
.card__body { font-size: 14px; line-height: 1.62; color: var(--text-primary); }
.card__body em { color: var(--text-primary); font-style: normal; }

/* --- Pricing ------------------------------------------------------------ */

.pricing__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 28px;
  border-bottom: 1px solid var(--line);
}

.plan { display: flex; flex-direction: column; gap: 18px; padding: 28px; }
.plan__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* The plan's name, as the portal has it: a heading at reading size rather than
 * 12px tracked caps. Caps at that size read as a label for the card, and the
 * name of the thing being sold is not a label. */
.plan__name { margin: 0; font-size: 18px; font-weight: 600; letter-spacing: -0.01em; color: var(--text-primary); }

/* What the plan is for, in one line, before the list of what it includes. The
 * site sold three plans without ever saying this. */
.plan__tagline { font-size: 14px; line-height: 1.5; color: var(--text-secondary); margin: 0; }

/* The reason to choose annual, so it is accent rather than muted — the site
 * had it as an afterthought pulled up under the price with a negative margin. */
.plan__saving { font-size: 13px; font-weight: 500; line-height: 1.5; color: var(--accent-text); }
.plan__badge {
  padding: 3px 10px;
  border-radius: var(--radius-full);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 700;
}
.plan__price { display: flex; align-items: baseline; gap: 7px; }
.plan__price { display: flex; align-items: baseline; gap: 4px; }
.plan__amount { font-size: 42px; font-weight: 600; letter-spacing: -0.03em; line-height: 1; }
.plan__cadence { font-size: 14px; color: var(--text-secondary); }
/* Features are the card's content, not its metadata, so they take the primary
 * text role. §3.2 gives `fg-muted` to "labels, meta, secondary copy" — putting
 * a plan's actual substance there is what made these read washed out, and no
 * amount of tuning the neutral fixes a role applied to the wrong thing. */
.plan__features { display: flex; flex-direction: column; gap: 11px; margin: 0; padding: 0; list-style: none; font-size: 14px; line-height: 1.5; color: var(--text-primary); }

/* Checked off, the way the portal does it. A bare line does not say whether it
 * is something you get or something you are told; a tick does. Decorative, so
 * it is a pseudo-element and never reaches a screen reader as a word. */
.plan__features li { display: flex; gap: 8px; }
.plan__features li::before { content: "\2713"; color: var(--accent-text); flex: none; }

/* --- Billing period switch ---------------------------------------------- */

/* Both prices are rendered; this hides the one that is not selected. Keyed off
 * a data attribute so the script decides only which period is current. */
.cards[data-period="monthly"] [data-period="yearly"],
.cards[data-period="yearly"] [data-period="monthly"] { display: none; }

.period {
  display: inline-flex;
  gap: 2px;
  margin-top: 24px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--veil);
}
.period__option {
  border: 0;
  border-radius: var(--radius-md);
  padding: 6px 12px;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  text-transform: capitalize;
  color: var(--text-secondary);
  background: transparent;
  cursor: pointer;
  transition: background 150ms, color 150ms;
}
.period__option:hover { color: var(--text-primary); }
.period__option[aria-pressed="true"] { background: var(--success-tint); color: var(--text-primary); }
.plan .btn { margin-top: auto; }

/* --- Footer ------------------------------------------------------------- */

.footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-block: 32px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  color: var(--text-secondary);
}
.footer__links { display: flex; flex-wrap: wrap; gap: 20px; }
.footer__links a:hover { color: var(--text-primary); }

/* The annual alternative and the "unlimited" disclosure. Both are pricing
 * terms rather than decoration: the launch plan requires the disclosure to
 * appear wherever "unlimited" does, so it is part of the generated block and
 * not something a layout tweak can drop. */
.plan__annual { font-size: 13px; line-height: 1.5; color: var(--text-secondary); margin-top: -6px; }

/* Terms, not selling points: trial behaviour, downgrade behaviour and the
 * "unlimited" disclosure. These stay on the muted role deliberately — they are
 * required to be present and legible, not to compete with the plans. */
.pricing__terms {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 82ch;
  margin: 28px 0 0;
  padding: 0;
  list-style: none;
  font-size: 12px;
  line-height: 1.6;
  color: var(--text-secondary);
}

/* --- Prose --------------------------------------------------------------
 * Section body copy. Deliberately on the primary text role, not `.lede`'s
 * secondary one: design.md §3.2 gives `fg-muted` to "labels, meta, secondary
 * copy", and a section's argument is its content. This is the same ruling
 * that moved the plan features back off the muted role — a role applied to
 * the wrong thing is not fixed by tuning the value.
 */
.prose {
  max-width: 68ch;
  margin-top: 22px;
  font-size: 16px;
  line-height: 1.65;
  color: var(--text-primary);
}
.prose + .prose { margin-top: 16px; }

.section__link {
  display: inline-block;
  margin-top: 22px;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 2px;
  font-size: 14px;
  font-weight: 500;
}
.section__link:hover { border-bottom-color: var(--text-primary); }

/* Two panels rather than three, for the pairs that are genuinely a pair
 * (good fit / not the product). Same gap and collapse as `.cards`. */
.cards--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 900px) { .cards--2 { grid-template-columns: minmax(0, 1fr); } }

/* --- Brief anatomy ------------------------------------------------------
 * A ladder of hairline rows rather than bullets: the list is the section's
 * substance, and the rows give it the same structural weight the rest of the
 * page uses instead of decoration.
 */
.anatomy {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 44px;
  margin: 36px 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}
.anatomy li {
  padding: 15px 2px;
  border-bottom: 1px solid var(--line);
  font-size: 15px;
  line-height: 1.5;
}
@media (max-width: 760px) { .anatomy { grid-template-columns: minmax(0, 1fr); } }

/* --- FAQ ----------------------------------------------------------------
 * <details>, so it works with no JavaScript and stays keyboard-operable and
 * announced as a disclosure without any ARIA of our own. The default marker is
 * replaced rather than hidden: a disclosure with no affordance reads as a
 * heading that mysteriously does nothing.
 */
.faq { margin-top: 40px; border-top: 1px solid var(--line); }
.faq__item { border-bottom: 1px solid var(--line); }
.faq__q {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding: 20px 2px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q::after {
  content: "+";
  flex: none;
  font-size: 18px;
  font-weight: 500;
  line-height: 1;
  color: var(--text-secondary);
}
.faq__item[open] .faq__q::after { content: "\2212"; }
.faq__item[open] .faq__q { padding-bottom: 12px; }
.faq__a {
  max-width: 78ch;
  margin: 0;
  padding: 0 2px 22px;
  font-size: 14px;
  line-height: 1.65;
  color: var(--text-primary);
}

/* --- Closing CTA --------------------------------------------------------
 * Plain panel, not the accent border: the accent appears once per viewport
 * and here it belongs on the button, which is the action.
 */
.cta-band {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  padding: 52px;
  margin-top: 8px;
}
/* The band offers both doors — sign up, or install first — so its actions are
   a wrapping row rather than the single button this used to hold. */
.cta-band__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.cta-band .h2 { max-width: 16em; }
.cta-band .prose { margin-top: 0; }
@media (max-width: 620px) { .cta-band { padding: 32px 24px; } }

/* --- Document pages -----------------------------------------------------
 * Help, privacy and terms. One narrow measure, generous leading, and the
 * same hairline vocabulary as the rest of the site — these are read, not
 * scanned, and they are the pages a buyer checks before trusting the product
 * with their CRM.
 *
 * Body copy takes the primary text role (§3.2): a policy's clauses are its
 * content. Only the "last updated" stamp is meta.
 */
.doc { max-width: 78ch; padding-block: 64px 96px; }
.doc h1 { font-size: clamp(32px, 4vw, 44px); line-height: 1.08; }
.doc__meta { display: block; margin-top: 14px; font-size: 13px; color: var(--text-secondary); }
.doc h2 {
  margin-top: 46px;
  margin-bottom: 14px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  font-size: 21px;
  line-height: 1.25;
}
.doc h1 + p, .doc__meta + p { margin-top: 24px; }
.doc p { margin-bottom: 15px; font-size: 15px; line-height: 1.7; color: var(--text-primary); }
.doc ul, .doc ol { margin: 0 0 18px; padding-left: 22px; font-size: 15px; line-height: 1.7; }
.doc li { margin-bottom: 9px; }
.doc li::marker { color: var(--text-secondary); }
.doc a { border-bottom: 1px solid var(--line-strong); }
.doc a:hover { border-bottom-color: var(--text-primary); }
.doc strong { font-weight: 600; }
.doc code {
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  font-size: 13px;
}
.doc figure { margin: 22px 0; }
.doc figure img { display: block; width: 100%; border: 1px solid var(--line); border-radius: var(--radius-md); }
.doc figcaption { margin-top: 10px; font-size: 13px; color: var(--text-secondary); }

/* Help's questions are disclosures, and they get the FAQ's exact visual
 * language so the two pages do not invent two answers to the same problem. */
.doc details { border-bottom: 1px solid var(--line); }
.doc h2 + details { border-top: 1px solid var(--line); }
.doc summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding: 18px 2px;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.doc summary::-webkit-details-marker { display: none; }
.doc summary::after { content: "+"; flex: none; font-size: 18px; font-weight: 500; line-height: 1; color: var(--text-secondary); }
.doc details[open] summary::after { content: "\2212"; }
.doc details[open] summary { padding-bottom: 10px; }
.doc details > *:not(summary) { padding-inline: 2px; }
.doc details > p:last-child { margin-bottom: 20px; }

/* --- Blog ---------------------------------------------------------------
 * On the site's dark ground like every other page (design/design.md §1).
 *
 * A post is a .doc: the Help and Privacy measure and hairline headings, with
 * the quick answer, the proof line and the author added. */
.post { padding-bottom: 56px; }
.post__trail { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 22px; }
.doc .post__trail a { border-bottom: 0; }
.post__trail a:hover { color: var(--text-primary); }
.answer { display: flex; flex-direction: column; gap: 12px; margin-top: 32px; padding: 26px 28px; }
.doc .answer p { margin: 0; font-size: 17px; line-height: 1.62; }
.doc .post__proof { margin-top: 18px; font-size: 14px; line-height: 1.6; color: var(--text-secondary); }

/* The close sits outside the .doc so its button and links keep their own
 * edges rather than the article's underline. */
.post__end { display: flex; flex-direction: column; gap: 48px; max-width: 78ch; padding-bottom: 96px; }
.cta-band__actions .section__link { margin-top: 0; }
/* At the article's measure the page-width heading runs to three lines. */
.post__end .cta-band .h2 { font-size: clamp(24px, 3vw, 32px); }
.author { display: flex; align-items: center; gap: 16px; padding-top: 28px; border-top: 1px solid var(--line); }
.author__photo {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 56px; height: 56px; object-fit: cover;
  border: 1px solid var(--line); border-radius: var(--radius-full); background: var(--surface-3);
  font-size: 17px; font-weight: 600; color: var(--text-secondary);
}
.author__text { display: flex; flex-direction: column; gap: 2px; }
.author__name { font-size: 16px; font-weight: 600; }
.author__role { font-size: 14px; line-height: 1.5; color: var(--text-secondary); }
/* Posts use the page's content area (Marius, 16 September 2026): the article
 * and its close take the .wrap measure like every other page, never the whole
 * browser, and only running text keeps a narrower measure, at 100ch rather than
 * the Help pages' 78. Help and Privacy stay on the plain .doc. */
.doc.post { max-width: var(--measure); }
.doc.post h1 { max-width: 24em; }
.doc.post p, .doc.post ul, .doc.post ol { max-width: 100ch; }
.post__end.wrap { max-width: var(--measure); }

/* A post section: its own hairline and a numbered eyebrow, so the H2s read as
 * chapters rather than a run of body text. Posts without the wrapper keep the
 * .doc heading rule. */
.post__section { margin-top: 72px; padding-top: 36px; border-top: 1px solid var(--line); }
.doc .post__section h2 { margin: 0 0 18px; padding: 0; border: 0; font-size: clamp(24px, 2.6vw, 30px); max-width: 30em; }
.post__section .cards { margin-top: 24px; }
.doc .post__section .card__body { margin: 0; max-width: none; }
.doc .post__section h3 { margin: 48px 0 12px; font-size: 20px; line-height: 1.3; letter-spacing: -0.02em; }
.doc .post figure.post__figure { margin: 28px 0; }
.post__toc { margin-top: 32px; padding-top: 22px; border-top: 1px solid var(--line); }
.post__toc .label { display: block; margin-bottom: 10px; }
.doc .post__toc ol { margin-bottom: 0; }
.post__figure img { display: block; width: 100%; height: auto; border-radius: var(--radius-md); }
/* In a post's table the first column names the row, so it reads as a label,
 * not muted text, and takes only the width it needs. */
.post .example td:first-child { width: auto; color: var(--text-primary); font-weight: 600; }

.related__head { margin-bottom: 12px; }
.related { margin: 0; padding: 0; list-style: none; }
.related a,
.topic__posts a { display: flex; align-items: baseline; justify-content: space-between; gap: 24px; }
.related a::after,
.topic__posts a::after { content: "\2192"; flex: none; font-size: 13px; color: var(--text-secondary); }
.related a:hover::after,
.topic__posts a:hover::after { color: var(--text-primary); }
.related a { padding: 14px 2px; border-top: 1px solid var(--line); font-size: 15px; }
.related li:last-child a { border-bottom: 1px solid var(--line); }

/* The index groups posts under the keyword map's hubs, so every hub gets a
 * link from /blog rather than sliding down a date-ordered list. */
.topic { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 32px; padding-block: 48px; border-top: 1px solid var(--line); }
.topic:last-child { border-bottom: 1px solid var(--line); }
.topic__head { grid-column: span 4; display: flex; flex-direction: column; gap: 12px; }
.topic__name { font-size: 28px; line-height: 1.15; }
.topic__blurb { max-width: 32ch; font-size: 14px; line-height: 1.6; color: var(--text-secondary); }
.topic__posts { grid-column: span 8; margin: 0; padding: 0; list-style: none; }
.topic__posts a { padding: 16px 2px; border-bottom: 1px solid var(--line); font-size: 17px; font-weight: 500; letter-spacing: -0.01em; }
.topic__posts li:first-child a { padding-top: 0; }
@media (max-width: 900px) {
  .topic { gap: 20px; }
  .topic__head, .topic__posts { grid-column: span 12; }
}

/* --- Integrations directory --------------------------------------------- */

.cards--auto { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

.integration { display: flex; flex-direction: column; gap: 10px; padding: 24px; }
.integration__name { font-size: 17px; font-weight: 600; }
.integration__body { font-size: 14px; line-height: 1.6; color: var(--text-primary); }
.integration__go { margin-top: auto; padding-top: 12px; font-size: 13px; font-weight: 500; color: var(--text-secondary); }
.integration:hover .integration__go { color: var(--text-primary); }

/* --- The next tier: built, not available -------------------------------
 * Deliberately unlike .integration. A cell that looks like a destination
 * card reads as available however it is labelled, so this one is dashed,
 * unlinked, has no hover, and dims its mark to two thirds the size. The
 * chip beside the heading carries the state; the shape carries it too.
 */
.soon {
  list-style: none; padding: 0; margin: 20px 0 0;
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px;
}
.soon__cell {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 20px 10px;
  border: 1px dashed var(--line); border-radius: 14px;
}
.soon__glyph { height: 30px; width: auto; object-fit: contain; opacity: .62; }
/* A single-colour glyph has no branded mark to show, so it is tinted to the
   surrounding text the way the product's own surfaces tint it. */
.soon__glyph--tint {
  display: block; width: 30px; background: currentColor; color: var(--text-secondary);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
}
.soon__name { font-size: 12px; color: var(--text-secondary); text-align: center; }
.chip {
  display: inline-block; font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-secondary); border: 1px solid var(--line); border-radius: 999px; padding: 3px 9px;
}
@media (max-width: 760px) { .soon { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* --- Pricing summary on the homepage ------------------------------------
 * The full comparison lives on /pricing. This is the "enough to choose the
 * next step" version §9 asks the homepage to carry.
 */
.summary { display: flex; flex-direction: column; align-items: flex-start; gap: 14px; padding: 40px; }

/* --- Illustrative example table ----------------------------------------- */

.example { margin: 24px 0; overflow-x: auto; }
.example table { width: 100%; border-collapse: collapse; font-size: 14px; }
.example th, .example td { padding: 12px 14px; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top; }
.example th { font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-secondary); }
.example td:first-child { width: 34%; color: var(--text-secondary); }
.quote {
  margin: 22px 0;
  padding: 18px 22px;
  border-left: 2px solid var(--accent-line);
  background: var(--veil);
  font-size: 15px;
  line-height: 1.65;
}

/* --- The product hero ---------------------------------------------------
 * Copy on the left, the product working on the right, above the fold. Both
 * columns align to the top: the mockup keeps a fixed height across its three
 * screens so nothing on the page moves as the demo runs, and the step strip
 * sits above the stage where it can never be displaced by the panel below.
 *
 * The mockup is scoped .theme-light. The extension is light-native, so this
 * is not a stylistic choice: it is the product as it renders, and on this
 * dark page it is also what makes the mockup unmistakably the thing to look
 * at. The roles under .theme-light come from the same generated sheet as the
 * page, so the mockup is coloured from the extension's own table.
 *
 * Everything the demo does visually is keyed off data-step on .mock;
 * assets/js/demo.js only decides which step is current.
 */
.span-5 { grid-column: span 5; }
.span-7 { grid-column: span 7; }
@media (max-width: 900px) { .span-5, .span-7 { grid-column: span 12; } }

.hero--product { padding-top: 64px; }
.hero__grid { align-items: start; row-gap: 40px; }
.hero__copy { display: flex; flex-direction: column; align-items: flex-start; gap: 18px; padding-top: 8px; }
.hero__copy .lede { margin-top: 8px; }
.hero__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 6px; }
.hero__copy .fine + .fine { margin-top: -10px; }

/* The frame is a browser window: a slim bar with the LinkedIn URL and the
 * PortifyLead mark pinned as an installed extension, then the page. What has
 * to be legible at a glance is the layering — a LinkedIn page underneath,
 * the extension's panel floating over its right-hand side — so the page and
 * the panel are drawn as two different things: the page is flat and sunken,
 * the panel carries its own header, a stronger edge and a shadow. */
.mock {
  display: flex; flex-direction: column;
  border: 1px solid var(--line-strong);
  border-radius: 14px;
  background: var(--base);
  color: var(--text-primary);
  overflow: hidden;
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55);
}
.mock__bar { display: flex; align-items: center; gap: 12px; padding: 8px 12px; background: var(--surface-1); border-bottom: 1px solid var(--line); }
.bar__dots { display: inline-flex; gap: 5px; }
.bar__dots i { width: 9px; height: 9px; border-radius: var(--radius-full); background: var(--line); }
.bar__url {
  flex: 1; min-width: 0; padding: 4px 12px; border-radius: var(--radius-full);
  background: var(--veil); font-size: 11px; color: var(--text-secondary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bar__ext { display: inline-flex; width: 18px; height: 18px; color: var(--text-primary); }
.bar__ext svg { width: 100%; height: 100%; }

/* The page: the profile card is the whole width, the panel floats over its
 * right half. On narrow screens the two stack instead. */
.mock__page { position: relative; height: 552px; padding: 16px; }
.mock__profile {
  position: relative; height: 100%;
  padding: 0 58% 16px 18px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface-1);
  font-size: 13px; overflow: hidden;
}
.profile__cover { height: 56px; margin: 0 -18px 0 -18px; padding-right: 0; border-radius: 10px 10px 0 0; background: var(--line); }
.mock__profile .profile__cover { margin-right: calc(-58% - 18px); }
.profile__avatar { display: block; width: 68px; height: 68px; margin-top: -32px; border: 3px solid var(--surface-1); border-radius: var(--radius-full); background: var(--surface-1); }
.profile__name { margin-top: 8px; font-size: 16px; font-weight: 600; letter-spacing: -0.02em; }
.profile__headline { margin-top: 2px; line-height: 1.45; }
.profile__meta { margin-top: 5px; font-size: 12px; color: var(--text-secondary); }
.profile__actions { display: flex; gap: 8px; margin-top: 12px; }
.profile__btn { padding: 4px 12px; border: 1px solid var(--line-strong); border-radius: var(--radius-full); font-size: 12px; font-weight: 600; }
.profile__btn--solid { background: var(--text-primary); color: var(--surface-1); border-color: var(--text-primary); }
.profile__thread { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
.thread__label { display: block; margin-bottom: 6px; font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-secondary); }
.thread__msg { display: block; padding: 9px 11px; border-radius: 0 10px 10px 10px; background: var(--veil); font-size: 12px; line-height: 1.5; }
.m-src { border-radius: 3px; }

/* The panel: the extension, sitting on the page. Its own header, its own
 * step control (the extension's segmented progress), its own edge and shadow. */
.mock__panel {
  position: absolute; top: 28px; right: 28px; bottom: 28px; width: 52%;
  display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--line-strong); border-radius: 12px; background: var(--surface-1);
  box-shadow: 0 16px 40px rgba(20, 22, 26, 0.22), 0 2px 6px rgba(20, 22, 26, 0.10);
  overflow: hidden;
}
.panel__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--line); }
.panel__brand { font-size: 13px; font-weight: 600; letter-spacing: -0.04em; }
.panel__brand .brand__lead { color: var(--accent-text); }
.panel__dest { padding: 3px 9px; border: 1px solid var(--line); border-radius: var(--radius-full); background: var(--veil); font-size: 11px; font-weight: 600; color: var(--text-secondary); }

.mock__steps {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 3px;
  margin: 8px; padding: 3px; list-style: none;
  border-radius: 9px; background: var(--veil);
}
.mock__step {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px; width: 100%;
  padding: 6px 4px; border: 0; border-radius: 7px; background: none;
  font: inherit; font-size: 11px; font-weight: 600; color: var(--text-faint); cursor: pointer; white-space: nowrap;
  transition: background-color 160ms ease, color 160ms ease, box-shadow 160ms ease;
}
.mock__step:hover { color: var(--text-secondary); }
.mock__num { color: inherit; }
.mock__step[aria-current="step"] { background: var(--surface-1); color: var(--accent-text); box-shadow: 0 1px 2px rgba(20, 22, 26, 0.10); }

.panel__body { position: relative; flex: 1; min-height: 0; }
.screen { position: absolute; inset: 0; display: none; flex-direction: column; }
.screen__title { display: flex; align-items: center; gap: 10px; padding: 8px 14px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); font-size: 13px; font-weight: 600; }
.pill { padding: 2px 8px; border-radius: var(--radius-full); background: var(--success-tint); color: var(--success); font-size: 11px; font-weight: 600; }
.photo-row { display: flex; align-items: center; gap: 12px; padding: 9px 14px; border-bottom: 1px solid var(--line); }
.photo-row__img { width: 36px; height: 36px; border-radius: var(--radius-full); flex: none; }
.photo-row__text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.photo-row__name { font-size: 13px; font-weight: 600; }
.panel__fields { margin: 0; }
.panel__fields .field { grid-template-columns: 96px minmax(0, 1fr); gap: 10px; padding: 8px 14px; }
.panel__fields dd { overflow-wrap: anywhere; }
.infer { display: inline-block; margin-left: 6px; padding: 1px 7px; border: 1px solid var(--warn-line); border-radius: var(--radius-full); background: var(--warn-tint); color: var(--warn); font-size: 10px; font-weight: 600; vertical-align: 1px; }
.saved { padding: 11px 14px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--success-tint); font-size: 13px; line-height: 1.5; }
.saved__ok { font-weight: 600; color: var(--success); }
.fu__label { padding: 12px 14px 6px; font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-secondary); }
.fu { margin: 0; padding: 0; list-style: none; }
.fu li { display: grid; grid-template-columns: 64px minmax(0, 1fr); gap: 12px; padding: 8px 14px; border-top: 1px solid var(--line); font-size: 13px; }
.fu__k { font-weight: 600; }
.panel__foot { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin-top: auto; padding: 10px 14px; border-top: 1px solid var(--line); }

@media (max-width: 620px) {
  .mock__page { height: auto; }
  .mock__profile { height: auto; padding-right: 18px; }
  .mock__profile .profile__cover { margin-right: -18px; }
  .mock__panel { position: static; width: auto; margin-top: 12px; }
  .panel__body { min-height: 470px; }
}

/* Which screen is up. */
.mock[data-step="1"] .screen--1, .mock[data-step="2"] .screen--2, .mock[data-step="3"] .screen--3 { display: flex; }

/* Rows arrive in order on whichever screen is up; on the profile screen the
 * source values light up on the profile as they are captured. */
.mock .m-field { animation: field-in 380ms ease both; }
.mock .m-field[data-i="1"] { animation-delay: 150ms; }
.mock .m-field[data-i="2"] { animation-delay: 450ms; }
.mock .m-field[data-i="3"] { animation-delay: 750ms; }
.mock .m-field[data-i="4"] { animation-delay: 1050ms; }
.mock .m-field[data-i="5"] { animation-delay: 1350ms; }
.mock[data-step="1"] .m-src { animation: src-lit 1100ms ease both; }
.mock[data-step="1"] .m-src[data-src="name"]     { animation-delay: 100ms; }
.mock[data-step="1"] .m-src[data-src="title"]    { animation-delay: 400ms; }
.mock[data-step="1"] .m-src[data-src="company"]  { animation-delay: 700ms; }
.mock[data-step="1"] .m-src[data-src="location"] { animation-delay: 1000ms; }
/* On the brief screen it is the thread that lights up: that is the source. */
.mock[data-step="2"] .thread__msg { animation: src-lit 1400ms ease both; animation-delay: 150ms; }

@keyframes field-in { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
@keyframes src-lit {
  0%   { box-shadow: 0 0 0 0 transparent; }
  25%  { box-shadow: 0 0 0 3px var(--accent-line); }
  100% { box-shadow: 0 0 0 3px transparent; }
}

/* Reduced motion: every state still renders, nothing animates in, and
 * demo.js does not auto-advance — the step strip still works by click. */
@media (prefers-reduced-motion: reduce) {
  .mock .m-field, .mock .m-src, .mock .thread__msg { animation: none !important; opacity: 1; transform: none; }
}

/* --- Destination logos ---------------------------------------------------
 * The marks are each vendor's trademark, shown to identify what PortifyLead
 * saves to. They keep their own brand colours rather than being tinted to the
 * page: a recoloured logo is a misused logo, and every vendor's guidelines say
 * so. A destination with no licensed file renders the monogram the product
 * itself falls back to — never a mark drawn here.
 */
.logos__head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 12px 24px; margin-bottom: 22px;
}
.logos {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 16px;
  margin: 0; padding: 0; list-style: none;
}
.logo-tile > a {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
  padding: 34px 16px 28px;
  border: 1px solid var(--line); border-radius: 14px; background: var(--surface-1);
  transition: border-color 140ms ease, background-color 140ms ease;
}
.logo-tile > a:hover { border-color: var(--line-strong); background: var(--surface-2); }
/* Full colour, and the reasoning is worth keeping because the opposite was
 * tried first.
 *
 * A single white set was the obvious call on this dark, accent-once page — and
 * it is what the contrast numbers wanted. It does not survive contact with the
 * artwork. Flattening to white (`brightness(0) invert(1)`) keeps only a mark's
 * alpha, and two of these five carry their identity in colour: Google Sheets
 * knocks its grid and folded corner out of the green in white, so the whole
 * symbol collapses to a plain rounded rectangle, and Zoho's four coloured
 * shapes become four identical blobs. Their official reversed variants have the
 * same problem for the same reason.
 *
 * So the marks render as each vendor drew them. The tiles stay dark, which is
 * what keeps this from becoming the bright band that would compete with the
 * hero mockup above — only the glyphs carry colour, not the surface.
 */
.logo-tile__glyph { display: block; width: auto; height: 64px; max-width: 132px; object-fit: contain; }
.logo-tile__name { font-size: 14px; font-weight: 600; text-align: center; }

/* The monogram, matching IntegrationLogo.tsx's fallback. */
.logo__monogram {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  border-radius: 8px; background: var(--surface-3);
  font-weight: 700; color: var(--text-secondary);
}
.logo-tile__glyph.logo__monogram { width: 64px; font-size: 27px; border-radius: 14px; }
.integration__glyph { display: block; width: auto; height: 44px; max-width: 96px; object-fit: contain; }
.integration__glyph.logo__monogram { width: 44px; font-size: 19px; border-radius: 10px; }
.hero__glyph { display: block; width: auto; height: 72px; max-width: 168px; margin-bottom: 22px; object-fit: contain; }
.hero__glyph.logo__monogram { width: 72px; height: 72px; font-size: 31px; border-radius: 16px; }

@media (max-width: 900px) { .logos { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 520px) { .logos { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* --- Sections with a schematic beside them -------------------------------
 * The prose sections were a wall of text. Each now pairs with a drawing of
 * the thing it describes, alternating side so the page does not fall into a
 * single column of left-aligned blocks. The art is decorative — every one is
 * aria-hidden, and the heading and prose carry the meaning on their own.
 */
/* Top-aligned, not centred: a short paragraph beside a tall drawing floats to
 * the middle and reads as unmoored. Aligning both to the top gives every
 * section the same starting line down the page. */
.split { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 40px; align-items: start; }
.split__text { grid-column: span 6; }
.split__art { grid-column: span 6; }
.split--flip .split__text { grid-column: 7 / span 6; grid-row: 1; }
.split--flip .split__art { grid-column: 1 / span 6; grid-row: 1; }
.split .prose { margin-top: 18px; }
.split .prose:first-child { margin-top: 0; }

.art {
  display: block; width: 100%; height: auto;
  padding: 22px; border: 1px solid var(--line); border-radius: 16px; background: var(--surface-1);
}

@media (max-width: 900px) {
  .split { gap: 28px; }
  .split__text, .split__art,
  .split--flip .split__text, .split--flip .split__art { grid-column: span 12; grid-row: auto; }
  .split__art { order: 2; }
}

/* --- Section rhythm ------------------------------------------------------
 * The page below the hero was one ground, one heading size and one 84px
 * interval from the logo strip to the footer. Every section now announces
 * itself with an eyebrow, and two of them sit on a raised band so the page
 * alternates instead of running flat. No new colour: the band is the surface
 * already in the palette, and the eyebrow is the label role already in use.
 */
.section__eyebrow { display: block; margin-bottom: 14px; }

/* A full-bleed raised band. A wrapper rather than a 100vw pseudo-element:
 * 100vw includes the scrollbar and would push the page sideways. */
.band {
  background: var(--surface-1);
  border-block: 1px solid var(--line);
}
.band + .band { border-top: 0; }

/* The one place type carries a section on its own. */
.statement { font-size: clamp(30px, 3.9vw, 50px); max-width: 15em; }
.prose--lead { max-width: 58ch; font-size: 18px; line-height: 1.6; }

/* Step numerals, not a small label: the workflow row is the page's most
 * structural moment and the numbers should do the counting. */
.card__num {
  font-size: 30px;
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.card:hover .card__num { color: var(--text-primary); }

/* --- The AI sparkle ------------------------------------------------------
 * A colour the design system did not have. design.md commits to one accent,
 * used once per viewport, and the extension's `.ai-wash` was built from the
 * accent tints specifically so AI-marked content introduced no new hue. The
 * only multi-hue gradient anywhere in the brand work is a *counter-example* —
 * the "mark on a busy background, not allowed" tile in the logo canvas.
 *
 * It is here because Marius asked for it, and because the four-point sparkle
 * in a gradient is now the cross-product convention for "this was generated"
 * — the thing a reader already recognises. It is scoped tightly: the AI Sales
 * Brief section and nothing else, so it reads as a signifier rather than
 * decoration, and it starts on the brand accent so it is anchored to the
 * palette rather than floating free of it.
 *
 * If it spreads beyond AI-generated content it stops meaning anything. Keep
 * it to the brief.
 */
:root {
  --ai-gradient: linear-gradient(120deg, #00e676 0%, #22d3ee 48%, #a78bfa 100%);
}

.ai-star {
  display: inline-block;
  width: 0.78em;
  height: 0.78em;
  margin-right: 0.3em;
  vertical-align: -0.02em;
  flex: none;
}

/* --- Destination specifics ----------------------------------------------
 * A key/value ladder rather than cards: these are four answers to four
 * questions, and they read as a spec sheet because that is what they are.
 */
.spec { margin: 32px 0 0; border-top: 1px solid var(--line); }
.spec__row {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: 12px 32px;
  padding: 20px 2px;
  border-bottom: 1px solid var(--line);
}
.spec__key { font-size: 12px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-secondary); }
.spec__val { margin: 0; font-size: 15px; line-height: 1.65; color: var(--text-primary); }
@media (max-width: 760px) { .spec__row { grid-template-columns: minmax(0, 1fr); gap: 6px; } }

/* --- Analytics consent --------------------------------------------------
 * A raised panel in the corner rather than a full-width bar: it asks one
 * question and should not cover the hero while it does. Accept and Decline are
 * the same button, because a choice nudged by colour is not a free one, and
 * the accent stays with the page's own action.
 */
.consent {
  position: fixed;
  left: var(--gutter);
  bottom: 24px;
  z-index: 100;
  display: grid;
  gap: 14px;
  width: min(440px, calc(100vw - 2 * var(--gutter)));
  padding: 18px 20px;
  background: var(--surface-1);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-ambient);
}
.consent[hidden] { display: none; }
.consent__text { font-size: 14px; line-height: 1.6; color: var(--text-secondary); }
.consent__text a { color: var(--text-primary); text-decoration: underline; text-underline-offset: 3px; }
.consent__actions { display: flex; gap: 10px; }
@media (max-width: 560px) {
  .consent { left: 16px; right: 16px; bottom: 16px; width: auto; }
  .consent__actions .btn { flex: 1; }
}

/* The footer's way back to the consent question: a button, since it opens
 * something on this page, dressed as the links beside it. */
.footer__button {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.footer__button:hover { color: var(--text-primary); }
