/* ===========================================================================
   LexHost public site — the whole stylesheet.
   ---------------------------------------------------------------------------
   This site used to be seven React pages rendered with MUI. It is now plain
   HTML, so the look MUI used to compute at runtime is written out here once:
   the design tokens, the typography scale, and the handful of components the
   marketing pages actually used (button, card, chip, avatar, table, accordion,
   text field).

   THE TOKENS ARE THE CONTRACT. Every value under :root is copied from
   apps/frontend/src/styles/tokens.ts, and the type scale below is MUI's own
   default scale with this project's weight overrides folded in. Change a colour
   in tokens.ts and it has to be changed here too, or the app and the marketing
   site drift apart — that is the price of not shipping a framework.

   Breakpoints are MUI's: sm 600, md 900, lg 1200.
   Spacing is MUI's: one unit = 8px, so `py: {xs: 6, md: 10}` is 48px / 80px.
   =========================================================================== */

:root {
  /* --- Colour (tokens.ts) ------------------------------------------------ */
  --primary: #138b3f;
  --primary-light: #3fbd6d;
  --primary-dark: #126d34;
  --primary-contrast: #ffffff;
  --secondary: #0f172a;

  --bg: #ffffff;
  --bg-paper: #ffffff;
  --bg-subtle: #f8fafc;

  --text: #0f172a;
  --text-secondary: #475569;
  --divider: #e2e8f0;
  --grey-200: #eeeeee;
  --grey-400: #bdbdbd;
  --grey-500: #9e9e9e;

  --tint-soft: rgba(19, 139, 63, 0.12);
  --tint-softer: rgba(19, 139, 63, 0.1);
  --tint-veil: rgba(19, 139, 63, 0.5);
  --primary-outline: rgba(19, 139, 63, 0.5);
  --primary-hover: rgba(19, 139, 63, 0.04);
  --action-hover: rgba(0, 0, 0, 0.04);
  --action-selected: rgba(0, 0, 0, 0.08);

  --footer-text: #cbd5e1;
  --footer-muted: #94a3b8;
  --footer-heading: #ffffff;
  --footer-border: rgba(255, 255, 255, 0.1);

  --error: #d32f2f;
  --error-bg: #fdeded;
  --success-bg: #edf7ed;
  --warning-bg: #fff4e5;
  --warning-text: #663c00;
  --success-text: #1e4620;
  --error-text: #5f2120;
  /* The set had no INFO pair, because nothing on the site had said anything
     merely informative until the SEO dialog's "where to make the change" note.
     MUI's own info palette, so it matches the same note in the dashboard. */
  --info-bg: #e5f6fd;
  --info-text: #014361;

  /* --- Shape & type ------------------------------------------------------ */
  --radius: 12px;
  --radius-card: 16px;
  --radius-btn: 10px;
  --font:
    "Inter", "Helvetica Neue", Arial, -apple-system, BlinkMacSystemFont,
    sans-serif;

  /* MUI elevation 2 and 6 — the only two the marketing pages ever used. */
  --shadow-2:
    0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14),
    0 1px 5px 0 rgba(0, 0, 0, 0.12);
  --shadow-6:
    0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14),
    0 1px 18px 0 rgba(0, 0, 0, 0.12);

  --gradient-brand: linear-gradient(90deg, #138b3f, #3fbd6d);
  --gradient-hero-glow: radial-gradient(
    1200px 500px at 50% -10%,
    rgba(19, 139, 63, 0.1),
    rgba(255, 255, 255, 0)
  );

  /* Sticky header height, so anchored sections clear it. */
  --header-h: 64px;
}

/* --- Reset ---------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

/* Someone who asked their OS not to animate should not be flung down a page. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* The footer is pinned to the bottom on a short page. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

main {
  flex: 1;
}

img {
  max-width: 100%;
  border-style: none;
}

a {
  color: inherit;
}

h1,
h2,
h3,
h4,
h5,
h6,
p,
figure {
  margin: 0;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

button {
  font: inherit;
  color: inherit;
}

/* Keyboard focus stays visible everywhere; a mouse click does not draw one. */
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* `hidden` is how site.js hides things, and the browser's own rule for it is a
   plain `display: none` that ANY author `display` beats — `.card--fill` sets
   `display: flex`, so a filtered-out blog card stayed on screen. This is the
   one place !important is the right tool: it makes the attribute mean what it
   says everywhere. */
