/* ==========================================================================
   main.css — beinsarkari.com
   ==========================================================================

   Loaded non-blocking (preload + onload="this.rel='stylesheet'") from
   layout.php. Everything here is either below the first mobile viewport or
   a state that only exists after interaction, so arriving late costs
   nothing. All design tokens live in critical.css.

   Order of sections:
      1  BASE EXTENSIONS          12  TABLES
      2  NAV STATES               13  IMPORTANT LINKS
      3  SEARCH SUGGESTIONS       14  FAQ + SHARE
      4  LAYOUT + SIDEBAR         15  PROSE
      5  BOARD                    16  FORMS + NOTICES
      6  ROWS                     17  SUBSCRIBE / WA / POPUP / MOCK
      7  BADGES + TAGS            18  PAGE HEAD / CRUMBS / PAGER
      8  BLOCKS + LISTS           19  ERROR PAGE
      9  CARDS + SIDEBAR WIDGETS  20  FOOTER
     10  BUTTONS                  21  FIXED ELEMENTS (z-index scheme)
     11  ARTICLE + PANELS         22  ADS (non-reservation)
                                  23  BREAKPOINTS
                                  24  DARK / MOTION / PRINT
   ========================================================================== */


/* ==========================================================================
   1. BASE EXTENSIONS
   ========================================================================== */

a:hover { color: var(--c-link-hover); }

/* ---------------------------------------------------------------- touch ---
   The grey flash Android paints over a tapped link lands on the whole row
   — a 90px-tall block going grey for 300ms reads as a glitch, not as a
   response. It is replaced below with a press state we control, which is
   what makes the difference between "a website" and "an app" on a phone.
   Scoped to coarse pointers so a mouse keeps its normal hover behaviour. */
@media (pointer: coarse) {
  a,
  button,
  summary,
  [role="button"] { -webkit-tap-highlight-color: transparent; }

  /* Everything tappable sinks slightly under the thumb. transform only,
     so there is no layout cost and no CLS. */
  .row__link:active,
  .chip:active,
  .btn:active,
  .links__btn:active,
  .board__more:active,
  .block__more:active,
  .jump__item:active,
  .card .chip:active { transform: scale(0.98); }

  .row__link:active { background: var(--c-primary-50); }
}

/* Keeps the footer off-screen on thin pages (404, short static pages) so
   they do not read as broken. */
.main { display: block; min-height: 50vh; }

/* The upper half of the sticky header, above .nav. Carries the hairline
   that separates the two bands. */
.header__bar { box-shadow: inset 0 -1px 0 rgb(255 255 255 / 0.12); }

.search input::placeholder { color: var(--c-muted); }

/* Telugu and Devanagari carry taller glyph stacks than Latin; without the
   extra leading the matras collide with the line above. */
:lang(te) body,
:lang(hi) body,
:lang(te) .prose,
:lang(hi) .prose { line-height: 1.8; }

.alert-strip__track strong {
  padding: 1px 6px;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: #fff;
  background: var(--c-urgent);
  border-radius: var(--r-pill);
}

/* Hero illustration. Off until there is a column free for it — see the
   1024px block in section 23. */
.hero__art { display: none; }

/* ------------------------------------------------------- app launcher ---
   Four across on a phone, eight across once there is width. Fixed
   columns rather than auto-fit: a launcher whose tile count per row
   changes with the viewport stops being a memorised position, and the
   whole value of this grid is that a returning visitor taps "Results"
   without reading. */
.launch {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s-2);
  margin-bottom: var(--s-5);
  padding: var(--s-3);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
}
.launch__tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
  padding: var(--s-2) 2px;
  color: var(--c-text);
  border-radius: var(--r-md);
}
.launch__ico {
  display: grid;
  place-items: center;
  width: 2.875rem;
  height: 2.875rem;
  color: var(--tone-fg, var(--c-accent));
  background: var(--tone-bg, var(--c-accent-50));
  border-radius: var(--r-lg);
}
.launch__label {
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  /* Two lines maximum, so "Exam Calendar" and "Jobs" produce tiles of
     the same height and the grid stays a grid. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.launch__tile:hover { color: var(--c-text); background: var(--c-surface-2); }
.launch__tile:active { transform: scale(0.94); }

/* One hue per destination, reusing the status tokens so the launcher,
   the board headers and the badges all speak the same colour language. */
.launch__tile--jobs      { --tone-fg: var(--c-primary);     --tone-bg: var(--c-primary-50); }
.launch__tile--result    { --tone-fg: var(--c-open-fg);     --tone-bg: var(--c-open-bg); }
.launch__tile--admit     { --tone-fg: var(--c-accent);      --tone-bg: var(--c-accent-50); }
.launch__tile--answer    { --tone-fg: var(--c-upcoming-fg); --tone-bg: var(--c-upcoming-bg); }
.launch__tile--syllabus  { --tone-fg: var(--c-urgent-fg);   --tone-bg: var(--c-urgent-bg); }
.launch__tile--calendar  { --tone-fg: var(--c-accent);      --tone-bg: var(--c-accent-50); }
.launch__tile--scheme    { --tone-fg: var(--c-open-fg);     --tone-bg: var(--c-open-bg); }
.launch__tile--admission { --tone-fg: var(--c-primary);     --tone-bg: var(--c-primary-50); }
.launch__tile--mock      { --tone-fg: var(--c-accent);      --tone-bg: var(--c-accent-50); }

.empty {
  padding: var(--s-5) var(--s-4);
  font-size: var(--fs-md);
  color: var(--c-muted);
  text-align: center;
}

/* An empty board column is a 10rem hole in the middle of the homepage.
   The dashed well reads as "this fills up", not as a broken page. */
.board__body .empty {
  margin: var(--s-3);
  padding: var(--s-5) var(--s-3);
  border: 1px dashed var(--c-border-strong);
  border-radius: var(--r-md);
  background: var(--c-surface-2);
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-6) var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  text-align: center;
  color: var(--c-text-2);
}


/* ==========================================================================
   2. NAV STATES
   ========================================================================== */

/* The burger does not reveal a hidden drawer — the nav is already a visible
   scroller (critical.css). Opening it unwraps that one-line scroller into a
   two-column grid so the whole list is visible at once. Nothing is ever
   display:none, which is why JS-off visitors lose no navigation. */
.nav.is-open .nav__list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  overflow: visible;
  white-space: normal;
}
.nav.is-open .nav__list a {
  border-bottom: 1px solid rgb(255 255 255 / 0.12);
}

.header__burger.is-open span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.header__burger.is-open span:nth-child(2) { opacity: 0; }
.header__burger.is-open span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
.header__burger span { transition: transform 0.18s ease, opacity 0.18s ease; }


/* ==========================================================================
   3. SEARCH SUGGESTIONS
   ========================================================================== */

/* The dropdown itself is hidden at first paint, so all of it lives here.
   Markup for .search__opt* is generated by app.js (textContent only). */
.search__results {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 5;
  max-height: 60vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--c-surface);
  color: var(--c-text);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  box-shadow: var(--sh-3);
}

.search__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  min-height: var(--tap);
  padding: var(--s-2) var(--s-3);
  cursor: pointer;
  border-bottom: 1px solid var(--c-border);
}
.search__opt:last-child { border-bottom: 0; }
.search__opt[aria-selected="true"],
.search__opt:hover { background: var(--c-primary-50); }

.search__opt-title {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--c-text);
}
.search__opt-type {
  flex: 0 0 auto;
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--c-muted);
}
.search__empty {
  padding: var(--s-3);
  font-size: var(--fs-md);
  color: var(--c-muted);
}

/* Standalone search form on /search and the 404 page. Resets the header
   variant's flex ordering and frees the button width, because here the
   button holds a word instead of a 18px icon. */
/* The flex reset matters: .search carries `flex:1 0 100%` for the header's
   wrapping row, and inside .error-page (a flex *column*) that 100% basis
   would be read as a height. */
.search--page {
  order: 0;
  flex: 0 1 auto;
  width: 100%;
  max-width: 36rem;
  margin-block: var(--s-4);
}
.search--page button {
  width: auto;
  padding-inline: var(--s-4);
  font-weight: 700;
}


/* ==========================================================================
   4. LAYOUT + SIDEBAR
   ========================================================================== */

.layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-5);
  padding-bottom: var(--s-6);
}
.layout__main { min-width: 0; }

/* Hidden, not reordered, below 1024px: on mobile these widgets would push
   the article below the fold and cost LCP. */
.sidebar { display: none; }


/* ==========================================================================
   5. BOARD
   ========================================================================== */

.board__col--jobs { --col: var(--c-primary); }
.board__col--results { --col: var(--c-open); }
.board__col--admit { --col: var(--c-accent); }

/* The bar itself is in critical.css. What is left here is the detail that
   can safely arrive late because it changes no box size. */
