/* Self-hosted so no third party sees a visitor who has not submitted anything.
   font-display: swap keeps text visible while the file loads. */
@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url(/assets/fonts/inter-300.woff2) format('woff2');
}
@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/assets/fonts/inter-400.woff2) format('woff2');
}
@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/assets/fonts/inter-500.woff2) format('woff2');
}

/* ==========================================================================
   Tuned Tech, tunedtech.co
   Monochrome, Inter, restrained. Type does the work; color stays out of it.
   ========================================================================== */

:root {
  --bg:        #0b0c0e;
  --bg-raised: #101216;
  --fg:        #f2f3f5;
  --muted:     rgba(242, 243, 245, 0.62);
  --faint:     rgba(242, 243, 245, 0.38);
  --rule:      rgba(242, 243, 245, 0.10);
  --rule-firm: rgba(242, 243, 245, 0.18);

  --max:   78rem;
  --gutter: clamp(1.25rem, 5vw, 3rem);

  /* Spacing scale */
  --s-1: 0.5rem;
  --s-2: 1rem;
  --s-3: 1.5rem;
  --s-4: 2.5rem;
  --s-5: 4rem;
  --s-6: clamp(4.5rem, 11vw, 9rem);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

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

/* rem in a media query always resolves against the initial 16px root, so
   these thresholds stay stable even as the root itself changes. */
@media (min-width: 90rem)  { html { font-size: 17px; } }
@media (min-width: 110rem) { html { font-size: 18px; } }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: 1rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "cv11", "ss01";
}

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

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

section { padding-block: var(--s-6); }

/* Banded surfaces. The reference alternates near-black / white / cream to
   give the page a rhythm; full white would break a dark studio's mood, so
   this alternates two dark surfaces instead: same cadence, quieter. */
#approach, #process { background: var(--bg-raised); }

.section-label {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 var(--s-3);
  font-weight: 500;
}

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

h1, h2, h3 {
  font-weight: 400;          /* the restraint: big, but never bold */
  letter-spacing: -0.03em;
  line-height: 1.08;
  margin: 0;
  text-wrap: balance;
}

h1 { font-size: clamp(2.5rem, 7.5vw, 4.5rem); }
h2 { font-size: clamp(1.875rem, 4.5vw, 3rem); }
h3 { font-size: clamp(1.25rem, 1.6vw, 1.5rem); letter-spacing: -0.015em; line-height: 1.3; }

p { margin: 0; }
.lede    { font-size: clamp(1.0625rem, 1.9vw, 1.25rem); color: var(--muted); max-width: 46ch; }
.body    { color: var(--muted); max-width: 62ch; }
.measure { max-width: 54ch; }

a { color: inherit; }

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

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(11, 12, 14, 0.72);
  backdrop-filter: saturate(150%) blur(12px);
  -webkit-backdrop-filter: saturate(150%) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s var(--ease);
}
.site-header[data-stuck="true"] { border-bottom-color: var(--rule); }

/* Three tracks so the nav is centred on the page, not merely between the
   brand and the CTA (those two have different widths, which would push a
   flex-centred nav off-centre). */
.site-header .wrap {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s-3);
  min-height: 4.25rem;
}
.brand      { justify-self: start; }
.nav        { justify-self: center; }
.header-cta { justify-self: end; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  text-decoration: none;
  font-size: 0.9375rem;
  letter-spacing: -0.01em;
}
/* The mark has a baked-in black background, so it is framed deliberately
   as an iOS-style squircle rather than left to sit as a stray black square. */
.brand img {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 23%;
  display: block;
  box-shadow: 0 0 0 1px var(--rule);
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-size: 0.9375rem;
}
.nav a {
  color: var(--muted);
  text-decoration: none;
  transition: color 0.2s var(--ease);
}
.nav a:hover, .nav a:focus-visible { color: var(--fg); }

/* Below the breakpoint the centre track collapses and the CTA stays put,
   which is the one thing a visitor needs from a header on a phone. */
@media (max-width: 46rem) {
  .site-header .wrap { grid-template-columns: 1fr auto; }
  .nav { display: none; }
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.8125rem 1.375rem;
  border-radius: 0.5rem;
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease),
              color 0.2s var(--ease), transform 0.2s var(--ease);
}
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--fg); color: #0b0c0e; }
.btn-primary:hover { background: #fff; }

.btn-ghost { border-color: var(--rule-firm); color: var(--fg); }
.btn-ghost:hover { border-color: var(--fg); }

.btn-sm { padding: 0.5625rem 1rem; font-size: 0.875rem; }

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

.hero {
  padding-block: clamp(3.5rem, 11vh, 6.5rem) var(--s-6);
  border-bottom: 1px solid var(--rule);
  overflow: hidden;              /* rotated devices must not widen the page */
}
.hero-grid { display: grid; gap: var(--s-5); align-items: center; }
@media (min-width: 60rem) {
  /* minmax(0,·) so neither track is forced wider by its content. A --s-6
     gap here cost the headline ~77px and pushed it to four cramped lines. */
  .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, auto); gap: var(--s-5); }
}
.hero h1 { max-width: 18ch; }
.hero .lede { margin-top: var(--s-3); }

/* --- Fanned device trio ---------------------------------------------------
   A glance at what the studio makes. The same three apps get individual
   treatment in the work section below; here they read as one composition. */