[hidden] {
  display: none !important;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --- Typography ----------------------------------------------------------
   MUI's default scale with this theme's weight and letter-spacing overrides.
   Class names match the `variant` they replace, so a page reads the same as
   the JSX it came from.                                                     */

.h2 {
  font-size: 3.75rem;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.h3 {
  font-size: 3rem;
  font-weight: 700;
  line-height: 1.167;
  letter-spacing: -0.01em;
}

.h4 {
  font-size: 2.125rem;
  font-weight: 700;
  line-height: 1.235;
}

.h5 {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.334;
}

.h6 {
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.6;
}

.subtitle1 {
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.75;
}

.subtitle2 {
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.57;
}

.body1 {
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.5;
}

.body2 {
  font-size: 0.875rem;
  font-weight: 400;
  line-height: 1.43;
}

.caption {
  font-size: 0.75rem;
  font-weight: 400;
  line-height: 1.66;
}

.overline {
  display: block;
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 2.66;
  letter-spacing: 0.08333em;
  text-transform: uppercase;
}

/* The h2/h3 on a phone are set per page in the markup that needs it; these are
   the two the whole site shares, because a 60px headline does not fit 360px. */
@media (max-width: 899px) {
  .h2 {
    font-size: 2.25rem;
  }
  .h3 {
    font-size: 2rem;
  }
  .h4 {
    font-size: 1.75rem;
  }
}

.muted {
  color: var(--text-secondary);
}
.primary-text {
  color: var(--primary);
}
.bold {
  font-weight: 700;
}
.semibold {
  font-weight: 600;
}
.center {
  text-align: center;
}

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

.container {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding-inline: 16px;
}

.container--md {
  max-width: 900px;
}

@media (min-width: 600px) {
  .container {
    padding-inline: 24px;
  }
}

/* py: {xs: 6, md: 10} — the section rhythm the whole site uses. */
.section {
  padding-block: 48px;
}

.section--tight {
  padding-block: 32px;
}

@media (min-width: 900px) {
  .section {
    padding-block: 80px;
  }
  .section--tight {
    padding-block: 48px;
  }
}

.band {
  background: var(--bg-subtle);
  border-top: 1px solid var(--divider);
}

.band--bottom {
  border-bottom: 1px solid var(--divider);
}

.band--plain {
  background: var(--bg);
}

/* An anchored section must clear the sticky header, or its heading lands
   underneath it. Same 80 the React pages used. */
[id] {
  scroll-margin-top: 80px;
}

.stack {
  display: flex;
  flex-direction: column;
}

.gap-1 {
  gap: 8px;
}
.gap-15 {
  gap: 12px;
}
.gap-2 {
  gap: 16px;
}
.gap-3 {
  gap: 24px;
}
.gap-4 {
  gap: 32px;
}

/* The centred eyebrow + heading + lede that opens most sections. */
.section-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
  margin-bottom: 40px;
}

.section-head .lede {
  max-width: 640px;
}

.section-head--left {
  align-items: flex-start;
  text-align: left;
}

/*
 * Every public page's hero is the same height: 80vh.
 *
 * `min-height`, not `height`, so a hero whose copy is taller than the viewport
 * — the products one on a short laptop, any of them at a large text size —
 * grows instead of spilling out of its own section. On the pages that carry
 * one, the parallax layer is absolutely positioned and sizes off the section,
 * so it follows this without anything further.
 *
 * Centring is what makes the extra height usable: the sections keep the padding
 * they had, which is now a minimum rather than the thing setting the height, and
 * the container sits in the middle of whatever is left. `.container` is already
 * `width: 100%` with `margin: 0 auto`, so it still centres horizontally as a
 * flex item.
 *
 * `vh` ignores the retracting toolbar on mobile browsers, so a phone hero is
 * about a tenth taller than the visible viewport on first paint; `dvh` is the
 * unit that tracks it, if that turns out to matter.
 */
.hero,
.product-hero,
.pricing-hero,
.blog-hero,
.sales-hero,
.contact-hero {
  min-height: 80vh;
  display: flex;
  align-items: center;
}

/* --- Grid ----------------------------------------------------------------
   `--cols` is the desktop column count; everything collapses to one column on
   a phone and (where asked) two on a tablet.                                */

.grid {
  display: grid;
  gap: 24px;
  grid-template-columns: 1fr;
}

.grid--tight {
  gap: 16px;
}

@media (min-width: 600px) {
  .grid--sm2 {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .grid--2 {
    grid-template-columns: repeat(2, 1fr);
  }
  .grid--3 {
    grid-template-columns: repeat(3, 1fr);
  }
  .grid--4 {
    grid-template-columns: repeat(4, 1fr);
  }
  .grid--5 {
    grid-template-columns: repeat(5, 1fr);
  }
  .grid--6 {
    grid-template-columns: repeat(6, 1fr);
  }
}

/* --- Icons ---------------------------------------------------------------
   Inline SVG, sized and coloured by CSS exactly as MUI's SvgIcon is.        */

.ico {
  width: 1em;
  height: 1em;
  font-size: 24px;
  fill: currentColor;
  flex-shrink: 0;
  display: inline-block;
  user-select: none;
}

.ico--sm {
  font-size: 18px;
}
.ico--xs {
  font-size: 14px;
}

/* --- Buttons -------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid transparent;
  border-radius: var(--radius-btn);
  padding: 8px 20px;
  font-family: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.75;
  text-decoration: none;
  text-transform: none;
  cursor: pointer;
  background: none;
  transition:
    background-color 0.2s,
    border-color 0.2s,
    box-shadow 0.2s,
    color 0.2s;
  white-space: nowrap;
}

.btn .ico {
  font-size: 20px;
}

.btn--lg {
  padding: 10px 22px;
  font-size: 0.9375rem;
}

.btn--sm {
  padding: 5px 14px;
  font-size: 0.8125rem;
}

/* A button whose label is a sentence rather than a word, for the few that are.
   `.btn` is `white-space: nowrap` because a button normally must not break, and
   an inline-flex box sizes to its content — so a long label keeps its own width
   and pushes out of a narrow screen instead of wrapping. This lets that one
   wrap: the text may break, the icons may fall onto their own line beside it,
   and `max-width` is what actually stops the box growing past its parent.
   `line-height` comes back down because a wrapped label sets several lines and
   the button's own 1.75 is spacing meant for exactly one. */
.btn--wrap {
  white-space: normal;
  flex-wrap: wrap;
  max-width: 100%;
  text-align: center;
  line-height: 1.4;
}

.btn--contained {
  background: var(--primary);
  color: var(--primary-contrast);
}

.btn--contained:hover {
  background: var(--primary-dark);
}

.btn--outlined {
  border-color: var(--primary-outline);
  color: var(--primary);
}

.btn--outlined:hover {
  border-color: var(--primary);
  background: var(--primary-hover);
}

/*
 * The one button on this site that is not brand green.
 *
 * It marks the free SEO check in the header, where every other item is either
 * plain text or the green "Get Started". Green would put it in the same sentence
 * as the sign-up and it is not one — it is a tool anybody can use without an
 * account — so it is the site's amber instead: outlined, so it stays quieter
 * than the filled call to action beside it while still not reading as another
 * nav link.
 *
 * The amber is the same #ed6c02 the SEO report uses for "needs work", which is
 * the only other place this colour appears.
 */
.btn--accent {
  border-color: #ed6c02;
  color: #b45309;
}

.btn--accent:hover {
  border-color: #b45309;
  background: rgba(237, 108, 2, 0.08);
}

.btn--text {
  color: var(--primary);
}

.btn--text:hover {
  background: var(--primary-hover);
}

/* Inherit-coloured text button — the header's "Log in", the blog's "Back". */
.btn--plain {
  color: var(--text);
}

.btn--plain:hover {
  background: var(--action-hover);
}

/* On a green or photographic band, the button inverts. */
.btn--onDark {
  background: #fff;
  color: var(--primary);
}

.btn--onDark:hover {
  background: rgba(255, 255, 255, 0.9);
}

.btn--onDarkOutlined {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.6);
}

.btn--onDarkOutlined:hover {
  border-color: #fff;
  background: rgba(255, 255, 255, 0.08);
}

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.btn-row--center {
  justify-content: center;
}

@media (max-width: 599px) {
  .btn-row--stack {
    flex-direction: column;
    align-items: stretch;
  }
}

/* --- Chips ---------------------------------------------------------------- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding-inline: 12px;
  border-radius: 16px;
  border: 1px solid transparent;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  background: var(--action-selected);
  color: var(--text);
  text-decoration: none;
}

.chip--sm {
  height: 24px;
  padding-inline: 8px;
  border-radius: 12px;
  font-size: 0.75rem;
}

.chip--outlined {
  background: transparent;
  border-color: var(--divider);
  color: var(--text);
}

.chip--primary {
  background: var(--primary);
  color: var(--primary-contrast);
}

.chip--primary-outlined {
  background: transparent;
  border-color: var(--primary-outline);
  color: var(--primary);
}

.chip--onDark {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}

button.chip {
  cursor: pointer;
  font-family: inherit;
  transition:
    background-color 0.2s,
    border-color 0.2s,
    color 0.2s;
}

button.chip.chip--outlined:hover {
  background: var(--action-hover);
}

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

.card {
  background: var(--bg-paper);
  border: 1px solid var(--divider);
  border-radius: var(--radius-card);
  overflow: hidden;
}

.card--lift {
  transition:
    box-shadow 0.2s,
    transform 0.2s;
}

.card--lift:hover {
  box-shadow: var(--shadow-6);
  transform: translateY(-4px);
}

.card--fill {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.card-body {
  padding: 24px;
}

.card-body--lg {
  padding: 24px;
}

@media (min-width: 900px) {
  .card-body--lg {
    padding: 32px;
  }
}

/* A whole card that is one link. MUI's CardActionArea, without the ripple. */
.card-link {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  height: 100%;
  color: inherit;
  text-decoration: none;
}

.card-link:hover {
  background: var(--action-hover);
}

/* --- Avatars -------------------------------------------------------------- */

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--grey-400);
  color: #fff;
  font-size: 1.25rem;
  font-weight: 700;
  flex-shrink: 0;
  overflow: hidden;
}

.avatar--rounded {
  border-radius: var(--radius);
}

.avatar--tint {
  background: var(--tint-soft);
  color: var(--primary);
}

.avatar--primary {
  background: var(--primary);
  color: #fff;
}

.avatar--52 {
  width: 52px;
  height: 52px;
}
.avatar--48 {
  width: 48px;
  height: 48px;
}
.avatar--56 {
  width: 56px;
  height: 56px;
}
.avatar--onDark {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}

/* --- Feature / check lists ------------------------------------------------ */

.checklist {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.check-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}

.check-row .ico {
  color: var(--primary);
  margin-top: 2px;
}

/* --- Tables --------------------------------------------------------------- */

.table-wrap {
  background: var(--bg-paper);
  border: 1px solid var(--divider);
  border-radius: 4px;
  margin-inline: auto;
  overflow-x: auto;
}

.table-wrap--720 {
  max-width: 720px;
}
.table-wrap--860 {
  max-width: 860px;
}

table.tbl {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.tbl th,
.tbl td {
  padding: 16px;
  text-align: left;
  border-bottom: 1px solid var(--divider);
  vertical-align: middle;
}

.tbl th {
  font-weight: 500;
  line-height: 1.5rem;
}

.tbl thead th {
  font-weight: 700;
}

.tbl tbody tr:last-child td {
  border-bottom: none;
}

.tbl tbody tr:hover {
  background: var(--action-hover);
}

.tbl .num {
  text-align: right;
}

.tbl .tint-head th {
  background: var(--tint-soft);
}

/* The search box above the resource price list. */
.table-search {
  padding: 16px 16px 8px;
}

.table-empty td {
  color: var(--text-secondary);
  text-align: center;
  padding-block: 32px;
}

/* --- Accordion (the FAQ) --------------------------------------------------
   <details>/<summary>, so a question is open without JavaScript and the very
   first one is open on load exactly as MUI's `defaultExpanded` had it.      */

.accordion {
  background: var(--bg);
  border: 1px solid var(--divider);
  border-radius: 8px;
  margin-bottom: 12px;
  overflow: hidden;
  transition:
    box-shadow 0.2s,
    transform 0.2s;
}

.accordion:hover {
  box-shadow: var(--shadow-6);
  transform: translateY(-2px);
}

.accordion > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 16px;
  min-height: 48px;
  cursor: pointer;
  list-style: none;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.75;
}

.accordion > summary::-webkit-details-marker {
  display: none;
}

.accordion > summary .ico {
  transition: transform 0.2s;
  color: var(--text-secondary);
}

