/**
 * =============================================================================
 * PAGE: Collaborative Projects (listing) + single project
 * =============================================================================
 * No Figma frame yet: this stylesheet deliberately reuses the About / Partners
 * rhythm (--space-30 section tops, --container-content width, same type scale)
 * so both pages look native today and can be restyled from this one file when a
 * mockup lands. All values from variables.css. BEM naming. Mobile-first.
 *
 * The listing page is split into three sections, one per family of project
 * (.collab-group), each a stack of full-width .project-card rows. That card is
 * a component of its own, defined below: it used to be .card-blog plus a few
 * overrides, and the reasoning for the split is in the PROJECT CARD header.
 * =============================================================================
 */

/* =========================================================================
 * SHARED SECTION SPACING — mirrors .partners-* / .about-mission
 * ========================================================================= */
.collab-intro,
.collab-group,
.collab-timeline,
.collab-pubs,
.collab-header,
.collab-body,
.collab-objectives,
.collab-role,
.collab-consortium,
.collab-more {
  padding: var(--space-30) var(--padding-x) 0;
}

.collab-intro__inner,
.collab-group__inner,
.collab-timeline__inner,
.collab-pubs__inner,
.collab-header__inner,
.collab-body__inner,
.collab-objectives__inner,
.collab-role__inner,
.collab-consortium__inner,
.collab-more__inner {
  max-width: var(--container-content);
  margin-inline: auto;
}

/* The last section before the request-demo block closes the page rhythm. */
.collab-more {
  padding-bottom: var(--space-30);
}

/* Bottom of the LISTING page. Its sections only carry top padding, on the
   assumption that the request-demo block underneath brings the 80px that closes
   the page. That block can be switched off from the Page Sections box, and it is
   on this site, so whichever project section ends up last was sitting flush
   against the footer.
   Hung off :last-child rather than given to every section, because which one
   comes last depends on what is published and on the editor's section order:
   between two sections the 60px would double up with the next one's own top
   padding. If an editor appends a block after them, that block becomes the last
   child and brings its own spacing, which is the right answer too. */
.collab-page > .collab-group:last-child,
.collab-page > .collab-timeline:last-child {
  padding-bottom: var(--space-30);
}

/* Everything is flush left, matching the rest of the site. Kept as one explicit
   rule rather than relying on inheritance, so a centred heading cannot creep
   back in via a shared block or a parent's text-align. */
.collab-intro__title,
.collab-group__title,
.collab-timeline__title,
.collab-pubs__title,
.collab-body__title,
.collab-objectives__title,
.collab-role__title,
.collab-consortium__title,
.collab-more__title {
  text-align: left;
}

/* =========================================================================
 * INTRO
 * ========================================================================= */
/* Spacing follows page-about.css, which uses exactly two values:
     16px (--space-8)  → between a title and its OWN body copy
     40px (--space-20) → between a title and the BLOCK it introduces
   The intro below is the first case; every other section heading is the second. */
.collab-intro__title {
  font-family: var(--font-gilroy);
  font-weight: var(--fw-bold);
  font-size: var(--text-3xl);
  line-height: var(--lh-snug);
  color: var(--color-white);
  margin: 0 0 var(--space-8);
}

.collab-intro__body {
  font-family: var(--font-inter);
  font-size: var(--text-lg);
  line-height: var(--lh-relaxed);
  color: var(--color-white-70);
  margin: 0 0 var(--space-8);
}

.collab-intro__body:last-child {
  margin-bottom: 0;
}

/* =========================================================================
 * PROJECT SECTIONS — the three .collab-group blocks, and "Other projects"
 * ========================================================================= */
/* Title left, button flush right on the same baseline — the same header row as
   .partners-pubs__header and .home-uc__header. Only the single project page
   uses it now: the listing's own heading lost its button on 2026-08-20 and is a
   plain h2 carrying its own 40px. */
.collab-pubs__header,
.collab-more__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-8);
  margin-bottom: var(--space-20); /* 40px, the gap a heading owes the block below */
}

.collab-group__title,
.collab-timeline__title,
.collab-pubs__title,
.collab-body__title,
.collab-objectives__title,
.collab-role__title,
.collab-consortium__title,
.collab-more__title {
  font-family: var(--font-gilroy);
  font-weight: var(--fw-bold);
  font-size: var(--text-3xl);
  line-height: var(--lh-snug);
  color: var(--color-white);
  margin: 0 0 var(--space-20);
}

/* Inside a header row the 40px belongs to the row, not to the heading. */
.collab-pubs__header .collab-pubs__title,
.collab-more__header .collab-more__title {
  margin-bottom: 0;
}

/* A long title must not squeeze the pill, which has white-space: nowrap. */
.collab-pubs__header .btn,
.collab-more__header .btn {
  flex: 0 0 auto;
}

