/* ==========================================================================
   FlyBase Theme — design tokens + layout + components
   Bootstrap 5 base, Tabler Icons. One accent, overridden per site by a
   site-*.css loaded after this file (flyba.se / hangar.flyba.se).
   ========================================================================== */

/* Teste de fonte pros títulos (h1-h6/.h1-.h6), pedido pelo usuário — Rajdhani segue sendo o
   --font-sans do corpo do texto, não mexido.

   Conjunto COMPLETO gerado pela própria Google Fonts API (fonts.googleapis.com/css2?family=
   Source+Sans+3:ital,wght@0,200..900;1,200..900) — 7 unicode-range por estilo (cyrillic-ext/
   cyrillic/greek-ext/greek/vietnamese/latin-ext/latin), cada um com um arquivo woff2 PRÓPRIO.
   Primeira tentativa usou só 2 blocos avulsos (um normal, um italic) copiados de fora desse
   conjunto — por acaso os arquivos "cyrillic-ext" (não "latin"!), que não têm glifo latino
   nenhum. Sem unicode-range pra restringir, o navegador tentava aplicar esse arquivo errado a
   TUDO, não achava os glifos latinos e caía pro próximo da pilha (Rajdhani) em todo texto —
   por isso "Source Sans 3" nunca apareceu de verdade, só o fallback. unicode-range aqui não é
   frescura: é o que faz o navegador escolher o arquivo certo por caractere. */
@font-face {
  font-family: 'Source Sans 3';
  font-style: italic;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpMtKy2OAdR1K-IwhWudF-R3woqauLYxnV18JRx.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: italic;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpMtKy2OAdR1K-IwhWudF-R3woqY-LYxnV18JRx.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: italic;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpMtKy2OAdR1K-IwhWudF-R3woqa-LYxnV18JRx.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: italic;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpMtKy2OAdR1K-IwhWudF-R3woqZOLYxnV18JRx.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: italic;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpMtKy2OAdR1K-IwhWudF-R3woqaOLYxnV18JRx.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: italic;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpMtKy2OAdR1K-IwhWudF-R3woqaeLYxnV18JRx.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: italic;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpMtKy2OAdR1K-IwhWudF-R3woqZ-LYxnV18A.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpStKy2OAdR1K-IwhWudF-R3wIaZejf5HdF8Q.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpStKy2OAdR1K-IwhWudF-R3wsaZejf5HdF8Q.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpStKy2OAdR1K-IwhWudF-R3wMaZejf5HdF8Q.woff2) format('woff2');
  unicode-range: U+1F00-1FFF;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpStKy2OAdR1K-IwhWudF-R3wwaZejf5HdF8Q.woff2) format('woff2');
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpStKy2OAdR1K-IwhWudF-R3wAaZejf5HdF8Q.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpStKy2OAdR1K-IwhWudF-R3wEaZejf5HdF8Q.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/sourcesans3/v19/nwpStKy2OAdR1K-IwhWudF-R3w8aZejf5Hc.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   ESTILO GLOBAL DO TEMA — os 6 controles de "personalidade visual" do site
   mais comuns de ajustar, reunidos aqui num só lugar em vez de espalhados
   pelo arquivo. Cada um só afeta o que o nome diz — sobe/desce um valor e só
   aquilo muda. Mesma estrutura em site/hangar/theme/flytheme — só os valores
   deste bloco (accent/bg-image) mudam entre eles.
   ========================================================================== */
:root {
  /* 1. Cor predominante do site — SEMPRE a trinca R,G,B (nunca hex): metade
     do tema usa isso dentro de rgba() pra tingir superfícies (botões, links,
     glow, aba ativa, foco de formulário...). --accent-contrast é o texto que
     fica legível em cima de uma superfície pintada com o accent sólido. Este
     valor aqui é só o BASE do theme.css compartilhado — hangar.flyba.se em
     produção sobrescreve pra verde via site-hangar.css, carregado DEPOIS
     deste arquivo (ver header do arquivo). --accent-other-rgb/--bg-body/
     --bg-panel também são "por site", mas ficam no bloco "Site accent" logo
     abaixo — não fazem parte deste grupo de 6. */
  --accent-rgb: 157, 92, 255;
  --accent-contrast: #ffffff;

  /* 2. Transparência dos cards — sobe pra mais opaco/escuro (esconde mais o
     que está atrás), desce pra mais vidro. Todo card do tema deriva disto via
     --surface/--surface-2 (bloco "Glass surfaces" logo abaixo — a conta em si
     mora lá, só o valor que se ajusta é este). */
  --card-opacity: 0.34;

  /* 3 e 4. Borda dos cards — width: 0 remove a borda de todo card do tema de
     uma vez, sem precisar editar cada classe (.fb-card, .fb-product-card,
     .fb-brand-card, .fb-drone-card, .fb-package-card, .fb-kind-card — todas
     usam essas 2 variáveis). A cor é independente de --border-subtle (usada
     em ~50 outros lugares — divisor de lista, input, tabela...) de propósito,
     pra poder mexer só na borda do card sem afetar o resto do tema. */
  --card-border-width: 1px;
  --card-border-color: rgba(255, 255, 255, 0.08);

  /* 5 e 6. Imagem de fundo — caminho relativo a ESTE arquivo (assets/css/),
     não à página (ver body::before mais abaixo, onde background-size/
     -repeat/-position — o "encaixe" da imagem — continuam configurados à
     parte, são decisão de layout, não de estilo). Opacity baixa deixa a
     imagem discreta atrás dos cards; alta faz ela competir com o accent. */
  --bg-image: url("../img/bg01.avif");
  --bg-image-opacity: 0.35;
}

:root {
  /* Rajdhani é a fonte do site em produção; mantida para o tema novo não
     destoar do que já está no ar. Pesos usados: 300-700. */
  --font-sans: "Rajdhani", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Teste: fonte só pra títulos (ver @font-face acima) */
  --font-heading: "Source Sans 3", var(--font-sans);

  /* Squared-off variant: every surface in the theme reads off this scale, so
     these four values are the single switch for the whole look. Restore
     10px / 16px / 22px / 999px to go back to rounded corners.
     The tabs are deliberately NOT on this scale — they keep their own curves
     through --tab-r, so squaring the site never touches them. */
  --radius-sm: 0;
  --radius-md: 0;
  --radius-lg: 0;
  --radius-pill: 0;

  /* Form controls (inputs, checkboxes, .btn-fb-*) deliberately sit OUTSIDE the
     squared-off scale above, the same way --tab-r keeps the tabs curved. Cards,
     thumbnails and tables stay square; only the elements a user types into or
     submits stay soft — reference: cloud.boosteroid.com's auth forms. */
  --form-radius: 0.625rem; /* 10px */

  /* single stroke width shared by the tabs and the content frame — they are
     meant to read as one continuous line, so they must never diverge */
  --line-w: 2px;

  /* header height; the sticky sidebar offsets itself by this */
  --header-h: 84px;

  /* horizontal inset shared by the frame and the header's bottom rule, so the
     rule always lands exactly on the frame's two top corners */
  --frame-inset: 1.25rem;

  /* Rounding of the global outline's two TOP corners only — the bottom ones
     stay square. Also echoed by the menu's top-left and the right rail's
     top-right, so the three read as one radius. */
  --radius-frame: 8px;

  /* --bg-body / --bg-panel are per-site; declared in the Site accent block. */

  /* Glass surfaces. Every card and the menu share one translucent neutral, so
     the page hatch/background reads through all of them at the same strength.
     --card-opacity (declared in "ESTILO GLOBAL DO TEMA" at the top of this
     file) is the ONE knob: raise it for darker/more opaque cards (background
     shows through less), lower it for more glass. --surface and --surface-2
     both derive their alpha from it, so every card in the theme moves
     together (.fb-card, .fb-product-card, .fb-brand-card, .fb-drone-card,
     .fb-package-card, .fb-kind-card, the sidebar menu via --menu-bg — all
     trace back to these two, confirmed by grep before this comment was
     written). Never hardcode a third alpha for a "card" background — add it
     to --surface/--surface-2 (or a --surface-3 that also reads --card-opacity)
     instead, or it silently stops moving with this knob. Deliberately not
     per-scheme: the tint comes from the pattern and page behind, not the
     card. Nothing may use these to mask something opaque (see the hero and
     donut, which mask instead of painting a matching colour on top). */
  --surface: rgba(32, 32, 37, var(--card-opacity));
  --surface-2: rgba(52, 52, 60, var(--card-opacity));

  --bg-card: var(--surface);
  --bg-card-2: var(--surface-2);

  /* The menu panel sits on a neutral near-black of its own rather than the
     scheme-tinted card colour, matching the reference template's menu.
     Translucent on purpose: the newsite's sidebar is `background: transparent`
     so the page's pattern reads through it. Here it keeps some body but still
     lets the hatch show. Shares the card surface so panels stay consistent. */
  --menu-bg: var(--surface);

  --border-subtle: rgba(255, 255, 255, 0.08);
  --border-strong: rgba(255, 255, 255, 0.16);

  --text-primary: #f5f6f8;
  --text-secondary: #9aa0ac;
  /* #6b7280 (original) measured ~3.9:1 against --bg-panel — fails WCAG AA (4.5:1) for the
     normal-size text it's actually used on (fs-11/fs-12/fs-13 captions, KPI descriptions),
     reported unreadable by the user. #828a9b keeps the same hue, ~5.5:1 against every real
     background this theme composites onto (bg-body/bg-panel/surface/surface-2, all checked). */
  --text-muted: #828a9b;

  --success: #22c55e;
  --warning: #f5a524;
  --danger: #ef4444;
  --highlight: #f3e34f;

  --shadow-card: 0 10px 30px -14px rgba(0, 0, 0, 0.6);
}

/* ---- Site accent ----------------------------------------------------------
   flyba.se and hangar.flyba.se are two independent deployments of this same
   theme, differing only in hue. So there is ONE accent here, not a set of
   schemes: a site overrides the knobs below (see assets/css/site-*.css) and
   nothing else. Everything further down derives from --accent-rgb, so the
   two sites cannot drift apart in anything except the colour itself.
   --accent-rgb/--accent-contrast themselves live in "ESTILO GLOBAL DO TEMA"
   at the top of this file now (grouped with the other most-tuned knobs) —
   still per-site, still required, just physically relocated; nothing here
   changed functionally. Keep --accent-rgb as the channel triplet, never as a
   hex: half the theme needs it inside rgba() for tints and glows. --accent is
   derived FROM it, which is why the two can no longer disagree.
   -------------------------------------------------------------------------- */

