/* ==========================================================================
   Plan Z Motorsports, shared stylesheet.
   One dark theme, site-wide. Never prefers-color-scheme: the background is
   dark on every page and does not follow the operating system.
   ========================================================================== */

/* --------------------------------------------------------------------------
   TOKENS

   Brand accent is motorsport red, the hero is the red 2011 Nissan 370Z.
   Links are light coral on black. Accent is used only for large, graphical,
   or UI elements (kicker headings, rules, active states, focus ring), so it
   needs >= 3:1 (it also clears large-text >= 3:1).

   All values below are WCAG-verified (ratios in comments), measured on both
   --bg and --surface (cards), with margin so the gate has no borderline
   value. Non-color distinction is handled structurally too: links carry an
   underline, focus is a 3px solid outline with a 2px offset, invalid fields
   go dashed.

   Buttons stay monochrome (fg fill, bg text, about 21:1): white text on red
   is only 3.41:1, too low for body-size button labels. The red brands
   everything else.
   -------------------------------------------------------------------------- */

:root,
[data-theme="dark"] {
  --bg: #000000;
  --fg: #f2f2f2;
  --fg-muted: #9a9a9a; /* 7.46:1 on #000, 6.95:1 on surface */
  --accent: #ff453a; /* 6.16:1 on #000 (UI, large text) */
  --link: #ff6b5e; /* 7.52:1 on #000, 7.00:1 on surface */
  --link-hover: #ff9a8c; /* 10.25:1, perceptibly lighter than --link */
  --border: #666666; /* 3.66:1 on #000 */
  --focus-ring: #ff453a; /* = --accent, 6.16:1 on #000 */

  --surface: #0c0c0c;
  --shadow: 0 1px 3px rgb(0 0 0 / 0.6);
}

:root {
  /* System stack, no webfont, no 100 KB budget line to defend. */
  --font-sans:
    system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
    sans-serif;

  /* Fluid type: mobile-first, clamps at the container width. */
  --step--1: clamp(0.83rem, 0.79rem + 0.2vw, 0.94rem);
  --step-0: clamp(1rem, 0.95rem + 0.25vw, 1.13rem);
  --step-1: clamp(1.2rem, 1.11rem + 0.44vw, 1.5rem);
  --step-2: clamp(1.44rem, 1.29rem + 0.75vw, 2rem);
  --step-3: clamp(1.73rem, 1.48rem + 1.23vw, 2.66rem);
  --step-4: clamp(2.07rem, 1.68rem + 1.96vw, 3.55rem);

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

  --measure: 68ch;
  --container: 72rem;
  --radius: 4px;

  /* Shared height for the /about/ photo row. 16rem keeps the widest master
     (AR 1.333) at 341px, inside the 380px mobile content box, so no photo
     is capped by max-width and heights stay uniform at every viewport. */
  --gallery-media-h: 16rem;
}

/* --------------------------------------------------------------------------
   RESET
   -------------------------------------------------------------------------- */

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

* {
  margin: 0;
}

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

