/* ==========================================================================
   BTENG theme — component library (Phase 2)
   Loaded after bteng.css. Uses the same tokens; no raw brand hex except where
   a data-URI needs an inline colour.
   ========================================================================== */

/* ---- Prose (markdown content pages) ------------------------------------ */
.prose { max-width: 75ch; }
.prose > * + * { margin-top: var(--sp-4); }
.prose h2 { margin-top: var(--sp-7); }
.prose h3 { margin-top: var(--sp-5); }
.prose ul, .prose ol { padding-left: var(--sp-5); display: grid; gap: 6px; }
.prose ul { list-style: disc; }
.prose ol { list-style: decimal; }
.prose a { text-decoration: underline; text-underline-offset: 2px; }
.prose img { border-radius: var(--radius); }
.prose blockquote { border-left: 4px solid var(--c-brand); padding-left: var(--sp-4); color: var(--text-muted); }
.prose code { background: var(--c-tint); padding: 2px 6px; border-radius: 4px; font-size: .9em; }
.prose hr { border: none; border-top: 1px solid var(--c-border); }

/* ---- Badge / tag ------------------------------------------------------- */
.badge { display: inline-block; font-family: var(--font-heading); font-size: .72rem; font-weight: 500;
  letter-spacing: .03em; text-transform: uppercase; padding: 3px 10px; border-radius: 999px;
  background: var(--c-tint); color: var(--c-primary-deep); }
.badge--urgent { background: var(--c-urgent); color: var(--c-surface); }

/* ---- Filter chips (Awards / Media filtering — Phase 4) ----------------- */
.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: center; }
.chip { padding: 8px 16px; border: 1px solid var(--c-border); border-radius: 999px;
  background: var(--c-surface); color: var(--c-ink); font-family: var(--font-heading); font-weight: 500;
  cursor: pointer; transition: background var(--transition), color var(--transition), border-color var(--transition); }