.collab-group__intro,
.collab-timeline__intro {
  font-family: var(--font-inter);
  font-size: var(--text-lg);
  line-height: var(--lh-relaxed);
  color: var(--color-white-70);
  max-width: 780px;
  margin: 0 0 var(--space-20);
}

/* A stack of full-width rows, not a grid. The page is split into three families
   and most of them hold one or two projects, where a three-column grid left one
   card beside two empty columns. */
.collab-group__list,
.collab-more__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

/* Editor-only notes, rendered only for users who can act on them (see the
   template): one under the intro when no project at all is published, one in
   the timeline when no project is marked Completed. Same muted treatment in
   both places, because neither is page copy. */
.collab-intro__empty,
.collab-timeline__empty {
  font-family: var(--font-inter);
  font-size: var(--text-base);
  line-height: var(--lh-relaxed);
  color: var(--color-white-50);
  max-width: 780px;
  margin: var(--space-8) 0 0;
}

/* In the timeline the note follows the heading, which already owes the 40px
   below it; the 16px the note carries under the intro would stack on top. */
.collab-timeline__empty {
  margin-top: 0;
}

/* =========================================================================
 * PROJECT CARD — a component of its own, one card per row
 * =========================================================================
 * It used to be .card-blog plus a handful of overrides, i.e. the same object as
 * a blog teaser. A funded research programme is not an article: what identifies
 * it is its logo, whether it is running, how long it runs for, who funds it and
 * how many partners are in it, and none of that fits a photo-title-excerpt
 * column. See the header of parts/card-project.php.
 *
 * Two variants, same skeleton:
 *   --full     the listing page: excerpt, key facts, topics
 *   --compact  "Other projects" on a project page: identity and status only
 * ========================================================================= */
.project-card {
  position: relative;
  display: grid;
  /* One column by default; the visual column is ADDED by --media below, and only
     a project with a featured image gets that modifier. Written this way round
     on purpose: as a --no-media override it lost to the later --compact rule and
     a compact card with no photo put its whole body in the 200px picture track. */
  grid-template-columns: minmax(0, 1fr);
  overflow: hidden;
  border: 1px solid var(--color-white-10);
  border-radius: var(--radius-lg);
  background: var(--color-white-04);
  transition: border-color var(--transition-base), background var(--transition-base);
}

.project-card:hover,
.project-card:focus-within {
  border-color: var(--color-glass-lime-35);
  background: var(--color-white-10);
}

/* The whole card is the link target. The anchor inside the heading is stretched
   over the article, which keeps exactly one link in the accessibility tree with
   the project name as its text. z-index 1 puts it over the media and the logo
   plate, so no part of the card is a dead zone where a click does nothing. */
.project-card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* The stretched link swallows pointer events, so the hover state has to be
   driven from the article rather than from the anchor. */
.project-card:hover .project-card__title a,
.project-card:focus-within .project-card__title a {
  text-decoration: underline;
}

/* Visual left at a fixed width, everything else in the remaining space. A fixed
   column rather than a fraction: the photo is a marker, and letting it grow with
   the container turned the widest cards into a photo with a caption. Below 768px
   the two stack (see RESPONSIVE at the bottom of this file).

   A project with no featured image never gets this class, and is a full-width
   text row instead: a 280px panel of flat gradient beside the text is an empty
   box, not a placeholder. */
.project-card--media {
  grid-template-columns: 280px minmax(0, 1fr);
}

/* -------------------------------------------------------------------------
 * Media
 * ------------------------------------------------------------------------- */
.project-card__media {
  position: relative;
  overflow: hidden;
  min-height: 180px;
  background: var(--color-white-04);
}

.project-card__image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.project-card__status {
  position: absolute;
  top: var(--space-5);
  left: var(--space-5);
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-white-20);
  background: var(--color-glass-teal-75);
  font-family: var(--font-inter);
  font-size: var(--text-xs);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-normal);
  color: var(--color-white);
  backdrop-filter: blur(6px);
}

/* Live work is the state worth spotting down a long page, so only it gets the
   accent tint; the other two stay neutral. */
.project-card__status--ongoing {
  border-color: var(--color-glass-lime-35);
  color: var(--color-text-accent);
}

.project-card__status--completed {
  color: var(--color-white-70);
}

/* Same chip, in the heading instead of on a photo the card does not have. It
   sits in the flow, so the absolute placement and the blur behind it, which
   only ever existed to lift it off an image, are undone. */
.project-card__status--inline {
  position: static;
  display: inline-block;
  margin-bottom: var(--space-5);
  background: var(--color-white-04);
  backdrop-filter: none;
}

/* -------------------------------------------------------------------------
 * Body
 * ------------------------------------------------------------------------- */
/* One even gap between the rows (heading, excerpt, facts, topics, link) instead
   of a bottom margin per element: any of them can be missing on a thinly filled
   project, and per-element margins leave the gap of whichever one survived. */
.project-card__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-8);
  min-width: 0;
  padding: var(--space-10);
}

