/* ============================================================================
 * SILAH — APP SHELL
 * ----------------------------------------------------------------------------
 * Header, sidebar, mobile bottom navigation, page container and the
 * impersonation banner.
 *
 * Mobile first: the base rules describe the phone, and the desktop sidebar is
 * introduced at the 1024px breakpoint. Most of this audience arrives on a
 * phone, so the phone layout is the one that is written without compromise and
 * the desktop is the enhancement.
 * ========================================================================== */

/* --------------------------------------------------------------------------
 * Impersonation banner
 * --------------------------------------------------------------------------
 * Deliberately loud and permanently visible. A support agent must never be able
 * to forget which account they are inside, and a screenshot of the session must
 * carry the evidence.
 * ----------------------------------------------------------------------- */

.impersonation-banner {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-banner);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-2) var(--sp-4);
  background: var(--warning);
  color: var(--n-950);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  text-align: center;
}

.impersonation-banner a {
  color: inherit;
  text-decoration: underline;
  font-weight: var(--fw-semibold);
}

/* The countdown to the 30-minute cap. Tabular figures so the digits do not
   shift the surrounding text every second. */
.impersonation-banner__clock {
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-semibold);
  padding-inline: var(--sp-2);
  border-radius: var(--r-sm);
  background: rgb(0 0 0 / 0.12);
}

/* Under two minutes. The banner is already the warning colour, so urgency here
   has to come from motion rather than from another colour. */
.impersonation-banner__clock.is-ending {
  animation: imp-pulse 1s var(--ease) infinite;
}

@keyframes imp-pulse {
  50% { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
  .impersonation-banner__clock.is-ending { animation: none; text-decoration: underline; }
}

/* --------------------------------------------------------------------------
 * Header
 * ----------------------------------------------------------------------- */

.app-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  block-size: var(--header-h);
  padding-inline: var(--sp-4);
  background: var(--surface);
  border-block-end: 1px solid var(--border);
}

.app-header__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  color: var(--ink);
  letter-spacing: var(--tracking-tight);
}

.app-header__brand:hover {
  text-decoration: none;
  color: var(--brand-ink);
}

.app-header__mark {
  display: grid;
  place-items: center;
  inline-size: 32px;
  block-size: 32px;
  border-radius: var(--r-md);
  background: var(--brand);
  color: var(--brand-on);
  font-weight: var(--fw-bold);
  font-size: var(--fs-md);
  /* The wordmark is Latin at every language setting. */
  font-family: var(--font-latin);
}

/* --------------------------------------------------------------------------
 * Public navigation — signed-out only
 * --------------------------------------------------------------------------
 * Hidden below 1024px, where the bottom bar carries the same links. Showing
 * both would put one navigation above the thumb and an identical one under
 * it on the same screen.
 * ----------------------------------------------------------------------- */

.app-header__nav {
  display: none;
  align-items: center;
  gap: var(--sp-1);
  margin-inline-start: var(--sp-5);

  /*
   * THE NAV SHRINKS; THE HEADER NEVER GROWS.
   *
   * Without this a flex item refuses to shrink below its
   * content, so the nav pushed the header wider than the window and the
   * WHOLE PAGE scrolled sideways — every section, at every scroll
   * position, because of one row at the top. It appeared the moment the
   * navigation grew from seven entries to nine, but the fault was always
   * here: a header built to fit exactly the modules that existed on the
   * day it was written breaks on the day a module is added, which is the
   * one thing this codebase promises will be easy.
   *
   * The row is capped at four entries and a "More" menu instead, so it can
   * no longer outgrow the header — see shell-head.php. min-inline-size is
   * what keeps that true anyway if a label is ever translated long.
   *
   * AND IT MUST NOT SCROLL. Making this row overflow:auto did fix the
   * width, and clipped the "More" menu out of existence: an overflow on
   * one axis makes the other axis clip too, and the menu hangs BELOW the
   * row. The button opened, the panel was in the right place, and nothing
   * appeared on screen.
   */
  min-inline-size: 0;
}