.chip:hover { border-color: var(--c-primary); color: var(--c-primary-deep); }
.chip.is-active { background: var(--c-primary-strong); color: #fff; border-color: var(--c-primary-strong); }

/* Labelled chip groups (Career filters: location + employment type) */
.chips-bar { display: flex; flex-direction: column; gap: var(--sp-3); margin-bottom: var(--sp-6); }
.chips-group { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; justify-content: center; }
.chips-group__label { font-family: var(--font-heading); font-size: .8rem; font-weight: 500;
  color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; }
/* Reset control: no label, and it should not claim a chip's visual weight. */
.chips-group--reset { justify-content: center; }

/* ---- Breadcrumbs (on green page-hero → light text) --------------------- */
/* The reset only clears lists that carry a class (`ol[class]`), and this one
   has none — without these two declarations it keeps the UA's 40px list indent
   and sits offset from the heading below it. */
.breadcrumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  font-size: .85rem; padding: 0; margin: 0; list-style: none; }
.breadcrumbs li { display: flex; align-items: center; gap: var(--sp-2); color: var(--c-on-dark-muted); }
.breadcrumbs li + li::before { content: "/"; opacity: .5; }
.breadcrumbs a { color: var(--c-on-dark); }
.breadcrumbs a:hover { color: #fff; }
.breadcrumbs [aria-current] { color: #fff; }

/* ---- Interior page hero (banner) --------------------------------------- */
/* Two static paint layers — a diagonal tint plus one soft highlight — so the
   panel reads as a designed surface rather than a flat black block. Nothing
   animates here, so the cost is a single paint.
   Defaults fall back to the plain navy pair; each section overrides the two
   custom properties below. */
.page-hero { background: var(--c-navy); color: #fff;
  --hero-from: var(--c-navy); --hero-to: var(--c-navy-deep);
  background-image: var(--hero-glow),
                    linear-gradient(135deg, var(--hero-from) 0%, var(--hero-to) 100%);
  padding: clamp(var(--sp-6), 7vw, 72px) 0 clamp(var(--sp-6), 6vw, 56px); }

.page-hero--about              { --hero-from: var(--hero-about-from);    --hero-to: var(--hero-about-to); }
.page-hero--products-services  { --hero-from: var(--hero-products-from); --hero-to: var(--hero-products-to); }
.page-hero--awards-certificates{ --hero-from: var(--hero-awards-from);   --hero-to: var(--hero-awards-to); }
.page-hero--media              { --hero-from: var(--hero-media-from);    --hero-to: var(--hero-media-to); }
.page-hero--contact            { --hero-from: var(--hero-contact-from);  --hero-to: var(--hero-contact-to); }
/* With a background photograph the section gradient becomes a wash laid OVER
   the image rather than the background itself, so the banner keeps exactly the
   colour it has everywhere else and the photo reads as texture behind it. The
   gradient moves to a pseudo-element because --hero-from/--hero-to are solid
   tokens: compositing them at partial opacity is what lets the photo show
   through without redefining the palette per page. */
.page-hero--photo { position: relative; isolation: isolate;
  background-image: var(--hero-photo);
  background-size: cover; background-position: center; }
.page-hero--photo::before { content: ''; position: absolute; inset: 0; z-index: -1;
  background-image: var(--hero-glow),
                    linear-gradient(135deg, var(--hero-from) 0%, var(--hero-to) 100%);
  opacity: .88; }
.page-hero__eyebrow { color: var(--c-primary); }
.page-hero__title { color: #fff; margin-top: var(--sp-4); }
.page-hero__subtitle { color: var(--c-on-dark-muted); margin-top: var(--sp-3); max-width: 60ch; }

/* ---- Project / image card (used by carousel + grids) ------------------- */
/* `padding: 0` is deliberate: combined with .card the image would otherwise be
   inset by the card's own 24px and float inside a frame. The art runs edge to
   edge; only the caption below is padded. */
.project-card { display: flex; flex-direction: column; height: 100%; overflow: hidden; padding: 0;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius-lg);
  color: inherit; }
/* Not `a.` — on the home page the project card is a div whose image opens the
   lightbox instead of navigating. */
.project-card:hover { border-color: var(--c-primary-strong); color: inherit; }
.project-card__img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
.project-card__img--ph { display: flex; align-items: center; justify-content: center;
  background: var(--c-tint); color: var(--c-primary-strong); }
.project-card__body { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-5) var(--sp-5); }
.project-card__title { font-family: var(--font-heading); font-weight: 500; font-size: 1.15rem; color: var(--c-ink); }
.project-card__sub { color: var(--text-muted); font-size: .95rem; }

/* ---- Carousel (Swiper) ------------------------------------------------- */
.carousel { position: relative; }
.carousel .swiper { padding: 4px; margin: -4px; }       /* room for card hover/shadow */
.carousel .swiper-slide { height: auto; }                /* equal-height cards         */
.carousel__controls { display: flex; align-items: center; justify-content: center;
  gap: var(--sp-4); margin-top: var(--sp-6); }
.carousel__btn { width: 46px; height: 46px; border-radius: 50%; border: 1px solid var(--c-border);
  background: var(--c-surface); color: var(--c-primary-deep); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background var(--transition), color var(--transition), border-color var(--transition); }
.carousel__btn:hover { background: var(--c-primary-strong); color: #fff; border-color: var(--c-primary-strong); }
.carousel__btn.swiper-button-disabled { opacity: .4; cursor: default; }
.carousel__btn.swiper-button-disabled:hover { background: var(--c-surface); color: var(--c-primary-deep); border-color: var(--c-border); }
.carousel__pagination { display: flex; align-items: center; gap: 6px; }
.carousel__pagination .swiper-pagination-bullet { width: 9px; height: 9px; border-radius: 50%;
  background: var(--c-border); opacity: 1; cursor: pointer; transition: background var(--transition), width var(--transition); }
.carousel__pagination .swiper-pagination-bullet-active { background: var(--c-brand); width: 26px; border-radius: 5px; }

/* ---- Modal (Alpine + focus-trap) --------------------------------------- */
.modal { position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center; padding: var(--sp-4); }
.modal__backdrop { position: absolute; inset: 0; background: var(--c-scrim); backdrop-filter: blur(2px); }
.modal__dialog { position: relative; width: 100%; max-width: 520px; max-height: 90vh; overflow-y: auto;
  background: var(--c-surface); border-radius: var(--radius-lg); padding: clamp(var(--sp-5), 5vw, var(--sp-7));
  box-shadow: var(--shadow-pop); }
.modal__close { position: absolute; top: var(--sp-3); right: var(--sp-3); width: 40px; height: 40px;
  border: none; background: transparent; border-radius: var(--radius-sm); color: var(--c-muted);
  display: flex; align-items: center; justify-content: center; cursor: pointer; transition: background var(--transition), color var(--transition); }
.modal__close:hover { background: var(--c-tint); color: var(--c-primary-deep); }
.modal__title { margin-bottom: var(--sp-2); }
.modal__intro { color: var(--text-muted); margin-bottom: var(--sp-5); }

/* Result dialog shown after a modal form is submitted (see modal-sent.html.twig) */
/* Alpine drives the open/close transition inline (see partials/modal-*.twig);
   this only makes sure it collapses to nothing for anyone who asks for less
   motion, since inline styles cannot carry a media query. */
@media (prefers-reduced-motion: reduce) {
  .modal, .modal__dialog { transition-duration: 1ms !important; }
}
.modal__dialog--result { max-width: 420px; text-align: center; }
.modal__result-icon { width: 64px; height: 64px; margin: var(--sp-2) auto var(--sp-4);
  display: flex; align-items: center; justify-content: center; border-radius: 50%; }
.modal__result-icon.is-ok { background: var(--c-tint); color: var(--c-primary-deep); }
.modal__result-icon.is-fail { background: var(--c-notice-err-bg); color: var(--c-urgent); }
.modal__dialog--result .form__actions { display: flex; justify-content: center; }

/* ---- PDPA: privacy notice + consent checkbox ---------------------------
   The notice sits directly under each form that collects a name or contact
   detail (PDPA s.23), so it is read where the data is handed over rather than
   buried in the footer. */
.form-privacy { margin-top: var(--sp-4); font-size: .8125rem; line-height: 1.55;
  color: var(--text-muted); }
.form-privacy a { text-decoration: underline; }

/* Consent must be a deliberate, separate act (s.19), so it is laid out as its
   own block rather than tucked in beside the other inputs.

   The box is styled on the WRAPPER, never on the label. The plugin's checkbox
   template hard-codes style="display:inline" on that label, which an inline
   style wins outright — so padding, background and border applied to it were
   being drawn on an inline box, which cannot take vertical padding, and the
   result bled over the lines above and below. Making the wrapper the flex row
   blockifies both children, so the text wraps as a normal block. */
form [data-grav-field="checkbox"] .form-input-wrapper {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-4); background: var(--c-tint);
  border: 1px solid var(--c-border); border-radius: var(--radius); }
form [data-grav-field="checkbox"] .form-input-wrapper > label {
  margin: 0; font-size: .875rem; line-height: 1.55; color: var(--c-ink); cursor: pointer; }
form [data-grav-field="checkbox"] input[type="checkbox"] {
  flex: none; width: 18px; height: 18px; margin-top: 2px; accent-color: var(--c-primary-strong); }

/* A checkbox puts its text beside the box, so the field's own label above it is
   rendered empty. Left visible it adds a blank row — and its `for` points at an
   id the checkbox template hyphenizes, so it addresses nothing either. */
form .form__label:empty { display: none; }

/* A read-only position reads as "filled in for you", not as a broken input. */
form input[readonly] { background: var(--c-tint); color: var(--text-muted); cursor: default; }

/* ---- File upload (CV on the application form) -------------------------
   The form plugin's own .dropzone is a faint dashed box whose prompt text is
   injected by Dropzone's JavaScript; until that runs it looks like an empty
   gap under the label, which is easily read as a missing field. Giving it the
   theme's own surface, a clear brand-tinted border and real height means the
   control is visible and obviously clickable no matter what the script does. */
.form-input-file.dropzone {
  min-height: 116px; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--sp-2);
  padding: var(--sp-5); cursor: pointer;
  border: 2px dashed var(--c-accent-light, var(--c-border));
  border-radius: var(--radius-lg);
  background: var(--c-tint); color: var(--text-muted);
  transition: border-color var(--transition), background var(--transition); }