.board__title a {
  display: block;
  overflow: hidden;
  font-weight: 800;
  letter-spacing: 0.01em;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Decorative arrow, after the title, inside the coloured bar. Drawn in CSS
   rather than added to home.php so it stays out of the accessible name of
   the link — "Latest Jobs →" read aloud is noise. */
.board__title::after {
  content: "";
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  border-top: 2px solid var(--col-fg);
  border-right: 2px solid var(--col-fg);
  opacity: 0.85;
  transform: rotate(45deg);
}

.board__body { flex: 1 1 auto; }

/* An empty column is one short line of grey text against a full one, which
   makes the board look broken rather than merely new. Extra block padding
   keeps the three columns closer to the same height. */
.board__body > .empty { padding-block: var(--s-6); }

/* Reads as a button, not as a caption: this is the link that turns a
   homepage visit into a second pageview, so it gets real weight. */
.board__more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: var(--tap);
  padding: var(--s-2) var(--s-4);
  font-size: var(--fs-md);
  font-weight: 800;
  color: var(--col);
  background: var(--c-surface-2);
  border-top: 1px solid var(--c-border);
}
.board__more:hover {
  color: var(--col-fg);
  background: var(--col);
}


/* ==========================================================================
   6. ROWS
   ========================================================================== */

/* Grid, not flex-wrap. A wrapping flex row let the badge claim the right
   edge and squeeze the link down to its 14rem flex-basis, which is what
   made the meta list wrap into a ragged two-word column on a phone. With
   `1fr auto` the text column takes everything the badge does not need,
   so every row in a list breaks at the same place and the block reads as
   a set of equal boxes. */
.row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  border-bottom: 1px solid var(--c-border);
}
.row:last-child { border-bottom: 0; }

/* Whole row is the hit area — thumb accuracy on a phone is poor and a
   missed tap on a job row is a lost pageview. */
.row__link {
  grid-column: 1;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  color: inherit;
}
.row__body { flex: 1; min-width: 0; }

/* Sector artwork. A wall of pure blue links is what makes a sarkari
   listing feel like a 2009 forum; one small image per row is what turns
   the same markup into something that reads like an app feed. The
   width/height attributes are also on the tag so the row never reflows
   when it loads, and flex-shrink is off so a long title cannot squeeze
   the square into a sliver. */
.row__thumb {
  flex: 0 0 auto;
  width: 3.5rem;
  height: 3.5rem;
  margin-top: 2px;
  object-fit: cover;
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
}
/* inset bar rather than a border: a real border on hover would change the
   row's width and nudge every title one pixel sideways. */
.row__link:hover {
  background: var(--c-primary-50);
  box-shadow: inset 3px 0 0 var(--col, var(--c-primary));
}
.row__link:hover .row__title { text-decoration: underline; }

.row__title {
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--c-link);
}
.row--compact .row__title { font-size: var(--fs-md); font-weight: 500; }

.row__org {
  margin-top: var(--s-1);
  font-size: var(--fs-sm);
  color: var(--c-text-2);
}
.row__posts {
  font-weight: 700;
  color: var(--c-accent);
}
.row__excerpt {
  margin-top: var(--s-1);
  font-size: var(--fs-sm);
  color: var(--c-muted);
}

.row__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-4);
  margin-top: var(--s-2);
  font-size: var(--fs-sm);
  color: var(--c-text-2);
}
.row__meta li { display: flex; gap: var(--s-1); min-width: 0; }
.row__meta li span { color: var(--c-muted); }

/* The last date is the single fact that decides whether the row is worth
   a tap, so it gets a chip of its own rather than sitting in the same
   grey run as the qualification text. */
.row__meta .row__date {
  padding: 1px var(--s-2);
  font-weight: 700;
  color: var(--c-accent);
  background: var(--c-accent-50);
  border-radius: var(--r-sm);
}
.row__meta .row__date span { color: var(--c-accent); opacity: 0.75; }
.row__meta li.is-urgent {
  padding: 1px var(--s-2);
  font-weight: 700;
  color: var(--c-urgent-fg);
  background: var(--c-urgent-bg);
  border-radius: var(--r-sm);
}
.row__meta li.is-urgent span { color: var(--c-urgent-fg); opacity: 0.8; }
/* Qualification is the only variable-length item in the list, so on a
   narrow screen it takes a line of its own. Left to wrap naturally it
   dragged the date chip onto a second line on some rows and not others,
   which is what made a tidy list look ragged. Scoped to phones — on a
   wide row everything fits on one line anyway. */
@media (max-width: 767px) {
  .row__meta .row__qual { flex: 1 1 100%; }
}
.row__meta .row__time { color: var(--c-muted); }

.row__sub {
  display: block;
  margin-top: 2px;
  font-size: var(--fs-xs);
  color: var(--c-muted);
}

/* Compact rows carry no badge, no org and no meta list, so without a
   marker a board column is an undifferentiated wall of blue text. The dot
   is absolutely positioned and the padding reserves its gutter, so a
   two-line title wraps flush instead of hanging under the dot. */
.row--compact .row__link {
  position: relative;
  padding: var(--s-2) var(--s-4) var(--s-2) var(--s-5);
}
/* A thumbed compact row already has a visual anchor, so it keeps normal
   padding and drops the dot. */
.row--compact.row--thumbed .row__link { padding: var(--s-2) var(--s-4); }
.row--compact.row--thumbed .row__link::before { content: none; }
.row--compact .row__thumb { width: 2.5rem; height: 2.5rem; }
.row--compact .row__link::before {
  content: "";
  position: absolute;
  top: 0.95em;
  left: var(--s-4);
  width: 5px;
  height: 5px;
  background: var(--col, var(--c-border-strong));
  border-radius: 50%;
}


/* ==========================================================================
   7. BADGES + TAGS
   ========================================================================== */

.badge {
  align-self: center;
  justify-self: end;
  grid-column: 2;
  margin: 0 var(--s-4) var(--s-3);
  padding: 3px var(--s-2);
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
}
.badge--urgent   { color: var(--c-urgent-fg);   background: var(--c-urgent-bg);   border-color: var(--c-urgent); }
.badge--open     { color: var(--c-open-fg);     background: var(--c-open-bg);     border-color: var(--c-open); }
.badge--upcoming { color: var(--c-upcoming-fg); background: var(--c-upcoming-bg); border-color: var(--c-upcoming); }
.badge--extended { color: var(--c-primary);     background: var(--c-primary-50);  border-color: var(--c-primary-100); }
.badge--warn     { color: var(--c-upcoming-fg); background: var(--c-upcoming-bg); border-color: var(--c-upcoming); }
.badge--closed   { color: var(--c-closed-fg);   background: var(--c-closed-bg);   border-color: var(--c-border-strong); }

/* Badge in the article header sits in normal flow, not in a row. */
.article__head .badge {
  display: inline-block;
  margin: 0 0 var(--s-2);
}

.tag {
  display: inline-block;
  margin-left: var(--s-1);
  padding: 1px 5px;
  font-size: 0.6875rem;
  font-weight: 800;
  text-transform: uppercase;
  vertical-align: middle;
  border-radius: var(--r-sm);
}
.tag--new { color: #fff; background: var(--c-urgent); }


/* ==========================================================================
   8. BLOCKS + LISTS
   ========================================================================== */

.block {
  margin-bottom: var(--s-5);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
}
.block--urgent { border-left: 3px solid var(--c-urgent); }

.block__title {
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-lg);
  border-bottom: 1px solid var(--c-border);
}
.block__title a { color: var(--c-text); }
.block__title a:hover { color: var(--c-link); }
.block--urgent .block__title { color: var(--c-urgent-fg); }

.block__more {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  font-size: var(--fs-md);
  font-weight: 700;
  background: var(--c-surface-2);
  border-top: 1px solid var(--c-border);
  border-radius: 0 0 var(--r-md) var(--r-md);
}

.block__body { min-width: 0; }

/* .list is a dense stack of rows with in-feed ads spliced between them;
   .grid is the two-up variant used for related/trending blocks. */
.list { display: flex; flex-direction: column; }
.list > :last-child { border-bottom: 0; }

.grid { display: grid; grid-template-columns: 1fr; }

/* Shared by .block__body--chips usage in home/archive and the sidebar. */
/* Grid, not wrapped flex. Flex sized every chip to its own text, so
   "ITI" sat beside "Post Graduate" at a third of its width and a lone
   last chip stretched to the full row — the uneven boxes the whole
   facet list was being judged on. Equal tracks make every chip the same
   size no matter how many there are or how long the labels run. */
.card__body--chips {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  gap: var(--s-2);
  padding: var(--s-4);
}
.card__body--chips .chip {
  justify-content: center;
  min-width: 0;
  padding: 0 var(--s-2);
  overflow: hidden;
  text-align: center;
}

