/* =========================================================================
   InFlightAI — AI Inspection Model Library
   Matched to Figma "inspections-01-active" (node 4198:1698) at 1440.
   Layers on top of ../css/home.css — tokens, header, footer and topcap
   all come from there. Only page-specific rules live here.
   ========================================================================= */

:root {
  /* The design's content well is 1240 inside a 1440 frame -> 100px gutters,
     narrowing proportionally below that. home.css's --gutter is 48px, which is
     the homepage's rhythm, not this page's. */
  --lib-gutter: clamp(20px, 6.944vw, 100px);
  --lib-rail: 276px;
  --lib-gap: 36px;
  --lib-radius: 8px;
  /* Sticky rail offset. The sticky header is 82px tall and fixed at top 0, so
     once it is showing the rail has to clear it or its first 58px are hidden.
     The rail also sticks *before* the header appears, hence two values. */
  --rail-top: 24px;
  --rail-top-stuck: 106px; /* 82px bar + the same 24px breathing room */
}

/* The design sets DM Sans on the optical-size 14 axis; without it the variable
   font renders wider and copy blocks wrap a line early. */
.libfilter__intro,
.libsignup__copy,
.libselect select,
.libfield input,
.libtable__th,
.librow__cat,
.librow__desc,
.libtable__empty {
  font-family: "DM Sans", "Outfit", sans-serif;
  font-variation-settings: "opsz" 14;
}

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

/* =========================================================================
   Headline band (dark) — sits inside .topcap below the header
   ========================================================================= */
/* The header and footer are carried over from the homepage unchanged — this
   page adds no overrides to either. (The Figma frame draws them slightly
   differently; the homepage version wins.) */

.libhead {
  position: relative;
  z-index: 2;
  padding-top: 16px;
  text-align: center;
}
.libhead__text {
  max-width: 875px;
  margin-inline: auto;
  padding-inline: var(--lib-gutter);
}
.libhead__title {
  font-size: 36px;
  font-weight: 600;
  line-height: 42px;
  letter-spacing: -0.01em;
  color: var(--white);
}
.libhead__sub {
  max-width: 670px;
  margin: 4px auto 0;
  font-size: 16px;
  line-height: 21px;
  color: var(--gray-600);
}

/* --- the fanned card strip ------------------------------------------------
   7 cards, bottom-aligned, overlapping by 19px within a group and 28px
   between the two groups. Peak stacking is the tallest centre card. */
.libfan {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  margin-top: 26px;
  height: 200px;
}
.libfan__group {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  margin-right: -28px;
}
.libfan__group--b { margin-right: 0; }

.libfan__card {
  position: relative;
  flex: 0 0 auto;
  margin-right: -19px;
  border-radius: 12.8px 12.8px 0 0;
  overflow: hidden;
  background: var(--gray-600);
  box-shadow: -10px 4px 36px 0 var(--black);
}
.libfan__group > .libfan__card:last-child { margin-right: 0; }

.libfan__card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: bottom;
}
/* two cards are mirrored in the design so the scene doesn't read as repeated */
.libfan__card--flip img { transform: scaleX(-1); }

/* bottom scrim, per the Figma gradient fill */
.libfan__card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(0, 15, 35, 0) 75%, rgba(0, 15, 35, 0.8) 100%);
}

.libfan__card--sm { width: 150px; height: 160px; z-index: 1; }
.libfan__card--md { width: 220px; height: 180px; z-index: 2; }
.libfan__card--lg { width: 250px; height: 192px; z-index: 3; }
.libfan__card--xl { width: 250px; height: 200px; z-index: 4; }

/* =========================================================================
   Content
   ========================================================================= */
.libmain {
  position: relative;
  background: var(--white);
}
/* The blue/purple mesh is anchored to the foot of the content: a 1440x534
   image whose top sits 380px above the footer, so it bleeds 154px into it.
   It paints behind the table (which is opaque white) and shows in the
   margins either side. */