.hero-devices {
  display: flex;
  align-items: center;
  justify-content: center;
}
.hero-device {
  display: block;
  position: relative;
  flex: none;
  transition: transform 0.45s var(--ease);
}
.hero-device img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 579 / 1200;
  filter: drop-shadow(0 22px 44px rgba(0, 0, 0, 0.6));
}
.hero-device--side   { width: clamp(6rem, 12.5vw, 11rem);   z-index: 1; }
.hero-device--center { width: clamp(8rem, 16vw, 13.5rem);   z-index: 2; }

.hero-device--left  { transform: rotate(-8deg) translateY(1.75rem); margin-right: -2.25rem; }
.hero-device--right { transform: rotate( 8deg) translateY(1.75rem); margin-left:  -2.25rem; }

.hero-device--left:hover   { transform: rotate(-8deg) translateY(0.75rem); }
.hero-device--right:hover  { transform: rotate( 8deg) translateY(0.75rem); }
.hero-device--center:hover { transform: translateY(-1rem); }

@media (prefers-reduced-motion: reduce) {
  .hero-device:hover { transform: none; }
  .hero-device--left  { transform: rotate(-8deg) translateY(1.75rem); }
  .hero-device--right { transform: rotate( 8deg) translateY(1.75rem); }
}
.hero-actions {
  margin-top: var(--s-4);
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}
.hero-meta {
  margin-top: var(--s-4);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  font-size: 0.875rem;
  color: var(--faint);
}
.hero-meta span { display: inline-flex; align-items: center; gap: 0.5rem; }
.hero-meta span::before {
  content: "";
  width: 3px; height: 3px;
  border-radius: 50%;
  background: currentColor;
}

/* ---------- Positioning grid ---------- */

.grid-3 {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  margin-top: var(--s-5);
}
.point h3 { margin-bottom: 0.625rem; }
.point p { color: var(--muted); font-size: 1rem; }
.point .num {
  display: block;
  font-size: 0.75rem;
  color: var(--faint);
  letter-spacing: 0.12em;
  margin-bottom: var(--s-2);
  font-variant-numeric: tabular-nums;
}

/* ---------- Work ---------- */

.work { border-top: 1px solid var(--rule); }
.work-grid {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  margin-top: var(--s-5);
  align-items: start;
}

/* No card chrome: the device bezel in each PNG is the frame. Adding a
   bordered card around it would read as a frame inside a frame. */
.case { display: flex; flex-direction: column; }

/* A link, but hidden from the accessibility tree: the "App Store" link
   below carries the same destination, so exposing both would put two
   identical tab stops on every card. */
.case-media {
  margin-bottom: var(--s-3);
  display: flex;
  justify-content: flex-start;   /* device shares the text's left edge */
  transition: transform 0.35s var(--ease);
  cursor: pointer;
}
.case-media img {
  display: block;
  /* Sized by width + aspect-ratio rather than max-height, so the box is
     reserved BEFORE the image loads. With width:auto these collapsed to
     zero height, which both shifted the layout on load and stopped the
     lazy loader from firing. ~12.5rem wide lands the device near 414px
     tall. A full-bleed phone would make each card taller than the viewport. */
  width: min(100%, 12.5rem);
  height: auto;
  aspect-ratio: 579 / 1200;
  object-fit: contain;
  /* Lifts the device off the background. Requires a transparent PNG. */
  filter: drop-shadow(0 18px 34px rgba(0, 0, 0, 0.55));
}
.case:hover .case-media { transform: translateY(-4px); }

.case .tag {
  font-size: 0.75rem;
  color: var(--faint);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 0.5rem;
}
.case h3 { margin-bottom: 0.4375rem; }
.case p { font-size: 1rem; color: var(--muted); }

/* Distinguishes a cofounded product from work done for a client.
   Scoped under .case so it outranks `.case p` without !important. */
.case .case-role {
  margin-top: 0.625rem;
  font-size: 0.8125rem;
  color: var(--faint);
}

.case-link {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  margin-top: 0.875rem;
  font-size: 0.875rem;
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  padding-bottom: 2px;
  align-self: flex-start;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.case-link:hover { color: var(--fg); border-bottom-color: var(--rule-firm); }
.case-link svg { width: 0.75rem; height: 0.75rem; }

/* ---------- Process ---------- */

.process { border-top: 1px solid var(--rule); }
.steps {
  margin-top: var(--s-5);
  display: grid;
  gap: 0;
  border-top: 1px solid var(--rule);
}
.step {
  display: grid;
  grid-template-columns: 4rem 1fr;
  gap: var(--s-3);
  padding-block: var(--s-3);
  border-bottom: 1px solid var(--rule);
  align-items: start;
}
.step .num {
  color: var(--faint);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
  padding-top: 0.2rem;
}
.step h3 { margin-bottom: 0.375rem; }
.step p { color: var(--muted); font-size: 1rem; max-width: 58ch; }
@media (max-width: 34rem) {
  .step { grid-template-columns: 1fr; gap: 0.5rem; }
}

/* ---------- Contact ---------- */

.contact { border-top: 1px solid var(--rule); }
.contact-inner {
  display: grid;
  gap: var(--s-5);
  grid-template-columns: 1fr;
  margin-top: var(--s-4);
}
@media (min-width: 52rem) {
  .contact-inner { grid-template-columns: 1fr 1.15fr; gap: var(--s-6); align-items: start; }
}

.form { display: grid; gap: var(--s-2); }
.field { display: grid; gap: 0.4375rem; }
.field label {
  font-size: 0.8125rem;
  color: var(--muted);
  letter-spacing: 0.01em;
  font-weight: 500;
}
/* var(--bg), not --bg-raised. The panel these sit in is --bg-raised, so the
   inputs were exactly the same colour as their container and read as flat
   outlines, while the selects were darker. Same value everywhere now, so every
   field sits recessed in the panel. */
.field input,
.field textarea {
  font: inherit;
  font-size: 0.9375rem;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  padding: 0.75rem 0.875rem;
  width: 100%;
  transition: border-color 0.2s var(--ease), background-color 0.2s var(--ease);
}
.field textarea { min-height: 8rem; resize: vertical; }
.field input::placeholder,
.field textarea::placeholder { color: var(--faint); }
.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--rule-firm);
  background: #0e1014;
}
.form .btn { margin-top: 0.5rem; justify-self: start; }
.form-note { font-size: 0.8125rem; color: var(--faint); }