body {
  min-height: 100svh;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Media never overflows and never causes CLS: width and height are on every
   <img>. */
img,
picture,
svg,
video {
  display: block;
  max-width: 100%;
  height: auto;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

h1,
h2,
h3,
h4 {
  line-height: 1.15;
  text-wrap: balance;
  font-weight: 700;
}

p,
li {
  text-wrap: pretty;
}

h1 {
  font-size: var(--step-4);
}
h2 {
  font-size: var(--step-3);
}
h3 {
  font-size: var(--step-2);
}
h4 {
  font-size: var(--step-1);
}

ul[class],
ol[class] {
  list-style: none;
  padding: 0;
}

/* --------------------------------------------------------------------------
   MOTION, honor prefers-reduced-motion (blocking accessibility requirement)
   -------------------------------------------------------------------------- */

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

/* --------------------------------------------------------------------------
   FOCUS, visible, never color-only.
   The 2px offset plus solid ring reads against any backdrop and does not
   depend on the accent or link hues.
   -------------------------------------------------------------------------- */

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

:focus:not(:focus-visible) {
  outline: none;
}

.skip-link {
  position: absolute;
  left: var(--space-xs);
  top: var(--space-xs);
  z-index: 100;
  padding: var(--space-xs) var(--space-m);
  background: var(--bg);
  color: var(--fg);
  border: 2px solid var(--fg);
  border-radius: var(--radius);
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: translateY(0);
}

/* --------------------------------------------------------------------------
   LINKS, underline carries the meaning, color is decoration.
   Satisfies "distinguishable by more than color" independent of hue.
   -------------------------------------------------------------------------- */

a {
  color: var(--link);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

a:hover {
  color: var(--link-hover);
  text-decoration-thickness: 2px;
}

/* --------------------------------------------------------------------------
   LAYOUT PRIMITIVES
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-m);
}

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

.prose > * + * {
  margin-block-start: var(--space-m);
}

/* Subheads get real breathing room above them, never on the first child.
   This is the single source of "space before a header" across the site. */
.prose :is(h2, h3):not(:first-child) {
  margin-block-start: var(--space-xl);
}

.prose h3:not(:first-child) {
  margin-block-start: var(--space-l);
}

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

/* Consistent vertical rhythm between a section's blocks (prose, figure,
   gallery, value list). */
.section > * + * {
  margin-block-start: var(--space-l);
}

/* Home only. The hero and its three content sections stack directly with
   no border or background between them, so two adjacent space-2xl
   paddings read as one oversized gap instead of two intentional ones.
   Collapse the inner padding so consecutive blocks share a single
   space-2xl gap. Scoped to .page-home so single-section pages (which pair
   one .section with .cta-band, already visually separated by a border)
   are unaffected. */
.page-home .hero + .section,
.page-home .section + .section {
  padding-block-start: 0;
}

/* Utility classes replacing inline style attributes. */
.center-inline {
  margin-inline: auto;
}

/* Combined selector, not bare .figure--narrow: .figure (below) resets
   margin to 0 with equal specificity, which silently wins on source order
   alone and breaks the centering it sets here. */
.figure.figure--narrow {
  max-width: 28rem;
  margin-inline: auto;
}

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

/* Auto-fit grid: no media queries, collapses to one column on mobile. */
.grid {
  display: grid;
  gap: var(--space-l);
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
}

/* --------------------------------------------------------------------------
   HEADER, NAV, FOOTER
   -------------------------------------------------------------------------- */

.site-header {
  border-block-end: 1px solid var(--border);
  background: var(--bg);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  padding-block: var(--space-s);
}

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

.site-nav a {
  text-decoration: none;
  font-size: var(--step--1);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.site-nav a:hover {
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

/* Current page marked structurally (aria-current), not by color alone: it
   keeps the link color and adds weight plus an underline. Excludes .btn,
   whose fill is --fg; recoloring its text to --fg would render it
   invisible. */
.site-nav a[aria-current="page"]:not(.btn) {
  color: var(--link);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

.site-nav a.btn[aria-current="page"] {
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

.site-footer {
  border-block-start: 1px solid var(--border);
  padding-block: var(--space-xl);
  color: var(--fg-muted);
  font-size: var(--step--1);
}

/* --------------------------------------------------------------------------
   COMPONENTS
   -------------------------------------------------------------------------- */

.btn {
  display: inline-block;
  padding: var(--space-s) var(--space-l);
  border: 2px solid var(--fg);
  border-radius: var(--radius);
  background: var(--fg);
  color: var(--bg);
  font-weight: 700;
  text-decoration: none;
  letter-spacing: 0.02em;
}

.btn:hover {
  background: transparent;
  color: var(--fg);
}

.btn--ghost {
  background: transparent;
  color: var(--fg);
}

.btn--ghost:hover {
  background: var(--fg);
  color: var(--bg);
}

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

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

/* Mixed-orientation masters normalized to one card aspect. This is a
   display crop via object-fit, not editorial image work. The sole
   sanctioned exception to the no-crop rule, scoped to /cars/ (the only
   page using .card__media): the fleet grid needs uniform card heights. */
.card__media {
  aspect-ratio: 3 / 2;
  width: 100%;
  object-fit: cover;
}

.breadcrumb {
  font-size: var(--step--1);
  color: var(--fg-muted);
  padding-block: var(--space-m);
}

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

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

/* --------------------------------------------------------------------------
   HERO, contained width, mobile-first. Sits on --bg with no gradient
   overlay, so a full-bleed dark section cannot band.
   -------------------------------------------------------------------------- */

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

/* The figure has margin: 0 by design (so it sits flush inside other
   layouts), so the hero needs its own rule to open space before the
   kicker that follows it. Scoped to .hero so it cannot double up with
   .prose > * + * wherever .kicker is used elsewhere as a first child. */
.hero .kicker {
  margin-block-start: var(--space-l);
}

.hero__media {
  width: 100%;
  height: auto;
  border-radius: var(--radius);
}

.hero__title {
  margin-block-start: var(--space-l);
}

.hero__lede {
  max-width: var(--measure);
  margin-block-start: var(--space-m);
  font-size: var(--step-1);
  color: var(--fg-muted);
}

.hero__cta {
  margin-block-start: var(--space-l);
}

/* --------------------------------------------------------------------------
   FORMS, labels always associated, errors via aria-describedby.
   Error state carries an icon and text prefix, never color alone.
   -------------------------------------------------------------------------- */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.field label {
  font-weight: 600;
}

.field .hint {
  font-size: var(--step--1);
  color: var(--fg-muted);
}

.field input,
.field textarea,
.field select {
  padding: var(--space-s);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--fg);
  width: 100%;
}

.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"],
.field select[aria-invalid="true"] {
  border-width: 2px;
  border-style: dashed; /* non-color cue */
}

.field .error {
  font-size: var(--step--1);
  font-weight: 600;
}

.field .error::before {
  content: "\26A0 ";
}

/* Honeypot, hidden from humans, present for bots. Not display:none, some
   bots skip those. Kept out of the tab order and the accessibility tree. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   CONTENT PATTERNS
   -------------------------------------------------------------------------- */

/* Eyebrow label. --accent as text clears 4.5:1 only on --bg (6.16:1), not
   on --surface, so kickers must sit on the page background, never inside a
   card. */
.kicker {
  display: block;
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  margin-block-end: var(--space-xs);
}

.lede {
  font-size: var(--step-1);
  color: var(--fg-muted);
  max-width: var(--measure);
}

/* Checkmark value list. The check is decorative (aria-hidden), meaning is
   the text. */
.value-list {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--space-m);
}

.value-list li {
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  gap: var(--space-s);
  align-items: start;
}

.value-list .tick {
  color: var(--accent);
  font-weight: 700;
}

/* Hub/card grid uses .grid + .card. Card title links inherit --link. */
.card__title {
  font-size: var(--step-1);
  margin-block-end: var(--space-2xs);
}

.card__title a {
  text-decoration: none;
}

.card__title a:hover {
  text-decoration: underline;
  text-decoration-thickness: 2px;
}

/* Whole-card click target without nesting interactive elements: the title
   link is stretched over the card via ::after. */
.card--link {
  position: relative;
}

.card--link .card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* Call-to-action band, sits on --bg. */
.cta-band {
  text-align: center;
  padding-block: var(--space-2xl);
  border-block: 1px solid var(--border);
}

.cta-band h2 {
  margin-block-end: var(--space-m);
}

.cta-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-m);
  justify-content: center;
  margin-block-start: var(--space-l);
}

/* Footer internals */
.site-footer__grid {
  display: grid;
  gap: var(--space-l);
  grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
}

.site-footer nav ul {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--space-2xs);
}

.site-footer a {
  color: var(--link);
}

.site-footer__logo {
  width: 120px;
  height: auto;
}

.risk-note {
  font-weight: 600;
  color: var(--fg);
}

/* --------------------------------------------------------------------------
   CAR CARDS AND BADGES
   -------------------------------------------------------------------------- */

.car-card .card__body {
  display: grid;
  gap: var(--space-s);
}

.car-card .spec {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-2xs);
  font-size: var(--step--1);
  color: var(--fg-muted);
}

/* Transmission badge, the driver filter. Text plus border carry it, not
   color alone; border uses --accent, which clears 3:1. */
.badge {
  display: inline-block;
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.15em 0.6em;
  border: 2px solid var(--accent);
  border-radius: 999px;
  color: var(--fg);
}

/* Definition list for spec sheets and process timelines. */
.deflist {
  display: grid;
  gap: var(--space-s);
}

.deflist div {
  display: grid;
  gap: var(--space-2xs);
  padding-block-end: var(--space-s);
  border-block-end: 1px solid var(--border);
}

.deflist dt {
  font-weight: 700;
}

.deflist dd {
  margin: 0;
  color: var(--fg-muted);
}

/* --------------------------------------------------------------------------
   SCHEDULE
   -------------------------------------------------------------------------- */

.event-list {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--space-m);
}