:root {
  /* --- the per-site knobs --- */

  /* Matiz do site IRMÃO. Usado só pela aba inativa, que representa o outro
     contexto — ela é pintada na cor de lá, não na daqui, para o clique já
     anunciar que leva a outro site. Todo deploy precisa declarar os dois
     (este e --accent-rgb, esse último em "ESTILO GLOBAL DO TEMA" no topo). */
  --accent-other-rgb: 34, 224, 127;
  --bg-body: #0b0a13;
  --bg-panel: #100e1c;

  /* --- derived; do not set per site --- */
  --accent: rgb(var(--accent-rgb));
  --accent-2: color-mix(in srgb, var(--accent) 74%, #000);
  --border-accent: rgba(var(--accent-rgb), 0.32);
  --border-accent-strong: rgba(var(--accent-rgb), 0.6);
}

/* ---- Base -------------------------------------------------------------- */

/* The page colour lives on <html> ONLY, and <body> is explicitly transparent.
   Two things depend on that, and both break silently:
   - body::before sits at z-index -1, and in the root stacking context
     negative-z descendants paint BEFORE in-flow ones, so any opaque body
     background hides the texture layer;
   - Bootstrap's reboot sets `body { background-color: var(--bs-body-bg) }`,
     which is #fff, so simply omitting a background here is not enough —
     transparent has to be declared to beat it. */
html {
  background: var(--bg-body);
  color: var(--text-primary);
}

body {
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  font-family: var(--font-heading);
}

/* Page hatch, built the way the newsite does it: a fixed, full-viewport layer
   at z-index -1, which paints above the page colour but below every element.
   Fixed (not scrolled) means it stays put while content moves over it, and any
   translucent surface — the menu — reveals it. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: var(--bg-image-opacity);
  background-image: var(--bg-image);
  background-repeat: repeat;
  background-position: center;
}

a { color: inherit; }

::selection { background: rgba(var(--accent-rgb), 0.35); }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); }

/* ---- Top navbar --------------------------------------------------------- */

/* Frosted glass, as in the reference template: a fully transparent background
   with backdrop-filter: blur(30px), so scrolled content stays visible but
   diffused behind the bar.
   Sticky rather than fixed: the tabs have to land exactly on the header's
   bottom rule, and staying in flow keeps that alignment without a compensating
   offset on the content.
   The rule under the navbar belongs to the navbar (::after), not to the frame,
   so it stays put while the content scrolls beneath it. The frame therefore
   carries no top border — there must only ever be one line. */
.fb-navbar {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: var(--header-h);
  padding: 0 1.5rem;
  background: transparent;
  backdrop-filter: blur(30px);
  -webkit-backdrop-filter: blur(30px);
  position: sticky;
  top: 0;
  z-index: 100;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .fb-navbar { background: var(--bg-body); }
}

/* The line the tabs sit on. A box with only top and side borders rather than a
   flat bar, so the outline can round its two top corners: the side borders are
   short stubs that carry the curve down to where the frame's own borders take
   over. The frame is pushed down by exactly that radius so the two meet end to
   end instead of overlapping. Drawn below the tabs, whose opaque fills
   interrupt it. */
.fb-navbar::after {
  content: "";
  position: absolute;
  inset-inline: var(--frame-inset);
  bottom: calc(-1 * var(--radius-frame));
  height: calc(var(--radius-frame) + var(--line-w));
  border: var(--line-w) solid var(--border-accent);
  border-bottom: none;
  border-radius: var(--radius-frame) var(--radius-frame) 0 0;
}

.fb-brand {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  flex-shrink: 0;
}

.fb-brand-mark {
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.fb-brand-mark span { color: var(--accent); }

.fb-burger {
  width: 2.35rem;
  height: 2.35rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-subtle);
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.15rem;
}

.fb-burger:hover { color: var(--text-primary); border-color: var(--border-strong); }

/* ---- Tab switcher ---------------------------------------------------------
   Trapezoidal tabs that fuse into the content frame below. Each tab is clipped
   to a trapezoid (narrower at the top) and painted in two layers: the element
   itself is the stroke, and ::before is the fill, inset from every edge EXCEPT
   the bottom. Leaving the bottom open is what makes the active tab's stroke and
   the frame's top border read as one continuous line wrapping the page.
   The group's outer edges slant; shared inner edges stay vertical, so any
   number of tabs tile into a single trapezoid silhouette.
   -------------------------------------------------------------------------- */

.fb-tabs {
  /* 30% smaller than the reference geometry (was 30/62/208/9/7px) — kept
     proportional across every dimension, not just font-size, so the trapezoid
     silhouette and its corner-softening math stay in the same ratio. */
  --tab-slant: 21px;  /* horizontal run of the slanted outer edges */
  --tab-h: 43.4px;
  --tab-min: 145.6px;
  --tab-r: 6.3px;       /* softening on the group's two outer top corners */
  --tab-lift: 4.9px;    /* how much shorter the resting tabs are */

  position: relative;
  z-index: 2;
  display: flex;
  align-items: stretch;
  align-self: flex-end;
  flex-shrink: 0;
  height: var(--tab-h);
  /* flush with the navbar's bottom edge, so the tabs' last --line-w overlaps
     the header rule drawn there and can interrupt it */
  margin-bottom: 0;
}

.fb-tab {
  --slant-l: 0px;
  --slant-r: 0px;
  --r-l: 0px;
  --r-r: 0px;
  /* Aba em repouso = o OUTRO site. Traço, preenchimento e ícone saem de
     --accent-other-rgb, não do accent daqui. */
  --tab-stroke: rgba(var(--accent-other-rgb), 0.32);
  --tab-fill:
    linear-gradient(180deg, rgba(var(--accent-other-rgb), 0.12), rgba(var(--accent-other-rgb), 0.04)),
    var(--bg-body);
  /* closed at the bottom: the frame's line runs across and caps the tab */
  --tab-bottom: var(--line-w);

  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: var(--tab-min);
  margin-top: var(--tab-lift);
  padding: 0 0.98rem;
  color: var(--text-secondary);
  font-size: 0.665rem;
  font-weight: 600;
  text-decoration: none;
  background: var(--tab-stroke);
  transition: color 0.15s ease;

  /* The three points per corner approximate an arc; the offsets are tuned for
     the default slant, and --tab-r: 0 collapses them back to a sharp corner. */
  clip-path: polygon(
    calc(var(--slant-l) - 0.45 * var(--r-l)) calc(0.9 * var(--r-l)),
    calc(var(--slant-l) + 0.15 * var(--r-l)) calc(0.25 * var(--r-l)),
    calc(var(--slant-l) + var(--r-l)) 0,
    calc(100% - var(--slant-r) - var(--r-r)) 0,
    calc(100% - var(--slant-r) - 0.15 * var(--r-r)) calc(0.25 * var(--r-r)),
    calc(100% - var(--slant-r) + 0.45 * var(--r-r)) calc(0.9 * var(--r-r)),
    100% 100%,
    0 100%
  );
}

.fb-tabs > .fb-tab:first-child { --slant-l: var(--tab-slant); --r-l: var(--tab-r); }
.fb-tabs > .fb-tab:last-child { --slant-r: var(--tab-slant); --r-r: var(--tab-r); }
/* overlap by one stroke so neighbours share a single divider line */
.fb-tabs > .fb-tab + .fb-tab { margin-left: calc(-1 * var(--line-w)); }

.fb-tab::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--tab-fill);
  clip-path: polygon(
    calc(var(--slant-l) + var(--line-w) - 0.45 * var(--r-l)) calc(var(--line-w) + 0.9 * var(--r-l)),
    calc(var(--slant-l) + var(--line-w) + 0.15 * var(--r-l)) calc(var(--line-w) + 0.25 * var(--r-l)),
    calc(var(--slant-l) + var(--line-w) + var(--r-l)) var(--line-w),
    calc(100% - var(--slant-r) - var(--line-w) - var(--r-r)) var(--line-w),
    calc(100% - var(--slant-r) - var(--line-w) - 0.15 * var(--r-r)) calc(var(--line-w) + 0.25 * var(--r-r)),
    calc(100% - var(--slant-r) - var(--line-w) + 0.45 * var(--r-r)) calc(var(--line-w) + 0.9 * var(--r-r)),
    calc(100% - var(--line-w)) calc(100% - var(--tab-bottom)),
    var(--line-w) calc(100% - var(--tab-bottom))
  );
}

.fb-tab-label {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.42rem;
  white-space: nowrap;
}

.fb-tab i {
  width: 1.33rem;
  height: 1.33rem;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.77rem;
  background: rgba(var(--accent-other-rgb), 0.16);
  color: rgba(var(--accent-other-rgb), 0.9);
}

.fb-tab:not(.is-active):hover {
  color: var(--text-primary);
  --tab-stroke: rgba(var(--accent-other-rgb), 0.55);
  --tab-fill:
    linear-gradient(180deg, rgba(var(--accent-other-rgb), 0.2), rgba(var(--accent-other-rgb), 0.07)),
    var(--bg-body);
}

/* The active tab is the only one that breaks through the frame's line: it is
   taller than the others and open at the bottom, so its stroke and the frame's
   top border join into a single outline wrapping the content. */
.fb-tab.is-active {
  --tab-stroke: var(--accent);
  --tab-fill:
    linear-gradient(
      180deg,
      rgba(var(--accent-rgb), 0.32) 0%,
      rgba(var(--accent-rgb), 0.12) 50%,
      rgba(var(--accent-rgb), 0.04) 100%
    ),
    var(--bg-body);
  --tab-bottom: 0px;
  margin-top: 0;
  color: var(--text-primary);
  z-index: 1;
  filter: drop-shadow(0 -1px 16px rgba(var(--accent-rgb), 0.35));
}

.fb-tab.is-active i { background: rgba(var(--accent-rgb), 0.2); color: var(--accent); }

.fb-tab.is-active .fb-tab-label::after {
  content: "";
  position: absolute;
  left: 10%;
  right: 10%;
  bottom: -10.5px;
  height: 2.1px;
  border-radius: 2.1px;
  background: var(--accent);
  box-shadow: 0 0 12px rgba(var(--accent-rgb), 0.9);
}

.fb-navbar-search {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill);
  padding: 0.55rem 1.1rem;
  max-width: 560px;
  min-width: 0;
  color: var(--text-muted);
}

.fb-navbar-search i { font-size: 1.05rem; flex-shrink: 0; }

.fb-navbar-search input {
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-size: 0.875rem;
  width: 100%;
  min-width: 0;
}

.fb-navbar-search input::placeholder { color: var(--text-muted); }

.fb-navbar-actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-shrink: 0;
  margin-left: auto;
}

.fb-icon-btn {
  width: 2.4rem;
  height: 2.4rem;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  font-size: 1.05rem;
  text-decoration: none;
}

.fb-icon-btn:hover { color: var(--text-primary); border-color: var(--border-strong); }

.fb-flag {
  width: 2.4rem;
  height: 2.4rem;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-subtle);
  font-size: 1.1rem;
}

/* ---- Content frame — colored border that "wraps" the whole page body --- */

/* Starts one radius below the header so its straight side borders begin exactly
   where the header rule's rounded corners end. Bottom corners stay square. */
.fb-frame {
  position: relative;
  margin: var(--radius-frame) var(--frame-inset) var(--frame-inset);
  border: var(--line-w) solid var(--border-accent);
  border-top: none; /* the header's ::after is this frame's top edge */
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

/* Three containers side by side: menu / content / auxiliary rail. The rail
   drops below the content first, then the menu collapses into the burger. */
.fb-body {
  --col-menu: 264px;
  --col-menu-mini: 68px;   /* coluna do menu recolhido (só ícones) */
  --col-rail: 340px;

  padding: 1.5rem;
  display: grid;
  grid-template-columns: var(--col-menu) minmax(0, 1fr) var(--col-rail);
  gap: 1.5rem;
  align-items: start;
}

/* pages that render no auxiliary rail collapse to two columns */
.fb-body--no-rail { grid-template-columns: var(--col-menu) minmax(0, 1fr); }

@media (max-width: 1400px) {
  .fb-body { grid-template-columns: var(--col-menu) minmax(0, 1fr); }
  .fb-aside { grid-column: 1 / -1; }
}

/* O menu some abaixo de 992px, mas a regra vive junto do botão hambúrguer, no
   fim do arquivo — aqui ela viria ANTES do `.fb-sidebar { display: flex }` base
   e perderia a cascata por ordem, com a mesma especificidade. */
@media (max-width: 992px) {
  .fb-body { grid-template-columns: minmax(0, 1fr); }
}

/* mirrors the menu: the rail's first panel echoes the outline's top-right */
.fb-aside > *:first-child { border-start-end-radius: var(--radius-frame); }

/* ---- Sidebar ------------------------------------------------------------ */

/* O painel é a COLUNA, não o menu: assim o fundo desce até o fim do conteúdo,
   como o .app-sidebar do newsite, em vez de parar no último item. Por isso
   align-self: stretch — o grid usa align-items: start, que encolheria a coluna
   à altura do conteúdo dela. A aderência no scroll passou para o .fb-menu, que
   rola dentro deste painel. */
.fb-sidebar {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding: 0.5rem;
  background: var(--menu-bg);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  border-start-start-radius: var(--radius-frame);
}

/* Coluna central. min-width:0 impede que tabela larga ou palavra longa estoure
   a célula do grid em vez de rolar dentro dela. */
.fb-main { min-width: 0; }

/* ---- Sidebar menu ---------------------------------------------------------
   Mirrors the reference template's menu: uppercase category headings, items
   made of icon + label + angle, and accordion submenus nesting up to three
   levels. Submenu rows are marked by a short gradient dash instead of an icon
   and fade in one after another when their branch opens.
   -------------------------------------------------------------------------- */

.fb-menu,
.fb-submenu {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* sem fundo próprio — quem pinta é .fb-sidebar; aqui só a aderência no scroll */
.fb-menu {
  position: sticky;
  top: calc(var(--header-h) + 1rem);
}

.fb-menu__category {
  padding: 0.85rem 0.85rem 0.5rem;
  color: var(--text-muted);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;
}

.fb-menu__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-decoration: none;
  cursor: pointer;
}

.fb-menu__item:hover,
.fb-menu__item.active {
  background: rgba(255, 255, 255, 0.05);
  color: var(--accent);
}

/* the icon glow is what makes an active row read at a glance */
.fb-menu__item:hover .fb-menu__icon,
.fb-menu__item.active .fb-menu__icon {
  color: var(--accent);
  filter: drop-shadow(0 0 5px rgba(var(--accent-rgb), 0.9));
}

/* The reference marks the active row with a neutral lift plus the accent on the
   text and the glowing icon — not with a tinted fill. */
.fb-menu__item.active { background: rgba(255, 255, 255, 0.07); }

.fb-menu__icon {
  flex-shrink: 0;
  width: 1.15rem;
  font-size: 1.15rem;
  line-height: 1;
  text-align: center;
}

.fb-menu__label { flex: 1; white-space: nowrap; }

.fb-menu__angle {
  flex-shrink: 0;
  font-size: 0.85rem;
  transition: transform 0.2s ease;
}

.has-sub.open > .fb-menu__item > .fb-menu__angle { transform: rotate(90deg); }

/* ---- Submenus ---- */

.fb-submenu { display: none; }

/* Estado inicial pelo CSS, não pelo JS: um ramo marcado .open no servidor já
   nasce expandido mesmo antes do theme.js rodar (ou sem JS). O toggle continua
   sendo do JS, que escreve display inline e vence esta regra.
   Descendente, não filho direto: quando o ramo tem um grupo "ver todos"
   (.fb-menu__panel, ver abaixo), a .fb-submenu não é mais filha direta de
   .has-sub. :not(--more) exclui só o grupo extra do "ver todos" — esse é
   controlado por clique (.fb-menu__more.is-open), não por este "sempre
   aberto"; qualquer outro .fb-submenu (com ou sem .fb-menu__panel por volta)
   continua abrindo do jeito que sempre abriu. */
.fb-menu .has-sub.open .fb-submenu:not(.fb-submenu--more) { display: block; }

.fb-submenu li { position: relative; padding-inline-start: 1.5rem; }

.fb-submenu .fb-menu__item {
  padding: 0.4rem 0.75rem;
  font-weight: 500;
  font-size: 0.82rem;
  background: transparent;
  border-color: transparent;
  inset-inline-start: 0;
  transition: inset-inline-start 0.25s ease;
}

.fb-submenu .fb-menu__item::before {
  content: "";
  position: absolute;
  inset-inline-start: -4px;
  width: 10px;
  height: 2px;
  background: linear-gradient(to left, transparent, var(--text-secondary));
}

.fb-submenu .fb-menu__item:hover { inset-inline-start: 0.35rem; }

.fb-submenu .fb-menu__item:hover::before,
.fb-submenu .fb-menu__item.active::before {
  background: linear-gradient(to left, transparent, var(--accent));
}

/* staggered reveal, one row after the next, as in the reference menu */
.fb-submenu > li > .fb-menu__item {
  animation: fb-menu-in 0.2s ease-in-out both;
}

.fb-submenu > li:nth-child(1) > .fb-menu__item { animation-delay: 0.05s; }
.fb-submenu > li:nth-child(2) > .fb-menu__item { animation-delay: 0.1s; }
.fb-submenu > li:nth-child(3) > .fb-menu__item { animation-delay: 0.15s; }
.fb-submenu > li:nth-child(4) > .fb-menu__item { animation-delay: 0.2s; }
.fb-submenu > li:nth-child(5) > .fb-menu__item { animation-delay: 0.25s; }
.fb-submenu > li:nth-child(6) > .fb-menu__item { animation-delay: 0.3s; }
.fb-submenu > li:nth-child(7) > .fb-menu__item { animation-delay: 0.35s; }
.fb-submenu > li:nth-child(8) > .fb-menu__item { animation-delay: 0.4s; }
.fb-submenu > li:nth-child(n + 9) > .fb-menu__item { animation-delay: 0.45s; }

@keyframes fb-menu-in {
  from { transform: translateY(1.5rem); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .fb-submenu > li > .fb-menu__item { animation: none; }
}

/* ---- Submenu "ver todos" (`.fb-menu__more`) -----------------------------------
   Alguns ramos (Produtos) têm tipos demais pra mostrar todos de cara — só os
   destacados (ver buildMainMenu() em functions.php) ficam visíveis, o resto
   entra aqui, atrás de um botão. .fb-menu__panel embrulha a --primary (sempre
   aberta) e este grupo (fechado até o clique) num só bloco, pra que os dois
   se movam juntos quando a rail colapsada os transforma num flyout (ver
   § Menu toggle, mais abaixo). Sem esse embrulho a --more reaproveitaria a
   mesma classe .fb-submenu e a regra "sempre aberto" acima a abriria também —
   por isso ela é excluída de propósito ali (:not(.fb-submenu--more)). */

.fb-menu__more-toggle {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.35rem;
  width: 100%;
  margin-top: 0.15rem;
  padding: 0.4rem 0.75rem 0.4rem 1.5rem;
  border: 0;
  background: none;
  color: var(--text-secondary);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
}

.fb-menu__more-toggle:hover { color: var(--accent); }

.fb-menu__more-toggle i { font-size: 0.8rem; transition: transform 0.2s ease; }

.fb-menu__more.is-open .fb-menu__more-toggle i { transform: rotate(180deg); }

.fb-menu__more .fb-submenu--more { display: none; }

.fb-menu__more.is-open .fb-submenu--more { display: block; }

.fb-badge-new {
  flex-shrink: 0;
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  background: rgba(var(--accent-rgb), 0.16);
  color: var(--accent);
  padding: 0.18rem 0.4rem;
  border-radius: var(--radius-sm);
}

.fb-cta {
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  background: var(--bg-panel);
}

.fb-cta h4 { font-size: 0.95rem; font-weight: 700; margin: 0 0 0.4rem; }
.fb-cta p { color: var(--text-secondary); font-size: 0.82rem; margin: 0 0 1rem; line-height: 1.5; }

.fb-cta-art {
  height: 96px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(var(--accent-rgb), 0.55);
  font-size: 3.75rem;
  margin-bottom: 0.75rem;
}

/* ---- Buttons -------------------------------------------------------------- */

/* Gradient reads as the site's own accent fading into its darker mix, never a
   hardcoded hue — so it re-tints automatically between flyba.se and Hangar
   (reference: cloud.boosteroid.com login button). Hover is a brightness
   filter, not a second gradient: swapping `background` can't tween, and a
   reversed copy of the same two stops was too close in luminance to read as
   a hover at all. */
.btn-fb-accent {
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  border: 1px solid transparent;
  color: var(--accent-contrast);
  font-weight: 700;
  border-radius: var(--form-radius);
  padding: 0.6rem 1.1rem;
  font-size: 0.875rem;
  transition: filter 0.2s ease;
}

.btn-fb-accent:hover { color: var(--accent-contrast); filter: brightness(1.12); }

.btn-fb-outline {
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text-primary);
  border-radius: var(--form-radius);
  padding: 0.6rem 1.1rem;
  font-weight: 600;
  font-size: 0.875rem;
}

.btn-fb-outline:hover { color: var(--text-primary); border-color: var(--accent); }

/* filled neutral — the quiet partner to btn-fb-accent when both sit together */
.btn-fb-soft {
  background: var(--bg-card-2);
  border: 1px solid transparent;
  color: var(--text-primary);
  border-radius: var(--form-radius);
  padding: 0.6rem 1.1rem;
  font-weight: 600;
  font-size: 0.875rem;
}

.btn-fb-soft:hover { color: var(--text-primary); background: var(--bg-panel); }

.btn-fb-sm { padding: 0.42rem 0.75rem; font-size: 0.78rem; border-radius: var(--form-radius); }
.btn-fb-lg { padding: 0.85rem 1.6rem; font-size: 0.9rem; }

/* keeps the icon optically centred against the label in every button size */
.btn-fb-accent,
.btn-fb-outline,
.btn-fb-soft {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  text-decoration: none;
  line-height: 1.2;
}

/* Bootstrap's own .btn dims :disabled automatically; these don't inherit that
   rule, so without this a disabled save/submit button looks fully clickable. */
.btn-fb-accent:disabled,
.btn-fb-outline:disabled,
.btn-fb-soft:disabled,
.btn-fb-danger:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  filter: none;
}

