/* ============================================================================
   components.css — shared chrome from Direction B
   ----------------------------------------------------------------------------
   The pieces the shared partials need: wrap, sticky nav (real horizontal
   lockup), the Sun ticker band, buttons, eyebrow pill, section kicker, footer,
   and the reveal-on-scroll transition.

   Section components (work cards, service grid, pricing tiers, quote cards)
   are deliberately NOT here yet — they arrive with the pages that use them in
   SBB-2. Every value below resolves through tokens.css.
   ========================================================================== */

/* -------------------------------------------------------------------- wrap */
.wrap {
  max-width: var(--wrap-max);
  margin: 0 auto;
  padding: 0 var(--wrap-pad);
}

.slab { padding: var(--section-y) 0; }

/* Slab that follows its own page hero — the hero already paid the top gap. */
.slab--tight { padding-top: 0; }

/* ------------------------------------------------------------------ header */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--surface-nav);
  -webkit-backdrop-filter: var(--blur-nav);
  backdrop-filter: var(--blur-nav);
  border-bottom: var(--border-hairline) solid var(--line);
}

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-20);
  max-width: var(--wrap-max);
  margin: 0 auto;
  padding: var(--nav-pad-y) var(--wrap-pad);
}

/* Real horizontal lockup. Height is fixed and width is auto so the mark can
   never be distorted, whatever the source file's intrinsic size. */
/* `display:block` on the <picture> keeps it from adding an inline baseline gap
   under the lockup, which would push the header a few pixels taller. */
.nav-brand { display: flex; align-items: center; }
.nav-brand picture { display: block; }

.nav-logo {
  height: var(--nav-logo-h);
  width: auto;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--nav-gap);
}

.nav-links a {
  font-size: var(--fs-350);
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  transition: color var(--t-fast) var(--ease);
}

.nav-links a:hover,
.nav-links a[aria-current="page"] { color: var(--text-primary); }

.nav-links a.nav-cta {
  color: var(--accent-ink);
  background: var(--accent);
  padding: var(--cta-pill-pad-y) var(--cta-pill-pad-x);
  border-radius: var(--radius-pill);
  font-weight: var(--fw-black);
}

.nav-links a.nav-cta:hover { background: var(--accent-hover); color: var(--accent-ink); }

/* --------------------------------------------------------- mobile controls
   The call button and the toggle, as one flex item. Hidden entirely above
   --bp-md so the desktop header keeps its original two-child space-between and
   gains no call control.

   The gap is 8px between boxes; the toggle carries a -10px right margin to
   align its bars optically, so the *visible* separation between the Sun disc
   and the first bar reads as 18px. That is what keeps them from looking like
   one control. */
.nav-actions {
  display: none;
  align-items: center;
  gap: var(--space-8);
}

/* A filled Sun disc. Deliberately the opposite treatment to the toggle's open
   bars — solid against line — because the two sit 18px apart and must not read
   as a pair. Midnight glyph on Sun is the same 11.66:1 pairing the buttons use. */
.nav-call {
  display: none;
  position: relative;
  align-items: center;
  justify-content: center;
  width: var(--nav-toggle-size);
  height: var(--nav-toggle-size);
  flex: none;
  border-radius: var(--radius-round);
  background: var(--accent);
  color: var(--accent-ink);
  transition: background var(--t-fast) var(--ease);
}

/* `border-radius: 50%` clips hit-testing to the circle, so the four corners of
   the 44px box would fall through to .nav-actions and do nothing — a tap that
   lands on the control's own bounding box but misses. A descendant is
   hit-tested on its own rectangle regardless of the parent's radius, so this
   overlay restores the full 44 x 44 target while the disc stays round. Same
   stretched-link pattern the cards use. */
.nav-call::after {
  content: "";
  position: absolute;
  inset: 0;
}

.nav-call:hover { background: var(--accent-hover); color: var(--accent-ink); }
.nav-call svg { display: block; flex: none; }

/* ------------------------------------------------------------- nav toggle
   Hidden above --bp-md. The box is a full --nav-toggle-size touch target while
   the bars stay 24px, so the negative right margin keeps the icon optically
   aligned with the wrap edge instead of the target's edge. */
.nav-toggle {
  display: none;
  width: var(--nav-toggle-size);
  height: var(--nav-toggle-size);
  padding: 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  margin-right: -10px;
}

.nav-toggle span {
  display: block;
  width: 24px;
  height: var(--border-bar);
  background: var(--accent);
  border-radius: var(--radius-xs);
  transition: transform var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}

/* Bars fold into a close mark. 7.5px = one bar (2.5) + one gap (5). */
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7.5px) rotate(-45deg); }

/* ------------------------------------------------------- ticker / Sun field
   Copy is seasonless by policy — "Now booking", never "Now booking · <season>".
   A dated ticker goes stale the moment the season turns. */
.ticker {
  background: var(--accent);
  color: var(--accent-ink);
  padding: var(--ticker-pad-y) 0;
  overflow: hidden;
  white-space: nowrap;
}

.ticker-track {
  display: inline-flex;
  align-items: center;
  animation: ticker-slide var(--ticker-duration) linear infinite;
}

.ticker-track span {
  font-size: var(--fs-650);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-tight);
}

.ticker-track span::after {
  content: "";
  display: inline-block;
  width: 10px;
  height: 10px;
  background: var(--accent-ink);
  border-radius: var(--radius-xs);
  margin: 0 var(--ticker-item-gap);
  vertical-align: 2px;
  transform: rotate(45deg);
}

@keyframes ticker-slide {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .ticker-track { animation: none; }
}

/* ----------------------------------------------------------------- buttons */
.btn {
  display: inline-block;
  font-size: var(--fs-400);
  font-weight: var(--fw-black);
  padding: var(--btn-pad-y) var(--btn-pad-x);
  border-radius: var(--radius-pill);
  transition: background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease);
}

/* Doubled class (.btn.btn-x) rather than a bare modifier. At (0,1,0) these lost
   to any descendant rule of the form `.wrapper a` (0,1,1) — which is exactly how
   the primary CTA ended up Sun-on-Sun. At (0,2,0) a button keeps its own colours
   inside any prose wrapper. The `.cta .btn-ghost` override below is also (0,2,0)
   and still declared later, so it continues to win where it should. */
.btn.btn-sun { background: var(--accent); color: var(--accent-ink); }
.btn.btn-sun:hover { background: var(--accent-hover); transform: translateY(var(--lift-hover)); }

.btn.btn-ghost { border: var(--border-medium) solid var(--line); color: var(--text-primary); }
.btn.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }

.btn.btn-midnight { background: var(--surface-base); color: var(--accent); }
.btn.btn-midnight:hover { background: var(--surface-deep); transform: translateY(var(--lift-hover)); }

/* -------------------------------------------------------- eyebrow + kicker */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-10);
  border: var(--border-hairline) solid var(--line);
  border-radius: var(--radius-pill);
  padding: var(--pill-pad-y) var(--pill-pad-x);
  font-size: var(--fs-200);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--accent);
}