.event {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-xs) var(--space-m);
  padding: var(--space-m) var(--space-l);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.event__date {
  font-weight: 700;
  min-width: 14rem;
}

.event__venue {
  color: var(--fg-muted);
}

.event .status-tag {
  margin-inline-start: auto;
}

/* Marks an elapsed schedule row without relying on color alone: the
   "Completed" status tag carries the meaning, the muted date de-emphasizes
   it. Never opacity here: it composites the row against --bg and would
   silently drag --fg-muted on --surface under the 4.5:1 floor. */
.is-past .event__date {
  color: var(--fg-muted);
}

.status-tag {
  display: inline-block;
  font-size: var(--step--1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.1em 0.5em;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--fg-muted);
}

/* --------------------------------------------------------------------------
   FORMS (layout)
   -------------------------------------------------------------------------- */

.form {
  display: grid;
  gap: var(--space-l);
  max-width: 40rem;
}

.form .required {
  color: var(--accent);
}

.timeline {
  list-style: none;
  padding: 0;
  counter-reset: step;
  display: grid;
  gap: var(--space-m);
}

.timeline li {
  display: grid;
  grid-template-columns: 2rem 1fr;
  gap: var(--space-s);
  align-items: start;
}

.timeline li::before {
  counter-increment: step;
  content: counter(step);
  font-weight: 700;
  color: var(--accent);
  border: 2px solid var(--accent);
  border-radius: 999px;
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
}