/* ---- Generic card --------------------------------------------------------- */

.fb-card {
  background: var(--bg-card);
  border: var(--card-border-width) solid var(--card-border-color);
  border-radius: var(--radius-lg);
  padding: 1.3rem 1.4rem;
  box-shadow: var(--shadow-card);
}

.fb-card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.1rem;
}

/* Métricas do newsite: título do card em accent, 1.15rem, peso 500. */
.fb-card-header h3 {
  font-size: 1.15rem;
  font-weight: 500;
  margin: 0;
  color: var(--accent);
  text-transform: capitalize;
}
.fb-card-header p { color: var(--text-secondary); font-size: 0.82rem; margin: 0.2rem 0 0; }

.fb-link-accent {
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 600;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  white-space: nowrap;
}

.fb-link-accent:hover { color: var(--accent); text-decoration: underline; }

/* ---- Stat cards ------------------------------------------------------------ */

.fb-stat {
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  padding: 1.1rem 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  height: 100%;
}

.fb-stat-icon {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-sm);
  background: rgba(var(--accent-rgb), 0.14);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
}

.fb-stat-value { font-size: 1.65rem; font-weight: 800; line-height: 1; }
.fb-stat-label { color: var(--text-secondary); font-size: 0.83rem; }
.fb-stat-link { color: var(--accent); font-size: 0.78rem; font-weight: 700; text-decoration: none; display: inline-flex; align-items: center; gap: 0.25rem; }
.fb-stat-link:hover { color: var(--accent); }

/* ---- Drone / product cards -------------------------------------------------- */

.fb-drone-card {
  background: var(--bg-card-2);
  border: var(--card-border-width) solid var(--card-border-color);
  border-radius: var(--radius-md);
  padding: 1rem;
  min-width: 235px;
}

.fb-drone-status {
  display: inline-block;
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  padding: 0.22rem 0.5rem;
  border-radius: var(--radius-sm);
  margin-bottom: 0.6rem;
}

.fb-drone-status.is-active { background: rgba(var(--accent-rgb), 0.18); color: var(--accent); }
.fb-drone-status.is-inactive { background: rgba(255, 255, 255, 0.08); color: var(--text-muted); }

.fb-drone-img {
  height: 100px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: 3.2rem;
  opacity: 0.9;
}

.fb-drone-name { font-weight: 700; font-size: 0.95rem; margin: 0.35rem 0 0.1rem; }
.fb-drone-sn { color: var(--text-muted); font-size: 0.72rem; margin-bottom: 0.6rem; }

.fb-drone-meta {
  display: flex;
  gap: 0.9rem;
  font-size: 0.76rem;
  color: var(--text-secondary);
  flex-wrap: wrap;
}

.fb-drone-meta span { display: inline-flex; align-items: center; gap: 0.3rem; }

.fb-scroll-row {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  padding-bottom: 0.25rem;
  scrollbar-width: none;
}

.fb-scroll-row::-webkit-scrollbar { display: none; }

/* ---- Table ------------------------------------------------------------------ */

.fb-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }

.fb-table th {
  text-align: left;
  color: var(--text-muted);
  font-weight: 700;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0 0 0.7rem;
  border-bottom: 1px solid var(--border-subtle);
}

.fb-table td {
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: middle;
}

.fb-table tr:last-child td { border-bottom: none; }

.fb-table-drone { display: flex; align-items: center; gap: 0.6rem; font-weight: 600; }
.fb-table-drone i { color: var(--text-secondary); font-size: 1.1rem; }
/* logo real (quando o item tem um), no lugar do ícone genérico — mesma
   pegada visual, senão a imagem nasce sem tamanho e estoura a linha */
.fb-table-drone img { width: 1.3rem; height: 1.3rem; object-fit: contain; flex-shrink: 0; }

.fb-ring {
  width: 2.3rem;
  height: 2.3rem;
  border-radius: 50%;
  border: 2px solid var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.65rem;
  font-weight: 800;
  color: var(--text-primary);
}

.fb-table-dots {
  width: 1.8rem;
  height: 1.8rem;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
}

.fb-table-dots:hover { background: rgba(255, 255, 255, 0.06); color: var(--text-primary); }

/* ---- Donut chart (pure CSS) --------------------------------------------------- */

.fb-donut-wrap { display: flex; align-items: center; gap: 1.4rem; flex-wrap: wrap; }

.fb-donut {
  width: 148px;
  height: 148px;
  flex-shrink: 0;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The ring lives on the pseudo-element so the mask that punches its hole can't
   reach the label in the middle. A real hole, not a disc painted in the card
   colour — the card is translucent, so a matching disc would show as a patch. */
.fb-donut::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(var(--donut-slices, var(--accent) 0 100%));
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 15px), #000 calc(100% - 15px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 15px), #000 calc(100% - 15px));
}

.fb-donut-center { position: relative; z-index: 1; text-align: center; }
.fb-donut-center b { display: block; font-size: 1.5rem; font-weight: 800; }
.fb-donut-center span { font-size: 0.7rem; color: var(--text-secondary); }

.fb-legend { display: flex; flex-direction: column; gap: 0.55rem; flex: 1; min-width: 150px; }
.fb-legend-item { display: flex; align-items: center; gap: 0.55rem; font-size: 0.8rem; }
.fb-legend-dot { width: 0.55rem; height: 0.55rem; border-radius: 50%; flex-shrink: 0; }
.fb-legend-label { color: var(--text-secondary); flex: 1; }
.fb-legend-value { font-weight: 700; }

/* ---- Progress / checklist ------------------------------------------------------ */

.fb-progress { height: 8px; border-radius: var(--radius-pill); background: rgba(255, 255, 255, 0.08); overflow: hidden; margin: 0.6rem 0 1.1rem; }
.fb-progress-bar { height: 100%; background: var(--accent); border-radius: var(--radius-pill); transition: width 0.2s ease; }

/* ---- Right-column list items (maintenance, comparisons, updates) --------------
   Espelha os itens do trilho do newsite: sem gap entre linhas — a separação é
   uma divisória por item — padding 0.5rem, miniatura 4rem (.avatar-xl), título
   0.8125rem/500 (.fs-13) e apoio 0.6875rem (.fs-11).
   -------------------------------------------------------------------------- */

.fb-list { display: flex; flex-direction: column; }

.fb-list-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem;
  border-bottom: 1px solid var(--border-subtle);
}

.fb-list-item:last-child { border-bottom: none; }

.fb-list-thumb {
  width: 4rem;
  height: 4rem;
  border-radius: var(--radius-sm);
  background: var(--bg-card-2);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: 1.25rem;
  flex-shrink: 0;
  overflow: hidden;
}

.fb-list-thumb.is-vs { display: flex; gap: 2px; font-size: 0.95rem; }

/* o trilho também carrega arte real de produto, não só ícone */
.fb-list-thumb img { width: 100%; height: 100%; object-fit: contain; }

.fb-list-empty { color: var(--text-secondary); font-size: 0.8125rem; margin: 0; padding: 0.75rem 0.5rem; }

.fb-list-body { min-width: 0; flex: 1; }
.fb-list-title { font-weight: 500; font-size: 0.8125rem; color: var(--text-primary); }
a.fb-list-title:hover { color: var(--accent); }
.fb-list-sub { color: var(--text-muted); font-size: 0.6875rem; margin-top: 0.1rem; display: block; }

.fb-list-meta {
  margin-left: auto;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--accent);
  background: rgba(var(--accent-rgb), 0.12);
  padding: 0.25rem 0.55rem;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  flex-shrink: 0;
}

/* ---- Package item card (`.fb-package-card`) -----------------------------------
   Grid of compact cards for "Conteúdo do pacote" (site product-detail.php,
   aba Opções de Combo) — was a table/list before, reads better as a small
   grid when there's nothing but a name and a count per row. Reuses
   .fb-list-title/.fb-list-meta as-is (already the right look for exactly this
   name+badge pair, see .fb-list above) inside a card shell instead of a list
   row — no thumbnail, the reference list this replaced didn't have one
   either and the package's own name is already the accordion header above. */
.fb-package-card {
  background: var(--bg-card-2);
  border: var(--card-border-width) solid var(--card-border-color);
  border-radius: var(--radius-md);
  padding: 0.85rem 1rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  height: 100%;
}

/* ---- Hero -----------------------------------------------------------------
   Photo bleeds into the top-right of the body and is faded back to the card
   colour on the left, so the copy always sits on flat background. The stats
   strip is a separate band below, split off by a full-bleed rule.
   -------------------------------------------------------------------------- */

.fb-hero {
  --hero-pad: 2.5rem;

  position: relative;
  overflow: hidden;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
}

