/* ===========================================================
   directory.css: /modules/, the module and feature directory.

   Loaded after platform.css, theme-oxford.css and content.css, so the hero,
   the .pf-board strip and the dark .final band all come from those and only
   the directory itself is styled here.

   Two things to know before editing:

   1. The filter is CSS only. Six radios plus "all" sit before .dir-tabs and
      .dir-groups as siblings, and the :checked rules below do the hiding.
      Every rule is keyed on an area id, so adding an area to $UB_DIR_AREAS
      means adding it to the three selector lists here as well. That is the
      price of not shipping JavaScript for something this simple, and it is
      worth paying: the page works with scripts blocked, which a directory a
      stranger lands on from a search result should.

   2. There are no separator lines between groups. A rule between two groups
      would still be drawn when the group above it is filtered out, because
      the adjacent-sibling selector matches on document order rather than on
      what is visible. Spacing and the heading rule carry the separation
      instead, so nothing can leave a stray line at the top of a filtered view.
   =========================================================== */

.dir-hero .sub { max-width: 68ch; }

/* Tighter than the standard 128px .sec: this is a tool, and the first row of
   the list should be close to the tabs that filter it. The 42 here plus the
   34px of cover padding inside .dir-tabs is the 76px of air the strip actually
   sits in. Change them together. */
.dir-sec { padding: 64px 0 96px; }

/* ── the filter ─────────────────────────────────────────────────────────── */

/* Off-screen, not display:none. The radios have to keep their place in the tab
   order so the group stays operable as one native radio group: tab to it,
   arrow keys to move, which is the behaviour a tab strip should have and the
   behaviour we get free by not building it out of buttons. */
.dir-radio {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* Seven square tiles on one row.

   Not sticky. It was, and at the old pill size that was worth the 66px it
   cost. A row of tiles this size would hold a quarter of a phone screen for
   the whole length of a 61 item list, which is more than a filter you use once
   is worth, so the row scrolls away with the page instead.

   The row is a 7 column grid at desktop widths and one horizontally scrolling
   line below 1100px, where a seventh of the viewport stops being a usable
   target. Either way it is one row, never two: nothing wraps. */
.dir-tabs {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 10px;
  padding-bottom: 30px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--rule);
}

/* aspect-ratio does the squaring, so the tile follows the column width at
   every viewport size without a media query per breakpoint. */
.dir-tab {
  aspect-ratio: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 1vw, 16px);
  padding: 14px 12px;
  text-align: center;
  font-family: var(--f-ui);
  font-size: var(--fs-md);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: 0.01em;
  color: var(--ink-2);
  background: var(--paper);
  border: 1px solid var(--rule);
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.dir-tab:hover { color: var(--ink); border-color: var(--ink); }

/* The icon is the tile now, so it is drawn large and fluid: it grows with the
   tile rather than sitting as a fixed mark in the middle of a big empty box.
   It inherits currentColor from the label, which is what flips it to paper on
   the selected tile. */
.dir-tab-ic {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--hazard);
  transition: color 120ms ease;
}
.dir-tab-ic svg {
  display: block;
  width: clamp(34px, 3.4vw, 52px);
  height: clamp(34px, 3.4vw, 52px);
}

/* Wraps to two lines inside the tile: "Safety and compliance" does not fit a
   square on one. balance keeps the two lines even rather than orphaning a
   word. */
.dir-tab-t {
  white-space: normal;
  text-wrap: balance;
  max-width: 100%;
  /* Two lines' worth whether the label needs them or not. Without it the tiles
     centre their own stack, so a one line label lifts its icon and drops its
     count a few pixels and the row reads as slightly crooked. */
  display: flex;
  align-items: center;
  min-height: 2.3em;
}

.dir-tab-n {
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
  transition: color 120ms ease;
}

/* "All" is last in the strip, and that is all it needs to be. It used to carry
   margin-left:auto to push it to the far end, which read as deliberate while
   the tabs fitted one row; at the larger size they wrap, and it left "All"
   stranded alone at the right of the second row. It now simply follows the
   last area. */

/* Selected. One rule, one selector per area: keep this list in step with
   $UB_DIR_AREAS. */
