/* =========================================================================
   BEFELLO OPPORTUNITY GRID + CARD
   -------------------------------------------------------------------------
   Loaded only where a grid is rendered. Every value comes from the design
   system tokens declared on :root in style.css, so this file introduces no
   colour, radius, spacing step or type size of its own.

   Scope: every selector begins with .befello-opportunity-grid or
   .befello-opportunity-card. Nothing here can reach the header, the
   navigation, the footer, the single Opportunity page, Essay pages or any
   plugin.

   The chips reuse .befello-chip from style.css rather than restating it —
   one chip component across the platform, one place to change it.

   NO HOVER. There is no card lift, no shadow on hover, no image zoom, no
   background change, no animated border and no hover-only affordance. Every
   control looks interactive at rest. Focus and active states do the work.
   ========================================================================= */

.befello-opportunity-grid {
	/* ---------------------------------------------------------------------
	   Three knobs, one layout rule.

	   --befello-grid-columns  the MOST cards a row may hold. Set per grid by
	                           the shortcode's columns attribute, as an inline
	                           custom property on the wrapper.
	   --befello-grid-min      the narrowest a card may become before it stops
	                           reading well. Measured rather than guessed:
	                           below roughly 210px the two chips stop fitting
	                           side by side, the title runs to four lines and
	                           Apply drops under 110px. 14rem keeps clear of
	                           that.
	   --befello-grid-gap      the gutter, shared by the layout maths below.

	   Any of the three can be overridden on an instance — a full-width
	   section that wants denser cards can lower --befello-grid-min without
	   new CSS.
	   --------------------------------------------------------------------- */
	--befello-grid-columns: 3;
	--befello-grid-min: 14rem;
	--befello-grid-gap: var(--bf-space-5);

	display: grid;
	gap: var(--befello-grid-gap);
	margin: 0;

	/*
	 * The column count is a ceiling, never a floor, and this is the one rule
	 * that enforces it at every width.
	 *
	 * The inner calc() is the track width a row of exactly N columns would
	 * produce in THIS container. While the container is wide enough for that
	 * to exceed --befello-grid-min it wins, and auto-fill lays out exactly N
	 * columns. Once the container narrows to the point where N columns would
	 * push a card below the minimum, the minimum wins instead and auto-fill
	 * simply fits fewer — 6 becomes 5, then 4, 3, 2 and finally 1 — with no
	 * media query and no rule per number.
	 *
	 * min(100%, …) is the guard that keeps a card from overflowing a
	 * container narrower than the minimum itself, such as a slim sidebar.
	 *
	 * auto-fill, not auto-fit, so a short final row keeps the card width of
	 * the rows above it rather than stretching two cards across the width of
	 * four.
	 */
	grid-template-columns: repeat(
		auto-fill,
		minmax(
			min(
				100%,
				max(
					var(--befello-grid-min),
					calc(
						(100% - (var(--befello-grid-columns) - 1) * var(--befello-grid-gap))
						/ var(--befello-grid-columns)
					)
				)
			),
			1fr
		)
	);
}

/* --- Mobile: one card per row, always ------------------------------------
   The formula above is a container question — how many readable cards fit?
   This is a device question, and they are not the same question, which is
   why this one is a media query.

   Left to the formula, a second column appears the moment the container
   reaches 468px (two 224px cards plus the gap). A large phone, or a page
   template with little horizontal padding, reaches that on its own — so the
   readable-width floor was never a guarantee of a single column on a phone.
   It reads as a compressed desktop grid rather than a mobile list.

   640px is the phone/tablet line: every current phone in portrait sits below
   it, and a portrait tablet at 768px is above it and keeps the layout it
   already had. The grid keeps its `columns` value for when the viewport
   grows; this only overrides how those columns are laid out here, so nothing
   about the desktop or tablet layout changes. */
@media (max-width: 640px) {
	.befello-opportunity-grid {
		grid-template-columns: minmax(0, 1fr);
	}
}

.befello-opportunity-grid__empty {
	margin: 0;
	color: var(--bf-ink-muted);
	font-size: var(--bf-text-base);
	line-height: var(--bf-leading-body);
}

