/* Search composition. The controls themselves come unmodified from the form
   block's ui/, which this file is registered as depending on; what changes here
   is the shell around them — square white cards on a hairline edge, no shadow
   and no sage band, which is the screen the client signed off. */

.lpd-search {
  --lpd-card-pad: 24px;
  --lpd-motion: 240ms cubic-bezier(0.4, 0, 0.2, 1);
  width: min(100%, 1120px);
  margin: 0 auto;
  padding: var(--lpd-card-pad);
  background: var(--lp-surface);
  color: var(--lp-ink);
  font: var(--lp-weight-regular) var(--lp-text-md) / var(--lp-text-md-line)
    var(--lp-font);
  /* The panel's only relief. The frame draws it offset down and right rather
     than centred, which is why this is not a symmetric ambient shadow. */
  box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.05);
}
.lpd-search *,
.lpd-search *::before,
.lpd-search *::after {
  box-sizing: border-box;
}

/* Chaplin clips #site-content, which cuts an open drop-down off at the bottom
   of the page. Only the vertical axis is freed: the horizontal clip is what
   keeps the mobile full-bleed rule at the end of this file from putting a
   scrollbar on the page. clip/visible is a legal pair — it is visible beside
   hidden that the cascade would coerce. */
#site-content:has(.lpd-search) {
  overflow-x: clip;
  overflow-y: visible;
}

/* Theme reset — same reason as public-layout.css: Chaplin styles bare elements
   and those rules outrank ui/forms.css, so the scope has to win them back. */
.lpd-search label {
  margin: 0;
}
.lpd-search button,
.lpd-search button:hover,
.lpd-search button:focus {
  border-radius: 0;
  text-decoration: none;
  transition: none;
}
.lpd-search ul {
  margin: 0;
  padding: 0;
  list-style: none;
}
.lpd-search li {
  margin: 0;
}
/* Chaplin pads bare <form> and <section> generously — 30px and 80px — and both
   outrank anything class-based here, so the scope has to win them back. */
.lpd-search form,
.lpd-search section {
  margin: 0;
  padding: 0;
}
.lpd-search .lp-control__input,
.lpd-search .lp-control__select {
  width: auto;
  max-width: none;
  height: auto;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  line-height: var(--lp-text-md-line);
}

/* Icons ------------------------------------------------------------------ */
/* The exported Figma vectors, drawn as masks so CSS owns the colour: the same
   chevron is black on a section head and brand green on the card's button.
   Each mask is sized as a share of its box, which is the proportion the vector
   has on Figma's own 24px grid — the exports are cropped to the path. */
.lpd-icon {
  display: block;
  flex: none;
  width: var(--lp-icon-md);
  height: var(--lp-icon-md);
  background: currentColor;
  -webkit-mask: var(--lpd-glyph) no-repeat center / var(--lpd-glyph-size, 100%);
  mask: var(--lpd-glyph) no-repeat center / var(--lpd-glyph-size, 100%);
}
.lpd-icon--loupe {
  --lpd-glyph: url("icons/loupe.svg");
  --lpd-glyph-size: 83.333%;
}
.lpd-icon--arrow-up-right {
  --lpd-glyph: url("icons/arrow-up-right.svg");
  --lpd-glyph-size: 50%;
}
.lpd-icon--arrow-down {
  --lpd-glyph: url("icons/arrow-narrow-down.svg");
}
.is-open > .lpd-icon--arrow-down {
  --lpd-glyph: url("icons/arrow-narrow-up.svg");
}
.lpd-icon--chevron {
  --lpd-glyph: url("icons/chevron-down.svg");
  --lpd-glyph-size: 58.333% 33.333%;
}
.is-open > .lpd-icon--chevron {
  --lpd-glyph: url("icons/chevron-up.svg");
}

/* Form ------------------------------------------------------------------- */
/* Every label is carried for screen readers and none is drawn: the design puts
   the field's name inside its own control. */
.lpd-search__label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* The frame insets the bar 48px from the panel top: 24 of it the container's
   padding and 24 the form's own. Scoped past the theme reset above, which is a
   descendant selector and would otherwise outrank a bare class. */
.lpd-search .lpd-search-form {
  padding-top: 24px;
}
.lpd-search__bar {
  display: flex;
  gap: 12px;
  align-items: stretch;
}
.lpd-search__bar .lp-field {
  flex: 1 1 auto;
  min-width: 0;
}
/* The bar states itself in brand rather than through the library's empty and
   filled edges: it is the one control on the page that is always the way in. */
