/* ==========================================================================
   incuba-frontoffice.css - camada Incuba.Açores EXCLUSIVA do Frontoffice

   Carregado depois de cssnovo-style.css e incuba-base.css pelo Frontoffice e
   pelo ecrã de login do Backoffice (que usa o cartão de login do site). NUNCA
   pelas páginas do Backoffice. Era a parte final de incuba-tokens.css, movida
   sem reescrever regras e com a ordem relativa preservada.

   As regras do tema excluem o Backoffice com a guarda
   body:not(.templateDetailsBackoffice):not(.bo-login-body).
   Nota: o login do Backoffice já não usa a classe .bo-login-body, por isso a
   segunda exclusão não exclui nada. NÃO a retirar: conta como uma classe de
   especificidade (0,1,0) em ~237 seletores e é ela que, por exemplo, mantém os
   títulos .gra-text-blue-800 do cartão em amarelo contra a regra do DS
   ".dark-mode .templateDetailsA .contentPageText .gra-text-blue-800" (0,4,0).
   Retirada num teste: 26 títulos de sobre.php passam de amarelo a cinzento.

   Índice (numeração herdada de incuba-tokens.css)
     3. Componentes      cartões, listas e páginas do Frontoffice
     5. Tema Incuba      remapeamento amarelo do Frontoffice (5.1 a 5.4)
     6. Componentes      popups, hero e notificações
     7. Tema             estados ativos/selecionados
     8. Componentes      rodapé fixo, galerias, inscrições, identidades
     9. Responsivo       adaptação a mobile do Frontoffice (prefixo fo-)
    10. Hour Picker      acento do seletor de horas
    11. Responsivo       rodapé e Área de Membro em mobile

   A ordem das regras conta: ao mover blocos, manter a ordem relativa das que
   se sobrepõem.
   ========================================================================== */

/* ==========================================================================
   3. COMPONENTES - CARTÕES, LISTAS E PÁGINAS
   Regras da aplicação (Frontoffice): alturas fixas de cartões, tabelas, listas
   laterais. Dependem de classes do DS e dos nomes próprios (.agenda-card-*, ...).
   ========================================================================== */

/* Equal-height cards site-wide: cssnovo-style.css's own .details .text
   rule caps description text at max-height:168px but with
   overflow-y:auto (a scrollbar inside the card, not a clean cutoff) -
   titles and descriptions of different lengths still push cards in the
   same row to different heights. Multi-line ellipsis clamp instead, on
   both the title and the description, so every card in a
   .gra-row-bs/.gra-col-bs-* grid ends up the same height regardless of
   how much text the content actually has. */
