/* ============================================================
   components.css - shared components and page sections
   ============================================================
   Every section that appears across the site lives in this file.
   Design tokens (colors, spacing, type scale) come from tokens.css;
   element resets and primitives (.container, .section, buttons,
   .eyebrow, .section-head) live in base.css; page-only styles live
   in assets/css/pages/ (one file per page). No build step: pushing to
   main redeploys the live site via GitHub Pages in about a minute.

   Fonts: Switzer for headings (var(--font-display)), Inter for
   everything else (var(--font-body)). IBM Plex Mono was removed
   sitewide by the designer, never reintroduce it.

   Table of contents (in file order):
     1. Header (sticky bar + mobile nav)
     2. Hero (capped 1440px composition + photo overlay)
     3. Technology (alternating rows + before/after slider)
     4. Case Studies spotlight (accordion + crossfading photo)
     5. Applications (white cards + materials pills)
     6. Contact (details, map, form card, custom checkbox)
     7. Partners (logo strip)
     8. Footer
     9. Cookie banner

   Interactive behavior (nav toggle, slider drag, accordion, form
   tabs and submit, cookie dismiss) is in assets/js/main.js; the CSS
   here only reacts to the attributes and custom properties that
   script sets (data-open, aria-expanded, aria-selected, data-state,
   --pos, [hidden]).

   Layout values mirror the Figma "SPP - Home" frame. */

/* ============================================================
   Header
   ============================================================
   Sticky top bar, identical markup on every page. How to edit:
   nav links are plain HTML inside <header> on EVERY page, so a
   nav change must be repeated across all pages (no includes,
   there is no build step). */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg-page);
  border-bottom: 1px solid var(--border-header);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-9);
  /* No min-height ON PURPOSE: .container is content-box, so a 103px
     min-height would ADD to the 18px paddings and inflate the bar (this
     bug shipped once, do not reintroduce it). The height comes out of
     the content: 67px logo + 2x18px padding = exactly Figma's 103px.
     The mobile nav dropdown below pins itself at inset 103px to match,
     keep the two in sync if the header height ever changes. */
  padding-block: 18px;
}
.site-header__logo {
  flex-shrink: 0;
}
.site-header__logo img {
  width: 120px;
  height: auto;
  display: block;
}
.site-nav {
  display: flex;
  align-items: center;
  gap: 36px; /* Figma 451:190 */
}
.site-nav__links {
  display: flex;
  align-items: center;
  gap: 30px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--type-ui);
  color: var(--text-secondary);
}
.site-nav__links a:hover {
  color: var(--accent);
}

/* Language switch (top right, EN/DE). Same markup on every page: the current
   language is a non-clickable span with aria-current="page", the other language
   is a link. main.js writes the visitor's explicit choice to localStorage so it
   overrides the auto-redirect in assets/js/lang-redirect.js on later visits. */
.lang-switch {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--type-ui);
  color: var(--text-secondary);
}
.lang-switch a {
  color: var(--text-secondary);
}
.lang-switch a:hover {
  color: var(--accent);
}
.lang-switch span[aria-current="page"] {
  color: var(--text-primary);
  font-weight: 600;
}

/* Mobile nav toggle: hamburger drawn from one span plus its two pseudo
   elements. main.js flips data-open on .site-nav and aria-expanded on
   this button; the dropdown styling lives in the 1024px block below. */
.nav-toggle {
  display: none;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
}
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
  content: "";
  display: block;
  width: 22px;
  height: 2px;
  background: var(--text-primary);
  border-radius: 2px;
  transition: transform 200ms ease, opacity 200ms ease;
}
.nav-toggle span::before { transform: translateY(-7px); }
.nav-toggle span::after { transform: translateY(5px); }

/* Open state: the middle bar fades out and the outer bars rotate into an X.
   main.js flips aria-expanded on the button, nothing else is needed. */
.nav-toggle span {
  transition: transform 200ms ease, opacity 200ms ease, background-color 200ms ease;
}
.nav-toggle[aria-expanded="true"] span {
  background-color: transparent;
}
.nav-toggle[aria-expanded="true"] span::before {
  transform: translateY(0) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] span::after {
  transform: translateY(-2px) rotate(-45deg);
}

