/* Design tokens — colors, type, spacing. Keep the current visual identity.
   Breakpoints (custom props can't hold media queries — documented here):
   36em(576) / 48em(768) / 64em(1024) / 80em(1280, .container max). */
:root {
  --brand:        #ff7031;
  --brand-strong: #e05b2b;
  --navy:         #12445d;
  --ink:          #363c4b;
  --text:         #444;
  --muted:        #707070;
  --ok: #0b6; --info: #06b; --err: #b00000; --warn: #b26a00;
  --bg: #fff; --surface: #f2f2f2; --line: #ddd;

  --font-sans: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --fs-base: 1rem;
  --fs-lg: clamp(1.25rem, 1rem + 1vw, 1.5rem);
  --fs-xl: clamp(1.5rem, 1.2rem + 1.8vw, 2.25rem);

  --sp-1: .25rem; --sp-2: .5rem; --sp-3: .75rem; --sp-4: 1rem;
  --sp-5: 1.5rem; --sp-6: 2rem; --sp-7: 3rem; --sp-8: 4rem;

  --radius-sm: 4px; --radius: 8px;
  --shadow-1: 0 1px 3px rgb(0 0 0 / .12);
  --shadow-2: 0 4px 12px rgb(0 0 0 / .15);
  --header-h: 4.5rem;
  --container: 80rem;
}
/* Reset + base typography (mobile-first). */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Offset in-page anchor jumps (FAQ index, back-to-top, ...) so targets clear
     the sticky header instead of landing under it. */
  scroll-padding-top: calc(var(--header-h) + var(--sp-3));
}
body {
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: 1.55;
  color: var(--text);
  background: var(--bg);
  /* Sticky footer: on short pages the main area grows so the footer sits at
     the bottom of the viewport rather than mid-screen. */
  display: flex; flex-direction: column; min-height: 100vh;
}
.site-main { flex: 1 0 auto; }
img, picture, svg { max-width: 100%; display: block; }
a { color: var(--brand); text-decoration: none; }
a:hover { color: var(--brand-strong); text-decoration: underline; }
h1, h2, h3, h4 { line-height: 1.2; color: var(--brand); }   /* orange, like the original */
h1 { font-size: var(--fs-xl); }

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

.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--sp-4); }

.skip-link {
  position: absolute; left: -9999px; top: 0;
  background: var(--ink); color: #fff; padding: var(--sp-2) var(--sp-4);
}
.skip-link:focus { left: 0; z-index: 100; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* O Alpine põe x-cloak em tudo o que não pode ser visto antes de ele arrancar.
 * Sem esta regra, os painéis de um tabset aparecem TODOS empilhados durante um
 * frame, em cada carregamento da página. Não faz nada onde o Alpine não é
 * carregado, que é a maior parte deste sítio. */
[x-cloak] {
  display: none !important;
}
/* Shared form primitives -- buttons, flash messages, form fields.
   Extracted from catalog's components.css into the 2026 shared kit so
   catalog + campus render identical controls (BEM-lite, class-scoped). */

/* ---- Buttons ------------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp-2);
  min-height: 44px; padding: var(--sp-2) var(--sp-5);
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink);
  font: inherit; font-weight: 600; line-height: 1.2;
  text-align: center; text-decoration: none; cursor: pointer;
}
.btn:hover { text-decoration: none; }
.btn--primary { background: var(--brand); color: #fff; }
.btn--primary:hover { background: var(--brand-strong); color: #fff; }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn--sm { min-height: 36px; padding: var(--sp-1) var(--sp-3); }

/* O botao secundario: a accao alternativa, visivel mas nao convidativa. Precisa
   de contorno proprio -- um .btn simples desenha a borda transparente, o que o
   deixa indistinguivel do fundo do painel em que costuma viver. */
.btn--secondary {
  background: var(--surface);
  border-color: var(--line);
  color: var(--ink);
}
.btn--secondary:hover { background: var(--bg); border-color: var(--ink); color: var(--ink); }

/* Um botao que se le' como um link. Para accoes DENTRO de uma frase ou de uma
   linha de tabela -- «remover», «mudar nome» -- onde um bloco de 44px domina a
   linha em que vive e faz de uma tabela de tres ficheiros um ecra' inteiro.
   Continua a ser um <button>, porque continua a submeter um formulario: o que
   muda e' o aspecto, nao a natureza. */
.btn--link {
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--brand);
  font-weight: inherit;
  text-decoration: underline;
}
.btn--link:hover { text-decoration: underline; }

