/* ==========================================================================
   Express Core: WordPress additions to the concept CSS (every site).
   main.css and pages.css are the family's shared concept layer (the Express
   Facades copies; copy them over again when the concept changes). Each child
   adds its own layer and its own WordPress additions.
   ========================================================================== */

/* Tender form: honeypot field (bots fill it, people never see it) */
.form-hp {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Tender form: a message from the server (expired form, file too large, sending failed) */
.tender-form__alert {
  margin-top: 20px;
  padding: 16px 20px;
  border: 1px solid rgba(180, 35, 24, 0.35);
  border-radius: var(--r-md);
  background: #FDF1F0;
  color: var(--c-error);
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1.5;
}

/* Tender form: while it is being sent */
.tender-form.is-sending button[type="submit"] {
  opacity: 0.6;
  cursor: progress;
}

/* Project status "Won" (the concept has on site, complete and TBC) */
.status--won { background: var(--c-ink); color: var(--c-accent); }

/* Logged-in editors: keep the sticky header and section nav below the WordPress toolbar */
body.admin-bar .site-header { top: 32px; }
body.admin-bar.page-inner { --sticky-top: calc(76px + 32px); }
@media (min-width: 768px) {
  body.admin-bar.page-inner { --sticky-top: calc(86px + 32px); }
}
@media (max-width: 782px) {
  body.admin-bar .site-header { top: 46px; }
  body.admin-bar.page-inner { --sticky-top: calc(76px + 46px); }
}
@media (max-width: 600px) {
  body.admin-bar .site-header { top: 0; }
}

/* Content from the editor on plain pages */
.screen-reader-text {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.prose .alignleft { float: left; margin: 0 1.5em 1em 0; }
.prose .alignright { float: right; margin: 0 0 1em 1.5em; }
.prose .aligncenter { margin-inline: auto; }
.prose .wp-caption-text,
.prose figcaption { font-size: var(--fs-xs); color: var(--c-muted); }

/* Project cards: "Delivered by Express Facades" (lead, T-013 review). A small label on the photo, in
   the style of the "Visualisation" label (pages.css .media-label), at the top so the two never meet.
   It lets clicks through to the card link. */
.project-card__media { position: relative; }
.delivered-by {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  max-width: calc(100% - 20px);
  padding: 4px 12px 4px 4px;
  border-radius: var(--r-pill);
  background: rgba(20, 20, 20, 0.8);
  font-size: var(--fs-xs);
  font-weight: 500;
  line-height: 1.25;
  color: #fff;
  pointer-events: none;
}
.delivered-by__icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--c-accent);
  color: var(--c-ink);
}
.delivered-by__icon .icon { width: 13px; height: 13px; }

/* Cookie banner and cookie settings (T-014; not in the concept: inc/consent.php, assets/js/consent.js).
   A light card at the foot of the screen, above the mobile bar; Accept and Reject look the same. */
.consent-banner {
  position: fixed;
  left: var(--gutter);
  bottom: var(--gutter);
  z-index: 45;
  display: grid;
  gap: 14px;
  width: min(34rem, calc(100% - 2 * var(--gutter)));
  padding: 22px 22px 16px;
  border: 1px solid var(--c-line);
  border-radius: var(--r-lg);
  background: var(--c-surface);
  box-shadow: var(--shadow-lift);
  color: var(--c-ink);
}
.consent-banner[hidden] { display: none; }
.consent-banner__title { font-size: 1.125rem; font-weight: 600; line-height: 1.3; letter-spacing: -0.01em; }
.consent-banner__text { margin-top: 6px; font-size: var(--fs-small); line-height: 1.55; color: var(--c-text-2); }
.consent-banner__text a,
.consent-dialog__intro a { color: var(--c-ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgba(20, 20, 20, 0.35); }
.consent-banner__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.consent-banner__choice { justify-content: center; padding: 5px 20px; }
.consent-banner__settings {
  min-height: 44px;
  padding: 0 10px;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--c-ink);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-color: rgba(20, 20, 20, 0.28);
  cursor: pointer;
}
.consent-banner__settings:hover { color: var(--c-accent-ink); text-decoration-color: var(--c-accent-ink); }

@media (max-width: 767.98px) {
  .consent-banner {
    left: 12px;
    right: 12px;
    bottom: calc(12px + env(safe-area-inset-bottom));
    width: auto;
    max-height: calc(100vh - 24px);
    overflow-y: auto;
    padding: 18px 18px 12px;
  }
  .consent-banner__actions { display: grid; grid-template-columns: 1fr 1fr; }
  .consent-banner__settings { grid-column: 1 / -1; justify-self: start; padding: 0; }
}
@media (max-width: 379.98px) {
  .consent-banner__actions { grid-template-columns: 1fr; }
  .consent-banner__choice { white-space: normal; }
}
/* Short screens (1.3.4, T-034 A3-01; WCAG 2.2, 2.4.11): a landscape phone or a laptop zoomed to 200% (about
   640 to 740 x 334 to 400) left the stacked banner (253px, two thirds of the screen) and the sticky header and
   section nav so little room that most focus stops landed under the banner. Here the banner is compact: the
   title is not shown (it still names the region), one small paragraph across the full width, the two buttons
   and "Cookie settings" side by side in one row, smaller padding, and at most about a third of the screen. */