#dir-all:checked     ~ .dir-tabs [for="dir-all"],
#dir-money:checked   ~ .dir-tabs [for="dir-money"],
#dir-site:checked    ~ .dir-tabs [for="dir-site"],
#dir-safety:checked  ~ .dir-tabs [for="dir-safety"],
#dir-office:checked  ~ .dir-tabs [for="dir-office"],
#dir-subbies:checked ~ .dir-tabs [for="dir-subbies"],
#dir-assets:checked  ~ .dir-tabs [for="dir-assets"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
#dir-all:checked     ~ .dir-tabs [for="dir-all"] .dir-tab-n,
#dir-money:checked   ~ .dir-tabs [for="dir-money"] .dir-tab-n,
#dir-site:checked    ~ .dir-tabs [for="dir-site"] .dir-tab-n,
#dir-safety:checked  ~ .dir-tabs [for="dir-safety"] .dir-tab-n,
#dir-office:checked  ~ .dir-tabs [for="dir-office"] .dir-tab-n,
#dir-subbies:checked ~ .dir-tabs [for="dir-subbies"] .dir-tab-n,
#dir-assets:checked  ~ .dir-tabs [for="dir-assets"] .dir-tab-n {
  color: oklch(0.75 0 0);
}
#dir-all:checked     ~ .dir-tabs [for="dir-all"] .dir-tab-ic,
#dir-money:checked   ~ .dir-tabs [for="dir-money"] .dir-tab-ic,
#dir-site:checked    ~ .dir-tabs [for="dir-site"] .dir-tab-ic,
#dir-safety:checked  ~ .dir-tabs [for="dir-safety"] .dir-tab-ic,
#dir-office:checked  ~ .dir-tabs [for="dir-office"] .dir-tab-ic,
#dir-subbies:checked ~ .dir-tabs [for="dir-subbies"] .dir-tab-ic,
#dir-assets:checked  ~ .dir-tabs [for="dir-assets"] .dir-tab-ic {
  color: var(--paper);
}

/* Keyboard focus has to land on the label, because the input it belongs to is
   off-screen. Without this the whole strip is silently unusable by keyboard. */
#dir-all:focus-visible     ~ .dir-tabs [for="dir-all"],
#dir-money:focus-visible   ~ .dir-tabs [for="dir-money"],
#dir-site:focus-visible    ~ .dir-tabs [for="dir-site"],
#dir-safety:focus-visible  ~ .dir-tabs [for="dir-safety"],
#dir-office:focus-visible  ~ .dir-tabs [for="dir-office"],
#dir-subbies:focus-visible ~ .dir-tabs [for="dir-subbies"],
#dir-assets:focus-visible  ~ .dir-tabs [for="dir-assets"] {
  outline: 2px solid var(--hazard);
  outline-offset: 2px;
}

/* The filter itself. */
#dir-money:checked   ~ .dir-groups .dir-group:not([data-area="money"]),
#dir-site:checked    ~ .dir-groups .dir-group:not([data-area="site"]),
#dir-safety:checked  ~ .dir-groups .dir-group:not([data-area="safety"]),
#dir-office:checked  ~ .dir-groups .dir-group:not([data-area="office"]),
#dir-subbies:checked ~ .dir-groups .dir-group:not([data-area="subbies"]),
#dir-assets:checked  ~ .dir-groups .dir-group:not([data-area="assets"]) {
  display: none;
}

/* ── groups ─────────────────────────────────────────────────────────────── */

.dir-group { padding-top: 56px; }

/* Holds the heading, nothing else. The count that used to sit beside it went
   on 16 August 2026: the tab above already carries the same number, and saying
   it twice is one more thing to read for nothing. */
.dir-group-hd {
  padding-bottom: 18px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--rule);
}
.dir-group-hd h2 {
  margin: 0;
  font-family: var(--f-deck);
  font-weight: var(--fw-deck);
  /* --fs-h2, not --fs-h3. h3 floors at 22px and does not pass the 23px card
     title until 958px wide, so on every phone and most tablets the group
     heading read as smaller than the cards filed under it. h2 floors at 32px,
     which keeps the order right at every width. */
  font-size: var(--fs-h2);
  line-height: 1.02;
  letter-spacing: -0.03em;
  color: var(--ink);
  text-wrap: balance;
}
/* ── the cards ──────────────────────────────────────────────────────────── */

.dir-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
  grid-template-columns: minmax(0, 1fr);
}
.dir-grid > li { display: flex; }