.filterbar {
  display: flex;
  gap: var(--s-2);
  margin-bottom: var(--s-4);
  padding-bottom: var(--s-1);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.filterbar::-webkit-scrollbar { display: none; }
.filterbar .chip { flex: 0 0 auto; }

/* The sidebar column is barely 20rem wide, so chips of wildly different
   lengths ("PhD" next to "Central Government") wrapped into a ragged
   right edge with single chips stranded on their own row — the block read
   as scattered rather than as a list. Letting them grow fills every row
   edge to edge. flex-basis stays auto, so a chip is still at least as wide
   as its label and the text can never wrap inside the pill. Scoped to the
   sidebar because the home and archive versions of this block sit in a
   full-width column where natural wrapping already looks even. */
/* The sidebar is narrow enough that two tracks is the only honest fit. */
.sidebar .card__body--chips { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Chip count suffix, used in the category list. */
.chip small {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--c-muted);
}
.chip:hover { border-color: var(--c-primary); background: var(--c-primary-50); }


/* ==========================================================================
   9. CARDS + SIDEBAR WIDGETS
   ========================================================================== */

.card {
  margin-bottom: var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
}
.card--urgent { border-top: 3px solid var(--c-urgent); }

.card__title {
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-base);
  border-bottom: 1px solid var(--c-border);
}
.card--urgent .card__title { color: var(--c-urgent-fg); }

.card__body { min-width: 0; }

.card__text {
  padding: var(--s-3) var(--s-4) 0;
  font-size: var(--fs-md);
  color: var(--c-text-2);
}

.exam-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: var(--tap);
  padding: var(--s-2) var(--s-4);
  color: inherit;
  border-bottom: 1px solid var(--c-border);
}
.exam-row:last-child { border-bottom: 0; }
.exam-row:hover { background: var(--c-primary-50); }

.exam-row__date {
  flex: 0 0 3.25rem;
  padding: 2px 0;
  font-size: var(--fs-xs);
  font-weight: 800;
  text-align: center;
  color: var(--c-primary);
  background: var(--c-primary-50);
  border-radius: var(--r-sm);
}
.exam-row__name { font-size: var(--fs-md); }

.sidebar__sticky { position: sticky; top: calc(var(--header-h) + var(--s-3)); }


/* ==========================================================================
   10. BUTTONS
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: var(--tap);
  padding: 0 var(--s-5);
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--c-link);
  background: var(--c-surface);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-md);
}
.btn:hover { border-color: var(--c-primary); background: var(--c-primary-50); }

.btn--primary {
  color: #fff;
  background: var(--c-primary);
  border-color: var(--c-primary);
}
.btn--primary:hover { color: #fff; background: var(--c-primary-600); }

/* The in-article apply button. Green, because "open" is the semantic. */
.cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  margin: var(--s-4);
  min-height: 3.25rem;
  padding: var(--s-2) var(--s-5);
  font-size: var(--fs-lg);
  font-weight: 800;
  color: #fff;
  background: var(--c-open);
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
}
.cta:hover { color: #fff; filter: brightness(0.94); }
.cta small { font-size: var(--fs-xs); font-weight: 600; opacity: 0.9; }


/* ==========================================================================
   11. ARTICLE + PANELS
   ========================================================================== */

.article { min-width: 0; }

/* Short band rather than a full 1200x630 hero: the source art is 1.9:1
   and would eat 190px of a phone screen, pushing the instant-links pad
   out of view and defeating the point of it. object-fit crops instead of
   squashing, and the subject sits in the right third of every render so a
   centred vertical crop never clips it. */
.article__banner {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 1;
  object-fit: cover;
  background: var(--c-surface-2);
  border-radius: var(--r-lg);
}
@media (min-width: 48rem) {
  .article__banner { aspect-ratio: 3.4 / 1; }
}

.article__head { padding: var(--s-4) 0 var(--s-2); }

.article__title {
  font-size: clamp(1.375rem, 5.4vw, 1.875rem);
  letter-spacing: -0.01em;
}
/* The lede is the only summary of the article a visitor gets, and as
   plain grey text it read as the first boring paragraph of a wall of
   them. Boxing it makes it the thing the eye lands on after the title
   and gives the article a visible beginning. */
.article__lede {
  margin-top: var(--s-4);
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-lg);
  line-height: 1.5;
  color: var(--c-text);
  background: var(--c-surface-2);
  border-left: 4px solid var(--c-primary);
  border-radius: 0 var(--r-md) var(--r-md) 0;
}

/* Stat strip. Two equal columns on a phone, four on a tablet — always a
   divisor of four, so the last row is never one orphan tile beside a
   gap. min-width:0 on the tile because a long salary string would
   otherwise push the grid wider than the article. */
.stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-2);
  margin-top: var(--s-4);
}
.stat {
  --tone: var(--c-accent);
  --tone-bg: var(--c-accent-50);
  min-width: 0;
  padding: var(--s-3);
  text-align: center;
  background: var(--tone-bg);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
}
.stat__value {
  font-size: var(--fs-xl);
  font-weight: 800;
  line-height: 1.15;
  color: var(--tone);
  overflow-wrap: anywhere;
}
.stat__value small {
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
}
.stat__label {
  margin-top: var(--s-1);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--c-muted);
}
.stat__sub { font-size: var(--fs-xs); color: var(--c-text-2); }
.stat--urgent { --tone: var(--c-urgent-fg); --tone-bg: var(--c-urgent-bg); }
.stat--posts  { --tone: var(--c-primary);   --tone-bg: var(--c-primary-50); }
.stat--salary { --tone: var(--c-open-fg);   --tone-bg: var(--c-open-bg); }
/* A pay scale is a long string, not a figure — shrink it so it does not
   wrap to four lines and drag the other three tiles down with it. */
.stat--salary .stat__value { font-size: var(--fs-md); }

.article__byline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-1) var(--s-3);
  margin-top: var(--s-3);
  font-size: var(--fs-sm);
  color: var(--c-muted);
}
.article__updated { font-weight: 600; color: var(--c-text-2); }
.article__cat {
  padding: 2px var(--s-2);
  font-weight: 700;
  background: var(--c-primary-50);
  border-radius: var(--r-sm);
}

.article__langs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-3);
  padding: var(--s-2) var(--s-3);
  font-size: var(--fs-sm);
  background: var(--c-surface-2);
  border: 1px dashed var(--c-border-strong);
  border-radius: var(--r-md);
}
.article__langs a {
  display: inline-flex;
  align-items: center;
  min-height: 2rem;
  padding: 0 var(--s-2);
  font-weight: 700;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-sm);
}

.article__body { margin-bottom: var(--s-5); }

/* --------------------------------------------------------------------
   Panels.

   Previously the accent on a panel was a 3px border-top. Against an 8px
   radius that border bends into a thick coloured arc at both corners,
   and only four of the eight panel types carried one — so a page of
   panels had four different box shapes in it. That is what read as
   "the boxes are not right".

   Now every panel is the same shape and the accent lives on a single
   custom property that colours a bar inside the title. One rule paints
   all of them; a variant only overrides the colour.
   -------------------------------------------------------------------- */
.panel {
  --panel-accent: var(--c-primary);
  margin-bottom: var(--s-5);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  /* Clips the title's tint to the rounded corners. Also stops a wide
     table inside .table-scroll from poking past the rounded edge. */
  overflow: hidden;
}
.panel--links { --panel-accent: var(--c-accent); }
.panel--dates { --panel-accent: var(--c-upcoming); }
.panel--faq   { --panel-accent: var(--c-border-strong); }

.panel__title {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  /* Tinted header plane. The box now has a head and a body instead of
     being one undifferentiated rectangle with a rule across it, which
     is what makes a run of panels scannable on a phone. */
  background: var(--c-surface-2);
  border-bottom: 1px solid var(--c-border);
}
/* align-self: stretch ties the bar's height to the title text, so a
   title that wraps to two lines on a narrow phone grows the bar with
   it instead of leaving it stranded against the first line. */
.panel__title::before {
  content: "";
  flex: 0 0 auto;
  align-self: stretch;
  width: 4px;
  min-height: 1.15em;
  background: var(--panel-accent);
  border-radius: var(--r-pill);
}
/* Notes sit at the foot of a panel, below a table or a link list. Given
   a tinted band they read as a footnote to the block above instead of as
   another stray line of grey text. */
