:root {
    --face: #c5c5c5;
    --face-light: #e6e6e1;
    --field: #e5e4e1;
    --ink: #101010;
    --white: #fff;
    --muted: #565656;
    --blue: #0000c8;
    --blue-bright: #1010ff;
    --red: #d90022;
    --amber: #ffc962;
    --green: #2f9229;
    --desktop: #5b7070;
    /* Brand blue of the Praxis mark — lighter and cooler than the UI --blue-bright,
       which belongs to the Windows chrome and shouldn't be conflated with it. */
    --praxis-blue: #1f9cf0;
    --line: #111;
    /* Positive offsets (top/left) come first in each pair: the first shadow paints
       on top, so those edges run full length and the bottom/right ones mitre into
       them, as Windows drew it. Inner 1px pair stays ahead of the outer 2px pair. */
    --raise: inset 1px 1px #fff, inset -1px -1px #161616, inset 2px 2px #ddd, inset -2px -2px #888;
    --sink: inset 1px 1px #7b7b7b, inset -1px -1px #fff, inset 2px 2px #202020, inset -2px -2px #ddd;
    --sink-shallow: inset 1px 1px #7b7b7b, inset -1px -1px #fff;
    /* Blue controls need their own bevel ramp; the neutral highlight washes out. */
    --raise-blue: inset 1px 1px #a8a8ff, inset -1px -1px #00002e, inset 2px 2px #5a5aff, inset -2px -2px #000090;
    --sink-blue: inset 1px 1px #000090, inset -1px -1px #a8a8ff, inset 2px 2px #00002e, inset -2px -2px #5a5aff;
    --label-bevel: inset 1px 1px #eee, inset -1px -1px #888;
    --mono: monospace;
}