.dir-card {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-content: start;
  gap: 0 16px;
  padding: 22px 24px 24px;
  border: 1px solid var(--rule);
  background: var(--paper);
  text-decoration: none;
  color: inherit;
  transition: border-color 120ms ease, background 120ms ease;
}
/* Every child is placed explicitly. .dir-card-go used to carry grid-row:1 and
   nothing else, which put it in the placement algorithm's definite-row pass,
   ahead of the auto-placed name, so it took column 1 and the arrow rendered to
   the left of the title. Definite row AND column on all three, no surprises. */
/* The title carries the page. At --fs-md it was the same size as the body
   copy around it and 61 of them read as a wall; at --fs-xl a name is legible
   at a glance and the eye can move down a column without reading. */
.dir-card-name {
  grid-column: 1;
  grid-row: 1;
  font-family: var(--f-display);
  font-weight: 700;
  font-size: var(--fs-xl);
  line-height: 1.2;
  letter-spacing: -0.018em;
  color: var(--ink);
  text-wrap: balance;
}
/* Up a step with the title, and out of --ink-mute into --ink-2: at 11:1 it is
   supporting text you can actually read rather than a grey caption. */
.dir-card-line {
  grid-column: 1 / -1;
  grid-row: 2;
  margin-top: 10px;
  font-family: var(--f-ui);
  font-size: var(--fs-md);
  line-height: 1.5;
  color: var(--ink-2);
  text-wrap: pretty;
}
.dir-card-go {
  grid-column: 2;
  grid-row: 1;
  align-self: start;
  font-size: var(--fs-lg);
  line-height: 1.35;
  color: var(--hazard);
  transition: transform 0.15s ease;
}

a.dir-card:hover { border-color: var(--ink); background: var(--paper-2); }
a.dir-card:hover .dir-card-go { transform: translateX(3px); }
a.dir-card:focus-visible { outline: 2px solid var(--hazard); outline-offset: 2px; }

/* Named but not yet linked. Lighter rule and a quieter name, so it reads as an
   entry in a list rather than as a link that failed. No badge and no label:
   this is a page we have not written yet, not a gap in the product, and
   captioning it as one would say something untrue. */
.dir-card.is-plain {
  border-color: var(--rule-soft);
  background: transparent;
}
.dir-card.is-plain .dir-card-name { color: var(--ink-2); font-weight: 500; }

/* ── wider screens ──────────────────────────────────────────────────────── */

/* Three columns at the top end, not four. The fourth column was worth having
   while the titles were 17.5px; at 23px it turns most names into three cramped
   lines, and a directory is read down a column rather than across a row. */
@media (min-width: 660px) {
  .dir-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
}
@media (min-width: 1180px) {
  .dir-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
  .dir-group { padding-top: 72px; }
}

/* ── phones ─────────────────────────────────────────────────────────────── */

/* Below this width a seventh of the viewport stops being a usable target, so
   the row keeps its tile size and scrolls instead. Deliberately wider than the
   900px breakpoint the rest of the page uses: the tiles run out of room before
   the cards do. */
@media (max-width: 1100px) {
  /* One scrolling line rather than seven slivers.

     Seven tiles across a 390px screen would be 50px each, which is neither
     giant nor tappable, so below 1100px the row keeps its tile size and
     scrolls sideways instead. It is still one row: nothing wraps at any
     width.

     Full-bleed, so the row runs to both edges of the phone and the last tile
     is not left hard against the frame at the end of the scroll. The partly
     visible tile at the right edge is the affordance that says it moves. */
  .dir-tabs {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    gap: 8px;
    margin-left: calc(var(--gutter) * -1);
    margin-right: calc(var(--gutter) * -1);
    padding-left: var(--gutter);
    padding-right: var(--gutter);
    padding-bottom: 24px;
  }
  .dir-tabs::-webkit-scrollbar { display: none; }
  .dir-tab {
    flex: 0 0 auto;
    width: clamp(132px, 38vw, 168px);
    font-size: var(--fs-sm);
    padding: 12px 10px;
  }
}

@media (max-width: 900px) {
  .dir-sec { padding: 34px 0 64px; }
  .dir-group { padding-top: 40px; }

  .dir-card { padding: 18px 18px 20px; }
}

@media (prefers-reduced-motion: reduce) {
  .dir-tab,
  .dir-card,
  .dir-card-go { transition: none; }
}
