/* ============================================================
   FONTS — self-hosted (no request to Google: faster, and visitors'
   IPs aren't sent to a third party). Latin subset only, which covers
   every Portuguese character plus €, º and typographic dashes/quotes.
   Inter is one variable file serving all three weights (downloaded
   once). Licences (SIL OFL 1.1) sit next to the files in /assets/fonts/.
   Filenames carry the upstream version: an updated font gets a new
   filename, since these URLs are cached for a year and not versioned.
   ============================================================ */
@font-face {
  font-family: "Barlow Condensed"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("/assets/fonts/barlow-condensed-v13-600-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Barlow Condensed"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("/assets/fonts/barlow-condensed-v13-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Barlow Condensed"; font-style: normal; font-weight: 800; font-display: swap;
  src: url("/assets/fonts/barlow-condensed-v13-800-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/assets/fonts/inter-v20-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("/assets/fonts/inter-v20-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("/assets/fonts/inter-v20-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("/assets/fonts/jetbrains-mono-v24-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ============================================================
   DESIGN TOKENS
   Palette: bright editorial ground (white / light grey), black
   condensed headlines, red as the single accent color.
   ============================================================ */
:root {
  --bg: #FFFFFF;
  --bg-alt: #F4F4F5;
  --surface: #FFFFFF;
  --surface-2: #ECECEE;
  --ink: #141414;
  --ink-soft: #333335; /* long-form body copy — softer than --ink, darker than --muted */
  --muted: #6B6B70;
  --line: #E4E4E7;

  --red: #E1261C;
  --red-dim: #B81E16;
  --success: #2E7D32;
  --gold: #B8912A; /* Quadro de Honra only — a deliberate one-off exception to red-as-the-only-accent */
  --badge-navy: #083165; /* exact background colour baked into the rank-insignia PNGs */

  --footer-bg: #3A3A3D;
  --footer-ink: #F2F1EA;
  --footer-line: #4C4C50;
  --footer-muted: #9A9A9E;

  --font-display: "Barlow Condensed", sans-serif;
  --font-body: "Inter", sans-serif;
  --font-mono: "JetBrains Mono", monospace;

  --radius: 4px;
  --wrap: 1180px;
  --wrap-narrow: 760px;
}

@media (prefers-reduced-motion: no-preference) {
  :root { --transition-base: 220ms cubic-bezier(.4,0,.2,1); --transition-slide: 600ms ease; }
}
@media (prefers-reduced-motion: reduce) {
  :root { --transition-base: 0ms; --transition-slide: 0ms; }
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  line-height: 1.05;
  margin: 0 0 0.4em;
  color: var(--ink);
}
h1 { font-size: clamp(2.6rem, 6vw, 4.6rem); font-weight: 800; }
h2 { font-size: clamp(1.7rem, 3.2vw, 2.4rem); }
h3 { font-size: 1.15rem; text-transform: none; letter-spacing: 0; }

p { margin: 0 0 1em; }
a { color: var(--red); text-decoration: none; }
a:hover { text-decoration: underline; }
/* height:auto is the required companion to max-width:100% now that images carry
   intrinsic width/height attributes — without it, an image shrunk by max-width
   keeps the attribute's height and distorts. Rules that genuinely need a fixed
   height (.card__media img, .brand__mark, hero media) set it explicitly and win
   on specificity. */
img { max-width: 100%; height: auto; display: block; }

:focus-visible {
  outline: 3px solid var(--red);
  outline-offset: 2px;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: -999px; top: 0; z-index: 1000;
  background: var(--red); color: #fff; padding: 10px 16px;
  font-weight: 600;
}
.skip-link:focus { left: 0; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 24px; }
.wrap--narrow { max-width: var(--wrap-narrow); }

.eyebrow {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 0.78rem;
  color: var(--red);
  margin: 0 0 0.6em;
}
.muted { color: var(--muted); }
.small { font-size: 0.85rem; }
/* No max-width here — page-header ledes should fill the same margins as
   everything else on the page (the .wrap they sit in already caps line
   length site-wide), not stop short and leave the right side of the
   heading block looking oddly narrower than the content below it. */
.lede { font-size: 1.15rem; color: var(--muted); }

/* ---- Buttons ---- */
.btn {
  display: inline-block;
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
  font-size: 1rem;
  padding: 14px 28px;
  border-radius: var(--radius);
  border: 2px solid transparent;
  transition: transform var(--transition-base), background var(--transition-base), color var(--transition-base);
}
.btn:hover { text-decoration: none; transform: translateY(-2px); }
.btn--sm { font-weight: 600; font-size: 0.8rem; padding: 9px 20px; }
.btn--primary { background: var(--red); color: #fff; }
.btn--primary:hover { background: var(--red-dim); }
.btn--ghost { background: transparent; border-color: var(--ink); color: var(--ink); }
.btn--ghost:hover { background: var(--ink); color: #fff; }

.link-arrow {
  display: inline-block;
  margin-top: 28px;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--red);
}

/* ---- Header / nav ---- */
/* Sticky so it stays visible on scroll, with the same dark background as the
   footer (bookending the page) — every color below that was tuned for the
   header's old white background is flipped to the footer's --footer-* tokens
   so it stays readable against the dark bar. The two dropdown panels (desktop
   floating submenu, mobile slide-down panel) deliberately stay light/white —
   overridden back in their own rules — since a light popover under a dark bar
   is the more common, easier-to-read pattern than an all-dark menu. */
.site-header {
  background: var(--footer-bg); border-bottom: 1px solid var(--footer-line);
  position: sticky; top: 0; z-index: 100;
}
.site-header__inner {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  column-gap: 24px; padding: 16px 24px;
}
.brand { display: flex; align-items: center; gap: 10px; color: var(--footer-ink); justify-self: start; }
.brand:hover { text-decoration: none; }
.brand__mark {
  height: 64px; width: auto;
  flex-shrink: 0;
}
.brand__text {
  font-family: var(--font-display); font-weight: 800; font-size: 1.5rem;
  text-transform: uppercase; letter-spacing: 0.03em; line-height: 1;
  display: flex; flex-direction: column;
}
.brand__sub {
  font-family: var(--font-mono); font-size: 0.6rem; font-weight: 500;
  text-transform: none; letter-spacing: 0.02em; color: var(--muted);
}
.brand__lines {
  display: flex; flex-direction: column; justify-content: space-between;
  height: 64px; /* matches .brand__mark so the two lines top/bottom-align to the logo */
}
.brand__line {
  font-family: var(--font-display); font-weight: 800; font-size: 1.7rem;
  text-transform: uppercase; white-space: nowrap; line-height: 1;
}
/* No letter-spacing tricks: "Bombeiros" (9 letters) and "Valongo" (7)
   are matched to the same ink width by scaling VALONGO's font-size up
   instead, measured from each word's natural (normal-spacing) width. */
.brand__line--black { color: var(--footer-ink); }
.brand__line--red { color: var(--red); font-size: 2.091rem; position: relative; top: -4px; }
.nav { justify-self: center; }
.header-aside { justify-self: end; display: flex; align-items: center; gap: 20px; }
.social-links { display: flex; align-items: center; gap: 14px; }
.social-links a { color: var(--ink); display: flex; }
.social-links a:hover { color: var(--red); }
.social-links svg { width: auto; height: 18px; fill: currentColor; }
/* Emergency contact number — kept red and legible at every width, not just
   on hover, since this is the number people call when something's on fire. */
.phone-link {
  display: flex; align-items: center; gap: 7px; color: var(--red);
  font-family: var(--font-mono); font-weight: 700; font-size: 1.1rem; white-space: nowrap;
}
.phone-link:hover { text-decoration: none; }
.phone-link svg { width: 20px; height: 20px; fill: currentColor; flex-shrink: 0; }
.nav__menu {
  list-style: none; display: flex; gap: 28px; margin: 0; padding: 0;
  font-family: var(--font-mono); font-size: 0.85rem; text-transform: uppercase;
  letter-spacing: 0.05em; align-items: center;
}
.nav__menu a { color: var(--footer-ink); }
.nav__menu a:hover { color: var(--red); text-decoration: none; }
.nav__toggle { display: none; }

.nav__item--dropdown { position: relative; }
.nav__dropdown-toggle {
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  font: inherit; text-transform: inherit; letter-spacing: inherit; color: var(--footer-ink);
  display: inline-flex; align-items: center; gap: 4px;
}
.nav__dropdown-toggle:hover { color: var(--red); }
.nav__caret { width: 12px; height: 12px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform var(--transition-base); }
.nav__dropdown-toggle[aria-expanded="true"] .nav__caret { transform: rotate(180deg); }
.nav__submenu {
  list-style: none; margin: 14px 0 0; padding: 8px 0; position: absolute; top: 100%; left: 0;
  min-width: 230px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 12px 24px rgba(20,20,20,0.12);
  display: none; z-index: 60;
}
.nav__item--dropdown.is-open .nav__submenu { display: block; }
.nav__submenu a {
  display: block; padding: 10px 18px; text-transform: none; letter-spacing: 0;
  font-family: var(--font-body); font-size: 0.9rem; white-space: nowrap;
  /* Overrides .nav__menu a's footer-ink: the submenu panel itself is always
     light (white on desktop, --bg-alt on the mobile accordion), so its links
     need dark text in both contexts — unlike the dark bar around them. */
  color: var(--ink);
}
.nav__submenu a:hover { background: var(--bg-alt); }

/* ---- Hero ---- */
/* min-height:78vh alone grows without limit on a tall/portrait viewport
   (a portrait monitor, a narrow-but-tall window) — the hero can end up
   1500px+ tall, mostly empty space around the vertically-centered text.
   A separate max-height doesn't fix this: per spec, min-height always wins
   when the two conflict, so max-height gets silently ignored whenever it's
   smaller than 78vh. height:min(...) picks whichever value is actually
   smaller, which is the real fix — 78vh on typical short screens, 820px
   as a hard ceiling on tall ones. */
.hero-carousel {
  position: relative; overflow: hidden;
  height: min(78vh, 820px);
  /* Floor beneath the vh-based height above: the text stack (padding +
     eyebrow + 2-line title + 2-line lede + button) plus room for the
     controls bar below it needs ~525px in the worst case — the title's
     6vw clamp grows it from ~429px on a narrow phone up to ~509px once
     the viewport is wide enough to hit the clamp's 4.6rem ceiling. Below
     this floor, the flex-centered content in .hero-slide overflows top
     and bottom and the button ends up under the controls. min-height
     always wins over the height formula when it's larger, so this only
     kicks in on short viewports and never fights the max-height cap on
     tall ones. */
  min-height: 540px;
  background: var(--bg);
}
.hero-slide {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity var(--transition-slide, 600ms ease);
}
.hero-slide.is-active { opacity: 1; pointer-events: auto; z-index: 1; }
/* Right-side panel — diagonal stripe background (or video/image, clipped
   to the same shape), exactly as it originally was. */
.hero-slide__media {
  position: absolute; top: 0; right: 0; bottom: 0;
  /* Text keeps its own left position/max-width (.hero__title etc.) — this
     just pulls the media panel's boundary closer to it (was 62%/860px) so
     the empty gap between text and video/image shrinks and media gets the
     freed-up width, without moving the text column itself. */
  left: max(58%, 720px); overflow: hidden;
  background:
    linear-gradient(100deg, var(--bg) 0%, transparent 12%),
    repeating-linear-gradient(135deg, var(--red) 0 3px, var(--red-dim) 3px 6px);
  /* Fixed 90px lean, not the original 15%-of-box-width: with the new left
     panel's box being a different width than this one, "15% of each box's
     own width" produces two different absolute slopes — they'd only look
     parallel by coincidence at whatever width made both boxes equally
     wide. A shared fixed-px lean is the only way to guarantee the same
     angle regardless of how wide either box actually is. */
  clip-path: polygon(90px 0, 100% 0, 100% 100%, 0% 100%);
  opacity: 0.94;
}
.hero-slide__video, .hero-slide__image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Mirrored stripe panel on the left, diagonal edge parallel to the right
   panel's — grows with .wrap's own centering margin on wide viewports and
   shrinks to nothing on narrower ones, so it can never reach far enough
   right to touch the text: the boundary is derived from the same "how far
   left can text go" formula .hero-slide__inner's centering uses, minus a
   40px safety margin, with the same 90px top/bottom lean as the right
   panel so the two cuts read as genuinely parallel. */
.hero-slide::before {
  content: "";
  position: absolute; top: 0; left: 0; bottom: 0;
  width: max(0px, calc((100vw - 1180px) / 2 + 24px - 40px));
  background: repeating-linear-gradient(135deg, var(--red) 0 3px, var(--red-dim) 3px 6px);
  clip-path: polygon(0 0, 100% 0, calc(100% - 90px) 100%, 0 100%);
  opacity: 0.94;
}
.hero-slide__scrim { position: absolute; inset: 0; background: linear-gradient(0deg, var(--bg) 0%, transparent 22%); }
/* width:100% is required here: .wrap's margin:0 auto disables flexbox's
   default cross-axis stretch (per spec, auto margins on the cross axis
   opt an item out of stretch), so without an explicit width this shrinks
   to fit each slide's own text instead of the row — different title
   lengths per slide then centered to different widths, producing a
   different left edge per slide instead of a consistent one. */
.hero-slide__inner { position: relative; width: 100%; padding-top: 80px; padding-bottom: 60px; }
/* Cap each text block so a long admin entry never blows up the slide: eyebrow
   to 1 line, title/summary to 2 — anything past that ellipses. min-height is
   the other half of this: it reserves that same space (in em, so it scales
   with each element's own font-size) even when a slide's actual text is
   shorter or its lede is empty, so every slide occupies the same block
   height and the CTA button doesn't jump up/down as the carousel advances
   between slides with different amounts of text. */
.hero-slide__inner .eyebrow {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-height: 1.6em; /* 1 line at this element's inherited line-height: 1.6 */
}
/* Carries the size/weight that the h1 element rule used to supply — the slide
   headline is an h2 now (the page's single h1 is the sr-only one at the top of
   home.php), so the visual scale has to come from the class instead. */
.hero__title {
  max-width: 620px;
  font-size: clamp(2.6rem, 6vw, 4.6rem); font-weight: 800;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  min-height: 2.1em; /* 2 lines at the h1/h2/h3 line-height: 1.05 */
}
.hero__lede {
  font-size: 1.2rem; color: var(--muted); max-width: 540px; margin-bottom: 2em;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  min-height: 3.2em; /* 2 lines at the inherited body line-height: 1.6 */
}
.hero__actions { display: flex; gap: 16px; flex-wrap: wrap; }
/* A longer cta_label wrapping to 2 lines was the last source of height
   difference between slides (109px vs 83px) — the button grows wider
   instead, which is the normal way a button handles longer text. */
.hero__actions .btn { white-space: nowrap; }

/* ---- Carousel controls ---- */
/* Positioned between the two side panels' actual edges (not just viewport-
   center) since they're different widths — reuses the same boundary
   formulas as .hero-slide::before (left) and .hero-slide__media (right)
   so this tracks them exactly at any viewport width. Overrides the .wrap
   class already on this element in the markup: margin/max-width/padding
   are reset here since positioning is fully explicit instead. */
.hero-carousel__controls {
  position: absolute; bottom: 28px; z-index: 2;
  left: max(0px, calc((100vw - 1180px) / 2 - 16px));
  right: calc(100vw - max(58vw, 720px));
  margin: 0; max-width: none; padding: 0;
  display: flex; align-items: center; justify-content: center; gap: 20px;
}
.hero-carousel__arrow {
  background: transparent; border: 1px solid var(--line); border-radius: 50%;
  width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;
  color: var(--ink); cursor: pointer; flex-shrink: 0;
  transition: background var(--transition-base), border-color var(--transition-base);
}
.hero-carousel__arrow:hover { background: var(--ink); color: #fff; border-color: var(--ink); }
.hero-carousel__arrow svg { width: 18px; height: 18px; }
.hero-carousel__dots { display: flex; gap: 10px; }
.hero-carousel__dot {
  width: 10px; height: 10px; border-radius: 50%; padding: 0;
  background: var(--line); border: none; cursor: pointer;
  transition: background var(--transition-base), transform var(--transition-base);
}
.hero-carousel__dot:hover { background: var(--muted); }
.hero-carousel__dot.is-active { background: var(--red); transform: scale(1.2); }

.hero__ticker {
  position: relative; z-index: 1;
  border-top: 1px solid var(--line);
  background: var(--bg-alt);
}
.hero__ticker-inner { display: flex; gap: 0; }
.ticker-item {
  flex: 1; padding: 28px 24px; text-align: center;
}
.ticker-item__icon {
  width: 48px; height: 48px; margin: 0 auto 14px; display: block;
  fill: none; stroke: var(--muted); stroke-width: 4;
  stroke-linecap: round; stroke-linejoin: round;
}
.ticker-item__value {
  display: block; font-family: var(--font-display); font-size: 2rem;
  font-weight: 800; color: var(--red); margin-bottom: 6px;
}
.ticker-item__label {
  display: block; font-size: 0.75rem; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted);
}

/* ---- Sections ---- */
.section { padding: 72px 0; }
.section--alt { background: var(--bg-alt); }
/* The map is the first thing in this section, right below the sticky
   header — the header (measured ~97px) is taller than the base section's
   72px top padding, so scrolling here started cutting into the map itself
   almost immediately instead of just covering blank padding first. */
.section--map { padding-top: 120px; }
.section__head { margin-bottom: 36px; }
.page-header { padding: 64px 0 40px; background: var(--bg-alt); }
/* A solid badge (not just tinted text) reads reliably whether the header
   sits on the plain --bg-alt fill or an arbitrary banner photo behind it —
   an opaque background doesn't depend on what's underneath the way the
   sitewide red-text-on-transparent .eyebrow does elsewhere on the page. */
.page-header .eyebrow {
  display: inline-block; background: var(--red); color: #fff;
  padding: 4px 10px; border-radius: var(--radius);
}
/* Optional per-page banner (set via /admin/pages.php → "Adicionar banner"):
   the actual image URL is inlined per-page as background-image (paired
   with a dark scrim in the same value, since an inline style can't be
   layered with a class's own background-image — the later one simply wins).
   Padding is intentionally NOT overridden here — it inherits .page-header's
   64px/40px so a page's header is the same height whether or not it has a
   banner; this rule only supplies what doesn't already exist on the plain
   variant: image sizing and the lighter text colours needed once there's a
   photo behind them instead of the flat --bg-alt fill. */
.page-header--banner {
  background-size: cover; background-position: center; background-repeat: no-repeat;
}
.page-header--banner h1 { color: #fff; }
.page-header--banner .lede { color: rgba(255, 255, 255, 0.85); }
.two-col { display: grid; grid-template-columns: 2fr 1fr; gap: 48px; }

/* ---- /contactos — primary contacts + map ---- */
/* Map first (left, wider) then contacts (right, narrower) — the contacts
   column only needs to fit an address, phone, email and a button, so it
   stays fixed-width rather than splitting the row evenly with the map. */
.contact-map-layout { display: grid; grid-template-columns: 1fr 340px; gap: 48px; align-items: start; }
.contact-line { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; }
.contact-line:last-of-type { margin-bottom: 0; }
/* Address spans two lines — align the pin with the first line instead of
   centering it against the whole two-line block. */
.contact-line--top { align-items: flex-start; }
.contact-line--top svg { margin-top: 2px; }
.contact-line svg {
  width: 20px; height: 20px; flex-shrink: 0;
  fill: none; stroke: var(--red); stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.map-embed {
  position: relative; width: 100%; padding-bottom: 75%;
  border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line);
  background: var(--surface-2); /* visible briefly while Leaflet/tiles load */
  /* Leaflet's own CSS gives its internal panes/controls z-index values up
     to 1000 (.leaflet-top/.leaflet-bottom), meant to stack within the map
     itself. Without a stacking context here, those escape and compare
     directly against the sticky header's z-index:100 in the page's root
     stacking context — winning, and painting over the header while
     scrolling. isolation:isolate + an explicit z-index traps them inside
     this box so only the box's own z-index (not its descendants') is ever
     compared against the header. */
  isolation: isolate; z-index: 0;
}
.map-embed iframe,
.map-embed .leaflet-container { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* Custom marker for the Leaflet map on /contactos — a white badge holding
   the org's favicon, ringed in red, on a small red pin point. Built with
   plain CSS (div icon) rather than a generated image, since Leaflet's
   marker is just an absolutely-positioned HTML element anyway. */
.map-pin { background: none; border: none; }
.map-pin__badge {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin: 0 auto;
  background: #fff; border: 3px solid var(--red); border-radius: 50%;
  box-shadow: 0 2px 6px rgba(20, 20, 20, 0.35); overflow: hidden;
}
/* Deliberately smaller than the badge (not 100%/cover) — the favicon has
   its own red border baked into the artwork, which touched this badge's
   red ring with no gap and read as the icon spilling over it. */
.map-pin__badge img { width: 28px; height: 28px; object-fit: contain; }
.map-pin__point {
  display: block; width: 0; height: 0; margin: -3px auto 0;
  border-left: 9px solid transparent; border-right: 9px solid transparent;
  border-top: 15px solid var(--red);
}

/* ---- Homepage services row ---- */
/* Centered icon above text like the hero__ticker/counters band, but on the
   page's own background (no gray strip) with a slim divider between items
   instead — icons in red, title using the ticker label's plain/uppercase
   treatment rather than the display font, and a smaller .btn since this
   section reads at a smaller scale than the hero carousel. */
.services-row { display: flex; gap: 0; }
.services-row__item {
  flex: 1; padding: 40px 32px; text-align: center;
  border-right: 1px solid var(--line);
}
.services-row__item:last-child { border-right: none; }
.services-row__icon {
  display: block; width: 48px; height: 48px; margin: 0 auto 16px;
  fill: none; stroke: var(--red); stroke-width: 4;
  stroke-linecap: round; stroke-linejoin: round;
}
.services-row__item h3 {
  font-family: var(--font-body); font-size: 0.75rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink);
  margin: 0 0 20px;
}

/* ---- /corpo-de-bombeiros/bombeiros — rank hierarchy ---- */
/* Quadro de Comando: same "icon/count/label above a divider" shape as the
   services row and homepage ticker, but the rank-insignia image standing in
   for the icon and a real headcount standing in for the ticker's stat. */
.ranks-command { display: flex; gap: 0; }
.ranks-command__item {
  flex: 1; padding: 36px 24px; text-align: center;
  border-right: 1px solid var(--line);
}
.ranks-command__item:last-child { border-right: none; }
.ranks-command__badge {
  display: block; height: 108px; width: auto; margin: 0 auto 18px;
  border-radius: var(--radius); box-shadow: 0 1px 3px rgba(20, 20, 20, 0.15);
}
/* Cadete/Infante/etc. with no artwork yet — same footprint as a real badge
   (width guessed at the ~0.63 aspect ratio the actual insignia PNGs share). */
.ranks-command__badge--placeholder {
  width: 68px;
  background: repeating-linear-gradient(135deg, var(--surface-2) 0 8px, var(--bg-alt) 8px 16px);
}
.ranks-command__count {
  display: block; font-family: var(--font-display); font-size: 2rem;
  font-weight: 800; color: var(--red); margin-bottom: 6px;
}
.ranks-command__name {
  display: block; font-size: 0.75rem; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted);
}

/* Quadro Ativo: one column per career path, each a vertical "ladder" reading
   senior (top) to estagiário (bottom) — a thin line strung behind the badges
   is what actually reads as hierarchy, not just a stack of equal rows. */
.ranks-active { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; }
.ranks-active__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  margin: 0 0 22px; padding-bottom: 12px; border-bottom: 2px solid var(--red);
}
.ranks-active__title {
  font-family: var(--font-body); font-size: 0.85rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink);
  margin: 0;
}
/* Career total — same scale as the Quadro de Comando counts, so the two
   sections read as the same kind of number even though this one sums a
   whole column instead of labelling a single badge. */
.ranks-active__total {
  font-family: var(--font-display); font-size: 2rem; font-weight: 800;
  color: var(--red); line-height: 1; flex-shrink: 0;
}
.ranks-active__ladder { list-style: none; margin: 0; padding: 0; position: relative; }
.ranks-active__ladder::before {
  content: ""; position: absolute; left: 21px; top: 4px; bottom: 4px;
  width: 1px; background: var(--line);
}
.ranks-active__tier {
  position: relative; display: flex; align-items: center; gap: 16px; padding: 9px 0;
}
.ranks-active__badge {
  position: relative; z-index: 1; flex-shrink: 0;
  width: 42px; height: 67px; object-fit: cover; border-radius: var(--radius);
  /* Halo matches this section's own background (section--alt), so the
     connector line reads as passing behind the badge, not through it. */
  box-shadow: 0 0 0 4px var(--bg-alt);
}
/* Cadetes, Infantes and any future entry with no rank-insignia artwork yet —
   same footprint as a real badge so the ladder's line/spacing don't shift. */
.ranks-active__badge--placeholder {
  background: repeating-linear-gradient(135deg, var(--surface-2) 0 6px, var(--bg-alt) 6px 12px);
}
.ranks-active__body {
  display: flex; align-items: baseline; justify-content: space-between;
  flex: 1; gap: 12px; min-width: 0;
}
.ranks-active__name { font-size: 0.92rem; color: var(--ink); }
.ranks-active__tier-count {
  font-family: var(--font-display); font-size: 1.1rem; font-weight: 800;
  color: var(--red); flex-shrink: 0;
}

/* Quadro de Honra / Quadro de Reserva: a single figure, not a rank — same
   structure for both, side by side in one section, Honra breaking from
   red-as-the-only-accent into gold since that's the distinction being
   marked, Reserva staying on-brand red via the --red modifier below. */
.honor-boards { display: grid; grid-template-columns: 1fr 1fr; gap: 40px; }
.honor-board { text-align: center; }
.honor-board--red .honor-board__count { color: var(--red); }
.honor-board .section__head { margin-bottom: 8px; }
/* "Quadro de Honra" and "Quadro de Reserva" are different lengths — left to
   wrap naturally, one can fold to a second line before the other does at
   some in-between viewport width, pushing its badge/count down a row out
   of line with its neighbour. Locking both to one line guarantees the two
   columns always match, at every width. */
.honor-board .eyebrow, .honor-board h2 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Backdrop matches the rank-badge PNGs' own navy and the buttons' corner
   radius, so the transparent honra.png symbol reads as part of the same
   badge family instead of floating loose on the section background. */
.honor-board__symbol-wrap {
  display: flex; align-items: center; justify-content: center;
  width: 140px; height: 140px; margin: 0 auto 8px;
  background: var(--badge-navy); border-radius: var(--radius);
}
.honor-board__symbol { display: block; width: 100px; height: 100px; }
.honor-board__count {
  display: block; font-family: var(--font-display); font-size: 4rem;
  font-weight: 800; line-height: 1; color: var(--gold);
}

/* ---- Cards / grids ---- */
.grid { display: grid; gap: 24px; }
.grid--vehicles { grid-template-columns: repeat(3, 1fr); }
.grid--news { grid-template-columns: repeat(3, 1fr); }
.grid--events { grid-template-columns: repeat(3, 1fr); }
.grid--events-preview { grid-template-columns: repeat(4, 1fr); }
.grid--departments { grid-template-columns: repeat(3, 1fr); }
.grid--departments .card__body { padding: 24px; }
/* Reserve space for 2 lines regardless of actual title length, so every
   card's phone/email rows start at the same height even when some
   department names wrap and others don't. */
.grid--departments h3 { margin: 0 0 14px; line-height: 1.3; min-height: 2.6em; }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
  box-shadow: 0 1px 2px rgba(20,20,20,0.04);
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}
.card:hover { transform: translateY(-4px); box-shadow: 0 12px 24px rgba(20,20,20,0.1); }
.card__media { position: relative; aspect-ratio: 4/3; background: var(--surface-2); overflow: hidden; }
.card__media img { width: 100%; height: 100%; object-fit: cover; }
.card__media--placeholder {
  width: 100%; height: 100%;
  background: repeating-linear-gradient(135deg, var(--surface-2) 0 14px, var(--bg-alt) 14px 28px);
}
.card .tag {
  position: absolute; top: 12px; left: 12px;
  background: var(--red); color: #fff; font-family: var(--font-mono);
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em;
  padding: 4px 10px; border-radius: 2px;
}
.card__body { padding: 18px; }
/* One treatment for every content-card title (news, veículos, eventos). News
   and event titles each declared this separately while vehicle cards silently
   kept the condensed display font — same component, three different headings.
   Declared once here so any future card type inherits it automatically. */
.card__body h3 { margin-bottom: 6px; font-family: var(--font-body); font-weight: 700; }
.card__body a { color: var(--ink); }

/* Whole card is clickable, not just the title — the title link grows an
   invisible ::after covering the card (the "stretched link" pattern), so
   there's still only one real <a>, no nested/duplicate links. */
.card--news { position: relative; }
.card--news h3 a::after { content: ""; position: absolute; inset: 0; }
.card--event .card__date {
  text-align: center; padding: 18px; background: var(--bg-alt);
  border-bottom: 1px solid var(--line);
}
.card--event .card__date .day {
  display: block; font-family: var(--font-display); font-size: 2rem;
  font-weight: 800; color: var(--red); line-height: 1;
}
.card--event .card__date .month {
  display: block; font-size: 0.75rem; letter-spacing: 0.08em;
  color: var(--muted); margin-top: 4px;
}
.card--event .card__body .event-list__location { margin-bottom: 8px; }
.card--event .card__body p:last-child { margin-bottom: 0; }

/* Every event card lines up on title/time/location regardless of text
   length — clamp the title to 2 lines (reserving that height even when
   the title is short) and truncate the single-line time to fit. */
.card--event__title {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: 2.3em;
}
.event-list__time { flex-wrap: nowrap; }
.event-list__time .event-list__text {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
/* Homepage preview cards have no description, so location is also capped
   to a single truncated line to keep every card the same height. */
.grid--events-preview .event-list__location { flex-wrap: nowrap; }
.grid--events-preview .event-list__location .event-list__text {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
/* Full events page has more room: let location wrap up to 2 lines
   (ellipsis beyond that), with the pin icon pinned to the first line
   instead of centering across both. */
.grid--events .event-list__location {
  display: grid; grid-template-columns: 14px 1fr; align-items: start; gap: 6px;
}
.grid--events .event-list__location .event-list__icon { margin-top: 3px; }
.grid--events .event-list__location .event-list__text {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  min-height: 3.2em; /* reserves 2 lines even for 1-line locations, so the divider below always lands at the same height */
}
.card--event__desc { font-family: var(--font-body); font-size: 1rem; }
.card--event__divider { border: none; border-top: 1px solid var(--line); margin: 14px 0; }

/* ---- Event list ---- */
.event-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; background: var(--line); }
.event-list__item { display: flex; gap: 20px; background: var(--surface); padding: 18px 20px; align-items: flex-start; }
.event-list__date {
  font-family: var(--font-mono); text-align: center; min-width: 56px;
  background: var(--bg-alt); border-radius: var(--radius); padding: 8px 0; flex-shrink: 0;
}
.event-list__date .day { display: block; font-size: 1.3rem; color: var(--red); font-weight: 700; }
.event-list__date .month { display: block; font-size: 0.7rem; color: var(--muted); letter-spacing: 0.08em; }
.event-list__time, .event-list__location {
  display: flex; align-items: center; gap: 6px; margin: 0 0 4px;
}
.event-list__location { margin-bottom: 8px; }
.event-list__icon {
  width: 14px; height: 14px; flex-shrink: 0;
  fill: none; stroke: var(--muted); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.event-list__body { display: flex; flex-wrap: wrap; gap: 8px 32px; flex: 1; min-width: 0; }
.event-list__meta { flex: 1 1 220px; min-width: 200px; }
.event-list__desc { flex: 2 1 320px; margin: 0; color: var(--muted); }

/* ---- Forms ---- */
.form { display: flex; flex-direction: column; gap: 18px; margin-top: 8px; }
.form__row { display: flex; flex-direction: column; gap: 6px; }
.form__row label { font-size: 0.85rem; font-weight: 600; }
.form__row input[type="text"],
.form__row input[type="email"],
.form__row input[type="tel"],
.form__row input[type="date"],
.form__row select,
.form__row textarea {
  background: var(--surface); border: 1px solid var(--line); color: var(--ink);
  padding: 12px 14px; border-radius: var(--radius); font-family: var(--font-body); font-size: 1rem;
}
/* Safari draws a native <select> its own way (own height, font size and a
   double-arrow control) and ignores most of the styling above, so the native
   look is switched off in every browser and the arrow drawn here instead:
   the nav menu's caret, in --muted, with room kept for it on the right. */
.form__row select {
  appearance: none; -webkit-appearance: none;
  line-height: normal; padding-right: 42px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6' fill='none' stroke='%236B6B70' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 16px;
}
/* Border tint is an extra affordance, NOT the focus indicator — `outline: none`
   used to leave keyboard users with only this subtle colour shift to go on.
   The global :focus-visible ring now applies here like everywhere else, but
   its default 3px/2px-offset reads as a bold, separate second ring right
   next to the field's own 1px border — thinned and flushed against the
   field here so the two read as one crisp line instead of doubled up. */
.form__row input:focus, .form__row select:focus, .form__row textarea:focus { border-color: var(--red); }
.form__row input:focus-visible, .form__row select:focus-visible, .form__row textarea:focus-visible {
  outline-width: 1px;
  outline-offset: 0;
}
.form__row--checkbox { flex-direction: row; align-items: flex-start; gap: 10px; font-size: 0.9rem; color: var(--muted); }
.form__row--checkbox input { margin-top: 4px; }

/* Custom square checkbox, sized to match this row's text inputs (measured:
   ~45.5px from their own padding+border+line-height) — the browser default
   checkbox is ~13px, a stray dot next to full-height fields in the same
   row. Still a real checkbox (appearance:none only changes the paint, not
   the semantics/keyboard behaviour), just drawn as a square with its own
   checkmark instead of the native tick. */
.form__row-trio input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  width: 45px; height: 45px; flex-shrink: 0; margin: 0;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); cursor: pointer; position: relative;
}
.form__row-trio input[type="checkbox"]:checked { background: var(--red); border-color: var(--red); }
.form__row-trio input[type="checkbox"]:checked::after {
  content: ''; position: absolute; left: 50%; top: 45%;
  width: 11px; height: 20px; border: solid #fff; border-width: 0 3px 3px 0;
  transform: translate(-50%, -50%) rotate(45deg);
}
.form__row-trio input[type="checkbox"]:focus-visible { outline: 1px solid var(--red); outline-offset: 0; }

.form__row input:disabled, .form__row select:disabled, .form__row textarea:disabled {
  background: var(--bg-alt); color: var(--muted); cursor: not-allowed;
}

/* Two related fields on one line (birthdate+place of birth, postal
   code+city) — stacks back to one column when there isn't room.
   min-width:0 on the grid item is required, not optional — a bare 1fr
   track is minmax(auto, 1fr), and that "auto" minimum resolves to the
   input's own default intrinsic width (browsers give a plain text input
   real width even with no content/size set). Without this, the column
   refuses to shrink past that point and drags the whole row wider than
   its container instead of resizing with it. */
.form__row-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.form__row-pair > .form__row { min-width: 0; }

/* Same shape, 1fr/2fr instead of even halves — sócios' postal-code/city
   pair, where the first field is a short, fixed-format value that doesn't
   need as much room as the free-text field beside it. Self-contained (not
   layered on .form__row-pair) so it doesn't depend on cascade order
   against that rule's own mobile override below. */
.form__row-pair--1-2 { display: grid; grid-template-columns: 1fr 2fr; gap: 18px; }
.form__row-pair--1-2 > .form__row { min-width: 0; }
@media (max-width: 480px) {
  .form__row-pair--1-2 { grid-template-columns: 1fr; }
}

/* Sócios' birthdate/naturalidade pair — same idea as --1-2 above but 2fr/3fr
   instead of 1fr/2fr. At 1fr/2fr, "Data de nascimento *" (measured ~141px
   at this label's actual font) loses to its own column width right around
   the point this row is squeezed but not yet stacked (~481-489px viewport,
   where a 1fr share of a .wrap--narrow row is only ~138px) and wraps to two
   lines while "Naturalidade *" stays on one — 2fr/3fr gives the first
   column ~25px more headroom at that same worst case, comfortably above
   what the label needs. */
.form__row-pair--2-3 { display: grid; grid-template-columns: 2fr 3fr; gap: 18px; }
.form__row-pair--2-3 > .form__row { min-width: 0; }
@media (max-width: 480px) {
  .form__row-pair--2-3 { grid-template-columns: 1fr; }
}

/* Recruitment's "already a firefighter?" question plus its two dependent
   fields — the checkbox column stays compact, the two text fields split
   the rest of the row evenly. All three use the same label-above-field
   shape, so top-aligning (rather than bottom-aligning) keeps the labels
   in a level row. */
.form__row-trio { display: flex; gap: 18px; align-items: flex-start; }
.form__row-trio > .form__row:first-child { flex: 0 0 auto; }
.form__row-trio > .form__row:not(:first-child) { flex: 1 1 0; min-width: 0; }
@media (max-width: 640px) {
  .form__row-trio { flex-direction: column; align-items: stretch; }
}

/* Sócios' Cartão de Cidadão / NIF / N.º Utente, and Recrutamento's birth
   date / naturalidade / escolaridade — three equal-width, short
   fields on one line, unlike .form__row-trio's checkbox+two-fields
   shape above. Stacks a breakpoint earlier (640px, matching .form__row-trio)
   since three columns need more room to stay comfortable than a 2-column
   split does. Same min-width:0 requirement as .form__row-pair above — three
   narrow 1fr tracks hit an input's default intrinsic width sooner than two
   wider ones do, which is exactly what made this row (and not the others)
   stop resizing and overflow its container. */
.form__row-trio-even { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.form__row-trio-even > .form__row { min-width: 0; }
@media (max-width: 640px) {
  .form__row-trio-even { grid-template-columns: 1fr; }
}
@media (max-width: 480px) {
  .form__row-pair { grid-template-columns: 1fr; }
}

/* Membership fee period and payment method — shown as selectable cards
   rather than a plain <select> since they're meaningful choices worth seeing
   every option for at a glance. flex:1 on each card splits the row evenly
   however many options there are; flex-wrap + a 150px basis lets the longer
   labels (4 periods, 3 methods) drop to a second line on narrower widths
   instead of squeezing. */
.payment-options { display: flex; flex-wrap: wrap; gap: 14px; }
.payment-option {
  flex: 1 1 150px; display: flex; align-items: center; gap: 10px; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: var(--radius); cursor: pointer; font-weight: 600;
}
.payment-option:has(input:checked) { border-color: var(--red); background: rgba(225,38,28,0.04); }
/* On phones: a grid, not a flex column — in a column the 150px flex-basis
   above becomes each option's height. The short plan names fit two per row;
   the longer method names (--stacked) keep one per row. */
@media (max-width: 480px) {
  .payment-options { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .payment-options--stacked { grid-template-columns: 1fr; }
  .payment-option { padding: 10px 14px; }
}

/* Every form we collect a phone number on is for a Portuguese association;
   the dial code was previously a full country selector next to the field
   (removed — one fixed prefix instead of asking every visitor to pick their
   country just to submit a local number), now a static "+351" laid over the
   input's own left padding so the number field itself gets the full width. */
.phone-group { position: relative; }
.phone-group__prefix {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  font-weight: 600; color: var(--muted); pointer-events: none;
}
.phone-group input[type="tel"] { width: 100%; padding-left: 58px; }

/* E-mail + phone on one line (recruitment/cadets forms) — phone stays just
   wide enough for a 9-digit local number ("912 345 678") instead of
   stretching to fill the row like it does everywhere else .phone-group is
   used (e.g. the sócios form); e-mail absorbs the rest of the line. */
/* --fill-fit is the same layout under a generic name, for any pair where the
   first field takes the rest of the line and the second keeps its natural
   width (e.g. a name next to a date). */
.form__row-pair--contact, .form__row-pair--fill-fit { display: flex; gap: 18px; align-items: flex-start; }
.form__row-pair--contact > .form__row:first-child, .form__row-pair--fill-fit > .form__row:first-child { flex: 1 1 auto; min-width: 0; }
.form__row-pair--contact > .form__row:last-child, .form__row-pair--fill-fit > .form__row:last-child { flex: 0 0 auto; }
.form__row-pair--contact .phone-group input[type="tel"] { width: calc(12ch + 5px + 58px); }
@media (max-width: 480px) {
  /* align-items:stretch is required here, not just flex-direction — the base
     rule's align-items:flex-start (needed to top-align email/phone next to
     each other in row mode) otherwise carries into column mode too, where it
     means "size each row to its own content" instead of "fill the width",
     leaving the email input and phone-group both stuck at their browser
     default input width instead of spanning edge to edge like every other
     field on the form. Same fix .form__row-trio's own breakpoint already
     uses below. */
  .form__row-pair--contact, .form__row-pair--fill-fit { flex-direction: column; align-items: stretch; }
  .form__row-pair--contact .phone-group input[type="tel"] { width: 100%; }
}

.requirements-list, .benefits-list { list-style: none; margin: 0 0 20px; padding: 0; display: flex; flex-direction: column; gap: 10px; }
/* Direct children only: a list nested in an item (e.g. inside a
   .details-box) keeps its own look instead of inheriting the check mark. */
.requirements-list > li, .benefits-list > li {
  position: relative; padding-left: 30px; color: var(--ink-soft);
}
.requirements-list > li::before, .benefits-list > li::before {
  content: ""; position: absolute; left: 0; top: 0.35em; width: 16px; height: 16px;
  background: var(--red);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5 13l4 4L19 7' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M5 13l4 4L19 7' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
}
/* A secondary clarification (acceptable alternatives, an exception for a
   specific case, etc.) breaks onto its own indented line instead of
   running inline in the same sentence as the main requirement/document —
   easier to scan than a long parenthetical crammed into one line. */
.requirements-list__detail { display: block; margin-top: 4px; margin-left: 18px; color: var(--muted); font-size: 0.92em; }

/* Sócios' Quotas section — regular vs. reformado membership fee, each shown
   as a bordered card (same tokens as .card/.payment-option elsewhere, no
   hover-lift or media since this is plain reference data, not a link or a
   form control) listing all three billing frequencies at once so a visitor
   doesn't have to guess the quarterly/annual rate from the monthly one. */
.pricing-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.pricing-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 24px; background: var(--surface); }
.pricing-card h3 { margin: 0 0 4px; }
.pricing-card__note { color: var(--muted); font-size: 0.9rem; margin: 0 0 18px; }
.pricing-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.pricing-list li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding-bottom: 12px; border-bottom: 1px solid var(--line);
}
.pricing-list li:last-child { border-bottom: none; padding-bottom: 0; }
.pricing-list__value { font-family: var(--font-display); font-weight: 800; font-size: 1.3rem; color: var(--red); white-space: nowrap; }
@media (max-width: 640px) {
  .pricing-grid { grid-template-columns: 1fr; }
}

/* Recruitment process steps — numbered circles connected by a vertical
   line, like a simple vertical stepper/roadmap rather than a literal
   dated calendar (exact timing varies per candidate/training cohort). */
.process-timeline { list-style: none; margin: 0 0 20px; padding: 0; }
.process-timeline li { display: flex; gap: 20px; position: relative; padding-bottom: 32px; }
.process-timeline li:last-child { padding-bottom: 0; }
.process-timeline li:not(:last-child)::before {
  content: ""; position: absolute; left: 19px; top: 40px; bottom: 0; width: 2px; background: var(--line);
}
.process-timeline__step {
  flex-shrink: 0; width: 40px; height: 40px; border-radius: 50%; background: var(--red); color: #fff;
  display: flex; align-items: center; justify-content: center; z-index: 1;
  font-family: var(--font-display); font-weight: 800; font-size: 1.1rem;
}
.process-timeline h3 { margin: 6px 0 6px; font-size: 1.15rem; }
.process-timeline p { margin: 0; color: var(--ink-soft); }

/* Honeypot: visually hidden but present in the DOM/tab order excluded. */
.hp-field { position: absolute; left: -9999px; top: -9999px; }

.alert { padding: 16px 18px; border-radius: var(--radius); margin-bottom: 20px; }
.alert--success { background: rgba(46,125,50,0.08); border: 1px solid var(--success); color: #1E5221; }
.alert--error { background: rgba(225,38,28,0.06); border: 1px solid var(--red); color: var(--red-dim); }
/* Functional (eligibility) rejections — e.g. recruitment's age/schooling
   rules — are an actual "you don't qualify", not a fixable format slip, so
   they get a harder-stop treatment: solid red instead of the soft tint. */
.alert--error-strong { background: var(--red); border: 1px solid var(--red); color: #fff; font-weight: 600; }
.alert ul { margin: 0; padding-left: 20px; }

/* ---- Footer ----
   Deliberately dark against an otherwise light site — an anchor at
   the bottom of the page and the one place the old charcoal palette
   survives, purely for contrast. */
.site-footer { background: var(--footer-bg); border-top: 1px solid var(--footer-line); color: var(--footer-ink); }
.site-footer .brand__text { color: var(--footer-ink); }
.site-footer .brand__sub { color: var(--footer-muted); }
.footer-accred__mark { max-width: 220px; height: auto; }
.site-footer__inner {
  display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 32px;
  padding: 48px 24px;
}
.footer-col h3 {
  font-family: var(--font-mono); font-size: 0.8rem; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--footer-muted); margin-bottom: 12px;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.footer-col ul a { color: var(--footer-ink); }
.footer-col .muted { color: var(--footer-muted); }
.site-footer__legal { padding: 16px 24px; border-top: 1px solid var(--footer-line); font-size: 0.8rem; color: var(--footer-muted); }

/* Contact column: icon + text rows (address can wrap to 2 lines, icon
   pinned to the first one — same grid-alignment trick as the event cards'
   location line), then the social icons as their own row underneath. */
.footer-contact__item {
  display: grid; grid-template-columns: 16px 1fr; align-items: start; gap: 10px;
  margin: 0 0 14px; font-size: 0.9rem; color: var(--footer-ink);
}
.footer-contact__item:last-of-type { margin-bottom: 20px; }
.footer-contact__item a { color: var(--footer-ink); }
.footer-contact__icon {
  width: 16px; height: 16px; margin-top: 2px; flex-shrink: 0;
  fill: none; stroke: var(--footer-muted); stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.site-footer .social-links a { color: var(--footer-ink); }

/* ---- Prose (news article body) ---- */
.prose { font-size: 1.05rem; }
.prose__cover { border-radius: var(--radius); margin-bottom: 28px; }
.prose h2 { margin-top: 1.4em; }
.prose p { color: var(--ink-soft); }
.attachment-link { margin-top: 2em; }
.attachment-link a {
  display: inline-block; color: var(--ink); background: var(--bg-alt);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 10px 16px; font-weight: 600; font-size: 0.9rem;
}
.attachment-link a:hover { border-color: var(--red); text-decoration: none; }

/* ---- Bylaws (estatutos) ---- */
.bylaws-chapter { margin-top: 2.6em; padding-top: 1.6em; border-top: 1px solid var(--line); }
.bylaws-chapter:first-child { margin-top: 0; padding-top: 0; border-top: none; }
.bylaws-chapter h2 { margin-bottom: 0.2em; }
.bylaws-section { font-size: 1rem; text-transform: uppercase; letter-spacing: .03em; color: var(--muted); margin: 1.6em 0 0.4em; }
.bylaws-article { margin-top: 1.8em; }
.bylaws-article h3 { font-size: 1.1rem; margin-bottom: 0.6em; }
.bylaws-clause { margin: 0 0 0.7em; color: var(--ink-soft); }
.bylaws-clause__num { font-weight: 700; color: var(--ink); margin-right: 0.4em; }
.bylaws-subclause { margin: 0 0 0.5em 1.6em; color: var(--ink-soft); }
.bylaws-subclause__letter { font-weight: 700; color: var(--ink); margin-right: 0.4em; }
.bylaws-intro {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  margin: 1.6em 0 2.4em;
}
.bylaws-intro p { margin: 0; }
.bylaws-download { display: inline-flex; align-items: center; gap: 8px; flex-shrink: 0; }
.bylaws-download svg { width: 18px; height: 18px; }
@media (max-width: 640px) {
  .bylaws-intro { flex-direction: column; align-items: stretch; }
}

/* ---- Responsive ---- */
@media (max-width: 900px) {
  .grid--vehicles, .grid--news, .grid--events, .grid--events-preview, .grid--departments { grid-template-columns: repeat(2, 1fr); }
  .two-col, .contact-map-layout { grid-template-columns: 1fr; }
  .services-row { flex-direction: column; }
  .services-row__item { border-right: none; border-top: 1px solid var(--line); }
  .services-row__item:first-child { border-top: none; }
  .ranks-command { flex-direction: column; }
  .ranks-command__item { border-right: none; border-top: 1px solid var(--line); }
  .ranks-command__item:first-child { border-top: none; }
  .ranks-active { grid-template-columns: 1fr; gap: 32px; }
  .honor-boards { grid-template-columns: 1fr; gap: 40px; }
  .site-footer__inner { grid-template-columns: 1fr 1fr; }
  /* left:40% used to be the rule here, but the math never works: the title/
     lede can extend to ~644px from the left edge (see .hero__title's own
     max-width), and 40% of anything ≤900px tops out at 360px — guaranteed
     overlap, not just at one unlucky width. Hiding the panel below the same
     900px breakpoint the nav already collapses at removes the fragile
     partial-width zone entirely instead of chasing a percentage that fits. */
  /* The diagonal side-panel pair genuinely can't fit here (see above), but
     the video/image itself doesn't have to disappear — shown instead as a
     plain rectangular banner stacked above the text, full width, no
     clip-path. Slides with neither (no video/image set) fall back to
     .hero-slide__media's own striped background instead of an empty box. */
  .hero-slide::before { display: none; }
  .hero-slide__media {
    display: block; position: relative; inset: auto;
    width: 100%; height: 200px; flex: 0 0 200px;
    clip-path: none; left: auto;
  }
  .hero-slide { justify-content: flex-start; }
  .hero-slide__inner { padding-top: 16px; padding-bottom: 40px; }
  .hero-carousel { height: calc(200px + 400px); min-height: 0; }
  /* No side panels at this width, so the desktop between-the-two-panels
     math doesn't apply — just center in the full row like everything
     else does at this breakpoint. */
  .hero-carousel__controls { left: 0; right: 0; }
}

/* Header collapses to a hamburger at its own, wider breakpoint — the nav
   carries three dropdowns ("Associação", "Corpo de Bombeiros", "Serviços")
   plus Contactos, so its natural width (measured: brand ~174px + nav ~530px
   + phone ~143px + gaps) no longer fits next to the logo and phone number
   below that width. Below it, the grid's auto-sized nav column holds its
   full width and squeezes the logo/phone 1fr columns instead — their
   nowrap text starts getting visually clipped/overlapping rather than the
   row collapsing cleanly. Re-measure brand/nav/phone widths (devtools,
   full-width viewport) whenever a nav item is added or removed, and adjust
   this breakpoint to stay comfortably above their combined width. */
@media (max-width: 1060px) {
  .nav__menu {
    position: absolute; top: 100%; left: 0; right: 0; z-index: 50;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--bg); border-top: 1px solid var(--line);
    box-shadow: 0 8px 16px rgba(20,20,20,0.08);
    display: none;
  }
  .nav__menu.is-open { display: flex; }
  .nav__menu li { border-bottom: 1px solid var(--line); }
  /* This panel is still light (see .nav__menu background above), but the base
     rule now sets --footer-ink for the dark header bar — put it back to dark
     text here since these links render against the white panel, not the bar. */
  .nav__menu a { display: block; padding: 16px 24px; color: var(--ink); }
  /* The submenu is a floating card on desktop; here it's just an inline
     accordion under its own toggle, inside the same full-width mobile panel. */
  .nav__item--dropdown { position: static; }
  .nav__dropdown-toggle { width: 100%; justify-content: space-between; padding: 16px 24px; color: var(--ink); }
  .nav__submenu {
    position: static; margin: 0; padding: 0; background: var(--bg-alt);
    border: none; border-radius: 0; box-shadow: none;
  }
  .nav__submenu li { border-bottom: 1px solid var(--line); }
  .nav__submenu li:last-child { border-bottom: none; }
  .nav__submenu a { padding: 14px 24px 14px 36px; }
  .nav__toggle {
    display: flex; flex-direction: column; gap: 4px; background: none; border: none;
    padding: 8px; cursor: pointer;
  }
  /* The toggle button itself sits in the dark bar (not the white panel it
     opens), so its bars need the light color, unlike the links above. */
  .nav__toggle span { width: 22px; height: 2px; background: var(--footer-ink); }

  /* The centered "auto" nav column only works when brand/aside are also
     "auto"-sized against 1fr — otherwise there isn't enough room for the
     full brand lockup AND a centered column, and the toggle button ends up
     overlapping the logo. Give brand/aside their natural width and let the
     (now much smaller, toggle-only) nav float in the remaining flexible
     space instead of trying to center it. */
  .site-header__inner { grid-template-columns: auto 1fr auto; column-gap: 12px; }
  .nav { justify-self: end; }
}
@media (max-width: 640px) {
  .grid--vehicles, .grid--news, .grid--events, .grid--events-preview, .grid--departments { grid-template-columns: 1fr; }
  .hero__ticker-inner { flex-direction: column; }
  .ticker-item { border-bottom: 1px solid var(--line); }
  .ticker-item:last-child { border-bottom: none; }
  .hero-carousel__controls { bottom: 16px; }
  .site-footer__inner { grid-template-columns: 1fr; }

  /* The emergency number itself must stay visible and readable even at the
     smallest sizes, not just the tappable icon — it's the number people call
     when something's on fire, hiding the digits defeats the point. */
  .header-aside { gap: 12px; }
}

/* Below ~428px, brand (174px) + nav toggle (38px) + phone number (143px) at
   their full desktop sizes no longer fit the row even with zero gap/padding
   — every real phone is under that width, so this isn't an edge case, it's
   the normal case. The toggle's tap target is left alone (already compact,
   and shrinking it further hurts usability); brand and phone are scaled
   down together with tighter row spacing so all three sit side by side
   without the text/number overflowing into each other, verified against
   iPhone SE's 320px viewport — the narrowest width still meaningfully in
   use — down to the pixel.
   Scaled by the same factor (~0.62) for both brand__line sizes, preserving
   the ratio that keeps "BOMBEIROS"/"VALONGO" matched in rendered width
   (see the comment on .brand__line above) — only the vertical nudge on the
   red line doesn't scale linearly (it's a fixed-px optical correction, not
   proportional to font-size) so it's re-tuned by eye instead. */
@media (max-width: 480px) {
  .site-header__inner { padding: 12px 16px; column-gap: 8px; }
  .brand { gap: 8px; }
  .brand__mark { height: 40px; }
  .brand__lines { height: 40px; }
  .brand__line--black { font-size: 1.05rem; }
  .brand__line--red { font-size: 1.29rem; top: -2px; }
  .phone-link { font-size: 0.85rem; gap: 5px; }
  .phone-link svg { width: 16px; height: 16px; }
}

/* ---- Important side note (icon + text), e.g. under the associados benefits ---- */
/* The look is shared with .details-box__body below, so both stay in sync. */
.note, .details-box__body {
  padding: 14px 16px; color: var(--ink-soft); font-weight: 500;
  /* Same faint red tint the selected .payment-option uses, so the callout
     reads as part of the same visual language rather than a new color. */
  background: rgba(225,38,28,0.03); border: 1px solid rgba(225,38,28,0.18); border-radius: var(--radius);
}
/* Icon beside the text. A .details-box__body takes this layout only when it
   starts with a .note__icon: otherwise it holds ordinary flowing content
   (paragraphs, lists) that a flex row would put side by side. */
.note, .details-box__body:has(> .note__icon:first-child) { display: flex; align-items: flex-start; gap: 12px; }
.note { margin: 0; }
.note__icon { width: 20px; height: 20px; flex-shrink: 0; margin-top: 2px; color: var(--red); }

/* ---- Box revealed on demand (a native <details>, so no JavaScript) ----
   <details class="details-box"><summary>text <span class="details-box__toggle">
   Saber mais<svg …/></span></summary><div class="details-box__body">…</div></details>
   The summary keeps the surrounding text style; the body looks like .note. */
.details-box summary { cursor: pointer; list-style: none; }
.details-box summary::-webkit-details-marker { display: none; }
.details-box__toggle {
  display: inline-flex; align-items: center; gap: 4px; margin-left: 6px;
  color: var(--red); font-weight: 600; font-size: 0.9rem; white-space: nowrap;
}
.details-box summary:hover .details-box__toggle { text-decoration: underline; }
.details-box__toggle svg {
  width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2.5;
  stroke-linecap: round; stroke-linejoin: round; transition: transform var(--transition-base);
}
.details-box[open] .details-box__toggle svg { transform: rotate(180deg); }
.details-box__body { margin-top: 12px; }
.details-box__body > :first-child:not(.note__icon) { margin-top: 0; } /* the icon keeps the 2px that lines it up with the text */
.details-box__body > :last-child { margin-bottom: 0; }

/* ---- "Mais Informação" modal (associados) ---- */
.modal-dialog {
  border: none; border-radius: var(--radius); padding: 0;
  width: min(480px, calc(100vw - 32px)); max-height: calc(100vh - 32px);
  background: var(--bg); color: var(--ink);
  box-shadow: 0 20px 50px rgba(20,20,20,0.3);
}
.modal-dialog::backdrop { background: rgba(20,20,20,0.55); }
/* Padding lives on the form, not the dialog, so a click on the dialog
   element itself can only ever be the backdrop (see info-modal.js). */
.modal-dialog > form { padding: 24px; }
.modal-dialog__title { margin: 0 0 16px; font-size: 1.4rem; }
.modal-dialog__actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 8px; }
