/* Archer Braddock Private Investigations
   One stylesheet. Every value below comes from a token; no raw colours in
   component rules. Contrast ratios for each pair are recorded in the project
   build notes section 6 and were computed, not estimated. */

/* ---------------------------------------------------------------- tokens */

:root {
  --color-surface: #ffffff;
  --color-surface-alt: #f3f5f8;
  --color-surface-dark: #00102e;

  --color-text: #233452;
  --color-heading: #00102e;
  --color-muted: #666666;
  /* Valid ONLY on --color-surface-dark. Measured 1.00 on surface and 1.09 on
     surface-alt, so using it on a light surface is a guaranteed failure. */
  --color-text-on-dark: #ffffff;

  --color-cta: #d23e3e;
  --color-cta-label: #ffffff;
  --color-link: #ba2c2c;
  --color-link-hover: #8f2222;

  /* The ring is two-tone because no single colour clears 3:1 against all four
     surfaces it can appear over (surface, surface-alt, the dark band, and the
     CTA fill itself). An exhaustive sweep of the luminance axis returned zero
     candidates, so this is not a preference between solid colours. */
  --color-focus: #00102e;
  --color-focus-halo: #ffffff;

  --color-border: #c7cdd6;
  /* Anything a user must perceive to operate a control, which here means every
     form field. --color-border at 1.60 is decorative only. */
  --color-border-strong: #7e8794;
  /* Divider on the dark band only. Decorative, so it carries no contrast
     obligation, but it is tokenised so no raw value sits in a component rule. */
  --color-border-on-dark: rgba(255, 255, 255, 0.2);

  --color-success: #1b6b3a;
  --color-warning: #8a5a00;
  --color-danger: #b3261e;

  --stack:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu,
    Cantarell, "Helvetica Neue", sans-serif;

  --step--1: 0.875rem;
  --step-0: 1rem;
  --step-1: clamp(1.125rem, 1.06rem + 0.32vw, 1.25rem);
  --step-2: clamp(1.3rem, 1.19rem + 0.55vw, 1.6rem);
  --step-3: clamp(1.6rem, 1.36rem + 1.2vw, 2.25rem);
  --step-4: clamp(2rem, 1.45rem + 2.9vw, 4rem);

  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs: 0.75rem;
  --space-s: 1rem;
  --space-m: 1.5rem;
  --space-l: 2rem;
  --space-xl: 3rem;
  --space-2xl: 4rem;
  --space-3xl: 6rem;

  --radius-s: 0.25rem;
  --radius-m: 0.5rem;
  --radius-pill: 999px;
  /* 20px, measured off the live site's own content images. --radius-m (8px)
     is for UI surfaces (cards, inputs); photographs use this larger value. */
  --radius-image: 1.25rem;

  --border-hairline: 1px solid var(--color-border);
  --border-control: 1px solid var(--color-border-strong);
  --border-card: 1px solid var(--color-surface-dark);
  --shadow-card:
    0 1px 2px rgba(0, 16, 46, 0.08), 0 8px 24px rgba(0, 16, 46, 0.06);

  --width-prose: 62ch;
  --width-wide: 1120px;

  --z-header: 100;
  --z-skip-link: 200;

  --duration-fast: 150ms;
  --duration-base: 200ms;
  --ease-out: cubic-bezier(0.2, 0.6, 0.3, 1);
}

/* ---------------------------------------------------------------- base */

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

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

body {
  margin: 0;
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--stack);
  font-size: var(--step-0);
  line-height: 1.6;
}

h1,
h2,
h3,
h4 {
  margin: 0 0 var(--space-s);
  color: var(--color-heading);
  font-weight: 600;
  text-wrap: balance;
}

h1 {
  font-size: var(--step-4);
  line-height: 1.12;
  letter-spacing: -0.015em;
}
h2 {
  font-size: var(--step-3);
  line-height: 1.18;
}
h3 {
  font-size: var(--step-2);
  line-height: 1.25;
}
h4 {
  font-size: var(--step-1);
  line-height: 1.3;
}

p,
ul,
ol {
  margin: 0 0 var(--space-s);
}