.panel__note {
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-sm);
  color: var(--c-text-2);
  background: var(--c-surface-2);
  border-top: 1px solid var(--c-border);
  border-radius: 0 0 var(--r-md) var(--r-md);
}
/* The first element after the title already carries .prose's 1rem top
   margin, so the bottom pad has to be the same 1rem or the box looks
   top-heavy — text pressed against the lower edge while the title floats.
   Same value left and right, so the content sits in an even frame.

   The pad lives on the panel, not on `> :not(.panel__title)`. That child
   selector only ever reached element children, so an editor field pasted
   as loose prose — which is how every job_details block arrives — put the
   padding on the only elements in the paragraph, the .dt/.num highlight
   spans, and blew a 1rem gap either side of every date and amount while
   the sentence itself ran to the panel edge. Padding the container cannot
   miss a text node, so the frame holds whatever the editor pastes.
   The title is pulled back out to the edges to keep its tinted band
   full-bleed. */
.panel.prose { padding: 0 var(--s-4) var(--s-4); }
.panel.prose > .panel__title { margin-inline: calc(var(--s-4) * -1); }

/* Overview key/value list — the block visitors screenshot and share.

   Stacked by default. A 9.5rem label column on a 320px phone leaves
   about 100px for the value, so "Rs. 21,700 - 69,100 (Level 3, initial
   basic ...)" shredded into six ragged lines. Label above value gives
   the value the full width; the label is small and muted so the two
   still read as a pair rather than as two separate lines of text.
   The two-column form returns at 30rem, where it fits. */
.kv__row {
  display: grid;
  gap: 2px;
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-md);
  border-bottom: 1px solid var(--c-border);
}
/* Zebra, not just rules: on a long overview the eye loses the row it was
   on when every band is the same colour. */
.kv__row:nth-child(even) { background: var(--c-surface-2); }
.kv__row:last-child { border-bottom: 0; }
.kv__row dt {
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--c-muted);
}
.kv__row dd { font-weight: 600; }

@media (min-width: 30rem) {
  .kv__row {
    grid-template-columns: 10.5rem minmax(0, 1fr);
    gap: var(--s-3);
    align-items: baseline;
  }
  .kv__row dt {
    font-size: var(--fs-md);
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
  }
}

/* --------------------------------------------------------------------
   Instant Links — the jump pad under the article title.

   Modelled on a phone home screen rather than a list of links, because
   that is the shape a thumb already knows: fixed-size tiles, icon over
   label, centred, nothing to read before you can act. Two columns at
   320px, growing with the viewport, so the pad never costs more than
   about three rows of height no matter how many sections exist.
   -------------------------------------------------------------------- */
.jump {
  margin-bottom: var(--s-5);
  padding: var(--s-3);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
}
.jump__title {
  margin-bottom: var(--s-3);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--c-muted);
}
/* auto-fit over a fixed minimum rather than a fixed column count: the
   same declaration gives 2 columns on a 320px phone, 3 on a large one
   and 5 in the desktop content column, with no breakpoints to maintain. */
.jump__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.25rem, 1fr));
  gap: var(--s-2);
}
.jump__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  min-height: 4.75rem;
  padding: var(--s-2) var(--s-1);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  color: var(--c-text-2);
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  /* Two-line cap. Labels like "Selection Process" are long and a third
     line would make one tile taller than its neighbours and break the
     grid's rhythm; the icon above already carries the meaning. */
  overflow: hidden;
}
.jump__item span {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.jump__ico {
  width: 22px;
  height: 22px;
  color: var(--c-primary);
}
.jump__item:active { transform: scale(0.96); }
@media (hover: hover) and (pointer: fine) {
  .jump__item:hover {
    color: var(--c-primary);
    background: var(--c-primary-50);
    border-color: var(--c-primary);
    text-decoration: none;
  }
}
.jump__item:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }

/* The target section flashes once when a jump lands on it, so the eye
   knows the tap did something. :target only matches after a hash
   navigation, so normal scrolling never triggers it. */
.panel:target { animation: jump-land 1.2s ease-out 1; }
@keyframes jump-land {
  0%, 20% { box-shadow: 0 0 0 3px var(--c-primary); }
  100%    { box-shadow: var(--sh-1); }
}

/* Table of contents */
/* Shares the jump pad's radius so the two navs read as one family —
   the pad jumps to the fixed job sections, this one to the headings
   inside the written article. */
.toc {
  margin-bottom: var(--s-5);
  padding: var(--s-3) var(--s-4);
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
}
.toc__title {
  margin-bottom: var(--s-2);
  font-size: var(--fs-md);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--c-muted);
}
.toc ol {
  list-style: decimal;
  padding-left: 1.5rem;
  font-size: var(--fs-md);
}
.toc li { margin-bottom: 2px; }
/* Full 44px rows even though it makes a long TOC tall; the 768px rule
   below splits it into two columns to win the height back. */
.toc li a {
  display: flex;
  align-items: center;
  min-height: var(--tap);
}
.toc__l1,
.toc__l2 { margin-left: 0; }
.toc__l3 { margin-left: var(--s-3); }
.toc__l4 { margin-left: var(--s-5); }


/* ==========================================================================
   12. TABLES
   ========================================================================== */

/* The wrapper scrolls, never the page. overscroll-behavior stops a
   horizontal swipe inside the table from triggering browser back. */
.table-scroll {
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

/* 18rem (288px) is narrower than the content box of a 320px phone, so
   two-column tables never scroll; wider ones scroll inside the wrapper. */
/* Scroll affordance toggled by app.js when content is clipped to the right;
   without it a phone user cannot tell the fee/date table continues. */
.table-scroll.is-scrollable {
  box-shadow: inset -16px 0 14px -14px rgb(16 24 40 / 0.35);
}

.table {
  min-width: 18rem;
  font-size: var(--fs-md);
}
.table caption { text-align: start; }
.table th,
.table td {
  padding: var(--s-2) var(--s-3);
  text-align: start;
  vertical-align: top;
  border-bottom: 1px solid var(--c-border);
}
.table thead th {
  font-size: var(--fs-sm);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
  color: var(--c-muted);
  background: var(--c-surface-2);
}
.table tbody th { font-weight: 600; }
.table tbody td { color: var(--c-text-2); }
.table tbody tr:last-child th,
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:nth-child(even) { background: var(--c-surface-2); }


/* ==========================================================================
   13. IMPORTANT LINKS
   ========================================================================== */

.links { padding: var(--s-2) 0; }
.links li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border-bottom: 1px solid var(--c-border);
}
.links li:last-child { border-bottom: 0; }

.links__label {
  flex: 1 1 10rem;
  font-size: var(--fs-md);
  font-weight: 600;
}