.lpd-search__bar .lp-control {
  --lp-edge: var(--lp-brand);
  height: 100%;
  min-height: 60px;
  padding: 9px 13px;
  background: var(--lp-surface-empty);
}
.lpd-search__bar .lp-control__input {
  font-size: 18px;
}
.lpd-search__bar .lpd-icon {
  background: var(--lp-ink-icon);
}
.lpd-search .lpd-search-submit {
  flex: none;
  width: 241px;
  padding: 10px 16px;
  border: 0;
  background: var(--lp-brand);
  color: #fff;
  font: inherit;
  letter-spacing: 0.032px;
  cursor: pointer;
}
/* The Button component's own states. :focus-visible takes the hover fill
   because the design draws no separate focus — the theme reset above has
   already taken the outline off every button in here. */
.lpd-search .lpd-search-submit:hover,
.lpd-search .lpd-search-submit:focus-visible {
  background: var(--lp-brand-hover);
}
.lpd-search .lpd-search-submit:active {
  background: var(--lp-brand-active);
}

/* The count badge is neutral in the library, where it sits in a form control
   among many. Here it is the only thing saying a filter is on, so it takes the
   chosen colourway the tag badge already uses — including its × , whose brand
   artwork is a separate export rather than the neutral one recoloured. The URL
   is relative to this file, hence the walk back to the UI package. */
.lpd-search .lp-badge--count {
  border-color: var(--lp-badge-edge);
  background: var(--lp-surface-option-chosen);
  color: var(--lp-accent);
}
.lpd-search .lp-badge--count .lp-badge__remove::before {
  background: url("../form/ui/icons/badge-close.svg") no-repeat center / 7.5px;
}

/* A single-choice filter has no count to show, so its × is the same badge with
   the number left out. A fixed square rather than the badge's padding, which is
   asymmetric to sit the number off the left edge: 26px is the height a count
   badge stands beside it in the row, and it centres the × in both axes.

   :hover and :focus are named too: the theme reset at the top of this file
   flattens border-radius on those, and a pseudo-class outranks a plain class. */
.lpd-search .lp-select__clear,
.lpd-search .lp-select__clear:hover,
.lpd-search .lp-select__clear:focus {
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 26px;
  height: 26px;
  padding: 0;
  border: var(--lp-edge-width) solid var(--lp-badge-edge);
  border-radius: var(--lp-radius-pill);
  background: var(--lp-surface-option-chosen);
}
/* The icon box is the pill's own box rather than a 12px one nested inside it:
   two boxes centred inside each other each round their own way, and the label
   each filter is sized to leaves the control on a fractional pixel, so the ×
   drifted off the circle by up to a pixel and by a different amount per filter.
   One box and an even icon size makes every offset a whole number. */
.lpd-search .lp-select__clear::before {
  width: 100%;
  height: 100%;
  background: url("../form/ui/icons/badge-close.svg") no-repeat center / 8px;
}

/* The frame sizes every filter to its own name and lets the row wrap, so it
   reads as a set of chips and not as five equal columns. A bare <select> could
   not do that — it is only possible because ui/select.js has already replaced
   it with a div by the time this applies. */
.lpd-search__filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 16px;
}
/* Basis is the filter's own name, so the five keep the relative widths the
   frame draws — then they grow to take up the rest of the row rather than
   leaving it ragged. The floor keeps a short one a target worth hitting, and
   the ceiling stops a wide row stretching five filters past the width they read
   at. 100% is the second half of that ceiling: it keeps the 140px floor from
   forcing an overflow on a narrow viewport. */
.lpd-search__filters .lp-field {
  flex: 1 1 auto;
  min-width: 140px;
  max-width: min(300px, 100%);
}
/* The menu is not bound by the same width: a filter sized to the two words of
   its own name leaves its options wrapping over four lines each. It is absolute
   and overlays what is beside it, so it can take the room it needs. */
.lpd-search__filters .ts-dropdown {
  min-width: 260px;
}

.lpd-search .lpd-search-reset[hidden] {
  display: none;
}
.lpd-search .lpd-search-reset {
  display: inline-block;
  margin-top: 12px;
  color: var(--lp-ink-hint);
  font-size: var(--lp-text-sm);
  line-height: var(--lp-text-sm-line);
  text-decoration: underline;
}

/* Results ---------------------------------------------------------------- */
.lpd-search-results {
  display: grid;
  gap: 8px;
  margin-top: 40px;
}
/* Not drawn — the design has no count. The element stays because view.js reads
   it into the live region and moves focus to it after a search, so a screen
   reader is still told how many were found and put at the top of them.
   Absolute, so it takes no row of the results grid. */