/* Title column left, logo plate flush right on the same top edge. */
.project-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-8);
  width: 100%;
}

.project-card__heading {
  min-width: 0;
}

.project-card__title {
  font-family: var(--font-gilroy);
  font-weight: var(--fw-bold);
  font-size: var(--text-xl);
  line-height: var(--lh-snug);
  color: var(--color-white);
  margin: 0;
}

.project-card__title a {
  color: inherit;
  text-decoration: none;
}

.project-card__fullname {
  font-family: var(--font-inter);
  font-size: var(--text-sm);
  line-height: var(--lh-normal);
  color: var(--color-white-50);
  margin: var(--space-2) 0 0;
}

/* White plate: project logos are almost always dark-on-white and would vanish
   against the dark card. Beside the title rather than over the photo, where it
   fought with the status chip and covered the image at card size. */
/* Wider than tall, and wider than it looks like it needs to be: a funding
   programme's logo is nearly always a wordmark with an EU flag or a strapline
   beside it, so height is what a plate this size wastes and width is what makes
   the mark readable. */
.project-card__logo {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 124px;
  height: 64px;
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-white);
}

.project-card__logo img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

.project-card__excerpt {
  font-family: var(--font-inter);
  font-size: var(--text-lg);
  line-height: var(--lh-relaxed);
  color: var(--color-white-70);
  margin: 0;
  /* Excerpts are written to no fixed length; three lines keeps every card in a
     section the same shape without truncating a short one. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* -------------------------------------------------------------------------
 * Key facts
 * ------------------------------------------------------------------------- */
/* Label above value, in a row of columns rather than a bordered panel: the card
   is already a panel, and a second border inside it read as a nested box. The
   auto-fit cannot strand anything here because there are at most four facts and
   an unfilled one is dropped by the template, never rendered as a dash. */
.project-card__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-6) var(--space-10);
  width: 100%;
  margin: 0;
  padding: var(--space-8) 0 0;
  border-top: 1px solid var(--color-white-10);
}

.project-card__fact-label {
  font-family: var(--font-inter);
  font-size: var(--text-xs);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-normal);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-white-50);
  margin: 0;
}

.project-card__fact-value {
  font-family: var(--font-inter);
  font-size: var(--text-base);
  line-height: var(--lh-normal);
  color: var(--color-white);
  margin: var(--space-2) 0 0;
}

/* -------------------------------------------------------------------------
 * Topics
 * ------------------------------------------------------------------------- */
.project-card__topics {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.project-card__topic {
  padding: var(--space-1) var(--space-5);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-white-15);
  font-family: var(--font-inter);
  font-size: var(--text-sm);
  line-height: var(--lh-relaxed);
  color: var(--color-white-70);
}

/* The look comes from the shared .link-arrow component in btn.css. The card body
   is a flex column, so the auto margin pins the link to the bottom edge: two
   cards side by side on a wide screen line their links up even when one has an
   extra line of excerpt. */
.project-card__more {
  margin-top: auto;
}

/* ---------------------------------------------------------------------------
 * PENDING PROJECT — listed, but nothing to read yet
 * ---------------------------------------------------------------------------
 * No website and no description, so the card carries no link (see
 * parts/card-project.php). The hover state has to go with the link: a card that
 * lights up under the cursor reads as clickable, and this one is not.
 * --------------------------------------------------------------------------- */
.project-card--pending:hover,
.project-card--pending:focus-within {
  border-color: var(--color-white-10); /* the resting value — no change on hover */
  background: var(--color-white-04);
}

/* Sits where .project-card__more would, so a column of cards keeps its rhythm.
   Quieter than the status chip beside it — it reports an absence, and should
   not compete with the project name. */
.project-card__pending {
  margin-top: auto;
  padding: var(--space-2) var(--space-5);
  border: 1px dashed var(--color-white-20);
  border-radius: var(--radius-full);
  font-family: var(--font-inter);
  font-size: var(--text-xs);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-normal);
  color: var(--color-white-50);
}

/* The affordance is a <span>, and the stretched link covers it anyway, so the
   arrow nudge has to be driven from the article: hovering the photo should move
   it too, not only hovering the text column. */
.project-card:hover .link-arrow__icon,
.project-card:focus-within .link-arrow__icon {
  transform: translate(2px, -2px);
}

/* -------------------------------------------------------------------------
 * Compact variant — "Other projects" at the bottom of a project page
 * ------------------------------------------------------------------------- */
/* Same card, shorter: the template drops the excerpt, the facts and the topics,
   and the media narrows so three of them read as a see-also list rather than as
   a second listing page. */
.project-card--compact.project-card--media {
  grid-template-columns: 200px minmax(0, 1fr);
}

.project-card--compact .project-card__media {
  min-height: 120px;
}

.project-card--compact .project-card__body {
  gap: var(--space-6);
  padding: var(--space-8) var(--space-10);
}

