/* ==========================================================================
   Elevate — components.css
   Buttons, cards, placeholder imagery, before/after, accordion, forms.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Buttons — pill radius chosen site-wide
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-size: var(--fs-small);
  font-weight: 600;
  line-height: 1;
  padding: 15px var(--space-6);
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease-standard),
    color var(--dur-fast) var(--ease-standard),
    transform var(--dur-fast) var(--ease-standard),
    border-color var(--dur-fast) var(--ease-standard);
}
.btn--primary { background: var(--accent); color: var(--white); }
.btn--primary:hover { background: var(--accent-strong); transform: translateY(-2px); }
/* Every button gets a real press, not just the primary */
.btn:active { transform: translateY(0) scale(0.98); transition-duration: 90ms; }

/* On dark/photo backgrounds the primary button inverts to a white pill */
.hero .btn--primary,
.on-ink .btn--primary,
.section--ink .btn--primary { background: var(--white); color: var(--ink); }
.hero .btn--primary:hover,
.on-ink .btn--primary:hover,
.section--ink .btn--primary:hover { background: var(--plaster); }

.btn--secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--line);
}
.btn--secondary:hover { background: var(--sand); }

/* The photo hero counts as a dark surface too — without this the outline button
   renders ink-on-photo and disappears. */
.hero .btn--secondary,
.on-ink .btn--secondary,
.section--ink .btn--secondary { color: var(--white); border-color: rgba(255, 255, 255, 0.45); }
.hero .btn--secondary:hover,
.on-ink .btn--secondary:hover,
.section--ink .btn--secondary:hover { background: rgba(255, 255, 255, 0.12); border-color: rgba(255, 255, 255, 0.7); }

/* Text link with drawing underline */
.link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 600;
  font-size: var(--fs-small);
  color: var(--accent);
  position: relative;
}
.section--ink .link { color: var(--accent-soft); }
.link::after {
  content: "";
  position: absolute;
  left: 0; bottom: -3px;
  width: 100%; height: 1.5px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease-standard);
}
.link:hover::after { transform: scaleX(1); }
.link .link__arrow { transition: transform var(--dur-fast) var(--ease-standard); }
.link:hover .link__arrow { transform: translateX(3px); }

/* --------------------------------------------------------------------------
   Placeholder imagery system (design.md imagery)
   Concrete-toned block, "PLACEHOLDER" label + caption, fixed aspect ratio.
   -------------------------------------------------------------------------- */
.ph {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  text-align: center;
  background: var(--sand);
  background-image:
    repeating-linear-gradient(45deg,
      rgba(20, 23, 26, 0.025) 0 12px,
      transparent 12px 24px);
  color: var(--stone);
  border-radius: var(--radius-md);
  overflow: hidden;
  padding: var(--space-5);
}
.ph[data-ratio="16-9"] { aspect-ratio: 16 / 9; }
.ph[data-ratio="4-3"]  { aspect-ratio: 4 / 3; }
.ph[data-ratio="3-2"]  { aspect-ratio: 3 / 2; }
.ph[data-ratio="1-1"]  { aspect-ratio: 1 / 1; }
.ph__tag {
  font-family: var(--font-body);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
.ph__caption {
  font-size: var(--fs-small);
  max-width: 32ch;
  color: var(--stone);
}

/* Real photos reuse the placeholder ratios so swapping one in shifts nothing. */
.ph--img { padding: 0; background-image: none; }
.ph--img > img { width: 100%; height: 100%; object-fit: cover; }

/* --------------------------------------------------------------------------
   Service card
   -------------------------------------------------------------------------- */
.card {
  display: flex;
  flex-direction: column;
  /* Every card fills its grid row, so a two-line heading in one card can no longer
     leave its neighbours short. */
  height: 100%;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: transform var(--dur-base) var(--ease-standard),
    box-shadow var(--dur-base) var(--ease-standard),
    border-color var(--dur-base) var(--ease-standard);
}
.card:hover,
.card:focus-within { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--accent); }
.card__media { overflow: hidden; }
.card__media .ph { border-radius: 0; }
.card__media img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.card__media img,
.card__media .ph { transition: transform var(--dur-slow) var(--ease-standard); }
.card:hover .card__media img,
.card:focus-within .card__media img,
.card:hover .card__media .ph { transform: scale(1.045); }
.card:active { transform: translateY(-1px); transition-duration: var(--dur-fast); }
.card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6);
  flex: 1;
}
.card__body p { color: var(--stone); font-size: var(--fs-small); }
.card__body .link { margin-top: var(--space-2); }
/* Pinned to the bottom of the body, so the "View X" links sit on one line across the
   row whether the heading above them wrapped or not. Scoped to the service cards:
   the CTA card has no image and centres its own content, so pinning would strand its
   link at the foot of an empty panel. */