.form-status { font-size: 0.9375rem; margin-top: 0.5rem; }
.form-status[data-state="ok"]  { color: var(--fg); }
.form-status[data-state="err"] { color: #ff8f8f; }

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

.site-footer {
  border-top: 1px solid var(--rule);
  padding-block: var(--s-4);
  font-size: 0.875rem;
  color: var(--faint);
}
.site-footer .wrap {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-3);
  align-items: center;
  justify-content: space-between;
}
.site-footer a { color: var(--muted); text-decoration: none; }
.site-footer a:hover { color: var(--fg); }
.footer-links { display: flex; gap: var(--s-3); flex-wrap: wrap; }

/* ---------- Focus + motion ---------- */

:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: 3px;
  border-radius: 3px;
}

.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; }
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--fg);
  color: #0b0c0e;
  padding: 0.75rem 1rem;
  border-radius: 0 0 0.5rem 0;
  z-index: 100;
}
.skip-link:focus { left: 0; }

.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;
}

/* ---------- Split rows (text one side, media the other) ---------- */

.split {
  display: grid;
  gap: var(--s-4);
  align-items: center;
  margin-top: var(--s-5);
}
@media (min-width: 56rem) {
  .split { grid-template-columns: 1fr 1fr; gap: var(--s-6); }
  /* Media first in source order would fight the reading order on mobile,
     so it stays second in the DOM and is moved with grid instead. */
  .split--flip .split-media { order: -1; }
}
/* Transparent device render, so no border-radius (there is no box to round)
   and no full-bleed: at column width this ran 765px tall and swamped the
   copy beside it. */
.split-media img {
  display: block;
  width: min(100%, 18rem);
  height: auto;
  margin-inline: auto;
  filter: drop-shadow(0 18px 34px rgba(0, 0, 0, 0.5));
}
.split-copy h3 { margin-bottom: 0.625rem; }
.split-copy p  { color: var(--muted); }

/* ---------- FAQ ---------- */

.faq-list { margin-top: var(--s-5); border-top: 1px solid var(--rule); }
.faq-item { border-bottom: 1px solid var(--rule); }
.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding-block: var(--s-3);
  cursor: pointer;
  list-style: none;
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
  transition: color 0.2s var(--ease);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { color: #fff; }
.faq-item summary::after {
  content: "";
  flex: none;
  width: 0.625rem;
  height: 0.625rem;
  border-right: 1.5px solid var(--faint);
  border-bottom: 1.5px solid var(--faint);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.3s var(--ease);
}
.faq-item[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.faq-item p {
  color: var(--muted);
  font-size: 0.9375rem;
  max-width: 68ch;
  padding-bottom: var(--s-3);
}

/* ---------- Service cards (footer of home, and slug pages) ---------- */

.services { border-top: 1px solid var(--rule); }
.service-grid {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  margin-top: var(--s-5);
}
.service {
  display: block;
  text-decoration: none;
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: 0.875rem;
  padding: var(--s-3);
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.service:hover { border-color: var(--rule-firm); transform: translateY(-2px); }
.service[aria-current="page"] { border-color: var(--rule-firm); }
.service h3 { margin-bottom: 0.5rem; }
.service p { color: var(--muted); font-size: 1rem; }
.service .go {
  display: inline-block;
  margin-top: 0.875rem;
  font-size: 0.875rem;
  color: var(--faint);
}

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

.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-3);
}

/* ---------- Testimonial marquee ----------
   Full-bleed so cards run edge to edge. The track holds the cards twice and
   translates exactly -50%, so the loop is seamless; the duplicate set is
   aria-hidden so screen readers hear each review once. */

.testimonials { border-top: 1px solid var(--rule); overflow: hidden; }

.marquee {
  margin-top: var(--s-5);
  overflow: hidden;
  /* Fade the cards into the page edges instead of cutting them off. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.marquee-track {
  display: flex;
  gap: var(--s-3);
  width: max-content;
  /* Twelve cards carry eleven gaps, so -50% lands half a gap short of where
     the duplicate set starts and the loop visibly jumps. The trailing pad
     makes the width 12 cards + 12 gaps, so -50% is exactly one set. */
  padding-right: var(--s-3);
  animation: marquee-scroll 64s linear infinite;
}
@keyframes marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
.marquee:hover .marquee-track,
.marquee:focus-within .marquee-track { animation-play-state: paused; }

.tcard {
  flex: none;
  width: min(21rem, 78vw);
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: 0.875rem;
  padding: var(--s-3);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.tcard .stars {
  color: var(--fg);
  font-size: 0.75rem;
  letter-spacing: 0.18em;
}
.tcard blockquote {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--fg);
}
.tcard figcaption {
  font-size: 0.8125rem;
  color: var(--faint);
  margin-top: auto;
  padding-top: 0.25rem;
}
.tcard figcaption .app { color: var(--muted); }

/* Motion here is decorative; without it the row becomes a normal swipeable
   strip rather than disappearing. */
@media (prefers-reduced-motion: reduce) {
  .marquee { overflow-x: auto; }
  .marquee-track { animation: none; }
}

/* ==========================================================================
   Advantage carousel + alternating feature rows
   The reference uses commissioned illustrations for these panels. These are
   built from markup instead, so they stay sharp, weigh nothing, and inherit
   the palette.
   ========================================================================== */

.advantage { border-top: 1px solid var(--rule); }

/* Grid rather than flex: the heading carries a 54ch measure, which as a flex
   item claimed the full row and pushed the controls onto a line of their own. */
.adv-head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: var(--s-3);
}
@media (max-width: 40rem) {
  .adv-head { grid-template-columns: 1fr; }
  .adv-controls { justify-self: start; }
}
.adv-controls { display: flex; gap: 0.5rem; }
.adv-btn {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  border: 1px solid var(--rule-firm);
  background: transparent;
  color: var(--fg);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: border-color 0.2s var(--ease), background-color 0.2s var(--ease), opacity 0.2s var(--ease);
}
.adv-btn:hover:not(:disabled) { border-color: var(--fg); background: rgba(242,243,245,0.06); }
.adv-btn:disabled { opacity: 0.3; cursor: default; }
.adv-btn svg { width: 1rem; height: 1rem; }