.lpd-search-count {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.lpd-card {
  transition: background-color var(--lpd-motion);
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: var(--lpd-card-pad);
  border: var(--lp-edge-width) solid var(--lp-edge-card);
  background: var(--lp-surface);
}

/* Expanded, the card is tinted so its accordion sections — which carry a border
   but no fill of their own — read against something other than the white of the
   cards above and below it. */
.lpd-search .lpd-card.is-open {
  background: var(--lp-surface-empty);
}

.lpd-card__head {
  display: flex;
  gap: 16px;
  min-height: 28px;
  align-items: flex-start;
  justify-content: space-between;
}
.lpd-search .lpd-card__title {
  margin: 0;
  padding: 2px 0;
  color: var(--lp-ink);
  font-family: var(--lp-font);
  font-size: 20px;
  font-weight: 600;
  line-height: var(--lp-text-md-line);
  letter-spacing: normal;
}
.lpd-search .lpd-card__link {
  display: inline-flex;
  flex: none;
  gap: var(--lp-space-xs);
  align-items: center;
  color: var(--lp-brand-ink);
  font-family: var(--lp-font-ui);
  font-size: var(--lp-text-sm);
  font-weight: 600;
  line-height: var(--lp-text-sm-line);
  text-decoration: none;
}
.lpd-search .lpd-card__link .lpd-icon {
  background: #a9b7b4;
}
/* Shut, the description is three lines and an ellipsis; open, it is whatever it
 * is. Neither end is a height CSS can transition between, so view.js measures
 * both and puts the answer here for the length of the animation — the clamp
 * only draws the ellipsis, and max-height is what moves. overflow stays hidden
 * at both ends now, because that is what the clip is made of.
 *
 * The clamp is dropped by its own class rather than by the card's .is-open: it
 * cuts to three lines the instant it is back, so driven off the card it would
 * return at the start of a close and undo it before it ran.
 */
.lpd-card__summary {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  max-height: calc(3 * var(--lp-text-md-line));
  margin: 0;
  overflow: hidden;
  transition: max-height var(--lpd-motion);
  color: var(--lp-ink-option-support);
}
.lpd-card__summary.is-unclamped {
  -webkit-line-clamp: none;
}

.lpd-card__meta {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  align-items: center;
  /* 12px from the summary, of which the card's own 8px gap is already there. */
  margin-top: 4px;
}
/* A link, not a button: the frame draws it as brand text with an arrow beside
   it and no chrome at all. */
.lpd-search .lpd-card__toggle {
  display: inline-flex;
  gap: var(--lp-space-xs);
  align-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--lp-brand-ink);
  font-family: var(--lp-font);
  font-size: var(--lp-text-sm);
  font-weight: 600;
  line-height: var(--lp-text-sm-line);
  cursor: pointer;
}
.lpd-search .lpd-card__toggle .lpd-icon {
  background: #9fb0a8;
}

/* Sections --------------------------------------------------------------- */
.lpd-card__body > .lpd-collapse__inner {
  display: grid;
  gap: 4px;
}
/* Margin, not padding, and on the child rather than the wrapper — see the note
   on .lpd-collapse__inner below. */
.lpd-card__body > .lpd-collapse__inner > :first-child {
  margin-top: 16px;
}

/* Collapsible panel.
 *
 * A shut panel is a 0fr grid row rather than [hidden], because display cannot be
 * transitioned and the design asks for these to grow rather than appear. The
 * inner wrapper does the clipping: the row carries the height, the wrapper keeps
 * its content from setting a floor under it. Padding lives on the wrapper too,
 * so shut really is zero.
 *
 * visibility, not opacity — it is transitionable, and it takes the shut content
 * out of the accessibility tree the way [hidden] used to. */
.lpd-collapse {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition:
    grid-template-rows var(--lpd-motion),
    visibility var(--lpd-motion);
}
.lpd-collapse.is-open {
  grid-template-rows: 1fr;
  visibility: visible;
}
/*
 * Nothing on this wrapper may carry padding. A 0fr track zeroes the item's
 * content box, but its padding is not compressible — the track grows to hold it
 * and every shut panel stands that many pixels open. (12px of section padding
 * did exactly that.) Spacing goes on the children, where the clip can take it.
 */
