/* ===========================================================================
   CAMADA 4 - COMPONENTES
   Todo componente reaproveitavel do produto. Nenhuma pagina redeclara nada
   daqui.

   MECANISMO CONTRA DUPLICACAO: cada componente expoe custom properties
   locais (--btn-bg, --card-padding, --alert-fg). Os modificadores SO
   reatribuem essas variaveis, nunca repetem declaracao de layout. Por isso
   `.ipr-btn` tem ~15 declaracoes escritas uma vez e `.ipr-btn--danger` tem
   duas linhas.

   Toda variavel tem VALOR PADRAO no bloco base. Sem isso, um `.ipr-btn` sem
   modificador (ou um alerta cuja categoria nao bateu no mapa) resolveria para
   "invalido no tempo de computacao": o botao ficaria transparente e o texto
   herdaria a cor do card. Falha silenciosa e o pior modo de falha.
   =========================================================================== */

/* --------------------------------------------------------------- BOTAO --- */
.ipr-btn {
  /* Padroes: um botao sem modificador continua legivel. */
  --btn-bg: var(--color-surface-2);
  --btn-fg: var(--color-text-primary);
  --btn-border: transparent;
  --btn-height: 2.75rem;
  --btn-padding-inline: var(--space-5);
  --btn-shadow: none;

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: var(--btn-height);
  padding-inline: var(--btn-padding-inline);
  border: 1px solid var(--btn-border);
  border-radius: var(--radius-md);
  background: var(--btn-bg);
  color: var(--btn-fg);
  box-shadow: var(--btn-shadow);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur-fast), box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-fast);
}

.ipr-btn--primary {
  --btn-bg: var(--gradient-accent);
  --btn-fg: var(--color-text-on-accent);
  --btn-shadow: var(--shadow-sm), var(--shadow-highlight);
}
/* O hover ESCURECE. Clarear o azul reduziria o contraste do texto branco
   justamente no estado em que o usuario esta prestes a clicar; a sensacao de
   "acendeu" vem do brilho externo, nao da cor de fundo. */
.ipr-btn--primary:hover {
  --btn-bg: var(--gradient-accent-hover);
  --btn-shadow: var(--shadow-accent-glow), inset 0 0 0 1px rgba(140, 190, 255, .35);
}
.ipr-btn--primary:active { transform: translateY(1px); }

/* Reflexo cruzando o botao no hover. Reaproveita `ipr-sweep`, o mesmo
   keyframe da luz que atravessa a cortina de abertura: um movimento de luz
   escrito uma vez, usado em duas escalas.

   `overflow: hidden` prende o reflexo dentro do raio do botao. Nao afeta o
   anel de foco, que e outline e desenha por fora da caixa. */
.ipr-btn--primary { overflow: hidden; }
.ipr-btn--primary::after {
  content: "";
  position: absolute;
  inset-block: -50%;
  inset-inline-start: 0;
  inline-size: 40%;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--color-sheen-edge), transparent);
  /* Estacionado fora da vista. Sem isto o reflexo ficaria parado no meio do
     botao enquanto ninguem passa o mouse. */
  transform: translateX(-160%) skewX(-16deg);
}
.ipr-btn--primary:hover::after { animation: ipr-sweep 700ms var(--ease-in-out); }

.ipr-btn--secondary { --btn-bg: var(--color-surface-2); --btn-border: var(--color-border-strong); }
.ipr-btn--secondary:hover { --btn-bg: var(--color-surface-3); }

.ipr-btn--ghost { --btn-bg: transparent; --btn-fg: var(--color-text-secondary); }
.ipr-btn--ghost:hover { --btn-bg: var(--color-surface-2); --btn-fg: var(--color-text-primary); }

.ipr-btn--danger { --btn-bg: var(--color-danger-solid); --btn-fg: var(--color-danger-solid-fg); }

.ipr-btn--sm    { --btn-height: 2.25rem; --btn-padding-inline: var(--space-4); font-size: var(--font-size-xs); }
.ipr-btn--lg    { --btn-height: 3rem;    --btn-padding-inline: var(--space-6); font-size: var(--font-size-md); }
.ipr-btn--block { width: 100%; }

.ipr-btn:disabled,
.ipr-btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; pointer-events: none; }