* { box-sizing: border-box; }
html { min-height: 100%; background: var(--desktop); }
body {
    min-height: 100vh;
    margin: 0;
    color: var(--ink);
    background: var(--desktop);
    font: 14px/1.35 var(--mono);
    font-variant-ligatures: none;
    -webkit-font-smoothing: none;
    /* Mobile WebKit may inflate text in wide blocks without reporting it in the
       computed font-size. Anything that measures rendered height against a line
       height read from CSS — the popup fit in place() — would be reading a
       different scale than the one on screen. */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

a { color: var(--blue-bright); text-decoration: none; }
/* Every :hover rule in this file is gated on a hovering pointer. A touch browser
   parks a synthetic cursor wherever you last tapped, so whatever ends up under
   that point inherits hover and keeps it until the next tap somewhere else —
   which is how dismissing a menu leaves the element revealed beneath it looking
   moused-over. Each gate wraps its rule in place rather than being hoisted into
   one block at the end, so cascade order against the :active and [aria-selected]
   rules interleaved here is exactly what it was. */
@media (hover: hover) {
    a:hover { text-decoration: underline; }
}
button, input, textarea, select { font: inherit; }
button { cursor: pointer; }
.option-btn,
.option-btn a,
.option-details a,
.custom-select,
.custom-options,
.title-option,
.remove-filter-btn,
.pagination-tab,
.page-arrow,
.form-submit { user-select: none; -webkit-user-select: none; }

/* Short, stepped page-change signal. */
.screen-refresh {
    position: absolute;
    z-index: 9999;
    inset: 0;
    background: var(--face);
    pointer-events: none;
    animation: screen-refresh-down 150ms steps(4, end) 100ms forwards;
}
.screen-refresh span { display: none; }
@keyframes screen-refresh-down {
    from { background: var(--face); clip-path: inset(0 0 0 0); }
    to { background: var(--face); clip-path: inset(100% 0 0 0); }
}
@media (prefers-reduced-motion: reduce) {
    .screen-refresh { display: none; }
}

.content-wrapper {
    position: relative;
    width: min(1120px, calc(100% - 28px));
    min-height: 100vh;
    margin: 0 auto;
    padding: 16px;
    background: var(--face);
    border-left: 1px solid var(--line);
    border-right: 1px solid var(--line);
    box-shadow: 6px 0 0 rgba(0,0,0,.13), -6px 0 0 rgba(0,0,0,.13);
}

.field { background: var(--face); box-shadow: var(--raise); }

/* Shared page-title bar: identity and page name on the left, page actions right. */
.page-header {
    width: 100%;
    min-height: 31px;
    margin: 0 0 8px;
    padding: 4px 5px;
    display: flex;
    align-items: center;
    gap: 0;
    color: #fff;
    background: linear-gradient(90deg, var(--blue-bright), var(--praxis-blue));
    font-size: 13px;
    font-weight: 600;
}
.page-title { display: flex; align-items: center; min-width: 0; margin: 0; font: inherit; }
.page-title::before { content: "PRAXIS NOVA /"; align-self: stretch; display: flex; align-items: center; padding: 0 3px; margin-right: 6px; font-size: 14px; font-weight: 600; letter-spacing: .05em; }
.title { color: inherit; font-size: 14px; font-weight: 600; letter-spacing: .05em; }
.title-options { display: flex; gap: 6px; margin-left: auto; }
.title-option { display: inline-flex; align-items: center; min-height: 23px; padding: 4px 10px; color: var(--ink); background: var(--face); box-shadow: var(--raise); font-size: 12px; outline: 1px solid var(--ink); outline-offset: -1px; }
@media (hover: hover) {
    .title-option:hover { text-decoration: underline; }
}
/* Pressed labels move without changing the box dimensions. .is-pressing is the
   touch stand-in for :active; see setupNavigationState. */
.title-option.is-pressing, .title-option.is-navigating { box-shadow: var(--sink); padding: 5px 9px 3px 11px; }
/* :active is gated wherever .is-pressing also applies. Touch fires :active the
   instant a finger lands, while .is-pressing deliberately waits out the scroll
   test — leaving both live would split the press into two beats, the pressed
   look arriving on contact and the hover look catching up 110ms later. Gated,
   .is-pressing alone drives the whole appearance on touch and it arrives as one.
   Controls with no .is-pressing rule keep their instant :active. */
@media (hover: hover) {
    .title-option:active { box-shadow: var(--sink); padding: 5px 9px 3px 11px; }
}

.home-wordmark { display: flex; align-items: center; width: fit-content; margin: 0 auto 15px; color: var(--ink); font-size: clamp(30px, 5vw, 50px); font-weight: 600; letter-spacing: -.12em; line-height: 1; }
.home-wordmark span { padding: 4px 4px; }
/* Canon Praxis mark; sans-serif because the letterforms are the logo. */
.home-wordmark span:first-child { padding: 2px; color: var(--praxis-blue); background: var(--white); border: 2px solid var(--praxis-blue); border-radius: 5px; border-top-right-radius: 0; border-bottom-right-radius: 0; font-family: system-ui, -apple-system, "Segoe UI", Arial, sans-serif; font-weight: normal; letter-spacing: .01em; text-transform: uppercase; }
/* Letter-spacing trails the last letter too, eating into the right padding. */
.home-wordmark span + span { padding-right: calc(4px + .12em); color: #fff; background: var(--ink); text-shadow: 0 0 4px #fff; }

/* Home */
.options-container { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; padding: 12px; min-height: 285px; }
.opportunity-group, .action-group, .option-btn-container { display: flex; flex-direction: column; align-items: stretch; }
.action-group { gap: 12px; }
.option-btn-container { min-width: 0; }
span.option-btn { display: flex; background: var(--face); box-shadow: var(--raise); font-family: var(--mono); font-size: clamp(23px, 3.3vw, 37px); line-height: .98; }
.option-btn a { flex: 1; padding: 12px; }
@media (hover: hover) {
    .option-btn a:hover { text-decoration: underline; text-decoration-thickness: 3px; outline: 3px solid var(--blue-bright); outline-offset: -8px;}
}
/* The press and navigating rules further down only recolour this outline, so it
   has to be drawn here too — on touch there is no hover to have drawn it. */
.option-btn a.is-touched, .option-btn a.is-navigating { text-decoration: underline; outline: 3px solid var(--blue-bright); outline-offset: -8px; }
/* The underline's natural thickness follows the font, which shrinks with the
   clamped size on a phone until it no longer reads as the same weight as the 3px
   outline beside it. Keyed to the pointer rather than to .is-touched so the
   thickness holds through the navigating state too, instead of thinning at the
   moment of the tap. Desktop keeps deriving it from the font as it does today. */
@media (pointer: coarse) {
    .option-btn a.is-touched, .option-btn a.is-navigating { text-decoration-thickness: 2px; outline-width: 2px; }
}
.option-btn .info { flex: 0 0 46px; display: grid; place-items: center; padding: 0; color: var(--blue-bright); background: none; border: 0; border-left: 1px solid var(--ink); font: inherit; line-height: 1; user-select: none; -webkit-user-select: none; }
@media (hover: hover) {
    .option-btn .info:hover { text-decoration: none; outline: 3px solid var(--blue-bright); outline-offset: -8px; }
}
.option-btn .info.is-touched { text-decoration: none; outline: 3px solid var(--blue-bright); outline-offset: -8px; }
/* Has to follow the shorthand above, not sit with the other coarse overrides
   further up: same specificity, so whichever comes last wins, and the shorthand
   would put the width back to 3px. */
@media (pointer: coarse) {
    .option-btn .info.is-touched { outline-width: 2px; }
}
.option-btn .info:focus-visible { outline: 3px solid var(--blue-bright); outline-offset: -6px; }

.option-details { display: none; padding: 8px; background: var(--field); }
.option-details.open { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.option-details-row { display: contents; color: var(--ink); font-size: 16px; }
.option-details a { text-decoration: underline; text-decoration-thickness: 2px; }
@media (hover: hover) {
    .option-details a:hover { outline: 2px solid var(--blue-bright); outline-offset: 2px;}
}
.option-details a.is-touched, .option-details a.is-navigating { outline: 2px solid var(--blue-bright); outline-offset: 2px; }

.option-btn a.is-pressing,
.option-details a.is-pressing,
.option-btn a.is-navigating,
.option-details a.is-navigating,
.option-btn .info.is-pressing { color: var(--red); outline-color: var(--red); }
@media (hover: hover) {
    .option-btn a:active,
    .option-details a:active,
    .option-btn .info:active { color: var(--red); outline-color: var(--red); }
}

/* Opportunity workspace */
.columns { display: grid; grid-template-columns: minmax(0, 1fr) 240px; align-items: stretch; box-shadow: none; }
.repo-column { display: flex; flex-direction: column; min-width: 0; }

/* Pagination toolbar: a raised "Page N" tab overlapping the top-left of the
   recessed results box, with a horizontal Prev/Next arrow pair alongside it. */
.repo-toolbar { position: relative; z-index: 2; display: flex; align-items: flex-end; gap: 6px; margin-bottom: 4px; }
.pagination-tab { display: inline-flex; align-items: flex-end; min-height: 24px; padding: 0 4px; color: var(--ink); margin-left: 0px; background: var(--field); box-shadow: var(--sink-shallow); font-size: 14px; font-weight: bold; letter-spacing: .05em;}
.pagination-arrows { display: flex; gap: 4px; }
.page-arrow { display: inline-flex; align-items: center; justify-content: center; min-width: 24px; min-height: 24px; padding: 4px; color: var(--ink); background: var(--face); border: 0; box-shadow: var(--raise); font-size: 14px; line-height: 1; }
/* No .is-touched counterpart on purpose: like the detail tokens, the hover wash
   is a cursor preview, and on touch it would only linger after the press that
   already answered it. The .is-pressing bevel below is the whole affordance. */
@media (hover: hover) {
    .page-arrow:hover:not(:disabled) { background: var(--field); }
}
.page-arrow.is-pressing:not(:disabled) { box-shadow: var(--sink); padding: 3px 5px 1px 7px; }
@media (hover: hover) {
    .page-arrow:active:not(:disabled) { box-shadow: var(--sink); padding: 3px 5px 1px 7px; }
}
/* At 24px these sit under the ~44px a fingertip actually covers, so WebKit's
   touch adjustment quietly redirects near-misses to them. That fires the click
   but not the press styling: the click is retargeted, while the pointerdown
   .is-pressing rides on reports the real element under the finger, the toolbar.
   Widening the hit area to cover the near-miss makes both agree. Transparent, so
   the bevel keeps its 24px look. The insets are bounded by the neighbours — 2px
   is half the 4px gap between the two arrows, and 4px is the toolbar's bottom
   margin, past which this would start intercepting taps meant for the cards. */
@media (pointer: coarse) {
    .page-arrow { position: relative; }
    .page-arrow:not(:disabled)::after { content: ""; position: absolute; inset: -8px -2px -4px; }
}
.page-arrow:focus-visible { outline: 1px dotted var(--ink); outline-offset: 2px; }
.page-arrow:disabled { color: var(--muted); background: var(--face); box-shadow: var(--sink); cursor: default;}
.repo { position: relative; min-width: 0; min-height: 520px; padding: 3px; background: var(--face); box-shadow: var(--sink); }
/* Keep the inner bevel above the scrolling cards. An inset shadow painted on
   .repobox itself sits behind its children, so the cards conceal it as they
   scroll. This non-scrolling overlay preserves the double recessed frame. */
.repo::after { content: ""; position: absolute; z-index: 1; inset: 3px; box-shadow: var(--sink); pointer-events: none; }
/* isolation: isolate walls off every z-index used inside (card links z-index:2,
   detail-token folds z-index:3, popups z-index:40/41) into their own stacking
   context. Without it those values are compared directly against .repo::after's
   z-index:1 sink overlay above, and every one of them is higher — so folds and
   popups would poke out past the recessed frame instead of being covered by it
   like the rest of the card content. Isolated, the whole repobox paints as one
   auto-stacked unit that the overlay's z-index:1 simply sits in front of. */
.repobox { isolation: isolate; height: 100%; max-height: 70vh; min-height: 70vh; overflow-x: hidden; overflow-y: scroll; scrollbar-gutter: stable; background: var(--field); padding: 4px; }
.repo-status-message { margin: auto; padding: 20px; color: var(--muted); text-align: center; }
/* Deliberately NOT a stacking context: popups (z-index 40) must be free to
   overflow this card and paint above the cards below it. */
.opportunity-card { display: grid; grid-template-columns: minmax(0, 1.5fr) 1fr; column-gap: 14px; padding: 12px; border-bottom: 1px solid #999; background: var(--field); min-height:135px; }
.opportunity-main-column, .opportunity-details-column { display: flex; flex-direction: column; gap: 8px; min-width: 0; align-self: start; }
.opportunity-main-column {  gap: 16px; margin-bottom: 8px; align-items: flex-start; }
.opportunity-details-column { align-items: stretch; }
/* Connects the deadline token up to the title. Anchoring the line to the title
   rather than the deadline lets it span the title's full height whatever the
   wrap, so a short last line can't leave it hanging in the gap; the -16px
   bottom is the main column's gap, landing it exactly on the deadline's top
   border. position: relative without a z-index keeps this a plain containing
   block, so the line still paints under the link (z-index: 1) below.
   --connector-x is nudged by drawTitleBackground when the default column would
   graze the end of a wrapped line; see connectorOffset there. */
.opportunity-title { position: relative; }
.opportunity-title::after { content: ""; position: absolute; left: var(--connector-x, 75px); top: 4px; bottom: -16px; width: 1px; background: var(--desktop); }
/* z-index raises the title above the deadline connector line (see
   .opportunity-title::after), which would otherwise paint over it by DOM order. The
   isolation here already makes the link an atomic stacking context — it exists
   to contain .link-line-backgrounds' z-index: -1 — so the whole title subtree
   lifts as one unit, and 1 still sits well below the popups' 40. */
.opportunity-card a { font-size: 18px; font-weight: 600;  position: relative; isolation: isolate; z-index: 1; display: inline-block; min-width: 0; max-width: 100%; padding: 8px; background: var(--desktop); color: #fff; text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 2px; vertical-align: top; }
.link-text { display: inline; }
.link-line-backgrounds { position: absolute; z-index: -1; inset: 0; pointer-events: none; }
.link-line-background { position: absolute; display: block; background: var(--desktop); pointer-events: auto; }
.opportunity-card a.line-background-ready { background: transparent; pointer-events: none; }
.opportunity-card a.line-background-ready .link-text { pointer-events: auto; }
.opportunity-card a .link-text svg { fill: none; stroke: currentColor; }
.opportunity-card a:not(.line-background-ready) .link-text, .opportunity-card .link-text { outline: 0px solid var(--ink); outline-offset: 7px; }
/* .is-touched is the deliberate-tap half of hover: same look, but driven by a
   real press on this element rather than by a synthetic cursor left behind. */
.opportunity-card a.is-touched .link-text { text-decoration-thickness: 2px; outline: 2px solid var(--blue-bright); outline-offset: 6px; }
.opportunity-card a.is-touched:not(.line-background-ready) { color: var(--blue-bright); background: var(--field); }
.opportunity-card a.is-touched.line-background-ready { color: var(--blue-bright); text-decoration-color: var(--blue-bright); }
.opportunity-card a.is-touched.line-background-ready .link-line-background { background: var(--field); }
@media (hover: hover) {
    .opportunity-card a:not(.line-background-ready):hover .link-text,
    .opportunity-card a.line-background-ready:has(.link-text:hover, .link-line-background:hover) .link-text { text-decoration-thickness: 2px; outline: 2px solid var(--blue-bright); outline-offset: 6px; }
    .opportunity-card a:not(.line-background-ready):hover { color: var(--blue-bright); background: var(--field); }
    .opportunity-card a.line-background-ready:has(.link-text:hover, .link-line-background:hover) { color: var(--blue-bright); text-decoration-color: var(--blue-bright); }
    .opportunity-card a.line-background-ready:has(.link-text:hover, .link-line-background:hover) .link-line-background { background: var(--field); }
}

.top-right-grid { border: 1px solid var(--ink); padding: 8px; min-width: 0; max-width: 100%; }
.details-summary-row { display: flex; justify-content: flex-start; align-items: flex-start; align-self: stretch; flex-wrap: wrap; gap: 4px; }
.grid-cell, .date-cell { min-width: 0; padding: 4px 5px; text-align: center; font-size: 14px; text-transform: uppercase; letter-spacing: .04em; }
.grid-cell { background: none; border: 1px solid var(--ink); flex: 0 1 auto; display: flex; align-items: center; justify-content: center; max-width: 100%; overflow: hidden; overflow-wrap: anywhere; line-height: 1.15; box-shadow: none; }
.fee-cell { min-height: 26px; padding: 3px 5px; color: var(--ink); background: var(--amber); box-shadow: none; }
.type-cell { border-color: var(--blue-bright) !important; color: var(--white); background: var(--blue-bright)}
.type-cell::selection, .title::selection { background: #fff; color: var(--ink); }
/* Shared details affordance: a dotted underline on the token label (hard-edged
   square dots via a sharp-stop gradient, not soft radial circles, so they stay
   pixel-crisp; round-tiled so the row always ends on a whole dot rather than
   text-decoration's arbitrary clipped one) plus a "+" badge tucked flush into
   the token's own top-right corner (its border coincides with the cell's
   border, not floating past it). The badge sits outside document flow, so the
   cell reserves matching right-side padding to keep the centered label clear
   of its footprint. Both reveal the details on hover/focus, flipping
   up/down to avoid clipping. */
.detail-token.has-details { --detail-tail-length: 8px; --detail-tail-width: 12px; --detail-fold-size: 13px; --detail-fold-center-offset: 7.5px; position: relative; overflow: visible; padding-right: 16px; cursor: help; user-select: none; }
.detail-token.has-details:focus-visible { outline: 1px dotted var(--ink); outline-offset: 2px; }
/* Pressing the token dims it briefly — the only motion in an otherwise static
   affordance, so a click reads as registered before the popup pins. A `filter`
   on the token itself would dim its whole subtree, including the corner fold
   (z-index 3) and any already-open popup (z-index 40), bleeding their colors
   into the darkened layer. This overlay sits above the plain label (z-index
   auto) but below both, so it dims only the token's own face. Excluded when
   the popup itself is the thing under the pointer: the popup is a DOM
   descendant of the token, so :active on it bubbles to the token too, and
   without the :has() guard a press inside the popup would dim the token. */
/* Touch has no cursor to preview a token with, so the dim would only ever flash
   as the popup itself opens — feedback for something already answered. */
@media (hover: hover) {
    .detail-token.has-details:active:not(:has(.details-popup:hover))::before { content: ""; position: absolute; inset: 0; z-index: 1; background: rgba(0, 0, 0, .15); pointer-events: none; }
}
.detail-token-label { position: relative; display: inline-block; }
/* The underline is a hover-only cue: the corner fold already advertises that
   details exist at rest. currentColor so it survives the pinned inversion. */
.detail-token-label::after { content: ""; position: absolute; left: 0; right: -4px; bottom: -1px; height: 2px; display: none; background-image: linear-gradient(to right, currentColor 0 2px, transparent 2px 5px); background-size: 5px 2px; background-position: left center; background-repeat: round no-repeat; }
/* Focus styling keys off :focus-visible, never :focus-within. Clicking
   anywhere inside the token — including inside its popup — hands the token
   browser focus, since it is the nearest focusable ancestor. Reacting to that
   would make a click in the popup restyle the token as if hovered.
   :focus-visible matches keyboard focus only, so the keyboard affordance
   survives while mouse-driven focus stays invisible. */
.detail-token.has-details:focus-visible .detail-token-label::after { display: block; }
/* Sits flush against the INSIDE of the cell's border (top/right: 0 lands
   exactly on the padding edge) rather than extending out over it, so the
   cell's own border stays visible unbroken. Only the inner two sides get
   their own border; the outer top/right edges rely on that real border
   instead of a second coincident line, so there's nothing to drift at
   different zoom levels. */
.detail-token-fold { position: absolute; top: 0; right: 0; width: var(--detail-fold-size, 13px); height: var(--detail-fold-size, 13px); display: grid; place-items: center; color: var(--ink); background: inherit; border-bottom: 1px solid var(--ink); border-left: 1px solid var(--ink); font-size: 12px; font-weight: 700; line-height: 1; z-index: 3; }
/* Hover inks the fold in; the eligibility token additionally clears its own
   field wash so the filled corner reads against plain white. */
.detail-token.has-details:focus-visible .detail-token-fold { color: #fff; background: var(--ink); }
.award-cell.detail-token.has-details:focus-visible,
.eligibility-cell.detail-token.has-details:focus-visible { background: #fff; }
/* Pinned is the full inversion, and must win over the hover/focus rules. */
.detail-token.popup-pinned,
.award-cell.detail-token.has-details.popup-pinned:hover,
.award-cell.detail-token.has-details.popup-pinned:focus-visible,
.eligibility-cell.detail-token.has-details.popup-pinned:hover,
.eligibility-cell.detail-token.has-details.popup-pinned:focus-visible { color: #fff; background: var(--ink); }
/* The fold stays black-filled whenever pinned, hover/focus notwithstanding —
   no white-border variant. */
.detail-token.popup-pinned .detail-token-fold { color: #fff; background: var(--ink); }
.details-popup { position: absolute; right: -1px; bottom: 100%; transform: translateX(calc(var(--popup-inline-offset, 0px) + var(--popup-shift-x, 0px))); z-index: 40; display: none; width: max-content; min-width: 150px; max-width: min(340px, 75vw); padding-bottom: var(--detail-tail-length); color: var(--ink); font-size: 14px; font-weight: 400; line-height: 1.4; text-align: left; text-transform: none; letter-spacing: 0; cursor: default; user-select: text; }
/* An actively hovered popup always wins over a merely pinned one that happens
   to sit later in paint order. */
.detail-token.has-details:focus-visible:not(.popup-pinned) .details-popup { z-index: 41; }
.fee-cell .details-popup { --dialogue-background: var(--amber); --popup-inline-offset: 50%; right: var(--detail-fold-center-offset); }
.fee-cell .details-popup-content { text-align: center; font-size: 18px; }
.details-popup-content { position: relative; z-index: 1; display: block; max-height: 220px; overflow-y: auto; padding: 6px 10px; overflow-wrap: anywhere; background: var(--dialogue-background, #fff); border: 1px solid var(--ink); box-shadow: 2px 2px 0px var(--ink); cursor: default; }
/* Applied by fitContent when the text fits within the growth allowance: the cap
   comes off and the scroller is dismantled outright. A class swapping overflow,
   not an inline max-height — iOS WebKit was observed accepting an inline cap
   raise on this composited scroller and then never relaying it out (heights read
   back still clamped to 220px). Removing the scroller is a change it can't skip,
   and with no cap and overflow visible there is nothing left to mis-measure. */
.details-popup-content.is-grown { max-height: none; overflow-y: visible; }
/* No separate track gutter — just the thumb, floating on the popup's own
   background. Its edges can sit flush against the popup's real border on any
   side depending on scroll position (the scrollbar occupies the padding box,
   right up against it) — border-side rules can't target only the sides that
   happen to be touching, so the thumb goes borderless entirely rather than
   doubling into a 2px line wherever it does touch. */
.details-popup-content::-webkit-scrollbar-track { background: transparent; }
.details-popup-content::-webkit-scrollbar-thumb { border: none; }
/* Only the glyphs themselves take the text caret; the padding and the box
   around them stay a plain pointer. */
.details-popup-text { cursor: text; }
.details-popup-paragraph { display: block; }
.details-popup-paragraph + .details-popup-paragraph { margin-top: .85em; }
.details-popup-paragraph br { content: ""; display: block; margin-top: .65em; }
.details-popup-paragraph strong { font-weight: 700; }
.details-popup-paragraph em { font-style: italic; }
.details-popup-tail { position: absolute; left: var(--tail-left, 0px); bottom: 0; z-index: 0; width: var(--detail-tail-width); height: var(--detail-tail-length); background: var(--ink); box-shadow: none; filter: none; clip-path: polygon(0 0, 100% 0, 50% 100%); }
.details-popup-tail::after { content: ""; position: absolute; top: 0; left: 1px; width: calc(var(--detail-tail-width) - 2px); height: calc(var(--detail-tail-length) - 1px); background: var(--dialogue-background, #fff); clip-path: polygon(0 0, 100% 0, 50% 100%); }
/* Opening downward, the tail hangs from the token's own bottom border rather
   than from the fold's — the fold's lower edge sits mid-cell, and a tail
   starting there would cross the token's remaining height. */
.detail-token.popup-down .details-popup { top: calc(100% + 1px); bottom: auto; padding-top: var(--detail-tail-length); padding-bottom: 0; }
.detail-token.popup-down .details-popup-tail { top: 0; bottom: auto; clip-path: polygon(50% 0, 100% 100%, 0 100%); }
.detail-token.popup-down .details-popup-tail::after { top: 1px; clip-path: polygon(50% 0, 100% 100%, 0 100%); }
/* Mobile draws no persistent scrollbar, so a popup with more text below reads as
   finished. This marks it. It hangs off .details-popup rather than the scrolling
   box, which would carry it out of view on the first drag, and sits inside the
   tail's padding so it lands in the content's own bottom-right corner either way
   up. It clears itself at the end of the scroll — see markScrollable.

   --details-marker-inset holds the marker clear of the column the overlay
   scrollbar runs down, so the bar travels the full height of the box instead of
   stopping short against it. The text gutter is measured from the marker's far
   edge, not the border, so the two stay spaced however the inset is tuned.

   The gutter is reserved on every popup, not only the ones that scroll. Making
   it conditional looks tidier and is a trap: the gutter changes where the text
   wraps, so a box that gains one on becoming scrollable is taller for it, which
   is the same measurement that decided whether it scrolls. That loop is what
   produced boxes that were given extra height and left scrollable anyway.
   Constant, the fit is always measured against the final wrap, and the marker
   is left as pure paint with no say in layout. The centred fee popup gets the
   same reserve mirrored on its left so its text stays centred. */
@media (pointer: coarse) {
    .details-popup { --details-marker-inset: 9px; --details-marker-width: 16px; --details-marker-gap: 0px; --details-gutter: calc(var(--details-marker-inset) + var(--details-marker-width) + var(--details-marker-gap)); }
    .details-popup-content { padding-right: var(--details-gutter); }
    .fee-cell .details-popup-content { padding-left: var(--details-gutter); }
    .details-popup.has-more::after { content: "▼"; position: absolute; right: var(--details-marker-inset); bottom: calc(var(--detail-tail-length) + 3px); z-index: 2; width: var(--details-marker-width); color: var(--ink); background: var(--dialogue-background, #fff); font-size: 10px; line-height: 1; text-align: center; pointer-events: none; }
    .detail-token.popup-down .details-popup.has-more::after { bottom: 3px; }
}
.detail-token.has-details:focus-visible .details-popup,
.detail-token.popup-pinned .details-popup { display: block; }
.detail-token.popup-dismissed .details-popup { display: none !important; }
/* Every hover affordance lives here rather than beside its focus twin above,
   because touch browsers synthesize hover from incidental contact — a finger
   passing over a token mid-scroll would open and restyle it. Gated on a real
   hovering pointer, touch is left with the deliberate tap (which only fires on
   press and release together) as the single way in, and desktop is unchanged.
   These trail the pinned rules, but pinned wins on specificity, not order. */
@media (hover: hover) {
    .detail-token.has-details:hover .detail-token-label::after { display: block; }
    .detail-token.has-details:hover .detail-token-fold { color: #fff; background: var(--ink); }
    .award-cell.detail-token.has-details:hover,
    .eligibility-cell.detail-token.has-details:hover { background: #fff; }
    .detail-token.has-details:hover .details-popup { z-index: 41; display: block; }
}
/* Award info sits to the left of eligibility in a shared right-aligned row;
   the 10px inset is carried by the row so both tokens keep the same gap from
   the pane edge the eligibility token used to hold on its own. */
.detail-token-row { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: flex-start; gap: 4px; margin-right: 10px; }
.award-cell, .eligibility-cell { min-height: 20px; padding: 3px 5px; color: var(--ink); background: var(--field); box-shadow: none; }
.date-cell { border: 1px solid var(--ink); padding: 4px; width: fit-content; color: var(--ink); text-align: left; box-shadow: none; background: transparent; text-transform: none; letter-spacing: 0; font-size: 14px; }
.date-cell strong { color: var(--ink); font-weight: 600; }

/* Filters */
.filters { align-self: start; padding: 8px; }
.filters-title { padding: 2px 4px; font-weight: 600; font-size: 12px; letter-spacing: .05em; }
.filters-divider { border-top: 1px solid #777; padding-top: 8px; }
.applied-filters { display: flex; flex-wrap: wrap; align-items: flex-start; align-content: flex-start; gap: 4px; min-height: 38px; overflow: visible; border: 1px solid var(--ink); padding: 6px; background: var(--field); }
.filter-item { font-size: 14px; color: var(--muted); }
.active-filter { display: flex; gap: 5px; align-items: center; padding: 2px 4px; color: #fff; background: var(--blue-bright); }
.exclude-filter { background: var(--red); }
.rolling-filter { background: var(--green); }
.remove-filter-btn { position: relative; flex: 0 0 15px; width: 15px; height: 15px; padding: 0; border: 0; color: inherit; background: transparent; font-size: 0; }
.remove-filter-btn::before { content: ""; position: absolute; top: 4px; left: 4px; width: 1px; height: 1px; background: currentColor; box-shadow: 6px 0 currentColor, 1px 1px currentColor, 5px 1px currentColor, 2px 2px currentColor, 4px 2px currentColor, 3px 3px currentColor, 2px 4px currentColor, 4px 4px currentColor, 1px 5px currentColor, 5px 5px currentColor, 0 6px currentColor, 6px 6px currentColor; }
.remove-filter-btn.is-touched { color: var(--ink); background: #fff; }
@media (hover: hover) {
    .remove-filter-btn:hover { color: var(--ink); background: #fff; }
}
.remove-filter-btn:focus-visible { outline: 1px dotted currentColor; outline-offset: 1px; }
.filter-selectors { display: flex; flex-direction: column; gap: 9px; padding: 8px 4px; }
.checkbox-filters { display: flex; flex-wrap: wrap; gap: 9px 14px; }
.custom-select { position: relative; width: 100%; }
.custom-selected { position: relative; width: 100%; min-height: 23px; padding: 4px 29px 4px 7px; border: 0; color: var(--ink); background: #fff; box-shadow: var(--sink); cursor: pointer; font-size: 14px; line-height: 15px; text-align: left; }
.custom-selected::after { content: "▼"; position: absolute; top: 2px; right: 2px; bottom: 2px; display: grid; place-items: center; width: 19px; color: var(--ink); background: var(--face); box-shadow: var(--raise); font-size: 9px; line-height: 1; }
.custom-selected:active { background: var(--face); }
.custom-selected:active::after { box-shadow: var(--sink); padding-top: 1px; }
.custom-selected:focus-visible { outline: none; }
.custom-selected:focus-visible::before,
.custom-select.open .custom-selected::before { content: ""; position: absolute; top: 4px; right: 23px; bottom: 4px; left: 4px; border: 1px dotted var(--ink); pointer-events: none; }
.custom-select.open .custom-selected { color: #fff; background: #fff; outline: none; }
.custom-select.open .custom-selected:active { background: var(--face); }
.custom-options { position: absolute; z-index: 20; top: 100%; right: 0; left: 0; display: none; margin: 1px 0 0; padding: 2px; list-style: none; background: #fff; border: 1px solid var(--ink); box-shadow: 2px 2px 0 rgba(0,0,0,.5); }
.custom-options[aria-hidden="false"] { display: block; }
.custom-options li { position: relative; padding: 4px 6px; cursor: pointer; font-size: 14px; }
/* aria-selected is the menu's own keyboard/tap highlight and stays ungated;
   only the pointer half is conditional. */
.custom-options li[aria-selected="true"], .custom-options li.is-touched { color: var(--field); background: var(--blue-bright); outline: 0px solid var(--ink); }
@media (hover: hover) {
    .custom-options li:hover { color: var(--field); background: var(--blue-bright); outline: 0px solid var(--ink); }
}
.custom-options li.is-pressing .opt-label { display: inline-block; transform: translate(1px, 1px); }
@media (hover: hover) {
    .custom-options li:active .opt-label { display: inline-block; transform: translate(1px, 1px); }
}
.custom-options li.clear-filter-option { color: var(--red); }
.custom-options li.clear-filter-option[aria-selected="true"], .custom-options li.clear-filter-option.is-touched { color: var(--field); background: var(--red); }
@media (hover: hover) {
    .custom-options li.clear-filter-option:active { color: var(--field); background: var(--red); }
}
@media (hover: hover) {
    .custom-options li.clear-filter-option:hover { color: var(--field); background: var(--red); }
}
.visually-hidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(1px, 1px, 1px, 1px); white-space: nowrap; }
.checkbox-label { display: flex; align-items: center; gap: 7px; cursor: pointer; font-size: 14px; user-select: none; -webkit-user-select: none; }
.checkbox-input {
    position: relative;
    width: 13px;
    height: 13px;
    margin: 0;
    flex: 0 0 13px;
    appearance: none;
    -webkit-appearance: none;
    /* iOS Safari keeps its own rounded corners on an appearance: none checkbox. */
    border: 0;
    border-radius: 0;
    background: #fff;
    box-shadow: var(--sink);
    cursor: pointer;
}
.checkbox-input:checked::after {
    content: "";
    position: absolute;
    top: 1px;
    left: 4px;
    width: 3px;
    height: 7px;
    border: solid var(--ink);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}
.checkbox-input:active,
.checkbox-label:active .checkbox-input { background: var(--face); }
.checkbox-input:focus-visible { outline: 1px dotted var(--ink); outline-offset: 2px; }

/* Feedback */
.feedback { padding: 3px; background: var(--face); box-shadow: var(--raise); }
.feedback-form { display: flex; flex-direction: column; align-items: flex-start; padding: 20px; background: var(--field); box-shadow: var(--sink); }
.feedback-intro { width: 100%; margin: 0 0 17px; padding-bottom: 11px; border-bottom: 1px solid var(--ink); font-size: 14px; font-weight: 600; }
.feedback-form .form-row { width: min(100%, 660px); display: flex; flex-direction: column; gap: 6px; margin: 8px 0; }
.feedback-form label { font-size: 12px; font-weight: 600; }
.form-input, .form-textarea { width: 100%; padding: 7px; color: var(--ink); background: #fff; border: 0; border-radius: 0; box-shadow: var(--sink); outline: 0; }
.form-textarea { min-height: 160px; resize: vertical; }
.form-input:focus, .form-textarea:focus { outline: 1px dotted var(--ink); outline-offset: -4px; }
.form-row.name { max-width: 360px !important; }
.form-submit { align-self: flex-start; padding: 7px 12px; color: var(--ink); background: var(--field); outline: 1px solid var(--ink); outline-offset: -1px; border: 0; box-shadow: var(--raise); font-weight: 600; }
.form-submit:active { box-shadow: var(--sink); padding: 8px 11px 6px 13px; }
.form-submit:focus-visible { outline: 1px dotted #fff; outline-offset: -4px; }
.form-submit[disabled] { background: var(--face); box-shadow: var(--sink); opacity: 0.6; cursor: default; }
.form-submit[disabled]:hover { background: var(--face); }
/* Status and button share a row, then wrap on narrow screens. */
.feedback-form .form-row.form-actions { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 12px; min-height: 60px}
.form-actions .form-submit { align-self: center; }
.form-status { min-height: 1.35em; margin: 0; font-size: 14px; padding: 7px; font-weight: bold; color: var(--ink); background: var(--amber); box-shadow: var(--sink-shallow);}
.form-status:empty { display: none; }
.form-status-success, .thanks-label { background: var(--green); color: #fff; font-size: 24px; box-shadow: none;}
.form-status-error {  font-weight: normal; }

/* Each success label carries its own color and dismissal menu. */
.form-status-thanks { padding: 0; background: none; box-shadow: none; }
.thanks-stack { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 10px; }
/* Hidden labels keep their slots so dismissal does not shift the row. */
.thanks-item { position: relative; display: inline-flex; visibility: hidden; }
.thanks-item.is-in { visibility: visible; }
.thanks-item.is-gone { visibility: hidden; }
.thanks-label { padding: 8px; border: 0; font: inherit; font-size: 24px; font-weight: bold; line-height: 1.35; }
.thanks-label:focus-visible { outline: 2px dashed #fff; outline-offset: -5px; }
@media (hover: hover) {
    .thanks-label:hover { outline: 2px dashed #fff; outline-offset: -5px; }
}
.thanks-menu { position: absolute; top: calc(100% + 3px); left: 0; z-index: 40; display: none; }
.thanks-item.is-open .thanks-menu { display: block; }
.thanks-menu-option { display: block; width: 100%; padding: 4px 10px; color: var(--ink); background: #fff; border: 1px solid var(--ink); box-shadow: 2px 2px 0 var(--ink); font-size: 14px; font-weight: 400; text-align: left; white-space: nowrap; }
.thanks-menu-option.is-touched { text-decoration: underline; }
@media (hover: hover) {
    .thanks-menu-option:hover { text-decoration: underline; }
}
.thanks-menu-option.is-pressing { box-shadow: none; transform: translate(2px, 2px); }
@media (hover: hover) {
    .thanks-menu-option:active { box-shadow: none; transform: translate(2px, 2px); }
}
.thanks-item.is-leaving { pointer-events: none; animation: thanks-fade 200ms steps(2, end) forwards; }
@keyframes thanks-fade { from { opacity: 1; } to { opacity: 0; } }
.form-status-loading { font-size: 14px; font-weight: 600; background: none;}
.form-spinner { display: inline-block; width: 14px; height: 14px; margin-right: 8px; vertical-align: -1px; background: #fff; box-shadow: var(--raise); animation: form-spinner-step 240ms steps(4, end) infinite; }
@keyframes form-spinner-step { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .form-spinner { animation: none; }
    .thanks-item.is-leaving { opacity: 0; animation: none; }
}

::-webkit-scrollbar { width: 14px; height: 14px; }
::-webkit-scrollbar-track { background: var(--face); }
::-webkit-scrollbar-thumb { background: var(--face); border: 1px solid var(--ink); }

@media (max-width: 700px) {
    .content-wrapper { width: 100%; padding: 10px; }
    .options-container { grid-template-columns: 1fr; min-height: 0; }
    .columns { grid-template-columns: 1fr; }
    .filters { order: -1; }
    .filter-selectors { display: grid; grid-template-columns: 1fr 1fr; align-items: center; }
    .checkbox-filters { grid-column: 1 / -1; }
    /* 380px is the intended height, but a short phone can only show so much
       before the recessed frame runs off the bottom of the screen. fitRepoHeight
       measures how much room is actually left below the box and publishes it as
       --repobox-fit, so the box gives up height only when it would otherwise be
       clipped. The var is read nowhere outside this query, so desktop keeps the
       70vh sizing from the base rule untouched. */
    .repo { min-height: 0; }
    .repobox { min-height: 0; max-height: none; height: max(380px, var(--repobox-fit, 380px)); }
    .opportunity-card { grid-template-columns: 1fr; }
    .top-right-grid { width: 100%; }
    .detail-token-row { justify-content: flex-start; }
}

/* Safari zooms the page in whenever a focused field's text measures under 16px,
   and offers no way back out. Keyed to the pointer rather than the width so a
   narrow desktop window keeps the 14px body type in its form. */
@media (pointer: coarse) {
    .form-input, .form-textarea { font-size: 16px; }
    /* Mobile draws its own overlay scrollbars and ignores the ::-webkit-scrollbar
       rules above entirely, leaving a pale bar that vanishes against these light
       fills. scrollbar-color is the one lever those overlay bars do respect. It
       is confined to coarse pointers because on desktop Chrome it takes over
       from the ::-webkit-scrollbar styling rather than sitting alongside it,
       which would flatten the beveled bars into plain ones. */
    .repobox, .details-popup-content, .custom-options { scrollbar-color: var(--ink) transparent; }
}

@media (max-width: 430px) {
    .page-title::before { padding: 0 5px; }
    .filter-selectors { grid-template-columns: 1fr; }
    .feedback-form { padding: 13px; }
}