@media (max-width: 1024px) {
  .nav-toggle { display: inline-flex; }
  .site-nav {
    position: absolute;
    inset: 103px 0 auto 0; /* 103px = header height, see the header note above */
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-5);
    padding: var(--space-6) clamp(20px, 5vw, 120px) var(--space-8);
    background: var(--bg-page);
    border-bottom: 1px solid var(--border-header);
    box-shadow: var(--shadow-md);
    transform: translateY(-12px);
    opacity: 0;
    pointer-events: none;
    transition: transform 200ms ease, opacity 200ms ease;
  }
  .site-nav[data-open="true"] {
    transform: translateY(0);
    opacity: 1;
    pointer-events: auto;
  }
  .site-nav__links {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-5);
    font-size: var(--type-h6);
  }
  .site-nav .btn { align-self: flex-start; }
}

/* ============================================================
   Hero
   ============================================================
   Two-column grid: copy on the left, full-height photo on the
   right. The photo is a Figma placeholder export (marked with the
   IMG/... (replace) convention in index.html), swap the <img>
   there when the client sends real photography. */
.hero {
  /* Figma: white page with a soft steel-blue wash from the bottom-left. */
  background-color: var(--bg-page);
  background-image: linear-gradient(226deg, rgba(234, 241, 247, 0) 34%, rgba(234, 241, 247, 1) 100%);
}
.hero__inner {
  display: grid;
  grid-template-columns: 1fr 640px;
  align-items: stretch;
  min-height: 700px;
  /* Cap the composition at the Figma frame width and center it. Up to 1440
     the photo touches the viewport's right edge exactly as in Figma; on
     wider screens the whole block centers, so the photo keeps a FIXED left
     position relative to the 1200px content and bleeds only 120px past it
     instead of chasing the viewport edge. */
  max-width: 1440px;
  margin-inline: auto;
}
.hero__copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-8);
  /* Align the hero text left edge to the same gutter as the centered 1200px
     container content, so hero copy lines up with every other section.
     min(100vw, 1440px) mirrors the capped .hero__inner width above. */
  padding-left: max(clamp(20px, 5vw, 120px), calc((min(100vw, 1440px) - 1200px) / 2));
  padding-right: var(--space-20);
  padding-block: var(--space-16);
}
.hero__copy-wrap {
  max-width: 592px;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}
/* Hero eyebrow is Inter SemiBold in Figma (slightly heavier than section eyebrows). */
.hero .eyebrow {
  font-weight: 600;
}
.hero__title {
  color: #000; /* Figma headline uses plain black */
}
.hero__subhead {
  max-width: 592px;
  line-height: 1.55;
}
.hero__cta {
  display: flex;
  align-items: center;
  gap: 30px;
  flex-wrap: wrap;
}
/* "Watch the process": white button with hairline border (Figma btn-play). */
.btn-play {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 16px 32px;
  border-radius: var(--radius-lg);
  background: #fff;
  border: 1px solid var(--border-card);
  box-shadow: var(--shadow-button);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 16px;
  line-height: 1;
  color: var(--text-primary);
  transition: border-color 150ms ease, background-color 150ms ease;
}
.btn-play:hover {
  background: var(--accent-soft);
}
.btn-play__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  flex-shrink: 0;
}
.hero__media {
  position: relative;
  min-height: 700px;
}
.hero__media img,
.hero__media .img-placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Figma 451:212: dark bottom gradient over the hero photo. It lives on
   ::after (not baked into the image), so it survives a photo swap, keep
   it when replacing the placeholder. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(13, 17, 22, 0.05) 0%, rgba(13, 17, 22, 0.12) 60%, rgba(13, 17, 22, 0.6) 100%);
  pointer-events: none;
}

@media (max-width: 1024px) {
  .hero__inner {
    grid-template-columns: 1fr;
    min-height: 0;
  }
  .hero__media {
    order: -1;
    min-height: 360px;
  }
  .hero__copy {
    padding-block: var(--space-12);
  }
}

/* ============================================================
   Technology
   ============================================================
   Alternating text/media rows (.tech-row, .tech-row--reverse
   flips the columns) plus the before/after slider further down.
   Row copy lives in index.html; media slots are placeholder
   Figma exports until real photos arrive. */