.project-card--compact .project-card__logo {
  width: 96px;
  height: 52px;
}

/* =========================================================================
 * TIMELINE — the completed projects, at the bottom of the listing page
 * =========================================================================
 * Not cards. A finished project is track record, and fifteen of them as cards
 * is fifteen teasers asking to be chosen between; as a run of years it is one
 * thing to take in. Three columns per row: the year, the rail, the entry.
 *
 * The rail is drawn on the marker column rather than as a border on the list,
 * so it lines up with the dots by construction and cannot drift when the year
 * column changes width.
 * ========================================================================= */
.collab-timeline__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Everything on a row is centred on the row, rather than aligned to its top:
   a row is one line tall without a logo and 68px tall with one, and a dot
   pinned a fixed distance from the top sat beside the name in the first case
   and above it in the second. */
.collab-timeline__item {
  display: grid;
  grid-template-columns: 72px 20px minmax(0, 1fr);
  align-items: center;
  column-gap: var(--space-6);
}

.collab-timeline__year {
  font-family: var(--font-gilroy);
  font-weight: var(--fw-bold);
  font-size: var(--text-xl);
  line-height: 1;
  color: var(--color-text-accent);
  text-align: right;
}

/* The rail: a full-height line down the middle of this column, with the dot
   drawn over it. ::before is the line, ::after the dot. */
.collab-timeline__marker {
  position: relative;
  display: block;
  align-self: stretch;
  min-height: 100%;
}

.collab-timeline__marker::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  transform: translateX(-50%);
  background: var(--color-white-15);
}

/* The rail runs from the first dot to the last one, not from the top of the
   first row to the bottom of the last: a line hanging above the first project
   and below the last one reads as a list that has been cut off. */
.collab-timeline__item:first-child .collab-timeline__marker::before {
  top: 50%;
}

.collab-timeline__item:last-child .collab-timeline__marker::before {
  bottom: 50%;
}

/* One completed project: a rail between one dot and itself is a stray mark. */
.collab-timeline__item:first-child:last-child .collab-timeline__marker::before {
  content: none;
}

.collab-timeline__marker::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 9px;
  height: 9px;
  border-radius: var(--radius-circle);
  transform: translate(-50%, -50%);
  background: var(--color-white-30);
  transition: background var(--transition-base), box-shadow var(--transition-base);
}

/* The first project of a year is the one the eye should land on, so its dot is
   the accent one; the rest of that year stay quiet. */
.collab-timeline__item--year .collab-timeline__marker::after {
  background: var(--color-text-accent);
  box-shadow: 0 0 0 4px var(--color-glass-lime-12);
}

/* The row itself. Hovering anywhere on it lifts the whole entry, not just the
   name, so the row reads as one target even though only the name is a link. */
.collab-timeline__entry {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  min-width: 0;
  padding: var(--space-7) var(--space-8);
  border-radius: var(--radius-md);
  transition: background var(--transition-base);
}

.collab-timeline__entry:hover {
  background: var(--color-white-04);
}

/* White plate, like the cards: project logos are almost always dark-on-white
   and would vanish against the page. */
.collab-timeline__logo {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  height: 44px;
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-white);
}

.collab-timeline__logo img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

/* The reserved slot of a project that has no logo, on a timeline where others
   do (see the template): it holds the column open so every project name starts
   at the same x, and shows nothing. An empty white plate would read as a logo
   that failed to load. */
.collab-timeline__logo--empty {
  background: none;
}

.collab-timeline__text {
  min-width: 0;
}

.collab-timeline__name {
  font-family: var(--font-gilroy);
  font-weight: var(--fw-semibold);
  font-size: var(--text-lg);
  line-height: var(--lh-snug);
  color: var(--color-white);
  margin: 0;
}

.collab-timeline__name a {
  color: inherit;
  text-decoration: none;
}

.collab-timeline__entry:hover .collab-timeline__name a,
.collab-timeline__name a:focus-visible {
  text-decoration: underline;
}

.collab-timeline__meta {
  font-family: var(--font-inter);
  font-size: var(--text-sm);
  line-height: var(--lh-normal);
  color: var(--color-white-50);
  margin: var(--space-1) 0 0;
}

/* =========================================================================
 * SINGLE PROJECT — header (identity + key facts)
 * =========================================================================
 * One section holding both, so a project's identity and its numbers read as a
 * single unit. They were two sections and the page's --space-30 (60px) top
 * padding fell between them, which is what made the top of the page look like a
 * pile of unrelated boxes.
 * ========================================================================= */
.collab-identity {
  display: flex;
  align-items: flex-start;
  gap: var(--space-10);
  /* 20px to the facts panel below, not the 60px a section boundary gave it. */
  margin-bottom: var(--space-10);
}

/* Trimmed from 180×108: at that size the plate anchored the whole block and
   outweighed the chip + pills + link column beside it. */