.pill::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: var(--radius-round);
  background: var(--accent);
}

.kicker {
  font-size: var(--fs-200);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-ultra);
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--space-18);
}

.sec-sub {
  color: var(--text-muted);
  max-width: 560px;
}

/* ------------------------------------------------------------------ footer */
.site-footer {
  background: var(--surface-deep);
  padding: var(--footer-pad-y) 0;
  font-size: var(--fs-300);
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
}

.foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-16);
  flex-wrap: wrap;
}

.foot-contact {
  display: flex;
  align-items: center;
  gap: var(--space-18);
  flex-wrap: wrap;
}

.foot-contact a { transition: color var(--t-fast) var(--ease); }
.foot-contact a:hover { color: var(--text-primary); }

/* Instagram: the full-colour brand glyph, lifted verbatim from
   bulldog-enterprises/src/partials/footer.html — same gradient stops, same
   geometry. Never flatten it to a monochrome mark. */
.foot-ig {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
}

.foot-ig svg { flex: none; }

/* ------------------------------------------------------------------ reveal */
.rv {
  opacity: 0;
  transform: translateY(var(--reveal-from));
  transition: opacity var(--t-reveal) var(--ease), transform var(--t-reveal) var(--ease);
}

.rv.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .rv { opacity: 1; transform: none; transition: none; }
}

/* ------------------------------------------------------------------ mobile
   Breakpoint literals: 860px (nav collapses, grids stack), 480px. Custom
   properties cannot be used in @media, so the values are inlined here and
   documented in tokens.css. */
@media (max-width: 860px) {
  .wrap { padding: 0 var(--wrap-pad-sm); }

  .nav { padding: var(--nav-pad-y-sm) var(--wrap-pad-sm); }
  .nav-logo { height: var(--nav-logo-h-sm); }

  /* Mobile panel. Sized to scroll rather than clip: the nav grows to 6-7 items
     in later briefs and the panel must absorb that without a redesign. */
  .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    background: var(--surface-deep);
    border-bottom: var(--border-hairline) solid var(--line);
    max-height: calc(100vh - 100%);
    overflow-y: auto;
    overscroll-behavior: contain;
    box-shadow: 0 18px 40px rgba(0, 0, 0, .38);
  }

  .nav-links.open { display: flex; }

  .nav-links a {
    padding: var(--nav-drawer-pad-y) var(--wrap-pad-sm);
    width: 100%;
    border-top: var(--border-hairline) solid var(--line);
    /* Each row clears the 44px touch target on its own. */
    min-height: var(--nav-toggle-size);
    display: flex;
    align-items: center;
  }

  .nav-links a.nav-cta { border-radius: 0; text-align: center; justify-content: center; }

  .nav-toggle { display: flex; }

  /* Call button and toggle appear together, and only here. Both are a full
     44px box, so the header's height is still set by the touch target rather
     than the 40px lockup — a second control does not make it taller. */
  .nav-actions { display: flex; }
  .nav-call { display: flex; }

  .slab { padding: var(--section-y-sm) 0; }

  .ticker-track span { font-size: var(--fs-650-sm); }
}

/* ============================================================================
   SBB-2 — section components
   ----------------------------------------------------------------------------
   The pieces the homepage and /work/ are built from. Every value resolves
   through tokens.css; there is not a raw colour in this file.

   Two standing laws are enforced structurally here, not by convention:

   1. READING TIER. Long-form body copy never sits on raw midnight. Work-card
      copy, the services lane, and testimonials all sit on --surface-reading
      (warm charcoal) or --surface-card.

   2. MOBILE MEDIA. No hero image or video is ever cropped, zoomed, or
      object-fit:cover'd. Card media is `width:100%; height:auto` at every
      breakpoint, so it always renders at its natural ratio — which is why the
      image pipeline preserves aspect and never crops. There is deliberately
      no `object-fit` declaration anywhere below.
   ========================================================================== */

/* -------------------------------------------------------------- section head */
h2.sec {
  font-size: var(--fs-display-section);
  line-height: var(--lh-section);
  letter-spacing: var(--ls-heading);
  max-width: 20ch;
}

.sec-head { margin-bottom: var(--space-56); }
.sec-head h2.sec { margin-bottom: var(--space-22); }
.sec-head .sec-sub { margin-bottom: 0; }

/* --------------------------------------------------------------------- hero */
.hero { padding: var(--hero-y-top) 0 var(--hero-y-bottom); }
.hero .pill { margin-bottom: var(--space-36); }

/* Homepage h1 steps down from the global display size — see the note on
   --fs-display-hero-home. Scoped to .hero so /work/'s .page-hero and the
   legacy pages are untouched. */
.hero h1 {
  max-width: 17ch;
  font-size: var(--fs-display-hero-home);
}

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

/* ------------------------------------------------------------- hero layout
   `auto` on the second track lets the panel take exactly its max-width and
   hands the rest to the headline; minmax(0,1fr) stops the copy column from
   being widened by any long unbreakable string. */
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--hero-grid-gap);
  align-items: center;
}

/* ------------------------------------------------------- hero brand panel
   A framed box around the mark — the boxed-logo treatment, not a loose image.

   Three forces are balanced here:

   1. The FRAME has to read as deliberate at 300px, so the border is 2px of
      Sun at .62 rather than a .4 hairline. At 1px it disappeared against the
      navy and the panel stopped reading as a box at all.
   2. The mark is a raster that already carries a generous internal margin —
      the lockup is roughly the middle half of the square — so the padding is
      8px, not 16px. Stacking a second full margin inside the baked one only
      shrinks the mark.
   3. The mark must not get SMALLER than it was without the frame. Because
      box-sizing is border-box, border and padding eat into the declared
      width, so --brand-panel-max was grown by exactly 2*(8+2) to compensate.
      Net: the image is 300px wide, up from 298px.

   The background matches --surface-base deliberately: the raster bakes the
   same #1D2030 field, so the 8px gap reads as part of the mark's own field
   rather than as a contrasting ring fighting the border.

   4. The Sun bloom is what makes the panel read as lit rather than outlined.
      It is a box-shadow with NO spread — see --glow-brand — so it falls off
      softly instead of stamping a second ring outside the border. Shadows
      cost no layout, so the mark keeps its full 300px / 166px. */
.hero-brand {
  width: var(--brand-panel-max);
  max-width: 100%;
  padding: var(--brand-panel-pad);
  background: var(--surface-base);
  border: var(--brand-panel-border) solid var(--accent-line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--glow-brand);
  transition: box-shadow var(--t-reveal) var(--ease);
}

.hero-brand:hover { box-shadow: var(--glow-brand-hover); }

@media (prefers-reduced-motion: reduce) {
  .hero-brand { transition: none; }
}

.hero-brand-mark {
  display: block;
  width: 100%;
  height: auto;
}