.technology {
  background: var(--bg-page);
}
/* Figma 465:3: this heading block uses an 18px gap (Applications keeps 20px). */
.technology .section-head {
  gap: 18px;
}
.technology .section-head .intro {
  line-height: 1.62;
}
/* Figma 465:5: tracking -1px and plain black on this section's headings. */
.technology .h2 {
  letter-spacing: -1px;
  color: #000;
}
.technology .h3 {
  color: #000;
}
.tech-row {
  display: grid;
  grid-template-columns: 1fr 520px; /* text | media */
  align-items: center;
  gap: var(--space-14);
  margin-top: var(--space-20);
}
.tech-row--reverse {
  grid-template-columns: 520px 1fr; /* media | text */
}
.tech-row__text {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 616px;
}
.tech-row__text .body-sm,
.tech-row__text p {
  font-size: 16px;
  line-height: 1.62;
  color: var(--text-secondary);
}
/* Row eyebrows use 1.44px tracking in Figma (heading eyebrow keeps 1.68px). */
.tech-row__text .eyebrow {
  letter-spacing: 1.44px;
}
.tech-row__media {
  width: 100%;
  min-height: 400px;
}
.tech-row__media .img-placeholder {
  height: 100%;
  min-height: 400px;
  border-radius: var(--radius-2xl);
}
.tech-row__img {
  width: 100%;
  height: 400px;
  object-fit: cover;
  border: 1px solid var(--border-strong); /* Figma: before/after and jet slots */
  border-radius: var(--radius-2xl);
}
/* Bath slot (reverse row) uses the lighter border in Figma. */
.tech-row--reverse .tech-row__img {
  border-color: var(--border);
}
/* Jet slot: Figma 465:31 has a 150px dark gradient hugging the bottom edge. */
.tech-row__media--overlay {
  position: relative;
}
.tech-row__media--overlay::after {
  content: "";
  position: absolute;
  left: 1px;
  right: 1px;
  bottom: 1px;
  height: 150px;
  border-radius: 0 0 var(--radius-2xl) var(--radius-2xl);
  background: linear-gradient(180deg, rgba(13, 17, 22, 0) 0%, rgba(13, 17, 22, 0.6) 100%);
  pointer-events: none;
}
.tech-row--reverse .tech-row__media {
  order: -1;
}
.tech-row .link-arrow {
  margin-top: var(--space-2);
}

/* Before / after comparison slider (Figma 465:12), draggable via --pos.
   The whole widget is driven by the --pos custom property (a percentage,
   default 50%): main.js updates it on pointer drag and on arrow keys via
   the handle. The divider and handle sit at left: var(--pos), and the
   before layer is revealed by the clip-path below, nothing else moves. */
.ba-slider {
  position: relative;
  height: 400px;
  background: #fff;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-2xl);
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  cursor: ew-resize;
  --pos: 50%;
}
.ba-slider__layer {
  position: absolute;
  inset: 0;
}
.ba-slider__layer img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}
/* Clip the before layer to everything left of the handle position. */
.ba-slider__layer--before {
  clip-path: inset(0 calc(100% - var(--pos)) 0 0);
}
/* Placeholder treatment until the client sends a true before/after pair:
   the same photo is used on both sides (as in Figma), so the "before" side
   is desaturated to make the comparison visible while dragging. DELETE this
   filter rule once real before and after photos are in place. */
.ba-slider__layer--before img {
  filter: grayscale(0.9) brightness(0.95) contrast(0.92);
}
.ba-slider__tag {
  position: absolute;
  top: 23px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 11px;
  line-height: normal;
  letter-spacing: 1.32px;
  text-transform: uppercase;
}
.ba-slider__tag--before {
  left: 23px;
  color: #eef2f5;
}
.ba-slider__tag--after {
  left: calc(50% + 33px); /* Figma: 33px right of the resting divider */
  color: #fff;
}
.ba-slider__divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--pos);
  width: 2px;
  margin-left: -1px;
  background: #fff;
}
.ba-slider__handle {
  position: absolute;
  top: 50%;
  left: var(--pos);
  transform: translate(-50%, -50%);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.28); /* Figma 487:2 */
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  cursor: ew-resize;
}

@media (max-width: 1024px) {
  .tech-row {
    grid-template-columns: 1fr;
    gap: var(--space-9);
    margin-top: var(--space-12);
  }
  /* Stacked rows always show the image FIRST, then the text (designer's
     call for mobile), regardless of the desktop DOM/visual order. */
  .tech-row__media { order: -1; }
}

/* ============================================================
   Video (YouTube embed)
   ============================================================
   The YouTube player is embedded directly in index.html (client wants
   the video visible right away, no facade). This block only frames it:
   a 16:9 slot at content width with the shared media border and radius.
   Change the video by editing the iframe src in index.html. */