.card--link .card__body .link { margin-top: auto; padding-top: var(--space-4); }
.card--link { color: inherit; }

/* Solid pine panel closing the services grid — the one place the accent fills a whole
   surface, which works because it is a call to action sitting in a row of photos. */
.card--cta { background: var(--accent); border-color: var(--accent); }
.card--cta .card__body { justify-content: center; }
.card--cta h3 { color: var(--white); }
.card--cta p { color: var(--accent-soft); }
.card--cta .link { color: var(--white); }
.card--cta:hover { border-color: var(--accent-strong); background: var(--accent-strong); }

/* --------------------------------------------------------------------------
   Process steps
   -------------------------------------------------------------------------- */
.steps { counter-reset: step; display: grid; gap: var(--space-6); }
@media (min-width: 641px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1025px) { .steps { grid-template-columns: repeat(4, 1fr); } }
.step {
  counter-increment: step;
  padding-top: var(--space-5);
  border-top: 2px solid var(--accent);
}
.step::before {
  content: counter(step, decimal-leading-zero);
  display: block;
  font-family: var(--font-head);
  font-size: 1.75rem;
  font-weight: 600;
  color: var(--accent);
  margin-bottom: var(--space-3);
}
.step h3 { margin-bottom: var(--space-2); }
.step p { color: var(--stone); font-size: var(--fs-small); }
.section--ink .step p { color: var(--stone); }

/* --------------------------------------------------------------------------
   Testimonials
   -------------------------------------------------------------------------- */
.quote {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-7);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.quote blockquote {
  font-family: var(--font-head);
  font-size: var(--fs-lead);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.01em;
}
.quote figcaption { font-size: var(--fs-small); color: var(--stone); }
.quote figcaption strong { color: var(--ink); }

/* --------------------------------------------------------------------------
   Review carousel — sideways scroller
   Native overflow + scroll-snap does the work. The buttons only nudge
   scrollLeft, so drag, trackpad, touch, and arrow keys all keep working with
   no JS at all; JS off just leaves two dead buttons beside a scrollable strip.
   -------------------------------------------------------------------------- */
.carousel__header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-6);
  margin-bottom: clamp(1.75rem, 3vw, 2.5rem);
}
.carousel__nav { display: none; gap: var(--space-3); flex: none; }
@media (min-width: 641px) { .carousel__nav { display: flex; } }

.carousel__btn {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--paper);
  color: var(--ink);
  transition: background var(--dur-fast) var(--ease-standard),
    border-color var(--dur-fast) var(--ease-standard);
}
.carousel__btn svg { width: 20px; height: 20px; }
.carousel__btn:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.carousel__btn:active { transform: scale(0.95); }

.carousel__track {
  display: flex;
  gap: var(--space-5);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* Gutter as padding, not margin, so the first card lines up with the container
     while the strip itself still runs to the viewport edge. scroll-padding keeps
     a snapped card off the very edge. */
  padding: var(--space-2) var(--container-gutter) var(--space-6);
  scroll-padding-inline: var(--container-gutter);
  scrollbar-width: thin;
  list-style: none;
}
.carousel__track > li { flex: 0 0 min(84vw, 360px); scroll-snap-align: start; display: flex; }
/* The track is focusable so keyboard users can scroll it; make that visible. */
.carousel__track:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.review {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-6);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.review__head { display: flex; align-items: center; gap: var(--space-3); }
.review__avatar {
  flex: none;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-head);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.04em;
}
.review__who { display: flex; flex-direction: column; line-height: 1.3; }
.review__who span { font-size: var(--fs-small); color: var(--stone); }
/* Ink, not gold: the palette carries one chromatic accent and it is pine, which is
   reserved for interaction. See design.md color/usage accent-budget. */