.form-input-file.dropzone:hover,
.form-input-file.dropzone.dz-drag-hover { border-color: var(--c-primary-strong); background: var(--c-surface); }
/* Dropzone's own prompt, once it has been injected. */
.form-input-file.dropzone .dz-message { margin: 0; color: inherit; font-family: var(--font-heading); }
/* A queued file should look like an attachment, not like the empty state. */
.form-input-file.dropzone.dz-started { flex-direction: row; flex-wrap: wrap; justify-content: flex-start; min-height: 0; }

/* Grav form notices (validation errors, success text). The form plugin emits
   `notices <status>`; without these the message renders as unstyled body text. */
.notices { margin-bottom: var(--sp-4); padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm); border-left: 4px solid var(--c-border); background: var(--c-page); }
.notices p { margin: 0; }
.notices.success { border-left-color: var(--c-primary-strong); background: var(--c-tint); color: var(--c-primary-deep); }
.notices.error { border-left-color: var(--c-urgent); background: var(--c-notice-err-bg); color: var(--c-urgent); }
.notices.warning { border-left-color: var(--c-notice-warn); background: var(--c-notice-warn-bg); color: var(--c-notice-warn); }

/* ---- Image lightbox (full-size view of card images) --------------------- */
.js-lightbox { cursor: zoom-in; }
/* Opens and closes on opacity + transform only, so the overlay is composited
   rather than repainted. `.is-open` is added a frame after `hidden` is cleared
   (and removed before it is restored) — see openLightbox/closeLightbox. */