/* No measure cap on paragraphs. The live site sets max-width: none and runs
   text the full width of the content lane; capping at --width-prose left every
   page looking like the text had been shoved to one side. --width-prose is
   still available for anywhere a genuinely narrow measure is wanted. */

img {
  max-width: 100%;
  height: auto;
}

/* Inline links keep their underline permanently. --color-link against
   --color-text is 2.07:1, below the 3.0 WCAG 1.4.1 requires where colour
   alone separates a link from surrounding prose, so the underline is what
   satisfies the criterion and is never removed for style. */
a {
  color: var(--color-link);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

a:hover {
  color: var(--color-link-hover);
}

:focus-visible {
  /* Transparent outline preserved so forced-colors mode, which discards
     box-shadow, still renders an indicator. */
  outline: 2px solid transparent;
  outline-offset: 2px;
  box-shadow:
    0 0 0 2px var(--color-focus-halo),
    0 0 0 5px var(--color-focus);
  border-radius: var(--radius-s);
}

@media (forced-colors: active) {
  :focus-visible {
    outline: 3px solid CanvasText;
  }
}

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

.skip-link {
  position: absolute;
  left: var(--space-s);
  top: calc(-1 * var(--space-3xl));
  z-index: var(--z-skip-link);
  padding: var(--space-2xs) var(--space-s);
  background: var(--color-surface);
  color: var(--color-link);
  border-radius: var(--radius-s);
  transition: top var(--duration-fast) var(--ease-out);
}

.skip-link:focus {
  top: var(--space-s);
}

.container {
  max-width: var(--width-wide);
  margin-inline: auto;
  padding-inline: var(--space-s);
}

/* The live /services/ page centres its H1 across the full lane. */
.page-title {
  text-align: center;
}

.hero-image {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-image);
  margin-block: var(--space-m);
}

/* Two-column text-and-photo band, the layout the live site uses on /about/,
   /services/, and /contact/, with the photo alternating sides down the page.

   Text comes FIRST in source on every row, including the rows where the photo
   sits on the left, so the heading is never announced after the image that
   supports it. The `--media-left` modifier reorders visually at the desktop
   breakpoint only; below it every row stacks text then photo, keeping the
   heading ahead of its image on mobile.

   CROPPING, a recorded deviation (DECISIONS.md D24). These photos are cropped
   with `object-fit: cover` to a forced `aspect-ratio`, which the build
   standard's cropping policy otherwise bans outside a card grid. It is done
   here on the site owner's explicit instruction, twice given, that the rebuild
   match the original: the live site crops every one of these images the same
   way (measured `object-fit: cover` and `border-radius: 20px` on its own
   contact page), and showing them uncropped at their native ratios is what
   produced the mismatched, wrong-shaped images the owner rejected. The ratios
   below are measured from the live pages, not invented. */
.media-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-l);
  align-items: center;
  margin-block: var(--space-2xl);
}

.media-row__figure {
  margin: 0;
}

.media-row__media {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-image);
  object-fit: cover;
}

/* Measured off the live site: /about/ runs 0.74 and 0.78, /contact/ 0.85. */
.media-row--tall .media-row__media {
  aspect-ratio: 4 / 5;
  height: auto;
}

/* /services/ runs 1.40 and 1.61. */
.media-row--wide .media-row__media {
  aspect-ratio: 3 / 2;
  height: auto;
}

/* /contact/ lower image runs 1.23. */
.media-row--squarish .media-row__media {
  aspect-ratio: 5 / 4;
  height: auto;
}

.media-row__text > :last-child {
  margin-bottom: 0;
}

@media (min-width: 48rem) {
  .media-row {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-xl);
  }

  .media-row--media-left .media-row__figure {
    order: -1;
  }
}

/* The services page runs one continuous two-column grid and ALTERNATES which
   column the text sits in, service by service, all the way down: odd entries
   left, even entries right. Measured off all fifteen headings on the live
   page, which alternate without exception. The first two entries also carry a
   photo, placed in the opposite cell from their text, so the zigzag reads the
   same whether or not a given row has an image.

   Getting this wrong is what made the rebuilt page look malformed: every
   image-less entry was pinned to the left column, so the page ran two proper
   two-column rows and then fourteen half-empty ones. */