.hero-sub {
  margin-top: var(--space-32);
  max-width: 620px;
  color: var(--text-muted);
  font-size: var(--fs-550);
  line-height: var(--lh-body);
}

.hero-sub strong { color: var(--text-primary); font-weight: var(--fw-bold); }

.hero-ctas {
  display: flex;
  gap: var(--space-18);
  margin-top: var(--space-44);
  flex-wrap: wrap;
}

/* ------------------------------------------------------- proof band (Sun 1)
   Full-bleed Sun field, midnight type. Shipped work only — anything still in
   build lives in .proof-inbuild BELOW the band, never among the names. */
.proof {
  background: var(--accent);
  color: var(--accent-ink);
  padding: var(--ticker-pad-y) 0;
  overflow: hidden;
  white-space: nowrap;
}

.proof-track {
  display: inline-flex;
  align-items: center;
  animation: ticker-slide var(--ticker-duration) linear infinite;
}

.proof-track span {
  font-size: var(--fs-650);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-tight);
}

.proof-track span::after {
  content: "";
  display: inline-block;
  width: 10px;
  height: 10px;
  background: var(--accent-ink);
  border-radius: var(--radius-xs);
  margin: 0 var(--ticker-item-gap);
  vertical-align: 2px;
  transform: rotate(45deg);
}

@media (prefers-reduced-motion: reduce) {
  .proof-track { animation: none; }
}

.proof-inbuild {
  padding: var(--space-18) 0 0;
  font-size: var(--fs-300);
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  letter-spacing: var(--ls-tight);
}

.proof-inbuild b { color: var(--text-primary); font-weight: var(--fw-bold); }

/* ---------------------------------------------------------------- work card
   ONE component, used by the homepage (four cards) and /work/ (six). Media
   alternates side on desktop and always stacks above the copy on mobile. */
.work-stack {
  display: flex;
  flex-direction: column;
  gap: var(--stack-gap);
}

.work-card {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: var(--grid-gap-wide);
  align-items: center;
}

.work-card:nth-child(even) { grid-template-columns: 1fr 1.25fr; }
.work-card:nth-child(even) .work-media { order: 2; }

.work-media {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface-deep);
  border: var(--border-hairline) solid var(--line);
}

/* Natural ratio, always. See law 2 in the header block. */
.work-media img,
.work-media video {
  display: block;
  width: 100%;
  height: auto;
}

.flag-badge {
  position: absolute;
  top: var(--space-16);
  left: var(--space-16);
  z-index: 2;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--fs-150);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  padding: var(--badge-pad-y) var(--badge-pad-x);
  border-radius: var(--radius-pill);
}

/* Body copy on the warm reading tier — never raw midnight. */
.work-copy {
  background: var(--surface-reading);
  border: var(--border-hairline) solid var(--line-warm);
  border-radius: var(--radius-lg);
  padding: var(--card-pad-y) var(--card-pad-x);
  color: var(--text-body);
}

.work-loc {
  font-size: var(--fs-200);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--space-14);
}

.work-copy h3 {
  font-size: var(--fs-700);
  letter-spacing: var(--ls-heading);
  line-height: var(--lh-heading);
}

.work-copy p {
  color: var(--text-muted-warm);
  margin-top: var(--space-16);
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
}

.work-links {
  margin-top: var(--space-26);
  display: flex;
  flex-direction: column;
  gap: var(--space-12);
  align-items: flex-start;
}

/* Primary affordance is the case study; the live site is secondary, below it. */
.case-link {
  font-weight: var(--fw-black);
  font-size: var(--fs-400);
  color: var(--accent);
}

.case-link:hover { text-decoration: underline; text-underline-offset: 5px; }

.live-link {
  font-size: var(--fs-300);
  font-weight: var(--fw-semibold);
  color: var(--text-muted-warm);
  transition: color var(--t-fast) var(--ease);
}

.live-link:hover { color: var(--text-body); }

/* ------------------------------------------------- services / reading tier */
.reading {
  background: var(--surface-reading);
  color: var(--text-body);
  border-radius: var(--radius-tier) var(--radius-tier) 0 0;
}

.reading .sec-sub { color: var(--text-muted-warm); }

.svc-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--grid-gap);
}

.svc {
  background: var(--surface-card);
  border: var(--border-hairline) solid var(--line-warm);
  border-radius: var(--radius-lg);
  padding: var(--card-pad-y) var(--card-pad-x);
}

.svc-icon {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-md);
  background: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--space-24);
}

.svc h3 { font-size: var(--fs-600); letter-spacing: var(--ls-tight); line-height: var(--lh-card-title); }

.svc p {
  color: var(--text-muted-warm);
  margin-top: var(--space-14);
  font-size: var(--fs-375);
  line-height: var(--lh-relaxed);
}

/* --------------------------------------------------- pricing promise (Sun 2)
   NO NUMBERS. Standing ruling — no tier name, no dollar figure, no range on
   the homepage. The figures live on /pricing/ only. */
.promise { background: var(--accent); color: var(--text-on-accent); }
.promise .kicker { color: var(--text-on-accent); opacity: .6; }
.promise h2.sec { color: var(--text-on-accent); }
.promise .sec-sub { color: var(--text-on-accent-muted); }

.promise-points {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--grid-gap-tight);
  margin-bottom: var(--space-44);
}

.promise-point {
  background: var(--fill-on-accent);
  border: var(--border-medium) solid var(--line-on-accent);
  border-radius: var(--radius-lg);
  padding: var(--tier-pad-y) var(--tier-pad-x);
}

.promise-point h3 {
  font-size: var(--fs-600);
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-card-title);
  color: var(--text-on-accent);
}

.promise-point p {
  margin-top: var(--space-12);
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
  color: var(--text-on-accent-soft);
}

.promise-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-24);
  flex-wrap: wrap;
}

.promise-foot .note { font-weight: var(--fw-bold); color: var(--text-on-accent-soft); }

/* -------------------------------------------------------------- testimonials
   Ships DARK on the reading tier. Renders only when the data file has
   entries — see src/data/testimonials.json. */
.quote-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--grid-gap);
}

.quote {
  background: var(--surface-card);
  border: var(--border-hairline) solid var(--line-warm);
  border-radius: var(--radius-lg);
  padding: var(--quote-pad-y) var(--quote-pad-x);
}

.quote .mark {
  color: var(--accent);
  font-size: var(--fs-900);
  font-weight: var(--fw-black);
  line-height: var(--lh-quote-mark);
  display: block;
  margin-bottom: var(--space-24);
}