/* Carregando: o rotulo fica invisivel mas continua ocupando espaco, entao o
   botao NAO encolhe e o layout nao pula. */
.ipr-btn.is-loading { pointer-events: none; }
.ipr-btn.is-loading .ipr-btn__label { opacity: 0; }
.ipr-btn.is-loading .ipr-btn__spinner { display: block; position: absolute; }

/* ------------------------------------------------------------- SPINNER --- */
/* Usa currentColor, entao serve em qualquer variante de botao sem versao propria. */
.ipr-spinner {
  width: var(--spinner-size, 1rem);
  aspect-ratio: 1;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: var(--radius-pill);
  animation: ipr-spin .6s linear infinite;
  display: none;
}

/* ---------------------------------------------------------------- CAMPO --- */
.ipr-field { display: flex; flex-direction: column; gap: var(--space-2); }

.ipr-field__label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
}
.ipr-field__required { color: var(--color-accent-400); margin-inline-start: 2px; }

.ipr-field__control { position: relative; display: flex; align-items: center; }

.ipr-field__icon {
  position: absolute;
  inset-inline-start: var(--space-4);
  display: grid;
  color: var(--color-text-muted);
  pointer-events: none;
  transition: color var(--dur-base);
}

.ipr-field__input {
  width: 100%;
  height: var(--field-height, 2.875rem);
  padding-inline: var(--space-4);
  background: var(--color-input-bg);
  color: var(--color-text-primary);
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-md);
  transition: border-color var(--dur-base), box-shadow var(--dur-base);
}
.ipr-field__input::placeholder { color: var(--color-text-muted); }
.ipr-field--with-icon   .ipr-field__input { padding-inline-start: 2.875rem; }
.ipr-field--with-action .ipr-field__input { padding-inline-end: 3rem; }

.ipr-field__input:focus-visible {
  border-color: var(--color-focus);
  box-shadow: var(--shadow-focus);
  outline: none;
}
/* O icone acende junto com o foco: detalhe barato que faz parecer produto. */
.ipr-field__control:focus-within .ipr-field__icon { color: var(--color-accent-400); }

.ipr-field__action {
  position: absolute;
  inset-inline-end: var(--space-2);
  width: 2.25rem;
  height: 2.25rem;
  display: grid;
  place-items: center;
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.ipr-field__action:hover { color: var(--color-text-primary); background: var(--color-surface-2); }

.ipr-field__hint { font-size: var(--font-size-xs); color: var(--color-text-muted); }

/* O erro sempre vem acompanhado do icone: cor nunca e o unico portador da
   informacao (daltonismo). */
.ipr-field__error {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-xs);
  color: var(--color-danger);
}
.ipr-field--invalid .ipr-field__input {
  border-color: var(--color-danger);
  box-shadow: var(--shadow-focus-danger);
}
.ipr-field--invalid .ipr-field__input:focus-visible { animation: ipr-shake 320ms var(--ease-in-out); }

/* ------------------------------------------------------------- CHECKBOX --- */
.ipr-checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  cursor: pointer;
  font-size: var(--font-size-sm);
  color: var(--color-text-secondary);
}
/* O input real fica invisivel mas focavel e legivel por leitor de tela. */
.ipr-checkbox__input { position: absolute; opacity: 0; width: 1.125rem; height: 1.125rem; margin: 0; }
.ipr-checkbox__box {
  width: 1.125rem;
  height: 1.125rem;
  flex-shrink: 0;
  border: 1px solid var(--color-border-control);
  border-radius: var(--radius-xs);
  background: var(--color-input-bg);
  display: grid;
  place-items: center;
  transition: background var(--dur-fast), border-color var(--dur-fast);
}
.ipr-checkbox__box::after {
  content: "";
  width: .3rem;
  height: .55rem;
  border: 2px solid #FFF;
  border-top: 0;
  border-left: 0;
  transform: rotate(45deg) translateY(-1px);
  opacity: 0;
  transition: opacity var(--dur-fast);
}
.ipr-checkbox__input:checked ~ .ipr-checkbox__box {
  background: var(--color-accent-500);
  border-color: var(--color-accent-400);
}
.ipr-checkbox__input:checked ~ .ipr-checkbox__box::after { opacity: 1; }
.ipr-checkbox__input:focus-visible ~ .ipr-checkbox__box {
  outline: var(--ring-width) solid var(--color-focus);
  outline-offset: var(--ring-offset);
}

