/* ==========================================================================
   Pre-COP31 — Pacific identity layer

   app.css supplies the structure: shell, cards, tables, forms, dark mode.
   This file supplies the *identity*: the palette, the cloth patterns and the
   components that exist only in this platform — badges, the scanner verdict
   screen, Participant 360 and the Command Centre.

   Design intent: the palette comes from the COP31 / Pacific Pre-COP31
   Stakeholder Engagement Programme visual direction, sampled from the artwork
   itself rather than eyeballed. Its stated primary direction is *white with
   dark and light green*: white is the ground, the two greens carry the
   paper-cut contour shapes, and every piece of type is set in one royal blue.

     #FFFFFF  ground        #3AB97C  light green    #149356  dark green
     #134FA0  royal blue — headings, links, actions

   There are exactly two greens. Nothing here may introduce a third: a tint of
   one of them, or white, or the blue.

   Blue carries the authority; the greens are the pattern and the accent, and
   appear where something is *good* — a granted scan, a confirmed accreditation.
   The contour artwork (assets/img/vd-*.svg) is traced from the deck's own
   graphics; the masi geometry stays as a fine texture on brand surfaces, never
   as wallpaper behind reading matter.

   There is no purple anywhere in this platform. Section 15 exists to hold that
   true against the inherited stylesheet, which is full of it.
   ========================================================================== */

/* ────────────────────────── 1. Palette override ────────────────────────── */

:root,
[data-theme="light"] {
  --brand:        #134fa0;     /* royal blue — the direction sets all type in it */
  --brand-2:      #2c6fc9;     /* lighter blue — accents and gradients */
  --brand-soft:   #eaf1fb;
  --brand-deep:   #0e3c7a;     /* the programme's navy — headers, darkest band */
  --reef:         #149356;     /* dark green  — rgb(20,147,86) */
  --reef-2:       #3ab97c;     /* light green — rgb(58,185,124) */
  --sand:         #f1f8f4;     /* the barely-green off-white the deck rests on */
  --coral:        #ea0b16;     /* the programme's red — genuine urgency only */

  --bg:           #f4f8f5;
  --surface:      #ffffff;
  --surface-2:    #f7fbf8;
  --sidebar:      #0b3a6b;
  --sidebar-hover:#12508f;
  --sidebar-text: #a5c3e0;

  --bs-primary:     #134fa0;
  --bs-primary-rgb: 19,79,160;
}

[data-theme="dark"] {
  --brand:        #6aa8f0;
  --brand-2:      #94c3f8;
  --brand-soft:   #0e2745;
  --brand-deep:   #071a33;
  --reef:         #3ab97c;
  --reef-2:       #5ed09a;
  --sand:         #131a16;
  --coral:        #ef7f74;

  --bg:           #060f1a;
  --surface:      #0e1b2a;
  --surface-2:    #132436;
  --sidebar:      #030a14;
  --sidebar-hover:#0d2038;

  --line:         #1b3149;
  --line-2:       #26415d;

  --bs-primary:     #4aa8ee;
  --bs-primary-rgb: 74,168,238;
}

/* The masi band, re-woven in the mark's palette: ocean triangles, a reef-green
   counterpoint, and both diamonds taken straight off the logo. The base file's
   version is indigo; overriding the image rather than the shape keeps one
   source of truth for the geometry. */
.pc-masi {
  background-color: #eaf3ee;
  background-image: url("data:image/svg+xml,%3Csvg width='76' height='26' viewBox='0 0 76 26' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='76' height='26' fill='%23eaf3ee'/%3E%3Cpath d='M0 26 L19 5 L38 26 Z' fill='%23b7d1ee'/%3E%3Cpath d='M38 0 L57 21 L76 0 Z' fill='%23bfe5cf'/%3E%3Cpolygon points='19,12 22.5,15.5 19,19 15.5,15.5' fill='%23134fa0'/%3E%3Cpolygon points='57,7 60.5,10.5 57,14 53.5,10.5' fill='%23149356'/%3E%3C/svg%3E");
  border-color: #cfe4d8;
}
[data-theme="dark"] .pc-masi {
  background-color: #0d1f18;
  background-image: url("data:image/svg+xml,%3Csvg width='76' height='26' viewBox='0 0 76 26' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='76' height='26' fill='%230d1f18'/%3E%3Cpath d='M0 26 L19 5 L38 26 Z' fill='%231d4470'/%3E%3Cpath d='M38 0 L57 21 L76 0 Z' fill='%2314452e'/%3E%3Cpolygon points='19,12 22.5,15.5 19,19 15.5,15.5' fill='%236aa8f0'/%3E%3Cpolygon points='57,7 60.5,10.5 57,14 53.5,10.5' fill='%233ab97c'/%3E%3C/svg%3E");
  border-color: #1a3328;
}

/* ─────────────────────────── 2. Government mark ────────────────────────── */

.pc-crest {
  display: inline-flex; align-items: center; gap: .6rem;
}
.pc-crest-mark {
  width: 2.1rem; height: 2.1rem; flex: none;
  border-radius: 9px;
  background: linear-gradient(140deg, var(--brand) 0%, var(--brand-2) 100%);
  color: #fff; display: grid; place-items: center;
  font-size: .95rem; box-shadow: var(--shadow-xs);
}
.pc-crest-text { line-height: 1.15; }
.pc-crest-title {
  font-weight: 800; letter-spacing: -.02em; font-size: 1rem; color: var(--ink);
}
.pc-crest-sub {
  font-size: .66rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}

/* The ministry attribution line that appears under headers and in footers.
   Small, uppercase, never competing with the conference name above it. */
.pc-ministry {
  font-size: .68rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}

/* ───────────────────────────── 3. Hero band ────────────────────────────── */

/* Every public page opens with one of these, so this is what carries the
   programme's identity across the site rather than only the homepage.

   It is one of the deck's banners: white ground, the paper-cut contours
   entering from one end, the title in blue in the clear space. It used to be a
   blue gradient slab with a masi weave over it — which, under a white header,
   read as a coloured stripe stuck between two white things.

   Built exactly as the homepage's banded sections are, and for the same
   reason: it is the same artwork in the same role, so it has to be the same
   construction or the two read as two different sites.

   Which means: the crest crop, full width, pinned to the top edge, at its own
   proportions. It was a slab cut out of the middle of the wide banner and
   parked in the right-hand 56% — shapes with hard vertical edges floating in
   a corner, connected to no edge of anything, which is exactly what it looked
   like. A contour has to run off the frame it belongs to. */
.pc-hero {
  position: relative; overflow: hidden;
  background: #fff;
  color: var(--ink);

  /* ── Why this band is sized by HEIGHT, not by width ──────────────────────
     The homepage sections paint the crest at `100% auto`: the artwork spans
     the width and the box has to be exactly as tall as that renders it. On a
     tall section that is fine. On a page header it is not — 18.167vw is 349px
     of banner on a 1920 screen before a word of the title, which is exactly
     the "too much padding on bigger screens" here.

     So the header drives it the other way round: `auto 100%` sizes the artwork
     to the BAND's height and lets it repeat across the width. Still never
     stretched and never sliced — the aspect ratio is held either way; the only
     difference is which dimension is the free one. That buys a header depth
     that stays sensible at 2560px instead of growing without limit. */
  --crest: clamp(48px, 4.4vw, 78px);

  padding-top: calc(var(--crest) + clamp(.4rem, .8vw, .8rem));
  padding-bottom: clamp(.85rem, 1.6vw, 1.4rem);
  border-bottom: 1px solid color-mix(in srgb, var(--reef) 18%, transparent);
}
.pc-hero::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0;
  height: var(--crest); pointer-events: none;
  /* `auto 100%` — height-driven, so the aspect ratio is held and the band's
     depth is free. It repeats across the width rather than being stretched to
     it; at this depth one tile is ~5.5x as wide as it is tall, so a desktop
     header shows two of them. */
  background: var(--img-band-crest) left top / auto 100% repeat-x;
  -webkit-mask-image: none; mask-image: none;
}
.pc-hero > * { position: relative; z-index: 1; }

/* Carried over from the blue header these used to sit on, as inline
   `rgba(255,255,255,…)` — invisible the moment the ground went white. */
.pc-hero-back { color: var(--brand); font-weight: 600; text-decoration: none; }
.pc-hero-back:hover { color: var(--brand-deep); text-decoration: underline; }
.pc-hero-tag {
  background: color-mix(in srgb, var(--reef-2) 16%, #fff);
  color: var(--brand-deep); font-weight: 600;
}

@media (max-width: 61.99em) {
  /* Shallower still on a phone, where the header is competing with the whole
     screen. The wide crop stays: sized by height it tiles at any width, so
     there is no reason to swap in the portrait one here. */
  .pc-hero { --crest: clamp(44px, 8vw, 68px); }
}

/* A page title, not a hero title. At 5.4vw this ran to 3.9rem — the same size
   as the homepage's headline — and a two-word heading set that large is most
   of why these headers felt enormous. */
.pc-hero h1 {
  color: var(--brand); font-weight: 800; letter-spacing: -.035em;
  font-size: clamp(1.7rem, 3.4vw, 2.6rem); line-height: 1.05;
  margin-bottom: .15rem;
}
.pc-hero-dates {
  font-size: clamp(.95rem, 1.6vw, 1.2rem); font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--brand-2);
}
.pc-hero-lede { color: var(--muted); max-width: 42rem; font-size: 1.02rem; }

[data-theme="dark"] .pc-hero { background: var(--surface); }

/* An inner-page hero: the same band, but not shouting.
   The homepage hero carries the conference name once and can afford 3.9rem. A
   session title is longer, arbitrary, and read while deciding whether to
   attend — at homepage size a normal title fills the viewport on its own and
   pushes every fact about the session below the fold. */
.pc-hero-page { padding: clamp(1.7rem, 4vw, 2.9rem) 0; }
.pc-hero-page h1 { font-size: clamp(1.5rem, 3.1vw, 2.35rem); line-height: 1.12; }

/* Facts under an inner-page title: date, time, room. Wraps to as many lines as
   it needs on a phone rather than overflowing the band. */
.pc-hero-meta {
  display: flex; flex-wrap: wrap; gap: .35rem 1.25rem;
  color: var(--muted); font-size: .93rem; font-weight: 500;
}
.pc-hero-meta span { display: inline-flex; align-items: center; gap: .4rem; }
.pc-hero-meta i { color: var(--reef); }

/* A reading column for pages with no second column to fill.
   Full-width body text on a 1900px monitor is unreadable, and a lone card in a
   12-column row is worse: it looks like the rest of the page failed to load.

   min() rather than a bare rem: this is a line-length limit, and rem is not one
   here. Chrome scales its default font with the system display setting, so the
   root is 19.3px on a Windows machine at 125% and 16px on the next — the same
   62rem measures 1196px on one and 992px on the other. The rem half still lets
   the column shrink for somebody who has chosen a smaller text size. */
.pc-container-narrow { max-width: min(62rem, 1000px); }

/* The opposite of the rule above: a page that is mostly a WIDE grid — the
   programme's four-day board, the calendar's list rows — rather than reading
   copy, and whose content stopped growing at Bootstrap's own 1320px cap while
   a 1920px or 2560px monitor still had thick margins either side of it. Same
   min(rem, px) reasoning as .pc-container-narrow: a bare rem cap moves with
   Windows display scaling, so a hard px ceiling travels alongside it. Applied
   to the public programme page (2026-09-11, "expand ... so more text is
   covered and spans well across the screen"). */
.pc-container-wide { max-width: min(96rem, 1600px); }

/* Keeps the decision panel in view while the content column scrolls past it.
   Only above lg — below that the columns are stacked, and sticky there would
   pin the panel over the content it belongs beside. */
@media (min-width: 992px) {
  .pc-sticky-side { position: sticky; top: 1.25rem; }
}

/* ── A single fact: icon, label, value ──────────────────────────────────────
   Used down the side of an event page, and across it when there is no side.
   The icon is fixed-width so values line up in either arrangement. */
.pc-fact { display: flex; align-items: flex-start; gap: .7rem; }
.pc-fact > i {
  flex: 0 0 1.15rem; width: 1.15rem; margin-top: .15rem;
  color: var(--brand); font-size: 1.02rem; text-align: center;
}
.pc-fact-label {
  font-size: .7rem; letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; margin-bottom: .1rem;
}
.pc-fact-value { font-weight: 600; line-height: 1.35; }

/* ────────────────────────── 4. Participant badge ───────────────────────── */
/* Sized to a real conference badge: 86 × 122 mm, the common CR100 insert.
   Fixed millimetres, not percentages — this element exists to be printed, and
   a badge that is "responsive" is a badge that comes out of the printer the
   wrong size for the holder. */

