/* ==========================================================================
   Optrical — section renderers
   One block per section type in app/sections/. Depends on tokens.css + base.css.
   ========================================================================== */

/* --- page_banner ---------------------------------------------------------- */

.banner {
  position: relative;
  display: flex;
  align-items: center;
  min-height: var(--banner-min-h);
  background: var(--c-navy) center / cover no-repeat;
  isolation: isolate;
}
.banner::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--c-navy);
  opacity: 0.5;
  z-index: -1;
}
.banner__title {
  font-family: var(--f-body);
  font-size: var(--fs-banner);
  font-weight: 600;
  line-height: 1.2;
  color: #fff;
  margin: 0;
}
@media (max-width: 767px) {
  .banner { min-height: 190px; }
  .banner__title { font-size: 30px; }
}

/* --- hero_slider ---------------------------------------------------------- */

.hero {
  position: relative;
  overflow: hidden;
  background: var(--c-navy);
}
.hero__track { display: grid; }
.hero__slide {
  grid-area: 1 / 1;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 500px;
  background: center / cover no-repeat;
  opacity: 0;
  transition: opacity 0.8s var(--ease);
  pointer-events: none;
}
.hero__slide[data-active="true"] { opacity: 1; pointer-events: auto; }
.hero__slide[data-overlay="true"]::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
}
.hero__caption {
  position: relative;
  max-width: 760px;
  padding: var(--sp-9) var(--sp-7);
  text-align: center;
  font-family: var(--f-head);
  font-size: var(--fs-hero);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 1px;
  color: #fff;
  text-shadow: var(--text-shadow-hero);
}
@media (max-width: 900px) {
  .hero__slide { min-height: 320px; }
  .hero__caption { font-size: 29px; padding: var(--sp-6) var(--sp-4); }
}