.lightbox { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center;
  padding: var(--sp-5); background: var(--c-scrim); backdrop-filter: blur(3px); cursor: zoom-out;
  opacity: 0; transition: opacity .2s ease;
  /* While fading out the overlay is still in the DOM and still covers the
     viewport; without this it would keep swallowing clicks at opacity 0 — and
     background-tab timer throttling can stretch that window to seconds. */
  pointer-events: none; }
.lightbox.is-open { opacity: 1; pointer-events: auto; }
.lightbox .lightbox__frame { transform: scale(.96); transition: transform .2s ease; }
.lightbox.is-open .lightbox__frame { transform: scale(1); }
/* The frame holds the picture and its caption bar as one panel, so the caption
   tracks the image rather than floating against the overlay. */
.lightbox__frame { display: flex; flex-direction: column; max-width: min(1080px, 100%); max-height: 100%;
  background: var(--c-surface); border-radius: var(--radius); box-shadow: var(--shadow-pop);
  overflow: hidden; cursor: default; }
.lightbox__img { max-width: 100%; min-height: 0; object-fit: contain;
  border-radius: 0; box-shadow: none; cursor: default; }
.lightbox__caption { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5); border-top: 1px solid var(--c-border); background: var(--c-surface); }
.lightbox__caption-text { min-width: 0; }
.lightbox__title { font-family: var(--font-heading); font-weight: 700; font-size: 1.125rem;
  color: var(--c-ink); margin: 0; max-width: none; }
