/* Chapter portal — design tokens, layout and components.
   Data-display colours follow a validated palette; the brand colour is set per
   chapter from Settings and drives only the chrome. */

:root {
  color-scheme: light;

  --surface:      #fcfcfb;
  --surface-2:    #f3f3f0;
  --page:         #f9f9f7;
  --ink:          #0b0b0b;
  --ink-2:        #52514e;
  --muted:        #898781;
  --line:         #e1e0d9;
  --line-strong:  #c3c2b7;

  /* Status — fixed, never themed. Always paired with an icon and a label. */
  --good:      #0ca30c;
  --warning:   #fab219;
  --serious:   #ec835a;
  --critical:  #d03b3b;
  --good-bg:     #e9f6e9;
  --warning-bg:  #fdf3dd;
  --critical-bg: #fbeaea;

  /* Chart series (validated categorical order, light steps) */
  --series-1: #2a78d6;
  --series-2: #eb6834;
  --series-3: #1baf7a;

  --masthead:    #17171a;
  --masthead-2:  #232328;
  --masthead-ink:#f4f3ef;

  --radius:   10px;
  --radius-s: 7px;
  --shadow:   0 1px 2px rgba(11,11,11,.05), 0 4px 14px rgba(11,11,11,.05);
  --shadow-lg:0 6px 30px rgba(11,11,11,.14);
  --ring:     0 0 0 3px color-mix(in srgb, var(--brand) 30%, transparent);
}

@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --surface:     #1a1a19;
    --surface-2:   #232322;
    --page:        #0d0d0d;
    --ink:         #ffffff;
    --ink-2:       #c3c2b7;
    --muted:       #898781;
    --line:        #2c2c2a;
    --line-strong: #383835;
    --good-bg:     #14301f;
    --warning-bg:  #33280d;
    --critical-bg: #331a1a;
    --series-1: #3987e5;
    --series-2: #d95926;
    --series-3: #199e70;
    --shadow:   0 1px 2px rgba(0,0,0,.4), 0 4px 14px rgba(0,0,0,.3);
    --shadow-lg:0 6px 30px rgba(0,0,0,.5);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface:     #1a1a19;
  --surface-2:   #232322;
  --page:        #0d0d0d;
  --ink:         #ffffff;
  --ink-2:       #c3c2b7;
  --muted:       #898781;
  --line:        #2c2c2a;
  --line-strong: #383835;
  --good-bg:     #14301f;
  --warning-bg:  #33280d;
  --critical-bg: #331a1a;
  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --shadow:   0 1px 2px rgba(0,0,0,.4), 0 4px 14px rgba(0,0,0,.3);
  --shadow-lg:0 6px 30px rgba(0,0,0,.5);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
a { color: inherit; }
h1, h2, h3 { letter-spacing: -.02em; margin: 0; }
code, .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em; }
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 4px; }

/* ------------------------------------------------------------- masthead -- */
.masthead {
  background: var(--masthead);
  color: var(--masthead-ink);
  border-bottom: 3px solid var(--brand);
  position: sticky; top: 0; z-index: 40;
}
.masthead-inner {
  max-width: 1440px; margin: 0 auto; padding: 0 22px;
  display: flex; align-items: center; gap: 26px; min-height: 62px;
}
.brand { display: flex; align-items: center; gap: 11px; text-decoration: none; color: inherit; }
.brand-logo { height: 38px; width: auto; max-width: 190px; display: block; }
.brand-mark {
  width: 34px; height: 34px; border-radius: 8px; flex: none;
  background: var(--brand); color: var(--on-brand);
  display: grid; place-items: center; font-weight: 800; font-size: 18px;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.2; }
.brand-text strong { font-size: 15px; letter-spacing: -.01em; }
.brand-text small { font-size: 11.5px; color: #a6a49c; text-transform: uppercase; letter-spacing: .09em; }

.mainnav { display: flex; gap: 3px; margin-left: auto; flex-wrap: wrap; }
.mainnav a {
  padding: 8px 14px; border-radius: var(--radius-s); text-decoration: none;
  color: #c9c7c0; font-weight: 500; font-size: 14.5px; white-space: nowrap;
}
.mainnav a:hover { background: var(--masthead-2); color: #fff; }
.mainnav a.current { background: var(--brand); color: var(--on-brand); }

.usermenu { position: relative; }
.usermenu summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; gap: 9px;
  padding: 5px 9px 5px 5px; border-radius: 99px; background: var(--masthead-2);
}
.usermenu summary::-webkit-details-marker { display: none; }
.avatar {
  width: 29px; height: 29px; border-radius: 50%; flex: none;
  background: var(--brand); color: var(--on-brand);
  display: grid; place-items: center; font-size: 12px; font-weight: 700;
}
.uname { font-size: 13.5px; color: #e8e6e0; }
.usermenu-panel {
  position: absolute; right: 0; top: calc(100% + 9px); width: 268px;
  background: var(--surface); color: var(--ink); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lg); padding: 7px; z-index: 50;
}
.usermenu-head { margin: 0; padding: 9px 11px; display: flex; flex-direction: column; }
.usermenu-head span { color: var(--muted); font-size: 12.5px; }
.usermenu-roles { margin: 0; padding: 0 11px 9px; display: flex; flex-wrap: wrap; gap: 4px; }
.usermenu-panel a, .usermenu-panel .linkbtn, .usermenu-panel .themetoggle {
  display: block; width: 100%; text-align: left; padding: 8px 11px;
  border-radius: var(--radius-s); text-decoration: none; border: 0;
  background: none; color: inherit; font: inherit; cursor: pointer;
}
.usermenu-panel a:hover, .usermenu-panel button:hover { background: var(--surface-2); }
.usermenu-panel form { border-top: 1px solid var(--line); margin-top: 5px; padding-top: 5px; }

/* ----------------------------------------------------------------- page -- */
.page { max-width: 1440px; margin: 0 auto; padding: 26px 22px 70px; }
.page.narrow { max-width: 900px; }
.page-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 18px; flex-wrap: wrap; margin-bottom: 20px;
}
.page-head h1 { font-size: 25px; }
.page-head .sub { color: var(--muted); margin: 3px 0 0; font-size: 14px; }
.page-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.breadcrumb { font-size: 13px; color: var(--muted); margin-bottom: 5px; }
.breadcrumb a { text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }
.section { margin-top: 30px; }
.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 12px; flex-wrap: wrap;
}
.section-head h2 { font-size: 13px; text-transform: uppercase; letter-spacing: .09em; color: var(--muted); }
.pagefoot {
  max-width: 1440px; margin: 0 auto; padding: 20px 22px 34px;
  display: flex; gap: 18px; flex-wrap: wrap;
  color: var(--muted); font-size: 13px; border-top: 1px solid var(--line);
}