.video-section {
  background: var(--bg-page);
}
.video-embed {
  position: relative;
  margin-top: var(--space-12); /* 56px, heading-to-video gap from Figma */
  aspect-ratio: 16 / 9; /* 1200x675 at full content width */
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-2xl);
  overflow: hidden;
}
.video-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ============================================================
   Case Studies - Split Spotlight
   ============================================================
   Accordion of case rows (one open at a time) next to one large
   photo. main.js sets aria-expanded on the active .case-item and
   swaps the photo from that row's data-image attribute; ALL of
   the expanded-card styling below keys off aria-expanded, there
   is no separate "active" class. How to add a case study: add a
   .case-item row in index.html (with data-image), main.js picks
   it up automatically; 4 of 5 case study pages still carry
   [Placeholder] copy awaiting client text. */
.spotlight {
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.spotlight__image {
  position: relative;
  min-height: 771px;
}
.spotlight__image img,
.spotlight__image .img-placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.spotlight__image img {
  /* Crossfade when the active case changes: main.js drops opacity to 0,
     waits these 180ms, swaps src, then restores opacity. Keep the JS
     timeout and this duration in sync. */
  transition: opacity 180ms ease; /* crossfade when the active case changes */
}
.spotlight__panel {
  background: var(--bg-surface);
  padding: 80px;
  display: flex;
  flex-direction: column;
  gap: var(--space-10);
}
.spotlight__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 560px;
}
.spotlight__head .h2 {
  font-size: 46px;
  letter-spacing: -1px;
}
.spotlight__eyebrow {
  color: var(--accent);
}
.spotlight__intro {
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1.55;
  color: var(--text-secondary);
}
.case-list {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.case-item {
  border-radius: var(--radius-xl);
  transition: background-color 150ms ease, box-shadow 150ms ease;
}
.case-item__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding: var(--space-5) var(--space-6);
}
.case-item__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  line-height: 1.3;
  color: var(--text-primary);
}
.case-item__body {
  display: none;
  padding: 10px var(--space-6) var(--space-6);
  flex-direction: column;
  gap: 26px; /* Figma: desc to Read-more spacing in the expanded card */
}
.case-item__desc {
  color: var(--text-secondary);
  font-size: 16px;
  line-height: 1.6;
}
.case-item__arrow {
  color: var(--accent);
  flex-shrink: 0;
  transition: transform 150ms ease;
}
/* Expanded card: white surface lifted off the gray panel. */
.case-item[aria-expanded="true"] {
  background: #fff;
  border: 1px solid var(--border);
  box-shadow: 0 8px 11px rgba(22, 27, 33, 0.08);
  border-radius: var(--radius-xl);
}
.case-item[aria-expanded="true"] .case-item__head {
  padding: var(--space-6) var(--space-6) 0; /* Figma card: p-24 */
}
/* Figma renders no arrow on collapsed rows (empty icon slot), and the
   expanded card hides it too (rule further down), so the arrow markup is
   currently never visible; it stays in the HTML in case the design
   changes its mind. */
.case-item:not([aria-expanded="true"]) .case-item__arrow {
  display: none;
}
.case-item[aria-expanded="true"] .case-item__body {
  display: flex;
}
.case-item[aria-expanded="true"] .case-item__arrow {
  display: none;
}
.case-item:not([aria-expanded="true"]) .case-item__head {
  cursor: pointer;
}
.case-item:not([aria-expanded="true"]):hover .case-item__title {
  color: var(--accent);
}

@media (max-width: 1024px) {
  .spotlight {
    grid-template-columns: 1fr;
  }
  .spotlight__image { min-height: 360px; }
  .spotlight__panel { padding: var(--space-12) clamp(20px, 5vw, 80px); }
}

/* ============================================================
   Applications
   ============================================================
   2x2 grid of white numbered cards plus the materials pill
   strip. Card copy is edited directly in index.html. */
.applications {
  background: var(--bg-page);
  border-top: 1px solid #ededed; /* Figma 473:2 */
}
.apps-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  margin-top: 60px; /* Figma: heading-to-grid gap */
}
/* White card on the white page, defined by border + soft shadow. All four
   cards share this shadow ON PURPOSE: the Figma file misses it on the
   second card, that is a Figma omission, not a design cue, do not remove
   it from one card to "match". */