.adv-scroller {
  display: flex;
  gap: var(--s-3);
  margin-top: var(--s-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  /* Full-bleed edges would clip the card shadows, so scroll padding keeps
     a snapped card clear of the container edge. */
  scroll-padding-inline: 0;
  padding-bottom: 0.25rem;
}
.adv-scroller::-webkit-scrollbar { display: none; }

.advcard {
  flex: 0 0 clamp(16rem, 31%, 21rem);
  scroll-snap-align: start;
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: 1rem;
  padding: var(--s-3);
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}
.advcard h3 { margin: 0; }
.advcard > p { color: var(--muted); font-size: 1rem; }
.advcard .mockwrap { margin-top: auto; padding-top: var(--s-3); display: grid; gap: 0.625rem; }

/* ---------- Light mock UI chips ---------- */

.mock {
  background: var(--fg);
  color: #101216;
  border-radius: 0.75rem;
  padding: 0.75rem 0.875rem;
  font-size: 0.8125rem;
  line-height: 1.35;
  box-shadow: 0 10px 24px rgba(0,0,0,0.45);
}
.mock-row { display: flex; align-items: center; justify-content: space-between; gap: 0.625rem; }
.mock-sub { color: rgba(16,18,22,0.55); font-size: 0.75rem; }
.mock-big { font-size: 1.375rem; letter-spacing: -0.02em; line-height: 1.1; }
.mock-rule { height: 1px; background: rgba(16,18,22,0.12); margin: 0.5rem 0; }
.mock-pill {
  font-size: 0.6875rem;
  padding: 0.1875rem 0.5rem;
  border-radius: 999px;
  background: rgba(16,18,22,0.08);
  color: rgba(16,18,22,0.7);
  white-space: nowrap;
}
.mock-ic {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 23%;
  background: #101216;
  flex: none;
}
.mock-notif { display: flex; gap: 0.625rem; align-items: flex-start; }
.mock-notif strong { font-weight: 500; }

.mock-bars { display: flex; align-items: flex-end; gap: 0.3125rem; height: 2.75rem; margin-top: 0.5rem; }
.mock-bars i { flex: 1; background: rgba(16,18,22,0.18); border-radius: 2px; }
.mock-bars i:last-child { background: #101216; }

.mock-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.5rem; }
.mock-grid span { aspect-ratio: 1; border-radius: 23%; background: rgba(16,18,22,0.10); }
.mock-grid span.on { background: #101216; }

/* ---------- Alternating feature rows ---------- */

.rows { border-top: 1px solid var(--rule); }
.row-item {
  display: grid;
  gap: var(--s-4);
  align-items: center;
  margin-top: var(--s-6);
}
.row-item:first-of-type { margin-top: var(--s-5); }
@media (min-width: 56rem) {
  .row-item { grid-template-columns: 1fr 1fr; gap: var(--s-6); }
  .row-item:nth-of-type(even) .row-media { order: -1; }
}
.row-copy h3 { margin-bottom: 0.625rem; }
.row-copy p { color: var(--muted); }
.row-copy p + p { margin-top: 0.875rem; }

.panel {
  background:
    radial-gradient(120% 90% at 20% 0%, rgba(242,243,245,0.07), transparent 62%),
    var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: 1.125rem;
  padding: var(--s-4);
  display: grid;
  gap: 0.75rem;
  align-content: center;
  min-height: 17rem;
}

/* ==========================================================================
   Richer card media
   The first pass put one small chip in each card and left the middle empty.
   These bleed a real screenshot off the card edge and stack two or three
   elements over it, which is what gives the reference its depth.
   ========================================================================== */

:root {
  /* The only colour on the site. Used for live/status pills so a card has
     one point of focus. Set to var(--fg) to go fully monochrome again. */
  --accent: #e0a244;
}

.advcard { overflow: hidden; position: relative; }

/* Media that runs off the bottom of the card. */
.adv-figure {
  position: relative;
  /* Top-aligned, like the reference: media sits directly under its copy.
     Pushing it to the bottom with margin-top:auto left a dead gap in the
     middle of every card that had less text than the tallest one. */
  padding-top: var(--s-3);
}
.adv-figure .adv-phone {
  display: block;
  width: 76%;
  /* Without an explicit height the height="1200" attribute applies as a
     presentational hint and the card grows to 1382px. */
  height: auto;
  aspect-ratio: 579 / 1200;
  margin: 0 auto -22%;
  border-radius: 0.75rem;
  filter: drop-shadow(0 16px 30px rgba(0,0,0,0.6));
}
.adv-float {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 34%;
  z-index: 2;
}

/* Stacked chips for cards without a device. */
.adv-stack {
  padding-top: var(--s-3);
  display: grid;
  gap: 0.5rem;
  align-content: start;
}

.mock-lg { padding: 0.9375rem 1rem; }
.mock-xl { font-size: 1.75rem; letter-spacing: -0.03em; line-height: 1.05; }
.mock-strong { font-weight: 500; }

.mock-pill--live {
  background: var(--accent);
  color: #1a1206;
  font-weight: 500;
}

/* Line-item rows, like an order or a schedule. */
.mock-items { display: grid; gap: 0.4375rem; }
.mock-note {
  color: rgba(16,18,22,0.5);
  font-size: 0.6875rem;
  line-height: 1.35;
}

/* Home-screen grid using the real app icons. */
/* minmax(0, 1fr) rather than 1fr: a plain 1fr track will not shrink below its
   content's min-content size, and these icons carry width="128", so the grid
   floored at 128px per cell and blew out to 538px inside a 357px card. */
.mock-home {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.5rem;
  max-width: 13rem;      /* icons at home-screen scale, not tile scale */
}
/* height:auto is load-bearing. Without it the height="128" attribute applies
   as a presentational hint and each grid row floors at 128px, which is what
   made this card the tallest in the row. */
.mock-home img,
.mock-home span {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  border-radius: 23%;
}
.mock-home span { background: rgba(16,18,22,0.10); }
.mock-home img { box-shadow: 0 2px 6px rgba(0,0,0,0.25); }

/* ---------- Panels in the feature rows ---------- */

.panel {
  position: relative;
  overflow: hidden;
}
.panel-figure { position: relative; margin-top: 0.5rem; }
.panel-figure img {
  display: block;
  width: 58%;
  height: auto;
  aspect-ratio: 553 / 1200;
  margin: 0 auto -14%;
  border-radius: 0.75rem;
  filter: drop-shadow(0 16px 30px rgba(0,0,0,0.6));
}
.panel-float { position: absolute; left: 0; right: 0; bottom: 26%; z-index: 2; }

.panel-appicon {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 23%;
  display: block;
  flex: none;
}

.mock-kv { display: grid; gap: 0.5rem; }
.mock-kv > div { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; }
.mock-kv .k { color: rgba(16,18,22,0.55); font-size: 0.75rem; }
.mock-kv .v { font-size: 0.9375rem; }

/* ==========================================================================
   Ambient glow and device backlight
   Ported from the PepTrack site. Two layers do the work: an oversized section
   wash that bleeds past the section edges so it never ends on a hard line,
   and a tight high-alpha bloom hugging the devices so the blue reads as light
   coming off the screens rather than a tint laid over the page.
   ========================================================================== */

:root {
  --signal:      0, 128, 229;    /* the app signal blue */
  --signal-lift: 58, 165, 240;   /* lighter tone, for variation between blooms */
}

section { position: relative; isolation: isolate; }
section > .wrap { position: relative; z-index: 1; }

/* The wash is inset -20% horizontally, which pushes past the section and
   gives the page a horizontal scrollbar. `clip` contains it without creating
   a scroll container, which `hidden` would, and that would break the sticky
   header. */
.hero, .advantage, .testimonials, .contact, #approach, #process { overflow: clip; }

.hero::before,
.advantage::before,
.testimonials::before,
.contact::before,
#approach::after,
#process::after {
  content: "";
  position: absolute;
  /* Bleeds past the section so the wash never terminates on an edge. */
  inset: -10% -20%;
  z-index: 0;
  pointer-events: none;
}

/* Fixed pixel radii rather than rem: these are light sources, so they should
   not grow with the type scale. */
.hero::before {
  background:
    radial-gradient(circle 460px at 72% 8%,  rgba(var(--signal), 0.17), transparent 70%),
    radial-gradient(circle 380px at 12% 58%, rgba(var(--signal), 0.11), transparent 70%),
    radial-gradient(circle 340px at 78% 96%, rgba(var(--signal), 0.09), transparent 70%);
}
.advantage::before {
  background:
    radial-gradient(circle 420px at 84% 14%, rgba(var(--signal), 0.13), transparent 70%),
    radial-gradient(circle 340px at 10% 88%, rgba(var(--signal), 0.09), transparent 70%);
}
.testimonials::before {
  background:
    radial-gradient(circle 380px at 14% 20%, rgba(var(--signal), 0.12), transparent 70%),
    radial-gradient(circle 320px at 86% 88%, rgba(var(--signal), 0.08), transparent 70%);
}
.contact::before {
  background:
    radial-gradient(circle 440px at 50% 96%, rgba(var(--signal), 0.16), transparent 70%),
    radial-gradient(circle 320px at 16% 12%, rgba(var(--signal), 0.09), transparent 70%);
}
/* Fainter over the raised bands, which otherwise go muddy rather than lit. */
#approach::after,
#process::after {
  background:
    radial-gradient(circle 400px at 86% 6%, rgba(var(--signal), 0.10), transparent 70%),
    radial-gradient(circle 320px at 8% 82%, rgba(var(--signal), 0.06), transparent 70%);
}

