/* ══════════════════════════════════════════════════════════════════════════
   THE DIRECTORY GRID  ·  /colleges
   ══════════════════════════════════════════════════════════════════════════

   Replaced the text list on 2026-09-07, when the record went from 60 colleges to
   1,458 and the old page became 564 KB of unbroken links. At that size the
   problem stopped being aesthetic: nobody shortlists from a list of 1,115 names
   under one heading, and PRODUCT.md names a low-cost Android on mobile data as
   the primary reading device.

   Three things carry it. A mark per college so the eye has something to catch, a
   filter so the list can be cut down, and `content-visibility` so a phone does
   not lay out cards it cannot see.

   ⚠️ **THIS IS HEAVIER THAN WHAT IT REPLACED AND THAT IS A TRADE, NOT A WIN.**
   The same 1,458 colleges went from 564 KB to 969 KB of HTML, 89 KB gzipped,
   because every row gained a tile, a wrapper and a status flag. Two rounds of
   trimming got it down from 1,069 KB by deleting `data-name` and `data-place`,
   which duplicated text already in the card; the script reads that text instead.
   What is bought is a scannable grid and a working filter. What protects the
   phone is `content-visibility` and lazy images, which cut LAYOUT and DECODE
   rather than bytes — the cost that actually stalls a cheap Android on a list
   this long. If the byte count has to come down further, the answer is fewer
   cards on first paint, not a thinner card.

   ⚠️ Its own stylesheet rather than more of `ice-colleges.css`, which is shared
   with the college PAGE. The directory is the only surface with 1,460 of
   anything and its performance rules have no business on a profile.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── the tile ──────────────────────────────────────────────────────────────
   Logo and monogram share one plate at one size. 153 of 1,460 colleges publish a
   logo, so a grid that styles the two differently reads as broken on the other
   1,307; the monogram is the design and the logo replaces its contents. */

.ctile {
  --ctile-size: 52px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  inline-size: var(--ctile-size);
  block-size: var(--ctile-size);
  border-radius: 12px;
  background: var(--ctile-plate, #eef1f6);
  box-shadow: inset 0 0 0 1px var(--ctile-edge, rgb(15 23 42 / 8%));
  overflow: hidden;
}

.ctile[data-size="128"] { --ctile-size: 72px; border-radius: 16px; }

.ctile__img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: contain;
  /* ⚠️ `contain`, never `cover`. Many of these marks are wordmarks three times
     wider than they are tall, and cropping one square makes it a different logo.
     The padding keeps a full-bleed favicon off the plate's rounded corner. */
  padding: 6px;
}