.quote blockquote {
  font-size: var(--fs-450);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.quote .who { margin-top: var(--space-28); display: flex; flex-direction: column; gap: var(--space-2); }
.quote .who b { font-weight: var(--fw-black); color: var(--accent); font-size: var(--fs-400); }
.quote .who span { font-size: var(--fs-300); color: var(--text-muted-warm); font-weight: var(--fw-semibold); }

/* ------------------------------------------------------------- CTA (Sun 3) */
.cta { background: var(--accent); color: var(--text-on-accent); }
.cta h2 {
  font-size: var(--fs-display-closing);
  line-height: var(--lh-closing);
  letter-spacing: var(--ls-display);
  max-width: 18ch;
}

.cta-sub {
  margin-top: var(--space-26);
  max-width: 540px;
  color: var(--text-on-accent-soft);
  font-size: var(--fs-500);
  font-weight: var(--fw-semibold);
}

.cta-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--grid-gap-wide);
  align-items: start;
  margin-top: var(--space-56);
}

.cta-book h3,
.cta-note h3 {
  font-size: var(--fs-600);
  letter-spacing: var(--ls-tight);
  color: var(--text-on-accent);
}

.cta-book > p {
  margin-top: var(--space-12);
  color: var(--text-on-accent-soft);
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
}

.cta-book .btn { margin-top: var(--space-26); }

/* Book and call, side by side, wrapping to two rows when the column is too
   narrow to hold both. The margin moves to the row so a wrap does not stack two
   26px gaps; (0,3,0) beats the `.cta-book .btn` rule above at (0,2,0). */
.cta-actions {
  margin-top: var(--space-26);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-12);
}

.cta-book .cta-actions .btn { margin-top: 0; }

.cta-call {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
}

.cta-call svg { flex: none; }

.cta-checklist { list-style: none; margin-top: var(--space-28); display: grid; gap: var(--space-12); }

.cta-checklist li {
  display: flex;
  align-items: center;
  gap: var(--space-10);
  font-size: var(--fs-350);
  font-weight: var(--fw-semibold);
  color: var(--text-on-accent-soft);
}

.cta-checklist svg { flex: none; }

.cta-contacts {
  margin-top: var(--space-44);
  display: flex;
  gap: var(--space-14);
  flex-wrap: wrap;
}

.cta-contacts a {
  font-weight: var(--fw-black);
  font-size: var(--fs-350);
  border: var(--border-medium) solid var(--line-on-accent-strong);
  border-radius: var(--radius-pill);
  padding: var(--chip-pad-y) var(--chip-pad-x);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}

.cta-contacts a:hover { background: var(--surface-base); color: var(--accent); border-color: var(--surface-base); }

/* A ghost button is drawn for a midnight field; on the Sun it needs midnight
   ink and a midnight hairline or it disappears into the background. */
.cta .btn-ghost,
.promise .btn-ghost {
  border-color: var(--line-on-accent-strong);
  color: var(--text-on-accent);
}

.cta .btn-ghost:hover,
.promise .btn-ghost:hover {
  border-color: var(--surface-base);
  background: var(--surface-base);
  color: var(--accent);
}

/* The global focus ring in base.css is Sun. On a Sun field that is Sun on Sun —
   1.00:1, an invisible ring on every control in these two bands. Midnight gives
   the same 11.66:1 the ink already gets. Nothing changes at rest; this is only
   the keyboard state. */
.cta :focus-visible,
.promise :focus-visible { outline-color: var(--accent-ink); }

/* Except the form, which is a midnight panel sitting ON the Sun field — a
   midnight ring there would be the same invisibility the other way round. Equal
   specificity to the rule above (0,2,0), so source order decides and this wins.
   The form markup itself is untouched. */
.cta-note :focus-visible { outline-color: var(--accent); }

/* The form sits in a midnight panel ON the Sun field — navy stays dominant
   and the inputs get the contrast a Sun background cannot give them. */
.cta-note {
  background: var(--surface-base);
  border-radius: var(--radius-lg);
  padding: var(--card-pad-y) var(--card-pad-x);
  color: var(--text-primary);
}

.cta-note h3 { color: var(--text-primary); }

.cta-note > p {
  margin-top: var(--space-12);
  color: var(--text-muted);
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
}

.contact-form { margin-top: var(--space-26); display: grid; gap: var(--space-16); }

.form-row { display: grid; gap: var(--space-8); }

.form-row label {
  font-size: var(--fs-200);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  color: var(--text-muted);
}

.form-row .optional { font-weight: var(--fw-semibold); letter-spacing: 0; text-transform: none; }

.form-row input,
.form-row textarea {
  font: inherit;
  font-size: var(--fs-400);
  color: var(--text-primary);
  background: var(--surface-deep);
  border: var(--border-hairline) solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-12) var(--space-14);
  width: 100%;
  transition: border-color var(--t-fast) var(--ease);
}

.form-row textarea { resize: vertical; line-height: var(--lh-body); }
.form-row input:focus, .form-row textarea:focus { border-color: var(--accent); }

.hidden-field { position: absolute; left: -9999px; }

.btn-form {
  justify-self: start;
  font-size: var(--fs-400);
  font-weight: var(--fw-black);
  background: var(--accent);
  color: var(--accent-ink);
  padding: var(--btn-pad-y) var(--btn-pad-x);
  border-radius: var(--radius-pill);
  transition: background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}

.btn-form:hover { background: var(--accent-hover); transform: translateY(var(--lift-hover)); }

/* ------------------------------------------------------ /work/ page pieces */
.page-hero { padding: var(--hero-y-top) 0 var(--space-56); }
.page-hero h1 { max-width: 18ch; }

.page-hero .hero-sub { margin-top: var(--space-26); }

/* Currently in build — no image, no live link, visibly not shipped work. */
.inbuild {
  border-top: var(--border-hairline) solid var(--line);
  padding-top: var(--space-40);
  margin-top: var(--stack-gap);
}

.inbuild-row {
  background: var(--surface-reading);
  border: var(--border-hairline) solid var(--line-warm);
  border-radius: var(--radius-lg);
  padding: var(--card-pad-y) var(--card-pad-x);
  color: var(--text-body);
  max-width: 720px;
}

.inbuild-row h3 { font-size: var(--fs-600); letter-spacing: var(--ls-tight); }

.inbuild-row p {
  margin-top: var(--space-12);
  color: var(--text-muted-warm);
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
}

/* -------------------------------------------------------- SBB-2 mobile ---- */
@media (max-width: 860px) {
  .hero { padding: var(--hero-y-top-sm) 0 var(--hero-y-bottom-sm); }
  .page-hero { padding: var(--hero-y-top-sm) 0 var(--space-40); }

  /* Brand panel stacks ABOVE the headline, centred and smaller. Single column
     means the headline gets the full measure back — it is never squeezed into
     a narrow column beside the mark. */
  .hero-grid { grid-template-columns: 1fr; gap: var(--hero-grid-gap-sm); }
  .hero-brand { order: -1; width: var(--brand-panel-max-sm); margin: 0 auto; }

  .sec-head { margin-bottom: var(--space-40); }

  .proof-track span { font-size: var(--fs-650-sm); }

  /* Media always stacks ABOVE the copy — never reordered under it. */
  .work-card,
  .work-card:nth-child(even) { grid-template-columns: 1fr; gap: var(--space-26); }
  .work-card:nth-child(even) .work-media { order: 0; }
  .work-stack { gap: var(--stack-gap-sm); }

  .work-copy { padding: var(--space-28) var(--space-22); }

  .svc-grid { grid-template-columns: 1fr; }
  .promise-points { grid-template-columns: 1fr; gap: var(--grid-gap-tight-sm); }
  .promise-point { padding: var(--tier-pad-y-sm) var(--tier-pad-x-sm); }
  .quote-grid { grid-template-columns: 1fr; }
  .cta-grid { grid-template-columns: 1fr; gap: var(--space-32); margin-top: var(--space-40); }
  .cta-note { padding: var(--space-28) var(--space-22); }
  .reading { border-radius: var(--radius-tier-sm) var(--radius-tier-sm) 0 0; }
  .inbuild { margin-top: var(--stack-gap-sm); }
  .inbuild-row { padding: var(--space-28) var(--space-22); }
}