.accordion[open] > summary .ico {
  transform: rotate(180deg);
}

.accordion-body {
  padding: 8px 16px 16px;
  color: var(--text-secondary);
}

/* --- Form fields ----------------------------------------------------------
   MUI's outlined TextField: a floating label is more machinery than this site
   needs, so the label sits above the box. Everything else — the radius, the
   focus ring, the helper line — matches.                                    */

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field > label {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text);
}

.field .req {
  color: var(--error);
}

.field input,
.field select,
.field textarea {
  width: 100%;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1.4375em;
  color: var(--text);
  background: var(--bg-paper);
  padding: 14px;
  border: 1px solid var(--divider);
  border-radius: 4px;
  transition:
    border-color 0.2s,
    box-shadow 0.2s;
}

.field textarea {
  resize: vertical;
}

.field select {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23475569'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 24px;
  padding-right: 40px;
}

.field input:hover,
.field select:hover,
.field textarea:hover {
  border-color: var(--text);
}

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: inset 0 0 0 1px var(--primary);
}

.field .helper {
  font-size: 0.75rem;
  line-height: 1.66;
  color: var(--text-secondary);
  /* Reserved even when empty, so a row of fields does not jump when one of
     them starts showing an error. */
  min-height: 1.25em;
}

.field.is-invalid input,
.field.is-invalid select,
.field.is-invalid textarea {
  border-color: var(--error);
}

.field.is-invalid input:focus,
.field.is-invalid textarea:focus {
  box-shadow: inset 0 0 0 1px var(--error);
}

.field.is-invalid .helper {
  color: var(--error);
}

/* An input with a leading icon (the email boxes). */
.field .adorned {
  position: relative;
  display: flex;
  align-items: center;
}

.field .adorned .ico {
  position: absolute;
  left: 12px;
  font-size: 20px;
  color: var(--text-secondary);
  pointer-events: none;
}

.field .adorned input {
  padding-left: 42px;
}

.field--sm input,
.field--sm select,
.field--sm textarea {
  padding: 9px 12px;
  font-size: 0.875rem;
}

fieldset.radio-group {
  border: 0;
  margin: 0;
  padding: 0;
}

.radio-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.radio-row label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 1rem;
  cursor: pointer;
}

.radio-row input[type="radio"] {
  accent-color: var(--primary);
  width: 20px;
  height: 20px;
  margin: 0;
}

/* The feature chips are checkboxes wearing a chip. */
.chip-check {
  position: relative;
  display: inline-flex;
}

.chip-check input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}

.chip-check span {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding-inline: 12px;
  border-radius: 16px;
  border: 1px solid var(--divider);
  font-size: 0.8125rem;
  font-weight: 600;
  transition:
    background-color 0.2s,
    border-color 0.2s,
    color 0.2s;
}

.chip-check input:checked + span {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}

.chip-check input:focus-visible + span {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* --- Alerts --------------------------------------------------------------- */

.alert {
  display: flex;
  gap: 12px;
  padding: 6px 16px;
  border-radius: 4px;
  font-size: 0.875rem;
  line-height: 1.43;
  align-items: center;
  min-height: 48px;
}

.alert--success {
  background: var(--success-bg);
  color: var(--success-text);
}

.alert--error {
  background: var(--error-bg);
  color: var(--error-text);
}

.alert--warning {
  background: var(--warning-bg);
  color: var(--warning-text);
}

/* --- Divider -------------------------------------------------------------- */

.divider {
  border: 0;
  border-top: 1px solid var(--divider);
  margin: 0;
}

/* ===========================================================================
   Header
   =========================================================================== */

.header {
  position: sticky;
  top: 0;
  z-index: 1100;
  background: rgba(255, 255, 255, 0.7);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--divider);
}

.header .container {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: var(--header-h);
}

.logo {
  display: flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
  margin-right: auto;
}

.logo img {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  display: block;
}

.logo span {
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text);
}

.logo em {
  font-style: normal;
  color: var(--primary);
}

.logo--light span {
  color: #fff;
}

.nav {
  display: none;
  align-items: center;
  gap: 8px;
}

@media (min-width: 900px) {
  .nav {
    display: flex;
  }
}

.nav a.btn.is-active {
  font-weight: 700;
  color: var(--primary);
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  color: var(--text);
  margin-left: auto;
}

.nav-toggle:hover {
  background: var(--action-hover);
}

@media (min-width: 900px) {
  .nav-toggle {
    display: none;
  }
}

/* The mobile drawer. Hidden from assistive tech and from the tab order until
   it is opened — `inert` is set alongside `hidden` in site.js. */
.drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 1200;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.2s;
}

.drawer-scrim.is-open {
  opacity: 1;
}

.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 260px;
  max-width: 85vw;
  background: var(--bg-paper);
  box-shadow: var(--shadow-6);
  transform: translateX(100%);
  transition: transform 0.2s;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.drawer-scrim.is-open .drawer {
  transform: translateX(0);
}

.drawer-head {
  padding: 16px;
}

.drawer-list a {
  display: block;
  padding: 12px 16px;
  text-decoration: none;
  font-size: 1rem;
}

.drawer-list a:hover {
  background: var(--action-hover);
}

.drawer-list a.is-active {
  background: var(--action-selected);
  font-weight: 700;
}