/* Tighter on a laptop: the five entries fit for the price of 4px of padding
   each, and the row never has to scroll at all. */
@media (max-width: 78rem) {
  .app-header__nav  { margin-inline-start: var(--sp-3); }
  .app-header__link { padding-inline: var(--sp-2); }
}

/* The "More" trigger is a <button>, so it arrives with none of the type,
   colour or background a link gets — it is given the link's own class and
   only the differences are set here. */
.app-header__more {
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

.app-header__more[aria-expanded="true"] .dropdown__caret {
  transform: rotate(180deg);
}

.app-header__link {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: var(--sp-2);
  white-space: nowrap;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
  color: var(--ink-soft);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}

.app-header__link:hover {
  background: var(--surface-sunken);
  color: var(--ink);
  text-decoration: none;
}

.app-header__link.is-active {
  background: var(--brand-soft);
  color: var(--brand-ink);
}

.app-header__link .icon { color: currentColor; }

@media (min-width: 1024px) {
  .app-header__nav { display: flex; }
}

@media (prefers-reduced-motion: reduce) {
  .app-header__link { transition: none; }
}

.app-header__actions {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  margin-inline-start: auto;
}

/* --------------------------------------------------------------------------
 * Icon button — the default for a header action
 * --------------------------------------------------------------------------
 * Icon only, no text label: an icon in a labelled context reads fine and keeps
 * the header compact. The accessible name comes from the sr-only span inside.
 * ----------------------------------------------------------------------- */

.icon-btn {
  display: inline-grid;
  place-items: center;
  inline-size: 38px;
  block-size: 38px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}

.icon-btn:hover {
  background: var(--surface-sunken);
  color: var(--ink);
}

.icon-btn[aria-expanded="true"],
.icon-btn.is-active {
  background: var(--brand-soft);
  color: var(--brand-ink);
}

/*
 * The fold control. Hidden on a phone, where there is no sidebar to fold and
 * the bottom navigation is doing the job instead.
 *
 * It sits AFTER .icon-btn deliberately. Both selectors are one class, so the
 * later one wins — written above, `display: none` here loses to the
 * `display: inline-grid` on .icon-btn and the button appears on every phone.
 */
.app-header__burger { display: none; }

/* aria-expanded is true whenever the sidebar is unfolded, which is most of the
   time — so the "pressed" tint above would make this button look permanently
   active. It is a toggle, not a state indicator. */
.app-header__burger[aria-expanded="true"] {
  background: transparent;
  color: var(--ink-soft);
}

.app-header__burger[aria-expanded="true"]:hover {
  background: var(--surface-sunken);
  color: var(--ink);
}

/* --------------------------------------------------------------------------
 * Notification bell
 * --------------------------------------------------------------------------
 * A link, not a dropdown: the header carries only a count (see
 * Notification::unreadCount) and the list is a page of its own.
 *
 * The count sits ON the bell rather than beside it so the header keeps its
 * rhythm of equal 38px squares whether the badge is there or not — a number
 * that widens the row when it appears makes everything after it jump.
 * ----------------------------------------------------------------------- */

.notif-bell { position: relative; }

.notif-bell.has-unread { color: var(--ink); }

.notif-bell__count {
  position: absolute;
  inset-block-start: 3px;
  inset-inline-end: 2px;

  min-inline-size: 17px;
  padding-inline: 4px;
  border-radius: 999px;

  background: var(--danger);
  color: #fff;

  font-size: 10px;
  font-weight: var(--fw-bold);
  line-height: 17px;
  text-align: center;

  /* Western digits, always left-to-right — "9+" must not flip on an Urdu
     page, where it would render as "+9". */
  direction: ltr;
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
 * Body layout
 * ----------------------------------------------------------------------- */

/* Two columns: the sidebar, and everything else. The sidebar runs from the very
   top of the window, so the header lives inside the second column rather than
   above both. */
.app-shell {
  display: flex;
  align-items: flex-start;
}

.app-shell__col {
  flex: 1 1 auto;
  /* Without this a wide table inside the column stretches the whole layout
     instead of scrolling inside its own wrapper. */
  min-inline-size: 0;
  display: flex;
  flex-direction: column;
  min-block-size: 100vh;
}

.app-main {
  flex: 1 1 auto;
  min-inline-size: 0;
  padding: var(--sp-4);
  /* Room for the bottom navigation, plus the phone's home indicator. */
  padding-block-end: calc(var(--bottomnav-h) + var(--sp-6) + env(safe-area-inset-bottom, 0px));
}

.container {
  inline-size: 100%;
  max-inline-size: var(--container);
  margin-inline: auto;
}

/*
 * INSIDE THE APP SHELL THERE IS NO READING-MEASURE CAP.
 *
 * A centred 1240px column makes sense for a page of prose read at any window
 * size. It is wrong for a screen that already has a sidebar: the sidebar has
 * taken its share, and capping what is left leaves two dead margins on a wide
 * monitor while the tables inside are scrolling sideways. Application screens
 * use the width they are given; only the documents keep the measure.
 */
.app-shell--nav .app-main > .container {
  max-inline-size: none;
}

.container--narrow {
  max-inline-size: var(--content-max);
}

/* --------------------------------------------------------------------------
 * Page header
 * ----------------------------------------------------------------------- */

.page-head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-block-end: var(--sp-5);
}

.page-head__text { flex: 1 1 260px; min-inline-size: 0; }
.page-head__title { font-size: var(--fs-2xl); }

.page-head__sub {
  margin-block-start: var(--sp-1);
  color: var(--ink-muted);
  font-size: var(--fs-sm);
}

.page-head__actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-inline-start: auto;
}