/* ----------------------------------------------------------------- CARD --- */
.ipr-card {
  --card-padding: clamp(1.5rem, 4vw, 2.5rem);
  --card-bg: var(--color-surface-1);
  --card-border: var(--color-border);
  --card-shadow: var(--shadow-md), var(--shadow-highlight);
  --card-radius: var(--radius-xl);

  position: relative;
  padding: var(--card-padding);
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
}
.ipr-card__title    { font-size: var(--font-size-xl); }
.ipr-card__subtitle { color: var(--color-text-secondary); font-size: var(--font-size-sm); }

.ipr-card--elevated { --card-shadow: var(--shadow-xl), var(--shadow-highlight); }
/* Card "sem casca": usado no celular, onde o formulario flutua sobre o fundo
   em vez de virar card dentro de card. Fica AQUI, e nao no CSS da pagina,
   porque a proxima tela de credencial vai precisar do mesmo comportamento. */
.ipr-card--bare {
  --card-bg: transparent;
  --card-border: transparent;
  --card-shadow: none;
  --card-radius: 0;
  --card-padding: 0;
}
/* Vidro. So compensa onde existe algo INTERESSANTE atras: no login, o painel
   tem brilho e malha por baixo. Sobre fundo chapado o desfoque nao produz
   nada e o unico efeito seria o cartao ficar mais escuro. */
.ipr-card--glass {
  --card-bg: var(--color-surface-glass);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
          backdrop-filter: blur(22px) saturate(150%);
}

.ipr-card--accent { overflow: hidden; }
.ipr-card--accent::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 2px;
  background: var(--gradient-hairline);
}
.ipr-card--bare.ipr-card--accent::before { display: none; }

/* --------------------------------------------------------------- ALERTA --- */
.ipr-alert {
  /* Padrao = informativo. Uma categoria desconhecida vinda do Python cai
     aqui em vez de renderizar um alerta sem cor nenhuma. */
  --alert-bg: var(--color-info-bg);
  --alert-fg: var(--color-info-fg);
  --alert-border: var(--color-info-border);
  --alert-accent: var(--color-info);

  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--alert-border);
  border-radius: var(--radius-lg);
  background: var(--alert-bg);
  color: var(--alert-fg);
  font-size: var(--font-size-sm);
}
.ipr-alert__icon  { color: var(--alert-accent); flex-shrink: 0; display: grid; }
.ipr-alert__text  { flex: 1; }
.ipr-alert__close {
  border: 0;
  background: transparent;
  color: inherit;
  opacity: .6;
  cursor: pointer;
  padding: 0;
  display: grid;
}
.ipr-alert__close:hover { opacity: 1; }

.ipr-alert--success { --alert-bg: var(--color-success-bg); --alert-fg: var(--color-success-fg); --alert-border: var(--color-success-border); --alert-accent: var(--color-success); }
.ipr-alert--danger  { --alert-bg: var(--color-danger-bg);  --alert-fg: var(--color-danger-fg);  --alert-border: var(--color-danger-border);  --alert-accent: var(--color-danger); }
.ipr-alert--warning { --alert-bg: var(--color-warning-bg); --alert-fg: var(--color-warning-fg); --alert-border: var(--color-warning-border); --alert-accent: var(--color-warning); }
.ipr-alert--info    { --alert-bg: var(--color-info-bg);    --alert-fg: var(--color-info-fg);    --alert-border: var(--color-info-border);    --alert-accent: var(--color-info); }

.ipr-alert.is-dismissing {
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity var(--dur-base), transform var(--dur-base);
}