.drawer-actions {
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.drawer-actions .btn {
  width: 100%;
}

/* ===========================================================================
   Footer
   =========================================================================== */

.footer {
  background: var(--secondary);
  color: var(--footer-text);
  margin-top: 64px;
}

.footer .container {
  padding-block: 48px;
}

.footer-grid {
  display: grid;
  gap: 32px;
  grid-template-columns: 1fr;
}

@media (min-width: 600px) {
  .footer-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .footer-grid {
    /* md={4} md={2} md={2} md={4} of MUI's 12 columns. */
    grid-template-columns: 4fr 2fr 2fr 4fr;
  }
}

.footer h2,
.footer .footer-heading {
  color: var(--footer-heading);
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.57;
  margin-bottom: 12px;
}

.footer p,
.footer .footer-muted {
  color: var(--footer-muted);
}

.footer-links {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.footer-links a {
  color: var(--footer-text);
  text-decoration: none;
  font-size: 0.875rem;
  line-height: 1.43;
  width: fit-content;
}

.footer-links a:hover {
  color: var(--footer-heading);
  text-decoration: underline;
}

.footer-social {
  display: flex;
  gap: 8px;
  margin-top: 16px;
}

.footer-social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  color: var(--footer-text);
}

.footer-social a:hover {
  color: var(--footer-heading);
  background: rgba(255, 255, 255, 0.08);
}

.footer-social .ico {
  font-size: 20px;
}

.footer-bottom {
  margin-top: 40px;
  padding-top: 24px;
  border-top: 1px solid var(--footer-border);
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: space-between;
  color: var(--footer-muted);
  font-size: 0.75rem;
  line-height: 1.66;
}

/* ===========================================================================
   Home
   =========================================================================== */

.hero {
  position: relative;
  /* The photograph layer is taller than the section so it has room to move.
     Without this it would show above and below. */
  overflow: hidden;
  isolation: isolate;
}

/*
 * A PARALLAX LAYER, AND THE SLACK THE MOVEMENT SPENDS.
 *
 * Two sections use one of these: the hero and the CTA banner. The mechanics are
 * shared and only the picture differs, so the selector is `[data-parallax]` —
 * the same marker site.js looks for. One hook for both halves means a layer can
 * never be positioned here and skipped there.
 *
 * Overhanging its section by 20% top and bottom, which is the whole budget for
 * the movement: site.js translates the layer and clamps itself to the overhang
 * it measures off the live element, so changing the 20% changes how far the
 * picture can travel and nothing else. A layer that exactly filled its section
 * would uncover an edge the moment it moved.
 *
 * `background-size: cover` on a box 40% taller than the section crops the image
 * tighter than it would at the section's own height — that is the trade for the
 * slack, and on both of these shots it reads as a slightly closer view.
 *
 * A section holding one of these needs `position: relative`, `overflow: hidden`
 * and a `position: relative; z-index: 1` container, or the layer paints over
 * the copy.
 */
[data-parallax] {
  position: absolute;
  inset: -20% 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.hero-bg {
  background-image: url("/assets/img/home_hero_2.webp");
}

/* The products hero. Its copy is left-aligned on a pane of frosted glass, and
   the photograph is anchored right behind it — the empty white down the left of
   that picture is what the copy now sits on, and the desk and rack stay in
   frame on the right. */
.product-hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.product-hero .container {
  position: relative;
  z-index: 1;
  padding-top: 64px;
  padding-bottom: 48px;
}

.product-hero-bg {
  background-image: url("/assets/img/product_hero_1.webp");
  background-position: right center;
}

/*
 * The product shortcuts, pulled up so they overlap the hero by 30%.
 *
 * The 30% is of the cards' own height, which is why that height is stated here
 * rather than left to the labels: a fixed height is what makes the fraction
 * mean anything, and `calc(var(--nav-card-h) * -0.3)` keeps the two numbers
 * tied together, so changing the height keeps the overlap at 30%.
 *
 * `position: relative` with a z-index lifts the row over the hero's parallax
 * layer, exactly as `.stats` does on the home page — the row is a sibling of
 * the hero, not a child, so the hero's `overflow: hidden` does not clip it.
 *
 * Six across is a desktop layout only. The generic grid falls back to one
 * column, which would be a six-card stack on a phone, so the small sizes get
 * their own progression: two up, then three, then all six.
 */
.product-nav {
  --nav-card-h: 88px;
  position: relative;
  z-index: 1;
  margin-top: calc(var(--nav-card-h) * -0.3);
  padding-bottom: 32px;
}

.product-nav .grid {
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 600px) {
  .product-nav .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (min-width: 900px) {
  .product-nav .grid {
    grid-template-columns: repeat(6, 1fr);
  }
}

/* The same glass as the stat cards, and grey for the same reason: below the
   hero the page is white, and a white pane on it would only read as a card
   across the part still standing on the photograph. */
.product-nav-card {
  height: var(--nav-card-h);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 12px;
  border: none;
  background: rgba(226, 232, 240, 0.7);
  color: var(--text);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
  transition: background-color 0.2s;
}

.product-nav-card:hover {
  background: rgba(226, 232, 240, 0.95);
}

/* Same pane as the contact hero: 40% white over a 5px blur, sized to the copy
   it holds. `.section-head` centres its contents and carries a 40px bottom
   margin meant for a heading above a grid; neither applies to a hero panel, so
   the left modifier goes on in the markup and the margin comes off here. */
.product-hero-panel {
  max-width: 880px;
  margin-bottom: 0;
  padding: 32px 24px;
  background: rgba(255, 255, 255, 0.4);
}

@media (min-width: 900px) {
  .product-hero-panel {
    padding: 40px;
  }
}

/*
 * Below the desktop breakpoint, hold the RIGHT of the photograph in frame.
 *
 * The picture is weighted right — empty white down the left, the desk and the
 * rack on the right — and `cover` on a tall narrow box crops hard from the
 * sides: at phone widths only about a fifth of the image's width survives.
 * Centred, that fifth is the empty end of the desk. Anchored right, it is the
 * rack and as much in front of it as the width allows.
 *
 * Keyed to the same 900px the panel's layout uses, so the framing and the
 * layout turn over together: above it the panel sits left and the box is wide
 * enough that centring already leaves the right-hand side showing.
 */
@media (max-width: 899px) {
  .hero-bg {
    background-position: right center;
  }
}

.hero .container {
  position: relative;
  z-index: 1;
  padding-top: 80px;
  /* Deeper at the bottom on purpose: the stats band is pulled up into it, and
     this is the photo it overlaps. */
  padding-bottom: 112px;
  display: flex;
}

@media (min-width: 900px) {
  .hero .container {
    padding-top: 128px;
    padding-bottom: 160px;
    /* Desktop puts the copy on the left. The panel's own max-width is what
       leaves the right of the photograph showing. */
    justify-content: flex-start;
  }
}

/*
 * The copy sits on frosted glass rather than straight on the picture.
 *
 * The photograph is a lit server room — bright glass and pale floor down the
 * left, near-black racks down the right — and no single text colour survives
 * both halves. 70% white with a 5px blur behind it gives the words one
 * predictable background wherever they land, and still lets the room through.
 *
 * No border and no shadow: at this opacity the panel is legible on its own, and
 * an edge drawn round it would make it a card sitting on the photograph rather
 * than a pane of the room's own glass.
 *
 * `backdrop-filter` is unsupported in a few places and degrades to exactly the
 * 70% white, which is legible on its own; the blur is what makes it pleasant,
 * not what makes it work.
 */
/* The blur is the part both heroes share; the tint is not. Kept as one rule so
   the two panes cannot end up blurring by different amounts. */
.hero-panel,
.contact-hero-panel,
.product-hero-panel,
.product-nav-card,
.stat {
  border-radius: var(--radius-card);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
}

.hero-panel {
  background: rgba(255, 255, 255, 0.7);
}

.hero-panel {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  text-align: center;
  padding: 32px 24px;
}

@media (min-width: 900px) {
  .hero-panel {
    max-width: 680px;
    align-items: flex-start;
    text-align: left;
    padding: 40px;
  }

  .hero-panel .btn-row {
    justify-content: flex-start;
  }
}

.hero h1 {
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

@media (min-width: 900px) {
  .hero h1 {
    font-size: 3rem;
  }
}

/* "your business" — the one green phrase in the headline. */
.hero h1 .accent {
  color: var(--primary);
}

.hero .lede {
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 1.6;
  color: var(--text-secondary);
}

/* Stats straddle the hero — half on the photo, half off it. */
.stats {
  position: relative;
  z-index: 1;
  margin-top: -56px;
  padding-bottom: 32px;
}

@media (min-width: 900px) {
  .stats {
    margin-top: -88px;
    padding-bottom: 48px;
  }
}

.stats .grid {
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 900px) {
  .stats .grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

/* These sit on the same frosted glass as the heroes' panels: 70% white over a
   5px blur, with the border `.card` gives them and the shadow they used to
   carry both taken off. The shadow was there to stop them reading as pale
   patches stuck to the photograph they straddle — the blur does that job now,
   by showing the picture continuing underneath rather than hiding it.

   The tint is a slate grey rather than white — at 70% white these vanished into
   the page below the photograph, which is white too, and only the half standing
   on the picture read as a card at all. This is the slate `--divider` is cut
   from, and over white it settles at about #ebeff4: enough to hold an edge
   without the border, still light enough to pass as glass.

   The tint is also what holds the text where `backdrop-filter` is unsupported,
   and it is legible on its own.

   `border: none` rather than a narrower `.card` selector because these are the
   only cards that want no edge; the rest of the app's cards keep theirs. */
.stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 168px;
  padding-inline: 16px;
  border: none;
  background: rgba(226, 232, 240, 0.7);
}

.stat .value {
  color: var(--primary);
  font-weight: 800;
}

/* The green CTA banner floating on a full-bleed photograph. */
/* The photograph is a layer inside it now, the same as the hero's — see
   [data-parallax] above for what the section has to provide. */
.cta-photo {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.cta-bg {
  background-image: url("/assets/img/lexhost_hosting_2.png");
}

.cta-photo .container {
  position: relative;
  z-index: 1;
  padding-block: 64px;
}

@media (min-width: 900px) {
  .cta-photo .container {
    padding-block: 96px;
  }
}

.cta-card {
  /* Sheer green over the picture rather than a solid block of it — the token is
     the brand green at 50%. The blur is what makes that legible: the photo runs
     from black racks on the left to a white office on the right, and without it
     that hard edge cuts straight through the headline.

     No border: at 50% the panel already separates itself from the photograph,
     and a light edge drawn round it only announced that it was a box. */
  background: var(--tint-veil);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  color: #fff;
  border-radius: var(--radius-card);
  text-align: center;
  padding: 48px 24px;
}

.cta-card p {
  margin: 16px auto 32px;
  max-width: 560px;
  opacity: 0.9;
}

/*
 * Tablet and up, the card stops well short of the container and sits at the
 * left. Full width it was a green stripe with the photograph peeping out at the
 * ends; at this size it is a card ON a picture, which is the point of putting
 * it on one. It covers the dark racks down the left of the shot and leaves the
 * bright office to the right of it showing.
 *
 * Phones keep the full-width centred card: there is no width there to give
 * away, and a cap would do nothing but add a margin.
 *
 * AFTER the base rules, not before: a media query adds no specificity, so
 * `text-align: center` above would win on source order alone.
 */
@media (min-width: 600px) {
  .cta-photo .container {
    display: flex;
    justify-content: flex-start;
  }

  .cta-card {
    max-width: 640px;
    text-align: left;
  }

  /* The paragraph centres itself with auto margins in the full-width card. */
  .cta-card p {
    margin-inline: 0;
  }
}

@media (min-width: 900px) {
  .cta-card {
    padding: 64px;
  }
}

/* The sales-partner recruitment band. */
.recruit {
  background: var(--primary);
  color: #fff;
}

.recruit .container {
  padding-block: 48px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  align-items: flex-start;
}

@media (min-width: 900px) {
  .recruit .container {
    padding-block: 64px;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
}

.recruit-copy {
  display: flex;
  gap: 16px;
  align-items: center;
}

.recruit-copy p {
  opacity: 0.9;
}

/* The flipped opening quote. `align-self` matters: in a stretched flex column
   the span would be full width, and mirroring a full-width box throws the glyph
   over to the right-hand edge. */
.quote-mark {
  display: flex;
  align-self: flex-start;
  color: var(--primary);
  opacity: 0.3;
  transform: scaleX(-1);
}

.quote-mark .ico {
  font-size: 40px;
}

.rating {
  display: flex;
  gap: 2px;
  color: #faaf00;
  margin-bottom: 12px;
}

.rating .ico {
  font-size: 18px;
}

.testimonial-quote {
  margin-bottom: 24px;
  flex: 1;
  font-style: italic;
}

.person {
  display: flex;
  align-items: center;
  gap: 16px;
}

/* The long keyword-rich SEO block every page carries. */
.prose {
  max-width: 820px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.prose--wide {
  max-width: 860px;
}

.prose p {
  color: var(--text-secondary);
}

/* "Areas we serve" — a masonry of state headings. */
.areas {
  column-count: 2;
  column-gap: 32px;
}

@media (min-width: 600px) {
  .areas {
    column-count: 3;
  }
}

@media (min-width: 900px) {
  .areas {
    column-count: 4;
  }
}

@media (min-width: 1200px) {
  .areas {
    column-count: 5;
  }
}

.areas > div {
  /* Keeps a state's cities in the same column as its heading — without it a
     column break orphans them under the wrong state name. */
  break-inside: avoid;
  margin-bottom: 20px;
}

.areas .state {
  font-weight: 600;
  font-size: 0.875rem;
  line-height: 1.43;
}

.areas .city {
  font-size: 0.875rem;
  line-height: 1.43;
  color: var(--grey-500);
}

/* ===========================================================================
   Blog
   =========================================================================== */

.blog-card {
  position: relative;
}

.blog-card .cover {
  position: relative;
}

.blog-card .cover img {
  display: block;
  width: 100%;
  height: 220px;
  object-fit: cover;
}

.blog-card .cover .placeholder {
  height: 220px;
  background: var(--grey-200);
  color: var(--grey-400);
  display: flex;
  align-items: center;
  justify-content: center;
}

.blog-card .cover .placeholder .ico {
  font-size: 56px;
}

.blog-card .cover .chip {
  position: absolute;
  top: 12px;
  left: 12px;
  font-weight: 700;
}

/* Green accent line between the image and the card body. */
.blog-card .accent {
  height: 5px;
  background: var(--primary);
}

.blog-card .card-body {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
}

.blog-card h3 {
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.3;
  margin-bottom: 8px;
}

.blog-card .excerpt {
  color: var(--text-secondary);
  margin-bottom: 16px;
  flex: 1;
}

.blog-meta {
  display: flex;
  align-items: center;
  gap: 16px;
  color: var(--text-secondary);
  font-size: 0.75rem;
  line-height: 1.66;
}

.blog-meta .with-icon {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.blog-filters {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-bottom: 32px;
}

@media (min-width: 900px) {
  .blog-filters {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
}

.blog-filters .chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.blog-search {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
}

@media (min-width: 900px) {
  .blog-search {
    min-width: 280px;
    width: 280px;
  }
}

.blog-search .ico {
  position: absolute;
  left: 12px;
  font-size: 20px;
  color: var(--text-secondary);
  pointer-events: none;
}

.blog-search input {
  width: 100%;
  font-family: inherit;
  font-size: 0.875rem;
  padding: 9px 12px 9px 40px;
  border: 1px solid var(--divider);
  border-radius: 4px;
  background: var(--bg-paper);
}

.blog-search input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: inset 0 0 0 1px var(--primary);
}

.blog-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding-block: 64px;
}

/* --- One article ---------------------------------------------------------- */

.article-head .h3 {
  font-size: 1.875rem;
  margin-bottom: 16px;
}

@media (min-width: 900px) {
  .article-head .h3 {
    font-size: 2.625rem;
  }
}

.article-byline {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 32px;
}

.article-cover {
  width: 100%;
  max-height: 420px;
  object-fit: cover;
  border-radius: var(--radius-card);
  margin-bottom: 32px;
  display: block;
}

.article-rule {
  height: 6px;
  border-radius: var(--radius-card);
  margin-bottom: 32px;
  background: var(--gradient-brand);
}

.article-body p {
  margin-bottom: 20px;
  line-height: 1.8;
  font-size: 18px;
}

.article-body h2 {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.334;
  margin-top: 32px;
  margin-bottom: 16px;
}

.article-cta {
  background: var(--bg-subtle);
  margin-top: 32px;
}

.article-cta .card-body {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: center;
  justify-content: space-between;
}

.related-card .card-link {
  flex-direction: row;
  align-items: stretch;
}

.related-card .cover {
  width: 110px;
  flex-shrink: 0;
  object-fit: cover;
  display: block;
}

@media (min-width: 600px) {
  .related-card .cover {
    width: 150px;
  }
}

.related-card .placeholder {
  width: 110px;
  flex-shrink: 0;
  background: var(--grey-200);
  color: var(--grey-400);
  display: flex;
  align-items: center;
  justify-content: center;
}

@media (min-width: 600px) {
  .related-card .placeholder {
    width: 150px;
  }
}

.related-card .card-body {
  padding: 20px;
}

/* ===========================================================================
   Products
   =========================================================================== */

.product-row {
  display: grid;
  gap: 32px;
  align-items: center;
  grid-template-columns: 1fr;
}

@media (min-width: 900px) {
  .product-row {
    grid-template-columns: repeat(2, 1fr);
    gap: 64px;
  }
}

.product-row img {
  width: 100%;
  height: 240px;
  object-fit: cover;
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-6);
  display: block;
}

@media (min-width: 900px) {
  .product-row img {
    height: 380px;
  }
}

/* Odd sections put the picture on the right. On a phone both stack with the
   picture last, which is what MUI's `column-reverse` did. */
@media (min-width: 900px) {
  .product-row--flip .product-media {
    order: 2;
  }
}

@media (max-width: 899px) {
  .product-row .product-media {
    order: 2;
  }
}

.product-detail {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.product-detail .h3 {
  font-size: 1.75rem;
}

@media (min-width: 900px) {
  .product-detail .h3 {
    font-size: 2.25rem;
  }
}

.product-detail .intro {
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1.6;
}

.product-features {
  display: grid;
  gap: 8px;
  grid-template-columns: 1fr;
}

@media (min-width: 600px) {
  .product-features {
    grid-template-columns: repeat(2, 1fr);
  }
}

.product-eyebrow {
  display: flex;
  align-items: center;
  gap: 12px;
}

.product-solo {
  max-width: 820px;
  margin-inline: auto;
}

/* ===========================================================================
   Pricing
   =========================================================================== */

/* The pricing hero. The photograph — green glass credits streaming down a white
   corridor — replaces the radial glow this section used to paint: a layer sits
   over the section's own background, so the gradient would only have been paint
   nobody sees. `--gradient-hero-glow` is now unused. */
.pricing-hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.pricing-hero-bg {
  background-image: url("/assets/img/pricing_hero_1.webp");
}

/* The contact hero. Its copy is a left-aligned stack, and the left of this
   photograph is the empty sunlit wall — the woman at the desk sits to the right
   of where the heading ends. */
.contact-hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.contact-hero .container {
  position: relative;
  z-index: 1;
  padding-block: 48px;
}

/* Anchored right at every width, overriding the `center` [data-parallax]
   otherwise gives it. Cropping happens against the right edge, so the woman at
   the desk is the part that survives and the blank sunlit wall is what gets cut
   — centred, a narrow viewport keeps the wall and the photograph reads as an
   empty white box. This matters above 900px too now that the section is tall:
   `cover` on a taller box crops the sides rather than the top and bottom. */
.contact-hero-bg {
  background-image: url("/assets/img/contact_hero_1.webp");
  background-position: right center;
}

/* The copy sits on the same frosted glass the home hero uses. The left of this
   photograph is a sunlit wall, so dark text on it is legible but weak; the pane
   gives the words a background of their own without hiding the room. Sized and
   padded here rather than on .hero-panel because this copy is a left-aligned
   .stack, not that panel's centred flex column. */
/* A lighter tint than the home hero's 70% — enough to lift the words off the
   picture without turning the pane into a white card, so the room still reads
   through it. Where `backdrop-filter` is unsupported this degrades to the 40%
   alone: thinner cover than the home hero's, and it holds because the copy sits
   over the sunlit wall on the left of this photograph rather than the subject
   further right. */
.contact-hero-panel {
  max-width: 720px;
  padding: 32px 24px;
  background: rgba(255, 255, 255, 0.4);
}

@media (min-width: 900px) {
  .contact-hero-panel {
    padding: 40px;
  }
}

.pricing-hero .container {
  position: relative;
  z-index: 1;
  padding-block: 48px;
  text-align: center;
}

@media (min-width: 900px) {
  .pricing-hero .container {
    padding-block: 72px;
  }
}

.pricing-hero .lede {
  max-width: 720px;
  margin-inline: auto;
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 1.6;
  color: var(--text-secondary);
}

.credit-type {
  position: relative;
  overflow: hidden;
  height: 100%;
}

/* An oversized ghost of the same icon, bled off the corner — decoration only,
   so it is hidden from assistive tech. */
.credit-type .ghost {
  position: absolute;
  top: -28px;
  right: -28px;
  color: var(--primary);
  opacity: 0.06;
  pointer-events: none;
}

.credit-type .ghost .ico {
  font-size: 180px;
}

.credit-type .card-body {
  position: relative;
}

.icon-square {
  width: 84px;
  height: 84px;
  border-radius: var(--radius);
  margin-bottom: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--primary);
  color: var(--primary);
}

.icon-square .ico {
  font-size: 44px;
}

/* The five-step "life of a credit" thread. */
.journey {
  position: relative;
}

/* The thread the steps hang off. Behind them, at the vertical centre of the
   96px circles, and only where the steps are actually in a row — stacked on a
   phone, a horizontal rule would run through nothing. */
.journey::before {
  content: "";
  display: none;
  position: absolute;
  top: 48px;
  left: 10%;
  right: 10%;
  border-top: 2px dashed var(--divider);
  z-index: 0;
}

@media (min-width: 900px) {
  .journey::before {
    display: block;
  }
}

.journey-grid {
  position: relative;
  z-index: 1;
  display: grid;
  gap: 32px;
  grid-template-columns: 1fr;
}

@media (min-width: 600px) {
  .journey-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .journey-grid {
    grid-template-columns: repeat(5, 1fr);
    gap: 16px;
  }
}

.journey-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
  padding-inline: 8px;
}

.journey-badge {
  position: relative;
}

.journey-circle {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--primary);
  /* Opaque, so the dashed thread stops at the circle rather than running
     across the icon. */
  background: var(--bg-paper);
  color: var(--primary);
}

.journey-circle .ico {
  font-size: 46px;
}

.journey-num {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--primary);
  color: var(--primary-contrast);
  font-size: 13px;
  font-weight: 700;
}

.example-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-block: 4px;
  font-size: 0.875rem;
  color: var(--text-secondary);
}

.example-row--total {
  color: var(--text);
  font-weight: 700;
}

/* ===========================================================================
   Contact
   =========================================================================== */

.contact-grid {
  display: grid;
  gap: 32px;
  grid-template-columns: 1fr;
}

@media (min-width: 900px) {
  .contact-grid {
    /* md={4} / md={8} of MUI's 12 columns. */
    grid-template-columns: 1fr 2fr;
  }
}

.channel .card-body {
  display: flex;
  gap: 16px;
  align-items: center;
}

.channel .ico {
  color: var(--primary);
}

.channel a,
.channel .value {
  font-weight: 600;
  color: inherit;
  text-decoration: none;
}

.channel a:hover {
  text-decoration: underline;
}

.card--primary {
  background: var(--primary);
  color: var(--primary-contrast);
  border: none;
}

.card--primary p {
  opacity: 0.9;
}

.form-grid {
  display: grid;
  gap: 20px;
  grid-template-columns: 1fr;
}

@media (min-width: 600px) {
  .form-grid--2 {
    grid-template-columns: repeat(2, 1fr);
  }
}

.form-grid .span-2 {
  grid-column: 1 / -1;
}

.form-section {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.form-section > h3 {
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.75;
}

.form-sections {
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.form-sections > * + * {
  border-top: 1px solid var(--divider);
  padding-top: 32px;
}

.link-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
}

.link-row {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: flex-start;
}

@media (min-width: 600px) {
  .link-row {
    flex-direction: row;
  }
}

.link-row .field {
  flex: 1;
  width: 100%;
}

/*
 * One-box rows — "cities you serve". A city is one word, and a box the width of
 * the whole card invites a paragraph. So it stops exactly where the first box of
 * a two-box row does: the full width less the two 12px gaps and the 34px remove
 * button, halved.
 */
@media (min-width: 600px) {
  .link-list[data-single] .link-row .field {
    flex: 0 1 calc((100% - 58px) / 2);
  }
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  color: var(--text-secondary);
  flex-shrink: 0;
}

.icon-btn:hover {
  background: var(--action-hover);
}

.icon-btn[disabled] {
  opacity: 0.4;
  pointer-events: none;
}

.logo-picker {
  display: flex;
  gap: 16px;
  align-items: center;
}

.logo-picker img {
  width: 96px;
  height: 96px;
  object-fit: contain;
  border-radius: 4px;
  border: 1px solid var(--divider);
  background: var(--bg-paper);
  padding: 8px;
}

/* ===========================================================================
   Sales partners
   =========================================================================== */

/*
 * The photograph is a parallax layer now. Everything on top of it — the scrim,
 * the copy, the type sizes — is exactly as it was.
 *
 * Two things this hero needs that the other three do not:
 *
 *   `overflow: hidden`, because the layer overhangs the section.
 *
 *   the layer at `z-index: -1` in an isolated stacking context, because the
 *   scrim below is a ::before and a pseudo-element is always the FIRST child.
 *   Left at auto the layer would paint over the scrim in DOM order and the
 *   white copy would lose the dark backing it is legible against. The
 *   isolation is what keeps that -1 inside this section instead of sending the
 *   picture behind the page.
 */
.sales-hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.sales-hero-bg {
  background-image: url("/assets/img/sales_hero_bg.webp");
  z-index: -1;
}

/* Dark scrim so the white hero text stays readable over the photo. */
.sales-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(15, 23, 42, 0.55),
    rgba(15, 23, 42, 0.65)
  );
}

.sales-hero .container {
  position: relative;
  z-index: 1;
  padding-block: 80px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  text-align: center;
  color: #fff;
}

@media (min-width: 900px) {
  .sales-hero .container {
    padding-block: 128px;
  }
}

.sales-hero h1 {
  font-size: 2.125rem;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.02em;
  max-width: 880px;
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.45);
}