.collab-identity__logo {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 150px;
  height: 90px;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--color-white);
}

.collab-identity__logo img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

/* Column of three rows: meta line, topic pills, link. An even gap between them
   replaces the per-element bottom margins those three used to carry, which was
   the other half of the clutter — three different rhythms in one small block. */
.collab-identity__text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-6);
  min-width: 0;
}

/* Status chip and full name on one baseline, wrapping only when they have to. */
.collab-identity__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-5);
}

.collab-identity__status {
  flex: 0 0 auto;
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-white-20);
  background: var(--color-white-04);
  font-family: var(--font-inter);
  font-size: var(--text-xs);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-normal);
  color: var(--color-white-70);
  margin: 0;
}

.collab-identity__status--ongoing {
  border-color: var(--color-glass-lime-35);
  background: var(--color-glass-lime-12);
  color: var(--color-text-accent);
}

/* The family the project belongs to, on the status chip's baseline. Plain text
   with a rule in front of it rather than a second chip: two pills side by side
   read as two states of the same thing, and only one of them is a state. */
.collab-identity__category {
  flex: 0 0 auto;
  padding-left: var(--space-5);
  border-left: 1px solid var(--color-white-20);
  font-family: var(--font-inter);
  font-size: var(--text-sm);
  line-height: var(--lh-normal);
  color: var(--color-white-70);
  margin: 0;
}

.collab-identity__fullname {
  font-family: var(--font-gilroy);
  font-weight: var(--fw-semibold);
  font-size: var(--text-xl);
  line-height: var(--lh-snug);
  color: var(--color-white);
  margin: 0;
}

.collab-topics {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.collab-topics__item {
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-white-15);
  background: var(--color-white-04);
  font-family: var(--font-inter);
  font-size: var(--text-sm);
  line-height: var(--lh-normal);
  color: var(--color-white-70);
}

/* .link-arrow (btn.css) carries the look. The pill this used to be is gone: a
   44px green button directly under a row of chips and pills outweighed the whole
   block it belonged to. Nothing to add here — the column gap above already
   spaces it — so the class stays only as a hook for future page-level tweaks. */

/* =========================================================================
 * SINGLE PROJECT — key facts
 * =========================================================================
 * ONE bordered panel with a fixed 3-column grid inside, not one bordered box
 * per fact in an auto-fill grid. The old version fitted five boxes across a
 * 1200px container, so the common six-fact project ended on a single stranded
 * box below the row. A fixed column count cannot strand anything: six facts are
 * two rows of three, and a short last row is plain whitespace inside the panel
 * rather than a lone floating card.
 * ========================================================================= */
.collab-facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-8) var(--space-10);
  margin: 0;
  padding: var(--space-8) var(--space-10);
  border: 1px solid var(--color-white-10);
  border-radius: var(--radius-lg);
  background: var(--color-white-04);
}

.collab-facts__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.collab-facts__label {
  font-family: var(--font-inter);
  font-size: var(--text-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-white-50);
  margin: 0;
}

.collab-facts__value {
  font-family: var(--font-gilroy);
  font-weight: var(--fw-semibold);
  font-size: var(--text-lg);
  line-height: var(--lh-snug);
  color: var(--color-white);
  margin: 0;
}

/* =========================================================================
 * SINGLE PROJECT — description, objectives, role
 * ========================================================================= */
.collab-body__content {
  font-family: var(--font-inter);
  font-size: var(--text-lg);
  line-height: var(--lh-relaxed);
  color: var(--color-white-70);
}

.collab-body__content > * + * {
  margin-top: var(--space-6);
}

.collab-body__content h2,
.collab-body__content h3 {
  font-family: var(--font-gilroy);
  font-weight: var(--fw-semibold);
  font-size: var(--text-xl);
  line-height: var(--lh-snug);
  color: var(--color-white);
  margin-top: var(--space-10);
}

.collab-body__content a {
  color: var(--color-text-accent);
}

.collab-body__content img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
}

.collab-objectives__list {
  display: grid;
  gap: var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* A leading rule rather than a bullet glyph: it survives a two-line objective
   without the text wrapping back under the marker. */
.collab-objectives__item {
  padding-left: var(--space-6);
  border-left: 2px solid var(--color-glass-lime-35);
  font-family: var(--font-inter);
  font-size: var(--text-lg);
  line-height: var(--lh-relaxed);
  color: var(--color-white-70);
}

.collab-role__body {
  font-family: var(--font-inter);
  font-size: var(--text-lg);
  line-height: var(--lh-relaxed);
  color: var(--color-white-70);
}

.collab-role__body p {
  margin: 0 0 var(--space-6);
}

.collab-role__body p:last-child {
  margin-bottom: 0;
}

/* =========================================================================
 * SINGLE PROJECT — consortium
 * ========================================================================= */
.collab-consortium__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-5);
  list-style: none;
  margin: 0;
  padding: 0;
}

