/* 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--sm { min-height: 36px; padding: var(--sp-1) var(--sp-3); }

/* 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.
   ========================================================================== */

.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 -------------------------- */

.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 ------------------------------------------------------- */

.coupon { display: flex; gap: var(--space-2, 0.5rem); align-items: flex-end; flex-wrap: wrap; }
.coupon .field { flex: 1 1 12rem; margin: 0; }

/* --- 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); }
.checkout__actions--split { justify-content: space-between; }

/* --- 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; }

.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; }
/* 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. */
.field__textarea {
  width: 100%;
  padding: .55rem .7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius, 4px);
  background: var(--surface, #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(--border);
}

.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; }
/* 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;
}

/* 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; }

/* 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; }

/* «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/js/tabs.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); }