.libmain::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -154px;
  height: 534px;
  /* cover, not 100% 100% — the design crops this image rather than squashing it */
  background: url("../img/library/footer-gradient.webp") center / cover no-repeat;
  pointer-events: none;
}
.lib-shell { position: relative; z-index: 1; }
.libgrid {
  display: grid;
  grid-template-columns: var(--lib-rail) 1fr;
  gap: var(--lib-gap);
  align-items: start;
  padding-block: 60px 80px;
}

/* --- left rail ----------------------------------------------------------- */
.librail {
  display: flex;
  flex-direction: column;
  gap: var(--lib-gap);
  position: sticky;
  top: var(--rail-top);
  transition: top 0.4s var(--ease);
}
/* body.is-stuck is set by home.js when the sticky bar slides in */
body.is-stuck .librail { top: var(--rail-top-stuck); }
@media (prefers-reduced-motion: reduce) { .librail { transition: none; } }

/* The panel wrapper is inert at desktop — `display: contents` lets the forms
   sit directly in the rail's flex column, so the layout is untouched. It only
   becomes a real (collapsing) box at the mobile breakpoint below. */
.librail__panel { display: contents; }

/* Mobile disclosure buttons — hidden until the breakpoint claims them */
.libdisclose { display: none; }

.libfilter {
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: flex-start;
}
.libfilter__rule {
  display: block;
  width: 100%;
  height: 4px;
  background: var(--grad-brand);
}
.libfilter__intro {
  font-family: "DM Sans", "Outfit", sans-serif;
  font-size: 16px;
  font-weight: 600;
  line-height: 22px;
  color: var(--black);
}

/* select + text input share one shell */
.libselect,
.libfield { display: block; width: 100%; }

.libselect select,
.libfield input {
  width: 100%;
  height: 46px;
  /* No vertical padding. The design's 46px box is 14 + text + 14, but in CSS
     that padding comes out of the content box, leaving 16px for a line box
     that needs ~18px at this size — so ascenders and descenders were clipped.
     A form control centres its text in the content box, so letting the height
     alone define the box keeps the 46px and gives the line room. */
  padding: 0 16px;
  border: 1px solid var(--light);
  border-radius: var(--lib-radius);
  background: var(--white);
  font-family: "DM Sans", "Outfit", sans-serif;
  font-size: 14px;
  font-weight: 500;
  color: var(--black);
  transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease);
}
.libselect select {
  appearance: none;
  padding-right: 40px;
  /* the design's caret, inlined so it can't 404 */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='4.93' viewBox='0 0 8 4.93333'%3E%3Cpath d='M4 4.93333L0 0.933333L0.933333 0L4 3.06667L7.06667 0L8 0.933333L4 4.93333Z' fill='%23627489'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
}
/* an unset filter shows its label in the muted tone; picking a term inks it */
.libselect select:has(option[value=""]:checked) { color: var(--gray); }
.libselect select option { color: var(--black); }
.libfield input::placeholder { color: var(--gray); opacity: 1; }

.libselect select:hover,
.libfield input:hover { border-color: var(--blue-300); }
.libselect select:focus-visible,
.libfield input:focus-visible {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(46, 136, 254, 0.18);
}
.libselect select:disabled {
  background-color: var(--light-300);
  color: var(--gray-600);
  cursor: not-allowed;
}

/* pill button — Reset Filters / Sign up */
.libpill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 11/15 rather than 12/16 — same reason as .libsignup: the 1px border sits
     inside the box here, so the design's 39x131 needs the padding reduced. */
  padding: 11px 15px;
  border: 1px solid var(--blue-300);
  border-radius: 80px;
  background: transparent;
  font-family: "Outfit", sans-serif;
  font-size: 12px;
  font-weight: 500;
  line-height: 15px;
  letter-spacing: 1.44px;
  text-transform: uppercase;
  color: var(--blue);
  transition: background-color 0.18s var(--ease), border-color 0.18s var(--ease),
    color 0.18s var(--ease), transform 0.18s var(--ease);
}
.libpill:hover { background: var(--blue-100); border-color: var(--blue); }
.libpill:active { transform: translateY(1px); }
.libpill:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }

/* sign-up card */
.libsignup {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  /* 15 not 16: Figma's stroke sits outside the 244px content box, so with
     border-box + a 1px border, 15px padding is what reproduces that width —
     and at 16px the copy wraps to a fifth line. */
  padding: 15px;
  border: 1px solid var(--light);
  border-radius: var(--lib-radius);
  background: var(--white);
}
.libsignup__mark { width: 36px; height: auto; }
.libsignup__copy {
  font-family: "DM Sans", "Outfit", sans-serif;
  font-size: 16px;
  font-weight: 600;
  line-height: 22px;
  color: var(--black);
}
.libsignup__copy a { color: var(--blue); }
.libsignup__copy a:hover { text-decoration: underline; }

/* =========================================================================
   Model table
   ========================================================================= */
.libtable {
  min-width: 0;
  /* Figma puts a drop shadow on the `model-list` wrapper (node 4198:1847):
     0px 5px 5px rgba(0,15,35,0.15). Using box-shadow rather than the literal
     `filter: drop-shadow` — visually identical on a rounded rect, and it
     avoids creating a stacking/containing block around the panel. The radius
     here shapes the shadow only; the head and rows still draw their own. */
  border-radius: var(--lib-radius);
  box-shadow: 0 5px 5px rgba(0, 15, 35, 0.15);
}

.libtable__head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 40px 16px 20px;
  border: 1px solid var(--light);
  border-radius: var(--lib-radius) var(--lib-radius) 0 0;
  background: var(--light-300);
}
.libtable__th {
  font-family: "DM Sans", "Outfit", sans-serif;
  font-size: 14px;
  font-weight: 500;
  line-height: 18px;
  color: var(--gray);
}
.libtable__th--name { flex: 0 0 300px; }

.libtable__body { display: flex; flex-direction: column; }

.libtable__empty {
  padding: 32px 20px;
  border: 1px solid var(--light);
  border-top: 0;
  border-radius: 0 0 var(--lib-radius) var(--lib-radius);
  background: var(--white);
  font-family: "DM Sans", "Outfit", sans-serif;
  font-size: 14px;
  color: var(--gray);
}

/* --- a row --------------------------------------------------------------- */
.librow {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 16px 20px;
  border: 1px solid var(--light);
  border-top: 0;
  background: var(--white);
  text-align: left;
  color: inherit;
  transition: background-color 0.18s var(--ease);
}
.librow:last-child { border-radius: 0 0 var(--lib-radius) var(--lib-radius); }
.librow:hover { background: var(--light-300); }
.librow:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: -2px;
  background: var(--light-300);
}

.librow__id {
  display: flex;
  align-items: center;
  gap: 16px;
  flex: 0 0 300px;
  min-width: 0;
}
.librow__thumb {
  flex: 0 0 auto;
  width: 80px;
  height: 62px;
  border-radius: var(--lib-radius);
  object-fit: cover;
  background: var(--light);
}
/* models without artwork yet render the design's placeholder tile */
.librow__thumb--empty {
  background:
    repeating-conic-gradient(var(--light-300) 0% 25%, var(--white) 0% 50%) 0 0 / 16px 16px;
}
.librow__names { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.librow__name {
  font-family: "Outfit", sans-serif;
  font-size: 16px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--black);
}
.librow__cat {
  font-family: "DM Sans", "Outfit", sans-serif;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--gray);
}

.librow__main {
  display: flex;
  align-items: center;
  gap: 26px;
  flex: 1 1 0;
  min-width: 0;
}
.librow__desc {
  flex: 1 1 0;
  min-width: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  font-family: "DM Sans", "Outfit", sans-serif;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--gray);
}

.librow__go {
  flex: 0 0 auto;
  width: 44.728px;
  height: 32.728px;
  /* The pill's 1px stroke is centred on the viewBox edge, so its outer half
     falls exactly on the boundary — and <svg> clips to that by default,
     flattening the curve. */
  overflow: visible;
}
.librow__go rect {
  fill: transparent;
  stroke: var(--blue-300);
  transition: fill 0.18s var(--ease), stroke 0.18s var(--ease);
}
.librow__go path { fill: var(--blue); transition: fill 0.18s var(--ease); }
.librow:hover .librow__go rect,
.librow:focus-visible .librow__go rect { fill: var(--blue); stroke: var(--blue); }
.librow:hover .librow__go path,
.librow:focus-visible .librow__go path { fill: var(--white); }