/* --- The card ------------------------------------------------------------
   Not a link, and never wrapped in one. A card-sized anchor swallows the
   controls inside it and gives a screen reader one enormous link whose name
   is the whole card.

   The card is a CONTAINER. Because `columns` is configurable, the viewport
   can no longer tell the card how much room it has: a 4-column card at
   1440px (295px) is narrower than a 1-column card on a phone (327px). Media
   queries would style those two identically wrong. Container queries let the
   card answer the only question that matters — how wide am I? — so one card
   reads correctly at every column count without a variant per number. */

.befello-opportunity-card {
	container: befello-card / inline-size;
	display: flex;
	flex-direction: column;
	min-width: 0;
	background: var(--bf-surface);
	border: 1px solid var(--bf-border);
	border-radius: var(--bf-radius-lg);   /* 5px card */
	box-shadow: none;
	overflow: hidden;   /* keeps the image's corners inside the card's */
}

/* --- Proportions ---------------------------------------------------------
   Three bands, expressed once as tokens rather than as three copies of the
   layout. An element cannot query itself, so these sit on the card's
   children — media and body — and everything inside inherits from there.

   Band 1 (base, under 300px) is the compact end: 4, 5 and 6 columns on
   desktop, and the narrowest phones. Band 2 is the balanced treatment that
   3 columns and ordinary phones get. Band 3 is 2 columns, where there is
   room to breathe and cramped type would look like a mistake.

   Type never drops below 12px caption / 16px title, which is the floor the
   system sets in §2 — the card gets compact by tightening leading and
   spacing, not by shrinking text until it stops being readable. */

.befello-opportunity-card > * {
	--card-pad: var(--bf-space-4);          /* 16 */
	--card-gap: 10px;
	--card-title: 1rem;                     /* 16 */
	--card-title-leading: 1.3;
	--card-chip-h: 26px;
	--card-chip-pad: 8px;
	--card-ui-text: var(--bf-text-xs);      /* 12 · Caption */
	--card-meta-h: 26px;
	--card-icon: 14px;
	--card-ratio: 16 / 10;
	--card-title-pad: 5px;
}

@container befello-card (min-width: 300px) {
	.befello-opportunity-card > * {
		--card-pad: var(--bf-space-5);      /* 20 */
		--card-gap: var(--bf-space-3);      /* 12 */
		--card-title: 1.125rem;             /* 18 */
		--card-title-leading: 1.35;
		--card-chip-h: 30px;
		--card-chip-pad: 10px;
		--card-ui-text: var(--bf-text-sm);  /* 14 · UI */
		--card-meta-h: 30px;
		--card-icon: 16px;
		--card-ratio: 16 / 9;
		--card-title-pad: 4px;
	}
}

@container befello-card (min-width: 440px) {
	.befello-opportunity-card > * {
		--card-pad: var(--bf-space-6);      /* 24 */
		--card-gap: var(--bf-space-4);      /* 16 */
		--card-title: var(--bf-text-h4);    /* 20 · Card heading */
		--card-title-leading: var(--bf-leading-h4);
		--card-chip-h: 32px;
		--card-title-pad: 3px;
	}
}

/* --- Media --------------------------------------------------------------
   Landscape, not square. A fixed ratio means a row of cards keeps one
   baseline whatever dimensions were uploaded, and object-fit crops rather
   than letterboxes. Absolutely positioned so the image cannot drive the
   card's height.

   The narrow band uses 16:10 rather than 16:9. At 4+ columns the card is
   tall and thin, and a shallower crop is what stops it reading as a column
   of stacked boxes — the image gets proportionally more of the card back. */

.befello-opportunity-card__media {
	position: relative;
	aspect-ratio: var(--card-ratio);
	background: var(--bf-accent-soft);
	line-height: 0;
}

.befello-opportunity-card__media .befello-opportunity-card__image {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	max-width: none;
	margin: 0;
	object-fit: cover;
}

/* The scrim. Only drawn when there are labels to carry — an image with no
   chips gets no gradient, because there would be nothing for it to do.

   --befello-text at 55%, fading to nothing over the bottom half. It exists
   to stop a pale photo eating the chips' edges, not to darken the picture,
   so it is deliberately weak: §14 rules out decoration for its own sake and
   the chips already carry their own solid backgrounds. */
