/* Public portal, listing, and marketplace surfaces. */

body.public-marketplace {
  /* Where the page stops growing. 1920px is the commonest desktop width, so a
     normal monitor still fills edge to edge and only genuinely wide screens are
     centred -- which is the only place the unbounded layout went wrong. In rem
     so a reader who scales their font up keeps the same column count. */
  --page-max-width: 120rem;
  /* The portal's accent and highlight are the brand teal; elsewhere they
     stay amber and yellow. */
  --accent-secondary: var(--color-brand);
  --highlight-bg: var(--color-brand-soft);
  color-scheme: light;
}

/* Fluid below the cap, fixed above it, with no media query: `max-width` is
   inert until the viewport reaches it. Bootstrap's `.container-fluid` already
   supplies `width: 100%` and `margin-inline: auto`, so this is the whole
   mechanism. The full-bleed navbar border and footer background sit on the
   outer elements and are unaffected. */
.public-marketplace .portal-main,
.public-marketplace .portal-navbar-inner,
.public-marketplace .portal-footer-inner {
  max-width: var(--page-max-width);
}

/* No `!important`: the phone action bar's bottom padding
   (`.portal-main:has(.mobile-action-bar)`, same specificity, later) must win. */
.public-marketplace .portal-main {
  /* Room above the footer: with none, a page ending in a card (Saved,
     Search, a collection) sat flush on it. */
  padding-bottom: var(--section-gap);
  padding-top: var(--page-top);
}

.public-marketplace .public-page-shell--home {
  padding-top: 0;
}

.public-marketplace :is([id], [tabindex="-1"]) {
  scroll-margin-top: calc(var(--sticky-top) + 1.5rem);
}

.public-marketplace .portal-navbar {
  background: var(--color-surface);
  min-height: 4.25rem;
}

.public-marketplace .portal-navbar .nav-link {
  border-radius: var(--radius-pill);
  color: var(--color-text-muted);
  font-weight: 600;
  padding: 0.5rem 1rem;
}

/* The finder: a tonal pill (`.btn-tonal`), the same height and shape as the
   search beside it, so the two read as one pair of ways in. It is on every
   page, so it never takes the filled style the page's own action needs. */
.public-marketplace .btn.portal-navbar-finder {
  border-radius: var(--radius-pill);
  flex: 0 0 auto;
  font-weight: 600;
  gap: 0.5rem;
  min-height: 3rem;
  padding-inline: 1rem;
  white-space: nowrap;
}

.public-marketplace .portal-navbar .nav-link.active,
.public-marketplace .portal-navbar .nav-link:hover,
.public-marketplace .portal-navbar .nav-link:focus-visible {
  background: var(--color-brand-soft);
  color: var(--color-brand);
}

.public-marketplace .btn {
  align-items: center;
  border-radius: 0.7rem;
  display: inline-flex;
  font-weight: 700;
  justify-content: center;
  min-height: 2.75rem;
}

.public-marketplace .btn-primary {
  --bs-btn-bg: var(--color-brand);
  --bs-btn-border-color: var(--color-brand);
  --bs-btn-hover-bg: var(--color-brand-hover);
  --bs-btn-hover-border-color: var(--color-brand-hover);
  --bs-btn-color: var(--color-on-brand);
  --bs-btn-hover-color: var(--color-on-brand);
  --bs-btn-active-color: var(--color-on-brand);
}

.skip-link {
  background: var(--surface);
  border: 2px solid var(--accent-primary);
  color: var(--text);
  inset-inline-start: 1rem;
  padding: 0.5rem 1rem;
  position: fixed;
  top: -5rem;
  z-index: 2000;
}

.skip-link:focus {
  top: 1rem;
}

.landing-page {
  width: 100%;
}

/* A section is its heading and one gap above it -- no rule, and no padding
   below as well, which doubled the space between two sections. */
.landing-section {
  padding-top: var(--section-gap);
}

.landing-section[id] {
  scroll-margin-top: 5rem;
}

.landing-eyebrow {
  color: var(--accent-primary);
  letter-spacing: 0;
}

/* The hero is a rounded panel in the brand colour, white type on teal, the
   eyebrow in the soft brand tint. One column below lg; copy beside the subject
   sphere from lg up. A panel inside the container bleeds nowhere, so nothing
   here can widen the page. */
.marketplace-hero {
  background: linear-gradient(135deg, var(--color-brand), var(--color-brand-hover));
  border-radius: var(--radius-panel);
  color: var(--color-on-brand);
  display: grid;
  gap: 2rem;
  /* `minmax(0, …)`: a grid track defaults to min-content width, and the chip
     row's min-content is every chip on one line. */
  grid-template-columns: minmax(0, 1fr);
  margin-block-start: 1rem;
  padding: var(--panel-pad);
}

.marketplace-hero .landing-eyebrow {
  color: var(--highlight-bg);
}

.marketplace-hero-copy {
  max-width: 46rem;
  min-width: 0;
}

.marketplace-hero h1 {
  color: var(--color-on-brand);
  font-size: var(--text-display);
  letter-spacing: -0.01em;
  line-height: 1.05;
  margin-bottom: 1.25rem;
  text-wrap: balance;
}

.marketplace-hero .btn-primary {
  background: var(--color-surface);
  border-color: var(--color-surface);
  color: var(--color-brand);
}

.marketplace-hero .btn-primary:hover,
.marketplace-hero .btn-primary:focus-visible {
  background: var(--highlight-bg);
  border-color: var(--highlight-bg);
  color: var(--color-brand);
}

.marketplace-hero .btn-outline-primary {
  border-color: color-mix(in srgb, var(--color-on-brand) 60%, transparent);
  color: var(--color-on-brand);
}

.marketplace-hero .btn-outline-primary:hover,
.marketplace-hero .btn-outline-primary:focus-visible {
  background: color-mix(in srgb, var(--color-on-brand) 14%, transparent);
  border-color: var(--color-on-brand);
  color: var(--color-on-brand);
}

@media (min-width: 992px) {
  .marketplace-hero {
    align-items: center;
    gap: clamp(2rem, 6vw, 5rem);
    grid-template-columns: minmax(0, 1.15fr) minmax(18rem, 0.85fr);
    margin-block-start: 1.5rem;
    min-height: min(32rem, calc(100vh - 6rem));
  }
}

.marketplace-hero .lead {
  font-size: var(--text-lead);
  line-height: 1.65;
  max-width: 40rem;
}

/* The landing hero's first finder question. Chips reuse the marketplace pill
   treatment so they read as the same kind of thing as a course chip on a card,
   rather than as a new control the visitor has to learn. */
.marketplace-hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 2rem;
}

.marketplace-hero-actions .btn {
  min-height: 3rem;
  padding-inline: 1.25rem;
}

/* The subject row. One line of chips that scrolls sideways on a phone and
   wraps on a wide screen; each chip is a link into the finder with its
   subject answered. The gutters are negative-margined away so the row runs to
   the screen edge and the last chip peeks in, which is the scroll hint. */
.subject-sphere {
  margin-top: 0.5rem;
}

.subject-sphere__nodes {
  display: flex;
  gap: 0.5rem;
  /* The containing block for each chip's visually-hidden count: without it
     those absolute spans escaped the scroller and widened the page to every
     chip's width, pushing three of the five tabs off a phone's screen. */
  position: relative;
  margin-bottom: 0;
  margin-inline: calc(-1 * var(--bs-gutter-x, 1.5rem) / 2);
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  padding-block: 0.25rem;
  padding-inline: calc(var(--bs-gutter-x, 1.5rem) / 2);
  scroll-padding-inline: calc(var(--bs-gutter-x, 1.5rem) / 2);
  scroll-snap-type: inline proximity;
  scrollbar-width: none;
}

.subject-sphere__node {
  flex: 0 0 auto;
  scroll-snap-align: start;
}