.pc-badge {
  width: 86mm; height: 122mm;
  background: var(--surface); color: #0f172a;
  border-radius: 3mm; overflow: hidden;
  display: flex; flex-direction: column;
  box-shadow: var(--shadow); border: 1px solid var(--line);
  page-break-inside: avoid; break-inside: avoid;
}
.pc-badge-head {
  background: var(--badge-band, #0059b3); color: var(--badge-ink, #fff);
  padding: 4mm 5mm 3mm; position: relative; overflow: hidden;
}
.pc-badge-head::before {
  content: ''; position: absolute; inset: 0;
  background-image: var(--tapa); background-size: 74px 74px; opacity: .3;
}
.pc-badge-head > * { position: relative; z-index: 1; }
.pc-badge-conf {
  font-size: 3.4mm; font-weight: 800; letter-spacing: -.01em; line-height: 1.1;
}
.pc-badge-host {
  font-size: 2.2mm; letter-spacing: .09em; text-transform: uppercase;
  opacity: .85; margin-top: .4mm;
}
.pc-badge-body {
  flex: 1; padding: 4mm 5mm 2mm; display: flex; flex-direction: column;
  align-items: center; text-align: center; min-height: 0;
}
.pc-badge-photo {
  width: 26mm; height: 26mm; border-radius: 50%; object-fit: cover;
  border: 0.8mm solid var(--badge-band, #0059b3); background: #e2e8f0;
  display: grid; place-items: center; font-size: 8mm; font-weight: 700;
  color: var(--badge-band, #0059b3); flex: none;
}
.pc-badge-name {
  font-size: 5.4mm; font-weight: 800; line-height: 1.12; margin-top: 2.5mm;
  letter-spacing: -.02em; color: #0f172a;
}
.pc-badge-name.long  { font-size: 4.6mm; }
.pc-badge-name.longer{ font-size: 3.9mm; }
.pc-badge-role  { font-size: 2.9mm; color: #475569; margin-top: .8mm; line-height: 1.25; }
.pc-badge-org   { font-size: 3.1mm; font-weight: 600; color: #0f172a; margin-top: 1.2mm; line-height: 1.2; }
.pc-badge-country {
  font-size: 2.7mm; letter-spacing: .14em; text-transform: uppercase;
  color: #64748b; margin-top: .8mm; font-weight: 600;
}
.pc-badge-cat {
  margin-top: auto; align-self: stretch;
  background: var(--badge-band, #0059b3); color: var(--badge-ink, #fff);
  font-size: 3.2mm; font-weight: 800; letter-spacing: .18em; text-transform: uppercase;
  padding: 1.8mm 2mm; text-align: center;
}
.pc-badge-foot {
  display: flex; align-items: center; gap: 3mm; padding: 3mm 5mm 4mm;
  background: #fff;
}
.pc-badge-qr { width: 21mm; height: 21mm; flex: none; display: block; }
.pc-badge-ref {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 2.7mm; color: #334155; line-height: 1.5; text-align: left;
}
.pc-badge-ref strong { display: block; font-size: 3.2mm; color: #0f172a; }

/* Reverse face */
.pc-badge-back { justify-content: space-between; }
.pc-badge-back-body {
  padding: 5mm; font-size: 2.8mm; line-height: 1.5; color: #334155; flex: 1;
}
.pc-badge-back-body h4 {
  font-size: 3.2mm; font-weight: 800; margin: 0 0 1.5mm; color: #0f172a;
}
.pc-badge-emergency {
  background: #fef2f2; border-top: 0.5mm solid #fecaca; color: #991b1b;
  padding: 3mm 5mm; font-size: 2.6mm; font-weight: 600; text-align: center;
}

/* The digital badge in the portal: same identity, screen proportions. */
/* ── The digital badge ──────────────────────────────────────────────────────
   Deliberately the same object as the printed one (app/views/pdf/badges.php):
   white card, the layered green contour waves at top and bottom, the two marks
   paired, navy name over organisation over QR. A delegate holding a phone and
   a delegate holding card stock should be showing a door the same badge — if
   the screen version looks like a different document, the printed one starts
   looking like a forgery.

   Greens are the PreCOP31 mark's own: #00A850 with a lighter and deeper tone.
   Fixed, not themed: this is a credential, and it must not restyle itself when
   somebody changes the brand colour in settings or switches to dark mode. */
.pc-digital-badge {
  max-width: 24rem; margin: 0 auto; border-radius: var(--radius);
  overflow: hidden; box-shadow: var(--shadow-lg);
  background: #fff; color: #0F2A44;
  position: relative;
}
/* The deck's own crops, as on the printed card and the homepage. They carry a
   viewBox and no width/height, so the ratio is declared rather than left to be
   inferred — and it must match the viewBox exactly, because both files are
   preserveAspectRatio="none" and will happily stretch to any box given. */
.pc-digital-wave { display: block; width: 100%; height: auto; }
.pc-digital-wave-crest { aspect-ratio: 820 / 199; }
.pc-digital-wave-base  { aspect-ratio: 1200 / 184; }

.pc-digital-marks { text-align: center; margin: -2.2rem 0 0; position: relative; z-index: 1; }
.pc-digital-plate {
  display: inline-flex; align-items: center;
  background: #fff; border-radius: .5rem; padding: .4rem .7rem;
  box-shadow: 0 2px 10px rgba(2, 24, 48, .12);
}
/* The lockup comes from app/views/public/_marks.php — the same partial the
   homepage uses — so the marks, their order and their optical balance are the
   site's, not a second set maintained here. Heights follow that file's rule:
   match weight, so the squarer marks get more of it. */
.pc-digital-lockup { display: inline-flex; align-items: center; gap: .5rem; }
.pc-digital-lockup .pc-lockup { display: block; width: auto; }
.pc-digital-lockup .pc-lockup-square { height: 2.05rem; }
.pc-digital-lockup .pc-lockup-mid    { height: 1.95rem; }
.pc-digital-lockup .pc-lockup-wide   { height: 1.45rem; }
.pc-digital-lockup .rule { flex: none; width: 1px; height: 1.5rem; background: #D3DCE6; }
/* Namespaced, and not merely as a convention: these were `.crest`, `.mark` and
   `.rule` until Bootstrap's own `.mark, mark { background-color:
   var(--bs-highlight-bg) }` — its text highlighter — painted #FFF3CD behind the
   Pre-COP31 logo on every digital badge. The printed badge was unaffected
   because DomPDF never loads Bootstrap, so the two versions disagreed and only
   the screen one was wrong. A generic class name inside a component is a
   collision waiting for a framework to claim it. */
.pc-digital-plate img { display: block; }

/* The photograph, on the phone exactly as on the printed card. A delegate
   holding this up at a door and an officer holding the paper badge have to be
   looking at the same document, or the printed one starts looking like the
   forgery. Square, because the stored file is cropped square for print too. */
.pc-digital-photo {
  display: block; margin: .9rem auto 0;
  width: 6.4rem; height: 6.4rem; object-fit: cover;
  border-radius: .7rem; border: 3px solid #fff;
  box-shadow: 0 4px 14px rgba(0, 40, 90, .18);
}
.pc-digital-photo + .pc-digital-name { margin-top: .7rem; }

.pc-digital-name {
  font-size: 1.45rem; font-weight: 800; line-height: 1.15;
  color: #003898; margin: .9rem 0 0; padding: 0 1.1rem; text-align: center;
}
.pc-digital-role { font-size: .82rem; color: #4A5D70; text-align: center; margin-top: .2rem; }
.pc-digital-org  {
  font-size: .95rem; font-weight: 700; text-align: center;
  margin-top: .5rem; padding: 0 1.1rem; color: #0F2A44;
}
.pc-digital-ctry {
  font-size: .72rem; color: #4A5D70; text-align: center; margin-top: .2rem;
  text-transform: uppercase; letter-spacing: .08em;
}
/* Which event this is, above the identity — the same block, in the same
   order, as the printed card. */
.pc-digital-event {
  text-align: center; margin-top: .9rem; padding: 0 1.1rem;
  font-size: .72rem; font-weight: 800; letter-spacing: .09em;
  text-transform: uppercase; color: #149356; line-height: 1.3;
}
.pc-digital-when {
  text-align: center; font-size: .72rem; color: #6B7C8D; margin-top: .2rem;
}
.pc-digital-rule {
  width: 4.5rem; height: 1px; background: #C8D4E0; margin: .7rem auto .2rem;
}

.pc-digital-qr {
  background: #fff; padding: 1rem 1.4rem .4rem; display: grid; place-items: center;
}
.pc-digital-qr img { width: 100%; max-width: 14rem; height: auto; display: block; }

/* ───────────────────── 5. Scanner verdict screen (§22) ─────────────────── */
/* Read across a room, in a doorway, on a phone held at arm's length. Colour is
   never the only signal — each state has an icon and a word, because a
   colour-blind officer must not have to guess, and phone screens in sunlight
   wash colour out long before they wash out contrast. */

.pc-verdict {
  border-radius: var(--radius); overflow: hidden; color: #fff;
  box-shadow: var(--shadow-lg);
}
.pc-verdict-head {
  padding: 1.6rem 1.4rem; text-align: center; position: relative; overflow: hidden;
}
.pc-verdict-head::before {
  content: ''; position: absolute; inset: 0;
  background-image: var(--tapa); background-size: 96px 96px; opacity: .18;
}
.pc-verdict-head > * { position: relative; z-index: 1; }
.pc-verdict-icon { font-size: 3.2rem; line-height: 1; }
.pc-verdict-title {
  font-size: clamp(1.35rem, 4.2vw, 2rem); font-weight: 800;
  letter-spacing: .04em; margin: .5rem 0 0; color: #fff;
}
.pc-verdict-reason { font-size: .95rem; opacity: .92; margin-top: .35rem; }

.pc-verdict.granted .pc-verdict-head { background: linear-gradient(135deg, #047857, #10b981); }
.pc-verdict.denied  .pc-verdict-head { background: linear-gradient(135deg, #991b1b, #dc2626); }
.pc-verdict.review  .pc-verdict-head { background: linear-gradient(135deg, #b45309, #f59e0b); }

.pc-verdict-body { background: var(--surface); color: var(--ink); padding: 1.2rem 1.1rem; }
.pc-verdict-action {
  background: var(--surface-2); border-top: 1px solid var(--line);
  padding: .85rem 1.1rem; font-size: .88rem; color: var(--ink-2);
}

/* The person, big enough to check against the face in front of you. */
.pc-scan-person { display: flex; gap: 1rem; align-items: center; }
.pc-scan-photo {
  width: 5.2rem; height: 5.2rem; border-radius: 12px; object-fit: cover;
  flex: none; background: var(--surface-2); display: grid; place-items: center;
  font-size: 1.6rem; font-weight: 700; color: var(--muted);
  border: 2px solid var(--line);
}
.pc-scan-name { font-size: 1.3rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.15; }
.pc-scan-meta { font-size: .88rem; color: var(--muted); }

/* Scanner shell: minimal chrome, large targets, thumb-reachable actions. */
/* ── Connection state ──────────────────────────────────────────────────────
   One line, two states. It replaces a four-line panel that explained what an
   offline queue is and appeared even on a device with a good connection.
   Nobody at a door reads an explanation; they need to know which of two
   things is true. */
.pc-net-bar {
  display: flex; align-items: center; gap: .6rem;
  padding: .6rem .9rem; margin-bottom: 1rem;
  border-radius: var(--radius); font-size: .95rem;
}
.pc-net-bar[hidden] { display: none !important; }
.pc-net-bar > i     { font-size: 1.15rem; flex: 0 0 auto; }
.pc-net-text        { font-weight: 700; letter-spacing: .01em; }

/* Offline: amber, because it changes what a verdict on this screen means. */
.pc-net-bar.is-offline {
  background: #7c2d12; color: #fff; border: 1px solid #9a3412;
}

/* Sending: quiet. The queue is going up on its own and needs no attention. */
.pc-net-bar.is-sending {
  background: color-mix(in srgb, var(--brand) 10%, var(--surface));
  color: var(--brand-deep); border: 1px solid color-mix(in srgb, var(--brand) 25%, transparent);
}
.pc-net-bar.is-sending > i { animation: pcNetSpin 1.1s linear infinite; }
@keyframes pcNetSpin { to { transform: rotate(360deg); } }

.pc-net-pill {
  background: rgba(255,255,255,.18); border-radius: 999px;
  padding: .15rem .6rem; font-size: .8rem; font-weight: 700; white-space: nowrap;
}
.pc-net-bar.is-sending .pc-net-pill {
  background: color-mix(in srgb, var(--brand) 16%, transparent);
}
.pc-net-send {
  margin-left: auto; flex: 0 0 auto;
  border: 1px solid currentColor; background: transparent; color: inherit;
  border-radius: 8px; padding: .25rem .7rem; font-size: .82rem; font-weight: 700;
}
.pc-net-send[hidden] { display: none !important; }

.pc-scanner-stage {
  background: #000; border-radius: var(--radius); overflow: hidden;
  aspect-ratio: 1 / 1; max-height: 26rem; position: relative;
}
.pc-scanner-stage video, .pc-scanner-stage canvas { width: 100% !important; height: 100% !important; object-fit: cover; }
.pc-scan-reticle {
  position: absolute; inset: 14%; border: 3px solid rgba(255,255,255,.85);
  border-radius: 18px; pointer-events: none;
  box-shadow: 0 0 0 100vmax rgba(0,0,0,.35);
}
.pc-scan-reticle::after {
  content: ''; position: absolute; left: 6%; right: 6%; top: 50%; height: 2px;
  background: var(--brand-2); box-shadow: 0 0 12px var(--brand-2);
  animation: pcScanLine 2.4s ease-in-out infinite;
}
@keyframes pcScanLine {
  0%, 100% { transform: translateY(-6.5rem); opacity: .3; }
  50%      { transform: translateY(6.5rem);  opacity: 1; }
}
.pc-op-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .5rem; min-height: 6.5rem; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font-weight: 700; font-size: .92rem; text-align: center; padding: 1rem .6rem;
  transition: transform .12s ease, box-shadow .15s ease, border-color .15s ease;
}
.pc-op-btn i { font-size: 1.7rem; color: var(--brand); }
.pc-op-btn:hover { border-color: var(--brand); box-shadow: var(--shadow); color: var(--ink); }
.pc-op-btn:active { transform: scale(.98); }

/* ────────────────────── 6. Participant 360 (§29, §30) ──────────────────── */

.pc-journey {
  display: flex; flex-wrap: wrap; gap: .4rem; align-items: center;
}
.pc-journey-step {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .34rem .7rem; border-radius: 999px;
  font-size: .78rem; font-weight: 700; white-space: nowrap;
  background: var(--surface-2); color: var(--muted); border: 1px solid var(--line);
}
.pc-journey-step.done {
  background: var(--brand-soft); color: var(--brand); border-color: transparent;
}
[data-theme="dark"] .pc-journey-step.done { color: var(--brand-2); }
.pc-journey-step.pending { opacity: .62; }
.pc-journey-sep { color: var(--line-2); font-size: .8rem; }

/* Timeline. The rail is a border on the list, so entries can be any height
   and the line still runs continuously behind them. */
.pc-tl { list-style: none; margin: 0; padding: 0 0 0 1.6rem; border-left: 2px solid var(--line); }
.pc-tl li { position: relative; padding: 0 0 1.15rem .3rem; }
.pc-tl li::before {
  content: ''; position: absolute; left: -2.05rem; top: .38rem;
  width: .72rem; height: .72rem; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--brand);
}
.pc-tl li.muted::before { border-color: var(--line-2); }
.pc-tl-time {
  font-size: .74rem; color: var(--muted); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.pc-tl-label { font-weight: 600; font-size: .92rem; }
.pc-tl-detail { font-size: .82rem; color: var(--muted); }

/* Zone access grid — a yes/no board, not a table to read. */
.pc-access-grid { display: grid; gap: .5rem; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); }
.pc-access-item {
  display: flex; align-items: center; gap: .55rem; padding: .6rem .75rem;
  border-radius: var(--radius-sm); border: 1px solid var(--line);
  background: var(--surface-2); font-size: .86rem; font-weight: 600;
}
.pc-access-item i { font-size: 1.05rem; }
.pc-access-item.yes { color: var(--success); border-color: color-mix(in srgb, var(--success) 35%, transparent); }
.pc-access-item.no  { color: var(--muted); }

/* ────────────────────── 7. Command Centre (§49, §78) ───────────────────── */
/* Operational, not decorative: dense numbers, a live pulse, and a feed that is
   plainly a log rather than an animation. */

.pc-cc {
  background: linear-gradient(135deg, var(--brand-deep), var(--brand));
  border-radius: var(--radius); color: #fff; padding: 1.3rem;
  position: relative; overflow: hidden;
}
.pc-cc::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image: var(--tapa); background-size: 118px 118px; opacity: .2;
}
.pc-cc > * { position: relative; z-index: 1; }
.pc-cc-grid {
  display: grid; gap: .9rem;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
}
.pc-cc-stat {
  background: rgba(255,255,255,.11); border: 1px solid rgba(255,255,255,.16);
  border-radius: var(--radius-sm); padding: .85rem .9rem;
}
.pc-cc-value {
  font-size: clamp(1.6rem, 3.4vw, 2.3rem); font-weight: 800; line-height: 1;
  letter-spacing: -.03em; font-variant-numeric: tabular-nums;
}
.pc-cc-label {
  font-size: .68rem; letter-spacing: .13em; text-transform: uppercase;
  opacity: .82; margin-top: .3rem; font-weight: 600;
}
.pc-cc-sub { font-size: .74rem; opacity: .7; margin-top: .15rem; }

.pc-feed { list-style: none; margin: 0; padding: 0; }
.pc-feed li {
  display: flex; gap: .7rem; padding: .55rem 0;
  border-bottom: 1px dashed var(--line); font-size: .86rem;
}
.pc-feed li:last-child { border-bottom: 0; }
.pc-feed-time {
  font-variant-numeric: tabular-nums; color: var(--muted);
  font-size: .78rem; font-weight: 600; flex: none; width: 4.2rem;
}
/* Rows the poller has just added, so a watcher can see what is new without
   the whole list moving. */
.pc-feed li.fresh { animation: pcFresh 2.4s ease-out; }
@keyframes pcFresh {
  from { background: color-mix(in srgb, var(--brand) 14%, transparent); }
  to   { background: transparent; }
}

/* ───────────────────────── 8. Programme cards ──────────────────────────── */

/* The day stays in sight while its sessions scroll. It stops under the bar
   stuck to the top of the window (the site navigation, or the portal's bar),
   whose height board.js measures into --pc-stick-top; at top: 0 it slid
   behind that bar and vanished, which is what it was meant to prevent. */
.pc-prog-day {
  position: sticky; top: var(--pc-stick-top, 0px); z-index: 3;
  background: var(--bg); padding: .7rem 0 .5rem;
  border-bottom: 2px solid var(--brand);
  margin-bottom: .9rem;
}
.pc-prog-day h3 { margin: 0; font-size: 1.05rem; letter-spacing: -.02em; }

.pc-prog-item {
  display: grid; grid-template-columns: 6.2rem 1fr auto; gap: 1rem;
  padding: .95rem 1rem; border: 1px solid var(--line);
  border-left: 4px solid var(--evt, var(--brand));
  border-radius: var(--radius-sm); background: var(--surface);
  margin-bottom: .6rem; transition: box-shadow .15s ease, transform .15s ease;
}
.pc-prog-item:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.pc-prog-time {
  font-weight: 700; font-size: .88rem; font-variant-numeric: tabular-nums;
  color: var(--ink-2); line-height: 1.35;
}
.pc-prog-title { font-weight: 700; font-size: 1rem; letter-spacing: -.01em; }
.pc-prog-meta { font-size: .8rem; color: var(--muted); margin-top: .2rem; }
@media (max-width: 640px) {
  .pc-prog-item { grid-template-columns: 1fr; gap: .35rem; }
  .pc-prog-item > :last-child { justify-self: start; }
}

/* Pillar and category chips take their colour from the row's own data, so a
   pillar renamed and recoloured in Administration needs no CSS change. */
.pc-chip {
  display: inline-flex; align-items: center; gap: .32rem;
  padding: .17rem .55rem; border-radius: 999px;
  font-size: .7rem; font-weight: 700; letter-spacing: .02em;
  background: color-mix(in srgb, var(--chip, #64748b) 14%, transparent);
  color: var(--chip, #64748b);
  border: 1px solid color-mix(in srgb, var(--chip, #64748b) 28%, transparent);
}
[data-theme="dark"] .pc-chip {
  background: color-mix(in srgb, var(--chip, #64748b) 24%, transparent);
  color: color-mix(in srgb, var(--chip, #64748b) 70%, white);
}

/* ─────────────────────── 9. Announcement severities ────────────────────── */

.pc-notice {
  border-radius: var(--radius-sm); padding: .85rem 1rem;
  border-left: 4px solid var(--nc, var(--info)); background: var(--surface-2);
  display: flex; gap: .7rem; align-items: flex-start;
}
.pc-notice i { color: var(--nc, var(--info)); font-size: 1.1rem; margin-top: .1rem; }
.pc-notice-title { font-weight: 700; }
/* The programme's three colours, not the six-hue status set these carried.
   An announcements page showing sky blue, cyan, amber and two reds beside a
   green-and-navy identity reads as a stock admin theme showing through — and
   it was the same fault the homepage cards had. Green for routine, navy for
   the ones worth noticing, red kept for genuine urgency so it still means
   something when it appears. */
.pc-notice.info      { --nc: var(--reef); }
.pc-notice.notice    { --nc: var(--brand); }
.pc-notice.important { --nc: var(--brand-deep); }
.pc-notice.urgent    { --nc: var(--coral); background: color-mix(in srgb, var(--coral) 7%, var(--surface)); }
.pc-notice.urgent .pc-notice-title { color: var(--coral); }
/* Used by the settings screens for state rather than announcements. `warning`
   is loud on purpose: it is what tells an administrator that email is being
   diverted and no delegate is hearing from the platform. */
.pc-notice.warning   { --nc: var(--coral); background: color-mix(in srgb, var(--coral) 8%, var(--surface)); }
.pc-notice.success   { --nc: var(--reef); }
.pc-notice.danger    { --nc: var(--coral); background: color-mix(in srgb, var(--coral) 7%, var(--surface)); }

/* ── Mode choice: a pair of radios that read as cards ────────────────────────
   Live/Test is a consequential switch — one of the two settings means nobody
   receives anything — so it gets the room to say what each option does rather
   than being a dropdown with two words in it. */
.pc-mode-choice { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
@media (max-width: 575.98px) { .pc-mode-choice { grid-template-columns: 1fr; } }

.pc-mode-option {
  display: flex; gap: .6rem; align-items: flex-start; cursor: pointer;
  border: 2px solid var(--line); border-radius: var(--radius-sm);
  padding: .7rem .8rem; background: var(--surface); transition: border-color .15s, background .15s;
}
.pc-mode-option:hover { border-color: var(--brand); }
.pc-mode-option input { margin-top: .22rem; flex: 0 0 auto; }
.pc-mode-option.is-on { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 6%, var(--surface)); }
.pc-mode-option.warn.is-on { border-color: #d97706; background: color-mix(in srgb, #d97706 9%, var(--surface)); }
.pc-mode-option:focus-within { outline: 2px solid var(--brand); outline-offset: 2px; }

/* ──────────────────────────── 10. Venue map ────────────────────────────── */

/* ── The venue plan ─────────────────────────────────────────────────────────
   The scroller is what makes this usable on a phone. The plan itself has a
   minimum width, so on a narrow screen it overflows and is swiped rather than
   compressed: at 356px across, eighteen pins and their labels overlap into an
   unreadable heap, and a plan you cannot read is worse than no plan. */
.pc-map-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  /* Room for the pin shadow, and for the scrollbar not to sit on the border. */
  padding-bottom: .35rem;
}
.pc-map {
  position: relative; border-radius: var(--radius); overflow: hidden;
  background: linear-gradient(160deg, #e8f1f9, #f3f6fa);
  border: 1px solid var(--line); aspect-ratio: 16 / 10;
  min-width: 34rem;
}
[data-theme="dark"] .pc-map { background: linear-gradient(160deg, #0c1f33, #060f1a); }
.pc-map-marker {
  position: absolute; transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: .2rem;
  cursor: pointer;
}
.pc-map-pin {
  width: 1.9rem; height: 1.9rem; border-radius: 50%;
  display: grid; place-items: center; color: #fff; font-size: .85rem;
  background: var(--brand); box-shadow: 0 2px 8px rgba(0,0,0,.28);
  border: 2px solid #fff;
}
.pc-map-marker.desk   .pc-map-pin { background: #0284c7; }
.pc-map-marker.media  .pc-map-pin { background: #dc2626; }
.pc-map-marker.medical .pc-map-pin { background: #16a34a; }
.pc-map-marker.transport .pc-map-pin { background: #b45309; }
.pc-map-label {
  font-size: .66rem; font-weight: 700; background: var(--surface);
  padding: .1rem .4rem; border-radius: 4px; white-space: nowrap;
  border: 1px solid var(--line); color: var(--ink);
}
.pc-map-marker.active .pc-map-pin { outline: 3px solid var(--brand-2); }

/* ─────────────────────────── 11. Certificate ───────────────────────────── */

.pc-cert {
  background: #fff; color: #0f172a; padding: 3rem;
  border: 10px solid var(--brand); border-radius: 4px;
  text-align: center; position: relative; max-width: 60rem; margin: 0 auto;
}
.pc-cert::before {
  content: ''; position: absolute; inset: .55rem;
  border: 1.5px solid color-mix(in srgb, var(--brand) 40%, transparent);
  pointer-events: none;
}
.pc-cert-eyebrow {
  letter-spacing: .3em; text-transform: uppercase; font-size: .78rem;
  color: var(--brand); font-weight: 700;
}
.pc-cert-name {
  font-size: clamp(1.8rem, 5vw, 3rem); font-weight: 800; letter-spacing: -.02em;
  margin: 1.2rem 0 .3rem; border-bottom: 2px solid var(--line); display: inline-block;
  padding: 0 2rem .5rem;
}
.pc-cert-event { font-size: 1.25rem; font-weight: 700; margin-top: 1rem; }

/* ──────────────────────────── 12. Print rules ──────────────────────────── */

@media print {
  /* Everything that is navigation, action or chrome disappears. What remains
     is the artefact: a badge sheet, an attendance list, a certificate. */
  .pc-topbar, .pc-sidebar, .pc-page-head, .pc-backdrop, .no-print,
  .btn, .alert, nav, footer, .pc-masi { display: none !important; }
  .pc-main   { margin: 0 !important; padding: 0 !important; }
  .pc-shell  { display: block !important; }
  body       { background: #fff !important; }
  .pc-card   { border: none !important; box-shadow: none !important; }

  .pc-badge {
    box-shadow: none; border: 1px solid #cbd5e1;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .pc-badge-sheet {
    display: grid !important; grid-template-columns: repeat(2, 86mm);
    gap: 6mm; justify-content: center;
  }
  .pc-cert { border-width: 10px; -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  /* A table split across pages is unreadable without its header repeated, and
     an attendance register is exactly the document that runs to twelve pages. */
  thead { display: table-header-group; }
  tr, .pc-prog-item { page-break-inside: avoid; }
  a[href]::after { content: ''; }   /* no printed URLs cluttering a register */
}

/* Badge sheet on screen: same grid, so what you see is what prints. */
.pc-badge-sheet {
  display: grid; grid-template-columns: repeat(auto-fill, 86mm);
  gap: 6mm; justify-content: center;
}

/* ───────────────────────────── 13. Utilities ───────────────────────────── */

.pc-num { font-variant-numeric: tabular-nums; }
.pc-truncate-2 {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.pc-dot-live {
  display: inline-block; width: .5rem; height: .5rem; border-radius: 50%;
  background: var(--success); animation: pcPulse 2s ease-in-out infinite;
}
@keyframes pcPulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 55%, transparent); }
  50%      { opacity: .75; box-shadow: 0 0 0 .38rem transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .pc-dot-live, .pc-scan-reticle::after, .pc-feed li.fresh { animation: none !important; }
}

/* ─────────────────────── 14. Official marks in the shell ───────────────────
   The event mark replaces the typographic crest wherever it has been uploaded.
   It sits on a white plate rather than the brand gradient: the logo carries its
   own colour, and a coloured tile behind it muddies both. */

/* The event mark is a horizontal lockup — PACIFIC / PRECOP31 / FIJI-TUVALU
   stacked across roughly 2.6 : 1 — so it must not be forced into the square
   tile the typographic crest uses. Height is fixed and width follows the
   artwork; squeezing it into a square shrinks the wordmark to the point where
   the words stop being words. */
.pc-crest-mark-img {
  width: auto !important; height: 2.1rem; aspect-ratio: auto;
  background: #fff !important;
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: .16rem .36rem;
  display: block;
}
.pc-crest-mark-img img { height: 100%; width: auto; object-fit: contain; display: block; }

[data-theme="dark"] .pc-crest-mark-img { background: #fff !important; border-color: transparent; }

/* The public shell puts the mark on the brand bar, so it needs the same plate. */
.lp-navbar .pc-crest-mark-img { border-color: rgba(255,255,255,.35); }

/* Paired marks — arms on the left, event mark on the right — as used on the
   public hero and the sign-in panel. The arms are line art that reads on any
   ground; the event mark is blue-on-transparent and needs its white plate. */
.pc-marks { display: flex; align-items: center; gap: 1.15rem; flex-wrap: wrap; }
.pc-marks img { display: block; }
.pc-marks .arms { height: 3.6rem; width: auto; }
.pc-marks .event {
  height: 3rem; width: auto;
  background: #fff; border-radius: 8px; padding: .42rem .62rem;
  box-shadow: 0 6px 18px -8px rgba(5,25,47,.7);
}

/* The three-partner lockup. Every mark gets the same white plate: two of the
   three are dark-ink crests, so against the footer's navy they would otherwise
   disappear into it. Sized by optical weight rather than by height — the
   squarer a mark is, the more height it gets, or the governments' marks come
   out a fraction of the Pre-COP31 lockup's area. See _marks.php. */
.pc-marks .pc-lockup {
  width: auto;
  background: #fff; border-radius: 8px; padding: .4rem .55rem;
}
.pc-marks .pc-lockup-wide   { height: 2.7rem; }
.pc-marks .pc-lockup-mid    { height: 3.7rem; }
.pc-marks .pc-lockup-square { height: 3.8rem; }
.pc-marks .rule {
  width: 1px; align-self: stretch; min-height: 2.4rem;
  background: rgba(255,255,255,.28);
}

/* ──────────────────── 15. Sign-in — the two-column panel ─────────────────
   The inherited .pc-auth-split is only a max-width; the markup it wraps is a
   pitch panel and a form, which want to sit side by side on a wide display and
   stack on a phone. That layout is written here rather than in the base file
   because it is this platform's screen, not AttendFlow's.

   .pc-auth is a flex *column* (the navbar and masi trim sit at their natural
   height at the top), so the split needs margin:auto to centre in what's left
   rather than align-items on the parent, which would lay the trim out beside
   the form. */

.pc-auth-split {
  width: 100%; max-width: 68rem; margin: auto;
  padding: 34px 18px; position: relative; z-index: 1;
  display: grid; gap: clamp(1.5rem, 4vw, 3.25rem);
  grid-template-columns: 1fr;
  align-items: center;

  /* Guaranteed room for the base contour band. .pc-auth is a flex column with
     nothing after this split, so the split's own bottom edge is where
     .pc-auth ends — and the band is pinned to that same bottom edge with
     `bottom: 0`. A fixed 34px told the split nothing about how tall the
     artwork actually is, so on any laptop-height window (roughly under
     800px) the sign-in footer rendered ON TOP of the band instead of above
     it — dark text over a photographic green pattern, half swallowed by it.
     Tied to the same --base custom property the artwork itself uses (defined
     on .pc-auth, inherited here), so the two cannot drift apart again. */
  padding-bottom: calc(var(--base) + 1.5rem);
}
@media (min-width: 62em) {
  .pc-auth-split { grid-template-columns: 1.05fr .95fr; }
}

/* ── The programme's ground, not the old blue one ────────────────────────────
   Sign-in and registration were still the inherited blue gradient — a wall of
   #0059b3 with a tapa runner over it — while every public page in front of
   them had moved to white with the contour bands. Arriving here from the
   homepage read as landing on a different site.

   Same construction as .hp-has-contour on the homepage: white, one crop of the
   artwork at the top edge and one at the bottom, each at its own proportions
   so nothing is stretched. The screen is a flex column with the bar at the top
   and the split centred in what is left, so the crops are pinned to the
   screen's own edges. */
.pc-auth {
  background: #fff;
  --crest: 18.167vw;   /* vd-band-crest.svg 1200x218 */
  --base:  15.333vw;   /* vd-band-base.svg  1200x184 */
}
.pc-auth::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0;
  height: var(--crest); width: auto;
  background: var(--img-band-crest) center top / 100% auto no-repeat;
  opacity: 1; z-index: 0; pointer-events: none;
  -webkit-mask-image: none; mask-image: none;
}
.pc-auth::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0;
  height: var(--base);
  background: var(--img-band-base) center bottom / 100% auto no-repeat;
  z-index: 0; pointer-events: none;
}
@media (max-width: 61.99em) {
  /* The portrait crops, exactly as the homepage swaps them below the split. */
  .pc-auth { --crest: 24.268vw; --base: 35.732vw; }
  .pc-auth::before { background-image: var(--img-portrait-crest); }
  .pc-auth::after  { background-image: var(--img-portrait-base); }
}

/* Inside the split the pitch panel is a left column, not the small centred
   header the base file styles it as. Blue type on a whisper of the light
   green, which is the homepage's tinted-band treatment. */
.pc-auth-split .pc-auth-brand {
  text-align: left; margin-bottom: 0;
  border-radius: var(--radius); padding: clamp(1.4rem, 3vw, 2.2rem);
  background: color-mix(in srgb, var(--reef-2) 9%, #fff);
  border: 1px solid color-mix(in srgb, var(--reef) 20%, transparent);
  color: var(--ink);
}
/* app.css sets every one of these to white for the blue ground it was built
   for, and does it with two-class selectors — so these have to match that
   weight or the copy stays white on a white panel. */
.pc-auth-split .pc-auth-brand,
.pc-auth-split .pc-auth-brand .pc-auth-point-title,
.pc-auth-split .pc-auth-point strong { color: var(--ink); }

.pc-auth-split .pc-auth-brand h1,
.pc-auth-split .pc-auth-brand h2 { color: var(--brand); }

.pc-auth-split .pc-auth-brand p,
.pc-auth-split .pc-auth-brand .pc-auth-lede,
.pc-auth-split .pc-auth-brand .pc-auth-point-text,
.pc-auth-split .pc-auth-point span { color: var(--muted); }
.pc-auth-split .pc-auth-brand .pc-auth-point i,
.pc-auth-split .pc-auth-brand .bi { color: var(--reef); }
/* Kept for the FALLBACK row only — the panel normally renders the combined
   lockup below. The note this rule carried ("the marks carry their own white
   background as JPEGs") stopped being true at some point: all three are
   transparent PNGs, so the plate was no longer covering a white box, it was
   adding one. Left in place because the three-mark row only appears when the
   combined artwork is missing, and that row's balance was tuned with it. */
.pc-auth-split .pc-marks .pc-lockup {
  background: #fff; border-radius: 7px; padding: .3rem .45rem;
  box-shadow: 0 2px 10px -6px rgba(5,25,47,.5);
}

/* ── The combined lockup on the sign-in panel ──────────────────────────────
   No plate: this ground is color-mix(--reef-2 9%, #fff), which is a hair off
   white, and the artwork's dark ink reads on it directly. Same decision as the
   footer, for the same reason, and the opposite of what the three marks above
   needed on the blue panel this screen used to have.

   Sized by width with the height following, like every other copy — the
   artwork's ratio has changed once already and a width clamp is the only form
   that does not silently resize when it changes again. The panel is a column
   in a split layout, so max-width keeps it inside that column when the split
   collapses on a phone. */
.pc-auth-lockup {
  display: block;
  width: clamp(10rem, 7rem + 11vw, 15.5rem);
  max-width: 100%;
  height: auto;
}
.pc-auth-split .pc-marks .rule {
  background: color-mix(in srgb, var(--brand) 25%, transparent);
}
/* .pc-band lays its tapa in a ::before at inset 0 and lifts `> .container`
   above it. The panel's child is a plain div, so it needs lifting here or the
   cloth prints over the words. */
.pc-auth-split .pc-auth-brand > * { position: relative; z-index: 1; }

/* On a phone the pitch is below the form: somebody signing in on the concourse
   wants the fields first, and the panel is context, not an obstacle. */
@media (max-width: 61.99em) {
  .pc-auth-split { display: flex; flex-direction: column-reverse; }
  .pc-auth-split .pc-auth-brand { width: 100%; }
}

/* The form column inside the split is a column of the grid, so the base file's
   auto margins would strand it against the pitch panel. */
.pc-auth-split .pc-auth-wrap {
  max-width: 30rem; margin: 0; padding: 0; width: 100%;
}
@media (max-width: 61.99em) {
  .pc-auth-split .pc-auth-wrap { margin: 0 auto; }
}

/* The tapa runner that used to sit behind this screen has gone with the blue
   ground: ::before now carries the contour crest (see above). */

/* The attribution under the card was white for the blue ground and is
   invisible on white. Same for the link colour, which was picked to read
   against navy. */
/* Spans the whole split, so it is centred on the PAGE rather than inside
   the form column it used to live in. */
.pc-auth-foot   { grid-column: 1 / -1; text-align: center; color: var(--muted); }
.pc-auth-foot a { color: var(--brand); }

/* Bootstrap ships `code` in pink (#d63384). Every reference the platform shows
   a person — PC31-000002, BDG-TY85ZYW, a credential token — is marked up as
   code, so that default puts a colour on screen that is in no part of the
   Pre-COP31 identity. References are identifiers, not warnings: ink weight
   carries them, and the brand carries the ones that are links. */
:root, [data-theme="light"] { --bs-code-color: #003090; }
[data-theme="dark"]         { --bs-code-color: #79c4f7; }
code { color: var(--bs-code-color); }
a > code, code a { color: inherit; }

/* ══════════════ 16. Public chrome — nav, reveals, footer, controls ═══════
   Shared by every public page so the homepage's visual language does not stop
   at the homepage. The homepage's own composition lives in home.css. */

/* ── Skip link ── */
.pc-skip {
  position: absolute; left: 1rem; top: -4rem; z-index: 2000;
  background: var(--brand); color: #fff; font-weight: 700;
  padding: .6rem 1rem; border-radius: 0 0 10px 10px; text-decoration: none;
  transition: top .18s ease;
}
.pc-skip:focus { top: 0; color: #fff; outline: 3px solid #fff; outline-offset: 2px; }

/* ── Navigation ─────────────────────────────────────────────────────────
   Over a hero the bar is glass: the panel behind it is a designed image and a
   solid strip across the top of it wastes the best part of the page. Past the
   hero it takes its own ground, because text needs a background it can be read
   against, not a tint of whatever happens to scroll underneath. */
.pc-nav {
  transition: background .35s ease, backdrop-filter .35s ease,
              box-shadow .35s ease, padding .28s ease;
}
.pc-nav-over {
  background: transparent !important;
  box-shadow: none !important;
  padding: .85rem 0;
}
.pc-nav-over::after { opacity: 0 !important; }     /* no masi over the hero */
.pc-nav-over.is-solid {
  background: linear-gradient(135deg,
              color-mix(in srgb, var(--brand-deep) 92%, transparent) 0%,
              color-mix(in srgb, var(--brand)      92%, transparent) 100%) !important;
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
          backdrop-filter: blur(14px) saturate(1.3);
  box-shadow: 0 10px 30px -14px rgba(3, 20, 44, .75) !important;
  padding: .42rem 0;
}
.pc-nav-over.is-solid::after { opacity: .3 !important; }

/* ── The public header ───────────────────────────────────────────────────────
   One bar, one colour, always. It used to be transparent over the homepage
   hero and blue everywhere else, restyling itself as the reader scrolled; on a
   white hero that transition read as a fault rather than as an effect, so the
   bar is now fixed in every sense — white ground, blue type, a hairline under
   it, and no state that depends on scroll position.

   White rather than the blue it was: the programme's direction is white with
   two greens, and a blue slab across the top of every page is the one thing on
   these pages that would not be following it.

   Everything inside the bar is written for a dark ground — `text-white` on the
   logo link and the theme toggle, white links, a white ghost button. Those are
   in the markup and shared with the staff shell, so they are overridden here
   rather than edited there. */
.pc-nav-white,
.pc-nav-white.solid,
.pc-nav-white.scrolled {
  background: rgba(255,255,255,.92) !important;
  -webkit-backdrop-filter: blur(14px) saturate(1.25);
          backdrop-filter: blur(14px) saturate(1.25);
  border-bottom: 1px solid color-mix(in srgb, var(--brand) 13%, transparent);
  box-shadow: 0 6px 22px -18px rgba(11, 42, 82, .5) !important;
}
.pc-nav-white::after { opacity: 0 !important; }   /* no masi weave on white */

.pc-nav-white .lp-nav-link,
.pc-nav-white .text-white,
.pc-nav-white .lp-btn-ghost { color: var(--brand) !important; }

.pc-nav-white .lp-nav-link:hover,
.pc-nav-white .lp-nav-link:focus { background: var(--brand-soft); color: var(--brand-deep) !important; }
.pc-nav-white .lp-nav-link.active,
.pc-nav-white .pc-nav-link.is-active { background: transparent; color: var(--brand-deep) !important; }
.pc-nav-white .pc-nav-link::after { background: var(--brand); }

.pc-nav-white .lp-btn-ghost {
  background: transparent;
  border-color: color-mix(in srgb, var(--brand) 32%, transparent);
}
.pc-nav-white .lp-btn-ghost:hover,
.pc-nav-white .lp-btn-ghost:focus-visible {
  background: var(--brand-soft); border-color: var(--brand);
  color: var(--brand-deep) !important;
}
.pc-nav-white .lp-btn-solid { background: var(--brand); color: #fff !important; }
.pc-nav-white .lp-btn-solid:hover { background: var(--brand-deep); color: #fff !important; }

.pc-nav-white .pc-nav-burger { background: var(--brand-soft); }
.pc-nav-white .pc-nav-burger span { background: var(--brand); }

/* .pc-marks gives every mark a white plate so the two dark-ink crests survive
   the footer's navy. The bar is white, so here that plate is a white rectangle
   on white, announcing itself with a shadow. */
.pc-nav-white .pc-nav-marks .pc-lockup {
  background: none; box-shadow: none; padding: 0; border-radius: 0;
}

/* The mobile panel drops out of a white bar, so it needs its own ground
   instead of inheriting the dark bar's translucency. */
.pc-nav-white .pc-nav-panel { background: #fff; border-top: 1px solid var(--line); }
.pc-nav-white .pc-nav-panel a { color: var(--brand); }
.pc-nav-white .pc-nav-panel a:hover,
.pc-nav-white .pc-nav-panel a:focus-visible { background: var(--brand-soft); color: var(--brand-deep); }
.pc-nav-white .pc-nav-panel a.is-active { color: var(--brand-deep); box-shadow: inset 3px 0 0 var(--reef); }

[data-theme="dark"] .pc-nav-white,
[data-theme="dark"] .pc-nav-white.solid,
[data-theme="dark"] .pc-nav-white.scrolled {
  background: color-mix(in srgb, var(--surface) 92%, transparent) !important;
  border-bottom-color: var(--line);
}
[data-theme="dark"] .pc-nav-white .pc-nav-panel { background: var(--surface); }

/* Underline that grows from the centre — an active state that animates in
   rather than appearing fully formed. */
.pc-nav-link { position: relative; }
.pc-nav-link::after {
  content: ''; position: absolute; left: 50%; right: 50%; bottom: -.32rem; height: 2px;
  background: #fff; border-radius: 2px; opacity: .9;
  transition: left .26s cubic-bezier(.22,1,.36,1), right .26s cubic-bezier(.22,1,.36,1);
}
.pc-nav-link:hover::after, .pc-nav-link:focus-visible::after { left: 0; right: 0; }
.pc-nav-link.is-active { font-weight: 700; }
.pc-nav-link.is-active::after { left: 0; right: 0; }

/* ── Mobile menu ── */
.pc-nav-burger {
  width: 2.4rem; height: 2.4rem; flex: none; border: 0; padding: 0;
  background: rgba(255,255,255,.14); border-radius: 10px;
  display: grid; place-content: center; gap: 4px; cursor: pointer;
}
.pc-nav-burger span {
  display: block; width: 1.05rem; height: 2px; border-radius: 2px; background: #fff;
  transition: transform .28s cubic-bezier(.22,1,.36,1), opacity .18s ease;
}
.pc-nav-burger.is-open span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.pc-nav-burger.is-open span:nth-child(2) { opacity: 0; }
.pc-nav-burger.is-open span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.pc-nav-panel {
  overflow: hidden; max-height: 0; opacity: 0;
  background: var(--brand-deep);
  transition: max-height .34s cubic-bezier(.22,1,.36,1), opacity .22s ease;
}
/* Room for every link, and a scroll inside the panel on a short screen. It
   was 22rem: with Exhibitors added (2026-09-25) the eighth link, Sign in,
   fell below it, and on a phone this panel is the only way to Sign in. */
.pc-nav-panel.is-open {
  max-height: calc(100vh - 4.5rem);
  max-height: min(40rem, calc(100dvh - 4.5rem));
  overflow-y: auto; overscroll-behavior: contain;
  opacity: 1;
}
.pc-nav-panel a {
  display: block; padding: .8rem 1.1rem; color: rgba(255,255,255,.88);
  text-decoration: none; font-weight: 600; border-top: 1px solid rgba(255,255,255,.1);
}
.pc-nav-panel a:hover, .pc-nav-panel a:focus-visible { background: rgba(255,255,255,.1); color: #fff; }
.pc-nav-panel a.is-active { color: #fff; box-shadow: inset 3px 0 0 var(--reef-2); }

/* ── Scroll reveal ──────────────────────────────────────────────────────
   Gated on .pc-js, which only exists when scripting is running. Without it
   nothing here applies and the page is simply visible — a reveal system that
   can leave content hidden is a bug wearing a design's clothes. */
.pc-js [data-reveal] {
  opacity: 0;
  transition: opacity .7s cubic-bezier(.22,1,.36,1), transform .7s cubic-bezier(.22,1,.36,1);
  will-change: opacity, transform;
}
.pc-js [data-reveal="up"]    { transform: translate3d(0, 34px, 0); }
.pc-js [data-reveal="left"]  { transform: translate3d(-38px, 0, 0); }
.pc-js [data-reveal="right"] { transform: translate3d(38px, 0, 0); }
.pc-js [data-reveal="scale"] { transform: scale(.94); }
.pc-js [data-reveal="fade"]  { transform: none; }
.pc-js [data-reveal].is-in   { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .pc-js [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }
}

/* ── Footer ─────────────────────────────────────────────────────────────
   Pale mint, not a dark panel.

   #bfe5cf is the Ministry's choice, and it is already in this stylesheet — it
   is the light triangle in .pc-masi, the shared band used for section
   dividers. So the footer is not introducing a colour; it is finally sitting
   on one the site already uses, which is what "all the greens look different"
   was really about.

   Going light also settles the join that kept failing. A dark panel under the
   contour artwork meant a drop from 48% lightness to 16% in a single band, and
   no amount of hue-matching fixes a cliff. At 82% the footer sits between the
   white page and the hills painted on it, so the page gets quieter towards the
   bottom instead of falling off a shelf.

   Type inverts with the ground: the direction sets text in royal blue, and on
   pale mint that is legible where white was not. The closing bar stays dark —
   it is the one band that should feel like the end of the page.

   One blue, not blue-body/green-headings. The eyebrows and the muted dates
   used to be a deep green (#0d5334 / #2f5843) sitting next to navy body text
   and navy links — two colour families in one small panel that read as a
   mismatch rather than a hierarchy. #134FA0 is the requested navy for all of
   it; 5.8:1 on the mint, still comfortably past AA. */
:root, [data-theme="light"] {
  --pc-foot-bg:   #bfe5cf;   /* hsl(145,42%,82%) — the Ministry's mint */
  --pc-foot-ink:  #134FA0;   /* body text and links — 5.8:1 on the mint */
  --pc-foot-h:    #134FA0;   /* eyebrows, icons — same navy, not green */
  --pc-foot-mute: #134FA0;   /* dates and venue — same navy, not green */
  --pc-foot-rule: rgba(19,79,160,.18);
  --pc-foot-base: #3ab97c;   /* the closing bar — the light brand green */
  --pc-foot-base-ink: #073520;  /* 5.5:1 on it; white would be 2.5:1 */
}
[data-theme="dark"] {
  /* The mint would be a lamp in a dark room. Same hue, taken down to a panel
     that belongs in the dark theme, with the type flipping back to light. */
  --pc-foot-bg:   #0b4529;
  --pc-foot-ink:  rgba(255,255,255,.84);
  --pc-foot-h:    #9fe3bf;
  --pc-foot-mute: rgba(255,255,255,.68);
  --pc-foot-rule: rgba(255,255,255,.12);
  --pc-foot-base: #052b19;
  --pc-foot-base-ink: rgba(255,255,255,.82);
}

.pc-footer { position: relative; margin-top: auto; }

/* ── The footer closes on the contour artwork, not on masi ─────────────────
   Two masi textures were removed here on 2026-09-11 after a complaint about
   the weave: the woven strip across the top of the footer, and a faint tapa
   diamond laid over the whole panel. What replaced them is the base crop of
   the programme's contour artwork, rising from the footer's floor. These are
   the same shapes that close the homepage's banded sections and the sign-in
   screen.

   Built exactly like .hp-contour-base, and bound by the same rule: never
   stretched, never cropped. The band is as deep as the crop's own aspect
   ratio (--base), and the panel only reserves the part of that depth the
   shapes reach under the text (--base-clear, the homepage's measured
   figures), so the links never sit on green. */
.pc-footer-body {
  --base: 15.333vw;          /* vd-band-base.svg 1200x184 */
  /* .53, down from .60 on 2026-09-12. The figure is the depth the shapes reach
     across the span the text occupies, and the rule above now holds that span
     inside the middle 80% of the frame, where the measured profile is 0.568 of
     a band whose own depth is .53 + the clamp below. Re-measured after the
     change: 19-24px of clear air at every width from 992 to 2560. Do not raise
     the container cap and this figure together. */
  --base-clear: .53;
  background: var(--pc-foot-bg);
  color: var(--pc-foot-ink);
  /* Top padding cut from 3.9rem to 2.6rem on 2026-09-12. The BOTTOM term is
     not free to cut with it: it is the measured clearance above the artwork,
     and scratchpad/band_profile.js shows the shapes reach 0.57 of the band
     under the container at 1920 but 0.72 at 1440 and 0.85 at 1200 — the
     container takes a larger share of a narrower frame and reaches into the
     tall peaks near the edges. The flat .60 is already only just enough there,
     which is what the extra term below is absorbing. */
  padding: clamp(1.9rem, 3.2vw, 2.6rem) 0
           calc(var(--base) * var(--base-clear) + clamp(1.25rem, 2.5vw, 2rem));
  position: relative; overflow: hidden;
}
.pc-footer-body::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0;
  height: var(--base); pointer-events: none;
  background: var(--img-band-base) center bottom / 100% auto no-repeat;
}
@media (max-width: 61.99em) {
  /* The portrait crop below the split, swapped exactly as the homepage and
     the sign-in screen swap it. vd-portrait-base.svg 820x293. */
  .pc-footer-body { --base: 35.732vw; --base-clear: 1; }
  .pc-footer-body::before { background-image: var(--img-portrait-base); }
}
.pc-footer-body > .container { position: relative; z-index: 1; }

/* ── Keep the text out of the artwork's tall ends ────────────────────────────
   --base-clear is ONE number, but the share of the frame the text covers is
   not: .container is capped at 1320px, so it is 69% of a 1920px frame and 95%
   of a 1200px one. The artwork's peaks are at the frame EDGES — it reaches
   0.57 of its depth across the middle 80% and 0.99 at the very edge — so on
   the widths where the container nearly fills the frame it reached straight
   into those peaks and the links sat on green. Measured deficits before this
   rule: -23px at 992, -16px at 1200, -17px at 1400 (scratchpad/band_profile2.js).

   Raising --base-clear to cover the worst case would have made the footer
   taller everywhere to fix four widths. Holding the text inside the middle 80%
   instead puts every width back on the 0.57 profile the flat figure was
   measured from, and costs no height at all. Above ~1650px the 1320px cap is
   already inside 80vw, so nothing moves on a large screen.

   Wide crop only: below 62em the portrait artwork reaches full depth
   everywhere, --base-clear is 1, and there is nothing to hold the text away
   from. */
@media (min-width: 62em) {
  .pc-footer-body > .container { max-width: min(1320px, 80vw); }
}

/* Ratios, and a floor of ZERO.
   A rem minimum cannot work here: the accessibility panel scales the root font
   size, so minmax(21rem, …) became a 504px track on a phone at the largest
   step and the footer ran off the side of the screen — for exactly the readers
   that setting exists to help. minmax(0, …) lets every track shrink to what is
   in it, so large text wraps instead of overflowing, and the 1.6fr share is
   what keeps the three-mark lockup on one line at normal size without naming a
   width that can outgrow its container. */
.pc-footer-head {
  display: grid;
  align-items: start;
  /* The ROW gap is the one that costs height, and it costs it three times over
     below 75em where the brand takes a row of its own and the columns wrap. */
  gap: clamp(1.35rem, 2.6vw, 1.9rem) clamp(1.5rem, 3.5vw, 3.25rem);
  grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
}
/* Four across only where there is genuinely room. Media-query em is the
   INITIAL font size, not the scaled one, so this boundary stays put whatever
   the reader has chosen. */
@media (max-width: 74.99em) {
  .pc-footer-head { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .pc-footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 35.99em) {
  .pc-footer-head { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* No white plates on the marks here. The plates exist because two of the three
   are dark-ink crests that would have vanished into the old navy panel; on
   pale mint that reasoning inverts — dark ink reads perfectly, and three white
   cards on a soft green ground look like stickers. */
.pc-footer-marks { margin-bottom: 1.15rem; gap: .85rem; }
.pc-footer-marks .pc-lockup {
  background: transparent; padding: 0; border-radius: 0;
  max-width: 100%; object-fit: contain;
}
.pc-footer-marks .pc-lockup-wide   { height: 2.3rem; }
.pc-footer-marks .pc-lockup-mid    { height: 3.7rem; }
.pc-footer-marks .pc-lockup-square { height: 3.1rem; }
.pc-footer-marks .rule { background: var(--pc-foot-rule); }

/* ── The combined lockup in the footer ─────────────────────────────────────
   The three-mark row above is the fallback; this is what the footer normally
   renders. Same file and same partial as the navigation bar — see _lockup.php.

   No plate, for the reason the marks above lost theirs: the ground is pale
   mint, not the old navy, and dark ink reads on it. The artwork's own interior
   whites — the Fiji shield, the inside of the coat of arms — stay white
   against the mint exactly as they do in the three separate marks.

   Sized by width like the bar's copy, because one image has one known ratio
   and a width can be clamped; the crest height then follows. Fitted so the
   crests land near the 3.1rem the square mark had, and capped at 17.5rem
   because .pc-footer-brand is a grid column, not the page — max-width keeps it
   inside that column at every breakpoint, including the one where the brand
   block spans the full grid. */
/* ── The vertical rhythm, tightened 2026-09-12 ───────────────────────────────
   The footer was called out as too tall. The contour band at the foot cannot
   be made shorter — the artwork is never stretched and never cropped, its
   depth is its own aspect ratio, and the clearance above it is measured from
   where the shapes actually reach (see .pc-footer-body). So the height had to
   come out of the type instead: every margin through here is roughly a quarter
   smaller than it was, which is enough to read as a change without making the
   columns cramped. */
.pc-footer-lockup {
  display: block;
  width: clamp(11rem, 8rem + 12vw, 17.5rem);
  max-width: 100%;
  height: auto;
  margin-bottom: .85rem;
}

.pc-footer-lede {
  color: var(--pc-foot-ink); font-size: 1.02rem; font-weight: 700;
  line-height: 1.35; max-width: min(26rem, 100%); margin-bottom: .55rem;
}
.pc-footer-where {
  color: var(--pc-foot-mute); font-size: .9rem; line-height: 1.55; margin: 0;
}

.pc-footer-h {
  font-size: .7rem; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--pc-foot-h); margin-bottom: .7rem;
}

.pc-footer-links { list-style: none; padding: 0; margin: 0; font-size: .92rem; }
.pc-footer-links li { margin-bottom: .42rem; }
/* break-word, not anywhere: `anywhere` split "Announcements" as
   "Announcement / s" at the largest text step. This breaks a word only when it
   genuinely cannot fit. */
.pc-footer-links a { overflow-wrap: break-word; }

.pc-footer-body a { color: var(--pc-foot-ink); text-decoration: none; }
.pc-footer-body a:hover,
.pc-footer-body a:focus-visible { color: var(--pc-foot-h); text-decoration: underline; }

/* Contact lines, with the icon in its own slot so the addresses align down one
   edge instead of ragging against the width of each glyph. */
.pc-footer-contact { list-style: none; padding: 0; margin: 0; font-size: .92rem; }
.pc-footer-contact li { display: flex; align-items: center; gap: .65rem; margin-bottom: .7rem; }
.pc-footer-contact a { display: flex; align-items: center; gap: .65rem; }
.pc-footer-contact span { min-width: 0; overflow-wrap: anywhere; }
.pc-footer-contact .bi {
  flex: none; width: 1.7rem; height: 1.7rem; border-radius: 50%;
  display: inline-grid; place-items: center; font-size: .78rem;
  background: rgba(19,79,160,.13); color: var(--pc-foot-h);
  transition: background .18s ease, color .18s ease;
}
/* :focus-visible as well as :hover — the text already answered the keyboard
   (.pc-footer-body a:focus-visible), and leaving the slot out of it gave a
   tabbing reader a half-lit control. */
.pc-footer-contact a:hover .bi,
.pc-footer-contact a:focus-visible .bi { background: var(--pc-foot-h); color: #fff; }

/* ── The closing strip ───────────────────────────────────────────────────────
   A row of its own under the four columns rather than a fifth column, because
   .pc-footer-head has exactly four grid tracks on a wide screen: a fifth child
   does not make a fifth column, it drops onto a second grid row under the
   brand and reads as the footer having broken.

   Accounts, support address, hashtag — read across. The accounts reuse
   .pc-footer-contact wholesale, so the round slot, the hover swap and the link
   colours cannot drift from the column above. */
.pc-footer-social {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: .6rem clamp(1.25rem, 3vw, 2.5rem);
  margin-top: clamp(1.25rem, 2.4vw, 1.75rem);
  padding-top: clamp(.9rem, 1.8vw, 1.25rem);
  border-top: 1px solid var(--pc-foot-rule);
}

/* ICONS ONLY. With no label beside it the glyph is the whole control, so the
   slot carries the touch target itself: 2.5rem = 40px, up from the 1.7rem the
   labelled rows use, where the text made the link tall enough on its own. */
.pc-footer-social-list { display: flex; flex-wrap: wrap; gap: .4rem; }
.pc-footer-social-list li { margin-bottom: 0; }
.pc-footer-social-list .bi { width: 2.5rem; height: 2.5rem; font-size: 1.1rem; }

/* The support address. Not in the Secretariat column: as a 30-character string
   in a minmax(0, 1fr) grid track it wrapped over two or three lines and made
   that the tallest column in the footer. On this full-width row it sits on one
   line at every width the strip does not wrap at. */
.pc-footer-mail {
  display: inline-flex; align-items: center; gap: .6rem;
  min-width: 0; font-size: .92rem;
}
.pc-footer-mail span { min-width: 0; overflow-wrap: anywhere; }
.pc-footer-mail .bi {
  flex: none; width: 1.9rem; height: 1.9rem; border-radius: 50%;
  display: inline-grid; place-items: center; font-size: .8rem;
  background: rgba(19,79,160,.13); color: var(--pc-foot-h);
  transition: background .18s ease, color .18s ease;
}
.pc-footer-mail:hover .bi,
.pc-footer-mail:focus-visible .bi { background: var(--pc-foot-h); color: #fff; }

/* Not a link: it is a hashtag to use, and there is no one canonical place a
   hashtag resolves to across three networks. */
.pc-footer-tag {
  font-size: 1.05rem; font-weight: 800; letter-spacing: .01em;
  color: var(--pc-foot-h); white-space: nowrap;
}

/* Centred once the strip has wrapped, so no part is left hanging against an
   edge with a gap beside it. */
@media (max-width: 61.99em) {
  .pc-footer-social { justify-content: center; text-align: center; }
  .pc-footer-social-list { justify-content: center; }
}

/* The closing bar. Outside .pc-footer-body on purpose, so its ground runs the
   full width of the page rather than stopping at the container — and dark, so
   the page ends on something definite rather than fading out. */
.pc-footer-base {
  position: relative; z-index: 1;
  background: var(--pc-foot-base);
  color: var(--pc-foot-base-ink);
  font-size: .85rem;
  padding: 1rem 0;
}
.pc-footer-base .container {
  display: flex; flex-wrap: wrap; gap: .35rem 1.5rem;
  align-items: center; justify-content: space-between;
}
.pc-footer-base a { color: var(--pc-foot-base-ink); }
/* Not faded. On #3ab97c there is no room: dropping this to 86% alpha
   already puts it at 4.2:1, under AA, and 72% reaches 3.3:1. The copyright
   is set apart by size and position instead of by being harder to read. */
.pc-footer-host { color: var(--pc-foot-base-ink); opacity: 1; }
/* ── Back to top ────────────────────────────────────────────────────────
   Built out of the identity rather than dropped on top of it: the ocean
   gradient, the reef green as the glow, and an arrow that draws itself. */
.pc-totop {
  position: fixed; right: clamp(.9rem, 2.5vw, 1.75rem); bottom: clamp(.9rem, 2.5vw, 1.75rem);
  width: 3.1rem; height: 3.1rem; padding: 0; border: 0; border-radius: 50%;
  z-index: 1040; cursor: pointer;
  background: linear-gradient(140deg, var(--brand-2) 0%, var(--brand) 55%, var(--brand-deep) 100%);
  box-shadow: 0 10px 26px -8px rgba(0,48,144,.62), 0 0 0 0 rgba(0,160,80,.5);
  opacity: 0; transform: translateY(14px) scale(.86);
  transition: opacity .34s cubic-bezier(.22,1,.36,1), transform .34s cubic-bezier(.22,1,.36,1),
              box-shadow .3s ease;
}
.pc-totop.is-in { opacity: 1; transform: none; }
.pc-totop:hover, .pc-totop:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 16px 34px -10px rgba(0,48,144,.7), 0 0 0 6px rgba(0,160,80,.22);
}
.pc-totop:focus-visible { outline: 3px solid var(--reef-2); outline-offset: 3px; }
.pc-totop svg { width: 100%; height: 100%; display: block; }
.pc-totop-ring {
  fill: none; stroke: rgba(255,255,255,.34); stroke-width: 1.5;
  stroke-dasharray: 126; stroke-dashoffset: 126;
  transition: stroke-dashoffset .7s cubic-bezier(.22,1,.36,1);
}
.pc-totop.is-in .pc-totop-ring { stroke-dashoffset: 0; }
.pc-totop-arrow {
  fill: none; stroke: #fff; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
}
.pc-totop:hover .pc-totop-arrow { animation: pcArrowNudge .9s ease-in-out infinite; }

@keyframes pcArrowNudge {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-2.5px); }
}

@media (prefers-reduced-motion: reduce) {
  .pc-totop { transition: opacity .2s linear; transform: none !important; }
  .pc-totop-ring { transition: none; stroke-dashoffset: 0; }
  .pc-totop:hover .pc-totop-arrow { animation: none; }
}

/* A nav asked to float over a hero must not also occupy a strip of the page
   above it — sticky positioning still takes its space in flow, which is what
   pushes the hero down and leaves white ground behind transparent links. Over
   a hero it is taken out of flow entirely; the hero's own top padding is what
   keeps the copy clear of it. */
.pc-has-overlay-nav .pc-nav-over {
  position: fixed; top: 0; left: 0; right: 0; z-index: 1030;
}

/* ── The arms in the header ──────────────────────────────────────────────
   Line art on a dark bar, so no white plate: the Coat of Arms is drawn to sit
   on its own and a tile behind it only makes a sticker of it. Slightly taller
   than the old square mark because it is a detailed device — below about
   2.2rem the supporters and the canoe turn to mud. */
.pc-navarms {
  display: block; flex: none;
  height: 2.35rem; width: auto;
  line-height: 0;
}
.pc-navarms img { height: 100%; width: auto; display: block; }
@media (max-width: 35.99em) { .pc-navarms { height: 2rem; } }

/* ── The partner lockup in the public bar ────────────────────────────────────
   Fiji, Australia, Pre-COP31 — app/views/public/_marks.php, the same partial
   the hero, the footer and the sign-in panel render. The Ministry's own JPEG
   lockup stood here alone until this; it needed a white plate on the blue bar
   it was designed for, and .pc-navmark went with it.

   ── Why there are no rules here ──────────────────────────────────────────────
   The partial prints a hairline between each pair, and the hero, footer and
   sign-in panel all keep theirs. In a bar this tall they are three more
   vertical strokes in the row that already carries the whole site's
   navigation, and at 2rem they read as a rendering seam rather than as
   punctuation. Suppressed for this variant only.

   ── One knob ────────────────────────────────────────────────────────────────
   --m is the SQUARE mark's height and the other two are struck off it, at the
   optical-weight ratios _marks.php sorts the files into: the squarer a mark
   is, the more height it gets, or the two crests come out a fraction of the
   Pre-COP31 lockup's area. Rescaling the bar is therefore one number per
   breakpoint instead of three that can drift out of proportion.

   The row must never wrap — .pc-marks allows it, and here a wrap is a bar
   twice its own height — so the marks are pinned and Register, the greedy
   flex item beside them (app.css gives it width:100% below 992px), gives up
   the space instead.

   ── One fluid curve, not a value per breakpoint ──────────────────────────
   This used to be four fixed --m values with media queries between them, each
   sized for the worst width in its band. That is the wrong shape for the
   problem: a lockup sized for 768px is the same size at 991px, so it looked
   correct at exactly four widths and progressively too small everywhere in
   between — and it stepped visibly as the window crossed a breakpoint.

   --m is now a single continuous curve from the narrowest phone to a wide
   desktop, and the gap, the bar's height and its gutter ride the same
   viewport so the whole assembly scales together rather than the marks
   growing inside fixed padding. The floor is where the Fiji crest's
   supporters stop resolving; the ceiling is where a bigger mark would start
   competing with the page's own title.

   The slope is the constraint, not a preference: three marks cost roughly
   4.9 times --m in width, and the worst case is ~768px, where the bar also
   carries Programme/Venue/FAQ/Announcements and two buttons. 1.35vw keeps the
   row inside the container there and still reaches a proper size at 1920. */
.pc-nav-marks {
  /* Phone band: the bar is lockup + Register + burger and nothing else, so
     the lockup may take everything those two do not. The slope is set by the
     tightest width rather than the widest — at 320 the three marks, the
     button and the burger come to the whole screen, and one pixel more
     overflows. */
  --m: clamp(1.5rem, 9.78vw - .46rem, 2.6rem);
  flex-wrap: nowrap;
  gap: clamp(.4rem, .18rem + .55vw, 1.05rem);
}

/* From 768 the bar also carries Programme, Venue, FAQ and Sign in, so the
   lockup gives most of that space back and then grows gently again with the
   viewport. The small step at the breakpoint is the links arriving, not a
   stepped size — within each band the curve is continuous. */
@media (min-width: 48em) {
  .pc-nav-marks { --m: clamp(2.3rem, .95rem + 2.9vw, 4.25rem); }
}
.pc-nav-marks .pc-lockup {
  flex: none;
  background: none; box-shadow: none; padding: 0; border-radius: 0;
}
.pc-nav-marks .pc-lockup-square { height: var(--m); }
.pc-nav-marks .pc-lockup-mid    { height: calc(var(--m) * .95); }
.pc-nav-marks .pc-lockup-wide   { height: calc(var(--m) * .67); }
.pc-nav-marks .rule { display: none; }

/* ── The combined lockup, when the Ministry's single file is present ────────
   The three-mark row above is the fallback now; this is what the bar normally
   renders. See nav.php.

   Sized by WIDTH, not height, and that is the whole difference. The row above
   is three images whose widths are whatever their own ratios make them, so it
   is driven by a height variable and its total width has to be PREDICTED —
   "three marks cost roughly 4.9 times --m", a number that has to be re-derived
   by hand every time one of the files is swapped. One image has one known
   ratio, so giving it a width and letting the height follow makes overflow
   arithmetically impossible at any viewport: the element is never wider than
   the clamp, whatever the artwork inside it is.

   ── But the numbers are FITTED to the crests ──────────────────────────────
   Width is what the bar constrains; crest size is what a reader judges, and
   the crests run the full height of the trimmed file, so crest height IS image
   height. Each band below therefore reproduces the heights the three marks
   had at the same viewport — 58px at 1440, 44 at 992, 37 at 768, 24 at 320 —
   and the width follows from the ratio.

   Matching the old row's total WIDTH instead was the first attempt and came
   out visibly worse than what it replaced: the artwork carries the spacing
   between the marks inside the file, so at the same overall width the crests
   landed 33px against 58 — fine line work at half size.

   That ratio belongs to the supplied artwork and has already changed once:
   the first file was 7.74:1, its replacement 5.55:1, and the clamp that gave a
   51px crest under the first gave 72px under the second. New artwork means
   re-running navprobe.js, not assuming these widths still mean what they meant.

   ── The ceiling is the GUTTER, not the viewport ───────────────────────────
   `.pc-nav > .container` is justify-content:center — that is what made the
   bar's left and right insets equal, which is the whole point of it. The
   consequence is that an overflowing row does not push the page sideways and
   does not clip: the surplus splits evenly and is absorbed by the container's
   own padding. So "document scrollWidth == viewport" is NOT proof this fits.
   A first fit passed that test at every width while quietly eating the bar's
   20–26px gutter down to 4px at 768 and 992 — the same left-inset complaint
   the centring was introduced to settle, reintroduced by the thing sitting
   inside it.

   What is measured instead (navprobe.js) is the children's total against the
   container's inner width. Sizes below are fitted to leave the gutter intact.

   Three bands, because the BAR's slack is not monotonic and one curve cannot
   follow it. Measured spare width inside the container: 26px at 768, 18px at
   992, 149px at 1200, 176px at 1440, 20px at 1920. It tightens at 992 because
   that is where Announcements arrives (d-lg-inline), opens right up through
   1200–1440, then closes again at 1920 — not because the bar runs out of
   screen, but because .container caps at 1320px and stops growing while the
   links do not. A single slope fitted to the 992 pinch wastes 150px at 1440;
   one fitted to 1440 eats the gutter at 992. The breakpoints below are the
   bar's own (48em, 75em), not new ones invented for the logo. */
.pc-nav-lockup {
  display: block;
  flex: none;
  /* Phone band — lockup + Register + burger only. 128px at 320, 215 at 576. */
  width: clamp(7.5rem, 1.2rem + 34vw, 13.5rem);
  height: auto;
  /* The file is trimmed to its ink, so none of that width is padding. */
  max-width: 100%;
}
@media (min-width: 48em) {
  /* The pinch: Programme/Venue/FAQ from 768, Announcements from 992, two
     buttons throughout. 195px at 768 and 238 at 992 are the most each leaves
     with the container's gutter still intact. */
  .pc-nav-lockup { width: clamp(12.2rem, 2.97rem + 19.2vw, 17.5rem); }
}
@media (min-width: 75em) {
  /* Where the bar finally has room: 285px at 1200, 322 at 1440 — the width
     that puts the crests back at the 58px they had. Capped at 22rem because
     .container stops growing at 1320px while the links do not, so past there
     the lockup would be expanding into a bar that is not. */
  .pc-nav-lockup { width: clamp(17.5rem, 6.27rem + 15.4vw, 22rem); }
}

/* The link is the lockup and nothing else, so app.css's menu-item padding on
   `.pc-nav a` is 24px of nothing — and on a phone that is a quarter of what
   the three marks have to fit in. */
.pc-nav .pc-nav-home { padding: 0; }

/* The bar's own type. app.css sets .lp-nav-link at .945rem and the buttons to
   Bootstrap's .btn-sm, which rendered at 14px — the smallest text on the page,
   on the one element every visitor uses. */
.pc-nav .pc-nav-link { font-size: .97rem; }
.pc-nav .lp-btn-ghost,
.pc-nav .lp-btn-solid { font-size: .92rem; padding: .48rem 1rem; }
.pc-nav-theme { font-size: 1.1rem; }

/* app.css gives every link in a .pc-nav the staff sidebar's hover fill —
   right for the sidebar, wrong here, where it put a blue box around the
   government crest whenever the pointer crossed it. A government mark is not
   a menu item; the link still shows focus, it just does not get painted. */
.pc-nav .pc-nav-home:hover,
.pc-nav .pc-nav-home:focus { background: transparent; }
.pc-nav .pc-nav-home:focus-visible {
  outline: 3px solid var(--brand); outline-offset: 3px; border-radius: 6px;
}

/* The bar and its gutter ride the same viewport the marks do.

   Height, the gap between the bar's items, and the gutter down each side are
   all one curve each, for the same reason --m is: a bar whose padding is fixed
   while its contents scale gets tight at one end of a band and loose at the
   other. The marks are still set SHORTER than the single Ministry lockup they
   replaced (which stood at 4.4rem), so carrying three cannot make the bar
   taller than it was on any page of the site.

   The gutter is deliberately a little wider than Bootstrap's fixed .75rem at
   every width above a phone: the first thing in the bar is now a government
   crest rather than a word, and a crest set hard against the edge of the
   screen reads as a mistake. */
/* ── The bar is full width, with its own symmetric gutter ──────────────────
   NOT Bootstrap's .container, and this is the fix for "the spacing on the
   left does not match the spacing on the right".

   .container is a stepped max-width: 720 at 768, 960 at 992, 1140 at 1200,
   1320 at 1400 and then nothing above that. The bar's left gutter is
   therefore (viewport - that step) / 2, which does not grow smoothly with the
   viewport — it JUMPS. Measured across the range it went 41 → 109 → 53 → 95
   → 88 → 335 px, while the gap before Register absorbed whatever was left
   over. Two spaces that are supposed to read as a pair, one of them lurching
   at every breakpoint.

   A full-width bar with one clamp for the gutter makes the left inset a
   smooth function of the viewport at every width, and the right inset is the
   same number by construction. The bar is chrome that spans the page; it was
   never the thing the container's reading-width was for. */
.pc-nav > .container {
  /* One smooth cap, not Bootstrap's four steps. Below 1320 the bar is full
     width and the gutter is the clamp; above it the content stops widening
     and centres, so the left inset grows smoothly instead of jumping. */
  max-width: 1320px;
  width: 100%;
  margin-inline: auto;
  padding-inline: clamp(1rem, 2.6vw, 3.5rem);
}
.pc-nav .container {
  min-height: clamp(3.9rem, 3.2rem + 2.4vw, 6rem);
  gap: clamp(.45rem, .2rem + .62vw, 1.15rem) !important;
  /* padding-inline is set by the .pc-nav > .container rule above — one
     gutter, one number, so the left and right insets cannot drift apart. */
}

/* app.css turns every .lp-btn into a full-width block below 992px — right for
   a landing page section, wrong in a flex bar, where it made Register a greedy
   item that swallowed every pixel the viewport gained. The lockup grew 6px
   between a 320 and a 414 screen while the button grew 88. The buttons take
   their own width here and the marks get the rest. */
.pc-nav .lp-btn-solid,
.pc-nav .lp-btn-ghost { width: auto; flex: none; }

/* One number the curve cannot carry. Below ~26rem the bar is marks + Register
   + burger and nothing else, and Register's own label is what runs out of
   room first — so it, not the lockup, is what gives way. */
@media (max-width: 25.99em) {
  .pc-nav .lp-btn-solid { font-size: .86rem; padding: .44rem .8rem; }
}

/* Register sits in the bar as a solid button — the primary action on the site,
   and previously only reachable from a footer link. */

/* A guaranteed gap between the lockup and the first control.

   .pc-nav-home carries me-auto, which pushes everything else to the right —
   but "auto" is whatever is left over, and at the widths where the bar is
   fullest that is nearly nothing, so the Register button ended up sitting
   against the Pre-COP31 mark. A margin here is a floor that auto cannot eat:
   the marks always finish, then there is clear space, then the controls
   start. It scales with the viewport for the same reason everything else in
   this bar does. */
.pc-nav .pc-nav-register,
.pc-nav .pc-nav-home + .lp-btn-ghost {
  margin-left: clamp(.6rem, .35rem + .8vw, 1.4rem);
}

/* ── The lockup sits at the LEFT gutter ────────────────────────────────────
   This bar was centred as a single group for a while, and the reasoning was
   sound as far as it went: with the lockup pinned left and the controls
   pinned right, the space between them is not a designed value at all — it is
   whatever is left after both ends are placed, so it moved with the viewport
   AND with how many links were showing (measured across the range: 17, 27,
   36, 55, 69, 102, 131, 239, 248px). Centring made the two outer insets equal
   by construction.

   What it also did was float the lockup into the middle of the bar. The
   Ministry asked for it back on the left, and they are right: a government
   mark belongs at the edge of the bar, where every other site puts it, and a
   masthead that starts a third of the way across reads as a mistake however
   even its margins are. The uneven middle gap is the price, and it is the
   cheaper of the two — nobody looks at a navigation bar and measures the gap
   between the logo and the first button.

   So: the row starts at the gutter, and .pc-nav-home keeps the me-auto that
   pushes the controls to the right-hand gutter. The floor margin above is
   what stops Register touching the artwork when "auto" has nothing left to
   give — that rule is the one doing the work now, not this block.

   ── …and the gutter is the VIEWPORT's, not the page column's ──────────────
   Left-aligning the row was not enough on a wide screen, and the reason was
   not the row at all. `.container` caps at 1320px and centres what is left
   over, so the bar's own left inset is 97px at 1440 but 350px at 1920 and
   683px at 2560: the lockup was already hard against its container's edge
   while the container floated in the middle of the screen. Nothing about the
   logo's own sizing could have fixed that, which is worth remembering — the
   first two attempts at "move it left" were spent on the wrong element.

   The bar therefore opts out of the page column and spans the viewport with
   its own gutter. A sticky masthead that runs edge to edge while the content
   beneath it stays in a 1320px column is the ordinary arrangement, and it is
   what puts a government mark where a reader looks for it. The padding below
   is deliberately within a pixel of what .container was already giving at
   phone and tablet widths, so only the wide screens change. */
.pc-nav > .container {
  justify-content: flex-start;
  max-width: none;
  width: 100%;
  padding-inline: clamp(1rem, .6rem + 1.6vw, 2.75rem);
}
.pc-nav-register { white-space: nowrap; }
@media (max-width: 40.99em) { .pc-nav-register .bi { margin-right: 0 !important; } }

/* On a phone the bar was carrying brand + theme toggle + Register + Sign in +
   burger, which came to fifteen pixels more than the screen and pushed the
   whole page sideways. Sign in is the one that goes: it is already the first
   item in the burger panel, and the visitor who has not registered outnumbers
   the one coming back to sign in by a very long way. */
@media (max-width: 34.99em) {
  .pc-nav .lp-btn-ghost { display: none; }
}

/* ── Work waiting, in the sidebar ────────────────────────────────────────
   A count on the menu item that has something in it, and a slow pulse while
   it is not zero. This is the difference between a list of twenty places and
   a list of the two that need a decision today.

   The pulse is deliberately slow and small — it is a signal at the edge of
   attention, not something blinking at an officer for eight hours. It stops
   entirely once the queue is empty, and under reduced-motion it never runs;
   the number is the information, the movement only draws the eye to it. */
.pc-nav a { position: relative; }

/* ── Two kinds of number, and only one of them moves ──────────────────────
   Every count used to be coral and every count used to pulse, which is the
   same as none of them pulsing: a sidebar where four numbers are all flashing
   at once teaches a staff member within a day to look past all four, including
   the one that was a decision nobody had made.

   So the quiet form is the DEFAULT here, and .is-urgent is the exception the
   layout adds only to queues that are blocked on a human — session approvals,
   accreditation held for review, unresolved door incidents. Badges waiting to
   print is a real number and a useful one, but it sits in the hundreds for
   days during badge production, and a permanent flashing light in the corner
   of the screen is not information. */
.pc-nav-count {
  margin-left: auto;
  min-width: 1.45rem; height: 1.45rem; padding: 0 .4rem;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: rgba(255, 255, 255, .14); color: #dcece9;
  font-size: .68rem; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;
}

.pc-nav-count.is-urgent {
  background: var(--coral); color: #fff;
  font-weight: 800;
  box-shadow: 0 0 0 0 rgba(224, 0, 16, .55);
  animation: pcWaiting 2.6s ease-out infinite;
}
@keyframes pcWaiting {
  0%   { box-shadow: 0 0 0 0    rgba(224, 0, 16, .5); }
  70%  { box-shadow: 0 0 0 .5rem rgba(224, 0, 16, 0); }
  100% { box-shadow: 0 0 0 0    rgba(224, 0, 16, 0); }
}

/* The item itself leans very slightly toward the reader while it holds work. */
.pc-nav a.has-work span:first-of-type { font-weight: 700; }

/* Reduced motion takes the movement away, not the signal: the urgent count
   keeps its colour and gains a steady ring, so it still reads as the different
   kind of number it is. */
@media (prefers-reduced-motion: reduce) {
  .pc-nav-count.is-urgent {
    animation: none;
    box-shadow: 0 0 0 2px rgba(224, 0, 16, .35);
  }
}

/* ── "Start here" ────────────────────────────────────────────────────────
   The four steps of running the conference, in order, on the dashboard. Any
   step with work waiting is pulled forward by colour and a count; the rest
   stay quiet. Dismissed once and it never returns. */
.pc-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem; }

.pc-step {
  display: flex; gap: .9rem; align-items: flex-start;
  padding: .9rem 1rem; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface-2);
  transition: border-color .2s ease, background .2s ease;
}
.pc-step.has-work {
  border-color: color-mix(in srgb, var(--coral) 45%, transparent);
  background: color-mix(in srgb, var(--coral) 5%, var(--surface));
}
.pc-step.is-done { opacity: .72; }

.pc-step-n {
  width: 1.9rem; height: 1.9rem; flex: none; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--brand); color: #fff;
  font-weight: 800; font-size: .8rem;
}
.pc-step.has-work .pc-step-n { background: var(--coral); }
.pc-step.is-done  .pc-step-n { background: var(--reef); }

.pc-step-body  { min-width: 0; }
.pc-step-title { font-weight: 700; color: var(--ink); }
.pc-step-text  { font-size: .9rem; color: var(--muted); line-height: 1.55; margin-top: .1rem; }

.pc-step-badge {
  display: inline-block; margin-left: .45rem;
  padding: .12rem .5rem; border-radius: 999px;
  background: var(--coral); color: #fff;
  font-size: .66rem; font-weight: 800; letter-spacing: .02em;
  vertical-align: .1em;
}

/* The scanner is a two-step screen and now says so: choose the context, then
   scan. Somebody handed a scanner at a door has seconds to work it out. */
.pc-scan-step {
  display: inline-grid; place-items: center;
  width: 1.5rem; height: 1.5rem; border-radius: 50%;
  background: var(--brand); color: #fff;
  font-size: .78rem; font-weight: 800;
  vertical-align: .06em; margin-right: .3rem;
}

/* ══════════════════════════════════════════════════════════════════════════
   DataTables inside a card
   ══════════════════════════════════════════════════════════════════════════
   The Bootstrap 5 renderer emits a .dt-container holding three Bootstrap
   `.row`s: controls, then the table, then info and paging. Left alone the
   control rows sit flush against the card edge with no gutter of their own,
   so the panel reads as three stacked fragments. These rules give the two
   control rows the card's own padding and rule them off from the table.

   Selectors target `> .row` rather than DataTables' own .dt-layout-* classes:
   the Bootstrap integration replaces those with plain grid rows, so styling
   the documented class names silently matches nothing. */
.pc-dt > .row {
  margin: 0; --bs-gutter-x: 0; align-items: center; gap: .75rem;
}
.pc-dt > .row:first-child,
.pc-dt > .row:last-child { padding: .75rem 1.05rem; }
.pc-dt > .row:first-child { border-bottom: 1px solid var(--line); }
.pc-dt > .row:last-child  { border-top: 1px solid var(--line); }
.pc-dt > .row > [class^="col"] { padding: 0; }
.pc-dt > .row:nth-child(2) { margin: 0; gap: 0; }

/* "25" and "per page" are a select and a label side by side in one div. The
   select is a .form-select, which Bootstrap makes display:block/width:100% —
   so without this the label drops onto a second line under the box. */
.pc-dt .dt-length { display: flex; align-items: center; gap: .5rem; }
.pc-dt .dt-length select { width: auto; display: inline-block; flex: 0 0 auto; }
.pc-dt .dt-length label { margin: 0; white-space: nowrap; font-size: .8125rem; color: var(--muted); }

.pc-dt .dt-search { display: flex; justify-content: flex-end; }
.pc-dt .dt-search input { min-width: 16rem; max-width: 100%; }
.pc-dt .dt-info { font-size: .8125rem; color: var(--muted); padding: 0; }

/* The pager. Without the Bootstrap 5 renderer these were bare grey blocks. */
.pc-dt .pagination { margin: 0; }
.pc-dt .page-link {
  border-radius: var(--radius-sm); margin-left: .25rem;
  border-color: var(--line-2); color: var(--ink-2);
  background: var(--surface); font-size: .8125rem; padding: .35rem .7rem;
}
.pc-dt .page-item.active .page-link {
  background: var(--brand); border-color: var(--brand); color: #fff;
}
.pc-dt .page-item.disabled .page-link { color: var(--muted); opacity: .5; }
.pc-dt .page-link:hover { background: var(--surface-2); border-color: var(--brand); }

/* Sorting affordance: a column you can sort should look like one. */
.pc-dt table.dataTable thead th.dt-orderable-asc,
.pc-dt table.dataTable thead th.dt-orderable-desc { cursor: pointer; }

/* On a phone the two control rows stack rather than squeeze. */
@media (max-width: 575.98px) {
  .pc-dt > .row:first-child,
  .pc-dt > .row:last-child { padding: .7rem .85rem; }
  .pc-dt .dt-search input { min-width: 0; width: 100%; }
  .pc-dt > .row > [class^="col"] { width: 100%; text-align: left !important; }
  .pc-dt .dt-search { justify-content: flex-start; }
}

/* ══════════════════════════════════════════════════════════════════════════
   Sidebar rail (collapsed navigation)
   ══════════════════════════════════════════════════════════════════════════
   Collapsing narrows the sidebar to its icons rather than hiding it, so the
   navigation never disappears from under someone mid-task — and the pending-
   work counts stay visible, which is the whole reason they exist. */
/* The class goes on <html>, not <body>, so an inline head script can restore
   the collapsed state before first paint — <body> does not exist yet at that
   point, and applying it afterwards makes the whole page jump sideways on
   every single load. */
.pc-rail { --sidebar-w: 76px; }

.pc-sidebar { transition: width .2s ease, flex-basis .2s ease; }

.pc-rail .pc-nav { padding: 0 12px; }
.pc-rail .pc-nav a { justify-content: center; padding: 11px 0; position: relative; }
.pc-rail .pc-nav a > span:not(.pc-nav-count) { display: none; }
.pc-rail .pc-nav a i { width: auto; font-size: 1.15rem; }
.pc-rail .pc-sidebar-foot { display: none; }

/* A group heading with no room for words becomes a rule, so the grouping is
   still legible as grouping. */
.pc-rail .pc-nav-label {
  font-size: 0; padding: 10px 18px 4px; height: 1px; overflow: hidden;
}
.pc-rail .pc-nav-label::after {
  content: ''; display: block; height: 1px; background: var(--line);
}

/* In the rail the count has no row to sit at the end of, so it pins to the
   icon's corner the way a notification badge does. */
.pc-rail .pc-nav-count {
  position: absolute; top: 3px; right: 8px; margin: 0;
  min-width: 1.1rem; height: 1.1rem; padding: 0 .25rem; font-size: .6rem;
}

/* The desktop collapse control has no purpose below 992px, where the sidebar
   is already a drawer with its own toggle. */
@media (max-width: 991.98px) {
  #pcSidebarCollapse { display: none !important; }
  .pc-rail { --sidebar-w: 252px; }
}

/* ── Activation outcome pages ─────────────────────────────────────────────
   One large mark carries the whole message before any text is read: this
   worked, or it did not and here is what to do. */
.pc-activate-mark {
  width: 4.5rem; height: 4.5rem; margin: 0 auto;
  border-radius: 50%; display: grid; place-content: center;
  font-size: 2rem; line-height: 1;
}
.pc-activate-mark.success { background: color-mix(in srgb, var(--success) 16%, transparent); color: var(--success); }
.pc-activate-mark.info    { background: color-mix(in srgb, var(--info) 16%, transparent);    color: var(--info); }
.pc-activate-mark.warning { background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning); }
.pc-activate-mark.danger  { background: color-mix(in srgb, var(--danger) 14%, transparent);  color: var(--danger); }

.pc-ref {
  background: var(--surface-2); border: 1px solid var(--line);
  padding: .25rem .6rem; border-radius: var(--radius-sm);
  font-size: .95rem; letter-spacing: .04em;
}

/* ── Notification inbox ───────────────────────────────────────────────────
   Unread rows carry weight and a tinted ground; read ones recede but stay,
   so this morning's dismissed item can still be found this afternoon. */
.pc-notif-list { list-style: none; margin: 0; padding: 0; }

.pc-notif {
  display: flex; align-items: flex-start; gap: .85rem;
  padding: 1rem 1.15rem;
  border-bottom: 1px solid var(--line);
}
.pc-notif:last-child { border-bottom: 0; }
.pc-notif.is-unread { background: color-mix(in srgb, var(--brand) 5%, transparent); }

.pc-notif-dot {
  width: .6rem; height: .6rem; border-radius: 50%; margin-top: .45rem;
  flex: none; background: var(--muted);
}
.pc-notif-dot.important { background: var(--coral); }
.pc-notif-dot.warning   { background: var(--warning); }
.pc-notif-dot.info      { background: var(--brand); }

.pc-notif-subject { font-weight: 600; font-size: .9rem; color: var(--ink); }
.pc-notif.is-unread .pc-notif-subject { font-weight: 700; }
.pc-notif-body { font-size: .85rem; color: var(--ink-2); margin-top: .15rem; }
.pc-notif-time { font-size: .75rem; color: var(--muted); margin-top: .3rem; }

/* The topbar bell. The count is the whole point of it, so it is legible at a
   glance and only present when it is not zero. */
.pc-bell { position: relative; }
.pc-bell-count {
  position: absolute; top: 1px; right: 0;
  min-width: 1.05rem; height: 1.05rem; padding: 0 .22rem;
  border-radius: 999px; background: var(--coral); color: #fff;
  font-size: .62rem; font-weight: 800; line-height: 1.05rem;
  text-align: center; font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--surface);
}

/* ══════════════════════════════════════════════════════════════════════════
   Programme: view switcher and calendar
   ══════════════════════════════════════════════════════════════════════════
   Laid out entirely on the server; this is grid and colour only. On a phone
   the seven columns become one, because seven columns on a 390px screen is
   seven columns nobody can read. */

.pc-viewswitch {
  display: inline-flex; margin-bottom: 1.1rem;
  border: 1px solid var(--line-2); border-radius: var(--radius-sm);
  overflow: hidden; background: var(--surface);
  /* Five fixed-padding tabs come to 331px, so on a 320px screen the last one
     hung 22px off the side and took the whole page with it. Wrapping to a
     second row is right for a switcher: a horizontally scrolling strip hides
     the option on the end behind a gesture nobody is told about. The radius
     goes with it — a rounded outline around two wrapped rows cuts the corner
     off the tabs that land at the fold. */
  flex-wrap: wrap;
  max-width: 100%;
}
@media (max-width: 22.5em) {
  .pc-viewswitch { border-radius: 0; }
  .pc-viewswitch a { flex: 1 1 auto; text-align: center; padding-inline: .7rem; }
}
.pc-viewswitch a {
  padding: .45rem 1rem; font-size: .85rem; font-weight: 600;
  color: var(--ink-2); text-decoration: none;
  border-right: 1px solid var(--line-2);
}
.pc-viewswitch a:last-child { border-right: 0; }
.pc-viewswitch a:hover { background: var(--surface-2); }
.pc-viewswitch a.is-active { background: var(--brand); color: #fff; }

/* overflow: clip, not hidden. Both keep the rounded corners, but hidden makes
   the card a scroll container of its own, and a sticky heading inside one
   sticks to the card, which never scrolls, so it would never stick at all.
   hidden stays first as the fallback for a browser without clip. */
.pc-cal {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden; overflow: clip;
}
/* The day and its arrows stay in sight while the day's sessions scroll, just
   under the bar at the top of the window (see .pc-prog-day). */
.pc-cal-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .85rem 1rem; border-bottom: 1px solid var(--line);
  position: sticky; top: var(--pc-stick-top, 0px); z-index: 3;
  background: var(--surface);
}
.pc-cal-title { margin: 0; font-size: 1.05rem; font-weight: 700; }

.pc-cal-weekdays {
  display: grid; grid-template-columns: repeat(7, 1fr);
  background: var(--surface-2); border-bottom: 1px solid var(--line);
}
.pc-cal-weekdays span {
  padding: .5rem; text-align: center;
  font-size: .72rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}

.pc-cal-week { display: grid; grid-template-columns: repeat(7, 1fr); }
.pc-cal-week.is-single { grid-template-columns: 1fr; }

.pc-cal-day {
  min-height: 7.5rem; padding: .5rem;
  border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
  display: flex; flex-direction: column; gap: .3rem;
}
.pc-cal-day:nth-child(7n) { border-right: 0; }
.pc-cal-week.is-single .pc-cal-day { border-right: 0; min-height: 12rem; }
.pc-cal-day.is-muted { background: var(--surface-2); opacity: .6; }
.pc-cal-day.is-today { background: color-mix(in srgb, var(--brand) 7%, transparent); }

.pc-cal-daynum { display: flex; align-items: baseline; gap: .4rem; }
.pc-cal-daynum a {
  font-weight: 700; font-size: .9rem; color: var(--ink); text-decoration: none;
}
.pc-cal-day.is-today .pc-cal-daynum a {
  background: var(--brand); color: #fff;
  width: 1.6rem; height: 1.6rem; border-radius: 50%;
  display: inline-grid; place-content: center; font-size: .8rem;
}
/* The weekday name only earns its place in the stacked phone layout, where
   the column headings are gone. */
.pc-cal-dow { display: none; font-size: .7rem; font-weight: 700;
              text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }

/* --evt is the VENUE's colour, set inline per session by _calendar.php, so the
   Day view says which hotel in the same way the board and the list already do.
   It falls back to the brand colour for a session with no venue. */
.pc-cal-event {
  display: block; text-decoration: none;
  background: color-mix(in srgb, var(--evt, var(--brand)) 10%, transparent);
  border-left: 3px solid var(--evt, var(--brand));
  border-radius: 5px; padding: .3rem .45rem;
  font-size: .76rem; line-height: 1.3; color: var(--ink);
}
.pc-cal-event:hover { background: color-mix(in srgb, var(--evt, var(--brand)) 18%, transparent); }
.pc-cal-time { font-weight: 700; color: var(--evt, var(--brand)); margin-right: .3rem; }
/* The hotel, in the same colour as the spine — without a name beside it the
   tint is a colour with nothing to decode it. */
.pc-cal-venue { color: var(--evt, var(--brand)); font-weight: 700; }
.pc-cal-name { display: inline; }
.pc-cal-meta {
  display: flex; flex-wrap: wrap; gap: .5rem; align-items: center;
  margin-top: .25rem; font-size: .72rem; color: var(--muted);
}
.pc-cal-state {
  padding: .05rem .4rem; border-radius: 999px; font-weight: 700; font-size: .68rem;
  background: var(--surface-2); color: var(--muted);
}
.pc-cal-state-register { background: color-mix(in srgb, var(--success) 18%, transparent); color: var(--success); }
.pc-cal-state-waitlist { background: color-mix(in srgb, var(--warning) 20%, transparent); color: var(--warning); }
.pc-cal-state-full,
.pc-cal-state-closed,
.pc-cal-state-past    { background: var(--surface-2); color: var(--muted); }

.pc-cal-none { color: var(--muted); font-size: .85rem; margin: .5rem 0 0; }

/* Below 768px the grid stops being a grid. Seven columns on a phone is seven
   columns nobody can read, and empty days are noise — so the stacked view
   drops them and names the days it keeps. */
@media (max-width: 767.98px) {
  .pc-cal-weekdays { display: none; }
  .pc-cal-week, .pc-cal-week.is-single { grid-template-columns: 1fr; }
  .pc-cal-day { min-height: 0; border-right: 0; }
  .pc-cal-day:not(.has-events) { display: none; }
  .pc-cal-dow { display: inline; }
  .pc-cal-event { font-size: .82rem; padding: .45rem .6rem; }
}

/* ── "Where I stand" strip ────────────────────────────────────────────────
   Four numbers, each answering a question a delegate actually asks and each
   a link to the thing it counts. A number that cannot be clicked is trivia. */
.pc-glance {
  display: grid; gap: .7rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.pc-glance-item {
  display: flex; align-items: center; gap: .7rem;
  padding: .8rem .9rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink); text-decoration: none;
  transition: border-color .15s, transform .15s;
}
a.pc-glance-item:hover { border-color: var(--brand); transform: translateY(-1px); }
.pc-glance-item > .bi { font-size: 1.35rem; color: var(--muted); flex: none; }

.pc-glance-item.ok    > .bi { color: var(--success); }
.pc-glance-item.warn  > .bi { color: var(--warning); }
.pc-glance-item.alert > .bi { color: var(--coral); }
.pc-glance-item.warn  { border-color: color-mix(in srgb, var(--warning) 45%, var(--line)); }
.pc-glance-item.alert { border-color: color-mix(in srgb, var(--coral) 45%, var(--line)); }

.pc-glance-label {
  font-size: .7rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.pc-glance-value { font-size: 1.05rem; font-weight: 700; line-height: 1.2; }

/* ══════════════════════════════════════════════════════════════════════════
   Form validation, and checkboxes you can actually see
   ══════════════════════════════════════════════════════════════════════════ */

/* A field the browser rejected. The mark has to survive on a long form while
   the reader scrolls back to it, so it is a border and a tint, not a tooltip
   that vanishes the moment focus moves. */
.pc-field-invalid .form-control,
.pc-field-invalid .form-select,
.form-control.is-invalid,
.form-select.is-invalid {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 16%, transparent);
}

.pc-field-error {
  display: block;
  margin-top: .3rem;
  font-size: .78rem;
  font-weight: 600;
  color: var(--danger);
}
.pc-field-error::before {
  content: '\F33A';               /* bi-exclamation-circle */
  font-family: bootstrap-icons;
  font-weight: normal;
  margin-right: .3rem;
}

/* A radio group's message belongs to the question, once — not to each option. */
.form-check.pc-field-invalid { border-radius: var(--radius-sm); }

/* ── Checkboxes and radios ───────────────────────────────────────────────
   Bootstrap's defaults are a 1em box with a hairline border: on a white form,
   on a laptop screen, in a foyer, they read as decoration rather than as
   something you are being asked to act on. The consent tick on the
   registration form is a legal statement and was the least visible control on
   the page. Bigger, heavier border, and an unmistakable checked state. */
.form-check-input {
  width: 1.15rem; height: 1.15rem;
  margin-top: .18rem;
  border: 2px solid var(--line-2);
  background-color: var(--surface);
  cursor: pointer;
  transition: border-color .12s, background-color .12s, box-shadow .12s;
}
.form-check-input:hover { border-color: var(--brand); }
.form-check-input:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
}
.form-check-input:checked {
  background-color: var(--brand);
  border-color: var(--brand);
}
.form-check-label { cursor: pointer; padding-left: .15rem; }

/* The consent tick specifically: it is agreement to a privacy notice, so it
   gets a panel of its own rather than sitting loose at the foot of the form. */
.pc-consent {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  /* Bootstrap's .form-check pulls its input LEFT by 1.5em, out of the box's
     own padding — a pattern that assumes .form-check is an invisible wrapper.
     Here it IS the bordered panel, so a flat 1rem of left padding put the
     checkbox 6.9px outside its own border. The inset has to cover the pull as
     well as the gap, or the control sits on the frame. */
  padding: .9rem 1rem .9rem calc(1.5em + 1rem);
}
.pc-consent.pc-field-invalid {
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 7%, var(--surface-2));
}

/* ── "Add to home screen" bar ─────────────────────────────────────────────
   Installing matters beyond convenience: the service worker has to be in place
   BEFORE somebody stands at a door with no signal. Sits above the cookie
   notice and clear of the accessibility button in the bottom-left corner.
   Shown once and dismissible; see assets/js/offline-queue.js. */
.pc-install-bar {
  position: fixed; z-index: 1085;   /* above the cookie notice, never under it */
  left: 50%; transform: translateX(-50%);
  bottom: calc(5.5rem + env(safe-area-inset-bottom));
  width: min(94vw, 30rem);
  display: flex; align-items: center; gap: .7rem;
  padding: .75rem .9rem;
  border-radius: 12px;
  background: #003898; color: #fff;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .35);
  font: 500 .82rem/1.45 system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
}
.pc-install-bar span { flex: 1 1 auto; }
.pc-install-bar strong { font-weight: 700; }
.pc-install-go {
  flex: 0 0 auto; border: 0; border-radius: 8px;
  background: #fff; color: #003898;
  font-weight: 700; font-size: .82rem; padding: .45rem .9rem; cursor: pointer;
}
.pc-install-x {
  flex: 0 0 auto; border: 0; background: transparent; color: #fff;
  font-size: 1.35rem; line-height: 1; padding: 0 .2rem; cursor: pointer; opacity: .8;
}
.pc-install-x:hover { opacity: 1; }

/* ═══════════════════════════════ FAQ ═══════════════════════════════════
   Shared by the FAQ page and the homepage's questions section, because the
   two show the same entries from app/views/public/_faq.php and a reader who
   follows "All questions" must not arrive at a differently-styled version of
   what they were just reading.

   Built on <details>/<summary>. No JavaScript is involved in opening one: the
   browser gives keyboard operation, find-in-page (which reveals a match inside
   a closed answer) and correct semantics for a screen reader, none of which a
   div-and-click accordion gets for free. */
.pc-faq {
  border-bottom: 1px solid var(--line);
}
.pc-faq:last-child { border-bottom: 0; }

.pc-faq-q {
  display: flex; align-items: flex-start; gap: .9rem;
  padding: 1rem .25rem; cursor: pointer;
  font-weight: 650; color: var(--ink); line-height: 1.45;
  list-style: none;                      /* Firefox */
  transition: color .18s ease;
}
/* Safari and Chrome draw their own triangle; ours is the chevron on the right. */
.pc-faq-q::-webkit-details-marker { display: none; }
.pc-faq-q:hover { color: var(--brand); }
.pc-faq-q span { flex: 1; }

.pc-faq-chev {
  flex: 0 0 auto; margin-top: .15rem; color: var(--brand);
  transition: transform .22s ease;
}
.pc-faq[open] > .pc-faq-q > .pc-faq-chev { transform: rotate(180deg); }

.pc-faq-a {
  padding: 0 .25rem 1.15rem; max-width: 54rem;
  color: var(--muted); font-size: .95rem; line-height: 1.65;
}
.pc-faq-a p:last-child { margin-bottom: 0; }
.pc-faq-a a { font-weight: 600; }
.pc-faq-list { margin: 0; padding-left: 1.15rem; }
.pc-faq-list li { margin-bottom: .3rem; }

/* The keyboard focus ring belongs on the whole row, not on the text node —
   a summary is the control here. */
.pc-faq-q:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .pc-faq-q, .pc-faq-chev { transition: none; }
}


/* ═══════════════════ Venue — two hotels, twenty minutes apart ═══════════════
   The whole page is organised around one failure: a delegate going to the wrong
   hotel. --v-accent is set per venue in the markup and carried down every
   element in that venue's section, so the colour — not the heading — is what
   tells a reader which hotel they are looking at once they have scrolled past
   the name. That matters most on a phone, where the two cannot sit side by side
   and there is nothing else to compare against. */

/* The warning that opens the page. Not an .alert: Bootstrap's alerts are for
   the result of an action, and this is a standing fact about the conference. */
.pc-venue-alert {
  display: flex; gap: .9rem; align-items: flex-start;
  background: var(--sand);
  border: 1px solid color-mix(in srgb, var(--reef) 22%, transparent);
  border-left: 4px solid var(--reef);
  border-radius: var(--radius, 12px);
  padding: 1rem 1.1rem; margin-bottom: 1.6rem;
  font-size: .97rem; line-height: 1.55; color: var(--ink);
}
.pc-venue-alert > i { font-size: 1.35rem; color: var(--reef); line-height: 1.2; flex: none; }
.pc-venue-alert strong { color: var(--brand-deep); }

/* ── The chooser ──────────────────────────────────────────────────────────
   Two cards that are a decision, not a summary. Side by side from 40em so the
   choice reads as a choice; stacked below that, where each still carries its
   own accent down its leading edge. */
.pc-venue-pick { display: grid; gap: 1rem; margin-bottom: 2.4rem; }
@media (min-width: 40em) { .pc-venue-pick { grid-template-columns: 1fr 1fr; } }

.pc-venue-pick-card {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: .95rem; padding: .85rem .95rem;
  background: var(--surface); color: inherit; text-decoration: none;
  border: 1px solid var(--line, #dfe7ee);
  border-left: 5px solid var(--v-accent);
  border-radius: var(--radius, 12px);
  box-shadow: 0 10px 26px -22px rgba(11,42,82,.55);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.pc-venue-pick-card:hover,
.pc-venue-pick-card:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 16px 34px -20px rgba(11,42,82,.5);
  border-color: var(--v-accent);
  color: inherit;
}
.pc-venue-pick-card:focus-visible { outline: 2px solid var(--v-accent); outline-offset: 3px; }

.pc-venue-pick-img {
  grid-column: 1;
  width: 4.6rem; height: 4.6rem; border-radius: 10px; overflow: hidden; flex: none;
  background: var(--v-soft);
}
.pc-venue-pick-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* grid-column pinned explicitly on the body and the arrow, not left to
   auto-placement: venue.php stopped rendering .pc-venue-pick-img on
   2026-09-11 (the gallery photographs came off the page), and this card is
   only ever 2 children when there is no photo. Auto-placement then filled
   columns 1 and 2 of this 3-column grid in source order — the name/area text
   landed in the (now empty) image column and the arrow inherited the middle
   1fr column, floating next to the text instead of sitting at the card's
   right edge. Pinning both keeps the arrow in the last column whether or not
   a photo is present, on either venue independently. */
.pc-venue-pick-body {
  grid-column: 2;
  display: flex; flex-direction: column; gap: .15rem; min-width: 0;
}
.pc-venue-pick-name { font-weight: 700; color: var(--brand-deep); line-height: 1.25; }
.pc-venue-pick-area,
.pc-venue-pick-rooms { font-size: .85rem; color: var(--muted, #5a6b7b); }
.pc-venue-pick-area i { color: var(--v-accent); margin-right: .3rem; }
.pc-venue-pick-go {
  grid-column: 3;
  width: 2rem; height: 2rem; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: var(--v-soft); color: var(--v-accent);
}

/* ── A venue's own section ───────────────────────────────────────────────── */
.pc-venue-block { margin-bottom: 3rem; scroll-margin-top: 6.5rem; }
.pc-venue-head { margin-bottom: 1.1rem; }
.pc-venue-name {
  font-size: clamp(1.35rem, 1rem + 1.5vw, 2rem); font-weight: 800;
  color: var(--brand-deep); margin: 0 0 .25rem;
  /* The rule under the name is the accent doing its job again. */
  padding-bottom: .5rem; border-bottom: 3px solid var(--v-accent);
  display: inline-block;
}
.pc-venue-where { font-size: .95rem; color: var(--muted, #5a6b7b); margin: .45rem 0 0; }
.pc-venue-where i { color: var(--v-accent); margin-right: .35rem; }

.pc-venue-h3 {
  font-size: .78rem; font-weight: 800; letter-spacing: .13em;
  text-transform: uppercase; color: var(--v-accent); margin: 0 0 .7rem;
}
.pc-venue-h3-sub { margin-top: 1.5rem; }

/* ── Photographs ──────────────────────────────────────────────────────────
   A scroll-snap rail. It swipes with no JavaScript whatever, which is why it
   is built this way rather than as a carousel — see venue.js. */
.pc-venue-gallery { margin-bottom: 1.4rem; }
.pc-venue-rail {
  display: flex; gap: .75rem; overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch; scrollbar-width: thin;
  padding-bottom: .35rem; border-radius: var(--radius, 12px);
}
.pc-venue-rail:focus-visible { outline: 2px solid var(--v-accent); outline-offset: 3px; }
.pc-venue-shot { flex: 0 0 min(100%, 34rem); scroll-snap-align: start; margin: 0; }
.pc-venue-shot img,
.pc-venue-single img {
  width: 100%; aspect-ratio: 3 / 2; object-fit: cover; display: block;
  border-radius: var(--radius, 12px);
}
.pc-venue-single { margin: 0 0 1.4rem; }
/* The aerial is a wide crop — forcing it to 3:2 would cut the resort in half. */
.pc-venue-single img { aspect-ratio: auto; max-height: 22rem; object-fit: cover; }

.pc-venue-dots { display: flex; gap: .4rem; justify-content: center; margin-top: .6rem; }
.pc-venue-dot {
  width: .55rem; height: .55rem; border-radius: 50%; padding: 0;
  border: 0; background: color-mix(in srgb, var(--v-accent) 26%, transparent);
  transition: background-color .2s ease, transform .2s ease;
}
.pc-venue-dot.is-on { background: var(--v-accent); transform: scale(1.25); }
.pc-venue-dot:focus-visible { outline: 2px solid var(--v-accent); outline-offset: 2px; }

/* ── Plan beside rooms ────────────────────────────────────────────────────
   One column on a phone with the plan FIRST: at the venue, a delegate is
   looking for the way in, and the room list is what they have already read. */
.pc-venue-cols { display: grid; gap: 1.4rem; }
@media (min-width: 56em) {
  .pc-venue-cols { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); align-items: start; }
}

.pc-venue-plan {
  margin: 0; background: var(--surface);
  border: 1px solid var(--line, #dfe7ee); border-radius: var(--radius, 12px);
  overflow: hidden;
}
.pc-venue-plan-head {
  display: flex; justify-content: space-between; align-items: center; gap: .6rem;
  padding: .6rem .85rem; background: var(--v-soft);
  font-size: .82rem; font-weight: 700; color: var(--v-accent);
}
.pc-venue-plan-head i { margin-right: .35rem; }
.pc-venue-plan-open { font-weight: 600; opacity: .85; white-space: nowrap; }
.pc-venue-plan-frame {
  display: block; background: #fff; cursor: zoom-in; line-height: 0;
}
.pc-venue-plan-frame:focus-visible { outline: 2px solid var(--v-accent); outline-offset: -2px; }
/* `contain`, never `cover`: a cropped map is a wrong map. Letterboxing on white
   is the correct answer for a plan — losing the Convention Center off the edge
   is not. */
.pc-venue-plan-frame img {
  width: 100%; max-height: 26rem; object-fit: contain; display: block;
}
.pc-venue-plan-note {
  margin: 0; padding: .6rem .85rem; font-size: .85rem; line-height: 1.45;
  color: var(--muted, #5a6b7b); border-top: 1px solid var(--line, #dfe7ee);
}
.pc-venue-plan-note i { color: var(--v-accent); margin-right: .35rem; }

/* ── Rooms ────────────────────────────────────────────────────────────────── */
.pc-venue-roomlist { list-style: none; margin: 0; padding: 0; }
.pc-venue-roomlist li {
  display: flex; flex-wrap: wrap; gap: .4rem .8rem;
  justify-content: space-between; align-items: baseline;
  padding: .6rem 0; border-bottom: 1px solid var(--line, #e6edf3);
}
.pc-venue-roomlist li:last-child { border-bottom: 0; }
.pc-venue-room-name { font-weight: 700; color: var(--brand-deep); }
.pc-venue-room-meta { display: inline-flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.pc-venue-chip {
  display: inline-flex; align-items: center; gap: .3rem;
  font-size: .76rem; font-weight: 700;
  color: var(--v-accent); background: var(--v-soft);
  padding: .2rem .55rem; border-radius: 999px;
}
.pc-venue-cap { font-size: .85rem; color: var(--muted, #5a6b7b); white-space: nowrap; }
.pc-venue-cap i { margin-right: .25rem; }
/* Said in words, not as a dash. A blank where a capacity should be reads as a
   bug; "to be confirmed" reads as the truth, which is what it is. */
.pc-venue-cap-tbc { font-style: italic; opacity: .85; }

.pc-venue-pending {
  display: flex; gap: .5rem; align-items: flex-start;
  font-size: .9rem; color: var(--muted, #5a6b7b); margin: 0;
}
.pc-venue-pending i { color: var(--v-accent); }

.pc-venue-services { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; }
.pc-venue-services li { display: flex; align-items: center; gap: .55rem; font-size: .92rem; }
.pc-venue-services i { color: var(--v-accent); }

/* ── The area map: the airport and both venues ───────────────────────────────
   Full width, above the two venue sections. The frame is allowed taller than
   a venue plan's 26rem because this map is wide and is the only thing in its
   row; `contain` still, never a crop. */
.pc-venue-area { margin-bottom: 3rem; scroll-margin-top: 6.5rem; }
/* No height cap: at 26rem the wide map letterboxed with white bands either
   side. At full width it is as tall as it needs to be and no taller. */
.pc-venue-area-frame img { max-height: none; }
/* The drive times as text, for a phone or tablet, where the map's own small
   print is too small to read. From 62em the map is shown wide enough to read
   its own legend, so the list would only repeat it. */
.pc-venue-routes {
  list-style: none; margin: 0; padding: .35rem .85rem;
  display: grid; gap: .1rem 1.4rem;
  border-top: 1px solid var(--line, #dfe7ee);
}
@media (min-width: 48em) {
  .pc-venue-routes { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 62em) {
  .pc-venue-routes { display: none; }
}
.pc-venue-routes li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .6rem;
  padding: .45rem 0;
}
.pc-venue-route-name { flex: 1 0 100%; font-weight: 700; color: var(--brand-deep); font-size: .92rem; }
.pc-venue-route-time { font-weight: 800; color: var(--v-accent); font-size: 1.05rem; white-space: nowrap; }
.pc-venue-route-time i { margin-right: .3rem; }
.pc-venue-route-km { font-size: .88rem; color: var(--muted, #5a6b7b); white-space: nowrap; }

/* ── Between the two ─────────────────────────────────────────────────────── */
.pc-venue-between {
  background: var(--surface-2); border: 1px solid var(--line, #dfe7ee);
  border-radius: var(--radius, 12px); padding: 1.2rem 1.3rem;
  font-size: .95rem; line-height: 1.6;
}
.pc-venue-between .pc-venue-h3 { color: var(--brand); }

/* ── The full-screen plan viewer ──────────────────────────────────────────
   Fixed, above everything, with the image at its NATURAL size and the box
   scrolling around it. That is the point: the Crowne Plaza map is 4000px wide
   and only becomes readable when it is allowed to be 4000px wide. */
.pc-plan-viewer {
  position: fixed; inset: 0; z-index: 2000;
  background: rgba(8, 24, 40, .94);
  display: flex; flex-direction: column;
}
.pc-plan-viewer[hidden] { display: none; }
body.pc-plan-open { overflow: hidden; }

.pc-plan-viewer-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .7rem 1rem; color: #fff; flex: none;
  background: rgba(0,0,0,.25);
}
.pc-plan-viewer-title { font-weight: 700; font-size: .95rem; }
.pc-plan-viewer-close {
  width: 2.4rem; height: 2.4rem; border-radius: 50%; border: 0; flex: none;
  background: rgba(255,255,255,.14); color: #fff; font-size: 1rem;
  display: grid; place-items: center; cursor: pointer;
}
.pc-plan-viewer-close:hover { background: rgba(255,255,255,.26); }
.pc-plan-viewer-close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

.pc-plan-viewer-scroll { flex: 1 1 auto; overflow: auto; padding: 1rem; }
.pc-plan-viewer-scroll img {
  display: block; margin: 0 auto; background: #fff; border-radius: 8px;
  /* No max-width: the whole reason for this view is to escape one. On a phone
     it is bigger than the screen and scrolls, which is exactly right for a
     site map. */
  max-width: none;
}

@media (prefers-reduced-motion: reduce) {
  .pc-venue-pick-card, .pc-venue-dot { transition: none; }
  .pc-venue-pick-card:hover, .pc-venue-pick-card:focus-visible { transform: none; }
}

/* ── Exhibitors: the Bula Village (2026-09-25) ───────────────────────────────
   The plan card borrows the venue page's (.pc-venue-plan); the page sets
   --v-accent to the poster's green. A booth is marked by a ring laid over the
   plan image at the position read from the Ministry's PDF (Exhibitors::MAP_POINTS),
   sized in percent of the image so it scales with it. */
.pc-exhibitors-intro { max-width: 48rem; margin-bottom: 1.2rem; }
.pc-exhibitors-map { scroll-margin-top: 6.5rem; }
/* #1a9255 is the poster's own background green, sampled from the image. */
.pc-exhibitors-band { background: #1a9255; }
.pc-exhibitors-plan { position: relative; line-height: 0; background: #1a9255; }
.pc-exhibitors-plan img { width: 100%; height: 100%; display: block; }
/* On a laptop the plan is held to the screen's height, so the booth a shared
   link rings and its name above the plan are in view together: at full width
   the plan was 775px tall and booths 5 to 13 fell below a 768px screen. 13rem
   is the sticky bar, the plan's head and the name bar. A block, not a grid
   item, so the auto margins centre it without collapsing its width. */
@media (min-width: 62em) {
  .pc-exhibitors-plan {
    max-width: calc((100vh - 13rem) * 2519 / 1760);
    max-width: calc((100svh - 13rem) * 2519 / 1760);
    margin-inline: auto;
  }
}
.pc-booth-pin {
  position: absolute; width: 6.6%; aspect-ratio: 1; transform: translate(-50%, -50%);
  border-radius: 8px; border: 2px solid transparent; cursor: pointer;
  transition: border-color .15s ease;
}
.pc-booth-pin:hover { border-color: rgba(255, 255, 255, .85); }
.pc-booth-pin.is-on {
  border: clamp(3px, .4vw, 5px) solid #ffd23f;
  box-shadow: 0 0 0 3px rgba(255, 210, 63, .35), 0 0 22px 6px rgba(255, 210, 63, .55);
  animation: pc-booth-pulse 1.4s ease-in-out 3;
}
@keyframes pc-booth-pulse {
  50% { box-shadow: 0 0 0 7px rgba(255, 210, 63, .2), 0 0 30px 12px rgba(255, 210, 63, .7); }
}
/* The booth being looked for, named in words above the plan. */
.pc-booth-callout {
  margin: 0; padding: .55rem .85rem; font-weight: 700; font-size: .95rem; line-height: 1.35;
  background: var(--v-accent); color: #fff;
}
/* Empty until a booth is picked: 0px tall, but still rendered, because a live
   region that appears with its first message is often not read out. */
.pc-booth-callout:empty { padding: 0; }

.pc-exhibitors-h { margin-top: 2rem; }
.pc-exhibitor-list {
  list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem;
}
@media (min-width: 48em) {
  .pc-exhibitor-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.pc-exhibitor {
  display: flex; align-items: center; gap: .75rem; padding: .55rem .7rem;
  background: var(--surface); border: 1px solid var(--line, #dfe7ee); border-radius: 10px;
  scroll-margin-top: 6.5rem; transition: border-color .15s ease, box-shadow .15s ease;
}
.pc-exhibitor.is-on {
  border-color: var(--v-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--v-accent) 25%, transparent);
}
.pc-exhibitor-nos { display: flex; gap: .3rem; flex: none; }
.pc-exhibitor-no {
  width: 2.2rem; height: 2.2rem; border-radius: 50%; display: grid; place-items: center;
  background: var(--v-accent); color: #fff; font-weight: 800; font-size: .95rem;
  text-decoration: none;
}
.pc-exhibitor-no:hover { color: #fff; filter: brightness(1.1); }
.pc-exhibitor-no:focus-visible { outline: 2px solid var(--v-accent); outline-offset: 2px; }
.pc-exhibitor-text { flex: 1; min-width: 0; }
.pc-exhibitor-name { display: block; font-weight: 600; line-height: 1.3; }
.pc-exhibitor-note { display: block; font-size: .82rem; color: var(--muted, #5a6b7b); margin-top: .1rem; }
.pc-exhibitor.is-tbc .pc-exhibitor-name { font-weight: 500; font-style: italic; color: var(--muted, #5a6b7b); }
/* Greyed for "to be confirmed", still 5.6:1 for its white number. */
.pc-exhibitor.is-tbc .pc-exhibitor-no { background: color-mix(in srgb, var(--v-accent) 45%, #5a6b7b); }
.pc-exhibitor-share { flex: none; white-space: nowrap; }
.pc-exhibitor-share i { margin-right: .25rem; }
/* A phone keeps the icon and gives the name the room, "Copied" included: the
   green state and the tick say it there (app.js section 4). */
@media (max-width: 26em) {
  .pc-exhibitor-share-label,
  .pc-exhibitor-share .pc-copied-label { display: none; }
  .pc-exhibitor-share i { margin-right: 0; }
}

.pc-exhibitors-foot { display: grid; gap: 1.4rem; margin-top: 2.2rem; }
@media (min-width: 48em) {
  .pc-exhibitors-foot { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
  .pc-booth-pin, .pc-exhibitor { transition: none; }
  .pc-booth-pin.is-on { animation: none; }
}

/* ── Self-submitting filter bars ──────────────────────────────────────────
   assets/js/filters.js adds .is-autofilter to a bar once it has actually
   attached its listeners. Only then does the Apply button become redundant,
   and only then is it hidden — with scripting off the class is never added,
   the button stays, and the bar works the way it always did. A control that
   is hidden before its replacement exists is a form nobody can submit. */
.is-autofilter .pc-filter-apply { display: none; }

/* The bar dims very slightly while the next page is on its way, so a change
   to a dropdown visibly does something on a slow connection instead of
   appearing to have been ignored. */
form[data-autofilter][aria-busy="true"] { opacity: .55; pointer-events: none; }
form[data-autofilter] { transition: opacity .12s linear; }

@media (prefers-reduced-motion: reduce) {
  form[data-autofilter] { transition: none; }
}

/* ═══════════ Scanner layout — a phone in a doorway, first ═══════════════════
   Source order is set-up → camera → verdict → recent, which is the order an
   officer needs them in on a phone. On a laptop the grid below puts set-up and
   recent back into a left column beside the camera, so the desktop screen is
   unchanged. Before this the history feed sat between the session picker and
   the camera, and every scan started with a scroll past a list of names. */
.pc-scan-layout { display: flex; flex-direction: column; gap: 1rem; }
.pc-scan-verdict:empty { display: none; }

@media (min-width: 992px) {
  .pc-scan-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
    grid-template-areas: "setup camera" "history verdict";
    align-items: start;
  }
  .pc-scan-setup   { grid-area: setup; }
  .pc-scan-camera  { grid-area: camera; }
  .pc-scan-verdict { grid-area: verdict; }
  .pc-scan-history { grid-area: history; }
}

/* Two jobs side by side rather than stacked: on a phone that is one row of
   buttons instead of two, and the session picker stays above the fold. */
.pc-op-row { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.pc-op-btn { cursor: pointer; margin: 0; min-height: 5.5rem; padding: .85rem .6rem; }
.pc-op-label { font-size: .95rem; line-height: 1.2; }
.pc-op-help  { font-size: .72rem; font-weight: 400; color: var(--muted); line-height: 1.25; }
.pc-op-btn.is-on {
  border-color: var(--brand); box-shadow: var(--shadow);
  background: color-mix(in srgb, var(--brand) 7%, var(--surface));
}
.pc-op-btn.is-on i { transform: scale(1.06); }

/* The door this scanner is set to, kept next to the camera so it is visible at
   the moment of scanning rather than only up in the set-up card. */
.pc-scan-door {
  display: inline-flex; align-items: center; gap: .35rem; margin-top: .35rem;
  background: color-mix(in srgb, var(--brand) 10%, transparent);
  color: var(--brand); border-radius: 999px; padding: .2rem .7rem;
  font-size: .78rem; font-weight: 700; max-width: 100%;
}
.pc-scan-door span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.pc-scan-controls { display: flex; gap: .5rem; flex-wrap: wrap; }
.pc-scan-controls .btn { flex: 1 1 auto; min-height: 3rem; font-weight: 700; }

@media (max-width: 991.98px) {
  /* A square stage eats a phone screen. Cap it so the camera, the buttons and
     the first line of the verdict are reachable without scrolling. */
  .pc-scanner-stage { aspect-ratio: 4 / 3; max-height: 46vh; }
  .pc-op-btn { min-height: 5rem; }
  .pc-op-help { display: none; }          /* the label says it; the help is desktop detail */
  .pc-scan-controls #startBtn { flex-basis: 100%; }
}

/* iOS zooms the whole page when a control smaller than 16px takes focus, and
   never zooms back out. Both of these are typed into at a door. */
.pc-scan-select, .pc-scan-input { font-size: 16px; min-height: 3rem; }

/* The thumb has to find these while the other hand holds a badge. */
.pc-verdict-action .btn { min-height: 3rem; font-weight: 700; }

/* Briefly mark what the officer has just been moved to, so the jump is
   explained rather than disorienting. */
@keyframes pcNudge {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--brand) 55%, transparent); }
  100% { box-shadow: 0 0 0 .75rem transparent; }
}
.pc-nudge { animation: pcNudge 1.1s ease-out 2; border-radius: var(--radius); }

/* ── The session picker ─────────────────────────────────────────────────────
   A native <select> holding the whole programme is a scrolling drum with no
   search, which is hard to use in a doorway. This is the trigger for a
   full-screen, searchable list instead. */
.pc-session-pick {
  display: flex; align-items: center; gap: .75rem; width: 100%; text-align: left;
  min-height: 3.4rem; padding: .6rem .85rem; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
}
.pc-session-pick:active { transform: scale(.995); }
.pc-session-pick.is-set { border-color: var(--brand); }
.pc-session-pick-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.pc-session-pick-text strong {
  font-size: .95rem; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pc-session-pick-text small { color: var(--muted); font-size: .78rem; }
.pc-session-pick > .bi { color: var(--muted); flex: 0 0 auto; }

.pc-session-search {
  position: relative; padding: .75rem 1rem; border-bottom: 1px solid var(--line);
}
.pc-session-search .bi {
  position: absolute; left: 1.75rem; top: 50%; transform: translateY(-50%);
  color: var(--muted); pointer-events: none;
}
/* 16px, or iOS zooms the page the moment this takes focus. */
.pc-session-search .form-control { padding-left: 2.4rem; font-size: 16px; min-height: 3rem; }

.pc-session-list { list-style: none; margin: 0; padding: 0; }
.pc-session-group {
  padding: .6rem 1rem .35rem; font-size: .75rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
  background: var(--bg); position: sticky; top: 0; z-index: 1;
}
.pc-session-row {
  display: flex; align-items: center; gap: .85rem; width: 100%; text-align: left;
  padding: .8rem 1rem; min-height: 3.6rem;      /* a thumb, not a mouse pointer */
  border: 0; border-bottom: 1px solid var(--line); background: transparent; color: var(--ink);
}
.pc-session-row:hover  { background: var(--bg); }
.pc-session-row:active { background: color-mix(in srgb, var(--brand) 8%, transparent); }
.pc-session-row.is-on  { background: color-mix(in srgb, var(--brand) 10%, transparent); }
.pc-session-row.is-on > .bi { color: var(--brand); margin-left: auto; font-size: 1.15rem; }
.pc-session-when {
  flex: 0 0 4.2rem; display: flex; flex-direction: column; line-height: 1.15;
}
.pc-session-when strong { font-size: .9rem; }
.pc-session-when small  { color: var(--muted); font-size: .7rem; }
.pc-session-what { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.pc-session-what strong { font-size: .92rem; }
.pc-session-what small  { color: var(--muted); font-size: .76rem; }
.pc-session-empty { padding: 2rem 1rem; text-align: center; color: var(--muted); font-size: .9rem; }

/* ── Visa page ──────────────────────────────────────────────────────────────
   The words are the Ministry's and are reproduced exactly; this is only the
   typography that makes them readable on a phone. */
.pc-visa .pc-card-body p { line-height: 1.65; }

/* The two government links are shown as their full addresses, because that is
   how the source gives them and an officer may need to read one aloud. Long,
   so they must wrap rather than widen the page. */
.pc-visa-apply,
.pc-visa .btn-outline-secondary {
  display: inline-flex; align-items: center; text-align: left;
  white-space: normal; word-break: break-all; max-width: 100%;
}
.pc-visa-apply i, .pc-visa .btn-outline-secondary i { flex: 0 0 auto; }

/* The one timing instruction on the page. */
.pc-visa-flag {
  display: flex; gap: .75rem; align-items: flex-start;
  border-left: 3px solid var(--brand);
  background: color-mix(in srgb, var(--brand) 6%, transparent);
  border-radius: 0 var(--radius) var(--radius) 0; padding: .85rem 1rem;
}
.pc-visa-flag i { color: var(--brand); font-size: 1.1rem; margin-top: .15rem; }

/* Shown a second time on its own so it is copied exactly, not paraphrased. */
.pc-visa-quote {
  background: color-mix(in srgb, var(--brand) 8%, var(--surface));
  border: 1px dashed color-mix(in srgb, var(--brand) 35%, transparent);
  border-radius: var(--radius); padding: .8rem 1rem;
  font-weight: 700; color: var(--brand-deep); text-align: center;
}

.pc-visa-people { list-style: decimal; margin: 0; padding-left: 1.25rem; display: grid; gap: .75rem; }
.pc-visa-person::marker { font-weight: 700; color: var(--muted); }
.pc-visa-person-body {
  display: flex; flex-direction: column; gap: .3rem; min-width: 0;
  border: 1px solid var(--line); border-radius: var(--radius); padding: .8rem .9rem;
}
.pc-visa-person-body strong { font-size: .95rem; line-height: 1.35; }
.pc-visa-person-body a { font-size: .88rem; word-break: break-all; }

/* ── A list refreshing in place ────────────────────────────────────────────
   assets/js/livelist.js replaces the whole list region — filter bar, table
   and pager — when somebody types in the search box or turns a page. The
   swap is usually far too quick to see; on a slow connection it is not, and
   without this the screen looks frozen while the request is in the air.

   Dimmed rather than blanked, and the controls stay put: the reader keeps
   typing into the same box they were already in, and the row they were
   reading does not vanish before its replacement arrives. */
#pcView.is-loading {
  opacity: .55;
  transition: opacity .12s ease-in;
}
#pcView.is-loading .table,
#pcView.is-loading .pagination { pointer-events: none; }

/* The search box is the exception: it must never go dead or dim under the
   cursor, because the reader is still typing in it while this is on. */
#pcView.is-loading form[data-autofilter] { opacity: 1; }