/* --------------------------------------------------------------------------
 * The shared furniture of the new layout
 * --------------------------------------------------------------------------
 * A pill above a heading, a heading, and an optional line of copy beside it.
 * Written for the home page and moved here the moment the public directories
 * wanted the same three shapes — a utility two areas use is not one page's,
 * and the copy left behind is the copy that drifts.
 * ----------------------------------------------------------------------- */

.pill {
  display: inline-block;
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--brand-border);
  border-radius: var(--r-full);
  background: var(--brand-soft);
  color: var(--brand-ink);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
}

/* On the dark hero the tinted-on-white version disappears, so the same pill is
   re-lit rather than replaced: one class, two grounds. */
.pill--on-dark {
  border-color: var(--panel-line);
  background: var(--panel-line-hover);
  color: var(--panel-ink);
}

.section-title {
  margin-block: var(--sp-3) 0;
  font-size: clamp(1.5rem, 2vw + 1rem, 2.25rem);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  max-inline-size: 20ch;
}

.section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-6);
  margin-block-end: var(--sp-5);
  flex-wrap: wrap;
}

.section-head__main { min-inline-size: 0; }

.section-head__lede {
  flex: 1 1 22ch;
  max-inline-size: 46ch;
  color: var(--ink-soft);
  line-height: var(--lh-normal);
}

/* --------------------------------------------------------------------------
 * THE PUBLIC SITE'S OWN FURNITURE
 * --------------------------------------------------------------------------
 * Everything below is scoped to `.app-shell:not(.app-shell--nav)`, which is
 * exactly the public site: areas/public/includes/header.php is the only file
 * in the codebase that sets $showSidebar = false, so a shell without the nav
 * modifier is a page a visitor reached from the header — never an admin or a
 * member screen.
 *
 * WHY SCOPE IT RATHER THAN CHANGE .page-head OUTRIGHT: forty-four files use
 * that class, and most of them are application screens. A 36px display title
 * over a table of reports is not a better admin page, it is a worse one — the
 * table is what the reader came for. The landing pages are the opposite case:
 * the words ARE the page until the visitor decides to look further down.
 *
 * The three classes above (.pill, .section-title, .section-head) are NOT
 * scoped: they were written for the home page and they are what the public
 * pages now borrow, which is the whole point of moving them out of home.css.
 * ----------------------------------------------------------------------- */