.hero__dots {
  position: absolute;
  left: 0; right: 0; bottom: var(--sp-2);
  display: flex;
  justify-content: center;
  gap: 8px;
}
.hero__dot {
  width: 16px; height: 16px;
  padding: 0;
  border: 2px solid rgba(255, 255, 255, 0.5);
  border-radius: 50%;
  background: transparent;
  transition: background-color var(--transition), border-color var(--transition);
}
.hero__dot[aria-current="true"],
.hero__dot:hover { background: #fff; border-color: #fff; }

.hero__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px; height: 40px;
  border: 0;
  background: transparent;
  color: rgba(255, 255, 255, 0.8);
  font-size: 28px;
  line-height: 1;
}
.hero__arrow:hover { color: #fff; }
.hero__arrow--prev { left: 15px; }
.hero__arrow--next { right: 15px; }
@media (max-width: 900px) { .hero__arrow { display: none; } }

/* --- split ---------------------------------------------------------------- */

.split {
  display: grid;
  gap: var(--sp-7);
  align-items: center;
}
/*
 * Two columns from 768px up. Both children are vertically centred against
 * each other by `align-items: center` on .split above — the figure is a fixed
 * box and the body is whatever height its copy needs, so whichever is shorter
 * sits centred rather than hanging at the top of the row.
 *
 * Below this breakpoint the split is a single column and alignment does not
 * apply.
 */
@media (min-width: 768px) {
  .split { grid-template-columns: 1fr 1fr; }
  .split[data-media="left"] .split__body  { order: 2; }
  .split[data-media="left"] .split__media { order: 1; }
}
.split__body > :last-child { margin-bottom: 0; }
.split__cta { margin-top: var(--sp-5); }

/*
 * The figure is a fixed box — the same proportions a missing-image placeholder
 * occupies — so section heights stay consistent whether or not a file has
 * landed, and every split across the site presents the same size frame.
 *
 * The image inside is NOT scaled by default: it renders at its own pixel size,
 * anchored top-left, and anything past the edge is clipped by the figure. That
 * keeps screenshot text legible at 1:1 rather than shrunk to fit.
 *
 * The figure keeps its own height rather than stretching to match the copy
 * beside it, and sits vertically centred in the row — `align-self: center`
 * rather than `stretch` (which would distort the aspect-ratio box) or
 * `start` (which pinned it to the top whenever the copy ran taller).
 */
.split__media {
  position: relative;
  align-self: center;
  margin: 0;
  aspect-ratio: 567 / 378;
  overflow: hidden;
  background: var(--c-panel);
}
.split__media > .img-ph { width: 100%; height: 100%; }

/* Opt-in: fill the box, cropping the overflow. */
.split__media > .media-img--cover {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
/* Whole image visible, letterboxed against the figure background. */
.split__media > .media-img--contain {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: left top;
}
/*
 * Default. No scaling whatsoever — the image renders at its own pixel size,
 * centred in the figure, and anything past the edges is clipped by the
 * figure's overflow:hidden.
 *
 * Centring is deliberate: a mismatched image then shows an even margin (too
 * small) or loses all four edges equally (too large), which reads immediately
 * as "wrong size" rather than looking like an intentional top-left crop. That
 * makes the artwork problem visible so a correctly sized file can replace it.
 *
 * Absolute + translate rather than object-position, because at intrinsic size
 * the img is a block in normal flow — object-position only moves content
 * inside a box that has been given a size, which this one has not.
 */
.split__media > .media-img--actual {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: block;
  width: auto;
  height: auto;
  max-width: none;      /* beats the global img { max-width: 100% } */
  object-fit: none;
}
.split__media::after { display: none; }
.split__media::after {
  content: "";
  position: absolute;
  right: -18px;
  bottom: -18px;
  width: 120px; height: 90px;
  z-index: -1;
  background-image: radial-gradient(var(--c-border) 1.5px, transparent 1.5px);
  background-size: 12px 12px;
}
@media (max-width: 767px) { .split__media::after { display: none; } }

/*
 * Zoomable figures. The button is added by site.js and covers the figure, so
 * the whole image is the hit area. Only the class is styled here — without
 * JS the class never appears and none of this applies.
 */
.split__media--zoomable { cursor: zoom-in; }
.split__media-zoom {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: zoom-in;
}
/* A magnifier badge, so it reads as openable rather than as a dead image. */
.split__media-zoom::after {
  content: "⤢";
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(2, 49, 81, 0.78);
  color: #fff;
  font-size: 15px;
  line-height: 1;
  opacity: 0;
  transition: opacity var(--transition);
}
.split__media--zoomable:hover .split__media-zoom::after,
.split__media-zoom:focus-visible::after { opacity: 1; }
.split__media-zoom:focus-visible {
  outline: 2px solid var(--c-orange);
  outline-offset: -2px;
}
/* Touch has no hover, so the badge is always visible there. */
@media (hover: none) {
  .split__media-zoom::after { opacity: 1; }
}

/* --- lightbox -------------------------------------------------------------- */

/*
 * The dialog is a flex column: image area first, caption pinned beneath it.
 * Sizing the panel to its content (fit-content) rather than a fixed width
 * stops a small image from sitting in a large empty box, while the max-*
 * values keep a big one inside the viewport.
 */
.lightbox {
  position: relative;         /* positioning context for the close button */
  max-width: min(94vw, 1400px);
  max-height: 92vh;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: var(--c-surface);
  overflow: hidden;           /* the image area scrolls, not the panel */
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);
  /* Centre the panel itself — dialog defaults to margin:auto only when the
     UA stylesheet is left alone, and max-height above can defeat it. */
  margin: auto;
}
.lightbox::backdrop { background: rgba(0, 0, 0, 0.72); }

/*
 * Lock the page behind the modal. Without this the background scrolls under
 * the dialog on touch, and closing leaves the reader somewhere they never
 * chose. `position: fixed` with a negative top offset (set in JS) holds the
 * exact scroll position rather than snapping to the top, which is what a bare
 * `overflow: hidden` does on iOS.
 */
body.has-lightbox {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden;
}

.lightbox__body {
  display: flex;
  flex-direction: column;
  max-height: 92vh;
}

/*
 * The image sits in a padded, centred area. It scales DOWN to fit both axes
 * but is never enlarged past 1:1 — the point of the lightbox is to read
 * detail at native resolution, not to blow a small image up.
 */
.lightbox__figure {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: var(--sp-6);
  min-height: 0;              /* lets the flex child shrink instead of overflow */
  overflow: auto;
  background: var(--c-panel);
}
.lightbox__img {
  display: block;
  max-width: 100%;
  max-height: calc(92vh - 140px);   /* leaves room for padding + caption */
  width: auto;
  height: auto;
  object-fit: contain;
}
.lightbox__cap {
  flex: none;
  margin: 0;
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--c-border);
  font-size: 14px;
  line-height: 1.6;
  color: var(--c-text-muted);
  background: var(--c-surface);
}
/*
 * Fixed to the panel corner rather than floated into the content flow, so it
 * never sits on top of the image or pushes it off-centre.
 */