.befello-opportunity-card__media:has( .befello-opportunity-card__chips )::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	height: 55%;
	background: linear-gradient(
		to top,
		rgba(37, 42, 39, 0.55),
		rgba(37, 42, 39, 0.22) 45%,
		rgba(37, 42, 39, 0)
	);
	pointer-events: none;
}

.befello-opportunity-card__body {
	display: flex;
	flex-direction: column;
	gap: var(--card-gap);
	/* auto top margin on the meta pins the meta AND the actions to the
	   bottom, so Apply lines up across cards whose titles run to different
	   lengths. */
	flex: 1 1 auto;
	padding: var(--card-pad);
	min-width: 0;
}

/* --- Chips ---------------------------------------------------------------
   Type and funding sit on the lower edge of the image rather than above the
   title. Two things come of that: the card loses a whole row plus its gap
   from the body — the single biggest reason the 4-column card ran tall —
   and the labels read as belonging to the opportunity in the picture
   instead of floating in the text block.

   z-index 1 puts them over the scrim, which is the media's ::after and so
   the last child in paint order. Without it the gradient would sit on top
   of the very labels it exists to support. */

.befello-opportunity-card__chips {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--bf-space-2);
}

.befello-opportunity-card__media .befello-opportunity-card__chips {
	position: absolute;
	z-index: 1;
	inset-inline: var(--card-pad);
	bottom: var(--bf-space-3);
	line-height: 1;
}

/* The card's own chip metrics, tuned per band. Colour, radius and weight
   still come from .befello-chip in style.css — this only resizes the box,
   so a chip on a card and a chip on the single page stay the same
   component. */
.befello-opportunity-card .befello-chip {
	height: var(--card-chip-h);
	padding: 0 var(--card-chip-pad);
	font-size: var(--card-ui-text);
}

/* --- Title --------------------------------------------------------------
   H3: the card sits under a section heading wherever it is used, so H3 keeps
   the document outline sane on a homepage full of grids. */

.befello-opportunity-card__title {
	margin: 0;
	font-size: var(--card-title);
	font-weight: 700;
	line-height: var(--card-title-leading);
	letter-spacing: -0.01em;
	overflow-wrap: break-word;
	text-wrap: pretty;
}

/* Deliberately not line-clamped. Clamping the compact title to three lines
   was tried and removed: it reclaimed about 5% of card height and cost the
   end of the opportunity's name, which is the one thing on the card a reader
   is scanning for. It also needs overflow:hidden on the heading, which clips
   the focus ring and the hit-area padding of the link inside it. Tightened
   leading plus the labels moving onto the image already did the real work.
   Every card in a row is the same height regardless, because the grid
   stretches them and the meta carries margin-top:auto. */

.befello-opportunity-card__title-link {
	color: var(--bf-ink);
	text-decoration: none;
	/* Vertical padding on an inline box grows the hit area without moving
	   anything: a title is the most-tapped control on the card after Apply
	   and has to clear 24px (WCAG 2.5.8). The padding is per band because
	   the type is: at 16px the box is ~17px tall and needs 5px a side, at
	   20px it needs 3px. Padding rather than display:block, because a block
	   anchor would make the empty space beside a short title clickable —
	   which the card must not do. */
	padding-block: var(--card-title-pad);
}

/* No hover state, and this is the rule that enforces it. The theme
   underlines links on hover globally; the compound selector is specific
   enough to beat that here without touching links anywhere else on the
   site. The title is the card's headline — it should look the same whether
   or not a pointer happens to be resting on it. Keyboard users still get
   the ring below. */
.befello-opportunity-card .befello-opportunity-card__title-link:hover,
.befello-opportunity-card .befello-opportunity-card__title-link:focus {
	color: var(--bf-ink);
	text-decoration: none;
}

/* --- Meta ---------------------------------------------------------------
   Deadline and country are one metadata area, not two blocks. They were
   pushed to opposite edges by space-between, which read as two unrelated
   things that happened to share a row; left-aligned with a single gap they
   read as one group, and the country still drops below the deadline when
   the card is too narrow for both. */