.app-shell:not(.app-shell--nav) .page-head {
  display: block;
  margin-block: var(--sp-6) var(--sp-8);
}

.app-shell:not(.app-shell--nav) .page-head__title {
  /* The same scale the home page's section headings use, so a visitor moving
     from the home page to a directory sees one typeface doing one job. */
  font-size: clamp(1.75rem, 2.4vw + 1rem, 2.75rem);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  max-inline-size: 22ch;
}

.app-shell:not(.app-shell--nav) .page-head__sub {
  margin-block-start: var(--sp-3);
  /* --ink-soft, not --ink-muted: this line is a sentence a visitor is meant to
     read, not a caption they are meant to skip. */
  color: var(--ink-soft);
  font-size: var(--fs-lg);
  line-height: var(--lh-normal);
  max-inline-size: 60ch;
}

/* The pill sits above the title and is spaced from it here rather than by the
   page, so all seven directories agree without seven copies of a margin. */
.app-shell:not(.app-shell--nav) .page-head .pill {
  margin-block-end: var(--sp-3);
}

.app-shell:not(.app-shell--nav) .page-head__actions {
  margin-block-start: var(--sp-4);
  margin-inline-start: 0;
}

/* -- the filter bar --------------------------------------------------------
 * A panel on the public site instead of a bare row of controls. Two reasons,
 * and neither is decoration:
 *
 *   1. It says where the filters END. In the bare version the row of selects
 *      ran straight into the first result card, and on a narrow window the
 *      filters and the results interleaved as one column of boxes.
 *
 *   2. It gives the submit button somewhere to sit. It used to align to the
 *      bottom of a select and look like a third field.
 *
 * The panel is only the frame — the controls inside are the same shared field
 * component every form on the site uses.
 * ----------------------------------------------------------------------- */

.app-shell:not(.app-shell--nav) .filter-bar {
  align-items: end;
  gap: var(--sp-4);
  padding: var(--sp-5);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--sh-1);
  margin-block-end: var(--sp-6);
}

/* Fields share the row evenly and keep a floor, so two filters do not become
   two 90px stubs beside a wide button. */
.app-shell:not(.app-shell--nav) .filter-bar .field {
  flex: 1 1 220px;
  min-inline-size: 0;
}

/* The button is a row item, not a field: no label above it, so it has to be
   told to line up with the controls rather than with their labels. */
.app-shell:not(.app-shell--nav) .filter-bar .btn {
  flex: 0 0 auto;
  min-block-size: 44px;
}

@media (max-width: 40rem) {
  /* On a phone the button spans the row: a 44px target beside a full-width
     select reads as an afterthought, and it is the one thing here that
     actually does something. */
  .app-shell:not(.app-shell--nav) .filter-bar {
    padding: var(--sp-4);
  }

  .app-shell:not(.app-shell--nav) .filter-bar .btn {
    flex: 1 1 100%;
  }
}

/* -- the result count ------------------------------------------------------
 * "429 candidates — showing 20 of 429" sits between the filter panel and the
 * first card and belongs to neither, so it is given room on both sides rather
 * than being read as a caption on whichever one it happens to touch.        */

.app-shell:not(.app-shell--nav) .feed__head {
  margin-block-end: var(--sp-5);
}

/* --------------------------------------------------------------------------
 * Sidebar — desktop only
 * ----------------------------------------------------------------------- */

.app-sidebar {
  display: none;
  flex: 0 0 var(--sidebar-w);
  inline-size: var(--sidebar-w);
  position: sticky;
  /* From the very top of the window, not from under the header. */
  inset-block-start: 0;
  block-size: 100vh;
  background: var(--surface);
  border-inline-end: 1px solid var(--border);
  transition: flex-basis var(--dur) var(--ease), inline-size var(--dur) var(--ease);
}

/* The brand sits at the top of the sidebar and lines up with the header beside
   it — same height, same border, so the two read as one bar across the window. */