/* ============================================================================
   SBB-4 — /pricing + /after-launch
   ----------------------------------------------------------------------------
   Both pages are almost entirely long-form copy, so LAW 1 (reading tier) does
   the structural work here: every paragraph on both pages sits on
   --surface-reading or on a --surface-card lifted off it. Nothing below ever
   renders body copy on raw midnight.

   The price card is ONE component shared by three surfaces — the /pricing build
   ladder, the /pricing care plans, and the /after-launch paths — so a change to
   card chrome lands on all three at once.

   Breakpoint literals: 1080px and 560px are new here (the price grid steps
   4 -> 2 -> 1). 860px and 480px remain the system breakpoints.
   ========================================================================== */

/* Continuation panel. `.reading` rounds its top corners because it is the head
   of the warm tier; a section that CONTINUES that tier must not round again or
   the field reads as separate slabs stacked on each other. */
.reading--flat { border-radius: 0; }

/* -------------------------------------------------------------- price card */
.price-grid {
  display: grid;
  gap: var(--grid-gap-tight);
  align-items: stretch;
}

.price-grid--4 { grid-template-columns: repeat(4, 1fr); }
.price-grid--3 { grid-template-columns: repeat(3, 1fr); }

.price-card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface-card);
  border: var(--border-hairline) solid var(--line-warm);
  border-radius: var(--radius-lg);
  padding: var(--tier-pad-y) var(--tier-pad-x);
  transition: transform var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}

.price-card:hover { transform: translateY(var(--lift-card-hover)); }

/* The common pick. Sun border + badge, no size change — the tier is marked,
   not inflated, so the ladder still reads as a ladder. */
.price-card--pick { border-color: var(--accent); }

.price-badge {
  position: absolute;
  top: 0;
  left: var(--tier-pad-x);
  transform: translateY(-50%);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--fs-150);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  padding: var(--badge-pad-y) var(--badge-pad-x);
  border-radius: var(--radius-pill);
  white-space: nowrap;
}

.price-name {
  font-size: var(--fs-300);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  color: var(--accent);
}

.price-fig {
  margin-top: var(--space-12);
  font-size: var(--fs-800);
  font-weight: var(--fw-black);
  line-height: var(--lh-tier-price);
  letter-spacing: var(--ls-heading);
  color: var(--text-body);
}

.price-fig small {
  font-size: var(--fs-400);
  font-weight: var(--fw-bold);
  color: var(--text-muted-warm);
  letter-spacing: 0;
}

.price-meta {
  margin-top: var(--space-8);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  color: var(--text-muted-warm);
}

/* Explicit class, NOT `.price-card > p`. The name, figure, meta and foot are
   all <p> too, and a child-element selector (0,1,1) outranks their single
   classes (0,1,0) — which silently flattened every price to body size. */
.price-copy {
  margin-top: var(--space-18);
  font-size: var(--fs-375);
  line-height: var(--lh-relaxed);
  color: var(--text-muted-warm);
}

.price-list {
  list-style: none;
  margin-top: var(--space-22);
  padding-top: var(--space-22);
  border-top: var(--border-hairline) solid var(--line-warm);
  display: grid;
  gap: var(--space-10);
}

.price-list li {
  position: relative;
  padding-left: var(--space-22);
  font-size: var(--fs-375);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

/* Sun tick, drawn in CSS so the list costs no markup and no SVG per row. */
.price-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 10px;
  width: 7px;
  height: 7px;
  border-radius: var(--radius-xs);
  background: var(--accent);
}

.price-list li b { color: var(--text-primary); font-weight: var(--fw-bold); }

/* Pushes any trailing note to the bottom edge so cards of unequal copy length
   still line their footers up. */
.price-foot {
  margin-top: auto;
  padding-top: var(--space-22);
  font-size: var(--fs-300);
  line-height: var(--lh-relaxed);
  color: var(--text-muted-warm);
}

/* ------------------------------------------------------------- plain notes */
.tier-note {
  margin-top: var(--space-32);
  padding: var(--space-24) var(--space-26);
  background: var(--surface-card);
  border-left: var(--border-bar) solid var(--accent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.tier-note b { color: var(--text-primary); font-weight: var(--fw-black); }
.tier-note + .tier-note { margin-top: var(--space-14); }

/* -------------------------------------------------------- single services */
.svc-rows {
  display: grid;
  gap: var(--space-12);
}

.svc-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-24);
  align-items: center;
  background: var(--surface-card);
  border: var(--border-hairline) solid var(--line-warm);
  border-radius: var(--radius-lg);
  padding: var(--space-26) var(--space-30);
}

.svc-row h3 {
  font-size: var(--fs-450);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-tight);
  color: var(--text-primary);
}

.svc-row p {
  margin-top: var(--space-8);
  font-size: var(--fs-375);
  line-height: var(--lh-relaxed);
  color: var(--text-muted-warm);
  max-width: 62ch;
}

.svc-row .fig {
  font-size: var(--fs-700);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-heading);
  color: var(--accent);
  white-space: nowrap;
}

/* --------------------------------------------------------------------- FAQ */
.faq {
  display: grid;
  gap: var(--space-10);
  max-width: 78ch;
}

.faq-item {
  background: var(--surface-card);
  border: var(--border-hairline) solid var(--line-warm);
  border-radius: var(--radius-lg);
}

.faq-item summary {
  cursor: pointer;
  list-style: none;
  padding: var(--space-22) var(--space-26);
  padding-right: var(--space-56);
  position: relative;
  font-size: var(--fs-450);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-tight);
  color: var(--text-primary);
}

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

/* Plus that becomes a minus. Two bars, one rotated away when open. */
.faq-item summary::before,
.faq-item summary::after {
  content: "";
  position: absolute;
  right: var(--space-26);
  top: 50%;
  width: 13px;
  height: var(--border-bar);
  background: var(--accent);
  border-radius: var(--radius-xs);
  transition: transform var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}

.faq-item summary::before { transform: translateY(-50%); }
.faq-item summary::after { transform: translateY(-50%) rotate(90deg); }
.faq-item[open] summary::after { transform: translateY(-50%) rotate(0deg); opacity: 0; }

