/* Program Hub — visual design from the approved mockup (yhub2 /p/ca639aa35228),
   with a mobile-first responsive layer the mockup didn't have. Base styles are
   the phone layout; min-width queries build up to the desktop design. */

:root {
  --blue:        #003DA5;
  --blue-mid:    #1A56C4;
  --blue-light:  #EEF3FF;
  --blue-deep:   #001a4d;
  --amber:       #F59E0B;
  --amber-light: #FEF3C7;
  --teal:        #0EA5E9;
  --green:       #10B981;
  --coral:       #F97316;
  --danger:      #DC2626;
  --bg:          #F4F6FB;
  --card:        #FFFFFF;
  --text:        #111827;
  --muted:       #6B7280;
  --border:      #E5E7EB;
  --radius:      12px;
  --shadow:      0 2px 12px rgba(0,0,0,.08);
  --shadow-lg:   0 8px 32px rgba(0,0,0,.12);
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  -webkit-text-size-adjust: 100%;
}

a { color: var(--blue); }

/* ── NAV ──────────────────────────────────────────────────────────────────── */

nav.topbar {
  background: var(--blue);
  min-height: 60px;
  display: flex; align-items: center;
  padding: 0 16px; gap: 10px;
  flex-wrap: wrap;
}
.nav-logo {
  display: flex; align-items: center; gap: 10px;
  /* ★ "Program Hub" is 8 characters longer than the name this bar was built
     for, and the bar also has to hold an avatar and a menu button. clamp() lets the
     wordmark give way on a narrow phone instead of pushing them onto a second row. */
  color: #fff; font-weight: 800; font-size: clamp(14px, 4.3vw, 17px);
  letter-spacing: -.3px; white-space: nowrap;
  text-decoration: none; margin-right: auto;
  padding: 12px 0;
}
.nav-logo img { width: 30px; height: auto; display: block; }

.nav-toggle {
  background: rgba(255,255,255,.15); border: 0; color: #fff;
  width: 40px; height: 40px; border-radius: 8px; cursor: pointer;
  font-size: 17px; display: flex; align-items: center; justify-content: center;
}
.nav-avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--amber); color: var(--blue);
  font-weight: 700; font-size: 13px;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}

.nav-links {
  display: none; width: 100%; flex-direction: column;
  gap: 2px; padding: 4px 0 12px;
}
.nav-links.open { display: flex; }
.nav-link, .nav-links form button {
  color: rgba(255,255,255,.85); text-decoration: none;
  font-size: 15px; font-weight: 500;
  padding: 12px 12px; border-radius: 6px;
  background: none; border: 0; font-family: inherit; text-align: left;
  cursor: pointer; min-height: 44px;
  display: flex; align-items: center; gap: 6px;
}
.nav-link:hover, .nav-link.active,
.nav-links form button:hover { background: rgba(255,255,255,.15); color: #fff; }

@media (min-width: 900px) {
  nav.topbar { padding: 0 32px; flex-wrap: nowrap; height: 60px; }
  .nav-logo { font-size: 18px; margin-right: 40px; padding: 0; }
  .nav-toggle { display: none; }
  .nav-links {
    display: flex; flex-direction: row; align-items: center;
    width: auto; flex: 1; gap: 4px; padding: 0;
  }
  .nav-link, .nav-links form button {
    font-size: 14px; padding: 6px 14px; min-height: 0;
  }
}

/* ── HERO ─────────────────────────────────────────────────────────────────── */

.hero {
  position: relative; overflow: hidden;
  background: var(--blue-deep);
  min-height: 340px;
  padding: 48px 20px 44px;
  display: flex; align-items: center; justify-content: center;
  text-align: center;
}
.hero-photos { position: absolute; inset: 0; display: flex; gap: 3px; }
.hero-photo { flex: 1; overflow: hidden; }
.hero-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* On a phone, five slivers read as noise — show two, then three, then all five. */
.hero-photo:nth-child(n+3) { display: none; }

.hero-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(to right,
    rgba(0,26,77,.65) 0%, rgba(0,38,100,.55) 50%, rgba(0,26,77,.65) 100%);
}
.hero-content { position: relative; z-index: 2; width: 100%; }
.hero h1 {
  color: #fff; font-size: 30px; font-weight: 800;
  line-height: 1.15; letter-spacing: -.5px; margin-bottom: 12px;
}
.hero h1 span { color: var(--amber); display: block; }
.hero p {
  color: rgba(255,255,255,.75); font-size: 15px;
  max-width: 520px; margin: 0 auto 26px; line-height: 1.6;
}