.app-sidebar__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  block-size: var(--header-h);
  flex: 0 0 auto;
  padding-inline: var(--sp-4);
  border-block-end: 1px solid var(--border);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  color: var(--ink);
  letter-spacing: var(--tracking-tight);
  white-space: nowrap;
  overflow: hidden;
}

.app-sidebar__brand:hover {
  text-decoration: none;
  color: var(--brand-ink);
}

/* Only the nav scrolls; the brand stays put. */
.app-sidebar__nav {
  block-size: calc(100vh - var(--header-h));
  overflow-y: auto;
  padding: var(--sp-4) var(--sp-3);
}

.app-sidebar__group + .app-sidebar__group {
  margin-block-start: var(--sp-6);
}

.app-sidebar__label {
  padding-inline: var(--sp-3);
  margin-block-end: var(--sp-2);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-muted);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
  color: var(--ink-soft);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}

.nav-item + .nav-item { margin-block-start: 2px; }

.nav-item:hover {
  background: var(--surface-sunken);
  color: var(--ink);
  text-decoration: none;
}

.nav-item.is-active {
  background: var(--brand-soft);
  color: var(--brand-ink);
}

.nav-item__text {
  flex: 1 1 auto;
  min-inline-size: 0;
  white-space: nowrap;
  overflow: hidden;
}

.nav-item__badge {
  margin-inline-start: auto;
  min-inline-size: 20px;
  padding-inline: var(--sp-1);
  border-radius: var(--r-full);
  background: var(--brand);
  color: var(--brand-on);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  text-align: center;
}

/* --------------------------------------------------------------------------
 * Folded sidebar — icons only
 * --------------------------------------------------------------------------
 * The state lives in a cookie and is written into the class list by PHP, so a
 * returning visitor never sees it unfold. See sidebar_collapsed().
 *
 * The labels are removed from the flow rather than hidden with visibility: a
 * hidden label still reserves its width, which is the whole thing being
 * reclaimed here. The title attribute on each item carries the name instead.
 * ----------------------------------------------------------------------- */

.app-shell.is-collapsed .app-sidebar {
  flex-basis: var(--sidebar-w-folded);
  inline-size: var(--sidebar-w-folded);
}

.app-shell.is-collapsed .app-sidebar__word,
.app-shell.is-collapsed .app-sidebar__label,
.app-shell.is-collapsed .nav-item__text {
  display: none;
}

.app-shell.is-collapsed .app-sidebar__brand,
.app-shell.is-collapsed .nav-item {
  justify-content: center;
  padding-inline: 0;
}

.app-shell.is-collapsed .app-sidebar__nav {
  padding-inline: var(--sp-2);
}

/* A group separator still needs to exist once its heading is gone, or the
   groups run together into one undifferentiated column of icons. */
.app-shell.is-collapsed .app-sidebar__group + .app-sidebar__group {
  margin-block-start: var(--sp-4);
  padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--border);
}

/* The count still has to be visible when the label it belonged to is not —
   an unread badge is the one thing worth interrupting for. */
.app-shell.is-collapsed .nav-item__badge {
  position: absolute;
  inset-block-start: 2px;
  inset-inline-end: 2px;
  margin: 0;
  font-size: 10px;
  line-height: 16px;
  min-inline-size: 16px;
}

.app-shell.is-collapsed .nav-item {
  position: relative;
}

@media (prefers-reduced-motion: reduce) {
  .app-sidebar { transition: none; }
}

/* --------------------------------------------------------------------------
 * Bottom navigation — mobile only
 * ----------------------------------------------------------------------- */

.bottom-nav {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: var(--z-header);
  display: flex;
  background: var(--surface);
  border-block-start: 1px solid var(--border);
  padding-block-end: env(safe-area-inset-bottom, 0px);
}

.bottom-nav__item {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-block-size: var(--bottomnav-h);
  padding-block: var(--sp-2);
  color: var(--ink-muted);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  text-align: center;
}