/* margin-top: auto pushes the meta AND the actions below it to the bottom of
   the card, so Apply lines up across a row of cards with titles of different
   lengths. A card with no meta has nothing carrying that auto margin — hence
   the :has() rule below, which hands it to the actions instead. Without
   :has() support the only cost is the old slight misalignment. */
.befello-opportunity-card__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--bf-space-2) var(--bf-space-3);
	margin-top: auto;
	padding-top: var(--bf-space-1);
	font-size: var(--card-ui-text);
	font-weight: 500;
	line-height: 1;
	color: var(--bf-ink-muted);
}

/* §8's deadline-status, to the letter: white, one hairline, 4px, a small
   semantic dot. Fixed height so it and the country sit on one baseline. */
.befello-opportunity-card__deadline {
	display: inline-flex;
	align-items: center;
	gap: var(--bf-space-2);
	height: var(--card-meta-h);
	padding: 0 var(--card-chip-pad);
	background: var(--bf-surface);
	border: 1px solid var(--bf-border);
	border-radius: var(--bf-radius-md);   /* 4px chip */
	white-space: nowrap;
}

/* Inherently circular, so exempt from the 5px rule. */
.befello-opportunity-card__dot {
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--bf-success);
	flex: 0 0 auto;
}

/* Urgent and closed are red; the words already say which. */
.befello-opportunity-card__deadline--urgent,
.befello-opportunity-card__deadline--closed {
	color: var(--bf-urgent);
	font-weight: 600;
}

.befello-opportunity-card__deadline--urgent .befello-opportunity-card__dot,
.befello-opportunity-card__deadline--closed .befello-opportunity-card__dot {
	background: var(--bf-danger);
}

.befello-opportunity-card__country {
	display: inline-flex;
	align-items: center;
	gap: var(--bf-space-2);
	min-width: 0;
	height: var(--card-meta-h);
	overflow-wrap: break-word;
}

.befello-opportunity-card__country .befello-icon {
	width: var(--card-icon);
	height: var(--card-icon);
	margin: 0;
	flex: 0 0 auto;
	color: var(--bf-ink-muted);
}

/* --- Actions ------------------------------------------------------------
   A neutral utility beside a filled primary. The copy button was yellow —
   the same yellow as the category tag — which made a secondary action look
   like a third label. White with one hairline borrows the deadline chip's
   treatment instead, so the card's two quiet surfaces match each other and
   neither competes with Apply. §3: yellow supports the interface, it does
   not compete with primary actions. */

.befello-opportunity-card__actions {
	display: flex;
	align-items: stretch;
	gap: var(--bf-space-2);
	margin-top: var(--bf-space-1);
}

.befello-opportunity-card__body:not(:has(.befello-opportunity-card__meta)) .befello-opportunity-card__actions {
	margin-top: auto;
}

/* [hidden] is a UA rule at the same specificity as this class, so an author
   `display` would win and flash the button before the script reveals it. */
.befello-opportunity-card__copy[hidden] {
	display: none;
}

.befello-opportunity-card__copy {
	/* One cell, two states stacked in it. The icon and the "Copied" label
	   share a single grid area, so the button is always as wide as the wider
	   of the two and swapping between them moves nothing. Toggling display
	   instead — the obvious version — grew the button from 44px to 93px on
	   click and shoved the Apply button 49px to the right, under a finger
	   that is usually on its way to Apply. */
	display: grid;
	grid-template-areas: "state";
	place-items: center;
	flex: 0 0 auto;
	min-width: 44px;
	min-height: 44px;
	padding: 0 var(--bf-space-3);
	background: var(--bf-surface);
	color: var(--bf-ink-muted);
	border: 1px solid var(--bf-border);
	border-radius: var(--bf-radius-lg);   /* 5px, matching the button beside it */
	font-family: var(--befello-font);
	font-size: var(--bf-text-xs);
	font-weight: 600;
	line-height: 1;
	cursor: pointer;
}

.befello-opportunity-card__copy .befello-icon,
.befello-opportunity-card__copy-done {
	grid-area: state;
}

.befello-opportunity-card__copy .befello-icon {
	width: 18px;
	height: 18px;
	margin: 0;
	flex: 0 0 auto;
}