.hero-search {
  display: flex; max-width: 600px; margin: 0 auto;
  box-shadow: 0 8px 40px rgba(0,0,0,.25); border-radius: 12px;
}
.hero-search input {
  flex: 1; min-width: 0; border: none; border-radius: 12px 0 0 12px;
  padding: 15px 16px; font-size: 16px; /* 16px avoids iOS zoom-on-focus */
  outline: none; background: #fff; font-family: inherit;
}
.hero-search button {
  background: var(--amber); border: none; border-radius: 0 12px 12px 0;
  padding: 0 20px; font-size: 15px; font-weight: 700; color: #fff;
  cursor: pointer; white-space: nowrap; font-family: inherit;
}
.hero-search button:hover { background: #D97706; }

.hero-stats {
  display: flex; justify-content: center; gap: 26px;
  margin-top: 28px; flex-wrap: wrap;
}
.hero-stat { color: rgba(255,255,255,.8); font-size: 12px; }
.hero-stat strong { display: block; color: #fff; font-size: 20px; font-weight: 800; }

@media (min-width: 600px) {
  .hero-photo:nth-child(3) { display: block; }
  .hero h1 { font-size: 38px; }
  .hero h1 span { display: inline; }
}
@media (min-width: 900px) {
  .hero { padding: 80px 32px 70px; min-height: 440px; }
  .hero-photo { display: block !important; }
  .hero h1 { font-size: 46px; margin-bottom: 14px; }
  .hero p { font-size: 18px; margin-bottom: 36px; }
  .hero-search input { padding: 18px 20px; }
  .hero-search button { padding: 0 28px; }
  .hero-stats { gap: 40px; margin-top: 40px; }
  .hero-stat { font-size: 13px; }
  .hero-stat strong { font-size: 22px; }
}

/* ── SECTIONS ─────────────────────────────────────────────────────────────── */

.section { padding: 32px 16px; }
.section.white { background: #fff; }
.section-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 18px; gap: 12px; flex-wrap: wrap;
}
.section-header h2 { font-size: 19px; font-weight: 700; }
/* :not(.btn) — this rule styles the small "See all →" text link. Without the guard it also
   wins over .btn (specificity 0,1,1 vs 0,1,0) and paints button text blue on a blue
   background: invisible until hover darkens the fill behind it. */
.section-header a:not(.btn) { font-size: 14px; color: var(--blue); font-weight: 600; text-decoration: none; }
@media (min-width: 900px) {
  .section { padding: 48px 32px; }
  .section-header { margin-bottom: 24px; }
  .section-header h2 { font-size: 22px; }
}

.divider { border: none; border-top: 1px solid var(--border); margin: 0; }

/* ── CATEGORY CARDS ───────────────────────────────────────────────────────── */

.cat-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.cat-card {
  background: var(--card); border-radius: var(--radius); padding: 18px 12px;
  text-align: center; border: 2px solid transparent;
  box-shadow: var(--shadow); transition: all .2s;
  text-decoration: none; color: inherit; display: block;
}
.cat-card:hover { border-color: var(--blue); transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.cat-icon { font-size: 26px; margin-bottom: 8px; }
.cat-name { font-size: 13px; font-weight: 700; color: var(--text); margin-bottom: 4px; }
.cat-count { font-size: 12px; color: var(--muted); }
@media (min-width: 600px) { .cat-grid { grid-template-columns: repeat(3, 1fr); gap: 14px; } }
@media (min-width: 1000px) { .cat-grid { grid-template-columns: repeat(6, 1fr); } }

/* ── PROGRAM AREAS (two-tier browse) ──────────────────────────────────────────
   An area card is bigger than a category tile because it is a CONTAINER, not a
   destination for filing — and it carries its categories as chips so the second
   tier is visible at rest. Same geometry as the approved mockup. */

.area-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 620px)  { .area-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .area-grid { grid-template-columns: repeat(3, 1fr); } }

.area-card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 18px 18px 16px;
  display: flex; flex-direction: column; gap: 10px;
  box-shadow: var(--shadow);
}
.area-card:hover { border-color: var(--blue-mid); }
.area-top {
  display: flex; align-items: center; gap: 12px;
  text-decoration: none; color: inherit;
}
.area-icon {
  width: 44px; height: 44px; border-radius: 10px; flex: none;
  background: var(--blue-light);
  display: flex; align-items: center; justify-content: center; font-size: 22px;
}
.area-name { display: block; font-size: 16.5px; font-weight: 800; letter-spacing: -.2px; }
.area-count { display: block; font-size: 12px; color: var(--muted); }
.area-empty { font-size: 12.5px; color: var(--muted); margin: 0; }

.cat-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.cat-chip {
  font-size: 12.5px; font-weight: 600; text-decoration: none;
  padding: 5px 10px; border-radius: 999px;
  background: var(--bg); border: 1px solid var(--border); color: var(--text);
  display: inline-flex; align-items: center; gap: 5px;
}
.cat-chip:hover { border-color: var(--blue-mid); background: var(--blue-light); }
.cat-chip b { font-weight: 700; color: var(--muted); font-size: 11.5px;
              font-variant-numeric: tabular-nums; }

/* The nav menu carries both tiers, so the category rows are indented under
   their area and the area row is the one that reads as a heading. */
.page-eyebrow a { color: inherit; text-decoration: underline;
                  text-underline-offset: 2px; }

.nav-dd-area { font-weight: 700; }
.nav-dd-cat { padding-left: 26px !important; }

.empty-state {
  background: var(--card); border: 1px dashed var(--border);
  border-radius: var(--radius); padding: 22px; text-align: center;
  color: var(--muted); font-size: 14.5px;
}

/* ── ACTIVITY CARDS ───────────────────────────────────────────────────────── */

.activity-grid { display: grid; grid-template-columns: 1fr; gap: 16px; }
@media (min-width: 560px)  { .activity-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px)  { .activity-grid { grid-template-columns: repeat(3, 1fr); gap: 20px; } }
@media (min-width: 1200px) { .activity-grid { grid-template-columns: repeat(4, 1fr); } }

.activity-card {
  background: var(--card); border-radius: var(--radius); overflow: hidden;
  box-shadow: var(--shadow); transition: all .2s;
  text-decoration: none; color: inherit; display: block;
}
.activity-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.card-thumb { width: 100%; aspect-ratio: 4/3; position: relative; overflow: hidden; background: var(--blue-light); }
.card-thumb img { width: 100%; height: 100%; object-fit: cover; }
.card-thumb-placeholder {
  width: 100%; height: 100%; display: flex; align-items: center;
  justify-content: center; font-size: 40px;
}
.card-heart {
  position: absolute; top: 10px; right: 10px;
  background: rgba(255,255,255,.9); border: 0; border-radius: 50%;
  width: 34px; height: 34px; display: flex; align-items: center; justify-content: center;
  font-size: 15px; box-shadow: 0 2px 8px rgba(0,0,0,.15); cursor: pointer;
}
.card-heart.active { background: #FEE2E2; }
.card-body { padding: 16px; }
.card-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.card-title { font-size: 15px; font-weight: 700; line-height: 1.35; margin-bottom: 8px; }
.card-desc {
  font-size: 13px; color: var(--muted); line-height: 1.5; margin-bottom: 12px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.card-meta { display: flex; gap: 14px; font-size: 12px; color: var(--muted); flex-wrap: wrap; }
.card-meta span { display: flex; align-items: center; gap: 4px; }

/* ── CHIPS ────────────────────────────────────────────────────────────────── */

.chip {
  font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 100px;
  text-transform: uppercase; letter-spacing: .04em; display: inline-block;
}
.chip-cat   { background: var(--blue-light);  color: var(--blue); }
/* A category chip is a LINK as of 1.24.0 — an activity can be in several and each one is
   a page worth reaching. The underline is dropped because the pill already reads as a
   control; the hover is what says it is one. */
a.chip-cat { text-decoration: none; }
a.chip-cat:hover { background: var(--blue); color: #fff; }
.chip-tag   { background: var(--amber-light); color: #92400E; }
.chip-green { background: #D1FAE5; color: #065F46; }
.chip-grey  { background: #F3F4F6; color: var(--muted); }

/* ── CATEGORY PAGE HEADER ─────────────────────────────────────────────────── */

/* ── PAGE HEAD ────────────────────────────────────────────────────────────────
   Every page that isn't the home page gets this band. Before it existed, each
   one opened with a bare <h1> on flat grey — correct, and indistinguishable from
   any other admin panel.

   The signature is the sliver motif: the approved hero is five vertical photo
   panels behind deep blue, so the same rhythm is repeated here as light on blue.
   Photographs belong on the front door; their geometry is what carries the
   identity onto the other twelve pages. */

.page-head, .cat-page-header {
  position: relative; overflow: hidden;
  background: linear-gradient(135deg, var(--blue-deep) 0%, var(--blue) 58%, #0A57C9 100%);
  color: #fff; padding: 26px 16px 28px;
}
/* ✗ REMOVED in 0.8.1 — do not reintroduce the vertical sliver stripes here.
   0.8.0 echoed the hero's five photo panels as light-on-blue stripes across the
   band. Two things were wrong with it, and the second is the disqualifying one:

   1. Text ran straight through them on any page with a longer subtitle
      (category, YMCAs) — words competing with texture.
   2. A vertical rule reads as STRUCTURE. It implies a column edge or a divider
      that carries meaning, and these carried none. A device that looks like
      information but isn't is worse than no device at all — and masking the
      stripes clear of the text would have kept the false signal, just moved it.

   The band's identity is the deep-blue gradient, the amber eyebrow and its
   rule, and the display type. That is enough; the stripes were the accessory to
   take off before leaving the house. */
.page-head-inner {
  position: relative; max-width: 1100px; margin: 0 auto;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
}
.page-head-inner > div:not(.page-head-icon) { flex: 1 1 240px; min-width: 0; }
.page-head-icon { font-size: 34px; line-height: 1; flex: none; }
/* The YMCAs page uses the real Y mark instead of an emoji. Sized to sit on the
   same optical line as the icons on the other pages. */
.page-head-mark img { height: 38px; width: auto; display: block; }
/* Line icons inherit the band's white through currentColor, held slightly back
   so the amber eyebrow stays the only accent. Category pages keep their emoji. */
.page-head-icon svg {
  width: 38px; height: 38px; display: block; color: rgba(255,255,255,.88);
}

/* The eyebrow names which half of the app you're standing in (Library vs
   Administration). That's real information in an app split down the middle —
   it isn't a decorative label. */
.page-eyebrow {
  display: flex; align-items: center; gap: 9px;
  font-size: 11px; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--amber); margin-bottom: 7px;
}
.page-eyebrow::after {
  content: ''; height: 2px; width: 24px; border-radius: 2px;
  background: var(--amber); opacity: .85;
}

.page-head h1, .cat-page-header h1 {
  font-size: 26px; font-weight: 800; letter-spacing: -.02em; margin: 0;
}
.page-head p, .cat-page-header p {
  color: rgba(255,255,255,.78); font-size: 14px; margin-top: 6px; max-width: 62ch;
}
.page-crumbs {
  font-size: 13px; color: rgba(255,255,255,.72); margin-bottom: 7px;
}
.page-crumbs a { color: rgba(255,255,255,.92); text-decoration: none; font-weight: 600; }
.page-crumbs a:hover { color: #fff; text-decoration: underline; }

/* The action slot pushes right on a wide screen and drops below the title on a
   phone, where a button squeezed beside a long activity name is unreadable. */
.page-head-action { margin: 14px 0 0; flex: none; }
.page-head .btn.on-blue { background: #fff; color: var(--blue); }
.page-head .btn.on-blue:hover { background: var(--amber-light); color: var(--blue-deep); }
.page-head-inner > .btn.on-blue { margin-top: 14px; }
@media (min-width: 700px) {
  .page-head-action, .page-head-inner > .btn.on-blue { margin: 0 0 0 auto; }
}

.cat-page-icon { font-size: 34px; line-height: 1; flex: none; }
.cat-page-header { display: flex; align-items: center; gap: 14px; }
.cat-page-header > * { position: relative; }

@media (min-width: 900px) {
  .page-head, .cat-page-header { padding: 40px 32px 44px; gap: 20px; }
  .page-head-icon, .cat-page-icon { font-size: 46px; }
  .page-head-mark img { height: 50px; }
  .page-head-icon svg { width: 50px; height: 50px; }
  .page-head h1, .cat-page-header h1 { font-size: 34px; }
  .page-head p, .cat-page-header p { font-size: 15px; }
}

/* ── GENERIC CARD / FORMS (admin + auth pages) ────────────────────────────── */

.wrap { max-width: 1100px; margin: 0 auto; padding: 24px 16px 48px; }
@media (min-width: 900px) { .wrap { padding: 32px 32px 56px; } }

h1 { font-size: 24px; font-weight: 800; margin-bottom: 6px; }
h2 { font-size: 19px; font-weight: 700; color: var(--blue); margin-bottom: 12px; }
h3 { font-size: 15px; font-weight: 700; margin-bottom: 4px; }
@media (min-width: 900px) { h1 { font-size: 30px; } h2 { font-size: 21px; } }

.sub { color: var(--muted); font-size: 14px; margin-bottom: 20px; }

/* Hairline + a tight two-layer shadow instead of one soft blur: the blur alone
   made every card float at the same depth, which is most of why these pages
   read as a template. The border does the separating; the shadow only lifts. */
.card {
  background: var(--card); border-radius: var(--radius);
  padding: 20px; margin-bottom: 18px;
  border: 1px solid var(--border);
  box-shadow: 0 1px 2px rgba(16,24,40,.04), 0 2px 6px rgba(16,24,40,.05);
}
@media (min-width: 700px) { .card { padding: 26px 28px; } }

/* A card's heading was blue text floating above its content with nothing tying
   the two together. The rule and the amber tick give the card a masthead. */
.card > h2 {
  position: relative;
  padding-bottom: 11px; margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
}
.card > h2::after {
  content: ''; position: absolute; left: 0; bottom: -1px;
  width: 30px; height: 2px; background: var(--amber); border-radius: 2px;
}

label { display: block; font-size: 13px; font-weight: 700; margin-bottom: 5px; }
/* ★ Every text-entry type, listed explicitly. `input[type=number]` was missing, so
   the Duration and Year boxes on the activity form fell back to the browser default
   and looked nothing like the fields above them — reported 2026-08-17. A bare
   `input` selector would be tidier but would also catch checkboxes and file
   pickers, so the list stays explicit and there is a test that keeps it complete. */
input[type=text], input[type=email], input[type=search], input[type=password],
input[type=number], input[type=tel], input[type=url], input[type=date],
select, textarea {
  width: 100%; padding: 12px 14px; font: inherit; font-size: 16px;
  border: 1px solid var(--border); border-radius: 10px; background: #fff; color: var(--text);
}
/* Duration and Year are typed, not nudged, so the spinner arrows only make the box
   a different shape from its neighbours. Removing them is what makes it match. */
input[type=number] { -moz-appearance: textfield; appearance: textfield; }
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--blue); outline-offset: 1px; border-color: var(--blue);
}
textarea { min-height: 110px; resize: vertical; }
/* The short description is capped at 300 characters and lives on a card, so it wants
   a couple of lines rather than the full-height box the long fields use. */
textarea.short { min-height: 68px; }
.field { margin-bottom: 16px; }
.hint { font-size: 12.5px; color: var(--muted); margin-top: 5px; }
.hint-inline { font-weight: 400; color: var(--muted); font-size: 12.5px; }

/* ── Drag-to-reorder (categories) ─────────────────────────────────────────── */

/* Handles stay hidden until reorder.js has actually run and set .reorder-ready. A grip that
   does nothing is worse than no grip, and the <noscript> arrows are the fallback. */
.draghandle-cell { display: none; }
.reorder-ready .draghandle-cell { display: table-cell; }

.draghandle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;          /* iPad-sized: this is the whole interaction */
  padding: 0; border: 1px solid transparent; border-radius: 10px;
  background: transparent; color: var(--muted);
  font-size: 20px; line-height: 1; cursor: grab;
  touch-action: none;                 /* without this a touch-drag scrolls the page instead */
}
.draghandle:hover { background: var(--blue-light); color: var(--blue); }
.draghandle:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }
.draghandle:active { cursor: grabbing; }

.reorder tr.dragging {
  opacity: .55;
  outline: 2px dashed var(--blue);
  background: var(--blue-light);
}
/* While a drag is in flight, stop the pointer selecting the row's text as it passes over. */
.reorder.is-dragging { user-select: none; -webkit-user-select: none; }
.reorder.is-dragging .draghandle { cursor: grabbing; }

.reorder-status { font-size: 13px; color: var(--blue); font-weight: 600; min-height: 19px; }

/* Stacked (phone) layout: the table is blocks, so the handle can't be a table-cell. Pin it to
   the top-right of the card instead of letting it sit as a full-width row of its own. */
@media (max-width: 779px) {
  .reorder-ready .draghandle-cell { display: block; position: absolute; top: 4px; right: 4px; padding: 0; }
  .reorder-ready .table.stack tr { position: relative; padding-right: 52px; }
  .reorder-ready .draghandle-cell::before { display: none; }   /* suppress the data-label */
}

/* Activity form. Mobile first: one column, pairing up only when there's width. */
.grid-2 { display: grid; grid-template-columns: 1fr; gap: 0 16px; }
@media (min-width: 640px) { .grid-2 { grid-template-columns: 1fr 1fr; } }

/* Tag / subject-area pickers. Wrapping chips beat a multi-select on a tablet,
   which is what a lot of these YMCAs are using. */
.checkrow { display: flex; flex-wrap: wrap; gap: 8px; }
.check {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 12px; border: 1px solid var(--border); border-radius: 999px;
  background: #fff; font-size: 14px; cursor: pointer;
  min-height: 40px;   /* finger-sized, per the iPad-first rule */
}
.check input { width: 16px; height: 16px; margin: 0; flex: none; }
.check:has(input:checked) {
  border-color: var(--blue); background: var(--blue-light); font-weight: 600;
}

/* The slim/keep decision. Amber because it is waiting on a person, not an error. */
.slim-offer {
  border: 1px solid #F0C36D; background: #FFF8E8;
  border-radius: var(--radius); padding: 14px 16px; margin-bottom: 16px;
}
.slim-offer p { margin-bottom: 10px; font-size: 14px; }

/* Inline row editor (taxonomy). Stacks on a phone, one line from tablet up —
   these rows live inside .table.stack, so the breakpoint matches its 780px. */
.inline-edit { display: flex; flex-direction: column; gap: 7px; }
.inline-edit input { width: 100%; }
.inline-edit .icon-input { width: 100%; text-align: center; font-size: 18px; }
@media (min-width: 780px) {
  .inline-edit { flex-direction: row; align-items: center; gap: 8px; }
  .inline-edit .icon-input { width: 56px; flex: none; }
  .inline-edit input[name="name"] { flex: 0 1 190px; }
  .inline-edit input[name="description"] { flex: 1 1 auto; min-width: 0; }
  .inline-edit .btn { flex: none; }
}

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--blue); color: #fff; border: 0; border-radius: 10px;
  font: inherit; font-size: 15px; font-weight: 700;
  padding: 12px 22px; min-height: 46px; cursor: pointer; text-decoration: none;
}
.btn:hover { background: #002E7A; }
.btn.amber { background: var(--amber); }
.btn.amber:hover { background: #D97706; }
.btn.ghost { background: #fff; color: var(--blue); border: 1.5px solid var(--blue); }
.btn.ghost:hover { background: var(--blue-light); }
.btn.small { padding: 7px 13px; min-height: 36px; font-size: 13.5px; }
.btn.danger { background: var(--danger); }
.btn.link { background: none; color: var(--blue); padding: 4px 6px; min-height: 0; font-weight: 600; }

.flash {
  padding: 13px 16px; border-radius: 10px; margin-bottom: 14px;
  font-size: 14.5px; border: 1px solid;
}
.flash.success { background: #F0FDF4; border-color: var(--green);  color: #065F46; }
.flash.danger  { background: #FEF2F2; border-color: var(--danger); color: #991B1B; }
.flash.warning { background: var(--amber-light); border-color: var(--amber); color: #92400E; }

/* ── TABLES → cards on phone ──────────────────────────────────────────────── */

.table { width: 100%; border-collapse: collapse; font-size: 14.5px; }
/* Tinted header on the brand blue rather than neutral grey, anchored by a solid
   rule — a grey strip over grey rules is the generic-table look. */
.table th {
  text-align: left; padding: 11px 12px; background: var(--blue-light); color: var(--blue);
  font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
  border-bottom: 2px solid #D8E3FA;
}
.table th:first-child { border-top-left-radius: 8px; }
.table th:last-child  { border-top-right-radius: 8px; }
.table td { padding: 13px 12px; border-top: 1px solid var(--border); vertical-align: middle; }
@media (min-width: 780px) {
  .table tbody tr { transition: background .12s; }
  .table tbody tr:hover { background: #FAFBFF; }
}

.table.stack, .table.stack thead, .table.stack tbody,
.table.stack tr, .table.stack th, .table.stack td { display: block; }
.table.stack thead { display: none; }
.table.stack tr {
  border: 1px solid var(--border); border-radius: 12px;
  margin-bottom: 12px; padding: 6px 4px; background: #fff;
}
.table.stack td { border: 0; padding: 7px 12px; }
.table.stack td::before {
  content: attr(data-label); display: block;
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted); margin-bottom: 2px;
}
@media (min-width: 780px) {
  /* ★★ Each part gets the display it is SUPPOSED to have, and tbody is the one
     that matters: `display: table` on a tbody makes it an independent table, so it
     computes its own column widths and stops sharing a grid with thead. The header
     labels then sit wherever their own text puts them — nowhere near the columns
     they name. That is what this was doing from 780px up, on every stacked table.
     tbody is `table-row-group`, never `table`. Reported by sflack on the People
     page, 2026-08-17. */
  .table.stack { display: table; width: 100%; }
  .table.stack thead { display: table-header-group; }
  .table.stack tbody { display: table-row-group; }
  .table.stack tr { display: table-row; border: 0; border-radius: 0; margin: 0; padding: 0; }
  .table.stack th, .table.stack td { display: table-cell; }
  .table.stack td { padding: 13px 12px; border-top: 1px solid var(--border); }
  .table.stack td::before { display: none; }
}

/* The second-choice provider button. Quieter, never hidden — the hint is a guess
   from the email domain, and a wrong guess must not remove the working button. */
.provider.quiet { opacity: .72; }
.provider.quiet:hover { opacity: 1; }

/* Group heading inside a form. Used on the change-password page to separate the box
   that confirms who you are from the boxes you are choosing — the first one does not
   react to typing, and without the split that looks like a fault. */
.fieldgroup {
  font-size: 12px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--blue); margin: 6px 0 10px; padding-top: 14px;
  border-top: 1px solid var(--border);
}
.fieldgroup:first-of-type { padding-top: 0; border-top: 0; }

/* Upload progress (static/js/submitting.js). The solid bar is bytes actually sent;
   the striped state is the server working, which cannot be measured and is not
   pretended to be. */
.uploadbar {
  display: block; height: 8px; border-radius: 999px; overflow: hidden;
  background: var(--border); margin: 12px 0 6px;
}
.uploadbar-fill {
  display: block; height: 100%; width: 0; background: var(--blue);
  transition: width .2s linear;
}
.uploadbar.working .uploadbar-fill {
  background: repeating-linear-gradient(115deg, var(--blue) 0 12px, var(--blue-mid) 12px 24px);
  animation: uploadbar-move 1s linear infinite;
}
@keyframes uploadbar-move { to { background-position: 24px 0; } }
.uploadbar-note { font-size: 13px; color: var(--muted); margin: 0 0 4px; }
@media (prefers-reduced-motion: reduce) {
  .uploadbar-fill { transition: none; }
  .uploadbar.working .uploadbar-fill { animation: none; }
}

/* ── FILE PREVIEW OVERLAY (static/js/lightbox.js) ──────────────────────────
   Full-bleed on a phone, inset on a desktop. The stage is the only scrolling
   thing; the page behind is frozen so arrowing does not move it. */
html.lightbox-open { overflow: hidden; }
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(4, 12, 32, .88);
  display: flex; flex-direction: column;
}
.lightbox[hidden] { display: none; }
.lightbox-bar {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; background: var(--blue-deep); color: #fff;
  flex: none; flex-wrap: wrap;
}
.lightbox-cap {
  flex: 1 1 200px; min-width: 0; font-size: 14px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lightbox-btn {
  font: inherit; font-size: 14px; font-weight: 700;
  background: rgba(255,255,255,.14); color: #fff;
  border: 0; border-radius: 8px; padding: 9px 14px; min-height: 40px;
  cursor: pointer; flex: none;
}
.lightbox-btn:hover { background: rgba(255,255,255,.26); }
.lightbox-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.lightbox-btn[hidden] { display: none; }
.lightbox-stage {
  flex: 1; min-height: 0; padding: 12px;
  display: flex; align-items: center; justify-content: center;
}
.lightbox-stage img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  border-radius: 8px; background: #fff;
}
.lightbox-stage iframe {
  width: 100%; height: 100%; border: 0; border-radius: 8px; background: #fff;
}
@media (min-width: 900px) {
  .lightbox-stage { padding: 20px 28px 28px; }
  .lightbox-stage iframe, .lightbox-stage img { max-width: 1100px; }
}

/* The submit button while the form is in flight (static/js/submitting.js). */
[data-busy][disabled] { opacity: .75; cursor: progress; }
.filerow-actions { white-space: nowrap; }

/* A draft: saved, but deliberately not in the library yet. Blue rather than amber —
   it is a state, not a warning, and nothing is wrong with it. */
.chip-draft {
  background: #EFF6FF; color: #1D4ED8; border: 1px solid #93C5FD; font-weight: 700;
}

/* The key file — the one document to open first. Amber because it is the page's
   accent for "pay attention to this", and it is the only chip that ever competes
   with the category colour, so it wins by being the warm one. */
.chip-key {
  background: var(--amber-light); color: #92400E;
  border: 1px solid var(--amber); font-weight: 700;
}

/* ── OFFICE READING VIEW (templates/office_preview.html) ───────────────────
   Shown inside the preview overlay's iframe. Plain on purpose: this is what the
   document says, not what it looks like. */
body.doc-preview { background: #fff; padding: 18px 20px 40px; max-width: 46rem; margin: 0 auto; }
.doc-preview-note {
  font-size: 13px; color: #92400E; background: var(--amber-light);
  border: 1px solid var(--amber); border-radius: 8px; padding: 9px 12px; margin-bottom: 16px;
}
.doc-preview-text { white-space: pre-wrap; font-size: 15px; line-height: 1.65; color: var(--text); }
.doc-preview-empty { color: var(--muted); font-size: 15px; }
.doc-preview-pics { margin-top: 22px; border-top: 1px solid var(--border); padding-top: 16px; }
.doc-preview-pics h2 {
  font-size: 12px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--blue); margin: 0 0 10px;
}
.doc-preview-pics img {
  max-width: 100%; height: auto; display: block; margin: 0 0 14px;
  border: 1px solid var(--border); border-radius: 8px;
}

/* ── SUPPLY CHECKLIST (static/js/supplies.js) ──────────────────────────────
   Finger-sized rows, because this gets used standing at a supply cupboard with a
   phone in one hand. */
.supply-tools { display: flex; gap: 8px; flex-wrap: wrap; }
.supply-group + .supply-group { margin-top: 26px; }
.supply-group h3 {
  font-size: 12px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--blue); margin: 0 0 6px;
}
.supply-list { list-style: none; margin: 0; padding: 0; }
.supply-list li { border-bottom: 1px solid var(--border); }
.supply-list li:last-child { border-bottom: 0; }
.supply-list label {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 4px; min-height: 44px; cursor: pointer; font-size: 15px;
}
.supply-list input { width: 20px; height: 20px; flex: none; accent-color: var(--blue); }
.supply-list li.got span { text-decoration: line-through; color: var(--muted); }

@media print {
  /* What gets carried to the cupboard: the list, and nothing else. */
  nav.topbar, .page-head, .guide, .supply-tools, .card:not(#supplies),
  footer, .foot { display: none !important; }
  #supplies { border: 0; box-shadow: none; padding: 0; }
  .supply-list label { min-height: 0; padding: 4px 0; }
  .supply-list input { -webkit-appearance: none; appearance: none;
    border: 1px solid #555; width: 14px; height: 14px; border-radius: 2px; }
}

/* ── DROP ZONE (static/js/dropzone.js) ─────────────────────────────────────
   Sits ABOVE the file input, which stays visible and usable — the script is an
   addition, never a replacement, so uploading survives it failing entirely. */
.dropzone {
  border: 2px dashed var(--border); border-radius: var(--radius);
  background: #FAFBFF; padding: 22px 18px; margin-bottom: 12px;
  text-align: center; cursor: pointer; transition: border-color .12s, background .12s;
}
.dropzone:hover { border-color: var(--blue-mid); }
.dropzone.over { border-color: var(--blue); background: var(--blue-light); }
.dropzone-prompt { font-weight: 700; font-size: 15px; color: var(--text); margin: 0; }
.dropzone-sub { font-size: 13px; color: var(--muted); margin: 4px 0 0; }
.dropzone-list {
  font-size: 13px; color: var(--blue); margin: 10px 0 0; font-weight: 600;
  overflow-wrap: anywhere;
}
@media (prefers-reduced-motion: reduce) { .dropzone { transition: none; } }

/* ✗ Password-checklist styles removed with password auth: .pwcheck, the
   :has() rules on input[name="password"], and pwcheck.js itself. */

/* ── PAGE GUIDE (collapsed by default on every page) ──────────────────────── */

.guide {
  background: var(--blue-light); border: 1px solid #C7D7F5;
  border-radius: var(--radius); margin-bottom: 20px;
}
.guide summary {
  cursor: pointer; padding: 13px 16px; font-weight: 700;
  font-size: 14px; color: var(--blue); list-style: none;
}
.guide summary::-webkit-details-marker { display: none; }
.guide summary::before { content: '▸ '; }
.guide[open] summary::before { content: '▾ '; }
.guide .guide-body { padding: 0 16px 16px; font-size: 14px; color: #374151; }
.guide .guide-body p { margin-bottom: 9px; }
.guide .guide-body ul { padding-left: 20px; margin-bottom: 9px; }
.guide .guide-body li { margin-bottom: 4px; }

/* ── LOGIN ────────────────────────────────────────────────────────────────── */

.login-page {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  padding: 24px 16px; position: relative; overflow: hidden; background: var(--blue-deep);
}
.login-page .hero-photos { position: absolute; inset: 0; display: flex; gap: 3px; }
.login-page .hero-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(to right, rgba(0,26,77,.82) 0%, rgba(0,38,100,.75) 50%, rgba(0,26,77,.82) 100%);
}
.login-card {
  position: relative; z-index: 2; width: 100%; max-width: 420px;
  background: var(--card); border-radius: 16px; padding: 30px 24px;
  box-shadow: var(--shadow-lg);
}
.login-head { text-align: center; margin-bottom: 24px; }
.login-head .badge {
  font-size: 11px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 8px;
}
.login-head h1 { font-size: 26px; margin-bottom: 4px; }
.provider {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: 13px; margin-bottom: 10px; min-height: 50px;
  border: 1.5px solid var(--border); border-radius: 10px; background: #fff;
  font: inherit; font-size: 15px; font-weight: 600; color: var(--text);
  text-decoration: none; cursor: pointer;
}
.provider:hover { border-color: var(--blue); background: var(--blue-light); }
.provider .mark { font-weight: 800; width: 22px; text-align: center; }
.provider .mark.g { color: #4285F4; }
.provider .mark.m { color: #00A4EF; }

.or {
  display: flex; align-items: center; gap: 12px;
  margin: 18px 0; color: var(--muted); font-size: 12px;
  text-transform: uppercase; letter-spacing: .08em; font-weight: 700;
}
.or::before, .or::after { content: ''; flex: 1; border-top: 1px solid var(--border); }

/* ── MISC ─────────────────────────────────────────────────────────────────── */

.foot { text-align: center; font-size: 12.5px; color: #9CA3AF; padding: 26px 16px 40px; }
.foot a { color: #9CA3AF; }

/* The library is empty on day one, so these are the most-seen screens in the
   whole app — an oversized emoji and one grey line was the wrong first
   impression. A dashed well reads as a space waiting to be filled, which is
   what it is, and the copy points at the action instead of stating a fact. */
.empty {
  text-align: center; padding: 34px 20px; color: var(--muted);
  border: 1.5px dashed var(--border); border-radius: var(--radius);
  background: linear-gradient(180deg, #FCFDFF, var(--bg));
}
.empty .big {
  font-size: 26px; margin: 0 auto 12px; width: 56px; height: 56px;
  display: flex; align-items: center; justify-content: center;
  background: var(--blue-light); border-radius: 50%;
}
.empty p { font-size: 14px; max-width: 46ch; margin: 0 auto; }
.empty a { font-weight: 700; }

.rowactions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* ── PASSWORD SETUP (auth pages) ──────────────────────────────────────────── */
/* NB: `.or` already exists above, next to the provider buttons — reuse it
   rather than defining a second, near-identical copy. */

/* Collapsed by default so the invite screen still leads with SSO; opens itself
   when a submission failed validation. */
.pw-setup { border: 1px solid var(--border); border-radius: var(--radius); }
.pw-setup summary {
  cursor: pointer; padding: 13px 16px; font-weight: 700; font-size: 14px;
  color: var(--blue); list-style: none;
}
.pw-setup summary::-webkit-details-marker { display: none; }
.pw-setup summary::before { content: '▸ '; }
.pw-setup[open] summary::before { content: '▾ '; }
.pw-setup-body { padding: 0 16px 16px; }

/* ── BROWSE FILTERS ───────────────────────────────────────────────────────── */

.filters {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end;
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 14px; margin-bottom: 18px;
}
.filter-field { display: flex; flex-direction: column; gap: 4px; flex: 1 1 140px; }
.filter-field > span {
  font-size: 11px; font-weight: 800; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted);
}
.filter-field select { width: 100%; }
.filters .btn { flex: 0 0 auto; }

/* ── NAV CATEGORIES MENU ──────────────────────────────────────────────────── */

.nav-dd summary { cursor: pointer; list-style: none; }
.nav-dd summary::-webkit-details-marker { display: none; }
.nav-dd summary::after { content: ' ▾'; opacity: .7; }
/* On a phone the nav is already a stacked column, so the menu just becomes an
   indented list rather than a floating panel — no positioning to get wrong. */
.nav-dd-menu { display: flex; flex-direction: column; padding: 2px 0 6px 12px; }
.nav-dd-menu a {
  display: flex; align-items: center; gap: 8px;
  color: rgba(255,255,255,.85); text-decoration: none;
  font-size: 14px; padding: 9px 12px; border-radius: 6px; min-height: 40px;
}
.nav-dd-menu a:hover { background: rgba(255,255,255,.15); color: #fff; }
.nav-dd-icon { width: 18px; text-align: center; }
.nav-dd-count { margin-left: auto; font-size: 11px; opacity: .6; }

/* ── ACCOUNT MENU (the avatar) ─────────────────────────────────────────────
   Password and Sign out used to sit in the main nav, as peers of Home and Ask.
   They are not destinations, they are settings for the account — and for someone
   who signed in with Google or Microsoft, "Password" advertised a credential
   they do not need. Both now live behind the avatar.

   The panel is absolutely positioned at EVERY width, not just desktop: the
   avatar sits in the top bar rather than inside the collapsible .nav-links, so
   there is no stacked column for it to fall into on a phone. */
.nav-acct { position: relative; flex-shrink: 0; }
.nav-acct summary { cursor: pointer; list-style: none; }
.nav-acct summary::-webkit-details-marker { display: none; }
.nav-acct-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 40;
  min-width: 240px; max-width: min(320px, calc(100vw - 24px));
  padding: 6px; background: var(--blue-deep);
  border-radius: 10px; box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; gap: 2px;
}
.nav-acct-who {
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 12px 12px; margin-bottom: 4px;
  border-bottom: 1px solid rgba(255,255,255,.18);
  min-width: 0;
}
.nav-acct-who strong { color: #fff; font-size: 14px; }
.nav-acct-who span {
  color: rgba(255,255,255,.7); font-size: 12px; line-height: 1.4;
  overflow-wrap: anywhere;
}
.nav-acct-menu a,
.nav-acct-menu button {
  display: flex; align-items: center; gap: 8px;
  color: rgba(255,255,255,.85); text-decoration: none;
  font: inherit; font-size: 14px; text-align: left;
  padding: 9px 12px; border-radius: 6px; min-height: 40px;
  background: none; border: 0; cursor: pointer; width: 100%;
}
.nav-acct-menu a:hover, .nav-acct-menu button:hover {
  background: rgba(255,255,255,.15); color: #fff;
}
.nav-acct-menu a.here { background: rgba(255,255,255,.12); color: #fff; }
.nav-acct-menu form { margin: 0; }

@media (min-width: 900px) {
  .nav-dd { position: relative; }
  .nav-dd-menu {
    position: absolute; top: 100%; left: 0; z-index: 30;
    min-width: 230px; padding: 6px;
    background: var(--blue-deep); border-radius: 10px;
    box-shadow: var(--shadow-lg);
  }
}

/* ── Comparing a re-read against what is there (0.30.0) ──────────────────────
   ★ Mobile first: the two sides STACK by default and only sit side by side once
     there is room. On a phone a two-column diff makes both columns too narrow to
     read, and this screen exists to be read carefully — a cramped comparison is
     how someone ends up ticking everything.
   ★ The proposed side is tinted, the current side is plain. Only one of the two is
     a change, and it should be the one that catches the eye. */
.compare-head {
  display: flex; align-items: flex-start; gap: 10px; cursor: pointer;
  padding-bottom: 12px; margin-bottom: 12px; border-bottom: 1px solid var(--border);
}
.compare-head input { margin-top: 3px; width: 18px; height: 18px; flex: 0 0 auto; }
.compare-pair { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 780px) {
  .compare-pair:not(.stacked) { grid-template-columns: 1fr 1fr; }
}
.compare-side { border: 1px solid var(--border); border-radius: 8px; padding: 12px; }
.compare-side.proposed { background: var(--blue-light); border-color: var(--blue); }
.compare-tag {
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin-bottom: 6px;
}
.compare-text { white-space: pre-wrap; word-break: break-word; line-height: 1.5; }
.compare-text.empty { color: var(--muted); font-style: italic; }
.btn-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

/* ── Choosing the card picture (0.32.0) ──────────────────────────────────────
   ★★ Tiles, not a list of filenames. The card picture is the first thing anyone sees
      of an activity, and it was being chosen automatically and never shown; picking a
      picture by reading "0c008eee7ad2.pdf" is not picking.
   ★ auto-fill with a minimum, so it is two across on a phone and as many as fit on a
     desktop without a media query per breakpoint. */
.cardpick { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
.cardpick form { display: contents; }
.cardpick-tile {
  display: flex; flex-direction: column; gap: 6px; width: 100%; padding: 6px;
  background: var(--card); border: 2px solid var(--border); border-radius: 10px;
  cursor: pointer; text-align: left; font: inherit; color: inherit;
}
.cardpick-tile:hover { border-color: var(--blue-mid); }
.cardpick-tile.chosen { border-color: var(--blue); background: var(--blue-light); }
.cardpick-tile img {
  width: 100%; aspect-ratio: 4/3; object-fit: cover; border-radius: 6px;
  background: var(--blue-light);
}
.cardpick-name {
  font-size: 11px; color: var(--muted); line-height: 1.3;
  overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}
.cardpick-flag { font-size: 11px; font-weight: 600; color: var(--blue); }
/* The same idea, smaller, in the files table. */
/* Playing a video on the activity page.
   ★★ The play button is drawn HERE, in CSS, and that is not a stylistic choice: YouTube's
      poster image would be a request to Google on page load, which is the exact thing the
      click-to-play facade exists to avoid. No remote asset of any kind. */
.video-holder {
  position: relative; margin: 0 0 14px; border-radius: 12px; overflow: hidden;
  background: #111827; aspect-ratio: 16 / 9;
}
.video-facade {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px; text-decoration: none;
  color: #fff; text-align: center; padding: 16px;
}
.video-facade:hover .video-play { transform: scale(1.08); background: var(--blue); }
.video-facade:focus-visible { outline: 3px solid #fff; outline-offset: -6px; }
.video-play {
  width: 62px; height: 62px; border-radius: 50%; background: rgba(255, 255, 255, 0.16);
  border: 2px solid #fff; flex: none; position: relative;
  transition: transform .12s ease, background .12s ease;
}
/* The triangle: a border trick, so there is no image and no glyph to go missing. */
.video-play::after {
  content: ''; position: absolute; top: 50%; left: 54%; transform: translate(-50%, -50%);
  border-style: solid; border-width: 11px 0 11px 18px;
  border-color: transparent transparent transparent #fff;
}
.video-caption { display: block; line-height: 1.35; }
.video-caption .hint-inline { color: rgba(255, 255, 255, 0.72); display: block; }
.video-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* Shown over the frame until the player loads. A dark rectangle doing nothing is
   indistinguishable from a press that did not register. */
.video-loading {
  position: absolute; top: 50%; left: 50%; width: 34px; height: 34px; margin: -17px;
  border: 3px solid rgba(255, 255, 255, .28); border-top-color: #fff; border-radius: 50%;
  animation: video-spin .8s linear infinite; pointer-events: none;
}
@keyframes video-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .video-loading { animation: none; border-top-color: rgba(255, 255, 255, .6); }
}
@media (min-width: 700px) { .video-holder { max-width: 620px; } }

/* Combining two categories. Wraps to a column on a phone, where a row of five controls
   would otherwise squeeze every one of them to nothing. */
.merge-row { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border); }
.merge-row h3 { margin: 0 0 10px; font-size: 15px; }
.merge-controls { display: flex; flex-direction: column; gap: 8px; }
.merge-controls label { font-size: 13px; font-weight: 600; color: var(--muted); }
@media (min-width: 760px) {
  .merge-controls { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 10px; }
  .merge-controls label { font-weight: 400; }
  .merge-controls select, .merge-controls input[type="text"] { flex: 1 1 150px; }
}

/* An activity inside a collection: its title, and the way back out. The rule between
   rows earns its place — every row carries a button, so where one row ends matters. */
.member-list { list-style: none; margin: 0; padding: 0; }
.member-list li {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 0; border-bottom: 1px solid var(--border);
}
.member-list li:last-child { border-bottom: 0; }
.member-list form { margin-left: auto; }

/* How often something has been opened. Quiet on purpose: it is a footnote to the thing
   itself, not a headline, and a number nobody asked for should not shout. */
.usage-line { font-size: 12.5px; color: var(--muted); margin: 6px 0 0; }
.popular-list .hint-inline { margin-left: auto; }

/* Adding an activity that already exists, below what is already in the week. */
.adopt-row { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.adopt-controls { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.adopt-controls select { flex: 1 1 240px; }

.filerow-card { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.filerow-thumb {
  width: 56px; height: 42px; object-fit: cover; border-radius: 6px;
  border: 2px solid var(--border); background: var(--blue-light);
}
.filerow-thumb.chosen { border-color: var(--blue); }

/* ── Collections (1.0.0) ─────────────────────────────────────────────────────
   ★ A collection card must not be mistaken for an activity card sitting in the same
     grid — a week and a craft sheet are different kinds of thing. The badge and the
     heavier border do that without inventing a second layout to maintain. */
.collection-card {
  display: block; background: var(--card); border: 2px solid var(--blue);
  border-radius: var(--radius); overflow: hidden; text-decoration: none;
  color: inherit; box-shadow: var(--shadow);
}
.collection-card:hover { box-shadow: var(--shadow-lg); }
.collection-card .card-thumb { position: relative; }
.collection-badge {
  position: absolute; top: 10px; left: 10px; background: var(--blue); color: #fff;
  font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px;
}
.collection-card h3 { font-size: 17px; margin-bottom: 8px; line-height: 1.3; }
.chip-home { background: var(--blue-light); color: var(--blue); text-decoration: none; }
.chip-home:hover { background: var(--blue); color: #fff; }
.chip-draft { background: var(--amber-light); color: #92400E; }
/* The search results' collection group, so it reads as a different kind of answer. */
.search-group-label {
  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 4px 0 10px;
}

/* ── A button that is working should look like it (1.0.2) ────────────────────
   ★★ submitting.js already set aria-busy and changed the label, but a greyed-out button
      with different words is a weak signal — sflack's report was that Delete "just sits
      there and you have no idea if it's doing anything". Deleting an activity forgets one
      uploaded copy per file over the network, so on a 25-file week there is real dead
      time to account for. A moving spinner is the part the eye actually notices.
   ★ Driven off aria-busy, which the script sets anyway, so no button needs new markup. */
.btn[aria-busy="true"] {
  position: relative;
  padding-left: 34px;
  cursor: progress;
}
.btn[aria-busy="true"]::before {
  content: "";
  position: absolute;
  left: 13px;
  top: 50%;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: btn-spin .7s linear infinite;
  opacity: .85;
}
.btn.small[aria-busy="true"] { padding-left: 26px; }
.btn.small[aria-busy="true"]::before { left: 8px; width: 10px; height: 10px; margin-top: -5px; }
@keyframes btn-spin { to { transform: rotate(360deg); } }
/* A spinner that cannot spin should not pretend to: keep the reserved space and the
   cursor, drop the motion. */
@media (prefers-reduced-motion: reduce) {
  .btn[aria-busy="true"]::before { animation: none; border-right-color: currentColor; opacity: .5; }
}

/* Rotate sits under its tile, in the same grid cell (1.4.1). */
.cardpick-cell { display: flex; flex-direction: column; gap: 4px; }
.cardpick-rotate { align-self: stretch; font-size: 11px; padding: 2px 6px; }

/* Enlarge is a corner badge over the picture, not a button under it (1.4.3).
   ★ A sibling of the tile, positioned over it — inside the label or button it would also
     select or clear the card, which is the click that is already spoken for. */
.cardpick-cell { position: relative; }
.cardpick-zoom {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  width: 26px; height: 26px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.92); color: var(--blue);
  border: 1px solid var(--border); text-decoration: none;
  font-size: 13px; line-height: 1; box-shadow: 0 1px 4px rgba(0,0,0,.18);
}
.cardpick-zoom:hover { background: var(--blue); color: #fff; border-color: var(--blue); }
.cardpick-rotate { font-size: 11px; padding: 2px 6px; }

/* The card picture in the admin list (1.6.0). Small, and a dash where there is none —
   a missing picture is worth seeing at a glance. */
.listthumb-cell { width: 68px; }
.listthumb {
  width: 56px; height: 42px; object-fit: cover; border-radius: 6px;
  border: 1px solid var(--border); background: var(--blue-light); display: block;
}
.listthumb.none {
  width: 56px; height: 42px; border-radius: 6px; border: 1px dashed var(--border);
  display: flex; align-items: center; justify-content: center; color: var(--muted);
  font-size: 13px;
}
@media (max-width: 780px) { .listthumb-cell { width: auto; } }

/* ── The chosen card picture follows the radio, not the server (1.7.1) ────────
   ★★ The review screen painted `.chosen` from the proposal and never moved it, so clicking
      a different picture selected it without LOOKING selected. Driving the highlight from
      :checked makes the radio the single source of truth.
   ★ The tick is CSS too, so it moves with the outline rather than being another thing that
     can disagree. */
.cardpick-tile:has(input:checked) {
  border-color: var(--blue);
  background: var(--blue-light);
  box-shadow: 0 0 0 1px var(--blue);
}
.cardpick-tile:has(input:checked)::after {
  content: "✓ On the card";
  font-size: 11px;
  font-weight: 600;
  color: var(--blue);
}
/* Keyboard users get the same outline the mouse gets. */
.cardpick-tile:focus-within { outline: 2px solid var(--blue-mid); outline-offset: 2px; }

/* ── The week's plan, leading its collection page (1.9.0) ────────────────────
   ★ Mobile first: stacks, then sits side by side once there is room. The point is that it
     reads as the page's opening statement rather than a row in a list. */
.plan-lead {
  display: flex; flex-direction: column; gap: 14px;
  border-left: 4px solid var(--blue);
}
@media (min-width: 700px) {
  .plan-lead { flex-direction: row; align-items: center; justify-content: space-between; }
  .plan-lead-actions { flex: 0 0 auto; }
}
.plan-lead-body { min-width: 0; }
.plan-lead h2 { margin: 2px 0 6px; overflow-wrap: anywhere; }
.plan-lead-eyebrow {
  font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--blue);
}
.plan-lead-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ★ 1.10.0 — the split review screen is editable in place. The base input rules above
   already give these boxes their border, padding and focus ring, so all that is needed is
   the weight the old <strong> title heading carried (a list of seventeen still has to scan)
   and a taller overview box. ⚠️ --line and --ink DO NOT EXIST in this stylesheet; the names
   are --border and --text. An undefined var falls back to nothing and looks deliberate. */
.titlefield { margin: 2px 0 12px }
.titlefield label { font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); display: block; margin-bottom: 4px }
.titlefield input[type=text] { font-size: 17px; font-weight: 600 }
textarea.compare-edit { min-height: 170px; line-height: 1.5 }

/* ★ 1.13.0 — the Ask hand-off. A button that looks like a link, because it IS a second
   destination for the same typed words, not a second action on the same one. Sits on the
   hero photograph, so its colour comes from the hero's own text scale rather than --blue,
   which would vanish against the overlay. */
.hero-ask {
  max-width: 600px; margin: 12px auto 0; text-align: center;
  font-size: 14px; color: rgba(255,255,255,.82); line-height: 1.5;
}
.hero-ask .linklike {
  background: none; border: none; padding: 0; font: inherit; font-weight: 700;
  color: #fff; text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.hero-ask .linklike:hover { color: var(--amber-light); }
.hero-ask .linklike:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
/* The empty-search offer. Bordered like the amber notices elsewhere: it is a suggestion
   after a dead end, not an error. */
.askoffer { border-left: 3px solid var(--amber); display: flex; flex-direction: column;
  align-items: flex-start; gap: 10px; margin-top: 16px; }

/* ★ 1.15.0 — the tag control. Chips in front of a plain text field, which stays the
   source of truth so the control degrades to something typeable. */
.tagpick { display: flex; flex-direction: column; gap: 8px; }
.tagpick-hidden { display: none; }
.tagpick-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.tagpick-chip { display: inline-flex; align-items: center; gap: 6px; }
.tagpick-x {
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  font: inherit; font-size: 15px; line-height: 1; color: inherit; opacity: .65;
}
.tagpick-x:hover { opacity: 1; }
.tagpick-x:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.tagpick-entry { max-width: 340px; }

/* ★ 1.17.0 — the category icon picker. Lesley: "from a desktop I don't know how to get
   an emoji to add." The panel opens in the flow under its field, so it is impossible to
   position wrongly or clip, and the emoji themselves are set large enough to be aimed at
   with a thumb (44px targets on a phone, per the mobile-first pass). */
.emojipick { display: inline-flex; align-items: center; }
.emojipick-btn {
  background: var(--blue-light); color: var(--blue); border: 1px solid transparent;
  border-radius: 8px; font: inherit; font-size: 14px; font-weight: 600;
  padding: 9px 12px; min-height: 40px; cursor: pointer; white-space: nowrap;
}
.emojipick-btn:hover { background: var(--blue); color: #fff; }
.emojipick-btn:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
/* ★ 1.17.1 — sflack's screenshot: the panel was as wide as the card, so twelve glyphs
   sat in one long stripe against ~800px of empty space and stopped reading as a grid.
   Capped, so each group is two tidy rows directly under the field the eye is already on.
   No top padding: the sticky headings below own the top edge. */
.emojipick-panel {
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--card);
  padding: 0 14px 14px; margin-top: 10px; overflow-y: auto;
  max-width: 520px; max-height: min(420px, 60vh);
  box-shadow: var(--shadow);
}
/* ★ Sticky, because the fold was slicing "MUSIC & STAGE" in half, which reads as broken
   rather than as scrollable. The heading you are inside now stays whole and legible at
   the top; a half-visible ROW of emoji under it is an honest "more below". */
.emojipick-group {
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 0; padding: 10px 0 6px;
  position: sticky; top: 0; z-index: 1; background: var(--card);
}
.emojipick-grid { display: flex; flex-wrap: wrap; gap: 4px; }
.emojipick-choice {
  background: none; border: 1px solid transparent; border-radius: 8px;
  width: 44px; height: 44px; font-size: 22px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.emojipick-choice:hover { background: var(--blue-light); border-color: var(--blue); }
.emojipick-choice:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }
.emojipick-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border);
}
.emojipick-foot .hint { margin: 0; flex: 1 1 200px; }
/* In a table row the panel is a child of the cell, below the editor line. */
td > .emojipick-panel { margin-top: 8px; }