@media (max-height: 500px) {
  .consent-banner {
    left: 12px;
    right: 12px;
    bottom: calc(8px + env(safe-area-inset-bottom));
    width: auto;
    max-width: 52rem;
    max-height: 34vh;
    overflow-y: auto;
    gap: 6px;
    padding: 8px 14px;
    border-radius: var(--r-md, 20px);
  }
  .consent-banner__title { display: none; }
  .consent-banner__text { margin-top: 0; font-size: 0.78125rem; line-height: 1.35; }
  .consent-banner__actions { display: flex; flex-wrap: nowrap; align-items: center; gap: 6px; }
  .consent-banner__choice { min-height: 40px; padding: 2px 14px; white-space: nowrap; }
  .consent-banner__settings { grid-column: auto; min-height: 40px; padding: 0 6px; white-space: nowrap; }
}
@media (max-height: 500px) and (max-width: 379.98px) {
  .consent-banner__choice { padding: 2px 10px; }
}

.consent-dialog {
  width: min(36rem, calc(100% - 24px));
  max-width: none;
  max-height: calc(100% - 24px);
  padding: 0;
  border: 0;
  border-radius: var(--r-lg);
  background: var(--c-surface);
  color: var(--c-ink);
  box-shadow: var(--shadow-lift);
}
.consent-dialog::backdrop { background: rgba(14, 14, 13, 0.55); }
.consent-dialog__form { position: relative; display: grid; gap: 16px; padding: 28px 24px 22px; }
.consent-dialog__title { padding-right: 48px; font-size: clamp(1.5rem, 1.3rem + 0.8vw, 1.875rem); font-weight: 600; letter-spacing: -0.03em; line-height: 1.15; }
.consent-dialog__intro { font-size: var(--fs-small); line-height: 1.55; color: var(--c-text-2); }
.consent-option {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 0 0;
  border-top: 1px solid var(--c-line);
}
.consent-option__title { font-size: 1rem; font-weight: 600; line-height: 1.35; }
.consent-option__title label { cursor: pointer; }
.consent-option__text { margin-top: 4px; font-size: var(--fs-xs); line-height: 1.5; color: var(--c-muted); }
.consent-option__state {
  flex: none;
  padding: 4px 12px;
  border-radius: var(--r-pill);
  background: var(--c-surface-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--c-text-2);
  white-space: nowrap;
}
.consent-switch {
  flex: none;
  position: relative;
  width: 52px;
  height: 30px;
  margin: 0;
  border-radius: var(--r-pill);
  background: var(--c-line-2);
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  transition: background-color 0.25s var(--ease);
}
.consent-switch::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(28, 28, 26, 0.3);
  transition: transform 0.25s var(--ease);
}
.consent-switch:checked { background: var(--c-accent-ink); }
.consent-switch:checked::after { transform: translateX(22px); }
.consent-switch:focus-visible { outline: 3px solid var(--c-accent-ink); outline-offset: 3px; }
.consent-dialog__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 20px; padding-top: 8px; }
.consent-dialog__actions .btn { justify-content: center; padding: 6px 24px; }
.consent-dialog__close {
  position: absolute;
  top: 16px;
  right: 16px;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--c-line);
  border-radius: 50%;
  background: var(--c-surface);
  color: var(--c-ink);
  cursor: pointer;
}
.consent-dialog__close:hover { border-color: var(--c-ink); }
.consent-dialog__close .icon { width: 17px; height: 17px; }
@media (prefers-reduced-motion: reduce) {
  .consent-switch,
  .consent-switch::after { transition: none; }
}

/* Search (T-014; not in the concept): search.php, inc/search.php */
.search-form { display: flex; gap: 8px; max-width: 40rem; margin-top: 28px; }
.search-form__input { flex: 1 1 auto; min-width: 0; border-radius: var(--r-pill); padding-left: 22px; }
.search-form__button { flex: none; }
.search-results__count { margin-bottom: 8px; font-size: var(--fs-small); color: var(--c-muted); }
.search-results__list { display: grid; max-width: 52rem; border-top: 1px solid var(--c-line); }
.search-result { display: grid; gap: 6px; padding: 22px 0; border-bottom: 1px solid var(--c-line); }
.search-result__kind { font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-accent-ink); }
.search-result__title { font-size: var(--fs-h3); font-weight: 600; line-height: 1.25; letter-spacing: -0.02em; }
.search-result__title a { text-decoration: underline; text-decoration-thickness: 1.5px; text-underline-offset: 4px; text-decoration-color: rgba(20, 20, 20, 0.2); }
.search-result__title a:hover { color: var(--c-accent-ink); text-decoration-color: var(--c-accent-ink); }
.search-result__text { max-width: 44rem; color: var(--c-text-2); }
.search-results__empty { display: grid; gap: 12px; max-width: 44rem; color: var(--c-text-2); }
.search-results__links { display: flex; flex-wrap: wrap; gap: 0 22px; }
@media (max-width: 479.98px) {
  .search-form__button .btn__label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .search-form__button { padding: 6px; }
}