@media (min-width: 900px) {
  .sales-hero h1 {
    font-size: 3.25rem;
  }
}

.sales-hero .lede {
  max-width: 680px;
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 1.6;
  opacity: 0.92;
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.45);
}

.earnings-cell .amount {
  font-weight: 700;
  color: var(--primary);
}

.earnings-cell .per {
  display: block;
  font-size: 0.75rem;
  color: var(--text-secondary);
}

.list-head {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-bottom: 40px;
}

@media (min-width: 600px) {
  .list-head {
    flex-direction: row;
    justify-content: space-between;
    align-items: flex-end;
  }
}

/* --- Print ---------------------------------------------------------------
   Somebody printing a blog post wants the article, not the furniture.      */

@media print {
  .header,
  .footer,
  .drawer-scrim,
  .article-cta,
  .blog-filters {
    display: none !important;
  }

  body {
    color: #000;
  }
}

/* ===========================================================================
   The free SEO Compliance Check — /seo-check
   ---------------------------------------------------------------------------
   A port of the dashboard's SEO widget to this site, in plain CSS. It is the
   one screen here with real state in it, so it carries more rules than the rest
   of the marketing pages put together.

   Everything below reuses the tokens at the top of this file. Nothing here
   invents a colour: the four verdicts map onto the palette the alerts already
   use, so a "fail" row and an error banner are the same red.
   =========================================================================== */

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