.review__stars { color: var(--ink); letter-spacing: 0.14em; line-height: 1; }
.review blockquote { font-size: var(--fs-small); line-height: 1.65; color: var(--stone); }
.review__project {
  margin-top: auto;      /* pins the project line to the bottom on every card height */
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   FAQ accordion
   -------------------------------------------------------------------------- */
.faq { border-top: 1px solid var(--line); }
.faq__item { border-bottom: 1px solid var(--line); }
.faq__q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  text-align: left;
  padding: var(--space-5) 0;
  background: transparent;
  border: none;
  font-family: var(--font-head);
  font-size: var(--fs-h3);
  font-weight: 600;
  color: var(--ink);
}
.faq__icon { flex: none; position: relative; width: 20px; height: 20px; }
.faq__icon::before,
.faq__icon::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 14px; height: 2px;
  background: var(--accent);
  transform: translate(-50%, -50%);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.faq__icon::after { transform: translate(-50%, -50%) rotate(90deg); }
.faq__q[aria-expanded="true"] .faq__icon::after { transform: translate(-50%, -50%) rotate(0); }
.faq__panel {
  overflow: hidden;
  height: 0;
  transition: height var(--dur-base) var(--ease-standard);
}
.faq__panel-inner { padding-bottom: var(--space-5); color: var(--stone); max-width: 68ch; }

/* --------------------------------------------------------------------------
   Before / after slider
   -------------------------------------------------------------------------- */
.ba {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 2;
  border-radius: var(--radius-md);
  overflow: hidden;
  user-select: none;
  touch-action: pan-y;
  border: 1px solid var(--line);
}
/* Portrait source pairs (e.g. the bathroom set) get a taller frame than the 3:2 default */
.ba--tall { aspect-ratio: 4 / 5; max-width: 560px; margin-inline: auto; }
.ba__layer { position: absolute; inset: 0; }
.ba__layer .ph { height: 100%; border-radius: 0; }
.ba__layer img { width: 100%; height: 100%; object-fit: cover; }
.ba__after { clip-path: inset(0 0 0 var(--pos, 50%)); }
.ba__badge {
  position: absolute;
  top: var(--space-4);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  background: var(--ink);
  color: var(--white);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  z-index: 3;
}
.ba__badge--before { left: var(--space-4); }
.ba__badge--after { right: var(--space-4); background: var(--accent); }
.ba__divider {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--pos, 50%);
  width: 2px;
  background: var(--accent);
  transform: translateX(-50%);
  z-index: 4;
}
.ba__handle {
  position: absolute;
  top: 50%; left: var(--pos, 50%);
  transform: translate(-50%, -50%);
  width: 48px; height: 48px;
  border-radius: var(--radius-pill);
  background: var(--white);
  border: 2px solid var(--accent);
  box-shadow: var(--shadow-md);
  display: grid;
  place-items: center;
  color: var(--accent);
  z-index: 5;
  cursor: ew-resize;
}
.ba__handle:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.ba__handle svg { width: 22px; height: 22px; }

/* --------------------------------------------------------------------------
   Gallery tile — a button, so click-to-enlarge is keyboard operable for free
   -------------------------------------------------------------------------- */
.tile {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: none;
  background: var(--sand);
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: zoom-in;
  transition: transform var(--dur-base) var(--ease-standard),
    box-shadow var(--dur-base) var(--ease-standard);
}
.tile img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-standard);
}
.tile:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.tile:hover img,
.tile:focus-visible img { transform: scale(1.06); }
.tile:active { transform: translateY(-1px); transition-duration: var(--dur-fast); }