/* =========================================================================
   Detail view — replaces the table in place. Same 928px shell.

   Layout from Figma 4255:1156 ("model-03-boxes"). Three changes from the
   original detail view: the card carries its own header row instead of
   inheriting the list's grey bar, the gallery runs edge to edge in a tinted
   band, and the model's metadata moves out of the prose into a fixed side
   column. Horizontal padding therefore lives on the rows, not the card — the
   band has to reach the card's borders.
   ========================================================================= */
.libdetail {
  display: flex;
  flex-direction: column;
  gap: 26px;
  padding-block: 20px 26px;
  border: 1px solid var(--light);
  border-radius: var(--lib-radius);
  background: var(--white);
}

/* --- header: identity on the left, navigation on the right ------------- */
.libdetail__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding-inline: 26px;
}
.libdetail__eyebrow {
  font-family: "DM Sans", "Outfit", sans-serif;
  font-variation-settings: "opsz" 14;
  font-size: 14px;
  font-weight: 500;
  line-height: normal;
  color: var(--gray);
}
.libdetail__title {
  font-family: "Outfit", sans-serif;
  font-size: 36px;
  font-weight: 500;
  line-height: normal;
  letter-spacing: -0.01em;
  color: var(--black);
}
.libdetail__nav {
  display: flex;
  align-items: center;
  gap: 16px;
  /* the design sits these on the title's baseline rather than its box */
  padding-bottom: 6px;
  white-space: nowrap;
}
.libback,
.libnext {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: "DM Sans", "Outfit", sans-serif;
  font-variation-settings: "opsz" 14;
  font-size: 14px;
  font-weight: 500;
  line-height: 18px;
  color: var(--gray);
  transition: color 0.18s var(--ease);
}
.libback:hover,
.libnext:hover { color: var(--blue); }
.libback:focus-visible,
.libnext:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; border-radius: 2px; }
.libback__chev,
.libnext__chev { font-size: 18px; line-height: 1; }

/* --- gallery: a tinted band, shots running past the card's right edge ---
   It scrolls rather than shrinking to fit, so a fade and a chevron mark that
   there is more to see. Both only appear when the strip actually overflows —
   most models carry a single shot. */
.libgallery { position: relative; }
.libdetail__gallery {
  display: flex;
  gap: 16px;
  padding: 16px;
  list-style: none;
  background: var(--light-300);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* Must match the padding. Without it the first tile's snap position is 16px
     in, so the browser scrolls the band on load: the left padding disappears,
     and that unprompted scroll ends Chrome's LCP recording before anything has
     painted — multi-shot models reported no LCP at all. */
  scroll-padding-inline: 16px;
  overscroll-behavior-x: contain;
  scroll-behavior: smooth;
  scrollbar-width: none;
}
.libdetail__gallery::-webkit-scrollbar { display: none; }
.libshot {
  flex: 0 0 auto;
  width: 326px;
  height: 253px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--light);
  scroll-snap-align: start;
}
.libshot img { width: 100%; height: 100%; object-fit: cover; }
.libshot--empty {
  /* on the tinted band the old light-300 checker would be invisible */
  background:
    repeating-conic-gradient(var(--light) 0% 25%, var(--white) 0% 50%) 0 0 / 24px 24px;
}

/* Each edge gets a fade and an arrow, shown only while there is something to
   reach in that direction — so at rest a single-shot model has neither, and a
   strip scrolled to the end offers only the way back. */