.links__btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 7.5rem;
  min-height: var(--tap);
  padding: 0 var(--s-4);
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--c-primary);
  background: var(--c-primary-50);
  border: 1px solid var(--c-primary-100);
  border-radius: var(--r-md);
}
.links__btn:hover { color: #fff; background: var(--c-primary); }
.links__btn--primary {
  color: #fff;
  background: var(--c-accent);
  border-color: var(--c-accent);
}
.links__btn--primary:hover { color: #fff; background: var(--c-accent-600); }


/* ==========================================================================
   14. FAQ + SHARE
   ========================================================================== */

.faq { border-bottom: 1px solid var(--c-border); }
.faq:last-of-type { border-bottom: 0; }
.faq summary {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: var(--tap);
  padding: var(--s-2) var(--s-4);
  font-size: var(--fs-md);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::before {
  content: "+";
  flex: 0 0 1.25rem;
  font-size: var(--fs-lg);
  font-weight: 800;
  color: var(--c-accent);
}
.faq[open] summary::before { content: "\2212"; }
.faq__body { padding: 0 var(--s-4) var(--s-3); }

.share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: var(--s-5);
  padding: var(--s-3) var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
}
.share__label {
  font-size: var(--fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--c-muted);
}
.share__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: 0 var(--s-4);
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--c-text);
  background: var(--c-surface-2);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-md);
}
.share__btn--wa { color: #fff; background: #1ba84f; border-color: #1ba84f; }
.share__btn--tg { color: #fff; background: #2a93d0; border-color: #2a93d0; }
/* Confirmation state written by app.js after a successful copy. */
.share__btn.is-copied {
  color: var(--c-open-fg);
  background: var(--c-open-bg);
  border-color: var(--c-open);
}


/* ==========================================================================
   15. PROSE — editor-authored HTML, so every element needs a default
   ========================================================================== */

.prose { font-size: var(--fs-base); color: var(--c-text); }
.prose > * + * { margin-top: var(--s-4); }

/* Headings earn a rule and a red tick. Without them a 2000-word
   notification is one undifferentiated grey column and visitors cannot
   see where "how to apply" starts, so they leave for a site that signposts
   it. The marker is a pseudo-element so the editor HTML stays plain. */
.prose h2 {
  position: relative;
  margin-top: var(--s-6);
  padding-bottom: var(--s-2);
  padding-left: var(--s-3);
  font-size: var(--fs-xl);
  border-bottom: 2px solid var(--c-border);
}
.prose h2::before {
  content: "";
  position: absolute;
  top: 0.15em;
  left: 0;
  width: 4px;
  height: 1.1em;
  background: var(--c-primary);
  border-radius: var(--r-pill);
}
.prose h3 {
  margin-top: var(--s-5);
  font-size: var(--fs-lg);
  color: var(--c-accent);
}
.prose h4 { font-size: var(--fs-base); margin-top: var(--s-4); }

.prose a { text-decoration: underline; text-underline-offset: 2px; }

/* Dates, wrapped by highlight_dates(). The whole reason anyone reads a
   notification is to find these, so they get a chip the eye can scan for
   instead of blending into the sentence. Inline-block would break the
   line wrap mid-sentence, so it stays inline with a tight pad. */
.dt {
  padding: 0 4px;
  font-weight: 700;
  color: var(--c-accent-600);
  background: var(--c-accent-50);
  box-shadow: inset 0 -1px 0 var(--c-accent);
  border-radius: var(--r-sm);
}
.prose strong .dt,
.prose .dt { white-space: nowrap; }
/* Inside a date table the column is already dates — chips there would be
   pure noise. */
.prose table .dt,
.table .dt,
.kv .dt { padding: 0; background: none; box-shadow: none; }

/* Money, post counts and percentages, wrapped by highlight_numbers().
   Weight and colour only — a second chip style next to .dt would turn a
   paragraph into a ransom note. */
.num { font-weight: 700; color: var(--c-primary); white-space: nowrap; }
.prose table .num,
.table .num,
.kv .num,
.stat .num { font-weight: inherit; color: inherit; }

/* Lists carry most of the substance in a notification — eligibility,
   documents, steps — and a grey disc does nothing to mark an item as
   something to check off. Ticks for unordered, numbered discs for
   ordered, which is how the steps read on paper. */
.prose ul { list-style: none; padding-left: 0; }
.prose ul > li {
  position: relative;
  padding-left: var(--s-6);
}
.prose ul > li::before {
  content: "";
  position: absolute;
  top: 0.42em;
  left: 0.35rem;
  width: 0.5rem;
  height: 0.3rem;
  border: solid var(--c-primary);
  border-width: 0 0 2px 2px;
  transform: rotate(-45deg);
}
.prose ol {
  list-style: none;
  padding-left: 0;
  counter-reset: prose-step;
}
.prose ol > li {
  position: relative;
  padding-left: var(--s-6);
  counter-increment: prose-step;
}
.prose ol > li::before {
  content: counter(prose-step);
  position: absolute;
  top: 0.1em;
  left: 0;
  display: grid;
  place-items: center;
  width: 1.375rem;
  height: 1.375rem;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: #fff;
  background: var(--c-primary);
  border-radius: 50%;
}
.prose li + li { margin-top: var(--s-2); }
/* Nested lists go back to plain markers — a second level of ticks reads
   as a flat list again, which defeats the nesting. */
.prose li > ul,
.prose li > ol { margin-top: var(--s-2); padding-left: var(--s-4); }
.prose li > ul > li,
.prose li > ol > li { padding-left: var(--s-4); }
.prose li > ul > li::before {
  top: 0.55em;
  left: 0;
  width: 5px;
  height: 5px;
  border: 0;
  background: var(--c-muted);
  border-radius: 50%;
  transform: none;
}
.prose li > ol > li::before {
  content: counter(prose-step) ".";
  display: block;
  width: auto;
  height: auto;
  color: var(--c-primary);
  background: none;
  border-radius: 0;
}

.prose blockquote {
  padding: var(--s-2) var(--s-4);
  color: var(--c-text-2);
  border-left: 3px solid var(--c-primary-100);
}

/* Callouts, built by callout_boxes() from "Note: ...", "Important: ...",
   "Warning: ..." paragraphs the editor already writes. Three tones only:
   more than three and the page is back to being undifferentiated, just
   in colour. */
.callout {
  --cal: var(--c-accent);
  --cal-bg: var(--c-accent-50);
  position: relative;
  padding: var(--s-3) var(--s-4) var(--s-3) var(--s-5);
  background: var(--cal-bg);
  border: 1px solid var(--c-border);
  border-left: 4px solid var(--cal);
  border-radius: var(--r-md);
}
.callout__title {
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--cal);
}
.callout__body { margin-top: var(--s-1); }
.callout--warn { --cal: var(--c-urgent-fg); --cal-bg: var(--c-urgent-bg); }
.callout--tip  { --cal: var(--c-open-fg);   --cal-bg: var(--c-open-bg); }
.prose code {
  padding: 1px 4px;
  font-family: var(--font-mono);
  font-size: 0.875em;
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-sm);
}
.prose pre {
  padding: var(--s-3);
  overflow-x: auto;
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
}
.prose pre code { padding: 0; background: none; border: 0; }

.prose img {
  margin-inline: auto;
  border-radius: var(--r-md);
}
.prose hr { border: 0; border-top: 1px solid var(--c-border); }
.prose strong { font-weight: 700; }

/* Editor tables get the same treatment as .table, plus their own scroller
   since the editor cannot add a wrapper. */
.prose table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  font-size: var(--fs-md);
}
.prose th,
.prose td {
  padding: var(--s-2) var(--s-3);
  text-align: start;
  border: 1px solid var(--c-border);
}
.prose th { background: var(--c-surface-2); }


/* ==========================================================================
   16. FORMS + NOTICES
   ========================================================================== */

.form { padding: var(--s-4); }
.form__row { margin-bottom: var(--s-4); }
.form__row label {
  display: block;
  margin-bottom: var(--s-1);
  font-size: var(--fs-md);
  font-weight: 600;
}
.form__row input,
.form__row textarea {
  width: 100%;
  padding: var(--s-2) var(--s-3);
  min-height: var(--tap);
  /* 16px minimum — a smaller input makes iOS zoom the whole page on focus. */
  font-size: var(--fs-base);
  background: var(--c-surface);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-md);
}
.form__row textarea { min-height: 9rem; resize: vertical; }
.form__row input:focus,
.form__row textarea:focus { border-color: var(--c-primary); }

/* Honeypot: off-canvas rather than display:none, because the bots worth
   catching skip display:none fields. */
.form__honey {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.notice {
  margin: var(--s-4) var(--s-4) 0;
  padding: var(--s-3) var(--s-4);
  font-size: var(--fs-md);
  border: 1px solid transparent;
  border-radius: var(--r-md);
}
.notice--ok {
  color: var(--c-open-fg);
  background: var(--c-open-bg);
  border-color: var(--c-open);
}
.notice--error {
  color: var(--c-urgent-fg);
  background: var(--c-urgent-bg);
  border-color: var(--c-urgent);
}
/* .notice--error renders as a <ul>, so put the markers back. */
ul.notice--error { list-style: disc; padding-left: 2rem; }


/* ==========================================================================
   17. SUBSCRIBE
   ========================================================================== */

.card--subscribe { border-top: 3px solid var(--c-accent); }

.subscribe {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4) var(--s-4);
}
.subscribe input {
  flex: 1 1 10rem;
  min-width: 0;
  min-height: var(--tap);
  padding: 0 var(--s-3);
  font-size: var(--fs-base);
  background: var(--c-surface);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-md);
}
.subscribe button {
  flex: 0 0 auto;
  min-height: var(--tap);
  padding: 0 var(--s-4);
  font-size: var(--fs-md);
  font-weight: 700;
  color: #fff;
  background: var(--c-accent);
  border-radius: var(--r-md);
}
.subscribe button[disabled] { opacity: 0.6; cursor: progress; }

/* Always rendered, but empty until app.js writes a message, so it must not
   reserve height — an empty line here would push the sidebar ad down. */
.subscribe__msg {
  flex: 1 0 100%;
  font-size: var(--fs-sm);
  color: var(--c-muted);
}
.subscribe__msg:empty { display: none; }
.subscribe__msg.is-ok { color: var(--c-open-fg); font-weight: 600; }
.subscribe__msg.is-error { color: var(--c-urgent-fg); font-weight: 600; }


/* ==========================================================================
   17b. WHATSAPP CHANNEL
   ========================================================================== */

/* WhatsApp green, hard-coded rather than tokenised: it is the brand's
   colour, not ours, and it must not follow a theme change. */