/* Caption rises out of the bottom edge on hover/focus */
.tile__cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: var(--space-6) var(--space-4) var(--space-4);
  text-align: left;
  font-size: var(--fs-small);
  font-weight: 500;
  color: var(--white);
  background: linear-gradient(180deg, transparent, rgba(20, 23, 26, 0.78));
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur-base) var(--ease-standard),
    transform var(--dur-base) var(--ease-standard);
}
.tile:hover .tile__cap,
.tile:focus-visible .tile__cap { opacity: 1; transform: none; }
/* Touch has no hover, so the caption would never appear. Show it by default there. */
@media (hover: none) {
  .tile__cap { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   Lightbox — native <dialog>, so Escape, focus trap, and inertness are free
   -------------------------------------------------------------------------- */
.lightbox {
  /* The `* { margin: 0 }` reset kills the dialog's default `margin: auto`, which is
     what centres it in the viewport. Put it back. */
  margin: auto;
  max-width: min(94vw, 1200px);
  max-height: 92vh;
  padding: 0;
  border: none;
  background: transparent;
  overflow: visible;
}
.lightbox::backdrop { background: rgba(20, 23, 26, 0.88); animation: lb-fade var(--dur-base) var(--ease-standard); }
.lightbox[open] { animation: lb-in var(--dur-base) var(--ease-standard); }
@keyframes lb-in { from { opacity: 0; transform: scale(0.97); } }
@keyframes lb-fade { from { opacity: 0; } }
.lightbox img {
  width: 100%;
  max-height: 82vh;
  object-fit: contain;
  border-radius: var(--radius-md);
}
.lightbox__cap {
  margin-top: var(--space-4);
  text-align: center;
  font-size: var(--fs-small);
  letter-spacing: 0.06em;
  color: var(--plaster);
}
/* Sits inside the top-right of the image, so it stays on screen at any viewport height */
.lightbox__close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  z-index: 1;
  width: 44px; height: 44px;
  display: grid;
  place-items: center;
  font-size: 1.75rem;
  line-height: 1;
  color: var(--white);
  background: rgba(20, 23, 26, 0.55);
  backdrop-filter: blur(6px);
  border: none;
  border-radius: var(--radius-pill);
  transition: background var(--dur-fast) var(--ease-standard);
}
.lightbox__close:hover { background: rgba(20, 23, 26, 0.85); }
.lightbox__close:focus-visible { outline-color: var(--plaster); }

/* --------------------------------------------------------------------------
   Lead form
   -------------------------------------------------------------------------- */
.form { display: grid; gap: var(--space-5); }
.field { display: grid; gap: var(--space-2); }
.field label { font-size: var(--fs-small); font-weight: 600; }
.field .req { color: var(--accent); }
.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 13px var(--space-4);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  transition: border-color var(--dur-fast) var(--ease-standard),
    box-shadow var(--dur-fast) var(--ease-standard);
}
.field textarea { resize: vertical; min-height: 130px; }
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.field--error input,
.field--error select,
.field--error textarea { border-color: #B42318; }
.field__error { font-size: var(--fs-small); color: #B42318; min-height: 1.2em; }
/* Honeypot — visually and physically hidden but present for bots */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form__status { border-radius: var(--radius-md); padding: var(--space-5); font-size: var(--fs-small); }
.form__status[hidden] { display: none; }
.form__status--success { background: var(--accent-soft); color: var(--accent-strong); border: 1px solid var(--accent); }
.form__status--error { background: #FEF3F2; color: #B42318; border: 1px solid #FDA29B; }

/* --------------------------------------------------------------------------
   Scroll reveal (paired with motion.js) — reduced-motion safe by default
   -------------------------------------------------------------------------- */
/* Only hide reveals once JS confirms motion is on (html.motion). No-JS / reduced-motion = visible. */
html.motion .reveal { opacity: 0; transform: translateY(12px); }
html.motion .reveal.is-in { opacity: 1; transform: none; transition: opacity var(--dur-slow) var(--ease-standard), transform var(--dur-slow) var(--ease-standard); }

/* Hero entrance — pure CSS on purpose. The H1 is the most important element on the
   page, so it must never depend on JS running to become visible. `backwards` holds
   the from-state through the delay, so there is no flash of the final position. */
.hero__inner > * { animation: hero-in var(--dur-slow) var(--ease-standard) backwards; }
.hero__inner > :nth-child(2) { animation-delay: 60ms; }
.hero__inner > :nth-child(3) { animation-delay: 120ms; }
.hero__inner > :nth-child(4) { animation-delay: 180ms; }
@keyframes hero-in { from { opacity: 0; transform: translateY(12px); } }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  /* Explicit, not just a near-zero duration: the hero must land visible with no delay held. */
  .hero__inner > * { animation: none !important; }
  html, html.lenis { scroll-behavior: auto; }
  * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; }
}