.collab-consortium__item {
  display: flex;
}

/* Renders as <a> when the partner has a website, <div> otherwise — hence the
   display/inherit resets so both tags look identical. */
.collab-partner {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-5) var(--space-6);
  border: 1px solid var(--color-white-10);
  border-radius: var(--radius-lg);
  background: var(--color-white-04);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--transition-base), background var(--transition-base), transform var(--transition-base);
}

.collab-partner--link:hover,
.collab-partner--link:focus-visible {
  border-color: var(--color-glass-lime-35);
  background: var(--color-white-10);
  transform: translateY(-2px);
}

.collab-partner__name {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-gilroy);
  font-weight: var(--fw-semibold);
  font-size: var(--text-lg);
  line-height: var(--lh-snug);
  color: var(--color-white);
}

.collab-partner__arrow {
  display: inline-flex;
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  color: var(--color-text-accent);
  transition: transform var(--transition-fast);
}

.collab-partner__arrow svg {
  width: 100%;
  height: 100%;
}

.collab-partner--link:hover .collab-partner__arrow {
  transform: translate(2px, -2px);
}

.collab-partner__country {
  font-family: var(--font-inter);
  font-size: var(--text-sm);
  line-height: var(--lh-normal);
  color: var(--color-text-accent);
}

/* =========================================================================
 * RESPONSIVE
 * ========================================================================= */
@media (max-width: 1024px) {
  .collab-intro,
  .collab-group,
  .collab-timeline,
  .collab-pubs,
  .collab-header,
  .collab-body,
  .collab-objectives,
  .collab-role,
  .collab-consortium,
  .collab-more {
    padding-inline: var(--padding-x-md);
  }

  /* Three columns get tight enough here that a value like "2.8 million euros
     from Interreg NWE" breaks mid-phrase. Two columns keep each value on one or
     two clean lines; six facts then read as three rows. */
  .collab-facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* The photo gives up width before the facts do: at 280px the four fact
     columns of a fully filled project started wrapping two per row. */
  .project-card--media {
    grid-template-columns: 220px minmax(0, 1fr);
  }

  .project-card--compact.project-card--media {
    grid-template-columns: 170px minmax(0, 1fr);
  }
}

@media (max-width: 768px) {
  .collab-intro,
  .collab-group,
  .collab-timeline,
  .collab-pubs,
  .collab-header,
  .collab-body,
  .collab-objectives,
  .collab-role,
  .collab-consortium,
  .collab-more {
    padding-inline: var(--padding-x-sm);
    padding-top: var(--space-16);
  }

  .collab-intro__title,
  .collab-group__title,
  .collab-timeline__title,
  .collab-pubs__title,
  .collab-body__title,
  .collab-objectives__title,
  .collab-role__title,
  .collab-consortium__title,
  .collab-more__title {
    font-size: var(--text-2xl);
  }

  /* Title and button stack, button back under the heading — same behaviour as
     .partners-pubs__header. */
  .collab-pubs__header,
  .collab-more__header {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-8);
  }

  /* Logo above the text, and the facts panel drops to a single column: three
     label/value pairs across a phone is unreadable. */
  .collab-identity {
    flex-direction: column;
    gap: var(--space-8);
  }

  .collab-facts {
    grid-template-columns: 1fr;
    padding: var(--space-8);
  }

  .collab-consortium__grid {
    grid-template-columns: 1fr;
  }

  /* The card stacks: photo on top as a banner, everything else under it. A
     170px-wide photo beside text on a phone is a thumbnail, not a visual. Both
     selectors are needed, and in this order: the compact one has to match the
     two-class specificity of the rule it is undoing. */
  .project-card--media,
  .project-card--compact.project-card--media {
    grid-template-columns: minmax(0, 1fr);
  }

  .project-card__media {
    min-height: 0;
    aspect-ratio: 16 / 9;
  }

  /* The logo plate is the first thing to cost more than it gives at this width:
     it takes a third of the row from a title that now has to wrap. It stays,
     smaller, because it is how a reader recognises a programme they know. */
  .project-card__logo {
    width: 96px;
    height: 52px;
  }

  .project-card__body {
    padding: var(--space-8);
  }

  /* Two fact columns at most on a phone: "Funding programme" over a value like
     "Interreg North-West Europe" needs the width of half a screen, not a third
     of one, and auto-fit would otherwise still fit three at 140px. */
  .project-card__facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* The timeline keeps its rail — it is what makes the section read as a run of
     years rather than a list — but the year column shrinks to what four digits
     need, and the logo goes, because at this width the plate takes half the row
     from the project name. */
  .collab-timeline__item {
    grid-template-columns: 44px 16px minmax(0, 1fr);
    column-gap: var(--space-4);
  }

  .collab-timeline__year {
    font-size: var(--text-lg);
  }

  .collab-timeline__logo {
    display: none;
  }

  .collab-timeline__entry {
    padding-inline: var(--space-5);
  }
}

