/* WebHostGB -- premium managed web design service.
   Light paper background with deep forest green and warm terracotta used
   as accents: green for headings/text/outlines, rust for CTAs and
   highlights. Newsreader serif for headings, Inter for everything else.
   Radius language is deliberately binary -- crisp (var(--radius)) on
   utilitarian surfaces like inputs, fully pill on interactive chrome
   (nav, buttons, avatars) -- rather than one uniform medium rounding,
   which is what makes this read as "designed" instead of templated. */

:root {
  /* Backgrounds, light to raised -- warm cream, closer to parchment than
     paper, paired with a much deeper near-black forest green (used as its
     own surface, not just a text/outline color -- see .hero-panel). */
  --paper: #faf3e6;
  /* A subtly lighter shade of `--paper` itself (not `--paper-raised`,
     which is closer to white) -- used by `.msection-tint` to alternate a
     marketing page's background band to band while still reading as the
     same warm cream, not a jump to a neutral white section. */
  --paper-tint: #fdf8f0;
  --paper-raised: #fffdf9;
  --paper-sunken: #e9e0cf;
  /* `--paper` at two alphas, for the sticky header's frosted sheet. Named
     rather than written as literals at the point of use: they *are*
     `--paper`, and as two hard-coded `rgb(250 243 230 / ...)` values they
     were the one surface a palette change would have missed. */
  --paper-veil: rgb(250 243 230 / 82%);
  --paper-veil-strong: rgb(250 243 230 / 94%);

  /* Green -- deep forest green, press_circle's accent, in place of navy. */
  --green-950: #0f2419;
  --green-900: #1a3527;
  --green-800: #234634;
  --green-700: #3a5c47;
  --green-500: #6b8574;

  /* Rust -- warm terracotta in place of gold: cream-tint, clay, mid
     terracotta, burnt orange, rust, deep umber. */
  --rust-200: #eccbb0;
  --rust-300: #e0a67e;
  --rust-400: #cc7a4a;
  --rust-500: #b8623a;
  --rust-600: #9c4e2d;
  --rust-700: #7d3d22;
  --rust-tint: rgb(156 78 45 / 13%);

  --ink-muted: #6b6a5c;
  --ink-faint: #93907d;

  --border: rgba(26, 53, 39, 0.1);
  --border-strong: rgba(26, 53, 39, 0.18);

  --font-display: "Newsreader", Georgia, Cambria, "Times New Roman", Times, serif;
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --radius: 6px;
  --radius-md: 14px;
  --radius-lg: 20px;
  --radius-pill: 999px;

  /* The duration on its own, so a rule that needs a *delay* matching the
     transition can have one. `--transition` is a shorthand (duration plus
     easing) and can't be used where only a time is valid. */
  --transition-time: 0.18s;
  --transition: var(--transition-time) ease;
  --shadow-card: 0 20px 45px -22px rgba(15, 36, 25, 0.3);
  --shadow-dropdown: 0 18px 40px -18px rgba(15, 36, 25, 0.3);

  /* The one gap size every card grid/stack should use -- see `.stack`,
     `.dashboard-grid`, `.site-grid` -- so "how far apart do cards sit"
     is answered in exactly one place instead of each layout picking its
     own number. */
  --card-gap: 24px;

  /* Shared application shell measurements. */
  --app-rail-w: 76px;
  --app-panel-w: 276px;
  --admin-header-clearance: 84px;
  --app-topbar-h: 60px;

  /* Editorial marketing accents and type scale. */
  --grad-warm: linear-gradient(135deg, #cc7a4a 0%, #9c4e2d 55%, #7d3d22 100%);
  --grad-forest: linear-gradient(160deg, #234634 0%, #0f2419 100%);
  --grad-dawn: linear-gradient(135deg, #eccbb0 0%, #faf3e6 45%, #e0a67e 100%);
  --shadow-lift: 0 2px 4px -2px rgb(15 36 25 / 12%), 0 12px 24px -12px rgb(15 36 25 / 18%),
    0 32px 64px -32px rgb(15 36 25 / 28%);
  --shadow-deep: 0 4px 8px -4px rgb(15 36 25 / 16%), 0 24px 48px -20px rgb(15 36 25 / 26%),
    0 60px 100px -50px rgb(15 36 25 / 36%);
  --step-display: clamp(2.75rem, 1.4rem + 5.4vw, 5.25rem);
  --step-h1: clamp(2.25rem, 1.3rem + 3.9vw, 4rem);
  --step-h2: clamp(1.85rem, 1.25rem + 2.4vw, 2.9rem);
  --step-h3: clamp(1.25rem, 1.05rem + 0.8vw, 1.6rem);
  --step-lede: clamp(1.05rem, 0.98rem + 0.42vw, 1.3rem);
}

* {
  box-sizing: border-box;
}

/* The `hidden` attribute has to actually hide things.

   Browsers implement it as `[hidden] { display: none }` in the *user-agent*
   stylesheet, which any author rule setting `display` beats -- so
   `.logo-preview { display: block }` silently defeated `<img hidden>` and
   the element rendered anyway (as a broken-image icon and its alt text,
   since it also had no src). The same trap caught `.logo-placeholder`
   (inline-flex, so placeholder and real logo both showed at once) and
   `.image-gallery` (grid, so an empty gallery rendered as an empty grid).

   One of the few genuinely correct uses of `!important`: this is a
   browser-behaviour guarantee, not a style preference, and anything that
   wants an element visible should not be marking it `hidden`. */
[hidden] {
  display: none !important;
}

html {
  color-scheme: light;
  /* In-page anchors ease rather than jump. Overridden entirely under
     `prefers-reduced-motion` at the foot of this block. */
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--green-700);
  background: var(--paper);
  background-image:
    radial-gradient(60rem 34rem at 88% -8%, rgba(156, 78, 45, 0.1), transparent 60%),
    radial-gradient(55rem 34rem at -10% 15%, rgba(15, 36, 25, 0.05), transparent 55%);
  background-repeat: no-repeat;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Brand-tinted selection instead of the browser's default blue, which
   fights the paper/green palette everywhere text is highlighted. */
::selection {
  background: var(--rust-200);
  color: var(--green-950);
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  color: var(--green-900);
  line-height: 1.15;
  font-weight: 600;
  margin: 0 0 0.5em;
  letter-spacing: -0.01em;
  /* Evens out the line lengths of a wrapped heading rather than leaving a
     single orphaned word on the last line. The single highest-value
     typographic improvement available here: headings are the most-read
     text on every marketing page, and a stranded "business." underneath a
     full line is the most common thing that makes an otherwise careful
     layout look unconsidered. Ignored by browsers that don't support it,
     so there's no fallback to write. */
  text-wrap: balance;
}

/* Prose gets `pretty` rather than `balance`: it only fixes the last line
   (no orphans/short widows) instead of re-balancing the whole block, which
   is the right trade for multi-line body copy. */
p {
  margin: 0 0 1em;
  text-wrap: pretty;
}

h1 {
  font-size: clamp(2.4rem, 2rem + 2vw, 3.6rem);
}

h2 {
  font-size: clamp(1.7rem, 1.5rem + 1vw, 2.3rem);
}

h3 {
  font-size: 1.25rem;
}

a {
  color: var(--rust-700);
  text-decoration: none;
  transition: color var(--transition);
}

a:hover {
  color: var(--green-900);
}

:focus-visible {
  outline: 3px solid var(--rust-500);
  outline-offset: 2px;
}

.text-muted {
  color: var(--ink-muted);
}

/* One step quieter than .text-muted -- for secondary metadata sitting
   right underneath a more important muted line (e.g. the site detail
   page's status explanation, in .text-muted, followed by its "Created
   {date}" line in this). */
.text-faint {
  color: var(--ink-faint);
  font-size: 0.85rem;
}

.container {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 24px;
}

.site-layout {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ---------- Header ---------- */

/* Sticky, with a scroll state.
   
   `position: sticky` was commented out here. On a long marketing page that
   means the only route to the call to action is scrolling back to the top,
   which is the single most common reason a visitor gives up. It is on now,
   and `static/js/header.js` adds `.is-scrolled` once the page has moved,
   so the header sits flat and borderless over the hero and then earns its
   border and shadow as it starts overlapping content.

   That class is applied with hysteresis (see header.js): on above ~26px,
   off below ~6px. The gap exists because the rules below change the
   header's height, and a single threshold let ordinary scroll jitter flip
   it repeatedly. If the padding or the collapsing tagline below are ever
   retuned, the size of that dead band should be checked against how much
   height actually changes.

   Nothing here depends on that class existing -- without JS the header is
   simply always in its resting state, which is still correct. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  padding: 14px 0;
  border-bottom: 1px solid transparent;
  transition: padding var(--transition), border-color var(--transition),
    box-shadow var(--transition);
}

/* The frosted background lives on a pseudo-element, not on `.site-header`
   itself, and this is load-bearing rather than stylistic.

   `backdrop-filter` makes an element the containing block for any
   `position: fixed` descendant. With the filter on the header, the mobile
   menu's full-screen `inset: 0` panel resolved against the *header's* ~60px
   box instead of the viewport, so it rendered as a sliver -- visible only
   as the top edge peeking below the nav.

   Moving the filter down to `::before` leaves the header itself
   filter-free, so `position: fixed` descendants escape to the viewport
   where they belong. `z-index: -1` keeps it behind the header's own
   content, and the header's `z-index: 40` means the whole stacking context
   still paints above the page. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--paper-veil);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  transition: background-color var(--transition);
}

/* The warm accent rule along the header's bottom edge, mirroring the one
   the footer draws along its top.

   The two ends of the page were the two places with no shared signal at
   all: the footer had a 3px `--grad-warm` rule and the header a plain grey
   hairline, so they read as pieces of different sites bracketing the same
   page. Hidden at rest -- the header is a frosted sheet over the hero
   until you scroll -- and revealed with the rest of the scrolled state.

   2px against the footer's 3px, on purpose: the footer's sits on near
   black where it needs the weight to read, and the same bar on cream is
   heavier than a header edge should be. */
.site-header::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  height: 2px;
  background: var(--grad-warm);
  opacity: 0;
  transition: opacity var(--transition);
}

/* No grey border on top of the accent rule -- the footer draws one edge,
   not two, and a 1px hairline directly under a 2px warm bar reads as a
   mistake rather than as a detail. The border declaration stays on
   `.site-header` above so the element still reserves the pixel and the
   header does not shift by 1px when it scrolls. */
.site-header.is-scrolled {
  padding: 9px 0;
  box-shadow: 0 10px 30px -22px rgb(15 36 25 / 45%);
}

.site-header.is-scrolled::before {
  background: var(--paper-veil-strong);
}

.site-header.is-scrolled::after {
  opacity: 0.85;
}

.header-flex {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.site-brand {
  display: inline-flex;
  align-items: center;
  color: var(--green-900);
}

/* The brand lockup, drawn inline by `ui::logo` rather than loaded through
   an `<img>` -- see that module for why. The consequence here is that the
   wordmark is real page type: it inherits `--font-display` from this
   rule and `currentColor` from the link, instead of falling back to
   Georgia inside an isolated SVG document.

   The shrink-on-scroll the old lockup had is kept, moved from collapsing a
   tagline to scaling the whole mark. */
.brand-lockup {
  display: block;
  height: 30px;
  width: auto;
  transition: height var(--transition), opacity var(--transition);
}

.brand-lockup-word {
  font-family: var(--font-display);
}

/* The mark's gradient, on a light background. Overrides the presentation
   attributes on the stops themselves -- see `ui::logo` for why both
   exist. Scoped to nothing in particular on purpose: these are the
   defaults, and the footer overrides them below. */
.logo-stop-from {
  stop-color: var(--rust-400);
}

.logo-stop-to {
  stop-color: var(--rust-600);
}

.site-header.is-scrolled .brand-lockup {
  height: 26px;
}

.site-brand:hover .brand-lockup {
  opacity: 0.82;
}

/* Plain links in a row, not a segmented pill.

   The bar was carrying three bounded shapes side by side -- a bordered,
   tinted pill around the nav, a filled pill on the current link, and the
   CTA button -- against a brand lockup that is itself a piece of artwork.
   Four competing objects in one 60px strip. The container was the one
   doing the least work: it grouped links that are already visibly grouped
   by sitting next to each other.

   What replaces it is the footer's link treatment, which is the calmest
   thing on the page and now the only navigation idiom on it: text that
   goes rust and grows an underline. */
.site-nav {
  display: flex;
  align-items: center;
  gap: 4px;
}

.nav-link {
  position: relative;
  padding: 8px 12px;
  color: var(--green-900);
  font-size: 0.88rem;
  font-weight: 500;
  white-space: nowrap;
  transition: color var(--transition);
}

/* Inset from the padding so the rule sits under the word rather than
   under the hit area, which is wider than the text on purpose. */
.nav-link::after {
  content: "";
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 2px;
  height: 2px;
  border-radius: 2px;
  background: var(--grad-warm);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--transition);
}

.nav-link:hover {
  color: var(--rust-700);
}

.nav-link:hover::after {
  transform: scaleX(1);
}

/* The current section, marked in the nav. A multi-link nav with no active
   state gives no sense of where you are, which across six near-peer
   marketing pages is genuinely disorienting.

   It applies to the signed-in bar too (Dashboard / Help centre /
   Support). That bar used to hold one link and had no active state at all,
   because app screens carry no canonical path for it to compare against --
   they read the request path from `middleware::viewer_role` instead. The
   match is by section, so it stays lit on `/support/new` as well as
   `/support`.

   Underlined rather than filled. A near-black pill was the heaviest mark
   on a bar whose job is to be quiet, and the underline says the same thing
   in the same language as the hover it replaces. */
.nav-link.is-current {
  color: var(--rust-700);
  font-weight: 600;
}

.nav-link.is-current::after {
  transform: scaleX(1);
}

/* Product mega-menu ---------------------------------------------------
   The product has several substantial areas, each mirroring a part of
   the customer dashboard. A single Features link hid that value; putting
   all of them in the top bar would turn the header into a directory. This
   native details menu keeps one calm trigger and gives the product enough
   room to explain itself when somebody asks. */
.marketing-feature-menu > summary {
  display: flex;
  align-items: center;
  gap: 5px;
  list-style: none;
  cursor: pointer;
}

.marketing-feature-menu > summary::-webkit-details-marker {
  display: none;
}

.marketing-feature-chevron {
  display: inline-flex;
  color: var(--ink-faint);
  font-size: 0.82rem;
  line-height: 1;
  transform: translateY(-2px);
  transition: transform var(--transition), color var(--transition);
}

.marketing-feature-menu[open] .marketing-feature-chevron {
  color: var(--rust-600);
  transform: translateY(1px) rotate(180deg);
}

.marketing-feature-panel {
  position: absolute;
  top: calc(100% + 18px);
  left: 50%;
  z-index: 5;
  width: min(920px, calc(100vw - 40px));
  padding: 18px;
  border: 1px solid var(--border);
  border-radius: 24px;
  background: var(--paper-raised);
  box-shadow: 0 26px 70px -28px rgb(15 36 25 / 42%);
  transform: translateX(-50%);
  display: grid;
  grid-template-columns: minmax(210px, 0.72fr) minmax(0, 2fr);
  gap: 18px;
  animation: marketing-menu-in 0.16s ease-out;
}

@keyframes marketing-menu-in {
  from { opacity: 0; transform: translate(-50%, -7px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

.marketing-feature-panel-intro {
  padding: 22px;
  border-radius: var(--radius-lg);
  background:
    radial-gradient(circle at 95% 0%, rgb(204 122 74 / 24%), transparent 42%),
    linear-gradient(155deg, var(--green-900), var(--green-950));
  color: var(--paper-raised);
}

.marketing-feature-panel-intro .eyebrow {
  margin: 0 0 15px;
  color: var(--rust-300);
}

.marketing-feature-panel-intro strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1.55rem;
  line-height: 1.08;
  letter-spacing: -0.025em;
}

.marketing-feature-panel-intro p:not(.eyebrow) {
  margin: 12px 0 20px;
  color: rgb(244 237 224 / 74%);
  font-size: 0.86rem;
  line-height: 1.55;
}

.marketing-feature-panel-intro a {
  color: var(--paper-raised);
  font-size: 0.82rem;
  font-weight: 650;
}

.marketing-feature-panel-intro a:hover {
  color: var(--rust-300);
}

.marketing-feature-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}

/* Five destinations leave one useful item on the final row. Let it use
   the available width instead of reading like a missing sixth feature. */
.marketing-feature-grid > a:last-child:nth-child(odd) {
  grid-column: 1 / -1;
}

.feature-area-grid > .feature-area-card:last-child:nth-child(odd) {
  grid-column: 1 / -1;
}

.marketing-feature-grid > a {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  align-items: start;
  gap: 11px;
  min-height: 120px;
  padding: 17px;
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  color: var(--green-900);
  transition: background-color var(--transition), border-color var(--transition), transform var(--transition);
}

.marketing-feature-grid > a:hover,
.marketing-feature-grid > a:focus-visible {
  border-color: var(--border);
  background: var(--paper-tint);
  color: var(--green-900);
  transform: translateY(-1px);
}

.marketing-feature-index {
  color: var(--rust-500);
  font-size: 0.67rem;
  font-weight: 750;
  letter-spacing: 0.08em;
  padding-top: 3px;
}

.marketing-feature-grid small,
.marketing-feature-grid strong,
.marketing-feature-grid em {
  display: block;
}

.marketing-feature-grid small {
  margin-bottom: 3px;
  color: var(--rust-600);
  font-size: 0.67rem;
  font-weight: 750;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.marketing-feature-grid strong {
  font-family: var(--font-display);
  font-size: 1.04rem;
  letter-spacing: -0.01em;
}

.marketing-feature-grid em {
  margin-top: 5px;
  color: var(--ink-muted);
  font-size: 0.78rem;
  font-style: normal;
  line-height: 1.45;
}

.marketing-feature-arrow {
  color: var(--ink-faint);
  transition: color var(--transition), transform var(--transition);
}

.marketing-feature-grid > a:hover .marketing-feature-arrow {
  color: var(--rust-600);
  transform: translateX(3px);
}

/* Sign in + the primary CTA. */
.header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}

/* An outlined pill: the secondary half of a secondary/primary pair.

   It has been two wrong things. First a filled pill that took a rust tint
   on hover, which put a second solid button beside the real one and made
   the two compete. Then a plain text link, which fixed that and created
   the opposite problem -- it was indistinguishable from the six nav links
   to its left, so the header ended with seven pieces of identical text and
   then a button.

   Outlined is the resolution, and it is the pairing the marketing pages
   already use everywhere: `.btn` and `.btn-secondary` side by side, one
   filled and one bordered. Same tokens, at header scale. Signing in is a
   real destination for the minority who have an account, and it should
   look like a control without claiming to be the thing we are selling. */
.header-signin {
  padding: 8px 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  color: var(--green-900);
  font-size: 0.86rem;
  font-weight: 600;
  white-space: nowrap;
  transition: border-color var(--transition), color var(--transition),
    background-color var(--transition);
}

.header-signin:hover {
  border-color: var(--green-800);
  background: var(--paper-raised);
  color: var(--green-800);
}

/* The same fill as `.btn`, deliberately.
   `.btn` is `linear-gradient(155deg, --rust-500, --rust-700)` and this was
   `--grad-warm` (135deg, through a lighter clay) -- two different primary
   buttons, one directly above the other on every marketing page. Kept
   smaller than `.btn`, because a header CTA should not compete with the
   hero's, but the same colour. */
.header-cta {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  background: linear-gradient(155deg, var(--rust-500), var(--rust-700));
  color: var(--paper-raised);
  font-size: 0.86rem;
  font-weight: 600;
  white-space: nowrap;
  box-shadow: 0 6px 16px -8px rgb(156 78 45 / 65%);
  transition: transform var(--transition), box-shadow var(--transition), filter var(--transition);
}

.header-cta:hover {
  color: var(--paper-raised);
  transform: translateY(-1px);
  filter: saturate(1.08);
  box-shadow: 0 12px 26px -10px rgb(156 78 45 / 75%);
}

.header-cta:active {
  transform: none;
}

/* Nudges on hover -- a small motion cue that this goes somewhere, without
   animating the whole button. */
.header-cta-arrow {
  transition: transform var(--transition);
}

.header-cta:hover .header-cta-arrow {
  transform: translateX(3px);
}


@media (prefers-reduced-motion: reduce) {

  .site-header,
  .site-header::after,
  .header-cta,
  .header-cta-arrow,
  .nav-link,
  .nav-link::after,
  .brand-lockup {
    transition: none;
  }
}

.mobile-menu {
  display: none;
}

/* Account dropdown */

.account-menu {
  position: relative;
}

/* A hairline, now that the nav around it is plain text. The account menu
   is the one thing in the bar that opens rather than navigates, and with
   the pill container gone there was nothing left saying so -- it read as a
   link with an avatar stuck to it. */
.account-menu summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px 4px 4px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  list-style: none;
  cursor: pointer;
  color: var(--green-900);
  font-size: 0.88rem;
  font-weight: 500;
  transition: background-color var(--transition), border-color var(--transition);
}

.account-menu summary::-webkit-details-marker {
  display: none;
}

/* The disclosure caret in the account menu and the site switcher. It was
   in the markup of both but had no rule at all, so it rendered as a bare
   unaligned "˅" glyph at body size. Rotates to point up when open, which
   is the only affordance either control has for its state. */
.account-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  line-height: 1;
  /* The glyph sits optically low in its own box; nudge it back up so it
     centres against the label rather than the descender line. */
  transform: translateY(-2px);
  color: var(--ink-muted);
  transition: transform var(--transition);
}

.account-menu[open] .account-chevron {
  transform: translateY(1px) rotate(180deg);
}

.account-menu summary:hover,
.account-menu[open] summary {
  background: var(--rust-tint);
  border-color: transparent;
}

/* Now carries the signed-in user's real initials rather than a hardcoded
   "A", so it has to hold two characters in 28px, not one: `line-height: 1`
   stops the pair sitting low against the disc, and the slight negative
   tracking keeps them from touching the edges. */
.account-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--rust-500);
  color: var(--paper-raised);
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.01em;
}

.account-menu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 10px);
  min-width: 200px;
  overflow: hidden;
  background: var(--paper-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-dropdown);
  padding: 6px;
  display: flex;
  flex-direction: column;
}

.account-menu-heading {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-faint);
  padding: 8px 10px 4px;
  margin: 0;
}

.account-menu-panel a,
.account-menu-panel button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 10px;
  border-radius: var(--radius);
  color: var(--green-900);
  background: none;
  border: none;
  font-size: 0.9rem;
  font-family: var(--font-body);
  cursor: pointer;
}

.account-menu-panel a:hover,
.account-menu-panel button:hover {
  background: var(--rust-tint);
  color: var(--green-900);
}

/* ---------- Hero ---------- */

/* Full-bleed -- this is the one shared dark gradient surface (also used by
   `.band-dark`/`.cta-band`) but spanning the full viewport width edge to
   edge, rather than a rounded/shadowed card sitting inside the page's
   `.container`. Marketing pages aren't wrapped in one shared container
   (see base::marketing), so `.hero` supplies its own background and the
   nested `.container` inside it centers the actual content at the normal
   1120px marketing width. Padding is Tier 3 from the "Marketing pages"
   spacing system further down this file -- fixed px, identical on
   `.band`/`.cta-band`, deliberately not `clamp()` so it can't drift. */
.hero {
  width: 100%;
  background: linear-gradient(160deg, var(--green-900), var(--green-950));
  padding: 56px 0 112px;
}

/* The text column -- no longer its own boxed/coloured panel now that the
   full-bleed `.hero` above supplies the background; kept as the semantic
   wrapper so the colour-override rules below (h1, eyebrow, lede, trust
   strip, secondary button) still apply wherever `.hero-panel` sits on
   top of the dark background. */
.hero-panel {
  padding: 0;
}

/* `h2` as well as `h1`: a landing page's `hero` block draws an `<h2>`
   when it is not the page's opener (see `blocks::render_page`), and the
   panel sits on the dark green background either way. Without this the
   heading would fall back to `--green-900` on near-black and be
   effectively invisible. */
.hero-panel h1,
.hero-panel h2 {
  color: var(--paper-raised);
}

.hero-panel .eyebrow {
  color: var(--rust-400);
}

.hero-panel .eyebrow::before {
  background: var(--rust-400);
}

.hero-panel .lede {
  color: rgba(244, 237, 224, 0.72);
}

.hero-panel .trust-strip {
  border-top-color: rgba(244, 237, 224, 0.14);
}

.hero-panel .trust-item {
  color: rgba(244, 237, 224, 0.85);
}

.hero-panel .trust-item::before {
  color: var(--rust-400);
}

.hero-panel .btn-secondary {
  border-color: rgba(244, 237, 224, 0.32);
  color: var(--paper-raised);
}

.hero-panel .btn-secondary:hover {
  border-color: var(--rust-400);
  color: var(--rust-300);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.73rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--rust-600);
  margin-bottom: 20px;
}

.eyebrow::before {
  content: "";
  flex: none;
  width: 20px;
  height: 2px;
  border-radius: 2px;
  background: var(--grad-warm);
}

.hero h1 {
  max-width: 780px;
  margin-left: auto;
  margin-right: auto;
}

.lede {
  max-width: 560px;
  margin: 0 auto 32px;
  font-size: 1.1rem;
  color: var(--ink-muted);
}

.hero-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 56px;
}

.trust-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px 28px;
  padding-top: 40px;
  border-top: 1px solid var(--border);
  max-width: 880px;
  margin: 0 auto;
}

.trust-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.88rem;
  color: var(--green-900);
}

.trust-item::before {
  content: "✓";
  color: var(--rust-600);
  font-weight: 700;
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 28px;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  background: linear-gradient(155deg, var(--rust-500), var(--rust-700));
  color: var(--paper-raised);
  transition: background-color var(--transition), box-shadow var(--transition),
    transform var(--transition), border-color var(--transition), color var(--transition);
}

.btn:hover {
  box-shadow: 0 10px 22px -10px rgba(156, 78, 45, 0.55);
  transform: translateY(-1px);
  color: var(--paper-raised);
}

.btn:active {
  box-shadow: none;
  transform: translateY(0);
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

.btn-secondary {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--green-900);
}

.btn-secondary:hover {
  border-color: var(--green-900);
  color: var(--green-900);
  box-shadow: none;
}

.btn-sm {
  min-height: 34px;
  padding: 0 16px;
  font-size: 0.85rem;
  flex-shrink: 0;
}

/* ---------- Cards & forms ---------- */

.card {
  background: var(--paper-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: clamp(24px, 5vw, 36px);
  transition: border-color var(--transition);
}

.auth-card {
  max-width: 420px;
  margin: 64px auto;
  text-align: left;
}

.auth-card-wide {
  max-width: 560px;
}

.auth-eyebrow {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rust-600);
  margin-bottom: 8px;
}

.substep-dots {
  display: flex;
  gap: 6px;
  margin-bottom: 20px;
}

.substep-dot {
  width: 22px;
  height: 4px;
  border-radius: 999px;
  background: var(--border-strong);
}

.substep-dot-filled {
  background: var(--rust-500);
}

.form-field {
  margin-bottom: 18px;
}

/* A responsive 2-up layout for a run of short, related fields (social
   platform links, phone + email, brand colours) that otherwise stack
   full-width one per line regardless of how short their values actually
   are -- reads as considered rather than padded out. Column gap only, so
   each `.form-field`'s own bottom margin still supplies the vertical
   rhythm: a form can freely mix `.form-grid` runs with plain stacked
   fields (e.g. a two-up run of short inputs followed by a full-width
   textarea) without the spacing changing between them. Collapses to one
   column on mobile -- see the Mobile section. */
.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 20px;
}

/* A label-less `ui::input::Select` (`.hide_label()`) sitting inline in a
   button toolbar, e.g. the admin ticket status select -- no stacked field
   below/above it to leave room for. */
.form-field-compact {
  margin-bottom: 0;
  width: auto;
}

/* Shrink the select itself (not just the wrapper) to line up with the
   btn-sm buttons it sits next to in a toolbar row -- otherwise the default
   11px/13px padding makes it noticeably taller than its neighbours. */
.form-field-compact select {
  width: auto;
  padding: 7px 32px 7px 13px;
  font-size: 0.85rem;
}

.form-field label {
  display: block;
  margin-bottom: 7px;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--green-900);
}

/* Wraps a field's label together with its optional "?" tip trigger
   (`ui::input::field_label`) so they sit on one line -- the label itself
   loses its own bottom margin in favour of the row's, since it's no
   longer the direct child carrying that spacing. */
.form-label-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 7px;
}

.form-label-row label {
  margin-bottom: 0;
}

.form-label-row > .field-tip {
  position: relative;
  display: inline-flex;
}

.field-tip-trigger {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--paper-raised);
  color: var(--ink-muted);
  font-size: 0.68rem;
  font-weight: 700;
  line-height: 1;
  cursor: help;
}

.field-tip-trigger:hover,
.field-tip-trigger:focus-visible {
  border-color: var(--rust-500);
  color: var(--rust-600);
}

.field-tip-bubble {
  display: none;
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  z-index: 5;
  width: 260px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--green-950);
  color: var(--paper-tint);
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.45;
  box-shadow: var(--shadow-card);
}

.form-label-row > .field-tip:hover .field-tip-bubble,
.form-label-row > .field-tip:focus-within .field-tip-bubble {
  display: block;
}

/* Supporting copy and the compact question-mark field tooltip used to share
   one unscoped selector. On narrow screens that turned paragraphs into tiny
   inline-flex strips. Keep tooltip positioning inside a label row and let
   ordinary field tips read like ordinary supporting copy. */
p.field-tip {
  display: block;
  margin: 7px 0 0;
  color: var(--ink-muted);
  font-size: 0.82rem;
  line-height: 1.55;
}

p.field-tip strong {
  color: var(--green-900);
}

.form-field input,
.form-field textarea,
.form-field select {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--paper-raised);
  color: var(--green-900);
  font-family: var(--font-body);
  font-size: 0.95rem;
  transition: border-color var(--transition);
}

.form-field input:focus,
.form-field textarea:focus,
.form-field select:focus {
  outline: none;
  border-color: var(--rust-500);
}

.form-field input::placeholder,
.form-field textarea::placeholder {
  color: var(--ink-faint);
}

/* File inputs keep the dependable native filename behaviour, but the
   operating-system button no longer looks unrelated to every other app
   control. This benefits logo, page-image and support attachments alike. */
.form-field input[type="file"] {
  padding: 6px;
  color: var(--ink-muted);
  cursor: pointer;
}

.form-field input[type="file"]::file-selector-button {
  min-height: 36px;
  margin-right: 12px;
  padding: 7px 14px;
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) - 2px);
  background: var(--paper-tint);
  color: var(--green-900);
  font: 600 0.86rem var(--font-body);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}

.form-field input[type="file"]::file-selector-button:hover {
  border-color: var(--rust-400);
  background: var(--rust-tint);
}

/* Native select arrows look different in every browser/OS -- draw our own
   chevron and hide the native one so it matches the rest of the controls. */
.form-field select {
  appearance: none;
  padding-right: 40px;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%231a3527' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5 8 10 13 15 8'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
  background-size: 16px;
}

/* Radios/checkboxes shouldn't inherit the full-width text-input treatment
   above -- reset the box model and let the wrapping label lay itself out
   as a row instead of the default block stack. Native checkbox/radio
   rendering varies a lot across browsers and OSes, so both are drawn from
   scratch (appearance: none) rather than just tinted via accent-color, to
   look identical everywhere and match the rest of the form controls.

   Technique follows moderncss.dev's "Pure CSS Custom Styled Radio
   Buttons"/"Pure CSS Custom Checkbox Style": `display: grid` centers a
   `::before` indicator that's scaled in/out on `:checked`, rather than
   relying on border tricks or `::after`, which render inconsistently on
   replaced form controls. */
.form-field input[type="radio"],
.form-field input[type="checkbox"] {
  appearance: none;
  box-sizing: border-box;
  flex-shrink: 0;
  display: grid;
  place-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  margin: 0;
  border: 1.5px solid var(--border-strong);
  background: var(--paper-raised);
  cursor: pointer;
  transition: border-color var(--transition);
}

.form-field input[type="checkbox"] {
  border-radius: 4px;
}

.form-field input[type="radio"] {
  border-radius: 50%;
}

.form-field input[type="radio"]::before,
.form-field input[type="checkbox"]::before {
  content: "";
  width: 9px;
  height: 9px;
  transform: scale(0);
  transition: transform 120ms ease-in-out;
  box-shadow: inset 16px 16px var(--rust-500);
}

.form-field input[type="radio"]::before {
  border-radius: 50%;
}

.form-field input[type="checkbox"]::before {
  /* Carves a checkmark out of the solid box-shadow fill above. */
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}

.form-field input[type="radio"]:checked,
.form-field input[type="checkbox"]:checked {
  border-color: var(--rust-500);
}

.form-field input[type="radio"]:checked::before,
.form-field input[type="checkbox"]:checked::before {
  transform: scale(1);
}

.form-field input[type="radio"]:hover,
.form-field input[type="checkbox"]:hover {
  border-color: var(--rust-500);
}

.form-field label:has(> input[type="radio"]),
.form-field label:has(> input[type="checkbox"]) {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 400;
  cursor: pointer;
}

.form-hint {
  margin: 6px 0 0;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.form-error {
  background: rgba(178, 58, 42, 0.08);
  border: 1px solid rgba(178, 58, 42, 0.28);
  color: #9c3626;
  padding: 10px 14px;
  border-radius: var(--radius);
  font-size: 0.88rem;
  margin-bottom: 18px;
}

.form-success {
  background: rgba(58, 92, 71, 0.1);
  border: 1px solid rgba(58, 92, 71, 0.28);
  color: var(--green-800);
  padding: 10px 14px;
  border-radius: var(--radius);
  font-size: 0.88rem;
  margin-bottom: 18px;
}

.form-notice {
  margin-bottom: 18px;
  padding: 10px 14px;
  border: 1px solid rgba(156, 78, 45, 0.25);
  border-radius: var(--radius);
  background: rgba(156, 78, 45, 0.08);
  color: var(--rust-700);
  font-size: 0.88rem;
}

/* ---------- Banner (standing alert + CTA strip) ---------- */

/* A "you need to do this before anything else can happen" prompt -- title,
   message and one action button in a single quiet, even-toned strip. Kept
   deliberately muted and uniform (a plain sunken surface, no side accent
   bar, no rust/green tinting) -- the serif title font is what marks it as
   a distinct moment on the page, the same way `.card` headings already do,
   rather than a loud color doing that job. `Warning`/`Info` still exist as
   two variants, but only shift the surface a shade darker/lighter, not
   the whole color language. */
.banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  margin: 24px 0;
  padding: 20px 24px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--paper-sunken);
}

.banner-text {
  min-width: 0;
}

.banner-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--green-900);
}

.banner-message {
  margin: 4px 0 0;
  font-size: 0.9rem;
  color: var(--ink-muted);
}

.banner .btn {
  flex-shrink: 0;
}

/* The launch prompt has two equally important next steps. Its approval
   control is a form (and therefore a block element), so lay the pair out as
   an explicit action row instead of allowing the form to fall beneath the
   preview link. */
.action-group {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

.launch-approval-actions {
  flex-wrap: nowrap;
  gap: 12px;
  flex-shrink: 0;
}

.launch-approval-actions form {
  margin: 0;
}

.launch-approval-actions .btn {
  white-space: nowrap;
}

@media (max-width: 520px) {
  .launch-approval-actions {
    width: 100%;
    flex-direction: column;
    align-items: stretch;
  }

  .launch-approval-actions .btn,
  .launch-approval-actions form {
    width: 100%;
  }
}

.banner-info {
  background: var(--paper-raised);
}

/* A confirmation, not a prompt. Green rather than the rust of a warning,
   and a left rule so it reads as "this happened" at a glance without
   competing with the warning banner directly above it when both are on
   screen -- a failed card and a registered domain can be true at once. */
/* What the customer has already told us about a page still waiting on a
   brief. Quoted rather than shown in a disabled field: it is a record of
   what they said, not something they are editing. */
.manage-existing-brief {
  margin: 0 0 20px;
}

.manage-brief-quote {
  margin: 6px 0 0;
  padding: 12px 16px;
  border-left: 3px solid var(--border-strong);
  background: var(--paper-tint);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  white-space: pre-wrap;
  color: var(--green-900);
  font-size: 0.92rem;
}

.banner-success {
  background: rgb(26 53 39 / 6%);
  border-left: 3px solid var(--green-800);
}

/* ---------- Toast (global flash notification) ---------- */

/* Fixed to the corner of the viewport, above everything else on the page
   -- see `middleware::flash`/`ui::toast`. Only ever holds one toast at a
   time in practice (one flash cookie), but stacks vertically if that ever
   changes. */
.toast-viewport {
  position: fixed;
  z-index: 1000;
  right: 20px;
  bottom: 20px;
  display: flex;
  flex-direction: column-reverse;
  gap: 12px;
  max-width: min(360px, calc(100vw - 40px));
  pointer-events: none;
}

/* Same muted, no-side-colour language as `.banner`: a plain raised card,
   with only a small dot (not the whole background) carrying which of the
   two tones this is, and the serif display font on the message itself
   doing the work of marking it as a distinct, noteworthy moment. */
.toast {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--paper-raised);
  box-shadow: var(--shadow-dropdown);
  pointer-events: auto;
  animation: toast-in 0.2s ease;
}

.toast.is-leaving {
  opacity: 0;
  transform: translateY(6px);
  transition:
    opacity 0.2s ease,
    transform 0.2s ease;
}

.toast-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--ink-muted);
}

.toast-success .toast-dot {
  background: var(--green-700);
}

.toast-error .toast-dot {
  background: var(--rust-600);
}

.toast-message {
  flex: 1;
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.95rem;
  color: var(--green-900);
}

.toast-close {
  flex-shrink: 0;
  border: none;
  background: none;
  padding: 4px;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--ink-faint);
  cursor: pointer;
}

.toast-close:hover {
  color: var(--ink-muted);
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---------- Manage site (business profile + per-page content) ---------- */

/* The setup wizard's form column inside the app shell.

   `.manage-layout` (still used by the four wizard screens not yet moved)
   gave the form ~870px as the `1fr` beside its 220px nav. In the shell
   there is no such column, so the cap has to be stated -- a brief is prose
   someone writes by hand, and at the shell's full width the lines get long
   enough to lose your place between them. */
/* The wizard screens' wrapper. It was `.manage-layout`, a 220px + 1fr
   grid whose left column held a second nav *inside* the form's own content
   area. That nav is the app sidebar now, so this is a plain block and only
   the measure below still matters. */
.setup-shell {
  padding-top: 8px;
}

/* A brief is prose someone writes by hand. The old grid gave it ~870px as
   its `1fr`; without that column it would stretch to the shell's full
   width, and the lines get long enough to lose your place between them. */
.setup-form {
  max-width: 780px;
  /* Centred in the content column. The form is the whole point of these
     screens, and left-aligning it against a 264px sidebar left all the
     spare width piled up on one side. */
  margin-inline: auto;
}

/* The wizard steps, and only them.

   A box around every group would be the same as a box around none: the
   contrast is what carries the meaning. These are the steps that must be
   finished before anything is built, where Pages is optional and the
   account links are permanent furniture -- so this group gets the border
   and the others stay quiet. */
.app-setup-nav {
  padding: 10px 8px;
  border: 1px solid var(--rust-300);
  border-radius: var(--radius-md);
  background: var(--rust-tint);
}

.app-setup-nav .app-nav-group-label {
  color: var(--rust-700);
}


.manage-card {
  max-width: none;
  margin: 28px 0 0;
}

/* A sub-heading inside a manage-card, between the h2 and a related group
   of fields (e.g. "Brand colours" within Creative direction) -- lighter
   than starting a whole new card for a two-field cluster. */
.manage-subheading {
  margin: 28px 0 4px;
  font-size: 0.98rem;
}

/* ---------- Brief: key-detail emphasis (sites::views::manage_business) --- */

/* Every field on the business brief is optional and none of them gate
   submitting, but four of them do most of the work in the generation
   prompt (`sites::models::CORE_BRIEF_FIELDS`). These styles mark just
   those four. Deliberately a quiet accent rather than anything resembling
   a validation error -- an unanswered key detail is a missed opportunity,
   not a mistake, and dressing it as a failure would be both inaccurate and
   nagging. */
.brief-progress {
  margin-bottom: 20px;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--rust-400);
  border-radius: var(--radius-lg);
  background: var(--paper-tint);
}

.brief-progress-complete {
  border-left-color: var(--green-700);
}

.brief-progress-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
}

.brief-progress-head strong {
  font-size: 1rem;
}

.brief-progress-head span {
  font-size: 0.86rem;
}

.brief-progress-list {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
}

.brief-progress-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

.brief-progress-item.is-done {
  color: var(--green-800);
  font-weight: 600;
}

/* Fixed-size dot so the labels stay on one baseline whether or not a tick
   is present -- an empty span still occupies its slot. */
.brief-progress-tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--paper-raised);
  font-size: 0.66rem;
  line-height: 1;
}

.brief-progress-item.is-done .brief-progress-tick {
  border-color: transparent;
  background: var(--green-700);
  color: var(--paper);
}

/* Wraps one of the four fields so its badge sits directly above the
   field's own label rather than floating loose in the card. */
.core-field {
  margin-top: 18px;
}

.core-field-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 6px;
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  background: var(--rust-tint);
  color: var(--rust-700);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.core-field-badge-done {
  background: rgba(26, 53, 39, 0.08);
  color: var(--green-800);
}

/* The "still blank" callout above the review summary
   (sites::views::review). Sits outside the cards, not inside one, so it
   reads as a note about the summary rather than another section of it. */
.review-gaps {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 20px;
  margin-bottom: 20px;
  padding: 16px 20px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--rust-400);
  border-radius: var(--radius-lg);
  background: var(--paper-tint);
}

.review-gaps-body {
  flex: 1;
  min-width: 240px;
}

.review-gaps-body strong {
  display: block;
  margin-bottom: 2px;
}

.review-gaps-body p {
  margin: 0;
  font-size: 0.86rem;
}

/* ---------- Brief: primary-goal radio cards ---------- */

/* This choice sets the call-to-action wording on every generated page, but
   as a `<select>` with the first option pre-selected it looked already
   answered and went unread. Cards make it a visible decision and show the
   consequence (`sites::models::default_primary_action`'s phrase) inline. */
.goal-options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px;
  margin-top: 8px;
}

.goal-option {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
}

.goal-option:hover {
  border-color: var(--border-strong);
}

.goal-option input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

/* `:has()` keeps the selected state correct after a click without any JS;
   the server-rendered `.is-selected` class covers the initial paint (and
   older browsers) so the two never disagree on first load. */
.goal-option.is-selected,
.goal-option:has(input:checked) {
  border-color: var(--rust-500);
  background: var(--paper-tint);
}

.goal-option:focus-within {
  outline: 2px solid var(--rust-400);
  outline-offset: 2px;
}

.goal-option-label {
  font-weight: 600;
  font-size: 0.92rem;
}

.goal-option-cta {
  font-size: 0.76rem;
  color: var(--ink-muted);
}

/* ---------- Async upload status line ---------- */

/* Progress/result text for the in-place image uploads
   (static/js/async-upload.js) -- replaces the page reload those forms used
   to rely on to show that anything had happened. */
.upload-status {
  margin: 10px 0 0;
  font-size: 0.84rem;
  color: var(--ink-muted);
}

.upload-status.is-error {
  color: var(--rust-700);
}

/* A just-uploaded thumbnail, appended client-side. It has no Remove button
   (that needs a server-rendered token and image id -- see async-upload.js)
   so it's dimmed very slightly to read as "not fully settled until you
   reload" rather than looking like a broken sibling of the others. */
.image-gallery-item-fresh {
  opacity: 0.92;
}

/* Secondary/optional fields tucked behind a plain-language disclosure
   (e.g. "Add more detail") instead of shown inline -- the point is to keep
   the form that's actually on screen short, without dropping any fields.
   Styled as a soft, low-commitment toggle rather than the browser's
   default triangle-and-bold-text treatment. */
.optional-fields {
  margin-top: 28px;
  border-top: 1px solid var(--border);
  padding-top: 20px;
}

.optional-fields summary {
  cursor: pointer;
  list-style: none;
  color: var(--green-700);
  font-size: 0.9rem;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.optional-fields summary::-webkit-details-marker {
  display: none;
}

.optional-fields summary::before {
  content: "+";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1px solid var(--green-700);
  font-size: 0.8rem;
  line-height: 1;
  transition: transform var(--transition);
}

.optional-fields[open] summary::before {
  content: "\2212";
}

.optional-fields summary:hover {
  color: var(--green-900);
}

.optional-fields>.optional-fields-body {
  margin-top: 20px;
}

/* Sits below a run of section.card blocks that share one <form> (e.g. the
   business profile, now split into several smaller cards instead of one
   long one) -- gives the submit button the same breathing room a card would,
   without it needing to be a card itself. */
.manage-form-submit {
  margin-top: 28px;
}

/* The one-time "you're all set, we're writing your site" banner shown right
   after the wizard's final Review & start step -- same green success
   palette as .form-success, but a full card so it reads as a distinct
   milestone rather than a small inline toast. */
.manage-card-success {
  background: rgba(58, 92, 71, 0.08);
  border-color: rgba(58, 92, 71, 0.28);
}

.manage-card-success h2 {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
  color: var(--green-800);
}


/* Creative direction picker -- Choices.js (static/js/creative-direction-
   select.js), the one third-party widget currently on this page. Themed
   entirely through Choices.js's own CSS custom properties (see its default
   choices.css) rather than overriding each of its internal classes by
   hand -- less likely to silently drift out of sync with a future
   Choices.js version, and the properties map cleanly onto this site's
   existing tokens anyway. Scoped to `.creative-direction-field` so it only
   ever affects this one field. */
.creative-direction-field .choices {
  --choices-guttering: 0;
  --choices-bg-color: var(--paper-raised);
  --choices-bg-color-dropdown: var(--paper-raised);
  --choices-keyline-color: var(--border-strong);
  --choices-border-radius: var(--radius);
  --choices-border-radius-item: var(--radius-pill);
  --choices-primary-color: var(--green-900);
  --choices-item-color: var(--paper-raised);
  --choices-highlighted-color: var(--paper-sunken);
  --choices-text-color: var(--green-900);
  --choices-font-size-md: 0.95rem;
  --choices-font-size-sm: 0.86rem;
  --choices-input-height: 46px;
  font-family: var(--font-body);
}

/* Choices.js only darkens `--choices-keyline-color` on focus/open (via
   `color-mix`) -- this site's other inputs instead switch to the rust
   accent (see `.form-field input:focus`), so that needs its own rule
   rather than a variable. */
.creative-direction-field .choices.is-focused .choices__inner,
.creative-direction-field .choices.is-open .choices__inner {
  border-color: var(--rust-500);
}

.creative-direction-field .choices__list--dropdown {
  box-shadow: var(--shadow-dropdown);
}

.creative-direction-field .choices__input {
  font-family: var(--font-body);
  color: var(--green-900);
}

.creative-direction-field .choices__placeholder {
  color: var(--ink-faint);
  opacity: 1;
}

/* ---------- Brief / Revisions tabs ---------- */

.manage-tabs {
  display: flex;
  gap: 4px;
  margin: 20px 0 0;
  border-bottom: 1px solid var(--border);
}

.manage-tab {
  padding: 10px 4px;
  margin-right: 20px;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--ink-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  transform: translateY(1px);
}

.manage-tab:hover {
  color: var(--green-900);
}

.manage-tab.active {
  color: var(--green-900);
  border-bottom-color: var(--rust-500);
}

/* ---------- Locked / read-only brief state ---------- */

.form-field input:disabled,
.form-field textarea:disabled,
.form-field select:disabled {
  background: var(--paper-sunken);
  color: var(--ink-muted);
  cursor: not-allowed;
  border-color: var(--border);
}

.form-field input[type="checkbox"]:disabled,
.form-field input[type="radio"]:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

/* ---------- Review & start / locked brief summaries ---------- */

.review-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 16px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
}

.review-section-head h2 {
  margin: 0;
  padding: 0;
  border-top: none;
}

.review-note {
  margin: 0 0 20px;
}

.review-field+.review-field {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

.review-field-label {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--rust-600);
  margin-bottom: 5px;
}

.review-field p {
  margin: 0;
  line-height: 1.6;
  white-space: pre-wrap;
}

.logo-slot {
  margin-bottom: 14px;
}

.logo-preview {
  display: block;
  max-width: 160px;
  max-height: 160px;
  object-fit: contain;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 8px;
  background: var(--paper-raised);
}

/* Shown when a site has no logo yet. The slot used to be an empty gap --
   a bare file input with nothing indicating what it was for or what would
   appear there. Doubles as an honest preview: initials set in the display
   face is exactly the fallback the generated site uses when there's no
   logo, so this isn't a decorative stand-in, it's what they'd get. */
.logo-placeholder {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 160px;
  height: 120px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  background: var(--paper-tint);
  text-align: center;
}

.logo-placeholder-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 10px;
  background: linear-gradient(135deg, var(--green-700), var(--green-950));
  color: var(--paper-tint);
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.logo-placeholder-label {
  font-size: 0.78rem;
  color: var(--ink-muted);
}

.image-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 14px;
  margin: 16px 0;
}

.image-gallery-item {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--paper-raised);
}

.image-gallery-item img {
  display: block;
  width: 100%;
  height: 110px;
  object-fit: cover;
}

.image-gallery-item form {
  margin: 8px;
}

.checkbox-group {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-top: 4px;
}

/* `.radio-option` shares this: `ui::input::Radio` emits the same shape as
   the checkbox (a label wrapping the control and its text) but had no rule
   of its own, so the domain-choice radios on the site setup screen were
   rendering with the browser's default label layout -- no gap, no
   alignment, and inheriting the form label's bold weight. */
.checkbox-option,
.radio-option {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 400;
  font-size: 0.92rem;
  cursor: pointer;
}

/* Design-style tag cloud (STYLE_TAGS) -- same present-when-ticked checkbox
   underneath as .checkbox-group, but presented as toggleable pills rather
   than a checklist, since the point is "pick a handful quickly" rather
   than "review every option carefully". */
.tag-cloud {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}

.tag-pill {
  display: inline-flex;
  align-items: center;
  padding: 7px 16px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--paper-raised);
  color: var(--green-900);
  font-size: 0.86rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition), color var(--transition);
}

.tag-pill:hover {
  border-color: var(--green-700);
}

.tag-pill input {
  /* Keep the checkbox in the DOM (and in the form submission) but visually
     hidden -- the pill itself is the control. */
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.tag-pill:has(input:checked) {
  background: var(--green-900);
  border-color: var(--green-900);
  color: var(--paper-raised);
}

.tag-pill:has(input:focus-visible) {
  outline: 3px solid var(--rust-500);
  outline-offset: 2px;
}

.code-input {
  letter-spacing: 0.4em;
  font-size: 1.4rem;
  text-align: center;
  font-family: var(--font-body);
}

/* ---------- Domain search (after checkout) ---------- */

/* Domain choice is a small decision inside the normal customer area, not
   another login/setup screen. Keep it comfortably readable while giving
   the two routes enough room to explain themselves. */
.domain-setup-page {
  max-width: 860px;
}

.domain-choice-card {
  padding: clamp(24px, 4vw, 34px);
}

.domain-choice-intro {
  margin-bottom: 24px;
}

.domain-choice-intro .text-muted {
  margin: 7px 0 0;
}

.domain-plan-note {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  color: var(--green-800);
  font-size: 0.85rem;
  font-weight: 700;
}

.domain-plan-note::before {
  content: "✓";
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: rgb(63 157 106 / 15%);
  color: #1f6b45;
  font-size: 0.72rem;
}

fieldset.domain-mode-options {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin: 0 0 28px;
  padding: 0;
  border: 0;
}

fieldset.domain-mode-options.form-field .domain-mode-option {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 12px;
  min-height: 100%;
  margin: 0;
  padding: 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  color: var(--green-900);
  cursor: pointer;
  transition: border-color var(--transition), background-color var(--transition),
    box-shadow var(--transition);
}

fieldset.domain-mode-options.form-field .domain-mode-option:hover {
  border-color: var(--rust-400);
}

fieldset.domain-mode-options.form-field .domain-mode-option:has(input:checked) {
  border-color: var(--rust-500);
  background: var(--rust-tint);
  box-shadow: 0 0 0 1px var(--rust-500);
}

fieldset.domain-mode-options.form-field .domain-mode-option:has(input:focus-visible) {
  outline: 3px solid rgb(204 122 74 / 24%);
  outline-offset: 2px;
}

fieldset.domain-mode-options.form-field .domain-mode-option input {
  margin-top: 3px;
}

.domain-mode-option-copy {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 4px;
}

.domain-mode-option-copy strong {
  font-size: 0.94rem;
}

.domain-mode-option-copy span {
  color: var(--ink-muted);
  font-size: 0.8rem;
  font-weight: 400;
  line-height: 1.45;
}

.domain-mode-option-badge {
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  background: rgb(63 157 106 / 14%);
  color: #1f6b45;
  font-size: 0.68rem;
  font-weight: 700;
  white-space: nowrap;
}

/* The selected radio can sit anywhere inside the choice fieldset, so
   `:has()` lets the related panel follow it without adding JavaScript to a
   two-state form. */
.domain-mode:has(#source-customer_owns:checked) .domain-search-fields,
.domain-mode:has(#source-we_register:checked) .domain-owned-fields {
  display: none;
}

.domain-mode-panel {
  padding-top: 26px;
  border-top: 1px solid var(--border);
}

.domain-mode-panel-heading {
  margin-bottom: 20px;
}

.domain-mode-panel-heading h2 {
  margin: 0;
  font-size: clamp(1.35rem, 1.15rem + 0.65vw, 1.65rem);
}

.domain-mode-panel-heading .text-muted {
  margin: 6px 0 0;
  max-width: 58ch;
}

.domain-search-field {
  margin-bottom: 0;
}

#domain-query.htmx-request {
  opacity: 0.6;
}

.domain-search-status {
  display: none;
  margin-top: 8px;
  color: var(--ink-muted);
  font-size: 0.8rem;
}

.domain-search-status.htmx-request,
.htmx-request .domain-search-status {
  display: block;
}

.domain-results-region {
  margin-top: 18px;
}

.domain-search-prompt {
  margin: 0;
  padding: 14px 16px;
  border-radius: var(--radius);
  background: var(--paper-sunken);
  color: var(--ink-muted);
  font-size: 0.84rem;
}

.domain-results-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

.domain-results-heading strong {
  color: var(--green-900);
  font-size: 0.9rem;
}

.domain-results-heading span {
  color: var(--ink-muted);
  font-size: 0.78rem;
}

.domain-results {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.domain-result {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
}

.domain-result-main,
.domain-result-action {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.domain-result-main {
  flex-wrap: wrap;
}

.domain-result-action {
  justify-content: flex-end;
}

.domain-result form {
  margin: 0;
}

.domain-result-name {
  min-width: 0;
  color: var(--green-900);
  font-weight: 700;
  overflow-wrap: anywhere;
}

.domain-result-state {
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  background: rgb(26 53 39 / 7%);
  color: var(--ink-muted);
  font-size: 0.68rem;
  font-weight: 700;
  white-space: nowrap;
}

.domain-result-state-available {
  background: rgb(63 157 106 / 14%);
  color: #1f6b45;
}

.domain-result-included {
  color: var(--ink-muted);
  font-size: 0.78rem;
  white-space: nowrap;
}

.domain-result-is-unavailable {
  background: var(--paper-sunken);
}

.domain-result-is-unavailable .domain-result-name {
  color: var(--ink-muted);
  font-weight: 600;
}

.domain-results-empty {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 16px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  color: var(--ink-muted);
}

.domain-results-empty strong {
  color: var(--green-900);
  font-size: 0.9rem;
}

.domain-results-empty span {
  font-size: 0.82rem;
}

.domain-dev-hint {
  margin: 12px 0 0;
  color: var(--ink-faint);
  font-size: 0.78rem;
  font-style: italic;
}

@media (max-width: 700px) {
  fieldset.domain-mode-options {
    grid-template-columns: 1fr;
  }

  .domain-result {
    grid-template-columns: 1fr;
  }

  .domain-result-action {
    justify-content: space-between;
  }

  .domain-results-heading {
    align-items: flex-start;
    flex-direction: column;
    gap: 3px;
  }
}

@media (max-width: 460px) {
  fieldset.domain-mode-options.form-field .domain-mode-option {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .domain-mode-option-badge {
    grid-column: 2;
    justify-self: start;
  }

  .domain-result-action {
    align-items: stretch;
    flex-direction: column;
  }

  .domain-result-action .btn,
  .domain-result-action form {
    width: 100%;
  }
}

/* ---------- Content area ---------- */

.site-content {
  flex: 1;
  position: relative;
}

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

.site-footer {
  position: relative;
  isolation: isolate;
  border-top: none;
  background: var(--green-950);
  color: rgb(246 240 228 / 78%);
  padding: clamp(48px, 6vw, 72px) 0 28px;
  margin-top: 0;
}

/* Brand block on the left, grouped link columns on the right. Replaces a
   single undifferentiated row of five links -- with the site now having
   marketing pages, an articles section and a signed-in area, one flat row
   gave no sense of what belongs to what. */
.footer-top {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 2fr);
  gap: 40px;
  align-items: start;
  padding-bottom: 32px;
  border-bottom: 1px solid var(--border);
}

.footer-brand {
  max-width: 34ch;
}

/* WebHostGB's own brand logo. Note this is *not* a customer's site mark --
   that's `.site-avatar`, deliberately renamed after the two shared this
   class name and the avatar's bordered near-white tile leaked onto the
   logo here. */
.site-logo {
  display: inline-flex;
  /* The lockup's wordmark is `currentColor` (see `ui::logo`), so the
     colour it takes is set here rather than baked into the artwork. */
  color: var(--green-900);
}

.site-logo .brand-lockup {
  height: 32px;
  width: auto;
  display: block;
}

.footer-blurb {
  margin: 14px 0 0;
  font-size: 0.88rem;
  color: var(--ink-muted);
  line-height: 1.6;
}

.footer-price {
  margin: 10px 0 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--green-800);
}

.footer-links {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 24px 20px;
}

.footer-link-group {
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.footer-link-heading {
  margin: 0 0 2px;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
}

.footer-link-group a {
  color: var(--green-900);
  font-size: 0.88rem;
  font-weight: 500;
  text-decoration: none;
}

.footer-link-group a:hover {
  color: var(--rust-700);
}

/* Set apart from the links above it, and quieter. Somebody signing in for
   the first time should not weigh this against "Sign in" -- it is only
   meaningful to clients who predate the current platform, and to everyone
   else it should read as a footnote rather than a choice.

   Written as `.footer-link-group a.footer-legacy-link` rather than the
   bare class plus `!important`. `.footer-link-group a` is (0,1,1) and a
   lone class is (0,1,0), so the base rule would win -- and reaching for
   `!important` to settle that is how a stylesheet stops being predictable.
   One more qualifier does the same job honestly.

   `margin-top` rather than relying on the column's `gap`: gap and margin
   stack, which is what gives this one link its extra separation without
   touching the spacing of the others. */
.footer-link-group a.footer-legacy-link {
  margin-top: 6px;
  padding-top: 11px;
  border-top: 1px solid var(--border);
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--ink-muted);
}

.footer-link-group a.footer-legacy-link:hover {
  color: var(--rust-700);
}

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding-top: 20px;
}

.footer-bottom .text-muted {
  margin: 0;
  font-size: 0.82rem;
}

.footer-legal-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
}

.footer-legal-nav a {
  color: var(--ink-muted);
  font-size: 0.82rem;
  font-weight: 500;
}

.footer-legal-nav a:hover {
  color: var(--rust-700);
}

/* ---------- Marketing pages (pricing, etc.) ----------
   Marketing pages (see base::marketing) render *without* one shared page-
   level `.container` -- `main.site-content` puts each page's markup
   straight in, so a page is free to mix full-bleed colour sections with
   normal reading-width sections wrapped in their own `div.container`.

   Every distinct piece of content is its own `<section class="msection">`
   -- never two unrelated pieces of content sharing one section -- so each
   block can carry its own background and identical top/bottom padding.
   `.msection` is full-bleed (background paints edge to edge, same idea as
   `.band`/`.hero`/`.cta-band`) with a `div.container` nested inside for
   the actual 1120px reading width. One padding value, `padding: 64px 0`,
   applies to every `.msection` on every marketing page -- no per-component
   overrides, which is what made spacing read as inconsistent before.

   `.msection-intro` is the one padding variant, reserved for the page-
   opening intro directly under the header (`.info-hero`, or a page's own
   hero block): more top space since nothing sits above it, less bottom
   since a heading leads straight into the next section.

   `.msection-tint` alternates the background to a lighter tone
   (`--paper-raised` instead of the base `--paper`), applied to every other
   `.msection` down a page -- the same alternating-bands pattern as
   godaddy.com, instead of one flat page-length paper background.

   Full-bleed colour bands (`.hero`, `.band`, `.cta-band`) are a separate,
   intentionally louder component with their own fixed padding (see their
   own comments further up this file) and aren't part of the `.msection`
   alternation -- their colour already reads as a break in the rhythm on
   its own. */
.msection {
  width: 100%;
  padding: 64px 0;
}

.msection-intro {
  padding: 96px 0 64px;
}

.msection-tint {
  background: var(--paper-tint);
}

.pricing-card-wrap {
  display: flex;
  justify-content: center;
}

.pricing-card {
  max-width: 420px;
  width: 100%;
  text-align: center;
}

/* Home's teaser card gets a bit more presence than the plain `.card`
   surface -- a rust-tinted gradient, a matching border and a floating
   "everything included" tag, so it reads as the plan itself rather than
   just another content card sitting in the page. Additive on top of
   `.pricing-card` (not a replacement) so Pricing's own summary panel,
   which uses `.pricing-card` alone inside `.pricing-card-expanded`,
   is unaffected. */
.pricing-card-highlight {
  position: relative;
  margin-top: 14px;
  border: 1px solid var(--rust-200);
  background: linear-gradient(160deg, var(--paper-raised) 55%, var(--rust-tint) 220%);
  box-shadow: 0 32px 60px -32px rgba(156, 78, 45, 0.38);
}

.pricing-card-highlight::before {
  content: "Everything included";
  position: absolute;
  top: -14px;
  left: 50%;
  transform: translateX(-50%);
  padding: 6px 18px;
  border-radius: 999px;
  background: var(--rust-500);
  color: var(--paper-raised);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Pricing page's own, wider take on `.pricing-card` -- Home's compact
   teaser card stays as-is (`.pricing-card` alone), but the Pricing page
   itself is the one place a visitor is actively deciding, so it earns a
   fuller card: price/CTA on the left, everything included laid out on
   the right as wide horizontal rows (icon-led chips flowing left to
   right) rather than three narrow squeezed-together columns. */
.pricing-card-expanded {
  max-width: 1040px;
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: 48px;
  text-align: left;
  padding: 48px;
}

.pricing-card-summary {
  text-align: center;
  padding-right: 48px;
  border-right: 1px solid var(--border);
}

.pricing-card-summary .btn {
  width: 100%;
}

.pricing-card-note {
  margin-top: 16px;
  font-size: 0.82rem;
  color: var(--ink-muted);
}

/* Stacked full-width rows instead of a 3-up column grid -- each group's
   items flow horizontally within its row (see the `.check-list`
   override below) so the whole card reads left-to-right, not as three
   cramped vertical lists side by side. */
.pricing-card-details {
  display: flex;
  flex-direction: column;
}

.pricing-card-group {
  display: flex;
  align-items: baseline;
  gap: 28px;
  padding: 20px 0;
  border-bottom: 1px solid var(--border);
}

.pricing-card-group:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.pricing-card-group:first-child {
  padding-top: 0;
}

.pricing-card-group h3 {
  flex: 0 0 150px;
  font-size: 1rem;
  margin: 0;
  color: var(--green-900);
}

.pricing-card-group .check-list {
  flex: 1;
  flex-direction: row;
  flex-wrap: wrap;
  margin-bottom: 0;
  gap: 12px 28px;
  font-size: 0.88rem;
}

.pricing-card-group .check-list li {
  flex: 0 0 auto;
}

@media (max-width: 860px) {
  .pricing-card-expanded {
    grid-template-columns: 1fr;
    padding: 32px 24px;
  }

  .pricing-card-summary {
    padding-right: 0;
    padding-bottom: 32px;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }

  .pricing-card-group {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .pricing-card-group h3 {
    flex-basis: auto;
  }
}

/* Bento-style breakdown of what's on the site -- a real photo per page
   (mirroring the showcase gallery's tinted-photo treatment) rather than
   plain text cards, with two large tiles (Home, About) and three
   smaller ones (Services, Gallery, Contact) so it reads as designed
   rather than five identical boxes in a row. */
.page-strip {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 20px;
}

.page-strip-item {
  position: relative;
  grid-column: span 3;
  min-height: 260px;
  display: flex;
  align-items: flex-end;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background-size: cover;
  background-position: center;
}

.page-strip-item::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(20, 18, 10, 0) 32%, rgba(20, 18, 10, 0.85) 100%);
}

.page-strip-item-sm {
  grid-column: span 2;
  min-height: 200px;
}

.page-strip-item-inner {
  position: relative;
  z-index: 1;
  padding: 24px;
}

.page-strip-item-inner .item-badge {
  margin-bottom: 10px;
}

.page-strip-item-inner h3 {
  margin: 0 0 6px;
  font-size: 1.15rem;
  color: #fff;
}

.page-strip-item-inner p {
  margin: 0;
  max-width: 34ch;
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.88);
}

@media (max-width: 860px) {
  .page-strip {
    grid-template-columns: repeat(2, 1fr);
  }

  .page-strip-item,
  .page-strip-item-sm {
    grid-column: span 1;
  }
}

@media (max-width: 520px) {
  .page-strip {
    grid-template-columns: 1fr;
  }
}

.price-tag {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
  /* Combined with the `.eyebrow`/`.text-muted`/`.check-list` margins below,
     every gap inside `.pricing-card` works out to the same 24px -- these
     used to be a mismatched 28/16/24/36px in sequence. */
  margin: 4px 0 24px;
}

.price-amount {
  font-family: var(--font-display);
  font-size: 3rem;
  font-weight: 600;
  color: var(--green-900);
}

.price-period {
  color: var(--ink-muted);
  font-size: 1rem;
}

.pricing-card .text-muted {
  margin-bottom: 24px;
}

.pricing-card .btn {
  width: 100%;
  margin-top: 0;
}

.check-list {
  list-style: none;
  margin: 0 0 24px;
  padding: 0;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.check-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 0.92rem;
  color: var(--green-900);
}

.check-list li::before {
  content: "✓";
  color: var(--rust-600);
  font-weight: 700;
  flex-shrink: 0;
}

/* `.check-list`'s muted counterpart -- an "x" instead of a checkmark, for
   the losing side of a `.compare-card` comparison (see `.compare-grid`
   below) rather than a list of things included in a plan. */
.cross-list {
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.cross-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 0.92rem;
  color: var(--ink-muted);
}

.cross-list li::before {
  content: "\2715";
  color: var(--ink-faint);
  font-weight: 700;
  flex-shrink: 0;
}

/* A two-card side-by-side comparison ("the DIY way" vs. "WebHostGB") --
   `.compare-card-highlight` is the WebHostGB side: a rust border and a
   small lift so it reads as the clear pick rather than two neutral
   options presented as equals. */
.compare-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
  align-items: stretch;
}

.compare-card {
  padding: 32px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.compare-card h3 {
  margin-bottom: 18px;
}

.compare-card-highlight {
  border: 2px solid var(--rust-500);
}

.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

.feature-grid-item h3 {
  margin-bottom: 10px;
}

/* Just a width constraint now -- the FAQ section's title lives in the
   standard `.section-heading` above it (see `ui::marketing::section`),
   not inside `.faq` itself. */
.faq {
  max-width: 720px;
  margin: 0 auto;
}

.faq-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.faq-item {
  background: var(--paper-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 4px 20px;
}

.faq-item summary {
  padding: 16px 0;
  cursor: pointer;
  font-weight: 600;
  color: var(--green-900);
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

.faq-item summary::after {
  content: "+";
  font-size: 1.3rem;
  color: var(--rust-600);
  transition: transform var(--transition);
  flex-shrink: 0;
}

.faq-item[open] summary::after {
  transform: rotate(45deg);
}

.faq-item p {
  padding-bottom: 18px;
  margin: 0;
}

/* Full-bleed, same as `.hero`/`.band` -- combine with a nested
   `div.container` to center the heading/paragraph/button at the normal
   marketing width. Tier 3 padding, see the "Marketing pages" comment. */
.cta-band {
  width: 100%;
  background: linear-gradient(160deg, var(--green-900), var(--green-950));
  padding: 56px 0 112px;
  text-align: center;
}

.cta-band h2 {
  color: var(--paper-raised);
}

.cta-band p {
  color: rgba(244, 237, 224, 0.72);
  margin-bottom: 24px;
}

/* ---------- Content bands (color blocks, presscircle.co-inspired) ----------
   A page built entirely from `.card`/`.feature-grid` boxes on the same
   paper background starts to feel like one long stack of identical
   tiles. These give a marketing page two more section "shapes" to reach
   for: a full-color block (dark or tinted) and a hairline-separated row
   list -- so a page reads as a sequence of distinct moments rather than
   a repeated card grid all the way down. */

/* A full-bleed colour block, same dark gradient as `.hero`/`.cta-band` so
   a page can drop in another one without a new palette -- spans the full
   viewport width; combine with a nested `div.container` (plus
   `.band-split`/`.quote-block` on that inner container) to center the
   actual content at the normal marketing width. Tier 3 padding, see the
   "Marketing pages" comment further down this file. */
.band {
  width: 100%;
  padding: 56px 0 112px;
}

.band-dark {
  background: linear-gradient(160deg, var(--green-900), var(--green-950));
}

.band-dark h2 {
  color: var(--paper-raised);
}

.band-dark p {
  color: rgba(244, 237, 224, 0.78);
}

.band-dark .eyebrow {
  color: var(--rust-400);
}

.band-dark .eyebrow::before {
  background: var(--rust-400);
}

/* Asymmetric two-column split -- a short label+headline on one side, a
   supporting paragraph or two on the other, rather than everything
   centered. The shape presscircle.co uses for its darker sections. */
.band-split {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: clamp(24px, 5vw, 56px);
  align-items: center;
}

.band-split h2 {
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  margin-bottom: 0;
}

.band-split .band-copy p {
  margin-bottom: 14px;
}

.band-split .band-copy p:last-child {
  margin-bottom: 0;
}

/* A quieter, tinted alternative to `.band-dark` -- for a pull-quote or
   standalone statement that should stand apart from the page background
   without the weight of the dark gradient. */
.band-tint {
  background: var(--paper-sunken);
}

.quote-block blockquote {
  margin: 0 0 28px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(1.5rem, 3.4vw, 2.2rem);
  line-height: 1.3;
  color: var(--green-950);
}

.quote-support {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
}

.quote-support p {
  color: var(--ink-muted);
  font-size: 0.95rem;
  line-height: 1.65;
  margin: 0;
}

/* A numbered list of rows separated by a hairline rule instead of a
   bordered card grid -- for a step-by-step or checklist section, so it
   reads as a different shape from a `.process-card`/`.feature-grid` grid
   used elsewhere on the same page. Lives inside its own `.msection`
   together with the `.section-heading` that introduces it, so it needs no
   margin of its own -- the section's padding provides the surrounding
   space. */
.numbered-row {
  display: grid;
  grid-template-columns: 56px 1fr;
  align-items: baseline;
  gap: 20px;
  padding: 22px 0;
  border-bottom: 1px solid var(--border);
}

.numbered-rows .numbered-row:last-child {
  border-bottom: none;
}

.numbered-row-index {
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: var(--rust-600);
}

.numbered-row h3 {
  margin-bottom: 6px;
}

.numbered-row p {
  margin: 0;
}

/* A photo-forward variant of the hero -- splits the dark panel into a
   text column and a real photograph, rather than the plain centered
   panel alone. Text becomes left-aligned in this variant, matching the
   asymmetric layout instead of fighting it with centered type. */
.hero-split {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  align-items: stretch;
  gap: clamp(24px, 5vw, 56px);
}

.hero-split .hero-panel {
  text-align: left;
}

.hero-split .hero-actions,
.hero-split .trust-strip {
  justify-content: flex-start;
}

/* The photo now reads as its own card sitting on the flat colour band,
   rather than a clipped half of one shared rounded box -- so it keeps its
   own radius/shadow now that `.hero-split` no longer clips both columns
   into a single card. */
.hero-split .hero-photo {
  background-size: cover;
  background-position: center;
  min-height: 100%;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}

@media (max-width: 860px) {
  .hero-split {
    grid-template-columns: 1fr;
  }

  .hero-split .hero-photo {
    min-height: 220px;
    order: -1;
  }
}

/* A two-column image + text section for a real supporting photograph
   next to a paragraph or two -- the plain-paper counterpart to
   `.band-split` (which is a solid colour block), used wherever a page
   needs an actual photo rather than another card or block of colour.
   `.media-row-reverse` puts the photo on the right instead of the left
   without needing a second, mirrored markup pattern. */
.media-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 5vw, 56px);
  align-items: center;
}

.media-row-image {
  order: 1;
}

.media-row-text {
  order: 2;
}

.media-row-reverse .media-row-image {
  order: 2;
}

.media-row-reverse .media-row-text {
  order: 1;
}

.media-row-image img {
  width: 100%;
  height: 100%;
  min-height: 300px;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  display: block;
}

/* A landing page opening on a `hero_split` block draws its headline as
   the document's `<h1>` rather than an `<h2>` (see `blocks::render_page`).
   Without this it would jump to the full hero size -- up to 3.6rem --
   inside a two-column media row built for section headings. The level is
   a document-structure decision; the size should not change with it. */
.media-row-text h1 {
  font-size: clamp(1.7rem, 1.5rem + 1vw, 2.3rem);
}

.media-row-text p {
  margin-bottom: 14px;
}

.media-row-text p:last-child {
  margin-bottom: 0;
}

@media (max-width: 720px) {
  .media-row {
    grid-template-columns: 1fr;
  }

  .media-row-image img {
    min-height: 220px;
  }
}

/* Small colour-block badge for a `.feature-grid-item` -- three items in a
   row reading identically (same white card, same border) is exactly the
   "everything is just a card" flatness to avoid; a coloured badge per
   item gives each one its own accent without changing the grid shape. */
.item-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  margin-bottom: 16px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--paper-raised);
}

.item-badge-a {
  background: var(--green-800);
}

.item-badge-b {
  background: var(--rust-500);
}

.item-badge-c {
  background: var(--green-500);
}

/* A row of headline numbers backing up a claim made in the surrounding
   copy (years trading, revisions included, pages designed, etc.) -- shared
   by the homepage and About page rather than each hand-rolling its own
   "stat card" shape. Deliberately plainer than `.feature-grid`/`.card`
   (no drop shadow, quieter border) so four/five of these in a row read as
   one calm strip of facts, not five competing cards. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 24px;
  text-align: center;
}

.stat-item {
  padding: 32px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
}

.stat-number {
  display: block;
  font-family: var(--font-display);
  font-size: 2.4rem;
  font-weight: 600;
  color: var(--green-900);
  margin-bottom: 6px;
}

/* Also restyled further down in the "In-app polish" block, which sets the
   size and casing but not `display` -- so this rule is not redundant and
   deleting it would silently un-block the label. */
.stat-label {
  display: block;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/* "Who it's for" -- a photo bento grid of the kinds of business the
   service suits (two wide tiles, four narrower ones, same recipe as
   Pricing's `.page-strip`/How it works' `.workflow-grid`: a real photo
   per category with a gradient overlay carrying the title and examples)
   rather than plain bordered text tiles, so the section shows the range
   of businesses instead of just listing it. Only used on Home, so this
   can be a full bento treatment without affecting any other page. */
.who-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 20px;
}

.who-item {
  position: relative;
  grid-column: span 6;
  min-height: 260px;
  display: flex;
  align-items: flex-end;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background-size: cover;
  background-position: center;
  transition: transform var(--transition);
}

.who-item:hover {
  transform: translateY(-3px);
}

.who-item::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(15, 13, 8, 0.12) 0%, rgba(15, 13, 8, 0.6) 48%, rgba(15, 13, 8, 0.94) 100%);
}

.who-item-sm {
  grid-column: span 3;
  min-height: 220px;
}

.who-item-inner {
  position: relative;
  z-index: 1;
  padding: 24px;
}

.who-item-title {
  display: block;
  margin-bottom: 6px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.1rem;
  color: #fff;
}

.who-item-examples {
  display: block;
  max-width: 30ch;
  font-size: 0.82rem;
  color: rgba(255, 255, 255, 0.88);
}

@media (max-width: 960px) {

  .who-item,
  .who-item-sm {
    grid-column: span 6;
  }
}

@media (max-width: 560px) {

  .who-item,
  .who-item-sm {
    grid-column: span 12;
  }
}

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

   One header for every screen inside the account, rendered by
   `ui::page_header`. It replaced four: `.dashboard-header` (breadcrumb and
   an `h1`), `.page-head-row` (title and a right-hand action, no
   breadcrumb), `.site-detail-head` (the same plus an avatar and a status
   pill) and the support thread's own `.support-head` -- along with two
   screens that had no header block at all.

   A header earns its keep by being identical everywhere: that is what lets
   a reader learn once where the back link lives and stop looking for it.
   Four near-identical hand-built ones could never manage that, which is why
   this is a component and not a convention. */

/* ---------- Application shell ----------

   The signed-in app and the admin area, from `layout::base::render_app`.
   Both used to be the marketing layout with app content poured into its
   centred column: a sticky marketing header above, a marketing footer
   below, and a navigation list squeezed inside the same measure as a blog
   post. That reads as a website with a nav on it rather than an
   application -- the chrome runs horizontally, the working area is narrow,
   and there are two competing places to look for where you are.

   Here the sidebar is pinned to the edge of the viewport with its own
   surface, and content takes the rest of the window. Nothing is capped to
   a container. */

.app-body {
  /* The marketing background gradient belongs to pages that are selling
     something; behind an application it just makes the content area look
     unfinished. */
  background-image: none;
  background: var(--paper-tint);
}

/* Column counts are stated by the markup (`has-rail`/`has-panel`), not
   worked out here. Each combination needs a different
   `grid-template-columns`, and inferring it with `:has()` would silently
   reserve a column's width wherever that selector isn't supported.

   The default is the panel-only case, which is what the admin area uses
   and what every screen looked like before the rail existed. */
.app-shell {
  display: grid;
  grid-template-columns: 264px minmax(0, 1fr);
  min-height: 100vh;
  /* Both track sizes are lengths, so the column itself animates -- which is
     the only way to smooth this. `display: none` on a column can't be
     transitioned at all, so the content would have snapped across the
     window the instant the box was ticked. */
  transition: grid-template-columns 0.2s ease;
}

/* Rail alone: account-level screens, where there is no context to put in a
   panel. The content simply gets the width back. */
.app-shell.has-rail {
  grid-template-columns: var(--app-rail-w) minmax(0, 1fr);
}

.app-shell.has-rail.has-panel {
  grid-template-columns: var(--app-rail-w) var(--app-panel-w) minmax(0, 1fr);
}

/* ---------- Mobile drawer ----------

   A checkbox and two labels, no script. Every navigation in this app is a
   full page load, so the box resets on the way out -- there is no "menu
   still open over the page you just opened" bug to write, and the drawer
   cannot be broken by a blocked script, which matters because on a phone
   the sidebar is the only navigation there is.

   All three parts are inert above the breakpoint. */

/* Off-screen rather than `display: none`, so it stays focusable and the
   toggle is reachable by keyboard. */
.app-drawer-state {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.app-drawer-backdrop {
  display: none;
}

/* Desktop collapse. The toggle now closes the *panel* and leaves the rail
   standing -- collapsing the rail too would take away the five permanent
   destinations, which is the one thing this layout exists to keep in
   place. Zero-width tracks rather than fewer tracks: going from three to
   two is not an interpolatable change, so it would jump. */
.app-drawer-state:checked~.app-shell.has-rail.has-panel {
  grid-template-columns: var(--app-rail-w) 0 minmax(0, 1fr);
}

/* The admin area has no rail, so there the toggle still closes its only
   column -- same meaning, one fewer column to keep. */
.app-drawer-state:checked~.app-shell:not(.has-rail) {
  grid-template-columns: 0 minmax(0, 1fr);
}

/* Hidden only once the column has finished closing. Without the delay the
   panel would vanish on the first frame and the content would slide into
   an already-empty space; without `visibility` at all, a zero-width panel
   would keep its links in the tab order -- a keyboard trap in something
   the user has explicitly put away. */
.app-drawer-state:checked~.app-shell .app-panel {
  visibility: hidden;
  transition: visibility 0s linear 0.2s;
}

.app-drawer-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-right: 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--paper-raised);
  cursor: pointer;
}

.app-drawer-icon {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 18px;
}

.app-drawer-icon span {
  height: 2px;
  border-radius: 2px;
  background: var(--green-900);
}

/* The checkbox is invisible, so its focus ring has to be drawn on the
   label -- otherwise tabbing to the menu shows nothing at all. */
.app-drawer-state:focus-visible~.app-shell .app-drawer-toggle,
.app-drawer-toggle:focus-visible {
  outline: 2px solid var(--rust-500);
  outline-offset: 2px;
}

/* Sticky with its own scroll rather than `position: fixed`, so the grid
   still reserves the column and no content ever slides under it. A long
   nav (admin's per-site page list) scrolls inside the sidebar instead of
   forcing the page taller. */
/* Sticky with its own scroll rather than `position: fixed`, so the grid
   still reserves the column and no content ever slides under it. Both nav
   columns work this way. */
.app-rail,
.app-panel {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  /* Contents get clipped as a column narrows rather than reflowing into an
     ever-thinner column on every frame of the collapse. */
  overflow-x: hidden;
  border-right: 1px solid var(--border);
}

/* The rail is the darker of the two, so the permanent column reads as the
   edge of the application and the panel reads as part of the page it
   belongs to. */
.app-rail {
  background: var(--paper-tint);
}

.app-panel {
  background: var(--paper-raised);
}

.app-rail-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  min-height: 100%;
  padding: 18px 8px;
}

.app-panel-inner {
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-height: 100%;
  padding: 22px 16px;
}

/* Both hold their shape while a track closes -- left to fill a shrinking
   column, the labels would rewrap on every frame. Desktop only: below the
   breakpoint these are a drawer of a different width. */
@media (min-width: 901px) {

  .app-rail-inner {
    width: var(--app-rail-w);
  }

  .app-panel-inner {
    width: var(--app-panel-w);
  }
}

.app-rail-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4px;
}

.app-rail-nav {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 100%;
}

/* Icon above label, so the whole thing fits 76px and still names itself.
   An icon-only rail is a rail you have to learn before you can use it, and
   these are read by people who sign in once a fortnight. */
.app-rail-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 9px 4px;
  border-radius: var(--radius);
  color: var(--ink-muted);
  font-size: 0.66rem;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  transition: background-color var(--transition), color var(--transition);
}

.app-rail-item:hover {
  background: var(--paper-raised);
  color: var(--rust-700);
}

.app-rail-item.is-current {
  background: var(--green-900);
  color: var(--paper);
}

.app-rail-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 20px;
}

/* Any SVG in a nav slot gets a size, whether or not it carried one.
   The admin rail rendered nothing at all because `marketing::icon` emits
   no width, height or class, and an SVG with none of those has no
   intrinsic size inside a flex container -- it collapsed to zero. The
   icons themselves are fixed now; this stops the next one repeating it. */
.app-rail-icon svg {
  width: 20px;
  height: 20px;
}

.app-nav-mark svg {
  width: 18px;
  height: 18px;
}

.app-rail-label {
  display: block;
  /* Two words at 66px is one line each; without this "All websites" breaks
     mid-word. */
  overflow-wrap: break-word;
  hyphens: auto;
}

.app-main {
  min-width: 0;
  background: var(--paper);
}

/* `margin-inline: auto` is what centres the measure in the column. Without
   it the content pinned itself to the left edge and the whole window's
   spare width piled up on the right, which on a wide monitor reads as a
   layout that has come apart rather than one that is deliberately narrow. */
.app-main-inner {
  max-width: 1240px;
  margin-inline: auto;
  padding: 0 40px 72px;
}

/* ---------- Top bar ----------

   Inside the content column rather than across the full width, so the
   sidebar keeps the top-left corner for the brand and the two never
   compete for the same spot.

   Sticky, and translucent with a blur, so scrolling a long page never
   leaves the account menu and the way out somewhere off-screen. */
.app-topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  min-height: var(--app-topbar-h);
  padding: 0 40px;
  background: rgb(250 243 230 / 82%);
  backdrop-filter: blur(10px);
}

/* Stated rather than inherited. The marketing copy of this component lives
   inside `.site-nav`, which is `display: none` below 1020px -- so the whole
   account menu disappears on a phone there, and the mobile sheet carries
   those links instead. In the app shell there is no sheet: this menu is
   the only route to Log out at any width. */
.app-account {
  display: block;
}

.app-account summary {
  max-width: min(340px, 34vw);
  padding: 4px 8px 4px 4px;
}

/* The initial remains a useful avatar, but it should not be the only clue
   about which account is active. The full address remains in the markup and
   title; only addresses too long for the top bar receive an ellipsis. */
.account-summary-email {
  display: block;
  min-width: 0;
  max-width: 24ch;
  overflow: hidden;
  color: var(--green-900);
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The search trigger, centred in the bar.

   `margin-inline: auto` on a flex child with `justify-content: flex-end`
   siblings is what centres it while the account menu stays pinned right --
   simpler than a three-column grid for two items. */
.app-search {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-inline: auto;
  min-width: min(360px, 40vw);
  padding: 8px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--paper-raised);
  color: var(--ink-muted);
  font-family: inherit;
  font-size: 0.86rem;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--transition), color var(--transition);
}

.app-search:hover {
  border-color: var(--border-strong);
  color: var(--green-900);
}

.app-search:focus-visible {
  outline: 2px solid var(--rust-500);
  outline-offset: 2px;
}

.app-search-icon {
  display: flex;
  flex: 0 0 auto;
  color: var(--ink-faint);
}

.app-search-icon svg {
  width: 16px;
  height: 16px;
}

.app-search-label {
  flex: 1 1 auto;
}

/* Shown, not hidden: a shortcut nobody has been told about may as well not
   exist, and printing it on the control is how people learn it. */
.app-search-kbd {
  flex: 0 0 auto;
  padding: 2px 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--paper);
  font-family: inherit;
  font-size: 0.72rem;
  color: var(--ink-faint);
}

@media (max-width: 700px) {

  /* The label and shortcut go; the icon alone still reads as search, and
     the bar has no room for three things on a phone. */
  .app-search {
    min-width: 0;
    padding: 8px;
  }

  .app-search-label,
  .app-search-kbd {
    display: none;
  }

  .app-account summary {
    max-width: 190px;
  }

  .account-summary-email {
    max-width: 14ch;
  }
}

@media (max-width: 420px) {
  .app-account summary {
    max-width: 160px;
  }

  .account-summary-email {
    max-width: 10ch;
  }
}

/* The panel is anchored to the menu's right edge (see `.account-menu-panel`)
   and the menu is the last thing in the bar, so it opens inward rather than
   off the side of the window. */
.app-account .account-menu-panel {
  min-width: 220px;
}

.account-menu-email {
  margin: 0 0 6px;
  padding: 0 10px 8px;
  border-bottom: 1px solid var(--border);
  font-size: 0.82rem;
  color: var(--green-900);
  /* An address is one unbroken token and would otherwise widen the panel
     to fit the longest one someone happens to have. */
  overflow-wrap: anywhere;
}

/* First stop for a keyboard user, since the sidebar is the same on every
   screen and tabbing through it each time is the cost of putting
   navigation first in source order. */
.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 20;
  padding: 10px 16px;
  border-radius: var(--radius);
  background: var(--green-900);
  color: var(--paper);
  font-size: 0.9rem;
}

.skip-link:focus {
  left: 12px;
  top: 12px;
}

/* ---------- Panel contents ---------- */

.app-site {
  padding: 0 4px;
}

.app-site-current {
  display: flex;
  /* Top, not centre. The text block is two lines when a name is long and
     one when it isn't, so centring made the mark drift up and down the
     button depending on whose site you were looking at. Aligned to the
     top, the mark and the first line of the name always share a baseline
     row. */
  align-items: flex-start;
  gap: 9px;
  padding: 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper);
  list-style: none;
}

/* Only the `<summary>` version is clickable. With one website the same
   block renders as a plain `div` that names the site and does nothing --
   and it was carrying a pointer cursor and a hover border, which is a
   control that promises something it can't do. Most customers have one
   website, so that was the common case. */
summary.app-site-current {
  cursor: pointer;
}

.app-site-current::-webkit-details-marker {
  display: none;
}

.app-site-switcher[open] .app-site-current,
summary.app-site-current:hover {
  border-color: var(--border-strong);
}

.app-site-mark {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius);
  background: var(--green-900);
  color: var(--paper);
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
}

.app-site-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  min-width: 0;
  /* Takes the space the mark and chevron don't, so a long name wraps
     inside the button rather than pushing the chevron out of it. */
  flex: 1;
}

.app-site-text strong {
  font-size: 0.86rem;
  line-height: 1.3;
  /* Two lines, then an ellipsis.

     `overflow-wrap: anywhere` was here for domains, which have no spaces
     to break at -- but applied to a business name it breaks mid-word, so
     "Ashworth Pike Surveyors" could come apart as "Ashworth Pike Surve /
     yors". A name wraps at its spaces on its own; this only steps in for
     one long enough to push the nav down the column. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* A domain is an unbroken technical identifier, unlike the business names
   the site switcher normally carries. Keep it inside the panel and make the
   owning website read as quiet supporting navigation beneath it. */
.app-domain-current .app-site-text strong {
  overflow-wrap: anywhere;
}

.app-domain-current .app-site-text a {
  color: var(--ink-muted);
  font-size: 0.76rem;
  line-height: 1.3;
}

.app-domain-current .app-site-text a:hover {
  color: var(--rust-700);
}

.app-site-chevron {
  flex: 0 0 auto;
  /* Nudged down to sit against the name's line rather than its box top,
     now that the row is top-aligned. */
  margin-top: 2px;
  color: var(--ink-faint);
  font-size: 0.85rem;
  line-height: 1;
}

/* The status pill, scoped down for this one place.

   Everywhere else a status pill sits in a page header or a table row with
   room around it. Here it is the third thing in a 244px column, after a
   28px disc and beside a chevron -- at the full 0.72rem with 12px of
   horizontal padding it was taking nearly half the width the name needed,
   which is what made the block look crowded. Same colours and the same
   uppercase treatment, two sizes down. */
.app-site-current .status-pill {
  padding: 1px 7px;
  font-size: 0.6rem;
  letter-spacing: 0.05em;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Overlays the nav rather than pushing it down.

   In normal flow, opening the switcher shoved every section link several
   rows lower -- so the act of checking which site you're on moved the
   thing you were about to click. Absolute, with the shadow that says it's
   a layer rather than part of the column. */
.app-site-menu {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 6px);
  z-index: 10;
  display: flex;
  flex-direction: column;
  max-height: 60vh;
  overflow-y: auto;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  box-shadow: var(--shadow-dropdown);
}

/* The positioning context for the menu above. */
.app-site-switcher {
  position: relative;
}

.app-site-menu a {
  padding: 8px 10px;
  border-radius: var(--radius);
  color: var(--green-800);
  font-size: 0.86rem;
  text-decoration: none;
}

.app-site-menu a:hover {
  background: var(--paper-tint);
  color: var(--rust-700);
}

.app-nav-group+.app-nav-group {
  margin-top: 4px;
}

.app-nav-group-label {
  margin: 0 0 6px;
  padding: 0 10px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.app-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.app-nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--green-800);
  font-family: inherit;
  font-size: 0.88rem;
  font-weight: 500;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.app-nav-item:hover {
  background: var(--paper-tint);
  color: var(--rust-700);
}

.app-nav-item.is-current {
  background: var(--green-900);
  color: var(--paper);
  font-weight: 600;
}

.app-nav-item.is-current:hover {
  background: var(--green-800);
  color: var(--paper);
}

/* The item's left slot: a line icon, a wizard step number, or nothing.

   Fixed width and always rendered, including when it's empty (see
   `layout::app_nav::Mark`). The page list has no marks, and letting those
   labels sit 28px left of every other label in the column would look
   worse than having no icons anywhere. */
.app-nav-mark {
  flex: 0 0 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 18px;
  color: var(--ink-muted);
  transition: color var(--transition);
}

/* Quieter than the label at rest, so the column reads as words with marks
   beside them rather than a row of symbols. Both lift together on hover,
   and on the current row the icon goes fully to the inverted colour --
   `stroke: currentColor` on the inline SVG is what makes that possible at
   all (see `ui::icon`). */
.app-nav-item:hover .app-nav-mark {
  color: var(--rust-600);
}

.app-nav-item.is-current .app-nav-mark {
  color: var(--paper);
}

/* Status pills on the selected row.

   Every `.status-*` tone is a dark ink on a near-transparent tint, which is
   correct on the paper background the pills were designed against and
   invisible on the dark green of a selected row. Measured against
   `--green-900`: alert 1.62:1, quiet 2.27:1, live 1.26:1 -- the "Awaiting
   review" pill disappeared completely and the rust one was barely there.

   So on the current row the whole pill inverts: a translucent white chip
   with paper-coloured text at 12.02:1. The tone is carried by the dot
   `.status-pill::before` draws rather than by the text colour, which is
   what lets all four tones stay distinguishable without any of them
   fighting the background. */
.app-nav-item.is-current .status-pill {
  background: rgb(250 243 230 / 16%);
  color: var(--paper);
}

.app-nav-item.is-current .status-live::before {
  background: #7fd4a3;
}

.app-nav-item.is-current .status-alert::before {
  background: var(--rust-300);
}

.app-nav-item.is-current .status-warn::before {
  background: #e8c268;
}

.app-nav-item.is-current .status-info::before {
  background: #9dc0f5;
}

.app-nav-item.is-current .status-quiet::before {
  background: rgb(250 243 230 / 55%);
}

/* `display: block` kills the descender gap an inline SVG inherits from the
   text baseline, and `flex-shrink: 0` keeps the drawing square if one of
   these is ever dropped into a container narrower than the slot it has
   here. Sizing stays on the element's own width/height attributes, so an
   icon is still the right size with the stylesheet blocked. */
.app-icon {
  display: block;
  flex-shrink: 0;
}

.app-nav-label {
  min-width: 0;
}

/* Step numbers sit in the same slot as the icons and are sized to match
   their optical weight -- a numeral at label size would be louder than any
   of the icons and pull the eye down the wizard rather than along it. */
.app-nav-step {
  font-size: 0.72rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}


@media (max-width: 900px) {

  /* One column, with both nav columns lifted out of the flow into a
     single drawer. Two 76px+244px columns beside the content is not a
     phone layout, and a rail alone would be five icons taking a fifth of
     the width on every screen.

     Every combination collapses to one track, so the shell doesn't need
     to know which columns exist down here. */
  .app-shell,
  .app-shell.has-rail,
  .app-shell.has-rail.has-panel,
  .app-drawer-state:checked~.app-shell,
  .app-drawer-state:checked~.app-shell.has-rail,
  .app-drawer-state:checked~.app-shell.has-rail.has-panel,
  .app-drawer-state:checked~.app-shell:not(.has-rail) {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The rail and the panel become the two halves of one sliding drawer:
     the rail on the left as a narrow strip, the panel filling the rest.
     Keeping them side by side rather than stacking preserves the
     distinction the layout is for -- the permanent five stay in one place
     while the context changes beside them, on a phone as on a desktop. */
  .app-rail,
  .app-panel {
    display: block;
    position: fixed;
    top: 0;
    bottom: 0;
    z-index: 40;
    height: auto;
    /* Its own width plus its border, so the right edge lands just past
       x = 0 rather than on it. `-100%` alone leaves the 1px border sitting
       exactly at the screen edge. A column offset from the left needs its
       offset added too -- see `.has-rail .app-panel` below. */
    transform: translateX(calc(-100% - 2px));
    /* Hidden once it has finished sliding, which is what stops the drop
       shadow bleeding back in: a translated element still paints, and the
       shadow reaches further than the border does. Delayed by the slide
       duration so the animation is still visible on the way out. */
    visibility: hidden;
    transition: transform var(--transition), visibility 0s linear var(--transition-time);
    box-shadow: var(--shadow-dropdown);
  }

  .app-rail {
    left: 0;
    width: var(--app-rail-w);
  }

  /* Sits immediately right of the rail when both are open, and at the
     screen edge when it is the only column (the admin area). */
  .app-panel {
    left: 0;
    width: min(280px, 76vw);
  }

  /* Offset by the rail's width, so hiding it means travelling its own
     width *plus* that offset. This was `-120%` for both columns, which is
     enough for a column at x = 0 and 20px short for one starting 76px in
     -- which is exactly the sliver of border that stayed on screen. */
  .has-rail .app-panel {
    left: var(--app-rail-w);
    width: min(280px, calc(84vw - var(--app-rail-w)));
    transform: translateX(calc(-100% - var(--app-rail-w) - 2px));
  }

  .app-drawer-state:checked~.app-shell .app-rail,
  .app-drawer-state:checked~.app-shell .app-panel,
  /* The offset panel's closed transform is more specific than the shared
     one, so its open state has to be too -- otherwise it stays put. */
  .app-drawer-state:checked~.app-shell.has-rail .app-panel {
    /* Undoes the desktop rule above: below the breakpoint `:checked`
       means open, not collapsed. Visibility comes back immediately, with
       no delay, so the columns are there for the whole slide in. */
    visibility: visible;
    transform: none;
    transition: transform var(--transition), visibility 0s;
  }

  /* Labels beside icons rather than under them, since the drawer has the
     width for it and a vertical strip of tiny captions is harder to read
     than a list. */
  .app-rail-inner {
    padding: 18px 6px;
  }

  .app-drawer-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 35;
    background: rgb(15 36 25 / 45%);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--transition), visibility var(--transition);
  }

  .app-drawer-state:checked~.app-shell .app-drawer-backdrop {
    opacity: 1;
    visibility: visible;
  }

  .app-main-inner {
    padding: 0 20px 56px;
  }

  .app-topbar {
    padding: 0 20px;
  }
}

.page-header {
  padding: 40px 0 12px;
}

/* Above the title, not beside it: the back link is about leaving this
   screen, and putting it on the title's row made it compete with the
   screen's primary action for the same corner.

   Scoped to `.page-header` for the specificity: `.breadcrumb-link` is
   defined much later in this file and sets its own `margin-bottom`, so a
   single-class rule here would lose on source order no matter what it
   said. Everything else about the link -- arrow, colour, hover -- is
   shared with the same component on public article pages and is
   deliberately left to `.breadcrumb-link`. */
.page-header .page-header-back {
  margin-bottom: 12px;
}

/* Title block on the left, actions on the right, wrapping to two rows on
   narrow screens rather than squeezing either. */
.page-header-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
}

/* `min-width: 0` so a long title -- a ticket subject, a domain name --
   wraps instead of pushing the actions off the row. */
.page-header-main {
  min-width: 0;
}

/* Avatar, title and status pill on one line. Centred rather than
   baseline-aligned: the avatar is a 44px tile and the pill is 24px tall,
   and neither has a baseline worth aligning to. */
.page-header-titles {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
}

.page-header-title {
  margin: 0;
  font-size: clamp(1.9rem, 1.4rem + 1.9vw, 2.7rem);
  line-height: 1.06;
  letter-spacing: -0.026em;
  text-wrap: balance;
}

.page-header-lede {
  margin: 10px 0 0;
  max-width: 62ch;
  font-size: 1.02rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

/* Secondary facts rather than a description -- the ticket page's
   "site · page · category · opened". Smaller than the lede
   on purpose: the two can appear together and need to rank. */
.page-header-meta {
  margin: 8px 0 0;
  font-size: 0.86rem;
  color: var(--ink-muted);
}

.page-header-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

/* Some header actions have to be forms rather than links, because they
   have a side effect and a link would let them be prefetched -- the
   article editor's Unpublish and Delete, for instance. A block-level
   `<form>` would break the row, so they sit in it like any other control. */
.page-header-actions form {
  display: inline-flex;
  margin: 0;
}

@media (max-width: 640px) {

  .page-header {
    padding-top: 28px;
  }

  /* Full width on a narrow screen: a wrapped action row that stays
     left-aligned under a wrapped title reads as part of the title block. */
  .page-header-actions {
    width: 100%;
  }
}

.dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--card-gap);
  padding: 24px 0 64px;
}

.collection-filter {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 22px;
}

.collection-filter input {
  width: min(100%, 360px);
  min-height: 44px;
  padding: 10px 15px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--paper-raised);
  color: var(--green-900);
  font: inherit;
}

.collection-filter input:focus {
  outline: none;
  border-color: var(--rust-400);
  box-shadow: 0 0 0 4px rgb(204 122 74 / 16%);
}

.collection-filter span {
  color: var(--ink-muted);
  font-size: 0.8rem;
  white-space: nowrap;
}

[data-list-item][hidden] {
  display: none !important;
}

.collection-filter + .site-grid,
.collection-filter + .domain-list,
.billing-filter + .billing-list {
  margin-top: 12px;
  padding-top: 0;
}

@media (max-width: 520px) {
  .collection-filter {
    align-items: stretch;
    flex-direction: column;
    gap: 6px;
  }

  .collection-filter input {
    width: 100%;
  }
}

/* Site list on /dashboard, used only from two sites upward -- one site
   gets `.site-hero` instead (see `pages::views::dashboard::render`'s doc
   comment).

   Capped at two columns rather than auto-fitting as many as will go.
   Two is by far the most common count that reaches this grid at all
   (three or more is rare), so it's the case worth being exactly right:
   two columns fills the row perfectly, and gives each card enough width
   for the status sentence and progress track it now carries. Auto-fit
   would instead squeeze three narrow columns onto a wide screen to suit
   a count almost nobody has. The odd-count empty cell that auto-fit was
   avoiding only ever mattered for a single site, which no longer renders
   here. */
.site-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--card-gap);
  padding: 24px 0 64px;
}

/* When performance follows the website list, the list no longer needs its
   old page-ending bottom padding. */
.site-grid:has(+ .dashboard-overview) {
  padding-bottom: 24px;
}

/* ---------- Dashboard: single-site panel ---------- */

/* The full-width panel that replaces a lone card. It carries what the card
   couldn't fit and what people actually sign in to find out: what state the
   site is in, in a sentence; how far through the build it is; the one thing
   to do next; and direct links to the handful of places they come back for.
   See `pages::views::dashboard::render`'s doc comment. */
.site-hero {
  margin: 24px 0 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.site-hero + .dashboard-overview {
  margin-top: 24px;
}

.site-hero-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}

.site-hero-identity {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.site-hero-identity-text {
  min-width: 0;
}

.site-hero-identity-text h2 {
  margin: 0;
  font-size: 1.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.site-hero-name {
  margin: 2px 0 0;
  font-size: 0.88rem;
  color: var(--ink-muted);
}

.site-hero-name.is-placeholder {
  font-style: italic;
}

.site-hero-status {
  margin: 0;
  font-size: 1rem;
  max-width: 62ch;
}

/* Five dots and a label -- the glanceable form of the per-site page's full
   annotated workflow list, driven by the same `site_workflow_stage`. */
.site-hero-progress {
  padding: 16px 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.progress-track {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 0;
}

.progress-track-step {
  flex: 1;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunken);
}

.progress-track-step.is-complete {
  background: var(--green-700);
}

.progress-track-step.is-active {
  background: var(--rust-500);
}

.site-hero-progress-label {
  margin: 10px 0 0;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--ink-muted);
}

.site-hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* Plain text links, not buttons -- these are shortcuts past the per-site
   page, and as buttons they'd out-shout the actual primary action above. */
.site-hero-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  font-size: 0.9rem;
}

.site-hero-links a {
  color: var(--green-800);
  font-weight: 600;
  text-decoration: none;
}

.site-hero-links a:hover {
  color: var(--rust-700);
  text-decoration: underline;
}

.site-hero-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 0.84rem;
  color: var(--ink-muted);
}

.site-hero-footer-divider {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentcolor;
  opacity: 0.5;
  flex-shrink: 0;
}

.site-hero-footer a {
  margin-left: auto;
  color: var(--green-800);
  font-weight: 600;
}

/* First-run state. Carries its own action rather than leaning on the
   header button -- on a page with nothing else, "what do I do?" and the
   button that answers it belong together. */
/* ---------- Empty states ----------

   Two weights, from `ui::empty`. This replaced five near-identical
   spellings -- `.dashboard-empty`, `.data-table-empty`, `.admin-empty`,
   `.review-empty` and the help centre's own -- which had drifted into
   three different alignments and two different type colours for what is
   the same idea. */

/* A screen with nothing on it. Centred and generous, because it's the
   only thing the reader has to look at. */
.empty-panel {
  margin: 24px 0 0;
  padding: clamp(32px, 6vw, 56px);
  text-align: center;
}

.empty-panel-title {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 1.15rem + 1vw, 1.9rem);
  letter-spacing: -0.018em;
}

.empty-panel-body {
  margin: 0 auto;
  /* Capped as prose, not as layout: this is the one piece of real writing
     on an otherwise bare screen. */
  max-width: 46ch;
  color: var(--ink-muted);
}

.empty-panel-action {
  margin-top: 20px;
}

/* A section of an otherwise-full page. One quiet line, left-aligned with
   the content it stands in for -- centring it would draw more attention
   than the populated version of the same section gets. */
.empty-inline {
  margin: 0;
  padding: 20px 4px;
  color: var(--ink-muted);
  font-size: 0.9rem;
}

/* Inside a table wrapper it takes the table's own generous padding, since
   there it really is standing in for rows. */
.data-table-wrap>.empty-inline {
  padding: 44px 20px;
  text-align: center;
}

/* Visually hidden but still read aloud -- the progress track's dots are
   pure colour, so each carries its stage name for screen readers. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.dashboard-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* The site list's own card -- a little more presence than a plain
   `.dashboard-card` (which is also reused for flatter, single-fact tiles
   elsewhere), since this is the one card a customer scans repeatedly to
   tell several sites apart. A gentle hover lift signals the whole tile
   leads somewhere, not just the button at the bottom of it. */
.site-card-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.site-card-identity {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.site-card-identity-text {
  min-width: 0;
}

.site-card-identity-text h3 {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.site-card-domain {
  margin: 2px 0 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.site-card-domain.is-placeholder {
  font-style: italic;
}

/* Replaced the old created-date/price row. A card's job in the 2+ case is
   "which site is this and does it need me?", and a creation date answered
   neither -- this is `site_status_description`'s plain-English sentence
   instead, the same one the single-site panel leads with. */
.site-card-status {
  margin: 0;
  font-size: 0.86rem;
  color: var(--ink-muted);
}

.website-health-notice strong {
  display: block;
  margin-bottom: 4px;
  color: var(--green-900);
}

.website-health-notice p {
  margin: 0;
}

/* Override `.dashboard-card-actions .btn`'s default `flex: 1` stretch --
   right for a card with several actions sharing the row, but this card
   only ever has one, and a single full-width button reads as heavier than
   the rest of the tile warrants. Sized to its label and left-aligned
   instead. */
.site-card .dashboard-card-actions {
  justify-content: flex-start;
}

.site-card .dashboard-card-actions .btn {
  flex: none;
  min-width: 0;
}

/* Grid items default to their min-content width. The five-stage flex track
   inside a card otherwise forces a 460px column on a 375px phone. */
.site-card,
.progress-track-step {
  min-width: 0;
}

.dashboard-card h3 {
  margin: 0;
}

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  border: 1px solid transparent;
}

.status-pill::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentcolor;
}

.status-live {
  background: rgb(63 157 106 / 14%);
  border-color: rgb(63 157 106 / 30%);
  color: #1f6b45;
}

.status-quiet {
  background: rgb(26 53 39 / 7%);
  border-color: var(--border);
  color: var(--ink-muted);
}

.dashboard-stat {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.dashboard-stat-value {
  font-family: var(--font-display);
  font-size: 2.2rem;
  font-weight: 600;
  color: var(--green-900);
}

.dashboard-stat-label {
  color: var(--ink-muted);
  font-size: 0.88rem;
}

.progress-bar {
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunken);
  overflow: hidden;
}

.progress-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--rust-400), var(--rust-600));
  border-radius: var(--radius-pill);
}

.dashboard-card-actions {
  margin-top: auto;
  padding-top: 4px;
}

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

/* The standard vertical spacer for any sequence of cards/sections that
   should stack top-to-bottom with consistent breathing room between them
   -- e.g. Settings' Logo / Contact details / Articles cards, or Profile's
   Display name / Email address cards. `.card`/`.dashboard-card` carry no
   margin of their own (so a lone card, or one already spaced by a parent
   grid, isn't forced to add extra), so anything stacking more than one of
   them needs this (or `.dashboard-grid`/`.site-grid`, for a multi-column
   layout instead) rather than relying on the cards to space themselves.
   Same `--card-gap` as those grids, so vertical and grid spacing always
   match. */
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--card-gap);
}

.dashboard-card-actions .btn {
  flex: 1;
  min-width: 140px;
}

.workflow-card {
  margin: 24px 0;
  padding: 28px;
}

.workflow-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 24px;
}

.workflow-card-head .eyebrow,
.workflow-card-head h2 {
  margin-bottom: 0;
}

/* ---------- Site page: the site's own pages ---------- */

/* The list of a customer's actual pages and what state each is in -- the
   main content of `/dashboard/{id}` and the thing that screen exists to
   show that the dashboard's summary panel doesn't. Rows rather than cards:
   there are five of them, they're read as a set, and the useful comparison
   is "which one still needs me", which scans far better down a column of
   aligned status pills than across a grid of tiles. */
.site-pages-card {
  margin: 24px 0;
  padding: 28px;
}

.site-page-list {
  display: flex;
  flex-direction: column;
}

/* Square corners on purpose: the negative margins let each row bleed to
   the card's inner edges, so the hover fill reads as a full-width band
   across the list rather than a rounded pill floating inside it. */
.site-page-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 12px;
  margin: 0 -12px;
  border-top: 1px solid var(--border);
  color: inherit;
  text-decoration: none;
  transition: background var(--transition);
}

.site-page-row:first-child {
  border-top: 0;
}

.site-page-row:hover {
  background: var(--paper-tint);
}

.site-page-row-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.site-page-row-text strong {
  font-size: 0.98rem;
}

.site-page-row-text span {
  font-size: 0.82rem;
}

.site-page-row-chevron {
  color: var(--ink-faint);
  flex-shrink: 0;
  transition: transform var(--transition), color var(--transition);
}

.site-page-row:hover .site-page-row-chevron {
  color: var(--rust-600);
  transform: translateX(2px);
}

.workflow-steps {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.workflow-step {
  position: relative;
  display: block;
  min-width: 0;
  padding-right: 18px;
}

.workflow-step:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 14px;
  left: 30px;
  right: 0;
  height: 2px;
  background: var(--border);
}

.workflow-step.is-complete:not(:last-child)::after {
  background: var(--green-700);
}

.workflow-step-marker {
  position: relative;
  z-index: 1;
  display: grid;
  flex: 0 0 30px;
  width: 30px;
  height: 30px;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--paper-raised);
  color: var(--ink-muted);
  font-size: 0.76rem;
  font-weight: 700;
  margin-bottom: 12px;
}

.workflow-step.is-complete .workflow-step-marker,
.workflow-step.is-active .workflow-step-marker {
  border-color: var(--green-800);
  background: var(--green-800);
  color: white;
}

.workflow-step.is-active .workflow-step-marker {
  box-shadow: 0 0 0 5px rgba(26, 53, 39, 0.1);
}

.workflow-step strong,
.workflow-step small {
  display: block;
}

.workflow-step strong {
  margin-top: 0;
  color: var(--green-900);
  font-size: 0.82rem;
}

.workflow-step small {
  margin-top: 4px;
  color: var(--ink-muted);
  font-size: 0.72rem;
  line-height: 1.4;
}

.workflow-step.is-upcoming {
  opacity: 0.58;
}

.status-alert {
  background: var(--rust-tint);
  border-color: rgb(156 78 45 / 32%);
  color: var(--rust-700);
}

@media (max-width: 800px) {
  .workflow-steps {
    grid-template-columns: 1fr;
    gap: 18px;
  }

  .workflow-step {
    display: flex;
    gap: 10px;
    padding-right: 0;
  }

  .workflow-step-marker {
    margin-bottom: 0;
  }

  .workflow-step:not(:last-child)::after {
    top: 30px;
    bottom: -18px;
    left: 14px;
    right: auto;
    width: 2px;
    height: auto;
  }

  .workflow-card-head {
    align-items: flex-start;
    flex-direction: column;
  }
}

/* ---------- Revisions ---------- */

.revision-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 24px;
}

.revision-item {
  padding: 14px 18px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: var(--radius);
  background: var(--paper-raised);
}

.revision-item:has(.revision-status-pending) {
  border-left-color: var(--ink-faint);
}

.revision-item:has(.revision-status-in_progress) {
  border-left-color: var(--rust-500);
}

.revision-item:has(.revision-status-applied) {
  border-left-color: var(--green-700);
}

.revision-item:has(.revision-status-cancelled) {
  border-left-color: var(--border);
}

.revision-item-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}

.revision-item-head .text-muted {
  font-size: 0.82rem;
}

.revision-item p {
  margin: 4px 0 0;
  line-height: 1.55;
}

.revision-status-pending {
  background: rgba(26, 53, 39, 0.08);
  color: var(--ink-muted);
}

.revision-status-in_progress {
  background: rgba(156, 78, 45, 0.12);
  color: var(--rust-700);
}

.revision-status-applied {
  background: rgba(26, 53, 39, 0.08);
  color: var(--green-800);
}

.revision-status-cancelled {
  background: rgba(0, 0, 0, 0.05);
  color: var(--ink-faint);
  text-decoration: line-through;
}

/* ---------- Sites list ---------- */

/* Most accounts have one or two sites, occasionally three -- a stacked list
   of wider, detail-carrying rows reads as considered and roomy at that
   count, where the old 3-column grid looked sparse/unfinished with empty
   cells. Capped narrower than the page container so rows stay readable
   instead of stretching edge-to-edge. */
/* Deliberately understated compared to the old dashed tile -- with only a
   few sites on the page, a full-height grid cell for "add another" competed
   with the sites themselves for attention. A plain trailing link fits its
   actual weight in the flow: something most people reach for occasionally,
   not the primary action. */
/* ---------- Site detail ---------- */

/* A customer's own site mark. Named `site-avatar`, not `site-logo`: that
   name already belonged to WebHostGB's brand logo in the footer, and the
   collision meant the footer's logo picked up this 44px bordered tile and
   its near-white fill. See `pages::views::site_avatar`. */
.site-avatar,
.site-avatar-fallback {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 10px;
}

.site-avatar {
  overflow: hidden;
  background: var(--paper-raised);
  border: 1px solid var(--border);
}

.site-avatar img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.site-avatar-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--green-700) 0%, var(--green-950) 100%);
  color: var(--paper-tint);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: 0.02em;
}

/* The site switcher borrows `.account-menu` for its dropdown behaviour, but
   the header's summary is shaped around a 28px avatar circle sitting at its
   left edge -- which is why that rule carries `padding: 4px 10px 4px 4px`.
   The switcher has no avatar, just a label and a caret, so it inherited
   4px of left padding, no resting border, and a 26px height while standing
   next to a 44px pill button.

   These override it into a proper secondary button: same height, same
   radius, same border treatment as `.btn.btn-secondary` beside it, so the
   two controls in that row read as siblings rather than as a button and a
   stray piece of text. */
.site-switcher summary {
  gap: 10px;
  min-height: 44px;
  padding: 0 20px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: transparent;
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 600;
  white-space: nowrap;
  transition: border-color var(--transition), background-color var(--transition);
}

.site-switcher summary:hover,
.site-switcher[open] summary {
  /* Matches `.btn-secondary:hover` rather than `.account-menu`'s rust tint
     fill, which was designed to read against the header's frosted bar. */
  background: transparent;
  border-color: var(--green-900);
}

.site-switcher summary:focus-visible {
  outline: 2px solid var(--green-700);
  outline-offset: 2px;
}

/* Anchored left rather than right. The header's panel hangs off the right
   edge because that is where the avatar sits; in this left-aligned action
   row, right-anchoring drifts the panel away from its own trigger. */
.site-switcher .account-menu-panel {
  right: auto;
  left: 0;
}

/* ---------- Mobile ---------- */

@media (max-width: 1020px) {


  .form-grid {
    grid-template-columns: 1fr;
  }


  .site-nav {
    display: none;
  }

  /* Hidden with the nav it belongs beside -- both are replaced by the
     burger menu, which carries the same links plus the CTA. Without this
     the CTA would sit next to the burger and crowd it off small screens. */
  .header-actions {
    display: none;
  }

  .mobile-menu {
    display: block;
  }

  /* Kept visible over the open sheet alongside the burger -- a full-screen
     menu that hides the logo loses the one bit of orientation on the
     screen. */
  .site-brand {
    position: relative;
    z-index: 2;
  }

  /* A shade smaller on a phone. The full lockup is ~5:1, so 30px tall is
     150px wide -- comfortable beside a burger at 390px, tight at 320. */
  .brand-lockup {
    height: 27px;
  }

  /* Icon-only trigger. The word "Menu" beside a burger is redundant --
     the icon is universally understood, and dropping it buys back the
     width that made the header feel tight on a 360px phone. The label
     lives on `aria-label` instead, so nothing is lost to a screen reader. */
  /* Above the sheet, so the burger stays visible and usable as the close
     control while the menu is open. */
  .mobile-menu > summary {
    position: relative;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border: 1px solid var(--border-strong);
    border-radius: 14px;
    background: var(--paper-raised);
    list-style: none;
    cursor: pointer;
    color: var(--green-900);
    transition: background-color var(--transition), border-color var(--transition);
  }

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

  .mobile-menu[open] > summary {
    background: var(--green-900);
    border-color: var(--green-900);
    color: var(--paper-raised);
  }

  /* Three bars, not two: the middle one fades out as the outer two cross,
     which reads as a considered transition rather than two lines snapping
     into an X. */
  .mobile-menu-icon {
    display: flex;
    flex-direction: column;
    gap: 4px;
    width: 18px;
  }

  .mobile-menu-icon span {
    width: 100%;
    height: 1.8px;
    border-radius: 2px;
    background: currentcolor;
    display: block;
    transition: transform var(--transition), opacity var(--transition);
  }

  .mobile-menu[open] .mobile-menu-icon span:first-child {
    transform: translateY(5.8px) rotate(45deg);
  }

  .mobile-menu[open] .mobile-menu-icon span:nth-child(2) {
    opacity: 0;
  }

  .mobile-menu[open] .mobile-menu-icon span:last-child {
    transform: translateY(-5.8px) rotate(-45deg);
  }

  /* A full-height sheet under the header rather than a 220px dropdown.
     On a phone the dropdown wasted the screen and made every link a small
     target in the top-right corner -- the hardest place to reach one-handed.
     This puts the links in the middle of the screen at a size you can hit
     without looking. */
  /* z-index 0, not 39.
     
     The panel lives inside `.site-header`, which has `z-index: 40` and so
     forms a stacking context. Everything in here is therefore painted at
     the header's level relative to the page -- which is what makes the
     panel cover the page without needing a high z-index of its own. Inside
     that context, though, 39 would have put it above the brand and the
     burger, hiding the very control used to close it. 0 keeps it above the
     header's non-positioned content but below the two things lifted to
     z-index 2 below. */
  .mobile-menu-panel {
    position: fixed;
    inset: 0;
    z-index: 0;
    display: flex;
    flex-direction: column;
    /* Clears the header, which sits above this at z-index 40. */
    padding: 88px 20px calc(24px + env(safe-area-inset-bottom, 0px));
    background: var(--paper);
    overflow-y: auto;
    overscroll-behavior: contain;
    animation: mobile-menu-in 0.22s cubic-bezier(0.22, 1, 0.36, 1);
  }

  .mobile-menu-panel::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
      radial-gradient(46% 34% at 88% 6%, rgb(204 122 74 / 16%), transparent 70%),
      radial-gradient(50% 34% at 6% 96%, rgb(35 70 52 / 12%), transparent 72%);
  }

  @keyframes mobile-menu-in {
    from {
      opacity: 0;
      transform: translateY(-8px);
    }

    to {
      opacity: 1;
      transform: none;
    }
  }

  .mobile-menu-nav {
    position: relative;
    display: flex;
    flex-direction: column;
    border-top: 1px solid var(--border);
  }

  .mobile-menu-nav a {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 4px;
    border-bottom: 1px solid var(--border);
    color: var(--green-900);
    font-family: var(--font-display);
    font-size: 1.45rem;
    letter-spacing: -0.02em;
    line-height: 1.15;
    text-decoration: none;
  }

  .mobile-feature-menu {
    border-bottom: 1px solid var(--border);
  }

  .mobile-feature-menu > summary {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 4px;
    list-style: none;
    cursor: pointer;
    color: var(--green-900);
    font-family: var(--font-display);
    font-size: 1.45rem;
    letter-spacing: -0.02em;
    line-height: 1.15;
  }

  .mobile-feature-menu > summary::-webkit-details-marker {
    display: none;
  }

  .mobile-feature-menu > summary[aria-current="page"] {
    color: var(--rust-600);
  }

  .mobile-feature-menu[open] > summary .mobile-menu-arrow {
    color: var(--rust-600);
    transform: rotate(45deg);
  }

  .mobile-feature-links {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
    padding: 0 0 16px 32px;
  }

  .mobile-feature-links a {
    min-height: 54px;
    padding: 11px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--paper-raised);
    font-family: var(--font-body);
    font-size: 0.82rem;
    font-weight: 650;
    letter-spacing: 0;
    line-height: 1.3;
  }

  .mobile-feature-links a span {
    margin-left: auto;
    color: var(--rust-500);
  }

  .mobile-menu-nav a[aria-current="page"] {
    color: var(--rust-600);
  }

  .mobile-menu-index {
    font-family: var(--font-body);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    color: var(--rust-400);
  }

  /* Pushed to the right edge; the flex gap above handles the rest. */
  .mobile-menu-arrow {
    margin-left: auto;
    color: var(--ink-faint);
    font-size: 1.1rem;
    transition: color var(--transition), transform var(--transition);
  }

  .mobile-menu-nav a[aria-current="page"] .mobile-menu-arrow {
    color: var(--rust-500);
  }

  .mobile-menu-foot {
    position: relative;
    margin-top: auto;
    padding-top: 28px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
  }

  .mobile-menu-cta {
    display: block;
    width: 100%;
    padding: 16px 20px;
    border: none;
    border-radius: var(--radius-pill);
    /* Same fill as `.btn` and `.header-cta` -- see the note on the latter. */
    background: linear-gradient(155deg, var(--rust-500), var(--rust-700));
    color: var(--paper-raised);
    font-family: var(--font-body);
    font-size: 1rem;
    font-weight: 600;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    box-shadow: 0 10px 24px -12px rgb(156 78 45 / 70%);
  }

  .mobile-menu-cta:hover {
    color: var(--paper-raised);
  }

  .mobile-menu-signin {
    color: var(--green-900);
    font-size: 0.92rem;
    font-weight: 500;
    text-align: center;
    text-decoration: none;
  }

  .mobile-menu-price {
    margin: 8px 0 0;
    text-align: center;
    color: var(--ink-muted);
    font-size: 0.85rem;
  }

  .mobile-menu-price strong {
    font-family: var(--font-display);
    font-size: 1.1rem;
    color: var(--green-900);
  }

  .mobile-menu-email {
    color: var(--rust-600);
    font-size: 0.88rem;
    font-weight: 500;
    text-align: center;
    text-decoration: none;
  }

  @media (prefers-reduced-motion: reduce) {
    .mobile-menu-panel {
      animation: none;
    }

    .mobile-menu-icon span {
      transition: none;
    }
  }

  /* The header already sits above the sheet on z-index (40 vs 39), so it
     stays usable while the sheet is open -- which is what lets the burger
     double as the close button. It does drop its scrolled shadow, which
     would otherwise cast a line across the top of the panel. */
  .mobile-menu[open] {
    position: static;
  }

  .site-header.is-scrolled:has(.mobile-menu[open]) {
    box-shadow: none;
    border-bottom-color: transparent;
  }

  /* The accent rule goes with the shadow and the border, for the same
     reason: three horizontal lines across the top of an open full-screen
     sheet, none of which belong to it. */
  .site-header.is-scrolled:has(.mobile-menu[open])::after {
    opacity: 0;
  }

  .hero,
  .band,
  .cta-band {
    padding: 32px 0 56px;
  }

  .msection {
    padding: 40px 0;
  }

  .feature-grid {
    grid-template-columns: 1fr;
  }

  .dashboard-grid,
  .site-grid,
  .ticket-detail-layout {
    grid-template-columns: 1fr;
  }

  /* Age above the subject rather than beside it, so neither gets squeezed
     into an unreadable column on a phone. */
  .ticket-queue-row,
  .ticket-row {
    align-items: flex-start;
    flex-wrap: wrap;
  }

  .ticket-row-side {
    align-items: flex-start;
    flex-direction: row;
    gap: 10px;
  }

  /* Brand above links rather than beside them. Left-aligned, not centred:
     the link columns are lists, and centred lists are harder to scan than
     centred prose. */
  .footer-top {
    grid-template-columns: 1fr;
    gap: 28px;
  }

  .footer-bottom {
    justify-content: center;
    text-align: center;
  }
}

@media (min-width: 1021px) {
  .account-menu {
    display: block;
  }
}

/* ---------- Admin: sites table ---------- */

.admin-area {
  padding: 36px 0 72px;
}

.admin-eyebrow {
  margin: 0;
  color: var(--rust-600);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.admin-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.admin-tab {
  padding: 9px 14px;
  border-radius: var(--radius-pill);
  color: var(--ink-muted);
  font-size: 0.88rem;
  font-weight: 600;
  text-decoration: none;
}

.admin-tab:hover,
.admin-tab.active {
  background: var(--paper-sunken);
  color: var(--green-900);
}

.admin-tab.active {
  box-shadow: inset 0 0 0 1px var(--border-strong);
}

.admin-section {
  margin-top: 40px;
}

.admin-section-heading {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  /* The heading zeroes its children's margins below, which was fine while
     everything that followed one was a `.data-table-wrap` or a
     `.backup-list` bringing its own top margin. A bare form -- the "Back
     up now" button -- has none, so the button sat flush against the
     heading. Adjacent sibling margins collapse, so this does not double up
     with the 20px those lists already carry. */
  margin-bottom: 20px;
}

.admin-section-heading h2,
.admin-section-heading p {
  margin-bottom: 0;
}

/* `.card` deliberately carries no margin -- it is used inside grids and as
   a lone panel. That leaves a card stacked directly after a section flush
   against whatever ended it, which on the backups screen is the "Back up
   now" button sitting on the Schedule card's top edge. */
.admin-section+.card {
  margin-top: 32px;
}

.admin-callout {
  margin-top: 20px;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--rust-500);
  border-radius: var(--radius);
  background: var(--paper-sunken);
}

.admin-callout p {
  margin: 4px 0 0;
  color: var(--ink-muted);
  font-size: 0.88rem;
}

/* Fits however many cards the page actually has onto one row, rather than
   a fixed count. This class is shared: billing renders four summary cards
   (total / active / past due / cancelled) and users renders three. At a
   hardcoded three, billing's fourth card wrapped onto a line of its own
   and read as a separate, more important stat than the three above it;
   hardcoding four instead would have left users with an empty cell. With
   `auto-fit`, the surplus tracks collapse and whatever cards exist stretch
   to fill the row, so both pages come out even and adding a card later
   doesn't need this rule touched.

   The 200px floor is what drives the responsive behaviour too -- columns
   drop naturally as width runs out, ending at one on a phone, which is why
   there are no breakpoint overrides for this grid any more. */
.admin-summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
  margin-top: 24px;
}

/* Operational dashboards have four peer metrics. Keeping all four on one
   row at desktop widths makes the summary read as a single group. */
.admin-kpi-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.admin-summary-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.admin-summary-card span {
  color: var(--ink-muted);
  font-size: 0.82rem;
}

.admin-summary-card strong {
  color: var(--green-900);
  font-family: var(--font-display);
  font-size: 2rem;
}

/* A lifecycle is a sequence, not six unrelated cards. The connecting rule
   keeps the eye moving left-to-right; the compact mobile grid preserves the
   same order without forcing an unreadable horizontal scroller. */
.business-funnel {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 0;
  margin: 24px 0 18px;
}

.business-funnel-stage {
  position: relative;
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 4px;
  padding: 16px 14px;
  border-block: 1px solid var(--border);
  background: var(--paper-raised);
}

.business-funnel-stage:first-child {
  border-left: 1px solid var(--border);
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
}

.business-funnel-stage:last-child {
  border-right: 1px solid var(--border);
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
}

.business-funnel-stage + .business-funnel-stage {
  border-left: 1px solid var(--border);
}

.business-funnel-stage strong {
  color: var(--green-900);
  font-family: var(--font-display);
  font-size: 1.8rem;
  line-height: 1;
}

.business-funnel-stage span {
  font-size: 0.82rem;
  font-weight: 650;
}

.business-funnel-stage small {
  color: var(--ink-muted);
  font-size: 0.72rem;
}

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

  .business-funnel {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }

  .business-funnel-stage,
  .business-funnel-stage:first-child,
  .business-funnel-stage:last-child,
  .business-funnel-stage + .business-funnel-stage {
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
  }
}

@media (max-width: 520px) {
  .admin-kpi-grid {
    grid-template-columns: 1fr;
  }
}

/* The table's surround. Deliberately not a scroll container.

   It used to be one: `max-height: calc(100vh - 320px)` with
   `overflow: auto`, so the sticky header below had a scrollport to stick
   to. That worked, but the price was an inner scrollbar on every admin
   screen -- a table that stopped short of its own last row, a second
   scrollbar beside the page's, and a list you could not read by scrolling
   the page it was on.

   The header still sticks. It resolves against the viewport now instead
   of this box, offset by `--app-topbar-h` so it lands under the top bar
   rather than behind it, which is the behaviour the inner scroll was
   approximating in the first place.

   `clip` rather than `hidden` for the rounded corners. Both clip the
   table's square corners to the wrap's radius, but `hidden` would make
   this a scroll container again and re-capture the sticky header -- the
   exact bug the max-height was introduced to work around. `clip` does
   not create one. */
.data-table-wrap {
  background: var(--paper-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: clip;
  margin-top: 24px;
}

.data-table {
  width: 100%;
  /* `separate`, not `collapse`: cell-owned borders are what let the wrap
     clip the corners cleanly, and they keep a sticky header honest if one
     is ever added -- a collapsed table's borders belong to the table, so
     the header scrolls away from its own bottom border. */
  border-collapse: separate;
  border-spacing: 0;
}

.data-table th {
  text-align: left;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
  color: var(--ink-muted);
  padding: 14px 20px;
  background: var(--paper-sunken);
  border-bottom: 1px solid var(--border);
}

.data-table td {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  font-size: 0.92rem;
}

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

.data-table tbody tr:hover td {
  background: var(--paper-sunken);
}

.admin-table-subline {
  display: block;
  margin-top: 3px;
}

.data-table a.row-link {
  color: var(--green-900);
  text-decoration: none;
}

.data-table a.row-link:hover strong {
  text-decoration: underline;
}

.article-mobile-label {
  display: none;
}

/* Customer article lists become real rows on a phone rather than a desktop
   table hidden behind sideways scrolling. Admin tables retain the generic
   overflow behaviour because every one of their columns is operationally
   significant. */
@media (max-width: 640px) {
  .data-table-wrap.article-table-wrap {
    overflow: clip;
  }

  .article-table,
  .article-table tbody,
  .article-table tr,
  .article-table td {
    display: block;
    width: 100%;
  }

  .article-table thead {
    display: none;
  }

  .article-table tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 8px 16px;
    padding: 16px 18px;
    border-bottom: 1px solid var(--border);
  }

  .article-table tbody tr:last-child {
    border-bottom: none;
  }

  .article-table td {
    padding: 0;
    border: 0;
  }

  .article-table tbody tr:hover td {
    background: transparent;
  }

  .article-title,
  .article-category,
  .article-updated {
    grid-column: 1;
  }

  .article-status,
  .article-action {
    grid-column: 2;
    justify-self: end;
  }

  .article-status {
    grid-row: 1;
  }

  .article-category {
    grid-row: 2;
  }

  .article-action {
    grid-row: 2 / span 2;
    align-self: end;
  }

  .article-updated {
    grid-row: 3;
    font-size: 0.78rem !important;
  }

  .article-mobile-label {
    display: inline;
  }
}

.table-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  white-space: nowrap;
}

.table-actions form,
.admin-jobs-table form {
  margin: 0;
}

.btn-danger {
  border-color: rgba(178, 58, 42, 0.35);
  background: transparent;
  color: #9c3626;
}

.btn-danger:hover {
  border-color: #9c3626;
  background: rgba(178, 58, 42, 0.08);
  color: #9c3626;
  box-shadow: none;
}

.status-info {
  background: rgba(37, 99, 235, 0.09);
  color: #2453a6;
}

.site-row-removed td {
  opacity: 0.64;
}

.site-row-removed td:last-child {
  opacity: 1;
}

.job-row-stale td {
  background: rgba(178, 58, 42, 0.045);
}

.admin-id {
  color: var(--ink-faint);
  font-size: 0.75rem;
}

.job-error-cell {
  max-width: 320px;
  color: var(--ink-muted);
  font-size: 0.8rem !important;
  line-height: 1.45;
  overflow-wrap: anywhere;
}

@media (max-width: 900px) {

  .admin-area-heading,
  .admin-section-heading {
    align-items: flex-start;
    flex-direction: column;
  }

  /* The one scroll that stays. A 1050px table cannot be read on a 390px
     phone by any means that isn't worse than sideways scrolling, and this
     is admin, where the alternative -- dropping columns -- would hide the
     data someone opened the screen for. Horizontal only: the vertical
     inner scroll is gone at every width. */
  .data-table-wrap {
    overflow-x: auto;
    overflow-y: clip;
  }

  .admin-jobs-table {
    min-width: 1050px;
  }
}


/* ---------- Admin: page editor ---------- */

.admin-editor-toolbar {
  margin-top: 20px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}

/* Stacked, matching `.admin-topbar`. It was a flex row with the heading at
   1.4rem sitting beside a full-size status pill -- so the page name read
   smaller than every other admin title while the pill shouted next to it. */
.admin-editor-toolbar-title {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.admin-editor-toolbar-title h1 {
  margin: 0;
  font-size: clamp(1.5rem, 1.25rem + 1vw, 2rem);
  line-height: 1.12;
  letter-spacing: -0.022em;
  overflow-wrap: anywhere;
}

/* The status sits inside the eyebrow line, after the page kind. */
.admin-editor-status {
  display: inline-flex;
  align-items: center;
  margin-left: 10px;
}

/* A compact pill for use inside a line of small caps, where the standard
   one is taller than the text it sits in. */
.status-pill-sm {
  padding: 2px 8px;
  font-size: 0.64rem;
  letter-spacing: 0.05em;
}

.admin-editor-toolbar-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.admin-editor-toolbar-actions form {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.editor-unsaved-status {
  color: var(--rust-700);
  font-size: 0.78rem;
  font-weight: 600;
}

/* Stacked by default: code above, preview below.
   
   Side by side sounds right and measures wrong. With the per-site sidebar
   taking 260px of a 1600px shell, each half of a split is roughly 640px --
   a tablet viewport. The job on this screen is judging how a page looks
   before it goes on a real business's domain, and you cannot do that in a
   pane narrower than the layout you're shipping. Stacked gives the preview
   the full width; the editor keeps a fixed, generous height above it.

   `.is-split` restores the side-by-side view for anyone who prefers it --
   `static/js/admin-editor.js` toggles it and remembers the choice. */
.admin-editor-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* Both panes deliberately generous. The editor wants enough rows to see
     a section of markup without constant scrolling; the preview wants
     enough height to show a real fold rather than a letterbox. Together
     they run past the viewport, which is correct -- this screen is worked
     through top to bottom, not glanced at. */
  grid-template-rows: minmax(420px, 46vh) minmax(680px, 1fr);
  gap: 16px;
  margin-top: 16px;
  min-height: calc(100vh - 240px);
}

.admin-editor-split.is-split {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  height: calc(100vh - 220px);
  min-height: 640px;
}

@media (max-width: 900px) {

  /* Never side by side on a narrow screen -- two 300px panes are useless. */
  .admin-editor-split.is-split {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(420px, 46vh) minmax(680px, 1fr);
    height: auto;
  }
}

.admin-editor-pane {
  display: flex;
  flex-direction: column;
  background: var(--paper-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.admin-editor-pane-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--paper-sunken);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-muted);
}

.admin-pane-status {
  color: var(--ink-muted);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}

.admin-pane-status.is-error {
  color: var(--rust-700);
}

.admin-editor-fallback {
  flex: 1;
  width: 100%;
  min-height: 0;
  padding: 18px;
  resize: none;
  border: 0;
  outline: 0;
  background: #17202a;
  color: #f2f5f7;
  font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  tab-size: 2;
}

.admin-monaco {
  flex: 1;
  min-height: 0;
}

/* Secondary, occasional actions (regenerate, open saved) grouped away from
   the constant ones (save, approve) so they stop competing on every visit. */
.admin-editor-action-group {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-right: 10px;
  margin-right: 4px;
  border-right: 1px solid var(--border);
}

.admin-editor-action-group form {
  margin: 0;
}

/* The Cmd+S hint on the Save draft button -- discoverable without a
   tooltip, quiet enough not to read as part of the label. */
.btn-shortcut {
  margin-left: 7px;
  padding: 1px 5px;
  border-radius: var(--radius);
  background: rgb(255 255 255 / 18%);
  font-size: 0.72rem;
  font-weight: 600;
  opacity: 0.85;
}

.btn-secondary .btn-shortcut {
  background: rgb(26 53 39 / 8%);
}

/* Preview width switcher. */
.admin-preview-widths {
  display: flex;
  gap: 2px;
  margin-left: auto;
  margin-right: 12px;
  padding: 2px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunken);
}

.admin-preview-width {
  border: 0;
  background: none;
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font: inherit;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-muted);
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}

.admin-preview-width:hover {
  color: var(--green-900);
}

.admin-preview-width.is-active {
  background: var(--paper-raised);
  color: var(--green-900);
  box-shadow: 0 1px 2px rgb(15 36 25 / 12%);
}

/* Holds the iframe and, when a device width is picked, centres it on a
   contrasting backdrop so the constrained edges are obvious. */
.admin-preview-stage {
  flex: 1;
  min-height: 0;
  display: flex;
  justify-content: center;
  background: #fff;
}

.admin-preview-stage.is-constrained {
  background: var(--paper-sunken);
  padding: 12px 0;
}

.admin-preview-frame {
  flex: 1;
  min-height: 0;
  width: 100%;
  max-width: var(--preview-width, 100%);
  border: 0;
  background: #fff;
  transition: max-width var(--transition);
}

/* The full-screen toggle, in the preview pane's header beside the device
   widths. Sits at the end of that row rather than with them: they change
   what the preview *is*, this changes how much of the screen it gets. */
.admin-preview-expand {
  border: 1px solid var(--border);
  background: var(--paper-raised);
  border-radius: var(--radius-pill);
  padding: 3px 11px;
  margin-left: 8px;
  font: inherit;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--ink-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--transition), color var(--transition);
}

.admin-preview-expand:hover {
  border-color: var(--border-strong);
  color: var(--green-900);
}

.admin-preview-expand[aria-pressed="true"] {
  border-color: var(--green-900);
  color: var(--green-900);
}

/* Full screen is this pane covering the viewport, with its own header kept
   on screen -- the device widths and the status line are exactly what you
   want while looking at a page at full width, and the native Fullscreen
   API would have taken them away with the rest of the document. */
.admin-editor-pane.is-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 60;
  height: 100vh;
  height: 100dvh;
  max-width: none;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: var(--paper);
  display: flex;
  flex-direction: column;
}

/* The pane's own header keeps its seam against the stage below it. */
.admin-editor-pane.is-fullscreen .admin-editor-pane-head {
  border-bottom: 1px solid var(--border);
}

.admin-editor-pane.is-fullscreen .admin-preview-stage {
  flex: 1;
  min-height: 0;
}

/* Scroll-locked for the same reason every other overlay here is: the admin
   shell behind would otherwise keep its own scrollbar and drift. */
body.preview-fullscreen-open {
  overflow: hidden;
}

.admin-preview-stage.is-constrained .admin-preview-frame {
  flex: none;
  width: var(--preview-width, 100%);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}

@media (max-width: 960px) {

  .admin-editor-split {
    grid-template-columns: 1fr;
    height: auto;
  }

  .admin-editor-pane {
    height: 60vh;
  }
}

/* ---------- Articles: prose body (shared by the editor's live preview
   and the standalone preview document -- see sites::views::articles) ---------- */

.article-preview-page {
  padding: 40px 0 64px;
}

.article-body {
  max-width: 720px;
  margin: 0 auto;
  color: var(--green-900);
  line-height: 1.7;
}

.article-body h1 {
  font-size: 2.1rem;
  margin: 0 0 24px;
}

.article-body h2 {
  font-size: 1.5rem;
  margin: 40px 0 12px;
}

.article-body h3 {
  font-size: 1.2rem;
  margin: 32px 0 10px;
}

.article-body p {
  margin: 0 0 18px;
}

.article-body ul,
.article-body ol {
  margin: 0 0 18px;
  padding-left: 24px;
}

.article-body li {
  margin-bottom: 6px;
}

.article-body a {
  color: var(--rust-600);
  text-decoration: underline;
}

.article-body blockquote {
  margin: 0 0 18px;
  padding: 4px 20px;
  border-left: 3px solid var(--border-strong);
  color: var(--ink-muted);
  font-style: italic;
}

.article-body code {
  background: var(--paper-sunken);
  border-radius: 4px;
  padding: 2px 6px;
  font-size: 0.9em;
}

.article-body pre {
  background: var(--green-950);
  color: var(--paper);
  border-radius: var(--radius);
  padding: 16px 20px;
  overflow-x: auto;
  margin: 0 0 18px;
}

.article-body pre code {
  background: none;
  padding: 0;
  color: inherit;
}

.article-body img {
  max-width: 100%;
  border-radius: var(--radius);
  margin: 0 0 18px;
}

.article-body table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 18px;
}

.article-body th,
.article-body td {
  border: 1px solid var(--border);
  padding: 8px 12px;
  text-align: left;
}

.article-body hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 32px 0;
}

/* ---------- Articles: editor ---------- */

/* The editor's own title-and-actions bar.

   Still here because the two *admin* editors use it -- the marketing
   article editor (`articles::views::admin`) and the landing page builder
   (`landing::views::admin`), both of which sit inside the admin shell and
   its sidebar rather than the account layout. The customer-facing article
   editor was moved onto `.page-header`; these weren't, because admin is a
   separate surface with its own navigation and standardising across the
   two would mean giving admin screens a back link they don't need. */
.article-editor-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 16px 0;
}

.article-editor-toolbar-title {
  display: flex;
  align-items: center;
  gap: 12px;
}

.article-editor-toolbar-title h1 {
  font-size: 1.4rem;
  margin: 0;
}

.article-editor-toolbar-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.article-editor-toolbar-actions form {
  display: inline-flex;
}

/* Toast UI Editor mount point -- the editor manages everything inside
   this div itself (toolbar, WYSIWYG/markdown tabs, content area), so this
   just gives it a card-like frame consistent with the rest of the app and
   a comfortable minimum height. */
/* `.markdown-editor-mount` is the generic one (static/js/markdown-editor.js);
   `.article-editor-mount` is the older per-feature markup. Same treatment
   for both -- the editor supplies its own chrome, so all this does is give
   it the app's border and corner radius instead of its default. */
.article-editor-mount,
.markdown-editor-mount {
  margin: 16px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.article-editor-mount .toastui-editor-defaultUI,
.markdown-editor-mount .toastui-editor-defaultUI {
  border: 0;
}

.article-editor-save-bar {
  position: sticky;
  bottom: 12px;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 16px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-veil-strong);
  box-shadow: var(--shadow-card);
  backdrop-filter: blur(10px);
}

.article-draft-notice {
  margin: 0 0 20px;
  padding: 13px 16px;
  border-left: 3px solid var(--rust-500);
  background: var(--rust-tint);
  color: var(--green-900);
}

.article-publishing-settings,
.article-version-history {
  margin: 0 0 22px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.article-publishing-settings > summary,
.article-version-history > summary {
  padding: 15px 18px;
  color: var(--green-900);
  font-weight: 700;
  cursor: pointer;
}

.article-publishing-settings[open] > summary,
.article-version-history[open] > summary {
  border-bottom: 1px solid var(--border);
}

.article-settings-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(240px, .7fr);
  gap: 24px;
  padding: 20px;
}

.article-settings-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 18px;
}

.article-settings-fields .form-field:nth-last-child(-n+2) {
  grid-column: 1 / -1;
}

.article-settings-media {
  padding-left: 22px;
  border-left: 1px solid var(--border);
}

.article-settings-media h3 {
  margin: 0 0 14px;
}

.article-cover-preview {
  width: 100%;
  aspect-ratio: 16 / 9;
  margin-bottom: 14px;
  border-radius: var(--radius-md);
  object-fit: cover;
}

.article-quality-checklist {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

.article-quality-checklist h3 {
  margin-bottom: 2px;
}

.article-quality-checklist ul {
  display: grid;
  gap: 10px;
  margin: 16px 0 0;
  padding: 0;
  list-style: none;
}

.article-quality-checklist li {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  align-items: start;
  color: var(--ink-muted);
}

.article-quality-checklist li > span {
  grid-row: 1 / 3;
  color: var(--ink-faint);
  font-weight: 800;
}

.article-quality-checklist li strong,
.article-quality-checklist li small {
  display: block;
}

.article-quality-checklist li strong {
  color: var(--green-900);
  font-size: .9rem;
}

.article-quality-checklist li small {
  margin-top: 1px;
  line-height: 1.35;
}

.article-quality-checklist li.is-ready > span {
  color: var(--success);
}

.article-version-list {
  display: grid;
  gap: 0;
}

.article-version-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 13px 18px;
  border-bottom: 1px solid var(--border);
}

.article-version-row:last-child {
  border-bottom: 0;
}

.article-version-row > div {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

@media (max-width: 760px) {
  .article-settings-grid,
  .article-settings-fields {
    grid-template-columns: 1fr;
  }

  .article-settings-fields .form-field:nth-last-child(-n+2) {
    grid-column: auto;
  }

  .article-settings-media {
    padding: 20px 0 0;
    border-top: 1px solid var(--border);
    border-left: 0;
  }

  .article-editor-save-bar,
  .article-version-row {
    align-items: stretch;
    flex-direction: column;
  }
}

/* Business identity assets are intentionally plain, printable objects. The
   cards inherit the dashboard system; only the generated artefacts need
   their own sizing and framing. */
.business-asset-qr {
  display: block;
  width: min(220px, 100%);
  height: auto;
  margin: 8px 0 18px;
  background: #fff;
  border: 1px solid var(--border);
  padding: 10px;
}

.business-essentials-grid,
.business-identity-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: var(--card-gap);
}

.business-essentials-grid > :first-child {
  grid-column: 1 / -1;
}

.business-signature-preview {
  display: block;
  width: 100%;
  min-height: 130px;
  margin: 8px 0 18px;
  padding: 22px;
  background: #fff;
  border: 1px solid var(--border);
}

@media (max-width: 760px) {
  .business-essentials-grid,
  .business-identity-grid {
    grid-template-columns: 1fr;
  }
}

/* ---------- Public information pages ---------- */

.info-hero {
  max-width: 780px;
}

.info-hero h1 {
  max-width: 720px;
  font-size: clamp(2.6rem, 6vw, 4.8rem);
}

/* Showcase's own hero -- a left column of copy/stats/actions next to a
   right column of four overlapping real example-site photos, so a
   visitor sees actual variety before ever reaching the full gallery
   below (linked via the "Browse the gallery" action's #gallery anchor).
   Deliberately bespoke rather than reusing `.info-hero`/`.hero-split`:
   this is the one marketing page whose whole pitch is "look how varied
   the results are", so the header should show that, not just say it. */
.showcase-hero {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  align-items: center;
  gap: clamp(24px, 5vw, 56px);
}

.showcase-hero-copy h1 {
  max-width: 620px;
  font-size: clamp(2.4rem, 5.5vw, 3.6rem);
}

.showcase-hero-copy .lede {
  margin: 0 0 28px;
  text-align: left;
}

.showcase-hero-copy .hero-actions {
  justify-content: flex-start;
  margin-bottom: 0;
}

.showcase-hero-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin-bottom: 32px;
}

.showcase-hero-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.showcase-hero-stat-num {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 600;
  color: var(--green-900);
}

.showcase-hero-stat-label {
  font-size: 0.82rem;
  color: var(--ink-muted);
}

/* A fixed-height stage for the four absolutely-positioned photos below --
   percentages on `.showcase-hero-photo` size and place each tile relative
   to this box, so the stair-stepped collage holds together at any
   viewport width down to the 860px breakpoint where it collapses. */
.showcase-hero-collage {
  position: relative;
  height: 380px;
}

.showcase-hero-photo {
  position: absolute;
  width: 60%;
  height: 60%;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  border: 4px solid var(--paper-raised);
}

.showcase-hero-photo-1 {
  top: 0;
  left: 0;
  transform: rotate(-4deg);
  z-index: 2;
}

.showcase-hero-photo-2 {
  top: 4%;
  right: 0;
  transform: rotate(5deg);
  z-index: 1;
}

.showcase-hero-photo-3 {
  bottom: 0;
  left: 12%;
  transform: rotate(3deg);
  z-index: 3;
}

.showcase-hero-photo-4 {
  bottom: 6%;
  right: 6%;
  transform: rotate(-3deg);
  z-index: 1;
}

@media (max-width: 860px) {
  .showcase-hero {
    grid-template-columns: 1fr;
  }

  .showcase-hero-collage {
    height: 260px;
    order: -1;
  }
}

/* About's own hero -- same left-copy/right-photo-collage shape as
   `.showcase-hero`, but with its own class names since the two pages'
   photos/stats are unrelated and each page's hero is meant to be
   bespoke rather than a shared component (see `.showcase-hero`'s doc
   comment above). */
.about-hero {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  align-items: center;
  gap: clamp(24px, 5vw, 56px);
}

.about-hero-copy h1 {
  max-width: 620px;
  font-size: clamp(2.4rem, 5.5vw, 3.6rem);
}

.about-hero-copy .lede {
  margin: 0 0 28px;
  text-align: left;
}

.about-hero-copy .hero-actions {
  justify-content: flex-start;
  margin-bottom: 0;
}

.about-hero-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin-bottom: 32px;
}

.about-hero-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.about-hero-stat-num {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 600;
  color: var(--green-900);
}

.about-hero-stat-label {
  font-size: 0.82rem;
  color: var(--ink-muted);
}

.about-hero-collage {
  position: relative;
  height: 380px;
}

.about-hero-photo {
  position: absolute;
  width: 60%;
  height: 60%;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  border: 4px solid var(--paper-raised);
}

.about-hero-photo-1 {
  top: 0;
  left: 0;
  transform: rotate(-4deg);
  z-index: 2;
}

.about-hero-photo-2 {
  top: 4%;
  right: 0;
  transform: rotate(5deg);
  z-index: 1;
}

.about-hero-photo-3 {
  bottom: 0;
  left: 12%;
  transform: rotate(3deg);
  z-index: 3;
}

.about-hero-photo-4 {
  bottom: 6%;
  right: 6%;
  transform: rotate(-3deg);
  z-index: 1;
}

@media (max-width: 860px) {
  .about-hero {
    grid-template-columns: 1fr;
  }

  .about-hero-collage {
    height: 260px;
    order: -1;
  }
}

/* About's "what to expect" section -- a badge + title checklist grid,
   deliberately a new component (`.expect-grid`/`.expect-item`) rather
   than reusing `.who-grid`/`.who-item` (Home's dot-marker version of the
   same idea): `.who-grid` is a shared component Home also uses for its
   "who it's for" section, and this is a wider, two-column, more
   expansive treatment that would look wrong applied there too. */
.expect-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}

.expect-item {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 22px 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.expect-item .item-badge {
  flex-shrink: 0;
}

.expect-item-title {
  font-weight: 600;
  color: var(--green-900);
}

@media (max-width: 720px) {
  .expect-grid {
    grid-template-columns: 1fr;
  }
}

/* Pairs the quality-process `.numbered-rows` list with a real supporting
   photo alongside it, the same two-column shape as `.media-row` but
   built around the numbered list instead of a paragraph of copy. */
.process-timeline {
  display: grid;
  grid-template-columns: 0.9fr 1.1fr;
  gap: clamp(28px, 5vw, 56px);
  align-items: center;
}

.process-timeline-photo img {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 360px;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}

@media (max-width: 860px) {
  .process-timeline {
    grid-template-columns: 1fr;
  }

  .process-timeline-photo img {
    min-height: 240px;
  }
}

/* How it works' own hero -- same bespoke left-copy/right-photo-collage
   shape as `.showcase-hero`/`.about-hero`, with its own class names for
   the same reason those two have theirs (see `.about-hero`'s doc
   comment above). */
.how-hero {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  align-items: center;
  gap: clamp(24px, 5vw, 56px);
}

.how-hero-copy h1 {
  max-width: 620px;
  font-size: clamp(2.4rem, 5.5vw, 3.6rem);
}

.how-hero-copy .lede {
  margin: 0 0 28px;
  text-align: left;
}

.how-hero-copy .hero-actions {
  justify-content: flex-start;
  margin-bottom: 0;
}

.how-hero-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin-bottom: 32px;
}

.how-hero-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.how-hero-stat-num {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 600;
  color: var(--green-900);
}

.how-hero-stat-label {
  font-size: 0.82rem;
  color: var(--ink-muted);
}

.how-hero-collage {
  position: relative;
  height: 380px;
}

.how-hero-photo {
  position: absolute;
  width: 60%;
  height: 60%;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  border: 4px solid var(--paper-raised);
}

.how-hero-photo-1 {
  top: 0;
  left: 0;
  transform: rotate(-4deg);
  z-index: 2;
}

.how-hero-photo-2 {
  top: 4%;
  right: 0;
  transform: rotate(5deg);
  z-index: 1;
}

.how-hero-photo-3 {
  bottom: 0;
  left: 12%;
  transform: rotate(3deg);
  z-index: 3;
}

.how-hero-photo-4 {
  bottom: 6%;
  right: 6%;
  transform: rotate(-3deg);
  z-index: 1;
}

@media (max-width: 860px) {
  .how-hero {
    grid-template-columns: 1fr;
  }

  .how-hero-collage {
    height: 260px;
    order: -1;
  }
}

/* Contact's own hero -- same bespoke left-copy/right-photo-collage shape
   as `.showcase-hero`/`.about-hero`/`.how-hero`, with its own class
   names for the same reason those have theirs. No `.hero-actions` here
   (unlike the others) since the page's one real call to action is the
   form itself, right below. */
.contact-hero {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  align-items: center;
  gap: clamp(24px, 5vw, 56px);
}

.contact-hero-copy h1 {
  max-width: 620px;
  font-size: clamp(2.4rem, 5.5vw, 3.6rem);
}

.contact-hero-copy .lede {
  margin: 0 0 28px;
  text-align: left;
}

.contact-hero-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
}

.contact-hero-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.contact-hero-stat-num {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 600;
  color: var(--green-900);
}

.contact-hero-stat-label {
  font-size: 0.82rem;
  color: var(--ink-muted);
}

.contact-hero-collage {
  position: relative;
  height: 380px;
}

.contact-hero-photo {
  position: absolute;
  width: 60%;
  height: 60%;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  border: 4px solid var(--paper-raised);
}

.contact-hero-photo-1 {
  top: 0;
  left: 0;
  transform: rotate(-4deg);
  z-index: 2;
}

.contact-hero-photo-2 {
  top: 4%;
  right: 0;
  transform: rotate(5deg);
  z-index: 1;
}

.contact-hero-photo-3 {
  bottom: 0;
  left: 12%;
  transform: rotate(3deg);
  z-index: 3;
}

.contact-hero-photo-4 {
  bottom: 6%;
  right: 6%;
  transform: rotate(-3deg);
  z-index: 1;
}

@media (max-width: 860px) {
  .contact-hero {
    grid-template-columns: 1fr;
  }

  .contact-hero-collage {
    height: 260px;
    order: -1;
  }
}

/* How it works' six-step process, as a photo bento grid -- the same
   real-photo-with-gradient-overlay treatment as Pricing's `.page-strip`,
   but its own class names since this page's steps are its own content
   (two wide "getting started" tiles up top, four narrower "delivery"
   tiles below, on a 12-column track rather than `.page-strip`'s 6). */
.workflow-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 20px;
}

.workflow-item {
  position: relative;
  grid-column: span 6;
  min-height: 260px;
  display: flex;
  align-items: flex-end;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background-size: cover;
  background-position: center;
}

.workflow-item::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(15, 13, 8, 0.12) 0%, rgba(15, 13, 8, 0.6) 48%, rgba(15, 13, 8, 0.94) 100%);
}

.workflow-item-sm {
  grid-column: span 3;
  min-height: 220px;
}

.workflow-item-inner {
  position: relative;
  z-index: 1;
  padding: 24px;
}

.workflow-item-inner .item-badge {
  margin-bottom: 10px;
}

.workflow-item-inner h3 {
  margin: 0 0 6px;
  font-size: 1.1rem;
  color: #fff;
}

.workflow-item-inner p {
  margin: 0;
  max-width: 30ch;
  font-size: 0.82rem;
  color: rgba(255, 255, 255, 0.88);
}

@media (max-width: 960px) {

  .workflow-item,
  .workflow-item-sm {
    grid-column: span 6;
  }
}

@media (max-width: 560px) {

  .workflow-item,
  .workflow-item-sm {
    grid-column: span 12;
  }
}

.process-grid,
.contact-grid {
  display: grid;
  gap: 24px;
}

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

/* A centered link/button sitting just under a summarized section's content
   inside the same `.msection` (e.g. Home's mini how-it-works, right below
   its `.numbered-rows`) -- a "see the full page" link, not a new section
   of its own. */
.section-link {
  text-align: center;
  margin-top: 24px;
}

/* A short centered caption sitting under a grid section (e.g. the
   Showcase gallery's "these are example concepts" note) -- layout only,
   pair with `.text-muted` for the usual muted colour. */
.section-note {
  text-align: center;
  margin-top: 20px;
}

/* A block of real, readable prose (Home's closing SEO copy) rather than
   another card grid -- narrower than the full `.container` so paragraph
   line length stays comfortable, and normal (not muted) text colour since
   this is substantive content for a person to actually read, not a
   caption. */
.prose-block {
  max-width: 760px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Centers a heading + short intro paragraph above content further down the
   same `.msection` (e.g. introducing a `.numbered-rows` list) -- not an
   `.info-hero`, which owns the whole top of a page instead.

   `.section-heading-v2` below is the editorial version, and is what the
   marketing pages use now; this plain one survives on the public article
   pages. */
.section-heading {
  text-align: center;
  max-width: 560px;
  margin: 0 auto 40px;
}

.section-heading h2 {
  margin-bottom: 8px;
}

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

.process-card,
.contact-card {
  padding: 34px;
}

.process-number {
  display: inline-block;
  margin-bottom: 28px;
  color: var(--rust-600);
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 600;
}

.process-card h2,
.contact-card h2 {
  margin-bottom: 10px;
}

.contact-card .btn {
  margin-top: 16px;
}

.contact-card .item-badge {
  margin-bottom: 16px;
}

/* A lighter-weight call to action than `.cta-band` (full-bleed, reserved
   for the one big "ready to start" moment at the end of a page) -- this
   is the in-page nudge used partway down several pages, so it needs to
   read as an "act now" beat rather than blend into the same flat
   `.card` surface used for grids of unrelated content everywhere else.
   A warmer gradient, a fuller rust edge and its own lifted shadow (on
   top of `.card`'s base one) give it more presence than a plain card
   without going as loud as the full-bleed band. */
.info-cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 44px 48px;
  background: linear-gradient(125deg, var(--paper-raised) 30%, var(--rust-tint) 160%);
  border: 1px solid var(--rust-200);
  border-left: 6px solid var(--rust-500);
  box-shadow: 0 30px 60px -34px rgba(156, 78, 45, 0.4);
}

.info-cta h2,
.info-cta .eyebrow {
  margin-bottom: 0;
}

.info-cta .btn {
  flex-shrink: 0;
}

.plan-preview-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin: 20px 0 26px;
}

.plan-preview-item {
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-sunken);
}

.plan-preview-item span,
.plan-preview-item strong {
  display: block;
}

.plan-preview-item span {
  margin-bottom: 4px;
  color: var(--ink-muted);
  font-size: 0.75rem;
}

/* ---------- Showcase ---------- */

/* Each card is a picture of a website with its name written over it, not
   a photo with a caption panel bolted underneath.

   It was the latter, and the panel was the problem: a white block of text
   under every tile made a page of twenty-eight screenshots read as a page
   of twenty-eight forms. The screenshot is the thing worth looking at, so
   it gets the whole tile, and the words sit on it in the same dark green
   and paper the footer and the closing bands use. */
.showcase-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}

/* A real `<button>`, because the whole tile opens the modal preview (see
   static/js/showcase.js) and that has to work from a keyboard. `padding:
   0` and `border: 0` undo the UA button styling the tile does not want.
   The fixed ratio is what keeps a row even without any of the flex
   stretching the old caption panel needed -- and 4:3 rather than the
   capture's 3:2 makes the tile taller, which is most of "less cardy". */
.showcase-card {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--green-950);
  text-align: left;
  font: inherit;
  cursor: pointer;
  transition: transform var(--transition), box-shadow var(--transition);
}

.showcase-card:hover,
.showcase-card:focus-visible {
  transform: translateY(-3px);
  box-shadow: var(--shadow-card);
}

/* The frame fills the tile and drops its own chrome: a title bar is right
   on a single screenshot standing alone, and twenty-eight of them in a
   grid is twenty-eight rows of dots. */
.showcase-card .site-shot {
  position: absolute;
  inset: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: var(--green-950);
}

.showcase-card .site-shot-chrome {
  display: none;
}

.showcase-card .site-shot-page,
.showcase-card .site-shot-pending {
  height: 100%;
  aspect-ratio: auto;
  transition: transform 0.4s ease;
}

.showcase-card:hover .site-shot-page {
  transform: scale(1.03);
}

/* The scrim. Three stops rather than two: a straight linear fade leaves a
   visible band where it meets the image, and the middle stop is what
   makes it read as shadow instead of as a rectangle.

   Four stops, and the box is mostly padding.

   The height above the text is all gradient -- no content moved into it --
   so raising `padding-top` lengthens the fade rather than covering more of
   the design with anything solid. That is the difference between this and
   the first attempt at the card, which was 58% scrim because the style
   label was stacked inside it: same proportion, completely different
   effect.

   The stops are placed against where the words actually sit. Text occupies
   roughly the bottom 12%-52% of the box, so the scrim is at full strength
   through all of it and only starts giving way above 55%. The last two
   stops are the tail: a straight run to zero leaves a visible edge where
   it meets the image, and 55% at 78% is what turns it into shadow.

   Measured against the palest thing that can sit underneath -- a white
   hero band, which several of the twenty-eight have -- the title sits 38%
   up the scrim at 95.6% opacity and reads at 12.68:1, and the description
   below it at 13.8:1. The scrim does not drop to half strength until 80%
   of the way up, by which point there is nothing written on it. */
.showcase-card-overlay {
  position: absolute;
  inset: auto 0 0;
  padding: 76px 20px 18px;
  color: #f6f0e4;
  background: linear-gradient(
    to top,
    rgb(15 36 25 / 100%) 0%,
    rgb(15 36 25 / 94%) 52%,
    rgb(15 36 25 / 55%) 78%,
    rgb(15 36 25 / 0%) 100%
  );
}

.showcase-card-overlay h3 {
  margin: 0 0 4px;
  font-size: 1.05rem;
  line-height: 1.2;
  color: #fffdf9;
}

.showcase-card-overlay p {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.45;
  color: rgb(246 240 228 / 76%);
  /* Two lines, so a long blurb cannot push the scrim up over the design
     it is describing. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.showcase-badge {
  display: inline-block;
  margin-bottom: 10px;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--rust-tint);
  color: var(--rust-700);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* Floated into the top-left corner, over whatever the design happens to
   put there. A paper chip rather than the page's warm-tint version: light
   text on `--rust-tint` has nowhere near enough contrast against
   twenty-eight unknown backgrounds, and a near-opaque paper chip is
   legible over all of them. */
.showcase-card > .showcase-badge {
  position: absolute;
  top: 14px;
  left: 14px;
  margin-bottom: 0;
  background: rgb(246 240 228 / 94%);
  color: var(--rust-700);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

/* Full-screen preview -- a real modal (not a new tab/window) so a visitor
   can flip through several examples without ever leaving the Showcase
   page underneath it. Hidden by default; static/js/showcase.js toggles
   `.is-open` and swaps the iframe's `src` rather than mounting/unmounting
   several iframes at once. */
.showcase-modal {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  align-items: center;
  justify-content: center;
  padding: clamp(12px, 3vw, 40px);
  background: rgba(15, 36, 25, 0.72);
}

.showcase-modal.is-open {
  display: flex;
}

.showcase-modal-dialog {
  position: relative;
  width: 100%;
  max-width: 1440px;
  height: 100%;
  max-height: 92vh;
  background: var(--paper-raised);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-dropdown);
  display: flex;
  flex-direction: column;
}

.showcase-modal-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--border);
  background: var(--paper);
}

.showcase-modal-title {
  font-weight: 600;
  color: var(--green-900);
  font-size: 0.95rem;
}

.showcase-modal-actions {
  display: flex;
  align-items: center;
  gap: 6px;
}

.showcase-modal-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--paper-raised);
  color: var(--green-900);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  transition: border-color var(--transition), color var(--transition);
}

.showcase-modal-btn:hover {
  border-color: var(--rust-500);
  color: var(--rust-600);
}

.showcase-modal-frame-wrap {
  flex: 1;
  background: var(--paper-sunken);
}

.showcase-modal-frame-wrap iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* ---------- Support tickets ---------- */

.page-head-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
}

.page-head-row h1 {
  margin: 4px 0 0;
}

/* ---------- Tickets: lists and queues ---------- */

/* Tickets are threads, not table rows, so both the customer's list and the
   admin queue are rendered as conversation rows. Grouped under headings
   ("Waiting on you", "Needs a reply") so the only question that matters --
   whose turn is it -- is answered by the page's structure rather than by a
   pill the reader has to find in a column. */
.ticket-group {
  margin-top: 28px;
}

/* Heading and count on one line. The count is the customer's side of what
   the admin queue has always shown: how much is actually on you, without
   counting rows. */
.ticket-group-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.ticket-group-heading {
  margin: 0;
  font-size: 1.05rem;
}

.ticket-group-count {
  font-size: 0.8rem;
  color: var(--ink-faint);
  white-space: nowrap;
}

.ticket-group-note {
  margin: 4px 0 12px;
  font-size: 0.88rem;
  color: var(--ink-muted);
}

/* On the customer index, a heading and its conversation rows are one
   object. The admin queue also uses `.ticket-group`, so the cohesive card
   treatment is deliberately scoped to the customer-only class. */
.customer-ticket-group {
  margin-top: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  overflow: hidden;
}

.customer-ticket-group.is-waiting {
  border-color: rgb(156 78 45 / 32%);
}

.customer-ticket-group .ticket-group-head {
  padding: 18px 20px 0;
}

.customer-ticket-group .ticket-group-heading {
  font-size: 1.12rem;
}

.customer-ticket-group .ticket-group-count {
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunken);
  color: var(--ink-muted);
  font-weight: 650;
}

.customer-ticket-group .ticket-group-note {
  margin: 4px 0 0;
  padding: 0 20px 16px;
}

.customer-ticket-group .ticket-list {
  border: 0;
  border-top: 1px solid var(--border);
  border-radius: 0;
}

/* Closed tickets: a record, not a to-do list.

   They used to sit in the same group as live ones, sorted by recency
   alongside them, so a ticket closed yesterday outranked one we'd been
   working on all week. Collapsed by default and visually quieter, because
   the page's job is what needs doing. */
.ticket-archive {
  margin-top: 28px;
}

.ticket-archive-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-tint);
  color: var(--ink-muted);
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
  transition: background var(--transition), color var(--transition);
}

/* Safari renders a disclosure triangle via this pseudo-element, which
   `list-style: none` alone doesn't remove -- and we draw our own chevron. */
.ticket-archive-summary::-webkit-details-marker {
  display: none;
}

.ticket-archive-summary:hover {
  background: var(--paper-sunken);
  color: var(--green-900);
}

.ticket-archive-summary:focus-visible {
  outline: 2px solid var(--rust-500);
  outline-offset: 2px;
}

.ticket-archive-chevron {
  transition: transform var(--transition);
}

.ticket-archive[open] .ticket-archive-chevron {
  transform: rotate(180deg);
}

.ticket-archive[open] .ticket-archive-summary {
  margin-bottom: 12px;
}

.ticket-archive-note {
  margin: 12px 0 0;
  font-size: 0.82rem;
  color: var(--ink-faint);
}

@media (prefers-reduced-motion: reduce) {

  .ticket-archive-chevron,
  .app-shell,
  .app-rail,
  .app-panel,
  .app-drawer-backdrop {
    transition: none;
  }
}

.ticket-list {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  overflow: hidden;
}

/* `align-items: flex-start` with a little top padding on the side column
   rather than `center`: the row is now three lines tall (subject, preview,
   meta) and a vertically centred timestamp floated against the middle of
   the preview instead of lining up with the subject it belongs to. */
.ticket-row {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 16px 20px;
  border-top: 1px solid var(--border);
  color: inherit;
  text-decoration: none;
  transition: background var(--transition);
}

.ticket-row:first-child {
  border-top: 0;
}

.ticket-row:hover {
  background: var(--paper-tint);
}

.ticket-row-main {
  flex: 1;
  min-width: 0;
}

.ticket-row-title {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* The last thing said in the thread.

   This is the line that turned the list from an index into something you
   can triage: subject, site and category tell you a ticket exists, not
   whether it needs you, so deciding meant opening every row.

   Clamped to one line here as well as at 140 characters in
   `views::tickets::preview`. The Rust side keeps a 4KB body off the wire
   and cuts on a word; this keeps the row one line high at any width, which
   character counting alone can't promise. */
.ticket-row-preview {
  margin: 5px 0 0;
  font-size: 0.88rem;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ticket-row-speaker {
  margin-right: 5px;
  color: var(--green-900);
  font-weight: 600;
}

.ticket-row-meta {
  margin: 5px 0 0;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.ticket-row-side {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  /* Optically aligns with the subject line above, which is 1rem/1.6. */
  padding-top: 2px;
}

.ticket-row-time {
  font-size: 0.78rem;
  color: var(--ink-faint);
  /* The exact timestamp is in the `title`, matching the thread. */
  cursor: help;
  white-space: nowrap;
}

.ticket-row-go {
  color: var(--ink-faint);
  transition: transform var(--transition), color var(--transition);
}

.ticket-row:hover .ticket-row-go {
  transform: translateX(3px);
  color: var(--rust-600);
}

@media (prefers-reduced-motion: reduce) {

  .ticket-row-go {
    transition: none;
  }

  .ticket-row:hover .ticket-row-go {
    transform: none;
  }
}

@media (max-width: 560px) {

  /* The arrow is decoration doubling as an affordance; the whole row is
     already a link, and at this width the space is better spent on the
     preview. */
  .ticket-row-go {
    display: none;
  }
}

/* The admin queue's own row: age first, because triage is "what's been
   waiting longest", then subject/customer, then claim + status. */
.ticket-queue {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  overflow: hidden;
}

.ticket-queue-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 20px;
  border-top: 1px solid var(--border);
  color: inherit;
  text-decoration: none;
  transition: background var(--transition);
}

.ticket-queue-row:first-child {
  border-top: 0;
}

.ticket-queue-row:hover {
  background: var(--paper-tint);
}

/* Fixed width so the ages form a readable column down the left edge
   rather than shifting with each row's content. */
.ticket-queue-age {
  flex-shrink: 0;
  width: 52px;
  font-size: 0.8rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}

.ticket-queue-age.is-due {
  color: var(--rust-600);
}

.ticket-queue-age.is-overdue {
  color: var(--rust-700);
}

.ticket-queue-main {
  flex: 1;
  min-width: 0;
}

.ticket-queue-meta {
  margin: 3px 0 0;
  font-size: 0.82rem;
  color: var(--ink-muted);
}

.ticket-queue-side {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.ticket-queue-claim {
  font-size: 0.78rem;
  color: var(--ink-muted);
}

.ticket-queue-claim.is-unclaimed {
  color: var(--rust-700);
  font-weight: 600;
}

/* ---------- Tickets: detail ---------- */

.ticket-detail-meta {
  margin: 6px 0 0;
  font-size: 0.86rem;
  color: var(--ink-muted);
}

/* Thread on the left, the things you do to the ticket on the right --
   reading and administering are different jobs and were previously
   competing for the same toolbar row. */
.ticket-detail-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 24px;
  align-items: start;
  margin-top: 20px;
}

/* `min-width: 0` so a long unbroken string in a message body (a pasted URL,
   say) wraps instead of forcing the whole grid wider than its column. */
.ticket-detail-main {
  min-width: 0;
}

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

.ticket-detail-side h3 {
  margin: 0 0 10px;
  font-size: 0.95rem;
}

.ticket-detail-side p {
  margin: 0 0 12px;
  font-size: 0.85rem;
}

.ticket-detail-side form {
  margin: 0;
}

.ticket-reply-card {
  margin-top: 20px;
}

/* ---------- Support conversation ----------

   Shared by the customer's ticket page and the admin's, driven by
   `support::views::thread`. The old thread distinguished its two speakers
   with a background tint and a small pill, which meant working out who
   said what required noticing a colour. Here every message carries four
   independent signals -- avatar, name, role pill, and which side of the
   thread it sits on -- so the answer is never more than a glance away.

   "Own" is relative to the reader: a customer's own messages are green and
   right-aligned, and an admin sees exactly the same treatment applied to
   theirs. Nothing in the CSS knows which of the two is looking. */

/* Whose court the ball is in, stated outright.

   The status pill above says "Open", which is equally true of a ticket we
   replied to an hour ago and one nobody has picked up -- so the single
   most useful fact on the page was the one it didn't show. */
.support-state {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 20px 0 24px;
  padding: 16px 20px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--green-700);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
}

.support-state-dot {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-radius: var(--radius-pill);
  background: var(--green-700);
}

.support-state-copy {
  min-width: 0;
}

.support-state-copy strong {
  display: block;
  font-size: 0.95rem;
  color: var(--green-900);
}

.support-state-copy p {
  margin: 3px 0 0;
  font-size: 0.86rem;
  color: var(--ink-muted);
}

.support-state-count {
  margin-left: auto;
  font-size: 0.78rem;
  color: var(--ink-muted);
  white-space: nowrap;
}

.support-state.is-yours {
  border-left-color: var(--rust-500);
  background: var(--rust-tint);
}

.support-state.is-yours .support-state-dot {
  background: var(--rust-500);
}

.support-state.is-closed {
  border-left-color: var(--ink-faint);
  background: var(--paper-sunken);
}

.support-state.is-closed .support-state-dot {
  background: var(--ink-faint);
}

/* The thread sits on its own raised surface so the conversation reads as
   one object rather than a run of loose boxes down the page. */
.support-thread {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 22px;
  list-style: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
}

.support-thread> :first-child {
  margin-top: 0;
}

/* ---------- Day dividers ---------- */

.support-day {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 26px 0 18px;
}

.support-day::before,
.support-day::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--border);
}

.support-day-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
}

/* ---------- A message ---------- */

.support-msg {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0;
}

.support-msg+.support-msg {
  margin-top: 20px;
}

/* A burst from one person inside `GROUPING_WINDOW` reads as one turn:
   tighter spacing, and the avatar is kept for alignment but hidden. */
.support-msg.is-continued {
  margin-top: 6px;
}

.support-msg.is-continued .support-avatar {
  visibility: hidden;
}

.support-day+.support-msg {
  margin-top: 0;
}

/* Reversing the row is what puts the reader's own messages on the right.
   The DOM order stays avatar-then-content either way, so a screen reader
   still hears the author before the message. */
.support-msg-own {
  flex-direction: row-reverse;
}

.support-msg-col {
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* `min-width: 0` so a pasted URL wraps instead of forcing the whole
     thread wider than its container. */
  min-width: 0;
  max-width: min(560px, 82%);
}

.support-msg-own .support-msg-col {
  align-items: flex-end;
}

.support-msg-meta {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
}

.support-msg-own .support-msg-meta {
  flex-direction: row-reverse;
}

.support-msg-author {
  font-size: 0.87rem;
  font-weight: 650;
  color: var(--green-900);
}

.support-msg-time {
  font-size: 0.75rem;
  color: var(--ink-faint);
  /* The exact timestamp is in the `title`, so the pointer should say so. */
  cursor: help;
}

.support-msg-bubble {
  padding: 14px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper);
}

/* The one squared corner points back at its own avatar -- stated for both
   sides rather than leaving one as the default, so neither can be changed
   without the other being considered. */
.support-msg-other .support-msg-bubble {
  border-top-left-radius: 5px;
}

.support-msg-own .support-msg-bubble {
  border-top-right-radius: 5px;
  border-color: var(--green-900);
  background: var(--green-900);
  color: var(--paper);
}

.support-msg-own .support-msg-bubble a {
  color: var(--rust-200);
}

.support-msg-body {
  margin: 0;
  white-space: pre-wrap;
  line-height: 1.65;
  /* Customers paste URLs and error strings; neither should be allowed to
     push the layout sideways. */
  overflow-wrap: anywhere;
}

.support-msg-images {
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  margin: 14px 0 0;
}

.support-msg-own .support-msg-images .image-gallery-item {
  border-color: rgba(255, 255, 255, 0.2);
  background: rgba(255, 255, 255, 0.08);
}

/* ---------- Internal notes (admin only) ----------

   Placed after the `-own` rules deliberately: an internal note is written
   by staff, so it would otherwise inherit the solid green "my message"
   treatment, and a note that looks like a reply is the one mistake in this
   interface that reaches the customer. Same specificity, later wins. */
.support-msg-note .support-msg-bubble {
  border: 1px dashed var(--border-strong);
  background: var(--paper-sunken);
  color: var(--green-900);
}

.support-msg-note-flag {
  display: block;
  margin-bottom: 8px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--rust-600);
}

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

.support-avatar {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  user-select: none;
}

.support-avatar-own {
  background: var(--green-900);
  color: var(--paper);
}

.support-avatar-staff {
  background: var(--rust-500);
  color: #fff;
}

.support-avatar-customer {
  border-color: var(--border-strong);
  background: var(--paper-sunken);
  color: var(--green-900);
}

.support-avatar-note {
  border: 1px dashed var(--border-strong);
  background: var(--paper-sunken);
  color: var(--ink-muted);
}

/* ---------- Composer ---------- */

.support-composer {
  margin-top: 22px;
}

.support-composer-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 20px;
}

.support-composer-head h2 {
  margin: 0;
  font-size: 1.1rem;
}

.support-composer-head p {
  margin: 4px 0 0;
  font-size: 0.85rem;
}

.support-composer-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 4px;
}

@media (max-width: 640px) {

  .support-msg-col {
    max-width: 100%;
  }

  .support-avatar {
    width: 32px;
    height: 32px;
    font-size: 0.72rem;
  }

  .support-thread {
    padding: 16px 14px;
  }

  .support-state {
    align-items: flex-start;
    flex-wrap: wrap;
  }

  .support-state-count {
    margin-left: 0;
  }
}

@media (max-width: 720px) {

  .process-grid,
  .contact-grid,
  .plan-preview-grid,
  .compare-grid,
  .showcase-grid {
    grid-template-columns: 1fr;
  }

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

  .msection-intro {
    padding-top: 48px;
  }

  .info-cta {
    align-items: flex-start;
    flex-direction: column;
  }

  .showcase-modal-dialog {
    max-height: none;
    border-radius: var(--radius-md);
  }

  .band-split,
  .quote-support {
    grid-template-columns: 1fr;
  }

  .band-split h2 {
    margin-bottom: 8px;
  }

  .numbered-row {
    grid-template-columns: 36px 1fr;
  }
}

/* ---------- Status pill: private/warn variant (articles::views::admin,
   sites::views admin lists) -- amber/rust, distinct from the green "live"
   and grey "quiet" variants above. ---------- */

.status-warn {
  background: rgba(156, 78, 45, 0.12);
  color: var(--rust-700);
}

/* ---------- Articles: public index/show (articles::views::public) ---------- */

.post-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 32px;
}

.post-filter-pill {
  display: inline-flex;
  align-items: center;
  padding: 7px 16px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--paper);
  color: var(--ink-muted);
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.post-filter-pill:hover {
  border-color: var(--green-700);
  color: var(--green-800);
}

.post-filter-pill-active {
  background: var(--green-900);
  border-color: var(--green-900);
  color: var(--paper-tint);
}

.post-filter-pill-active:hover {
  color: var(--paper-tint);
}

/* The public library is an editorial product rather than another generic
   marketing hero. The note gives the opener a useful second column and the
   index heading creates a clear hand-off from promise to reading. */
.articles-hero {
  overflow: hidden;
}

.articles-hero-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(270px, 0.55fr);
  gap: clamp(40px, 7vw, 96px);
  align-items: end;
}

.articles-hero-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
}

.articles-hero-copy h1,
.articles-hero-copy p,
.articles-hero-note p {
  margin: 0;
}

.articles-hero-note {
  padding: 28px;
  border-top: 3px solid var(--rust-600);
  background: rgb(255 253 248 / 72%);
  box-shadow: var(--shadow-card);
  backdrop-filter: blur(8px);
}

.articles-hero-note strong {
  display: block;
  margin-top: 10px;
  font-family: var(--font-display);
  font-size: 1.22rem;
  line-height: 1.35;
  color: var(--green-900);
}

.articles-hero-note ul {
  display: grid;
  gap: 10px;
  margin: 22px 0 0;
  padding: 18px 0 0;
  border-top: 1px solid var(--border);
  list-style: none;
}

.articles-hero-note li {
  position: relative;
  padding-left: 18px;
  color: var(--ink-muted);
  font-size: 0.9rem;
  line-height: 1.45;
}

.articles-hero-note li::before {
  content: "";
  position: absolute;
  top: 0.58em;
  left: 0;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--rust-500);
}

.articles-index-head {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(280px, 0.65fr);
  gap: clamp(24px, 5vw, 72px);
  align-items: end;
  margin-bottom: 32px;
}

.articles-index-head h2,
.articles-index-head p {
  margin-top: 0;
}

.articles-index-head .lede-lg {
  margin-bottom: 4px;
  font-size: 1rem;
}

.articles-empty {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 28px 0;
  border-block: 1px solid var(--border);
}

.post-feature {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: 40px;
  align-items: center;
  padding-bottom: 48px;
  margin-bottom: 48px;
  border-bottom: 1px solid var(--border);
}

.post-feature-media {
  position: relative;
  display: block;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  aspect-ratio: 4 / 3;
  background: var(--paper-raised);
}

.post-feature-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.post-feature-media-fallback {
  width: 100%;
  height: 100%;
}

.post-feature-body h2 {
  font-family: var(--font-display);
  font-size: 1.9rem;
  line-height: 1.2;
  margin: 10px 0 12px;
}

.post-feature-body h2 a {
  color: inherit;
  text-decoration: none;
}

.post-feature-body h2 a:hover {
  color: var(--rust-600);
}

.post-feature-body .btn {
  margin-top: 16px;
}

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

.post-card {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--paper);
  border: 1px solid var(--border);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.post-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-card-hover, var(--shadow-card));
}

.post-card-media {
  position: relative;
  display: block;
  aspect-ratio: 16 / 10;
  background: var(--paper-raised);
}

.post-card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.post-card-media-fallback {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: space-between;
  padding: clamp(22px, 3vw, 34px);
  background: linear-gradient(135deg, var(--green-700) 0%, var(--green-950) 100%);
  overflow: hidden;
}

.post-card-media-fallback .post-card-media-number {
  align-self: flex-end;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(3.5rem, 8vw, 7rem);
  line-height: 0.8;
  letter-spacing: -0.06em;
  color: var(--paper-tint);
  opacity: 0.26;
}

.post-card-media-kicker {
  position: relative;
  z-index: 1;
  max-width: 20ch;
  color: var(--paper-tint);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.11em;
  line-height: 1.4;
  text-transform: uppercase;
}

.post-card-media-rule {
  position: absolute;
  right: 0;
  bottom: 30%;
  left: 34%;
  height: 1px;
  background: rgb(255 255 255 / 38%);
  transform: rotate(-8deg);
  transform-origin: right;
}

.post-card-media-fallback.post-tone-1 {
  background: linear-gradient(145deg, #8d3f2a, #3f1f18);
}

.post-card-media-fallback.post-tone-2 {
  background: linear-gradient(145deg, #273d4d, #101d27);
}

.post-card-media-fallback.post-tone-3 {
  background: linear-gradient(145deg, #9a6b28, #40311d);
}

.post-card-body {
  padding: 20px;
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 8px;
}

.post-card-category {
  display: inline-block;
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: rgba(26, 53, 39, 0.08);
  color: var(--green-800);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.post-card-body h3 {
  font-size: 1.15rem;
  margin: 0;
  line-height: 1.3;
}

.post-card-body h3 a {
  color: inherit;
  text-decoration: none;
}

.post-card-body h3 a:hover {
  color: var(--rust-600);
}

.post-card-body p {
  margin: 0;
  flex: 1;
}

.post-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--ink-muted);
  font-size: 0.82rem;
  margin-top: auto;
}

.post-meta-dot {
  opacity: 0.5;
}

/* ---------- Articles: single post page ---------- */

.post-page-head {
  padding-top: clamp(48px, 6vw, 72px);
  max-width: 1040px;
}

.post-page-head > h1,
.post-page-head > .post-standfirst,
.post-page-head > .post-byline,
.post-page-head > .post-page-kicker-row,
.post-page-head > .post-site-highlight {
  max-width: 720px;
}

.post-page-kicker-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 16px;
  margin-bottom: 22px;
}

.post-page-kicker-row .breadcrumb-link {
  margin: 0;
}

.post-page-category {
  padding-left: 16px;
  border-left: 1px solid var(--border-strong);
  color: var(--rust-700);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-decoration: none;
  text-transform: uppercase;
}

.post-page-category:hover {
  text-decoration: underline;
}

.post-page-head h1 {
  font-family: var(--font-display);
  font-size: clamp(2.55rem, 2rem + 2vw, 3.55rem);
  line-height: 1.08;
  letter-spacing: -0.035em;
  margin: 0 0 20px;
  text-wrap: balance;
}

.post-standfirst {
  max-width: 65ch;
  margin: 0 0 26px;
  font-size: clamp(1.1rem, 1rem + 0.4vw, 1.3rem);
  line-height: 1.65;
  color: var(--ink-muted);
}

.post-byline {
  display: flex;
  align-items: center;
  gap: 12px;
}

.post-byline-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 38px;
  height: 38px;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: var(--paper-raised);
  color: var(--rust-600);
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 700;
}

.post-byline > div {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.post-byline strong {
  color: var(--green-900);
  font-size: 0.86rem;
}

.post-byline > div > span {
  color: var(--ink-muted);
  font-size: 0.78rem;
}

/* A small "press circle"-style highlight box for a post's optional linked
   site (`Post::linked_site_id`) -- logo/avatar, name + one-sentence note,
   and a link, never a whole distinct post type (see that field's doc
   comment in `articles::models`). */
.post-site-highlight {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 24px;
  padding: 18px 20px;
  border-radius: var(--radius-lg);
  background: var(--paper-tint);
  border: 1px solid var(--border);
}

.post-site-highlight-body {
  flex: 1;
}

.post-site-highlight-body .eyebrow {
  margin: 0 0 2px;
}

.post-site-highlight-body strong {
  display: block;
  font-size: 1.05rem;
}

.post-site-highlight-body p {
  margin: 2px 0 0;
}

.post-site-highlight-logo {
  width: 52px;
  height: 52px;
  border-radius: var(--radius-md);
  object-fit: contain;
  background: var(--paper);
  border: 1px solid var(--border);
  flex-shrink: 0;
}

.post-site-highlight-logo-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.3rem;
  color: var(--green-800);
  background: var(--paper-raised);
}

.post-page-cover {
  max-width: 1120px;
  margin: 40px auto 0;
  padding: 0 24px;
}

.post-page-cover img {
  width: 100%;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  display: block;
}

.post-page-body {
  padding-top: 56px;
  padding-bottom: 8px;
}

.post-reading-layout {
  display: grid;
  grid-template-columns: minmax(0, 720px) minmax(220px, 1fr);
  gap: clamp(40px, 7vw, 88px);
  align-items: start;
  max-width: 1040px;
  margin: 0 auto;
}

.post-reading-layout > .article-body {
  max-width: none;
  margin: 0;
}

.post-page .post-reading-layout > .article-body {
  font-size: 1.04rem;
  line-height: 1.78;
}

.post-page .post-reading-layout > .article-body > p:first-child {
  margin-bottom: 22px;
  color: var(--green-900);
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 600;
  line-height: 1.55;
}

.post-page .post-reading-layout > .article-body h2 {
  margin-top: 48px;
  font-size: clamp(1.55rem, 1.42rem + 0.4vw, 1.78rem);
  letter-spacing: -0.018em;
}

.post-page .post-reading-layout > .article-body h3 {
  margin-top: 38px;
}

.post-reading-aside {
  position: sticky;
  top: 112px;
  display: flex;
  flex-direction: column;
  padding: 4px 0 4px 24px;
  border-left: 1px solid var(--border-strong);
}

.post-reading-aside strong {
  margin: 8px 0 10px;
  font-family: var(--font-display);
  font-size: 1.2rem;
  color: var(--green-900);
}

.post-reading-aside-copy {
  margin: 0 0 18px;
  color: var(--ink-muted);
  font-size: 0.82rem;
  line-height: 1.55;
}

.post-reading-aside a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  min-height: 46px;
  padding: 10px 0;
  border-top: 1px solid var(--border);
  color: var(--green-800);
  font-size: 0.88rem;
  font-weight: 600;
  text-decoration: none;
}

.post-reading-aside a:hover {
  color: var(--rust-600);
}

.post-tag-row {
  width: 100%;
  max-width: 1040px;
  margin: 40px auto 0;
  padding-top: 24px;
  padding-right: 320px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.post-tag-pill {
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--paper-tint);
  border: 1px solid var(--border);
  color: var(--ink-muted);
  font-size: 0.78rem;
  font-weight: 600;
}

/* ---------- Articles: admin (articles::views::admin) ---------- */

.admin-articles-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 24px;
  align-items: start;
}

.admin-articles-main {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.admin-articles-side {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.admin-articles-tag-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.admin-articles-tag-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 10px;
  border-radius: var(--radius-md);
  background: var(--paper-tint);
  font-size: 0.88rem;
}

.admin-articles-tag-remove {
  border: none;
  background: none;
  color: var(--ink-muted);
  cursor: pointer;
  font-size: 0.8rem;
  line-height: 1;
  padding: 2px 6px;
}

.admin-articles-tag-remove:hover {
  color: var(--rust-600);
}

.admin-articles-import-help {
  margin: 10px 0;
}

.admin-articles-import-help summary {
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--ink-muted);
  font-weight: 600;
}

.admin-articles-import-example {
  margin: 10px 0 0;
  padding: 12px 14px;
  background: var(--green-950);
  color: var(--paper);
  border-radius: var(--radius-md);
  font-size: 0.76rem;
  line-height: 1.5;
  overflow-x: auto;
  white-space: pre;
}

.admin-articles-editor-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}

/* The editor is the job on this screen, so it gets the full working width.
   Metadata follows as a compact settings grid instead of squeezing the
   writing surface beside a 300px stack of cards. */
.admin-article-main-card {
  min-width: 0;
  padding: clamp(22px, 3vw, 32px);
}

.admin-editor-context {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 28px;
  margin-bottom: 18px;
}

.admin-editor-context .breadcrumb-link {
  margin: 0;
}

.admin-editor-saved {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  color: var(--green-700);
  font-size: 0.82rem;
  font-weight: 700;
}

.admin-editor-saved span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: rgb(47 105 74 / 12%);
}

/* Right-hand sidebar of the post editor -- holds every metadata card
   (Organize, Client showcase, Cover image, Client logo, SEO) next to the
   title/excerpt/body column. Renamed from `-editor-media` now that it holds
   more than just the image-upload cards; kept `.admin-articles-media-
   preview` as-is below since it's still purely about image previews. */
.admin-articles-editor-side {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  align-items: start;
}

.admin-articles-editor-side h3 {
  margin: 0 0 14px;
  font-size: 1rem;
}

.admin-articles-editor-side > .card {
  margin: 0;
  box-shadow: none;
}

/* Optional metadata is disclosed on demand. The summary still tells the
   editor what is available and whether it has already been configured. */
.admin-editor-section {
  padding: 0;
  overflow: visible;
}

.admin-editor-section > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 19px 20px;
  color: var(--green-900);
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
}

.admin-editor-section > summary::-webkit-details-marker {
  display: none;
}

.admin-editor-section > summary::after {
  content: "+";
  margin-left: auto;
  color: var(--ink-faint);
  font-family: var(--font-body);
  font-size: 1.15rem;
  font-weight: 400;
}

.admin-editor-section[open] > summary::after {
  content: "\2212";
}

.admin-editor-section > summary small {
  order: 2;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunken);
  color: var(--ink-muted);
  font-family: var(--font-body);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.admin-editor-section > summary::after {
  order: 3;
}

.admin-editor-section-body {
  padding: 2px 20px 20px;
  border-top: 1px solid var(--border);
}

.admin-editor-section-body > .text-muted:first-child {
  margin-top: 16px;
  font-size: 0.86rem;
  line-height: 1.55;
}

/* The save control stays reachable through a long article, but now reads
   as a deliberate command bar rather than a blank floating beige card. */
.admin-articles-editor .article-editor-save-bar {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  bottom: 16px;
  z-index: 12;
  min-height: 64px;
  margin: 22px 160px -8px -8px;
  padding: 10px 12px 10px 16px;
  border-color: rgb(15 36 25 / 72%);
  border-radius: var(--radius-md);
  background: var(--green-950);
  color: var(--paper-tint);
  box-shadow: 0 18px 40px rgb(15 36 25 / 26%);
  backdrop-filter: none;
}

@media (max-width: 600px) {
  .article-editor-save-state span:last-child,
  .admin-articles-editor .article-editor-save-bar kbd {
    display: none;
  }

  .admin-articles-editor .article-editor-save-bar {
    bottom: 78px;
    gap: 10px;
    margin-inline: -12px;
    padding-left: 14px;
  }

  .admin-articles-editor .article-editor-save-bar .btn {
    padding-inline: 13px;
  }
}

.article-editor-save-state {
  display: flex;
  align-items: center;
  gap: 11px;
  min-width: 0;
}

.article-editor-save-dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #9cc8aa;
  box-shadow: 0 0 0 4px rgb(156 200 170 / 12%);
}

.article-editor-save-state div {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.article-editor-save-state strong {
  color: #fffaf0;
  font-size: 0.84rem;
  line-height: 1.35;
}

.article-editor-save-state span:last-child {
  color: rgb(244 237 224 / 62%);
  font-size: 0.72rem;
}

.admin-articles-editor .article-editor-save-bar.is-dirty .article-editor-save-dot {
  background: var(--rust-300);
  box-shadow: 0 0 0 4px rgb(224 158 115 / 14%);
}

.admin-articles-editor .article-editor-save-bar .btn {
  flex: none;
  min-height: 42px;
  padding: 9px 16px;
  background: var(--paper-tint);
  color: var(--green-950);
  box-shadow: none;
}

.admin-articles-editor .article-editor-save-bar .btn:hover {
  background: #fffaf0;
  color: var(--green-950);
}

.admin-articles-editor .article-editor-save-bar kbd {
  margin-left: 8px;
  padding: 2px 6px;
  border: 1px solid rgb(15 36 25 / 18%);
  border-radius: 5px;
  background: rgb(15 36 25 / 7%);
  font-family: var(--font-body);
  font-size: 0.66rem;
  font-weight: 700;
}

.admin-articles-media-preview {
  width: 100%;
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  margin-bottom: 12px;
  display: block;
  object-fit: cover;
  max-height: 160px;
}

/* Tags field in the post editor's "Organize" card -- a small vanilla-JS
   chip widget (static/js/admin-article-tags.js) over a hidden plain
   `<input name="tags">`, so it still submits as the same comma-joined
   string `ArticlesService::resolve_tag_names` already expects. */
.article-tags-widget {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
}

.article-tags-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.article-tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 6px 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--paper-tint);
  border: 1px solid var(--border);
  font-size: 0.82rem;
}

.article-tag-chip-remove {
  border: none;
  background: none;
  cursor: pointer;
  color: var(--ink-muted);
  font-size: 0.9rem;
  line-height: 1;
  padding: 2px 4px;
}

.article-tag-chip-remove:hover {
  color: var(--rust-600);
}

.article-tags-typer {
  flex: 1;
  min-width: 120px;
  border: none;
  outline: none;
  background: transparent;
  font: inherit;
  padding: 4px 2px;
}

@media (max-width: 960px) {
  .articles-hero-layout,
  .post-reading-layout {
    grid-template-columns: 1fr;
  }

  .post-reading-aside {
    position: static;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    padding: 22px 0 0;
    border-top: 1px solid var(--border-strong);
    border-left: 0;
  }

  .post-reading-aside .eyebrow,
  .post-reading-aside strong,
  .post-reading-aside-copy {
    grid-column: 1 / -1;
  }

  .post-tag-row {
    max-width: 720px;
    padding-right: 0;
  }

  .post-reading-aside a {
    margin-right: 18px;
  }

  .post-feature {
    grid-template-columns: 1fr;
  }

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

  .admin-articles-layout,
  .admin-articles-editor-grid {
    grid-template-columns: 1fr;
  }

  .admin-articles-editor-side {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 640px) {
  .articles-index-head {
    grid-template-columns: 1fr;
  }

  .articles-empty {
    align-items: flex-start;
    flex-direction: column;
  }

  .post-reading-aside {
    grid-template-columns: 1fr;
  }

  .post-reading-aside .eyebrow,
  .post-reading-aside strong {
    grid-column: auto;
  }

  .post-grid {
    grid-template-columns: 1fr;
  }

  .post-page-head h1 {
    font-size: 2rem;
  }
}

/* ---------- Landing pages: public blocks (landing::blocks) --------------

   Almost nothing lives here any more, and that is the point.

   Every block now renders through `ui::marketing` -- `section_revealed`,
   `heading`, `pill`, `icon`, `split`, `step_row`, `quote`, `faq_item`,
   `cta_band` -- so it inherits `.msection`, `.section-heading-v2`,
   `.features-grid`, `.included-list`, `.step-track`, `.faq-v2`,
   `.stat-band`, `.quote-card` and `.cta-v2` from the marketing site
   itself. The previous version reached for the *v1* equivalents of those,
   which is why a landing page read as an older, flatter site sitting
   inside this one.

   What remains is the handful of shapes the marketing pages have no
   equivalent for: the notice banner, the logo and badge rows, the media
   frames, the comparison table, the two-column prose split and the lead
   forms. ---------- */

/* The opening column. Matches the inline `max-width:56rem` the Features
   hero uses; a class rather than a style attribute because thirty pages
   render this and one number should live in one place. */
/* The opening column: pill, headline, lede, buttons.
   A flex column with a gap, not margins on the children. `.display` and
   `.display-sm` both set `margin: 0`, and they are declared *after* the
   landing rules, so `.landing-hero-title { margin-top: 24px }` lost on
   source order and every hero opened with its pill jammed against the
   headline. Features.rs works around the same collision with an inline
   `style="margin-top:24px"` on its `h1`, which is the tell.

   Spacing owned by the container cannot be overridden by a utility class
   the child happens to also carry, so this cannot happen again.

   `align-items: flex-start` because `.pill` is `inline-flex`: as a stretch
   flex item it would run the full width of the column. */
.landing-hero-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
  max-width: 56rem;
}

/* The gap already supplies 24px; this tops it up to the 34px the
   marketing heroes put above their buttons. */
.landing-hero-copy .mhero-actions {
  margin-top: 10px;
}

.landing-hero-split {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
}

.landing-hero-media img {
  width: 100%;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  object-fit: cover;
  aspect-ratio: 4 / 3;
}

/* The structure stays recognisably WebHostGB, while each broad sector gets
   one restrained accent. It is enough to stop thirty useful pages feeling
   mechanically cloned without turning them into thirty separate brands. */
.industry-page {
  --industry-accent: var(--rust-600);
  --industry-soft: rgb(159 76 43 / 10%);
}

.industry-page-food {
  --industry-accent: #9a4b2f;
  --industry-soft: rgb(154 75 47 / 11%);
}

.industry-page-creative {
  --industry-accent: #6d4d79;
  --industry-soft: rgb(109 77 121 / 11%);
}

.industry-page-care {
  --industry-accent: #9b5261;
  --industry-soft: rgb(155 82 97 / 11%);
}

.industry-page-professional {
  --industry-accent: #375a70;
  --industry-soft: rgb(55 90 112 / 11%);
}

.industry-page-sport {
  --industry-accent: #34624b;
  --industry-soft: rgb(52 98 75 / 11%);
}

.industry-page .feature-icon {
  color: var(--industry-accent);
  background: var(--industry-soft);
}

.landing-hero-assurance {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 24px;
  width: 100%;
  margin-top: 2px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

.landing-hero-assurance span {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--ink-muted);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.landing-hero-assurance span::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--industry-accent);
}

/* Feature blocks deliberately break the equal-card rhythm. The model's
   content is unchanged; only its hierarchy is expressed more editorially. */
.features-grid.landing-feature-grid {
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 18px;
}

.landing-feature-grid .feature-card {
  grid-column: span 6;
  min-height: 15rem;
  text-align: left;
}

.landing-feature-grid .feature-card:nth-child(6n + 1) {
  grid-column: span 7;
}

.landing-feature-grid .feature-card:nth-child(6n + 2) {
  grid-column: span 5;
}

.landing-feature-grid .feature-card:nth-child(6n + 3) {
  grid-column: span 4;
}

.landing-feature-grid .feature-card:nth-child(6n + 4) {
  grid-column: span 8;
}

.landing-service-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(34px, 6vw, 88px);
  margin-top: 44px;
  border-top: 1px solid var(--border-strong);
}

.landing-service-row {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr);
  gap: 18px;
  padding: 30px 0;
  border-bottom: 1px solid var(--border);
}

.landing-service-number {
  padding-top: 4px;
  color: var(--industry-accent);
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.08em;
}

.landing-service-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}

.landing-service-copy h3,
.landing-service-copy p {
  margin: 0;
}

.landing-service-copy h3 {
  font-family: var(--font-display);
  font-size: 1.25rem;
  line-height: 1.25;
}

.landing-service-copy > p:not(.feature-card-price) {
  color: var(--ink-muted);
  line-height: 1.65;
}

/* A slim notice above the opener. Dark, so it reads as a strip laid over
   the page rather than as the first section of it. */
.landing-banner {
  background: var(--green-950);
  color: var(--paper-tint);
  font-size: 0.9rem;
}

.landing-banner-inner {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px 18px;
  padding-top: 12px;
  padding-bottom: 12px;
  text-align: center;
}

.landing-banner-text {
  color: rgb(244 237 224 / 88%);
}

.landing-banner-link {
  color: var(--rust-300);
  font-weight: 500;
  white-space: nowrap;
}

.landing-logos,
.landing-badges,
.landing-counter {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  text-align: center;
}

.landing-logos-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 28px 40px;
}

.landing-logos-row img {
  height: 34px;
  width: auto;
  opacity: 0.72;
}

.landing-logo-text {
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--ink-muted);
}

.landing-badge-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
}

.landing-badge-image {
  width: 16px;
  height: 16px;
  object-fit: contain;
}

.landing-counter-value {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 2rem + 3vw, 4rem);
  font-weight: 600;
  line-height: 1;
  color: var(--green-900);
}

/* The stats band gains a title and a sentence per figure. Both are
   additions to the shared component rather than a fork of it: a landing
   page's figures are argued for, where a marketing page's are asserted. */
.stat-band-title {
  margin: 0 0 36px;
  font-family: var(--font-display);
  font-size: var(--step-h1);
  line-height: 1.05;
  letter-spacing: -0.022em;
  color: #f6f0e4;
  text-wrap: balance;
}

/* Ranged left inside a centred `.stat-band-item`, on purpose: a number and
   a two-word label read fine centred, but two or three lines of prose
   centred underneath them look like a poem. */
.stat-band-note {
  margin: 12px 0 0;
  font-size: 0.88rem;
  line-height: 1.55;
  color: rgb(244 237 224 / 66%);
  text-align: left;
}

/* The optional "from" price on a service card. */
.feature-card-price {
  margin-top: auto;
  padding-top: 12px;
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--rust-600);
}

.landing-splits {
  display: flex;
  flex-direction: column;
  gap: clamp(48px, 7vw, 96px);
  margin-top: 44px;
}

.landing-gallery {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin-top: 44px;
}

.landing-gallery-item {
  margin: 0;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--paper-raised);
}

.landing-gallery-item img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}

.landing-gallery-item figcaption {
  padding: 10px 14px;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/* One caption rule for the video, map and before/after blocks. They had
   three near-identical ones, which is how the before/after caption ended
   up centred while the video's was not. */
.landing-figure-caption {
  margin: 18px 0 0;
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--ink-muted);
  max-width: 68ch;
}

.landing-video,
.landing-map {
  position: relative;
  margin-top: 44px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--paper-sunken);
}

.landing-video {
  aspect-ratio: 16 / 9;
}

.landing-map {
  height: 380px;
}

.landing-video iframe,
.landing-map iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.landing-before-after {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  margin-top: 44px;
}

.landing-before-after figure {
  margin: 0;
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1px solid var(--border);
}

.landing-before-after img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}

.landing-before-after figcaption {
  padding: 10px 14px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
  background: var(--paper-raised);
}

/* A team card is a feature card with a photograph, so it inherits
   `.feature-card` and only overrides the parts that differ. */
.landing-team-photo {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius-md);
}

.landing-team-role {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--rust-600);
}

/* A single testimonial is one `.quote-card` held to a reading width
   rather than stretched across the container, where a two-sentence quote
   would run to a single very long line. */
.landing-quote-single {
  max-width: 46rem;
  margin: 0 auto;
}

.landing-quote-context {
  max-width: 46rem;
  margin: 0 auto 20px;
  padding-left: 18px;
  border-left: 3px solid var(--industry-accent);
}

.landing-quote-context p {
  margin: 5px 0 0;
  max-width: 56ch;
  color: var(--ink-muted);
  font-size: 0.86rem;
}

.quote-avatar-initial {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--rust-tint);
  color: var(--rust-700);
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
}

/* The pull quote borrows the dark band and replaces its grid with one
   large piece of type. */
.landing-pull-quote {
  text-align: center;
}

.landing-pull-quote blockquote {
  margin: 0 auto;
  max-width: 44rem;
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 1.1rem + 1.4vw, 2.1rem);
  line-height: 1.35;
  letter-spacing: -0.015em;
  color: #f6f0e4;
  text-wrap: pretty;
}

.landing-pull-quote-by {
  margin: 24px 0 0;
  font-size: 0.9rem;
  color: rgb(244 237 224 / 68%);
}

.landing-case-copy {
  max-width: 52rem;
}

.landing-case-stat {
  margin: 16px 0 0;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 1.2rem + 1.6vw, 2.4rem);
  font-weight: 600;
  line-height: 1.1;
  color: var(--rust-600);
}

/* Three columns: the row's label, us, them. `.landing-compare-us` is
   emphasised on both the header and the rows, so the eye lands on the
   column the page is arguing for. */
.landing-compare {
  margin-top: 44px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--paper-raised);
}

.landing-compare-head,
.landing-compare-row {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr) minmax(0, 1.1fr);
  gap: 20px;
  padding: 18px 24px;
  border-bottom: 1px solid var(--border);
}

.landing-compare-row:last-child {
  border-bottom: 0;
}

.landing-compare-head {
  background: var(--paper-sunken);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.landing-compare-label {
  font-weight: 600;
  color: var(--green-900);
}

.landing-compare-us {
  color: var(--green-800);
}

.landing-compare-row .landing-compare-us {
  font-weight: 500;
}

.landing-compare-them {
  color: var(--ink-muted);
}

.landing-pricing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 20px;
  margin-top: 44px;
}

/* The mid-page nudge with no photograph: a bordered panel so it still
   reads as a moment rather than as another paragraph. */
.landing-cta-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: clamp(32px, 5vw, 56px);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  text-align: center;
}

.landing-cta-panel .lede-lg {
  max-width: 52ch;
}

.landing-cta-panel .mhero-actions {
  margin-top: 8px;
  justify-content: center;
}

.landing-form-card,
.landing-newsletter {
  padding: clamp(28px, 4vw, 44px);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.landing-form-card form {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: 24px;
}

.landing-form-submit {
  display: flex;
  justify-content: flex-start;
}

.landing-newsletter {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 24px;
}

.landing-newsletter-form {
  display: flex;
  gap: 10px;
  flex: 1;
  min-width: 18rem;
}

.landing-newsletter-form input {
  flex: 1;
}

/* Long-form prose held to a reading width. `.article-body` supplies the
   typography, the same as a blog post. */
.landing-prose {
  max-width: 46rem;
}

.landing-prose-shell {
  display: grid;
  grid-template-columns: 9rem minmax(0, 46rem);
  gap: clamp(28px, 6vw, 80px);
  align-items: start;
}

.landing-prose-label {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-top: 8px;
  color: var(--industry-accent);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.landing-prose-label i {
  flex: 1;
  height: 1px;
  background: var(--industry-accent);
  opacity: 0.5;
}

.landing-prose h2 {
  margin-bottom: 24px;
}

.landing-two-col {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(28px, 4vw, 56px);
  margin-top: 44px;
}

.landing-two-col-side h3 {
  margin: 0 0 12px;
  font-size: 1.1rem;
}

/* A thin trail above the opener on both the index and a landing page.
   Matches the `BreadcrumbList` in each page's JSON-LD -- Google's guidance
   is that the markup should describe a trail the reader can actually see,
   and a landing page arrived at from an ad otherwise gives no clue that
   there is a site around it. */
/* On a landing page the trail is its own strip above the opener, because
   the opener comes from a block and cannot be reached into. `.msection`'s
   64px of vertical padding would put a chasm between the two, so this
   collapses it to a thin band. On the index the trail sits inside the
   hero instead, where no override is needed. */
.msection-breadcrumb {
  padding: 28px 0 0;
}

.msection-breadcrumb .landing-breadcrumb {
  margin-bottom: 0;
}

/* And the opener that follows gives up most of its own top padding. The
   two were a 28px strip and then 96px of nothing before the headline, so
   the trail read as an orphaned line floating above the page rather than
   as the top of it. */
.msection-breadcrumb+.msection-intro {
  padding-top: 32px;
}

/* One line, never two.
   `nowrap` plus a shrinkable last crumb: on a narrow screen the trade name
   ellipsises instead of the trail wrapping and turning a one-line
   orientation cue into a two-line paragraph. */
.landing-breadcrumb {
  display: flex;
  flex-wrap: nowrap;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 28px;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/* Separators drawn here rather than sitting in the markup as
   `aria-hidden` spans. Three reasons: the markup reads as a list of
   places instead of alternating places and punctuation; the chevron
   inherits `--ink-faint` so it recedes behind the links it divides; and it
   hangs off the *preceding* item, which is what lets the last crumb clip
   its own overflow without eating a chevron that belongs to it. */
.landing-breadcrumb> :not(:last-child)::after {
  content: "\203a";
  margin-left: 8px;
  color: var(--ink-faint);
}

.landing-breadcrumb a {
  flex: none;
  color: var(--ink-muted);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color var(--transition), border-color var(--transition);
}

.landing-breadcrumb a:hover {
  color: var(--rust-700);
  border-bottom-color: currentColor;
}

.landing-breadcrumb-current {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--green-900);
  font-weight: 500;
}

/* ---------- Industries index (landing::views::public::render_index) ----- */

.industries-index-hero {
  padding-top: 72px;
  padding-bottom: 52px;
}

.industries-index-hero .landing-hero-copy {
  gap: 20px;
}

.industries-index-hero .landing-hero-copy .mhero-actions {
  margin-top: 6px;
}

/* A short editorial hand-off stops the filters and cards feeling like a
   database dumped immediately underneath the marketing hero. */
.industry-directory-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 0.72fr);
  gap: clamp(28px, 6vw, 80px);
  align-items: end;
  margin-bottom: 32px;
}

.industry-directory-head h2,
.industry-directory-head p {
  margin: 0;
}

.industry-directory-title {
  display: grid;
  gap: 12px;
}

.industry-directory-intro {
  display: grid;
  gap: 12px;
  padding-left: 22px;
  border-left: 2px solid var(--rust-500);
  color: var(--ink-muted);
  line-height: 1.6;
}

.industry-directory-intro a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: fit-content;
  color: var(--green-800);
  font-size: 0.9rem;
  font-weight: 650;
  text-decoration: none;
}

.industry-directory-intro a:hover {
  color: var(--rust-700);
}

/* Filter links drawn as `.pill`s -- the same component the heroes use,
   with an active state and a count. Real links, so they work with
   scripting off and a crawler can follow every one. */
.industry-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 48px;
}

.industry-filter-pill {
  gap: 10px;
  text-decoration: none;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.industry-filter-pill:hover {
  border-color: var(--green-700);
}

.industry-filter-pill.is-active {
  border-color: var(--green-800);
  background: var(--green-800);
  color: var(--paper);
}

.industry-filter-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunken);
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--ink-muted);
}

.industry-filter-pill.is-active .industry-filter-count {
  background: rgb(246 240 228 / 22%);
  color: var(--paper);
}

.industry-group+.industry-group {
  margin-top: 56px;
}

.industry-group-heading {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 0 0 24px;
  font-family: var(--font-display);
  font-size: 1.2rem;
  color: var(--green-900);
}

.industry-group-count {
  flex: none;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* The rule after the heading, drawn rather than bordered so it stops at
   the text instead of running the full width above the first card. */
.industry-group-heading::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

.industry-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 20px;
  counter-reset: industry-card;
}

/* A column, so the "read this one" line can be pushed to the bottom edge
   and sit level across a row of cards whose descriptions differ in
   length. */
.industry-card {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 28px 26px 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  text-decoration: none;
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
  counter-increment: industry-card;
}

.industry-card::before {
  content: counter(industry-card, decimal-leading-zero);
  position: absolute;
  top: 22px;
  right: 22px;
  color: var(--rust-600);
  font-family: var(--font-display);
  font-size: 0.76rem;
  font-weight: 650;
  letter-spacing: 0.08em;
}

.industry-card:hover {
  border-color: var(--green-700);
  transform: translateY(-2px);
  box-shadow: var(--shadow-card, 0 12px 28px rgb(26 53 39 / 8%));
}

.industry-card-title {
  margin: 0;
  padding-right: 42px;
  font-size: 1.05rem;
  line-height: 1.3;
  color: var(--green-900);
}

.industry-card-text {
  margin: 12px 0 0;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

.industry-card-more {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: auto;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--rust-600);
}

.industry-card-arrow {
  transition: transform 0.15s ease;
}

.industry-card:hover .industry-card-arrow {
  transform: translateX(4px);
}

.industry-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 64px;
  padding-top: 28px;
  border-top: 1px solid var(--border);
}

.industry-pager-step {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--green-800);
  text-decoration: none;
}

.industry-pager-step:hover {
  color: var(--rust-600);
}

/* Still occupies its space at the ends of the run, so the numbers stay
   centred and the row does not jump sideways between screens. */
.industry-pager-step.is-disabled {
  color: var(--ink-muted);
  opacity: 0.4;
}

.industry-pager-numbers {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.industry-pager-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  height: 38px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: 0.9rem;
  color: var(--green-900);
  text-decoration: none;
}

.industry-pager-number:hover {
  border-color: var(--green-700);
  color: var(--green-800);
}

.industry-pager-number.is-current {
  border-color: var(--green-800);
  background: var(--green-800);
  color: var(--paper);
  font-weight: 600;
}

/* ---------- Landing pages: admin builder (landing::views::admin) -------- */

.landing-builder-head {
  margin-bottom: 16px;
}

.landing-builder-head h2 {
  margin: 0 0 4px;
}

.landing-block-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* `--paper-raised`, like every other card in the app. These were on
   `--paper`, which is the page background itself -- so a block card was
   exactly the colour of what sat behind it and read as transparent, with
   only the hairline border suggesting there was a card there at all. */
.landing-block-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  overflow: hidden;
}

/* `--paper-sunken` for the band, and the block's name in green small
   caps. That is the colour doing the standing-out, not a filled panel.
   The band was briefly `--green-900` with paper text, and it looked right
   on one card and wrong on fifteen: a block list is long, and a column of
   dark stripes makes the chrome louder than the fields somebody is
   actually there to edit.

   It also cost four overrides inside the band -- the drag handle, the
   muted preview, and the buttons in both states -- because every one of
   them is styled dark-on-light by default. That is a standing tax: any
   control added here later would start out invisible, which is exactly
   how the marketing heroes ended up with unreadable secondary buttons.
   A light band needs none of them. */
.landing-block-card-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: var(--paper-sunken);
}

/* The type carries the colour. Small caps because the block *type* is a
   category, and setting it apart from the headline underneath is what
   makes a collapsed card scannable. */
.landing-block-card-title strong {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--green-700);
}

/* The block's own content, which is what tells two Feature grids apart. */
.landing-block-card-title span {
  font-size: 0.82rem;
  color: var(--green-900);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.landing-block-drag-handle {
  cursor: grab;
  font-size: 1.1rem;
  color: var(--ink-muted);
  line-height: 1;
  padding: 4px;
}

.landing-block-card-title {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.landing-block-card-actions {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

.landing-block-card-editor {
  padding: 0 16px 16px;
}

/* Rust rather than green: this is a disclosure control, and it reads as
   something to click next to the plain text around it. */
.landing-block-card-editor>summary {
  padding: 10px 0 0;
  cursor: pointer;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--rust-700);
}


.landing-builder-save-bar {
  margin-top: 16px;
  display: flex;
  align-items: center;
  gap: 14px;
}

/* No longer needs its own top margin -- it's the first card in
   `.admin-articles-editor-side` now, which already spaces its children via
   `gap`, not a trailing card that used to need extra separation from the
   block list above it. */

@media (max-width: 900px) {
  .landing-feature-grid .feature-card,
  .landing-feature-grid .feature-card:nth-child(n) {
    grid-column: span 6;
  }

  .landing-service-list {
    grid-template-columns: 1fr;
  }

  .landing-gallery {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .landing-hero-split,
  .landing-before-after,
  .landing-two-col {
    grid-template-columns: 1fr;
  }

  /* The comparison table stacks into label / us / them, one under the
     other. The header row goes with it: three column titles above a
     single stacked column label nothing. */
  .landing-compare-head {
    display: none;
  }

  .landing-compare-row {
    grid-template-columns: 1fr;
    gap: 6px;
  }

  /* Without the header the two values need naming, so the pseudo-elements
     carry the labels the header row was providing. Hard-coded rather than
     read from `us_label`/`them_label`, which are per-block: getting the
     real labels down here would mean a custom property set inline on every
     row, and "Us"/"The alternative" is what the header defaults to
     anyway. */
  .landing-compare-us::before,
  .landing-compare-them::before {
    display: block;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-muted);
  }

  .landing-compare-row .landing-compare-us::before {
    content: "Us";
    margin-top: 8px;
  }

  .landing-compare-them::before {
    content: "The alternative";
    margin-top: 8px;
  }
}

@media (max-width: 640px) {

  .industries-index-hero {
    padding: 44px 0 46px;
  }

  .industry-directory-head {
    grid-template-columns: 1fr;
    gap: 22px;
  }

  .industry-directory-intro {
    padding-left: 18px;
  }

  .industry-filter {
    margin-right: calc(var(--space-4, 16px) * -1);
    padding-right: var(--space-4, 16px);
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .industry-filter::-webkit-scrollbar {
    display: none;
  }

  .industry-filter-pill {
    flex: none;
  }

  .landing-hero-assurance {
    align-items: flex-start;
    flex-direction: column;
  }

  .landing-feature-grid .feature-card,
  .landing-feature-grid .feature-card:nth-child(n) {
    grid-column: 1 / -1;
    min-height: 0;
  }

  .landing-prose-shell {
    grid-template-columns: 1fr;
    gap: 20px;
  }

  .landing-prose-label {
    max-width: 9rem;
  }

  .landing-gallery,
  .landing-before-after {
    grid-template-columns: 1fr;
  }

  .landing-newsletter {
    flex-direction: column;
    align-items: stretch;
    text-align: center;
  }

  /* At this width the pager's numbered run wraps under the two steps and
     reads as a block of its own, so centre it rather than leaving it
     ranged left under "Previous". */
  .industry-pager {
    justify-content: center;
    text-align: center;
  }

  .industry-pager-numbers {
    order: -1;
    width: 100%;
    justify-content: center;
  }
}

/* ---------- Shared card composition ---------- */

/* A consistent title, supporting copy and optional status/action area. */
.card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.card-header-copy {
  min-width: 0;
}

.card-header h2 {
  margin-bottom: 4px;
}

.card-header-copy > .text-muted {
  margin: 0;
  max-width: 54ch;
}

.card-header-aside {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

/* Keeps links and independent single-button forms aligned as one action set. */
.card-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
}

.card-actions form {
  margin: 0;
}

/* The benchmark cases are reference material, not overview rows. Giving
   them their own compact disclosure pattern keeps the case name, stable
   key and acceptance criteria readable without showing five long briefs
   at once. */
.ai-evaluation-intro {
  max-width: 78ch;
}

.ai-evaluation-list {
  display: grid;
  gap: 10px;
  margin-top: 4px;
}

.ai-evaluation-case {
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-sunken);
}

.ai-evaluation-summary {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 15px 16px;
  color: var(--green-900);
  cursor: pointer;
  list-style: none;
  transition: background-color 0.15s ease;
}

.ai-evaluation-summary::-webkit-details-marker {
  display: none;
}

.ai-evaluation-summary:hover,
.ai-evaluation-summary:focus-visible,
.ai-evaluation-case[open]>.ai-evaluation-summary {
  background: var(--paper-tint);
}

.ai-evaluation-summary:focus-visible {
  outline: 2px solid var(--rust-500);
  outline-offset: -2px;
}

.ai-evaluation-summary strong {
  flex: 1;
  min-width: 0;
}

.ai-evaluation-summary::after {
  width: 8px;
  height: 8px;
  flex: 0 0 8px;
  margin: 0 3px 4px 2px;
  border-right: 2px solid var(--ink-muted);
  border-bottom: 2px solid var(--ink-muted);
  content: "";
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}

.ai-evaluation-case[open]>.ai-evaluation-summary::after {
  margin-bottom: -4px;
  transform: rotate(225deg);
}

.ai-evaluation-key {
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--paper-raised);
  color: var(--ink-muted);
  font-size: 0.76rem;
  white-space: nowrap;
}

.ai-evaluation-body {
  display: grid;
  gap: 16px;
  padding: 18px;
  border-top: 1px solid var(--border);
  background: var(--paper-raised);
}

.ai-evaluation-brief,
.ai-evaluation-pass p {
  margin: 0;
}

.ai-evaluation-brief {
  max-width: 76ch;
}

.ai-evaluation-pass {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  padding: 12px 14px;
  border-left: 3px solid var(--rust-500);
  border-radius: var(--radius-md);
  background: var(--rust-tint);
}

.ai-evaluation-pass strong {
  padding-top: 2px;
  color: var(--rust-700);
  font-size: 0.72rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
}

@media (max-width: 600px) {
  .ai-evaluation-summary {
    align-items: flex-start;
    flex-wrap: wrap;
  }

  .ai-evaluation-summary strong {
    flex-basis: calc(100% - 24px);
  }

  .ai-evaluation-key {
    order: 2;
  }

  .ai-evaluation-summary::after {
    order: 1;
  }

  .ai-evaluation-pass {
    grid-template-columns: minmax(0, 1fr);
    gap: 6px;
  }
}

/* ---------- Expanded feature architecture ---------- */

.feature-area-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.feature-area-card {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto;
  align-items: start;
  gap: 14px;
  min-height: 168px;
  padding: 25px;
  border: 1px solid var(--border);
  border-radius: 24px;
  background: var(--paper-raised);
  color: var(--green-900);
  box-shadow: 0 16px 36px -34px rgb(15 36 25 / 45%);
  transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}

.feature-area-card:hover,
.feature-area-card:focus-visible {
  border-color: var(--rust-300);
  color: var(--green-900);
  box-shadow: 0 22px 50px -32px rgb(15 36 25 / 38%);
  transform: translateY(-3px);
}

.feature-area-number {
  color: var(--rust-500);
  font-size: 0.7rem;
  font-weight: 750;
  letter-spacing: 0.09em;
  padding-top: 5px;
}

.feature-area-copy,
.feature-area-copy small,
.feature-area-copy strong,
.feature-area-copy span {
  display: block;
}

.feature-area-copy small {
  margin-bottom: 6px;
  color: var(--rust-600);
  font-size: 0.7rem;
  font-weight: 750;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.feature-area-copy strong {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2vw, 1.55rem);
  letter-spacing: -0.02em;
}

.feature-area-copy span {
  max-width: 38ch;
  margin-top: 9px;
  color: var(--ink-muted);
  font-size: 0.9rem;
  line-height: 1.55;
}

.feature-area-arrow {
  color: var(--ink-faint);
  font-size: 1.15rem;
  transition: color var(--transition), transform var(--transition);
}

.feature-area-card:hover .feature-area-arrow {
  color: var(--rust-600);
  transform: translateX(4px);
}

.feature-detail-hero {
  position: relative;
  overflow: hidden;
}

.feature-detail-hero::after {
  content: "";
  position: absolute;
  right: -9vw;
  bottom: -180px;
  width: min(46vw, 620px);
  aspect-ratio: 1;
  border: 1px solid rgb(204 122 74 / 18%);
  border-radius: 50%;
  box-shadow: 0 0 0 55px rgb(204 122 74 / 5%), 0 0 0 110px rgb(35 70 52 / 4%);
  pointer-events: none;
}

.feature-detail-hero-copy {
  position: relative;
  z-index: 1;
  max-width: 59rem;
  margin-top: 30px;
}

.feature-detail-hero-copy h1 {
  margin-top: 22px;
  max-width: 16ch;
}

.feature-detail-hero-copy .lede-lg {
  max-width: 66ch;
  margin-top: 24px;
}

.feature-detail-split {
  display: grid;
  grid-template-columns: minmax(0, 0.78fr) minmax(480px, 1.22fr);
  align-items: center;
  gap: clamp(36px, 7vw, 82px);
}

.feature-detail-copy .display-sm {
  margin: 14px 0 18px;
}

.feature-detail-copy .lede-lg {
  margin: 0;
}

.feature-detail-checks {
  display: grid;
  gap: 11px;
  margin: 25px 0 0;
  padding: 0;
  list-style: none;
}

.feature-detail-checks li {
  position: relative;
  padding-left: 28px;
  color: var(--green-900);
  font-size: 0.88rem;
  font-weight: 600;
}

.feature-detail-checks li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: -1px;
  display: grid;
  place-items: center;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  background: rgb(58 92 71 / 14%);
  color: var(--green-800);
  font-size: 0.72rem;
}

.feature-dashboard-visual {
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 24px;
  background: var(--paper-raised);
  box-shadow: 0 35px 70px -42px rgb(15 36 25 / 55%);
}

.feature-dashboard-top {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 42px;
  padding: 0 15px;
  border-bottom: 1px solid var(--border);
  background: var(--paper-tint);
}

.feature-dashboard-top span:not(.feature-dashboard-mark) {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--border-strong);
}

.feature-dashboard-mark {
  width: 20px;
  height: 20px;
  margin-right: auto;
  border-radius: 7px;
  background: var(--grad-warm);
}

.feature-dashboard-body {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  min-height: 342px;
}

.feature-dashboard-nav {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 20px 12px;
  border-right: 1px solid var(--border);
  background: var(--paper-sunken);
}

.feature-dashboard-nav span {
  padding: 9px 10px;
  border-radius: 9px;
  color: var(--ink-muted);
  font-size: 0.67rem;
  font-weight: 650;
}

.feature-dashboard-nav span.is-active {
  background: var(--green-900);
  color: var(--paper-raised);
}

.feature-dashboard-canvas {
  padding: 24px;
  background:
    linear-gradient(var(--paper-raised), var(--paper-raised)) padding-box,
    radial-gradient(circle at 100% 0%, rgb(204 122 74 / 9%), transparent 40%);
}

.feature-dashboard-heading {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 22px;
}

.feature-dashboard-heading > span {
  width: 30px;
  height: 30px;
  border-radius: 10px;
  background: var(--rust-tint);
}

.feature-dashboard-heading strong {
  font-family: var(--font-display);
  font-size: 1rem;
}

.feature-dashboard-highlight {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px;
  border-radius: 16px;
  background: var(--green-900);
  color: var(--paper-raised);
}

.feature-dashboard-icon {
  display: grid;
  place-items: center;
  flex: 0 0 38px;
  height: 38px;
  border-radius: 12px;
  background: rgb(255 255 255 / 10%);
  color: var(--rust-300);
}

.feature-dashboard-icon svg {
  width: 19px;
  height: 19px;
}

.feature-dashboard-highlight strong,
.feature-dashboard-highlight small {
  display: block;
}

.feature-dashboard-highlight strong {
  font-size: 0.79rem;
}

.feature-dashboard-highlight small {
  margin-top: 4px;
  color: rgb(244 237 224 / 64%);
  font-size: 0.61rem;
}

.feature-dashboard-metrics {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 9px;
  margin-top: 12px;
}

.feature-dashboard-metrics span {
  height: 54px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--paper-tint);
}

.feature-dashboard-lines {
  display: grid;
  gap: 8px;
  margin-top: 20px;
}

.feature-dashboard-lines span {
  height: 7px;
  border-radius: 10px;
  background: var(--border);
}

.feature-dashboard-lines span:nth-child(1) { width: 82%; }
.feature-dashboard-lines span:nth-child(2) { width: 64%; }
.feature-dashboard-lines span:nth-child(3) { width: 72%; }

.feature-outcomes {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--border);
}

.feature-outcome {
  padding: 26px 28px 4px 0;
}

.feature-outcome + .feature-outcome {
  padding-left: 28px;
  border-left: 1px solid var(--border);
}

.feature-outcome > span {
  color: var(--rust-500);
  font-size: 0.7rem;
  font-weight: 750;
  letter-spacing: 0.09em;
}

.feature-outcome h3 {
  margin: 14px 0 8px;
  font-size: 1.15rem;
}

.feature-outcome p {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.9rem;
  line-height: 1.6;
}

@media (max-width: 860px) {
  .feature-detail-split {
    grid-template-columns: 1fr;
  }

  .feature-dashboard-visual {
    max-width: 680px;
  }
}

@media (max-width: 680px) {
  .feature-area-grid,
  .feature-outcomes {
    grid-template-columns: 1fr;
  }

  .feature-area-card {
    min-height: 0;
    padding: 20px;
  }

  .feature-area-grid > .feature-area-card:last-child:nth-child(odd) {
    grid-column: auto;
  }

  .feature-outcome,
  .feature-outcome + .feature-outcome {
    padding: 22px 0;
    border-left: 0;
    border-bottom: 1px solid var(--border);
  }

  .feature-outcome:last-child {
    border-bottom: 0;
  }

  .feature-dashboard-body {
    grid-template-columns: 1fr;
  }

  .feature-dashboard-nav {
    flex-direction: row;
    overflow: hidden;
    padding: 10px;
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }

  .feature-dashboard-nav span {
    flex: none;
  }

  .feature-dashboard-canvas {
    padding: 18px;
  }
}


/* ---------- Admin: at-a-glance stat row ---------- */

/* A short row of counts above a list. A table answers "what exists"; these
   answer "where does it stand", which is usually why the screen was
   opened. Deliberately plain numbers rather than cards -- they're context
   for the list below, not the main content. */
.admin-stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
  margin: 16px 0 20px;
  padding: 14px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-tint);
}

.admin-stat {
  display: flex;
  align-items: baseline;
  gap: 7px;
}

.admin-stat strong {
  font-family: var(--font-display);
  font-size: 1.35rem;
  line-height: 1;
  color: var(--green-900);
}

.admin-stat span {
  font-size: 0.82rem;
  color: var(--ink-muted);
}

.admin-stat.is-warning strong,
.admin-stat.is-warning span {
  color: var(--rust-700);
}

/* Review progress in the admin sites table. Paired with the fraction
   rather than replacing it -- the bar makes a column of rows comparable at
   a glance, the numbers stay for the exact answer. */
.admin-progress {
  display: flex;
  align-items: center;
  gap: 9px;
}

.admin-progress-bar {
  position: relative;
  flex: 1;
  min-width: 60px;
  max-width: 120px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunken);
  overflow: hidden;
}

.admin-progress-bar span {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--green-700);
}

.admin-progress-label {
  font-size: 0.8rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
}

/* ---------- Generic admin modal ---------- */

/* A plain overlay + centered dialog, same open/close shape as
   `.showcase-modal` (public showcase preview) but not that component --
   this one is for admin forms that want more room than a sidebar card
   gives them (e.g. the bulk-import JSON panel in `landing::views::admin::
   render_index`), not a website preview frame. Toggled via `.is-open`,
   same convention as `.showcase-modal.is-open`. */
.admin-modal {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  align-items: center;
  justify-content: center;
  padding: clamp(12px, 3vw, 40px);
  background: rgba(15, 36, 25, 0.72);
}

.admin-modal.is-open {
  display: flex;
}

.admin-modal-dialog {
  position: relative;
  width: 100%;
  max-width: 860px;
  max-height: 90vh;
  background: var(--paper-raised);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-dropdown);
  display: flex;
  flex-direction: column;
}

.admin-modal-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--paper);
}

.admin-modal-title {
  font-weight: 600;
  color: var(--green-900);
  font-size: 1.02rem;
}

.admin-modal-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--paper-raised);
  color: var(--green-900);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  transition: border-color var(--transition), color var(--transition);
}

.admin-modal-close:hover {
  border-color: var(--rust-500);
  color: var(--rust-600);
}

.admin-modal-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 20px;
}

/* The bulk-import JSON field's enhanced-editor mount and its fallback
   textarea -- same "Monaco progressively replaces a plain textarea" pattern
   as `.admin-editor-fallback`/`.admin-monaco`, just sized for a modal
   instead of the full-page HTML editor split view. */
.admin-modal-code-fallback {
  width: 100%;
  box-sizing: border-box;
  height: 380px;
  resize: vertical;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: #17202a;
  color: #f2f5f7;
  font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  tab-size: 2;
}

.admin-modal-code-editor {
  height: 380px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

@media (max-width: 640px) {
  .admin-modal-dialog {
    max-height: 100vh;
    border-radius: 0;
  }
}

/* ---------- Accessibility + motion preferences ---------- */

/* A single visible focus ring for every interactive element, in brand
   colour. Placed at the end of the sheet so it wins over component styles
   without needing `!important`, and scoped to `:focus-visible` so it shows
   for keyboard and assistive-tech users without outlining every mouse
   click. Individual components that need a different offset still set
   their own; this is the floor, not a ceiling. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--rust-500);
  outline-offset: 2px;
  border-radius: var(--radius);
}

/* Respect a stated preference for less motion. Some people get genuinely
   unwell from movement on screen, and everything animated in this app is
   decorative -- transitions, hover lifts, smooth anchor scrolling -- so
   there's nothing here worth keeping at that cost. Near-zero rather than
   `none` so transition-end handlers still fire. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* Hover lifts are pure decoration -- hold position instead. */
  .site-card:hover,
  .showcase-card:hover,
  .industry-card:hover {
    transform: none;
  }

  .industry-card:hover .industry-card-arrow {
    transform: none;
  }
}

/* Honour a stated preference for more contrast by strengthening the
   borders and muted text that carry most of this design's structure. */
@media (prefers-contrast: more) {
  :root {
    --border: rgba(26, 53, 39, 0.28);
    --border-strong: rgba(26, 53, 39, 0.45);
    --ink-muted: #4a4a3f;
    --ink-faint: #5f5d50;
  }
}

/* ---------- Site setup wizard ---------- */

/* The same four-step sequence shown in the setup sidebar. */
.wizard-progress {
  margin: 4px 0 26px;
}

.wizard-progress-count {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 0 0 8px;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--rust-700);
}

/* The step's own name, in the reader's register rather than the label's.
   Uppercase tracking is right for "STEP 2 OF 4" and wrong for "Look &
   feel", which is a thing rather than a category. */
.wizard-progress-name {
  color: var(--ink-muted);
  font-size: 0.78rem;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
}

.wizard-progress-bar {
  height: 4px;
  border-radius: 999px;
  background: var(--paper-sunken);
  overflow: hidden;
}

.wizard-progress-bar span {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--rust-500);
  transition: width var(--transition);
}

.wizard-lede {
  margin: 0 0 24px;
  max-width: 62ch;
  font-size: 1.02rem;
  color: var(--ink-muted);
}

/* One quiet line on the draft-creation screen answers the three questions
   most likely to make someone hesitate before they begin. */
.wizard-intro-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin: -10px 0 24px;
  color: var(--green-800);
  font-size: 0.84rem;
  font-weight: 600;
}

.wizard-intro-meta span {
  color: var(--rust-500);
}

/* Answers the payment question at the moment naming a site creates its
   saved setup, without turning it into another prominent block. */
.setup-payment-note {
  margin-top: 12px;
}

/* Back on the left, the single primary action on the right. Every step has
   exactly one way forward, which is most of what makes the flow feel
   simple to follow. */
.wizard-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 28px;
}

.wizard-actions form {
  margin: 0;
}

.wizard-back {
  color: var(--ink-muted);
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
}

.wizard-back:hover {
  color: var(--rust-700);
}

/* ---------- Wizard: style picker ---------- */

/* Selectable cards, not a multi-select of bare adjectives. This is the
   most visual decision in the brief and it was previously the least
   visual control on the form. */
.direction-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
  margin-top: 16px;
}

.direction-card {
  display: flex;
  gap: 12px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition), opacity var(--transition);
}

.direction-card:hover {
  border-color: var(--border-strong);
}

.direction-card input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.direction-card.is-picked,
.direction-card:has(input:checked) {
  border-color: var(--rust-500);
  background: var(--paper-tint);
}

.direction-card:focus-within {
  outline: 2px solid var(--rust-400);
  outline-offset: 2px;
}

/* Dimmed rather than disabled once the cap is reached: a disabled control
   gives no feedback about why it won't respond. */
.direction-card.is-unavailable {
  opacity: 0.45;
}

.direction-card-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.direction-card-body strong {
  font-size: 0.92rem;
}

.direction-card-body span {
  font-size: 0.8rem;
  color: var(--ink-muted);
  line-height: 1.45;
}

/* A small colour/shape sample per direction, so the choice is at least
   partly made with the eye rather than entirely from the words. */
.direction-swatch {
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: 8px;
  border: 1px solid var(--border);
}

.direction-swatch-professional_formal {
  background: linear-gradient(135deg, #1f3a5f, #86a0bd);
}

.direction-swatch-modern_minimal {
  background: linear-gradient(135deg, #f4f4f2, #c9c9c4);
}

.direction-swatch-friendly_approachable {
  background: linear-gradient(135deg, #f0a868, #f5d6b5);
  border-radius: 16px;
}

.direction-swatch-premium_sophisticated {
  background: linear-gradient(135deg, #1a1a1a, #6f6551);
}

.direction-swatch-bold_energetic {
  background: linear-gradient(135deg, #e63c1e, #ffb020);
}

.direction-swatch-playful_creative {
  background: linear-gradient(135deg, #7b4bd6, #34c6c6);
  border-radius: 50%;
}

.direction-swatch-rustic_traditional {
  background: linear-gradient(135deg, #7d5a3c, #c9a878);
}

.direction-swatch-tech_innovative {
  background: linear-gradient(135deg, #0e1a2b, #2f8fe0);
}

.direction-swatch-natural_organic {
  background: linear-gradient(135deg, #4f6b45, #a8bf8e);
}

/* ---------- Wizard: review ---------- */

.review-submit-card {
  border-color: var(--rust-300);
}

.review-price {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 10px;
}

/* The payment card. Tinted like the review screen's submit card, because
   they are the two halves of the same step and someone arriving here
   should recognise where they've landed rather than feel handed off. */
.checkout-card {
  border-color: var(--rust-300);
}

/* Sits under the action row rather than above it: it's reassurance about
   what happens after the click, so it reads in the order it applies. */
.checkout-note {
  margin: 16px 0 0;
  font-size: 0.85rem;
}

.review-price strong {
  font-family: var(--font-display);
  font-size: 2rem;
  line-height: 1;
  color: var(--green-900);
}

.review-price span {
  font-size: 0.88rem;
  color: var(--ink-muted);
}

/* ---------- Domains ---------- */

.domain-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
}

.domain-index-section {
  margin-top: 4px;
}

.domain-index-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
}

.domain-index-card-head h2 {
  margin: 0;
  font-size: 1.08rem;
}

.domain-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.domain-row-main {
  flex: 1;
  min-width: 0;
}

.domain-row-name {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.domain-row-name strong {
  font-size: 1.05rem;
}

.domain-row-meta {
  margin: 4px 0 0;
  font-size: 0.83rem;
  color: var(--ink-muted);
}

/* Connecting a domain someone already owns stalls until they change
   nameservers at their registrar -- a step we can't do for them. Saying so
   on the row beats leaving them to wonder why it's stuck on "Setting up". */
.domain-row-note {
  margin: 8px 0 0;
  padding: 8px 12px;
  border-radius: var(--radius-md);
  background: var(--paper-tint);
  font-size: 0.82rem;
  color: var(--ink-muted);
}

.domain-row-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.domain-row-actions form {
  margin: 0;
}

.domain-connect-card {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(340px, 1.1fr);
  align-items: center;
  gap: clamp(24px, 5vw, 52px);
  margin-top: var(--card-gap);
  padding-top: 24px;
  padding-bottom: 24px;
}

.domain-connect-copy h2 {
  margin: 0;
  font-size: 1.25rem;
}

.domain-connect-copy .text-muted {
  margin: 7px 0 0;
}

.domain-connect-card form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: 12px;
  margin: 0;
}

.domain-connect-card .form-field {
  margin: 0;
}

.domain-transfer-card {
  margin: var(--card-gap) 0 0;
  padding: 0;
  overflow: hidden;
}

.domain-transfer-summary {
  padding: 20px 24px;
  color: var(--green-900);
  cursor: pointer;
  list-style: none;
}

.domain-transfer-summary::-webkit-details-marker {
  display: none;
}

.domain-transfer-summary > span {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.domain-transfer-summary strong {
  font-family: var(--font-display);
  font-size: 1.18rem;
}

.domain-transfer-summary small {
  color: var(--ink-muted);
  font-size: 0.8rem;
  font-weight: 400;
}

.domain-transfer-summary::after {
  content: "+";
  float: right;
  margin-top: -32px;
  font-size: 1.3rem;
}

.domain-transfer-card[open] .domain-transfer-summary {
  border-bottom: 1px solid var(--border);
}

.domain-transfer-card[open] .domain-transfer-summary::after {
  content: "\2212";
}

.domain-transfer-body {
  padding: 22px 24px 24px;
}

@media (max-width: 760px) {
  .domain-connect-card {
    grid-template-columns: 1fr;
    gap: 20px;
  }
}

@media (max-width: 520px) {
  .domain-row-actions,
  .domain-row-actions .btn,
  .domain-row-actions form {
    width: 100%;
  }

  .domain-connect-card form {
    grid-template-columns: 1fr;
  }

  .domain-connect-card .btn {
    width: 100%;
  }
}

/* On the account list these are supporting details, not three separate
   dashboards. One quiet sentence keeps each domain row scannable. */
.domain-workspace-summary {
  margin: 10px 0 0;
  color: var(--ink-faint);
  font-size: 0.78rem;
  line-height: 1.5;
}

.domain-row-website-link {
  padding: 7px 3px;
  color: var(--ink-muted);
  font-size: 0.82rem;
  font-weight: 600;
}

.domain-row-website-link:hover {
  color: var(--rust-700);
}

.domain-overview-owner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: var(--card-gap);
  padding-top: 20px;
  padding-bottom: 20px;
}

.domain-overview-owner-copy {
  min-width: 0;
}

.domain-overview-site-title {
  margin: 2px 0 3px;
  font-size: 1.35rem;
}

.domain-overview-owner .text-muted {
  margin: 0;
}

.domain-overview-grid > :first-child,
.business-essentials-single > :first-child {
  grid-column: auto;
}

.domain-overview-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.domain-feature-card {
  gap: 10px;
}

.domain-feature-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.domain-feature-title {
  margin: 0;
  font-size: 1.55rem;
}

.domain-feature-card .dashboard-card-actions .btn {
  flex: none;
  min-width: 0;
}

.business-essentials-single {
  grid-template-columns: minmax(0, 820px);
}

.business-essentials-single > * {
  margin-top: 0;
  margin-bottom: 0;
}

/* These are task screens, so their card heading should support the page
   title rather than competing with it. Keeping it below the display scale
   also lets the current status remain on the same line more often. */
.business-essentials-single .card-header h2 {
  font-size: clamp(1.55rem, 3vw, 1.9rem);
}

/* A local demo domain needs a deliberate read-only state. A faded primary
   button looks broken and has weak contrast, while this compact status row
   explains why there is no action without pretending to be clickable. */
.domain-feature-unavailable {
  width: 100%;
  margin-top: 2px;
  padding: 13px 15px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-sunken);
  color: var(--ink-muted);
}

.domain-feature-unavailable strong,
.domain-feature-unavailable span {
  display: block;
}

.domain-feature-unavailable strong {
  color: var(--green-900);
  font-size: 0.86rem;
}

.domain-feature-unavailable span {
  margin-top: 2px;
  font-size: 0.78rem;
  line-height: 1.45;
}

/* Explanatory copy at the bottom of a feature card is an inset note, not a
   form field or tooltip. The split label keeps it easy to scan and prevents
   awkward mid-word wrapping on phones. */
.domain-guidance {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr);
  gap: 12px;
  margin-top: 4px;
  padding-top: 15px;
  border-top: 1px solid var(--border);
}

.domain-guidance strong {
  color: var(--green-900);
  font-size: 0.78rem;
}

.domain-guidance p {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.8rem;
  line-height: 1.55;
}

@media (max-width: 960px) {
  .domain-overview-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 680px) {
  .domain-overview-owner {
    align-items: flex-start;
    flex-direction: column;
    gap: 14px;
  }

  .domain-overview-owner .btn {
    width: 100%;
  }

  .domain-guidance {
    grid-template-columns: 1fr;
    gap: 4px;
  }
}

/* ---------- DNS records ---------- */

/* A grid rather than a table: each row expands in place to reveal its edit
   form, which a `<table>` can't do without either a nested form per cell or
   a separate edit page. Same column template on the header and every row
   keeps them aligned. */
.dns-table {
  margin: 20px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  overflow: hidden;
}

.dns-table-head,
.dns-row-summary {
  display: grid;
  grid-template-columns: 80px 140px minmax(0, 1fr) 110px 90px;
  gap: 12px;
  align-items: center;
  padding: 12px 18px;
}

.dns-table-head {
  background: var(--paper-sunken);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
}

.dns-row {
  border-top: 1px solid var(--border);
}

.dns-row-type {
  font-weight: 700;
  font-size: 0.8rem;
  color: var(--green-800);
}

.dns-row-name {
  font-weight: 600;
  font-size: 0.88rem;
}

/* Values are long and arbitrary (TXT verification strings especially), so
   they get monospace and are allowed to break anywhere rather than forcing
   the grid wider than its container. */
.dns-row-content {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.8rem;
  color: var(--ink-muted);
  overflow-wrap: anywhere;
}

.dns-row-ttl {
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.dns-row.is-locked {
  background: var(--paper-tint);
}

.dns-row-lock {
  display: inline-block;
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  background: rgb(26 53 39 / 8%);
  color: var(--green-800);
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: help;
}

/* Right-aligned so the Edit toggles and Managed pills form a clean column
   down the edge of the table rather than sitting against varying-length
   TTL text. */
.dns-row-action {
  text-align: right;
}

.dns-row-edit > summary {
  cursor: pointer;
  list-style: none;
}

.dns-row-edit > summary::-webkit-details-marker {
  display: none;
}

.dns-row-edit > summary:hover,
.dns-row-edit[open] > summary {
  background: var(--paper-tint);
}

.dns-row-edit .dns-row-action {
  color: var(--green-800);
  font-size: 0.82rem;
  font-weight: 600;
}

.dns-row-edit-body {
  padding: 18px;
  border-top: 1px solid var(--border);
  background: var(--paper-tint);
}

.dns-row-edit-body form {
  margin: 0;
}

.dns-form-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.dns-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0 16px;
}

/* Explains the locked rows before the reader hits one and wonders why it
   won't open. */
.dns-guard {
  margin: 20px 0 0;
  padding: 16px 20px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--green-700);
  border-radius: var(--radius-lg);
  background: var(--paper-tint);
}

.dns-guard strong {
  display: block;
  margin-bottom: 4px;
}

.dns-guard p {
  margin: 0;
  font-size: 0.86rem;
  color: var(--ink-muted);
}

@media (max-width: 860px) {

  /* The five-column grid stops working long before phone width; stack each
     record instead, keeping the value on its own line where it has room. */
  .dns-table-head {
    display: none;
  }

  .dns-row-summary {
    grid-template-columns: 1fr auto;
    row-gap: 4px;
  }

  .dns-row-content {
    grid-column: 1 / -1;
  }
}

/* ------------------------------------------------------------------ */
/* Admin: per-site overview + domains                                  */
/* ------------------------------------------------------------------ */

/* The overview's own wrapper. `min-width: 0` matters: this sits in the
   `.admin-site-layout` grid track next to the sidebar, and without it the
   long free text inside (revision instructions, job errors) can force the
   track wider than its share and push the sidebar off. */
.admin-overview {
  min-width: 0;
}

.admin-overview-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}

.admin-overview-head h1 {
  margin: 0 0 4px;
}

.admin-overview-head-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Six figures across the top. `auto-fit` rather than a fixed count so
   they stay legible in the narrow column left when the sidebar is open. */
.admin-overview-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-bottom: 24px;
}

/* The admin strip runs six across where the visitors screen runs three,
   so the shared `.stat-card` number is scaled down here rather than the
   admin getting its own card. Everything else -- surface, border, the
   alert state -- comes from the component. */
.admin-overview-stats .stat-card {
  padding: 14px 16px;
  gap: 6px;
}

.admin-overview-stats .stat-value {
  font-size: 1.3rem;
}

.admin-overview-stats .stat-label {
  font-size: 0.72rem;
}

/* A badge inside a stat shouldn't inherit the numeric size. */
.stat-value .status-pill {
  font-size: 0.78rem;
  vertical-align: middle;
}

/* Anything above zero here wants looking at -- failed jobs, open tickets,
   pages waiting on review. Zero stays quiet. */
.stat-card.is-alert {
  border-color: var(--rust-400);
  background: var(--rust-tint);
}

.admin-overview-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--card-gap, 20px);
  align-items: start;
}

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

.admin-overview-column {
  display: flex;
  flex-direction: column;
  gap: var(--card-gap, 20px);
  min-width: 0;
}

/* Cards that follow the grid rather than sitting inside it -- on the site
   overview, the pause card and the danger zone, both full width on purpose
   so they don't compete with a column of ordinary cards. The grid spaces
   its own children with `gap`, which says nothing about a sibling, and
   `.card` carries no margin by design, so they sat flush. Same
   `--card-gap` so the separation matches the spacing between every other
   card on the screen.

   `~` rather than `+`: there used to be exactly one card down here and the
   adjacent-sibling rule was enough. Adding a second left the first spaced
   and the second welded to it, which is the failure mode of writing a rule
   for the number of things that happen to exist. The general sibling
   combinator spaces however many there are. */
.admin-overview-grid~.card {
  margin-top: var(--card-gap, 20px);
}

.admin-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 12px;
}

.admin-card-head h2 {
  margin: 0;
  font-size: 1.05rem;
}

.admin-card h3 {
  margin: 20px 0 8px;
  font-size: 0.92rem;
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Rows are separated by rules rather than gaps: these lists get long and
   individually-bordered cards turn into visual noise at eight-plus. */
.admin-overview-list {
  display: flex;
  flex-direction: column;
}

.admin-overview-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-top: 1px solid var(--border);
  color: inherit;
  text-decoration: none;
}

.admin-overview-list>.admin-overview-row:first-child {
  border-top: none;
}

a.admin-overview-row:hover,
a.admin-overview-row:focus-visible {
  color: var(--rust-600);
}

.admin-overview-row-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  /* Revision instructions and ticket subjects are free text and can be
     long; without this the status pill gets pushed off the row. */
  overflow-wrap: anywhere;
}

.admin-overview-row-main small {
  font-size: 0.78rem;
}

.admin-job-error {
  margin: 12px 0 0;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--rust-tint);
  color: var(--rust-700);
  font-size: 0.85rem;
  overflow-wrap: anywhere;
}

.admin-domain-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.admin-domain-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.admin-domain-row-main {
  min-width: 0;
}

.admin-domain-row-name {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}

.admin-domain-row-main p {
  margin: 0;
  font-size: 0.86rem;
}

.admin-domain-zone {
  margin-top: 4px !important;
  color: var(--ink-faint);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem !important;
  overflow-wrap: anywhere;
}

.admin-domain-row-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ------------------------------------------------------------------ */
/* Billing (customer-facing)                                           */
/* ------------------------------------------------------------------ */

.billing-card+.billing-card {
  margin-top: var(--card-gap, 20px);
}

.billing-card-head {
  margin-bottom: 10px;
}

.billing-card-head h2 {
  margin: 4px 0 0;
}

.billing-card-head .eyebrow {
  margin: 0;
}

.billing-plan-description {
  max-width: 64ch;
  margin: 0;
  color: var(--ink-muted);
}

.billing-inclusions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
}

.billing-inclusions li {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 10px;
  border-radius: var(--radius-pill);
  background: rgb(26 53 39 / 6%);
  color: var(--green-800);
  font-size: 0.78rem;
  font-weight: 600;
}

.billing-inclusions li::before {
  content: "✓";
  color: #1f6b45;
  font-size: 0.7rem;
}

.billing-summary {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 20px;
  margin-top: 22px;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-tint);
}

.billing-summary div {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.billing-summary span,
.billing-summary p {
  color: var(--ink-muted);
  font-size: 0.8rem;
}

.billing-summary strong {
  color: var(--green-900);
  font-family: var(--font-display);
  font-size: 1.7rem;
  line-height: 1.05;
}

.billing-summary p {
  max-width: 48ch;
  margin: 0;
  text-align: right;
}

.billing-sites-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 28px;
}

.billing-sites-heading strong {
  color: var(--green-900);
  font-size: 0.9rem;
}

.billing-sites-heading span {
  font-size: 0.8rem;
}

/* Rules between rows rather than gaps -- a customer with three sites
   should read this as one list, not three floating cards. */
.billing-list {
  display: flex;
  flex-direction: column;
  margin-top: 20px;
}

.billing-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 16px 0;
  border-top: 1px solid var(--border);
}

.billing-list>.billing-row:first-child {
  border-top: none;
}

.billing-row-main {
  min-width: 0;
}

.billing-row-name {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}

.billing-row-name a {
  color: inherit;
  text-decoration: none;
}

.billing-row-name a:hover,
.billing-row-name a:focus-visible {
  color: var(--rust-600);
}

.billing-row-main p {
  margin: 0;
  font-size: 0.88rem;
}

.billing-row-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.billing-row-menu {
  position: relative;
}

.billing-row-menu summary {
  min-height: 40px;
  padding: 9px 10px;
  color: var(--ink-muted);
  font-size: 0.84rem;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.billing-row-menu summary::-webkit-details-marker {
  display: none;
}

.billing-row-menu summary::after {
  content: "\2026";
  margin-left: 6px;
  letter-spacing: 0.08em;
}

.billing-row-menu-panel {
  position: absolute;
  z-index: 4;
  top: calc(100% + 4px);
  right: 0;
  min-width: 150px;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  box-shadow: var(--shadow-dropdown);
}

.billing-row-menu-panel .billing-cancel-link {
  width: 100%;
}

.billing-setup-card {
  padding: 0;
  overflow: hidden;
}

.billing-setup-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 18px 24px;
  color: var(--green-900);
  cursor: pointer;
  list-style: none;
}

.billing-setup-summary::-webkit-details-marker {
  display: none;
}

.billing-setup-summary-copy {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 3px;
}

.billing-setup-summary-copy small {
  color: var(--ink-muted);
  font-size: 0.8rem;
  font-weight: 400;
}

.billing-setup-count {
  padding: 4px 9px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunken);
  color: var(--ink-muted);
  font-size: 0.75rem;
  font-weight: 650;
  white-space: nowrap;
}

.billing-setup-card[open] .billing-setup-summary {
  border-bottom: 1px solid var(--border);
}

.billing-setup-body {
  padding: 0 24px 8px;
}

.billing-setup-body>p {
  margin: 18px 0 0;
}

@media (max-width: 560px) {
  .billing-summary {
    align-items: flex-start;
    flex-direction: column;
    gap: 8px;
  }

  .billing-summary p {
    text-align: left;
  }
  .billing-row {
    align-items: flex-start;
  }

  .billing-row-actions {
    width: 100%;
  }

  .billing-setup-summary {
    align-items: flex-start;
    padding: 16px 18px;
  }

  .billing-setup-body {
    padding-inline: 18px;
  }
}

/* Cancellation stays easy to find without giving a destructive, uncommon
   action the same button weight as routine card and invoice management. */
.billing-cancel-link {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 8px;
  color: var(--ink-muted);
  font-size: 0.84rem;
  font-weight: 600;
  text-decoration: none;
}

.billing-cancel-link:hover,
.billing-cancel-link:focus-visible {
  color: var(--rust-700);
}

.billing-service-notice {
  margin-top: var(--card-gap);
}

.billing-help {
  margin-top: var(--card-gap);
  padding: 0;
  overflow: hidden;
}

.billing-help-summary {
  padding: 18px 22px;
  color: var(--green-900);
  font-weight: 650;
  cursor: pointer;
}

.billing-help[open] .billing-help-summary {
  border-bottom: 1px solid var(--border);
}

.billing-help-body {
  padding: 18px 22px;
}

.billing-help-body p {
  margin: 0;
}

.billing-help-body p + p {
  margin-top: 10px;
}

/* The explanation under a revision that couldn't be completed. Tinted
   rather than red: it's reassurance (you haven't lost a credit), not an
   error the customer has to act on. */
.revision-note {
  margin: 12px 0 0;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--paper-tint);
  color: var(--ink-muted);
  font-size: 0.86rem;
  line-height: 1.55;
}

/* ==================================================================== */
/* MARKETING v2 -- a louder, more editorial layer over the base system.  */
/*                                                                       */
/* The original marketing styling was correct but quiet: flat cream      */
/* bands, one accent colour barely used, no depth. That reads as honest  */
/* and cheap, which is the wrong signal for a service selling design.    */
/* This layer adds the things a premium studio site actually does --     */
/* layered gradients, grain, oversized editorial type, real photography, */
/* asymmetric bento layouts -- without touching the app's own screens.   */
/*                                                                       */
/* Everything here is additive. Nothing below overrides a `.msection`,   */
/* `.container` or button rule, so the signed-in app is unaffected.      */
/* ==================================================================== */

/* -------------------------------------------------------------------- */
/* Texture + decoration                                                  */
/* -------------------------------------------------------------------- */

/* Fine grain over dark panels. An SVG feTurbulence data URI rather than a
   raster: a few hundred bytes, resolution-independent, and it stops large
   flat green areas looking like dead space on a big monitor. */
/* On `::after`, not `::before`.

   Every element this class is applied to (`.mhero`, `.cta-v2`,
   `.price-hero`) also defines its own `::before` for a radial gradient.
   Those are the *same pseudo-element*, so the two rules merged and the
   later `background` shorthand reset this one's `background-image` --
   meaning the grain silently did nothing on precisely the three elements
   it was written for. `::after` is free on all of them. */
.grain::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.35'/%3E%3C/svg%3E");
}

/* Soft colour bloom used behind heroes. Two radial gradients, no element
   of its own -- attach to any positioned container. */
.bloom {
  position: relative;
  isolation: isolate;
  /* The bloom below deliberately bleeds past this section's edges to avoid
     a hard cut-off on the gradient. On a full-viewport-width section that
     bleed is horizontal overflow, and horizontal overflow anywhere on a
     page produces a scrollbar for the whole document -- which is exactly
     what it did.

     `clip` rather than `hidden`: `hidden` turns this into a scroll
     container, which would break `position: sticky` on any descendant and
     silently change what the header does on pages that use `.bloom`.
     `clip` crops without any of that. The `hidden` line before it is the
     fallback for browsers predating `clip` support. */
  overflow: hidden;
  overflow: clip;
}

.bloom::after {
  content: "";
  position: absolute;
  inset: -20% -10% auto;
  height: 70%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(38% 60% at 18% 30%, rgb(204 122 74 / 22%), transparent 70%),
    radial-gradient(42% 55% at 82% 12%, rgb(35 70 52 / 16%), transparent 72%);
  filter: blur(8px);
}

/* Faint blueprint grid -- signals "designed", not decorative clutter. */
.gridlines {
  position: relative;
}

/* The line ink is multiplied twice before it reaches the screen -- once by
   this rule's own `opacity`, and again by the radial mask that fades the
   grid out towards the edges. At 5% over 0.5 the strongest point of the
   whole pattern was 2.5% ink, which lands on #f4eee1 against #faf3e6
   paper: about two levels apart per channel, and effectively invisible on
   anything but a good monitor at full brightness.

   8% over 0.65 doubles it to 5.2%, or #eee9dc. Still quiet enough to read
   as texture rather than as a table, and now actually present. The mask is
   untouched, so the grid still dissolves before it reaches the section
   edges instead of stopping at a hard line. */
.gridlines::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.65;
  background-image:
    linear-gradient(to right, rgb(26 53 39 / 8%) 1px, transparent 1px),
    linear-gradient(to bottom, rgb(26 53 39 / 8%) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(70% 60% at 50% 40%, #000, transparent 75%);
}

/* -------------------------------------------------------------------- */
/* Editorial type                                                        */
/* -------------------------------------------------------------------- */

.display {
  font-family: var(--font-display);
  font-size: var(--step-display);
  line-height: 0.98;
  letter-spacing: -0.028em;
  margin: 0;
  text-wrap: balance;
}

.display-sm {
  font-family: var(--font-display);
  font-size: var(--step-h1);
  line-height: 1.03;
  letter-spacing: -0.022em;
  margin: 0;
  text-wrap: balance;
}

/* Rust gradient across a word or two of a heading. Kept to a `<span>` so
   the surrounding heading stays a solid, readable colour -- gradient text
   at body weight is a legibility problem, at display weight it isn't. */
.text-grad {
  background: var(--grad-warm);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Hand-drawn underline beneath an emphasised phrase. An inline SVG stroke
   rather than a border, so it keeps its sketchy character at any width. */
.underline-mark {
  position: relative;
  white-space: nowrap;
}

.underline-mark::after {
  content: "";
  position: absolute;
  left: -2%;
  right: -2%;
  bottom: -0.18em;
  height: 0.32em;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 20' preserveAspectRatio='none'%3E%3Cpath d='M4 14c48-7 96-10 145-9 47 1 94 5 147 12' fill='none' stroke='%23cc7a4a' stroke-width='6' stroke-linecap='round'/%3E%3C/svg%3E");
}

.lede-lg {
  font-size: var(--step-lede);
  line-height: 1.65;
  color: var(--ink-muted);
  max-width: 62ch;
  text-wrap: pretty;
}

/* A short line of small-caps metadata under a heading. */
.kicker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--rust-600);
}

.kicker::before {
  content: "";
  width: 26px;
  height: 2px;
  border-radius: 2px;
  background: var(--grad-warm);
}

/* -------------------------------------------------------------------- */
/* Badges + pills                                                        */
/* -------------------------------------------------------------------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--paper-raised);
  font-size: 0.83rem;
  font-weight: 500;
  color: var(--green-900);
  white-space: nowrap;
}

.pill-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--rust-500);
  flex: none;
}

.pill-dot-live {
  background: #3f9d6a;
  box-shadow: 0 0 0 3px rgb(63 157 106 / 20%);
}

.pill-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

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

/* On dark panels. */
.hero-panel .pill,
.band-dark .pill,
.mhero .pill {
  background: rgb(255 253 249 / 10%);
  border-color: rgb(255 253 249 / 22%);
  color: #f4ede0;
}

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

.mhero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--grad-forest);
  color: #f6f0e4;
  padding: clamp(72px, 9vw, 132px) 0 clamp(64px, 8vw, 112px);
}

.mhero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(48% 62% at 78% 8%, rgb(204 122 74 / 34%), transparent 68%),
    radial-gradient(38% 48% at 8% 92%, rgb(107 133 116 / 26%), transparent 70%);
}

.mhero-inner {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
}

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

.mhero h1 {
  font-family: var(--font-display);
  font-size: var(--step-display);
  line-height: 0.98;
  letter-spacing: -0.03em;
  margin: 20px 0 0;
  color: #fffdf9;
  text-wrap: balance;
}

.mhero-lede {
  margin: 22px 0 0;
  font-size: var(--step-lede);
  line-height: 1.62;
  color: rgb(246 240 228 / 80%);
  max-width: 54ch;
  text-wrap: pretty;
}

.mhero .kicker {
  color: var(--rust-300);
}

.mhero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 34px;
}

/* Scoped to `.mhero` -- the dark forest-gradient hero on the home page.

   These three lines were unscoped, so they also hit the light
   `.msection-intro` heroes on Pricing, Showcase, How it works and
   Features, which reuse `.mhero-actions` for the same button pair. That
   painted a `#f6f0e4` label onto a `#faf3e6` background: the secondary
   button's text was all but invisible on four pages, leaving a button
   that read as an empty outline. */
.mhero .mhero-actions .btn-secondary {
  background: transparent;
  color: #f6f0e4;
  border-color: rgb(246 240 228 / 38%);
}

.mhero .mhero-actions .btn-secondary:hover {
  background: rgb(246 240 228 / 10%);
  border-color: rgb(246 240 228 / 60%);
}

.mhero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 26px;
  margin-top: 32px;
  padding-top: 26px;
  border-top: 1px solid rgb(246 240 228 / 16%);
  font-size: 0.87rem;
  color: rgb(246 240 228 / 72%);
}

.mhero-meta span {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/* The stacked browser mock. Two layered cards behind a real screenshot,
   rotated slightly -- reads as "we make lots of these" in one glance,
   which a single flat image doesn't. */
.mhero-stack {
  position: relative;
  aspect-ratio: 4 / 3.2;
  perspective: 1400px;
}

.mhero-shot {
  position: absolute;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--paper-raised);
  box-shadow: var(--shadow-deep);
  border: 1px solid rgb(255 253 249 / 14%);
}

.mhero-shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.mhero-shot-back {
  inset: 2% 22% 26% 0;
  transform: rotate(-5deg);
  opacity: 0.55;
  filter: saturate(0.7);
}

.mhero-shot-mid {
  inset: 12% 10% 12% 12%;
  transform: rotate(2.5deg);
  opacity: 0.85;
}

.mhero-shot-front {
  inset: 24% 0 0 26%;
  transform: rotate(-1.5deg);
}

@media (max-width: 940px) {
  .mhero-stack {
    aspect-ratio: 4 / 2.9;
    max-width: 560px;
  }
}

/* A small floating card overlapping the stack -- the detail that makes a
   hero look art-directed rather than templated. */
.mhero-chip {
  position: absolute;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 15px;
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  box-shadow: var(--shadow-lift);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--green-900);
  white-space: nowrap;
}

.mhero-chip-a {
  left: -4%;
  top: 30%;
}

.mhero-chip-b {
  right: -2%;
  bottom: 12%;
}

@media (max-width: 620px) {
  .mhero-chip {
    display: none;
  }
}

/* -------------------------------------------------------------------- */
/* Marquee                                                               */
/* -------------------------------------------------------------------- */

.marquee {
  position: relative;
  overflow: hidden;
  padding: 18px 0;
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}

.marquee-track {
  display: flex;
  width: max-content;
  gap: 44px;
  animation: marquee-scroll 42s linear infinite;
}

.marquee:hover .marquee-track {
  animation-play-state: paused;
}

.marquee-item {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-size: 1.02rem;
  color: var(--green-800);
  white-space: nowrap;
  opacity: 0.75;
}

.marquee-item::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--rust-400);
  flex: none;
}

@keyframes marquee-scroll {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(-50%);
  }
}

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

.bento {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 16px;
  margin-top: 40px;
}

.bento-item {
  position: relative;
  overflow: hidden;
  grid-column: span 2;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 28px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--paper-raised);
  transition: transform var(--transition), box-shadow var(--transition);
}

.bento-item:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lift);
}

.bento-wide {
  grid-column: span 4;
}

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

.bento-tall {
  grid-row: span 2;
}

.bento-item h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-h3);
  line-height: 1.2;
  letter-spacing: -0.012em;
}

.bento-item p {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.95rem;
  line-height: 1.6;
}

/* The dark feature tile -- one per bento, as the anchor. */
.bento-dark {
  background: var(--grad-forest);
  color: #f6f0e4;
  border-color: transparent;
}

.bento-dark h3 {
  color: #fffdf9;
}

.bento-dark p {
  color: rgb(246 240 228 / 76%);
}

.bento-accent {
  background: var(--grad-dawn);
  border-color: rgb(204 122 74 / 26%);
}

.bento-media {
  padding: 0;
  min-height: 220px;
}

.bento-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

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

  .bento-item,
  .bento-wide,
  .bento-full {
    grid-column: span 2;
  }

  .bento-tall {
    grid-row: auto;
  }
}

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

  .bento-item,
  .bento-wide,
  .bento-full {
    grid-column: span 1;
  }
}

/* Icon tile inside a bento/feature card. */
.icon-tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 13px;
  background: var(--rust-tint);
  color: var(--rust-600);
  flex: none;
}

.icon-tile svg {
  width: 23px;
  height: 23px;
}

.bento-dark .icon-tile {
  background: rgb(246 240 228 / 12%);
  color: var(--rust-300);
}

/* -------------------------------------------------------------------- */
/* Stat band                                                             */
/* -------------------------------------------------------------------- */

.stat-band {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--grad-forest);
  color: #f6f0e4;
  padding: clamp(48px, 6vw, 80px) 0;
}

.stat-band-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 32px;
}

.stat-band-item {
  text-align: center;
}

.stat-band-num {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 1.6rem + 2.6vw, 3.6rem);
  line-height: 1;
  letter-spacing: -0.03em;
  color: #fffdf9;
}

.stat-band-label {
  display: block;
  margin-top: 10px;
  font-size: 0.9rem;
  line-height: 1.5;
  color: rgb(246 240 228 / 72%);
}

/* -------------------------------------------------------------------- */
/* Split feature rows                                                    */
/* -------------------------------------------------------------------- */

.split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(28px, 5vw, 68px);
  align-items: center;
}

.split+.split {
  margin-top: clamp(48px, 7vw, 96px);
}

.split-reverse .split-media {
  order: -1;
}

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

  .split-reverse .split-media {
    order: 0;
  }
}

.split-media {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-lift);
  aspect-ratio: 4 / 3;
}

.split-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.split h3 {
  font-family: var(--font-display);
  font-size: var(--step-h2);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 14px 0 0;
  text-wrap: balance;
}

.split p {
  color: var(--ink-muted);
  line-height: 1.68;
}

/* A tinted caption plate overlapping the image corner. */
.split-caption {
  position: absolute;
  left: 16px;
  bottom: 16px;
  right: 16px;
  padding: 12px 16px;
  border-radius: var(--radius-md);
  background: rgb(15 36 25 / 82%);
  backdrop-filter: blur(8px);
  color: #f6f0e4 !important;
  font-size: 0.85rem;
  line-height: 1.5;
}

/* -------------------------------------------------------------------- */
/* Numbered step track                                                   */
/* -------------------------------------------------------------------- */

.step-track {
  display: grid;
  gap: 0;
  margin-top: 44px;
  counter-reset: steps;
}

.step-row {
  position: relative;
  display: grid;
  grid-template-columns: 82px minmax(0, 1fr);
  gap: 24px;
  padding: 30px 0;
  border-top: 1px solid var(--border);
}

.step-row:first-child {
  border-top: none;
}

.step-num {
  font-family: var(--font-display);
  font-size: clamp(2.2rem, 1.6rem + 1.8vw, 3.1rem);
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--rust-400);
}

.step-row h3 {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-size: var(--step-h3);
  letter-spacing: -0.012em;
}

.step-row p {
  margin: 0;
  color: var(--ink-muted);
  line-height: 1.68;
  max-width: 68ch;
}

.step-aside {
  margin-top: 14px;
  padding: 14px 16px;
  border-left: 3px solid var(--rust-300);
  background: var(--paper-tint);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

@media (max-width: 600px) {
  .step-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
  }
}

/* -------------------------------------------------------------------- */
/* Testimonials                                                          */
/* -------------------------------------------------------------------- */

.quote-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: 20px;
  margin-top: 60px;
}

.quote-card {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 30px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--paper-raised);
  margin: 0;
}

.quote-card blockquote {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.14rem;
  line-height: 1.55;
  letter-spacing: -0.008em;
  color: var(--green-900);
  text-wrap: pretty;
}

.quote-mark {
  font-family: var(--font-display);
  font-size: 2.6rem;
  line-height: 0.7;
  color: var(--rust-300);
  margin: 0;
}

.quote-person {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.quote-avatar {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
}

.quote-name {
  display: block;
  font-weight: 600;
  font-size: 0.93rem;
}

.quote-role {
  display: block;
  font-size: 0.83rem;
  color: var(--ink-muted);
}

/* -------------------------------------------------------------------- */
/* FAQ (native disclosure)                                               */
/* -------------------------------------------------------------------- */

.faq-v2 {
  margin-top: 36px;
  border-top: 1px solid var(--border);
}

.faq-v2 details {
  border-bottom: 1px solid var(--border);
}

.faq-v2 summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 22px 0;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-size: 1.12rem;
  line-height: 1.4;
  letter-spacing: -0.008em;
  color: var(--green-900);
}

.faq-v2 summary::-webkit-details-marker {
  display: none;
}

.faq-v2 summary::after {
  content: "";
  width: 22px;
  height: 22px;
  flex: none;
  background-repeat: no-repeat;
  background-position: center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239c4e2d' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
  transition: transform var(--transition);
}

.faq-v2 details[open] summary::after {
  transform: rotate(45deg);
}

.faq-v2 summary:hover {
  color: var(--rust-600);
}

.faq-v2 .faq-answer {
  margin: 0;
  padding: 0 0 24px;
  max-width: 72ch;
  color: var(--ink-muted);
  line-height: 1.7;
}

/* -------------------------------------------------------------------- */
/* Comparison table                                                      */
/* -------------------------------------------------------------------- */

.vs-table {
  width: 100%;
  margin-top: 40px;
  border-collapse: separate;
  border-spacing: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--paper-raised);
  font-size: 0.95rem;
}

.vs-table th,
.vs-table td {
  padding: 16px 18px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.vs-table thead th {
  background: var(--paper-tint);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

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

/* Our column, called out. `box-shadow` inset rather than a border so it
   doesn't shift the table's column widths. */
.vs-ours {
  background: rgb(204 122 74 / 7%);
  box-shadow: inset 2px 0 0 var(--rust-400), inset -2px 0 0 var(--rust-400);
  font-weight: 500;
  color: var(--green-900);
}

.vs-table thead .vs-ours {
  background: rgb(204 122 74 / 14%);
  color: var(--rust-700);
  box-shadow: inset 2px 0 0 var(--rust-400), inset -2px 0 0 var(--rust-400), inset 0 2px 0 var(--rust-400);
}

.vs-table tbody tr:last-child .vs-ours {
  box-shadow: inset 2px 0 0 var(--rust-400), inset -2px 0 0 var(--rust-400), inset 0 -2px 0 var(--rust-400);
}

.vs-yes,
.vs-no {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 500;
}

.vs-yes::before,
.vs-no::before {
  content: "";
  width: 17px;
  height: 17px;
  flex: none;
  background-repeat: no-repeat;
  background-position: center;
}

.vs-yes::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233f9d6a' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

.vs-no::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2393907d' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");
}

.vs-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.vs-scroll:focus-visible {
  outline: 3px solid var(--rust-500);
  outline-offset: 4px;
}

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

.price-hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 56px);
  align-items: center;
  padding: clamp(32px, 5vw, 56px);
  border-radius: 26px;
  background: var(--grad-forest);
  color: #f6f0e4;
  box-shadow: var(--shadow-deep);
}

.price-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(50% 70% at 88% 4%, rgb(204 122 74 / 38%), transparent 68%);
}

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

.price-figure {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 14px 0 0;
}

.price-figure strong {
  font-family: var(--font-display);
  font-size: clamp(3.4rem, 2.2rem + 4.4vw, 5.4rem);
  line-height: 0.9;
  letter-spacing: -0.04em;
  color: #fffdf9;
}

.price-figure span {
  font-size: 1.02rem;
  color: rgb(246 240 228 / 72%);
}

.price-hero h2 {
  font-family: var(--font-display);
  font-size: var(--step-h2);
  line-height: 1.08;
  letter-spacing: -0.022em;
  margin: 0;
  color: #fffdf9;
}

.price-hero .lede-lg {
  color: rgb(246 240 228 / 78%);
}

.price-includes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.price-includes li {
  position: relative;
  padding-left: 26px;
  font-size: 0.94rem;
  line-height: 1.55;
  color: rgb(246 240 228 / 86%);
}

.price-includes li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.36em;
  width: 15px;
  height: 15px;
  background-repeat: no-repeat;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23e0a67e' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

/* -------------------------------------------------------------------- */
/* Closing CTA                                                           */
/* -------------------------------------------------------------------- */

.cta-v2 {
  position: relative;
  /* `clip` rather than `hidden`: it crops the grain and bloom layers just
     the same but doesn't make this a scroll container, which is what broke
     a sticky descendant elsewhere in this file. */
  overflow: clip;
  isolation: isolate;
  text-align: center;
  /* Full bleed, square corners. Horizontal padding stays at 0 because the
     `.container` inside already sets the reading width -- adding padding
     here would inset the text twice. */
  padding: clamp(56px, 8vw, 104px) 0;
  background: var(--grad-forest);
  color: #f6f0e4;
}

.cta-v2::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(44% 62% at 22% 8%, rgb(204 122 74 / 30%), transparent 66%),
    radial-gradient(40% 58% at 80% 96%, rgb(107 133 116 / 26%), transparent 70%);
}

.cta-v2 h2 {
  font-family: var(--font-display);
  font-size: var(--step-h1);
  line-height: 1.04;
  letter-spacing: -0.026em;
  margin: 18px auto 0;
  max-width: 18ch;
  color: #fffdf9;
  text-wrap: balance;
}

.cta-v2 p {
  margin: 20px auto 0;
  max-width: 54ch;
  font-size: var(--step-lede);
  line-height: 1.62;
  color: rgb(246 240 228 / 78%);
}

.cta-v2 .kicker {
  color: var(--rust-300);
}

.cta-v2-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
  margin-top: 34px;
}

.cta-v2-actions .btn-secondary {
  background: transparent;
  color: #f6f0e4;
  border-color: rgb(246 240 228 / 38%);
}

.cta-v2-actions .btn-secondary:hover {
  background: rgb(246 240 228 / 10%);
}

.cta-v2-note {
  margin-top: 22px !important;
  font-size: 0.87rem !important;
  color: rgb(246 240 228 / 60%) !important;
}

/* -------------------------------------------------------------------- */
/* Scroll reveal                                                         */
/* -------------------------------------------------------------------- */

/* Applied by static/js/reveal.js only when IntersectionObserver exists, so
   the content is visible by default and never depends on JS to be read --
   the class that hides it is added at runtime, not authored in the markup. */
.reveal-ready {
  opacity: 0;
  transform: translateY(18px);
}

.reveal-in {
  opacity: 1;
  transform: none;
  transition: opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1), transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

@media (prefers-reduced-motion: reduce) {

  .reveal-ready,
  .reveal-in {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .marquee-track {
    animation: none;
  }
}

/* The v2 section heading. `.section-heading`'s 560px cap was sized for the
   old, smaller heading tier; at the display scale a two-line heading wraps
   into four. Given its own class rather than a `:has()` override so the
   original component keeps working untouched wherever it's still used. */
.section-heading-v2 {
  max-width: 46rem;
  margin-bottom: 8px;
}

.section-heading-v2 .kicker {
  justify-content: center;
  margin: 0 0 16px;
}

.section-heading-v2 .lede-lg {
  margin: 18px auto 0;
}

.section-heading-left {
  text-align: left;
  margin-inline: 0;
  max-width: 52rem;
}

.section-heading-left .kicker {
  justify-content: flex-start;
}

.section-heading-left .lede-lg {
  margin-inline: 0;
}

/* ==================================================================== */
/* Footer v2 -- deep green, continuing the closing CTA band.             */
/*                                                                      */
/* Every marketing page now ends on a dark `.cta-v2` band, and a cream   */
/* footer directly beneath it read as a page that had stopped rather     */
/* than ended. Taking the footer to the darkest green in the palette     */
/* means the page closes on one continuous dark block, which is both     */
/* more considered and a much stronger frame for the light content       */
/* above it.                                                            */
/*                                                                      */
/* The footer surface itself is defined once in the base footer section; */
/* this block only styles its dark-theme descendants and decoration.     */
/* ==================================================================== */

/* A hairline of the accent across the very top -- the one place the rust
   appears against the dark, so the join reads as deliberate. */
.site-footer::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  z-index: 1;
  background: var(--grad-warm);
  opacity: 0.85;
}

.site-footer .footer-top {
  border-bottom-color: rgb(246 240 228 / 14%);
  padding-bottom: 36px;
}

/* The dark-background lockup.
   Two changes, no second asset. The wordmark takes paper instead of ink,
   because it is `currentColor`. And the mark's gradient lifts two steps up
   the rust ramp: `--rust-400` to `--rust-600` is right on cream and much
   too dark here. Measured against `--green-950`: `--rust-600` is 2.75:1,
   which is why the tail of the W was fading into the background, while
   `--rust-300` and `--rust-400` are 7.71:1 and 5.01:1. The ramp is not
   simply lightened for both, because on cream those two fall to 1.92:1 and
   2.95:1 -- each background needs the half of the ramp it can hold.

   This replaced `filter: brightness(0) invert(1)`, which made the text
   legible by crushing the whole image to white, and so threw away the one
   piece of brand colour at the foot of the page to do it. */
.site-footer .site-logo {
  color: #f6f0e4;
}

.site-footer .logo-stop-from {
  stop-color: var(--rust-300);
}

.site-footer .logo-stop-to {
  stop-color: var(--rust-400);
}

.site-footer .footer-blurb {
  color: rgb(246 240 228 / 68%);
  max-width: 38ch;
}

.site-footer .footer-price {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-top: 16px;
  color: #fffdf9;
}

.site-footer .footer-price strong {
  font-family: var(--font-display);
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: -0.02em;
}

.site-footer .footer-price span {
  font-size: 0.85rem;
  font-weight: 400;
  color: rgb(246 240 228 / 62%);
}

.footer-email {
  display: inline-block;
  margin-top: 16px;
  padding-bottom: 2px;
  border-bottom: 1px solid rgb(224 166 126 / 45%);
  color: var(--rust-300);
  font-size: 0.9rem;
  font-weight: 500;
  transition: color var(--transition), border-color var(--transition);
}

.footer-email:hover {
  color: #f0c9a8;
  border-bottom-color: #f0c9a8;
}

.site-footer .footer-link-heading {
  color: var(--rust-300);
  letter-spacing: 0.12em;
}

.site-footer .footer-link-group a {
  position: relative;
  width: fit-content;
  color: rgb(246 240 228 / 82%);
  transition: color var(--transition);
}

.site-footer .footer-link-group a:hover {
  color: #fffdf9;
}

/* An underline that grows from the left on hover -- cheap, and it makes a
   plain column of links feel considered. */
.site-footer .footer-link-group a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 0;
  height: 1px;
  background: var(--rust-300);
  transition: width var(--transition);
}

.site-footer .footer-link-group a:hover::after {
  width: 100%;
}

.footer-trust {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
  margin: 28px 0 0;
  padding: 0;
  list-style: none;
}

.footer-trust li {
  position: relative;
  padding-left: 24px;
  font-size: 0.86rem;
  color: rgb(246 240 228 / 72%);
}

.footer-trust li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.3em;
  width: 15px;
  height: 15px;
  background-repeat: no-repeat;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23e0a67e' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

.site-footer .footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px 28px;
  flex-wrap: wrap;
  margin-top: 28px;
  padding-top: 22px;
  border-top: 1px solid rgb(246 240 228 / 12%);
}

.site-footer .footer-bottom p {
  margin: 0;
  color: rgb(246 240 228 / 52%);
  font-size: 0.82rem;
}

@media (max-width: 720px) {
  .footer-trust {
    gap: 8px 20px;
  }
}

@media (prefers-reduced-motion: reduce) {

  .site-footer .footer-link-group a::after,
  .footer-email {
    transition: none;
  }
}

/* The "Account" word next to the avatar in the signed-in header. It has
   been in the markup since the account menu was built but never had a rule,
   so it inherited body type at full size next to a 28px avatar. */
.account-label {
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--green-900);
  line-height: 1;
}

@media (max-width: 520px) {

  /* Below this the avatar alone is enough -- the chevron already signals
     that it opens. */
  .account-label {
    display: none;
  }
}

/* ==================================================================== */
/* Auth screens                                                         */
/*                                                                      */
/* A full-bleed split: the form on the left, a branded panel on the      */
/* right. Sign-in was a 420px card floating in the middle of an empty    */
/* container, which is the cheapest-looking screen on any SaaS and sits  */
/* directly between the marketing site and the product.                 */
/* ==================================================================== */

.auth-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  align-items: stretch;
  /* Fills the space between header and footer without a hardcoded height:
     100dvh accounts for mobile browser chrome, and the fallback keeps
     older engines sensible. */
  min-height: 78vh;
  min-height: 78dvh;
}

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

.auth-form-side {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: clamp(40px, 6vw, 88px) clamp(20px, 4vw, 56px);
}

.auth-form {
  width: 100%;
  max-width: 26rem;
}

.auth-form h1 {
  margin: 14px 0 0;
}

.auth-form .lede-lg {
  margin: 16px 0 30px;
  font-size: 1rem;
}

.auth-email {
  color: var(--green-900);
  overflow-wrap: anywhere;
}

.auth-submit {
  margin-top: 22px;
}

.auth-submit .btn {
  width: 100%;
  padding: 15px 24px;
  font-size: 0.98rem;
}

.auth-note {
  margin: 20px 0 0;
  color: var(--ink-faint);
  font-size: 0.84rem;
  line-height: 1.6;
}

.auth-alt {
  margin-top: 32px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}

.auth-alt p {
  margin: 0 0 6px;
  color: var(--ink-muted);
  font-size: 0.9rem;
}

.auth-alt-link {
  color: var(--rust-600);
  font-size: 0.92rem;
  font-weight: 600;
  text-decoration: none;
}

.auth-alt-link:hover {
  color: var(--rust-700);
  text-decoration: underline;
}

/* The branded half. */
.auth-panel {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  padding: clamp(40px, 5vw, 72px);
  background: var(--grad-forest);
  color: #f6f0e4;
}

.auth-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(50% 60% at 84% 8%, rgb(204 122 74 / 32%), transparent 68%),
    radial-gradient(44% 52% at 8% 94%, rgb(107 133 116 / 26%), transparent 70%);
}

/* Below the split breakpoint the panel becomes a footer to the form
   rather than a column beside it -- worth keeping (it is the only
   reassurance on the page) but not worth a full screen of its own. */
@media (max-width: 900px) {
  .auth-panel {
    padding: 36px 24px 44px;
  }
}

.auth-panel-inner {
  max-width: 30rem;
}

.auth-panel .kicker {
  color: var(--rust-300);
}

.auth-panel-quote {
  margin: 20px 0 0;
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 1.05rem + 0.9vw, 1.75rem);
  line-height: 1.4;
  letter-spacing: -0.015em;
  color: #fffdf9;
  text-wrap: pretty;
}

.auth-panel-person {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 24px;
}

.auth-panel-person img {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
}

.auth-panel-name {
  display: block;
  font-weight: 600;
  font-size: 0.93rem;
  color: #fffdf9;
}

.auth-panel-role {
  display: block;
  font-size: 0.83rem;
  color: rgb(246 240 228 / 66%);
}

.auth-panel-facts {
  margin: 32px 0 0;
  padding: 28px 0 0;
  border-top: 1px solid rgb(246 240 228 / 16%);
  list-style: none;
  display: grid;
  gap: 11px;
}

.auth-panel-facts li {
  position: relative;
  padding-left: 26px;
  font-size: 0.92rem;
  line-height: 1.5;
  color: rgb(246 240 228 / 82%);
}

.auth-panel-facts li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.28em;
  width: 15px;
  height: 15px;
  background-repeat: no-repeat;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23e0a67e' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

/* The six-digit field deserves to look like what it is. */
.auth-form .code-input {
  font-family: var(--font-display);
  font-size: 1.9rem;
  letter-spacing: 0.35em;
  text-align: center;
  padding: 16px 12px;
}

/* ==================================================================== */
/* In-app polish                                                        */
/*                                                                      */
/* The signed-in screens were built before the marketing system and      */
/* never got its type scale, depth or texture -- so paying customers saw */
/* a plainer product than the site that sold it to them, which is the    */
/* wrong way round.                                                     */
/*                                                                      */
/* This upgrades the shared primitives every app screen already uses     */
/* (`.card`, page headers, `.status-pill`, form controls) rather than    */
/* rewriting a dozen views. One block, and every signed-in page moves at */
/* once -- and nothing here changes markup, so no view can fall behind.  */
/* ==================================================================== */

/* A soft warm wash behind the app, so content cards sit on something
   rather than on flat cream. Attached to the layout wrapper, not `body`,
   so it can't interfere with the marketing pages' own backgrounds. */
/* Hover lift removed. The rule said cards lift "where they're
   interactive", and then listed `.dashboard-card` and `.site-card` --
   neither of which is ever a link. `a.card` matches nothing in the
   codebase at all, so in practice every one of the thirty static cards in
   the app raised a shadow when the pointer crossed it, promising a click
   that does nothing.

   The border still darkens, which is enough to acknowledge the pointer on
   a card that holds buttons of its own. `a.card` keeps the lift for
   whenever a genuinely clickable card turns up. */
a.card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-lift);
}

.dashboard-card:hover,
.site-card:hover {
  border-color: var(--border-strong);
}

/* There used to be a second `.breadcrumb-link` block ~6800 lines above
   this one, a leftover from before the in-app design layer re-tiered it.
   Every declaration here already beat it on source order except
   `margin-bottom`, which quietly survived -- so a header setting its own
   back-link spacing lost to a rule nobody could see from where they were
   working. The two are merged; the surviving margin is that rule's. */
.breadcrumb-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 16px;
  color: var(--ink-muted);
  font-size: 0.88rem;
  font-weight: 500;
  text-decoration: none;
  transition: color var(--transition), gap var(--transition);
}

.breadcrumb-link:hover {
  color: var(--rust-600);
  gap: 9px;
}

/* Section headings inside the app -- the `p.eyebrow` + `h2` pairing used
   by nearly every card. Brings them in line with the marketing kicker
   without every view having to change. */
/* Status pills: filled rather than tinted, so state is legible at a
   glance across a long list instead of needing to be read. */
/* Form controls -- larger targets, clearer focus, and a real focus ring
   rather than the browser default outline. */
.form-field input,
.form-field select,
.form-field textarea {
  transition: border-color var(--transition), box-shadow var(--transition),
    background-color var(--transition);
}

.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--rust-400);
  box-shadow: 0 0 0 4px rgb(204 122 74 / 16%);
}

.form-label-row,
.form-field>label {
  font-weight: 600;
}

/* A visible, consistent focus ring for everything keyboard-reachable.
   The app had browser defaults, which vary and disappear entirely against
   the dark panels. */
.btn:focus-visible,
/* The first focusable thing on every marketing page, and it was the one
   link in the header without a ring -- so tabbing into the site began
   with the focus indicator nowhere. */
.site-brand:focus-visible,
/* A whole tile is the button here, and its only focus feedback was a 3px
   lift -- which is invisible against a page that scrolls. */
.showcase-card:focus-visible,
.nav-link:focus-visible,
.header-cta:focus-visible,
.header-signin:focus-visible,
.breadcrumb-link:focus-visible,
.site-page-row:focus-visible,
.admin-overview-row:focus-visible,
.billing-row-name a:focus-visible,
.footer-link-group a:focus-visible,
.mobile-menu summary:focus-visible,
.auth-alt-link:focus-visible {
  outline: 2px solid var(--rust-500);
  outline-offset: 3px;
  border-radius: var(--radius);
}

/* Empty states, which the app has several of and which all read as
   apologies rather than invitations. */
/* Tables in the app get the marketing table's treatment. */
@media (prefers-reduced-motion: reduce) {

  .card,
  .btn,
  .breadcrumb-link,
  .form-field input,
  .form-field select,
  .form-field textarea {
    transition: none;
  }

  .btn:hover {
    transform: none;
  }
}

/* Marketing pages end on a dark `.cta-v2` band that supplies its own
   generous bottom padding, so the footer sits flush against it. App and
   admin screens end on an ordinary card and need the gap back -- without
   it the last card butts straight into the footer.

   `:not()` on the layout rather than a class the views have to remember,
   so no screen can be added later and miss it. */
.site-content:not(:has(.cta-v2)):not(:has(.auth-split))+.site-footer {
  margin-top: 72px;
}

/* ==================================================================== */
/* Legal pages                                                          */
/* ==================================================================== */

.legal-updated {
  margin-top: 22px;
  font-size: 0.85rem;
  color: var(--ink-faint);
}

.legal-body {
  max-width: 46rem;
}

.legal-clause+.legal-clause {
  margin-top: 44px;
  padding-top: 44px;
  border-top: 1px solid var(--border);
}

.legal-clause h2 {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin: 0 0 18px;
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 1.1rem + 0.85vw, 1.7rem);
  line-height: 1.2;
  letter-spacing: -0.018em;
  text-wrap: balance;
}

.legal-clause-number {
  flex: none;
  font-family: var(--font-body);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--rust-400);
}

/* Long-form legal text is the one place on the site where line length and
   leading matter more than anything visual. */
.legal-clause p {
  margin: 0 0 16px;
  color: var(--ink-muted);
  font-size: 1rem;
  line-height: 1.75;
  text-wrap: pretty;
}

.legal-clause p:last-child {
  margin-bottom: 0;
}

.legal-clause a {
  color: var(--rust-600);
  font-weight: 500;
}

.legal-table-wrap {
  margin: 24px 0 8px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.legal-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--paper-raised);
  font-size: 0.92rem;
}

.legal-table th,
.legal-table td {
  padding: 14px 16px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}

.legal-table thead th {
  background: var(--paper-tint);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

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

.legal-table code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.84rem;
  color: var(--green-900);
  overflow-wrap: anywhere;
}

/* Legal links in the footer's bottom row. */
.footer-legal {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
}

.site-footer .footer-legal a {
  color: rgb(246 240 228 / 72%);
  font-size: 0.82rem;
  text-decoration: none;
  transition: color var(--transition);
}

.site-footer .footer-legal a:hover {
  color: #fffdf9;
  text-decoration: underline;
}

/* ==================================================================== */
/* Cookie consent bar                                                   */
/*                                                                      */
/* A bar, not a modal. It doesn't block the page, doesn't dim it, and    */
/* doesn't make "accept" the only escape -- both choices are equally     */
/* prominent and equally easy to click, which is what the ICO expects    */
/* and what a dark-pattern banner deliberately avoids.                   */
/* ==================================================================== */

.consent-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  padding: 16px 20px calc(16px + env(safe-area-inset-bottom, 0px));
  background: var(--green-950);
  color: rgb(246 240 228 / 82%);
  border-top: 3px solid transparent;
  border-image: var(--grad-warm) 1;
  box-shadow: 0 -16px 40px -24px rgb(15 36 25 / 60%);
  animation: consent-in 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes consent-in {
  from {
    transform: translateY(100%);
  }

  to {
    transform: none;
  }
}

.consent-inner {
  max-width: 1120px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px 32px;
  flex-wrap: wrap;
}

.consent-text {
  flex: 1 1 30rem;
  min-width: 0;
}

.consent-title {
  margin: 0 0 4px;
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: #fffdf9;
  letter-spacing: -0.01em;
}

.consent-text p {
  margin: 0;
  font-size: 0.87rem;
  line-height: 1.6;
}

.consent-text a {
  color: var(--rust-300);
  text-decoration: underline;
}

.consent-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}

/* Both buttons are the same size and weight. Making "decline" a faint
   text link is the standard dark pattern, and it isn't valid consent. */
.consent-accept,
.consent-decline {
  min-height: 44px;
  padding: 0 22px;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--transition), border-color var(--transition),
    color var(--transition);
}

.consent-accept {
  border: 1px solid transparent;
  background: var(--grad-warm);
  color: var(--paper-raised);
}

.consent-accept:hover {
  filter: saturate(1.08);
}

.consent-decline {
  border: 1px solid rgb(246 240 228 / 34%);
  background: transparent;
  color: #f6f0e4;
}

.consent-decline:hover {
  background: rgb(246 240 228 / 10%);
  border-color: rgb(246 240 228 / 55%);
}

.consent-accept:focus-visible,
.consent-decline:focus-visible {
  outline: 2px solid var(--rust-300);
  outline-offset: 3px;
}

@media (max-width: 620px) {
  .consent-actions {
    width: 100%;
  }

  .consent-accept,
  .consent-decline {
    flex: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .consent-bar {
    animation: none;
  }
}

/* ==================================================================== */
/* ADMIN v2 -- sidebar shell, command palette, working tables.          */
/*                                                                      */
/* The admin was seven pill tabs above a page. That works at three       */
/* sections and stops working at seven: no room for counts, no grouping, */
/* and on a narrow window the tabs wrapped and shoved the title around.  */
/* This is a proper tool layout -- persistent nav, sticky header,        */
/* keyboard-first search.                                               */
/* ==================================================================== */

/* One place defines how far the sticky site header reaches, so the shell's
   top padding and every `position: sticky` offset below it stay in step.
   They were separate numbers and drifted -- the shell had a *negative*
   top margin left over from `.admin-area`, which this rewrite deleted, so
   the page pulled itself up underneath the header. */
/* The app top bar's height, in one place.

   `.app-topbar` is `position: sticky`, so anything else that sticks below
   it has to know how tall it is or it parks underneath. That was one
   literal in the topbar rule and, until now, nothing else -- the table
   header stuck to `top: 0` of its own scroll box and never had to care.
   Now that it sticks to the viewport, the two numbers have to agree. */
@media (max-width: 900px) {

  :root {
    --app-topbar-h: 52px;
  }
}

/* -------------------------------------------------------------------- */
/* Sidebar                                                               */
/* -------------------------------------------------------------------- */

/* Retained for the admin-only bits that still sit inside the shared
   sidebar -- the brand block, the search trigger, the grouped nav. The
   card treatment is gone: `.app-sidebar` is the surface now, and a card
   inside it was a box inside a box. */
.admin-sidebar-inner {
  display: flex;
  flex-direction: column;
  gap: 20px;
}


/* The palette trigger, styled as a search field because that's what it
   behaves like -- a fake input is the clearest possible affordance for
   "type here to find something". */
.admin-search-trigger {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--paper);
  color: var(--ink-muted);
  font-family: var(--font-body);
  font-size: 0.86rem;
  cursor: pointer;
  transition: border-color var(--transition), background-color var(--transition);
}

.admin-search-trigger:hover {
  border-color: var(--rust-400);
  background: var(--paper-raised);
}

.admin-search-icon {
  display: inline-flex;
  flex: none;
  color: var(--ink-faint);
}

.admin-search-icon svg {
  width: 15px;
  height: 15px;
}

.admin-search-label {
  flex: 1;
  text-align: left;
}

.admin-search-kbd,
.command-foot kbd,
.command-esc {
  padding: 2px 6px;
  border: 1px solid var(--border-strong);
  border-radius: 5px;
  background: var(--paper-raised);
  font-family: var(--font-body);
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--ink-muted);
  line-height: 1.5;
}


.admin-sidebar-foot {
  padding-top: 14px;
  border-top: 1px solid var(--border);
}


/* -------------------------------------------------------------------- */
/* Main column                                                           */
/* -------------------------------------------------------------------- */

/* The admin page title block. It is now the first thing inside
   `.app-main-inner`, which carries no top padding of its own, so the
   spacing that used to come from the old shell's `padding: 32px 0` lives
   here -- matching `.page-header`'s 40px on the customer side. */
.admin-topbar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  padding-top: 40px;
  padding-bottom: 20px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--border);
}

.admin-topbar-text h1 {
  margin: 4px 0 0;
  font-size: clamp(1.6rem, 1.3rem + 1.2vw, 2.1rem);
  line-height: 1.1;
  letter-spacing: -0.022em;
}

.admin-topbar-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.admin-topbar-actions > .status-pill {
  margin-right: 2px;
}

.admin-editor-preview {
  gap: 8px;
}

/* Secondary visibility and destructive actions belong close to the article
   controls, but not at the same visual rank as preview and publish. */
.admin-editor-more {
  position: relative;
}

.admin-editor-more > summary {
  cursor: pointer;
  list-style: none;
}

.admin-editor-more > summary::-webkit-details-marker {
  display: none;
}

.admin-editor-more[open] > summary {
  border-color: var(--green-700);
  color: var(--green-900);
}

.admin-editor-more-menu {
  position: absolute;
  top: calc(100% + 9px);
  right: 0;
  z-index: 30;
  width: 13rem;
  padding: 7px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  box-shadow: var(--shadow-deep);
}

.admin-editor-more-menu form {
  margin: 0;
}

.admin-editor-more-menu button {
  width: 100%;
  min-height: 42px;
  padding: 9px 11px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--green-900);
  font: inherit;
  font-size: 0.86rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.admin-editor-more-menu button:hover {
  background: var(--paper-tint);
}

.admin-editor-more-menu .admin-editor-delete {
  color: var(--rust-700);
}

.admin-editor-more-menu form + form {
  border-top: 1px solid var(--border);
}

.admin-content {
  min-width: 0;
}

/* -------------------------------------------------------------------- */
/* Command palette                                                       */
/* -------------------------------------------------------------------- */

body.command-open {
  overflow: hidden;
}

.command-palette {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: clamp(48px, 12vh, 140px) 20px 20px;
}

.command-backdrop {
  position: absolute;
  inset: 0;
  background: rgb(15 36 25 / 42%);
  backdrop-filter: blur(3px);
}

.command-dialog {
  position: relative;
  width: 100%;
  max-width: 40rem;
  display: flex;
  flex-direction: column;
  max-height: min(60vh, 32rem);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  box-shadow: var(--shadow-deep);
  overflow: hidden;
  animation: command-in 0.16s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes command-in {
  from {
    opacity: 0;
    transform: translateY(-8px) scale(0.99);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

.command-input-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 15px 18px;
  border-bottom: 1px solid var(--border);
}

.command-input-icon {
  display: inline-flex;
  flex: none;
  color: var(--ink-faint);
}

.command-input-icon svg {
  width: 18px;
  height: 18px;
}

.command-input-row input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  outline: none;
  font-family: var(--font-body);
  font-size: 1.02rem;
  color: var(--green-900);
}

.command-results {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
}

.command-group {
  margin: 10px 0 4px;
  padding: 0 10px;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.command-group:first-child {
  margin-top: 2px;
}

.command-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 9px 10px;
  border-radius: var(--radius-md);
  color: var(--green-900);
  text-decoration: none;
}

.command-item:hover,
.command-item.is-active {
  background: var(--rust-tint);
  color: var(--rust-700);
}

.command-item-title {
  font-size: 0.93rem;
  font-weight: 600;
}

.command-item-sub {
  font-size: 0.79rem;
  color: var(--ink-muted);
  overflow-wrap: anywhere;
}

.command-item.is-active .command-item-sub {
  color: var(--rust-600);
}

.command-empty {
  margin: 0;
  padding: 22px 12px;
  text-align: center;
  color: var(--ink-muted);
  font-size: 0.9rem;
}

.command-foot {
  display: flex;
  gap: 18px;
  padding: 10px 16px;
  border-top: 1px solid var(--border);
  background: var(--paper-tint);
  font-size: 0.74rem;
  color: var(--ink-faint);
}

.command-foot span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

@media (max-width: 620px) {
  .command-foot {
    display: none;
  }
}

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

/* The toolbar above a table: a filter box and a live row count.

   Deliberately *outside* `.data-table-wrap`, and every table that has one
   must place it there. The wrap is a scroll container now (see its own
   comment), so a toolbar inside it scrolls away with the rows -- which
   defeats a filter box entirely. Two of the three tables had it nested
   inside, flush against the wrap's border with no padding at all.

   The top margin matches `.data-table-wrap`'s, so the toolbar inherits the
   same separation from whatever sits above it (usually a summary grid)
   that the table used to have. */
.table-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin: 28px 0 0;
}

/* When a toolbar is present it owns the gap above the table, so the wrap
   drops its own -- otherwise the two stack into a 52px trench. */
.table-toolbar+.data-table-wrap {
  margin-top: 12px;
}

.table-filter {
  position: relative;
  flex: 1 1 18rem;
  max-width: 26rem;
}

.table-filter input {
  width: 100%;
  padding: 10px 14px 10px 36px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--paper-raised);
  font-family: var(--font-body);
  font-size: 0.88rem;
  color: var(--green-900);
  transition: border-color var(--transition), box-shadow var(--transition);
}

.table-filter input:focus {
  outline: none;
  border-color: var(--rust-400);
  box-shadow: 0 0 0 4px rgb(204 122 74 / 16%);
}

.table-filter::before {
  content: "";
  position: absolute;
  left: 13px;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2393907d' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='M21 21l-4.3-4.3'/%3E%3C/svg%3E");
}

.table-count {
  font-size: 0.83rem;
  color: var(--ink-muted);
  white-space: nowrap;
}

/* The table header, in one block.

   It was in three: the box model on `.data-table th` above, the colour
   and weight in a second rule 6,500 lines further down, and the sticky
   behaviour in a third. All three won on different properties, so editing
   any one of them appeared to do nothing -- which is how the sticky
   header came to be documented as sticking to a scroll container that no
   longer exists. The two `thead th` rules are merged here; the shared box
   model stays on `.data-table th`, since it applies to any header cell.

   Header stays put while a long table scrolls -- the single biggest
   usability gain on a table of any length. It sticks to the viewport, so
   the offset is the height of the bar above it rather than 0, and
   `z-index` keeps it below `.app-topbar` (5) so it slides under rather
   than over. */
.data-table thead th {
  position: sticky;
  top: var(--app-topbar-h);
  z-index: 2;
  background: var(--paper-tint);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Below this breakpoint `.data-table-wrap` takes `overflow-x: auto` for
   wide tables, and scrolling on one axis is enough to make it a scroll
   container -- which is what sticky descendants resolve against. The
   viewport offset above would then be measured from the top of the wrap
   and push the header down into its own rows. Nothing scrolls vertically
   inside it there, so sticky has nothing left to do.

   Placed here rather than with the other mobile rules on purpose: a media
   query adds no specificity, so an override of an equal selector only
   works if it comes after the rule it overrides. */
@media (max-width: 900px) {

  .data-table thead th {
    position: static;
  }
}

.data-table th.is-sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

.data-table th.is-sortable::after {
  content: "\2195";
  margin-left: 6px;
  opacity: 0.35;
  font-size: 0.85em;
}

.data-table th[aria-sort="ascending"]::after {
  content: "\2191";
  opacity: 1;
  color: var(--rust-600);
}

.data-table th[aria-sort="descending"]::after {
  content: "\2193";
  opacity: 1;
  color: var(--rust-600);
}

.data-table th.is-sortable:hover {
  color: var(--rust-700);
}

.data-table tbody tr {
  transition: background-color var(--transition);
}

.data-table tbody tr:hover {
  background: var(--paper-tint);
}

.table-no-matches {
  padding: 28px 16px;
  text-align: center;
  color: var(--ink-muted);
  font-size: 0.92rem;
}

@media (prefers-reduced-motion: reduce) {
  .command-dialog {
    animation: none;
  }

  .admin-search-trigger,
  .data-table tbody tr {
    transition: none;
  }
}

/* The page-editor root. Its children (`.admin-editor-toolbar`,
   `.admin-editor-split`) were all styled, but the wrapper itself never
   was, so it had no stacking or width constraint of its own. */
.admin-editor {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-width: 0;
}

/* Chrome paints its own background on autofilled fields, which does not
   match any surface in this app. Pinning the inset shadow to the field's
   own colour keeps an autofilled input looking like every other one.

   Introduced alongside the floating-label pattern (since Chrome's
   background hid the raised label) and kept after it was removed: the
   mismatch it fixes has nothing to do with where the label sits. */
.form-field input:-webkit-autofill,
.form-field input:-webkit-autofill:hover,
.form-field input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 100px var(--paper-raised) inset;
  -webkit-text-fill-color: var(--green-900);
}

/* ==========================================================================
   Admin file browser
   ==========================================================================
   The screen behind /admin/files. Deliberately plain: it borrows the
   existing table, pill and card styles rather than inventing a second
   visual language, so the only things styled here are the parts a file
   listing has and no other admin screen does -- a breadcrumb trail, a
   name cell with an icon, and a text preview. */

.file-crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 18px 0 0;
  font-size: 0.9rem;
}

.file-crumb {
  color: var(--ink-muted);
  text-decoration: none;
  padding: 3px 8px;
  border-radius: 7px;
  transition: background 0.15s ease, color 0.15s ease;
}

.file-crumb:hover,
.file-crumb:focus-visible {
  background: var(--paper-tint);
  color: var(--green-900);
}

.file-crumb-sep {
  color: var(--border-strong);
  user-select: none;
}

.file-crumb-current {
  font-weight: 600;
  color: var(--green-900);
  padding: 3px 4px;
}

/* The whole cell is the link, so the icon and the name move together on
   hover rather than the name shifting under a stationary icon. */
.file-name {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--green-900);
  text-decoration: none;
  font-weight: 500;
}

.file-name:hover .file-icon,
.file-name:focus-visible .file-icon {
  color: var(--rust-600);
}

.file-name:hover span:last-child {
  text-decoration: underline;
}

.file-icon {
  display: inline-flex;
  color: var(--ink-muted);
  flex: 0 0 auto;
  transition: color 0.15s ease;
}

.file-icon svg {
  width: 17px;
  height: 17px;
}

/* Junk rows are tinted rather than hidden or auto-removed. Deciding what
   is genuinely stray is a judgement call an admin should make looking at
   the folder, not something a filename pattern should make on its own. */
.file-row-junk {
  background: rgb(156 78 45 / 4%);
}

.file-row-junk .file-name {
  color: var(--rust-700);
}

.file-row-junk .status-pill {
  margin-left: 9px;
  vertical-align: 1px;
}

.file-size,
.file-modified {
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.file-actions {
  text-align: right;
  white-space: nowrap;
}

.file-card {
  margin-top: 20px;
}

/* `pre` with no wrapping: source files are meant to be read as they were
   written, and soft-wrapping a minified line would be worse than a
   scrollbar. Capped in height so a long file can't push the delete button
   miles down the page. */
.file-preview {
  margin: 0;
  max-height: min(62vh, 640px);
  overflow: auto;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--paper-tint);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.82rem;
  line-height: 1.6;
  tab-size: 2;
}

.file-preview code {
  background: none;
  padding: 0;
  font: inherit;
  color: var(--green-900);
  white-space: pre;
}

.file-preview-actions {
  margin-top: 20px;
}

/* The site id and status under a root-level folder name -- kept quiet so
   the business name stays the thing you scan for. */
.file-sub {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  font-size: 0.76rem;
  color: var(--ink-muted);
  text-transform: capitalize;
}

/* ==========================================================================
   Admin file editor
   ========================================================================== */

/* The "what am I actually editing" banner. Three visual weights, because
   the categories genuinely differ in consequence: neutral for a file that
   behaves the way you'd expect, warning for build output that discards your
   edit, live for page source that publishes it. */
.file-notice,
.admin-notice {
  margin: 18px 0 0;
  padding: 15px 18px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: 10px;
  background: var(--paper-tint);
}

.file-notice strong,
.admin-notice strong {
  display: block;
  color: var(--green-900);
  font-size: 0.92rem;
  margin-bottom: 5px;
}

.file-notice p,
.admin-notice p {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

.file-notice p+p,
.admin-notice p+p {
  margin-top: 8px;
}

.file-notice code,
.admin-notice code {
  font-size: 0.85em;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgb(26 53 39 / 7%);
}

.file-notice.is-warning,
.admin-notice.is-warning {
  border-left-color: var(--rust-600);
  background: var(--rust-tint);
}

.file-notice.is-live,
.admin-notice.is-live {
  border-left-color: var(--green-700);
}

.file-head-actions {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* Says which editor you actually got. Monaco loads from a CDN, so "did the
   enhanced editor come up" is a real question with a real answer rather
   than something to assume. */
.file-mode {
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
  white-space: nowrap;
}

.file-mode.is-error {
  color: var(--rust-700);
}

.file-editor {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* The fallback editor and the Monaco container are deliberately the same
   height, so swapping one for the other doesn't make the page jump. */
.file-editor-textarea,
.file-editor-monaco {
  height: min(64vh, 660px);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.file-editor-textarea {
  width: 100%;
  box-sizing: border-box;
  padding: 14px 16px;
  resize: vertical;
  background: var(--paper-tint);
  color: var(--green-900);
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.82rem;
  line-height: 1.6;
  tab-size: 2;
  white-space: pre;
  overflow: auto;
}

.file-editor-textarea:focus-visible {
  outline: 2px solid var(--green-700);
  outline-offset: 1px;
}

.file-editor-monaco {
  overflow: hidden;
}

.file-editor-bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 14px;
}

.file-editor-status {
  font-size: 0.8rem;
  color: var(--rust-700);
}

@media (max-width: 700px) {
  .admin-card-head:has(.file-head-actions) {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .file-editor-textarea,
  .file-editor-monaco {
    height: 56vh;
  }
}

/* ==========================================================================
   Admin account detail
   ========================================================================== */

.admin-user-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 22px;
}

.admin-user-name {
  margin: 0 0 4px;
  font-size: 1.35rem;
}

.admin-user-head p {
  margin: 0;
  font-size: 0.88rem;
}

/* Makes the whole name cell a target back to the account, without turning
   the row into a link and swallowing the other cells' own links. */
.admin-row-link {
  color: inherit;
  text-decoration: none;
}

.admin-row-link:hover,
.admin-row-link:focus-visible {
  text-decoration: underline;
  text-decoration-color: var(--rust-600);
  text-underline-offset: 2px;
}

/* Irreversible actions get their own visual register. Not alarming for the
   sake of it -- erasure is a legitimate, routine obligation, and a card
   that shouts would push someone to avoid a thing they're required to do.
   The point is only that it can't be mistaken for the cards above it. */
.admin-danger {
  border-color: rgb(156 78 45 / 30%);
}

.admin-danger .admin-card-head h2 {
  color: var(--rust-700);
}

.admin-danger .btn {
  background: var(--rust-600);
  border-color: var(--rust-600);
  color: #fff;
}

.admin-danger .btn:hover {
  background: var(--rust-700);
  border-color: var(--rust-700);
}

.admin-danger form {
  margin-top: 6px;
}

/* Third variant per creative direction -- added so every style on the
   showcase grid has three examples rather than two. Each tint is taken
   from that demo site's own dominant colour, so the card reads as a
   preview of what opens rather than a generic swatch. */
/* ==========================================================================
   Help centre (/knowledgebase)
   ==========================================================================
   Built from the same marketing primitives as the rest of the public site
   -- .msection, .container, .display, .lede, .pill -- so it reads as part
   of the site rather than as a bolted-on docs tool. What's here is only
   the parts a help centre has and no marketing page does: a filterable
   list of topics, and a long-form prose column. */

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

.kb-hero-inner {
  max-width: 720px;
}

.kb-app-return {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  margin-bottom: 18px;
  color: var(--green-800);
  font-size: 0.86rem;
  font-weight: 650;
  text-decoration: none;
}

.kb-app-return:hover,
.kb-app-return:focus-visible {
  color: var(--rust-700);
  text-decoration: underline;
}

.kb-article-top .kb-app-return {
  margin-bottom: 14px;
}

.kb-hero .lede {
  margin-bottom: 28px;
}

/* Filters the list already on the page rather than querying a server. The
   whole help centre is one page of titles, so a round trip would be both
   slower and a dependency the page doesn't otherwise have. */
.kb-search {
  position: relative;
  max-width: 480px;
}

.kb-search-icon {
  position: absolute;
  left: 16px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  color: var(--ink-muted);
  pointer-events: none;
}

.kb-search-icon svg {
  width: 18px;
  height: 18px;
}

.kb-search input {
  width: 100%;
  box-sizing: border-box;
  padding: 15px 18px 15px 46px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: #fff;
  font: inherit;
  font-size: 0.98rem;
  color: var(--green-900);
  box-shadow: 0 10px 26px -22px rgb(26 53 39 / 45%);
}

.kb-search input:focus-visible {
  outline: 2px solid var(--green-700);
  outline-offset: 1px;
  border-color: transparent;
}

.kb-empty {
  border: 1px dashed var(--border-strong);
  border-radius: 14px;
  padding: 44px 28px;
  text-align: center;
  color: var(--ink-muted);
}

.kb-empty-title {
  margin: 0 0 8px;
  font-weight: 600;
  color: var(--green-900);
  font-size: 1.05rem;
}

.kb-empty p {
  margin: 0;
  font-size: 0.94rem;
  line-height: 1.7;
}

.kb-empty a {
  color: var(--rust-700);
}

.kb-groups {
  display: grid;
  gap: 44px;
}

/* Columns rather than a two-column grid. Grid rows size to their tallest
   item, and these categories hold 3 to 6 articles each -- so a short
   category sitting beside a long one left a hole underneath it as deep as
   the difference. Column flow packs them tightly instead, and
   `break-inside` keeps a category from being split across the gutter,
   which is the one thing columns get wrong by default. */
@media (min-width: 900px) {
  .kb-groups {
    display: block;
    column-count: 2;
    column-gap: 56px;
  }

  .kb-group {
    break-inside: avoid;
    margin-bottom: 46px;
  }

  .kb-group:last-child {
    margin-bottom: 0;
  }
}

.kb-group-head {
  display: flex;
  gap: 14px;
  align-items: center;
  margin-bottom: 10px;
}

.kb-group-icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 11px;
  background: var(--paper-tint);
  border: 1px solid var(--border);
  color: var(--green-800);
}

.kb-group-icon svg {
  width: 19px;
  height: 19px;
}

.kb-group-head h2 {
  margin: 0;
  font-size: 1.18rem;
  line-height: 1.25;
}

/* Sits outside `.kb-group-head` so it starts at the same left edge as the
   icon above it and the article list below it -- see the comment in
   `views::public::category_block`. */
.kb-group-description {
  margin: 0 0 16px;
  color: var(--ink-muted);
  font-size: 0.9rem;
  line-height: 1.6;
}

.kb-group-head h2 a {
  color: inherit;
  text-decoration: none;
}

.kb-group-head h2 a:hover,
.kb-group-head h2 a:focus-visible {
  text-decoration: underline;
  text-decoration-color: var(--rust-600);
  text-underline-offset: 3px;
}

.kb-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border);
}

.kb-item {
  border-bottom: 1px solid var(--border);
}

/* The whole row is the target. A help centre is scanned with a cursor
   already moving down the page, and a link that only covers the title
   makes people aim. */
/* No left padding: the article title has to start on the same edge as the
   category icon and description above it. The hover indent is done with a
   transform so it can't shift that edge in the resting state. */
.kb-item-link {
  display: block;
  padding: 14px 0;
  text-decoration: none;
  color: inherit;
  transition: transform 0.15s ease, color 0.15s ease;
}

.kb-item-link:hover,
.kb-item-link:focus-visible {
  transform: translateX(5px);
}

.kb-item-link:hover .kb-item-title,
.kb-item-link:focus-visible .kb-item-title {
  color: var(--rust-700);
}

@media (prefers-reduced-motion: reduce) {
  .kb-item-link {
    transition: color 0.15s ease;
  }

  .kb-item-link:hover,
  .kb-item-link:focus-visible {
    transform: none;
  }
}

.kb-item-title {
  display: block;
  font-weight: 600;
  color: var(--green-900);
  font-size: 0.99rem;
  line-height: 1.4;
}

.kb-item-excerpt {
  display: block;
  margin-top: 4px;
  color: var(--ink-muted);
  font-size: 0.88rem;
  line-height: 1.6;
}

.kb-list-wide {
  max-width: 780px;
}

.kb-crumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 9px;
  align-items: center;
  margin-bottom: 18px;
  font-size: 0.88rem;
  color: var(--ink-muted);
}

.kb-crumbs a {
  color: var(--ink-muted);
  text-decoration: none;
}

.kb-crumbs a:hover,
.kb-crumbs a:focus-visible {
  color: var(--green-900);
  text-decoration: underline;
}

.kb-article-title {
  font-family: var(--font-display, inherit);
  font-size: clamp(1.9rem, 4.2vw, 2.7rem);
  line-height: 1.14;
  margin: 0 0 16px;
  max-width: 20ch;
}

.kb-article-date {
  margin: 18px 0 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

.kb-article-layout {
  display: grid;
  gap: 40px;
}

@media (min-width: 940px) {
  .kb-article-layout {
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 56px;
    align-items: start;
  }
}

/* A measured column. Help articles are read start to finish far more often
   than marketing copy is, and a 65-character measure is the difference
   between reading and skimming. */
/* `--ink` was never declared, so this resolved to nothing and the body of
   every help article fell back to the inherited colour. It happened to
   look close enough to be missed. The palette's text colour is
   `--green-900`. */
.kb-prose {
  max-width: 68ch;
  color: var(--green-900);
  font-size: 1.02rem;
  line-height: 1.75;
}

.kb-prose>*+* {
  margin-top: 1.1em;
}

.kb-prose h2 {
  margin-top: 2.4em;
  margin-bottom: 0.6em;
  font-size: 1.32rem;
  line-height: 1.3;
  color: var(--green-900);
}

.kb-prose h2:first-child {
  margin-top: 0;
}

.kb-prose h3 {
  margin-top: 1.9em;
  margin-bottom: 0.5em;
  font-size: 1.08rem;
  color: var(--green-900);
}

.kb-prose ul,
.kb-prose ol {
  padding-left: 1.35em;
}

.kb-prose li+li {
  margin-top: 0.5em;
}

.kb-prose a {
  color: var(--rust-700);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.kb-prose strong {
  color: var(--green-900);
}

.kb-prose code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.88em;
  padding: 2px 6px;
  border-radius: 5px;
  background: rgb(26 53 39 / 7%);
}

.kb-prose table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.94rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}

.kb-prose th,
.kb-prose td {
  text-align: left;
  padding: 11px 15px;
  border-bottom: 1px solid var(--border);
}

.kb-prose thead th {
  background: var(--paper-tint);
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
}

.kb-prose tbody tr:last-child td {
  border-bottom: 0;
}

.kb-prose blockquote {
  margin-left: 0;
  padding-left: 18px;
  border-left: 3px solid var(--border-strong);
  color: var(--ink-muted);
}

.kb-aside {
  display: grid;
  gap: 18px;
}

@media (min-width: 940px) {
  .kb-aside {
    position: sticky;
    top: 96px;
  }
}

.kb-aside-card {
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 22px 24px;
  background: #fff;
}

.kb-aside-card h2 {
  margin: 0 0 12px;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-muted);
}

.kb-aside-card ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.kb-aside-card li+li {
  margin-top: 10px;
}

/* `:not(.btn)` matters: this rule is (0,0,2,0) and `.btn` is (0,0,1,0), so
   without it the sidebar's "Get in touch" button inherited dark green text
   on its dark green fill and was effectively unreadable. Scoping the link
   colour to real links rather than to everything anchor-shaped fixes it at
   the source instead of adding an override further down the file. */
.kb-aside-card a:not(.btn) {
  color: var(--green-900);
  text-decoration: none;
  font-size: 0.92rem;
  line-height: 1.5;
}

.kb-aside-card a:not(.btn):hover,
.kb-aside-card a:not(.btn):focus-visible {
  text-decoration: underline;
  text-decoration-color: var(--rust-600);
  text-underline-offset: 2px;
}

/* `flex`, not `block`. `.btn` carries `padding: 0 28px` and gets its
   vertical centring entirely from `align-items: center` against its
   `min-height: 44px` -- so switching it to `display: block` to make it
   full width dropped the flex context and pinned the label to the top of
   the box. `display: flex` fills the sidebar just as well and leaves the
   centring `.btn` already does intact. */
.kb-aside-cta .btn {
  display: flex;
  width: 100%;
}

.kb-aside-cta {
  background: var(--paper-tint);
}

.kb-aside-cta p {
  margin: 0 0 16px;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

.kb-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.kb-tag {
  font-size: 0.76rem;
  padding: 5px 11px;
  border-radius: 999px;
  background: var(--paper-tint);
  border: 1px solid var(--border);
  color: var(--ink-muted);
}

/* --- admin ---------------------------------------------------------- */

.kb-editor-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
  font-size: 0.86rem;
}

.kb-editor-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 22px;
}

.kb-tag-admin {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.kb-tag-delete {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font: inherit;
  font-size: 0.82rem;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--paper-tint);
  color: var(--ink-muted);
  cursor: pointer;
}

.kb-tag-delete:hover {
  border-color: rgb(156 78 45 / 40%);
  color: var(--rust-700);
}

.admin-row-actions {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* `.kb-group` is the unit `knowledgebase.js` hides when every article
   inside it is filtered out -- a heading left standing over an empty list
   reads as a section that failed to load. It needs no visual rules of its
   own (the `.kb-groups` grid handles spacing), but the JS depends on the
   class existing, so it is documented here rather than looking like a
   stray class in the markup. */
.kb-group {
  min-width: 0;
}

/* An article's header and body are consecutive bands, so the default
   section padding doubles up in the middle. These trim the facing edges
   without touching the outer spacing. */
.kb-article-top {
  padding-bottom: clamp(24px, 3vw, 36px);
}

.kb-article-body {
  padding-top: clamp(32px, 4vw, 52px);
}

/* The hero pill sat directly on the h1 -- the other marketing heroes get
   their spacing from `marketing::heading`'s kicker, which this page
   doesn't use because the pill carries the guide count instead. Scoped to
   the help centre rather than added to `.pill` itself, which appears in a
   dozen other contexts that are spaced correctly already. */
.kb-hero-inner .pill {
  margin-bottom: 22px;
}

/* Admin cards on the knowledgebase screens were rendered as consecutive
   siblings with no wrapper, so they sat flush against each other. A grid
   with a gap rather than margins on `.admin-card` itself: that class is
   also used inside grids elsewhere, where a margin would double up with
   the existing gap. */
/* `gap` spaces the cards from each other but says nothing about what sits
   above the stack, and on three of the four knowledgebase screens that is
   a summary grid or a notice with no bottom margin of its own -- so the
   first card sat flush against it. The margin belongs here rather than on
   the things above, which appear on other screens with their own spacing
   already correct. */
.kb-admin-stack {
  display: grid;
  gap: 24px;
  margin-top: 24px;
}

/* Sub-navigation across the three knowledgebase admin screens. Articles,
   categories and tags were one very long page; they are three pages now
   and this is what makes that legible as a group rather than as three
   unrelated destinations. */
.kb-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 24px;
  padding: 5px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--paper-tint);
  width: fit-content;
}

.kb-tab {
  padding: 8px 18px;
  border-radius: 8px;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink-muted);
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}

.kb-tab:hover {
  color: var(--green-900);
}

.kb-tab.active {
  background: var(--paper-raised);
  color: var(--green-900);
  box-shadow: 0 1px 3px rgb(26 53 39 / 12%);
}

.kb-tab-count {
  margin-left: 7px;
  font-variant-numeric: tabular-nums;
  opacity: 0.65;
}

/* The delete card is the last thing on the editor and sits outside the
   stacked form above it, so it needs its own separation. */
.kb-editor-danger {
  margin-top: 24px;
}

/* ==========================================================================
   Site page: primary actions
   ==========================================================================
   The row directly under the header on a site's own page. These are the
   three things someone actually opened the page to do, so they get real
   estate at the top rather than being tiles four rows down.

   Whole-card links rather than a card containing a button: there is exactly
   one destination per card, so making the card itself the target removes a
   click-accuracy problem for no cost. */
.site-quick-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  margin: 20px 0 28px;
}

.quick-action {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--transition), box-shadow var(--transition),
    transform var(--transition);
}

.quick-action:hover,
.quick-action:focus-visible {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-lift);
  transform: translateY(-1px);
}

.quick-action-title {
  font-weight: 600;
  font-size: 1rem;
  color: var(--green-900);
}

.quick-action-note {
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--ink-muted);
}

/* The one card that is the reason the subscription exists. Tinted rather
   than filled: a solid rust card here would out-shout the status banners
   directly above it, which are the only thing that should ever be louder
   than this. */
.quick-action.is-primary {
  border-color: rgb(156 78 45 / 34%);
  background: var(--rust-tint);
}

.quick-action.is-primary:hover,
.quick-action.is-primary:focus-visible {
  border-color: var(--rust-600);
}

.quick-action.is-primary .quick-action-title {
  color: var(--rust-700);
}

@media (prefers-reduced-motion: reduce) {

  .quick-action:hover,
  .quick-action:focus-visible {
    transform: none;
  }
}

/* Replaces the full five-step tracker once the site is live and nothing is
   outstanding -- see the comment at its render site. One line instead of a
   card, so the reassurance survives without the furniture. */
.site-progress-done {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 28px;
  padding: 14px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-tint);
  color: var(--ink-muted);
  font-size: 0.92rem;
}

.site-progress-tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--green-700);
  color: var(--paper-raised);
  font-size: 0.72rem;
}

/* ==================================================================== */
/* Enquiries                                                            */
/*                                                                      */
/* The inbox for a site's contact form -- see the `enquiries` module.    */
/* Built as a list of rows rather than a table: every row has the same   */
/* four things in it, and a table's column headings would be labelling   */
/* "who", "when" and "what they said", which the content already says.   */
/* ==================================================================== */

.tabs {
  display: flex;
  gap: 4px;
  margin: 0 0 18px;
  border-bottom: 1px solid var(--border);
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 14px;
  border-bottom: 2px solid transparent;
  color: var(--ink-muted);
  font-size: 0.88rem;
  font-weight: 500;
  text-decoration: none;
  transition: color var(--transition), border-color var(--transition);
}

.tab:hover {
  color: var(--rust-700);
}

.tab.is-current {
  color: var(--green-900);
  border-bottom-color: var(--rust-500);
  font-weight: 600;
}

.tab-count {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--paper-sunken);
  font-size: 0.72rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.enquiry-list {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  overflow: clip;
}

.enquiry-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  color: inherit;
  text-decoration: none;
  transition: background-color var(--transition);
}

.enquiry-row:last-child {
  border-bottom: 0;
}

.enquiry-row:hover {
  background: var(--paper-tint);
}

/* Unread is carried by weight and a dot, not by a background tint. A row
   that changes colour when read makes the list look like it has two kinds
   of thing in it; this way the list has one kind of thing, some of which
   you haven't opened. */
.enquiry-row.is-unread .enquiry-row-name {
  font-weight: 700;
}

.enquiry-row.is-unread .enquiry-row-preview {
  color: var(--green-800);
}

.enquiry-row-mark {
  flex: 0 0 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  border-radius: 50%;
  background: var(--paper-sunken);
  color: var(--green-800);
  font-size: 0.8rem;
  font-weight: 600;
}

.enquiry-row-body {
  flex: 1;
  min-width: 0;
}

.enquiry-row-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 3px;
}

.enquiry-row-name {
  font-size: 0.94rem;
  font-weight: 600;
  color: var(--green-900);
}

.enquiry-row-time {
  flex-shrink: 0;
  color: var(--ink-faint);
  font-size: 0.78rem;
}

/* One line, clipped. The full message is one click away, and a list whose
   rows are different heights is much harder to scan than one whose rows
   are not. */
.enquiry-row-preview {
  display: block;
  overflow: hidden;
  color: var(--ink-muted);
  font-size: 0.86rem;
  line-height: 1.5;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.enquiry-row-dot {
  flex: 0 0 8px;
  align-self: center;
  height: 8px;
  border-radius: 50%;
  background: var(--rust-500);
}

.enquiry-card {
  margin-top: 4px;
}

/* The reply routes, first. Someone opening an enquiry has already decided
   to answer it; the message underneath is what they're answering. */
.enquiry-contact {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 20px;
}

.enquiry-contact-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--green-900);
  text-decoration: none;
  transition: border-color var(--transition), background-color var(--transition);
}

.enquiry-contact-item:hover {
  border-color: var(--rust-400);
  background: var(--paper-tint);
}

.enquiry-contact-label {
  color: var(--ink-faint);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* `pre-wrap`, so the paragraph breaks someone typed are the paragraph
   breaks they get. The message is a stranger's text and is rendered as
   text -- no markdown, no linkification, nothing that interprets it. */
.enquiry-message {
  color: var(--green-900);
  font-size: 0.98rem;
  line-height: 1.65;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.enquiry-extras {
  display: grid;
  grid-template-columns: minmax(120px, max-content) 1fr;
  gap: 8px 20px;
  margin: 24px 0 0;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

.enquiry-extras dt {
  color: var(--ink-faint);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.enquiry-extras dd {
  margin: 0;
  color: var(--green-900);
  font-size: 0.92rem;
  overflow-wrap: anywhere;
}

.enquiry-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 18px;
}

.enquiry-actions form {
  margin: 0;
}

@media (max-width: 600px) {

  .enquiry-row-top {
    flex-direction: column;
    gap: 2px;
  }

  .enquiry-contact-item {
    flex: 1 1 100%;
  }
}

/* ==================================================================== */
/* Visitors                                                             */
/*                                                                      */
/* The page-view screen. The chart is inline SVG with no script -- the   */
/* CSP allows no inline script and no external assets, so a charting     */
/* library would mean a build step and a CDN exception for thirty        */
/* rectangles. It also means the chart prints, and works with JS off.    */
/* ==================================================================== */

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
  margin-bottom: 22px;
}

.stat-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.stat-label {
  color: var(--ink-muted);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.stat-value {
  color: var(--green-900);
  font-family: var(--font-display);
  font-size: 2.1rem;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

/* Direction is carried by an arrow as well as colour, so the comparison
   still reads for someone who can't distinguish the two. */
.stat-change {
  font-size: 0.8rem;
  font-weight: 500;
}

.stat-change.is-up {
  color: var(--green-700);
}

.stat-change.is-down {
  color: var(--rust-700);
}

.stat-change.is-flat {
  color: var(--ink-muted);
}

.chart-card,
.top-pages-card {
  margin-bottom: 18px;
}

.chart-title {
  margin: 0 0 16px;
  font-size: 1rem;
  font-weight: 600;
  color: var(--green-900);
}

/* `preserveAspectRatio: none` on the SVG plus a fixed height here is what
   lets the chart stretch to the column's width without the bars changing
   shape -- they're rectangles, so distortion along one axis is invisible,
   which is exactly why a bar chart suits a fluid container. */
.views-chart {
  display: block;
  width: 100%;
  height: 160px;
}

.views-bar {
  fill: var(--rust-400);
}

/* Days with no traffic keep a one-unit stub, drawn in the border colour
   so the axis reads as a row of days rather than stopping wherever the
   traffic did. */
.views-bar.is-empty {
  fill: var(--border-strong);
}

.top-pages {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.top-page {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 120px auto;
  align-items: center;
  gap: 14px;
}

.top-page-path {
  overflow: hidden;
  color: var(--green-900);
  font-size: 0.88rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.top-page-bar {
  height: 8px;
  border-radius: 999px;
  background: var(--paper-sunken);
  overflow: hidden;
}

.top-page-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--rust-400);
}

.top-page-count {
  color: var(--ink-muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 600px) {

  /* The proportion bar is the first thing to go: the number beside it says
     the same thing exactly, and the path is what someone came to read. */
  .top-page {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .top-page-bar {
    display: none;
  }
}

/* ==================================================================== */
/* Activity timeline                                                    */
/*                                                                      */
/* The History screen's main section -- see the `activity` module. Drawn */
/* as a rail with markers rather than a table, because the thing being   */
/* shown is a sequence, and a table's rows carry no sense of order       */
/* beyond the one you happen to have sorted them into.                   */
/* ==================================================================== */

/* No bottom margin: `.dashboard-card` is a flex column with its own 12px
   gap, so a margin here would add to it rather than replace it -- the
   heading sat 28px off its own content while the two cards below had no
   gap between them at all. The gap owns the spacing; the heading owns
   none of it. */
.history-section-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--green-900);
}

/* Same reasoning -- the flex gap already separates this from the empty
   state above it. */
.history-section-action {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 4px;
}

@media (max-width: 460px) {
  .history-section-action,
  .history-section-action .btn {
    width: 100%;
  }
}

.activity {
  margin: 0;
  padding: 0;
  list-style: none;
}

.activity-day {
  margin: 18px 0 10px;
  color: var(--ink-faint);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.activity-day:first-child {
  margin-top: 0;
}

/* The rail is a border on the entry rather than a separate element, so
   there is nothing to keep in step with the row's height. */
.activity-entry {
  position: relative;
  display: flex;
  gap: 14px;
  padding: 0 0 18px 0;
  margin-left: 5px;
  border-left: 2px solid var(--border);
}

/* The last entry's rail stops at its own marker instead of running on
   into empty space below the list. */
.activity-entry:last-child {
  border-left-color: transparent;
}

.activity-mark {
  position: absolute;
  left: -6px;
  top: 3px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--paper-raised);
  background: var(--ink-faint);
}

.activity-entry.is-milestone .activity-mark {
  background: var(--rust-500);
}

.activity-entry.is-problem .activity-mark {
  background: var(--rust-700);
}

.activity-body {
  padding-left: 16px;
  min-width: 0;
}

.activity-headline {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}

.activity-headline strong {
  color: var(--green-900);
  font-size: 0.94rem;
  font-weight: 600;
}

/* Who did it, as a quiet pill. This is the distinction the screen exists
   to draw -- what you asked for against what we did -- so it is always
   present, but it is chrome next to the sentence rather than competing
   with it. */
.activity-actor {
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  background: var(--paper-sunken);
  color: var(--ink-muted);
}

.activity-actor.is-customer {
  background: var(--rust-tint);
  color: var(--rust-700);
}

.activity-subject {
  margin: 4px 0 0;
  color: var(--ink-muted);
  font-size: 0.87rem;
  line-height: 1.5;
  overflow-wrap: anywhere;
}

.activity-time {
  display: block;
  margin-top: 4px;
  color: var(--ink-faint);
  font-size: 0.78rem;
}

/* The delete-account card. Marked out rather than styled loudly: a red
   panel shouting at someone reading their profile is alarming for no
   reason, but the one control here that can't be undone should not look
   like the two above it that can. */
.profile-card.is-destructive {
  border-color: var(--rust-300);
}

.profile-card.is-destructive h2 {
  color: var(--rust-700);
}

.session-list,
.article-category-list,
.article-image-library {
  display: grid;
  gap: 12px;
  margin-top: var(--card-gap);
}

.session-row,
.article-category-row,
.article-image-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 0;
  border-top: 1px solid var(--line);
}

.session-row-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.session-row-main p,
.session-row-main small {
  display: block;
  margin: 4px 0 0;
}

.article-category-manager > summary {
  cursor: pointer;
  font-weight: 700;
}

/* The article table supplies its own section gutter, but the category
   manager is another card and therefore has no global margin. Keep the
   three article-library blocks on one consistent vertical rhythm instead
   of letting the first two rounded cards touch. */
.article-category-manager {
  margin-top: var(--card-gap);
}

.article-image-row img {
  width: 72px;
  height: 54px;
  border-radius: var(--radius);
  object-fit: cover;
}

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: var(--card-gap);
}

@media (max-width: 42rem) {
  .session-row,
  .article-category-row,
  .article-image-row {
    align-items: stretch;
    flex-direction: column;
  }
}

/* ==================================================================== */
/* Features page                                                        */
/*                                                                      */
/* Its own grid rather than the existing `.feature-grid`, which is a     */
/* fixed three columns used on other marketing pages -- two of these     */
/* sections have four items, and widening the shared one to suit would   */
/* have changed every page that already uses it.                        */
/* ==================================================================== */

.features-grid {
  display: grid;
  /* Auto-fit rather than a column count, so a section of three and a
     section of four both fill their row instead of one leaving a hole. */
  grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
  gap: 20px;
}

.feature-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 26px 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.feature-card h3 {
  margin: 0;
  font-size: 1.02rem;
  line-height: 1.3;
}

.feature-card p {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.92rem;
  line-height: 1.6;
}

/* Tinted disc rather than a bare glyph. At this size a stroke icon on
   paper reads as an artefact; on its own surface it reads as a mark. */
.feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background: var(--rust-tint);
  color: var(--rust-700);
}

.feature-icon svg {
  width: 20px;
  height: 20px;
}

/* The panel's heading: which area you are in, in words.

   The rail says the same thing with a highlight, which is enough once you
   know the layout and not enough on a first visit. It sits above the site
   switcher rather than replacing it -- "Website" is the area, the switcher
   names which one. */
.app-panel-title {
  margin: 0 0 2px;
  padding: 0 10px;
  color: var(--green-900);
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* The rail's foot: the one item that leaves the area it sits in -- admin's
   way back to the customer app. `margin-top: auto` against the rail's
   column flex pins it down there without a fixed height. */
.app-rail-foot {
  margin-top: auto;
  width: 100%;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

/* The admin panel carries a search trigger the customer one doesn't, so it
   needs a little more room at the top than a list of links alone. */
.app-panel-inner.is-admin .admin-search-trigger {
  width: 100%;
  margin-bottom: 4px;
}

/* The domain and preview link under the site's identity block. Part of
   `.app-site` rather than the nav below it: all three answer "which site
   am I looking at", and separating them put the address further from the
   name than from the first nav item. */
.app-site-domain {
  margin: 6px 0 0;
  padding: 0 4px;
  color: var(--ink-muted);
  font-size: 0.82rem;
  overflow-wrap: anywhere;
}

.app-site-preview {
  display: inline-block;
  margin-top: 8px;
  padding: 0 4px;
  color: var(--rust-700);
  font-size: 0.82rem;
  font-weight: 600;
  text-decoration: none;
}

.app-site-preview:hover {
  text-decoration: underline;
}

/* Buttons at the foot of a panel -- things that change what the site has,
   as opposed to places to go. Separated by a rule so they don't read as
   two more nav items until you press one. */
.app-panel-actions {
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.app-panel-actions form {
  margin: 0;
}

/* A trailing badge on a nav row -- a page's review status, a domain count.
   Pushed to the end so the labels stay left-aligned however long the
   badges are. */
.app-nav-item .status-pill {
  margin-left: auto;
  flex-shrink: 0;
}

/* ==================================================================== */
/* The setup form's sections                                            */
/*                                                                      */
/* These were flattened to hairline-separated sections for a while, on   */
/* the theory that four boxes read as four tasks. They do -- but eight   */
/* fields with nothing around them read as one undifferentiated run, and */
/* a hairline is too weak to hold a group that size together. The boxes  */
/* were doing real work; the problem was how loud they were, not that    */
/* they existed.                                                        */
/*                                                                      */
/* So: same grouping, quieter. No shadow, less padding, less air between */
/* them. A stack of shadowed panels is what made the screen feel like    */
/* several floating things rather than one form in parts.                */
/* ==================================================================== */

.setup-form .card.manage-card {
  margin: 18px 0 0;
  padding: clamp(20px, 4vw, 28px);
  box-shadow: none;
}

/* Was `0 0 4px`, which was right when a lead paragraph sat underneath and
   supplied the gap. The fields sit directly under the heading now, so it
   has to carry that space itself. */
.setup-form .card.manage-card>h2 {
  margin: 0 0 16px;
  font-size: 1.02rem;
  font-weight: 600;
}

/* Quieter, and out of the flow of the form. It says the same thing on
   every screen, so it should not be the second most prominent thing on
   any of them. */
.wizard-saved-note {
  margin: 14px 0 0;
  color: var(--ink-faint);
  font-size: 0.8rem;
}

/* ---------- Features page: the included list ----------

   Rows with a hairline between them, not cards. These are things that are
   simply covered, and a row reads as an item on a list of what's included
   where a card reads as a feature being sold. The page has a card grid and
   a bento block already; a third box treatment would have made all three
   sections the same shape again, which is what this rewrite was fixing. */

.included-list {
  display: flex;
  flex-direction: column;
}

.included-row {
  display: flex;
  align-items: flex-start;
  gap: 18px;
  padding: 24px 0;
  border-bottom: 1px solid var(--border);
}

.included-row:first-child {
  padding-top: 0;
}

/* The rule falls *between* rows, so the last one doesn't draw a line
   against the section's own edge. */
.included-row:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.included-row h3 {
  margin: 0 0 6px;
  font-size: 1.05rem;
}

.included-row p {
  margin: 0;
  max-width: 62ch;
  color: var(--ink-muted);
  line-height: 1.65;
}

@media (max-width: 600px) {

  .included-row {
    gap: 14px;
    padding: 20px 0;
  }
}

/* ---------- Requests: progress, not a status word ----------

   A pill tells you the state; it does not tell you what comes next or how
   much of the wait is left, and this is the screen somebody opens because
   they are waiting. The track answers both. Cancelled requests get no
   track -- see `sites::views::requests::progress` for why drawing two
   thirds of a bar for something that will never finish is a lie. */

.request-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin: 24px 0;
}

/* Quota and progress are context for the request list, not three competing
   feature cards. Present them as one compact strip. */
.request-stats .stat-row {
  display: flex;
  gap: 0;
  margin-bottom: 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  overflow: hidden;
}

.request-stats .stat-card {
  flex: 1 1 0;
  min-width: 0;
  padding: 14px 18px;
  border: 0;
  border-left: 1px solid var(--border);
  border-radius: 0;
  background: transparent;
}

.request-stats .stat-card:first-child {
  border-left: 0;
}

.request-stats .stat-value {
  font-size: 1.75rem;
}

.request-limit-note {
  margin-top: 12px;
  text-align: center;
}

/* The correction option is a label with two lines of meaning. Without a
   layout rule it renders as one continuous inline sentence and can collide
   with the submit button below it. */
.revision-request-card {
  max-width: 820px;
}

.revision-request-card form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: min(100%, 760px);
}

.revision-request-card form > .form-field {
  width: 100%;
}

.check-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  margin: 2px 0 20px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-tint);
  color: var(--green-900);
  cursor: pointer;
}

.check-row input[type="checkbox"] {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
  accent-color: var(--rust-600);
}

.check-row span {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.check-row small {
  display: block;
  color: var(--ink-muted);
  font-size: 0.84rem;
  font-weight: 400;
  line-height: 1.45;
}

@media (max-width: 600px) {
  .request-stats .stat-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .request-stats .stat-card {
    padding: 12px;
  }

  .request-stats .stat-label {
    font-size: 0.68rem;
  }
}

.request-list {
  display: grid;
  gap: 18px;
  margin-top: 24px;
}

.request-card {
  background: var(--paper-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 22px 24px;
}

/* The one that is still moving is the one worth finding on a long list. */
.request-card.request-in_progress {
  border-color: var(--green-700);
}

.request-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.request-card-head h3 {
  margin: 0;
  font-size: 1.05rem;
}

.request-card-head p {
  margin: 4px 0 0;
  font-size: 0.85rem;
}

.request-track {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  margin: 20px 0;
  padding: 0;
  list-style: none;
  counter-reset: none;
}

.request-step {
  position: relative;
  padding-top: 22px;
  font-size: 0.85rem;
  color: var(--ink-faint);
}

.request-step strong {
  display: block;
  font-weight: 600;
  font-size: 0.9rem;
}

.request-step span {
  font-size: 0.78rem;
}

/* The connecting rule sits behind the dots rather than between them, so a
   three-step track reads as one line with markers on it instead of three
   separate badges. Drawn on the step rather than the list because each
   step needs to colour its own segment. */
.request-step::before {
  content: "";
  position: absolute;
  top: 6px;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--border);
}

.request-step:first-child::before {
  left: 6px;
}

.request-step:last-child::before {
  right: calc(100% - 8px);
}

.request-step-dot {
  position: absolute;
  top: 0;
  left: 0;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--border);
  background: var(--paper-raised);
}

.request-step.is-done::before,
.request-step.is-current::before {
  background: var(--green-700);
}

.request-step.is-done .request-step-dot,
.request-step.is-current .request-step-dot {
  border-color: var(--green-700);
}

.request-step.is-done .request-step-dot {
  background: var(--green-700);
}

.request-step.is-done,
.request-step.is-current {
  color: var(--green-900);
}

/* The step being worked on right now gets the ring, so the eye lands on
   "where is it" rather than on "where has it been". */
.request-step.is-current .request-step-dot {
  box-shadow: 0 0 0 4px rgb(58 92 71 / 18%);
}

.request-body {
  margin-top: 4px;
}

/* The customer's own words, set apart from our labels around them. */
.request-quote {
  margin: 0 0 8px;
  padding-left: 14px;
  border-left: 3px solid var(--border-strong);
  white-space: pre-wrap;
}

@media (max-width: 640px) {

  .request-track {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
  }

  /* Stacked, the horizontal rule between steps means nothing. */
  .request-step::before {
    display: none;
  }

  .request-step {
    padding-top: 0;
    padding-left: 26px;
  }
}

/* ---------- Conversions: what visitors did about it ---------- */

.conversions-card {
  margin-top: 20px;
}

.conversions-head p {
  margin: 4px 0 0;
  font-size: 0.88rem;
}

.conversions-figures {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 20px;
}

.conversion-figure {
  flex: 1 1 150px;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-tint);
}

.conversion-figure strong {
  display: block;
  font-family: var(--font-display);
  font-size: 1.9rem;
  line-height: 1.05;
  color: var(--green-900);
}

.conversion-figure span {
  display: block;
  margin-top: 4px;
  font-size: 0.82rem;
  color: var(--ink-muted);
}

/* The rate is the number that changes a decision, so it is the one that
   gets the accent. The two counts feeding it stay quiet. */
.conversion-rate {
  background: var(--rust-tint);
  border-color: transparent;
}

.conversion-rate strong {
  color: var(--rust-700);
}

.conversions-note {
  margin: 18px 0 0;
  font-size: 0.9rem;
  line-height: 1.6;
}

.conversions-caveat {
  margin: 14px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  font-size: 0.82rem;
  line-height: 1.55;
}

/* ---------- Backups ---------- */

.backup-list {
  display: grid;
  gap: 12px;
  margin-top: 20px;
}

.backup-row {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 16px 18px;
  background: var(--paper-raised);
}

.backup-row-failed {
  border-color: var(--rust-500);
  background: var(--rust-tint);
}

.backup-row-main {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.backup-name {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.88rem;
  color: var(--green-900);
}

.backup-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  font-size: 0.82rem;
}

.backup-when {
  font-size: 0.82rem;
  white-space: nowrap;
}

.backup-error {
  margin: 12px 0 0;
  padding: 10px 12px;
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--rust-700);
  font-size: 0.84rem;
  line-height: 1.5;
}

/* Restore is behind a disclosure rather than a button: it is the one
   destructive action on this screen, and it should cost a deliberate extra
   click for the same reason site removal moved off the sites table. */
.backup-restore {
  margin-top: 14px;
  border-top: 1px solid var(--border);
  padding-top: 12px;
}

.backup-restore summary {
  cursor: pointer;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--rust-700);
}

.backup-restore-body {
  margin-top: 14px;
  max-width: 520px;
}

.admin-callout-warn {
  border-color: var(--rust-500);
  background: var(--rust-tint);
}

/* ---------- Dashboard overview: the control panel ---------- */

.dashboard-overview {
  background: var(--paper-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 24px 26px 26px;
  margin-bottom: 28px;
}

.overview-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 22px;
}

.overview-head h2 {
  margin: 0;
  font-size: 1.15rem;
}

.overview-head p {
  margin: 4px 0 0;
  font-size: 0.88rem;
}

.overview-filters {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}

.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--paper);
}

/* Links, not buttons -- the filter is a navigation, so it should look and
   behave like one (middle-click, bookmark, back). See
   `pages::views::overview`. */
.filter-pill {
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--ink-muted);
  white-space: nowrap;
  transition: background-color var(--transition), color var(--transition);
}

.filter-pill:hover {
  background: var(--paper-tint);
  color: var(--green-900);
}

.filter-pill.is-current {
  background: var(--green-900);
  color: var(--paper);
}

.filter-pill.is-current:hover {
  background: var(--green-900);
  color: var(--paper);
}

.overview-chart {
  margin-top: 22px;
}

.overview-caption {
  margin: 10px 0 0;
  font-size: 0.82rem;
}

.overview-empty,
.overview-note {
  margin: 20px 0 0;
  padding: 14px 16px;
  border-radius: var(--radius-md);
  background: var(--paper-tint);
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--ink-muted);
}

@media (max-width: 720px) {

  .overview-head {
    flex-direction: column;
  }

  /* Ranged left with the heading once stacked; right-aligned pills under
     a left-aligned title read as belonging to nothing. */
  .overview-filters {
    align-items: flex-start;
    width: 100%;
  }
}

/* ---------- Enquiry replies ---------- */

.enquiry-reply-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 14px;
}

.enquiry-reply-head strong {
  font-size: 1rem;
  color: var(--green-900);
}

.enquiry-reply-head time {
  font-size: 0.82rem;
  color: var(--ink-faint);
}

/* A sent reply is history, not an action, so it sits back rather than
   competing with the enquiry it answers. */
.enquiry-reply-sent {
  background: var(--paper-tint);
}

.enquiry-reply-note {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 0.82rem;
  line-height: 1.55;
}

/* ---------- Landing builder: a CMS, not a JSON editor ---------- */

.landing-block-fields {
  display: grid;
  gap: 14px;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

.landing-block-field {
  margin: 0;
}

/* The picker: grouped, with each option's description visible. A
   thirty-item dropdown hid both. */
.landing-block-group {
  border-bottom: 1px solid var(--border);
}

.landing-block-group:last-of-type {
  border-bottom: 0;
}

.landing-block-group>summary {
  cursor: pointer;
  padding: 10px 2px;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--green-900);
}

.landing-block-group>summary:hover {
  color: var(--rust-700);
}

.landing-block-option {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 12px;
  margin-bottom: 4px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: transparent;
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition);
}

.landing-block-option:hover {
  background: var(--paper-tint);
  border-color: var(--border);
}

.landing-block-option strong {
  display: block;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--green-900);
}

.landing-block-option span {
  display: block;
  margin-top: 2px;
  font-size: 0.76rem;
  line-height: 1.45;
  color: var(--ink-muted);
}


/* ---------- Domain transfer out ---------- */

/* One domain's block inside the "Moving a domain away" card. Separated by
   a rule rather than by a border on each: a customer usually has one
   domain, and a single item in its own bordered box inside an already
   bordered card is two frames around one thing. */
.transfer-domain+.transfer-domain {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}

.transfer-domain-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 6px;
}

/* The auth code. Given weight out of proportion to its size on purpose --
   it is the one thing on the page the customer came for, it has to be
   copied character-for-character, and every character has to be
   distinguishable. Hence the monospace, the letter-spacing and the
   selectable block rather than a run of body text. */
.transfer-code {
  margin: 4px 0 16px;
  padding: 14px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-sunken);
  /* Long codes must wrap rather than overflow the card on a phone. */
  overflow-wrap: anywhere;
}

.transfer-code code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.05rem;
  letter-spacing: 0.06em;
  color: var(--green-900);
  /* Triple-click selects the whole code and nothing else. */
  user-select: all;
}


/* The transfer-out card on the admin domains screen, which follows either
   the domain list or the empty-state card. Neither carries a bottom margin
   and `.card` carries no top one, so this is the seam. Same `--card-gap`
   as `.admin-overview-grid~.card` for the same reason: the separation
   should match the spacing between every other pair of cards. */
.admin-domains-transfer {
  margin-top: var(--card-gap, 20px);
}


/* A single column of admin cards on a screen that isn't the two-column
   overview grid. `.card` carries no margin by design -- every other admin
   screen showing several puts them in `.admin-overview-grid` or
   `.admin-overview-column`, both of which space their own children -- so a
   plain run of them sits edge to edge. Used by the domain transfer detail
   screen, where the number of cards varies with the transfer's state and
   an adjacent-sibling rule would space the wrong pairs. */
.admin-card-stack {
  display: flex;
  flex-direction: column;
  gap: var(--card-gap, 20px);
  min-width: 0;
}

/* The gap is the only spacing between these children, so anything that
   carries a vertical margin of its own has it removed here.

   Flex does not collapse margins, so without this a child with 24px of its
   own margin gets that *and* the 20px gap -- and the seam between a notice
   and a card would be a different size from the seam between two cards, on
   the same screen, for no reason a reader could see. This started as three
   selectors naming the flash banners; it is every direct child now,
   because the next thing dropped into a stack has the same problem and
   nobody remembers a list. */
.admin-card-stack>* {
  margin-top: 0;
  margin-bottom: 0;
}


/* ---------- Admin: the home screen ---------- */

/* Only the queues with something in them, as pills. Wraps rather than
   grids: the row is one item long on a normal day and six on a bad one,
   and a grid would leave four empty columns of nothing on the good days
   this screen should feel quiet on. */
.admin-dash-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 20px 0 24px;
}

.admin-dash-pill {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 16px 8px 13px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--paper-raised);
  color: var(--green-900);
  text-decoration: none;
  transition: border-color var(--transition), background var(--transition);
}

.admin-dash-pill strong {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 500;
  line-height: 1;
}

.admin-dash-pill span {
  font-size: 0.88rem;
  color: var(--ink-muted);
}

.admin-dash-pill:hover,
.admin-dash-pill:focus-visible {
  border-color: var(--border-strong);
  background: var(--paper-tint);
}

/* Something is actually broken -- a failing job, a lapsed payment -- as
   opposed to a queue that merely has work in it. Kept to those two so the
   colour still means something when it appears. */
.admin-dash-pill.is-fault {
  border-color: var(--rust-400);
  background: var(--rust-tint);
  color: var(--rust-700);
}

.admin-dash-pill.is-fault span {
  color: var(--rust-700);
}

.admin-dash-pill.is-fault:hover,
.admin-dash-pill.is-fault:focus-visible {
  border-color: var(--rust-500);
  background: var(--rust-tint);
}

/* The "showing 5 of 90" line. Sits under the card head, above the panel's
   own description, so it needs to pull up against the heading rather than
   sit in the middle of the gap. */
.admin-dash-more {
  margin: -4px 0 0;
  font-size: 0.82rem;
}

/* The right-hand side of a dashboard row: one or two small pills. Kept
   from shrinking so a long subject line can't squeeze the wait time into
   two lines. */
.admin-dash-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* A job's error text. Clamped to two lines: these are stack-traceish and a
   single long one would push the other four jobs off the card. */
.admin-dash-error {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  margin-top: 2px;
  color: var(--rust-700);
  font-size: 0.78rem;
  overflow-wrap: anywhere;
}

/* The all-clear line, between the strip and the panels. `.form-success`
   carries a bottom margin meant for a form; here the grid below supplies
   its own spacing. */
.admin-dash-clear {
  margin-bottom: 24px;
}


/* ---------- Cancelling ---------- */

/* The four things somebody has to know before they commit. Rows rather
   than a bulleted list: each is a short bold claim with an explanation
   under it, and bullets would bury the claim in the prose. */
.cancel-facts {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.cancel-fact strong {
  display: block;
  color: var(--green-900);
}

.cancel-fact p {
  margin: 2px 0 0;
  font-size: 0.88rem;
}

/* The action row. The destructive control is `.btn-secondary` and the way
   out is a plain link, not the other way round -- a screen that dresses
   "keep it" as the primary button and the real action as an afterthought
   is a retention dark pattern, and the Terms promise no notice period and
   no fee. Leaving should be easy. */
.cancel-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px;
  margin-top: 24px;
}

.cancel-keep {
  font-size: 0.9rem;
  color: var(--ink-muted);
}

.cancel-keep:hover,
.cancel-keep:focus-visible {
  color: var(--green-900);
}

/* The notice on the billing page for a site already on its way out.
   Amber-tinted and inside the row it belongs to, so it reads as a state of
   that website rather than as a page-level warning. */
.cancel-scheduled {
  margin-top: 14px;
  padding: 14px 18px;
  border: 1px solid var(--rust-300);
  border-radius: var(--radius-md);
  background: var(--rust-tint);
}

.cancel-scheduled-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
  color: var(--rust-700);
}

.cancel-scheduled p {
  margin: 0 0 12px;
  font-size: 0.88rem;
}

.cancel-scheduled form {
  margin: 0;
}

/* The reason tally's right-hand side: share then count, the count heavier
   because it is the thing being measured and the share is the context. */
.cancel-tally {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

/* Free text somebody typed on their way out, under the reason in the admin
   table. Clamped: these can be a paragraph and one long answer should not
   set the row height for the whole table. */
.cancel-detail {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  margin-top: 2px;
  font-size: 0.8rem;
  overflow-wrap: anywhere;
}

/* The sites blocking an account deletion, each with its own way out. */
.account-billing-sites {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 16px 0;
}

.account-billing-site {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
}

/* Free text somebody typed on their way out, quoted on the admin site
   overview. A rule down the side rather than quote marks: this is
   somebody's own words inside a screen otherwise written by us, and the
   distinction matters when you are about to reply to them. */
.admin-quote {
  margin: 0 0 12px;
  padding: 10px 16px;
  border-left: 3px solid var(--rust-400);
  border-radius: 0;
  background: var(--paper-tint);
  color: var(--green-900);
  font-size: 0.9rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}


/* ---------- Admin: the page editor's prompt box ---------- */

/* Under the editor/preview split, full width. A third column would have
   taken space from the two panes being compared, and an instruction is a
   sentence -- it wants a line, not a column. */
.admin-prompt {
  margin-top: var(--card-gap, 20px);
  padding: 20px 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

.admin-prompt-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.admin-prompt-head h2 {
  margin: 0;
  font-size: 1.05rem;
}

.admin-prompt p {
  margin: 4px 0 14px;
  font-size: 0.88rem;
}

/* Field and button on one line, the field taking whatever is left. On a
   narrow screen they stack and the button goes full width, because a
   half-width button beside nothing looks like a mistake. */
.admin-prompt-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

/* Given the field treatment explicitly rather than wrapped in
   `.form-field`, which is where every other input in this app gets it.
   `.form-field` is a labelled block -- label above, hint below -- and this
   is an unlabelled control sitting in a flex row beside its button. Using
   it would mean fighting its layout to get one line. */
.admin-prompt-input {
  flex: 1;
  min-width: 0;
  padding: 11px 13px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--paper-raised);
  color: var(--green-900);
  font-family: var(--font-body);
  font-size: 0.95rem;
  resize: vertical;
  transition: border-color var(--transition);
}

.admin-prompt-input:focus {
  border-color: var(--rust-400);
}

.admin-prompt-input::placeholder {
  color: var(--ink-faint);
}

.admin-prompt-row .btn {
  flex-shrink: 0;
  /* Matches the two-row textarea beside it rather than sitting against its
     top edge. */
  min-height: 62px;
}

@media (max-width: 700px) {
  .admin-prompt-row {
    flex-direction: column;
  }

  .admin-prompt-row .btn {
    width: 100%;
    min-height: 0;
  }
}

.admin-prompt-result {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 14px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-tint);
}

/* Nothing was changed, so this reads as a fault rather than as a result. */
.admin-prompt-result.is-error {
  border-color: var(--rust-400);
  background: var(--rust-tint);
  color: var(--rust-700);
}

.admin-prompt-message {
  margin: 0;
  flex: 1;
  min-width: 12rem;
  font-size: 0.88rem;
}

/* ---------- Example-site screenshots (ui::site_shot) --------------------

   A picture of a website we designed, in just enough browser chrome that
   the eye reads it as a screen rather than a photograph. Replaced 48
   hotlinked stock photographs across the marketing pages -- see
   `ui::site_shot` for why these are captured images and not live frames.
   ---------- */

.site-shot {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--paper-sunken);
  box-shadow: var(--shadow-lift, 0 24px 48px -28px rgb(15 36 25 / 45%));
}

/* The title bar. Three dots and a rule, sized so it stays legible when the
   frame is 240px wide on a card and does not turn into a heavy band when
   it is 700px wide in a hero. */
.site-shot-chrome {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 9px 12px;
  background: var(--paper-raised);
  border-bottom: 1px solid var(--border);
}

.site-shot-chrome span {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--border-strong);
}

/* The first dot takes the brand's warm accent, so the frame reads as ours
   rather than as a generic macOS window. */
.site-shot-chrome span:first-child {
  background: var(--rust-400);
}

/* `aspect-ratio` on the image itself, matching the capture, so the
   browser reserves the right box before the file lands. A page of these
   without it reflows once per image as they arrive. */
.site-shot-page {
  display: block;
  width: 100%;
  /* `height: auto` is load-bearing, not tidiness.

     The `<img>` carries `width="1280" height="853"` so the browser can
     reserve the right box before the file arrives. Those attributes map to
     presentational hints -- effectively `height: 853px` -- and a *used*
     height beats `aspect-ratio`, which only applies when one dimension is
     auto. With `width: 100%` and that hint intact the image rendered
     337x853: a tall sliver, with `object-fit: cover` dutifully cropping a
     narrow vertical strip out of a landscape screenshot. */
  height: auto;
  aspect-ratio: 1280 / 853;
  object-fit: cover;
  object-position: top center;
  background: var(--paper);
}

/* ---------- Home: the two panels in "Why we built this" ----------------

   Both slots held stock photographs -- somebody with paperwork, and a team
   round a desk. Neither illustrated what the copy beside it says, and an
   example site of ours could not fill the first one either: using a site
   we are proud of as the *bad* half of a comparison argues against us.

   So they are mocks of the two things being described, built from the same
   browser chrome the real screenshots use so the section still reads as
   one piece. ---------- */

.stale-shot {
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  overflow: hidden;
  background: var(--paper-raised);
}

.stale-shot-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  padding: 28px 26px 32px;
  /* Faded, because the point of the panel is that nobody has been near it
     for three years. */
  opacity: 0.62;
  filter: grayscale(0.45);
}

/* Two blank rules standing in for the rest of a page nobody is reading.
   Deliberately not lorem text: a placeholder that says nothing is more
   honest than one that says something meaningless. */
.stale-shot-line {
  width: 55%;
  height: 10px;
  margin: 0;
  border-radius: 5px;
  background: var(--paper-sunken);
}

.stale-shot-line-wide {
  width: 78%;
  height: 16px;
}

.stale-shot-text {
  margin: 0;
  font-size: 0.95rem;
  color: var(--ink-muted);
}

.stale-shot-tag {
  margin-top: 6px;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  background: var(--paper-sunken);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* The other half: what asking for a change looks like. */
.request-thread {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 28px 26px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--paper-raised);
}

.request-bubble {
  max-width: 84%;
  margin: 0;
  padding: 14px 18px;
  border-radius: var(--radius-lg);
  font-size: 0.95rem;
  line-height: 1.5;
}

.request-bubble-them {
  align-self: flex-start;
  border-bottom-left-radius: var(--radius);
  background: var(--paper-sunken);
  color: var(--green-900);
}

.request-bubble-us {
  align-self: flex-end;
  border-bottom-right-radius: var(--radius);
  background: var(--green-800);
  color: var(--paper-raised) !important;
  font-weight: 500;
}

.request-thread-note {
  margin: 4px 0 0;
  align-self: flex-end;
  font-size: 0.78rem;
  color: var(--ink-faint);
}

/* ---------- Example shots inside the existing media slots ---------------

   `.bento-media`, `.mhero-shot` and `.split-media` were each written to
   hold a bare `<img>` that fills them edge to edge. They now hold a
   `.site-shot`, which is a frame *around* an image, so each one needs to
   let the frame fill it instead. ---------- */

.bento-media .site-shot,
.mhero-shot .site-shot,
.split-media .site-shot {
  height: 100%;
  /* The slot already supplies the rounding, the border and the shadow;
     the frame supplying its own on top produces a double edge. */
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.bento-media .site-shot-page,
.mhero-shot .site-shot-page,
.split-media .site-shot-page {
  /* Fill the slot rather than keeping the capture's 3:2. The slots have
     their own shapes -- a tall bento tile, a 4:3 split -- and cropping the
     top of a page to fit is exactly what `object-position: top` is for. */
  height: 100%;
  aspect-ratio: auto;
}

/* The hero stack is small and overlapping; three title bars in it is three
   too many. */
.mhero-shot .site-shot-chrome {
  display: none;
}

/* ---------- The quote nobody wants (ui::mock::change_quote) ------------ */

.quote-note {
  padding: 26px 24px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--paper-raised);
}

.quote-note-head {
  margin: 0 0 16px;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.quote-note-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
  font-size: 0.95rem;
  color: var(--ink-muted);
}

.quote-note-row:last-child {
  border-bottom: 0;
}

.quote-note-figure {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--green-900);
  white-space: nowrap;
}

/* The turnaround, which is the line that actually stings. */
.quote-note-total .quote-note-figure {
  color: var(--rust-700);
}

/* ---------- What is simply handled (information.rs) -------------------- */

.uptime-note {
  padding: 26px 24px;
}

.uptime-note-head {
  margin: 0 0 16px;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ---------- The sign-in panel's quote ---------------------------------- */

/* An initial rather than a stock headshot -- the quote is from a fictional
   customer, and a real stranger's face standing in for someone who does
   not exist is the one thing a testimonial must not do. */
.auth-panel-initial {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex: none;
  border-radius: 50%;
  background: var(--rust-500);
  color: var(--paper-raised);
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 600;
  line-height: 1;
}

/* A frame whose screenshot has not been generated yet.

   The alternative is the browser's broken-image icon, which is what the
   marketing pages showed between the views changing and
   `make showcase-screenshots` being run for the first time. An asset that
   has to be generated should degrade to something calm that still says
   what it is standing in for. */
.site-shot-pending {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  aspect-ratio: 1280 / 853;
  background:
    repeating-linear-gradient(135deg,
      var(--paper) 0 14px,
      var(--paper-sunken) 14px 28px);
}

.site-shot-pending-label {
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  background: var(--paper-raised);
  border: 1px solid var(--border);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--ink-muted);
  text-align: center;
}

/* Inside the media slots the frame fills its container, so the
   placeholder has to as well -- the aspect ratio is the slot's business
   there, not the shot's. */
.bento-media .site-shot-pending,
.mhero-shot .site-shot-pending,
.split-media .site-shot-pending {
  height: 100%;
  aspect-ratio: auto;
}
/* ---------- Admin: who has access (admins::views::team) ---------------- */

/* Marks the signed-in administrator's own row. The row also has no Remove
   button, and without this the reason looks arbitrary rather than "that is
   you". */
.admin-team-you {
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--rust-tint);
  color: var(--rust-700);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* The optional "why" on a grant, under whoever granted it. */
.admin-team-note {
  display: block;
  margin-top: 2px;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

/* ---------------------------------------------------------------------
   Notifications: the rail's bell, and the screen behind it.

   The bell is absent rather than empty when nothing is outstanding -- see
   `notifications::views::bell`. That is why there is no "no notifications"
   state styled here: it never renders.
   --------------------------------------------------------------------- */

.app-nav-bell {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  color: var(--ink-muted);
  background: var(--paper-tint);
  transition: background 120ms ease, color 120ms ease;
}

.app-nav-bell:hover,
.app-nav-bell:focus-visible {
  color: var(--green-900);
  background: var(--paper-sunken);
}

/* Sits on the bell rather than beside it: the rail is 76px wide and a
   number on its own line would push the four areas down by a row, which
   is the one thing the rail is not allowed to do. */
.app-nav-bell-count {
  position: absolute;
  top: -2px;
  right: -2px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  border: 2px solid var(--paper-raised);
  background: var(--rust-600);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.notification-actions {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 12px;
}

.notification-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.notification {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
}

/* Read rows stay, and stay quieter. Removing them on read would make the
   list jump under the reader's cursor; fading them says the same thing
   without moving anything. */
.notification:not(.is-unread) {
  background: var(--paper-tint);
  border-color: transparent;
}

.notification:not(.is-unread) .notification-headline {
  color: var(--ink-muted);
  font-weight: 500;
}

.notification-body {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-width: 0;
}

.notification-dot {
  flex: none;
  width: 8px;
  height: 8px;
  margin-top: 7px;
  border-radius: 999px;
  background: var(--rust-600);
}

.notification-text {
  min-width: 0;
}

.notification-headline {
  margin: 0;
  font-weight: 600;
  color: var(--green-900);
}

.notification-subject {
  margin: 2px 0 0;
  font-size: 0.9375rem;
  color: var(--green-900);
}

.notification-group-subjects {
  margin: 5px 0 0;
  color: var(--green-800);
  font-size: 0.84rem;
  font-weight: 600;
}

.notification-detail {
  margin: 4px 0 0;
  font-size: 0.9375rem;
  color: var(--ink-muted);
}

.notification-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  flex: none;
}

.notification-time {
  font-size: 0.8125rem;
  color: var(--ink-faint);
  white-space: nowrap;
}

.notification-action {
  font-weight: 600;
  color: var(--rust-600);
  white-space: nowrap;
}

/* Below the breakpoint the two columns stack: an action pinned to the
   right of a narrow row ends up under the detail text with nothing
   connecting them. */
@media (max-width: 640px) {

  .notification {
    flex-direction: column;
    gap: 12px;
  }

  .notification-meta {
    flex-direction: row-reverse;
    align-items: center;
    justify-content: space-between;
    width: 100%;
  }
}

/* ---------------------------------------------------------------------
   Managed growth workspace

   Eight capabilities share one page because the promise is one managed
   website, not eight mini-products. The tabs deliberately reuse the
   Enquiries navigation language. JavaScript progressively enhances the
   document into one focused panel at a time; without it, every section is
   still available as an ordinary document.
   --------------------------------------------------------------------- */

.growth-tabs {
  position: sticky;
  top: 72px;
  z-index: 8;
  max-width: 100%;
  margin: 0 0 24px;
  overflow-x: auto;
  background: var(--paper);
  scrollbar-width: none;
}

.growth-tabs::-webkit-scrollbar {
  display: none;
}

.growth-tabs .growth-tab {
  flex: none;
  white-space: nowrap;
}

.growth-tabs .growth-tab:focus-visible {
  outline-offset: -3px;
}

.growth-tabs .tab-count {
  min-width: 22px;
  text-align: center;
}

.growth-tabs.is-enhanced + .growth-section,
.growth-tabs.is-enhanced ~ .growth-section:not([hidden]) {
  margin-bottom: 0;
}

.growth-section[hidden] {
  display: none;
}

.growth-section {
  scroll-margin-top: 148px;
  margin-bottom: 24px;
  padding: clamp(26px, 3vw, 40px);
  gap: 20px;
}

.growth-section:last-child {
  margin-bottom: 0;
}

.growth-section > .eyebrow,
.growth-section > h2,
.growth-section > .text-muted {
  margin-bottom: 0;
}

.growth-section > h2 {
  max-width: 760px;
}

.growth-section > .text-muted {
  max-width: 760px;
}

.growth-section > .badge {
  align-self: flex-start;
}

.growth-recommendations,
.growth-memory-list,
.growth-thread-list,
.growth-conversation {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.growth-steward-settings {
  width: fit-content;
  max-width: 100%;
}

.growth-steward-settings > summary {
  color: var(--ink-muted);
  font-size: 0.875rem;
  font-weight: 650;
  cursor: pointer;
}

.growth-steward-settings[open] {
  width: min(100%, 560px);
  padding: 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-tint);
}

.growth-steward-settings[open] > summary {
  margin-bottom: 18px;
}

.growth-steward-settings form,
#concierge > form,
#reputation > form,
#changes > form,
.growth-add-form > form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  width: 100%;
}

/* These are substantial settings/actions rather than loose controls. A
   contained surface gives each form a clear start and end, while the
   capped measure keeps text fields comfortable to scan on wide screens. */
#concierge > form,
#reputation > form,
#changes > form,
.growth-add-form > form {
  width: min(100%, 780px);
  margin-top: 2px;
  padding: clamp(18px, 2.4vw, 24px);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-tint);
}

/* `.form-field` normally carries its own bottom margin. These forms use a
   flex/grid gap instead, so combining both produced uneven double spacing.
   Explicit widths also prevent the two-column grid and textarea from
   shrink-wrapping into a narrow strip on the left of a wide card. */
.growth-section form > .form-field,
.growth-section form > .form-grid {
  width: 100%;
}

.growth-section form .form-field {
  margin-bottom: 0;
}

.growth-section form > .form-grid {
  gap: 16px 20px;
}

#changes > form > .form-field:first-of-type {
  max-width: 320px;
}

.growth-section .checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  width: 100%;
  margin: 0;
  color: var(--green-900);
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.4;
}

.growth-section .checkbox-row input[type="checkbox"] {
  flex: none;
  width: 17px;
  height: 17px;
  margin: 1px 0 0;
  accent-color: var(--rust-600);
}

.growth-recommendation,
.growth-memory-row,
.growth-thread-row,
.growth-testimonial {
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-tint);
}

.growth-recommendation h3,
.growth-recommendation p,
.growth-memory-row p,
.growth-testimonial p {
  margin-bottom: 0;
}

.growth-recommendation .field-tip {
  display: block;
  color: var(--ink-muted);
  font-size: 0.875rem;
}

.growth-memory-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
}

.growth-memory-row > div:first-child {
  min-width: 0;
}

.growth-memory-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 8px;
  font-size: 0.8rem;
}

.growth-memory-disclosure {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-tint);
}

.growth-memory-disclosure > summary {
  padding: 16px 18px;
  color: var(--green-900);
  font-weight: 650;
  cursor: pointer;
}

.growth-memory-disclosure[open] > summary {
  border-bottom: 1px solid var(--border);
}

.growth-memory-disclosure > .growth-memory-list {
  padding: 14px;
}

.growth-memory-disclosure .growth-memory-row {
  background: var(--paper-raised);
}

.growth-add-form {
  border-top: 1px solid var(--border);
  padding-top: 18px;
}

.growth-add-form summary {
  width: fit-content;
  color: var(--rust-600);
  font-weight: 650;
  cursor: pointer;
}

.growth-add-form[open] summary {
  margin-bottom: 18px;
}

.growth-thread-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  color: var(--green-900);
  transition: border-color 120ms ease, background 120ms ease,
    transform 120ms ease;
}

.growth-thread-row:hover,
.growth-thread-row:focus-visible {
  border-color: var(--rust-300);
  background: var(--paper-raised);
  transform: translateY(-1px);
}

.growth-thread-row span:first-child {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.growth-two-column {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.growth-inner-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  padding: 22px;
  background: var(--paper-tint);
}

.growth-inner-card h3,
.growth-inner-card p {
  margin: 0;
}

.growth-inner-card input[type="file"] {
  width: 100%;
  padding: 10px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  color: var(--ink-muted);
  font: inherit;
  font-size: 0.85rem;
}

.growth-upload-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.growth-upload-list li {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: var(--paper-sunken);
  font-size: 0.82rem;
}

.growth-testimonial {
  margin: 0;
  border-left: 3px solid var(--rust-400);
}

.growth-testimonial footer {
  margin-top: 8px;
  color: var(--ink-muted);
  font-size: 0.875rem;
}

.growth-testimonial form {
  margin-top: 14px;
}

.growth-message {
  width: min(88%, 680px);
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: 4px var(--radius-lg) var(--radius-lg) var(--radius-lg);
  background: var(--paper-tint);
}

.growth-message.is-customer {
  align-self: flex-end;
  border-radius: var(--radius-lg) 4px var(--radius-lg) var(--radius-lg);
  border-color: var(--green-700);
  background: var(--green-900);
  color: var(--paper-raised);
}

.growth-message strong {
  display: block;
  margin-bottom: 4px;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.72;
}

.growth-message p {
  margin: 0;
  white-space: pre-wrap;
}

@media (max-width: 760px) {
  .growth-tabs {
    top: 60px;
    margin-inline: -4px;
    margin-bottom: 16px;
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 30px), transparent 100%);
  }

  .growth-tabs .growth-tab {
    padding-inline: 11px;
  }

  .growth-section {
    margin-bottom: 16px;
    padding: 22px 18px;
    gap: 16px;
  }

  #concierge > form,
  #reputation > form,
  #changes > form,
  .growth-add-form > form {
    padding: 16px;
  }

  .growth-section form > .form-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }

  #changes > form > .form-field:first-of-type {
    max-width: none;
  }

  .growth-two-column {
    grid-template-columns: 1fr;
  }

  .growth-memory-row {
    flex-direction: column;
  }

  .growth-recommendation,
  .growth-memory-row,
  .growth-thread-row,
  .growth-testimonial,
  .growth-inner-card {
    padding: 16px;
  }

  .growth-memory-row .card-actions {
    width: 100%;
  }

  .growth-message {
    width: 94%;
  }
}

/* ==================================================================== */
/* Why WebHostGB: decision pages                                        */
/* ==================================================================== */

/* The second mega-menu deliberately reuses the feature panel shell so
   both menus behave as one system. Its contents are denser: these are
   decision paths rather than feature descriptions. */
.marketing-why-panel {
  width: min(980px, calc(100vw - 40px));
  grid-template-columns: minmax(220px, 0.7fr) minmax(0, 2.3fr);
}

.marketing-why-columns {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.marketing-why-group {
  min-width: 0;
}

.marketing-why-heading {
  margin: 4px 10px 7px;
  color: var(--ink-faint);
  font-size: 0.67rem;
  font-weight: 750;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.marketing-why-group > a {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  min-height: 67px;
  padding: 11px 12px;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  color: var(--green-900);
  transition: background-color var(--transition), border-color var(--transition), transform var(--transition);
}

.marketing-why-group > a:hover,
.marketing-why-group > a:focus-visible {
  border-color: var(--border);
  background: var(--paper-tint);
  color: var(--green-900);
  transform: translateY(-1px);
}

.marketing-why-group small,
.marketing-why-group strong {
  display: block;
}

.marketing-why-group small {
  margin-bottom: 2px;
  color: var(--rust-600);
  font-size: 0.64rem;
  font-weight: 750;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.marketing-why-group strong {
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: -0.01em;
}

.marketing-why-group > a:hover .marketing-feature-arrow {
  color: var(--rust-600);
  transform: translateX(3px);
}

.marketing-why-group > a.marketing-why-about {
  border-top-color: var(--border);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}

.value-hero,
.value-hub-hero {
  position: relative;
  overflow: hidden;
}

.value-hero::after,
.value-hub-hero::after {
  content: "";
  position: absolute;
  right: -12vw;
  bottom: -260px;
  width: min(58vw, 760px);
  aspect-ratio: 1;
  border: 1px solid rgb(204 122 74 / 17%);
  border-radius: 50%;
  box-shadow: 0 0 0 70px rgb(204 122 74 / 5%), 0 0 0 140px rgb(35 70 52 / 4%);
  pointer-events: none;
}

.value-hero-copy {
  position: relative;
  z-index: 1;
  max-width: 69rem;
  margin-top: 30px;
}

.value-hub-hero .value-hero-copy {
  margin-top: 0;
}

.value-hero-copy h1 {
  max-width: 17ch;
  margin-top: 22px;
}

.value-hub-hero .value-hero-copy h1 {
  max-width: 18ch;
}

.value-hero-copy .lede-lg {
  max-width: 68ch;
  margin-top: 24px;
}

.value-area-grid,
.value-point-grid,
.value-health-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.value-area-card {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto;
  align-items: start;
  gap: 14px;
  min-height: 174px;
  padding: 26px;
  border: 1px solid var(--border);
  border-radius: 24px;
  background: var(--paper-raised);
  color: var(--green-900);
  box-shadow: 0 18px 40px -36px rgb(15 36 25 / 46%);
  transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}

.value-area-card:hover,
.value-area-card:focus-visible {
  border-color: var(--rust-300);
  color: var(--green-900);
  box-shadow: 0 26px 54px -34px rgb(15 36 25 / 42%);
  transform: translateY(-3px);
}

.value-area-index {
  padding-top: 5px;
  color: var(--rust-500);
  font-size: 0.7rem;
  font-weight: 750;
  letter-spacing: 0.1em;
}

.value-area-copy,
.value-area-copy small,
.value-area-copy strong,
.value-area-copy span {
  display: block;
}

.value-area-copy small {
  margin-bottom: 6px;
  color: var(--rust-600);
  font-size: 0.69rem;
  font-weight: 750;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.value-area-copy strong {
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 2vw, 1.55rem);
  letter-spacing: -0.025em;
}

.value-area-copy span {
  max-width: 42ch;
  margin-top: 9px;
  color: var(--ink-muted);
  font-size: 0.9rem;
  line-height: 1.55;
}

.value-area-arrow {
  color: var(--ink-faint);
  font-size: 1.15rem;
  transition: color var(--transition), transform var(--transition);
}

.value-area-card:hover .value-area-arrow {
  color: var(--rust-600);
  transform: translateX(4px);
}

.value-point {
  padding: 28px;
  border: 1px solid var(--border);
  border-radius: 22px;
  background: var(--paper-raised);
}

.value-point .feature-icon,
.value-health-check .feature-icon,
.value-standard-note .feature-icon {
  margin-bottom: 20px;
}

.value-point h3,
.value-health-check h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.2rem;
  letter-spacing: -0.015em;
}

.value-point p,
.value-health-check p {
  margin: 10px 0 0;
  color: var(--ink-muted);
  font-size: 0.9rem;
  line-height: 1.65;
}

.value-related-more {
  display: flex;
  justify-content: center;
  margin-top: 27px;
}

.value-related-more a {
  color: var(--green-800);
  font-size: 0.88rem;
  font-weight: 700;
}

.value-related-more a:hover {
  color: var(--rust-700);
}

.value-compare-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 24px;
  background: var(--paper-raised);
  box-shadow: 0 30px 65px -50px rgb(15 36 25 / 55%);
}

.value-compare-table {
  width: 100%;
  min-width: 820px;
  border-collapse: collapse;
  font-size: 0.82rem;
}

.value-compare-table th,
.value-compare-table td {
  padding: 18px 16px;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
  line-height: 1.45;
}

.value-compare-table tr:last-child th,
.value-compare-table tr:last-child td {
  border-bottom: 0;
}

.value-compare-table th:last-child,
.value-compare-table td:last-child {
  border-right: 0;
}

.value-compare-table thead th {
  background: var(--paper-tint);
  color: var(--green-900);
  font-size: 0.7rem;
  font-weight: 750;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.value-compare-table tbody th {
  width: 18%;
  color: var(--green-900);
  font-weight: 700;
}

.value-compare-table td {
  color: var(--ink-muted);
}

.value-compare-table .value-compare-us {
  background: rgb(234 240 233 / 72%);
  color: var(--green-900);
  font-weight: 650;
}

.value-compare-check {
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  margin-right: 7px;
  border-radius: 50%;
  background: var(--green-800);
  color: var(--paper-raised);
  font-size: 0.7rem;
}

.value-choice-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}

.value-choice-grid article {
  padding: 28px;
  border: 1px solid var(--border);
  border-radius: 22px;
  background: var(--paper-raised);
}

.value-choice-grid article.is-highlight {
  border-color: transparent;
  background: var(--green-900);
  color: var(--paper-raised);
}

.value-choice-grid h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.25rem;
}

.value-choice-grid p {
  margin: 13px 0 0;
  color: var(--ink-muted);
  font-size: 0.9rem;
  line-height: 1.65;
}

.value-choice-grid .is-highlight p {
  color: rgb(246 240 228 / 74%);
}

.value-choice-grid .is-highlight a {
  display: inline-block;
  margin-top: 18px;
  color: var(--rust-300);
  font-size: 0.82rem;
  font-weight: 700;
}

.value-ownership-split,
.value-fit-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
}

.value-ownership-card,
.value-fit-card {
  position: relative;
  overflow: hidden;
  padding: clamp(28px, 4vw, 46px);
  border: 1px solid var(--border);
  border-radius: 26px;
  background: var(--paper-raised);
}

.value-ownership-card.is-ours {
  background: var(--paper-tint);
}

.value-ownership-card h2,
.value-fit-card h2 {
  margin: 8px 0 22px;
  font-family: var(--font-display);
  font-size: clamp(1.65rem, 3vw, 2.25rem);
  letter-spacing: -0.035em;
}

.value-ownership-card ul,
.value-fit-card ul,
.value-price-band ul {
  display: grid;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.value-ownership-card li,
.value-fit-card li,
.value-price-band li {
  position: relative;
  padding-left: 27px;
  color: var(--green-900);
  font-size: 0.9rem;
  line-height: 1.5;
}

.value-ownership-card li::before,
.value-fit-card li::before,
.value-price-band li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: var(--rust-600);
  font-weight: 800;
}

.value-exit-strip {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 22px;
  background: var(--paper-raised);
}

.value-exit-strip > span {
  display: grid;
  gap: 9px;
  min-height: 132px;
  padding: 25px;
  border-right: 1px solid var(--border);
  color: var(--green-900);
  font-family: var(--font-display);
  font-size: 1.05rem;
  line-height: 1.3;
}

.value-exit-strip > span:last-child {
  border-right: 0;
}

.value-exit-strip strong {
  color: var(--rust-500);
  font-family: var(--font-body);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
}

.value-fit-mark {
  position: absolute;
  top: 26px;
  right: 28px;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgb(58 92 71 / 13%);
  color: var(--green-800);
  font-size: 1.2rem;
  font-weight: 750;
}

.value-fit-card.is-not-fit .value-fit-mark {
  background: rgb(204 122 74 / 13%);
  color: var(--rust-700);
}

.value-fit-card.is-not-fit li::before {
  content: "—";
  color: var(--ink-faint);
}

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

.value-industry-links a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 68px;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  color: var(--green-900);
  font-weight: 650;
  transition: border-color var(--transition), transform var(--transition);
}

.value-industry-links a:hover {
  border-color: var(--rust-300);
  color: var(--rust-700);
  transform: translateY(-2px);
}

.value-cost-stack {
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 24px;
  background: var(--paper-raised);
}

.value-cost-head,
.value-cost-row {
  display: grid;
  grid-template-columns: minmax(160px, 0.65fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 18px;
  align-items: center;
  padding: 18px 22px;
}

.value-cost-head {
  background: var(--green-900);
  color: var(--paper-raised);
  font-size: 0.68rem;
  font-weight: 750;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.value-cost-row {
  border-bottom: 1px solid var(--border);
  color: var(--ink-muted);
  font-size: 0.86rem;
  line-height: 1.5;
}

.value-cost-row:last-child {
  border-bottom: 0;
}

.value-cost-row > strong {
  color: var(--green-900);
  font-size: 0.9rem;
}

.value-cost-included {
  color: var(--green-800);
  font-weight: 650;
}

.value-cost-included > span {
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  margin-right: 7px;
  border-radius: 50%;
  background: var(--green-800);
  color: var(--paper-raised);
  font-size: 0.68rem;
}

.value-price-band,
.value-health-result {
  background: var(--green-900);
  color: var(--paper-raised);
}

.value-price-band .container,
.value-health-result .container {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(300px, 0.9fr);
  align-items: center;
  gap: clamp(36px, 7vw, 90px);
  padding-top: clamp(64px, 8vw, 104px);
  padding-bottom: clamp(64px, 8vw, 104px);
}

.value-price-band h2,
.value-health-result h2 {
  margin: 8px 0 16px;
  color: var(--paper-raised);
  font-family: var(--font-display);
  font-size: clamp(2.3rem, 5vw, 4rem);
  letter-spacing: -0.05em;
  line-height: 0.98;
}

.value-price-band p,
.value-health-result p {
  margin: 0;
  color: rgb(246 240 228 / 72%);
  line-height: 1.65;
}

.value-price-band li {
  color: rgb(246 240 228 / 88%);
}

.value-price-band li::before {
  color: var(--rust-300);
}

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

.value-question-list p {
  display: flex;
  align-items: center;
  gap: 13px;
  margin: 0;
  padding: 17px 19px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  color: var(--green-900);
  font-weight: 650;
}

.value-question-list p > span {
  display: grid;
  flex: 0 0 auto;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--rust-tint);
  color: var(--rust-700);
  font-family: var(--font-display);
}

.value-health-check {
  position: relative;
  padding: 29px;
  border: 1px solid var(--border);
  border-radius: 22px;
  background: var(--paper-raised);
}

.value-health-number {
  position: absolute;
  top: 27px;
  right: 28px;
  color: var(--rust-500);
  font-size: 0.7rem;
  font-weight: 750;
  letter-spacing: 0.1em;
}

.value-health-result .container {
  grid-template-columns: minmax(0, 1fr) minmax(260px, 0.62fr) auto;
}

.value-health-result h2 {
  max-width: 17ch;
  font-size: clamp(2rem, 4vw, 3.4rem);
}

.value-health-result .btn {
  white-space: nowrap;
}

.value-qa-track {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}

.value-qa-step {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr);
  gap: 14px;
  min-height: 176px;
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: 22px;
  background: var(--paper-raised);
}

.value-qa-step > span {
  padding-top: 4px;
  color: var(--rust-500);
  font-size: 0.7rem;
  font-weight: 750;
  letter-spacing: 0.1em;
}

.value-qa-step h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.12rem;
}

.value-qa-step p {
  margin: 10px 0 0;
  color: var(--ink-muted);
  font-size: 0.85rem;
  line-height: 1.6;
}

.value-standard-note {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 24px;
  max-width: 900px;
  margin-inline: auto;
  padding: clamp(30px, 5vw, 52px);
  border: 1px solid var(--border);
  border-radius: 26px;
  background: var(--paper-raised);
}

.value-standard-note .feature-icon {
  margin: 0;
}

.value-standard-note h2 {
  margin: 8px 0 14px;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 4vw, 2.8rem);
  letter-spacing: -0.035em;
  line-height: 1.08;
}

.value-standard-note p:last-child {
  max-width: 68ch;
  margin: 0;
  color: var(--ink-muted);
  line-height: 1.65;
}

@media (max-width: 980px) {
  .value-choice-grid,
  .value-qa-track {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .value-health-result .container {
    grid-template-columns: minmax(0, 1fr) minmax(260px, 0.75fr);
  }

  .value-health-result .btn {
    width: fit-content;
  }
}

@media (max-width: 760px) {
  .mobile-why-menu .mobile-feature-links {
    padding-left: 0;
  }

  .value-hero::after,
  .value-hub-hero::after {
    right: -240px;
    bottom: -190px;
    width: 520px;
  }

  .value-hero-copy {
    margin-top: 22px;
  }

  .value-area-grid,
  .value-point-grid,
  .value-health-grid,
  .value-choice-grid,
  .value-ownership-split,
  .value-fit-grid,
  .value-industry-links,
  .value-question-list,
  .value-qa-track {
    grid-template-columns: minmax(0, 1fr);
  }

  .value-area-card,
  .value-point,
  .value-health-check,
  .value-choice-grid article,
  .value-qa-step {
    min-height: 0;
    padding: 22px;
  }

  .value-area-card {
    grid-template-columns: 27px minmax(0, 1fr) auto;
  }

  .value-exit-strip {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .value-exit-strip > span {
    min-height: 118px;
    border-bottom: 1px solid var(--border);
  }

  .value-exit-strip > span:nth-child(2) {
    border-right: 0;
  }

  .value-exit-strip > span:nth-child(n + 3) {
    border-bottom: 0;
  }

  .value-cost-head {
    display: none;
  }

  .value-cost-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
    padding: 20px;
  }

  .value-cost-row > span:not(.value-cost-included)::before {
    content: "Often separate: ";
    color: var(--ink-faint);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
  }

  .value-price-band .container,
  .value-health-result .container {
    grid-template-columns: minmax(0, 1fr);
    gap: 26px;
  }

  .value-health-result .btn {
    width: 100%;
    white-space: normal;
  }
}

@media (max-width: 480px) {
  .mobile-why-menu .mobile-feature-links {
    grid-template-columns: minmax(0, 1fr);
  }

  .value-area-card {
    gap: 10px;
    padding: 20px 17px;
    border-radius: 20px;
  }

  .value-area-copy strong {
    font-size: 1.2rem;
  }

  .value-area-copy span {
    font-size: 0.84rem;
  }

  .value-exit-strip {
    grid-template-columns: minmax(0, 1fr);
  }

  .value-exit-strip > span {
    min-height: 0;
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }

  .value-exit-strip > span:nth-child(2),
  .value-exit-strip > span:nth-child(3) {
    border-bottom: 1px solid var(--border);
  }

  .value-exit-strip > span:last-child {
    border-bottom: 0;
  }

  .value-standard-note {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Support messenger: a compact view over the real ticket system. It feels
   conversational without implying that replies are instant or automated. */
.support-messenger-trigger {
  position: fixed;
  z-index: 850;
  right: 24px;
  bottom: 24px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 48px;
  padding: 8px 17px 8px 9px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: var(--radius-pill);
  background: var(--green-900);
  box-shadow: 0 14px 38px rgba(16, 46, 38, 0.24);
  color: #fff;
  font-size: 0.9rem;
  font-weight: 700;
  text-decoration: none;
}

.support-messenger-trigger:hover {
  transform: translateY(-1px);
  background: var(--green-800);
  color: #fff;
}

.support-messenger-trigger-icon {
  display: grid;
  width: 31px;
  height: 31px;
  place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  font-family: var(--font-display);
  font-size: 1.05rem;
}

.support-messenger {
  position: fixed;
  z-index: 900;
  inset: auto 24px 24px auto;
  width: min(430px, calc(100vw - 32px));
  height: min(760px, calc(100dvh - 48px));
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--paper-raised);
  box-shadow: 0 28px 80px rgba(20, 39, 34, 0.26);
  color: var(--green-950);
}

.support-messenger::backdrop {
  background: rgba(14, 26, 23, 0.28);
  backdrop-filter: blur(2px);
}

.support-messenger-surface {
  display: flex;
  height: 100%;
  min-height: 0;
  flex-direction: column;
}

.support-messenger-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 18px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  background: var(--green-900);
  color: #fff;
}

.support-messenger-header > div {
  display: flex;
  align-items: center;
  gap: 11px;
  min-width: 0;
}

.support-messenger-header strong,
.support-messenger-header small {
  display: block;
  color: inherit;
}

.support-messenger-header strong { font-size: 0.94rem; }
.support-messenger-header small {
  margin-top: 2px;
  opacity: 0.76;
  font-size: 0.72rem;
  line-height: 1.35;
}

.support-messenger-presence {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-radius: 50%;
  background: #6ee7a5;
}

.support-messenger-close {
  display: grid;
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  padding: 0;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  font-size: 1.55rem;
  line-height: 1;
  cursor: pointer;
}

.support-messenger-close:hover { background: rgba(255, 255, 255, 0.18); }

.support-messenger-content {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  background: var(--paper);
}

.support-messenger-content[aria-busy="true"] { cursor: progress; }

.support-messenger-loading,
.support-messenger-empty {
  display: grid;
  min-height: 260px;
  padding: 32px;
  place-content: center;
  justify-items: center;
  text-align: center;
}

.support-messenger-loading span {
  width: 28px;
  height: 28px;
  border: 3px solid var(--border);
  border-top-color: var(--green-700);
  border-radius: 50%;
  animation: support-messenger-spin 0.8s linear infinite;
}

.support-messenger-loading p,
.support-messenger-empty p {
  margin: 12px 0;
  color: var(--ink-muted);
}

.support-messenger-empty a + a,
.support-messenger-empty button + a { margin-top: 14px; }

.support-messenger-index { padding: 22px; }

.support-messenger-intro h2,
.support-messenger-thread-head h2 {
  margin: 0;
  font-size: 1.18rem;
}

.support-messenger-intro p {
  margin: 5px 0 20px;
  color: var(--ink-muted);
  font-size: 0.88rem;
}

.support-messenger-form,
.support-messenger-reply {
  display: grid;
  gap: 9px;
}

.support-messenger-form label {
  font-size: 0.79rem;
  font-weight: 700;
  color: var(--ink-muted);
}

.support-messenger-form select,
.support-messenger-form textarea,
.support-messenger-reply textarea {
  width: 100%;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--paper-raised);
  color: var(--green-950);
}

.support-messenger-form select {
  min-height: 44px;
  padding: 0 38px 0 12px;
}

.support-messenger-form textarea,
.support-messenger-reply textarea {
  min-height: 106px;
  padding: 12px;
  resize: vertical;
}

.support-messenger-attachment {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-width: 0;
}

.support-messenger-attachment small,
.support-messenger-reply-actions small {
  color: var(--ink-faint);
  font-size: 0.72rem;
}

.support-messenger-form > .btn {
  width: 100%;
  margin-top: 5px;
}

.support-messenger-conversations {
  margin-top: 26px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

.support-messenger-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.support-messenger-section-head h2 {
  margin: 0;
  font-size: 0.86rem;
}

.support-messenger-section-head a { font-size: 0.78rem; }

.support-messenger-ticket-list {
  display: grid;
  gap: 7px;
}

.support-messenger-ticket {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-width: 0;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--paper-raised);
  color: var(--green-950);
  text-decoration: none;
}

.support-messenger-ticket:hover {
  border-color: var(--green-500);
  background: var(--paper-tint);
  color: var(--green-950);
}

.support-messenger-ticket > div { min-width: 0; }

.support-messenger-ticket strong,
.support-messenger-ticket small {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.support-messenger-ticket strong { font-size: 0.82rem; }
.support-messenger-ticket small {
  margin-top: 3px;
  color: var(--ink-faint);
  font-size: 0.71rem;
}

.support-messenger-ticket-state {
  flex: 0 0 auto;
  color: var(--ink-faint);
  font-size: 0.7rem;
  font-weight: 700;
}

.support-messenger-ticket-state.is-waiting { color: var(--rust-600); }

.support-messenger-thread-view {
  display: flex;
  height: 100%;
  min-height: 0;
  flex-direction: column;
}

.support-messenger-thread-head {
  flex: 0 0 auto;
  padding: 16px 18px 13px;
  border-bottom: 1px solid var(--border);
  background: var(--paper-raised);
}

.support-messenger-thread-head > button {
  min-height: 44px;
  margin: -5px 0 8px -7px;
  padding: 3px 7px;
  border: 0;
  background: transparent;
  color: var(--green-700);
  font-size: 0.78rem;
  font-weight: 700;
  cursor: pointer;
}

.support-messenger-thread-head > div {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.support-messenger-thread-head h2 {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.support-messenger-thread-head p {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.76rem;
}

.support-messenger-thread-head > time {
  display: block;
  margin-top: 5px;
  color: var(--ink-faint);
  font-size: 0.68rem;
}

.support-messenger-thread {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 16px;
  background: var(--paper-sunken);
}

.support-messenger-thread .support-thread {
  padding: 0;
  border: 0;
  background: transparent;
}

.support-messenger-thread .support-msg-col { max-width: 84%; }
.support-messenger-thread .support-msg-bubble {
  padding: 10px 12px;
  font-size: 0.83rem;
}

.support-messenger-thread .support-avatar {
  width: 30px;
  height: 30px;
  font-size: 0.68rem;
}

.support-messenger-reply {
  flex: 0 0 auto;
  padding: 13px 15px 15px;
  border-top: 1px solid var(--border);
  background: var(--paper-raised);
}

.support-messenger-reply textarea { min-height: 70px; }

.support-messenger-reply-actions {
  display: flex;
  align-items: center;
  gap: 9px;
}

.support-messenger-attach-button {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 2px;
  color: var(--green-700);
  font-size: 0.76rem;
  font-weight: 700;
  cursor: pointer;
}

.support-messenger-reply-actions .support-messenger-error { flex: 1 1 auto; }
.support-messenger-reply-actions .btn { margin-left: auto; }

.support-messenger-error {
  margin: 0;
  color: var(--rust-700);
  font-size: 0.75rem;
}

@keyframes support-messenger-spin {
  to { transform: rotate(360deg); }
}

@media (max-width: 640px) {
  .support-messenger-trigger {
    right: 14px;
    bottom: 14px;
  }

  .support-messenger {
    inset: 0;
    width: 100%;
    height: 100dvh;
    border: 0;
    border-radius: 0;
  }

  .support-messenger-index { padding: 20px 18px; }
}

@media (prefers-reduced-motion: reduce) {
  .support-messenger-trigger:hover { transform: none; }
  .support-messenger-loading span { animation-duration: 1.8s; }
}