@media (min-width: 48rem) {
  .service-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-xl);
    align-items: center;
  }

  .service-row--right .media-row__text {
    grid-column: 2;
  }
}

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

.site-header {
  background: var(--color-surface-dark);
  color: var(--color-text-on-dark);
  padding-block: var(--space-xs);
}

.site-header__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-m);
}

.site-header__wordmark {
  color: var(--color-text-on-dark);
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
}

.site-header__wordmark:hover {
  color: var(--color-text-on-dark);
  text-decoration: underline;
}

.site-header__tel {
  color: var(--color-text-on-dark);
  font-weight: 600;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.site-header__tel:hover {
  color: var(--color-text-on-dark);
  text-decoration: underline;
}

/* Pushed to the far right only once the bar is a single row. Below this the
   header wraps, and an auto margin would strand the number alone on its own
   right-aligned row. */
@media (min-width: 48rem) {
  .site-header__tel {
    margin-left: auto;
  }
}

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-m);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* min-height rather than padding: an ordinary padding-block of .5rem measured
   34px here, under the 44px floor. */
.site-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--space-3xs);
  color: var(--color-text-on-dark);
  text-decoration: none;
}

.site-nav a:hover {
  color: var(--color-text-on-dark);
  text-decoration: underline;
}

/* :not(.btn) is load-bearing. Without it this selector ties .btn on
   specificity, wins on source order, and repaints a filled button's label in
   the same token as its fill: 1:1 contrast, invisible, while the page still
   scores 100. */
.site-nav a[aria-current]:not(.btn) {
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

.site-footer {
  background: var(--color-surface-dark);
  color: var(--color-text-on-dark);
  padding-block: var(--space-xl) var(--space-l);
  margin-top: var(--space-3xl);
}

.site-footer a {
  color: var(--color-text-on-dark);
}

.site-footer a:hover {
  color: var(--color-text-on-dark);
}

.site-footer p {
  max-width: none;
}

.site-footer__grid {
  display: grid;
  gap: var(--space-l);
  grid-template-columns: 1fr;
}

.site-footer__brand {
  margin-bottom: var(--space-2xs);
  color: var(--color-text-on-dark);
  font-size: var(--step-1);
  font-weight: 600;
}

.site-footer__heading {
  color: var(--color-text-on-dark);
  font-size: var(--step-1);
  margin-bottom: var(--space-2xs);
}

.site-footer__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-footer__list li {
  margin-bottom: var(--space-3xs);
}

.site-footer__legal {
  margin-top: var(--space-l);
  padding-top: var(--space-m);
  border-top: 1px solid var(--color-border-on-dark);
  font-size: var(--step--1);
}

@media (min-width: 48rem) {
  .site-footer__grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ---------------------------------------------------------------- sections */

.section {
  padding-block: var(--space-2xl);
}

.section--alt {
  background: var(--color-surface-alt);
}

.section--dark {
  background: var(--color-surface-dark);
  color: var(--color-text-on-dark);
}

.section--dark h1,
.section--dark h2,
.section--dark h3,
.section--dark h4 {
  color: var(--color-text-on-dark);
}

.prose {
  max-width: var(--width-prose);
}

.lead {
  font-size: var(--step-1);
}

.eyebrow {
  margin-bottom: var(--space-2xs);
  color: var(--color-muted);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ---------------------------------------------------------------- button */

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  margin-top: var(--space-m);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-height: 44px;
  padding: var(--space-2xs) var(--space-m);
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--duration-base) var(--ease-out),
    color var(--duration-base) var(--ease-out);
}

.btn--cta {
  background: var(--color-cta);
  color: var(--color-cta-label);
}

.btn--cta:hover {
  background: var(--color-link-hover);
  color: var(--color-cta-label);
}

.btn--ghost {
  background: transparent;
  border-color: currentColor;
  color: var(--color-link);
}

.btn--ghost:hover {
  color: var(--color-link-hover);
}

.section--dark .btn--ghost,
.site-header .btn--ghost {
  color: var(--color-text-on-dark);
}

.btn[aria-disabled="true"] {
  pointer-events: none;
  background: var(--color-muted);
  color: var(--color-surface);
}

/* ---------------------------------------------------------------- breadcrumb */

.breadcrumb {
  padding-block: var(--space-s);
  font-size: var(--step--1);
}

.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-2xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.breadcrumb li + li::before {
  content: "/";
  margin-right: var(--space-2xs);
  color: var(--color-muted);
}

/* ---------------------------------------------------------------- figure */

.figure {
  margin: 0 0 var(--space-l);
}

.figure > img,
.figure > .media-link > img {
  border-radius: var(--radius-m);
}

/* A lazy linked image with width and height both auto has no intrinsic size,
   so the anchor collapses to roughly 2px and target-size fails before the
   bytes land. One definite dimension reserves the box. Scoped to this parent
   deliberately: a ratio-cropped card lives elsewhere and must not inherit it. */
.figure > .media-link > img {
  display: block;
  width: 100%;
}

.figcaption {
  margin-top: var(--space-2xs);
  color: var(--color-muted);
  font-size: var(--step--1);
}

.media-link {
  display: block;
  text-decoration: none;
}

/* ---------------------------------------------------------------- cards */

.card-grid {
  display: grid;
  gap: var(--space-m);
  grid-template-columns: 1fr;
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 40rem) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 64rem) {
  .card-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

.card {
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: var(--border-hairline);
  border-radius: var(--radius-m);
  overflow: hidden;
  transition:
    transform var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out);
}