/* ---------------------------------------------------------------- flash -- */
.flashes { margin-bottom: 18px; display: grid; gap: 8px; }
.flash {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 11px 14px; border-radius: var(--radius); font-size: 14.5px;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--shadow);
}
.flash-icon {
  width: 20px; height: 20px; border-radius: 50%; flex: none; margin-top: 1px;
  display: grid; place-items: center; font-size: 12px; font-weight: 700; color: #fff;
}
.flash.success { background: var(--good-bg); border-color: color-mix(in srgb, var(--good) 35%, transparent); }
.flash.success .flash-icon { background: var(--good); }
.flash.error { background: var(--critical-bg); border-color: color-mix(in srgb, var(--critical) 35%, transparent); }
.flash.error .flash-icon { background: var(--critical); }
.flash.warning { background: var(--warning-bg); border-color: color-mix(in srgb, var(--warning) 45%, transparent); }
.flash.warning .flash-icon { background: var(--warning); color: #3a2800; }
.flash.info .flash-icon, .flash.message .flash-icon { background: var(--ink-2); }

/* --------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex; align-items: center; gap: 7px; justify-content: center;
  padding: 8px 15px; border-radius: var(--radius-s); cursor: pointer;
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink);
  font: 500 14px/1.3 inherit; text-decoration: none; white-space: nowrap;
}
/* A button never wider than what holds it. Its label does not wrap, so on a
   narrow screen a long one would otherwise push its row off the screen. */
.btn { max-width: 100%; }
.btn:hover { background: var(--surface-2); }
.btn.primary { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.btn.primary:hover { background: var(--brand-strong); border-color: var(--brand-strong); }
.btn.danger { border-color: var(--critical); color: var(--critical); background: transparent; }
.btn.danger:hover { background: var(--critical); color: #fff; }
.btn.ghost { border-color: transparent; background: transparent; }
.btn.ghost:hover { background: var(--surface-2); }
.btn.small { padding: 5px 11px; font-size: 13px; }
.btn.tiny { padding: 3px 9px; font-size: 12.5px; }
.btn[disabled], .btn.disabled { opacity: .45; pointer-events: none; }
.linkbtn { background: none; border: 0; color: inherit; font: inherit; cursor: pointer; padding: 0; }

/* ----------------------------------------------------------------- card -- */
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.card-pad { padding: 18px 20px; }
.card + .card { margin-top: 14px; }
.card-head {
  padding: 14px 20px; border-bottom: 1px solid var(--line);
  display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap;
}
.card-head h2, .card-head h3 { font-size: 15px; }
.card-head .hint { color: var(--muted); font-size: 13px; font-weight: 400; }
.card-foot { padding: 11px 20px; border-top: 1px solid var(--line); font-size: 13.5px; color: var(--muted); }

/* min(Xpx, 100%) on every auto-fit floor. A bare minmax(330px, 1fr) has a
   330px floor even when the container is 288px wide, so the track -- and the
   page with it -- overflows on a narrow phone. min() lets the floor collapse
   to the container when the container is the smaller of the two. */
/* Every one-column grid used as a vertical stack. Without an explicit track
   their single implicit column is sized `auto`, whose minimum is the widest
   min-content inside it -- so one non-wrapping line in one card widens the
   column, the stack, and the page. Naming them here rather than fixing each
   in turn: they are all the same shape and all had the same hole. */
.stack, .rows, .barlist, .flashes, .form, ul.comments {
  grid-template-columns: minmax(0, 1fr);
}

.grid { display: grid; gap: 14px; }
.grid.two   { grid-template-columns: repeat(auto-fit, minmax(min(330px, 100%), 1fr)); }
.grid.three { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.grid.wide-left { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
/* minmax(0, 1fr), not 1fr. A bare `1fr` is minmax(auto, 1fr), and that `auto`
   minimum is the item's min-content -- so a card holding anything that will
   not wrap stretches the track past the container and takes the page sideways
   with it. This is what pushed the dashboard's cards off a phone screen: the
   column grew to 566px on a 390px display. The two-column rule above always
   had the clamp; the single-column override had dropped it. */
@media (max-width: 1000px) { .grid.wide-left { grid-template-columns: minmax(0, 1fr); } }

/* ------------------------------------------------------------ stat tiles -- */
.stats { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.stat {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px; box-shadow: var(--shadow); text-decoration: none; display: block;
}
a.stat:hover { border-color: var(--brand); }
.stat-label { font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); }
.stat-value { font-size: 32px; font-weight: 650; letter-spacing: -.03em; margin: 5px 0 0; line-height: 1.1; }
.stat-meta { font-size: 13px; color: var(--muted); margin-top: 3px; }
.stat.attention { border-left: 3px solid var(--warning); }
.stat.alarm { border-left: 3px solid var(--critical); }

/* --------------------------------------------------------------- tables -- */
.table-wrap { overflow-x: auto; border-radius: var(--radius); }
table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
table.data th, table.data td {
  text-align: left; padding: 9px 14px; border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
table.data thead th {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); font-weight: 600; white-space: nowrap;
  background: var(--surface-2); border-bottom: 1px solid var(--line);
}
table.data tbody tr:hover { background: var(--surface-2); }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data td.num, table.data th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.data td.nowrap, table.data th.nowrap { white-space: nowrap; }
table.data a { font-weight: 500; }
.rowsub { display: block; color: var(--muted); font-size: 12.5px; }
.empty { padding: 44px 20px; text-align: center; color: var(--muted); }
.empty strong { display: block; color: var(--ink); margin-bottom: 4px; font-size: 15px; }

/* ---------------------------------------------------- pills and statuses -- */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: 99px; font-size: 12px; font-weight: 500;
  background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line);
  white-space: nowrap;
}
.pill.brand { background: color-mix(in srgb, var(--brand) 14%, transparent);
              border-color: color-mix(in srgb, var(--brand) 35%, transparent); color: var(--brand-step); }
.status {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 500; white-space: nowrap;
}
.status .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.status .sym { font-weight: 700; font-size: 12px; }
.status.good     { color: var(--good); }
.status.good .dot     { background: var(--good); }
.status.warning  { color: #8a6400; }
.status.warning .dot  { background: var(--warning); }
.status.critical { color: var(--critical); }
.status.critical .dot { background: var(--critical); }
.status.neutral  { color: var(--muted); }
.status.neutral .dot  { background: var(--line-strong); }
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) .status.warning { color: var(--warning); }
}
:root[data-theme="dark"] .status.warning { color: var(--warning); }

/* ---------------------------------------------------------------- forms -- */
.form { display: grid; gap: 18px; }
fieldset.group {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 20px 20px; margin: 0; background: var(--surface);
}
fieldset.group > legend {
  padding: 0 8px; margin-left: -8px; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em; color: var(--muted);
}
.fields { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
/* A datalist is a picklist, not content. Browsers know that; a layout engine
   measuring the page for a width check does not, and measured its options. */
datalist { display: none; }
.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.field.span-2 { grid-column: span 2; }
.field.span-all { grid-column: 1 / -1; }
@media (max-width: 620px) { .field.span-2 { grid-column: 1 / -1; } }
.field > label { font-size: 13px; font-weight: 600; }
.field .req { color: var(--critical); }
.field .help { font-size: 12px; color: var(--muted); }
input[type=text], input[type=email], input[type=password], input[type=number],
input[type=date], input[type=time], input[type=search], input[type=url],
input[type=datetime-local], select, textarea {
  width: 100%; padding: 8px 11px; font: inherit; font-size: 14px;
  border: 1px solid var(--line-strong); border-radius: var(--radius-s);
  background: var(--page); color: var(--ink);
}
textarea { resize: vertical; min-height: 76px; }
select { appearance: none; background-image:
  linear-gradient(45deg, transparent 50%, var(--muted) 50%),
  linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 16px) 52%, calc(100% - 11px) 52%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 30px; }
.check { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 400; }
.check input { width: 16px; height: 16px; accent-color: var(--brand); }
.checks { display: grid; gap: 9px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.form-actions {
  display: flex; gap: 9px; align-items: center; flex-wrap: wrap;
  padding-top: 4px;
}
.form-actions .spacer { margin-left: auto; }
.problems {
  background: var(--critical-bg); border: 1px solid color-mix(in srgb, var(--critical) 35%, transparent);
  border-radius: var(--radius); padding: 12px 16px; margin-bottom: 16px;
}
.problems strong { display: block; margin-bottom: 5px; color: var(--critical); font-size: 14px; }
.problems ul { margin: 0; padding-left: 19px; font-size: 14px; }
/* Something to look at rather than something to fix. The possible-duplicate
   panel is advisory -- it never stops the officer -- so it must not read as
   the same red refusal as a required field left empty. */
.problems.warn {
  background: var(--warning-bg);
  border-color: color-mix(in srgb, var(--warning) 45%, transparent);
}
.problems.warn strong { color: var(--ink); }
.problems.warn .help { display: block; margin: 8px 0 0; }

/* ------------------------------------------------------ the add-member steps */
/* Wraps rather than scrolls: five labels do not fit across a phone, and a
   progress indicator that runs off the side is worse than one on two lines. */
.wizsteps {
  display: flex; flex-wrap: wrap; gap: 7px 16px;
  margin: 0 0 16px; padding: 0; list-style: none; font-size: 13px;
}
.wizsteps li { display: flex; align-items: center; gap: 8px; min-width: 0; color: var(--muted); }
.wizsteps .t { min-width: 0; overflow-wrap: break-word; }
.wizsteps .n {
  display: grid; place-items: center; flex: none; width: 23px; height: 23px;
  border: 1px solid var(--line-strong); border-radius: 50%;
  font-size: 12px; font-weight: 700;
}
.wizsteps li.done { color: var(--ink-2); }
.wizsteps li.done .n { border-color: var(--good); color: var(--good); }
.wizsteps li.current { color: var(--ink); font-weight: 600; }
.wizsteps li.current .n {
  background: var(--brand); border-color: var(--brand); color: var(--on-brand);
}

/* ------------------------------------------------------- the ride wizard -- */
/* The progress strip. Flex-wrap rather than a four-column grid: on a 320px
   phone the four labels wrap to two lines instead of forcing the page wider
   than the screen. */
.steps {
  display: flex; flex-wrap: wrap; gap: 6px 10px;
  list-style: none; margin: 0 0 18px; padding: 0;
}
.steps li {
  display: flex; align-items: center; gap: 7px; min-width: 0;
  font-size: 13.5px; color: var(--muted);
}
.steps li + li::before {
  content: ""; width: 14px; height: 1px; background: var(--line-strong); flex: none;
}
.steps .n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  border: 1px solid var(--line-strong); font-size: 12px; font-weight: 600;
}
.steps .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.steps li.on { color: var(--ink); font-weight: 600; }
.steps li.on .n { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.steps li.done { color: var(--ink-2); }
.steps li.done .n { border-color: var(--good); color: var(--good); }

/* The destination chooser: one tap target per place, not a select of 335. */
.picks { display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; margin-top: 6px; }
.pick {
  display: flex; align-items: flex-start; gap: 11px; min-width: 0;
  padding: 11px 13px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); cursor: pointer;
}
.pick:hover { background: var(--surface-2); }
.pick input { margin-top: 2px; width: 18px; height: 18px; flex: none; accent-color: var(--brand); }
.pick-body { min-width: 0; }
/* Both halves of the clamp are needed: the name does not wrap, so its own
   preferred width is the whole string. */
.pick-body strong { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pick-body span { display: block; }
.pick:has(input:checked) { border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand); }
.pick.new { border-style: dashed; }

/* "We may already have this place" -- a warning that offers a way out. */
.notice {
  margin: 14px 0 0; padding: 12px 15px; border-radius: var(--radius);
  background: var(--warning-bg);
  border: 1px solid color-mix(in srgb, var(--warning) 35%, transparent);
}
.notice p { margin: 4px 0 8px; }
.notice ul { margin: 0; }
.notice .with-action { align-items: center; justify-content: space-between; }
/* "Change" beside a review heading reads as a link, not as part of the title. */
.form fieldset.group > legend .linkbtn {
  margin-left: 9px; font-size: 12.5px; font-weight: 400;
  color: var(--brand-step); text-decoration: underline;
}

/* --------------------------------------------------------------- filters -- */
.filters {
  display: flex; gap: 9px; flex-wrap: wrap; align-items: center;
  padding: 13px 16px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); margin-bottom: 16px; box-shadow: var(--shadow);
}
.filters input[type=search], .filters input[type=text] { min-width: 200px; flex: 1 1 230px; max-width: 380px; }
.filters select, .filters input[type=number] { width: auto; min-width: 130px; }
.filters .spacer { margin-left: auto; }
.segmented { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--radius-s); overflow: hidden; }
.segmented a {
  padding: 7px 13px; font-size: 13.5px; text-decoration: none; color: var(--ink-2);
  border-right: 1px solid var(--line-strong);
}
.segmented a:last-child { border-right: 0; }
.segmented a:hover { background: var(--surface-2); }
.segmented a.on { background: var(--brand); color: var(--on-brand); font-weight: 500; }