/* ---------------------------------------------------------------- MARCA --- */
/*
  O LOCKUP OFICIAL, nao uma reconstrucao dele.

  A palavra "iPresto Pro" e imagem, recortada do proprio arquivo de
  identidade. A versao anterior desenhava a palavra com texto e um "Pro" em
  pilula: nenhuma fonte de sistema tem o desenho arredondado do logo, entao
  aquilo nunca foi a marca, era uma imitacao dela.

  Tudo deriva de --brand-size, entao existe UM numero por tamanho e as
  proporcoes do logo se mantem em qualquer escala.
*/
.ipr-brand {
  display: inline-flex;
  align-items: center;
  /* 0.15 e o vao do arquivo original (91px de folga para 625px de marca).
     Copiar a medida do logo e o que impede o conjunto de parecer duas
     imagens encostadas. */
  gap: calc(var(--brand-size, 2.75rem) * .15);
}
.ipr-brand__mark { width: var(--brand-size, 2.75rem); height: auto; }
.ipr-brand__word {
  block-size: var(--brand-size, 2.75rem);   /* mesma altura da marca, como no logo */
  inline-size: auto;
  /* Guarda contra deformacao: `img { max-width: 100% }` (base.css) encolheria
     a LARGURA num container estreito enquanto a altura acima continua fixa, e
     o resultado seria o logo esticado. `contain` prefere mostrar menor a
     mostrar deformado. Logo esticado e o tipo de erro que ninguem reporta e
     todo mundo nota. */
  object-fit: contain;
  /* No lockup oficial a palavra fica 4,3% da altura da marca ACIMA do centro
     dela. Nao e imprecisao de recorte: e o alinhamento optico do proprio
     logo, medido no arquivo. Sem isto o conjunto fica "certo" e parece
     errado. */
  translate: 0 calc(var(--brand-size, 2.75rem) * -.043);
}
.ipr-brand--sm { --brand-size: 2.25rem; }
.ipr-brand--lg { --brand-size: 3.5rem; }
/*
  MODIFICADOR DE APRESENTACAO: a marca em tamanho de abertura, encenando a
  propria entrada.

  A coreografia mora AQUI, e nao no CSS da cortina, porque quem entra em cena
  e a marca. Qualquer tela futura que precise anunciar o produto (uma splash,
  um "sobre", a tela de primeiro acesso) chama o macro brand com size='display'
  e recebe a mesma entrada sem escrever CSS nenhum.

  Os dois aneis de pulso sao ::before e ::after do proprio elemento: um <img>
  nao aceita pseudo-elemento, entao eles pertencem a marca inteira e ficam
  ancorados no topo, que e onde a imagem esta.
*/
.ipr-brand--display {
  --brand-size: clamp(5rem, 13vw, 7rem);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-5);
}
.ipr-brand--display .ipr-brand__mark {
  animation: ipr-focus-in var(--dur-cinema) var(--ease-expo) 100ms both;
}
.ipr-brand--display .ipr-brand__word {
  /* Empilhada, a palavra na altura cheia da marca dominaria a composicao.
     0.55 devolve a marca o papel de protagonista. */
  block-size: calc(var(--brand-size) * .55);
  /* O ajuste optico existe para o lockup LADO A LADO. Empilhado, ele so
     desalinharia. */
  translate: none;
  animation: ipr-focus-in var(--dur-cinema) var(--ease-expo) 620ms both;
}
.ipr-brand--display::before,
.ipr-brand--display::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 50%;
  inline-size: var(--brand-size);
  block-size: var(--brand-size);
  border: 1px solid var(--color-accent-400);
  border-radius: 50%;
  /* `translate` e propriedade independente do `transform`: centraliza aqui e
     deixa o transform inteiro livre para o keyframe escalar o anel, sem ter
     que repetir a centralizacao dentro de cada quadro. */
  translate: -50% 0;
  animation: ipr-halo 1500ms var(--ease-out) var(--halo-delay, 520ms) both;
}
/* O segundo anel e o primeiro atrasado: dois atrasos, nao duas animacoes. */
.ipr-brand--display::after { --halo-delay: 820ms; }