.lightbox__sub { color: var(--text-muted); font-size: .9375rem; margin: 4px 0 0; max-width: none; }
.lightbox__close { width: 44px; height: 44px; flex: none;
  border: 1px solid var(--c-border); border-radius: 50%; background: var(--c-surface); color: var(--c-ink);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  transition: background var(--transition), border-color var(--transition); }
.lightbox__close:hover { background: var(--c-tint); border-color: var(--c-primary-strong); }
/* Used only when an image supplies no caption at all. */
.lightbox__close--bare { position: absolute; top: var(--sp-4); right: var(--sp-4); }

/* Step-through controls. Hidden via the `hidden` attribute unless the picture
   belongs to a declared gallery, so a single image is untouched by all of this.

   They are positioned against the overlay rather than the picture panel: the
   panel is only as wide as the photograph, so arrows inside it would sit on top
   of the image on a portrait scan and drift inwards on a narrow one. `cursor`
   is restated because the overlay itself is zoom-out. */
.lightbox__nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 1;
  width: 52px; height: 52px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--c-border); border-radius: 50%;
  background: var(--c-surface); color: var(--c-ink); cursor: pointer;
  box-shadow: var(--shadow-pop);
  transition: background var(--transition), border-color var(--transition); }
.lightbox__nav:hover { background: var(--c-tint); border-color: var(--c-primary-strong); }
.lightbox__nav--prev { left: var(--sp-4); }
.lightbox__nav--next { right: var(--sp-4); }

/* Position within the set. aria-hidden in the markup — it is a visual aid, and
   a screen reader gets the same information from the image's own caption. */
.lightbox__count { position: absolute; top: var(--sp-4); left: var(--sp-4); margin: 0;
  padding: 6px 12px; border-radius: 999px; font-size: .8125rem; font-weight: 600;
  background: var(--c-surface); color: var(--text-muted); border: 1px solid var(--c-border); }

/* Below roughly a phone's width the round buttons would overlap the picture, so
   they drop to the bottom corners where the caption bar already is. */
@media (max-width: 560px) {
  .lightbox__nav { top: auto; bottom: var(--sp-4); transform: none; width: 44px; height: 44px; }
  .lightbox__count { top: var(--sp-2); left: 50%; transform: translateX(-50%); }
}
html.lightbox-open, html.lightbox-open body { overflow: hidden; }

/* ---- Forms (full field set; maps to bteng.php form__ classes) ---------- */
.form__hint { color: var(--text-muted); font-size: .9rem; margin-top: var(--sp-1); }
.form__note { color: var(--text-muted); font-size: .85rem; margin-top: var(--sp-3); }
.form__row { display: grid; gap: var(--sp-4); }
@media (min-width: 560px) { .form__row--2 { grid-template-columns: 1fr 1fr; } }

.form__input, .form__select, textarea.form__input {
  width: 100%; padding: 12px 14px; background: var(--c-surface); color: var(--c-ink);
  border: 1px solid var(--c-border); border-radius: var(--radius); transition: border-color var(--transition), box-shadow var(--transition); }
.form__input::placeholder { color: var(--text-muted); opacity: .7; }
.form__input:focus, .form__select:focus { border-color: var(--c-primary); box-shadow: var(--ring); outline: none; }
textarea.form__input { min-height: 120px; resize: vertical; }