/* Keyboard focus is never hidden under the sticky header, the sticky section nav, the mobile bar or the
   cookie banner (WCAG 2.2, 2.4.11; Group T-015 R1-07 and R2-01). The page keeps that space free with
   scroll-padding, so a focused element scrolled into view clears the bars, forwards and backwards.
   Top: the concept's anchor offsets (main.css "[id]" 104px; pages.css ".page-inner main [id]"
   --sticky-top + 72px for the header and the section nav) move from the targets' scroll-margin to the
   page's scroll-padding, so anchor jumps land exactly where they did.
   Bottom: the mobile bar under 768px (--bottom-ui) and the cookie banner while it is open (--banner-h,
   set on <html> by consent.js). */
:root { --ex-top-ui: 104px; }
:root:has(> body.page-inner) { --ex-top-ui: calc(76px + 72px); }
@media (min-width: 768px) {
  :root:has(> body.page-inner) { --ex-top-ui: calc(86px + 72px); }
}
@media (max-width: 767.98px) {
  :root { --bottom-ui: calc(76px + env(safe-area-inset-bottom)); }
}
html {
  scroll-padding-top: var(--ex-top-ui);
  scroll-padding-bottom: calc(max(var(--bottom-ui, 0px), var(--banner-h, 0px)) + 12px);
}
/* The end of the page scrolls clear of the open cookie banner too (the footer links). */
body { padding-bottom: max(var(--bottom-ui, 0px), var(--banner-h, 0px)); }
/* On phones the open cookie banner covers the bottom bar (Call, the main call to action), which is the last stop in
   the tab order: a keyboard user would reach two links hidden under the banner. While the banner is open
   (html.consent-open, set by consent.js) the bar steps aside and leaves the tab order; it comes back after the
   choice (1.3.3, T-034; Group R6-01 family, WCAG 2.4.11). */
html.consent-open .mobile-bar { visibility: hidden; transform: translateY(110%); }
/* The legal pages' contents list sticks at the top of the screen from 1024px, where its last links sat under the
   open cookie banner (a stuck element cannot be scrolled clear of it). While the banner is open the list scrolls
   with the page, so scroll-padding lifts each link above the banner; it sticks again after the choice. */
html.consent-open .legal__toc { position: static; }
/* After a choice in the cookie banner, consent.js hands focus to <main> (tabindex -1 while focused) so the next
   Tab continues into the page (1.3.3, T-034 A2-06): no ring on the whole content area. */
#main[tabindex="-1"]:focus { outline: none; }
[id],
.page-inner main [id] { scroll-margin-top: 0; }
/* Links inside the sticky bars are always on screen: focusing them never scrolls the page. */
.site-header :is(a, button),
.section-nav :is(a, button) { scroll-margin-top: calc(-1 * var(--ex-top-ui)); }
/* "On this page" and the keyboard (1.3.4, T-034 A3-02): below 1024px the list fades out at its right edge while
   more links follow. main.js ("section nav and the keyboard") scrolls a focused link fully into the clear part
   of the bar and marks the list .is-end once it is scrolled to its end (or fits): then the fade goes, so the
   last link is never left under it. While a link has keyboard focus there is no fade at all (a long name, such as
   "MET-PRO SFS and HHB brackets" at 320px, is wider than the clear part), and while a link has focus the list does
   not snap, so it stays where it was scrolled. */
.section-nav__list.is-end,
.section-nav__list:has(:focus-visible) { -webkit-mask-image: none; mask-image: none; }
.section-nav__list:focus-within { scroll-snap-type: none; }
/* The list clips what overflows it, and the focus ring sits 3px outside a link (3px wide): 6px of padding all round,
   taken back by a negative margin, give the ring room without moving a link or changing the bar's size. */
.section-nav__list { margin: -6px; padding: 6px; scroll-padding-inline: 6px; }

/* Scrolling tables and other scroll regions reached by keyboard (tabindex="0", role="region"): a visible
   focus ring (F1-05). */
[role="region"][tabindex="0"]:focus-visible {
  outline: 3px solid var(--c-accent-ink, currentColor);
  outline-offset: 3px;
}

/* The map panel's Google map (1.3.6, T-038; inc/map.php, js/map.js). The panel shows the pin card with "Show map"
   and one line about Google; on the click the map fills the panel in place (same size, nothing moves) and the card
   underneath leaves the page and the tab order. Without JavaScript the button and the line are hidden. */
.map-embed__show { background: #fff; }
.map-embed__note {
  max-width: 44ch;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--c-text-2, var(--c-muted));
  text-wrap: balance;
}
.no-js .map-embed__show,
.no-js .map-embed__note { display: none; }
.map-embed.is-loaded > :not(.map-embed__frame) { visibility: hidden; }
.map-embed__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  background: #E7E5DE;
}
.map-embed__frame:focus-visible {
  outline: 3px solid var(--c-accent-ink, currentColor);
  outline-offset: -3px;
}