.card:hover,
.card:focus-within {
  transform: translateY(-2px);
  box-shadow: var(--shadow-card);
}

.card__media {
  display: block;
  width: 100%;
  height: auto;
}

.card__body {
  padding: var(--space-m);
}

.card__title {
  margin-bottom: var(--space-2xs);
  font-size: var(--step-2);
}

.card__title a {
  color: var(--color-heading);
  text-decoration: none;
}

.card__title a:hover {
  color: var(--color-link-hover);
  text-decoration: underline;
}

/* ---------------------------------------------------------------- jump list */

.jump-list {
  margin-block: var(--space-m) var(--space-2xl);
  padding: var(--space-m);
  background: var(--color-surface-alt);
  border-radius: var(--radius-m);
}

.jump-list ul {
  display: grid;
  gap: var(--space-3xs) var(--space-m);
  grid-template-columns: 1fr;
  margin: 0;
  padding: 0;
  list-style: none;
}

.jump-list a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

@media (min-width: 40rem) {
  .jump-list ul {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 64rem) {
  .jump-list ul {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ---------------------------------------------------------------- quotes */

.quote {
  margin: 0;
  padding: var(--space-m);
  background: var(--color-surface);
  border: var(--border-hairline);
  border-left: 4px solid var(--color-cta);
  border-radius: var(--radius-m);
}

.quote p {
  max-width: none;
}

.quote footer {
  margin-top: var(--space-2xs);
  color: var(--color-muted);
  font-size: var(--step--1);
  font-weight: 600;
}

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

@media (min-width: 64rem) {
  .quote-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* ---------------------------------------------------------------- icons */

.icon {
  width: 2rem;
  height: 2rem;
  fill: none;
  stroke: var(--color-cta);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------------------------------------------------------------- forms */

.field {
  margin-bottom: var(--space-m);
}

.field label {
  display: block;
  margin-bottom: var(--space-3xs);
  font-weight: 600;
}

.field input,
.field textarea {
  width: 100%;
  min-height: 44px;
  padding: var(--space-2xs) var(--space-xs);
  border: var(--border-control);
  border-radius: var(--radius-s);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
}

.field textarea {
  min-height: 8rem;
  resize: vertical;
}

.field__error {
  margin-top: var(--space-3xs);
  color: var(--color-danger);
  font-size: var(--step--1);
  font-weight: 600;
}

.field__required {
  color: var(--color-danger);
}

/* Honeypot. Off-canvas rather than display:none so a bot reading computed
   styles still sees a rendered field. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-note {
  color: var(--color-muted);
  font-size: var(--step--1);
}

.alert {
  padding: var(--space-m);
  border-radius: var(--radius-m);
  border-left: 4px solid var(--color-danger);
  background: var(--color-surface-alt);
}

.alert--success {
  border-left-color: var(--color-success);
}

/* ---------------------------------------------------------------- motion */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .card:hover,
  .card:focus-within {
    transform: none;
  }
}