.fb-hero__body {
  position: relative;
  padding: var(--hero-pad);
  min-height: 380px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* The photo is a real <img> rather than a background: a relative url() carried
   through a custom property resolves against the stylesheet, not the document,
   which puts it under assets/css/ and silently fails. */
.fb-hero__media {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

/* The photo is masked away on the left rather than covered by a matching
   overlay: the card is translucent, so an opaque overlay would be visible as a
   patch. Masking dissolves the image itself and lets the card's glass through. */
.fb-hero__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center right;
  -webkit-mask-image: linear-gradient(90deg, transparent 0 22%, #000 62%);
  mask-image: linear-gradient(90deg, transparent 0 22%, #000 62%);
}

.fb-hero__content { position: relative; z-index: 1; }

.fb-hero__eyebrow {
  color: var(--text-secondary);
  font-size: 0.9rem;
  margin: 0 0 1rem;
}

.fb-hero__title {
  margin: 0;
  /* wide enough to break after "maior base", as in the reference */
  max-width: 22ch;
  font-size: 2.35rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--text-primary);
}

.fb-hero__title em { color: var(--highlight); font-style: normal; }

.fb-hero__lead {
  color: var(--text-secondary);
  max-width: 58ch;
  margin: 1.5rem 0 2rem;
  font-size: 0.95rem;
  line-height: 1.9;
}

.fb-hero__actions { display: flex; gap: 1rem; flex-wrap: wrap; }

/* ---- Hero stats band ---- */

.fb-hero__stats {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  border-top: 1px solid var(--border-subtle);
}

.fb-hero__stat {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 1.5rem 2.25rem;
}

.fb-hero__stat:first-child { padding-left: var(--hero-pad); }

/* dividers sit between items only, and stop short of the band's edges */
.fb-hero__stat + .fb-hero__stat::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 1.35rem;
  bottom: 1.35rem;
  width: 1px;
  background: var(--border-subtle);
}

.fb-hero__stat > i {
  font-size: 1.75rem;
  color: var(--accent);
  line-height: 1;
  flex-shrink: 0;
}

.fb-hero__stat b {
  display: block;
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1.3;
}

.fb-hero__stat span {
  color: var(--text-secondary);
  font-size: 0.82rem;
}

@media (max-width: 768px) {
  .fb-hero { --hero-pad: 1.5rem; }
  .fb-hero__body { min-height: 0; }
  .fb-hero__media::after {
    background: linear-gradient(180deg, rgba(0, 0, 0, 0.35), var(--bg-card) 78%);
  }
  .fb-hero__title { font-size: 1.75rem; max-width: none; }
  .fb-hero__stat { padding: 1.1rem var(--hero-pad); }
  .fb-hero__stat + .fb-hero__stat::before { display: none; }
}

/* ---- Product cards (Explorar) ---------------------------------------------------- */

.fb-product-card {
  background: var(--bg-card);
  border: var(--card-border-width) solid var(--card-border-color);
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  height: 100%;
}

.fb-product-body {
  padding: 1.25rem;
  text-align: center;
  flex: 1;
}

/* 16:9 box with the product contained, not cropped — catalogue shots are shot
   on varying canvases and cropping them clips the airframe */
.fb-product-thumb {
  aspect-ratio: 16 / 9;
  margin-bottom: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: 2.75rem;
  text-decoration: none;
}

.fb-product-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.fb-product-title { font-weight: 600; font-size: 1rem; }
.fb-product-title a { text-decoration: none; }
.fb-product-title a:hover { color: var(--accent); }
.fb-product-cat { color: var(--text-secondary); font-size: 0.82rem; margin-top: 0.15rem; }
.fb-product-price { font-size: 1rem; font-weight: 600; margin-top: 0.35rem; }

/* split footer: view on the left, compare on the right, dashed rules between */
.fb-product-actions {
  display: flex;
  border-top: 1px dashed var(--border-subtle);
}

.fb-product-actions a {
  flex: 1;
  padding: 0.85rem 0.5rem;
  text-align: center;
  font-size: 0.82rem;
  text-decoration: none;
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
}

.fb-product-actions a + a { border-inline-start: 1px dashed var(--border-subtle); }
.fb-product-actions a:hover { background: rgba(255, 255, 255, 0.04); color: var(--text-primary); }
.fb-product-actions a.is-compare { color: var(--accent); }

/* ---- Brand card ------------------------------------------------------------ */

.fb-brand-card {
  background: var(--bg-card);
  border: var(--card-border-width) solid var(--card-border-color);
  border-radius: var(--radius-md);
  padding: 1rem;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.fb-brand-card__img {
  aspect-ratio: 16 / 9;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: 1.75rem;
  text-decoration: none;
}

.fb-brand-card__img img { width: 100%; height: 100%; object-fit: contain; }

.fb-brand-card__title {
  margin: 0;
  text-align: center;
  font-size: 0.92rem;
  font-weight: 600;
}

.fb-brand-card__title a { text-decoration: none; }
.fb-brand-card__title a:hover { color: var(--accent); }

/* ---- Dropdown ---------------------------------------------------------------
   Métricas do newsite: painel sem padding e sem raio interno (o item preenche a
   borda), item 0.5rem/0.9375rem em 0.8125rem, ativo com fundo tingido no accent.
   Fundo OPACO de propósito — o menu flutua sobre o conteúdo, e as superfícies de
   vidro do tema deixariam o texto de trás atravessar.
   -------------------------------------------------------------------------- */

.dropdown-menu {
  --bs-dropdown-bg: var(--bg-panel);
  --bs-dropdown-border-color: var(--border-subtle);
  --bs-dropdown-border-radius: var(--radius-sm);
  /* Todo item — inclusive o selecionado — usa o nível primário de texto, e o
     selecionado é distinguido SÓ pelo fundo tingido. Colorir o texto ativo com
     o accent e ainda clarear o fundo dele colocava roxo sobre roxo: media
     4.12:1, o pior contraste do menu. Com branco sobre o mesmo fundo escuro
     (0.08) são 16.34:1. Não aumente este alfa: cada ponto a mais aproxima o
     fundo do texto e derruba a legibilidade do item que mais importa. */
  --bs-dropdown-link-color: var(--text-primary);
  --bs-dropdown-link-hover-color: var(--text-primary);
  --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.07);
  --bs-dropdown-link-active-color: var(--text-primary);
  --bs-dropdown-link-active-bg: rgba(var(--accent-rgb), 0.08);
  --bs-dropdown-item-padding-y: 0.5rem;
  --bs-dropdown-item-padding-x: 0.9375rem;
  --bs-dropdown-font-size: 0.875rem;
  --bs-dropdown-padding-y: 0;
  --bs-dropdown-min-width: 11rem;

  overflow: hidden;
  box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.4);
  animation: fb-dropdown-in 0.2s both;
}

.dropdown-item {
  font-size: 0.8125rem;
  font-weight: 400;
  cursor: pointer;
}

.dropdown-divider { border-color: var(--border-subtle); }

@keyframes fb-dropdown-in {
  from { transform: translateY(0.5rem); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .dropdown-menu { animation: none; }
}

/* caret em chevron do Tabler, no lugar do triângulo do Bootstrap */
.dropdown-toggle::after {
  content: "\ea5f";
  font-family: tabler-icons;
  border: 0;
  vertical-align: -0.1em;
  margin-inline-start: 0.3rem;
  font-size: 0.85em;
  line-height: 1;
  transition: transform 0.15s ease;
}

.dropdown-toggle[aria-expanded="true"]::after { transform: rotate(180deg); }

/* ---- Category tile ----------------------------------------------------------
   Denser than a product card: square art, name, count. Used where the grid runs
   6-across, so it carries no actions and no footer.
   -------------------------------------------------------------------------- */

.fb-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  height: 100%;
  padding: 1.1rem 0.85rem;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  text-align: center;
  text-decoration: none;
  color: inherit;
}

.fb-tile:hover { border-color: var(--border-accent); color: inherit; }

.fb-tile__img {
  width: 100%;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.fb-tile__img img { width: 100%; height: 100%; object-fit: contain; }

.fb-tile__title { font-size: 0.88rem; font-weight: 600; line-height: 1.3; }
.fb-tile:hover .fb-tile__title { color: var(--accent); }
.fb-tile__meta { font-size: 0.75rem; color: var(--text-secondary); }

/* Copiado de theme/flytheme/assets/css/theme.css em 2026-08-25 — bloco ISOLADO, não
   sincronização do arquivo inteiro (ver AGENTS_LOG.md: sincronizar theme.css inteiro a
   partir do flytheme já quebrou o shell de autenticação). Fonte canônica continua sendo
   o flytheme; ver COMPONENTS.md § "Cartão de escolha". */
/* ---- Pick card (grade selecionável) --------------------------------------
   Igual .fb-tile na forma (arte quadrada + título), mas de ESCOLHA: um
   <input type="radio"> real vive dentro do próprio <label> (nunca precisa de
   for/id pareado — clicar em qualquer parte do cartão já marca, comportamento
   nativo do HTML). Escondido de forma acessível (não display:none — continua
   focável/anunciado por leitor de tela), com foco de teclado visível via
   :has(:focus-visible). Usado no wizard de "Incluir Equipamento" (marca/tipo/
   modelo) — mesmo padrão de "form-check estilizado como cartão inteiro" já
   usado no wizard de conexão (MSP/MSC/Pasta, ver hangar/ssr/inc/dashboard/
   default.php), só que com imagem em vez de texto como conteúdo principal.
   Estado selecionado precisa ser óbvio à distância (pedido do usuário): borda
   grossa do accent + fundo tingido + selo de check — as 3 pistas reforçam a
   mesma informação, não dependem só da cor (funciona pra quem tem baixa
   percepção de cor também). */
/* Coluna de largura FIXA (não "minmax(…, 1fr)"): o cartão/imagem precisa ficar do mesmo
   tamanho em toda tela do wizard, não esticar conforme o número de itens de cada etapa
   (marca/tipo/modelo nunca têm a mesma quantidade). 1fr esticaria e criaria tamanhos
   diferentes entre etapas. */
.fb-pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, 8.5rem);
  gap: 0.85rem;
}

.fb-pick-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  padding: 1rem 0.85rem;
  background: var(--bg-card);
  border: 1px solid var(--card-border-color);
  border-radius: var(--radius-md);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.fb-pick-card:hover { border-color: var(--border-accent); }

.fb-pick-card__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