/* -------------------------------------------------------------- calendar -- */
.calendar { width: 100%; border-collapse: collapse; table-layout: fixed; }
.calendar th {
  padding: 8px; font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); border-bottom: 1px solid var(--line); text-align: center;
}
.calendar td {
  border: 1px solid var(--line); vertical-align: top; height: 116px; padding: 5px;
  background: var(--surface);
}
.calendar td.outside { background: var(--page); }
.calendar td.today { box-shadow: inset 0 0 0 2px var(--brand); }
.calendar .daynum { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.calendar td.today .daynum { color: var(--brand-step); font-weight: 700; }
.calendar .holiday { font-size: 11px; color: var(--muted); display: block; margin-top: 2px; }
.calevent {
  display: block; margin-top: 4px; padding: 4px 6px; border-radius: 5px;
  font-size: 12px; line-height: 1.3; text-decoration: none;
  background: var(--surface-2); border-left: 3px solid var(--series-1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.calevent:hover { background: color-mix(in srgb, var(--brand) 12%, var(--surface-2)); }
.calevent.ride    { border-left-color: var(--series-2); }
.calevent.meeting { border-left-color: var(--series-1); }
.calevent.other   { border-left-color: var(--series-3); }
.calevent .att { color: var(--muted); font-variant-numeric: tabular-nums; }
.calnav { display: flex; align-items: center; gap: 10px; }
.calnav h2 { font-size: 18px; min-width: 190px; text-align: center; }

/* ----------------------------------------------------------------- bars -- */
.barlist { display: grid; gap: 9px; }
.barrow { display: grid; grid-template-columns: minmax(120px, 1.4fr) minmax(0, 3fr) auto; gap: 12px; align-items: center; font-size: 13.5px; }
.barrow .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* `display: flex` is doing real work here, on both elements, and taking it
   off makes every bar in the portal vanish.

   The track and the fill are both `<span>`s. Inside `.barrow` the track is a
   grid item and so is blockified for free, but on the shop's run pages it
   sits on its own in a card and stayed inline -- and `width`, `height` and
   `min-width` do not apply to a non-replaced inline box. The fill was inline
   everywhere, so its `width: 72%` was ignored on every screen: an empty span
   is zero wide, painting no background, and every bar drew as a bare track.
   The figures beside them were right, which is what made it look like a
   styling nicety rather than the chart being absent.

   Flex rather than `display: block` because the fill has to sit beside its
   siblings when a bar is split into segments, and one rule that always
   applies beats a second that has to be remembered. */
.bartrack { display: flex; background: var(--surface-2); border-radius: 4px; height: 12px; overflow: hidden; }
.barfill { height: 100%; background: var(--brand); border-radius: 0 4px 4px 0; min-width: 3px; }
.barrow .val { font-variant-numeric: tabular-nums; color: var(--ink-2); min-width: 46px; text-align: right; }
/* The 120px floor under the name column plus the value column is more than a
   narrow phone has to give. The name can take less; it ellipsises anyway. */
@media (max-width: 560px) {
  .barrow { grid-template-columns: minmax(0, 1.2fr) minmax(0, 2fr) auto; gap: 9px; }
  .barrow .name { min-width: 0; }
}
/* A supplier run that has reached its minimum. Colour is never the only
   signal: the figures beside it say so in words as well. */
.barfill.met { background: var(--good); }
.bartrack.tall { height: 16px; }

/* A bar split by the kind of work, rather than shaded by how much of it.
   Only the last segment rounds: the rounded end marks where the total stops.

   The 2px separator is a transparent border inside each segment's own width
   rather than a margin between them. The widths add up to 100% on the longest
   bar, so a margin would push its rounded end past the track and
   `overflow: hidden` would cut it off; `background-clip: padding-box` keeps
   the fill out of the border so the track shows through as a gap. A visible
   rule would read as a border drawn on the data instead. */
.bartrack.stacked { align-items: stretch; }
.barseg {
  height: 100%; min-width: 3px; background: var(--brand);
  box-sizing: border-box; border-right: 2px solid transparent;
  background-clip: padding-box;
}
.barseg:last-child { border-right: 0; border-radius: 0 4px 4px 0; }
.barseg.led   { background: var(--series-1); }
.barseg.swept { background: var(--series-2); }
.barseg.worked { background: var(--series-3); }
/* The year in review splits a bar the same way, into the two halves the
   dinner is asking about: somewhere new against somewhere the chapter had
   already been, and out for the first time against out before. The same two
   validated series colours, because it is the same kind of claim -- which,
   not how much -- and both are named in a key beside the chart. */
.barseg.fresh { background: var(--series-1); }
.barseg.again { background: var(--series-2); }

/* The key that stops colour being the only thing carrying identity. */
.barkey { display: flex; flex-wrap: wrap; gap: 6px 16px; list-style: none;
          margin: 0; padding: 0; font-size: 13px; color: var(--ink-2); }
.barkey li { display: flex; align-items: center; gap: 7px; }
.barkey-swatch { width: 10px; height: 10px; border-radius: 2px; flex: none;
                 background: var(--brand); }
.barkey-swatch.led   { background: var(--series-1); }
.barkey-swatch.swept { background: var(--series-2); }
.barkey-swatch.worked { background: var(--series-3); }
.barkey-swatch.fresh { background: var(--series-1); }
.barkey-swatch.again { background: var(--series-2); }
.runfigures { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 8px; font-size: 14px; }

/* -------------------------------------------------------------- listbox -- */
.rows { display: grid; }
.rowitem {
  display: flex; align-items: center; gap: 13px; padding: 11px 20px;
  border-bottom: 1px solid var(--line); text-decoration: none;
}
.rowitem:last-child { border-bottom: 0; }
.rowitem:hover { background: var(--surface-2); }
.rowitem .when {
  flex: none; width: 54px; text-align: center; line-height: 1.15;
  border-right: 1px solid var(--line); padding-right: 11px;
}
.rowitem .when .d { font-size: 19px; font-weight: 650; display: block; }
.rowitem .when .m { font-size: 11px; text-transform: uppercase; color: var(--muted); letter-spacing: .06em; }
.rowitem .body { min-width: 0; flex: 1; }
/* max-width as well as the parent's min-width: 0. The title does not wrap, so
   its own preferred width is the whole string; both halves of the clamp are
   needed for it to actually give. */
.rowitem .body strong { display: block; max-width: 100%; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rowitem .body span { color: var(--muted); font-size: 13px; }
.rowitem .end { margin-left: auto; text-align: right; flex: none; }

/* -------------------------------------------------------------- pager --- */
.pager { display: flex; align-items: center; gap: 14px; margin-top: 16px; color: var(--muted); font-size: 14px; }

/* ----------------------------------------------------------- attendance -- */
.att-toolbar {
  position: sticky; top: 62px; z-index: 20; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 16px; margin-bottom: 14px; box-shadow: var(--shadow);
  display: flex; gap: 10px; flex-wrap: wrap; align-items: center;
}
.att-count { font-weight: 650; font-variant-numeric: tabular-nums; }
table.att td { padding: 6px 12px; }
table.att tr.on { background: color-mix(in srgb, var(--brand) 8%, transparent); }
table.att select, table.att input[type=text] { padding: 5px 9px; font-size: 13px; }
table.att .rolecell { width: 210px; }
table.att .transcell { width: 130px; }
table.att .notecell { width: 210px; }
table.att .pickcell { width: 42px; }

/* --------------------------------------------------------------- login --- */
body.signin {
  background: var(--masthead); display: grid; place-items: center;
  /* dvh, not vh: on a phone 100vh counts the space behind the address bar, so
     the card is centred against a box taller than the screen -- it sits low,
     and a page with one card on it scrolls. The vh line stays first as the
     fallback for anything without dvh. */
  min-height: 100vh;
  min-height: 100dvh;
}
/* The card is the whole page here, so the wrapper's desktop padding and its
   70px footer gap only push it off centre. */
body.signin .page { width: 100%; padding: 16px; }
.signin-card {
  /* 100% of the wrapper, never a viewport unit. 92vw looked equivalent and was
     not: the card sits inside .page's padding and carried a 24px margin of its
     own, so at a 390px viewport it asked for 439px and overflowed by 49 --
     and place-items: center splits an overflow both ways, which clipped the
     card's left edge and put it out of reach of a sideways scroll. */
  width: min(430px, 100%);
  background: var(--surface); border-radius: 14px;
  box-shadow: var(--shadow-lg); overflow: hidden; margin: 0 auto;
}
.signin-head { padding: 30px 32px 22px; text-align: center; border-bottom: 1px solid var(--line); }
.signin-head .brand-mark, .signin-head .brand-logo { margin: 0 auto 14px; }
.signin-head h1 { font-size: 20px; overflow-wrap: break-word; }
.signin-head p { color: var(--muted); margin: 5px 0 0; font-size: 14px; }
.signin-head .brand-logo { max-width: 100%; }
.signin-body { padding: 24px 32px 30px; display: grid; gap: 15px; }
.signin-foot { text-align: center; padding: 0 32px 26px; color: var(--muted); font-size: 12.5px; }
/* A narrow phone: 32px of padding each side of a 358px card leaves the fields
   cramped, and on a 320px screen it leaves 256px. */
@media (max-width: 430px) {
  /* Tighter again on the narrowest phones, where four columns of cell padding
     is itself a fifth of the screen. */
  table.data:not(.stackable) th,
  table.data:not(.stackable) td { padding: 8px 7px; }
  .signin-head { padding: 26px 20px 18px; }
  .signin-body { padding: 20px 20px 24px; }
  .signin-foot { padding: 0 20px 22px; }
}

/* --------------------------------------------------------- misc helpers -- */
.muted { color: var(--muted); }
.small { font-size: 13px; }
.tabular { font-variant-numeric: tabular-nums; }
.stack { display: grid; gap: 14px; }
.inline { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* minmax(0, …) on both tracks. A bare `max-content 1fr` gives each track an
   automatic minimum of its content, so a long unbreakable value -- an email
   address, a destination name -- can push the list wider than the card it is
   in and take the whole page sideways with it. */
.kv { display: grid; grid-template-columns: minmax(0, max-content) minmax(0, 1fr); gap: 7px 18px; font-size: 14px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; min-width: 0; overflow-wrap: break-word; }
/* Narrow enough that two columns leave the value nothing: label above value. */
@media (max-width: 560px) {
  .kv { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .kv dt { margin-top: 8px; font-size: 12px; text-transform: uppercase; letter-spacing: .06em; }
  .kv dt:first-child { margin-top: 0; }
}
.divide { border: 0; border-top: 1px solid var(--line); margin: 18px 0; }
details.notes summary { cursor: pointer; color: var(--muted); font-size: 13.5px; }

@media print {
  .masthead, .pagefoot, .page-actions, .filters, .btn { display: none !important; }
  body, .card { background: #fff; box-shadow: none; }
  .page { padding: 0; max-width: none; }
}

@media (max-width: 860px) {
  .masthead-inner { flex-wrap: wrap; padding: 10px 16px; gap: 10px 12px; }

  /* The profile panel is anchored to its trigger with right: 0, so the
     trigger has to be at the right edge or the panel opens off the screen.
     On a desktop .mainnav's auto margin pushed the nav and the trigger over
     together; here the nav drops to a row of its own and takes that margin
     with it, leaving the trigger sitting next to the brand -- and a 268px
     panel opening leftward from something 250px in starts at -18px. That is
     the panel hanging off the left edge, clipped and unreadable. */
  .usermenu { margin-left: auto; }
  .usermenu summary { min-height: 44px; }
  /* And never wider than the screen, whatever it is anchored to. */
  .usermenu-panel { width: min(268px, calc(100vw - 24px)); }

  /* nowrap, or the navigation never scrolls at all: flex items wrap before
     overflow-x has anything left to do, so `overflow-x: auto` was dead and
     eight items became three stacked rows of pills. */
  .mainnav {
    order: 3; width: 100%; margin-left: 0;
    flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: thin; padding-bottom: 2px;
  }
  .mainnav a { flex: none; }

  /* The brand is what gives when the top row is tight -- it is the one thing
     up there that is decoration rather than a control. */
  .brand { min-width: 0; flex: 1 1 auto; }
  .brand-text { min-width: 0; }
  .brand-text strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .brand-text small { display: none; }

  .page { padding: 18px 16px 60px; }
  .calendar td { height: auto; min-height: 74px; }
  /* 16px exactly. Safari on iOS zooms the page whenever a field it is about to
     focus is smaller than this, and it does not zoom back out -- so tapping
     the email box on the sign-in screen left the page magnified and scrolled
     sideways. Only on the phone breakpoint, so desktop density is unchanged. */
  input[type=text], input[type=email], input[type=password], input[type=number],
  input[type=date], input[type=time], input[type=search], input[type=url],
  input[type=datetime-local], select, textarea { font-size: 16px; }

  /* The masthead stops being sticky here, and that is what fixes the
     attendance toolbar. Both were pinned; the toolbar was pinned to a
     hard-coded 62px, the desktop masthead height, while the masthead itself
     wraps its navigation onto a second row at this width and stands nearer
     110px -- so the toolbar, and the Save button on it, sat underneath the
     masthead. Rather than chase a height that depends on how many nav items
     the signed-in user can see, only one of them stays pinned. Two sticky
     bars would eat a third of a phone screen anyway. */
  .masthead { position: static; }
  .att-toolbar { top: 0; }

  /* Touch targets. 44px is Apple's floor and 48dp Android's; the desktop
     sizes here are nearer 26px, which is a miss on a moving row of members. */
  .btn.small, .btn.tiny { padding: 9px 14px; font-size: 14px; min-height: 40px; }
  .check input { width: 22px; height: 22px; }
  .mainnav a { padding: 10px 14px; }

  /* Four filters in one rigid inline-flex strip is 437px of nowrap links --
     it cannot shrink, so it hangs 80px off a 390px screen. Let it wrap into a
     block of equal buttons instead. */
  .segmented {
    display: flex; flex-wrap: wrap; width: 100%;
    border-radius: var(--radius-s); overflow: hidden;
  }
  .segmented a {
    flex: 1 1 auto; padding: 11px 12px; text-align: center;
    border-bottom: 1px solid var(--line-strong);
  }
  .segmented a:last-child { border-right: 0; }
}

/* On a narrow phone the name beside the avatar is what pushes the top row
   over. Nothing is lost: the panel it opens leads with the full name and the
   email address. */
@media (max-width: 480px) {
  .uname { display: none; }
  .usermenu summary { padding: 6px; }
  /* Narrow enough that "Save attendance" on one line is wider than the row it
     sits in. Two lines is better than a page that scrolls sideways. */
  .btn { white-space: normal; }
}

/* ------------------------------------------------- data entry on a phone --
   A table of inputs cannot shrink: the fields have floors, so the row simply
   gets wider than the screen and the whole sheet scrolls sideways. These are
   the two screens somebody actually fills in standing up -- ticking off who
   turned out, and recording a sale at a meeting -- so below this width each
   row becomes a card and the column headings become labels on the fields.

   Deliberately not applied to the read-only tables. A roster or an order list
   is for scanning, and scanning is what a wide table with a scroll is good
   at; stacking 486 members into 486 cards would be worse. */
@media (max-width: 700px) {
  /* A short table does not need stacking, but it does need to stop insisting
     on its own width. Headers are nowrap by default, and four of them plus
     14px of padding a side is more than a 320px screen has -- three tables
     were over by 16 to 44px on that alone. Wrapping the labels and trimming
     the padding brings them in without restructuring anything. */
  table.data:not(.stackable) thead th { white-space: normal; }
  table.data:not(.stackable) th,
  table.data:not(.stackable) td { padding: 9px 10px; }

  table.stackable thead {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  table.stackable, table.stackable tbody, table.stackable tfoot { display: block; }
  table.stackable tr {
    display: grid; gap: 2px 10px; align-items: center;
    border: 1px solid var(--line); border-radius: var(--radius);
    padding: 10px 12px; margin-bottom: 10px; background: var(--surface);
  }
  table.stackable td {
    display: block; border-bottom: 0; padding: 4px 0; width: auto;
    min-width: 0; text-align: left;
  }
  table.stackable td.num { text-align: left; }
  table.stackable td[data-label]::before {
    content: attr(data-label);
    display: block; font-size: 11px; text-transform: uppercase;
    letter-spacing: .07em; color: var(--muted); margin-bottom: 2px;
  }
  table.stackable tbody tr:hover { background: var(--surface); }

  /* Attendance: the tick and the name on one line, the rest beneath it. The
     tick column is 44px so it is a target rather than a dot, and the fixed
     desktop column widths have to be undone or they win. */
  table.att.stackable tr { grid-template-columns: 44px minmax(0, 1fr); }
  table.att.stackable td { width: auto; }
  table.att.stackable td.pickcell { grid-area: 1 / 1 / 2 / 2; justify-self: center; }
  table.att.stackable td.namecell { grid-area: 1 / 2 / 2 / 3; font-weight: 600; }
  table.att.stackable td.rolecell,
  table.att.stackable td.transcell,
  table.att.stackable td.notecell { grid-column: 1 / -1; }
  table.att.stackable td.pickcell input { width: 26px; height: 26px; }
  table.att.stackable tr.on { background: color-mix(in srgb, var(--brand) 8%, transparent); }

  /* A table that collapses to one card per row: a name, a line saying where
     that row stands, and everything else behind a twistie. Used by the roster
     (seven columns, of which a phone holds two) and by renewals. Scrolling
     sideways through 486 members to reach an expiry date is not reading, it
     is hunting. */
  table.disclose.stackable tr { grid-template-columns: minmax(0, 1fr) 44px; }
  /* th as well as td: the weather table's leg name is a row header, and
     turning it into a plain cell to satisfy a selector would throw away what
     it tells a screen reader. */
  table.disclose.stackable td.c-primary,
  table.disclose.stackable th.c-primary {
    grid-area: 1 / 1 / 2 / 2; text-align: left;
  }
  /* The summary line. It steps aside once the row is open, since the real
     columns then say the same thing in more detail. */
  table.disclose.stackable td.c-summary {
    grid-area: 2 / 1 / 3 / 2; display: flex; flex-wrap: wrap; gap: 4px 10px;
  }
  table.disclose.stackable tr:has(.rowtoggle:checked) td.c-summary { display: none; }
  table.disclose.stackable td.c-more {
    grid-area: 1 / 2 / 3 / 3; display: flex; align-items: center;
    justify-content: flex-end; padding: 0;
  }
  table.disclose.stackable td.secondary,
  table.disclose.stackable th.secondary { grid-column: 1 / -1; }
  table.disclose.stackable thead th.c-more { display: none; }

  /* Written as "hide the rest while the toggle is NOT checked", not "show it
     when it is". A browser without :has() then matches nothing and every
     column stays visible -- the table reverts to scrolling sideways, which is
     what it did before. The other way round, the same browser would hide the
     columns with no way to reach them. */
  table.disclose.stackable tr:has(.rowtoggle:not(:checked)) td.secondary,
  table.disclose.stackable tr:has(.rowtoggle:not(:checked)) th.secondary {
    display: none;
  }
  /* Selectors kept specific rather than relying on source order: the base
     rule that hides the twistie lives further down the file and would
     otherwise win inside this query. */
  table.disclose.stackable .twistie {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; flex: none; cursor: pointer;
    border-radius: 50%; color: var(--muted);
  }
  table.disclose.stackable .twistie::after {
    content: "⌄"; font-size: 19px; line-height: 1; margin-top: -6px;
  }
  table.disclose.stackable .twistie:hover { background: var(--surface-2); color: var(--ink); }
  /* The checkbox is off-screen, so its focus ring has to be borrowed. */
  table.disclose.stackable .rowtoggle:focus-visible + .twistie { box-shadow: var(--ring); }
  table.disclose.stackable tr:has(.rowtoggle:checked) .twistie::after {
    content: "⌃"; margin-top: 4px;
  }

  /* A sale line: product, then size and quantity side by side, then the run,
     then price and total. Placed explicitly rather than by auto-flow, because
     the cells' source order is the desktop column order and the two do not
     agree. */
  table.lines.stackable tr { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  table.lines.stackable td.c-product { grid-area: 1 / 1 / 2 / 3; }
  table.lines.stackable td.c-size    { grid-area: 2 / 1 / 3 / 2; }
  table.lines.stackable td.c-qty     { grid-area: 2 / 2 / 3 / 3; }
  table.lines.stackable td.c-run     { grid-area: 3 / 1 / 4 / 3; }
  table.lines.stackable td.c-price   { grid-area: 4 / 1 / 5 / 2; }
  table.lines.stackable td.c-total   { grid-area: 4 / 2 / 5 / 3; }
  table.lines.stackable td.c-remove  { grid-area: 5 / 1 / 6 / 3; }
  table.lines.stackable td.c-remove .btn { width: 100%; }
  /* A bare × is fine as a 40px column on a desktop table and unreadable as a
     full-width button, so the word appears only here. Selector kept specific
     rather than relying on source order: the base rule that hides it lives
     further down the file and would otherwise win inside this query. */
  table.lines.stackable td.c-remove .removelabel { display: inline; }
  table.lines.stackable tfoot tr { grid-template-columns: minmax(0, 1fr) auto; }
  table.lines.stackable tfoot th { border: 0; padding: 4px 0; background: none;
    font-size: 14px; text-transform: none; letter-spacing: 0; color: var(--ink); }
}

/* A ride that did not happen stays on the calendar as a record, but must not
   read as one that did. */
.calevent.off { opacity: .55; }
.calevent.off .att, .calevent.off { text-decoration: line-through; }

/* ---------------------------------------------------------------- weather

   The go/no-go badge. Colour is never the only signal: each carries a mark
   and the word itself, so it survives colour blindness, a monochrome print
   and a forced-colours mode. The status colours are the fixed ones, not the
   themed brand palette — this is the same "state" job as an expiry warning,
   and it must not change meaning when the chapter changes its orange. */
.verdict {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px; border-radius: 999px;
  font-size: 13.5px; font-weight: 600; letter-spacing: .01em;
  border: 1px solid transparent; white-space: nowrap;
}
.verdict-mark {
  display: inline-grid; place-items: center;
  width: 17px; height: 17px; border-radius: 50%;
  font-size: 11px; font-weight: 700; color: #fff;
}
.verdict.big { font-size: 16px; padding: 9px 18px; }
.verdict.big .verdict-mark { width: 21px; height: 21px; font-size: 13px; }

.verdict-go       { background: var(--good-bg);     color: #0a6b0a; border-color: color-mix(in srgb, var(--good) 35%, transparent); }
.verdict-go       .verdict-mark { background: var(--good); }
.verdict-care     { background: var(--warning-bg);  color: #7a5300; border-color: color-mix(in srgb, var(--warning) 45%, transparent); }
.verdict-care     .verdict-mark { background: var(--warning); color: #3a2800; }
.verdict-no-go    { background: var(--critical-bg); color: #96201f; border-color: color-mix(in srgb, var(--critical) 35%, transparent); }
.verdict-no-go    .verdict-mark { background: var(--critical); }
.verdict-unknown  { background: var(--surface-2);   color: var(--muted); border-color: var(--line); }

:root[data-theme="dark"] .verdict-go      { color: #7ede7e; }
:root[data-theme="dark"] .verdict-care    { color: #f7cf72; }
:root[data-theme="dark"] .verdict-no-go   { color: #f59b9b; }

ul.tight { margin: 0; padding-left: 18px; }
ul.tight li { margin: 3px 0; }

/* A field with a button beside it — the destination lookup. */
.with-action { display: flex; gap: 8px; align-items: stretch; }
.with-action input { flex: 1 1 auto; min-width: 0; }
.with-action .btn { flex: none; }
.help.good { color: #0a6b0a; }
.help.bad  { color: #96201f; }
:root[data-theme="dark"] .help.good { color: #7ede7e; }
:root[data-theme="dark"] .help.bad  { color: #f59b9b; }

/* Month picker on a meeting series. */
.monthgrid {
  display: grid; gap: 6px 14px;
  grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
}

/* Comments on a ride. */
ul.comments { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
ul.comments li { border-left: 2px solid var(--line-strong); padding-left: 12px; }
ul.comments p { margin: 3px 0 0; font-size: 14px; white-space: pre-wrap; }
.comment-head { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.pad-top-0 { padding-top: 0; }
/* Shown only where a row has become a card — see the 700px block. */
.removelabel { display: none; }
/* The roster's per-row twistie. It exists only once a row is a card; on a
   desktop the columns are all there and there is nothing to disclose. */
.twistie { display: none; }
/* On a desktop every column is on show, so there is nothing to disclose and
   nothing for the summary line to summarise. */
table.disclose th.c-more,    table.disclose td.c-more,
table.disclose th.c-summary, table.disclose td.c-summary { display: none; }
/* The summary line reads as a sentence of readings, so its parts sit in a row
   rather than stacking. */
table.disclose td.c-summary { align-items: baseline; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* A key or a curl line, shown once and meant to be copied. */
pre.keybox {
  margin: 0; padding: 10px 12px; border-radius: var(--radius);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: 12.5px; line-height: 1.5; overflow-x: auto;
  white-space: pre-wrap; word-break: break-all;
}

/* A card footer carrying a real problem rather than a note. */
.card-foot.small:not(.muted) strong { color: #96201f; }
:root[data-theme="dark"] .card-foot.small:not(.muted) strong { color: #f59b9b; }

/* -------------------------------------------------- the year in review -- */
/* Two screens off one set of macros: a report page an officer reads with the
   year in front of them, and a deck thrown on the wall at the end-of-year
   dinner. The markup is the same markup -- what changes here is how big it is
   drawn, because forty feet away 13px is not small, it is absent. */

/* A headline figure and how it sits against the years before it. */
.figures {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
}
.figures.narrow { grid-template-columns: repeat(auto-fit, minmax(min(196px, 100%), 1fr)); }
.figure {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 15px 17px;
  display: grid; gap: 4px; align-content: start; min-width: 0;
}
.fig-label { font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); }
.fig-value { font-size: 34px; font-weight: 650; letter-spacing: -.03em; line-height: 1.05; }
.fig-note { font-size: 12.5px; color: var(--muted); }
.figure .pill { justify-self: start; }

/* The comparison line is deliberately not colour-coded up-good, down-bad.
   Half of what it is set against carries no such judgement -- fewer new
   destinations than last year is not a worse year, it is a different one --
   and the two figures where it might, turnout and members out, are the last
   two anybody wants shown to the room in red at a dinner. The arrow and the
   word "above" or "below" say the direction; nothing here restates it in a
   channel that also implies a verdict. */
.delta {
  font-size: 13px; color: var(--muted); display: flex; gap: 5px;
  align-items: baseline; flex-wrap: wrap; min-width: 0;
}
.delta .arrow { font-size: 10px; color: var(--ink-2); }

/* Ranked lists: a rank column that takes only what a two-digit number needs. */
table.data th.rank, table.data td.rank { width: 46px; }

/* The named things a slide lists rather than tables: everyone out for the
   first time, everywhere the chapter had not been. A grid rather than a
   column, because 17 names down the left of a 16:9 screen is a list scrolling
   off the bottom with two thirds of the slide empty beside it. */
.namegrid {
  list-style: none; margin: 0; padding: 0; display: grid; gap: 4px 20px;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}
.namegrid li {
  display: flex; gap: 10px; align-items: baseline; min-width: 0;
  border-bottom: 1px solid var(--line); padding: 5px 0;
}
.namegrid li span { margin-left: auto; }

/* A short ordered list where the figure matters as much as the name. */
ol.bigl { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
ol.bigl li {
  display: flex; align-items: baseline; gap: 12px; min-width: 0;
  border-bottom: 1px solid var(--line); padding: 6px 0;
}
.bigl-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bigl-num { margin-left: auto; font-weight: 650; font-variant-numeric: tabular-nums; }

/* What the figures do not say, stated once rather than hedged everywhere. */
.caveats {
  margin: 0; padding-left: 18px; display: grid; gap: 8px;
  font-size: 13.5px; color: var(--ink-2); max-width: 92ch;
}

/* ------------------------------------------------------------- the deck -- */
/* The masthead, the flashes and the footer go: at the back of a function room
   they are not small, they are clutter around the one thing anybody is
   looking at. Everything needed to get out is in .deckbar. */
body.deckmode .masthead,
body.deckmode .pagefoot,
body.deckmode .flashes { display: none; }
body.deckmode { overflow: hidden; }

/* The deck scrolls, and that is the whole navigation model: the slides are
   sections in document order, reachable with a scrollbar, the space bar and
   an anchor link on the first one. Snapping is `proximity` rather than
   `mandatory` on purpose -- a slide whose table is taller than a phone must
   not fight the reader for the last 200px of it. */
.page.deck {
  max-width: none; padding: 0;
  height: 100vh; height: 100dvh;
  overflow-y: auto; scroll-snap-type: y proximity; scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { .page.deck { scroll-behavior: auto; } }

.deckbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 5;
  display: flex; align-items: center; gap: 12px; padding: 10px 14px;
  font-size: 13px; color: var(--muted); pointer-events: none;
}
.deckbar > * { pointer-events: auto; }
.deckbar-where { margin-right: auto; min-width: 0; overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap; }
.deckbar-count { font-variant-numeric: tabular-nums; }

.slide {
  min-height: 100vh; min-height: 100dvh;
  scroll-snap-align: start; display: grid; align-content: center;
  padding: clamp(52px, 7vh, 96px) clamp(16px, 4vw, 60px);
  border-bottom: 1px solid var(--line);
}
.slide-inner {
  width: min(1180px, 100%); margin: 0 auto; min-width: 0;
  display: grid; gap: clamp(12px, 2.2vh, 26px);
}
.slide h2 { font-size: clamp(22px, 3.2vw, 46px); letter-spacing: -.025em; line-height: 1.08; }
.slide h3 {
  font-size: clamp(12px, 1.1vw, 16px); text-transform: uppercase;
  letter-spacing: .08em; color: var(--muted); margin-bottom: 10px;
}
/* minmax(min(…), …) on both floors, or a 320px track sits in a 288px phone
   and takes the slide sideways with it. */
.slide-split {
  display: grid; gap: clamp(16px, 3vw, 44px); align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
}

.slide.title { text-align: center; }
.slide.title .slide-inner { justify-items: center; }
.slide-kicker {
  text-transform: uppercase; letter-spacing: .16em; color: var(--muted);
  font-size: clamp(11px, 1.4vw, 18px);
}
.slide-year {
  font-size: clamp(76px, 21vw, 250px); line-height: .88; font-weight: 700;
  letter-spacing: -.05em; font-variant-numeric: tabular-nums;
}
.slide-lead { font-size: clamp(17px, 2.5vw, 34px); color: var(--ink-2); line-height: 1.25; }
.slide-note { font-size: clamp(12.5px, 1.25vw, 17px); color: var(--muted); max-width: 78ch; }
.slide-onward { font-size: 14px; }
.slide-small { font-size: 13px; color: var(--muted); }
.slide-small summary { cursor: pointer; }

/* Everything a slide borrows from the report, at the size a room can read. */
.slide .fig-value { font-size: clamp(36px, 6vw, 88px); }
.slide .fig-label { font-size: clamp(10.5px, 1vw, 15px); }
.slide .delta, .slide .fig-note { font-size: clamp(11.5px, 1.05vw, 16px); }
.slide .figure { padding: clamp(12px, 1.6vh, 20px) clamp(14px, 1.4vw, 22px); }
.slide .barrow { font-size: clamp(13px, 1.2vw, 19px); }
.slide .bartrack { height: clamp(12px, 1.5vh, 20px); }
.slide .barkey { font-size: clamp(12px, 1.05vw, 16px); }
.slide table.data th, .slide table.data td { font-size: clamp(13px, 1.15vw, 19px); }
.slide .namegrid, .slide ol.bigl { font-size: clamp(13px, 1.25vw, 20px); }
.slide .caveats { font-size: clamp(12px, 1vw, 15px); }

/* Printed, a deck is a handout: one slide to a page, and the whole thing
   scrolls on paper whatever it does on screen. */
@media print {
  body.deckmode { overflow: visible; }
  .page.deck { height: auto; overflow: visible; }
  .deckbar { display: none; }
  .slide {
    min-height: 0; border-bottom: 0; padding: 0 0 18px;
    break-after: page; page-break-after: always;
  }
  .slide:last-child { break-after: auto; page-break-after: auto; }
}

/* ---------------------------------------------------------- prize draws
   The name that has just come out, on the officer's own screen. Big because
   it is the answer to the only question on the page, not because the page is
   a deck -- the deck has its own scale below. */
.drawn-name {
  font-size: clamp(28px, 5vw, 46px); font-weight: 650;
  letter-spacing: -.03em; line-height: 1.08; margin: 2px 0 4px;
}

/* Every name that came out, including the ones who had gone home. A one
   column grid, so minmax(0, 1fr) rather than a bare 1fr: the floor of an
   implicit track is the item's min-content, and a long name would push the
   page sideways on a phone. */
.drawn-list {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px;
  margin: 6px 0 0; padding: 0; list-style: none; counter-reset: drawn;
}
.drawn-item {
  display: grid; gap: 2px; min-width: 0;
  padding: 10px 13px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--surface);
}
.drawn-item-name { font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.drawn-item-meta { font-size: 12.5px; color: var(--muted); }
/* A name that was passed over is set back rather than marked in red. Nobody
   did anything wrong by going home early, and the list is read out loud. */
.drawn-item.absent { background: var(--surface-2); }
.drawn-item.absent .drawn-item-name { font-weight: 500; color: var(--ink-2); }

/* Abandon and Reopen: real actions, but not the one anybody came for. */
.quiet-action { margin-top: 14px; }

/* A button somebody has to hit from a standing position, at a lectern, in
   front of a room. Both the deck's buttons and the officer's "Draw a name"
   use it -- the situation is the same one. */
.btn.big { padding: 13px 26px; font-size: 17px; }
.deck-actions {
  margin-top: clamp(14px, 3vh, 34px);
  display: flex; gap: 12px; flex-wrap: wrap; justify-content: center;
}
.slide .btn.big { padding: clamp(11px, 1.6vh, 18px) clamp(20px, 2.4vw, 38px);
                  font-size: clamp(15px, 1.5vw, 24px); }
/* The winning name is the one thing on the wall, so it gets the room. It also
   has to survive "Van Der Beek-Fitzwilliam, Christopher" without going off
   the edge, which is what the wrap and the narrower clamp floor are for. */
.slide-year.drawn {
  font-size: clamp(32px, 7.5vw, 104px); overflow-wrap: anywhere;
  max-width: 100%;
}

/* Carrying on to the next prize. Set apart from the claim buttons above it:
   the evening moves on only once somebody has actually been handed the last
   one, and the two must not be adjacent enough to hit by accident. */
.nextprize {
  margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line);
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px;
}
.nextprize input[type="text"] { min-width: 0; }