/* The device fan sits in its own bloom, so the blue looks like it is coming
   off the screens. This is the part that reads as backlighting. */
.hero-devices { position: relative; }
.hero-devices::before {
  content: "";
  position: absolute;
  inset: -16% -20% -8%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 58% 50% at 50% 44%, rgba(var(--signal), 0.34), transparent 70%),
    radial-gradient(ellipse 42% 32% at 64% 74%, rgba(var(--signal), 0.22), transparent 72%),
    radial-gradient(ellipse 34% 26% at 32% 24%, rgba(var(--signal-lift), 0.16), transparent 74%);
  filter: blur(10px);
}

/* Same treatment, quieter, for the bleeding phone in the first card. */
.adv-figure { isolation: isolate; }
.adv-figure::before {
  content: "";
  position: absolute;
  inset: 8% -12% -6%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 60% 46% at 50% 52%, rgba(var(--signal), 0.26), transparent 72%),
    radial-gradient(ellipse 38% 28% at 66% 78%, rgba(var(--signal-lift), 0.14), transparent 74%);
  filter: blur(9px);
}
/* Scoped to the phone only: a blanket `> *` here overrode .adv-float's
   position:absolute, dropping the overlay chip into normal flow. */
.adv-figure > .adv-phone { position: relative; z-index: 1; }
.adv-float { z-index: 2; }