.app-cell {
  background: #fff;
  border: 1px solid var(--border-card);
  border-radius: 16px;
  padding: var(--space-8);
  display: flex;
  flex-direction: column;
  gap: 18px;
  box-shadow: 0 2px 12px rgba(22, 27, 33, 0.06);
}
.app-cell__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: 16px;
  background: var(--bg-surface);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: 2.4px;
  color: var(--accent);
}
.app-cell__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 21px;
  letter-spacing: -0.2px;
  color: #000; /* Figma card titles use plain black */
}
.app-cell__body {
  color: var(--text-secondary);
  font-size: 16px;
  line-height: 1.7;
}

/* Materials strip */
.materials {
  margin-top: var(--space-14);
  padding-top: var(--space-10);
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.materials__pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}
.pill {
  font-family: var(--font-body);
  font-size: var(--type-body-sm);
  color: var(--text-secondary);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 8px 18px;
}

@media (max-width: 768px) {
  .apps-grid {
    grid-template-columns: 1fr;
    margin-top: var(--space-12);
  }
}

/* ============================================================
   Contact
   ============================================================
   Details column (icon rows + map) next to the tabbed form card.
   The form is NOT wired to a backend yet: main.js intercepts
   submit and shows the thank-you state only. The endpoint
   (Formspree or Web3Forms) is a pending client decision; wire it
   in main.js when decided, nothing here needs to change. */
.contact {
  background: var(--bg-page);
  padding-block: 110px; /* Figma 451:375: py-110, not the shared 112 */
}
/* Scoped heading overrides (Figma contact heading differs from other
   sections): 48px title instead of the shared h2 size, and the intro is
   capped at 460px specifically so it wraps to two lines. These are
   deliberate, do not normalize them to the shared section-head styles. */
.contact .section-head {
  gap: 14px;
}
.contact .section-head .intro {
  max-width: 460px; /* wraps to two lines as in Figma */
  line-height: 1.55;
}
.contact .h2 {
  font-size: 48px;
  letter-spacing: -1px;
}
.contact .eyebrow {
  letter-spacing: 1.44px;
}
.contact__row {
  display: grid;
  grid-template-columns: 560px 1fr;
  gap: var(--space-18);
  margin-top: var(--space-12);
  align-items: start;
}
.contact__details {
  display: flex;
  flex-direction: column;
  gap: var(--space-9); /* Figma 556:817: 40px between the two rows */
  padding-top: 20px;
}
.contact-detail {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.contact-detail__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--accent-subtle);
  color: var(--accent);
  flex-shrink: 0;
}
.contact-detail__label {
  font-family: var(--font-body);
  font-weight: 400; /* Figma: Inter Regular */
  font-size: 12px;
  letter-spacing: normal;
  text-transform: uppercase;
  /* Verbatim Figma value. This warm gray (and #2a2622 below) sits OUTSIDE
     the token palette on purpose: the tokens are cool steel grays, the
     contact details are warm by design, do not swap in a token. */
  color: #635f5b; /* verbatim Figma value */
}
.contact-detail__value {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 18px;
  color: #2a2622; /* verbatim Figma value, warm near-black, outside the token palette on purpose */
}
.contact__map {
  position: relative;
  margin-top: 36px; /* Figma left column gap */
  height: 300px;
  border-radius: var(--radius-lg); /* Figma 451:389: 8px */
  overflow: hidden;
  border: 1px solid #cdd9e2; /* verbatim Figma value */
}
.contact__map iframe {
  width: 100%;
  height: 100%;
  border: 0;
}
/* Map chip removed: the chip node exists in Figma but is clipped/invisible. */

/* Form card */
.form-card {
  background: var(--bg-page);
  border: 1px solid var(--border-header);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 40px;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
/* Contact / Request-a-quote tab switch. main.js moves aria-selected
   between the two buttons and flips the hidden attribute on the matching
   .form-body panels (linked via aria-controls). */
.form-toggle {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-1);
  height: 46px;
  padding: 4px;
  background: var(--bg-surface);
  border-radius: var(--radius-xl);
}
.form-toggle__btn {
  border-radius: var(--radius-lg);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 14px;
  color: var(--text-secondary);
  text-align: center;
  transition: background-color 150ms ease, color 150ms ease;
}
.form-toggle__btn[aria-selected="true"] {
  background: var(--bg-page);
  color: var(--text-primary);
  font-weight: 700;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}