.bottom-nav__item:hover { text-decoration: none; }

.bottom-nav__item.is-active {
  color: var(--brand-ink);
}

.bottom-nav__item .icon {
  inline-size: 22px;
  block-size: 22px;
}

/* --------------------------------------------------------------------------
 * Sticky sub-navigation (tabs)
 * --------------------------------------------------------------------------
 * A sticky element cannot stick past the bottom edge of its own parent box, so
 * this must be a top-level sibling of the tab content — not nested inside a
 * short card, or it scrolls away almost immediately while the content beside it
 * is still scrolling.
 * ----------------------------------------------------------------------- */

.subnav {
  position: sticky;
  inset-block-start: var(--header-h);
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  margin-block-end: var(--sp-4);
  padding-block: var(--sp-2);
  background: var(--canvas);
  border-block-end: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
}

.subnav::-webkit-scrollbar { display: none; }

.subnav__link {
  flex: 0 0 auto;
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-full);
  color: var(--ink-soft);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}

.subnav__link:hover {
  background: var(--surface-sunken);
  color: var(--ink);
  text-decoration: none;
}

.subnav__link.is-active {
  background: var(--brand);
  color: var(--brand-on);
}

/* The section's one primary action belongs here, in the tab bar itself, rather
   than buried in the content below it. */
.subnav__actions {
  margin-inline-start: auto;
  display: flex;
  gap: var(--sp-2);
  padding-inline-start: var(--sp-3);
}

/* --------------------------------------------------------------------------
 * Footer
 * ----------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
 * Footer
 * --------------------------------------------------------------------------
 * Two shapes from one markup: a full multi-column footer in the public area,
 * and a single row of legal links everywhere else. See shell-foot.php for why
 * they share a file.
 * ----------------------------------------------------------------------- */

.app-footer {
  margin-block-start: var(--sp-8);
  padding: var(--sp-6) var(--sp-4);
  padding-block-end: calc(var(--bottomnav-h) + var(--sp-6));
  border-block-start: 1px solid var(--border);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: var(--fs-sm);
}

/* The columns. auto-fit rather than a fixed count, so the footer reflows from
   four columns to two to one without a single breakpoint — and so a platform
   with more modules simply gets another column. */
.app-footer__top {
  display: grid;
  gap: var(--sp-6);
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  max-inline-size: var(--container);
  margin-inline: auto;
  text-align: start;
}

/* The brand column is given more room than the link columns: it carries a
   sentence, and the others carry single words. */
.app-footer__brand { grid-column: span 2; max-inline-size: 32ch; }

@media (max-width: 40rem) {
  .app-footer__brand { grid-column: auto; }
}

.app-footer__name {
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  color: var(--ink);
}

.app-footer__pitch {
  margin-block: var(--sp-2) var(--sp-3);
  line-height: var(--lh-normal);
}

.app-footer__heading {
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  margin-block-end: var(--sp-3);
}

.app-footer__col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-2);
}

.app-footer__col a,
.app-footer__links a {
  color: var(--ink-soft);
  text-decoration: none;
}

.app-footer__col a:hover,
.app-footer__links a:hover {
  color: var(--ink);
  text-decoration: underline;
}

/* The legal line, and the compact footer's link row. */
.app-footer__bottom {
  max-inline-size: var(--container);
  margin-inline: auto;
  text-align: center;
}

.app-footer__top + .app-footer__bottom {
  margin-block-start: var(--sp-6);
  padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--border);
}

/* About, FAQ, Contact, Terms, Privacy. Wrapping is deliberate: five labels
   do not fit one phone row, and a footer is the one place where wrapping is
   harmless. */
.app-footer__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2) var(--sp-4);
  margin-block-end: var(--sp-3);
}
/* --------------------------------------------------------------------------
 * Desktop
 * ----------------------------------------------------------------------- */