.form__select { appearance: none; -webkit-appearance: none; padding-right: 40px;
  background-repeat: no-repeat; background-position: right 14px center;
  /* A data-URI cannot read a custom property; %235f6469 must be kept in step
     with --c-muted by hand if that token ever changes again. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235f6469' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); }

.form__checkbox, .form__radio { width: 18px; height: 18px; accent-color: var(--c-primary-strong); }
.form__group--choice { display: flex; align-items: center; gap: var(--sp-3); }
.form__group--choice .form__label { margin: 0; font-weight: 400; }

/* File upload (CV on careers — Phase 5) */
.form__file { display: block; }
.form__file input[type="file"] { width: 100%; padding: 10px 12px; border: 1px dashed var(--c-border);
  border-radius: var(--radius); background: var(--c-tint); cursor: pointer; }
.form__file input[type="file"]::file-selector-button { font: inherit; margin-right: var(--sp-3);
  padding: 8px 14px; border: none; border-radius: var(--radius-sm);
  background: var(--c-primary-strong); color: #fff; cursor: pointer; }

/* Validation + Grav form messages */
.form__input[aria-invalid="true"], .form__group.has-error .form__input,
.form__group.has-error .form__select { border-color: var(--c-urgent); }
.form__errors { color: var(--c-urgent); font-size: .9rem; margin-top: var(--sp-2); }
.form-messages, .alert { border-radius: var(--radius); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4); }
.alert.success, .form-message.success { background: var(--c-tint); color: var(--c-primary-deep); }
.alert.error { background: var(--c-notice-err-bg); color: var(--c-urgent); }

/* Hide Grav honeypot anti-spam field */
.form__group input[name="honeypot"], .honeypot { position: absolute !important; left: -9999px !important; }

/* ---- Grav Form plugin — brand alignment (element-scoped so it wins) ----- */
form .form-field { margin-bottom: var(--sp-4); }
form .form-field > label, form label.form-label { display: block; font-weight: 500; margin-bottom: var(--sp-2); }
form input[type="text"], form input[type="email"], form input[type="tel"],
form input[type="password"], form input[type="number"], form input[type="url"],
form textarea, form select {
  width: 100%; padding: 12px 14px; font: inherit; color: var(--c-ink);
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius);
  transition: border-color var(--transition), box-shadow var(--transition); }
form textarea { min-height: 120px; resize: vertical; }
form input:focus, form textarea:focus, form select:focus { border-color: var(--c-primary); box-shadow: var(--ring); outline: none; }
form input[type="file"] { width: 100%; padding: 10px 12px; border: 1px dashed var(--c-border);
  border-radius: var(--radius); background: var(--c-tint); cursor: pointer; }