.lightbox__close {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 50%;
  background: rgba(2, 49, 81, 0.82);
  color: #fff;
  font-size: 21px;
  line-height: 1;
  cursor: pointer;
}
@media (max-width: 640px) {
  .lightbox { max-width: 96vw; }
  .lightbox__figure { padding: var(--sp-4); }
  .lightbox__img { max-height: calc(92vh - 120px); }
}
.lightbox__close:hover { background: var(--c-navy); }
.lightbox__close:focus-visible {
  outline: 2px solid var(--c-orange);
  outline-offset: 2px;
}

/* --- rich_list ------------------------------------------------------------ */

/*
 * The list and the intro above it form one column: same width, same centred
 * position, so their left edges line up.
 *
 * Left at full container width the items ran to ~150 characters — the same
 * readability problem as unconstrained prose — while the intro was already
 * capped, so the two blocks looked unrelated.
 *
 * 70ch rather than prose's 62ch because each line carries a bold lead plus its
 * explanation. The intro is widened to match, scoped to this section only.
 */
.rich-list,
.section .rich-list-intro {
  max-width: 70ch;
  margin-inline: auto;
}
.rich-list {
  list-style: none;
  padding: 0;
  font-family: var(--f-head);
  line-height: var(--lh-prose);
}
.rich-list li {
  position: relative;
  padding-left: 1.4em;
  margin-bottom: var(--sp-2);
}
.rich-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--lh-prose) / 2 - 3px);
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--c-orange);
}
.rich-list strong { color: var(--c-navy); }

/* --- value_cards ---------------------------------------------------------- */

.value-card {
  border: 1px solid var(--c-border);
  background: var(--c-surface);
  padding: var(--sp-6);
}
/*
 * The icon slot takes either an inline SVG (sized in px, inherits the accent
 * colour) or a literal glyph such as an emoji. The wrapper carries only the
 * spacing so the two cases stay the same height in a row of cards.
 */
.value-card__icon {
  display: flex;
  align-items: center;
  min-height: 44px;
  color: var(--c-orange);
  margin-bottom: var(--sp-4);
}
.value-card__icon > svg { width: 44px; height: 44px; }
/*
 * Emoji need the system colour fonts named explicitly — a web font earlier in
 * the chain can otherwise suppress the glyph on Android Chrome and some iOS
 * WebViews.
 */
.value-card__glyph {
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  font-size: 34px;
  line-height: 1;
}
.value-card__title {
  font-family: var(--f-head);
  font-size: var(--fs-card-title);
  font-weight: 600;
  color: var(--c-navy);
  margin: 0 0 var(--sp-3);
}

/* --- team_grid ------------------------------------------------------------ */

.team-card {
  border: 1px solid var(--c-border);
  background: var(--c-surface);
  padding: var(--sp-6) var(--sp-4);
  text-align: center;
}
.team-card__photo {
  width: 160px; height: 160px;
  margin: 0 auto var(--sp-4);
  border-radius: 50%;
  overflow: hidden;
}
.team-card__photo img { width: 100%; height: 100%; object-fit: cover; }
.team-card__name {
  font-family: var(--f-head);
  font-size: 20px;
  font-weight: 600;
  color: var(--c-navy);
  margin: 0 0 var(--sp-1);
}
.team-card__role {
  font-weight: 600;
  color: var(--c-navy);
  margin: 0 0 var(--sp-3);
}
.team-card__bio {
  font-size: 15px;
  line-height: var(--lh-card);
  text-align: left;
}

/* --- cta ------------------------------------------------------------------ */