@media (min-width: 1024px) {
  .app-sidebar { display: block; }
  .bottom-nav  { display: none; }

  .app-header__burger { display: inline-grid; }

  /* The brand is in the sidebar now, so the header's copy would be a second one
     six pixels away. It stays in the markup for the phone, where there is no
     sidebar to hold it. */
  .app-shell--nav .app-header__brand { display: none; }

  .app-main {
    padding: var(--sp-6) var(--sp-8);
    padding-block-end: var(--sp-12);
  }

  .app-footer {
    padding-block-end: var(--sp-6);
  }
}

/* --------------------------------------------------------------------------
 * Header alignment
 * --------------------------------------------------------------------------
 * THE HEADER MUST START WHERE THE CONTENT STARTS. The bar itself has to run
 * the full width of the window — it carries the background and the bottom
 * border, and a 1240px bar floating on a wide monitor looks broken. But its
 * CONTENTS sit inside a .container that is capped and centred, so on any window
 * wider than the container the brand used to sit further out than the first
 * card below it.
 *
 * One line of padding fixes it, with no change to the markup: sp-4 on a narrow
 * window, and exactly the container's own margin once the window is wider than
 * the container. The footer's columns are capped the same way, so header,
 * content and footer share one pair of edges down the whole page.
 *
 * ONLY WITHOUT A SIDEBAR. An app screen with a sidebar deliberately has no
 * width cap (see the note beside .container), so capping its header would
 * misalign the very thing this rule exists to align.
 * ----------------------------------------------------------------------- */

.app-shell:not(.app-shell--nav) .app-header {
  padding-inline: max(var(--sp-4), calc((100% - var(--container)) / 2));
}

/* --------------------------------------------------------------------------
 * Role chip
 * --------------------------------------------------------------------------
 * "Admin" or "Platform owner" beside the avatar in the header. It replaced two
 * menu entries that used to advertise the staff area twice — what a person
 * needs at a glance is which hat they are wearing, and that is a label.
 *
 * Hidden below 40rem: on a phone the header is already the brand, four icons
 * and an avatar. The screen-reader name in the trigger still carries it.
 * ----------------------------------------------------------------------- */

.role-chip {
  display: none;
  padding: 2px var(--sp-2);
  border-radius: var(--r-full);
  background: var(--brand-soft);
  border: 1px solid var(--brand-border);
  color: var(--brand-ink);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}

@media (min-width: 40rem) {
  .role-chip { display: inline-block; }
}

/* --------------------------------------------------------------------------
 * The phone bar's centre button, and the sheet it opens
 * --------------------------------------------------------------------------
 * Two links, a plus, two links. The plus is raised out of the bar because it
 * is not a fifth destination — it is the way to all of them, and a control
 * that opens something should not look like a control that goes somewhere.
 * ----------------------------------------------------------------------- */

.bottom-nav__centre {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  inline-size: 4.5rem;
}

.bottom-nav__plus {
  display: grid;
  place-items: center;
  inline-size: 52px;
  block-size: 52px;
  /* Lifted clear of the bar's top edge, which is what makes it read as the
     one action rather than as the middle item of five. */
  margin-block-start: -28px;
  border: 3px solid var(--surface);
  border-radius: var(--r-full);
  background: var(--brand);
  color: var(--brand-on);
  box-shadow: var(--sh-3);
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease);
}

.bottom-nav__plus .icon {
  inline-size: 24px;
  block-size: 24px;
}

.bottom-nav__plus[aria-expanded="true"] {
  transform: rotate(45deg);
}

/* -- the sheet -------------------------------------------------------------
 * It rises from the bar it belongs to, so it is anchored to the bottom of the
 * window rather than floating in the middle of it. Opened and closed by the
 * same code as the header dropdowns — see ui.js.
 * ----------------------------------------------------------------------- */