.libgallery__fade,
.libgallery__nav {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s var(--ease);
}
.libgallery__fade {
  position: absolute;
  width: 90px;
}
.libgallery__fade--next {
  inset: 0 0 0 auto;
  background: linear-gradient(90deg, rgba(0, 15, 35, 0) 0%, rgba(0, 15, 35, 0.5) 100%);
}
.libgallery__fade--prev {
  inset: 0 auto 0 0;
  background: linear-gradient(270deg, rgba(0, 15, 35, 0) 0%, rgba(0, 15, 35, 0.5) 100%);
}
.libgallery__nav {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.libgallery__nav--next { right: 16px; }
.libgallery__nav--prev { left: 16px; }
.libgallery__nav svg {
  width: 48px;
  height: 48px;
  filter: drop-shadow(0 2px 5px rgba(0, 15, 35, 0.5));
}
.libgallery__nav--prev svg { rotate: 180deg; }
.libgallery__nav:focus-visible { outline: 2px solid var(--white); outline-offset: -4px; border-radius: 50%; }

.libgallery[data-more-next] .libgallery__fade--next,
.libgallery[data-more-prev] .libgallery__fade--prev { opacity: 1; }
.libgallery[data-more-next] .libgallery__nav--next,
.libgallery[data-more-prev] .libgallery__nav--prev { opacity: 1; pointer-events: auto; }

/* Only offer the grab cursor where there is actually somewhere to drag to. */
.libgallery[data-more-next] .libdetail__gallery,
.libgallery[data-more-prev] .libdetail__gallery { cursor: grab; }
/* Mid-drag the strip has to track the pointer exactly: snapping would fight it
   for position, and smooth scrolling would animate every pointermove. */
.libdetail__gallery.is-dragging {
  cursor: grabbing;
  scroll-snap-type: none;
  scroll-behavior: auto;
  user-select: none;
}

@media (prefers-reduced-motion: reduce) {
  .libdetail__gallery { scroll-behavior: auto; }
}

.libdetail__blocks {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-inline: 26px;
}
/* prose left, metadata right. minmax(0,…) so a long unbroken string in the
   prose can't push the fixed column out of the card. */
.libdetail__cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 200px;
  gap: 30px;
  align-items: start;
}
.libdetail__prose { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.libblock { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.libblock__title {
  font-family: "Outfit", sans-serif;
  font-size: 16px;
  font-weight: 500;
  line-height: normal;
  color: var(--black);
}
.libblock__body {
  font-family: "DM Sans", "Outfit", sans-serif;
  font-variation-settings: "opsz" 14;
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
  color: var(--gray);
  /* justified in the design; hyphenation keeps it from opening rivers */
  text-align: justify;
  hyphens: auto;
}

/* meta: one bordered box per fact, stacked down the side column. Was a single
   flowed row under the prose; the boxes are the point of the new layout. */
.libmeta {
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-family: "DM Sans", "Outfit", sans-serif;
  font-variation-settings: "opsz" 14;
  font-size: 12px;
  font-weight: 500;
  line-height: normal;
}
.libmeta__pair {
  display: flex;
  flex-direction: column;
  padding: 10px 16px;
  border: 1px solid var(--light);
  border-radius: var(--lib-radius);
}
.libmeta dt { color: var(--gray); }
.libmeta dd { color: var(--black); }
/* multi-value facts (industries) list one per line */
.libmeta dd span { display: block; line-height: 20px; }

.libfeatures {
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 100%;
  list-style: none;
}
.libfeature {
  display: flex;
  gap: 10px;
  align-items: center;
  font-family: "DM Sans", "Outfit", sans-serif;
  font-variation-settings: "opsz" 14;
  font-size: 14px;
  font-weight: 500;
  line-height: normal;
  color: var(--gray);
}
.libfeature__icon { flex: 0 0 auto; width: 24px; height: 24px; fill: var(--gray); }

/* sign-up "complete" state */
.libpill--done { gap: 10px; color: var(--blue); cursor: default; }
.libpill--done .libfeature__icon { width: 20px; height: 20px; fill: var(--blue); }

/* =========================================================================
   Flamingo Form inside the sign-up card (WordPress only).
   The plugin renders .ff-form > .ff-fields > .ff-field; the design wants
   placeholder-only fields matching .libfield, so labels go visually hidden
   and the inputs and submit inherit the card's own styling.
   ========================================================================= */
/* width: 100% is load-bearing — .libsignup is a flex column with
   align-items: flex-start, so the form would otherwise shrink-wrap its content
   and sit narrower than the card. */
.libsignup--form .ff-form { display: flex; flex-direction: column; gap: 16px; width: 100%; }
.libsignup--form .ff-fields { display: flex; flex-direction: column; gap: 16px; }
.libsignup--form .ff-field { display: block; width: 100%; }
/* the plugin spaces the submit with margin-top: 28px and forces min-width: 180px */
.libsignup--form .ff-submit-wrap { margin-top: 0; }

/* labels are announced but not drawn — the placeholder carries the design */
.libsignup--form .ff-label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.libsignup--form .ff-input {
  width: 100%;
  height: 46px;
  /* see .libselect select — vertical padding would clip the line box */
  padding: 0 16px;
  border: 1px solid var(--light);
  border-radius: var(--lib-radius);
  background: var(--white);
  font-family: "DM Sans", "Outfit", sans-serif;
  font-variation-settings: "opsz" 14;
  font-size: 14px;
  font-weight: 500;
  color: var(--black);
  transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease);
}
.libsignup--form .ff-input::placeholder { color: var(--gray); opacity: 1; }
.libsignup--form .ff-input:hover { border-color: var(--blue-300); }
.libsignup--form .ff-input:focus-visible {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(46, 136, 254, 0.18);
}

/* the plugin's submit takes the card's pill treatment */
.libsignup--form .ff-submit,
.libsignup--form button[type="submit"] {
  min-width: 0; /* the plugin sets 180px; the design's pill hugs its label */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 11px 15px;
  border: 1px solid var(--blue-300);
  border-radius: 80px;
  background: transparent;
  font-family: "Outfit", sans-serif;
  font-size: 12px;
  font-weight: 500;
  line-height: 15px;
  letter-spacing: 1.44px;
  text-transform: uppercase;
  color: var(--blue);
  cursor: pointer;
  transition: background-color 0.18s var(--ease), border-color 0.18s var(--ease);
}
.libsignup--form .ff-submit:hover,
.libsignup--form button[type="submit"]:hover { background: var(--blue-100); border-color: var(--blue); }

/* the plugin renders an error span per field up front; empty ones would each
   contribute their margin and push the card taller than the design */
.libsignup--form .ff-error:empty { display: none; }
.libsignup--form .ff-error {
  display: block;
  margin-top: 4px;
  font-family: "DM Sans", "Outfit", sans-serif;
  font-size: 12px;
  color: #c0392b;
}
/* --- success state ---------------------------------------------------------
   The plugin hides the fields and writes its message into .ff-message-success.
   The design replaces the intro copy with that message and adds a green
   "Sign up complete" pill, so: drop the intro, strip the plugin's green box
   back to plain copy, and hang the pill off the message as its own line. */
.libsignup--form:has(.ff-message-success) .libsignup__copy { display: none; }

.libsignup--form .ff-message-success {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font-family: "DM Sans", "Outfit", sans-serif;
  font-variation-settings: "opsz" 14;
  font-size: 16px;
  font-weight: 600;
  line-height: 22px;
  color: var(--black);
}
.libsignup--form .ff-message-success::after {
  content: "Sign up complete";
  display: inline-flex;
  align-items: center;
  /* The container's `gap` doesn't reach this pseudo-element -- the plugin writes
     the message as a bare text node, and the gap between that anonymous flex item
     and the pseudo isn't honoured -- so the 16px comes from a margin instead. */
  margin-top: 16px;
  /* left padding leaves room for the tick, which rides in as a background;
     min-height carries the design's 44px, border-box so the stroke sits inside it */
  box-sizing: border-box;
  min-height: 44px;
  padding: 0 15px 0 44px;
  border: 1px solid rgba(63, 195, 100, 0.45);
  border-radius: 80px;
  background: rgba(63, 195, 100, 0.06)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none'%3E%3Ccircle cx='12' cy='12' r='9.25' stroke='%233fc364' stroke-width='1.5'/%3E%3Cpath d='M8 12.3l2.7 2.7L16 9.7' stroke='%233fc364' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    no-repeat left 15px center;
  font-family: "Outfit", sans-serif;
  font-size: 12px;
  font-weight: 500;
  line-height: 15px;
  letter-spacing: 1.44px;
  text-transform: uppercase;
  color: var(--green);
}

/* validation errors keep the plugin's own red box */
.libsignup--form .ff-message-error {
  font-family: "DM Sans", "Outfit", sans-serif;
  font-size: 14px;
  line-height: 20px;
}

/* The footer keeps its own white background, so it has to paint above the
   content gradient that bleeds 154px past the end of .libmain. Plumbing only
   — no visual change to the shared footer. */
.libmain + .footer { position: relative; z-index: 1; }

/* =========================================================================
   Responsive
   ========================================================================= */
@media (max-width: 1100px) {
  .libgrid { grid-template-columns: 1fr; gap: 28px; }
  .librail { position: static; }
  .libfilter { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .libfilter__rule, .libfilter__intro { grid-column: 1 / -1; }
}

/* =========================================================================
   Mobile: the rail's two panels tuck behind disclosure buttons. Matches the
   site's mobile breakpoint (860px — where the nav becomes the hamburger).
   ========================================================================= */
@media (max-width: 860px) {
  /* The rail's children alternate button / panel, so a flex `gap` would sit on
     both sides of a collapsed (0px) panel and double the visual space between
     the two buttons. Drive the spacing off margins instead, so a closed panel
     contributes nothing. */
  .librail { gap: 0; }
  .libdisclose:not(:first-child) { margin-top: 12px; }
  .librail__panel[data-open] { margin-top: 12px; }

  /* Deliberately NOT shaped like the .libselect fields below them — these are
     actions, not inputs. Pill radius, brand accent, and a chevron chip that
     inverts on open. */
  .libdisclose {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
    min-height: 52px;
    padding: 8px 8px 8px 18px;
    border: 1px solid var(--blue-300);
    border-radius: 999px;
    background: var(--white);
    box-shadow: 0 8px 20px -14px rgba(46, 136, 254, 0.6), 0 1px 2px rgba(0, 15, 35, 0.04);
    font-family: "Outfit", sans-serif;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--black);
    text-align: left;
    transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease),
      transform 0.18s var(--ease);
  }
  .libdisclose:hover {
    border-color: var(--blue);
    box-shadow: 0 12px 24px -14px rgba(46, 136, 254, 0.75), 0 1px 2px rgba(0, 15, 35, 0.04);
    transform: translateY(-1px);
  }
  .libdisclose:active { transform: translateY(0); }
  .libdisclose:focus-visible {
    outline: none;
    border-color: var(--blue);
    box-shadow: 0 0 0 3px rgba(46, 136, 254, 0.22);
  }

  /* the chevron gets its own chip so it reads as a control, not a select caret */
  .libdisclose__chev {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--blue-100);
    color: var(--blue);
    transition: background-color 0.18s var(--ease), color 0.18s var(--ease);
  }
  .libdisclose[aria-expanded="true"] .libdisclose__chev {
    background: var(--blue);
    color: var(--white);
  }
  .libdisclose__label { display: inline-flex; align-items: center; gap: 10px; }
  /* 24-viewBox / 1.6 stroke / currentColor matches the site's icon set */
  .libdisclose__icon { flex: 0 0 auto; width: 19px; height: 19px; color: var(--blue); }
  /* the R&D lab uses the brand mark from the card it opens, so it keeps its
     own colour rather than inheriting the muted icon tone */
  .libdisclose__icon--mark { width: 18px; height: auto; }
  .libdisclose__count {
    display: inline-grid;
    place-items: center;
    min-width: 20px;
    height: 20px;
    padding-inline: 6px;
    border-radius: 10px;
    background: var(--blue);
    color: var(--white);
    font-size: 11px;
    font-weight: 600;
  }
  /* the explicit display would otherwise beat the UA's [hidden] rule, leaving
     an empty blue dot when no filters are applied */
  .libdisclose__count[hidden] { display: none; }
  .libdisclose__caret {
    flex: 0 0 auto;
    transition: transform 0.28s var(--ease);
  }
  .libdisclose[aria-expanded="true"] .libdisclose__caret { transform: rotate(180deg); }

  /* The collapsing box: 0fr -> 1fr slides the panel open at its natural
     height. minmax(0, …) is load-bearing — a bare `fr` track keeps an
     automatic minimum, so the sign-up card's padding and border would hold
     the closed panel open at 32px. */
  .librail__panel {
    display: grid;
    grid-template-rows: minmax(0, 0fr);
    overflow: hidden;
    transition: grid-template-rows 0.32s var(--ease);
  }
  .librail__panel[data-open] { grid-template-rows: minmax(0, 1fr); }
  /* a closed panel must not be tabbable or read out — hidden only once the
     slide has finished, and revealed immediately on the way open */
  .librail__panel > * {
    min-height: 0;
    visibility: hidden;
    transition: visibility 0s linear 0.32s;
  }
  .librail__panel[data-open] > * { visibility: visible; transition-delay: 0s; }

}