/*
 * Half the viewport, as asked. `min-height` rather than `height` because the
 * copy plus the form is taller than 50vh on a phone in landscape, and a fixed
 * height there would crop the button off.
 *
 * ── NO SCRIM OVER THE ARTWORK, AND THAT IS DELIBERATE ───────────────────────
 *
 * The other heroes on this site are photographs, and their copy sits on a
 * frosted pane because dark text on a photograph is not legible on its own.
 * This one is an illustration COMPOSED FOR THIS BLOCK: near-white, with its
 * subjects held to the left and right edges and the middle third left empty for
 * exactly the words that go there. A pane over it would cover nothing but the
 * white it was drawn to leave, and dim the drawing on either side of it.
 *
 * `background-color` stays as the ground beneath: it is what shows while a
 * 700 KB image is still arriving, and what remains if it never does.
 */
.seo-hero {
  position: relative;
  min-height: 50vh;
  display: flex;
  align-items: center;
  background-color: var(--bg-subtle);
  background-image: url("/assets/img/seo_check_hero.webp");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-bottom: 1px solid var(--divider);
}

/*
 * Below the point where the artwork's empty middle survives the crop.
 *
 * `cover` on a narrow viewport scales the image up and cuts the sides off, which
 * walks the magnifier and the left-hand column inward until they are under the
 * heading. A white veil at those widths — and only those — keeps the words off
 * the drawing without touching the wide layout the illustration was made for.
 */