form input[type="file"]::file-selector-button { font: inherit; margin-right: var(--sp-3); padding: 8px 14px;
  border: none; border-radius: var(--radius-sm); background: var(--c-primary-strong); color: #fff; cursor: pointer; }
form .form-errors, form .form-field .error { color: var(--c-urgent); font-size: .9rem; margin-top: var(--sp-2); }
/* The form plugin puts the marker inside the label as <span class="required">*</span>,
   so the older wrapper-based rule below never matched it and the asterisk
   inherited the label colour. Both spellings are covered now. */
form label .required, form .form__label .required {
  color: var(--c-required); font-weight: 600; margin-left: 2px; }
form .required > label::after, form .form-field.required label::after { content: " *"; color: var(--c-required); }
form .buttons, form .form-buttons { margin-top: var(--sp-5); }
form button.button, form input.button, form .buttons button, form button[type="submit"] {
  display: inline-flex; align-items: center; gap: var(--sp-2); font-family: var(--font-heading); font-weight: 600;
  font-size: .9375rem; line-height: 1; padding: 14px 28px; border-radius: var(--radius); border: 1px solid transparent;
  background: var(--c-primary-strong); color: #fff; cursor: pointer; transition: background var(--transition); }
form button.button:hover, form input.button:hover, form button[type="submit"]:hover { background: var(--c-primary-deep); color: #fff; }
/* Turnstile renders itself; a little breathing room above it is all it needs. */
form .turnstile-container { margin-top: var(--sp-2); }

/* ---- Splash overlay (date-gated front-page takeover) -------------------
   Everything an editor can change in Admin → Pages → Splash Page arrives as an
   inline custom property on .splash (see partials/splash-body.html.twig). The
   values below are the DEFAULTS used whenever a field is left empty, so an
   untouched splash page keeps the original theme look.
     --splash-img-w     picture width      (Picture → Image size)
     --splash-bg        page background    (Page background → colour/gradient)
     --splash-bg-image  page background    (Page background → image)
     --splash-dim       black veil opacity (Page background → Darken)
     --splash-fg/-muted text colours       (Page background → Text colour)
     --splash-btn-bg/-fg button colours    (Enter-website button)                */
.splash { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center;
  padding: var(--sp-6); text-align: center;
  color: var(--splash-fg, #fff);
  background: var(--splash-bg, linear-gradient(160deg, var(--c-navy) 0%, var(--c-navy-deep) 100%)); }
/* Background image + darkening veil ride on pseudo-elements so they stack over
   the background colour but under the content. */
.splash::before { content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image: var(--splash-bg-image, none); background-size: cover; background-position: center; }
.splash::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  background: rgba(0, 0, 0, var(--splash-dim, 0)); }
.splash__inner { position: relative; z-index: 1; max-width: 720px; width: 100%; }
/* The picture: admin-set width, but never wider than the panel and never taller
   than 60% of the screen — so a big image can't push the button out of view. */
.splash__img { display: block; width: var(--splash-img-w, 440px); max-width: 100%;
  max-height: 60vh; height: auto; object-fit: contain;
  margin: 0 auto var(--sp-5); border-radius: var(--radius-lg); }
.splash__img--ph { display: flex; align-items: center; justify-content: center;
  width: min(var(--splash-img-w, 440px), 100%); aspect-ratio: 4 / 5; max-height: 40vh;
  background: var(--c-wash-light); color: var(--c-brand); }
.splash__heading { color: var(--splash-fg, #fff); font-size: clamp(1.6rem, 4vw, 2.4rem); margin-bottom: var(--sp-3); }
.splash__subtitle { color: var(--splash-fg-muted, var(--c-on-dark-muted)); font-family: var(--font-heading);
  font-size: clamp(1rem, 2.2vw, 1.25rem); margin-bottom: var(--sp-4); }
.splash__msg { color: var(--splash-fg-muted, var(--c-on-dark)); margin-bottom: var(--sp-6); }
.splash__msg p { margin-inline: auto; }
/* Dark-text variant, for pale custom backgrounds. */
.splash--dark-text { --splash-fg: var(--c-ink); --splash-fg-muted: var(--c-muted); }
.splash--dark-text .splash__img--ph { background: rgba(0,0,0,.06); color: var(--c-primary-deep); }
/* Enter button — defaults to the white .btn--light look until colours are set. */
.splash__enter { background: var(--splash-btn-bg, #fff); color: var(--splash-btn-fg, var(--c-primary-deep));
  border-color: var(--splash-btn-bg, transparent); text-decoration: none; }
.splash__enter:hover { background: var(--splash-btn-bg, var(--c-tint)); color: var(--splash-btn-fg, var(--c-primary-deep));
  filter: brightness(.94); }
/* Static preview shown on the splash page's own route (not the takeover). */
.splash--preview { position: static; inset: auto; z-index: auto; min-height: 60vh;
  border-radius: var(--radius-lg); overflow: hidden; }
.splash--preview::before, .splash--preview::after { border-radius: var(--radius-lg); }

/* ---- Cookie consent banner (PDPA) ------------------------------------- */
.cookie-consent { position: fixed; left: var(--sp-4); right: var(--sp-4); bottom: var(--sp-4); z-index: 250;
  max-width: 1080px; margin-inline: auto; display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap; background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-pop); padding: var(--sp-4) var(--sp-5); }
.cookie-consent__text { margin: 0; font-size: .95rem; color: var(--text-muted); max-width: 72ch; }
.cookie-consent__text a { color: var(--c-primary-deep); text-decoration: underline; }
.cookie-consent__actions { display: flex; gap: var(--sp-3); flex: none; }
@media (max-width: 560px) { .cookie-consent { flex-direction: column; align-items: stretch; } }