/* Form body (one per tab); vertical rhythm 20px, same as the card. */
.form-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-5);
  width: 100%;
}
.form-body[hidden] {
  display: none;
}
/* Group heading inside the quote form ("Your workpiece", "Your company"). */
.form-section-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 17px;
  line-height: 1.3;
  color: var(--text-primary);
}
/* Two fields side by side. */
.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  width: 100%;
}
.field {
  width: 100%;
}
.field__textarea--tall {
  min-height: 110px;
}
/* File upload trigger, styled like an input. */
.upload-field {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  height: 48px;
  background: #fff;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 14px;
  cursor: pointer;
  transition: border-color 150ms ease, background-color 150ms ease;
}
.upload-field:hover {
  background: var(--accent-soft);
}
.upload-field__input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.upload-field__icon {
  flex-shrink: 0;
}

/* Small screens: the 40px card padding is too heavy, step it down so the
   fields keep a usable width on narrow phones. Everything else in the
   form already stacks (field rows collapse to one column below). */
@media (max-width: 768px) {
  .form-card {
    padding: var(--space-7); /* 28px */
  }
}
@media (max-width: 480px) {
  .form-card {
    padding: var(--space-5); /* 20px */
  }
  /* Toggle label size for small screens lives in the mobile pass block
     at the end of this file (12px at 768px and below, per the mobile frame). */
  .field-row {
    grid-template-columns: 1fr;
  }
}
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.field__label {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #000; /* Figma: text-black */
}
.field__input,
.field__textarea {
  width: 100%;
  height: 48px;
  padding: 0 15px;
  font-size: 15px;
  background: var(--bg-page);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
}
.field__textarea {
  height: auto;
  min-height: 92px;
  padding: 13px 15px;
  resize: vertical;
}
.field__input::placeholder,
.field__textarea::placeholder {
  color: var(--placeholder);
}
.field__input:focus,
.field__textarea:focus {
  outline: none;
  border-color: var(--accent);
}
/* Consent checkbox: native appearance is removed and the box plus tick
   are drawn in CSS (the checked ::after below is the checkmark), so it
   matches Figma exactly across browsers. */
.check {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: 14px;
  color: var(--text-secondary);
}
.check input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  margin: 0;
  background: #fff;
  border: 1.5px solid var(--border-check);
  border-radius: 3px;
  flex-shrink: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.check input[type="checkbox"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}