.cta { text-align: center; }
.cta__contact {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-4) var(--sp-6);
  margin-top: var(--sp-5);
}
.cta__contact span { display: inline-flex; align-items: center; gap: 8px; }

/* --- steps ---------------------------------------------------------------- */

.steps {
  list-style: none;
  counter-reset: step;
  margin: var(--sp-6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-6);
}
@media (min-width: 768px) { .steps { grid-template-columns: repeat(3, 1fr); } }
.step { position: relative; padding-top: var(--sp-5); border-top: 2px solid var(--c-border); }
.step__num {
  display: block;
  font-family: var(--f-body);
  font-size: 13px;
  letter-spacing: 0.12em;
  color: var(--c-orange);
  margin-bottom: var(--sp-2);
}
.step__title {
  font-family: var(--f-head);
  font-size: var(--fs-card-title);
  font-weight: 600;
  color: var(--c-navy);
  margin: 0 0 var(--sp-2);
}
.step__body { font-size: 15px; line-height: var(--lh-card); margin: 0; }

/* --- plans ---------------------------------------------------------------- */

.plan {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--c-border);
  background: var(--c-surface);
  padding: var(--sp-6) var(--sp-5);
}
.plan--featured { border-color: var(--c-navy); box-shadow: var(--shadow-card); }
.plan__name {
  font-family: var(--f-head);
  font-size: 20px;
  font-weight: 600;
  color: var(--c-navy);
  margin: 0 0 var(--sp-2);
}
.plan__blurb { font-size: 15px; line-height: var(--lh-card); margin: 0 0 var(--sp-4); }
.plan__features {
  list-style: none;
  margin: 0 0 var(--sp-5);
  padding: 0;
  font-size: 15px;
  line-height: 1.6;
  flex: 1;
}
.plan__features li { position: relative; padding-left: 1.3em; margin-bottom: var(--sp-2); }
.plan__features li::before {
  content: "";
  position: absolute; left: 0; top: 0.55em;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--c-orange);
}
.plan__cta { align-self: flex-start; }
.plans__note { margin-top: var(--sp-6); font-size: 15px; }

/* --- contact -------------------------------------------------------------- */

.contact-grid { display: grid; gap: var(--sp-7); margin-top: var(--sp-6); }
@media (min-width: 768px) { .contact-grid { grid-template-columns: 1.4fr 1fr; } }

.contact-details__item { margin-bottom: var(--sp-5); }
.contact-details__label {
  font-family: var(--f-body);
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-eyebrow);
  margin: 0 0 4px;
}
.contact-details__value { margin: 0; font-size: var(--fs-body); line-height: 1.6; }

.form__row { display: flex; flex-direction: column; gap: 5px; margin: 0 0 var(--sp-3); }
.form__row label { font-size: 15px; color: var(--c-navy); font-weight: 600; }
.form__row input, .form__row textarea, .form__row select {
  width: 100%;
  padding: 9px 13px;
  border: 1px solid var(--c-field-border);
  border-radius: var(--radius-btn);
  background: #fff;
  font-family: inherit;
  font-size: var(--fs-body);
  line-height: 1.5;
  transition: border-color var(--transition);
}

/*
 * Name and email share a row on anything wider than a phone. Below 560px the
 * grid collapses to one column and they stack, which is what a thumb wants.
 */
.form__pair { display: grid; gap: 0 var(--sp-4); }
@media (min-width: 560px) { .form__pair { grid-template-columns: 1fr 1fr; } }

/*
 * The native select arrow is inconsistent across platforms and sits oddly
 * against the bordered inputs, so it is replaced with an inline SVG chevron.
 * Extra right padding keeps long option text clear of it.
 */
.form__row select {
  appearance: none;
  padding-right: 38px;
  background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23023151' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  cursor: pointer;
}
.form__submit { margin: var(--sp-4) 0 0; }
.form__row input:focus, .form__row textarea:focus, .form__row select:focus {
  border-color: var(--c-navy);
  outline: 2px solid rgba(2, 49, 81, 0.15);
  outline-offset: 0;
}
.form__row textarea { resize: vertical; }
.form__err { font-size: 14px; color: var(--c-error); }
.form__trap { position: absolute; left: -9999px; }