.lpd-collapse__inner {
  min-height: 0;
  overflow: hidden;
}
/* One edge round the whole section rather than three on the head and three on
   the body: open, the two meet with no line between them, which is what the
   design draws — and closed, the head is not left with an open bottom. */
.lpd-section {
  border: var(--lp-edge-width) solid var(--lp-edge-filled);
}
.lpd-search .lpd-section__head {
  transition: color var(--lpd-motion);
  display: flex;
  gap: var(--lp-space-md);
  width: 100%;
  align-items: center;
  justify-content: space-between;
  padding: var(--lp-space-lg) var(--lp-control-pad-x);
  border: 0;
  background: none;
  color: var(--lp-ink-placeholder);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.lpd-search .lpd-section__head > span:first-child {
  line-height: 18px;
}
/* Shut, a section names itself the way an unanswered control does; open, it is
   a heading over what it holds. The frame draws the two that far apart. */
.lpd-search .lpd-section__head.is-open {
  color: var(--lp-ink);
}
.lpd-search .lpd-section__head .lpd-icon {
  --lpd-glyph-size: 61% 36%;
  width: var(--lp-icon-sm);
  height: var(--lp-icon-sm);
  background: var(--lp-ink-icon);
}
.lpd-section__body > .lpd-collapse__inner > * {
  padding: 0 var(--lp-control-pad-x) 20px;
}
.lpd-section__text {
  margin: 0;
  color: var(--lp-ink-option-support);
}
.lpd-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--lp-space-sm);
}
/* The library's badge reserves room for a remove button on its right. There is
   none here — a chip on a card is a statement, not a control. */
.lpd-chips .lp-badge {
  padding: var(--lp-space-xxs) var(--lp-space-md);
}
/* The library truncates the pill's text because a control's row cannot grow.
   A card's can, so once the chip has run out of room to wrap onto, let the
   words wrap inside it rather than cut the answer off. */
.lpd-chips .lp-pill__text {
  overflow: visible;
  white-space: normal;
  overflow-wrap: anywhere;
}

/* A listing's own page ---------------------------------------------------
 * The card's detail with nothing folded. The panel is .lpd-search's and the
 * sections and the spec grid are the card's — which is why the markup carries
 * both classes — so these three rules are the whole of what a page adds. */

/* The frame's panel is 24 at the sides and 32 top and bottom, where a card in
   the search list is 24 all round. */
.lpd-single {
  padding-block: 32px;
}
/* Not .lpd-card__summary: there the description is clamped to three lines and
   grows with the accordion, and here it is simply the text. pre-line because it
   is a textarea answer and the paragraphs in it are the submitter's. */
.lpd-single__summary {
  margin: 0 0 var(--lpd-card-pad);
  color: var(--lp-ink-option-support);
  white-space: pre-line;
}
.lpd-single__sections {
  display: grid;
  gap: 4px;
}

/* Spec grid --------------------------------------------------------------
 * The two sections that are a set of fields rather than one field's answer.
 * One field per row: a value or a row of chips gets the whole width. */
.lpd-spec__row {
  display: flex;
  gap: 24px;
  align-items: baseline;
  padding: var(--lp-space-md) 0;
  border-top: var(--lp-edge-width) solid #eaeaea;
}
.lpd-spec__label {
  flex: none;
  width: 172px;
  color: var(--lp-ink-placeholder);
}
.lpd-spec__value {
  min-width: 0;
  color: var(--lp-ink-option-support);
  overflow-wrap: anywhere;
}
/* An address is a link, but it is still a value in a table of them: it reads
   as the text around it until it is pointed at. */
/* Colour repeated here rather than inherited: the theme colours bare links from
   a descendant selector, which outranks the plain class above. */
.lpd-search a.lpd-spec__value {
  color: var(--lp-ink-option-support);
  text-decoration: none;
}
.lpd-search a.lpd-spec__value:hover,
.lpd-search a.lpd-spec__value:focus {
  color: var(--lp-brand-ink);
  text-decoration: underline;
}
/* Chips in a row sit beside their label rather than filling the row. */
.lpd-spec .lpd-chips {
  gap: var(--lp-space-xs);
}

@media (max-width: 640px) {
  .lpd-spec__row {
    flex-direction: column;
    gap: var(--lp-space-md);
  }
  .lpd-spec__label {
    width: auto;
  }
}