.wa {
  display: flex;
  gap: var(--s-3);
  margin-top: var(--s-5);
  padding: var(--s-4);
  background: linear-gradient(180deg, #f0fdf4, var(--c-surface));
  border: 1px solid #a7e3bd;
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
}
.wa__icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  color: #fff;
  background: #25d366;
  border-radius: 50%;
}
.wa__copy { flex: 1; min-width: 0; }
.wa__title {
  font-size: var(--fs-lg);
  font-weight: 800;
  line-height: var(--lh-tight);
  color: var(--c-text);
}
.wa__text {
  margin-top: var(--s-1);
  font-size: var(--fs-sm);
  color: var(--c-text-2);
}
.wa__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  margin-top: var(--s-3);
  padding: 0 var(--s-5);
  font-size: var(--fs-md);
  font-weight: 800;
  color: #fff;
  background: #25d366;
  border-radius: var(--r-pill);
  box-shadow: 0 2px 0 #128c42;
}
.wa__btn:hover { color: #fff; background: #1eb858; }
.wa__btn:active { transform: translateY(1px); box-shadow: 0 1px 0 #128c42; }
.wa__note {
  margin-top: var(--s-2);
  font-size: var(--fs-xs);
  color: var(--c-muted);
}


/* ==========================================================================
   17c. ALERT POPUP — bottom sheet on a phone, centred card on desktop
   ========================================================================== */

.pop {
  position: fixed;
  inset: 0;
  z-index: var(--z-popup);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0;
  background: rgb(16 24 40 / 0.5);
}
.pop[hidden] { display: none; }

.pop__sheet {
  position: relative;
  width: 100%;
  max-width: 30rem;
  max-height: 88vh;
  overflow-y: auto;
  padding: var(--s-5) var(--s-4) calc(var(--s-4) + var(--safe-b));
  background: var(--c-surface);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--sh-3);
  animation: pop-up 0.22s ease-out;
}
@keyframes pop-up {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}
@keyframes pop-in {
  from { transform: scale(0.96); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}

.pop__close {
  position: absolute;
  top: var(--s-2);
  right: var(--s-2);
  display: grid;
  place-items: center;
  width: var(--tap);
  height: var(--tap);
  color: var(--c-muted);
  border-radius: 50%;
}
.pop__close:hover { color: var(--c-text); background: var(--c-surface-2); }

.pop__title {
  padding-right: var(--s-6);
  font-size: var(--fs-xl);
  font-weight: 800;
  line-height: var(--lh-tight);
}
.pop__text {
  margin-top: var(--s-2);
  font-size: var(--fs-sm);
  color: var(--c-text-2);
}

/* The bare WhatsApp block inside the sheet keeps the icon and button but
   loses the panel chrome — a card inside a card reads as a mistake. */
.pop__wa { margin-top: var(--s-4); display: flex; gap: var(--s-3); }
.pop__wa .wa__btn { display: flex; width: 100%; }

/* "or" divider with rules either side. */
.pop__or {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-4);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--c-muted);
}
.pop__or::before,
.pop__or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--c-border);
}

.pop__sheet .subscribe { padding: var(--s-3) 0 0; }

.pop__later {
  display: block;
  width: 100%;
  min-height: var(--tap);
  margin-top: var(--s-2);
  font-size: var(--fs-sm);
  color: var(--c-muted);
}
.pop__later:hover { color: var(--c-text); text-decoration: underline; }

/* AdSense does not permit a dialog on top of an ad unit, so the anchor
   slot steps aside for as long as the sheet is open. visibility, not
   display: the slot keeps its box, so nothing reflows and the impression
   already counted is not disturbed. */
.ad-anchor.is-behind-popup { visibility: hidden; }


/* ==========================================================================
   17d. MOCK TESTS — in-article block, hub cards, numbered steps
   ========================================================================== */

/* Accent (navy) rather than primary (red): red is reserved for deadlines
   and urgency on this site, and a practice test is neither. The left bar
   marks it as a different kind of thing from the panels around it. */
.mock {
  margin-top: var(--s-5);
  padding: var(--s-4);
  background: linear-gradient(180deg, var(--c-accent-50), var(--c-surface));
  border: 1px solid var(--c-border);
  border-left: 4px solid var(--c-accent);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
}
.mock__head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
}
.mock__ico {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  color: #fff;
  background: var(--c-accent);
  border-radius: var(--r-lg);
}
.mock__kicker {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-accent);
}
.mock__title {
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: 1.25;
}
.mock__subject {
  margin-top: var(--s-3);
  font-size: var(--fs-sm);
  color: var(--c-muted);
}
.mock__points {
  margin: var(--s-3) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-sm);
}
.mock__points li {
  position: relative;
  padding-left: var(--s-5);
  margin-top: var(--s-2);
}
/* Tick drawn in CSS — three list items do not justify an SVG each. */
.mock__points li::before {
  content: "";
  position: absolute;
  top: 0.3em;
  left: 2px;
  width: 0.5rem;
  height: 0.3rem;
  border: solid var(--c-accent);
  border-width: 0 0 2px 2px;
  transform: rotate(-45deg);
}
.mock__btn {
  display: block;
  margin-top: var(--s-4);
  padding: var(--s-3) var(--s-4);
  min-height: var(--tap);
  font-size: var(--fs-md);
  font-weight: 700;
  text-align: center;
  color: #fff;
  background: var(--c-accent);
  border-radius: var(--r-md);
  box-shadow: 0 2px 0 var(--c-accent-600);
}
.mock__btn:hover { background: var(--c-accent-600); }
.mock__btn:active { transform: translateY(2px); box-shadow: none; }
.mock__note {
  margin-top: var(--s-2);
  font-size: var(--fs-xs);
  color: var(--c-muted);
  text-align: center;
}
.mock__note a { color: var(--c-accent); font-weight: 600; text-decoration: underline; }

/* Hub cards. Equal-height grid rather than flex rows — ten cards with
   subject lines of different lengths is exactly where ragged boxes show
   up, and the button has to sit on the same baseline in every card. */
.mockgrid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-3);
}
.mockcard {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: var(--s-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
}
.mockcard__title {
  font-size: var(--fs-md);
  font-weight: 700;
  line-height: 1.3;
}
.mockcard__subject {
  flex: 1 1 auto;
  font-size: var(--fs-sm);
  color: var(--c-muted);
}
.mockcard__btn {
  align-self: start;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: var(--s-2) var(--s-4);
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--c-accent);
  background: var(--c-accent-50);
  border: 1px solid var(--c-accent);
  border-radius: var(--r-pill);
}
.mockcard__btn:hover { color: #fff; background: var(--c-accent); }
.mockcard__btn:active { transform: scale(0.97); }

/* Numbered steps, used by the hub's "how it works" panel. */
.steps {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}
.steps li {
  position: relative;
  padding: 0 0 var(--s-3) var(--s-6);
  font-size: var(--fs-sm);
  counter-increment: step;
}
.steps li:last-child { padding-bottom: 0; }
.steps li::before {
  content: counter(step);
  position: absolute;
  top: 0;
  left: 0;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: #fff;
  background: var(--c-accent);
  border-radius: 50%;
}


/* ==========================================================================
   18. PAGE HEAD / CRUMBS / PAGER
   ========================================================================== */

.page-head { padding: var(--s-4) 0 var(--s-3); }
.page-head__title {
  font-size: clamp(1.25rem, 5vw, 1.75rem);
  letter-spacing: -0.01em;
}
.page-head__text {
  margin-top: var(--s-2);
  font-size: var(--fs-md);
  color: var(--c-text-2);
}
.page-head__count {
  margin-top: var(--s-1);
  font-size: var(--fs-sm);
  color: var(--c-muted);
}

.crumbs {
  background: var(--c-surface);
  border-bottom: 1px solid var(--c-border);
  font-size: var(--fs-sm);
}
.crumbs__list {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 2.25rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  white-space: nowrap;
}
.crumbs__list::-webkit-scrollbar { display: none; }
.crumbs__list li { display: flex; align-items: center; gap: var(--s-2); }
.crumbs__list li + li::before {
  content: "/";
  color: var(--c-border-strong);
}
.crumbs__list [aria-current] { color: var(--c-muted); }

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  margin: var(--s-5) 0;
}
.pager__arrow,
.pager__list a,
.pager__list .is-current,
.pager__gap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--tap);
  min-height: var(--tap);
  padding: 0 var(--s-3);
  font-size: var(--fs-md);
  font-weight: 700;
  border-radius: var(--r-md);
}
.pager__arrow,
.pager__list a {
  color: var(--c-link);
  background: var(--c-surface);
  border: 1px solid var(--c-border-strong);
}
.pager__arrow:hover,
.pager__list a:hover { background: var(--c-primary-50); }
.pager__list { display: flex; flex-wrap: wrap; gap: var(--s-1); }
.pager__list .is-current {
  color: #fff;
  background: var(--c-primary);
  border: 1px solid var(--c-primary);
}
.pager__gap { color: var(--c-muted); }


/* ==========================================================================
   19. ERROR PAGE
   ========================================================================== */

.error-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-6) var(--s-4);
  text-align: center;
}
.error-page__code {
  font-size: clamp(3rem, 18vw, 5rem);
  font-weight: 800;
  line-height: 1;
  color: var(--c-primary-100);
}
.error-page__title { font-size: var(--fs-xl); }
.error-page__text { font-size: var(--fs-md); color: var(--c-text-2); }
.error-page__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-2);
}