.notice {
  padding: var(--sp-3) var(--sp-4);
  border-left: 4px solid;
  margin: 0 0 var(--sp-5);
  font-size: 15px;
  line-height: 1.6;
}
.notice--ok   { border-color: var(--c-ok); background: var(--c-ok-bg); }
.notice--warn { border-color: var(--c-orange); background: var(--c-notice-soon-bg); }

/* --- Concept card icons --------------------------------------------------- */

.card__icon {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  padding: var(--sp-5) var(--sp-2) var(--sp-1);
}
.card-icon { width: 40px; height: 40px; color: var(--c-orange); }
.section--navy .card-icon { color: var(--c-orange); }

/* --- Announcement band ---------------------------------------------------- */

/*
 * Three kinds: soon (default), info and warn. Each states its own accent and
 * surface rather than inheriting one from the base rule, so adding a fourth
 * does not depend on knowing which kind the base happens to look like.
 */
.notice-band {
  position: relative;
  padding: var(--sp-7) var(--sp-6);
  border: 1px solid var(--c-border);
  border-left: 4px solid var(--c-notice-soon);
  background: var(--c-notice-soon-bg);
  box-shadow: var(--shadow-card);
}
.notice-band--soon { border-left-color: var(--c-notice-soon); background: var(--c-notice-soon-bg); }
.notice-band--info { border-left-color: var(--c-notice-info); background: var(--c-notice-info-bg); }
.notice-band--warn { border-left-color: var(--c-notice-warn); background: var(--c-notice-warn-bg); }

.notice-band__label {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--f-body);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-orange-deep);
  margin: 0 0 var(--sp-3);
}
.notice-band--soon .notice-band__label { color: var(--c-orange-deep); }
.notice-band--info .notice-band__label { color: var(--c-notice-info); }
.notice-band--warn .notice-band__label { color: var(--c-notice-warn); }

.notice-band__dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: currentColor;   /* inherits the label colour, so it matches the kind */
  box-shadow: 0 0 0 0 rgba(242, 100, 41, 0.55);
  animation: notice-pulse 2.4s infinite;
}
@keyframes notice-pulse {
  70%  { box-shadow: 0 0 0 11px rgba(242, 100, 41, 0); }
  100% { box-shadow: 0 0 0 0 rgba(242, 100, 41, 0); }
}
@media (prefers-reduced-motion: reduce) { .notice-band__dot { animation: none; } }

.notice-band__heading {
  font-family: var(--f-head);
  font-size: 30px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--c-navy);
  margin: 0 0 var(--sp-4);
}
.notice-band__body { font-family: var(--f-head); line-height: var(--lh-prose); max-width: 62ch; }
.notice-band__cta  { margin: var(--sp-5) 0 0; }

@media (max-width: 767px) {
  .notice-band { padding: var(--sp-5) var(--sp-4); }
  .notice-band__heading { font-size: 23px; }
}

/* --- CAPTCHA ---------------------------------------------------------------
   Shared by the public contact form and the admin sign-in. The reload button
   sits beside the image rather than under it, so the pair reads as one
   control. */

.captcha {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 6px 0 8px;
}
.captcha { flex-wrap: wrap; }
.captcha__img {
  display: block;
  width: 150px;
  height: 48px;
  border: 1px solid var(--c-rule);
  border-radius: 6px;
  background: var(--c-panel-alt);
}
.captcha__reload {
  flex: none;
  width: 32px;
  height: 32px;
  border: 1px solid var(--c-rule);
  border-radius: 6px;
  background: var(--c-surface);
  color: var(--c-navy);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.captcha__reload:hover,
.captcha__reload:focus-visible {
  background: var(--c-panel-alt);
  border-color: var(--c-navy);
}
.captcha input[name="captcha"] {
  /* Sized to five characters and no wider, so it reads as belonging to the
     image rather than as another full-width field. */
  width: 8em;
  flex: none;
  letter-spacing: .16em;
  font-size: 17px;
  text-transform: uppercase;
}
.captcha input[name="captcha"]::placeholder {
  letter-spacing: 0;
  text-transform: none;
  font-size: 14px;
}
.form__hint {
  display: block;
  margin-top: 4px;
  color: var(--c-steel);
  font-size: 13px;
}