/* ---------- Start a project page ---------- */

.start-hero { padding-block: clamp(3rem, 8vh, 5rem) var(--s-6); }
.start-grid { display: grid; gap: var(--s-5); align-items: start; }
@media (min-width: 58rem) {
  .start-grid { grid-template-columns: 1fr 1fr; gap: var(--s-6); }
}
.start-copy h1 { font-size: clamp(2.25rem, 5vw, 3.25rem); margin-bottom: var(--s-2); }
.start-copy .lede { margin-bottom: var(--s-4); }

.start-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }
.start-steps li { display: grid; grid-template-columns: 2.5rem 1fr; gap: var(--s-2); align-items: start; }
.start-steps .n {
  color: var(--faint);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
  padding-top: 0.25rem;
}
.start-steps h3 { font-size: 1.0625rem; margin-bottom: 0.25rem; }
.start-steps p { color: var(--muted); font-size: 0.9375rem; }


.start-formwrap {
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: 1.125rem;
  padding: clamp(1.5rem, 3vw, 2.25rem);
}
.form-grid > div:last-child {
  margin-top: 0.5rem;
  padding-top: var(--s-3);
  border-top: 1px solid var(--rule);
}
.field .opt { color: var(--faint); }
.field select {
  font: inherit;
  font-size: 0.9375rem;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
  padding: 0.75rem 0.875rem;
  width: 100%;
  appearance: none;
  /* Chevron drawn inline so the control needs no image asset. */
  background-image: linear-gradient(45deg, transparent 50%, var(--faint) 50%),
                    linear-gradient(135deg, var(--faint) 50%, transparent 50%);
  background-position: calc(100% - 1.125rem) 1.25rem, calc(100% - 0.75rem) 1.25rem;
  background-size: 0.375rem 0.375rem, 0.375rem 0.375rem;
  background-repeat: no-repeat;
}
.field select:focus { outline: none; border-color: var(--rule-firm); }

/* ==========================================================================
   Drawn scenes
   A full device render dictates its own height: at 76% of the card the LOCKD
   image forced 561px and set the height of all five cards. These scenes are
   drawn instead, so height is a decision rather than a consequence, and only
   the part of the phone that carries meaning is shown.
   ========================================================================== */

.phone-scene {
  position: relative;
  /* Card three is the tallest, so this can grow into that headroom for free. */
  height: 17rem;
  margin-top: var(--s-3);
  overflow: hidden;
}
/* A real lock screen, cropped by the scene rather than scaled to fit. The
   drawn frame this replaces was only ever a stand-in for this shot. */
.ps-shot {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  width: 62%;
  /* No hardcoded aspect-ratio. The width and height attributes on the img
     already reserve the box, and they track whatever image is swapped in.
     A pinned ratio silently stretches the next screenshot that differs. */
  height: auto;
  filter: drop-shadow(0 18px 34px rgba(0,0,0,0.6));
}




/* Sits over the phone and runs wider than it, the way a real banner does. */
.ps-notif { position: absolute; left: 0; right: 0; bottom: 1.25rem; z-index: 2; }

/* ---------- Landscape scenes for the feature rows ---------- */

.scene {
  position: relative;
  aspect-ratio: 16 / 10;
  border: 1px solid var(--rule);
  border-radius: 1.125rem;
  overflow: hidden;
  background:
    radial-gradient(120% 90% at 22% 0%, rgba(var(--signal), 0.13), transparent 62%),
    var(--bg-raised);
  padding: clamp(1rem, 2.4vw, 1.75rem);
  display: grid;
  gap: 0.75rem;
  align-content: center;
}

/* Wireframe blocks, the placeholder-content look the reference draws. */
.wire { display: grid; gap: 0.375rem; }
.wire i {
  display: block;
  height: 0.5rem;
  border-radius: 3px;
  background: rgba(255,255,255,0.14);
}
.wire i.short { width: 55%; }
.wire i.mid   { width: 76%; }

.scene-split { display: grid; grid-template-columns: 1fr 1fr; gap: 0.875rem; align-items: center; }