/* ==========================================================================
   20. FOOTER
   ========================================================================== */

.footer {
  margin-top: var(--s-6);
  color: rgb(255 255 255 / 0.82);
  background: var(--c-primary-700);
}
.footer a { color: rgb(255 255 255 / 0.82); }
.footer a:hover { color: #fff; }

.footer__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-5);
  padding-block: var(--s-6);
}
.footer__col ul { display: grid; gap: var(--s-1); }
.footer__col li a {
  display: block;
  padding: var(--s-1) 0;
  min-height: 1.75rem;
  font-size: var(--fs-md);
}

.footer__title {
  margin-bottom: var(--s-2);
  font-size: var(--fs-md);
  color: #fff;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.brand--footer { color: #fff; }
.brand--footer strong { font-size: var(--fs-lg); font-weight: 800; }
.footer__about {
  margin-top: var(--s-3);
  font-size: var(--fs-md);
  line-height: 1.6;
}
.footer__social {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-3);
}
.footer__social a {
  display: inline-flex;
  align-items: center;
  min-height: 2.25rem;
  padding: 0 var(--s-3);
  font-size: var(--fs-sm);
  font-weight: 600;
  border: 1px solid rgb(255 255 255 / 0.25);
  border-radius: var(--r-pill);
}

.footer__bottom {
  padding-block: var(--s-4);
  border-top: 1px solid rgb(255 255 255 / 0.14);
  font-size: var(--fs-sm);
}
.footer__bottom-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
}
.footer__legal ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-4);
}
.footer__legal li a { min-height: 2rem; display: inline-flex; align-items: center; }
.footer__copy { color: rgb(255 255 255 / 0.7); }
.footer__disclaimer {
  max-width: var(--wrap);
  margin: var(--s-3) auto 0;
  padding-inline: var(--gutter);
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: rgb(255 255 255 / 0.55);
}


/* ==========================================================================
   21. FIXED ELEMENTS — z-index and offset scheme
   ==========================================================================

   Three elements are pinned to the bottom of a mobile viewport and must
   never overlap, because a .sticky-cta sitting on top of .ad-anchor is an
   AdSense policy violation (obscured ad / accidental clicks).

   Stacked from the bottom edge upward:

       .ad-anchor     bottom: 0                        z 80
       .bottom-nav    bottom: 0, lifted by --anchor-lift   z 60
       .sticky-cta    bottom: bottom-nav height, lifted    z 70
       .header        top: 0 (sticky)                  z 90
       .pop           full-viewport sheet              z 100
       .skip-link     on focus                         z 999

   z-index runs the opposite way to the geometry on purpose: the ad has the
   highest of the three so nothing can ever be painted over it, and the CTA
   outranks the nav because they share an edge during the lift transition.

   The lift uses `transform`, not `bottom`. Shifts caused by a transform
   change are excluded from the Layout Instability spec, so revealing the
   anchor ad mid-scroll moves the nav and CTA with zero CLS cost. Changing
   `bottom` instead would score a real shift on every pageview that gets an
   anchor ad, which is most of them.

   --anchor-lift is 0px in :root and is set to --ad-anchor-h by app.js
   adding .has-anchor-ad to <body>; it inherits down to all three elements.
   ========================================================================== */

body {
  /* Clears the fixed bottom nav, plus the anchor ad once it exists. This
     padding grows at the end of the document, below everything visible, so
     it never shifts content. */
  padding-bottom: calc(var(--bottom-nav-h) + var(--anchor-lift) + var(--safe-b));
}
/* :has() rather than a JS-added class: the CTA is server-rendered, so the
   reservation is correct on first paint. Browsers without :has() lose only
   the last 56px of scroll room. */
body:has(.sticky-cta) {
  padding-bottom: calc(
    var(--bottom-nav-h) + var(--sticky-cta-h) + var(--anchor-lift) + var(--safe-b)
  );
}
body.has-anchor-ad { --anchor-lift: var(--ad-anchor-h); }

.bottom-nav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-bottom-nav);
  display: flex;
  height: calc(var(--bottom-nav-h) + var(--safe-b));
  padding-bottom: var(--safe-b);
  background: var(--c-surface);
  border-top: 1px solid var(--c-border);
  box-shadow: 0 -2px 10px rgb(16 24 40 / 0.08);
  transform: translateY(calc(-1 * var(--anchor-lift)));
  transition: transform 0.2s ease;
}
.bottom-nav a {
  flex: 1 1 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 0;
  /* Whole cell is the tap target; 5 cells across a 320px screen is 64px
     each, comfortably over the 44px floor. */
  padding: var(--s-1) 2px;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--c-muted);
}
.bottom-nav a span {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* App-style active state. A colour change alone is easy to miss on a
   cheap LCD in daylight, which is most of this audience, so the current
   tab also gets a tinted pill behind the icon and a bar on the top edge
   — the pattern every Android app uses, so it needs no learning. */
.bottom-nav a::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 0;
  height: 3px;
  background: var(--c-primary);
  border-radius: 0 0 var(--r-pill) var(--r-pill);
  transform: translateX(-50%);
  transition: width 0.18s ease;
}
.bottom-nav a svg {
  padding: 3px 11px;
  border-radius: var(--r-pill);
  transition: background-color 0.18s ease;
  box-sizing: content-box;
}
.bottom-nav a.is-active { color: var(--c-primary); }
.bottom-nav a.is-active::before { width: 2.25rem; }
.bottom-nav a.is-active svg {
  stroke-width: 2.2;
  background: var(--c-primary-50);
}
/* Press feedback. Native apps answer a tap instantly; a web page that
   does nothing until the next document arrives feels broken on a slow
   connection, and these visitors are mostly on one. */
.bottom-nav a:active { transform: scale(0.94); }
.bottom-nav a:active svg { background: var(--c-primary-100); }

.sticky-cta {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--bottom-nav-h) + var(--safe-b));
  z-index: var(--z-sticky-cta);
  padding: var(--s-2) var(--s-3);
  background: var(--c-surface);
  border-top: 1px solid var(--c-border);
  box-shadow: 0 -2px 10px rgb(16 24 40 / 0.1);
  transform: translateY(calc(-1 * var(--anchor-lift)));
  transition: transform 0.2s ease;
}
.sticky-cta a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  min-height: var(--tap);
  font-size: var(--fs-base);
  font-weight: 800;
  color: #fff;
  background: var(--c-open);
  border-radius: var(--r-md);
}
.sticky-cta a small { font-size: var(--fs-xs); font-weight: 600; opacity: 0.9; }

/* Revealed by app.js after ~25% scroll depth. :not([hidden]) rather than a
   plain .ad-anchor rule so the `hidden` attribute keeps working without an
   !important. */
.ad-anchor:not([hidden]) {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-ad-anchor);
  display: flex;
  align-items: center;
  min-height: calc(var(--ad-anchor-h) + var(--safe-b));
  padding-bottom: var(--safe-b);
  background: var(--c-surface);
  border-top: 1px solid var(--c-border-strong);
}
.ad-anchor .adsbygoogle { flex: 1 1 auto; }

/* Positioned fully outside the ad box, as a tab above its right edge:
   nothing may be painted over an ad unit. 44x30 rather than 44x44 for the
   same reason — a taller button would have to start overlapping the ad. */
.ad-anchor__close {
  position: absolute;
  top: -30px;
  right: 0;
  width: 44px;
  height: 30px;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--c-text-2);
  background: var(--c-surface);
  border: 1px solid var(--c-border-strong);
  border-bottom: 0;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}


/* ==========================================================================
   22. ADS — presentation only; all sizing is in critical.css
   ========================================================================== */

/* In-feed units sit between real job rows, so they get a visible frame and
   the label is pinned left. Anything less and the unit reads as content. */
.ad--feed_inline {
  margin: 0;
  padding: var(--s-3) var(--s-4);
  background: var(--c-surface-2);
  border-top: 1px solid var(--c-border);
  border-bottom: 1px solid var(--c-border);
}

/* Collapse a slot AdSense could not fill. Without this the reserved
   280px box and its "Advertisement" label stay on screen with nothing in
   them, which is exactly what it looks like — a broken page — and on a
   phone it costs a whole screen of scrolling. Google sets
   data-ad-status="unfilled" itself, so this is reading its own signal
   rather than second-guessing the auction. The height is still reserved
   at first paint, so collapsing later costs no CLS above the fold. */
.ad:has(> .adsbygoogle[data-ad-status="unfilled"]),
.ad:has(> .ad__box > .adsbygoogle[data-ad-status="unfilled"]) { display: none; }

.ad--multiplex { margin-block: var(--s-6); }
.ad--sidebar_top { margin-top: 0; }
.ad--sidebar_sticky { margin-bottom: 0; }