.lpd-search-empty {
  margin: 0;
  padding: var(--lpd-card-pad);
  border: var(--lp-edge-width) solid var(--lp-edge-card);
  background: var(--lp-surface);
  color: var(--lp-ink-hint);
}
/* Announced, never seen: the count it repeats is already on screen. */
.lpd-search-live {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* Both are focused programmatically, never tabbed to, so the ring is only ever
   wanted for the keyboard user it was moved for. :focus alone drew it round the
   whole results list on any click that landed on the container. */
.lpd-search-results:focus-visible,
.lpd-search-count:focus-visible {
  outline: 2px solid var(--lp-accent-ring);
  outline-offset: 2px;
}

/* Pager ------------------------------------------------------------------ */
/* The frame sets the pager's rule straight onto the last card's edge with no
   gap at all, so it reads as the foot of the list rather than one more item in
   it — hence the negative margin cancelling the container's own 8px. */
.lpd-search-pages {
  display: flex;
  flex-wrap: wrap;
  gap: var(--lp-space-xxs);
  align-items: center;
  justify-content: center;
  margin-top: -8px;
  padding: 12px 24px 16px;
  border-top: var(--lp-edge-width) solid var(--lp-edge-card);
}
.lpd-search .lpd-search-page,
.lpd-search .lpd-search-gap {
  display: inline-flex;
  width: 36px;
  height: 36px;
  align-items: center;
  justify-content: center;
  border-radius: var(--lp-radius-md);
  background: none;
  color: var(--lp-ink-muted);
  font-family: var(--lp-font-ui);
  font-size: var(--lp-text-sm);
  font-weight: var(--lp-weight-medium);
  line-height: var(--lp-text-sm-line);
  text-decoration: none;
}
.lpd-search .lpd-search-page.is-current {
  background: var(--lp-surface-hover);
  color: var(--lp-ink-option);
}

/* Loading ---------------------------------------------------------------- */
/* Placeholders in the card's own shape, so the list keeps its height while a
   page is in flight. Colour comes off the same edge token the controls use. */
.lpd-search-skeleton {
  padding: var(--lpd-card-pad);
  border: var(--lp-edge-width) solid var(--lp-edge-card);
  background: var(--lp-surface);
}
.lpd-search-skeleton span {
  display: block;
  height: 12px;
  background: var(--lp-edge-filled);
  animation: lpd-search-pulse 1.4s ease-in-out infinite;
}
.lpd-search-skeleton span + span {
  margin-top: 12px;
}
.lpd-search-skeleton .lpd-search-skeleton__title {
  width: 40%;
  height: 24px;
  margin-bottom: 16px;
}
.lpd-search-skeleton span:nth-last-child(2) {
  width: 70%;
}
.lpd-search-skeleton .lpd-search-skeleton__details {
  width: 35%;
  height: 20px;
  margin-top: 20px;
}

@keyframes lpd-search-pulse {
  50% {
    opacity: 0.45;
  }
}
@media (prefers-reduced-motion: reduce) {
  .lpd-search-skeleton span {
    animation: none;
  }
  .lpd-search * {
    transition-duration: 1ms;
  }
}

@media (max-width: 640px) {
  .lpd-search {
    --lpd-card-pad: 16px;
    /* Full bleed: the theme's content container is narrower than the phone and
       the panel is meant to reach both edges. */
    width: 100vw;
    margin-left: 50%;
    transform: translateX(-50%);
    /* The transform makes a stacking context, which traps the drop-down's own
       z-index inside the panel — the footer is positioned and comes later in
       the document, so it would paint over an open menu. Lifting the panel
       itself is what puts the menu back on top. */
    position: relative;
    z-index: 1;
  }
  /* The 241px button is wider than most of the row it would have to share. */
  .lpd-search__bar {
    flex-wrap: wrap;
  }
  .lpd-search__bar .lp-field {
    flex: 1 1 100%;
  }
  /* On its own row it no longer stretches to the search box beside it, so the
     box's own 60px has to be restated. */
  .lpd-search .lpd-search-submit {
    width: 100%;
    min-height: 60px;
  }
  /* One filter per row, so the ceiling that keeps five of them from stretching
     across a wide bar has nothing left to do. */
  .lpd-search__filters .lp-field,
  .lpd-search__filters .lp-field:first-child {
    flex: 1 1 100%;
    max-width: none;
  }
  .lpd-search__filters {
    gap: 8px;
  }
  /* Stacked one per row, they are the page's main targets on a phone. */
  .lpd-search__filters .lp-control {
    min-height: 48px;
  }
  .lpd-card__head {
    flex-direction: column;
    gap: 8px;
  }
  .lpd-card__meta {
    align-items: flex-start;
  }
}

.lpd-search .lpd-turnstile {
  margin-top: 12px;
}