.subject-sphere__chip {
  align-items: center;
  border-radius: var(--radius-pill);
  display: inline-flex;
  font-size: 0.9rem;
  font-weight: 600;
  gap: 0.5rem;
  min-height: 2.75rem;
  padding: 0.5rem 1rem;
  text-decoration: none;
  white-space: nowrap;
}

.subject-sphere__chip i {
  color: var(--identity-ink);
}

/* How much is behind the chip, so breadth is visible before the click. */
.subject-sphere__count {
  background: var(--identity-border);
  border-radius: var(--radius-pill);
  color: var(--identity-ink);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  padding: 0.25rem 0.5rem;
}

a.subject-sphere__chip:hover,
a.subject-sphere__chip:focus-visible {
  border-color: var(--color-brand);
  color: var(--color-text);
}

@media (min-width: 992px) {
  .subject-sphere {
    margin-top: 0;
  }

  .subject-sphere__nodes {
    flex-wrap: wrap;
    margin-inline: 0;
    overflow: visible;
    padding-inline: 0;
  }

  /* Script turns the list into a stage and takes each node out of flow. The
     nodes sit on a Fibonacci lattice and are placed with translate3d, so
     every label keeps facing the reader while the sphere turns. */
  .subject-sphere__nodes.is-spherical {
    display: block;
    height: 26rem;
    overflow: hidden;
    perspective: 1000px;
    position: relative;
    /* The drag is a rotation, not a text selection or a page scroll. */
    touch-action: none;
    user-select: none;
  }

  .is-spherical .subject-sphere__node {
    left: 50%;
    position: absolute;
    top: 50%;
    transition: none;
    will-change: transform;
  }

  /* Smaller and width-capped on the sphere: a chip wider than the sphere's
     own radius cannot help colliding with its neighbours. The full name stays
     in the DOM for a screen reader either way. */
  .is-spherical .subject-sphere__chip {
    font-size: var(--text-xs);
    max-width: 9.5rem;
    min-height: 0;
    padding: 0.25rem 0.75rem;
  }

  .is-spherical .subject-sphere__chip span {
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

.landing-section-header {
  align-items: end;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
}

.landing-section-header h2,
.landing-feed-header h2,
.partnership-cta h2 {
  font-size: var(--text-title);
  letter-spacing: -0.01em;
  margin-bottom: 0.5rem;
}

.partnership-cta {
  align-items: center;
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  display: flex;
  gap: 2rem;
  justify-content: space-between;
  margin-block: var(--section-gap) 0;
  padding: var(--card-pad);
}

.partnership-cta .btn {
  flex: 0 0 auto;
  min-width: 8.5rem;
}

.request-summary {
  display: grid;
  gap: 0.25rem 0.75rem;
  grid-template-columns: max-content minmax(0, 1fr);
}

.request-summary dt {
  color: var(--text-muted);
  font-weight: 700;
}

.request-summary dd {
  margin-bottom: 0;
  min-width: 0;
}

/* Here, not in dashboard.css: the enrollment modal on public pages renders
   this summary too, and the portal does not load dashboard.css. */
@media (max-width: 575.98px) {
  .request-summary {
    grid-template-columns: 1fr;
  }

  .request-summary dd:not(:last-child) {
    margin-bottom: 0.5rem;
  }
}

/* "Request sent" (`portal/includes/enrollment_success.html`), the end of the
   finder: the mark and who has the request, centred like a result; then the
   record and what happens next, left-aligned for reading. */
.request-success {
  padding-block: 1.5rem;
}

.request-success .success-mark {
  display: flex;
  margin: 0 auto 1rem;
}

.request-success__title {
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-variant-numeric: lining-nums;
  font-weight: 700;
  margin-bottom: 0.25rem;
  text-align: center;
}

.request-success__message {
  color: var(--text-muted);
  margin: 0 auto 1.25rem;
  max-width: 28rem;
  text-align: center;
}

.request-success .request-summary {
  background: var(--color-background);
  border-radius: var(--radius-card);
  margin-bottom: 1.25rem;
  padding: 1rem 1rem;
}

.request-next__title {
  font-size: var(--text-md);
  font-weight: 700;
  margin-bottom: 0.5rem;
}

.request-next {
  display: grid;
  gap: 0.5rem;
  margin: 0;
  padding-inline-start: 1.25rem;
}

/* The request sheet's group choices: the name, then the card's facts under
   it (Miller's Law, `docs/conventions.md`). Stacked even where `base.css`
   makes the label a flex row for its 44px target. */
.request-groups .form-check-label {
  align-items: flex-start;
  flex-direction: column;
  justify-content: center;
}

.request-group + .request-group {
  margin-top: 0.25rem;
}

/* The other groups, folded once a course was tapped (Hick's Law). */
.request-groups__more {
  margin-top: 0.5rem;
}

.request-groups__more > summary {
  align-items: center;
  color: var(--accent-primary);
  cursor: pointer;
  display: flex;
  font-weight: 600;
  min-height: 2.75rem;
}

.request-group__name {
  display: block;
  font-weight: 600;
}

.request-group__facts {
  color: var(--text-muted);
  display: block;
  font-size: var(--text-sm);
}

/* A group the sheet already knows (Tesler's Law, `docs/conventions.md`):
   stated, not asked -- a small label, then the group in the choices' own
   type. */
.request-groups--fact p {
  margin: 0;
}

.request-groups--fact .request-groups__label {
  color: var(--text-muted);
  font-size: var(--text-sm);
  margin-bottom: var(--space-1);
}

/* Asked about already: the button's "Requested", inside the sheet. */
.request-group--requested {
  padding-block: var(--space-2);
}

.request-group__requested {
  align-items: center;
  color: var(--color-brand);
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: 600;
  gap: var(--space-1);
}

.center-kicker {
  color: var(--accent-primary);
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0;
  text-transform: uppercase;
}

.entity-reading-copy {
  max-width: 48rem;
}

.entity-card-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.entity-empty-state {
  background: var(--surface);
  border-radius: var(--radius-card);
  padding: var(--panel-pad);
  text-align: center;
}

/* One empty state for every portal page (`includes/empty_state.html`).
   `crm-empty-icon` is styled in dashboard.css, which the portal does not load.
   The icon sits in a soft brand circle, the same mark the success states use
   (`.success-mark`, `base.css`), so an empty result reads as a designed
   moment rather than a missing one (`docs/aesthetic.md`). */
.entity-empty-state .crm-empty-icon {
  align-items: center;
  background: var(--color-brand-soft);
  border-radius: var(--radius-pill);
  block-size: 3.5rem;
  color: var(--color-brand);
  display: inline-flex;
  font-size: 1.5rem;
  inline-size: 3.5rem;
  justify-content: center;
  margin-bottom: 1rem;
}

.entity-empty-state p {
  margin-inline: auto;
  max-width: 34rem;
}

/* The way back out of an empty result: each chip undoes one choice. */
.empty-state-recovery {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
  margin: 0 0 1.25rem;
}

.empty-state-recovery .filter-chip {
  text-decoration: none;
}

/* Task-first public center profiles. */

.center-detail {
  min-width: 0;
}

.center-profile-tagline {
  color: var(--text-muted);
  display: -webkit-box;
  font-size: 1rem;
  line-height: 1.5;
  margin: 0 0 1rem;
  max-width: 48rem;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

/* The verified tick, wherever an organisation's name shows (the `org_name`
   tag, `portal/templatetags/portal_identity.py`): the text's size beside a
   name, smaller beside a profile's H1. */
.verified-mark {
  color: var(--color-brand);
  font-size: 0.85em;
  vertical-align: baseline;
}

.profile-name .verified-mark {
  font-size: 0.6em;
  vertical-align: middle;
}

/* On a profile the tick is its own ⓘ (`docs/conventions.md`, "Tips"), and
   keeps the size the name gives it. */
.hint-toggle:has(> .verified-mark) {
  font-size: inherit;
}

/* What kind of organisation, when not an education center: the grey tag of
   the cards (`.card-badge--mode`), sitting on the name's line. */
.org-kind {
  display: inline-block;
  vertical-align: middle;
  white-space: nowrap;
}

.center-profile-chips {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.center-profile-courses {
  margin-top: 1rem;
}

.center-profile-chips a {
  align-items: center;
  background: var(--color-brand-soft);
  border-radius: var(--radius-pill);
  color: var(--accent-primary);
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: 700;
  justify-content: center;
  min-height: 2.75rem;
  /* A two-letter subject ("IT") is still a 44px target. */
  min-width: 2.75rem;
  text-decoration: none;
  padding: 0.5rem 0.75rem;
}

.center-profile-chips a:hover,
.center-profile-chips a:focus-visible {
  border-color: var(--accent-primary);
  text-decoration: underline;
}

.group-teacher-byline {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
}

.group-teacher-byline__link {
  align-items: center;
  display: inline-flex;
  gap: 0.5rem;
  min-height: 2.75rem;
  text-decoration: none;
}

.group-teacher-byline__name {
  font-weight: 700;
}

.group-teacher-byline__link:hover .group-teacher-byline__name,
.group-teacher-byline__link:focus-visible .group-teacher-byline__name {
  text-decoration: underline;
}

/* Muted mark, matching the icons in the center hero's fact row. */
.group-teacher-byline i {
  color: var(--text-muted);
}

.entity-rating {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.char-count {
  color: var(--text-muted);
  font-size: var(--text-sm);
  text-align: end;
}

.group-detail .col-lg-4 .center-action-rail .detail-list {
  grid-template-columns: minmax(0, 1fr);
}

.center-detail__layout {
}

/* Sections of a profile page: a heading and the gap above it, no rule. */
.center-section {
  scroll-margin-top: calc(var(--sticky-top) + 4rem);
  padding-top: var(--section-gap);
}

.center-section:first-child {
  padding-top: 0;
}

.center-section h2 {
  font-size: var(--text-heading);
  margin-bottom: 0.75rem;
}

.review-summary strong {
  font-size: 1.5rem;
  margin-right: 0.5rem;
}

.review-list {
  display: grid;
  gap: 1rem;
}

.review-item {
}

.review-item:first-child {
  border-top: 0;
  padding-top: 0;
}

.review-item__head {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
}

.review-score {
  color: var(--calendar-amber);
  letter-spacing: 0.1em;
}

.review-item__text {
  overflow-wrap: anywhere;
}

/* A section title with a link to the whole collection
   (`includes/section_heading.html`): entity pages, articles and posts. */
.section-heading {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  justify-content: space-between;
  margin-bottom: 1rem;
}

.section-heading h2 {
  margin-bottom: 0;
}

.section-heading-link {
  align-items: center;
  display: inline-flex;
  font-weight: 700;
  gap: 0.5rem;
  min-height: 2.75rem;
  text-decoration: none;
  white-space: nowrap;
}

.center-course-grid,
.center-teacher-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.center-teacher-card,
.center-action-rail {
  border-color: var(--border);
  border-radius: var(--radius-card);
  box-shadow: none;
}

.center-teacher-photo,
.center-teacher-fallback {
  border-radius: 50%;
  height: 3.5rem;
  object-fit: cover;
  width: 3.5rem;
}

.center-opening-hours {
  font-size: var(--text-md);
}

.center-opening-hours li {
  align-items: center;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding-block: var(--space-1);
}

.center-action-rail {
  position: static;
}

.center-action-rail .card-body {
  padding: 1.25rem;
}

.center-action-rail__section {
  margin-top: var(--space-5);
}

.center-action-rail__section address,
.center-action-rail__section p {
  color: var(--text-muted);
  font-style: normal;
  margin-bottom: 0.5rem;
}

.center-action-rail__label {
  align-items: center;
  color: var(--text);
  display: flex;
  font-size: var(--text-sm);
  font-weight: 700;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.center-action-rail__label i,
.center-contact-links i {
  color: var(--accent-primary);
  width: 1rem;
}

/* One icon column: every glyph centred in the same fixed slot, so a phone,
   a Telegram plane, a pin and a route line up whatever their own width. */
.center-contact-links i {
  flex: 0 0 auto;
  text-align: center;
  width: 1.25rem;
}

.center-contact-links {
  display: grid;
  gap: 0.25rem;
}

.center-contact-links a {
  align-items: center;
  border-radius: var(--radius-sm);
  color: var(--text);
  display: flex;
  gap: 0.75rem;
  min-height: 2.5rem;
  overflow-wrap: anywhere;
  padding: 0.25rem 0.5rem;
  text-decoration: none;
}

.center-contact-links a:hover,
.center-contact-links a:focus-visible {
  background: var(--color-brand-soft);
  color: var(--accent-primary);
}

.center-social-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.center-social-links a {
  align-items: center;
  border-radius: 0.625rem;
  color: var(--accent-primary);
  display: inline-flex;
  height: 2.75rem;
  justify-content: center;
  text-decoration: none;
  width: 2.75rem;
}

.center-social-links a:hover,
.center-social-links a:focus-visible {
  background: var(--color-brand-soft);
}

@media (min-width: 992px) {
  .center-action-rail {
    height: auto;
    position: sticky;
    top: calc(var(--sticky-top) + 1rem);
  }
}

@media (max-width: 767.98px) {
  .entity-card-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .center-course-grid,
  .center-teacher-grid {
    grid-template-columns: minmax(0, 1fr);
  }

}

.public-listing-card {
  --bs-card-bg: var(--surface);
  border-color: var(--border);
  border-radius: var(--radius-card);
  transition:
    border-color var(--motion-fast) var(--motion-ease),
    box-shadow var(--motion-base) var(--motion-ease);
}

.brand-profile-shell,
.center-detail {
  margin-inline: auto;
  max-width: 72rem;
  width: 100%;
}

.brand-search-form {
  max-width: 40rem;
}

.center-list-brand-mark {
  height: 4.75rem;
  width: 4.75rem;
}

/* Blog and posts ---------------------------------------------------------
   Articles and brand posts share one vocabulary: an open reading page on
   white, and cards with the picture across the top. Reading typography for
   the body lives in rich-text.css under `.article-body`. */

/* A reading page sits on white, like print; listings keep the grey shell. */
.public-marketplace .portal-layout:has(.article-page) {
  background: var(--color-surface);
}

.article-index {
  margin-inline: auto;
  max-width: 76rem;
}

.article-lead {
  color: var(--color-text-muted);
  font-size: var(--text-lead);
  line-height: 1.55;
}

.article-eyebrow {
  color: var(--color-brand);
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  margin-bottom: 0.5rem;
}

.article-meta {
  align-items: baseline;
  color: var(--color-text-muted);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--text-sm);
  gap: 0.25rem 0.5rem;
}

.article-grid {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 768px) {
  .article-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 992px) {
  .article-grid:not(.article-grid--two) {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* Posts vary from a line to a photo; stretching a one-line card to a photo
   card's height leaves it mostly empty. */
.article-grid--posts {
  align-items: start;
}

/* `h-100` resolves against the grid area and would stretch the card anyway;
   the brand page's two-column grid still wants it. */
.article-grid--posts > .h-100 {
  height: auto !important;
}

/* Cards. The body is static so the stretched link covers the picture too.
   They hover like every other card -- border and shadow (`.marketplace-card`)
   -- and never move or zoom (`docs/ux.md`, "Trade-offs"). */

.marketplace-card.article-card .card-body {
  position: static;
}

.article-card-media {
  aspect-ratio: 16 / 9;
  background: var(--color-brand-soft);
  overflow: hidden;
}

.article-card-media img {
  display: block;
  height: 100%;
  object-fit: cover;
  width: 100%;
}

.article-card-placeholder {
  align-items: center;
  background: linear-gradient(
    135deg,
    var(--color-brand-soft),
    color-mix(in srgb, var(--color-brand) 22%, var(--color-surface))
  );
  color: var(--color-brand);
  display: flex;
  font-size: 2.5rem;
  height: 100%;
  justify-content: center;
}

.article-card .article-card-title {
  font-size: var(--text-lg);
  line-height: 1.3;
}

.article-card .article-card-footer {
  align-items: center;
  margin-top: auto;
  padding-top: var(--space-3);
}

.article-card-description {
  color: var(--color-text-muted);
  font-size: 1.0625rem;
  line-height: 1.55;
  margin-bottom: 1.5rem;
}

.article-card--featured {
  margin-bottom: 1.5rem;
}

.article-card.article-card--featured .article-card-title {
  font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2.125rem);
  -webkit-line-clamp: 3;
  line-height: 1.2;
  margin-bottom: 0.75rem;
}

@media (min-width: 768px) {
  .article-card--featured {
    display: grid;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  }

  .article-card--featured .article-card-media {
    aspect-ratio: auto;
    min-height: 22rem;
  }

  .marketplace-card.article-card--featured .card-body {
    justify-content: center;
    padding: var(--panel-pad);
  }
}

/* The reading page: a 46rem text column, a wider cover and wider figures. */
.article-page {
  margin-inline: auto;
  max-width: 64rem;
  padding-bottom: var(--section-gap);
}

.article-page-header,
.article-topics {
  margin-inline: auto;
  max-width: 46rem;
}

.article-page-title {
  font-size: clamp(2rem, 1.2rem + 2.6vw, 3.125rem);
  letter-spacing: -0.01em;
  line-height: 1.15;
  margin-bottom: 1rem;
}

.article-page-header .article-lead {
  margin-bottom: 1.75rem;
}

.article-byline {
  align-items: center;
  display: flex;
  gap: 0.75rem;
  margin-bottom: 2.5rem;
}

.article-byline-mark {
  border-radius: 0.625rem;
  flex: 0 0 auto;
  height: 2.5rem;
  width: 2.5rem;
}

.article-byline-name {
  font-weight: 700;
}

.article-cover {
  background: var(--color-surface-strong);
  border-radius: var(--radius-lg);
  margin: 0 0 2.5rem;
  overflow: hidden;
}

.article-cover img {
  display: block;
  height: auto;
  max-height: 36rem;
  object-fit: cover;
  width: 100%;
}

.article-topics {
  border-top: 1px solid var(--color-border);
  margin-top: 3rem;
  padding-top: 1.5rem;
}

.article-topics-title {
  color: var(--color-text-muted);
  font-family: inherit;
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  margin-bottom: 0.75rem;
  text-transform: uppercase;
}

.article-more {
  margin-inline: auto;
  max-width: 76rem;
  padding-block: var(--space-7) var(--section-gap);
}

.article-more--post {
  max-width: 52rem;
}

.article-more .section-heading {
  margin-bottom: 1.5rem;
}

.article-more .section-heading h2 {
  font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem);
}

/* A brand post has no title: the author row leads, the picture is the cover
   and the first paragraph is set as the heading (rich-text.css). */
.post-author {
  align-items: center;
  display: flex;
  gap: 1rem;
  margin-block: 0.5rem 2rem;
}

.post-author > .min-w-0 {
  flex: 1 1 auto;
}

.post-author-mark {
  align-items: center;
  background: var(--color-surface);
  border-radius: 50%;
  display: inline-flex;
  flex: 0 0 auto;
  height: 3rem;
  justify-content: center;
  overflow: hidden;
  width: 3rem;
}

.post-author-mark:has(.brand-mark-img) {
  border-color: transparent;
}

.post-author .post-author-mark {
  height: 3.5rem;
  width: 3.5rem;
}

.post-author-name {
  font-size: var(--text-lg);
  font-weight: 700;
}

.post-author-name a {
  color: var(--color-text);
  text-decoration: none;
}

.post-author-name a:hover {
  color: var(--color-brand);
}

.post-author-handle {
  color: var(--color-text-muted);
  font-size: var(--text-md);
  font-weight: 400;
}

/* On a phone the name and the trail already link to the centre; the button
   would only push the byline onto extra lines. */
.post-author .post-author-action {
  display: none;
  flex: 0 0 auto;
}

@media (min-width: 576px) {
  .post-author .post-author-action {
    display: inline-flex;
  }
}

/* Contain, not cover: post pictures are often posters a crop would cut. */
.post-page-image img {
  max-height: 40rem;
  object-fit: contain;
}

.post-collection-heading {
  align-items: center;
  display: flex;
  gap: 1rem;
  margin-bottom: 2rem;
}

.post-collection-heading .post-author-mark {
  height: 4rem;
  width: 4rem;
}

.post-collection-heading h1 {
  font-size: var(--text-title);
  margin-bottom: 0.25rem;
}

.post-body {
  overflow-wrap: anywhere;
}

.post-card-author {
  align-items: center;
  display: flex;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.post-card-author .post-author-mark {
  height: 2.5rem;
  width: 2.5rem;
}

.post-card-author .marketplace-card-context {
  display: flex;
  flex-direction: column;
  line-height: 1.35;
}

.post-card-brand {
  color: var(--color-text);
  font-weight: 700;
}

/* In a grid a post is a preview: a few lines, not the whole update. */
.post-card .post-body {
  display: -webkit-box;
  line-height: 1.55;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
}

/* Without a picture the words are the card, so they get the room. */
.post-card--text .post-body {
  border-inline-start: 0.1875rem solid var(--color-brand-soft);
  font-size: 1.0625rem;
  -webkit-line-clamp: 7;
  padding-inline-start: 1rem;
}

.search-tabs .nav-link {
  color: var(--text-muted);
  font-weight: 700;
  white-space: nowrap;
}

.search-tabs .nav-link:hover,
.search-tabs .nav-link:focus {
  color: var(--accent-primary);
}

.search-result-card {
  border-color: var(--border);
  border-radius: var(--radius-card);
  position: relative;
  transition:
    border-color var(--motion-fast) var(--motion-ease),
    box-shadow var(--motion-base) var(--motion-ease);
}

.search-result-card:hover,
.search-result-card:focus-within {
  box-shadow: var(--shadow-sm);
}

/* The title on the card scale, in the text colour like every card title. */
.search-result-title {
  font-size: var(--text-lg);
  line-height: 1.3;
  margin-bottom: 0.5rem;
}

.search-result-title a {
  color: var(--text);
  text-decoration: none;
}

.search-result-card:hover .search-result-title a,
.search-result-title a:focus-visible {
  color: var(--accent-primary);
}

/* A row, not a grid cell: the request sits at its own width. */
.search-result-card .card-request {
  margin-top: 0;
  width: auto;
}

/* The mark a card's provider row uses: a circle, the brand's soft tint for
   an icon, the logo when there is one. */
.search-result-avatar {
  align-items: center;
  background: var(--color-brand-soft);
  border-radius: var(--radius-pill);
  color: var(--accent-primary);
  display: inline-flex;
  flex: 0 0 3.5rem;
  font-size: 1.25rem;
  height: 3.5rem;
  justify-content: center;
  overflow: hidden;
}

.search-result-avatar img {
  height: 100%;
  object-fit: cover;
  width: 100%;
}

.search-result-summary {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.brand-mark {
  align-items: center;
  background: var(--surface);
  border-radius: 50%;
  color: var(--accent-primary);
  display: inline-flex;
  flex: 0 0 auto;
  height: 4rem;
  justify-content: center;
  overflow: hidden;
  width: 4rem;
}

.brand-mark:has(.brand-mark-img) {
  border-color: transparent;
}

.center-teacher-fallback.identity-fallback {
  font-size: 1.5rem;
}

@media (max-width: 991.98px) {
  .public-marketplace .portal-navbar-collapse {
    max-height: calc(100vh - 4.25rem);
    overflow-y: auto;
    padding-bottom: 1rem;
  }

}

@media (max-width: 575.98px) {
  .marketplace-hero-actions,
  .marketplace-hero-actions .btn,
  .landing-section-header,
  .partnership-cta {
    width: 100%;
  }

  .landing-section-header,
  .partnership-cta {
    align-items: flex-start;
    flex-direction: column;
  }

  .partnership-cta .btn {
    width: 100%;
  }

  .center-list-brand-mark {
    height: 3.5rem;
    width: 3.5rem;
  }

  .search-result-card .card-body {
    gap: 0.75rem !important;
  }

  .search-result-avatar {
    flex-basis: 2.75rem;
    height: 2.75rem;
  }

}

/* Multilingual marketplace listings and native landing carousels. */
.public-marketplace .portal-navbar-brand img {
  height: auto;
  width: 8.75rem;
}

.public-marketplace .footer-wordmark img {
  height: auto;
  width: 10.75rem;
}

/* No bottom padding of their own: `.portal-main` already leaves room above
   the footer, and doubling it left listings floating far above it. */
.marketplace-listing {
  width: 100%;
}

.marketplace-listing-header {
  margin-bottom: 1.5rem;
  max-width: 48rem;
}

.marketplace-listing-header h1 {
  font-size: var(--text-title);
  margin-bottom: 0.5rem;
}

.marketplace-listing-header .lead {
  font-size: var(--text-lg);
}

/* The page a collection belongs to, e.g. the branch above "Groups". */
.marketplace-listing-kicker {
  color: var(--text-muted);
  margin-bottom: 0.25rem;
}

.marketplace-listing-layout {
  display: block;
}

.marketplace-results-column,
.marketplace-results {
  min-width: 0;
}

/* While a filter change is in flight the results dim rather than vanish, so
   the page shows something is happening without a layout jump. */
.marketplace-results {
  transition: opacity var(--motion-fast) var(--motion-ease);
}

.marketplace-results.htmx-request {
  opacity: var(--loading-opacity);
}

/* The filter panel's "Show results (N)" while N is being recounted. */
#listing-result-count.is-stale {
  opacity: var(--loading-opacity);
}

/* The header search's page (`portal/site_search.html`): results dim while the
   next ones load, as a listing's do; the field is the site's field shape
   rather than Bootstrap's input group; the ways to browse are chips. */
.site-search-results {
  transition: opacity var(--motion-fast) var(--motion-ease);
}

.site-search-results.htmx-request {
  opacity: var(--loading-opacity);
}

.site-search-form .input-group > .form-control {
  border-radius: var(--field-radius) 0 0 var(--field-radius);
  min-height: var(--field-height);
}

.site-search-form .input-group > .btn {
  border-radius: 0 var(--field-radius) var(--field-radius) 0;
  min-width: var(--field-height);
}

.site-search-browse__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
}

.site-search-browse__chips .filter-chip {
  gap: 0.5rem;
  text-decoration: none;
}

/* A listing's filters: one sliders button at the end of the search row,
   with a count badge, opening one panel that holds every filter -- as hh,
   OLX, Uzum and Airbnb draw it (`docs/ux.md`). */
.listing-filter-dropdown {
  display: flex;
}

.listing-filter-toggle {
  align-items: center;
  display: inline-flex;
  justify-content: center;
  min-height: 3rem;
  min-width: 3rem;
  position: relative;
}

/* The count, on the button's corner; gone at zero. */
.listing-filter-count {
  align-items: center;
  /* The brand, with white figures: dark text on the portal's teal accent read
     at about 3:1, under the 4.5:1 a number this small needs. */
  background: var(--color-brand);
  border: 2px solid var(--color-surface);
  border-radius: var(--radius-pill);
  color: var(--color-on-brand);
  display: inline-flex;
  font-size: var(--text-2xs);
  font-weight: 700;
  height: 1.35rem;
  justify-content: center;
  min-width: 1.35rem;
  padding-inline: 0.25rem;
  position: absolute;
  right: -0.45rem;
  top: -0.45rem;
}

.listing-filter-count:empty {
  display: none;
}

.listing-filter-panel {
  --bs-dropdown-border-color: var(--color-border);
  --bs-dropdown-border-radius: var(--radius-card);
  box-shadow: var(--shadow-lg);
  max-height: min(75vh, 34rem);
  overflow-y: auto;
  padding: 1rem 1.25rem 0;
  width: min(28rem, calc(100vw - 2rem));
}

/* The way out, pinned to the panel's foot while its sections scroll. */
.listing-filter-panel__actions {
  align-items: center;
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  bottom: 0;
  display: flex;
  gap: 0.75rem;
  justify-content: flex-end;
  margin: 1rem -1.25rem 0;
  padding: 0.75rem 1.25rem;
  position: sticky;
}

.listing-filter-panel__actions .btn {
  min-height: 2.75rem;
}

/* A section's title is a heading, one level above the small uppercase
   labels that group the subjects inside it, so the two never read alike. */
.listing-filter-section__title {
  color: var(--color-text);
  font-size: var(--text-md);
  font-weight: 700;
  margin: 0 0 0.25rem;
}

.listing-filter-section__body {
  padding: 0.25rem 0 0.5rem;
}

/* Bootstrap hangs a .form-check input into the container's inline padding,
   which the rule above zeroes -- without this the box lands outside the
   panel and gets clipped by its overflow. */
.listing-filter-section__body.form-check {
  padding-left: 1.5em;
}

.listing-filter-section__body > div:not(.row),
.listing-filter-section__body > ul {
  display: grid;
  gap: 0.5rem;
}

.marketplace-results-column > .listing-local-search {
  max-width: none;
}

.listing-local-search {
  display: grid;
  flex: 1 1 22rem;
  gap: 0.5rem;
  grid-template-columns: minmax(0, 1fr) auto;
  min-width: 0;
}

/* A listing's search row also holds the filters button. */
.listing-local-search:has(.listing-filter-dropdown) {
  grid-template-columns: minmax(0, 1fr) auto auto;
}

.listing-local-search--wide {
  max-width: 48rem;
}

.listing-price-filter {
  padding-top: 0;
}

.price-range-values {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-bottom: 0.5rem;
}

.price-range-values output {
  color: var(--color-text);
  font-size: 1rem;
  font-weight: 700;
  min-width: 0;
  overflow-wrap: anywhere;
}

.price-range-values output:last-child {
  text-align: end;
}

.price-range-control {
  height: 3.25rem;
  position: relative;
}

.price-range-control::before {
  background: var(--color-border);
  border-radius: var(--radius-pill);
  content: "";
  height: 0.4rem;
  inset: 50% 0 auto;
  pointer-events: none;
  position: absolute;
  transform: translateY(-50%);
}

.price-range-control::after {
  background: var(--color-brand);
  border-radius: var(--radius-pill);
  content: "";
  height: 0.4rem;
  left: var(--price-range-start);
  pointer-events: none;
  position: absolute;
  right: calc(100% - var(--price-range-end));
  top: 50%;
  transform: translateY(-50%);
}

.price-range-control input[type="range"] {
  appearance: none;
  background: transparent;
  inset: 0;
  margin: 0;
  pointer-events: none;
  position: absolute;
  width: 100%;
}

.price-range-control input[type="range"]:focus {
  z-index: 2;
}

.price-range-control input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  background: var(--color-brand);
  border: 0;
  border-radius: 50%;
  box-shadow: none;
  cursor: grab;
  height: 1.125rem;
  pointer-events: auto;
  width: 1.125rem;
}

.price-range-control input[type="range"]::-moz-range-thumb {
  background: var(--color-brand);
  border: 0;
  border-radius: 50%;
  box-shadow: none;
  cursor: grab;
  height: 1.125rem;
  pointer-events: auto;
  width: 1.125rem;
}

.price-range-control input[type="range"]:focus-visible::-webkit-slider-thumb {
  box-shadow: var(--focus-ring);
}

.price-range-control input[type="range"]:focus-visible::-moz-range-thumb {
  box-shadow: var(--focus-ring);
}

/* One field shape for every search box on the portal (`--field-radius`),
   as tall as the buttons beside it. */
.listing-local-search .form-control {
  background: var(--color-surface);
  border-radius: var(--field-radius);
  min-height: 3rem;
  min-width: 0;
}

.listing-results-actions {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.listing-results-toolbar {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: space-between;
  min-height: 2.75rem;
}

.listing-results-actions .form-select {
  min-height: 2.75rem;
  min-width: 12rem;
}

.marketplace-card-grid {
  display: grid;
  gap: 1rem;
  /* One rule for every width: cards are at least 20rem, and as many fit as the
     viewport allows. `min()` collapses it to a single column on a phone.
     Nothing caps the column count here -- `--page-max-width` bounds the shell,
     which bounds this to four. Two mechanisms for one outcome would drift. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
}

.marketplace-card {
  --bs-card-bg: var(--color-surface);
  border-radius: var(--radius-card);
  box-shadow: none;
  min-width: 0;
  overflow: hidden;
  position: relative;
}

.landing-faq .accordion-item,
.landing-faq .accordion-button {
  background: var(--color-surface);
}

/* A band is a rounded panel on the page colour, not a stripe to the viewport
   edges: the frame says "one section" without a rule above it. The gap to
   the section before is outside the panel and its padding inside, or the
   white would start flush against whatever ends the section above. */
.landing-section--band {
  background: var(--color-surface);
  border-radius: var(--radius-panel);
  margin-top: var(--section-gap);
  padding: var(--panel-pad);
}

.landing-faq .accordion-button {
  box-shadow: none;
  color: var(--color-text);
  font-size: 1.1rem;
  font-weight: 700;
  gap: 1rem;
  padding-inline: 0;
}

.landing-faq .accordion-button::after {
  display: none;
}

.landing-faq .accordion-button:focus-visible {
  outline: 0.2rem solid color-mix(in srgb, var(--color-focus) 28%, transparent);
  outline-offset: 0.15rem;
}

.landing-faq .accordion-body {
  color: var(--color-text-muted);
  max-width: 56rem;
  padding-inline: 0;
}

.landing-faq-icon {
  align-items: center;
  border-radius: 50%;
  display: inline-flex;
  flex: 0 0 auto;
  height: 2.5rem;
  justify-content: center;
  margin-left: auto;
  width: 2.5rem;
}

.landing-faq .accordion-button.collapsed .fa-minus,
.landing-faq .accordion-button:not(.collapsed) .fa-plus {
  display: none;
}

.marketplace-card:hover,
.marketplace-card:focus-within {
  box-shadow: var(--shadow-sm);
}

.marketplace-card .card-body {
  display: flex;
  flex-direction: column;
  padding: 1rem;
  position: relative;
}

.marketplace-card-chips :is(a, span) {
  /* Height comes from the text; only links get a touch target (below). */
  align-items: center;
  background: var(--color-brand-soft);
  border-radius: var(--radius-pill);
  color: var(--color-brand);
  display: inline-flex;
  font-size: var(--text-xs);
  font-weight: 700;
  gap: 0.25rem;
  padding: 0.25rem 0.5rem;
}

.marketplace-card-chips a {
  /* A full touch target (44px, Fitts): the chips are links on phones too. */
  min-height: 2.75rem;
  position: relative;
  text-decoration: none;
  z-index: 2;
}

.marketplace-card-chips a:hover,
.marketplace-card-chips a:focus-visible {
  background: var(--color-brand);
  color: var(--color-on-brand);
}

/* Below the price in weight: the price is what a parent compares. */
.marketplace-card-title {
  display: -webkit-box;
  font-size: var(--text-lg);
  line-height: 1.3;
  margin-bottom: 0.25rem;
  overflow: hidden;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.marketplace-card-tagline {
  color: var(--color-text-muted);
  display: -webkit-box;
  line-height: 1.45;
  margin-bottom: 0.75rem;
  overflow: hidden;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.marketplace-card-context,
.marketplace-card-secondary {
  color: var(--color-text-muted);
  display: -webkit-box;
  font-size: var(--text-sm);
  line-height: 1.45;
  margin-bottom: 0.25rem;
  overflow: hidden;
  overflow-wrap: anywhere;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.marketplace-card-rating {
  align-items: center;
  display: flex;
  gap: 0.25rem;
}

.marketplace-card-rating i {
  color: var(--color-rating);
}

.marketplace-card-secondary {
  align-items: baseline;
  gap: 0.5rem;
}

.marketplace-card-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

/* The price is the strongest text on a card: it is what a learner compares. */
.marketplace-card-value {
  color: var(--color-text);
  display: block;
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.2;
  overflow-wrap: anywhere;
}

/* Price on the left, a tag or two on the right; the whole card is the link,
   so there is no arrow (`docs/conventions.md`). */
.marketplace-card-footer {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  justify-content: space-between;
  margin-top: auto;
  padding-top: 0.5rem;
}

.marketplace-center-heading {
  align-items: start;
  display: flex;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.marketplace-center-heading .center-list-brand-mark {
  height: 3rem;
  width: 3rem;
}

/* Who offers it, as hh and Coursera show the employer: a small logo, the
   name and place on one line, the heart at the end. */
.card-provider {
  align-items: center;
  display: flex;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.card-provider-mark {
  flex: 0 0 auto;
  height: 2.25rem;
  width: 2.25rem;
}

.card-provider-name {
  flex: 1 1 auto;
  font-size: var(--text-sm);
  font-weight: 600;
  margin: 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-eyebrow {
  color: var(--color-brand);
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  margin-bottom: 0.25rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Plain facts a parent compares, told apart by type, not by pills. */
.card-facts {
  color: var(--color-text-muted);
  display: -webkit-box;
  font-size: var(--text-sm);
  line-height: 1.45;
  margin-bottom: 0.25rem;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.card-salary {
  margin-bottom: 0.25rem;
}

.card-tags {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  justify-content: flex-end;
}

@media (max-width: 991.98px) {
  .public-marketplace .portal-navbar-actions {
    padding-top: 0.5rem;
  }
}

@media (min-width: 992px) {
  .public-marketplace .public-page-shell--home {
    padding-top: 0;
  }
}

@media (max-width: 575.98px) {
  .public-marketplace .portal-navbar-brand img {
    width: 7.75rem;
  }

  .listing-results-toolbar {
    align-items: stretch;
    flex-direction: column;
  }

  .listing-results-actions,
  .listing-results-actions .form-select {
    width: 100%;
  }

  .listing-results-actions .btn-group {
    flex: 0 0 auto;
  }
}

@media (min-width: 992px) {
  .brand-collection-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (min-width: 768px) and (max-width: 991.98px) {
  .brand-collection-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 767.98px) {
  .brand-collection-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.marketplace-pagination .page-link {
  align-items: center;
  border-radius: 50% !important;
  color: var(--color-text);
  display: inline-flex;
  justify-content: center;
  height: 3.5rem;
  width: 3.5rem;
  padding: 0.5rem;
  border: 0;
}

.marketplace-pagination .pagination {
  align-items: center;
  gap: 0.75rem;
}

.marketplace-pagination .page-link:hover,
.marketplace-pagination .page-link:focus-visible {
  background: var(--color-brand-soft);
  color: var(--color-brand);
}

.marketplace-pagination .pagination-range .page-link {
  background: var(--color-background);
  border-radius: var(--radius-pill) !important;
  cursor: default;
  font-size: 1.05rem;
  gap: 0.25rem;
  height: 3.5rem;
  min-width: 10rem;
  padding-inline: 1.25rem;
  width: auto;
}

.marketplace-pagination .pagination-range__total {
  color: var(--color-text-muted);
}

.marketplace-pagination .page-item.disabled .page-link {
  background: var(--color-surface);
  border-color: var(--color-border);
  color: var(--color-text-muted);
  opacity: 0.55;
}

.page-updated {
  /* Echoes .center-section's rule and rhythm so the note closes the page
     instead of floating under it. */
  display: flex;
  justify-content: flex-end;
  margin-top: var(--section-gap);
}

.page-updated__note {
  align-items: center;
  background: var(--bg);
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  display: inline-flex;
  font-size: var(--text-xs);
  gap: 0.5rem;
  line-height: 1.3;
  padding: 0.25rem 0.75rem;
}

.page-updated__note i {
  font-size: var(--text-2xs);
  opacity: 0.75;
}

@media (max-width: 575.98px) {
  .page-updated {
    justify-content: flex-start;
  }
}

/* Legal pages --------------------------------------------------------------
   A reading surface, not a scanning one: one narrow column, generous leading,
   and headings that separate sections rather than shout. */

.legal-page {
  margin-inline: auto;
  max-width: 46rem;
}

.legal-body {
  line-height: 1.7;
}

.legal-body h2 {
  font-size: var(--text-lg);
  margin-top: 2rem;
}

.legal-body ul {
  padding-inline-start: 1.25rem;
}

.legal-body li {
  margin-bottom: 0.5rem;
}

/* The phone action bar: the page's primary action, fixed to the bottom edge
   below lg. Hidden while the hero's own copy of the button is on screen, so
   there is never two. */
.mobile-action-bar {
  background: var(--color-surface);
  bottom: 0;
  box-shadow: var(--shadow-raised-bottom);
  display: flex;
  gap: 0.5rem;
  inset-inline: 0;
  padding: 0.75rem 1rem calc(0.75rem + env(safe-area-inset-bottom));
  position: fixed;
  transition: transform var(--motion-base) var(--motion-ease);
  z-index: 1030;
}

.mobile-action-bar.is-hidden {
  transform: translateY(110%);
}

.mobile-action-bar .btn {
  flex: 1 1 auto;
  min-height: 3rem;
}

@media (max-width: 991.98px) {
  .portal-main:has(.mobile-action-bar) {
    padding-bottom: 5.5rem;
  }
}

/* What's new feed ---------------------------------------------------------
   LinkedIn's layout and post anatomy: a centred column of posts -- the actor
   with the post's age and a share icon, one sentence, the records as rows --
   with a sticky rail beside it from lg. No action row under each post: the
   header goes to the brand and each row to its record. `docs/feed.md`. */

.landing-feed {
  display: grid;
  gap: var(--space-5);
  margin-inline: auto;
  max-width: 62rem;
}

@media (min-width: 992px) {
  .landing-feed {
    align-items: start;
    grid-template-columns: minmax(0, 1fr) 19rem;
  }

  .feed-rail {
    position: sticky;
    top: calc(var(--sticky-top) + var(--space-4));
  }
}

.landing-feed-main {
  min-width: 0;
}

.landing-feed-header {
  margin-bottom: var(--space-4);
}

.feed-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

/* The chosen chip as a listing's quick filter shows it: filled, not only
   outlined (`conventions.css`). */
.feed-filters .filter-chip.active {
  background: var(--color-brand-soft);
  border-color: var(--accent-primary);
  color: var(--accent-primary);
  font-weight: 600;
}

.feed-list {
  display: grid;
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.feed-post {
  background: var(--surface);
  border-radius: var(--radius-card);
  padding: var(--card-pad);
}

.feed-post-top {
  align-items: flex-start;
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.feed-post-header {
  align-items: center;
  color: inherit;
  display: flex;
  flex: 1 1 auto;
  gap: var(--space-3);
  min-height: 3rem;
  min-width: 0;
  text-decoration: none;
}

a.feed-post-header:hover .feed-post-name {
  text-decoration: underline;
}

.feed-post-avatar {
  border-radius: 50%;
  flex: 0 0 auto;
  height: 3rem;
  overflow: hidden;
  width: 3rem;
}

.feed-post-avatar .brand-mark-fallback {
  font-size: var(--text-lg);
}

.feed-post-actor {
  display: grid;
  min-width: 0;
}

.feed-post-name {
  color: var(--text);
  display: block;
  font-weight: 700;
  line-height: 1.3;
  text-decoration: none;
}

.feed-post-subtitle {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* The card's one other action: a quiet icon that takes a shape when pointed
   at, like the heart (`docs/aesthetic.md`, round 3). */
.feed-post-share {
  align-items: center;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--color-text-muted);
  display: inline-flex;
  flex: 0 0 2.75rem;
  height: 2.75rem;
  justify-content: center;
  margin-inline-end: calc(var(--space-2) * -1);
  padding: 0;
}

.feed-post-share:hover,
.feed-post-share:focus-visible {
  background: var(--color-brand-soft);
  color: var(--accent-primary);
}

.feed-post-share[data-share-state="done"] {
  color: var(--accent-primary);
}

.feed-post-text {
  margin-bottom: var(--space-1);
}

/* The records, a row each with a hairline between them -- no box around
   them inside the post's own surface (`docs/aesthetic.md`, round 3). A row is
   one link (stretched); pointing at it underlines its title. */
.feed-attachment {
  display: block;
  margin: 0;
}

.feed-attachment-row {
  align-items: center;
  display: flex;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  position: relative;
}

.feed-attachment-row:last-child {
  padding-bottom: 0;
}

.feed-attachment-row + .feed-attachment-row,
.feed-attachment-more {
  border-top: 1px solid var(--border);
}

.feed-attachment-row:hover .feed-attachment-title,
.feed-attachment-row:focus-within .feed-attachment-title,
.feed-attachment--article:hover .feed-attachment-title {
  text-decoration: underline;
}

.feed-attachment-row .save-toggle {
  position: relative;
  z-index: 2;
}

.feed-attachment-text {
  display: grid;
  flex: 1 1 auto;
  min-width: 0;
}

.feed-attachment-title {
  color: var(--text);
  font-weight: 600;
  overflow-wrap: anywhere;
  text-decoration: none;
}

/* A post's row is its opening, read rather than scanned: body weight, three
   lines at most. */
.feed-attachment-excerpt {
  -webkit-box-orient: vertical;
  display: -webkit-box;
  font-weight: 400;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

/* Facts wrap to a second line rather than losing the schedule's end. */
.feed-attachment-fact {
  -webkit-box-orient: vertical;
  color: var(--color-text-muted);
  display: -webkit-box;
  font-size: var(--text-sm);
  -webkit-line-clamp: 2;
  line-clamp: 2;
  margin: var(--space-1) 0 0;
  overflow: hidden;
}

/* The number a reader compares (price, salary) reads before the rest. */
.feed-attachment-fact--lead {
  color: var(--text);
  font-weight: 600;
}

.feed-attachment-thumb {
  border-radius: var(--radius-md);
  flex: 0 0 auto;
  height: 4rem;
  object-fit: cover;
  width: 4rem;
}

.feed-attachment-more {
  padding-top: var(--space-1);
}

.feed-attachment-more a {
  align-items: center;
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: 600;
  gap: var(--space-1);
  min-height: 2.75rem;
  text-decoration: none;
}

.feed-attachment--article {
  color: var(--text);
  display: grid;
  gap: var(--space-3);
  padding-top: var(--space-2);
  text-decoration: none;
}

.feed-attachment--article .reel-cover {
  aspect-ratio: 2 / 1;
  border-radius: var(--radius-md);
  display: block;
  object-fit: cover;
  width: 100%;
}

.feed-more {
  text-align: center;
}

.feed-end {
  display: grid;
  gap: var(--space-1);
  justify-items: center;
  padding-block: var(--space-2);
}

.feed-end .show-more-end i {
  color: var(--accent-primary);
}

/* A parent's review: the stars, their words, who wrote them, the course. */
.reel-stars {
  color: var(--color-rating);
  margin-bottom: var(--space-1);
}

.reel-quote {
  border: 0;
  line-height: 1.55;
  margin: 0 0 var(--space-1);
}

.reel-card-meta {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  margin-bottom: 0;
}

/* The rail: small boxes of plain rows -- the brand's mark, a name, a line --
   then the small print. */
.feed-rail {
  display: grid;
  gap: var(--space-3);
}

.feed-rail-box {
  background: var(--surface);
  border-radius: var(--radius-card);
  display: grid;
  gap: var(--space-3);
  padding: var(--card-pad);
}

.feed-rail-title {
  font-size: var(--text-md);
  font-weight: 700;
  margin: 0;
}

.reel-rows {
  display: grid;
  gap: var(--space-2);
  margin: 0;
}

.reel-row {
  align-items: center;
  display: flex;
  gap: var(--space-3);
  /* The whole row is the target, its link stretched over it (Fitts). */
  min-height: 2.75rem;
  position: relative;
}

.reel-row a {
  color: var(--text);
  font-weight: 600;
  text-decoration: none;
}

.reel-row:hover a {
  text-decoration: underline;
}

.reel-row-text {
  display: grid;
  min-width: 0;
}

.reel-row-meta {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* The number a reader compares, first in a rail row. */
.reel-row-price {
  color: var(--text);
}

.reel-row-mark {
  flex: 0 0 auto;
  height: 2.25rem;
  width: 2.25rem;
}

.reel-more {
  align-items: center;
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: 600;
  gap: var(--space-1);
  min-height: 2.75rem;
}

/* One centred line: the links are 44px rows (Fitts), so the plain "©" text
   is centred on them rather than riding at their top. Below lg the rail falls
   under the feed and the site footer follows straight after with the same
   links, so this one is not shown twice. */
.feed-rail-footer {
  align-items: center;
  color: var(--color-text-muted);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--text-xs);
  gap: 0 var(--space-3);
  justify-content: center;
}

@media (max-width: 991.98px) {
  .feed-rail-footer {
    display: none;
  }
}

.feed-rail-footer a {
  align-items: center;
  color: inherit;
  display: inline-flex;
  min-height: 2.75rem;
}

/* 404 ------------------------------------------------------------------------
   The code large, then search over the three listings, the main destinations,
   a report link. The code sits on every error page (500 too). */

.error-code {
  color: var(--color-brand);
  font-size: var(--text-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
}

.error-help {
  max-width: 40rem;
}

.error-search-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.error-destinations {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0;
}

/* Home: the promise, its search and its proof --------------------------------
   The hero says one thing, offers its first step, and backs it with live
   counts. White type on the brand panel; the proof figures in the highlight. */

.landing-promise {
  font-size: var(--text-lead);
  line-height: 1.5;
  margin-bottom: 1.25rem;
  max-width: 34rem;
  opacity: 0.92;
}

/* The hero's one decision: the filled Find a course, the search beside it
   (under it on phones), then the teachers' line. */
.landing-actions {
  align-items: stretch;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  max-width: 46rem;
}

.public-marketplace .btn.landing-cta {
  align-items: center;
  border-radius: var(--radius-pill);
  display: inline-flex;
  font-weight: 700;
  justify-content: center;
  min-height: 3rem;
  padding-inline: 1.5rem;
  white-space: nowrap;
}

.landing-search {
  display: grid;
  flex: 1 1 22rem;
  gap: 0.5rem;
  grid-template-columns: minmax(0, 1fr) auto;
  min-width: 0;
}

@media (min-width: 576px) {
  .landing-search:has(select) {
    grid-template-columns: minmax(0, 1fr) minmax(8rem, 11rem) auto;
  }
}

@media (max-width: 575.98px) {
  .public-marketplace .btn.landing-cta {
    width: 100%;
  }

  /* The words get the room; the city reads in fewer characters. */
  .landing-search:has(select) {
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) auto;
  }
}

.landing-search .form-control,
.landing-search .form-select {
  border-radius: var(--field-radius);
  min-height: 3rem;
}

/* Quiet beside the filled CTA: an outline square, 48px, the label spoken. */
.landing-search-submit {
  align-items: center;
  display: inline-flex;
  justify-content: center;
  min-height: 3rem;
  min-width: 3rem;
}

.landing-teachers {
  margin: 0.5rem 0 0;
}

.landing-teachers a {
  color: inherit;
  font-weight: 700;
  opacity: 0.92;
  text-underline-offset: 0.2em;
}

.landing-proof {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.25rem;
  margin: 1.25rem 0 0;
}

.landing-proof strong {
  color: var(--highlight-bg);
  font-size: 1.15em;
}

/* Home: the other side of the marketplace, two tiles side by side. */

.landing-sides {
  align-items: stretch;
  display: grid;
  gap: 1.5rem;
}

@media (min-width: 768px) {
  .landing-sides {
    grid-template-columns: 1fr 1fr;
  }
}

.landing-side {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.landing-side .btn {
  margin-top: auto;
}

/* Centres only now: one tile, centred, its action under the copy. */
.landing-sides--single {
  grid-template-columns: 1fr;
  text-align: center;
}

.landing-sides--single .landing-side {
  align-items: center;
}

/* Hubs: the pulse above a listing's results, and honest badges on cards. */

.hub-pulse {
  background: var(--color-surface);
  border-radius: var(--radius-card);
  margin-bottom: 1rem;
  padding: 0.75rem 1rem;
}

.hub-pulse-line {
  font-weight: 600;
  margin-bottom: 0.25rem;
}

.hub-pulse-new {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  margin: 0;
}

.hub-pulse-new a {
  font-weight: 600;
}

.card-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-bottom: 0.5rem;
}

.card-badge {
  border-radius: var(--radius-pill);
  color: var(--accent-primary);
  font-size: var(--text-2xs);
  font-weight: 700;
  padding: var(--space-1) var(--space-2);
  background: var(--color-brand-soft);
}

/* Tags differ by more than colour (Von Restorff, `docs/conventions.md`):
   "New" is the one teal tag; the lesson mode is a grey tag with its icon;
   a closing deadline is the warning tint with a clock, as `.alert-warning`
   and the vacancy page's "Closes in" carry it. */
.card-badge i {
  margin-inline-end: var(--space-1);
}

.card-badge--mode {
  background: var(--bg);
  color: var(--text);
}

.card-badge--soon {
  background: var(--identity-yellow-start);
  color: var(--identity-yellow-ink);
}

/* The account island's placeholder (`navbar.html`): the signed-out links for
   a visitor, the shape of the menu for a browser the `kvm` hint says is
   signed in, until the real menu replaces it. */
.portal-account__skeleton {
  display: none;
}

/* `!important` only to outrank the links' own `d-inline-block` utility. */
.is-member .portal-account--pending > :not(.portal-account__skeleton) {
  display: none !important;
}

.is-member .portal-account--pending {
  align-items: center;
  display: flex;
  gap: var(--space-2);
}

.is-member .portal-account--pending > .portal-account__skeleton {
  border-radius: var(--radius-pill);
  display: block;
  height: calc(1.5em + 0.5rem + 2px);
  width: 7.5rem;
}

/* The bell, which arrives with the menu as its own item beside it. */
.is-member .portal-account--pending > .portal-account__skeleton--bell {
  border-radius: var(--radius-sm);
  width: calc(1.5em + 0.5rem + 2px);
}

/* Navbar items that fit only on a wide screen (`portal/navbar.html`): the
   desktop row is full below 1600px, so Programmes and Schools wait for the
   phone menu or the room. */
@media (min-width: 992px) and (max-width: 1599.98px) {
  .portal-navbar-primary .nav-item--wide {
    display: none;
  }
}

/* A flash message's room, held before the first paint when the `kvf` hint
   says one is waiting (`portal_base.html`), until the account island brings
   it in place. */
.portal-messages__skeleton {
  display: none;
}

.has-message .portal-messages__skeleton {
  display: block;
}