/* ------------------------------------------------------------ AUXILIARES --- */
.ipr-eyebrow {
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
/* Sobretitulo vazio some por completo. Sem isto, a tela que nao tem
   sobretitulo (o login) ainda pagaria o `gap` do .ipr-stack em volta de um
   paragrafo em branco, e o cabecalho do cartao ficaria com um vao sem
   explicacao. `display: none` tira o elemento do fluxo flex, entao o gap
   tambem desaparece. */
.ipr-eyebrow:empty { display: none; }

.ipr-link { color: var(--color-accent-300); font-size: var(--font-size-sm); text-decoration: none; }
.ipr-link:hover { text-decoration: underline; }
.ipr-link--disabled { color: var(--color-text-muted); cursor: not-allowed; }

.ipr-icon {
  width: 1.25em;
  height: 1.25em;
  flex-shrink: 0;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.ipr-highlight {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}
.ipr-highlight__chip {
  width: 2rem;
  height: 2rem;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  display: grid;
  place-items: center;
  background: var(--color-accent-tint);
  border: 1px solid var(--color-accent-tint-strong);
  color: var(--color-accent-400);
}

.ipr-avatar {
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--color-accent-tint);
  color: var(--color-accent-300);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
}

/* Entrada escalonada dos filhos, sem uma linha de JavaScript.
   `--reveal-offset` empurra o grupo INTEIRO para frente no tempo. E por ele
   que o cartao de login espera a cortina de abertura sair, sem que este bloco
   precise saber que existe uma cortina. */
.ipr-reveal > * { animation: ipr-rise var(--dur-slow) var(--ease-out) both; }
.ipr-reveal > *:nth-child(1) { animation-delay: calc(var(--reveal-offset, 0ms) +   0ms); }
.ipr-reveal > *:nth-child(2) { animation-delay: calc(var(--reveal-offset, 0ms) +  60ms); }
.ipr-reveal > *:nth-child(3) { animation-delay: calc(var(--reveal-offset, 0ms) + 120ms); }
.ipr-reveal > *:nth-child(4) { animation-delay: calc(var(--reveal-offset, 0ms) + 180ms); }
.ipr-reveal > *:nth-child(5) { animation-delay: calc(var(--reveal-offset, 0ms) + 240ms); }


/* ---------------------------------------------------------------- BRILHO --- */
/*
  Brilho que acompanha o ponteiro.

  DIVISAO DE TRABALHO: o modulo JS `pointer-glow` escreve DUAS variaveis
  (--pointer-x e --pointer-y, em pixels relativos ao elemento) e nada mais.
  Cor, raio, forma e quando acende sao decisao do CSS. E por isso que o mesmo
  modulo serve o cartao daqui e o holofote do painel de marca (auth.css) sem
  uma linha de JavaScript a mais.

  Sem o modulo montado (celular, movimento reduzido, JS desligado) os valores
  padrao deixam o brilho parado no alto e nada quebra.
*/
.ipr-sheen { position: relative; isolation: isolate; }
.ipr-sheen::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Negativo: pinta SOBRE o fundo do cartao e SOB o texto. Positivo lavaria
     a leitura do formulario. */
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(
    var(--sheen-size, 18rem) circle at var(--pointer-x, 50%) var(--pointer-y, 0%),
    var(--color-sheen), transparent 70%);
  transition: opacity var(--dur-slow) var(--ease-out);
}
.ipr-sheen.is-lit::after { opacity: 1; }


/* -------------------------------------------------------------- ABERTURA --- */
/*
  Cortina de boas-vindas.

  DUAS DECISOES SUSTENTAM ESTE COMPONENTE:

  1. A coreografia e 100% CSS e TERMINA SOZINHA (`forwards` na saida). Se o
     JavaScript falhar, nao carregar ou o navegador for velho, a cortina sai
     do mesmo jeito. Abertura que depende de JS para sumir e uma tela preta
     esperando a hora de acontecer.

  2. `pointer-events: none` desde o primeiro quadro. A cortina nunca engole um
     clique nem uma tecla: quem ja sabe a senha comeca a digitar por baixo
     dela, sem esperar o show acabar.

  O modulo `intro` so ACRESCENTA o que CSS nao sabe fazer: nao repetir na
  mesma aba, pular no primeiro clique e tirar o elemento do DOM no fim.
*/
.ipr-intro {
  position: fixed;
  inset: 0;
  z-index: var(--z-intro);
  overflow: hidden;
  display: grid;
  place-items: center;
  pointer-events: none;
  background: var(--color-curtain);
  /* `reverse` toca ipr-fade de tras para frente (1 para 0) e `forwards`
     prende no ultimo quadro. Reaproveita o keyframe em vez de criar um
     "ipr-unfade" que seria a mesma coisa escrita ao contrario. */
  animation: ipr-fade 480ms var(--ease-out) calc(var(--intro-exit) + 180ms) reverse forwards;
}