/* Centre the in-article units inside the panel column. */
.ad--article_top,
.ad--article_mid,
.ad--article_bottom { max-width: 100%; }


/* ==========================================================================
   23. BREAKPOINTS
   ========================================================================== */

/* ----------------------------------------------- phone-only app shell ---
   Below the tablet breakpoint the page is treated as an app screen: one
   column, edge-to-edge cards, and anything that would wrap into a tall
   ragged block becomes a swipeable rail instead. */
@media (max-width: 767px) {
  /* Five qualification chips wrapped into three rows pushed the
     notification board — the thing people came for — off the first
     screen. As a rail they cost one row and gain a swipe affordance,
     which is the gesture an app user reaches for anyway. The negative
     margin lets the rail bleed to both edges so it is obvious there is
     more to the right. */
  .hero__chips {
    flex-wrap: nowrap;
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: var(--gutter);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
  }
  .hero__chips::-webkit-scrollbar { display: none; }
  .hero__chips .chip {
    flex: 0 0 auto;
    scroll-snap-align: start;
  }

  .hero { padding: var(--s-4) 0 var(--s-3); }
}

/* Narrow phones: a status badge parked on the right edge leaves barely
   15rem for the title and meta, so it moves to its own line under the
   text. Every row then breaks at the same width and the list reads as a
   stack of equal cards. */
@media (max-width: 479px) {
  .row { grid-template-columns: minmax(0, 1fr); }
  .row__link { padding-bottom: var(--s-2); }
  .badge {
    grid-column: 1;
    justify-self: start;
    margin: 0 var(--s-4) var(--s-3) calc(var(--s-4) + 3.5rem + var(--s-3));
  }
  .row:not(.row--thumbed) .badge { margin-left: var(--s-4); }
}

@media (min-width: 480px) {
  .grid { grid-template-columns: 1fr 1fr; }
  .grid > .row { border-right: 1px solid var(--c-border); }
  .grid > .row:nth-child(2n) { border-right: 0; }
}

@media (min-width: 768px) {
  :root { --gutter: 1.25rem; }

  /* Single-row header: brand, search, langs. */
  .header__row { flex-wrap: nowrap; gap: var(--s-4); }
  .header__burger { display: none; }
  .brand { order: 1; flex: 0 1 auto; }
  .search { order: 2; flex: 1 1 20rem; max-width: 30rem; }
  .langs { order: 3; }

  .nav__list { justify-content: flex-start; }
  .nav__list a { padding: 0 var(--s-4); }

  /* Chips can relax once there is a pointer or a wider screen. */
  .chip { min-height: 2.25rem; }
  .chip--lg { min-height: 2.75rem; }

  .alert-strip__inner { min-height: 2.75rem; }

  .hero { padding: var(--s-6) 0 var(--s-5); }

  /* All eight on one line once there is room for readable labels. */
  .launch { grid-template-columns: repeat(8, 1fr); }
  .launch__label { font-size: var(--fs-xs); }

  /* Ten cards, two columns — an even split, so no orphan row. */
  .mockgrid { grid-template-columns: repeat(2, 1fr); }

  .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  .kv__row { grid-template-columns: 12rem 1fr; }

  .toc ol { columns: 2; column-gap: var(--s-5); }
  .toc li { break-inside: avoid; }

  .article__head { padding-top: var(--s-5); }

  .links li { flex-wrap: nowrap; }

  /* Once there is room, the sheet becomes a centred card — a slab stuck
     to the bottom edge of a 1400px window looks like a cookie bar. */
  .pop { align-items: center; padding: var(--s-4); }
  .pop__sheet {
    border-radius: var(--r-lg);
    animation-name: pop-in;
  }

  .footer__grid { grid-template-columns: repeat(2, 1fr); }
  .footer__col--brand { grid-column: 1 / -1; }

  /* Mobile-only furniture. The desktop apply button lives in the overview
     panel (.cta) and the anchor slot is replaced by the sidebar units. */
  .bottom-nav,
  .sticky-cta { display: none; }
  body,
  body:has(.sticky-cta) { padding-bottom: 0; }
}

@media (min-width: 1024px) {
  /* On a mouse the nav stops being a scroller and wraps instead. A
     horizontal scroll bar nobody can see is fine under a thumb, but on
     desktop it silently truncated the last item — "Exam Calenda" with
     the r missing reads as a broken page, not as "scroll me". */
  .nav__list {
    flex-wrap: wrap;
    overflow-x: visible;
    white-space: normal;
  }

  .layout {
    grid-template-columns: minmax(0, 1fr) var(--sidebar-w);
    gap: var(--s-5);
    align-items: start;
  }

  .sidebar {
    display: block;
    min-width: 0;
    position: sticky;
    top: 0;
  }

  /* The hero illustration finally has somewhere to go: below this width
     the copy already fills the line and the art would only push the board
     down. Declared as a background, not an <img>, so the file is never
     requested on the phones that make up most of the traffic. */
  .hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 26rem;
    gap: var(--s-5);
    align-items: center;
  }
  .hero__art {
    display: block;
    aspect-ratio: 2 / 1;
    background: url("../img/hero-560.webp") center / contain no-repeat;
  }

  /* Three-column notification board — the layout these visitors already
     know how to read. Each column scrolls on its own so one long column
     cannot stretch the page to 4000px. */
  .board { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .board__body {
    max-height: 27rem;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .footer__grid { grid-template-columns: 1.4fr repeat(4, 1fr); }
  .footer__col--brand { grid-column: auto; }

  .ad-anchor:not([hidden]) { display: none; }
  body.has-anchor-ad { --anchor-lift: 0px; }
}

/* Retina copy of the hero art, scoped to the same width so a high-DPI
   phone still downloads nothing. */
@media (min-width: 1024px) and (min-resolution: 1.5dppx) {
  .hero__art { background-image: url("../img/hero-1120.webp"); }
}

@media (min-width: 1280px) {
  :root { --sidebar-w: 336px; }
  .article__title { font-size: 2rem; }
}

/* Fine pointer: tap targets can shrink, hover affordances become useful. */
@media (hover: hover) and (pointer: fine) {
  .row__link,
  .exam-row,
  .chip,
  .btn,
  .links__btn { transition: background-color 0.15s ease, border-color 0.15s ease; }
}


/* ==========================================================================
   24. DARK / MOTION / PRINT
   ========================================================================== */

@media (prefers-color-scheme: dark) {
  /* Tokens are already swapped in critical.css; these are the few places
     with a literal colour or a contrast that needs easing. */
  .block,
  .card,
  .panel { box-shadow: none; }
  .bottom-nav,
  .sticky-cta { box-shadow: 0 -2px 10px rgb(0 0 0 / 0.5); }
  .share__btn--wa { background: #17813d; border-color: #17813d; }
  .share__btn--tg { background: #1f6f9e; border-color: #1f6f9e; }

  /* The WhatsApp panel's pale green wash is unreadable against a dark
     surface, so only the brand green on the icon and button survives. */
  .wa {
    background: var(--c-surface);
    border-color: #1f6f45;
    box-shadow: none;
  }
  .wa__icon,
  .wa__btn { background: #17813d; }
  .wa__btn { box-shadow: 0 2px 0 #0d5527; }
  .wa__btn:hover { background: #1d9a4b; }
  /* Same reason as .wa — the pale navy wash goes muddy on a dark
     surface, so the accent stays only on the icon, bar and button. */
  .mock {
    background: var(--c-surface);
    box-shadow: none;
  }
  .mockcard { box-shadow: none; }
  .pop { background: rgb(0 0 0 / 0.65); }
  .pop__sheet { box-shadow: 0 -8px 24px rgb(0 0 0 / 0.6); }
  .error-page__code { color: var(--c-primary-100); }
  .footer { background: #0c1522; }
}

@media (prefers-reduced-motion: reduce) {
  /* Near-zero rather than `none` so transitionend listeners still fire. */
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* The universal selector above does not reach html's own computed
     scroll-behavior in every engine, so it is turned off by name too. */
  html { scroll-behavior: auto !important; }
}

@media print {
  /* Job notifications get printed and taken to cyber cafes. Strip
     everything that is not the notification itself. */
  .header,
  .nav,
  .alert-strip,
  .crumbs,
  .sidebar,
  .bottom-nav,
  .sticky-cta,
  .ad,
  .ad-anchor,
  .share,
  .wa,
  .mock,
  .pop,
  .footer,
  .pager,
  .toc { display: none; }

  body { padding: 0; background: #fff; color: #000; font-size: 12pt; }
  .layout { display: block; }
  .panel,
  .block { border: 1px solid #999; box-shadow: none; break-inside: avoid; }
  .table-scroll { overflow: visible; }
  a[href]::after { content: " (" attr(href) ")"; font-size: 9pt; color: #444; }
  .row__link[href]::after { content: ""; }
}