@media (max-width: 899px) {
  .seo-hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(255, 255, 255, 0.62);
  }
}

/* Above that veil, whichever way it fell. */
.seo-hero > .container {
  position: relative;
  z-index: 1;
}

.seo-hero-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  text-align: center;
  padding-block: 56px;
  max-width: 720px;
  margin-inline: auto;
}

.seo-hero h1 {
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.02em;
  margin: 0;
}

@media (min-width: 900px) {
  .seo-hero h1 {
    font-size: 3rem;
  }
}

.seo-hero h1 .accent {
  color: var(--primary);
}

.seo-hero .lede {
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--text-secondary);
  margin: 0;
}

.seo-hero-note {
  color: var(--text-secondary);
}

/* The address box and its button, side by side once there is room for both. */
.seo-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  max-width: 560px;
}

@media (min-width: 600px) {
  .seo-form {
    flex-direction: row;
  }
}

.seo-form input {
  flex: 1;
  width: 100%;
  padding: 14px 16px;
  font-family: inherit;
  font-size: 1rem;
  color: var(--text);
  background: var(--bg-paper);
  border: 1px solid var(--divider);
  border-radius: var(--radius-btn);
}

.seo-form input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--tint-softer);
}

.seo-form .btn {
  flex-shrink: 0;
}

/* --- The score ring ------------------------------------------------------- */

/*
 * The score gauge — a port of the dashboard's ScoreGauge, which is what sits at
 * the top of this same report there.
 *
 * The number and its BAND WORD are HTML layered over the SVG rather than SVG
 * text, so they inherit the page's font and need no counter-rotation trick to
 * stay upright.
 */
.seo-gauge {
  position: relative;
  flex-shrink: 0;
}

.seo-gauge svg {
  display: block;
}

.seo-gauge-track {
  fill: none;
  stroke: var(--divider);
}

.seo-gauge-value {
  fill: none;
  transition: stroke-dashoffset 0.6s ease-out;
}

/* The three bands api/seoCompliance.ts scores against: 90+, 60+, below. */
.seo-gauge--pass .seo-gauge-value { stroke: var(--primary); }
.seo-gauge--warn .seo-gauge-value { stroke: #ed6c02; }
.seo-gauge--fail .seo-gauge-value { stroke: var(--error); }
.seo-gauge--unknown .seo-gauge-value { stroke: var(--grey-500); }

.seo-gauge-face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  text-align: center;
}

.seo-gauge-number {
  font-weight: 800;
  line-height: 1;
}

.seo-gauge--pass .seo-gauge-number { color: var(--primary); }
.seo-gauge--warn .seo-gauge-number { color: #ed6c02; }
.seo-gauge--fail .seo-gauge-number { color: var(--error); }
.seo-gauge--unknown .seo-gauge-number { color: var(--text-secondary); }

/* The word is what carries the verdict; the colour only reinforces it. */
.seo-gauge-band {
  font-size: 0.75rem;
  color: var(--text-secondary);
}

/* --- The summary ---------------------------------------------------------- */

.seo-summary {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  text-align: center;
  padding: 24px;
  background: var(--bg-subtle);
  border: 1px solid var(--divider);
  border-radius: var(--radius-card);
  margin-bottom: 32px;
}

@media (min-width: 600px) {
  .seo-summary {
    flex-direction: row;
    text-align: left;
    padding: 32px;
  }
}

.seo-summary-facts {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.seo-summary-head {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
}

@media (min-width: 600px) {
  .seo-summary-head { justify-content: flex-start; }
}

.seo-host {
  font-size: 0.9375rem;
  font-weight: 700;
  word-break: break-word;
}

.seo-chip-warn {
  border-color: #ed6c02;
  color: #b45309;
}

/*
 * The per-category tallies — "250/286" under each category's own icon.
 *
 * The icon is how a reader finds the group they want before reading a word, and
 * the tooltip names it, so the row stays a row instead of seven labelled boxes.
 */
.seo-cats {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 16px;
}

@media (min-width: 600px) {
  .seo-cats { justify-content: flex-start; }
}

.seo-cat-stat {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.8125rem;
  color: var(--text-secondary);
  cursor: help;
}

.seo-cat-stat b {
  font-weight: 700;
  color: var(--text);
}

.seo-checked-on {
  font-size: 0.8125rem;
  color: var(--text-secondary);
}

/* The progress and the button that ends it, side by side — the button is wanted
   precisely when the bar is taking too long, so it sits where the eye already
   is. It wraps under the bar on a narrow screen rather than squeezing it. */
.seo-running {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
}

.seo-running-bar {
  flex: 1;
  min-width: 220px;
}

.seo-stop {
  flex-shrink: 0;
}

/* The crawl's progress. Indeterminate only while nothing has been discovered
   yet — once there is a denominator, a real fraction is not a guess. */
.seo-progress {
  height: 6px;
  border-radius: 3px;
  background: var(--divider);
  overflow: hidden;
}

.seo-progress span {
  display: block;
  height: 100%;
  background: var(--primary);
  transition: width 0.4s ease;
}

.seo-progress--idle span {
  width: 40% !important;
  animation: seo-progress-slide 1.4s ease-in-out infinite;
}

@keyframes seo-progress-slide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}

.seo-progress-note {
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- The findings --------------------------------------------------------- */

.seo-section {
  margin-bottom: 40px;
}

.seo-section > .h6 {
  margin: 0 0 12px;
}

.seo-findings-head {
  margin-bottom: 20px;
}

.seo-findings-head .h6 {
  margin: 0 0 4px;
}

.seo-findings-head p {
  margin: 0;
  max-width: 70ch;
}

.seo-findings-url {
  display: block;
  margin-top: 4px;
  word-break: break-all;
}

/*
 * TWO COLUMNS on a wide screen, one on a narrow one — the dashboard's layout.
 * Seven categories stacked in a single column is a page nobody scrolls to the
 * end of; side by side the whole checklist is roughly one screen.
 *
 * `align-items: start` so a short category does not stretch to match the tall
 * one beside it, leaving a gap under its last row.
 */
.seo-findings {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: 40px;
  row-gap: 28px;
  align-items: start;
}

@media (min-width: 900px) {
  .seo-findings {
    grid-template-columns: 1fr 1fr;
  }
}

.seo-cat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--divider);
}

.seo-cat-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  min-width: 0;
  font-size: 0.9375rem;
  font-weight: 700;
}

.seo-cat-icon {
  flex-shrink: 0;
  color: var(--text-secondary);
}

.seo-cat-count {
  flex-shrink: 0;
  font-size: 0.75rem;
  color: var(--text-secondary);
}

/* --- Check rows ----------------------------------------------------------- */

/*
 * What was checked on the left, what was found on the right, and the verdict in
 * a fixed column so the words line up down the page rather than ragging in and
 * out with the length of each value.
 */
.seo-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--divider);
}

.seo-row[data-tip] {
  cursor: help;
}