.sheet {
  position: fixed;

  /*
   * A CARD THAT FLOATS OVER THE BAR, not a panel welded to it.
   *
   * It used to run edge to edge and sit flat on the bottom bar, which read
   * as part of the furniture rather than as something that had just opened
   * — and a panel touching three sides of the screen gives the eye nowhere
   * to see that the page is still behind it. Inset on both sides and lifted
   * clear of the bar, it reads as one object with a beginning and an end.
   *
   * Capped as well as inset, so on a tablet it stops growing rather than
   * becoming a very wide strip of six small icons.
   */
  inset-inline: var(--sp-4);
  max-inline-size: 26rem;
  margin-inline: auto;
  inset-block-end: calc(var(--bottomnav-h) + var(--sp-4) + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-dropdown);

  max-block-size: 60vh;
  overflow-y: auto;
  padding: var(--sp-5) var(--sp-4);
  background: var(--surface-raised);
  /* All four corners now that it touches no edge, and a full border rather
     than a top rule for the same reason. */
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-4);

  animation: sheet-in var(--dur-base) var(--ease-out);
}

.sheet[hidden] { display: none; }

@keyframes sheet-in {
  from { transform: translateY(12px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .sheet { animation: none; }
  .bottom-nav__plus { transition: none; }
}

.sheet__title {
  margin-block-end: var(--sp-4);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  color: var(--ink-muted);
}

/* Three across on the narrowest phone this audience carries, four when there
   is room — a grid rather than a list, because these are destinations to
   recognise by icon, not lines to read. */
.sheet__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: var(--sp-3);
}

.sheet__item {
  display: grid;
  justify-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-2);
  border-radius: var(--r-md);
  color: var(--ink-soft);
  font-size: var(--fs-xs);
  text-align: center;
  text-decoration: none;
}

.sheet__item.is-active,
.sheet__item:hover {
  background: var(--surface-sunken);
  color: var(--ink);
}

.sheet__icon {
  display: grid;
  place-items: center;
  inline-size: 44px;
  block-size: 44px;
  border-radius: var(--r-md);
  background: var(--brand-soft);
  color: var(--brand-ink);
}

.sheet__item.is-active .sheet__icon {
  background: var(--brand);
  color: var(--brand-on);
}

/* --------------------------------------------------------------------------
 * The sidebar as a drawer, on a phone
 * --------------------------------------------------------------------------
 * Below the breakpoint the sidebar is display:none and the header's button
 * slides it in over the page instead of folding it — see ui.js. Everything
 * here is inside the media query, so a desktop is untouched: there the
 * sidebar is part of the layout and nothing covers the page.
 * ----------------------------------------------------------------------- */

.app-scrim { display: none; }

@media (max-width: 1023.98px) {
  .app-scrim {
    position: fixed;
    inset: 0;
    /* ABOVE the header and the bottom bar, both of which sit at --z-header:
       a scrim they poke through is a scrim with two live controls on it,
       and tapping either one while the drawer is open does something the
       person did not mean. */
    z-index: calc(var(--z-header) + 1);
    display: block;
    background: var(--overlay);
  }

  .app-scrim[hidden] { display: none; }

  .app-shell--nav.is-drawer .app-sidebar {
    display: flex;
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    /* One above the scrim, which is one above the header — so the drawer
       covers the whole window rather than starting under the header it
       was opened from. */
    z-index: calc(var(--z-header) + 2);
    inline-size: min(82vw, var(--sidebar-w));
    overflow-y: auto;
    box-shadow: var(--sh-4);
  }

  /* A drawer is open BECAUSE somebody asked for the labels, so the folded
     state never applies to it — otherwise a person who folded the sidebar on
     their desktop opens a column of unexplained icons on their phone. */
  .app-shell--nav.is-drawer.is-collapsed .app-sidebar {
    flex-basis: auto;
    inline-size: min(82vw, var(--sidebar-w));
  }

  .app-shell--nav.is-drawer.is-collapsed .app-sidebar__word,
  .app-shell--nav.is-drawer.is-collapsed .app-sidebar__label,
  .app-shell--nav.is-drawer.is-collapsed .nav-item__text {
    display: initial;
  }

  /* The three-line button is desktop-only above; on a phone it is the only way
     to the drawer, so it comes back. */
  .app-header__burger { display: inline-grid; }
}