/* Brilho de fundo. Primeiro no DOM, entao pinta atras de todo o resto. */
.ipr-intro__aura {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  inline-size: min(44rem, 92vw);
  aspect-ratio: 1;
  border-radius: 50%;
  translate: -50% -50%;
  background: radial-gradient(circle, var(--color-aurora), transparent 62%);
  filter: blur(70px);
  animation: ipr-bloom 1200ms var(--ease-expo) 60ms both;
}

/*
  Uma unica passagem de luz sobre a cena.

  Elemento proprio, e nao ::after da cortina, DE PROPOSITO: animacao de
  pseudo-elemento dispara `animationend` com o target apontando para o
  elemento dono. Se o reflexo fosse pseudo da cortina, o modulo `intro`
  receberia o fim do reflexo (aos 2,2s) achando que era o fim da cortina e
  cortaria a saida pela metade. Com elemento proprio, o `animationend` da
  cortina so pode ter um dono.
*/
.ipr-intro__sweep {
  position: absolute;
  inset-block: -20%;
  inset-inline-start: 0;
  inline-size: 42%;
  background: linear-gradient(90deg, transparent, var(--color-sheen), transparent);
  filter: blur(26px);
  animation: ipr-sweep 1400ms var(--ease-in-out) 780ms both;
}

.ipr-intro__stage {
  position: relative;
  display: grid;
  justify-items: center;
  gap: var(--space-6);
  padding: var(--space-8);
  animation: ipr-lift 620ms var(--ease-in-out) var(--intro-exit) both;
}

.ipr-intro__rule {
  inline-size: clamp(4rem, 16vw, 8rem);
  block-size: 1px;
  background: var(--gradient-hairline);
  animation: ipr-draw 700ms var(--ease-expo) 1080ms both;
}

.ipr-intro__lead {
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--color-text-muted);
  animation: ipr-rise 700ms var(--ease-expo) 1200ms both;
}

/*
  Enquanto a cortina EXISTE no DOM, tudo que vem depois dela espera a vez de
  entrar. Quando o modulo `intro` remove a cortina (sessao repetida, movimento
  reduzido, ou o usuario pulou), este seletor deixa de casar e a pagina
  aparece na hora.

  A PRESENCA DO ELEMENTO E O ESTADO. Nao existe classe para o JS e o CSS
  manterem em sincronia, entao nao existe como os dois discordarem.
*/
.ipr-intro ~ * { --reveal-offset: calc(var(--intro-exit) + 120ms); }

/* ----------------------------------------------------------------- MENU --- */
.ipr-nav { display: flex; flex-direction: column; gap: var(--space-1); }
.ipr-nav__link {
  display: flex;
  align-items: center;
  white-space: nowrap;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  text-decoration: none;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.ipr-nav__link:hover { background: var(--color-surface-1); color: var(--color-text-primary); }
.ipr-nav__link.is-active {
  background: var(--color-accent-tint);
  color: var(--color-accent-300);
}

/* --------------------------------------------------------- INDICADOR --- */
.ipr-stat { display: flex; flex-direction: column; gap: var(--space-2); --card-padding: var(--space-5); }
.ipr-stat__topo  { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.ipr-stat__icon  { color: var(--color-accent-400); }
.ipr-stat__label { font-size: var(--font-size-sm); color: var(--color-text-secondary); }
.ipr-stat__hint  { font-size: var(--font-size-xs); color: var(--color-text-muted); }

/* O numero e o unico elemento grande do cartao, e usa tabular-nums: sem isso
   os digitos tem larguras diferentes e uma coluna de valores fica desalinhada
   a cada atualizacao, como se a tela estivesse tremendo. */
.ipr-stat__value {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-bold);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-tight);
}

/* A variacao NUNCA depende so da cor: o macro sempre emite a seta junto do
   texto. A cor acelera; a seta e a palavra e que informam. */
.ipr-stat__delta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-muted);
}
.ipr-stat__delta .ipr-icon { width: 1em; height: 1em; }
.ipr-stat__delta--sobe  { color: var(--color-success-fg); }
.ipr-stat__delta--desce { color: var(--color-danger-fg); }


/* -------------------------------------------------------------- SELO --- */
/* Estado de registro (pago, aberto, cancelado). Sempre com a palavra dentro:
   cor nunca e o unico portador da informacao. */