.terms {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-l);
  background: var(--surface);
}

/* The reset zeroes every margin, and .terms is plain block content (not
   .prose), so without this its heading and paragraphs run together with
   no visible break. Mirrors .prose > * + *. */
.terms > * + * {
  margin-block-start: var(--space-m);
}

/* --------------------------------------------------------------------------
   FIGURE, CAPTION, FULL-SIZE LINK

   Content photos link their original master in images/ in a new tab. The
   anchor sits between .figure and the image, so the radius rule must list
   the .media-link variants or the image renders sharp-edged.
   -------------------------------------------------------------------------- */

/* :where() carries zero specificity, unlike a bare .figure selector. A
   normal-specificity margin reset here would keep winning cascade ties
   against any single-class spacing rule that targets a sibling of
   .figure (source order breaks the tie), silently cancelling that
   spacing. This was the exact cause of a zero-gap bug between the
   schedule page's final event and its photo: .section > * + * lost to
   .figure on source order alone. Zeroing the reset's specificity lets
   every such rule win as intended, without a one-off override per
   adjacency. */
:where(.figure) {
  margin: 0;
}

/* A figure that directly follows body copy needs breathing room; .figure
   itself stays margin-free so it can sit flush inside other layouts. */
.prose + .figure {
  margin-block-start: var(--space-xl);
}

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