/* --- No hover, anywhere in this component -------------------------------
   Deliberate, and it has to be written down rather than merely omitted.
   GeneratePress styles every bare <button> with a dark fill on hover, and
   the copy control is a real <button> — leaving hover unstated is what
   turned it charcoal. These selectors restate the resting state at a
   specificity that beats the theme's, for the copy button and for Apply,
   without touching buttons anywhere else on the site.

   :focus is restated for the same reason and is the less obvious half of it.
   Clicking a <button> leaves it focused, so GeneratePress's dark fill kept
   applying after the pointer moved away — the copy button went green,
   confirmed, then turned charcoal and stayed there until something else was
   clicked. Resetting :focus here removes the fill; the keyboard ring is an
   outline further down and is unaffected.

   Pointer hover is not an interaction the card needs: nothing here is
   hidden until you find it, and hover has no keyboard or touch equivalent
   (§17). Feedback lives in :active and :focus-visible instead, which every
   input method can reach. */

.befello-opportunity-card .befello-opportunity-card__copy:hover,
.befello-opportunity-card .befello-opportunity-card__copy:focus {
	background: var(--bf-surface);
	color: var(--bf-ink-muted);
	border-color: var(--bf-border);
}

/* Apply leaks the same way, one property over: the theme underlines links on
   :focus, which outranks .befello-btn's own text-decoration and drew a line
   through the label of a filled blue button. Keyboard-only in practice — a
   mouse click navigates away before it shows. The ring is the focus signal
   here, not a decoration on the text. */
.befello-opportunity-card a.befello-btn--primary:hover,
.befello-opportunity-card a.befello-btn--primary:focus {
	background-color: var(--befello-blue-dark);
	border-color: var(--befello-blue-dark);
	color: var(--bf-accent-ink);
	text-decoration: none;
}

/* Pressed: the softest step of the brand blue. Enough to register as a
   press on touch, where there is no cursor to confirm the tap landed. */
.befello-opportunity-card .befello-opportunity-card__copy:active {
	background: var(--bf-accent-soft);
	border-color: var(--bf-accent-soft-border);
	color: var(--bf-accent);
}

/* Confirmation is a state change, not a tooltip and not a hover reveal.
   visibility rather than display: hidden content still occupies its grid
   cell, which is what keeps the button's width constant. It is out of the
   accessibility tree either way, and the button's aria-label carries the
   confirmation for screen readers.

   The word matters: §3 says never communicate status by colour alone, so
   the green fill is paired with the label rather than replacing it. */
.befello-opportunity-card__copy-done {
	visibility: hidden;
}

.befello-opportunity-card .befello-opportunity-card__copy.is-copied,
.befello-opportunity-card .befello-opportunity-card__copy.is-copied:hover,
.befello-opportunity-card .befello-opportunity-card__copy.is-copied:focus {
	background: var(--bf-success-soft);
	border-color: var(--bf-success-soft);
	color: var(--bf-success-text);
}

.befello-opportunity-card__copy.is-copied .befello-opportunity-card__copy-done {
	visibility: visible;
}

.befello-opportunity-card__copy.is-copied .befello-icon {
	visibility: hidden;
}

/* Reuses .befello-btn--primary from style.css for colour, radius and type. */
.befello-opportunity-card__apply {
	flex: 1 1 auto;
}

/* --- Focus --------------------------------------------------------------
   style.css scopes its focus ring to .befello-opportunity, which is the
   single-page article — a card in a grid sits outside it, so the ring is
   restated here for the card's own controls. With hover gone, this and
   :active are the component's entire feedback vocabulary, so it stays
   unmissable. */

.befello-opportunity-card a:focus-visible,
.befello-opportunity-card button:focus-visible {
	outline: 2px solid var(--befello-blue);
	outline-offset: 2px;
	border-radius: var(--bf-radius-md);
}

/* The filled primary needs a ring that is not the colour of its own fill. */
.befello-opportunity-card .befello-btn--primary:focus-visible {
	outline-color: var(--bf-ink);
}

@media (prefers-reduced-motion: reduce) {
	.befello-opportunity-card * {
		transition-duration: 0.01ms !important;
		animation-duration: 0.01ms !important;
	}
}