.ipr-badge {
  --badge-bg: var(--color-surface-2);
  --badge-fg: var(--color-text-secondary);
  --badge-border: var(--color-border);

  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: .1rem .5rem;
  border: 1px solid var(--badge-border);
  border-radius: var(--radius-pill);
  background: var(--badge-bg);
  color: var(--badge-fg);
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-semibold);
  white-space: nowrap;
}
.ipr-badge--ok      { --badge-bg: var(--color-success-bg); --badge-fg: var(--color-success-fg); --badge-border: var(--color-success-border); }
.ipr-badge--aberto  { --badge-bg: var(--color-warning-bg); --badge-fg: var(--color-warning-fg); --badge-border: var(--color-warning-border); }
.ipr-badge--erro    { --badge-bg: var(--color-danger-bg);  --badge-fg: var(--color-danger-fg);  --badge-border: var(--color-danger-border); }
.ipr-badge--info    { --badge-bg: var(--color-info-bg);    --badge-fg: var(--color-info-fg);    --badge-border: var(--color-info-border); }


/* ------------------------------------------------------------- PAINEL --- */
/* Cartao com cabecalho: titulo a esquerda, acao a direita. A tela de
   relatorios e a de listagem vao usar o mesmo bloco. */
.ipr-panel { --card-padding: 0; display: flex; flex-direction: column; }
.ipr-panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-5) var(--space-4);
  border-bottom: 1px solid var(--color-border-subtle);
}
.ipr-panel__title { font-size: var(--font-size-md); font-weight: var(--font-weight-semibold); }
.ipr-panel__sub   { font-size: var(--font-size-xs); color: var(--color-text-muted); }
.ipr-panel__body  { padding: var(--space-5); }
/* Tabela encosta na borda do painel: o padding viria das celulas. */
.ipr-panel__body--flush { padding: 0; }


/* ------------------------------------------------------------ GRAFICO --- */
/*
  O SVG vem do macro `bar_chart`. Aqui so a cor e a tipografia: a GEOMETRIA
  esta nos atributos do proprio SVG, porque altura de barra e dado, e dado nao
  entra por CSS.
*/
.ipr-chart { width: 100%; height: auto; overflow: visible; }
.ipr-chart__bar {
  fill: var(--color-chart-bar);
  transition: fill var(--dur-fast);
}
/* O periodo corrente e o unico destacado. Destacar todos seria nao destacar. */
.ipr-chart__bar.is-atual { fill: var(--color-chart-bar-atual); }
.ipr-chart__bar:hover    { fill: var(--color-chart-bar-atual); }
/* Eixo recessivo: existe para dar referencia, nao para competir com a barra. */
.ipr-chart__base { stroke: var(--color-chart-axis); stroke-width: 1; }
/* Texto usa token de TEXTO, nunca a cor da serie: numero pintado da cor da
   barra parece parte do grafico e para de ser lido como numero. */
.ipr-chart__rotulo {
  fill: var(--color-text-muted);
  font-size: 11px;
  font-family: var(--font-sans);
}
.ipr-chart__valor {
  fill: var(--color-text-primary);
  font-size: 12px;
  font-weight: var(--font-weight-semibold);
  font-family: var(--font-sans);
}


/* ------------------------------------------------------------ TABELA --- */
.ipr-table-wrap { overflow-x: auto; }
.ipr-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-sm);
}
.ipr-table th,
.ipr-table td {
  padding: var(--space-3) var(--space-5);
  text-align: start;
  border-bottom: 1px solid var(--color-border-subtle);
  white-space: nowrap;
}
.ipr-table th {
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.ipr-table tbody tr:last-child td { border-bottom: 0; }
.ipr-table tbody tr { transition: background var(--dur-fast); }
.ipr-table tbody tr:hover { background: var(--color-surface-2); }
/* Numero em coluna: alinhado a direita e com digito de largura fixa, senao a
   coluna de valores nao da para comparar batendo o olho.
   Elemento do componente, e nao utilitario `.u-`: so faz sentido dentro de
   uma tabela. */
.ipr-table__num { text-align: end; font-variant-numeric: tabular-nums; }


/* ---------------------------------------------------------- ATIVIDADE --- */
/* Lista vertical com um fio ligando os pontos. Serve a qualquer historico. */
.ipr-feed { display: flex; flex-direction: column; gap: var(--space-5); }
.ipr-feed__item {
  position: relative;
  display: flex;
  gap: var(--space-3);
  padding-inline-start: var(--space-1);
}
.ipr-feed__dot {
  flex-shrink: 0;
  width: 1.75rem;
  height: 1.75rem;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--color-accent-tint);
  border: 1px solid var(--color-accent-tint-strong);
  color: var(--color-accent-400);
}
/* O fio que liga um ponto ao proximo. O ultimo item nao tem proximo. */
.ipr-feed__item:not(:last-child) .ipr-feed__dot::after {
  content: "";
  position: absolute;
  inset-block-start: 1.75rem;
  inset-inline-start: calc(var(--space-1) + .875rem);
  width: 1px;
  height: calc(100% + var(--space-5) - 1.75rem);
  background: var(--color-border);
}
.ipr-feed__texto { font-size: var(--font-size-sm); }
.ipr-feed__quando { font-size: var(--font-size-xs); color: var(--color-text-muted); }