.fb-pick-card:has(.fb-pick-card__input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.fb-pick-card__img {
  width: 100%;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* cover, não contain: imagem retangular precisa virar quadrada CORTANDO o excesso da
   dimensão maior (nunca sobrar borda vazia dentro do quadrado, nunca distorcer). Mesma
   decisão já tomada pra .fb-gallery na página de produto (ver AGENTS_LOG.md). */
.fb-pick-card__img img { width: 100%; height: 100%; object-fit: cover; }

.fb-pick-card__title { font-size: 0.88rem; font-weight: 600; line-height: 1.3; }
.fb-pick-card__meta { font-size: 0.75rem; color: var(--text-secondary); }

/* 2px de borda em vez de 1px "pula" o cartão 1px pro lado de fora ao marcar —
   compensado subtraindo 1px do padding nos dois eixos, resultado fica parado. */
.fb-pick-card:has(.fb-pick-card__input:checked) {
  padding: calc(1rem - 1px) calc(0.85rem - 1px);
  border-width: 2px;
  border-color: var(--accent);
  background: rgba(var(--accent-rgb), 0.08);
}

.fb-pick-card__check {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 1.35rem;
  height: 1.35rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 0.8rem;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.15s ease, transform 0.15s ease;
}

.fb-pick-card:has(.fb-pick-card__input:checked) .fb-pick-card__check {
  opacity: 1;
  transform: scale(1);
}

@media (prefers-reduced-motion: reduce) {
  .fb-pick-card,
  .fb-pick-card__check { transition: none; }
}

/* ---- Avatar ---------------------------------------------------------------- */

.fb-avatar {
  width: 2.4rem;
  height: 2.4rem;
  border-radius: var(--radius-sm);
  object-fit: cover;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.fb-avatar--initials {
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* ---- Centred section heading ------------------------------------------------ */

.fb-section-head { text-align: center; margin-bottom: 2.25rem; }

.fb-section-head__eyebrow {
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  margin: 0 0 0.4rem;
}

.fb-section-head h2,
.fb-section-head h3 { font-size: 1.5rem; font-weight: 600; margin: 0 0 0.6rem; }

.fb-section-head p {
  color: var(--text-secondary);
  font-size: 0.95rem;
  max-width: 58ch;
  margin: 0 auto;
}

/* ---- Responsive tweaks ------------------------------------------------------------ */

/* Between full desktop and the 768px stack below, there's no longer room for
   brand + tabs + search + actions in one row, but it isn't narrow enough to
   earn the mobile treatment either. Without an explicit wrap here, .fb-tabs
   just gets squeezed against the search bar (min-width holds the tab size,
   so the OTHER flex children lose the fight instead — the search bar and
   even the tabs' own position get pushed around/overlapped). 1400px reuses
   the same threshold .fb-body already collapses the aside rail at, so the
   two adjustments read as one responsive step, not two uncoordinated ones.
   Tabs get their own centered row at full (unshrunk) size — no min-width
   fight left to lose, since nothing else shares the row anymore. */
@media (max-width: 1400px) {
  .fb-navbar { flex-wrap: wrap; row-gap: 0.75rem; }
  .fb-tabs { order: 4; flex-basis: 100%; justify-content: center; }
}

@media (max-width: 992px) {
  .fb-tabs { --tab-slant: 15.4px; --tab-h: 37.8px; --tab-min: 105px; --tab-r: 4.9px; --tab-lift: 3.5px; }
  .fb-tab { font-size: 0.6125rem; padding: 0 0.63rem; }
  .fb-tab i { width: 1.155rem; height: 1.155rem; font-size: 0.7rem; }
  .fb-tab.is-active .fb-tab-label::after { bottom: -8.4px; }
}

@media (max-width: 768px) {
  .fb-navbar { flex-wrap: wrap; gap: 0.75rem; padding: 0.75rem 1rem 0; }
  .fb-navbar-search { order: 3; max-width: 100%; }
  .fb-tabs { order: 4; align-self: stretch; }
  .fb-tab { flex: 1; min-width: 0; }
  /* moving the token keeps the header rule and the frame edges in step */
  :root { --frame-inset: 0.75rem; }
  .fb-body { padding: 1rem; }
}

/* ---- Page header + breadcrumb ----------------------------------------------- */

.fb-page-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

.fb-page-head h1 { font-size: 1.35rem; font-weight: 600; margin: 0; }

.breadcrumb {
  --bs-breadcrumb-font-size: 0.8125rem;
  --bs-breadcrumb-divider-color: var(--text-muted);
  --bs-breadcrumb-item-padding-x: 0.4rem;
  margin: 0;
}

.breadcrumb-item a { color: var(--text-secondary); text-decoration: none; }
.breadcrumb-item a:hover { color: var(--accent); }
.breadcrumb-item.active { color: var(--text-primary); }

/* ---- Toolbar ----------------------------------------------------------------
   Barra de ações acima de uma listagem.
   -------------------------------------------------------------------------- */

.fb-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  margin-bottom: 1rem;
}

.fb-toolbar__count { color: var(--text-secondary); font-size: 0.8125rem; }

/* ---- Filter group + checkbox ------------------------------------------------
   O checkbox é o do Bootstrap retematizado — o markup .form-check continua
   valendo, o que mantém o contrato com o JS de filtro intacto.
   -------------------------------------------------------------------------- */

.fb-filter-group { padding: 1rem; border-bottom: 1px solid var(--border-subtle); }
.fb-filter-group:last-child { border-bottom: none; }

.fb-filter-group__title {
  color: var(--text-muted);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  margin: 0 0 0.75rem;
}

.fb-filter-group .form-check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: auto;
  margin-bottom: 0.4rem;
  padding-left: 0;
}

.fb-filter-group .form-check-input {
  --bs-form-check-bg: transparent;
  float: none;
  margin: 0;
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
}

.fb-filter-group .form-check-input:checked { background-color: var(--accent); border-color: var(--accent); }
.fb-filter-group .form-check-input:focus { border-color: var(--accent); box-shadow: 0 0 0 0.2rem rgba(var(--accent-rgb), 0.25); }

.fb-filter-group .form-check-label {
  font-size: 0.8125rem;
  color: var(--text-secondary);
  cursor: pointer;
  flex: 1;
  min-width: 0;
}

.fb-filter-group .form-check:hover .form-check-label { color: var(--text-primary); }

.fb-badge-count {
  flex-shrink: 0;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--text-muted);
  background: rgba(255, 255, 255, 0.06);
  padding: 0.1rem 0.4rem;
  border-radius: var(--radius-sm);
}

.fb-more-link {
  display: inline-block;
  margin-top: 0.35rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.fb-more-link:hover { color: var(--accent); text-decoration: underline; }

/* ---- noUiSlider (filtro de faixa) ------------------------------------------- */

.noUi-target { background: rgba(255, 255, 255, 0.08); border: none; box-shadow: none; height: 4px; }
.noUi-connect { background: var(--accent); }

.noUi-handle {
  width: 14px !important;
  height: 14px !important;
  right: -7px !important;
  top: -6px !important;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.25);
  cursor: pointer;
}

.noUi-handle::before, .noUi-handle::after { display: none; }

/* ---- Content tabs -----------------------------------------------------------
   Abas DENTRO de uma página. Nada a ver com .fb-tabs, que é a troca entre os
   dois sites: estas são planas, sublinhadas e vivem dentro de um card.
   -------------------------------------------------------------------------- */

.fb-content-tabs {
  --bs-nav-tabs-border-width: 0;
  --bs-nav-link-padding-y: 0.7rem;
  --bs-nav-link-padding-x: 1rem;
  --bs-nav-link-font-size: 0.875rem;
  --bs-nav-link-color: var(--text-secondary);
  --bs-nav-link-hover-color: var(--text-primary);

  gap: 0.25rem;
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: 1.25rem;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}

.fb-content-tabs::-webkit-scrollbar { display: none; }

.fb-content-tabs .nav-link {
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  font-weight: 600;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-bottom: -1px;
}

.fb-content-tabs .nav-link.active { background: none; color: var(--accent); border-bottom-color: var(--accent); }

/* ---- Accordion --------------------------------------------------------------- */

.accordion {
  --bs-accordion-bg: transparent;
  --bs-accordion-border-color: var(--border-subtle);
  --bs-accordion-border-radius: var(--radius-sm);
  --bs-accordion-btn-color: var(--text-primary);
  --bs-accordion-btn-bg: var(--bg-card-2);
  --bs-accordion-active-color: var(--accent);
  --bs-accordion-active-bg: rgba(var(--accent-rgb), 0.08);
  --bs-accordion-btn-focus-box-shadow: 0 0 0 0.2rem rgba(var(--accent-rgb), 0.2);
  --bs-accordion-btn-padding-y: 0.75rem;
  --bs-accordion-btn-padding-x: 1rem;
  --bs-accordion-body-padding-y: 1rem;
  --bs-accordion-body-padding-x: 1rem;
  --bs-accordion-color: var(--text-secondary);
}

.accordion-button { font-size: 0.875rem; font-weight: 600; }
.accordion-button::after { filter: invert(1) opacity(0.6); }
.accordion-item { margin-bottom: 0.5rem; }

/* ---- Spec table -------------------------------------------------------------
   Ficha de dados: pares rótulo/valor. Mais densa que a .fb-table, que é uma
   tabela de registros.
   -------------------------------------------------------------------------- */

.fb-spec-table { width: 100%; font-size: 0.85rem; }

.fb-spec-table th,
.fb-spec-table td { padding: 0.6rem 0.75rem; border-bottom: 1px solid var(--border-subtle); vertical-align: top; }

.fb-spec-table tr:last-child th,
.fb-spec-table tr:last-child td { border-bottom: none; }

.fb-spec-table th {
  width: 38%;
  text-align: left;
  font-weight: 600;
  color: var(--text-secondary);
  background: rgba(255, 255, 255, 0.02);
}

/* ---- Inline metric ----------------------------------------------------------- */

.fb-metric { display: inline-flex; align-items: center; gap: 0.6rem; }

.fb-metric__icon {
  width: 2.4rem;
  height: 2.4rem;
  border-radius: var(--radius-sm);
  background: var(--bg-card-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  color: var(--text-secondary);
  flex-shrink: 0;
  text-decoration: none;
}

.fb-metric__icon:hover { color: var(--accent); }
.fb-metric b { display: block; font-size: 1.05rem; font-weight: 700; line-height: 1.2; }
.fb-metric span { font-size: 0.7rem; color: var(--text-muted); }

/* estado ligado de um ícone-ação (favoritado, seguindo…) */
.fb-is-on { color: var(--danger); }

/* ---- Tag --------------------------------------------------------------------- */

.fb-tag {
  display: inline-block;
  padding: 0.2rem 0.6rem;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-secondary);
  background: rgba(255, 255, 255, 0.06);
  border-radius: var(--radius-sm);
}


.fb-product-detail__name { font-size: 1.5rem; font-weight: 600; color: var(--accent); margin: 0; }

.fb-product-detail__desc {
  color: var(--text-secondary);
  font-size: 0.875rem;
  line-height: 1.7;
  max-height: 200px;
  overflow-y: auto;
  padding-right: 0.5rem;
}

/* ---- Product gallery (swiper) ------------------------------------------------ */

.fb-gallery { position: relative; }
.fb-gallery .swiper-preview-details { aspect-ratio: 1; border: 1px solid var(--border-subtle); }
/* cover, not contain: the box is already forced to 1:1 above, but a wide/tall
   source photo under contain just letterboxes inside that square — the box
   reads as square, the visible photo still reads as a rectangle floating in
   it. cover crops instead, so the photo itself fills the square. */
.fb-gallery .swiper-slide img { width: 100%; height: 100%; object-fit: cover; }
.fb-gallery .swiper-view-details { margin-top: 0.5rem; }

.fb-gallery .swiper-view-details .swiper-slide {
  aspect-ratio: 1;
  border: 1px solid var(--border-subtle);
  cursor: pointer;
  opacity: 0.5;
}

.fb-gallery .swiper-view-details .swiper-slide-thumb-active { opacity: 1; border-color: var(--accent); }

.fb-gallery .swiper-button-next,
.fb-gallery .swiper-button-prev {
  color: var(--text-primary);
  background: rgba(0, 0, 0, 0.5);
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
}

.fb-gallery .swiper-button-next::after,
.fb-gallery .swiper-button-prev::after { font-size: 0.85rem; font-weight: 700; }

/* ---- Bootstrap components used raw ------------------------------------------
   Sem estas regras eles renderizam no tema CLARO padrão — fundo branco no meio
   da página escura.
   -------------------------------------------------------------------------- */

.list-group {
  --bs-list-group-bg: transparent;
  --bs-list-group-color: var(--text-primary);
  --bs-list-group-border-color: var(--border-subtle);
  --bs-list-group-item-padding-y: 0.6rem;
  --bs-list-group-item-padding-x: 0;
  --bs-list-group-action-hover-color: var(--text-primary);
  --bs-list-group-action-hover-bg: rgba(255, 255, 255, 0.04);
}

.alert {
  --bs-alert-bg: rgba(var(--accent-rgb), 0.1);
  --bs-alert-border-color: rgba(var(--accent-rgb), 0.3);
  --bs-alert-color: var(--text-primary);
  --bs-alert-border-radius: var(--radius-sm);
  font-size: 0.85rem;
}

.alert-warning {
  --bs-alert-bg: rgba(245, 165, 36, 0.12);
  --bs-alert-border-color: rgba(245, 165, 36, 0.35);
  --bs-alert-color: var(--text-primary);
}

.btn-close { filter: invert(1) opacity(0.6); }

/* --bg-card (--surface) is a translucent glass tint — fine floating over the
   flat page body everywhere else, but a modal sits over real page content,
   not that flat body, so that same tint lets whatever's underneath (menu,
   cards, text) show through and fight with the modal's own content. Opaque
   --bg-panel instead — this is the modal's own backdrop already dimming the
   page, it doesn't need a second layer of transparency on top of that. */
.modal-content {
  --bs-modal-bg: var(--bg-panel);
  --bs-modal-color: var(--text-primary);
  --bs-modal-border-color: var(--border-subtle);
  --bs-modal-header-border-color: var(--border-subtle);
  --bs-modal-footer-border-color: var(--border-subtle);
}

.text-muted { color: var(--text-muted) !important; }
.text-primary { color: var(--accent) !important; }
.text-secondary { color: var(--text-secondary) !important; }
.bg-light { background-color: var(--bg-card-2) !important; }
.text-success { color: var(--success) !important; }
.text-danger { color: #fca5a5 !important; } /* not var(--danger) like its siblings above: this used to be defined twice (here AND grouped with .invalid-feedback further down), with the later declaration silently winning — consolidated into this one, keeping the value that was actually rendering, so nothing on either live site changes; whether .text-danger SHOULD instead be var(--danger) is a separate, deliberate call for later. */
.text-warning { color: var(--warning) !important; }
.text-info { color: var(--accent) !important; }

/* ---- Font-size utilities ------------------------------------------------------
   Referenced throughout already-shipped code (dashboardSideKpiCard, INSIGHT_SEVERITY_META
   cards, the connection wizard) as .fs-11/.fs-12/.fs-13/.fs-15 etc., but never actually
   defined here — text using them silently fell back to the browser default (16px) instead
   of the intended dense/compact sizing. Values follow the reference template's own scale
   (Npx at the default 16px root), found via the .fs-13 = 0.8125rem note already in the
   .fb-list comment above. Discovered while porting the AI Dashboard page to flytheme (see
   AGENTS_LOG.md) — fixing here benefits every screen already using these classes, not just
   that one page. */
.fs-11 { font-size: 0.6875rem; }
.fs-12 { font-size: 0.75rem; }
.fs-13 { font-size: 0.8125rem; }
.fs-14 { font-size: 0.875rem; }
.fs-15 { font-size: 0.9375rem; }
.fs-16 { font-size: 1rem; }
.fs-18 { font-size: 1.125rem; }

/* ---- Severity pill (.fb-severity-pill) -----------------------------------------
   Small status pill for an arbitrary severity value (success/warning/danger/info/
   neutral) — same visual recipe as .fb-drone-status (tinted pill, bold, tight tracking)
   but not tied to the active/inactive equipment meaning. Modifier names match the
   Bootstrap colour names the backend already sends (ex.: soa/controllers/ai-dashboard-tools.php
   POC_KPI_SEVERITY_META['cls']), so callers don't need a remap step. */
.fb-severity-pill {
  display: inline-block;
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  padding: 0.22rem 0.5rem;
  border-radius: var(--radius-sm);
}
.fb-severity-pill.cls-success { background: rgba(34, 197, 94, 0.16); color: var(--success); }
.fb-severity-pill.cls-warning { background: rgba(245, 165, 36, 0.16); color: var(--warning); }
.fb-severity-pill.cls-danger { background: rgba(239, 68, 68, 0.16); color: var(--danger); }
.fb-severity-pill.cls-info { background: rgba(var(--accent-rgb), 0.18); color: var(--accent); }
.fb-severity-pill.cls-secondary { background: rgba(255, 255, 255, 0.08); color: var(--text-muted); }

/* ---- Brand header + kind card + social link ---------------------------------- */

.fb-brand-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--border-subtle);
}

.fb-brand-head__logo {
  width: 5rem;
  height: 5rem;
  flex-shrink: 0;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--bg-card-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.fb-brand-head__logo img { width: 100%; height: 100%; object-fit: contain; padding: 0.35rem; }
.fb-brand-head__id { flex: 1; min-width: 12rem; }
.fb-brand-head__id h1 { font-size: 1.35rem; font-weight: 600; margin: 0 0 0.2rem; }
.fb-brand-head__metrics { display: flex; gap: 1.5rem; flex-wrap: wrap; }

.fb-kind-card {
  height: 100%;
  padding: 1.1rem 0.85rem;
  text-align: center;
  background: var(--bg-card-2);
  border: var(--card-border-width) solid var(--card-border-color);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
}

.fb-kind-card__img { width: 3.5rem; height: 3.5rem; display: inline-flex; align-items: center; justify-content: center; }
.fb-kind-card__img img { width: 100%; height: 100%; object-fit: contain; }
.fb-kind-card__title { font-size: 0.9rem; font-weight: 600; margin: 0; }
.fb-kind-card__actions { display: flex; gap: 0.4rem; flex-wrap: wrap; justify-content: center; margin-top: auto; }

.fb-social {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0;
  color: var(--text-secondary);
  font-size: 0.85rem;
  text-decoration: none;
}

.fb-social:hover { color: var(--accent); }

.fb-social__icon {
  width: 2rem;
  height: 2rem;
  flex-shrink: 0;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
}

/* ---- Timeline ---------------------------------------------------------------
   A linha corre no ::after de cada <li>, não num container — assim ela pára
   sozinha no último item, sem regra extra.
   -------------------------------------------------------------------------- */

.fb-timeline {
  --dot: 11px;
  --gutter: 8.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
}

.fb-timeline__item { position: relative; padding-left: calc(var(--gutter) + 2rem); padding-bottom: 1.5rem; }

.fb-timeline__item::after {
  content: "";
  position: absolute;
  left: calc(var(--gutter) + 0.5rem);
  top: var(--dot);
  bottom: 0;
  width: 1px;
  background: var(--border-subtle);
}

.fb-timeline__item:last-child { padding-bottom: 0; }
.fb-timeline__item:last-child::after { display: none; }

.fb-timeline__item::before {
  content: "";
  position: absolute;
  left: calc(var(--gutter) + 0.5rem - var(--dot) / 2 + 0.5px);
  top: calc(var(--dot) / 2);
  width: var(--dot);
  height: var(--dot);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.2);
}

.fb-timeline__date {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--gutter);
  text-align: right;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
}

.fb-timeline__card {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 1rem;
  background: var(--bg-card-2);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  text-decoration: none;
}

.fb-timeline__card:hover { border-color: var(--border-accent); }

.fb-timeline__thumb {
  width: 4rem;
  height: 4rem;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.fb-timeline__thumb img { width: 100%; height: 100%; object-fit: contain; padding: 0.25rem; }
.fb-timeline__name { font-size: 0.95rem; font-weight: 600; margin: 0 0 0.25rem; }
.fb-timeline__card:hover .fb-timeline__name { color: var(--accent); }
.fb-timeline__excerpt { font-size: 0.8125rem; color: var(--text-secondary); margin: 0; }

@media (max-width: 576px) {
  .fb-timeline { --gutter: 0px; }
  .fb-timeline__item { padding-left: 1.75rem; padding-top: 1.5rem; }
  .fb-timeline__date { position: static; width: auto; text-align: left; display: block; margin-bottom: 0.4rem; }
  .fb-timeline__item::before { top: 1.6rem; left: 0; }
  .fb-timeline__item::after { left: calc(var(--dot) / 2); top: calc(1.6rem + var(--dot)); }
}

/* ---- Recent-activity list (`.fb-activity-list`) ------------------------------
   Compact vertical feed for a narrow sidebar card — ported from the reference
   template's dashboard widget (index-1.html, `.recent-activity-list`). NOT the
   same job as .fb-timeline above: that one is the wide, full-page timeline
   from timeline.html, built around a dedicated date column that only works
   with real width to spend. Squeezed into a one-third-width card (ex.: Hangar
   dashboard's "Atividade Recente") its --gutter has nowhere to go and the
   whole thing collapses — the reference solves narrow-card activity feeds
   with this simpler, dot-only list instead, so that's the one to reach for
   there, not a squeezed .fb-timeline. */
.fb-activity-list {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
}

.fb-activity-list::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 0.3rem;
  bottom: 0.3rem;
  width: 1px;
  background: var(--border-subtle);
}