/* ---- Flash messages ----------------------------------------------------- */
.flash {
  margin-bottom: var(--sp-5);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid; border-left-width: 4px;
  border-radius: var(--radius-sm);
  background: var(--bg);
}
.flash__body { margin: 0; }
.flash--ok   { border-color: var(--ok);   color: #064; background: #f0fbf6; }
.flash--info { border-color: var(--info); color: #024; background: #eff6fc; }
.flash--warn { border-color: var(--warn); color: #613; background: #fdf6ec; }
.flash--err  { border-color: var(--err);  color: #700; background: #fdf0f0; }

/* ---- Form fields -------------------------------------------------------- */
.field { margin-bottom: var(--sp-4); }
.field__label {
  display: block; margin-bottom: var(--sp-1);
  font-weight: 600; color: var(--ink);
}
.field__req { color: var(--err); }
.field__input,
.field__select {
  width: 100%; min-height: 44px;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  font: inherit; color: var(--text); background: var(--bg);
}
.field__input:focus-visible,
.field__select:focus-visible { border-color: var(--brand); }
textarea.field__input { min-height: 8rem; resize: vertical; }
.field--error .field__input,
.field--error .field__select { border-color: var(--err); }
.field__help { margin-top: var(--sp-1); font-size: .875rem; color: var(--muted); }
.field__error { margin-top: var(--sp-1); font-size: .875rem; color: var(--err); }

.field--check { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-2); align-items: start; }
.field__check { width: 1.25rem; height: 1.25rem; margin-top: .15rem; }
.field__check-label { font-weight: 400; }
.field--check .field__error { grid-column: 1 / -1; }
/* Shared site chrome -- the header bar + logo. Extracted from catalog's
   components.css into the 2026 kit so catalog (full header) and campus
   (logo-only header, w/header_logo) render the same header. */

/* ---- Header ------------------------------------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--bg);
  box-shadow: var(--shadow-1);
  border-bottom: 1px solid var(--line);
}
.site-header__inner {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--sp-4); min-height: var(--header-h);
}
.site-header__logo {
  display: inline-flex; align-items: center;
  margin-right: auto;
}
.site-header__logo img { display: block; width: 200px; height: auto; }
.site-header__logo:hover { text-decoration: none; }

/* ---- (flags) badge ------------------------------------------------------- */
/* Only rendered while this visitor has feature flags on, set from the gestao
   console (/ops/flags.html) -- see docs/specs/20260730-gestao-managed-feature-flags.md.
   Red and last in the header on purpose: it is the standing reminder that this
   browser is NOT seeing what everyone else sees, and clicking it goes back to the
   console to change or clear the set. Hover/title lists the active names.

   Deliberately NOT sharing the sites' nav-link classes (.account-nav__link,
   .app-account__logout): both live in layers that come AFTER chrome.css in every
   build order, so they would win the cascade. Self-contained instead. */
.flags-badge {
  display: inline-flex; align-items: center;
  padding: var(--sp-2);
  color: var(--err); font-weight: 700; font-size: .8125rem;
  white-space: nowrap; text-decoration: none;
}
.flags-badge:hover,
.flags-badge:focus { color: var(--err); text-decoration: underline; }
/* ==========================================================================
   Checkout — cart, the three steps, and the per-method screens.

   Built on this kit's own primitives (.btn, .field, .flash) rather than beside
   them: a checkout that looks like a different site is how a shopper decides they
   are on a phishing page.

   SHARED by catalog-mojo and campus-mojo. The order summary a member sees on
   campus is the same markup as the confirmation page on www, so it has to be the
   same stylesheet -- two copies would drift and the drift would show up as the
   same order rendered two ways.
   ========================================================================== */

/* No checkout e nas encomendas a coluna de conteudo e' mais estreita que a do
   site. A zona da mensagem (.flash-zone, no layout) alinha com ela: uma caixa
   que comeca antes do titulo da pagina le'-se como se pertencesse ao site e nao
   ao ecra. */
.checkout-page .flash-zone,
.orders-page .flash-zone { max-width: 68rem; }

.checkout {
  max-width: 68rem;
  margin: 0 auto;
  padding: var(--space-6, 1.5rem) var(--space-4, 1rem) var(--space-10, 3rem);
}

.checkout__title {
  margin: 0 0 var(--space-6, 1.5rem);
}

/* --- the three steps ------------------------------------------------------
   Shown on EVERY step including the last one: a shopper on the confirmation
   page still wants to know they reached the end rather than fell out of it. */

.steps {
  display: flex;
  gap: var(--space-2, 0.5rem);
  margin: 0 0 var(--space-8, 2rem);
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.steps__item {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  gap: var(--space-3, 0.75rem);
  padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
  border-top: 3px solid var(--color-border, #ddd);
  color: var(--color-text-muted, #666);
  font-weight: 500;
}

.steps__item::before {
  counter-increment: step;
  content: counter(step);
  display: grid;
  place-items: center;
  flex: 0 0 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: var(--color-border, #ddd);
  color: #fff;
  font-size: 0.875rem;
  font-weight: 700;
}

.steps__item--done { color: var(--color-text, #222); border-top-color: var(--color-primary, #f60); }
.steps__item--done::before { background: var(--color-primary, #f60); }

.steps__item--current { color: var(--color-text, #222); border-top-color: var(--color-primary, #f60); }
.steps__item--current::before { background: var(--color-primary, #f60); }

/* --- the layout: content beside a sticky summary -------------------------- */

/* A pagina de confirmacao nao tem barra lateral: o painel da encomenda ja' leva
   os totais, e repeti-los ao lado e' a mesma soma escrita duas vezes. */
.checkout__single { max-width: 44rem; }

.checkout__grid {
  display: grid;
  gap: var(--space-8, 2rem);
  grid-template-columns: 1fr;
}

@media (min-width: 60rem) {
  .checkout__grid { grid-template-columns: minmax(0, 1fr) 22rem; }
  .checkout__aside { position: sticky; top: var(--space-4, 1rem); align-self: start; }
}

.panel {
  border: 1px solid var(--color-border, #ddd);
  border-radius: var(--radius-md, 8px);
  padding: var(--space-5, 1.25rem);
  background: var(--color-surface, #fff);
}

.panel + .panel { margin-top: var(--space-5, 1.25rem); }
.panel__title { margin: 0 0 var(--space-4, 1rem); font-size: 1.125rem; }

/* --- the lines ------------------------------------------------------------
   One row per product. A bundle is the product on the first row and its courses
   underneath, each with its start date and NO price: the price belongs to the
   bundle, and repeating it per course reads as a total four times too big. */

.lines { margin: 0; padding: 0; list-style: none; }

.line {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-2, 0.5rem) var(--space-4, 1rem);
  padding: var(--space-4, 1rem) 0;
  border-bottom: 1px solid var(--color-border, #ddd);
}

.line:last-child { border-bottom: 0; }

.line__name { font-weight: 600; }
.line__meta { grid-column: 1; color: var(--color-text-muted, #666); font-size: 0.875rem; }
.line__price { grid-column: 2; grid-row: 1; text-align: right; white-space: nowrap; font-weight: 600; }
.line__was { display: block; font-weight: 400; font-size: 0.875rem; text-decoration: line-through; color: var(--color-text-muted, #666); }
.line__actions { grid-column: 2; text-align: right; }

.line__children { grid-column: 1 / -1; margin: var(--space-3, 0.75rem) 0 0; padding: 0 0 0 var(--space-5, 1.25rem); list-style: none; border-left: 2px solid var(--color-border, #ddd); }
.line__child { display: flex; justify-content: space-between; gap: var(--space-4, 1rem); padding: var(--space-1, 0.25rem) 0; font-size: 0.9375rem; }
.line__child-date { color: var(--color-text-muted, #666); white-space: nowrap; }

/* --- totals --------------------------------------------------------------- */

.totals { margin: 0; }
.totals__row { display: flex; justify-content: space-between; gap: var(--space-4, 1rem); padding: var(--space-2, 0.5rem) 0; }
.totals__row--grand { border-top: 1px solid var(--color-border, #ddd); margin-top: var(--space-2, 0.5rem); padding-top: var(--space-3, 0.75rem); font-size: 1.125rem; font-weight: 700; }
.totals__discount { color: var(--color-success, #2e7d32); }

/* --- the coupon box ------------------------------------------------------- */

/* Um codigo de cupao tem meia duzia de caracteres: uma caixa de largura total
   promete um campo de texto livre que nao e'. Campo curto, botao ao lado. */
.coupon { display: flex; gap: var(--space-2, 0.5rem); align-items: center; flex-wrap: wrap; }
.coupon__label { flex: 0 0 100%; margin-bottom: var(--space-1, 0.25rem); font-weight: 600; }
.coupon__input {
  flex: 0 1 11rem; min-height: 36px;
  padding: var(--space-1, 0.25rem) var(--space-3, 0.75rem);
  border: 1px solid var(--color-border, #ddd); border-radius: var(--radius-sm, 4px);
  font: inherit;
}

/* --- payment methods ------------------------------------------------------ */

.methods { display: grid; gap: var(--space-3, 0.75rem); margin: 0 0 var(--space-6, 1.5rem); }

.method {
  display: flex;
  align-items: center;
  gap: var(--space-3, 0.75rem);
  padding: var(--space-4, 1rem);
  border: 1px solid var(--color-border, #ddd);
  border-radius: var(--radius-md, 8px);
  cursor: pointer;
}

.method:has(input:checked) { border-color: var(--color-primary, #f60); box-shadow: inset 0 0 0 1px var(--color-primary, #f60); }
.method__label { font-weight: 600; }
.method__note { display: block; font-weight: 400; font-size: 0.875rem; color: var(--color-text-muted, #666); }

/* --- reference / bank details, the things a buyer copies down ------------- */

.paydata { margin: var(--space-4, 1rem) 0; border-collapse: collapse; width: 100%; }
.paydata th, .paydata td { padding: var(--space-3, 0.75rem); border-bottom: 1px solid var(--color-border, #ddd); text-align: left; }
.paydata th { width: 12rem; color: var(--color-text-muted, #666); font-weight: 500; }
.paydata td { font-size: 1.25rem; font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }

/* --- the MBWay wait ------------------------------------------------------- */

.await { text-align: center; padding: var(--space-8, 2rem) 0; }
.await__timer { font-size: 2.5rem; font-weight: 700; font-variant-numeric: tabular-nums; margin: var(--space-4, 1rem) 0; }
.await__hint { color: var(--color-text-muted, #666); }

.spinner {
  width: 2.5rem; height: 2.5rem; margin: 0 auto var(--space-4, 1rem);
  border: 3px solid var(--color-border, #ddd);
  border-top-color: var(--color-primary, #f60);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

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

/* --- actions -------------------------------------------------------------- */

.checkout__actions { display: flex; gap: var(--space-3, 0.75rem); flex-wrap: wrap; margin-top: var(--space-6, 1.5rem); }

/* Os botoes do checkout sao uma linha, nao uma pilha. O catalogo poe
   `.panel .btn { width: 100% }` -- desenhado para o botao unico de um painel --
   e a' largura toda cada um ocupa a linha inteira, o que apaga a diferenca entre
   a accao principal e a alternativa. Tres classes de especificidade para ganhar
   onde quer que os ficheiros sejam concatenados, em vez de depender da ordem. */
.checkout .checkout__actions .btn,
.checkout .coupon .btn { width: auto; }

/* row-reverse, nao row: «Continuar» vem PRIMEIRO no HTML para que o Enter dentro
   de um campo o accione, e nao o «Voltar». A ordem visual -- Voltar a' esquerda,
   Continuar a' direita -- e' reposta aqui. Ver carrinho/w/actions.tx. */
.checkout__actions--split { flex-direction: row-reverse; justify-content: space-between; }

/* A escolha «com/sem dados de faturacao». Um fieldset sem moldura: o que separa
   as duas hipoteses e' a legenda, nao uma caixa dentro de outra caixa. */
.choice { margin: 0 0 var(--space-5, 1.25rem); padding: 0; border: 0; }
.choice__legend { padding: 0; margin-bottom: var(--space-3, 0.75rem); font-weight: 600; }
.choice__option { display: flex; align-items: center; gap: var(--space-2, 0.5rem); padding: var(--space-1, 0.25rem) 0; cursor: pointer; }

/* O que cada hipotese abre fica indentado debaixo dela, para se ler como parte
   da escolha e nao como a seccao seguinte. */
.choice__panel { margin: var(--space-3, 0.75rem) 0 var(--space-5, 1.25rem) var(--space-6, 1.5rem); }

/* Duas caixas que sao um endereco so': a segunda nao tem rotulo, por isso o
   espaco normal entre campos le-se como se ela pertencesse ao seguinte. */
.field--tight { margin-bottom: var(--space-2, 0.5rem); }

/* --- the order summary on the confirmation page --------------------------- */

.summary__dl { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: var(--space-2, 0.5rem) var(--space-4, 1rem); }
.summary__dl dt { color: var(--color-text-muted, #666); }
.summary__dl dd { margin: 0; }

/* Uma segunda linha de morada e' a continuacao da primeira, nao um campo novo:
   sem rotulo, e por isso sem nada na coluna da esquerda. */
.summary__dl dd + dd { grid-column: 2; }

.orders-table { width: 100%; border-collapse: collapse; }
.orders-table th, .orders-table td { padding: var(--space-3, 0.75rem); border-bottom: 1px solid var(--color-border, #ddd); text-align: left; }
.orders-table th { color: var(--color-text-muted, #666); font-weight: 500; }

.badge { display: inline-block; padding: 0.125rem 0.5rem; border-radius: 999px; font-size: 0.8125rem; font-weight: 600; background: var(--color-border, #ddd); }
.badge--ok { background: #c8e6c9; color: #1b5e20; }
.badge--wait { background: #ffe0b2; color: #7a4100; }
.badge--off { background: #eee; color: #666; }
/* Auth pages (login, registar, recuperar/atualizar password, validacao email).
   Only the auth-specific card + layout live here; fields, flash boxes and
   buttons reuse the shared component styles. Loaded via bin/build-assets. */

.auth-container {
  display: flex;
  justify-content: center;
  padding-block: var(--sp-6);
}

.auth-card {
  width: 100%;
  max-width: 26rem;
  padding: var(--sp-6);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
}

.auth-card h1 {
  margin-bottom: var(--sp-5);
  font-size: var(--fs-lg);
}

.auth-card p { margin-bottom: var(--sp-4); }

.auth-form { margin-bottom: var(--sp-4); }

.auth-form .btn { width: 100%; }

/* Inline "Recuperar password" hint under the e-mail field on registration. */
.field__hint {
  margin-top: calc(var(--sp-1) * -1);
  margin-bottom: var(--sp-4);
  font-size: .875rem;
}

.auth-alt {
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
  font-size: .9375rem;
}

.auth-alt p { margin-bottom: var(--sp-2); }
.auth-alt p:last-child { margin-bottom: 0; }
/* Private-area chrome (campus + step-3 pages). Catted by campus only, NOT by
   catalog. Pairs with layouts::private + w/header_logo + w/footer_simple. */

/* Minimal footer: a muted copyright bar. Sticks to the bottom on short pages
   via base.css (body is a flex column and .site-main is flex: 1 0 auto). */
.site-footer-min {
  border-top: 1px solid var(--line);
  background: var(--surface);
  padding-block: var(--sp-4);
  color: var(--muted);
  font-size: .75rem;
  text-align: center;
}
.site-footer-min p { margin: 0; }

/* Auth pages: anchor the card near the top (~10% down) of the space between
   header and footer, not vertically centred. The flash message is taken out of
   flow (absolutely positioned just below the header) so showing/hiding it never
   shifts the card. */
.auth-page .site-main {
  display: grid;
  align-content: start;
  justify-items: center;
  padding-top: 10vh;
  position: relative;
}
.auth-page .site-main > .flash {
  position: absolute;
  top: var(--sp-5);
  left: 50%;
  transform: translateX(-50%);
  width: min(30rem, calc(100% - 2 * var(--sp-4)));
  margin: 0;
}

/* ====================================================================== */
/* Trainer application chrome (the authenticated /formadores pages)        */
/* ====================================================================== */

/* In-header navigation. The logo no longer pushes everything to the right
   (shared chrome.css gives it margin-right:auto); the account block owns the
   far corner instead. Wraps to multiple rows on narrow viewports (no JS). */
.site-header--app .site-header__logo { margin-right: var(--sp-5); }

.app-nav__list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4);
}
.app-nav__link {
  display: inline-block; padding: var(--sp-2) 0;
  color: var(--navy); font-weight: 600; font-size: .9375rem;
  border-bottom: 2px solid transparent;
}
.app-nav__link:hover { color: var(--brand); text-decoration: none; }
.app-nav__link.is-active { color: var(--brand); border-bottom-color: var(--brand); }

.app-account {
  margin-left: auto; display: flex; align-items: center; gap: var(--sp-4);
}
.app-account__name { color: var(--muted); font-size: .875rem; }
.app-account__logout {
  display: inline-block; padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--navy); font-weight: 600; font-size: .875rem; white-space: nowrap;
}
.app-account__logout:hover { border-color: var(--brand); color: var(--brand); text-decoration: none; }

/* Page body */
.app-main__inner { padding-block: var(--sp-6); }
.page-title { color: var(--navy); font-size: var(--fs-xl); margin: 0 0 var(--sp-5); }

/* Heading row: the page title on the left, the page's primary action pinned to
   its right and on the same line. Wraps under the heading when there is no room
   for both. */
.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-5);
}

.page-head .page-title { margin: 0; }
.section-title { color: var(--navy); font-size: var(--fs-lg); margin: var(--sp-6) 0 var(--sp-3); }
.section-subtitle { color: var(--ink); font-size: 1.05rem; margin: var(--sp-5) 0 var(--sp-2); }

/* Sub-navigation / list filters ("Listar: a decorrer | próximos | todos"),
   with the trainer typeahead floated to the right on the same row. */
.section-filters {
  margin: 0 0 var(--sp-4); color: var(--muted); font-size: .9375rem;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4);
}
.section-filters a { color: var(--navy); }
.section-filters .is-current { font-weight: 700; color: var(--text); }

/* Member/course typeahead: filters a listing in place as you type; with JS off
   it stays a plain GET form (?q=) or an inert box. Floated right -- either of the
   state tabs (.eval-search inside .section-filters) or, on the courses list, at
   the far right of the Filtros fieldset row, level with the "Filtrar" button
   (.filter-search pushed right inside the flex fieldset). */
.eval-search { display: inline-flex; align-items: center; gap: var(--sp-2); margin-left: auto; }
.filter-search { display: inline-flex; align-items: center; margin-left: auto; }
.eval-search input[type="search"],
.filters input[type="search"] {
  padding: var(--sp-2) var(--sp-3); border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); font-size: .9375rem;
  min-width: 12rem; max-width: 100%;
}
/* The evaluations box carries a longer placeholder ("Procurar formando ou
   curso…"), so it needs more room than the courses box or the text clips. */
.eval-search input[type="search"] { min-width: 22rem; }
.eval-search input[type="search"]:focus,
.filters input[type="search"]:focus { outline: none; border-color: var(--brand); }
.eval-search.is-loading input[type="search"] { opacity: .65; }

/* Data tables (the old .smart tables). Horizontal-scroll on overflow. */
.table-wrap { overflow-x: auto; }
.data-table {
  width: 100%; border-collapse: collapse; font-size: .9375rem;
  background: var(--bg);
}
.data-table caption { text-align: left; color: var(--muted); padding-bottom: var(--sp-2); }
.data-table th, .data-table td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line); text-align: left; vertical-align: top;
}
.data-table thead th {
  background: var(--surface); color: var(--ink);
  font-weight: 700; white-space: nowrap;
}
.data-table tbody tr:hover { background: #fafafa; }
.data-table td.num, .data-table th.num { text-align: right; white-space: nowrap; }
.data-table td.center, .data-table th.center { text-align: center; }

/* Definition-style tables (profile). */
.detail-table { width: 100%; border-collapse: collapse; font-size: .9375rem; max-width: 40rem; }
.detail-table th, .detail-table td {
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line);
  text-align: left; vertical-align: top;
}
.detail-table th { width: 12rem; color: var(--ink); font-weight: 600; background: var(--surface); }

/* Fit-to-content variant (evaluation "Identificação"): on wide screens the table
   shrinks to just the width its content needs, so a long course name stays on one
   line instead of wrapping inside the 40rem cap; it only wraps once the viewport
   gets narrow. */
.detail-table--fit { width: auto; max-width: none; }
.detail-table--fit td { white-space: nowrap; }
@media (max-width: 48rem) {
  .detail-table--fit { width: 100%; }
  .detail-table--fit td { white-space: normal; }
}

/* A muted "nothing here" line. */
.empty-state { color: var(--muted); padding: var(--sp-5) 0; }

/* Highlighted notice box (trainer notifications on the fóruns dashboard). */
.notice {
  margin: 0 0 var(--sp-5); padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--line); border-left: 4px solid var(--info);
  border-radius: var(--radius-sm); background: var(--surface);
}
.notice h2 { margin: 0 0 var(--sp-2); font-size: var(--fs-lg); color: var(--navy); }
.notice__body { margin: 0; }

/* Placeholder card for sections not yet migrated. */
.placeholder-card {
  border: 1px dashed var(--line); border-radius: var(--radius);
  padding: var(--sp-6); background: var(--surface); color: var(--muted);
  max-width: 44rem;
}
.placeholder-card h2 { margin-top: 0; color: var(--navy); }

/* Document / helper link lists. */
.link-list { margin: var(--sp-3) 0; padding-left: var(--sp-5); }
.link-list li { margin: var(--sp-2) 0; }

/* Pedagogical documents (plano de formação / sessão, atividades). */
.doc-section { margin: 0 0 var(--sp-6); }
.rich-text { line-height: 1.6; }
.rich-text > :first-child { margin-top: 0; }
.rich-text > :last-child { margin-bottom: 0; }
.rich-text p { margin: 0 0 var(--sp-3); }
.rich-text ul, .rich-text ol { margin: 0 0 var(--sp-3) var(--sp-5); }
.rich-text table { border-collapse: collapse; margin: var(--sp-3) 0; }
.rich-text th, .rich-text td { border: 1px solid var(--line); padding: var(--sp-2) var(--sp-3); }
.rich-text img { max-width: 100%; height: auto; }

/* Evaluation form/report. */
.eval-state, .eval-attendance { font-weight: 700; color: var(--navy); }
/* align-items: a flex row defaults to `stretch`, so a small inline "Cancelar"
   link next to a button grew to the button's full height with its text pinned
   to the top -- which read as a misaligned button. Centre them on the row. */
.form-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); margin-top: var(--sp-4); }
/* The submit buttons sit inside .auth-form, which stretches .btn to full width.
   In the criteria form we want normal-size buttons on a single left-aligned row. */
.form-actions .btn { width: auto; }

/* Manual / helper hint line (link to a PDF manual). */
.doc-hint { color: var(--muted); font-size: .9375rem; margin: 0 0 var(--sp-4); }

/* Secondary, muted inline link (e.g. "plano de sessão" under an aula title). */
.subtle-link { font-size: .8125rem; color: var(--muted); }

/* Listing filters (server GET forms + the fóruns client-side toggle). */
.filters { margin: 0 0 var(--sp-4); }
.filters fieldset {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: var(--sp-3) var(--sp-4); display: flex; flex-wrap: wrap;
  align-items: center; gap: var(--sp-2) var(--sp-4);
}
.filters legend { padding: 0 var(--sp-2); color: var(--ink); font-weight: 600; font-size: .875rem; }
.filter-field { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3); color: var(--muted); }
.filter-check { display: inline-flex; align-items: center; gap: var(--sp-1); color: var(--text); white-space: nowrap; }
.filters input[type="submit"] {
  padding: var(--sp-2) var(--sp-4); border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--navy); font-weight: 600; cursor: pointer;
}
.filters input[type="submit"]:hover { border-color: var(--brand); color: var(--brand); }
.table-filters { margin: 0 0 var(--sp-3); color: var(--text); }
.table-filters label { display: inline-flex; align-items: center; gap: var(--sp-2); cursor: pointer; }

/* Grelha upload widget (evaluation form). Drag-and-drop zone that POSTs the
   filled grid; the criteria submit buttons stay disabled until it succeeds. */
.grelha-drop {
  border: 2px dashed var(--line); border-radius: var(--radius);
  padding: var(--sp-5); margin: 0 0 var(--sp-3);
  background: var(--surface); text-align: center; max-width: 44rem;
}
.grelha-drop.is-dragover { border-color: var(--brand); background: #f4f8ff; }
.grelha-drop.is-done     { border-color: var(--success, #1a7f37); border-style: solid; }
.grelha-drop.is-error    { border-color: var(--danger, #c1121f); }
.grelha-drop__input { display: block; margin: 0 auto var(--sp-2); }
.grelha-drop__prompt { margin: 0 0 var(--sp-2); color: var(--ink); }
.grelha-drop__browse { color: var(--brand); font-weight: 600; cursor: pointer; }
.grelha-drop__browse:hover { text-decoration: underline; }
.grelha-drop__status { margin: 0; color: var(--muted); font-size: .9375rem; }
.grelha-drop.is-uploading .grelha-drop__status { color: var(--ink); }
.grelha-drop.is-done .grelha-drop__status { color: var(--success, #1a7f37); }
.grelha-drop.is-error .grelha-drop__status { color: var(--danger, #c1121f); }

/* Read-only self-evaluation questionnaire preview (aula/sessão page). Disabled
   inputs; purely for the trainer to consult the questionnaire structure. */
.quiz-preview { border: 0; padding: 0; margin: 0; max-width: 44rem; }
.quiz-question { margin: 0 0 var(--sp-4); }
.quiz-question__text { margin: 0 0 var(--sp-1); font-weight: 600; color: var(--ink); }
.quiz-question__hint { margin: 0 0 var(--sp-2); color: var(--muted); font-size: .875rem; }
.quiz-question__text-input { width: 100%; max-width: 32rem; }
.quiz-option { display: block; color: var(--muted); margin: var(--sp-1) 0; }

/* "Ver aula" button, floated top-right on the plano de sessão page. */
.ver-aula-float { float: right; margin: 0 0 var(--sp-3) var(--sp-4); }

/* Read-only evaluation criteria: the criterion-name header column stays on one
   line on wide screens (wraps only on narrow viewports), and the value column
   shrinks to just the space its content needs. */
.criteria-ro { max-width: none; }
.criteria-ro th { width: auto; white-space: nowrap; }
.criteria-ro td { width: 1%; white-space: nowrap; }
@media (max-width: 48rem) {
  .criteria-ro th { white-space: normal; }
}

/* Table cell helpers: never-wrap, and shrink-to-content (small ids). */
.data-table .nowrap, .nowrap { white-space: nowrap; }
.data-table .col-min { width: 1%; white-space: nowrap; }
/* Override the default thead nowrap for long headers that should wrap. */
.data-table th.wrap { white-space: normal; }

/* A tabela dos ficheiros de uma aula.
   A coluna da data partia-se em duas linhas -- «25-08-2026» numa, «19:01» na
   outra (Rosario, 2026-08-25). Uma data e' uma coisa so' e nao se le' partida.
   `width: 1%` com `nowrap` da'-lhe o minimo que precisa e deixa o resto da
   largura para o nome, que e' o que varia. */
.prp-aula-file-table .prp-aula-file-date,
.prp-aula-file-table .prp-aula-file-actions {
  width: 1%;
  white-space: nowrap;
}

/* O «mudar nome» e' um <details>: sem isto o triangulo do browser aparece ao
   lado de um botao, o que fica com ar de engano. */
.prp-rename > summary { list-style: none; cursor: pointer; }
.prp-rename > summary::-webkit-details-marker { display: none; }

/* O «mudar nome» fica NA MESMA LINHA do nome do ficheiro, a seguir a ele
   (Rosario, 2026-08-25) -- um <details> e' bloco por omissao e caia para
   baixo. A caixa que ele abre volta a ser bloco, senao sai da celula. */
.prp-rename { display: inline-block; margin-left: .5em; }
.prp-rename[open] > .prp-inline-form { display: block; margin-top: .25em; }

/* Os formularios de uma celula nao empurram nada: sem isto, cada <form> e' um
   bloco e a linha da tabela cresce por cada accao que la' esteja. */
.prp-aula-file-table .prp-inline-form { display: inline; margin: 0; }
.prp-aula-file-table .form-actions { display: inline; margin: 0; }

/* EV-230 -- os objetivos da aula sao um grupo de caixas de marcar, e um grupo
   precisa de <fieldset>/<legend> para o leitor de ecra saber que as caixas sao
   todas da mesma pergunta. A moldura que o browser desenha por omissao seria a
   unica caixa numa pagina de campos rasos, por isso fica sem ela e le'-se como
   qualquer outro campo. */
.field-group { border: 0; padding: 0; margin: 0 0 var(--sp-4); min-width: 0; }
.field-group > .field--check { margin-bottom: var(--sp-2); }

/* E, em leitura, a lista do que ele escolheu. */
.prp-aula-objetivos { margin: 0; padding-left: 1.25rem; }

/* Um objetivo que outra aula ja' trabalha continua a poder ser escolhido -- pode
   legitimamente ser trabalhado em mais do que uma -- mas le'-se como tomado, e
   diz por quem. Cinzento e nao desligado: desligar a caixa era decidir pelo
   formador uma coisa que e' dele. */
.field__check-label--tomado { color: var(--muted); }
.objetivo-tomado { margin-left: .4em; font-size: .875rem; font-style: italic; color: var(--muted); }

/* EV-175 -- os pedidos de atualização de uma aula, na linha por baixo dela.

   A sub-linha tem de LER-SE COMO PARTE DA AULA e não como a aula seguinte.
   Sem isto, uma tabela dentro de uma célula de outra tabela desenha-se com o
   mesmo peso da tabela que a contém, e quem passa os olhos conta o dobro das
   aulas. O fundo mais escuro e o recuo são o que diz «isto pertence à linha de
   cima»; o hover é desligado porque a linha não é clicável.

   `border-bottom: 0` na linha da aula em cima não se pode fazer daqui -- em
   CSS não há «o irmão anterior» -- por isso a separação fica ao contrário: a
   sub-linha não tem risca própria e a da aula serve as duas. */
.data-table tr.aula-pedidos > td {
  background: var(--surface);
  padding: var(--sp-3) var(--sp-4) var(--sp-4) var(--sp-5);
}
.data-table tbody tr.aula-pedidos:hover > td { background: var(--surface); }
.aula-pedidos__title {
  margin: 0 0 var(--sp-2); font-size: .8125rem; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.data-table--nested { font-size: .875rem; background: var(--bg); }
.data-table--nested th, .data-table--nested td { padding: var(--sp-2) var(--sp-3); }
.subtle { color: var(--muted); }

/* O histórico de um pedido de atualização: uma linha por passagem. As duas
   primeiras colunas são estreitas de propósito -- a data e o nome têm largura
   fixa, e o resto da mesa é para o texto, que é por que se abre a tabela.

   A mudança de estado vai por baixo do texto, em cinzento: diz o que aconteceu
   à linha sem competir com o que a pessoa escreveu. */
.upr-historico__quando { white-space: nowrap; width: 1%; }
.upr-historico__quem { white-space: nowrap; width: 1%; font-weight: 700; }
.upr-historico__mudanca { margin: var(--sp-1) 0 0; font-size: .8125rem; color: var(--muted); }

/* Os ficheiros de um pedido, dentro da célula da tabela: sem marcas nem recuo,
   porque são uma lista de um ou dois e não um texto com tópicos. */
.upr-files { margin: 0; padding: 0; list-style: none; }
.upr-files li { margin: 0 0 var(--sp-1); }
.upr-files li:last-child { margin-bottom: 0; }
/* Teacher onboarding (registration, the confirm gate, the §2.3 profile form).
 *
 * campus-mojo's FIRST site-local CSS layer. It lives here rather than in the
 * shared assets/2026 kit on purpose: catalog-mojo bundles the same shared
 * layers, and nothing on this page has any business changing that site's bytes.
 * Bundled last, after shared/private.css -- see bin/build-assets.
 *
 * Every selector below is new, so with the feature flag off (no page ever emits
 * these class names) the extra rules are inert.
 */

/* The registration form is a form, not a login box: give it room. */
.auth-card--wide { max-width: 44rem; }

/* forms.css styles input + select; the profile form is the first thing on the
   private side with textareas, so match them to the same box.

   `--bg` and NOT `--surface`: --surface is #f2f2f2, so a textarea came out grey
   while the input directly above it was white -- visible on the aula page,
   where «Título da aula» and «Resumo da aula» sit one under the other. Rosário,
   2026-08-28: «tira o fundo cinza da caixa de resumo». The fix is the token, not
   an override: they are the same kind of box and should read as one. */
.field__textarea {
  width: 100%;
  padding: .55rem .7rem;
  border: 1px solid var(--line, #ddd);
  border-radius: var(--radius, 4px);
  background: var(--bg, #fff);
  color: inherit;
  font: inherit;
  line-height: 1.45;
  resize: vertical;
}

.field__textarea:focus-visible {
  outline: 2px solid var(--focus, #0b5fff);
  outline-offset: 1px;
}

/* The derived completeness panel: lifecycle milestones, never workflow state. */
.onb-progress { margin-bottom: 2rem; }

.onb-milestones {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
}

.onb-milestone {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .5rem;
  padding: .5rem 0;
  border-bottom: 1px solid var(--line, #ddd);
}

.onb-milestone__label { font-weight: 600; }

.onb-milestone__state {
  margin-left: auto;
  font-size: .875rem;
}

.onb-milestone.is-done  .onb-milestone__state { color: var(--ok, #1a7f37); }
.onb-milestone.is-todo  .onb-milestone__state { color: var(--warn, #9a6700); }

/* "em falta", beside the label of a field the completeness rules count as
   missing. THE WORDS CARRY THE MEANING and the colour reinforces it: a marker
   that is only red is invisible to a colour-blind teacher and silent to a
   screen reader, and this is a form for trainers, not a decorative badge. */
.field__missing {
  color: var(--err, #b42318);
  font-size: .8125rem;
  font-weight: 600;
  white-space: nowrap;
}

/* The edit form itself -- same rhythm as the rest of the private area. */
.onb-form { max-width: 44rem; }
.onb-form .section-title { margin-top: 2rem; }

/* "Áreas de formação" find-as-you-type. The box sits directly above the select
   it filters, tight against it, so the two read as one control. */
.areas-find { margin-bottom: .375rem; }
.areas-find__count { margin-top: .25rem; }

/* O editor de HTML das sete secções públicas (EV-247). O SunEditor traz a folha
   dele -- carregada só nesta página, ver perfil_editar.html.tx -- e aqui só se
   lhe diz que ocupa a largura do campo, como qualquer outro controlo do
   formulário. A <textarea> por baixo é escondida por ele. */
.field--html .sun-editor {
  width: 100%;
  border-radius: var(--radius, 4px);
}

/* O QUE ELE ESCREVE TEM DE SE PARECER COM O QUE VAI SAIR. O SunEditor traz a
   sua própria caixa de escrita, com serifas e uma altura de linha que não são as
   do sítio -- escrever em Times e ver o perfil publicado em Helvetica é o género
   de surpresa que faz um formador voltar para corrigir o que estava bem. Os
   mesmos valores da página pública (.rich-text, em private.css). */
.field--html .sun-editor .sun-editor-editable {
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.6;
  color: inherit;
}
/* Teacher course proposals (§2.4 propor curso, the proposal list, the detail
 * page with its state panel, the visible annotations and the contract exchange).
 *
 * A campus-mojo SITE-LOCAL layer, like onboarding.css and for the same reason:
 * catalog-mojo bundles the same shared assets/2026 layers and nothing on these
 * pages has any business changing that site's bytes. Bundled last, after
 * onboarding.css -- see bin/build-assets.
 *
 */

/* The proposal forms follow the same rhythm as the profile form. */
.prp-form { max-width: 44rem; }
.prp-form .section-title { margin-top: 2rem; }

.prp-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin: 0 0 1.5rem;
}

/* O botao de uma tab, encostado a' direita (Rosario, 2026-08-27: «coloca os
   botoes nas tabs respetivas, sempre do lado direito, a laranja»).

   `flex-end` e nao `float`/`text-align`: .prp-actions ja' e' um flex container e
   um dia levara' dois botoes -- justify-content mantem-nos juntos a' direita,
   pela ordem em que estao escritos, e o `flex-wrap` continua a valer no telemovel. */
.prp-actions--end { justify-content: flex-end; }

/* On «Início» the button sits directly under the "As minhas propostas de curso"
   heading, and the shared rule above has no top margin -- so the two collided.
   Rosário, 2026-08-05: «Dá um espaço entre "as minhas propostas de curso" e o
   botão "propor um curso"». A margin on the actions rather than on the heading:
   the heading's spacing is the page rhythm and is not this block's to change. */
.painel-bloco__actions { margin-top: 1rem; }

/* The state, in the teacher's words. `--mine` marks the states whose next step
   belongs to the teacher, which is the one thing the list has to make obvious. */
.prp-state {
  display: inline-block;
  font-weight: 600;
}

.prp-state--mine { color: var(--warn, #9a6700); }

.prp-state--big {
  font-size: 1.25rem;
  margin: 0 0 .25rem;
}

.prp-state-panel { margin-bottom: 2rem; }

/* A instrução que o formador tem de ler ANTES de continuar -- hoje, a
   confirmação dos dados do contrato. Rosário, 2026-08-31: «deve aparecer em
   destaque a mensagem "Antes de prepararmos o seu contrato…"».

   Não é `.doc-hint`: essa é cinzenta e mais pequena que o texto da página, que é
   a voz certa para uma nota de rodapé e a errada para o passo seguinte de quem
   está a ler. O desenho é o da `.prp-note` -- a caixa que esta área já usa para
   o que vem da equipa -- com a cor do texto normal, porque isto é para ler. */
.prp-destaque {
  margin: 0 0 var(--sp-4);
  padding: 1rem 1.25rem;
  border-left: 3px solid var(--brand, #0b5fff);
  background: var(--surface-alt, #f6f8fa);
  font-size: 1.0625rem;
  font-weight: 600;
}

/* Annotations the team marked as visible to the teacher (§6.3). The internal
   ones are never rendered and never fetched, so they have no styling either. */
.prp-note {
  margin: 0 0 2rem;
  padding: 1rem 1.25rem;
  border-left: 3px solid var(--brand, #0b5fff);
  background: var(--surface-alt, #f6f8fa);
}

.prp-note .section-title { margin-top: 0; }

/* A caixa das notas é a mesma nas duas páginas -- proposta e aula -- por decisão
   da Rosário, 2026-08-28: «a formatação do comentário da equipa … deve ser igual
   à formatação que temos às anotações visiveis ao formador da proposta de
   curso». Por isso não há regra própria para `__entry`: o desenho é o de
   `.prp-note`, e a página da aula passou a usar a mesma estrutura. */
.prp-note__meta { margin: 0 0 .35rem; font-size: .8125rem; color: var(--muted); }

/* «Os Meus Cursos» empilha três blocos -- pedidos de atualização, propostas e
   cursos -- e sem isto a tabela de um encosta ao título do seguinte. Rosário,
   2026-08-26: «dá mais espaço entre o fim da tabela … e "as minhas propostas de
   curso". está muito em cima da tabela.» */
.painel-bloco + .painel-bloco { margin-top: 2.5rem; }

/* ---- separadores da página da proposta ---------------------------------- */
/* O mesmo desenho do tabset da gestão, com os tokens desta área. A folha é a
 * gémea do sites/campus-mojo/public/assets/tabs.21dda40257.js: o `x-cloak` que impede o flash
 * está no base.css, porque tem de valer para a página toda e não só aqui. */

.prp-tabset {
  margin-bottom: var(--sp-5);
}

.prp-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  margin: 0 0 -1px;
  padding: 0;
  list-style: none;
  border-bottom: 1px solid var(--line);
}

.prp-tab {
  font: inherit;
  cursor: pointer;
  padding: .5em 1em;
  border: 1px solid var(--line);
  border-bottom: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: var(--bg-soft, #f6f6f6);
  color: var(--text);
}

.prp-tab:hover { color: var(--brand); }

.prp-tab[aria-selected="true"] {
  background: var(--bg);
  font-weight: 600;
  position: relative;
  z-index: 1;
}

.prp-panel {
  border: 1px solid var(--line);
  border-top: none;
  padding: var(--sp-5);
}

/* ---- barra fixa --------------------------------------- */

.prp-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  margin: 0 0 var(--sp-5);
  padding: var(--sp-3) 0;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}

.prp-bar__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-3);
}

.prp-bar__nome { margin: 0; font-weight: 600; }

.prp-bar__estado {
  margin: 0;
  padding: .1em .6em;
  border-radius: var(--radius-sm);
  background: var(--bg-soft, #f0f0f0);
  font-size: .9375rem;
}

/* «de quem é a vez» é a única distinção que a barra faz, e é a que o formador
 * precisa de ver sem ler: laranja quando a bola está com ele. */
.prp-bar__estado--mine { background: #fdf0d5; font-weight: 600; }

.prp-bar__ajuda { margin-left: auto; }


/* o percurso do primeiro separador */
.prp-percurso { margin: 0 0 var(--sp-4); padding-left: 1.4em; }
.prp-percurso li { margin-bottom: var(--sp-2); }


/* A tabela-resumo das aulas (Rosário, 2026-08-27). Substituiu dez blocos
 * abertos ao mesmo tempo: aqui vê-se de relance em que pé está cada uma, e
 * escolhem-se várias para enviar de uma vez. O trabalho de cada aula é na
 * página dela. */
.prp-aulas-table { width: 100%; margin-bottom: var(--sp-4); }

/* As duas colunas estreitas encolhem ao conteúdo para o título ficar com o
 * resto -- é o título que se lê a percorrer a lista. */
.prp-aulas-table__pick,
.prp-aulas-table__fich,
.prp-aulas-table__n { width: 1%; white-space: nowrap; }

/* Números à direita, para se lerem em coluna. */
.prp-aulas-table__fich,
.prp-aulas-table__n { text-align: right; }

.prp-aulas-table__estado {
  width: 1%;
  white-space: nowrap;
  padding: .1em .6em;
  border-radius: var(--radius-sm);
  background: var(--bg-soft, #f0f0f0);
  font-size: .9375rem;
}

/* A mesma laranja da barra fixa, e pela mesma razão: «de quem é a vez» é a
 * única coisa que se tem de ver sem ler. */
.prp-aulas-table__estado--mine { background: #fdf0d5; font-weight: 600; }

/* O bloco de enviar/pedir de volta, na página de uma aula. Separado por uma
 * linha do que está acima porque é uma acção de outro peso: guardar faz-se dez
 * vezes, enviar faz-se uma e tira a aula das mãos de quem a escreveu. */
.prp-aula-enviar {
  margin-top: var(--sp-5);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line, #ddd);
}


/* «Adicionar aula» é a acção que a página oferece a quem chega ao separador, e é
 * onde a frase acima o deixa -- por isso é destacado, e não mais um botão
 * neutro entre outros (Rosário, 2026-08-27). O espaço por baixo separa-o da
 * tabela: colado a ela lia-se como um cabeçalho dela e não como uma acção. */
.prp-aula-add { margin-bottom: var(--sp-5); }
/* Avaliações -- the trainer's evaluation screen.
 *
 * The team's note leads the page, above Estado (melo, 2026-09-15): it is the one
 * thing there a person wrote FOR this teacher, and everything under it is the
 * system reporting facts.
 *
 * `.notice` in the shared kit carries a BOTTOM margin and no top, which is right
 * where a notice follows a heading mid-page. Leading the page it has nothing
 * above it to inherit space from, so it sat flush against "Voltar às
 * avaliações". This gives it the top margin the position needs, without
 * changing a component catalog-mojo bundles too. */
.notice--lead { margin-top: var(--sp-5); }

/* "Novo" -- shown only the first time this teacher opens this note, decided
 * before the visit is recorded (see EC::Campus::Controller::Avaliacoes).
 * Floated so it sits in the box's top-right corner without taking a line of its
 * own or pushing the heading down. */
.notice__flag {
  float: right;
  margin-left: var(--sp-3);
  padding: 0.1rem 0.5rem;
  border-radius: var(--radius-sm);
  background: var(--info);
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* ==========================================================================
   Fórum -- /campus/forum
   ==========================================================================

   Translated from the three reference pages on design.evolui.com (ver-forum,
   ler-responder-forum, criar-topico-forum). Those are Tailwind + DaisyUI; none
   of that is in this repo, so what came across is the STRUCTURE -- avatar +
   title + "X respondeu há N" + reply count per row, article/header/body/actions
   per message, the collapsed "Responder…" box that expands into the editor --
   rebuilt on the 2026 kit's own tokens.

   Message bodies reuse `.rich-text` from private.css, which already styles
   tables, lists and images inside user HTML. Nothing here restyles it.
   ========================================================================== */

/* --- Breadcrumbs -------------------------------------------------------- */

.breadcrumbs { margin: 0 0 var(--sp-4); font-size: .875rem; }

.breadcrumbs__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--muted);
}

.breadcrumbs__item + .breadcrumbs__item::before {
  content: "/";
  margin-right: var(--sp-2);
  color: var(--line);
}

.breadcrumbs a { color: var(--navy); }
.breadcrumbs a:hover { color: var(--brand); }

/* --- Avatars ------------------------------------------------------------
   Nobody has an uploaded picture yet, so the initials placeholder is the
   normal case, not the fallback. Both shapes are the same size so a list of
   mixed rows does not jitter.
   ----------------------------------------------------------------------- */

.avatar {
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface);
}

.avatar--sm { width: 2rem; height: 2rem; }

.avatar--initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--navy);
  background: var(--surface);
  font-weight: 700;
  font-size: .8125rem;
  letter-spacing: .02em;
  text-transform: uppercase;
}

/* The trainer and the EVOLUI team are the two voices a student looks for
   first, so they get the brand colour rather than the neutral one. */
.avatar--teacher { background: #ffe6d9; color: var(--brand-strong); }
.avatar--staff   { background: #dceaf2; color: var(--navy); }

/* --- The topic list ----------------------------------------------------- */

.topic-list { margin: 0; padding: 0; list-style: none; }

.topic-list__item + .topic-list__item { border-top: 1px solid var(--line); }

.topic-row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-3);
  color: inherit;
}

.topic-row:hover { background: #fafafa; text-decoration: none; }

.topic-row__main { flex: 1 1 auto; min-width: 0; }

.topic-row__title {
  display: block;
  font-size: .875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .01em;
  color: var(--navy);
  overflow-wrap: anywhere;
}

.topic-row__meta {
  display: block;
  margin-top: var(--sp-1);
  font-size: .875rem;
  color: var(--muted);
}

.topic-row__meta strong { color: var(--ink); font-weight: 600; }

.topic-row__count {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  color: var(--muted);
  font-size: .875rem;
  font-variant-numeric: tabular-nums;
}

.topic-row__count svg { width: 1.25rem; height: 1.25rem; }

/* A topic the reader has already caught up on recedes; the ones with unread
   messages stay at full weight. This is the whole reason the list is worth
   reading top to bottom. */
.topic-list__item--seen .topic-row { opacity: .7; }

.topic-row__unread {
  flex: 0 0 auto;
  min-width: 1.5rem;
  padding: 0 var(--sp-2);
  border-radius: 999px;
  background: var(--brand);
  color: #fff;
  font-size: .75rem;
  font-weight: 700;
  line-height: 1.5rem;
  text-align: center;
}

/* --- Messages ----------------------------------------------------------- */

.message-list { max-width: 44rem; }

.message {
  padding: var(--sp-4) 0;
  scroll-margin-top: var(--sp-6);
}

.message + .message { border-top: 1px solid var(--line); }

.message__header {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  font-size: .875rem;
  color: var(--muted);
}

.message__author {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--ink);
  font-weight: 700;
}

/* The reader needs to know, at a glance, whether an answer came from their
   trainer or from another student. */
.message__role {
  padding: 0 var(--sp-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--muted);
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
}

.message__role--teacher { background: #ffe6d9; color: var(--brand-strong); }
.message__role--staff   { background: #dceaf2; color: var(--navy); }

/* staff seat only: who really wrote a post made while assumed */
.message__ghost { color: var(--muted); font-weight: 400; font-size: .875em; }

/* Actions reveal on hover on a pointer device -- but they must be permanently
   visible to keyboard and touch users, who have no hover to give. */
.message__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-2);
}

@media (hover: hover) and (pointer: fine) {
  .message__actions { opacity: 0; transition: opacity .15s; }
  .message:hover .message__actions,
  .message:focus-within .message__actions { opacity: 1; }
}

/* The student being assessed, while a trainer reads the forum in read-only
   mode from the evaluation screen. */
.message--highlight {
  background: #fffbf0;
  box-shadow: inset 3px 0 0 var(--warn);
  padding-left: var(--sp-4);
}

/* --- Reply status, for trainers and staff only --------------------------
   The five states of the SLA machine. Students never see these.
   ----------------------------------------------------------------------- */

.reply-status {
  display: inline-block;
  padding: 0 var(--sp-2);
  border-radius: var(--radius-sm);
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
}

.reply-status--pending        { background: #ffe9e9; color: var(--err); }
.reply-status--done           { background: #e6f6ee; color: var(--ok); }
.reply-status--not_needed     { background: var(--surface); color: var(--muted); }
.reply-status--ask_not_needed { background: #fff3e0; color: var(--warn); }
.reply-status--for_evolui     { background: #e7effa; color: var(--info); }

/* --- The composer -------------------------------------------------------
   ONE component. The new-topic page and the inline reply render the same
   partial, so the two never drift -- which is exactly what happened to the
   old forum, where "novo tópico" and "responder" were separate templates
   wrapping the same editor with different buttons.
   ----------------------------------------------------------------------- */

.composer { margin-top: var(--sp-5); max-width: 44rem; }

.composer__prompt {
  width: 100%;
  padding: var(--sp-4);
  border: 2px dashed var(--line);
  border-radius: var(--radius);
  background: none;
  color: var(--muted);
  text-align: left;
  font: inherit;
  cursor: pointer;
}

.composer__prompt:hover,
.composer__prompt:focus { border-color: var(--brand); color: var(--ink); }

/* Without JS the prompt never appears and the form is simply open: the forum
   has to work for somebody whose editor failed to load. */
.composer__form[hidden] { display: none; }

.composer__field + .composer__field { margin-top: var(--sp-4); }

.composer__hint { color: var(--muted); font-size: .8125rem; margin: var(--sp-1) 0 0; }

/* The attachment field. A bare <input type="file"> is a browser widget we do not
   get to restyle, so this only gives it the same box and rhythm as the rest of
   the form instead of letting it sit flush against the label. */
.composer__file {
  display: block;
  width: 100%;
  max-width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font: inherit;
  font-size: .875rem;
}

.composer__file:focus { border-color: var(--brand); outline: none; }

/* Hidden until the browser-side size guard has something to say. */
.composer__field .field__error[hidden] { display: none; }

.composer__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}

/* SunEditor sits on top of a real <textarea> that carries the name and is what
   posts. Give the editor the same rounded box as the rest of the form so it
   does not look bolted on. */
.composer .sun-editor {
  --se-main-font-family: var(--font-sans);
  border-radius: var(--radius);
  border-color: var(--line);
}

/* What is typed looks exactly like what is published: the editable area takes
   the page's font and .rich-text's size, colour, line height and paragraph
   spacing instead of SunEditor's own, and so does the placeholder. */
.composer .sun-editor-editable {
  --se-content-font-family: var(--font-sans);
  --se-edit-font-size: var(--fs-base);
  --se-edit-font-color: var(--text);
  --se-edit-line-height: 1.6;
}

.composer .sun-editor-editable p { margin: 0 0 var(--sp-3); }
.composer .sun-editor .se-wrapper .se-placeholder { font-size: var(--fs-base); line-height: 1.6; }

/* --- Attachments -------------------------------------------------------- */

.attachments { margin: var(--sp-3) 0 0; padding: 0; list-style: none; }

.attachments__item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-right: var(--sp-3);
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: .875rem;
}

.attachments__item a { color: var(--navy); }
.attachments__size { color: var(--muted); }

/* --- Pagination ---------------------------------------------------------
   The old forum had none, over 3.5M messages.
   ----------------------------------------------------------------------- */

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  margin: var(--sp-6) 0 0;
  font-size: .875rem;
}

.pager__link {
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--navy);
}

.pager__link:hover { border-color: var(--brand); color: var(--brand); text-decoration: none; }
.pager__current { color: var(--muted); font-variant-numeric: tabular-nums; }

/* --- The pieces the three pages hang the above on ----------------------- */

/* «12 tópicos. 3 por ler.» above the list. */
.forum-summary { margin: 0 0 var(--sp-4); color: var(--muted); font-size: .875rem; }
.forum-summary strong { color: var(--brand-strong); }

/* The new-topic page is one column: the composer IS the page. */
.forum-novo { max-width: 44rem; }

.message__when { white-space: nowrap; }

/* Each SLA transition is its own little POST form (a state change may not be a
   GET), so the row of them has to lay out as if they were plain buttons. */
.message__actions form { margin: 0; }
/* Error pages rendered inside a site's own chrome (errors/chrome.html.tx).
   The 5xx page does not use this file -- it carries its own <style> so it can
   render when the asset pipeline is the thing that is broken. */
/* Capped measure: the container is as wide as the viewport on a desktop, and a
   single line of apology running the full 1280px reads as a mistake. */
.error-page { max-width: 44rem; padding-block: var(--sp-6) var(--sp-8); }
.error-page__title { color: var(--brand); font-size: var(--fs-xl); font-weight: 400; margin: 0 0 var(--sp-4); }
.error-page__lead { margin: 0 0 var(--sp-4); }
.error-page__links { margin: 0; padding-left: var(--sp-5); }
.error-page__links li { margin-bottom: var(--sp-1); }
.error-page__ref {
  margin-top: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: .8125rem;
}