/* ------------------------------------------------------------- SELECT --- */
/* Reaproveita .ipr-field__input inteiro; aqui so entra o que um <select> tem
   de proprio: a seta e o espaco para ela. A seta e background-image em SVG
   embutido, entao nao ha requisicao nem arquivo para manter. */
.ipr-select {
  appearance: none;
  padding-inline-end: 2.5rem;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23606F8A' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .75rem center;
  background-size: 1.1rem;
}

/* ------------------------------------------------------------- PESSOA --- */
/* Avatar + nome + e-mail numa celula de tabela. Serve a qualquer listagem de
   gente que apareca depois (clientes, contatos). */
.ipr-pessoa { display: flex; align-items: center; gap: var(--space-3); }
.ipr-pessoa__texto { display: flex; flex-direction: column; line-height: var(--line-tight); }
.ipr-pessoa__nome { font-weight: var(--font-weight-semibold); }
.ipr-pessoa__email { font-size: var(--font-size-xs); color: var(--color-text-muted); }

/* -------------------------------------------------------------- ACOES --- */
.ipr-acoes { display: flex; align-items: center; gap: var(--space-1); justify-content: flex-end; }
.ipr-acoes__form { margin: 0; display: contents; }
.ipr-acao {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.ipr-acao:hover { background: var(--color-surface-2); color: var(--color-text-primary); }
.ipr-acao--perigo:hover { background: var(--color-danger-bg); color: var(--color-danger); }

/* ---------------------------------------------------------- PAGINACAO --- */
.ipr-paginacao {
  display: flex;
  gap: var(--space-1);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--color-border-subtle);
}
.ipr-paginacao__item {
  min-width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  padding-inline: var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
  text-decoration: none;
  transition: background var(--dur-fast);
}
.ipr-paginacao__item:hover { background: var(--color-surface-2); }
.ipr-paginacao__item.is-atual {
  background: var(--color-accent-tint);
  color: var(--color-accent-300);
  font-weight: var(--font-weight-semibold);
}

/* -------------------------------------------------- GRUPO DE CAMPOS --- */
/* <fieldset> com <legend>: a associacao entre o titulo e os campos e nativa,
   entao o leitor de tela anuncia "Acesso, Perfil" em vez de so "Perfil". */
.ipr-grupo {
  border: 0;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.ipr-grupo + .ipr-grupo {
  padding-block-start: var(--space-6);
  border-top: 1px solid var(--color-border-subtle);
}
.ipr-grupo__titulo {
  padding: 0;
  font-size: var(--font-size-2xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* --------------------------------------------------- ESTADO VAZIO --- */
/* Uma tela sem dado ainda precisa dizer o que fazer. Serve a qualquer
   listagem futura que venha vazia. */
.ipr-empty {
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  padding: var(--space-12) var(--space-5);
  text-align: center;
}
.ipr-empty__icon {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: var(--radius-pill);
  background: var(--color-accent-tint);
  border: 1px solid var(--color-accent-tint-strong);
  color: var(--color-accent-400);
}
.ipr-empty__icon .ipr-icon { width: 1.6rem; height: 1.6rem; }
.ipr-empty__title { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); }
.ipr-empty__text  { color: var(--color-text-secondary); font-size: var(--font-size-sm); max-width: 42ch; }