.check input[type="checkbox"]:checked::after {
  content: "";
  width: 5px;
  height: 9px;
  margin-top: -1px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.check a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* Post-submit thank-you state. There is no backend yet: main.js prevents
   the default submit and sets data-state="success" on the card, which
   hides the form and shows this block via the two rules below. */
.form-card__success {
  display: none;
  flex-direction: column;
  gap: var(--space-3);
  text-align: center;
  padding: var(--space-8) 0;
}
.form-card[data-state="success"] form { display: none; }
.form-card[data-state="success"] .form-card__success { display: flex; }

@media (max-width: 1024px) {
  .contact__row {
    grid-template-columns: 1fr;
    gap: var(--space-10);
    margin-top: var(--space-12);
  }
}

/* ============================================================
   Partners
   ============================================================
   Centered logo strip. Logos are WebP placeholders converted at
   q90 by scripts/convert-images.sh; add a new partner by adding
   an <img class="partner-logo"> in the HTML. */
.partners {
  background: var(--bg-surface-2);
  text-align: center;
  padding-block: var(--space-14); /* 64px, overrides .section */
}
.partners__label {
  display: block;
  /* Inter, not mono, BY DESIGN: this overrides the historical .mono-label
     class still on the element. IBM Plex Mono was removed sitewide, do
     not bring it back here. */
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 1.44px;
  text-transform: uppercase;
  color: #8c99a4;
}
/* The logos live in two identical .partners__set groups (the second is an
   aria-hidden clone in the HTML). On desktop only the first set shows, as
   a centered wrapping row. On phones both sets render side by side and
   animate rightwards forever, a seamless marquee: each set slides by its
   own full width, so when the loop restarts the clone stands exactly
   where the original began. */
.partners__logos {
  display: flex;
  justify-content: center;
  margin-top: var(--space-7);
}
.partners__set {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 16px 47px;
}
.partners__set--clone {
  display: none;
}
@media (max-width: 768px) and (prefers-reduced-motion: no-preference) {
  .partners__logos {
    justify-content: flex-start;
    overflow: hidden;
  }
  .partners__set {
    flex-wrap: nowrap;
    flex-shrink: 0;
    min-width: max-content;
    gap: 40px;
    padding-right: 40px; /* keeps the gap across the loop seam */
    animation: partners-marquee 22s linear infinite;
  }
  .partners__set--clone {
    display: flex;
  }
  @keyframes partners-marquee {
    from { transform: translateX(-100%); }
    to { transform: translateX(0); }
  }
}
.partner-logo {
  width: auto;
  object-fit: contain;
}
.partner-logo[src*="partner-bridge"] {
  height: 34.65px; /* Figma 482:1247: exact 132x34.65 export, do not round */
}

/* ============================================================
   Footer (expanded, Figma 667:1089)
   ============================================================
   Brand block with tagline on the left, three link columns on the
   right (Navigation, Case Studies, Social media), then a legal row.
   Same repeat-per-page caveat as the header: the footer is plain
   HTML on every page, edit all pages together. */
.site-footer {
  background: var(--bg-page);
  padding-bottom: var(--space-10); /* 48px, per the expanded design */
}
.site-footer hr {
  border: none;
  border-top: 1px solid var(--border-header);
}
.site-footer__main {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-10);
  padding-block: var(--space-18); /* 80px */
}
.site-footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  width: 300px;
  flex-shrink: 0;
}
.site-footer__brand img {
  width: 144px;
  height: auto;
  display: block;
}
.site-footer__tagline {
  font-size: 13px;
  line-height: 1.5;
  color: #8c99a4;
}
.site-footer__cols {
  display: flex;
  gap: var(--space-14); /* 64px */
}
.site-footer__col {
  display: flex;
  flex-direction: column;
  gap: var(--space-5); /* 20px */
  width: 200px;
}
.site-footer__col-title {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 12px;
  text-transform: uppercase;
  color: #8c99a4;
}
.site-footer__col-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-3); /* 12px */
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 14px;
  color: var(--text-secondary);
}
.site-footer__col-links a:hover { color: var(--accent); }
.site-footer__bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  flex-wrap: wrap;
  padding-top: var(--space-6);
  font-size: 13px;
  line-height: normal;
  color: #8c99a4;
}
.site-footer__legal {
  display: flex;
  gap: var(--space-6); /* 24px, per the expanded design */
  flex-wrap: wrap;
}
.site-footer__legal a:hover { color: var(--accent); }

@media (max-width: 900px) {
  .site-footer__main {
    flex-direction: column;
    gap: var(--space-9);
    padding-block: var(--space-10);
  }
  .site-footer__cols {
    flex-wrap: wrap;
    gap: var(--space-9);
  }
  .site-footer__col {
    width: 160px;
  }
}

/* ============================================================
   Cookie banner
   ============================================================
   Fixed pill centered above the bottom edge. It ships with the
   hidden attribute in the HTML; main.js reveals it only when no
   consent flag is in localStorage and hides it again on accept.
   The [hidden] rule below is what actually removes it. */
.cookie-banner {
  position: fixed;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  z-index: 200;
  width: min(680px, calc(100% - 40px));
  display: flex;
  align-items: center;
  gap: var(--space-5);
  flex-wrap: wrap;
  justify-content: space-between;
  padding: var(--space-5) var(--space-6);
  background: var(--bg-page);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
}
.cookie-banner[hidden] { display: none; }
.cookie-banner__text {
  font-size: var(--type-body-sm);
  color: var(--text-secondary);
  flex: 1 1 320px;
}
.cookie-banner__actions {
  display: flex;
  gap: var(--space-3);
}
.cookie-banner .btn {
  padding: 10px 20px;
}

/* ============================================================
   Mobile pass (Figma "SPP - Home (Mobile)", node 658:537)
   ============================================================
   Phone-specific values from the mobile design frame. Kept in one
   block, last in the file, so these later rules win over the
   desktop values above at equal specificity. */