.faq-item summary:hover { color: var(--accent); }

.faq-item p {
  padding: 0 var(--space-26) var(--space-26);
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
  color: var(--text-muted-warm);
}

.faq-item p + p { padding-top: var(--space-14); }
.faq-item p b { color: var(--text-body); font-weight: var(--fw-bold); }

/* Prose links only — same exclusion as .longform a, for the same reason. */
.faq-item a:not(.btn) {
  color: var(--accent);
  font-weight: var(--fw-bold);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ------------------------------------------------- /after-launch prose lede */
.prose {
  max-width: 72ch;
  display: grid;
  gap: var(--space-20);
}

.prose p {
  font-size: var(--fs-450);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.prose strong { color: var(--text-primary); font-weight: var(--fw-black); }

/* --------------------------------------------------- /after-launch compare
   A real table: the content IS tabular, so <table> is the honest element and
   screen readers get row/column association for free. On mobile it scrolls
   inside its own container rather than forcing the page to scroll. */
.cmp-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: var(--radius-lg);
  border: var(--border-hairline) solid var(--line-warm);
}

.cmp {
  width: 100%;
  min-width: 720px;
  border-collapse: collapse;
  background: var(--surface-card);
  font-size: var(--fs-375);
}

.cmp th,
.cmp td {
  padding: var(--space-16) var(--space-18);
  text-align: left;
  border-bottom: var(--border-hairline) solid var(--line-warm);
  color: var(--text-body);
}

.cmp thead th {
  font-size: var(--fs-200);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
}

.cmp thead th span {
  display: block;
  margin-top: var(--space-6);
  font-size: var(--fs-300);
  font-weight: var(--fw-semibold);
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-muted-warm);
}

.cmp tbody th {
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}

.cmp tbody tr:last-child th,
.cmp tbody tr:last-child td { border-bottom: 0; }

.cmp .yes { color: var(--accent); font-weight: var(--fw-black); }
.cmp .no { color: var(--text-muted-warm); }

/* Two-up card grid for the /after-launch straight-talk answers. Reuses the
   .svc card so the chrome matches the services lane on the homepage; the only
   difference is the column count and that these carry no icon tile. */
.qa-grid { grid-template-columns: 1fr 1fr; }

/* ------------------------------------------------------------- SBB-4 mobile */
@media (max-width: 1080px) {
  .price-grid--4 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 860px) {
  .price-grid--3 { grid-template-columns: 1fr; }
  .price-card { padding: var(--tier-pad-y-sm) var(--tier-pad-x-sm); }
  .price-badge { left: var(--tier-pad-x-sm); }

  .svc-row {
    grid-template-columns: 1fr;
    gap: var(--space-14);
    padding: var(--space-24) var(--space-22);
  }

  .faq-item summary { padding: var(--space-20) var(--space-22); padding-right: var(--space-48); }
  .faq-item summary::before,
  .faq-item summary::after { right: var(--space-22); }
  .faq-item p { padding: 0 var(--space-22) var(--space-22); }

  .tier-note { padding: var(--space-20) var(--space-22); }
}

@media (max-width: 560px) {
  .price-grid--4 { grid-template-columns: 1fr; }
}

/* ============================================================================
   SBB-3 — section link + case study pages
   ----------------------------------------------------------------------------
   LAW 1 still governs: every paragraph on a case study sits on the warm reading
   tier or on a card lifted off it. The only midnight surfaces are the page hero
   and the media frames.
   ========================================================================== */

/* -------------------------------------------------------- section-level link
   Distinct from .case-link (per-card) on purpose: this one closes a whole
   section, so it is centred, boxed, and full-width rather than inline. */
.sec-more {
  margin-top: var(--stack-gap-sm);
  display: flex;
  justify-content: center;
}

.sec-more a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-12);
  padding: var(--btn-pad-y) var(--btn-pad-x);
  border: var(--border-medium) solid var(--accent-line);
  border-radius: var(--radius-pill);
  font-size: var(--fs-400);
  font-weight: var(--fw-black);
  color: var(--accent);
  transition: border-color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}

.sec-more a:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

/* The arrow travels on hover — the only motion, so it reads as direction
   rather than decoration. */
.sec-more a span {
  transition: transform var(--t-fast) var(--ease);
}

.sec-more a:hover span { transform: translateX(4px); }

@media (prefers-reduced-motion: reduce) {
  .sec-more a span { transition: none; }
  .sec-more a:hover span { transform: none; }
}

/* ------------------------------------------------------------ case study nav
   Breadcrumb back to the index. Sits above the H1 inside the page hero. */
.cs-back {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  margin-bottom: var(--space-24);
  font-size: var(--fs-300);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-tight);
  color: var(--text-muted);
  transition: color var(--t-fast) var(--ease);
}

.cs-back:hover { color: var(--accent); }

/* ------------------------------------------------------------- case study meta
   The fact strip under the hero: location, sector, what it runs on. Sits on
   midnight with the hero, so it carries no long-form copy — labels and values
   only. */
.cs-meta {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-20);
  margin-top: var(--space-44);
  padding-top: var(--space-28);
  border-top: var(--border-hairline) solid var(--line);
}

.cs-meta div { display: grid; gap: var(--space-6); align-content: start; }

.cs-meta dt {
  font-size: var(--fs-200);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  color: var(--accent);
}

.cs-meta dd {
  font-size: var(--fs-400);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  line-height: var(--lh-heading);
}

/* --------------------------------------------------------- case study media
   Same law as the work cards: natural ratio, never cropped, no object-fit. */
.cs-media {
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface-deep);
  border: var(--border-hairline) solid var(--line);
}

.cs-media img,
.cs-media video {
  display: block;
  width: 100%;
  height: auto;
}

.cs-figure { margin-top: var(--space-40); }

.cs-figure figcaption {
  margin-top: var(--space-14);
  font-size: var(--fs-300);
  line-height: var(--lh-relaxed);
  color: var(--text-muted-warm);
}

/* ---------------------------------------------------------- case study body */
.cs-body {
  max-width: 72ch;
  display: grid;
  gap: var(--space-22);
}

.cs-body h2 {
  font-size: var(--fs-700);
  line-height: var(--lh-card-title);
  letter-spacing: var(--ls-heading);
  color: var(--text-primary);
  margin-top: var(--space-24);
}

.cs-body h2:first-child { margin-top: 0; }