.seo-row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.seo-row-label {
  font-size: 0.875rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* How far a rolled-up row reaches: "all 12 pages", "3 of 12 pages". */
.seo-row-scope {
  flex-shrink: 0;
  font-size: 0.75rem;
  color: var(--text-secondary);
}

.seo-row-value {
  flex-shrink: 0;
  max-width: 180px;
  font-size: 0.875rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The verdict: the dot and the word, which always travel together. */
.seo-tag {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 82px;
  font-size: 0.75rem;
  color: var(--text-secondary);
}

.seo-tag::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.seo-tag--pass::before { background: var(--success-text); }
.seo-tag--warn::before { background: #ed6c02; }
.seo-tag--fail::before { background: var(--error); }
.seo-tag--unknown::before { background: var(--grey-500); }

/* Held in the layout even when empty, so the column of verdicts beside it does
   not shift from row to row. */
.seo-row-doc {
  flex-shrink: 0;
  display: flex;
  justify-content: flex-end;
  width: 28px;
}

.seo-doc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
}

.seo-doc:hover {
  background: var(--action-hover);
  color: var(--primary);
}

/* --- The tooltip ---------------------------------------------------------- */

/*
 * The page's ONE tooltip, positioned by assets/js/seo-check.js.
 *
 * It replaces the browser's own — `<title>` on a hexagon and `title=` on a row —
 * which cannot be styled, waits out the OS delay, and truncates a long URL
 * wherever the platform feels like it.
 */
.seo-tip {
  position: absolute;
  z-index: 1400;
  max-width: 320px;
  padding: 8px 10px;
  border-radius: 6px;
  background: #303030;
  color: #fff;
  font-size: 0.75rem;
  line-height: 1.45;
  pointer-events: none;
  box-shadow: var(--shadow-2);
}

.seo-tip::after {
  content: '';
  position: absolute;
  left: 50%;
  margin-left: -5px;
  border: 5px solid transparent;
  border-top-color: #303030;
  top: 100%;
}

.seo-tip--below::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: #303030;
}

.seo-tip-title,
.seo-tip-url,
.seo-tip-score,
.seo-tip-error {
  display: block;
}

.seo-tip-title {
  font-weight: 700;
}

/* A URL has no spaces to wrap at, so it must be allowed to break anywhere or it
   forces the tooltip as wide as the path is long. */
.seo-tip-url {
  opacity: 0.75;
  word-break: break-all;
}

.seo-tip-score {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
  font-weight: 600;
}

.seo-tip-error {
  margin-top: 4px;
  opacity: 0.75;
}

.seo-tip-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.seo-tip-dot--pass { background: var(--primary); }
.seo-tip-dot--warn { background: #ed6c02; }
.seo-tip-dot--fail { background: var(--error); }
.seo-tip-dot--unknown { background: var(--grey-500); }

/* --- The structure graph -------------------------------------------------- */

/*
 * The radial site map. Ported from the dashboard's SiteStructureGraph so the two
 * draw the same picture — see assets/js/seo-check.js for the layout maths.
 *
 * The SVG SCALES TO FIT its box and never scrolls: a site map is a shape, and a
 * shape you have to drag around to see is not one. The cost is that a fifty-page
 * website draws smaller, which is why the labels give way above 26 nodes and the
 * score inside each hexagon is what always survives.
 */
.seo-graph {
  overflow: hidden;
  background: var(--bg-subtle);
  border: 1px solid var(--divider);
  border-radius: var(--radius-card);
}

.seo-edge {
  stroke: var(--divider);
  stroke-width: 2;
}

.seo-hex {
  cursor: pointer;
}

.seo-hex polygon {
  stroke: var(--bg-paper);
  stroke-width: 2;
  transition: filter 0.15s;
}

.seo-hex:hover polygon,
.seo-hex:focus-visible polygon {
  filter: brightness(1.08);
  stroke: var(--text);
}

.seo-hex.is-active polygon {
  stroke: var(--text);
  stroke-width: 3;
}

/* The three score bands. Never the only signal — every hexagon prints its
   number, because amber and red are one colour to a lot of readers. */
.seo-hex--pass polygon { fill: var(--primary); }
.seo-hex--warn polygon { fill: #ed6c02; }
.seo-hex--fail polygon { fill: var(--error); }
.seo-hex--unknown polygon { fill: var(--grey-500); }

.seo-hex-score {
  text-anchor: middle;
  dominant-baseline: middle;
  font-size: 13px;
  font-weight: 800;
  fill: #fff;
  pointer-events: none;
}

.seo-hex-label {
  text-anchor: middle;
  font-size: 12px;
  fill: var(--text-secondary);
  pointer-events: none;
}

.seo-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-top: 12px;
  font-size: 0.8125rem;
  color: var(--text-secondary);
}

.seo-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* The swatch, so the legend says which colour it is naming. */
.seo-legend-item::before {
  content: "";
  width: 12px;
  height: 12px;
  border-radius: 3px;
}

.seo-legend--pass::before { background: var(--primary); }
.seo-legend--warn::before { background: #ed6c02; }
.seo-legend--fail::before { background: var(--error); }

/* --- The advice dialog ---------------------------------------------------- */

.seo-dialog {
  position: fixed;
  inset: 0;
  z-index: 1300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(15, 23, 42, 0.5);
  overflow-y: auto;
}

/*
 * HEAD, SCROLLING BODY, ACTIONS — the dashboard dialog's three parts.
 *
 * The panel itself does not scroll; its body does. A fifty-line entry would
 * otherwise take its own title and its Close button off the top of the screen,
 * which on a phone leaves no way out of the dialog but the back gesture.
 */
.seo-dialog-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 640px;
  max-height: min(90vh, 820px);
  background: var(--bg-paper);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-6);
  overflow: hidden;
}

.seo-dialog-head {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  /* Right padding clears the close button. */
  padding: 20px 56px 16px 24px;
}

.seo-dialog-heading {
  margin: 0;
  line-height: 1.3;
}

.seo-dialog-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 20px 24px;
  border-top: 1px solid var(--divider);
  border-bottom: 1px solid var(--divider);
}

.seo-dialog-actions {
  flex-shrink: 0;
  display: flex;
  justify-content: flex-end;
  padding: 12px 16px;
}

.seo-dialog-close {
  position: absolute;
  top: 12px;
  right: 12px;
}

/* 1. What the website says NOW — read before any of the advice below it. */
.seo-dialog-finding {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.seo-dialog-verdict {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
}

.seo-dialog-value {
  font-size: 0.875rem;
  font-weight: 600;
  word-break: break-word;
}

.seo-dialog-section {
  margin-top: 20px;
}

.seo-dialog-section h4 {
  margin: 0 0 6px;
}

.seo-dialog-blurb {
  display: block;
  margin-top: 8px;
}

.seo-dialog-nothing {
  margin-bottom: 6px;
}

.seo-dialog-panel p {
  margin: 0;
  line-height: 1.65;
}

.seo-dialog-panel code {
  padding: 1px 5px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.86em;
  background: var(--grey-200);
  border-radius: 4px;
  /* A long attribute must be able to break, or it widens the dialog and every
     paragraph in it scrolls sideways. */
  word-break: break-word;
}

/*
 * The priority chip. Filled where the rule is urgent and outlined where it is
 * not, so the three levels are told apart by weight as well as by hue.
 */
.seo-priority--filled {
  border: 0;
  color: #fff;
}

.seo-priority--critical.seo-priority--filled { background: var(--error); }
.seo-priority--important.seo-priority--filled { background: #ed6c02; }
.seo-priority--optional { border-color: var(--divider); color: var(--text-secondary); }

/* 5. Where to make the change — one note, the same for every row. */
.seo-note {
  display: flex;
  gap: 12px;
  margin-top: 24px;
  padding: 12px 14px;
  background: var(--info-bg);
  border-radius: var(--radius-card);
}

.seo-note .ico {
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--info-text);
}

.seo-note p + p {
  margin-top: 2px;
}

.seo-steps {
  margin: 8px 0 0;
  padding-left: 20px;
  font-size: 0.875rem;
  line-height: 1.65;
  color: var(--text-secondary);
}

.seo-steps li {
  margin-bottom: 8px;
}

/* --- The banner on every other page --------------------------------------- */

/*
 * Advertises the checker from the rest of the site. A link, not a card with a
 * button in it: the whole strip is the target, which is what makes it work on a
 * phone where a 40px button inside a banner is a miss waiting to happen.
 */
.seo-banner {
  display: block;
  background: var(--tint-softer);
  border-top: 1px solid var(--divider);
  border-bottom: 1px solid var(--divider);
  text-decoration: none;
  color: inherit;
  transition: background-color 0.2s;
}

.seo-banner:hover {
  background: var(--tint-soft);
}

.seo-banner .container {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-block: 20px;
}

.seo-banner-text {
  flex: 1;
  min-width: 0;
}

.seo-banner-text strong {
  display: block;
  font-size: 1rem;
}

.seo-banner-text span {
  font-size: 0.875rem;
  color: var(--text-secondary);
}

.seo-banner .ico {
  flex-shrink: 0;
  color: var(--primary);
}