@media (max-width: 768px) {
  /* Typography from the mobile frame. Listed here, last in the file,
     because several desktop headings carry scoped sizes (.spotlight__head
     .h2 46px, .contact .h2 48px) that would otherwise beat the generic
     mobile clamp in base.css and render huge on phones. */
  .display {
    font-size: 36px;
    letter-spacing: -1px;
  }
  .h2,
  .technology .h2,
  .spotlight__head .h2 {
    font-size: 32px;
    letter-spacing: -0.8px;
  }
  .video-section .h2,
  .contact .h2 {
    font-size: 30px;
    letter-spacing: -0.6px;
  }
  .eyebrow {
    font-size: 12px;
    letter-spacing: 1.44px;
  }
  .section-head .intro,
  .contact .section-head .intro,
  .spotlight__intro {
    font-size: 16px;
  }
  .app-cell__title {
    font-size: 20px;
  }
  .contact-detail__value {
    font-size: 16px;
  }
  .contact-detail__label {
    font-size: 11px;
  }
  .field__input,
  .field__textarea {
    font-size: 14px;
  }
  .form-toggle__btn {
    font-size: 12px;
  }
  .ba-slider__tag {
    font-size: 10px;
    letter-spacing: 1.2px;
  }

  /* Hero (mobile frame): the photo is hidden, the copy is centered on the
     gradient wash, and the two CTAs stack at equal width (the widest one
     sets the column, the other stretches to match). */
  .hero__media {
    display: none;
  }
  .hero__inner {
    min-height: 0;
  }
  .hero__copy {
    padding: var(--space-10) 20px var(--space-12); /* 48 / 20 / 56 */
  }
  .hero__copy-wrap {
    gap: var(--space-5); /* 20px */
    align-items: center;
    text-align: center;
    margin-inline: auto;
  }
  .hero__cta {
    flex-direction: column;
    align-items: stretch;
    width: max-content;
    max-width: 100%;
    gap: 12px;
  }

  /* Section heading stacks tighten to a 14px gap on phones (the scoped
     Technology 18px selector is repeated so this later rule wins). */
  .section-head,
  .technology .section-head {
    gap: 14px;
  }

  /* Technology rows: 20px image-to-text gap, 40px between rows,
     240px media height, 12px inside the text stack. */
  .tech-row {
    gap: var(--space-5);
    margin-top: var(--space-9);
  }
  .tech-row__text {
    gap: 12px;
  }
  .tech-row__img,
  .ba-slider {
    height: 240px;
  }
  .tech-row__media {
    min-height: 0;
  }

  /* Case studies: shorter photo, panel padding and rhythm from the
     mobile frame. */
  .spotlight__image {
    min-height: 300px;
  }
  .spotlight__panel {
    padding: var(--space-10) 20px; /* 48 / 20 */
    gap: var(--space-7);           /* 28px */
  }
  .case-item__head {
    padding: 18px 20px;
  }

  /* Video: 28px heading-to-player gap on phones. */
  .video-embed {
    margin-top: var(--space-7);
  }

  /* Applications: tighter cards with a smaller number badge. */
  .apps-grid {
    gap: var(--space-4);
    margin-top: var(--space-8);
  }
  .app-cell {
    padding: var(--space-6);
    gap: 14px;
  }
  .app-cell__num {
    width: 52px;
    height: 52px;
    border-radius: 14px;
    font-size: 17px;
  }

  /* Contact: 48px section padding (the scoped 110px desktop rule would
     otherwise survive on phones), tighter rhythm, 44px icon circles,
     220px map, full-width submit, 45px inputs, 16px form gaps. */
  .contact {
    padding-block: var(--space-10);
  }
  .contact__row {
    margin-top: var(--space-8);
    gap: var(--space-8);
  }
  .form-card,
  .form-body {
    gap: var(--space-4); /* 16px */
  }
  .form-body .btn {
    width: 100%;
  }
  .field__input {
    height: 45px;
  }
  .contact__details {
    gap: var(--space-5);
    padding-top: 0;
  }
  .contact-detail__icon {
    width: 44px;
    height: 44px;
  }
  .contact__map {
    height: 220px;
    margin-top: var(--space-8);
  }

  /* Partners: 40px section padding, 24px label-to-strip gap, marquee
     logos scaled to 72 percent of desktop sizes. */
  .partners {
    padding-block: var(--space-9); /* 40px */
  }
  .partners__logos {
    margin-top: var(--space-6); /* 24px */
  }
  .partner-logo[src*="partner-bfh"] { height: 72px; }
  .partner-logo[src*="partner-be-advanced"] { height: 19px; }
  .partner-logo[src*="partner-bridge"] { height: 25px; }
  .partner-logo[src*="partner-gebert-ruf"] { height: 40px; }
}