/* =============================================================================
 * NETWORK MAP — the Networks section, as a map
 * -----------------------------------------------------------------------------
 * Hiphen at Avignon, one marker per network, an arc out to each, and the
 * network's own card opening underneath when a marker is clicked. Markup and
 * projection: parts/collab-network-map.php. Behaviour: assets/js/collab-network-map.js.
 *
 * The map IS the section: there is no stack of cards beside or below it, the
 * cards are what opens. What sits under the map permanently is the rail, a row
 * of names, because seven markers a few pixels apart cannot be told apart by
 * pointing at them. Hovering a name lights its marker and its arc, which is what
 * makes the French cluster usable at all.
 * ========================================================================== */

.netmap {
  margin-bottom: var(--space-10);
}

.netmap__canvas {
  position: relative;
  border: 1px solid var(--color-white-10);
  border-radius: 16px;
  background: var(--color-white-04);
  overflow: hidden;
}

.netmap__svg {
  display: block;
  width: 100%;
  height: auto;
}

/* ---- Land ---------------------------------------------------------------- */

.netmap__land {
  fill: var(--color-secondary);
  stroke: var(--color-white-10);
  stroke-width: 0.5;
  vector-effect: non-scaling-stroke; /* Hairline at any rendered width. */
}

/* ---- Arcs ---------------------------------------------------------------- */

.netmap__link {
  fill: none;
  stroke: var(--color-text-accent);
  stroke-width: 1.2;
  stroke-opacity: 0.45;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  transition: stroke-opacity var(--transition-base), stroke-width var(--transition-base);
}

/* Dim every arc but the one being pointed at, so a single relationship can be
   read out of the bundle. Driven by a class on the wrapper rather than :hover on
   the arc, because the arc is not the thing you point at. */
.netmap.is-focused .netmap__link { stroke-opacity: 0.12; }

.netmap .netmap__link.is-active {
  stroke-opacity: 1;
  stroke-width: 2;
}

/* ---- Markers ------------------------------------------------------------- */

.netmap__node { cursor: pointer; }

.netmap__halo { fill: transparent; } /* Hit area only: a 4px dot is not clickable. */

.netmap__dot {
  fill: var(--color-white);
  stroke: var(--color-dark);
  stroke-width: 1.5;
  transition: r var(--transition-base), fill var(--transition-base);
}

.netmap__node:hover .netmap__dot,
.netmap__node:focus-visible .netmap__dot,
.netmap__node.is-active .netmap__dot {
  fill: var(--color-text-accent);
  r: 6.5;
}

.netmap__node[aria-expanded="true"] .netmap__dot {
  fill: var(--color-text-accent);
  r: 7;
  stroke: var(--color-white);
}

.netmap__node:focus-visible .netmap__halo {
  fill: none;
  stroke: var(--color-text-accent);
  stroke-width: 2;
}

/* Leader from a displaced marker back to the network's true position, and the
   tick that marks it. See "Spreading the pile-up" in the partial: without these
   the offset would read as the location, which it is not. */
.netmap__leader {
  stroke: var(--color-white-30);
  stroke-width: 1;
  stroke-dasharray: 2 2;
  vector-effect: non-scaling-stroke;
}

.netmap__node:hover .netmap__leader,
.netmap__node:focus-visible .netmap__leader,
.netmap__node.is-active .netmap__leader {
  stroke: var(--color-text-accent);
  stroke-dasharray: none;
}

/* ---- Hiphen -------------------------------------------------------------- */

.netmap__home-dot {
  fill: var(--color-text-accent);
  stroke: var(--color-dark);
  stroke-width: 1.5;
}

.netmap__home-pulse {
  fill: var(--color-text-accent);
  opacity: 0.25;
  transform-box: fill-box;
  transform-origin: center;
  animation: netmap-pulse 2.8s ease-out infinite;
}

@keyframes netmap-pulse {
  0%   { transform: scale(0.6); opacity: 0.35; }
  70%  { transform: scale(1.7); opacity: 0; }
  100% { transform: scale(1.7); opacity: 0; }
}

/* ---- Rail ---------------------------------------------------------------- */

/* Hidden on wide screens: the names are on the map. It comes back below the
   breakpoint where SVG text has shrunk past readable, and it is still the
   no-JavaScript list of links either way. */
.netmap__rail {
  display: none;
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.netmap__rail-item { margin: 0; }

.netmap__rail-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 7px var(--space-4) 7px var(--space-3);
  border: 1px solid var(--color-white-10);
  border-radius: 999px;
  background: var(--color-white-04);
  text-decoration: none;
  transition: background-color var(--transition-base), border-color var(--transition-base);
}

.netmap__rail-link:hover,
.netmap__rail-link:focus-visible,
.netmap__rail-link.is-active {
  background: var(--color-white-10);
  border-color: var(--color-white-30);
}