.gra-card-informative .details .text,
.gra-card-text .details .text {
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  max-height: none;
}
.gra-card-informative .details .title,
.gra-card-text .details .title {
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* iniciativas.php card title: FIXED-size content slot (height, not just
   max-height/line-clamp) rather than the site-wide rule above, which only
   bounds the *maximum* - a short title still left the details area
   shorter than a card with a full 2-line title next to it in the same row
   (no real height:100% exists for a non-carousel .card-vertical - see the
   ferramenta-card comment below), so rows still came out visibly uneven.
   A plain height (always 2 lines' worth, 2 x cssnovo's .details .title
   line-height of 28.8px) makes every card's title area identical
   regardless of actual text length - a 1-line title just leaves blank
   space inside its OWN fixed slot, not extra space added elsewhere to
   compensate. -webkit-line-clamp is layered on top only for the "…"
   glyph; the plain height + overflow:hidden is what actually guarantees
   the cut lands on a line boundary even if line-clamp itself doesn't
   render the ellipsis (see the agenda-card-details comment above for why
   that can silently not fire in this Chrome build depending on ancestor
   flex context - iniciativas.php's card is a plain block link like
   ferramenta-card, not agenda.php's flex one, so verified live that clamp
   DOES work here too, but the fixed height means it's not load-bearing
   for consistent height either way).
   The card's description was removed entirely (not just hidden) - see
   iniciativas.php's card markup - so there is no .iniciativa-card-text
   rule here anymore. */
.gra-card-informative.card-vertical .details .iniciativa-card-title {
  height: 57.6px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* parceiros.php card grid: same "fixed content slot, not just a max-height
   clamp" fix as .iniciativa-card-title above, applied to BOTH the title and
   the description this time, so every partner card in a row ends up
   exactly the same height regardless of how long the entity's name or
   blurb is - a short title/description just leaves blank space in its own
   fixed slot rather than shrinking the whole card.
   Title: fixed 2-line slot (57.6px = 2 x cards.scss's 28.8px .details
   .title line-height), overflow:hidden with no line-clamp/ellipsis at all
   - explicit ask: partner names should never show a truncating "…", only
   ever reserve the space for 2 lines. display/-webkit-line-clamp/
   text-overflow are explicitly reset here (not just left unset) because
   the site-wide `.gra-card-informative .details .title` rule above
   already sets those same 3 properties - CSS cascades per-property, so a
   more specific selector that stays silent on a property doesn't cancel a
   less specific rule that does declare it; each one has to be named and
   overridden here or the site-wide clamp/"…" would still win through.
   Description: fixed 2-line slot (48px = 2 x cards.scss's 24px .details
   .text line-height) WITH line-clamp for the "…" glyph, since descriptions
   are free text and can genuinely run past 2 lines. The same
   .partner-card-text rule is shared by servicos.php's card grid via
   .servico-card-text (identical need: reserve a consistent 2-line
   description slot so every service card in a row ends up the same
   height) rather than duplicating the block for a second page.
   display:flex + align-items:center (rather than plain block) vertically
   centers shorter/smaller-font names within the fixed slot instead of
   always sitting at the top, since parceirosTituloEstilo() in parceiros.php
   can shrink long names well below the 57.6px slot height.
   margin-top is knocked down from cards.scss's own `.card-vertical .details
   .title { margin-top: 24px !important }` (needs matching !important to
   win): the card's own DOM order is Imagem → Título → Descrição → rodapé
   (parceiros.php's inline `style="display:block"` on the card link beats
   cards.scss's `&.card-vertical { display:flex }`, so plain block-flow
   stacking order is what actually renders - no flex `order` trickery
   needed), .details starts right after the image with .title as its first
   child, so this margin-top is the imagem→título gap. 12px keeps it close
   to the base `.text { margin-top: 8px }` rule (untouched, título→
   descrição) and the footer's own gra-margin-top-3xs (8px, descrição→
   rodapé), so the rhythm between all three gaps reads even. */
.gra-card-informative.card-vertical .details .partner-card-title {
  height: 57.6px;
  overflow: hidden;
  display: flex;
  align-items: center;
  text-overflow: clip;
  -webkit-line-clamp: unset;
  margin-top: 12px !important;
}
.gra-card-informative.card-vertical .details .partner-card-text,
.gra-card-informative.card-vertical .details .servico-card-text {
  height: 48px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* Footer row ("Parceiro desde X" + tipo tag, same line, space-between):
   fixed height matching .gra-tag's own natural rendered height (cards.scss:
   8px vertical padding x2 + 16.8px line-height ≈ 32.8px, rounded to 33px)
   so every card's footer reserves the same vertical slot even when a given
   partner has no `desde` or no `tipo` (both nullable columns) and one or
   both sides render empty - the row's own left/right label spans are
   always in the DOM (see parceiros.php), just empty, so justify-content:
   space-between still keeps whichever side IS populated pinned to its
   correct edge instead of drifting to center. */
.partner-card-footer {
  height: 33px;
}

/* agenda.php's card grid is 4-up (.gra-col-bs-xl-4, 25% width) instead of
   the 3-up (33%) grid iniciativas.php/parceiros.php/galeria.php use, so
   the shared .gra-card-informative .details .title rule above (24px font,
   2-line clamp) was cutting real event titles off mid-word in these
   narrower cards. Scoped down here instead of changed globally, since the
   3-up pages have enough width for the DS default size already.

   Title/description length is capped server-side (agendaTruncarTexto() in
   agenda.php, word-boundary + "…") rather than via -webkit-line-clamp:
   verified live that line-clamp silently stops truncating at all on a
   descendant of a flex-grow .details spacer in this Chrome build
   (getComputedStyle reports display:flow-root instead of -webkit-box) - a
   plain character budget sidesteps that. The height below (not just
   max-height) is what actually GUARANTEES the box never exceeds 3
   lines/2 lines even in the rare case a string's real rendered width
   doesn't quite match the character-count estimate: overflow:hidden still
   cuts cleanly at a line boundary since the height is an exact multiple
   of the line-height, independent of whether -webkit-line-clamp itself
   renders anything.

   .agenda-card-details no longer carries flex-grow:1 as an inline style
   (agenda.php) - that was absorbing the ENTIRE row's leftover vertical
   space as one gap between the description and the time/local/vagas
   block, which is what made that gap look oversized once the description
   stopped being long enough to fill it. Every piece of the card is now a
   fixed size (image height, this title/description, the date-row/hora/
   local/vagas single lines), so the card's natural content height is
   already the same for every event without needing a flex spacer to
   force it - EXCEPT cssnovo's own .gra-card-informative.card-vertical
   .details{flex:1 0 auto} rule (a *class* rule, not inline) sets the same
   flex-grow:1 regardless, so the gap didn't actually go away just by
   deleting the inline style - verified live (flex-grow computed to "1"
   from that class rule, gap unchanged). flex-grow:0 below overrides it.
   font-size/height below need !important because cssnovo's own
   same-specificity (0,0,3,0) .gra-card-informative .details .title rule
   ties on specificity and otherwise wins - verified live without it. */
.agenda-card-details {
  flex-grow: 0 !important;
}
.agenda-card-details .title {
  font-size: 18px !important;
  line-height: 24px !important;
}
.agenda-card-title {
  height: 48px !important;
  overflow: hidden !important;
}
.agenda-card-text {
  height: 48px !important;
  overflow: hidden !important;
}

/* Only the "Agendado" state tag (agendaRenderizarEstadoData()'s 'gra-tag
   secondary alert' weight for agendaCalcularEstado()'s 'agendado' case) was
   pushing the whole card's title/description/info block down ~16px versus
   every other state ('a_decorrer'/'terminado'), even though total card
   height already matched via .gra-row-bs's flex stretch - verified live
   (getComputedStyle on the tag showed margin-bottom:16px for 'alert' only,
   0 for 'success'/'danger'). Root cause: cssnovo-style.css also bundles
   Bootstrap, whose OWN unrelated `.alert` component class sets
   margin-bottom:1rem - the DS's "alert" color-weight name for tags
   collides with Bootstrap's own component class name, so any
   `.gra-tag...alert` picks up that stray 16px. Scoped reset here (not a
   cssnovo-style.css edit, and not touching `.alert` sitewide) since this
   header row only ever holds this card's estado/tipo tags.

   Remaining ~4px still came from the estado tag's OWN weight: 'secondary'
   (agendado/terminado) is cssnovo's outline style, 2px border added on
   top of the same 8px/12px padding 'primary' (a_decorrer) uses without a
   border, so the row itself was 36.8px tall (16px padding + 16.8px
   line-height + 4px border) for agendado/terminado cards vs 32.8px for
   a_decorrer/no-estado ones - a fixed difference by state, not by event
   content, so no per-card variance could fix it. Pinning the row to the
   taller value (same "reserve the real rendered size" technique as
   .partner-card-footer above) and centering makes every state's row -
   and therefore every title/description/info block below it - start at
   the exact same position regardless of which estado tag's outline vs
   filled. */
.agenda-card-tags {
  min-height: 36.8px;
  align-items: center !important;
}
.agenda-card-tags .gra-tag {
  margin-bottom: 0 !important;
}

/* Same stray Bootstrap `.alert` margin-bottom:16px described above, hitting
   the Agenda's tabela view instead of the card grid (2026-09) - the estado
   tag sat visibly higher than the tipo tag in the same row (both centered
   via the td's default vertical-align:middle, but the estado tag's real
   layout box - margin included - was 16px taller), confirmed live via
   getComputedStyle on both tags in the same row. #agenda-tabela is the
   real table id (agenda-lista.php), reused as-is by parceiro.php's
   compacto Eventos tab. */
#agenda-tabela .gra-tag {
  margin-bottom: 0 !important;
}

/* ferramentas.php card title/description: FIXED-size content slots, same
   technique and reasoning as .iniciativa-card-title/-text above. A first
   attempt here used height:100% on the card itself (stretching every card
   in the row to match whichever one is naturally tallest) - wrong
   direction per explicit feedback: that grows the SHORT card with blank
   space instead of constraining the LONG one. Giving title/description a
   fixed height instead (2 lines each: 57.6px title at cssnovo's default
   24px/28.8px, 48px description at its 16px/24px) makes every card's
   natural content height identical up front, so no card-level stretch is
   needed at all - the longer description is what gets truncated/clamped,
   never the shorter card padded out. */
.gra-card-informative.card-vertical .details .ferramenta-card-title {
  height: 57.6px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.gra-card-informative.card-vertical .details .ferramenta-card-text {
  height: 48px !important;
  max-height: 48px !important;
  overflow: hidden !important;
  text-overflow: ellipsis;
  display: -webkit-box !important;
  -webkit-line-clamp: 2 !important;
  -webkit-box-orient: vertical !important;
}

/* galeria.php card title: same fixed 2-line slot technique as
   .iniciativa-card-title, so a 1-line album title and a 2-line one still
   leave every card in the row the same height (no card-level stretch). */
.gra-card-informative.card-vertical .details .galeria-card-title {
  height: 57.6px;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* agenda.php's table pushes the whole page wider than the viewport:
   .contentPageText sizes itself with max-width:fit-content, and even
   though .gra-table-wrapper has its own overflow-x:auto, its intrinsic
   (max-content) width still counts toward the fit-content calculation
   on its ancestor - so the card, and the whole page, stretch to fit
   the table's natural width instead of the table scrolling inside a
   normally-sized card. Forcing an explicit width stops it contributing
   an oversized intrinsic size upward. */
/* cssnovo-style.css sets .gra-table-wrapper's overflow-x:auto alone, with
   no overflow-y of its own - per the CSS overflow spec, a non-visible
   overflow-x with overflow-y left at its 'visible' default forces
   overflow-y to compute as 'auto' too (the two can't be visible/
   non-visible on different axes), turning this into an unintended
   vertical scroll box wherever page layout gives it less height than its
   content needs. Only horizontal scrolling for an over-wide table is
   ever wanted here - reasserted explicitly so content is never clipped
   vertically (e.g. espaco.php's Salas & Gabinetes table). */
.gra-table-wrapper {
  width: 100%;
  overflow-y: visible;
}

/* .contentPageText sizes itself with max-width:fit-content - it shrinks
   or grows to hug whatever content happens to be inside it. That's
   fine (even desired - the legal/text pages read as a narrower centered
   column on purpose, via their own inline max-width:900/700px on the
   .gra-container inside), EXCEPT on pages whose entire body is a
   dynamic grid/table with no long free-text paragraph to dominate the
   fit-content calculation - a sparse result (FAQ with no matches for a
   category, the one-tool Ferramentas page, a Galeria album with 1-2
   photos, a Serviços tag filtered to a single result) then collapses
   the whole card to a tiny box floating mid-page instead of staying
   the grid's normal width. Scoped to just those pages via
   .contentPageText-wide (currently: parceiros, faq, ferramentas,
   agenda, galeria, galeria-album, servicos' list view, iniciativas,
   espaco - its own Espaços Disponíveis table has the same fit-content
   leak, worse now that it flips between a table and a card grid on
   ?vista= too) -
   iniciativas specifically also flips between its own card grid and a
   table on ?vista= same as agenda, so without this the whole page's
   width visibly jumped every time that toggle was clicked. Do not widen
   this to every page, that would blow out the legal pages' intentionally
   narrower reading column into a lot of dead padding down one side.
   Capped at 1472px (not max-width:none) to match exactly what the
   normal fit-content pages settle at on their own (measured live:
   .gra-container's own 1360px cap + the card's 2x56px padding) - a
   first attempt at width:100%/max-width:none fixed the "collapses too
   small" bug but on a wide monitor made these pages stretch edge to
   edge with no side gutter, unlike every other page. */
.contentPageText-wide {
  max-width: 1472px !important;
  width: 100%;
}

/* Sidebar link lists inside a .gra-card-informative (servicos.php's "Outros
   Serviços", iniciativa.php's "Outras notícias"): plain rows with a
   bottom-border separator, no feedback at all on hover/focus so a visitor
   can't tell which row they're about to click. The card's own background is
   already blue-100, so a hover of blue-200 (one ramp step, the card's own
   :hover shade) reads as barely-there against it - blue-300 is the smallest
   jump that's actually visible at this scale, paired with a small slide so
   the row change registers even for anyone who can't tell the two blues
   apart. */
.gra-sidebar-list-link {
  border-radius: 8px;
  transition: background-color .15s ease, transform .15s ease;
}
.gra-sidebar-list-link:hover,
.gra-sidebar-list-link:focus-visible {
  background-color: var(--color_blue_300);
  transform: translateX(4px);
}
.dark-mode .gra-sidebar-list-link:hover,
.dark-mode .gra-sidebar-list-link:focus-visible {
  background-color: var(--color_gray_700);
}

/* Footer compliance logos (PRR/GRA/RP/UE/C19): gra-design-system.mjs ships
   its OWN dark/light logo-swap for anything classed .gra-logo-gov/-prr/
   -c19/-rp/-ue/-incuba - on every page load and theme toggle it force-sets
   these images' src to hardcoded paths under /img/logos/(darkmode/)... that
   don't exist in this project (this site's real logos live under
   assets/images/logo/, a different asset set entirely), silently
   overwriting whatever src footer.php had just set and leaving the image
   broken. footer.php renames these to footerLogo* instead of matching
   the vendor's class names, so its own light/dark <img> pair is left
   alone - only .footerLogoGov needs a replacement for the sizing
   .gra-logo-gov used to carry (the other four already size correctly via
   the generic .gra-logos img/.logosLeft img wrapper rules, untouched by
   this rename). */
.footerLogoGov {
  max-width: 132px;
  width: auto;
  margin-right: 12px;
  height: 32px;
}

/* ==========================================================================
   5. TEMA INCUBA - ACENTO AMARELO DO FRONTOFFICE
   5.1 a 5.4: cabeçalhos, ligações, botões, painéis, componentes e polimento.
   Todas as regras levam a guarda do Frontoffice; lêem os tokens --brand-*.
   ========================================================================== */

/* ============================================================
   5.1  INCUBA THEME — Frontoffice yellow accent (live default)
   ============================================================
   Approved 2026-08-31 after visual review via incuba-theme-preview.php —
   see docs/superpowers/specs/2026-08-31-frontoffice-incuba-yellow-theme-
   design.md. The temporary `.incuba-theme` preview gate has been removed;
   every rule below now applies to every Frontoffice body by default. Only
   exclusion of the two Backoffice body classes remains,
   `.templateDetailsBackoffice` / `.bo-login-body` (já sem uso; ver nota no topo) (confirmed exhaustive via
   grep across backoffice/** — no third variant exists), so the Backoffice
   can never match any rule below.

   Role-based remap, not a blue->yellow search/replace: measured live,
   text-on-white can only use yellow-900 and still clear WCAG AA (4.5:1) —
   every lighter yellow step fails on a white background (yellow-800 is
   3.78:1) — so light-mode headings and light-mode secondary/link text
   intentionally share that one step. Dark-mode text has real headroom
   (multiple steps clear AA on near-black/gray-900), so headings
   (yellow-300, softer/lighter for large display text) and interactive
   accents (yellow-400 — the closest official step to the #FFD304 brand
   reference — for links/buttons/active-state) get their own distinct
   steps there. ============================================================ */

/* Headings + secondary/accent text — LIGHT (Incuba Light). `.gra-text-blue-
   900` added in the completion pass: sobre.php's timeline star icon and
   area-membro.php both use it, and it was missing from this group even
   though its dark-mode counterpart already covers it a few rules below -
   an oversight in the original rollout, not a deliberate exclusion.
   Bumped from yellow_900 to yellow_400 per explicit request: reuse the
   exact same step as the Área de Membro nav active state (see navbar.php's
   `.userBorderLeft.active .credentials.job` rule) instead of the AA-safe
   yellow_900 this group used before. yellow_400 fails WCAG AA on a white
   background (measured, see the "Frontoffice yellow accent" header comment
   above for the original AA rationale) - the request was made with that
   contrast tradeoff acknowledged, so !important here is a deliberate
   accessibility regression, not an oversight. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-800,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-700,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-500,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-600,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-900 {
  color: var(--brand-accent) !important;
}

/* Headings + secondary/accent text — DARK (Incuba Dark, default). blue-500/
   600/900 were already on yellow_400; blue-800/700 bumped up from yellow_300
   to match, same "reuse Área de Membro active state's exact step" request
   as the LIGHT block above - one shared step for the whole group now
   instead of two. */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-text-blue-800,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-text-blue-700,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-text-blue-500,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-text-blue-600,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-text-blue-900 {
  color: var(--brand-accent) !important;
}

/* Links — LIGHT. `.pageContent p a` (admin rich-text content links - FAQ
   answers, legal pages, iniciativa/evento bodies) added in the completion
   pass: the DARK-mode pair for this selector already existed below
   (`.pageContent p a{color:yellow_400}` under Links — DARK), but light
   mode was never given the equivalent fix - only the classed `.gra-link`
   was, leaving these bare content links on blue_800 in light mode
   (live-verified on faq.php's accordion answers). */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-link,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .pageContent p a {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-link:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-link:focus-visible {
  color: var(--brand-text-on-light) !important;
  text-decoration: underline;
}

/* Links — DARK */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-link,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .pageContent p a {
  color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-link:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-link:focus-visible {
  color: var(--brand-accent-soft) !important;
  text-decoration: underline;
}

/* Chrome exceptions: the footer strip and the navbar both have their own
   fixed text color regardless of theme (footer stays black-on-neutral in
   dark mode — untouched here on purpose, see below; navbar stays legible
   against its own chrome background — see below). The general .gra-link
   rules above have a longer selector chain than cssnovo-style.css's own
   footer/navbar link rules, so without re-asserting these under the same
   Incuba guard, the general rule would win and repaint them the wrong
   color. Re-declared in both modes for symmetry/safety even though only
   the dark-mode pair is known today to need it (mirrors this file's own
   pre-existing .dark-mode .gra-footer-opt-2 .gra-link / .dark-mode
   .header-primary .gra-link exceptions above — same pattern, Backoffice-
   excluded). */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-footer-opt-2 .gra-link {
  color: var(--brand-on-chrome) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-footer-opt-2 .gra-link {
  color: var(--brand-on-chrome-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header.header-primary .gra-link {
  color: var(--brand-on-chrome) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-desktop-header.header-primary .gra-link {
  color: var(--brand-on-chrome) !important;
}

/* Primary button — LIGHT. Contrast (#000 text): yellow-400 14.79:1,
   yellow-500 ~11:1, yellow-600 9.92:1 — all clear AA (4.5:1) by a wide
   margin. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-primary {
  background-color: var(--brand-accent) !important;
  border-color: var(--brand-accent) !important;
  color: var(--brand-on-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-primary:hover {
  background-color: var(--brand-accent-hover) !important;
  border-color: var(--brand-accent-hover) !important;
  color: var(--brand-on-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-primary:active {
  background-color: var(--brand-accent-active) !important;
  border-color: var(--brand-accent-active) !important;
  color: var(--brand-on-accent) !important;
}

/* Primary button — DARK. cssnovo-style.css's own non-theme-marca-servicos
   dark-mode variant is gray_200 base / gray_400 hover / blue_300 active —
   none of it is the Incuba accent, so every state is remapped here, not
   just the one existing blue hit. */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-primary {
  background-color: var(--brand-accent) !important;
  border-color: var(--brand-accent) !important;
  color: var(--brand-on-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-primary:hover {
  background-color: var(--brand-accent-hover) !important;
  border-color: var(--brand-accent-hover) !important;
  color: var(--brand-on-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-primary:active {
  background-color: var(--brand-accent-active) !important;
  border-color: var(--brand-accent-active) !important;
  color: var(--brand-on-accent) !important;
}

/* Form input focus ring — cssnovo-style.css hardcodes this to raw Bootstrap
   blue (#86b7fe / #0d6efd40), not a DS token, so it needs its own override.
   rgba(255,212,81,.25) is yellow-400 (#FFD451) with alpha appended the same
   way the vendor rule appends alpha to its own blue — same technique,
   Incuba's token instead, not a new color. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .form-control:focus {
  border-color: var(--brand-accent) !important;
  box-shadow: 0 0 0 .25rem var(--brand-accent-focus) !important;
}

/* Agenda "De"/"Até" date filter icon — .gra-daterangepicker-container::before
   (the calendar glyph) renders in DS blue (blue_800 light / blue_300 dark,
   per gra-design-system-master's datePicker.scss) — needs the same yellow
   accent as the rest of this page's inputs, not the DS default. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-daterangepicker-container:before {
  color: var(--brand-accent) !important;
}

/* Agenda "De"/"Até" date filter input — hover/open state. The sitewide
   .form-control:focus rule right above gives every other text input a
   yellow border + glow box-shadow, but the page's own Select2 dropdowns
   (.select2 .select2-selection:hover / .select2-container--open above)
   use a plain border-color swap with NO glow — this input needs to match
   THAT pattern (it sits right next to those dropdowns in the same filter
   row), not the glow. Also overrides the vendor's own JS-set `.focus`
   class (assets/js/gra-design-system.mjs's `show.daterangepicker` handler
   adds it to daterangepickerInput on open, separately from :focus, with
   border-color:blue_500/300 from gra-design-system-master's datePicker.scss)
   which the box-shadow rule's selector never touched anyway. Same
   yellow_400 token in both themes, same as the Select2 rules above. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .daterangepickerInput:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body) .daterangepickerInput.focus,
body:not(.templateDetailsBackoffice):not(.bo-login-body) .daterangepickerInput:focus {
  border-color: var(--brand-accent) !important;
  box-shadow: none !important;
}

/* Light decorative tint backgrounds — LIGHT mode only. Dark mode's existing
   .dark-mode .gra-background-blue-100 { background-color: var(--color_gray_900) }
   rule (earlier in this file) is deliberately left alone — it's already a
   neutral structural treatment, not a blue accent, so there's nothing to
   remap there. The explicit :not(.dark-mode) below guarantees these two
   rules can never apply in dark mode even though this guard chain is more
   specific than that existing dark-mode rule. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-background-blue-100 {
  background-color: var(--brand-tint-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-background-blue-200 {
  background-color: var(--brand-tint-2) !important;
}

/* Navbar chrome — approved 2026-08-31: background moves from blue (blue_800
   light / blue_900 dark) to a neutral dark, since the navbar is structural
   chrome, not the accent itself. The active nav-item's color/text-shadow
   trick (see this file's own comment above on why the text-shadow half
   exists) moves from plain white to the yellow-400 accent — measured
   7.08:1 on gray_800, comfortably clears AA.
   Anchor-targeting fix: the rule below originally set `color` only on
   `li.menu-first-level.active` itself. That never painted the visible
   label, because cssnovo-style.css's own real rule for this exact label,
   ".header-primary .menus_desktop ul.menu li.menu-first-level:not(.gra-
   flex-row) a{color:#fff}", sets `color` directly on the <a> the text
   actually lives in - an element's own explicit color always wins over
   whatever its ancestor resolves to, !important or not, since inheritance
   only ever fills in a property no rule set directly on that element.
   Confirmed live: the <li> itself DID compute yellow-400 (so the text-
   shadow half above, which the <a> never overrides, always rendered
   correctly), but the <a> - and so the actual glyph color - stayed vendor
   white regardless. Re-targeting the <a> directly, the same way the
   vendor's own rule does, is what actually makes the label yellow. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header.header-primary {
  background-color: var(--brand-chrome-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-desktop-header.header-primary {
  background-color: var(--brand-chrome-2) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header.header-primary ul.menu li.menu-first-level.active,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header.header-primary ul.menu li.menu-first-level.active a {
  color: var(--brand-accent) !important;
  text-shadow: 1px 0 0 var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-desktop-header.header-primary ul.menu li.menu-first-level.active,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-desktop-header.header-primary ul.menu li.menu-first-level.active a {
  color: var(--brand-accent) !important;
  text-shadow: 1px 0 0 var(--brand-accent) !important;
}

/* Footer chrome, LIGHT mode only — approved 2026-08-31. Dark mode's
   existing neutral light-gray-strip/black-text treatment
   (.dark-mode .gra-footer-opt-2, defined in cssnovo-style.css itself) is
   explicitly NOT touched anywhere in this file — no dark-mode selector is
   written here, on purpose, and :not(.dark-mode) below makes that
   impossible to violate by accident. Text stays #fff from the vendor's own
   base rule; the chrome-exceptions block above already preserves .gra-link
   legibility here. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-footer-opt-2 {
  background-color: var(--brand-chrome-1) !important;
}

/* Strong blue-800/900 panels — approved 2026-08-31, resolving the Task 5
   open item. Site-wide inspection of every call site (contactos.php location
   header, candidatura.php info card, index.php hero, espaco.php CTA panel,
   parceiros.php section, the non-final dots in sobre.php's timeline) found
   one consistent role: a strong, solid, always-dark panel with white text —
   same "structural chrome, not the accent itself" role as the navbar/footer
   (see above), not role-based heading/link/button accent text. So it gets
   the same treatment: neutral gray, not yellow, at the matching step
   (blue-800 was the lighter of the two navy shades, blue-900 the darker —
   gray-800/gray-900 preserve that same two-step relationship). Unlike the
   navbar/footer, the original blue-800/900 utility classes are flat
   (cssnovo-style.css defines no `.dark-mode` variant for them — confirmed
   via grep, they render identically regardless of overall page mode), and
   --color_gray_800/900 are themselves flat :root values (also confirmed via
   grep — no `.dark-mode` override exists for them either), so a single
   un-split rule is the faithful equivalent: same constant weight as before,
   just recolored from the navy family to the neutral family. Contrast:
   white text 10.05:1 (gray-800) / 14.55:1 (gray-900), both far past AA.

   Two call sites keep the same class but play a different role, and are
   deliberately NOT exempted — PHP/markup changes are out of scope for this
   theme, and the DS itself offers no separate class to distinguish them:
   - area-membro.php's two Azor.ID icon chips (`gra-background-blue-800`
     sized 32/40px, holding a lock icon / the azorid_branco logo) — this
     isn't Incuba's own accent, it's colored icon-chip decoration for one
     external identity provider (see area-membro.php's own comment on
     Azor.ID vs Entra ID). It goes gray along with every other blue-800
     instance; a cosmetic, non-functional side effect, not a regression —
     the sibling Entra ID option already renders as a bordered/neutral chip,
     not a colored one, so this now reads as symmetrically neutral instead
     of asymmetrically blue-vs-neutral.
   - sobre.php's timeline: the final/current milestone dot uses the native,
     off-limits `gra-background-yellow-500` (never touched by this theme);
     every earlier milestone dot uses `gra-background-blue-800`, now gray —
     which if anything sharpens the intended contrast between "current"
     (yellow, unmistakable) and "past" (neutral), where before both read as
     similarly-saturated accent colors. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-background-blue-800 {
  background-color: var(--brand-chrome-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-background-blue-900 {
  background-color: var(--brand-chrome-2) !important;
}

/* Companion secondary/muted text for the panels above. `.gra-text-blue-200`
   (cssnovo-style.css: flat #E1ECF2, no `.dark-mode` variant either) is used
   nowhere in the Frontoffice except as body/label text directly inside a
   `.gra-background-blue-800`/`-900` panel (confirmed via grep across every
   Frontoffice page) — it exists purely as "light text on the dark panel
   above", so it follows the same panel recolor rather than becoming a
   yellow accent (this is not heading/link/button text). `--color_gray_200`
   is likewise flat (#E8E8E8, no `.dark-mode` override), so one rule covers
   both modes. Contrast against the new panel backgrounds: 8.20:1 (gray-800)
   / 11.87:1 (gray-900), both comfortably past AA — an improvement over the
   original blue-200-on-blue-800 pairing (6.87:1). */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-text-blue-200 {
  color: var(--brand-text-on-dark) !important;
}

/* ============================================================
   5.2  Component-level blue found via live Chrome/DOM audit — approved
   2026-08-31, completing the rollout. Everything in Tasks 1-9 above only
   ever targeted the reusable `.gra-text-blue-*`/`.gra-background-blue-*`
   utility classes; live-rendering every representative page (not just the
   preview page's sampler) surfaced several cssnovo-style.css COMPONENT
   rules that hardcode the same blue tokens directly under their own class
   names, bypassing every rule above entirely. Each is resolved on the same
   role-based logic as the rest of this file, not a blanket sweep — see the
   per-rule reasoning below. */

/* `.templateDetailsA` — the `<main class="pageContent templateDetailsA">`
   wrapper used on ~26 Frontoffice pages (agenda.php, evento.php,
   iniciativa.php, area-membro.php, candidatura.php, 404.php, etc. —
   confirmed via grep), rendering as a full-width page-background wash
   behind the white `.contentPageText` card. Same role, same source token
   (`--color_blue_200`) as the "light decorative tint backgrounds" already
   approved in the panels section above — this is that same rule, just
   under a different selector name cssnovo-style.css never expressed as the
   `.gra-background-blue-200` utility class. `.dark-mode .templateDetailsA`
   already uses `--color_gray_800` natively (untouched, confirmed via grep)
   so only light mode needs the fix, exactly mirroring the tint-background
   precedent. Live-verified on agenda.php. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .templateDetailsA {
  background-color: var(--brand-tint-2) !important;
}

/* `.gra-card-informative` / `.gra-card-text` — two DS card components (21
   and 3 Frontoffice files respectively, confirmed via grep) whose
   non-`.neutral` variant defaults to `--color_blue_100` background
   (`:hover` escalates to `--color_blue_200`), and whose `.picture` icon
   circle uses `--color_blue_100` regardless of variant. Same tint-token
   role as above; `.dark-mode` already goes neutral (`--color_gray_900`/
   `-800`, confirmed via grep, untouched here), so light mode gets the same
   yellow-100/200 tint-escalation pattern already established. The
   `.neutral` variant's own background (`#fff`, already whitelisted) must
   stay untouched, so the base-color rules below explicitly exclude it
   (`:not(.neutral)`) — the `:hover` rules do NOT, because cssnovo-style.css
   itself uses the identical blue_200 for both variants' hover state
   (`.gra-card-informative.neutral:hover` is a real, separate vendor rule
   equal to the base one; `.gra-card-text` has no `.neutral:hover` override
   at all, so its base `:hover` rule already covers both variants) — only
   the base/non-neutral variant and the shared `.picture` icon circle
   (present in both variants) needed a base-color rule. Live-verified: the
   preview page's own sampler only exercises `.neutral`, which is why this
   gap wasn't caught until real pages (index.php's Pilares icons, area-
   membro.php's "Identidades ligadas" card) were checked — and an earlier
   version of this exact rule, without `:not(.neutral)`, was caught turning
   the Pilares cards' white `.neutral` background yellow before this was
   committed. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-card-informative:not(.neutral) {
  background-color: var(--brand-tint-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-card-informative:hover {
  background-color: var(--brand-tint-2) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-card-text:not(.neutral) {
  background-color: var(--brand-tint-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-card-text:hover:not(.disabled) {
  background-color: var(--brand-tint-2) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-card-text .picture {
  background-color: var(--brand-tint-1) !important;
}

/* Completion pass: the icon glyph inside that same `.picture` circle
   (homepage/sobre.php Pillars - Inovação/Modernização/Simplificação) has no
   color class of its own, so it falls through to cssnovo-style.css's global
   icon default (`[class^="icon-"]{color:blue_800}`) rather than anything
   this file already targets - text-on-light-tint role, same yellow-900
   step used everywhere else for that. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-card-text .picture i {
  color: var(--brand-text-on-light) !important;
}

/* Sitewide fix, generalizing what used to be an index.php-only `#pilares`
   rule: `.gra-card-informative`/`.gra-card-text` are used two ways across
   the Frontoffice (confirmed via grep - every instance in every file, and
   via grep -rPzo that no `<a>`/`<button>` ever wraps a `<div>`/`<nav>`
   instance) - as a real `<a>` (or, once, a `<button class="openPopup">`)
   that navigates/opens something, or as a plain `<div>`/`<nav>` panel with
   no click target of its own (info tiles like this - Inovação/
   Modernização/Simplificação/Missão on both index.php AND sobre.php,
   Espaços' Comodidades/Horário/Localização, sidebar "Detalhes"/"Outros
   eventos" boxes, form panels, the reserva.php calendar shell, etc.). The
   old `#pilares`-scoped rule wrongly assumed sobre.php/candidatura.php's
   own copies of these same tiles were genuinely clickable and left them
   lit up on hover - they aren't.

   `pointer-events:none` (a first pass at this, and this rule's own
   previous version) is the wrong tool here: besides suppressing `:hover`
   it also disables mouse text selection, right-click, and any inner
   interactive element that doesn't get pointer-events explicitly re-armed
   - a real behavior change, not just a visual one. This version only ever
   touches `cursor` (unconditional - div/nav never had a pointer cursor to
   begin with, but this makes "not clickable" explicit rather than
   incidental) and, at `:hover`, re-declares the exact same value each
   card variant's OWN resting rule already uses right above/below - so
   hovering one of these panels renders identically to not hovering it.
   Text selection, focus, forms, Select2, checkbox-groups, buttons, links -
   all untouched, since nothing here is `pointer-events`. `<a>`/`<button>`
   instances never match these selectors (tag-restricted), so real
   clickable cards keep their normal cursor and hover exactly as-is. */
div.gra-card-informative,
nav.gra-card-informative,
div.gra-card-text,
nav.gra-card-text {
  cursor: default;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) div.gra-card-informative:hover:not(.neutral),
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) nav.gra-card-informative:hover:not(.neutral) {
  background-color: var(--brand-tint-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) div.gra-card-informative.neutral:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) nav.gra-card-informative.neutral:hover {
  background-color: var(--brand-surface-light) !important;
}
.dark-mode div.gra-card-informative.neutral:hover,
.dark-mode nav.gra-card-informative.neutral:hover {
  background-color: var(--brand-surface-dark-raised) !important;
}

/* cssnovo-style.css also has a dark-mode hover rule for the base (non-
   `.neutral`) `.gra-card-informative` - `.dark-mode .gra-card-informative
   :hover,.dark-mode .gra-card-informative.disabled{background:gray_800}`,
   easy to miss since it's bundled on one line with the unrelated
   `.disabled` selector - confirmed via direct byte inspection of the
   minified file, not just the usual grep-body extraction. */
.dark-mode div.gra-card-informative:hover:not(.neutral),
.dark-mode nav.gra-card-informative:hover:not(.neutral) {
  background-color: var(--brand-surface-dark-raised) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) div.gra-card-text:hover:not(.disabled):not(.neutral),
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) nav.gra-card-text:hover:not(.disabled):not(.neutral) {
  background-color: var(--brand-tint-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) div.gra-card-text.neutral:hover:not(.disabled),
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) nav.gra-card-text.neutral:hover:not(.disabled) {
  background-color: var(--brand-surface-light) !important;
}

/* Dark mode: cssnovo-style.css's own rules here have no `!important`, so
   these win outright without needing to out-specify them. Resting values
   confirmed via direct byte inspection of the minified file (not just
   grep-body extraction, which reads misleadingly clean but drops
   ancestor-selector context) - `.picture`'s own resting background is
   `gray_800` for the non-`.neutral` variant (its hover rule bumps it to
   `gray_900`, a real visible change, unlike what an earlier version of
   this comment assumed) but already `gray_900` for `.neutral` (so its
   shared hover rule, same `gray_900`, really is a no-op there - no cancel
   needed for that one case). */
.dark-mode div.gra-card-text:hover:not(.disabled),
.dark-mode nav.gra-card-text:hover:not(.disabled) {
  background-color: var(--brand-surface-dark-raised) !important;
}
.dark-mode div.gra-card-text.neutral:hover:not(.disabled),
.dark-mode nav.gra-card-text.neutral:hover:not(.disabled) {
  background-color: var(--brand-surface-dark) !important;
}
.dark-mode div.gra-card-text:hover:not(.disabled):not(.neutral) .picture,
.dark-mode nav.gra-card-text:hover:not(.disabled):not(.neutral) .picture {
  background-color: var(--brand-surface-dark-hover) !important;
}
.dark-mode div.gra-card-text:hover:not(.disabled) .details .title,
.dark-mode nav.gra-card-text:hover:not(.disabled) .details .title {
  color: var(--brand-text-on-dark) !important;
}
.dark-mode div.gra-card-text:hover:not(.disabled) .picture i:before,
.dark-mode nav.gra-card-text:hover:not(.disabled) .picture i:before {
  color: var(--brand-text-on-dark) !important;
}

/* Bare icons nested inside an already-neutral `.gra-text-gray-400` label
   (team-grid.php's "Ver perfil" arrow, and any other icon+muted-caption
   pairing that follows the same markup pattern): with no color class of
   their own they fall through to cssnovo-style.css's global icon default
   (`[class^="icon-"]{color:blue_800}`, an explicit rule that beats plain
   inheritance) instead of matching their own label text. `inherit` makes
   the icon follow whatever `.gra-text-gray-400` already resolves to
   (light: `--color_gray_500`, already fixed above) rather than hardcoding
   a second value that could drift from it. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-gray-400 i,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-gray-400 i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-500 i,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-500 i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-600 i,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-600 i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-700 i,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-700 i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-800 i,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-800 i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-900 i,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-text-blue-900 i::before {
  color: inherit !important;
}

/* Icons nested inside `.gra-btn.btn-primary`/`.btn-secondary` (espaco.php's
   "Ver Localização" buttons and any other icon+label button) - same "no
   color class of its own, falls through to the global icon default"
   pattern as every fix above, live-verified against these two button
   variants specifically. `inherit` matches the button's own already-fixed
   text color (`#000` primary / `--color_yellow_900` secondary) rather than
   hardcoding a duplicate value. `.dark-mode` wasn't affected (confirmed via
   the same live audit - no fix needed there). */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-primary i,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-secondary i {
  color: inherit !important;
}

/* `.icon.icon-primary` (the "active" state of espaco.php/iniciativas.php/
   agenda.php/galeria.php's card/table view-toggle buttons) - cssnovo-
   style.css hardcodes this to `--color_blue_800` background / `#fff` text,
   unconditional light default. `.dark-mode` already goes neutral (`--color_
   gray_200` bg / `#000` text, confirmed via grep, untouched here) - light
   mode gets the same solid-accent-active-state treatment already used for
   the primary button and the active pagination chip (yellow-400 fill,
   black text/icon). footer.php's `.backToTop` (same base class) already
   overrides this locally with its own higher-specificity `!important` rule
   - unaffected either way. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-primary {
  background-color: var(--brand-accent) !important;
  color: var(--brand-on-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-primary i:before {
  color: var(--brand-on-accent) !important;
}

/* `.gra-table thead` text color (sortable column headers - espaco.php's
   Salas & Gabinetes table and every other Frontoffice list table) -
   cssnovo-style.css hardcodes this to `--color_blue_800`, unconditional
   light default; the header CELL background is already fixed above
   (`.gra-table-wrapper .gra-table thead tr th`), but the text color itself
   was missed. `.dark-mode` already goes neutral (`--color_gray_200`,
   confirmed via grep, untouched here) - light mode gets the same
   text-on-light-tint yellow-900 step already used for the header
   background's own pairing (the accordion/tags/badges above). */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-table thead {
  color: var(--brand-text-on-light) !important;
}

/* Icons nested inside a `.gra-link` (table action "view" icon-only links -
   agenda.php/iniciativas.php/galeria.php - and any other `.gra-link` that
   pairs an icon with its text) - same "no color class of its own, falls
   through to the global icon default" pattern as every icon fix above,
   confirmed live via the icon's own `::before`. `inherit` matches
   whatever `.gra-link` itself already resolves to in each mode (both
   already fixed above) rather than a third hardcoded value. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-link i,
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-link i::before {
  color: inherit !important;
}

/* `.gra-filter .gra-filter-content .caption` (search-box helper text -
   "Exemplos: título, descrição." on iniciativas.php/agenda.php/galeria.php)
   - `--color_blue_800`, unconditional light default; `.dark-mode` already
   goes neutral (`--color_gray_200`, confirmed via grep, untouched here) -
   light mode gets the same text-on-white yellow-900 step used everywhere
   else for that role. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-filter .gra-filter-content .caption {
  color: var(--brand-text-on-light) !important;
}

/* `.parceiro-conteudo-divider` (parceiro.php's Notícias/Eventos section
   rule - renamed from `.projetos-conjuntos-divider` when that single mixed
   "Projetos Conjuntos" grid was split into the two separate sections) -
   previously an inline `border-top:1px solid var(--color_blue_200)`,
   unconditional in both modes (no `.dark-mode` variant ever existed for
   it, live-verified). A structural section divider, not an accent - moved
   to a class here so it can get the same light/dark neutral pairing every
   other divider in this file already uses. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .parceiro-conteudo-divider {
  border-top: 1px solid var(--color_gray_300);
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .parceiro-conteudo-divider {
  border-top: 1px solid var(--brand-border-dark);
}

/* Sorting controls ("Ordenar por" on agenda.php/galeria.php/iniciativas.php)
   - live-verified: gra-design-system.mjs wires these `<select>`s up as a
   Select2 widget, which hides the original element and renders its own
   `.select2-container` sibling span with an inline `style="width:100%"`,
   ignoring the original element's intended `style="width:180px/220px"`.
   That span sits inside a `.d-flex` toolbar row alongside the "Ordenar
   por" label, the per-page pills, and the view-toggle icons - as a flex
   item it also has the default `flex-shrink:1`, so even after forcing the
   width back with `!important` (beating the non-important inline style,
   which a stylesheet `!important` always does regardless of specificity)
   it still visually compressed below that width whenever the row ran out
   of space (measured 114px against a 180px target). `flex-shrink:0` stops
   that second squeeze. Together these fix the measured symptom: overflowing
   text and the built-in clear/dropdown-arrow icons crammed against it.
   Widths matched to each select's own original intended value. */
#agenda-ordenar + .select2-container {
  width: 220px !important;
  flex-shrink: 0 !important;
}
#galeria-ordenar + .select2-container,
#iniciativas-ordenar + .select2-container {
  width: 180px !important;
  flex-shrink: 0 !important;
}

/* `.graAvatarWithText` — the account-menu avatar's colored-initials circle
   (area-membro.php), `--color_blue_200` background, same tint-token role
   as above; `.dark-mode` already goes neutral gray (confirmed via grep),
   untouched here. The initials text itself (`--color_blue_900`) is left
   alone: against the very pale resulting yellow-200 fill it reads simply
   as dark, legible text, the same job `--color_gray_900`/`#000` do
   elsewhere in this file for text-on-yellow — not a leftover brand-blue
   accent, so remapping it isn't warranted by the same test this whole file
   applies everywhere else. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .graAvatarWithText {
  background-color: var(--brand-tint-2) !important;
}

/* Bare `<label>` — cssnovo-style.css's global `label{color:var(--color_
   blue_800)}` rule (unscoped, no `.gra-text-blue-800` class involved)
   styles every unclassed form label site-wide — confirmed live on area-
   membro.php's "Palavra-passe"/E-mail labels rendering blue_800 in light
   mode despite Task 1 already covering the classed `.gra-text-blue-800`
   everywhere it's used explicitly. `.dark-mode label` already goes neutral
   (`--color_gray_200`, confirmed via grep and confirmed visually — dark
   mode never showed this bug), so only light mode needs it, using the
   exact same target Task 1 already uses for this same source token
   (`--color_yellow_900`) so a labeled field's text matches the treatment
   every other blue-800 heading/accent text on the page already gets. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) label {
  color: var(--brand-text-on-light) !important;
}

/* `.templateIniciarSessao` — area-membro.php's own login/registration
   screen backdrop. IMPORTANT, live-verified: this class lives on a wrapper
   `<div>` (not `<body>`) — see this same file's own pre-existing comment
   above (~line 92) explaining why: the vendor's `body.templateIniciarSessao`
   selectors never match here on purpose, so the earlier GRA migration work
   already re-declared it unscoped, directly above, with the DS's own
   `var(--color_blue_400)` standing in for the stock photo this project
   doesn't hotlink. A first version of this rule here wrote
   `body(...).templateIniciarSessao` (compound, requiring the class on
   `<body>` itself) — dead code, verified live it changed nothing, exactly
   the mismatch the comment above warns about. Fixed to a descendant
   selector matching the real markup. Role: `--color_blue_400` at full
   saturation is a genuine medium-strength brand blue, not the "light tint"
   token family — same "solid panel behind a white card" role as the
   navbar/footer/blue-800/900 panels already handled above, so neutral
   gray, not yellow. `.dark-mode .templateIniciarSessao` (pre-existing,
   directly above) already goes neutral (`--color_gray_200`), so only
   light mode needs this. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .templateIniciarSessao {
  background-color: var(--brand-chrome-1) !important;
}

/* `.gra-table-wrapper .gra-table` header row and zebra striping — live-
   verified on agenda.php: the always-visible (not a hover/interaction
   state) header background and every second data row use `--color_blue_
   200`/`--color_blue_100` respectively, the same light-tint tokens as
   every rule above, directly beside the now-yellow `.templateDetailsA`
   page wrapper on the same page — leaving these blue would have looked
   like a half-finished fix, not a deliberate design choice. `.dark-mode`
   already goes neutral (`--color_gray_800`/`-900`, confirmed via grep,
   untouched here). Genuine interaction states on this same table (row
   `:hover`, `--color_blue_300`) are deliberately left alone, along with
   the rest of the shared select2/daterangepicker/notifications/accordion/
   tabs/wizard/tooltip pool found during the audit — those are the DS's
   own system-wide interactive-feedback color, identical in Backoffice and
   Frontoffice alike, not an Incuba brand accent, and out of proportion
   with this theme's scope to re-review component-by-component. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-table-wrapper .gra-table thead tr th {
  background-color: var(--brand-tint-2) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-table-wrapper .gra-table tbody tr:nth-child(2n) td,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-table-wrapper .gra-table tbody tr:nth-child(2n) th {
  background-color: var(--brand-tint-1) !important;
}

/* Secondary button/icon — Task 3 only ever named "Primary button" in its
   title and covered `.gra-btn.btn-primary`; `.gra-btn.btn-secondary`/
   `.btn-default` (the outline "Registar"/"Conhecer o Espaço"-style button)
   and `.icon.icon-secondary` (icon-only variant — navbar.php, agenda.php,
   espaco.php, galeria.php, iniciativa.php, iniciativas.php, pagination.php,
   confirmed via grep) were a real, separate gap: white background with
   `--color_blue_800` border+text, found live on area-membro.php's
   "Registar" button. `.dark-mode` already goes fully neutral (`#000`
   background, `--color_gray_200` border+text, confirmed via grep,
   untouched here) — light mode gets the equivalent Incuba treatment,
   following Task 1's own light-mode rule for this exact role (white-
   background text/border can only use yellow-900 and still clear AA;
   every lighter step fails, so hover/active/focus collapse to the same
   yellow-900 border+text — hover keeps its own feedback via the
   background tint, yellow-100 in place of the vendor's blue-100, the same
   "light tint" pairing used everywhere else in this file). */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-secondary,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-default,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-secondary {
  border-color: var(--brand-text-on-light) !important;
  color: var(--brand-text-on-light) !important;
}

/* Completion pass: cssnovo-style.css also has a separate, MORE specific
   `.icon.icon-secondary i::before{color:blue_800}` rule (live-verified via
   the icon's own `::before` computed style, not just its own `color`
   property) that the block above never reached - pseudo-elements inherit
   `color` from their host by default, but this explicit rule overrides
   that inheritance, so the glyph itself stayed blue even though the icon's
   own color was already correctly yellow-900. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-secondary i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-secondary i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-default i::before {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-secondary:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-default:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-secondary:hover {
  background-color: var(--brand-tint-1) !important;
  border-color: var(--brand-text-on-light) !important;
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-secondary:hover i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-secondary:hover i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-default:hover i::before {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-secondary:active,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-default:active,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-secondary:focus,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-default:focus,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-secondary:active,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-secondary:focus {
  border-color: var(--brand-text-on-light) !important;
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-secondary:active i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-default:active i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-secondary:focus i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-btn.btn-default:focus i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-secondary:active i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .icon.icon-secondary:focus i::before {
  color: var(--brand-text-on-light) !important;
}

/* `.icon.icon-secondary` — DARK (2026-10-14, team-grid.php popup close
   button: live-verified via Playwright getComputedStyle that the button is
   auto-focused the instant a popup opens - document.activeElement is the
   button itself before any click). cssnovo-style.css's dark-mode variant is
   gray_200 base / gray_400 hover / blue_300 on BOTH :active and :focus -
   the exact same "none of it is the Incuba accent" gap already documented
   and fixed above for `.gra-btn.btn-primary` (dark), so this reuses that
   identical yellow_400/500/600 escalation on the vendor's own #000
   background instead of inventing a new ramp. This is why the button
   looked blue on open with no interaction (:focus hit blue_300) and only
   looked neutral (never actually yellow) after a click moved focus away
   (gray_200 base) - fixing the base AND :hover AND :active/:focus together
   is what actually makes it yellow from the moment the popup opens, while
   still leaving a real (just yellow, not blue) focus indicator for keyboard
   navigation - never removed. */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .icon.icon-secondary {
  border-color: var(--brand-accent) !important;
  color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .icon.icon-secondary i::before {
  color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .icon.icon-secondary:hover {
  border-color: var(--brand-accent-hover) !important;
  color: var(--brand-accent-hover) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .icon.icon-secondary:hover i::before {
  color: var(--brand-accent-hover) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .icon.icon-secondary:active,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .icon.icon-secondary:focus {
  border-color: var(--brand-accent-active) !important;
  color: var(--brand-accent-active) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .icon.icon-secondary:active i::before,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .icon.icon-secondary:focus i::before {
  color: var(--brand-accent-active) !important;
}

/* `.gra-accordion` — live-verified on faq.php: its container and collapsed/
   expanded header states are static base colors (`--color_blue_100` fill,
   `--color_blue_300` border, `--color_blue_800` header text), not hover
   states — the same light-tint-plus-text-on-white pattern already fixed
   everywhere else, so it gets the same treatment, extending the 100/200
   tint-escalation family down one more real GRA step (`--color_yellow_300`
   for the border, same numbering as its source token, nothing invented).
   `.dark-mode` already goes fully neutral (confirmed via grep), untouched.
   The header's own `:hover` (`--color_blue_600` fill + white text) is
   deliberately left alone — a genuine interaction-feedback swap, not a
   tint step, matching every other shared-hover-state decision in this
   file. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-accordion .accordion-item {
  background-color: var(--brand-tint-1) !important;
  border-color: var(--brand-tint-3) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-accordion .accordion-item .accordion-header {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-accordion .accordion-item .accordion-header:not([aria-expanded="false"]) {
  background-color: var(--brand-tint-1) !important;
  color: var(--brand-text-on-light) !important;
  border-bottom-color: var(--brand-tint-3) !important;
}

/* Pagination active-page chip and checked checkboxes — two more static
   (not hover) "this is the selected/confirmed state" indicators using
   `--color_blue_900`/`--color_blue_800` solid fills with white icon/text,
   the exact same role Task 3 already gave the primary button's own
   `:active` state (solid yellow fill, black text/icon — every colored fill
   in this theme pairs with black, never white, for the same AA reason).
   `.dark-mode` already goes neutral for both (confirmed via grep:
   pagination inverts to a light `--color_gray_100` chip with black text;
   the checkbox to `--color_gray_200` with a black check), so only light
   mode needs a rule, and only the icon/text pairing needs flipping to
   black alongside the fill — mirroring dark mode's own light-fill/dark-
   icon inversion, not a new decision. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-pagination .navigation button.active {
  background-color: var(--brand-accent-active) !important;
  border-color: var(--brand-accent-active) !important;
  color: var(--brand-on-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .form-check .form-check-input:checked {
  background-color: var(--brand-accent-active) !important;
  border-color: var(--brand-accent-active) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .form-check .form-check-input:checked:after {
  color: var(--brand-on-accent) !important;
}

/* ============================================================
   5.3  Completion pass (real-browser review, live-verified across
   Dark and Light) — closing the remaining brand-blue gaps Tasks
   1-11 above missed. ============================================================ */

/* `.gra-tag.primary.info` — the DS's "type/category" pill (event type,
   room type, partner type, gallery/news/iniciativa tag — ~20 call sites
   across the Frontoffice, confirmed via grep). cssnovo-style.css hardcodes
   its background to a flat navy in light mode (`#103960`) and a flat pale
   blue in dark mode (`#7ebdff`, `.dark-mode .gra-tag.primary.info`), text
   always black (`.gra-tag.primary{color:#000}`, unconditional). Same
   "solid accent chip, black text" role Task 3 already gave the primary
   button — reuses that exact step (yellow-400) for both modes rather than
   inventing a new one, since `.gra-tag.primary.info`'s two hardcoded blues
   already had no light/dark relationship to preserve. The `.secondary`
   variant (danger/alert/success outline tags) and `.primary.success/
   .alert/.danger` (area-membro.php's status badges) are genuinely semantic
   feedback colors, confirmed unused-for-blue via grep — left untouched. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-tag.primary.info {
  background-color: var(--brand-accent) !important;
}

/* Breadcrumbs (`.gra-breadcrumb`, present on ~20 Frontoffice pages) — light
   mode is entirely unstyled by any `.dark-mode` guard in cssnovo-style.css:
   the crumb text (`.bc`), its `:hover`, and the `>` separator icon
   (`.gra-breadcrumb i:before`) all resolve to flat blues (`--color_blue_
   800`/`-500`/`-900`) with no override anywhere in this file. Text follows
   the same role as every other link/heading text on a white background
   (yellow-900, the one step that clears AA there — same value Task 1 gives
   `.gra-text-blue-800` and the Links-LIGHT rule already use), including on
   hover (cssnovo's own `:hover` swaps to a DIFFERENT, lighter blue that
   would otherwise flash back mid-interaction). The separator icon is pure
   chrome, not text or an accent — mapped to a neutral gray instead of
   yellow, consistent with "structural gets neutral, not brand accent."
   Dark mode already goes neutral for the crumb text/hover/icon (`--color_
   gray_200/-400`, cssnovo-style.css's own `.dark-mode` rules, confirmed via
   grep, untouched here) — only the CURRENT/selected crumb
   (`.dark-mode .gra-breadcrumb .bc.graBcSelected`) still resolves to
   `--color_blue_300`; remapped to the same yellow-400 the navbar's own
   active-nav-item indicator already uses for "this is where you are"
   (mirrors that established pattern rather than inventing a new one). */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-breadcrumb .bc,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-breadcrumb .bc:hover {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-breadcrumb i:before {
  color: var(--color_gray_600) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-breadcrumb .bc.graBcSelected {
  color: var(--brand-accent) !important;
}

/* `.gra-tabs a.gra-tab.active` (parceiro.php's Notícias/Eventos content
   tabs) — same gap as the breadcrumb's `.graBcSelected` above: light mode
   is untouched (cssnovo's own unmapped `--color_blue_900` renders as a
   dark, readable navy there, close enough to the rest of the page's
   "active = dark/bold" language that it needs no fix), but cssnovo's own
   dark-mode rule repaints the active tab's text AND underline
   `--color_blue_300` (a pale, washed-out blue that reads as "light blue"
   against the dark background and doesn't match anything else on the
   page). Remapped to the same yellow-400 the breadcrumb's own active
   indicator and the navbar's active-nav-item indicator already use for
   "this is where you are" - mirrors that established pattern rather than
   inventing a new one. Covers cssnovo's own `:hover.active` variant too
   (same washed-out blue) and the icon variant for completeness, even
   though this project's own tab labels are text-only today. */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-tabs a.gra-tab.active,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-tabs a.gra-tab:hover.active {
  color: var(--brand-accent) !important;
  border-bottom-color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-tabs a.gra-tab.active i:before,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-tabs a.gra-tab:hover.active i:before {
  color: var(--brand-accent) !important;
}

/* `.gra-background-blue-200` icon-circle chip (homepage/servicos.php/
   ferramentas.php/contactos.php icon badges) — same gap as `.gra-background-
   blue-100` had before this file's Task 6: no `.dark-mode` override at all
   in cssnovo-style.css, so the chip stays pale blue in dark mode. Its own
   `.gra-text-blue-800` glyph already resolves to yellow-300 there (the
   Incuba heading rule above, which - at 4 classes - already outranks the
   older pre-Incuba "keep the icon blue_800 inside this specific chip"
   exception a few hundred lines up in this file at only 3 classes), so
   yellow-on-still-pale-blue was the actual "Services icon not visible /
   insufficient contrast" bug: the glyph already de-blued, its background
   never did. One step lighter than `-100`'s own gray-900 dark target,
   preserving the same two-step light/dark relationship this file already
   uses for the blue-800/900 -> gray-800/900 panel pair. Frontoffice-scoped
   only: backoffice/pages/servicos.php uses this exact `.gra-background-
   blue-200 .gra-text-blue-800` combo for its own status tags and DOES rely
   on the pre-Incuba exception keeping them a legible navy-on-pale-blue
   pair - untouched here on purpose. */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-background-blue-200 {
  background-color: var(--brand-surface-dark-hover) !important;
}

/* Live-verified this chip's own icon glyph still rendered navy blue_800 in
   dark mode DESPITE the Incuba heading rule above (`body:not():not().dark-
   mode .gra-text-blue-800`, 4 classes) - the pre-Incuba exception this
   file's own comment above describes has a SECOND, wrapper-qualified
   branch (`.dark-mode .templateDetailsA .contentPageText .gra-background-
   blue-200 .gra-text-blue-800`, 5 classes) that outranks it, a branch this
   file's earlier reasoning missed. Re-asserted here at higher specificity,
   frontoffice-scoped only - backoffice/pages/servicos.php still needs the
   original exception exactly as documented above, since its own `-200`
   background never goes dark. */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-background-blue-200 .gra-text-blue-800,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .templateDetailsA .contentPageText .gra-background-blue-200 .gra-text-blue-800 {
  color: var(--brand-accent-soft) !important;
}

/* `.gra-status.primary.infoIcon i:before` (espaco.php's room-detail popup:
   the "X pax" / "Piso N" bare icon+label pair) - cssnovo-style.css hardcodes
   this straight to hex (`#195893` light / `#7ebdff` dark, not even a
   `var()`), so none of this file's token-based rules ever touched it. Same
   role as every other plain icon glyph sitting directly on a card/popup
   surface (agenda.php's calendar/clock/pin icons, already covered via
   `.gra-text-blue-500`) - given the identical light/dark step pair
   (yellow-900 / yellow-400) that role already uses everywhere else. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-status.primary.infoIcon i:before {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-status.primary.infoIcon i:before {
  color: var(--brand-accent) !important;
}

/* Inline `border`/`border-top: 1px solid var(--color_blue_200)` on three
   `.gra-background-blue-100` elements (contactos.php's map placeholder,
   espaco.php's gallery-grid tiles and floor-plan placeholder) - a raw var()
   in a `style=` attribute, invisible to every class-based rule above,
   confirmed via grep to always co-occur with this exact class. A
   stylesheet `!important` still beats a non-`!important` inline
   declaration (the applicable rule here), so this is reachable without
   touching markup. Paired to match each mode's already-fixed `-100`
   background: yellow-300 alongside light mode's yellow-100 fill (the same
   tint+border step relationship the accordion component already uses
   below), gray-700 alongside dark mode's gray-900 fill (this file's own
   established "border on a dark card surface" value, reused verbatim from
   the `.gra-card-informative`/`.gra-background-white` dark rule above). */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-background-blue-100 {
  border-color: var(--brand-tint-3) !important;
}

/* Completion pass: bare icons nested inside `.gra-background-blue-100`
   (galeria-album.php's "view album" arrow chip and any similar pattern) -
   same "no color class of its own, falls through to the global icon
   default" gap as every icon fix in this section, live-verified via the
   icon's own `::before`. Text-on-light-tint role, same yellow-900 step
   used everywhere else for it. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-background-blue-100 i,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-background-blue-100 i::before {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-background-blue-100 {
  border-color: var(--brand-border-dark) !important;
}

/* Table action-icon links (agenda.php/iniciativas.php/galeria.php's "Ver"
   icon-only `<a class="gra-link"><i class="icon-eye"></i></a>` inside a
   `.gra-table-wrapper`) - the sitewide `.gra-link:hover{text-decoration:
   underline}` rules above (both the generic pre-Incuba one and the
   Incuba-scoped light/dark pair) exist for TEXT links with no hover
   feedback of their own; applied to an icon-only link the underline just
   draws a stray line under the glyph, not real link affordance. `!important`
   here beats those rules' unqualified `text-decoration: underline`
   regardless of selector specificity. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-table-wrapper .gra-link:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-table-wrapper .gra-link:focus-visible {
  text-decoration: none !important;
}

/* Table/card action-icon BUTTONS (area-membro.php's Reservas "Cancelar"/
   Inscrições "Cancelar inscrição" POST forms, espaco.php's "Ver detalhes"
   popup triggers) - same `.gra-link` class as the icon-only `<a>` row
   actions above, but a real `<button>` (POST+CSRF, or a JS popup trigger
   with no href to navigate to). `.gra-link` only sets link-shaped
   properties (text-decoration/color) - it never resets a button's native
   browser chrome (background/border/padding), leaving a visible box
   around the icon. Same root cause as backoffice.css's own `.bo-actions
   button.gra-link` fix (this stylesheet's Backoffice-only counterpart,
   scoped to `.bo-actions` because that's the only place Backoffice ever
   renders one) - reproduced here for Frontoffice, unscoped by table
   wrapper since these buttons show up both inside `.gra-table-wrapper`
   rows and in espaco.php's card view. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) button.gra-link {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}

/* Same stray-underline-under-the-glyph issue as the `.gra-table-wrapper
   .gra-link:hover` fix above, for the button variant - unscoped for the
   same reason as the chrome-reset rule just above (card-view buttons
   outside any `.gra-table-wrapper`). */
body:not(.templateDetailsBackoffice):not(.bo-login-body) button.gra-link:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body) button.gra-link:focus-visible {
  text-decoration: none !important;
}

/* `.value` (homepage hero stat cards - "20+ Projetos concluídos" etc.):
   cssnovo-style.css hardcodes this bare class straight to
   `--color_blue_800`, unconditional; a SEPARATE native `.dark-mode .value
   {color:var(--color_gray_200)}` rule (not the `.gra-card-dashboard`-scoped
   one a few lines below it) already neutralizes dark mode on its own, so
   only light mode is a leftover brand blue here - same "bold text directly
   on a white card" role as every other light-mode heading, so it gets the
   same yellow-900 step already used everywhere else for that role. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .value {
  color: var(--brand-text-on-light) !important;
}

/* `.gra-card-text .details .title` (homepage/sobre.php/candidatura.php's
   "Pillars"-style cards - Inovação/Modernização/Simplificação, etc.):
   cssnovo-style.css hardcodes the base rule to `--color_blue_800`
   unconditional; `.dark-mode` already goes neutral (`--color_gray_200`,
   confirmed via grep, untouched here) so only light mode is a leftover
   brand blue - same "bold heading text on a light card" role as every
   other light-mode heading. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-card-text .details .title {
  color: var(--brand-text-on-light) !important;
}

/* Navbar "Mais" flyout (`#navbar-mais-menu` - Galeria/Marca/Ferramentas) -
   live-verified: this popup renders on its own opaque WHITE panel in light
   mode (confirmed via computed style, independent of the always-dark
   `.header-primary` chrome it drops down from) with `--color_blue_800`
   text, resolved through cssnovo-style.css's cascade rather than any single
   utility class this file already targets. `.dark-mode` already renders
   this same popup on a black panel with `--color_gray_200` text (confirmed
   live, untouched here) - only light mode needs the same "text on white"
   yellow-900 treatment already used everywhere else for that role. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) #navbar-mais-menu li a {
  color: var(--brand-text-on-light) !important;
}

/* `.graAvatarWithoutFoto` (navbar's "no profile photo" placeholder avatar
   circle, area-membro.php link) - `--color_blue_200` background, same
   tint-token role Task 9 already gave `.graAvatarWithText` (the WITH-photo/
   initials sibling), which is already fixed. `.dark-mode` already goes
   neutral (confirmed via grep, untouched here). The `icon-user` glyph
   inside keeps its own existing blue-800 value unchanged, on purpose - same
   call already made for `.graAvatarWithText`'s initials text: dark text on
   a pale tint reads as ordinary legible text, not a brand-blue accent. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .graAvatarWithoutFoto {
  background-color: var(--brand-tint-2) !important;
}

/* `.gra-tooltip .tooltiptext` (hover tooltips - navbar's "Idioma"/"Modo
   escuro" labels, the only Frontoffice user of this generic DS component,
   confirmed via grep) - `--color_blue_100` background / `--color_blue_800`
   text, unconditional light-mode default; the matching directional `:after`
   arrow triangles reuse the same background token as their border-color.
   `.dark-mode` already goes fully neutral (gray-900 bg / gray-200 text,
   confirmed via grep, untouched here) - light mode gets the same
   tint-background + yellow-900 text pairing already used for every other
   light-tint bubble (the accordion, the badges). */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tooltip .tooltiptext {
  background-color: var(--brand-tint-1) !important;
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tooltip .tooltiptext.topCenter:after,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tooltip .tooltiptext.topLeft:after,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tooltip .tooltiptext.topRight:after {
  border-top-color: var(--brand-tint-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tooltip .tooltiptext.bottomCenter:after,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tooltip .tooltiptext.bottomLeft:after,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tooltip .tooltiptext.bottomRight:after {
  border-bottom-color: var(--brand-tint-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tooltip .tooltiptext.left:after {
  border-left-color: var(--brand-tint-1) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tooltip .tooltiptext.right:after {
  border-right-color: var(--brand-tint-1) !important;
}

/* `.gra-footer-opt-2 .first-row/.second-row/.third-row` divider - light
   mode only, cssnovo-style.css hardcodes this to `--color_blue_600`
   unconditionally (the vendor's own `.dark-mode`/`.theme-marca-servicos`
   variants already go neutral, confirmed via grep, untouched here). A thin
   structural divider, not a brand accent - same "structural chrome, not
   the accent itself" call this file already made for the navbar/footer/
   panels above, so neutral gray rather than yellow. Reuses this file's own
   established "border on a dark surface" value (gray-700, same one just
   used two rules above) since the footer's own background is now
   gray-800/900 (light/dark) rather than the navy it used to be. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-footer-opt-2 .first-row,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-footer-opt-2 .second-row,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-footer-opt-2 .third-row {
  border-bottom-color: var(--brand-border-dark) !important;
}

/* ============================================================
   5.4  INCUBA THEME — Frontoffice polish pass (2026-08-31 continuation)
   ============================================================ */

/* "Ordenar por" / Agenda filter selects (`.gra-select.gra-simple-select`,
   the real GRA simple-select component - confirmed live: cssnovo-style.css
   wires it up as a Select2 widget itself, this is the actual DS
   implementation, not a bespoke choice made here). The widget's own skin
   (`.select2 .select2-selection__arrow:after` / `.select2-selection:hover`
   / `.select2.select2-container--open .select2-selection` / the dropdown
   panel border/hover) hardcodes `--color_blue_*` for its arrow icon, hover/
   open border, and results-list hover/selected state. `.dark-mode` uses the
   same tokens' blue-300 step, so - unlike most of this file - dark mode
   needs its own explicit fix here too, not just light. The clear "x"
   (`allowClear:true`, vendor's own unconditional default for every
   `gra-simple-select`, not something this project's markup opted into) is
   hidden outright: every current Frontoffice use is a "pick one, always
   has a value" control (sort order, page-size-adjacent filters), so a
   separate clear affordance is dead UI, and it's what was rendering as a
   vertically-misaligned blue icon. Scoped to the Frontoffice (2026-10): this
   rule also hid the "x" in the Backoffice, where optional selects (e.g. the
   news form's "Galeria associada") need it - see backoffice.css. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .select2 .select2-selection .select2-selection__clear {
  display: none !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .select2 .select2-selection__arrow:after {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .select2 .select2-selection:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .select2.select2-container--open .select2-selection {
  border-color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .select2-container .select2-dropdown {
  border-color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .select2-container .select2-dropdown .select2-search {
  border-bottom-color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .select2-container .select2-dropdown .select2-results ul li:not(:last-child) {
  border-bottom-color: var(--brand-tint-3) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .select2-container .select2-dropdown .select2-results ul li:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .select2-container .select2-dropdown .select2-results ul li.select2-results__option--highlighted,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .select2-container .select2-dropdown .select2-results ul li.select2-results__option--selected {
  background-color: var(--brand-accent-soft) !important;
  color: var(--brand-on-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .select2 .select2-selection__arrow:after {
  color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .select2 .select2-selection:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .select2.select2-container--open .select2-selection {
  border-color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .select2-container .select2-dropdown {
  border-color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .select2-container .select2-dropdown .select2-search {
  border-bottom-color: var(--brand-accent) !important;
}

/* Row-divider border between results options - live-verified separately
   from the highlighted/selected background above: dark mode's own default
   already goes fully neutral for the highlight/selected fill (`--color_
   gray_500`, confirmed via getComputedStyle, correctly left untouched),
   but the plain `:not(:last-child)` divider between rows is still `--color_
   blue_300`, missed on the first pass through this dark-mode block.
   `--color_gray_700` matches this file's own existing structural-divider
   convention (the footer's row dividers, above). */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .select2-container .select2-dropdown .select2-results ul li:not(:last-child) {
  border-bottom-color: var(--brand-border-dark) !important;
}

/* `.templateDetailsA` (the shared page wrapper - title/subtitle, breadcrumb,
   then the `.contentPageText` card - used on ~26 Frontoffice pages) never
   had its own bottom padding; its background only ever showed above/beside
   the card because the card's own `margin:auto` centers horizontally, not
   vertically, so `</main>` closed right where the card ended and the
   footer began immediately after - live-verified as the "content area ends
   abruptly, dark card touching the footer directly" bug. `72px` matches
   the exact value this same template already uses one level down for the
   equivalent top-of-card gap (`.gra-padding-bottom-4xl`, used inside every
   one of these pages' own content wrapper) - not a new number, the
   existing rhythm continued one level up. Both modes: the gap needs to
   exist regardless of which background color (`--color_yellow_200` light /
   `--color_gray_800` dark) is showing through it. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .templateDetailsA {
  padding-bottom: 72px;
}

/* Área de Membro sidebar nav (`area-membro.php`'s `.area-membro-nav-link`)
   - not a real DS component, a project-local class with zero rules of its
   own in cssnovo-style.css (confirmed via grep - it only ever existed as a
   hook for the inline `style=` attribute this replaces). The old inline
   style hardcoded a single `--color_blue_100/800` pair for the active tab
   that never varied with the theme toggle (server-rendered, not `.dark-
   mode`-aware) - live-verified as both the "dark mode active text is still
   blue" and "light mode active state should be yellow" bugs at once, since
   it was the same unconditional blue in both. There was also no hover rule
   at all (nothing to override, nothing to find in cssnovo-style.css
   either) - the "inactive hover has insufficient visibility" bug is
   literally zero visual feedback existing today.
   border-radius: this pass's fix also adds the 8px radius that was
   missing from every hover/active rule below (root cause of the "square
   corners" bug - a full-width row with a flat-filled background and no
   radius reads as the whole card being highlighted). 8px matches the
   real DS's own vertical-nav active/hover treatment
   (`gra-design-system-master`'s `.gra-menuBackoffice ul li.liOption.active`/
   `:hover`, both `border-radius:8px`) and this project's own
   `.gra-rounded-m` utility (`border-radius:8px!important` in
   cssnovo-style.css) - not an invented value. The full `.liOption`
   component itself (fixed-height collapsible hamburger chrome) doesn't
   fit this card-based sidebar, so only its active/hover radius is
   reused here, on the existing `.area-membro-nav-link` rules. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .area-membro-nav-link {
  color: var(--color_gray_600) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .area-membro-nav-link .area-membro-nav-icon {
  color: var(--color_gray_400) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .area-membro-nav-link:hover {
  background-color: var(--color_gray_100) !important;
  color: var(--color_gray_800) !important;
  border-radius: 8px !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .area-membro-nav-link:hover .area-membro-nav-icon {
  color: var(--color_gray_800) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .area-membro-nav-link.active {
  background-color: var(--brand-tint-1) !important;
  color: var(--brand-text-on-light) !important;
  font-weight: 700 !important;
  border-radius: 8px !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .area-membro-nav-link.active .area-membro-nav-icon {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .area-membro-nav-link {
  color: var(--color_gray_300) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .area-membro-nav-link .area-membro-nav-icon {
  color: var(--color_gray_500) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .area-membro-nav-link:hover {
  background-color: var(--brand-surface-dark-hover) !important;
  color: var(--color_gray_100) !important;
  border-radius: 8px !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .area-membro-nav-link:hover .area-membro-nav-icon {
  color: var(--color_gray_100) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .area-membro-nav-link.active {
  background-color: var(--brand-surface-dark-raised) !important;
  color: var(--brand-accent) !important;
  font-weight: 700 !important;
  border-radius: 8px !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .area-membro-nav-link.active .area-membro-nav-icon {
  color: var(--brand-accent) !important;
}

/* FAQ accordion header hover (`faq.php`, homepage FAQ block) - a previous
   pass here deliberately left this alone as "shared interaction-feedback
   color, not a brand accent" (see the accordion comment above); live
   re-verified this pass at the user's request and reversed: it reads as
   plain leftover brand blue, not a neutral system color, so it gets the
   same solid-fill/black-text hover treatment as every other genuine
   accent-hover in this file (the pagination active chip, the primary
   button). `.dark-mode`'s own hover is already neutral gray-400 (confirmed
   via grep, untouched) - only light mode was ever blue here. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-accordion .accordion-item .accordion-header:hover {
  background-color: var(--brand-accent-strong) !important;
  color: var(--brand-on-accent) !important;
  border-color: var(--brand-accent-strong) !important;
}

/* Navbar "Mais"/settings dropdown panel row hover+active fill
   (`.gra-desktop-header ul.menu li ul li`, the shared submenu-row skin
   both `#navbar-mais-menu` and `#navbar-settings-menu` use) - live-
   verified: hovering any row in either panel fills it solid `--color_
   blue_600` (light), and the "current page" row inside `#navbar-mais-menu`
   sits permanently on `--color_blue_900` text / `--color_blue_200` fill
   (`li.active`, e.g. visiting galeria.php highlights "Galeria" in the
   panel). Both are genuine leftover brand blue, not the shared neutral
   interaction color used elsewhere (that one's `--color_gray_400`/`-800`
   dark-mode pairing is untouched, confirmed via grep - only light mode's
   blue needed remapping). Text pairing follows this file's own solid-fill
   convention (black text on a colored fill, never white) - navbar.php's
   own `<style>` block separately re-asserts black specifically for the
   settings-dropdown `.gra-link` rows, since it loads after this file and
   must win there; the plain, non-`.gra-link` "Mais" rows are covered by
   the plain `a`/`i:before` rules below without needing that extra
   specificity. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header ul.menu li ul li:hover {
  background-color: var(--brand-accent-strong) !important;
  color: var(--brand-on-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header ul.menu li ul li:hover a,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header ul.menu li ul li:hover i:before {
  color: var(--brand-on-accent) !important;
}

/* `#navbar-mais-menu li a`'s own base-state fix (above, in the earlier
   navbar-polish pass) is an ID selector, which outranks every plain-class
   selector above regardless of `:hover` or source order - live-verified:
   without this, the hover fill above still painted the row yellow-600 but
   the text stayed yellow-900 instead of flipping to black, unreadable
   against its own fill. Matched to an equal ID selector to win correctly. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) #navbar-mais-menu li:hover a {
  color: var(--brand-on-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header ul.menu li ul li.active {
  background-color: var(--brand-tint-1) !important;
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header ul.menu li ul li.active a,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-desktop-header ul.menu li ul li.active i:before {
  color: var(--brand-text-on-light) !important;
}

/* Remaining inline `style="border...var(--color_blue_100/200)"` hairlines -
   confirmed via grep, sitewide: every one of these pairs a light-tint panel
   (`.gra-background-blue-100`, already remapped to yellow-100 above) or a
   plain section/heading divider with a 1px border set directly in the
   PHP markup (contactos.php, espaco.php, candidatura.php, iniciativa.php,
   evento.php, index.php's partner-logo tile, area-membro.php's Azor.ID/
   Entra ID rows). An attribute-contains selector reaches every one of them
   in one rule without touching PHP markup or the properties that come with
   the border shorthand (width/style stay whatever the inline rule already
   set) - `!important` is required and sufficient to win over a plain
   (non-`!important`) inline style, same rule the platform already applies
   for `!important` vs. any specificity. Light mode continues the same
   tint-step relationship already used for the fills (100->200 border);
   dark mode's own `.gra-background-blue-100` fill already goes neutral
   gray-900 (existing rule, untouched) but the accompanying border never
   did until now - `--color_gray_700` matches this file's own structural-
   divider convention used everywhere else. */
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) [style*="var(--color_blue_100)"] {
  border-color: var(--brand-tint-2) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) [style*="var(--color_blue_200)"] {
  border-color: var(--brand-tint-3) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode [style*="var(--color_blue_100)"],
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode [style*="var(--color_blue_200)"] {
  border-color: var(--brand-border-dark) !important;
}

/* ==========================================================================
   6. COMPONENTES - POPUPS, HERO E NOTIFICAÇÕES
   ========================================================================== */

/* .popup-espaco-detalhe: two-column layout for the shared "Ver detalhes"
   popup (assets/php/espaco-popup-detalhe.php, used by both espaco.php and
   reserva.php) - foto vertical on the left, info (nome/badges/descrição/
   disposições/equipamento/footer) on the right, per the 2026-09-14
   uniformização. Scoped to this one popup's own class, not to the bare
   .popupContainer, so cssnovo-style.css's shared shape (photo full-width on
   top) stays exactly as-is for every other .gra-popup on the site
   (team-grid.php's member profile, evento.php's "Agendar Visita"). The DS's
   .popupHeader/.popupContent/.popupFooter rules are all descendant
   selectors (".gra-popup .popupContainer .xxx"), not direct-child ones, so
   nesting them one level deeper inside the new .popupBody wrapper below
   doesn't lose any of their existing styling - only the two-column split
   itself is new.
   Image column: fixed width AND fixed height (2026-09-14 follow-up) - a
   real, definite `height` on a flex item is exactly what makes
   align-items:stretch (still set on the row below, for .popupBody's sake)
   leave that item alone; stretch only ever resizes items whose cross size
   is 'auto'. The first cut here used `min-height` instead, which does NOT
   opt out of stretch (a stretched item is only clamped by min/max
   afterwards) - live-measured proof: a 4-disposições popup stretched the
   photo to 457px while a 2-line-description popup sat at the 320px floor,
   i.e. the crop framing visibly changed popup to popup, which is the exact
   "fica espremida conforme o conteúdo" bug this fixes. With a definite
   height the photo renders at the same px size and crop every time,
   independent of the right column's content - object-fit:cover still keeps
   the source photo's own proportions intact (no stretch/deformation).
   Because .popupImg opts out of stretch this way, a right column taller
   than the photo makes the CONTAINER (and so .popupBody, which has no
   explicit height and so still stretches) grow past the photo's height
   instead of pulling the photo along - the photo simply top-aligns in the
   taller row (default behavior for a definite-cross-size item under
   align-items:stretch), and popupContent's own untouched
   flex:1;overflow-y:auto (cssnovo-style.css) is what scrolls, not the photo
   growing or the popup clipping content. A short right column, in turn,
   still gets stretched by the photo's own height (the taller of the two
   items) via popupBody's stretch, so popupContent's flex:1 grows to push
   popupFooter down to the photo's bottom edge - unaffected, still the same
   look as before this fix for that case.
   Padding sits on .popupImg itself (GRA spacing scale's "s" step = 24px,
   same value cssnovo-style.css's own unscoped .popupImg already uses) on
   all four sides, box-sizing:border-box so the 260px column width already
   includes it - the photo gets its own uniform inset instead of leaning on
   .popupBody's children's own left padding for the right-hand gap, so the
   respiro stays the same on every side and never depends on which side
   happens to sit next to another element's padding.
   Below 767.98px (this file's existing Bootstrap-breakpoint convention -
   see the @media blocks throughout cssnovo-style.css) it collapses back to
   the DS's own stacked column (photo on top, full width) so the info
   column is never squeezed narrower than it needs; the photo gets a fixed
   height there instead of a min-height since there's no sibling column
   height to track anymore. */
.gra-popup .popupContainer.popup-espaco-detalhe {
  flex-direction: row;
  align-items: stretch;
  max-width: 760px;
}

/* .gra-popup .popupContainer .popupImg (cssnovo-style.css) is 3 classes
   deep (specificity 0,3,0) and .popupImg img a 4th (0,4,0) - the same trap
   documented above for .popupHeader.no-border. A bare ".popup-espaco-
   detalhe .popupImg" is only 0,2,0 and quietly loses the padding-bottom/
   max-height fight no matter the file load order, which is exactly why
   padding-bottom and img max-height measured as the DS's own 0/300px
   instead of this file's values the first time this was written. Matching
   the base rule's own ".gra-popup .popupContainer" prefix on every
   selector below keeps this override strictly more specific. */
.gra-popup .popupContainer.popup-espaco-detalhe .popupImg {
  flex: 0 0 260px;
  width: 260px;
  height: 320px;
  padding: 24px;
  box-sizing: border-box;
}
.gra-popup .popupContainer.popup-espaco-detalhe .popupImg img {
  width: 100%;
  height: 100%;
  max-height: none;
  object-fit: cover;
}
.gra-popup .popupContainer.popup-espaco-detalhe .popupBody {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}
@media (max-width: 767.98px) {
  .gra-popup .popupContainer.popup-espaco-detalhe {
    flex-direction: column;
  }
  .gra-popup .popupContainer.popup-espaco-detalhe .popupImg {
    flex: 0 0 auto;
    width: 100%;
    height: 220px;
    min-height: 0;
    padding: 24px;
    box-sizing: border-box;
  }
  .gra-popup .popupContainer.popup-espaco-detalhe .popupImg img {
    height: 100%;
    max-height: none;
  }
}

/* Homepage hero stat cards (index.php - "Projetos desenvolvidos"/19,
   "Serviço mais prestado"/Consultoria em inovação, etc). cssnovo-style.css's
   base .gra-card-dashboard has no height/layout rules - it just sizes to
   whatever content lands in it - so with 4 cards side by side the shortest
   title ("Ações realizadas", 1 line) and the longest value ("Consultoria em
   inovação", wraps to 3 lines at the base 32px) produced 4 visibly different
   card heights (live-verified: 102px/126px/126px/203px). Height is pinned
   here instead to the component's own declared constants - padding(16*2) +
   title line-height(24px) * 2 lines + value margin-top(8) + value line-
   height at its base size(38.4px) - so it's a structural constant derived
   from the DS's own numbers, not a number picked to fit today's 4 phrases.
   Title gets a 2-line reservation (covers all 4 current titles); value's
   line-height is switched from the DS's fixed 38.4px to a relative one so
   assets/js/gra-hero-stat-fit-text.js can shrink its font-size until
   whatever text is there (1 or more lines) fits inside that fixed box,
   instead of only ever fitting single-line content. */
.hero-stat-card {
  height: 126.4px;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}
.hero-stat-card .title {
  min-height: 48px;
}
.hero-stat-card .value {
  height: 38.4px;
  line-height: 1.15;
  overflow: hidden;
  display: flex;
  align-items: center;
}

/* Notificações (navbar.php) — dois acertos localizados, sem tocar no cssnovo:
   1) o ícone do lixo (`<div class="deleteOne gra-link">`) herdava o
      `.gra-link:hover{text-decoration:underline}` do DS; as correções
      anteriores só cobriam `.gra-table-wrapper .gra-link` e `button.gra-link`.
      Hover/foco continuam percetíveis pela cor do DS (`.gra-link:hover`) e o
      foco ganha um contorno visível.
   2) o botão "Ver pedido" do detalhe fica fora de `.gra-notification` (que
      tem 32px de padding lateral), por isso ficava colado à margem esquerda
      do modal em vez de alinhado com o título e o texto. */
.gra-notification .deleteOne.gra-link:hover,
.gra-notification .deleteOne.gra-link:focus-visible,
.gra-notification .deleteOne.gra-link:active {
  text-decoration: none !important;
}
.gra-notification .deleteOne.gra-link:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
  border-radius: 4px;
}
.gra-notifications-box.readOne .notificacao-detalhe-acao {
  padding: 0 32px 24px;
}

/* ============================================================
   7.  Estados ativos/selecionados que ainda ficavam em azul (2026-10)
   ============================================================
   Lista fechada, levantada a partir do cssnovo-style.css e confirmada contra
   o que o projeto já remapeava - só os componentes realmente usados no
   Frontoffice cujo estado ativo/selecionado continuava azul (hover, foco,
   desativado e os restantes azuis do DS, que têm significado funcional,
   ficam como estão):

   - `.form-check-input:checked` (checkbox/radio) em modo ESCURO - o modo claro
     já estava remapeado; o site está atualmente forçado a escuro.
   - `.gra-tabs a.gra-tab.active` em modo CLARO (o escuro já estava).
   - `:active` de `.gra-link` e das ligações de conteúdo (`.pageContent p a`):
     o clique mantido pintava o texto/ícone de azul (blue_900 / blue_300).
   - (O assistente de reservas e a paginação do Frontoffice já usavam amarelo:
     o primeiro com regras próprias em reserva.php, a segunda com `.btn-primary`.)

   Mesmos passos de cor que o resto do tema: preenchimentos amarelos com texto
   preto (contraste >= 9:1), texto sobre branco em yellow_900 (único passo que
   cumpre AA), texto sobre escuro em yellow_400/500. Cada regra repete o
   escopo `:not(.templateDetailsBackoffice):not(.bo-login-body)` do resto do
   tema - o Backoffice tem o seu próprio bloco em backoffice.css. */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .form-check .form-check-input:checked {
  background-color: var(--brand-accent) !important;
  border-color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .form-check .form-check-input:checked:after {
  color: var(--brand-on-accent) !important;
}

body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tabs a.gra-tab.active,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tabs a.gra-tab:hover.active {
  border-bottom-color: var(--brand-accent-active) !important;
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tabs a.gra-tab.active i:before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-tabs a.gra-tab:hover.active i:before {
  color: var(--brand-text-on-light) !important;
}

body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-link:not(.error):active,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .pageContent p a:not(.error):active,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .gra-link:not(.error):active i:before,
body:not(.templateDetailsBackoffice):not(.bo-login-body):not(.dark-mode) .pageContent p a:not(.error):active i:before {
  color: var(--brand-text-on-light) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-link:not(.error):active,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .pageContent p a:not(.error):active,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-link:not(.error):active i:before,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .pageContent p a:not(.error):active i:before {
  color: var(--brand-accent-pressed) !important;
}

/* `.gra-btn.btn-secondary` / `.btn-default` em `:active` e `:focus` - MODO ESCURO.
   Os blocos anteriores cobrem o modo claro e o `.icon.icon-secondary` escuro;
   o botão secundário escuro (ex.: «Cancelar» do modal de inscrição) mantinha o
   azul do DS (blue_300) ao carregar - confirmado no browser com o estado
   :active forçado. Mesmo remapeamento do botão secundário claro, em yellow_400. */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-secondary:active,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-default:active,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-secondary:focus,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-default:focus {
  border-color: var(--brand-accent) !important;
  color: var(--brand-accent) !important;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-secondary:active i:before,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-default:active i:before,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-secondary:focus i:before,
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .gra-btn.btn-default:focus i:before {
  color: var(--brand-accent) !important;
}

/* ==========================================================================
   8. COMPONENTES - RODAPÉ FIXO, GALERIAS, INSCRIÇÕES E IDENTIDADES
   ========================================================================== */

/* Sticky footer (Frontoffice): every public page is body > navbar + main +
   footer (footer.php), but body was only as tall as its content, so on a
   short page the footer sat mid-screen with the page background below it.
   Making body a min-height:100vh column and letting main absorb the spare
   height pins the footer to the bottom edge on short pages, while on long
   pages main simply grows and the footer follows the content as before.
   Scoped like the rules above so the Backoffice layout is untouched. */
body:not(.templateDetailsBackoffice):not(.bo-login-body) {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
body:not(.templateDetailsBackoffice):not(.bo-login-body) > main {
  flex: 1 0 auto;
}

/* "Galeria Relacionada" (iniciativa.php): prévia de UMA linha da galeria.
   Cada foto inteira, na proporção original (sem recorte), todas com a mesma
   altura (--galeria-rel-h) e largura proporcional. O contentor tem a altura
   de uma linha e overflow:hidden: o flex-wrap manda para uma 2ª linha, fora
   da área visível, as fotos que já não cabem na largura disponível - assim o
   número de fotos mostradas depende do espaço, sem JS, sem 2ª linha e sem
   overflow horizontal. As restantes ficam na galeria completa. */
.galeria-relacionada {
  --galeria-rel-h: 140px;
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  height: var(--galeria-rel-h);
  overflow: hidden;
}
@media (min-width: 768px) {
  .galeria-relacionada { --galeria-rel-h: 180px; gap: 24px; }
}
.galeria-relacionada .galeria-item {
  flex: 0 0 auto;
  height: var(--galeria-rel-h);
  max-width: 100%;
  border-radius: 10px;
  overflow: hidden;
}
.galeria-relacionada .galeria-item a {
  display: block;
  width: 100%;
  height: 100%;
  text-decoration: none;
}
.galeria-relacionada .galeria-item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* "Evento Relacionado" / "Notícia Relacionada": cartão horizontal
   informativo (assets/php/relacionados.php) sobre o
   .gra-card-informative.card-horizontal do DS. Largura total, título
   completo (a regra global de 2 linhas com reticências não se aplica
   aqui) e linha de dados que quebra para baixo em ecrãs estreitos. */
.relacionado-card {
  display: block;
  text-decoration: none;
}
.relacionado-card + .relacionado-card { margin-top: 16px; }
.gra-card-informative.relacionado-card .details .title {
  display: block;
  overflow: visible;
  -webkit-line-clamp: unset;
  height: auto;
  margin-top: 8px;
}
.relacionado-card .relacionado-infos {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 24px;
  margin-top: 8px;
  font-size: 14px;
  font-weight: 700;
  color: var(--color_gray_600);
}
.relacionado-card .relacionado-texto {
  margin: 8px 0 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--color_gray_700);
}

/* Radios sem opção selecionada (estado inicial dos campos de seleção única
   das inscrições e de qualquer .form-check com radios): o HTML define que um
   radio cujo grupo não tem nenhum selecionado é `:indeterminate`. A regra do
   DS `.dark-mode .form-check .form-check-input:indeterminate` (cssnovo, feita
   para checkboxes parcialmente marcadas) não restringe o tipo, pelo que
   pintava TODAS as opções do grupo como círculo preenchido com um traço "–"
   até à primeira seleção - e, ao escolher uma, as restantes passavam
   bruscamente a círculo vazio. Os radios nunca têm estado indeterminado
   visual: repõem-se os valores base do .form-check-input (círculo vazio,
   contorno neutro). Checkboxes (.indeterminate-checkbox) não são afetadas. */
body:not(.templateDetailsBackoffice):not(.bo-login-body).dark-mode .form-check .form-check-input[type="radio"]:indeterminate {
  background-color: var(--bs-form-check-bg) !important;
  border-color: var(--bs-border-color) !important;
  --bs-form-check-bg-image: none;
}

/* Vídeo da lightbox (assets/js/galeria-album-lightbox.js, partilhado por
   galeria-album.php e pela Galeria Relacionada de iniciativa.php;
   movido para aqui de galeria-album.php, regras inalteradas): ocupa a
   mesma camada fixa que o .sl-image da SimpleLightbox mas com
   margens próprias, para nunca ficar full-bleed, e fica acima do
   .sl-image (que entretanto se mantém, invisível, só para a
   navegação/índice da biblioteca continuar a funcionar). */
.gra-gallery-video {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 10050;
    align-items: center;
    justify-content: center;
    padding: 24px;
    box-sizing: border-box;
}
.gra-gallery-video.is-aberto { display: flex; }
.gra-gallery-video video {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    background: #000;
    border-radius: 6px;
}
.gra-gallery-video .gra-gallery-video-embed {
    width: min(90vw, 160vh);
    max-width: 100%;
    max-height: 100%;
    aspect-ratio: 16 / 9;
}
.gra-gallery-video .gra-gallery-video-embed iframe {
    width: 100%;
    height: 100%;
    border: 0;
    border-radius: 6px;
    background: #000;
}

/* Área de membro, cartão "Identidades ligadas": ações secundárias
   (Atualizar / Desligar / Ligar) compactas e proporcionais ao texto - o
   .gra-btn do DS (padding 10px 20px, min-width 144px, 16px/24px) é
   dimensionado para ações principais. Só tamanho: cores, bordas, hover,
   foco e estado desativado continuam a vir do DS. */
.identidades-ligadas .gra-btn {
  padding: 3px 14px;
  min-width: 0;
  font-size: 14px;
  line-height: 20px;
  white-space: nowrap;
}

/* Ligação "Terminar sessão no Azor.ID": discreta, sem competir com as ações. */
.identidades-ligadas form > button.gra-link {
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: pointer;
}
@media (max-width: 575px) {
  .identidades-ligadas .d-flex.justify-content-between { flex-wrap: wrap; }
}

/* ============================================================
   9.  Frontoffice - adaptação a mobile (2026-10-23)
   Tudo o que está abaixo só produz efeito em ecrãs < 768px (ou < 576px),
   exceto as regras base que reproduzem exatamente o valor anterior em
   desktop. Os breakpoints são os do DS (cssnovo-style.css): 767.98px,
   575.98px e 1023/1024px (cabeçalho). Nenhum seletor aqui é usado pelo
   Backoffice (prefixo fo-).
   ============================================================ */

/* Grelhas de 2 colunas das páginas: o valor das colunas passou do style=""
   inline para --fo-cols, o que permite colapsar em mobile sem !important.
   Em desktop o resultado é idêntico ao inline anterior. */
.fo-grid { grid-template-columns: var(--fo-cols); }

/* Botão do menu mobile (gra-mobile-header): um <button> no lugar do <div>
   do DS, para ser focável e anunciado; só precisa de perder o aspeto nativo. */
.gra-mobile-header .navbar-mobile-toggle {
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  font: inherit;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}

@media (max-width: 767.98px) {
  .fo-grid { grid-template-columns: minmax(0, 1fr); }
  /* O cartão de conteúdo das páginas (DS: .templateDetailsA .contentPageText,
     padding 40px 56px) somado ao padding de 24px do .gra-container deixava
     só ~60% da largura de um telemóvel para o conteúdo. */
  .templateDetailsA .contentPageText { padding: 24px 0; }
}

/* Tabelas em modo vertical (DS: .gra-table.responsive-vertical, <= 1023px):
   o DS põe display:block em table/tr/td *descendentes* de .gra-table, mas
   estas tabelas são elas próprias o .gra-table, logo continuavam
   display:table e o padding-left:50% das células fazia a tabela crescer
   (631px dentro de 342px) e cortar os valores. */
@media (max-width: 1023.98px) {
  #conteudo-principal table.gra-table.responsive-vertical {
    display: block;
    width: 100%;
  }
}

@media (max-width: 767.98px) {
  /* DS: .gra-row-bs tem margem lateral de -.75rem mas as colunas sm só
     .5rem de padding, o que deixava o conteúdo das linhas 4px à esquerda
     do resto da página. */
  #conteudo-principal .gra-row-bs { margin-left: -.5rem; margin-right: -.5rem; }
}

@media (max-width: 1023.98px) {
  /* Título + descrição das linhas de Agenda/Notícias num só bloco, para não
     ficarem lado a lado dentro do td (display:flex) do modo vertical. */
  #conteudo-principal .responsive-vertical td > .fo-cel-texto { min-width: 0; }
}

/* Barra de ordenação / itens por página / vista (Agenda, Parceiros): alinhada
   à direita em desktop (como o margin-left:auto inline que tinha) e, em
   mobile, alinhada à esquerda e com quebra de linha em vez de transbordar. */
.fo-toolbar { margin-left: auto; }
@media (max-width: 767.98px) {
  .fo-toolbar { margin-left: 0; }
}

/* Barras de ferramentas das listagens (ordenar / por página / vista) que
   transbordavam do ecrã: passam a quebrar linha e a alinhar ao início.
   !important porque os containers trazem justify-content-end (utilitário do
   DS, !important) ou style="justify-content:flex-end" inline. */
@media (max-width: 767.98px) {
  .fo-toolbar-wrap { flex-wrap: wrap; justify-content: flex-start !important; row-gap: 12px; }
}

/* Ecrã de login da Área de Membro: o cartão (re-declarado 1:1 do DS acima)
   traz padding de 64px 120px, o que em mobile deixava ~150px para os campos. */
@media (max-width: 767.98px) {
  .templateIniciarSessao { padding-inline: 16px; }
  .templateIniciarSessao .sectionLogin { padding: 40px 24px; }
}

@media (max-width: 575.98px) {
  /* Login: Entrar/Registar (flex:1, min-width do .gra-btn) passam a linhas
     separadas quando não cabem lado a lado (320px). */
  .templateIniciarSessao .sectionLogin form .d-flex.gap-2 { flex-wrap: wrap; }

  /* Rodapé: as moradas passam de duas colunas (apertadas) a uma. */
  footer .contactsContent { flex-direction: column; gap: 16px; }
  footer .contactsContent .address span { max-width: none; }
  /* Áreas de toque dos links legais. */
  footer .links a { display: inline-block; padding-block: 8px; }
}

/* Carrossel de serviços da Home: o estilo que estava inline passa para classes
   (mesmos valores em desktop) para poder ser reorganizado em mobile. */
.fo-carousel-nav { display: flex; align-items: center; gap: 16px; }
.fo-carousel-track { flex: 1; min-width: 0; }

@media (max-width: 767.98px) {
  /* Carrossel: cartão em largura total e setas por baixo, centradas. */
  .fo-carousel-nav { flex-wrap: wrap; justify-content: center; row-gap: 16px; }
  .fo-carousel-track { flex: 0 0 100%; order: -1; }
  /* Cabeçalhos de secção: o link "Saber mais" passa para a linha seguinte em
     vez de ficar espremido (e partido) ao lado do título. */
  .fo-section-head { flex-wrap: wrap; row-gap: 8px; column-gap: 16px; }
}
@media (max-width: 575.98px) {
  /* Hero da Home: 48px de padding lateral (gra-padding-x-xl) deixavam ~290px. */
  .fo-hero.gra-padding-x-xl { padding-left: 24px; padding-right: 24px; }
}

/* Calendário do wizard de reserva (reserva.php): 7 colunas iguais; em mobile
   minmax(0,1fr) deixa as células encolher (com 1fr o min-content dos dias
   empurrava o domingo para fora do cartão). O cabeçalho passa a ter o mês em
   cima e os dois botões de navegação lado a lado por baixo. */
.fo-cal-grid { grid-template-columns: repeat(7, 1fr); }
@media (max-width: 575.98px) {
  .fo-cal-grid { grid-template-columns: repeat(7, minmax(0, 1fr)); }
  .fo-cal-head { flex-wrap: wrap; row-gap: 8px; column-gap: 8px; }
  .fo-cal-head > span { order: -1; flex: 0 0 100%; text-align: center; }
  .fo-cal-head > .espaco-cal-mes { flex: 1 1 0; min-width: 0; }
}

/* Datepicker do DS (daterangepicker): as células trazem padding 10px 14px
   (46px cada, 398px no total) e o painel não cabia em ecrãs < 420px (cortado
   78px à esquerda a 320px). Em mobile o painel ocupa a largura do ecrã e as
   7 colunas dividem-na por igual. */
@media (max-width: 575.98px) {
  body:not(.templateDetailsBackoffice) .daterangepicker { width: calc(100vw - 16px); max-width: 360px; }
  body:not(.templateDetailsBackoffice) .daterangepicker .drp-calendar { max-width: none; width: 100%; box-sizing: border-box; }
  body:not(.templateDetailsBackoffice) .daterangepicker .calendar-table table { width: 100%; }
  body:not(.templateDetailsBackoffice) .daterangepicker td, body:not(.templateDetailsBackoffice) .daterangepicker th { min-width: 0; width: 14.2857%; padding-left: 0; padding-right: 0; }
}

@media (max-width: 575.98px) {
  /* Wizard de reserva: Voltar / Seguinte (e equivalentes) tinham largura mínima
     fixa e transbordavam a 320px; passam a quebrar linha ocupando a largura. */
  #espaco-wizard .d-flex.justify-content-between:has(> [data-wizard-back]) { flex-wrap: wrap; gap: 8px; }
  #espaco-wizard .d-flex.justify-content-between:has(> [data-wizard-back]) > .gra-btn { flex: 1 1 auto; }
  /* Datepicker: células mais altas para toque. */
  body:not(.templateDetailsBackoffice) .daterangepicker td { padding-top: 12px; padding-bottom: 12px; }
}

/* Popups do DS (.gra-popup .popupContainer): o painel tem max-height:700px mas
   o conteúdo (imagem + texto + botões) ultrapassava-o em ecrãs baixos e os
   botões ficavam fora do painel. Em mobile o painel nunca passa da altura do
   ecrã e faz scroll interno. Fora do Backoffice. */
@media (max-width: 767.98px) {
  body:not(.templateDetailsBackoffice) .gra-popup .popupContainer {
    max-height: calc(100vh - 32px);
    max-height: calc(100dvh - 32px);
    overflow-y: auto;
  }
}

@media (max-width: 575.98px) {
  /* Rodapé do popup de detalhe do espaço: Cancelar / Reservar partilham a
     linha (ou quebram para duas, em largura total) em vez de ficarem
     desalinhados à direita. */
  .popup-espaco-detalhe .popupFooter > div:last-child { display: flex; flex-wrap: wrap; gap: 8px; }
  .popup-espaco-detalhe .popupFooter .closePopup { margin-right: 0; flex: 1 1 auto; }
  .popup-espaco-detalhe .popupFooter a { flex: 1 1 auto; display: flex; }
  .popup-espaco-detalhe .popupFooter a .gra-btn { flex: 1 1 auto; }
}

@media (max-width: 767.98px) {
  /* Áreas de toque (>= 44px) dos controlos que ficavam com 21-30px. */
  .gra-mobile-header .navbar-mobile-toggle { min-width: 44px; min-height: 44px; justify-content: center; }
  #conteudo-principal .gra-breadcrumb a.graBcLink { display: inline-block; padding-block: 12px; }
  footer .contacts .iconContent a { display: inline-block; padding-block: 10px; }
}

@media (max-width: 1023.98px) {
  /* Ligação "Saber mais →" dos cabeçalhos de secção: não parte a seta para a linha seguinte. */
  .fo-section-head > a { white-space: nowrap; flex-shrink: 0; }
}

/* Botões 12/24/36 ("Por página"): o min-width/min-height que estava inline
   passa para esta classe (mesmos valores em desktop) para poder ser
   aumentado em mobile sem !important. */
.fo-pp-btn { min-width: 36px; min-height: 0; }

@media (max-width: 767.98px) {
  /* Áreas de toque de ~44px sem aumentar os ícones/aspeto. */
  .fo-pp-btn { min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
  /* Ícone "Ver" (olho) das tabelas de Agenda/Notícias. */
  #conteudo-principal td.options a { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; }
  /* Filtrar/Fechar do painel de filtros: o padding aumenta a área clicável e a
     margem negativa compensa-o, por isso nada se desloca. (Sem display aqui:
     o DS e o style inline decidem qual dos dois é visível.) */
  #conteudo-principal .gra-filter .openFilter,
  #conteudo-principal .gra-filter .closeFilter { padding: 10px; margin: -10px; }
  /* Logótipos do rodapé (ligações para sites externos). */
  footer .gra-logos a, footer .logosLeft a { display: inline-flex; align-items: center; min-height: 44px; }
}

@media (max-width: 767.98px) {
  /* Mais áreas de toque: sino do cabeçalho mobile e ligações isoladas da Home. */
  .gra-mobile-header a.openNotifBox { padding: 10px; margin: -10px; }
  .fo-section-head > a.gra-link { padding-block: 10px; }
  #conteudo-principal .text-center > a.gra-link { display: inline-block; padding-block: 10px; }
}

@media (max-width: 575.98px) {
  /* Assistente de reservas: menos padding lateral (os cartões de resultado
     ficavam com 198px a 320px) e entrelinha legível nas listas de motivos. */
  #assistente-painel.gra-padding-s { padding: 12px !important; } /* !important: .gra-padding-s do DS também o é */
  #assistente-painel [data-assistente-resultados] li { line-height: 1.4; }
}

@media (max-width: 1023.98px) {
  /* O cabeçalho mobile é sticky (90px): sem isto, a validação de formulários,
     o foco por teclado e as âncoras deixavam o campo por baixo do cabeçalho. */
  html:has(#conteudo-principal) { scroll-padding-top: 104px; }
  #conteudo-principal :is(input, select, textarea, button, label, fieldset) { scroll-margin-top: 136px; }
}

/* ==========================================================================
   10. Hour Picker (assets/css/gra-timepicker.css): acento amarelo do Incuba.Açores no Frontoffice,
   com a mesma lógica do datepicker acima. O Backoffice fica com o azul do DS.
   ========================================================================== */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-timepicker-container:before { color: var(--brand-accent); }
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-timepicker .gra-timepicker-now { color: var(--brand-accent); }  /* como o "Hoje" do datepicker */
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-timepicker .gra-timepicker-now:hover:not(:disabled) { color: var(--brand-accent-hover); }
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-timepicker-container input:hover,
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-timepicker-container input.focus,
body:not(.templateDetailsBackoffice):not(.bo-login-body) .gra-timepicker-container input:focus { border-color: var(--brand-accent) !important; box-shadow: none !important; }

/* ==========================================================================
   11. Frontoffice - rodapé e Área de Membro em mobile (2026-10-30)
   ========================================================================== */

/* Barra de logótipos PRR/GRA/RP/UE (+ C19) do rodapé: em mobile, duas por
   linha numa grelha de colunas iguais, cada logótipo centrado na sua célula.
   As margens laterais do DS (a:not(:last-child){margin-right:41px}) e o
   justify-content:end/start desfaziam a distribuição; o tamanho continua
   limitado pelo DS (max 132x32) mas nunca excede a célula, e width/height
   automáticos mantêm a proporção (nada cortado nem deformado). Desktop igual. */
@media (max-width: 767.98px) {
  .gra-footer-opt-2 .third-row .gra-logos {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
    align-items: center;
    justify-items: center;
    width: 100%;
  }
  .gra-footer-opt-2 .third-row .gra-logos > a,
  .gra-footer-opt-2 .third-row .logosLeft > a {
    margin: 0;
    max-width: 100%;
    justify-content: center;
  }
  .gra-footer-opt-2 .third-row .gra-logos img,
  .gra-footer-opt-2 .third-row .logosLeft img {
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 32px;
    object-fit: contain;
  }
  .gra-footer-opt-2 .third-row .logosLeft { justify-content: center; }
}

/* Área de Membro: o contentor da navegação lateral era sempre sticky; em
   mobile (uma só coluna) ficava colado a 96px do topo enquanto se fazia scroll
   e cobria o conteúdo. Só é sticky quando está numa coluna própria (>= 768px). */
@media (min-width: 768px) {
  .fo-sidebar-sticky { position: sticky; top: 96px; }
}

/* Separadores da Área de Membro em mobile: uma só linha com deslocamento
   horizontal (em vez de 3 linhas empilhadas que ocupavam metade do ecrã). */
@media (max-width: 767.98px) {
  .gra-tabs.fo-tabs-scroll {
    flex-wrap: nowrap;
    row-gap: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
  }
  .gra-tabs.fo-tabs-scroll a.gra-tab.iconText {
    flex: 0 0 auto;
    white-space: nowrap;
    padding: 10px 16px;
    min-height: 44px;
  }
}