.figcaption {
  margin-block-start: var(--space-xs);
  font-size: var(--step--1);
  color: var(--fg-muted);
  text-align: center;
  font-style: italic;
}

/* Caption inside a card sits under the media, above the body text. */
.card .figcaption {
  text-align: left;
  font-style: normal;
  padding-inline: var(--space-l);
  padding-block-start: var(--space-s);
}

.media-link {
  display: block;
  text-decoration: none;
  border-radius: var(--radius);
}

.media-link:hover img {
  opacity: 0.92;
}

/* Lazy images inside a link must reserve their box before the bytes
   arrive. With width and height both auto, an unloaded image has no
   intrinsic size, the anchor collapses to about 2px, and WCAG target-size
   fails. One definite dimension lets the other resolve from the
   width/height attributes' aspect ratio.

   Standalone figures take a definite width. Scoped to `.figure >` so it
   cannot reach /cars/, whose .card__media cards are <li> children, not
   figures: giving those height:auto would silently undo their 3/2 crop. */
.figure > .media-link > img {
  width: 100%;
  height: auto;
}

/* --------------------------------------------------------------------------
   ABOUT PHOTO ROW, /about/ only. Masters are mixed-orientation (0.564 to
   1.333), so equal widths give wildly unequal heights. Uniformity comes
   from a shared max-height instead: every photo is the same height, its
   width follows its own aspect ratio, nothing is cropped.
   -------------------------------------------------------------------------- */

.gallery {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: var(--space-l) var(--space-m);
}

.gallery .figure {
  flex: 0 1 auto;
  max-width: 100%;
}

/* The caption must not set the figure's width: its text is far wider than
   a portrait photo, which would pad each figure out and wrap the row
   early. width:0 removes it from intrinsic sizing; min-width:100% then
   stretches it back to whatever width the image settled on. */
.gallery .figcaption {
  width: 0;
  min-width: 100%;
}

.gallery img {
  margin-inline: auto;
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

/* Gallery figures take a definite height instead, which is what makes the
   row uniform. This must outrank the .figure > .media-link > img rule
   above (0,3,1 vs 0,2,1), or the images revert to equal widths and ragged
   heights. object-fit: contain is a guard, not a crop: it engages only if
   max-width binds, and letterboxes rather than cuts. */
.gallery .figure > .media-link > img {
  width: auto;
  height: var(--gallery-media-h);
  max-width: 100%;
  object-fit: contain;
}

/* Below about 376px the content box is narrower than the landscape master
   at 16rem tall, so max-width would cap it and it alone would come up
   short. Lower the shared height until the widest photo fits, keeping the
   row uniform. */
@media (max-width: 23.5rem) {
  :root {
    --gallery-media-h: 13.5rem;
  }
}

/* --------------------------------------------------------------------------
   MOSAIC, /gallery/ only. CSS columns, not grid: a column layout lets
   every photo keep its own height, so the 34 masters (aspect ratios 0.544
   to 1.501) pack tightly with no crop and no forced aspect-ratio. Each
   item takes the full column width and resolves its own height from the
   width/height attributes, via the .figure > .media-link > img rule above.

   break-inside: avoid keeps a figure from splitting its caption across a
   column boundary. Columns fill top to bottom, so reading order is per
   column, which is fine for an unordered set of photos.
   -------------------------------------------------------------------------- */

.mosaic {
  column-count: 1;
  column-gap: var(--space-m);
}

.mosaic__item {
  break-inside: avoid;
  margin-block-end: var(--space-l);
}

@media (min-width: 40rem) {
  .mosaic {
    column-count: 2;
  }
}

@media (min-width: 64rem) {
  .mosaic {
    column-count: 3;
  }
}