/* A cropped device standing in the scene rather than a full render. */
.scene-phone {
  border: 4px solid #16181d;
  border-radius: 1.25rem 1.25rem 0 0;
  border-bottom: none;
  background: linear-gradient(180deg, #151b28, #0c1017);
  padding: 0.75rem 0.625rem 0;
  display: grid;
  gap: 0.5rem;
  align-self: end;
  height: 88%;
  box-shadow: 0 14px 28px rgba(0,0,0,0.5);
}
.scene-phone .bar {
  height: 0.375rem; border-radius: 2px;
  background: rgba(255,255,255,0.16);
}
.scene-phone .block {
  border-radius: 0.5rem;
  background: rgba(255,255,255,0.09);
  height: 2.25rem;
}

.swatches { display: flex; gap: 0.3125rem; }
.swatches span {
  width: 1.125rem; height: 1.125rem;
  border-radius: 4px;
  background: rgba(255,255,255,0.14);
}
.swatches span.on { background: rgb(var(--signal)); }

.cursor {
  position: absolute;
  width: 1.375rem; height: 1.375rem;
  color: #fff;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.6));
  pointer-events: none;
}

/* App Store listing stand-in. Apple's own badge is a brand asset with usage
   rules, so this is a neutral listing card rather than a lookalike. */
.listing { display: flex; align-items: center; gap: 0.75rem; }
.listing img { width: 3rem; height: 3rem; border-radius: 23%; flex: none; }
.listing .meta { flex: 1; min-width: 0; }
.listing .name { font-size: 0.9375rem; }
.listing .sub { font-size: 0.75rem; color: var(--faint); }
.listing .get {
  font-size: 0.6875rem; font-weight: 500;
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
  background: rgba(var(--signal), 0.9);
  color: #fff;
}
/* Stars sit beside the score rather than under it, on the score's baseline,
   which closes the gap that opened when the second line of caption went. */
.rating-inline { display: flex; align-items: baseline; gap: 0.5rem; }
.stars-sm {
  /* These sit on a .mock card, which is light. var(--fg) here was near-white
     on near-white, so the stars rendered but were invisible. */
  color: rgba(16, 18, 22, 0.82);
  font-size: 0.9375rem;
  letter-spacing: 0.1em;
  white-space: nowrap;
}

/* Chart drawn from divs so it needs no asset and recolours with the theme. */
.chart { display: flex; align-items: flex-end; gap: 0.375rem; height: 4.5rem; }
.chart i {
  flex: 1;
  border-radius: 3px 3px 0 0;
  background: linear-gradient(180deg, rgba(var(--signal), 0.85), rgba(var(--signal), 0.25));
}
.chart i.dim { background: rgba(255,255,255,0.10); }

/* ---------- Case study pages ---------- */

.cs-hero { padding-block: clamp(3rem, 8vh, 5rem) var(--s-5); }
.cs-grid { display: grid; gap: var(--s-5); align-items: center; }
@media (min-width: 58rem) { .cs-grid { grid-template-columns: 1.1fr 1fr; gap: var(--s-6); } }
.cs-hero h1 { font-size: clamp(2.25rem, 5.5vw, 3.5rem); margin-bottom: var(--s-2); }

.cs-device { display: flex; justify-content: center; }
.cs-device img {
  display: block;
  width: min(100%, 15rem);
  height: auto;
  aspect-ratio: 579 / 1200;
  filter: drop-shadow(0 22px 44px rgba(0,0,0,0.6));
}

.cs-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: var(--s-3);
  margin-top: var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--rule);
}
.cs-meta div { display: grid; gap: 0.25rem; }
.cs-meta .k { font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--faint); }
.cs-meta .v { font-size: 1.0625rem; }

.cs-quote {
  margin-top: var(--s-5);
  padding: var(--s-4);
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: 1.125rem;
}
.cs-quote blockquote {
  margin: 0;
  font-size: clamp(1.0625rem, 2vw, 1.25rem);
  line-height: 1.5;
}
.cs-quote figcaption { margin-top: 0.875rem; font-size: 0.875rem; color: var(--faint); }

/* auto-fit collapses the empty tracks, so a single shot stretched to the full
   1224px container: about 2.4x its own resolution, oversized and soft. auto-fill
   with a capped track keeps each shot at device scale however many there are. */
.cs-shots {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fill, minmax(0, 14rem));
  margin-top: var(--s-5);
}
.cs-shots img {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 18px 34px rgba(0,0,0,0.55));
}

.cs-next { border-top: 1px solid var(--rule); }
.cs-next-grid { display: grid; gap: var(--s-3); grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); margin-top: var(--s-4); }

.breadcrumbs { font-size: 0.8125rem; color: var(--faint); margin-bottom: var(--s-3); }
.breadcrumbs a { color: var(--muted); text-decoration: none; }
.breadcrumbs a:hover { color: var(--fg); }

/* ---------- Case study: try it yourself ---------- */

.cs-try {
  margin-top: var(--s-5);
  padding: var(--s-4);
  background: var(--bg-raised);
  border: 1px solid var(--rule);
  border-radius: 1.125rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}
.cs-try h3 { margin-bottom: 0.375rem; }
.cs-try p { color: var(--muted); font-size: 0.9375rem; }
.cs-try .btn { flex: none; }

/* ---------- Legal pages ---------- */

.legal-hero { padding-block: clamp(3rem, 8vh, 5rem) var(--s-4); border-bottom: 1px solid var(--rule); }
.legal-hero h1 { font-size: clamp(2.25rem, 5vw, 3.25rem); margin-bottom: var(--s-2); }
.legal-date { margin-top: var(--s-3); font-size: 0.875rem; color: var(--faint); }