.cs-body p {
  font-size: var(--fs-450);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.cs-body strong { color: var(--text-primary); font-weight: var(--fw-black); }

.cs-body ul {
  list-style: none;
  display: grid;
  gap: var(--space-12);
}

.cs-body ul li {
  position: relative;
  padding-left: var(--space-24);
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.cs-body ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 10px;
  width: 7px;
  height: 7px;
  border-radius: var(--radius-xs);
  background: var(--accent);
}

/* ------------------------------------------------------------ case study foot
   Live link + consult CTA. The live link is deliberately the quieter of the
   two — the page exists to start a conversation, not to send traffic away. */
.cs-foot {
  margin-top: var(--space-48);
  padding-top: var(--space-32);
  border-top: var(--border-hairline) solid var(--line-warm);
  display: flex;
  align-items: center;
  gap: var(--space-24);
  flex-wrap: wrap;
}

.cs-live {
  font-size: var(--fs-400);
  font-weight: var(--fw-bold);
  color: var(--text-muted-warm);
  transition: color var(--t-fast) var(--ease);
}

.cs-live:hover { color: var(--accent); }

/* -------------------------------------------------------------- SBB-3 mobile */
@media (max-width: 860px) {
  .cs-meta {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-18);
    margin-top: var(--space-32);
  }

  .cs-body h2 { font-size: var(--fs-600); }
  .cs-figure { margin-top: var(--space-28); }
  .sec-more { margin-top: var(--space-40); }
}

@media (max-width: 480px) {
  .cs-meta { grid-template-columns: 1fr; }
}

/* Figure leading a section needs no top gap; a body block following one does. */
.cs-figure:first-child { margin-top: 0; }
.cs-figure + .cs-body { margin-top: var(--space-40); }

/* ============================================================================
   SBB-5 — service pages, about, apps
   ----------------------------------------------------------------------------
   LAW 1 continues to govern: every paragraph on these pages sits on the warm
   reading tier or on a card lifted off it.
   ========================================================================== */

/* The long-form body block is shared with the case studies. `.cs-body` keeps
   working exactly as it did; `.longform` is the same component under a name
   that reads correctly on a page that is not a case study. */
.longform {
  max-width: 72ch;
  display: grid;
  gap: var(--space-22);
}

.longform h2 {
  font-size: var(--fs-700);
  line-height: var(--lh-card-title);
  letter-spacing: var(--ls-heading);
  color: var(--text-primary);
  margin-top: var(--space-24);
}

.longform h2:first-child { margin-top: 0; }

.longform h3 {
  font-size: var(--fs-600);
  line-height: var(--lh-card-title);
  letter-spacing: var(--ls-tight);
  color: var(--text-primary);
  margin-top: var(--space-12);
}

.longform p {
  font-size: var(--fs-450);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.longform strong { color: var(--text-primary); font-weight: var(--fw-black); }

/* Prose links only. WITHOUT :not(.btn) this rule is (0,1,1) and outranks
   .btn-sun at (0,1,0), which repainted the primary CTA's label Sun-on-Sun and
   underlined it. Buttons are not prose links and must be excluded by hand. */
.longform a:not(.btn) {
  color: var(--accent);
  font-weight: var(--fw-bold);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.longform ul { list-style: none; display: grid; gap: var(--space-12); }

.longform ul li {
  position: relative;
  padding-left: var(--space-24);
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.longform ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 10px;
  width: 7px;
  height: 7px;
  border-radius: var(--radius-xs);
  background: var(--accent);
}

/* Closing row on a service page: consult CTA plus the quieter pricing link. */
.svc-foot {
  margin-top: var(--space-48);
  padding-top: var(--space-32);
  border-top: var(--border-hairline) solid var(--line-warm);
  display: flex;
  align-items: center;
  gap: var(--space-24);
  flex-wrap: wrap;
}

.svc-foot .quiet {
  font-size: var(--fs-400);
  font-weight: var(--fw-bold);
  color: var(--text-muted-warm);
  transition: color var(--t-fast) var(--ease);
}

.svc-foot .quiet:hover { color: var(--accent); }

/* ------------------------------------------------------------- app cards
   The icon is masked to a real iOS superellipse, NOT a rounded rectangle.
   The polygon is a 64-point sample of |x|^5 + |y|^5 = 1 in polar form, which
   holds the corner shape to about 0.3px at the size these render — a rounded
   rect is visibly wrong next to a real app icon.

   The shadow lives on the WRAPPER as a filter, not a box-shadow on the image:
   box-shadow ignores clip-path and would draw a rectangle behind the mask,
   while drop-shadow follows the clipped alpha. */
.app-grid {
  display: grid;
  gap: var(--grid-gap);
}

.app-card {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-32);
  align-items: start;
  background: var(--surface-card);
  border: var(--border-hairline) solid var(--line-warm);
  border-radius: var(--radius-lg);
  padding: var(--card-pad-y) var(--card-pad-x);
}

.app-icon {
  width: var(--app-icon-size);
  flex: none;
  filter: drop-shadow(0 10px 22px rgba(0, 0, 0, .5));
}

.app-icon img {
  display: block;
  width: 100%;
  height: auto;
  clip-path: polygon(100.00% 50.00%,100.00% 54.92%,100.00% 59.94%,99.97% 65.16%,99.88% 70.66%,99.57% 76.50%,98.77% 82.58%,96.93% 88.52%,93.53% 93.53%,88.52% 96.93%,82.58% 98.77%,76.50% 99.57%,70.66% 99.88%,65.16% 99.97%,59.94% 100.00%,54.92% 100.00%,50.00% 100.00%,45.08% 100.00%,40.06% 100.00%,34.84% 99.97%,29.34% 99.88%,23.50% 99.57%,17.42% 98.77%,11.48% 96.93%,6.47% 93.53%,3.07% 88.52%,1.23% 82.58%,0.43% 76.50%,0.12% 70.66%,0.03% 65.16%,0.00% 59.94%,0.00% 54.92%,0.00% 50.00%,0.00% 45.08%,0.00% 40.06%,0.03% 34.84%,0.12% 29.34%,0.43% 23.50%,1.23% 17.42%,3.07% 11.48%,6.47% 6.47%,11.48% 3.07%,17.42% 1.23%,23.50% 0.43%,29.34% 0.12%,34.84% 0.03%,40.06% 0.00%,45.08% 0.00%,50.00% 0.00%,54.92% 0.00%,59.94% 0.00%,65.16% 0.03%,70.66% 0.12%,76.50% 0.43%,82.58% 1.23%,88.52% 3.07%,93.53% 6.47%,96.93% 11.48%,98.77% 17.42%,99.57% 23.50%,99.88% 29.34%,99.97% 34.84%,100.00% 40.06%,100.00% 45.08%);
}

.app-card h2 {
  font-size: var(--fs-700);
  line-height: var(--lh-card-title);
  letter-spacing: var(--ls-heading);
  color: var(--text-primary);
}

.app-tagline {
  margin-top: var(--space-8);
  font-size: var(--fs-450);
  font-weight: var(--fw-bold);
  color: var(--accent);
  line-height: var(--lh-heading);
}

.app-card > div > p:not(.app-tagline) {
  margin-top: var(--space-16);
  font-size: var(--fs-400);
  line-height: var(--lh-relaxed);
  color: var(--text-muted-warm);
}

.app-points {
  list-style: none;
  margin-top: var(--space-20);
  display: grid;
  gap: var(--space-10);
}

.app-points li {
  position: relative;
  padding-left: var(--space-22);
  font-size: var(--fs-375);
  line-height: var(--lh-relaxed);
  color: var(--text-body);
}

.app-points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 10px;
  width: 7px;
  height: 7px;
  border-radius: var(--radius-xs);
  background: var(--accent);
}