.fb-activity-list li { position: relative; padding-left: 1.75rem; margin-bottom: 1.25rem; }
.fb-activity-list li:last-child { margin-bottom: 0; }

.fb-activity-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.3rem;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--bg-card);
  border: 2px solid var(--accent);
}

.fb-activity-list__title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  font-size: 0.85rem;
  font-weight: 600;
  margin: 0 0 0.2rem;
}

.fb-activity-list__time { font-size: 0.7rem; font-weight: 500; color: var(--text-muted); flex-shrink: 0; white-space: nowrap; }
.fb-activity-list__excerpt { font-size: 0.8125rem; color: var(--text-secondary); margin: 0; }

/* ---- Compare table ----------------------------------------------------------
   O cabeçalho gruda ao rolar, senão a coluna perde a identidade numa tabela
   longa de especificações.
   -------------------------------------------------------------------------- */

.fb-compare { width: 100%; font-size: 0.8125rem; table-layout: fixed; }

.fb-compare th,
.fb-compare td { padding: 0.6rem 0.75rem; border: 1px solid var(--border-subtle); vertical-align: middle; }

.fb-compare thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg-panel);
  vertical-align: top;
  text-align: center;
}

.fb-compare tbody td { text-align: center; }

.fb-compare tbody td:first-child {
  font-weight: 600;
  color: var(--text-secondary);
  background: rgba(255, 255, 255, 0.02);
  text-align: left;
}

.fb-compare__group td {
  background: rgba(var(--accent-rgb), 0.07);
  color: var(--text-primary);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: left;
}

.fb-compare__product-img {
  width: 100%;
  max-width: 9rem;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  margin: 0 auto 0.5rem;
  display: block;
}

.fb-compare__product-name { font-size: 0.85rem; font-weight: 600; color: var(--text-primary); }
.fb-compare__product-meta { font-size: 0.72rem; color: var(--text-muted); }
.fb-compare__actions { display: flex; gap: 0.3rem; justify-content: center; flex-wrap: wrap; margin-top: 0.5rem; }

.btn-fb-danger {
  background: rgba(239, 68, 68, 0.14);
  border: 1px solid rgba(239, 68, 68, 0.3);
  color: #fca5a5;
  border-radius: var(--radius-sm);
  padding: 0.42rem 0.75rem;
  font-size: 0.78rem;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  text-decoration: none;
}

.btn-fb-danger:hover { color: #fff; background: var(--danger); }

/* ---- Text input --------------------------------------------------------------- */

.form-control {
  --bs-body-bg: transparent;
  background: rgba(255, 255, 255, 0.06);
  /* transparent, not omitted: keeps the box the same size when :focus paints it */
  border: 1px solid transparent;
  border-radius: var(--form-radius);
  color: var(--text-primary);
  font-size: 0.875rem;
  padding: 0.55rem 0.85rem;
}

.form-control::placeholder { color: var(--text-muted); }

.form-control:focus {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--accent);
  box-shadow: 0 0 0 0.2rem rgba(var(--accent-rgb), 0.2);
  color: var(--text-primary);
}

/* Bootstrap's own :disabled/[readonly] paints a light grey box regardless of
   theme — every field the PID compare form locks (readonly) or a wizard
   disables until connected inherits that unless overridden here too. */
.form-control:disabled,
.form-control[readonly] {
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-muted);
}

.tagify {
  --tag-bg: rgba(var(--accent-rgb), 0.16);
  --tag-text-color: var(--text-primary);
  --tags-border-color: var(--border-subtle);
  --tags-focus-border-color: var(--accent);
  --input-color: var(--text-primary);
  background: rgba(255, 255, 255, 0.04);
  border-radius: var(--radius-sm);
}

/* ---- Tagify dropdown (product-compare search, etc.) --------------------------
   Vendor ships this unthemed (white panel, blue accent) — same recipe as
   .dropdown-menu above: opaque --bg-panel (it floats over content; a glass
   surface would let text behind it show through), active item told apart only
   by a tinted background, never by colouring its text.
   Tagify APPENDS its dropdown to <body>, not inside .tagify, so the two colour
   custom properties the vendor CSS reads are overridden on :root — the only
   scope both .tagify and .tagify__dropdown share. Every override below needs
   !important: tagify.css loads AFTER theme.css on every page that uses it
   (see {{javascript}}, always after the theme.css <link>), and the vendor
   sheet redeclares these same two custom properties on :root too — same
   selector, same specificity, so without !important its later declaration
   would win and silently undo this whole block. */

:root {
  --tagify-dd-color-primary: var(--accent) !important;
  --tagify-dd-bg-color: var(--bg-panel) !important;
}

.tagify__dropdown__wrapper {
  border-color: var(--border-subtle);
  border-radius: var(--radius-sm);
  box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.4);
}

.tagify__dropdown__item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--text-primary);
  font-size: 0.875rem;
}

.tagify__dropdown__item:hover { background: rgba(255, 255, 255, 0.07); }

/* !important: same specificity as the vendor's own .tagify__dropdown__item--active
   rule, which loads after theme.css — see note above. */
.tagify__dropdown__item--active {
  background: rgba(var(--accent-rgb), 0.08) !important;
  color: var(--text-primary) !important;
}

.tagify__dropdown__item__avatar-wrap {
  width: 1.75rem;
  height: 1.75rem;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-card-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.tagify__dropdown__item__avatar-wrap img { width: 100%; height: 100%; object-fit: contain; }

.tagify__dropdown__item span { margin-left: auto; color: var(--text-secondary); font-size: 0.78rem; }

.tagify__dropdown__footer { color: var(--text-muted); border-top: 1px solid var(--border-subtle); }

/* ---- Search result -----------------------------------------------------------
   Conteúdo principal de uma página de resultados — maior que .fb-list-item.
   -------------------------------------------------------------------------- */

.fb-result { display: flex; gap: 1.25rem; padding: 1.25rem; border-bottom: 1px solid var(--border-subtle); }
.fb-result:last-child { border-bottom: none; }
.fb-result:hover { background: rgba(255, 255, 255, 0.02); }

.fb-result__thumb {
  width: 7rem;
  flex-shrink: 0;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--bg-card-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.fb-result__thumb img { width: 100%; height: 100%; object-fit: contain; padding: 0.25rem; }
.fb-result__body { min-width: 0; flex: 1; }

.fb-result__title {
  display: inline-block;
  font-size: 1rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  margin-bottom: 0.4rem;
}

.fb-result__title:hover { color: var(--accent); text-decoration: underline; }

.fb-result__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 1rem;
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-bottom: 0.5rem;
}

.fb-result__meta > span { display: inline-flex; align-items: center; gap: 0.3rem; }
.fb-result__meta a { color: var(--text-secondary); text-decoration: none; }
.fb-result__meta a:hover { color: var(--accent); }
.fb-result__excerpt { font-size: 0.85rem; color: var(--text-secondary); line-height: 1.6; margin: 0; }

@media (max-width: 576px) {
  .fb-result { gap: 0.85rem; padding: 1rem; }
  .fb-result__thumb { width: 4.5rem; }
}

/* ---- Menu toggle (botão hambúrguer) -----------------------------------------
   UMA classe, efeito diferente por breakpoint — que é o comportamento esperado
   de um único botão:
     desktop  → menu encolhe para só ícones (os rótulos somem, a coluna estreita)
     mobile   → menu abre como painel sobreposto (ele já nasce oculto)
   Antes a classe só tinha regra abaixo de 992px, então no desktop o botão não
   fazia absolutamente nada.
   -------------------------------------------------------------------------- */

@media (min-width: 993px) {
  body.fb-nav-toggled .fb-body { grid-template-columns: var(--col-menu-mini) minmax(0, 1fr) var(--col-rail); }
  body.fb-nav-toggled .fb-body--no-rail { grid-template-columns: var(--col-menu-mini) minmax(0, 1fr); }

  /* tudo que é texto sai; sobra o ícone centralizado */
  body.fb-nav-toggled .fb-menu__label,
  body.fb-nav-toggled .fb-menu__angle,
  body.fb-nav-toggled .fb-menu__category,
  body.fb-nav-toggled .fb-badge-new,
  body.fb-nav-toggled .fb-cta { display: none; }

  body.fb-nav-toggled .fb-menu__item {
    justify-content: center;
    padding-inline: 0;
    gap: 0;
  }

  /* Ramo com filhos: sem rótulo nem seta não há acordeão pra expandir em
     linha — em vez disso, o painel inteiro (.fb-menu__panel: a --primary
     sempre aberta + o grupo "ver todos") aparece como flyout ancorado ao
     ícone, só enquanto o ponteiro — ou o foco, pra quem navega por teclado,
     daí o :focus-within — estiver em cima do ramo. Some da tela no instante
     em que sai, sem clique, sem JS.
     .fb-menu__panel NÃO entra na lista de display:none acima: escondê-lo por
     padrão é papel só desta regra aqui, senão não haveria como reabri-lo no
     hover. O conteúdo de dentro (.fb-submenu--primary) já é "sempre aberto"
     pela regra lá em cima (.has-sub.open .fb-submenu) — ela não sabe nem
     precisa saber que está dentro de um flyout; some junto com o pai. */
  body.fb-nav-toggled .has-sub { position: relative; }

  body.fb-nav-toggled .has-sub .fb-menu__panel {
    display: none;
    position: absolute;
    left: 100%;
    top: 0;
    margin-left: 0.5rem;
    min-width: 14rem;
    padding: 0.5rem;
    background: var(--bg-panel);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.4);
    z-index: 60;
  }

  body.fb-nav-toggled .has-sub:hover .fb-menu__panel,
  body.fb-nav-toggled .has-sub:focus-within .fb-menu__panel {
    display: block;
  }

  /* dentro do flyout sobra espaço — desfaz a compressão ícone-only que a
     regra .fb-menu__item acima aplica a QUALQUER item, inclusive estes */
  body.fb-nav-toggled .has-sub .fb-menu__panel .fb-menu__item {
    justify-content: flex-start;
    padding: 0.4rem 0.75rem;
  }
}

@media (max-width: 992px) {
  /* Em tela pequena o menu não ocupa coluna: só existe quando o hambúrguer o
     abre, sobreposto ao conteúdo. */
  .fb-sidebar { display: none; }

  body.fb-nav-toggled::after {
    content: "";
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    z-index: 1040;
  }

  body.fb-nav-toggled .fb-sidebar {
    display: flex;
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 280px;
    z-index: 1050;
    background: var(--bg-body);
    padding: 1.25rem;
    overflow-y: auto;
    box-shadow: 20px 0 40px rgba(0, 0, 0, 0.5);
  }
}

/* ---- Single-column shell ----------------------------------------------------
   Alternativa ao .fb-body de 3 colunas, para telas anteriores ao login (home,
   entrada, cadastro, recuperação de senha): sem menu e sem trilho, o conteúdo
   ocupa a largura toda dentro da moldura.
   -------------------------------------------------------------------------- */

.fb-single { display: block; padding: 0; }

/* ---- Landing ----------------------------------------------------------------
   Blocos usados só na home pública de um site. Largura contida por dentro, para
   o texto não esticar até a borda da moldura numa tela larga.
   -------------------------------------------------------------------------- */

.fb-landing-hero {
  position: relative;
  overflow: hidden;
  padding: clamp(3rem, 8vw, 6rem) var(--frame-inset);
  text-align: center;
  border-bottom: 1px solid var(--border-subtle);
}

.fb-landing-hero__inner { max-width: 46rem; margin: 0 auto; position: relative; z-index: 1; }

.fb-landing-hero::before {
  content: "";
  position: absolute;
  top: -40%;
  left: 50%;
  width: min(60rem, 120%);
  aspect-ratio: 1;
  transform: translateX(-50%);
  background: radial-gradient(circle, rgba(var(--accent-rgb), 0.16), transparent 62%);
  pointer-events: none;
}

.fb-landing-hero h1 {
  font-size: clamp(2rem, 5vw, 3rem);
  font-weight: 600;
  line-height: 1.2;
  margin: 0 0 1rem;
}

.fb-landing-hero h1 em { color: var(--highlight); font-style: normal; }

.fb-landing-hero p {
  color: var(--text-secondary);
  font-size: 1.05rem;
  line-height: 1.7;
  margin: 0 auto 2rem;
  max-width: 38rem;
}

.fb-landing-hero__actions { display: flex; gap: 0.85rem; justify-content: center; flex-wrap: wrap; }

.fb-landing-section {
  padding: clamp(2.5rem, 6vw, 4.5rem) var(--frame-inset);
  border-bottom: 1px solid var(--border-subtle);
}

.fb-landing-section:last-child { border-bottom: none; }
.fb-landing-section__inner { max-width: 72rem; margin: 0 auto; }

.fb-feature {
  height: 100%;
  padding: 1.5rem;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}

.fb-feature:hover { border-color: var(--border-accent); }

.fb-feature__icon {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-sm);
  background: rgba(var(--accent-rgb), 0.14);
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.35rem;
  margin-bottom: 1rem;
}

.fb-feature h3 { font-size: 1rem; font-weight: 600; margin: 0 0 0.5rem; }
.fb-feature p { color: var(--text-secondary); font-size: 0.875rem; line-height: 1.65; margin: 0; }

.fb-step { display: flex; gap: 1rem; align-items: flex-start; }