.ctile__mono {
  font-size: calc(var(--ctile-size) * 0.32);
  font-weight: 650;
  letter-spacing: 0.01em;
  line-height: 1;
  color: var(--ctile-ink, #334155);
}

/* The province tints, all at one lightness so no province reads as more
   important than another, with the ink the same hue darkened rather than grey. */
.ctile[data-tint="koshi"]        { --ctile-plate: #e8f0fb; --ctile-ink: #234b86; --ctile-edge: rgb(35 75 134 / 14%); }
.ctile[data-tint="madhesh"]      { --ctile-plate: #fdeee9; --ctile-ink: #9a4526; --ctile-edge: rgb(154 69 38 / 14%); }
.ctile[data-tint="bagmati"]      { --ctile-plate: #eaf1ea; --ctile-ink: #2f5c3a; --ctile-edge: rgb(47 92 58 / 14%); }
.ctile[data-tint="gandaki"]      { --ctile-plate: #f3edfa; --ctile-ink: #58407f; --ctile-edge: rgb(88 64 127 / 14%); }
.ctile[data-tint="lumbini"]      { --ctile-plate: #fbf1e2; --ctile-ink: #85601f; --ctile-edge: rgb(133 96 31 / 14%); }
.ctile[data-tint="karnali"]      { --ctile-plate: #e6f2f2; --ctile-ink: #1f5f61; --ctile-edge: rgb(31 95 97 / 14%); }
.ctile[data-tint="sudurpaschim"] { --ctile-plate: #f7ecf1; --ctile-ink: #7e3556; --ctile-edge: rgb(126 53 86 / 14%); }

html[data-theme="dark"] .ctile[data-tint="koshi"]        { --ctile-plate: #16233a; --ctile-ink: #9dc0f0; }
html[data-theme="dark"] .ctile[data-tint="madhesh"]      { --ctile-plate: #3a1f16; --ctile-ink: #f0b199; }
html[data-theme="dark"] .ctile[data-tint="bagmati"]      { --ctile-plate: #16291b; --ctile-ink: #9ed0ac; }
html[data-theme="dark"] .ctile[data-tint="gandaki"]      { --ctile-plate: #241c34; --ctile-ink: #c1abe6; }
html[data-theme="dark"] .ctile[data-tint="lumbini"]      { --ctile-plate: #322611; --ctile-ink: #e2c483; }
html[data-theme="dark"] .ctile[data-tint="karnali"]      { --ctile-plate: #122a2b; --ctile-ink: #91cfd0; }
html[data-theme="dark"] .ctile[data-tint="sudurpaschim"] { --ctile-plate: #2f1723; --ctile-ink: #e3a3c0; }
html[data-theme="dark"] .ctile                           { --ctile-edge: rgb(255 255 255 / 10%); }

/* ⚠️ THE UNTINTED DEFAULT NEEDS A DARK VALUE TOO, and it did not have one.
   `data-tint` is the province, and the 171 hand-written and site-drafted records
   predate the province field: they carry a district and no province, so they
   fall through every tint rule to the base plate. In dark mode that left a
   near-white #eef1f6 square with near-black initials sitting in a row of dark
   tiles. Caught by reading the computed background on a dark render, not by
   looking at a screenshot of the tinted ones. */
html[data-theme="dark"] .ctile {
  --ctile-plate: #23262c;
  --ctile-ink: #c3c9d4;
}

html[data-theme="dark"] .ctile__img {
  /* A dark-on-transparent wordmark vanishes on a dark plate. A light backing
     keeps it legible without inverting the mark, which would misrepresent the
     institution's own colours. */
  background: rgb(255 255 255 / 92%);
  border-radius: 8px;
}

/* ── the filter ────────────────────────────────────────────────────────────
   Hidden until the script claims it with `data-ready`. A control that does
   nothing when clicked is a worse failure than an absent one, and every card is
   server-rendered so the page is complete without it. */

/* ⚠️ The one thing a reader without JavaScript is told, and it had no rule
   anywhere in the nine stylesheets this page loads. It rendered as an unstyled
   paragraph immediately under the lede, in the same size and colour, so the
   sentence that explains why there are no controls read as one more line of the
   introduction. It is the only surface on this page that has to be noticed by
   somebody who cannot be shown anything else. */
.directory-noscript {
  margin: 0 0 1.5rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--ice-color-border, rgb(15 23 42 / 14%));
  border-radius: var(--pf-radius, 14px);
  background: var(--ice-color-surface, #fff);
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ice-color-text);
}

.cdir-filter { display: none; }

.cdir-filter[data-ready="true"] {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
  align-items: end;
  margin: 0 0 2rem;
  padding: 1rem;
  border-radius: var(--pf-radius, 14px);
  background: var(--edu-glass, rgb(255 255 255 / 60%));
  box-shadow: inset 0 0 0 1px var(--edu-glass-edge, rgb(255 255 255 / 75%)),
              0 1px 2px rgb(12 26 56 / 5%),
              0 10px 28px -20px rgb(12 26 56 / 30%);
}

.cdir-filter__field { display: grid; gap: 6px; min-inline-size: 0; }

.cdir-filter__field label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--ice-color-text-muted);
}

.cdir-filter input,
.cdir-filter select {
  inline-size: 100%;
  min-inline-size: 0;
  padding: 0.6rem 0.75rem;
  font: inherit;
  font-size: 0.9375rem;
  color: var(--ice-color-text);
  background: var(--ice-color-bg);
  border: 1px solid var(--ice-color-border, rgb(15 23 42 / 14%));
  border-radius: 10px;
  transition: border-color 140ms var(--edu-ease, ease), box-shadow 140ms var(--edu-ease, ease);
}

.cdir-filter input:focus-visible,
.cdir-filter select:focus-visible {
  outline: none;
  border-color: var(--ice-color-action, #2F5BA8);
  box-shadow: 0 0 0 3px rgb(47 91 168 / 18%);
}

.cdir-filter input::placeholder { color: var(--ice-color-text-muted); opacity: 1; }

.cdir-filter__count {
  margin: 0;
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
  color: var(--ice-color-text-muted);
}

@media (min-width: 720px) {
  .cdir-filter[data-ready="true"] {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1.4fr) auto;
  }
  .cdir-filter__count { padding-block-end: 0.7rem; white-space: nowrap; }
}

/* ── groups and grid ─────────────────────────────────────────────────────── */

.cdir { display: grid; gap: 2.5rem; }
.cdir__group[hidden] { display: none; }

.cdir__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  padding-block-end: 0.6rem;
  border-block-end: 1px solid var(--ice-color-border, rgb(15 23 42 / 12%));
}

.cdir__head h2 { margin: 0; font-size: 1.125rem; letter-spacing: -0.01em; }

.cdir__count {
  margin: 0;
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
  color: var(--ice-color-text-muted);
}

.cdir__rule {
  margin: 0.75rem 0 0;
  max-inline-size: 68ch;
  font-size: 0.875rem;
  color: var(--ice-color-text-muted);
}

.cdir__grid {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}

/* ⚠️ TWO COLUMNS ONLY WHERE A NAME STILL FITS IN ONE, and 640px was too early.
   The card spends its width before the name gets any: a 52px mark, two 12px
   gaps and a right-hand column that is 75px wide on the ~1,366 cards carrying
   the "Unchecked" flag. At 640px in two columns that leaves the name about
   114px — fourteen characters a line — where the same card at 390px in ONE
   column gives it about 173px. Two columns from 768px give it about 162px,
   which is the phone's own measure. D-05: one column where two would make names
   unreadable.

   ⚠️ There is no three-column rule any more, and deleting it did not change the
   desktop. `edulibrary-brand.css` puts the filter in a 280px rail and holds this
   grid at two columns from 1100px with `body.edu.page-directory .cdir__grid`,
   which this file cannot outrank. So `@media (min-width: 1080px)` could only
   ever fire between 1080 and 1099px: a twenty-pixel band in which the name
   column was the narrowest anywhere on the site and then widened again at
   1100px. */
@media (min-width: 768px) { .cdir__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ── the card ────────────────────────────────────────────────────────────── */

.ccard {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  min-inline-size: 0;
  padding: 10px 12px;
  border-radius: var(--pf-radius, 14px);
  background: var(--ice-color-bg);
  box-shadow: inset 0 0 0 1px var(--ice-color-border, rgb(15 23 42 / 10%));
  transition: box-shadow 160ms var(--edu-ease, ease), transform 160ms var(--edu-ease, ease);
  /* ⚠️ PER CARD, NOT ON `.cdir__grid`, and that is the difference between an
     estimate and a lie. `content-visibility: auto` on the grid skipped ONE
     UNIVERSITY AT A TIME and reserved `contain-intrinsic-size` once for the
     whole of it: Tribhuvan's 1,115 cards stood in the scrollbar as 76 pixels
     instead of roughly 100,000, so the document claimed to be about 1,500px
     tall and grew by two orders of magnitude under the reader's thumb as each
     group came into view. Dragging the scrollbar to "the end" never reached the
     end, which on a phone is the whole way this page is read.

     It also bought nothing where it was needed. A skipped element is one that is
     ENTIRELY off screen, so the group actually being read was laid out in full —
     all 1,115 cards of it — which is exactly the work the property was added to
     avoid. On the card the reserved height is one card's height, the running
     total is right within a few per cent, and the cards below the fold INSIDE
     the group being read are the ones that get skipped.

     100px is the box: 16px padding twice plus a 1px border twice, over a body of
     two 23px name lines, a 2px gap and a 19px meta line. `auto` replaces it with
     the real height once a card has rendered. */
  content-visibility: auto;
  contain-intrinsic-size: auto 100px;
}

.ccard.is-out { display: none; }

.ccard:hover,
.ccard:focus-within {
  box-shadow: inset 0 0 0 1px var(--ice-color-action, #2F5BA8),
              0 2px 4px rgb(12 26 56 / 6%),
              0 12px 24px -18px rgb(12 26 56 / 40%);
  transform: translateY(-1px);
}

@media (prefers-reduced-motion: reduce) {
  .ccard { transition: none; }
  .ccard:hover, .ccard:focus-within { transform: none; }
}

.ccard__body { min-inline-size: 0; display: grid; gap: 2px; }

.ccard__link {
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ice-color-text);
  text-decoration: none;
  /* ⚠️ THE NAME WRAPS. It was clamped to two lines with an ellipsis until this
     pass, on the argument that a card growing to fit the longest name leaves
     holes across its row. It does not: the grid sizes a row to its tallest card
     and every card in that row stretches with it, so the cost is a row that is
     one line taller, not a hole.

     What the clamp cost instead: 238 of the 1,537 names in the record are over
     40 characters and 47 are over 50, and two lines of the card's ~21-character
     measure hold about 40. Eight separate institutions are called "College of
     Natural Resources Management, <place>" — Bardibas, Dullu, Katari, Khajura,
     Madichaur, Pakhribas, Purunchaur and Tikapur — and they are identical for
     their first 48 characters. Clamped, all eight read "College of Natural
     Resources Management,…" and the only thing telling them apart is the
     district on the line below. Kathford, Kantipur, Gandaki, NIET and Western
     Advance each ship a second record that collides the same way.
     D-13: essential institution names must not be silently truncated. */
  overflow-wrap: break-word;
}

/* The whole card is the target so a thumb does not have to find the text, while
   the link keeps the accessible name to itself. */
.ccard__link::after { content: ''; position: absolute; inset: 0; border-radius: inherit; }
.ccard__link:hover { color: var(--ice-color-action, #2F5BA8); }
.ccard__link:focus-visible { outline: 2px solid var(--ice-color-action, #2F5BA8); outline-offset: 3px; }

.ccard__meta {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--ice-color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ccard__flag {
  position: relative;
  flex: 0 0 auto;
  align-self: flex-start;
  margin-block-start: 2px;
  padding: 2px 7px;
  font-size: 0.6875rem;
  font-weight: 600;
  border-radius: 999px;
  color: #92400e;
  background: rgb(180 83 9 / 10%);
}

html[data-theme="dark"] .ccard__flag { color: #fbbf24; background: rgb(251 191 36 / 12%); }

.cdir__empty {
  margin: 0;
  padding: 1.5rem;
  border-radius: var(--pf-radius, 14px);
  background: rgb(15 23 42 / 4%);
  color: var(--ice-color-text-muted);
  text-align: center;
}

html[data-theme="dark"] .cdir__empty { background: rgb(255 255 255 / 5%); }