.app-foot {
  margin-top: var(--space-26);
  padding-top: var(--space-22);
  border-top: var(--border-hairline) solid var(--line-warm);
  display: flex;
  align-items: center;
  gap: var(--space-20);
  flex-wrap: wrap;
}

/* Coming-soon status is TYPE, never Apple's badge artwork. The badge may only
   be used unaltered and linked to a live listing, so a decorative or greyed
   one would breach Apple's guidelines. */
.app-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-10);
  padding: var(--pill-pad-y) var(--pill-pad-x);
  border: var(--border-hairline) solid var(--accent-line);
  border-radius: var(--radius-pill);
  font-size: var(--fs-200);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--accent);
}

.app-status::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: var(--radius-round);
  background: var(--accent);
}

.app-links {
  display: flex;
  align-items: center;
  gap: var(--space-20);
  flex-wrap: wrap;
  font-size: var(--fs-300);
  font-weight: var(--fw-bold);
}

.app-links a { color: var(--text-muted-warm); transition: color var(--t-fast) var(--ease); }
.app-links a:hover { color: var(--accent); }

/* --------------------------------------------------------------- SBB-5 mobile */
@media (max-width: 860px) {
  /* .qa-grid shipped in SBB-4 with no mobile collapse. The SBB-2 rule
     `.svc-grid { grid-template-columns: 1fr }` cannot win it — same specificity,
     and .qa-grid is declared later in the file — so the two columns survived to
     390px and pushed /services/ 9px past the viewport. */
  .qa-grid { grid-template-columns: 1fr; }

  .app-card {
    grid-template-columns: 1fr;
    gap: var(--space-22);
    padding: var(--space-28) var(--space-22);
  }

  .app-icon { width: var(--app-icon-size-sm); }
  .app-card h2 { font-size: var(--fs-600); }
  .longform h2 { font-size: var(--fs-600); }
}

/* ============================================================================
   SBB-5a — stretched links on navigational cards
   ----------------------------------------------------------------------------
   Before this, the only click target on a card was the heading text; the body
   and the padding were dead. The fix is the stretched-link pattern: the anchor
   stays in the heading (so the accessible name and the tab stop are unchanged)
   and gains an ::after that covers the whole card.

   The focus ring is drawn on that ::after, not on the anchor. The anchor's own
   box is just the heading text, so an outline there rings the words; the
   ::after IS the card, so an outline there rings the card. That also means no
   :has() is needed for focus — only for the hover and cursor affordances.

   `.svc` is shared with the /after-launch straight-talk cards, which carry no
   link. Every rule below is scoped so those are untouched.
   ========================================================================== */

.svc:has(h3 a) { position: relative; }

.svc h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: var(--radius-lg);
  cursor: pointer;
}

/* Hover belongs to the whole card, not the heading. */
.svc:has(h3 a):hover {
  border-color: var(--accent-line);
  transform: translateY(var(--lift-card-hover));
}

.svc:has(h3 a) { transition: border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease); }
.svc:has(h3 a):hover h3 { color: var(--accent); }

/* Ring the card, not the words. */
.svc h3 a:focus-visible { outline: none; }

.svc h3 a:focus-visible::after {
  outline: var(--border-medium) solid var(--accent);
  outline-offset: 3px;
}

/* ---------------------------------------------------- work cards (home + /work/)
   Same pattern, one complication: these carry TWO destinations. The case study
   link is stretched over the card; the live-site link is lifted above the
   overlay so it stays independently clickable and independently focusable. */
.work-card:has(.case-link) { position: relative; }

.case-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: var(--radius-lg);
  cursor: pointer;
}

.live-link {
  position: relative;
  z-index: 2;
}

.work-card:has(.case-link):hover .case-link {
  text-decoration: underline;
  text-underline-offset: 5px;
}

.case-link:focus-visible { outline: none; }

.case-link:focus-visible::after {
  outline: var(--border-medium) solid var(--accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .svc:has(h3 a) { transition: none; }
  .svc:has(h3 a):hover { transform: none; }
}

/* ------------------------------------------------- SBB-5b — service imagery
   A figure that is a direct child of .wrap is full wrap width by default; the
   sections that argue for editorial scale use that. `--measure` pulls a figure
   back to the prose measure so it sits with the paragraph it illustrates.
   Media itself is untouched: .cs-media keeps width:100% / height:auto and no
   object-fit at every breakpoint, per the mobile media law. */
.cs-figure--measure { max-width: 72ch; }

/* ------------------------------------------------- SBB-5b — Instagram in nav
   Desktop: glyph only, in a 44px box so the target is comfortable even though
   the mark is 22px. The label is CLIPPED rather than display:none — display
   none would strip it from the accessibility tree and leave the link unnamed. */
.nav-ig {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-10);
  min-width: var(--nav-toggle-size);
  min-height: var(--nav-toggle-size);
  border-radius: var(--radius-pill);
  transition: opacity var(--t-fast) var(--ease);
}

.nav-ig:hover { opacity: .78; }
.nav-ig svg { display: block; flex: none; }

.nav-ig-label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (max-width: 860px) {
  /* In the panel it becomes an ordinary row: full width, 44px tall, labelled. */
  .nav-ig {
    justify-content: flex-start;
    border-radius: 0;
  }

  .nav-ig-label {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
  }
}

/* ------------------------------------------------- SBB-7 — footer service areas
   A quiet second row under the contact line. Deliberately small: this is a
   supporting link row, not a sitemap section. It is suppressed on the town
   pages themselves — see `showTownLinks` in build.js. */
.foot-towns {
  margin-top: var(--space-20);
  padding-top: var(--space-18);
  border-top: var(--border-hairline) solid var(--line);
  display: flex;
  align-items: baseline;
  gap: var(--space-8) var(--space-18);
  flex-wrap: wrap;
}

.foot-towns-label {
  font-size: var(--fs-200);
  font-weight: var(--fw-black);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
  color: var(--text-muted);
  opacity: .65;
}

.foot-towns nav {
  display: flex;
  gap: var(--space-8) var(--space-18);
  flex-wrap: wrap;
}

.foot-towns a { transition: color var(--t-fast) var(--ease); }
.foot-towns a:hover { color: var(--accent); }

/* ------------------------------------------------- SBB-9 — no-proof footnote
   Sits under the CTA on the two service pages that have no portfolio work
   behind them yet. Quieter than body copy on purpose: it is a disclosure, not
   a pitch, and it should read as one. */
.foot-note {
  margin-top: var(--space-28);
  padding-top: var(--space-20);
  border-top: var(--border-hairline) solid var(--line-warm);
  font-size: var(--fs-300);
  line-height: var(--lh-relaxed);
  color: var(--text-muted-warm);
}