.fb-step__n {
  width: 2.25rem;
  height: 2.25rem;
  flex-shrink: 0;
  border-radius: 50%;
  border: 1px solid var(--border-accent);
  color: var(--accent);
  font-weight: 700;
  font-size: 0.9rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.fb-step h3 { font-size: 0.95rem; font-weight: 600; margin: 0 0 0.3rem; }
.fb-step p { color: var(--text-secondary); font-size: 0.85rem; line-height: 1.6; margin: 0; }

.fb-cta-band {
  text-align: center;
  padding: clamp(2.5rem, 6vw, 4rem) var(--frame-inset);
  background:
    radial-gradient(circle at 50% 120%, rgba(var(--accent-rgb), 0.18), transparent 60%),
    var(--bg-card);
}

.fb-cta-band h2 { font-size: clamp(1.4rem, 3.5vw, 2rem); font-weight: 600; margin: 0 0 0.75rem; }
.fb-cta-band p { color: var(--text-secondary); margin: 0 auto 1.75rem; max-width: 34rem; }

/* ---- Auth shell -------------------------------------------------------------
   Entrar, cadastrar e recuperar senha rodam num shell próprio, SEM header: são
   telas de foco único.

   REGRA CENTRAL: o ritmo vertical desta tela NÃO é definido aqui. Ele vem dos
   utilitários do Bootstrap no markup herdado — .card-body.p-md-5 (3rem), .mb-4
   entre logo/subtítulo/blocos, .mb-1 no título, .mb-3 no divisor, a margem
   padrão do <hr> — mais as regras .wizard-* logo abaixo. Sobrepor qualquer um
   desses (zerar o padding do .card-body, dar margem própria ao logo, trocar o
   padding do .wizard-content) desmonta a diagramação inteira. Já aconteceu.

   Aqui só entra: o fundo, o centramento e a largura.
   -------------------------------------------------------------------------- */

.fb-auth-page {
  min-height: 100vh;
  display: flex;
  background:
    radial-gradient(circle at 50% 40%,
      rgba(var(--accent-rgb), 0.18) 0%,
      rgba(var(--accent-rgb), 0.10) 40%,
      rgba(0, 0, 0, 0.35) 100%);
}

.fb-auth-shell {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.25rem;
}

.fb-auth-card {
  width: 100%;
  max-width: 34rem;
  background: var(--bg-panel);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: 0 1.5rem 3rem -1rem rgba(0, 0, 0, 0.55);
}

/* o cadastro é um wizard com campos lado a lado — precisa de mais largura */
.fb-auth-card--wide { max-width: 46rem; }

/* o wordmark ocupa o lugar dos <img> do logo, dentro do .mb-4 do original */
.fb-wordmark { font-size: 1.35rem; font-weight: 800; letter-spacing: 0.01em; }
.fb-wordmark span { color: var(--accent); }

.fb-auth-card .h5 { font-size: 1.25rem; font-weight: 600; color: var(--text-primary); }

/* O markup herdado traz container/row/col por dentro. Só a coluna EXTERNA é
   neutralizada: o encadeamento com ">" preserva a grade de 2 colunas dos
   campos, que é uma .row mais funda.
   (py-5 e my-4 foram removidos do markup na conversão — eram artefatos da casca
   antiga, de página inteira, e sendo utilitários !important não havia como
   vencê-los por especificidade.) */
.fb-auth-card .container,
.fb-auth-card .container-lg,
.fb-auth-card .container-fluid { max-width: none; padding-inline: 0; }

.fb-auth-card > .container > .row > [class*="col-"],
.fb-auth-card > .container-lg > .row > [class*="col-"],
.fb-auth-card > .container-fluid > .row > [class*="col-"] {
  flex: 0 0 100%;
  max-width: 100%;
  width: 100%;
}

.fb-auth-card .card { background: none; border: 0; }

.fb-auth-card .top-left,
.fb-auth-card .top-right,
.fb-auth-card .bottom-left,
.fb-auth-card .bottom-right { display: none; }

/* DESVIO DELIBERADO do ritmo herdado, em dois pontos só — pedido para a tela
   caber sem rolagem. O original usa 3rem em cima e embaixo (p-md-5) mais 3rem
   no rodapé do .wizard-content, somando ~96px de folga vertical que não carrega
   informação. O miolo (mb-4 / mb-1 / mb-3 entre os blocos) continua intocado.
   Só a lateral segue vindo do p-md-5. */
/* !important é obrigatório aqui: TODOS os utilitários de espaçamento do
   Bootstrap (.p-4, .p-md-5, .py-5, .my-4) são declarados com !important, então
   nenhuma especificidade os vence. A lateral continua vindo do .p-md-5. */
.fb-auth-card .card-body {
  padding-top: 1.75rem !important;
  padding-bottom: 1.5rem !important;
}

.fb-auth-card .wizard-content { padding-bottom: 0; }

/* ---- Auth social login (`.fb-auth-social`) -----------------------------------
   Reference: cloud.boosteroid.com/auth/start. Google keeps its own brand mark
   on a white square (`--icon--white`); other providers get a plain icon well
   instead — that's what reads as "vazado" against the dark item background.
   -------------------------------------------------------------------------- */

.fb-auth-social__item {
  display: flex;
  align-items: center;
  gap: 1rem;
  height: 3.125rem; /* 50px */
  margin-bottom: 0.6rem;
  border-radius: var(--form-radius);
  background: rgba(255, 255, 255, 0.06);
  overflow: hidden;
  color: var(--text-primary);
  text-decoration: none;
}

.fb-auth-social__item:last-child { margin-bottom: 0; }
.fb-auth-social__item:hover { background: rgba(255, 255, 255, 0.1); color: var(--text-primary); }

.fb-auth-social__icon {
  flex-shrink: 0;
  width: 3.125rem;
  height: 3.125rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.15rem;
  background: rgba(255, 255, 255, 0.05);
}

/* Google's own mark needs its native colours on white — every other provider
   uses --icon (currentColor) so it inherits --text-primary for free. */
.fb-auth-social__icon--white { background: #fff; color: #1f1f1f; }

.fb-auth-social__label { padding-right: 1.1rem; font-size: 0.9rem; font-weight: 600; }

/* ---- Typewriter (`.fb-typewriter`) --------------------------------------------
   Reference: cloud.boosteroid.com/auth/start (built on TypeIt.js there; here it's
   ~40 lines of vanilla JS in theme.js, no dependency).

   The FIRST phrase is real text in the markup, typed server-side, not by JS — a
   no-JS visit, a screen reader or a crawler all get the headline immediately.
   theme.js only takes over the cycle afterwards (hold → erase → type next →
   repeat). Markup:

   <span class="fb-typewriter" data-fb-typewriter='["Phrase one","Phrase two"]'>
     <span class="fb-typewriter__text">Phrase one</span>
     <span class="fb-typewriter__cursor" aria-hidden="true"></span>
   </span>

   The JSON array's first entry must equal the pre-typed text exactly, or the
   loop's first erase pass will visibly jump. -------------------------------- */

.fb-typewriter__cursor {
  display: inline-block;
  width: 0;
  height: 1em;
  vertical-align: -0.15em;
  border-right: 2px solid currentColor;
  margin-left: 0.05em;
  animation: fb-caret-blink 1s step-end infinite;
}

@keyframes fb-caret-blink { 50% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .fb-typewriter__cursor { animation: none; }
}

/* ---- Form wizard ------------------------------------------------------------
   Wizard-JS (form-wizard.js). Dois pontos que quebram em silêncio:

   1. `.wizard-step` é usado para DUAS coisas, distinguidas pelo pai:
        .wizard-content .wizard-step  → o painel da etapa (os campos)
        .wizard-nav     .wizard-step  → o indicador, criado em runtime pelo JS
      Estilizar sem escopo pinta o formulário como se fosse indicador.

   2. O JS só ALTERNA a classe .active — quem esconde a etapa inativa é o CSS.
      Sem a regra de display abaixo, todas as etapas aparecem empilhadas.

   Espaçamentos copiados do tema de referência: 60px acima da navegação e 3rem
   de respiro no conteúdo, que é o que dá o ritmo vertical da tela.
   -------------------------------------------------------------------------- */

.wizard-nav {
  display: flex;
  flex-wrap: wrap;
  margin: 60px 0 0;
  padding: 0;
  list-style: none;
}

.wizard-nav .wizard-step {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  cursor: pointer;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-muted);
  text-align: center;
}

/* trilho ligando um indicador ao próximo */
.wizard-nav .wizard-step::before {
  content: "";
  position: absolute;
  top: 0.55rem;
  left: 50%;
  right: -50%;
  height: 2px;
  background: var(--border-subtle);
}

.wizard-nav .wizard-step:last-child::before { display: none; }

.wizard-nav .dot {
  position: relative;
  z-index: 1;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 50%;
  background: var(--bg-panel);
  border: 2px solid var(--border-strong);
  transition: all 0.2s ease-in;
}

.wizard-nav .wizard-step.active,
.wizard-nav .wizard-step.current { color: var(--text-primary); }

.wizard-nav .wizard-step.active .dot,
.wizard-nav .wizard-step.current .dot {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.2);
}

.wizard-content { padding: 3rem 0; }

/* o painel só aparece quando o JS o marca como ativo */
.wizard-content .wizard-step {
  display: none;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  cursor: auto;
}

.wizard-content .wizard-step.active { display: block; }

.wizard-buttons {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.6rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border-subtle);
}

/* Mesmo par visual do resto do fluxo de auth (Voltar/Acessar em login.php ?view=email) —
   prev espelha .btn-fb-outline, next/finish espelham .btn-fb-accent. Reaproveitado também pelo
   wizard de importação de log de voo (page-dashboard-my-equipment-detail.js), então a troca
   melhora os dois lugares, não só o cadastro. */
.wizard-buttons .btn,
.wizard-btn {
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text-primary);
  border-radius: var(--form-radius);
  padding: 0.6rem 1.1rem;
  font-size: 0.875rem;
  font-weight: 600;
}

.wizard-buttons .btn.next,
.wizard-buttons .btn.finish {
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  border-color: transparent;
  color: var(--accent-contrast);
  font-weight: 700;
}

.wizard-buttons .btn:hover { filter: brightness(1.12); }

/* ---- Auth split/cover shell -------------------------------------------------
   Variante split-screen do shell de autenticação (equivalente ao "-cover" do
   tema de referência), usada pela tela inicial de auth (Google / Login com
   E-mail / Cadastro com E-mail) e pelas telas que vêm depois dela (login por
   e-mail, cadastro). Estrutura própria — NÃO reaproveita .fb-auth-shell/
   .fb-auth-card acima, que são do shell single-column (reset-password e afins
   continuam nesse).

   Painel direito é só cor/gradiente por enquanto — sem imagem/copy reais
   ainda (fica pra depois, ver theme/flytheme/MIGRATION.md). Some abaixo de
   992px: o formulário passa a ocupar a tela cheia, como o próprio tema de
   referência faz (d-xl-block d-none no original).
   -------------------------------------------------------------------------- */

.fb-auth-split { flex: 1; min-height: 100vh; display: flex; }

.fb-auth-split__form {
  flex: 1 1 50%;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 3rem 1.5rem;
}

.fb-auth-split__form-inner { width: 100%; max-width: 25rem; }

/* cadastro é um wizard com campos lado a lado — precisa de mais largura (mesmo
   motivo do .fb-auth-card--wide no shell single-column, ver acima) */
.fb-auth-split__form-inner--wide { max-width: 40rem; }

.fb-auth-split__cover {
  flex: 1 1 50%;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 3rem;
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(circle at 25% 15%, rgba(var(--accent-rgb), 0.4) 0%, transparent 55%),
    linear-gradient(160deg, rgba(var(--accent-rgb), 0.16), rgba(0, 0, 0, 0.7));
}