@media (max-width: 760px) {
  .libhead__title { font-size: 28px; line-height: 34px; }
  .libhead__sub { font-size: 14px; }
  .libfan { height: 120px; margin-top: 20px; }
  .libfan__card--sm { width: 90px; height: 96px; }
  .libfan__card--md { width: 132px; height: 108px; }
  .libfan__card--lg { width: 150px; height: 115px; }
  .libfan__card--xl { width: 150px; height: 120px; }

  /* the 200px meta column has nowhere to go beside the prose — stack it under */
  .libdetail__cols { grid-template-columns: 1fr; gap: 20px; }
  /* justification is a desktop-comp choice; at this measure it opens rivers */
  .libblock__body { text-align: left; }
  .libdetail__head { flex-direction: column; align-items: flex-start; gap: 10px; }
  .libdetail__nav { padding-bottom: 0; }
  .libdetail__title { font-size: 28px; }
  .libdetail__head, .libdetail__blocks { padding-inline: 16px; }
  .libshot { width: 260px; height: 202px; }

  .libfilter { grid-template-columns: 1fr; }
  .libgrid { padding-block: 36px 56px; }

  /* The rows stack into cards, so the column labels stop making sense — but
     only the *list* head is labels. The detail head carries "Back to Library",
     which is the only way out of a model on mobile, so it must stay. */
  .libtable__head:not(.libtable__head--detail) { display: none; }
  .libtable__body > .librow:first-child { border-top: 1px solid var(--light); border-radius: var(--lib-radius) var(--lib-radius) 0 0; }
  .librow { flex-wrap: wrap; gap: 12px; }
  .librow__id { flex: 1 1 100%; }
  .librow__main { flex: 1 1 100%; }
  .librow__desc { -webkit-line-clamp: 3; line-clamp: 3; }

  /* --- detail view ------------------------------------------------------- */
  .libdetail { padding: 16px 20px 24px; }
  .libdetail__title { font-size: 28px; line-height: 34px; }
  /* The design's 335x260 tile is wider than the card is on a phone, so a lone
     image would sit clipped with nothing to scroll to. Fit the card instead
     and keep the design's aspect ratio; multiple shots still swipe one at a
     time via the existing scroll-snap. */
  .libshot {
    width: min(335.48px, 100%);
    height: auto;
    aspect-ratio: 335.48 / 260;
  }
  .libmeta { gap: 8px 20px; }
}

@media (prefers-reduced-motion: reduce) {
  .libpill, .librow, .librow__go rect, .librow__go path,
  .libselect select, .libfield input, .libdisclose, .libdisclose__chev,
  .libdisclose__caret, .librail__panel { transition: none; }
  .libdisclose:hover { transform: none; }
  .librail__panel > * { transition-delay: 0s; }
}