.netmap__rail-link[aria-expanded="true"] {
  background: var(--color-text-accent);
  border-color: var(--color-text-accent);
}

.netmap__rail-marker {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-white);
  transition: background-color var(--transition-base);
}

.netmap__rail-link[aria-expanded="true"] .netmap__rail-marker {
  background: var(--color-text-dark);
}

.netmap__rail-name {
  font-family: var(--font-inter);
  font-size: var(--text-sm);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--color-white);
}

.netmap__rail-link[aria-expanded="true"] .netmap__rail-name {
  color: var(--color-text-dark);
}

/* ---- The card that opens under the map ----------------------------------- */

.netmap__panels { margin-top: var(--space-5); }

.netmap__hint {
  margin: 0;
  padding-top: var(--space-2);
  font-family: var(--font-inter);
  font-size: var(--text-sm);
  line-height: var(--lh-normal);
  color: var(--color-white-70);
}

.netmap__panel {
  position: relative;
  /* The card brings its own frame, so the panel only has to announce that it
     belongs to the marker above it. */
  animation: netmap-panel-in 260ms ease-out both;
}

@keyframes netmap-panel-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

.netmap__panel .project-card { margin: 0; }

/* ---- Narrow -------------------------------------------------------------- */

@media (max-width: 900px) {
  /* Below this the map is narrow enough that its 13-unit labels render under
     about 10px, so they come off and the rail carries the names instead. */
  .netmap__label { display: none; }
  .netmap__rail { display: flex; }
}

@media (max-width: 700px) {
  /* The rail becomes one scrolling row: seven pills wrapped over four lines eat
     the screen before the map has been looked at. */
  .netmap__rail {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: 2px;
  }

  .netmap__rail::-webkit-scrollbar { display: none; }

  .netmap__rail-item { flex: 0 0 auto; }

}

@media (prefers-reduced-motion: reduce) {
  .netmap__home-pulse { animation: none; }
  .netmap__panel { animation: none; }
}

/* The close bar sits above the card rather than over its corner, which is where
   the card puts the network's logo. */
/* `display: flex` beats the browser's own `[hidden] { display: none }`, so the
   bar has to opt back out explicitly. Without this the Close button shows with
   no panel open, and shows on every card when JavaScript is off. */
.netmap__panel-bar[hidden] { display: none; }

.netmap__panel-bar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--space-3);
}

.netmap__panel-close {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px var(--space-3) 5px 10px;
  border: 1px solid var(--color-white-15);
  border-radius: 999px;
  background: transparent;
  color: var(--color-white-70);
  font-family: var(--font-inter);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--transition-base), border-color var(--transition-base), color var(--transition-base);
}

.netmap__panel-close span { font-size: 16px; line-height: 1; }

.netmap__panel-close:hover,
.netmap__panel-close:focus-visible {
  background: var(--color-white-04);
  border-color: var(--color-white-40);
  color: var(--color-white);
}

/* ---- Names on the map ----------------------------------------------------
 * Drawn as SVG text so they live in the same coordinate space as the markers
 * and stay glued to them at any width. Which side of its marker each one sits
 * on is solved server-side; see "Labelling" in the partial.
 *
 * paint-order puts a dark stroke behind the fill, so a name stays readable
 * where it crosses a coastline. It is cheaper and sharper than a backing rect,
 * and it needs no extra element per label.
 * ------------------------------------------------------------------------- */

.netmap__label {
  font-family: var(--font-inter);
  font-size: 13px; /* user units: the SVG scales, so this scales with the map. */
  font-weight: var(--fw-semibold);
  fill: var(--color-white);
  paint-order: stroke fill;
  stroke: var(--color-dark);
  stroke-width: 3.5;
  stroke-linejoin: round;
  pointer-events: none; /* The marker's halo is the hit area, not the name. */
  transition: fill var(--transition-base);
}

.netmap__label--home {
  fill: var(--color-text-accent);
}

.netmap__node:hover .netmap__label,
.netmap__node:focus-visible .netmap__label,
.netmap__node.is-active .netmap__label,
.netmap__node[aria-expanded="true"] .netmap__label {
  fill: var(--color-text-accent);
}

/* Invisible slab over each name, so a name is as clickable as its dot. It has
   to opt back into pointer events, the text above it having opted out. */
.netmap__label-hit {
  fill: transparent;
  pointer-events: all;
}

/* Hiphen's marker is a filled dot with a ring, and it is drawn UNDER the
   network markers: Montpellier and Castanet-Tolosan sit within a few units of
   Avignon at world scale, so on top it would simply hide them. The ring is what
   still reads as "Hiphen is here" with two white dots inside it. */
.netmap__home-dot {
  fill: var(--color-text-accent);
  stroke: var(--color-dark);
  stroke-width: 1.5;
}

.netmap__leader--home {
  stroke: var(--color-text-accent);
  stroke-dasharray: none;
  stroke-opacity: 0.7;
}