.fb-auth-split__cover-content { position: relative; z-index: 1; max-width: 24rem; color: #fff; text-align: center; }
.fb-auth-split__cover-content .fb-wordmark { font-size: 1.75rem; }
.fb-auth-split__cover-content p { color: rgba(255, 255, 255, 0.78); }

.fb-auth-split__headline { font-size: 1.5rem; font-weight: 800; line-height: 1.35; color: #fff; }

/* Sibling of .fb-auth-split__cover-content, not a child — the headline stays
   vertically centred (the panel's align-items: center), while this pins to
   the panel's own bottom edge, same as the policy links in the reference. */
.fb-auth-split__terms {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 2rem;
  z-index: 1;
  text-align: center;
  font-size: 0.85rem;
}

/* No underline at rest — an always-on underline reads as disabled/legal
   fine print, not a link. Weight carries the affordance instead; underline
   is reserved for :hover, same convention as .fb-link-accent. */
.fb-auth-split__terms a {
  color: #fff;
  font-weight: 600;
  text-decoration: none;
}
.fb-auth-split__terms a:hover { text-decoration: underline; }

/* ---- CAD overlay (`.fb-cad`) --------------------------------------------------
   Decorative engineering-blueprint layer for the cover panel: a drone line
   sketch (white linework baked into the PNG's alpha channel, colour comes
   from CSS) plus animated SVG dimension lines and a telemetry chart. Classes
   live under the fb-cad__ namespace on purpose — the source material's names
   (.label, .small, .accent, .grid, .axis…) are common enough to collide with
   the rest of the theme or with Bootstrap (.small is a real Bootstrap
   utility; an unprefixed override here would have leaked everywhere).
   Decorative only: aria-hidden, pointer-events: none, z-index: 0 so the
   headline (z-index: 1) stays on top and legible. The 650KB sketch is a CSS
   background-image gated behind the same 992px breakpoint as the cover panel
   itself (below, in the media query) — display: none on the ancestor does
   NOT stop a browser from fetching an <img>, so this had to be background,
   not <img>, to actually skip the download on mobile.

   drone-cad-sketch.png ships pre-cleaned: the raw export had ~10% of its
   pixels sitting at alpha 1–40 (a faint dust/glow field spread across the
   whole canvas, not deliberate linework). Individually invisible, but at the
   size this renders in it reads as a uniform grey wash filling the image's
   whole bounding box — a hard rectangle against the plain gradient behind
   it, breaking the "no visible edge" effect this overlay exists for. Fixed
   by remapping the PNG's alpha channel: alpha ≤40 → 0, everything above
   rescaled back to the full 0–255 range. If this asset is ever regenerated,
   re-apply that curve or the rectangle comes back. */

/* opacity here, not on each child — one dial that keeps the whole layer behind
   the headline (z-index: 1) reading as background texture, not competing
   content. Was 1 (full-strength lines/labels/glow); text sat on top but the
   overlay itself pulled too much attention. */
.fb-cad { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; opacity: 0.12; }

.fb-cad__sketch {
  display: none;
  position: absolute;
  inset: 4% 3% 2%;
  width: 94%;
  height: 94%;
  opacity: 0.78;
  filter: drop-shadow(0 0 8px rgba(190, 145, 255, 0.16));
  background: url("../img/drone-cad-sketch.png?v=3") no-repeat center / contain;
}

.fb-cad__svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }

.fb-cad__cut {
  fill: none;
  stroke: #fff;
  stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 1 1;
  filter: drop-shadow(0 0 4px #9e58ff);
  animation: fbCadCutOscillate var(--duration, 5s) ease-in-out var(--phase, 0s) infinite alternate;
}
.fb-cad__extension { fill: none; stroke: #c49aff; stroke-width: 0.8; stroke-dasharray: 4 4; opacity: 0.72; }
.fb-cad__tick { stroke: #fff; stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.fb-cad__target { fill: none; stroke: #b87bff; stroke-width: 1.1; stroke-dasharray: 5 4; opacity: 0.82; }

.fb-cad__measure:nth-of-type(1) { --duration: 3.2s; --phase: -0.4s; }
.fb-cad__measure:nth-of-type(2) { --duration: 3.7s; --phase: -2.25s; }
.fb-cad__measure:nth-of-type(3) { --duration: 4.1s; --phase: -2.45s; }
.fb-cad__measure:nth-of-type(4) { --duration: 4.6s; --phase: -0.7s; }
.fb-cad__measure:nth-of-type(5) { --duration: 3.5s; --phase: -1.8s; }
.fb-cad__measure:nth-of-type(6) { --duration: 4.4s; --phase: -3.3s; }

.fb-cad__label {
  fill: #fff;
  font-size: 13px;
  letter-spacing: 1px;
  paint-order: stroke;
  stroke: #1b102f;
  stroke-width: 4px;
  stroke-linejoin: round;
}
.fb-cad__label--small { font-size: 10px; fill: #cdb8ef; letter-spacing: 0.7px; }
.fb-cad__label--accent { fill: #b678ff; }

.fb-cad__chart-bg { fill: rgba(16, 11, 29, 0.52); stroke: #a06cda; stroke-opacity: 0.58; }
.fb-cad__grid { fill: none; stroke: #a888ce; stroke-width: 0.6; stroke-opacity: 0.18; }
.fb-cad__axis { fill: none; stroke: #d8c7ef; stroke-width: 0.8; stroke-opacity: 0.55; }
.fb-cad__series {
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 520;
  stroke-dashoffset: 520;
  animation: fbCadPlot 6s linear 5.6s infinite;
}
.fb-cad__series--1 { stroke: #fff; }
.fb-cad__series--2 { stroke: #b96cff; animation-delay: 5.9s; }
.fb-cad__series--3 { stroke: #55d9ff; animation-delay: 6.2s; }
.fb-cad__chart-text { fill: #d7c6ec; font-size: 8px; letter-spacing: 0.5px; }

.fb-cad__corner { position: absolute; color: #d6c8ef; font-size: 10px; letter-spacing: 1.5px; opacity: 0.65; line-height: 1.7; }
.fb-cad__corner--tl { top: 28px; left: 32px; }
.fb-cad__corner--br { right: 32px; bottom: 25px; text-align: right; }

@keyframes fbCadCutOscillate {
  0% { stroke-dashoffset: 0.82; opacity: 0.48; }
  100% { stroke-dashoffset: 0; opacity: 1; }
}
@keyframes fbCadPlot {
  0% { stroke-dashoffset: 520; opacity: 0.35; }
  68%, 100% { stroke-dashoffset: 0; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .fb-cad__cut, .fb-cad__series { animation: none; }
}

@media (min-width: 992px) {
  .fb-auth-split__cover { display: flex; }
  .fb-cad__sketch { display: block; }
}

/* opções da tela inicial: Google / Login por e-mail / Cadastro por e-mail */
.fb-auth-choice { display: flex; flex-direction: column; gap: 0.75rem; }

/* .fb-auth-social__item already carries its own margin-bottom for standalone
   use; inside .fb-auth-choice the flex `gap` owns the spacing instead, so the
   two must not stack. */
.fb-auth-choice .fb-auth-social__item { margin-bottom: 0; }

/* ---- Form field -------------------------------------------------------------
   Rótulo, obrigatoriedade e mensagem, no padrão do Bootstrap que o markup usa.
   -------------------------------------------------------------------------- */

.form-label {
  display: block;
  text-align: left;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-secondary);
  margin-bottom: 0.35rem;
}

.text-default { color: var(--text-secondary) !important; }
.form-label .text-danger, .form-text { font-size: 0.75rem; }
.form-text { color: var(--text-muted); }

.form-select {
  background-color: rgba(255, 255, 255, 0.06);
  border: 1px solid transparent;
  border-radius: var(--form-radius);
  color: var(--text-primary);
  font-size: 0.875rem;
  padding: 0.55rem 0.85rem;
}

.form-select:focus { border-color: var(--accent); box-shadow: 0 0 0 0.2rem rgba(var(--accent-rgb), 0.2); }
.form-select option { background: var(--bg-panel); color: var(--text-primary); }
.form-select:disabled { background-color: rgba(255, 255, 255, 0.03); color: var(--text-muted); }

/* fill instead of outline for the box itself — same move as .form-control,
   so a checkbox never reads as a bordered square next to a borderless input */
.form-check-input {
  background-color: rgba(255, 255, 255, 0.1);
  border: 1px solid transparent;
  border-radius: 0.3125rem; /* 5px — its own smaller step of --form-radius */
}
.form-check-input:checked { background-color: var(--accent); border-color: var(--accent); }
.form-check-input:focus { border-color: var(--accent); box-shadow: 0 0 0 0.2rem rgba(var(--accent-rgb), 0.2); }

.input-group-text {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid transparent;
  border-radius: var(--form-radius);
  color: var(--text-secondary);
}

.invalid-feedback { color: #fca5a5 !important; }

/* botão claro do "entrar com Google": sobre fundo escuro precisa contraste */
 /* No tema de referência este botão é discreto sobre o fundo escuro, não um
    bloco branco — que roubaria a atenção do formulário. */
.btn-light {
  background: var(--bg-card-2);
  border: 1px solid var(--border-subtle);
  color: var(--text-primary);
  border-radius: var(--form-radius);
  padding: 0.6rem 1rem;
  font-size: 0.875rem;
  font-weight: 600;
}

.btn-light:hover { background: var(--bg-card); color: var(--text-primary); border-color: var(--border-strong); }

/* ---- Validação de formulário -------------------------------------------------
   Mesmo sistema do site em produção, em três camadas:

   1. `.highlight-error` — o Wizard-JS aplica no INPUT inválido ao tentar
      avançar, e remove sozinho após highlight_time (1s). É a única sinalização
      dos campos `required`; sem esta regra o "Próximo" simplesmente não avança
      e nada explica por quê.
   2. `.invalid-feedback` — mensagem por campo, revelada por classe pelo JS da
      página (ex.: senhas não conferem). No markup vem com `.d-block .d-none`.
   3. `.alert-*` — resposta da API no topo do formulário.
   -------------------------------------------------------------------------- */

.highlight-error {
  outline: 1px solid var(--danger);
  outline-offset: 0;
  border-color: var(--danger) !important;
}

.invalid-feedback,
.valid-feedback { font-size: 0.75rem; margin-top: 0.3rem; }
.valid-feedback { color: var(--success); }

.form-control.is-invalid,
.form-select.is-invalid { border-color: var(--danger); }

.form-control.is-invalid:focus,
.form-select.is-invalid:focus { box-shadow: 0 0 0 0.2rem rgba(239, 68, 68, 0.2); }

.form-control.is-valid,
.form-select.is-valid { border-color: var(--success); }

/* variantes de alerta — o Bootstrap as traz em tons claros */
.alert-danger {
  --bs-alert-bg: rgba(239, 68, 68, 0.12);
  --bs-alert-border-color: rgba(239, 68, 68, 0.35);
  --bs-alert-color: #fca5a5;
}

.alert-success {
  --bs-alert-bg: rgba(34, 197, 94, 0.12);
  --bs-alert-border-color: rgba(34, 197, 94, 0.35);
  --bs-alert-color: #86efac;
}

.alert-info {
  --bs-alert-bg: rgba(var(--accent-rgb), 0.12);
  --bs-alert-border-color: rgba(var(--accent-rgb), 0.35);
  --bs-alert-color: var(--text-primary);
}

/* ---- FullCalendar (aba "Voos" do equipamento) --------------------------------
   Vendor bundle (assets/libs/fullcalendar) ships its own light theme and
   injects it via JS, not a stylesheet we can just skip loading — its officially
   supported theming hook is these --fc-* custom properties (all consumed
   internally by the vendor's own generated CSS), same idea as overriding
   Bootstrap's --bs-* vars elsewhere in this file. Scoped to the calendar's own
   container so it can't leak into any other `.fc-*`-named thing on the page. */
#equipment-kpi-calendar {
  --fc-border-color: var(--border-subtle);
  --fc-page-bg-color: transparent;
  --fc-neutral-bg-color: var(--bg-card-2);
  --fc-neutral-text-color: var(--text-secondary);
  --fc-today-bg-color: rgba(var(--accent-rgb), 0.08);
  --fc-button-bg-color: var(--bg-card-2);
  --fc-button-border-color: var(--border-subtle);
  --fc-button-hover-bg-color: var(--bg-panel);
  --fc-button-hover-border-color: var(--border-strong);
  --fc-button-active-bg-color: var(--accent);
  --fc-button-active-border-color: var(--accent);
  --fc-button-text-color: var(--text-primary);
  --fc-event-bg-color: var(--accent);
  --fc-event-border-color: var(--accent);
  --fc-event-text-color: var(--accent-contrast);
  --fc-list-event-hover-bg-color: var(--bg-card-2);
  color: var(--text-primary);
}

/* Belt-and-suspenders for the couple of cells that don't fully key off the
   vars above in every vendor version — day-grid backgrounds and the day-number
   label specifically tend to stay hardcoded light. */
#equipment-kpi-calendar .fc-daygrid-day,
#equipment-kpi-calendar .fc-timegrid-slot,
#equipment-kpi-calendar .fc-list-day-cushion { background: transparent; }
#equipment-kpi-calendar .fc-col-header-cell { background: var(--bg-card-2); }
#equipment-kpi-calendar a { color: var(--text-primary); }
#equipment-kpi-calendar .fc-list-empty { background: transparent; color: var(--text-muted); }
/* FullCalendar renders each event as an <a> with no href (eventClick handles the click in JS,
   ver page-dashboard-my-equipment-detail.js) — browsers only auto-cursor:pointer an <a> that
   has a real href, so without this the "3 voos" pill looked unclickable on hover. */
#equipment-kpi-calendar .fc-event { cursor: pointer; }

/* ---- Calendário: visões "Geral"/"Ano" (grade de blocos) e "Lista" (últimos 50 voos) ----------
   Não são views nativas do FullCalendar — ele só desenha grades de dia/hora (nem "multiMonthYear"
   ajuda: renderiza 12 calendários completos, não 12 quadros-resumo), e a visão de lista dele é
   sempre por intervalo de datas, nunca "os N mais recentes". #equipment-kpi-calendar-custom é
   irmão de #equipment-kpi-calendar (criado em JS, ver page-dashboard-my-equipment-detail.js) —
   por isso usa direto os tokens globais do tema (--border-subtle, --bg-card-2 etc.), não as
   --fc-* daquele bloco acima (essas só existem dentro do container do FullCalendar). */
#equipment-kpi-calendar-custom {
  min-height: 200px;
}
.fc-flybase-tilegrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
  gap: 0.6rem;
  padding: 1rem;
}
.fc-flybase-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  padding: 1rem 0.5rem;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--bg-card-2);
  color: var(--text-primary);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.fc-flybase-tile:hover {
  border-color: var(--accent);
  background: rgba(var(--accent-rgb), 0.08);
}
.fc-flybase-tile__label {
  font-size: 1.1rem;
  font-weight: 600;
  text-transform: capitalize;
}
.fc-flybase-tile__count {
  font-size: 0.78rem;
  color: var(--text-muted);
}

/* ---- Lista "Voos do Dia" (mesma aba) ------------------------------------------
   Item gerado em JS (renderDayFlights, page-dashboard-my-equipment-detail.js) —
   <a href="javascript:void(0)"> já é clicável, mas sem um :hover próprio não
   fica óbvio que é (some junto com o resto do texto no card escuro). */
.flight-day-item {
  cursor: pointer;
  border-radius: var(--radius-sm);
  padding: 0.35rem;
  margin: -0.35rem;
  transition: background 0.15s ease;
}
.flight-day-item:hover { background: rgba(255, 255, 255, 0.05); }


/* ================================================================================
   Portado de theme/flytheme (COMPONENTS.md § "Botão que ativa input[type=file]" e
   § "Caixa de log expansível") em 2026-08-26, pro modal "Importar logs de voo".
   Fonte canônica: theme/flytheme/assets/css/theme.css + importar-logs.html.
   ================================================================================ */

/* Mesmo estado visual de :disabled, mas pra quando o "botão" é um <label>
   (ex.: seletor de arquivo customizado) — label não tem :disabled nativo, então
   o JS alterna aria-disabled em vez de disabled. pointer-events:none é
   obrigatório aqui: sem :disabled de verdade, o clique ainda abriria o input
   escondido se não for bloqueado explicitamente. */
.btn-fb-outline[aria-disabled="true"],
.btn-fb-soft[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* Botão "label envolve input[type=file]" (seletor de arquivo customizado).
   O input cobre o botão INTEIRO com opacity:0 em vez de ser encolhido a ~1px
   (.visually-hidden) — é o que as bibliotecas de upload fazem e o que
   COMPONENTS.md define como canônico.
   ⚠️ COMPONENTS.md justifica isso com uma proteção anticlickjacking do Chrome.
   NÃO É FATO ESTABELECIDO: a evidência registrada lá ("abria no Safari, não
   abria em NENHUMA janela do Chrome") foi colhida em 2026-08-26, enquanto um
   Chrome do Playwright sequestrava toda abertura do navegador nesta máquina —
   sob automação o seletor de arquivos é interceptado, então qualquer janela do
   Chrome falharia independentemente do CSS. Marcação correta, causa em aberto. */
.fb-file-trigger { position: relative; overflow: hidden; }
.fb-file-trigger__input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.fb-file-trigger:has(.fb-file-trigger__input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Caixa de log expansível (ex.: "Ver detalhes" de uma importação em andamento) —
   altura FIXA de 5 linhas monoespaçadas (não max-height: reserva o espaço das 5
   mesmo com 1 linha só, pra não "pular" de tamanho conforme o log cresce),
   rolagem quando passar disso. */
.fb-log-box {
  height: 6.25rem; /* 5 linhas de 1.25rem */
  overflow-y: auto;
  padding: 0.65rem 0.85rem;
  background: var(--bg-body);
  border: 1px solid var(--card-border-color);
  border-radius: var(--radius-md);
  font-family: 'Courier New', Courier, monospace;
  font-size: 0.78rem;
  line-height: 1.25rem;
}
.fb-log-box__line { margin: 0; color: var(--text-secondary); white-space: pre-wrap; word-break: break-word; }
.fb-log-box__line.is-success { color: var(--success); }
.fb-log-box__line.is-danger { color: var(--danger); }