.legal { padding-block: var(--s-5) var(--s-6); }
.legal .wrap { max-width: 46rem; }        /* a comfortable reading measure, not full width */
.legal h2 {
  font-size: clamp(1.25rem, 2vw, 1.5rem);
  margin-top: var(--s-5);
  margin-bottom: var(--s-2);
}
.legal h2:first-of-type { margin-top: 0; }
.legal p { color: var(--muted); margin-bottom: var(--s-2); }
/* :not(.btn) matters. `.legal a` is specificity 0,1,1 and `.btn-primary` is
   0,1,0, so without this the prose link colour wins and the button renders
   white text on a white fill. */
.legal a:not(.btn) { color: var(--fg); text-decoration: underline; text-underline-offset: 3px; }
.legal-list { color: var(--muted); padding-left: 1.125rem; margin: 0 0 var(--s-2); }
.legal-list li { margin-bottom: 0.75rem; }
.legal-list strong { color: var(--fg); font-weight: 500; }

/* ---------- Footer, grouped ---------- */

.site-footer .footer-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--s-4);
  padding-bottom: var(--s-4);
}
.footer-col { display: grid; gap: 0.5rem; align-content: start; }
.footer-h {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 0.25rem;
}
.footer-col a { color: var(--muted); text-decoration: none; font-size: 0.875rem; }
.footer-col a:hover { color: var(--fg); }

.site-footer .footer-base {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-3);
  justify-content: space-between;
  padding-top: var(--s-3);
  border-top: 1px solid var(--rule);
}

/* ---------- Start form: two columns, dark ---------- */

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.375rem 1rem;      /* more vertical room than horizontal */
}
.form-grid .field-wide,
.form-grid > div:last-child { grid-column: 1 / -1; }
@media (max-width: 34rem) {
  .form-grid { grid-template-columns: minmax(0, 1fr); }
}

.char-count {
  display: block;
  margin-top: 0.375rem;
  font-size: 0.75rem;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

.consent { margin-top: 0.5rem; }
.consent-row {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  cursor: pointer;
  font-size: 0.9375rem;
  color: var(--fg);
}
.consent-row input[type="checkbox"] {
  width: 1.0625rem;
  height: 1.0625rem;
  margin-top: 0.125rem;
  flex: none;
  accent-color: var(--fg);
  cursor: pointer;
}
/* No border: a bordered box inside a bordered panel inside a bordered field
   set is three frames deep and reads as clutter. Indentation and colour carry
   it instead. */
.consent-note {
  margin-top: 0.5rem;
  padding-left: 1.6875rem;   /* lines up under the checkbox label */
  font-size: 0.75rem;
  line-height: 1.55;
  color: var(--faint);
}
.consent-note a { color: var(--muted); }

/* ---------- Two-column comparison ---------- */

.versus { display: grid; gap: var(--s-3); margin-top: var(--s-5); }
@media (min-width: 52rem) { .versus { grid-template-columns: 1fr 1fr; gap: var(--s-4); } }
.versus-col {
  border: 1px solid var(--rule);
  border-radius: 1.125rem;
  padding: var(--s-4);
  background: var(--bg-raised);
}
.versus-col.is-ours { border-color: var(--rule-firm); }
.versus-col h3 { margin-bottom: var(--s-3); }
.versus-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.875rem; }
.versus-list li {
  display: grid;
  grid-template-columns: 1.125rem 1fr;
  gap: 0.75rem;
  align-items: start;
  font-size: 0.9375rem;
  color: var(--muted);
  line-height: 1.5;
}
.versus-list .mark { color: var(--faint); font-size: 0.875rem; line-height: 1.6; }
.is-ours .versus-list .mark { color: var(--accent); }

/* ---------- Types of apps ---------- */

.types { border-top: 1px solid var(--rule); }
.type-grid {
  list-style: none;
  margin: var(--s-4) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 0.625rem;
}
.type-grid li {
  border: 1px solid var(--rule);
  border-radius: 0.625rem;
  padding: 0.8125rem 1rem;
  font-size: 0.9375rem;
  color: var(--muted);
  background: var(--bg-raised);
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.type-grid li:hover { border-color: var(--rule-firm); color: var(--fg); }
/* Plain centred text under the grid rather than another bordered chip: it is
   a caption on the list, not another item in it. */
.type-more {
  margin-top: var(--s-4);
  text-align: center;
  color: var(--faint);
  font-size: 0.9375rem;
}

/* Small service label above each end-to-end row heading. */
.row-service {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 0.625rem;
  font-weight: 500;
}

/* Icons in the types grid, drawn inline so they stay sharp and take the
   surrounding colour rather than needing a second file per category. */
.type-grid li {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
}
.type-grid svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  color: var(--faint);
  transition: color 0.2s var(--ease);
}
.type-grid li:hover svg { color: rgb(var(--signal)); }

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

/* The full sixteen categories stack into a very long column on a phone. Show
   the six most representative, then say there are more. Desktop keeps all of
   them, where the grid costs nothing. */
/* Ten on desktop fills two clean rows of five; six on a phone fills three rows
   of two. The rest stay in the markup for search engines and for anyone who
   widens the window. */
.type-grid li:nth-child(n+11) { display: none; }

@media (max-width: 40rem) {
  .type-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .type-grid li:nth-child(n+7) { display: none; }
}

/* The split device ran nearly the full column width on a phone. */
@media (max-width: 40rem) {
  .split-media img { width: min(100%, 12.5rem); }
}

/* Work cards are a single column on a phone, so a left-aligned device leaves a
   wide gap to its right. Centre the device; the copy stays left to read. */
@media (max-width: 46rem) {
  .case-media { justify-content: center; }
}
