/* Tipografía de marca: solo para el wordmark "GIP" y la marca "G" del logo.
   El resto de la aplicación sigue con la fuente del sistema operativo. */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/vendor/fonts/ibm-plex-sans-600.woff2') format('woff2');
}

:root {
  --gip-primary: #17160f;
  --gip-primary-hover: #35332a;
  --gip-primary-soft: #efeee6;
  --gip-canvas: #f7f6f1;
  --gip-surface: #ffffff;
  --gip-surface-subtle: #f1efe6;
  --gip-ink: #17160f;
  --gip-muted: #7a776d;
  --gip-border: #e4e1d5;
  --gip-positive: #3d6b4f;
  --gip-negative: #9c4438;
  --gip-warning: #8a6a2c;
  --gip-shadow: 0 1px 2px rgba(23, 22, 15, 0.04);
  --bs-primary: #17160f;
  --bs-primary-rgb: 23, 22, 15;
  --bs-body-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --bs-body-color: var(--gip-ink);
  --bs-body-bg: var(--gip-canvas);
  --bs-border-color: var(--gip-border);
  color-scheme: light;
}

[data-theme='dark'] {
  --gip-primary: #efeee6;
  --gip-primary-hover: #ffffff;
  --gip-primary-soft: #26251e;
  --gip-canvas: #121110;
  --gip-surface: #1b1a16;
  --gip-surface-subtle: #221f19;
  --gip-ink: #efeee6;
  --gip-muted: #9a978c;
  --gip-border: #34322a;
  --gip-positive: #7cae8f;
  --gip-negative: #d08579;
  --gip-warning: #d1a35c;
  --gip-shadow: 0 1px 2px rgba(0, 0, 0, 0.32);
  --bs-primary: #efeee6;
  --bs-primary-rgb: 239, 238, 230;
  color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --gip-primary: #efeee6;
    --gip-primary-hover: #ffffff;
    --gip-primary-soft: #26251e;
    --gip-canvas: #121110;
    --gip-surface: #1b1a16;
    --gip-surface-subtle: #221f19;
    --gip-ink: #efeee6;
    --gip-muted: #9a978c;
    --gip-border: #34322a;
    --gip-positive: #7cae8f;
    --gip-negative: #d08579;
    --gip-warning: #d1a35c;
    --gip-shadow: 0 1px 2px rgba(0, 0, 0, 0.32);
    --bs-primary: #efeee6;
    --bs-primary-rgb: 239, 238, 230;
    color-scheme: dark;
  }
}

/* Los tiles de OSM son claros por diseño; en tema oscuro se invierten con un
   filtro CSS ("poor man's dark mode") en vez de cambiar de proveedor de mapas.
   Los marcadores quedan fuera del filtro para conservar sus colores. La capa
   satelital (foto real) queda excluida: invertirla arruinaría los colores. */
[data-theme='dark'] .leaflet-container:not(.gip-satellite-active) .leaflet-tile-pane {
  filter: invert(1) hue-rotate(180deg) brightness(0.95) contrast(0.9);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) .leaflet-container:not(.gip-satellite-active) .leaflet-tile-pane {
    filter: invert(1) hue-rotate(180deg) brightness(0.95) contrast(0.9);
  }
}
.gip-map {
  height: 260px;
}
.gip-map--picker {
  height: 220px;
}
.gip-map:fullscreen,
.gip-map--picker:fullscreen {
  height: 100vh;
  width: 100vw;
}
.gip-map-fullscreen a {
  font-size: 1.1rem;
  line-height: 26px;
  text-align: center;
}
.gip-modal-scroll {
  max-height: 70vh;
  overflow-y: auto;
}
.gip-edge-label {
  background: var(--gip-surface);
  border: 1px solid var(--gip-border);
  border-radius: 0 !important;
  box-shadow: none;
  color: var(--gip-ink);
  font-size: 10px;
  font-weight: 600;
  padding: 0 3px;
}
.gip-edge-label::before {
  display: none;
}
.gip-edge-label-editable {
  cursor: pointer;
  pointer-events: auto;
}
.gip-edge-label-editable:hover {
  border-color: var(--gip-primary);
}

* {
  scrollbar-width: thin;
  scrollbar-color: var(--gip-border) transparent;
}
body {
  background: var(--gip-canvas);
  color: var(--gip-ink);
  letter-spacing: -0.01em;
}
body:has(#app-shell) {
  padding-left: 14rem;
  padding-top: 3.75rem;
  transition: padding-left 0.2s ease;
}
/* Reserva visual inmediata mientras layout.js monta la navegación. Evita el flash
   de contenido sin sidebar durante navegación y recarga. */
#app-shell:empty::before {
  content: '';
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 1030;
  width: 14rem;
  background: var(--gip-sidebar);
  border-right: 1px solid var(--gip-border);
}
#app-shell:empty::after {
  content: '';
  position: fixed;
  inset: 0 0 auto 14rem;
  z-index: 1029;
  height: 3.75rem;
  background: var(--gip-surface);
  border-bottom: 1px solid var(--gip-border);
}
[data-theme='light'] #app-shell:empty::before {
  background: #fff;
}
body.gip-sidebar-collapsed:has(#app-shell) #app-shell:empty::before {
  transform: translateX(-102%);
}
body.gip-sidebar-collapsed:has(#app-shell) #app-shell:empty::after {
  left: 0;
}
html[data-sidebar-collapsed='true'] body:has(#app-shell) {
  padding-left: 0;
}
html[data-sidebar-collapsed='true'] #app-shell:empty::before {
  transform: translateX(-102%);
}
html[data-sidebar-collapsed='true'] #app-shell:empty::after {
  left: 0;
}
body.gip-sidebar-collapsed:has(#app-shell) {
  padding-left: 0;
}
h1,
h2,
h3,
h4,
h5,
h6,
.navbar-brand {
  font-family: var(--bs-body-font-family);
  font-weight: 600;
  letter-spacing: -0.02em;
}
a {
  color: var(--gip-ink);
  text-decoration-color: var(--gip-border);
  text-underline-offset: 0.18em;
}
a:hover {
  color: var(--gip-primary-hover);
  text-decoration-color: currentColor;
}

main.container,
main.container-fluid {
  max-width: 1520px;
  padding: 1.75rem 2rem !important;
}
main > .d-flex:first-child {
  min-height: 2.5rem;
  margin-bottom: 1.2rem !important;
}
h1.h3 {
  font-size: clamp(1.3rem, 1.5vw, 1.55rem);
  font-weight: 700;
  letter-spacing: -0.025em;
}
main h2.h5 {
  font-size: 1rem;
  font-weight: 650;
  letter-spacing: -0.015em;
}
main h2.h6,
.card h2.h6 {
  font-size: 0.72rem;
  letter-spacing: 0.04em;
}
.modal-title.h5 {
  font-size: 1.05rem;
  font-weight: 650;
}
.text-muted {
  color: var(--gip-muted) !important;
}
.gip-eyebrow {
  color: var(--gip-primary);
  font-size: 0.66rem;
  font-weight: 750;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.gip-dashboard-hero {
  min-height: 4rem;
}
.gip-dashboard-meta {
  display: flex;
  align-items: center;
  gap: 0.8rem;
}
.gip-dashboard-meta .btn {
  white-space: nowrap;
}
.gip-metric-card {
  position: relative;
  min-height: 6.2rem;
  border-top: 1px solid var(--gip-border);
}
.gip-metric-card .card-body {
  padding: 1.15rem 1.25rem;
}
.gip-metric-card .fs-4 {
  font-size: 1.4rem !important;
}
/* Lista de avisos: la gravedad se marca con una franja al costado, no tiñendo el fondo
   entero. Con seis avisos, seis rectángulos de color saturado tapaban todo lo demás; la
   franja conserva la señal y devuelve la pantalla a los números. */
.gip-attention-list {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.gip-attention-item {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--gip-border);
  border-left-width: 3px;
  border-radius: 0.4rem;
  color: var(--gip-ink);
  font-size: 0.86rem;
  text-decoration: none;
}
a.gip-attention-item:hover {
  background: var(--gip-surface-subtle);
  color: var(--gip-ink);
}
.gip-attention-item.is-danger {
  border-left-color: var(--gip-negative);
}
.gip-attention-item.is-warning {
  border-left-color: var(--gip-warning);
}
.gip-attention-item.is-info {
  border-left-color: var(--gip-muted);
}
.gip-attention-text {
  flex: 1;
  min-width: 0;
}
.gip-attention-go {
  flex: none;
  color: var(--gip-muted);
}
.gip-attention-ok {
  margin-top: 0.6rem;
  color: var(--gip-muted);
  font-size: 0.82rem;
}
.gip-attention-ok::before {
  content: '✓';
  margin-right: 0.4rem;
  color: var(--gip-positive);
  font-weight: 700;
}

/* Banda de cobertura: la cifra manda, la cuenta que la produce queda debajo en tono
   atenuado, y a la derecha la brecha contra los compromisos del mes. */
.gip-coverage-body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: 1.5rem;
  align-items: center;
}
.gip-coverage-amount {
  font-size: clamp(1.7rem, 3vw, 2.2rem);
  font-weight: 650;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.gip-coverage-hint {
  margin-top: 0.3rem;
  color: var(--gip-ink);
  font-size: 0.86rem;
}
.gip-coverage-math {
  margin-top: 0.35rem;
  color: var(--gip-muted);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}
.gip-coverage-gauge {
  padding-left: 1.5rem;
  border-left: 1px solid var(--gip-border);
}
.gip-coverage-gauge-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.4rem;
  color: var(--gip-muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.gip-coverage-gauge-head strong {
  color: var(--gip-ink);
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  text-transform: none;
}
.gip-coverage-gauge-note {
  margin-top: 0.45rem;
  font-size: 0.8rem;
}
.gip-coverage-trend {
  margin-top: 0.35rem;
  color: var(--gip-muted);
  font-size: 0.78rem;
}

/* Patrimonio neto: es la cifra más contundente de la posición y estaba en gris de 12px,
   con el mismo peso visual que el conteo de inmuebles. */
.gip-networth-amount {
  font-size: clamp(1.4rem, 2.2vw, 1.7rem);
  font-weight: 650;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.gip-networth-detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.35rem 1rem;
  margin: 0;
  padding-top: 0.7rem;
  border-top: 1px solid var(--gip-border);
  font-size: 0.82rem;
}
.gip-networth-detail dt {
  color: var(--gip-muted);
  font-weight: 400;
}
.gip-networth-detail dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.gip-accounts-empty-row td {
  border-top: 1px solid var(--gip-border);
}
.gip-projection-flat-amount {
  font-size: 1.3rem;
  font-weight: 650;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* Resultado del mes contra el mes anterior. */
.gip-month-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem;
}
.gip-month-item {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--gip-border);
  border-radius: 0.55rem;
}
.gip-month-item.is-emphasis {
  background: var(--gip-surface-subtle);
}
.gip-month-label {
  color: var(--gip-muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.gip-month-value {
  font-size: 1.05rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
/* La variación se muestra en gris: la dirección ya la da la flecha, y en un panel donde
   el color señala estados reales, "gastaste más que el mes pasado" no siempre es malo. */
.gip-month-delta {
  color: var(--gip-muted);
  font-size: 0.75rem;
}
.gip-month-scopes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.25rem;
  margin-top: 0.75rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--gip-border);
}
.gip-month-scope {
  color: var(--gip-muted);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}
.gip-month-scope strong {
  color: var(--gip-ink);
  font-weight: 650;
}

.gip-dashboard-agenda {
  background: var(--gip-surface-subtle);
}
.gip-dashboard-agenda-list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.6rem;
}
.gip-dashboard-agenda-item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.55rem;
  padding: 0.7rem;
  border: 1px solid var(--gip-border);
  border-radius: 0.55rem;
  background: var(--gip-surface);
  color: var(--gip-ink);
  text-decoration: none;
}
.gip-dashboard-agenda-item:hover {
  border-color: var(--gip-primary);
}
.gip-dashboard-agenda-item strong,
.gip-dashboard-agenda-item small {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gip-dashboard-agenda-item strong {
  font-size: 0.78rem;
}
.gip-dashboard-agenda-item small {
  margin-top: 0.2rem;
  color: var(--gip-muted);
  font-size: 0.68rem;
}
.gip-dashboard-agenda-date {
  color: var(--gip-primary);
  font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
}
.gip-dashboard-agenda-item .badge {
  grid-column: 1 / -1;
  justify-self: start;
}

/* Geometría editorial: controles y superficies sin esquinas redondeadas. */
.card,
.btn,
.form-control,
.form-select,
textarea,
.modal-content,
.gip-sidebar .nav-link,
.gip-account-trigger,
.gip-account-dropdown,
.gip-quick-action,
.gip-agenda-toolbar,
.gip-agenda-event,
.gip-calendar,
.gip-calendar-event,
.gip-toast,
.gip-confirm-dialog,
.gip-empty-state,
.gip-calendar-day {
  border-radius: 0 !important;
}
.alert {
  border-left: 0;
}

.gip-sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 1040;
  width: 14rem;
  display: flex;
  flex-direction: column;
  padding: 1rem 0.75rem 0.75rem;
  background: var(--gip-surface);
  color: var(--gip-ink);
  border-right: 1px solid var(--gip-border);
  overflow-y: auto;
  transition: transform 0.2s ease;
}
body.gip-sidebar-collapsed .gip-sidebar {
  transform: translateX(-102%);
}
.gip-brand {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 0.15rem 0.55rem 1.15rem;
}
.gip-brand-mark {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.gip-brand strong {
  display: block;
  font-family: 'IBM Plex Sans', var(--bs-body-font-family);
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.gip-brand-version {
  color: var(--gip-muted);
  font-size: 0.64rem;
  letter-spacing: 0.02em;
}
.gip-logo {
  width: 1.9rem;
  height: 1.9rem;
  display: grid;
  place-items: center;
  flex: none;
  background: var(--gip-ink);
  color: var(--gip-canvas);
  font-family: 'IBM Plex Sans', var(--bs-body-font-family);
  font-weight: 600;
  font-size: 1rem;
  box-shadow: none;
}
/* Accesos rápidos en la barra superior: como iconos, para no gastar alto del menú lateral
   —que es donde estaban y donde el espacio vertical es el recurso escaso— y para aprovechar
   la franja de arriba, que solo tenía el botón de nueva transacción. */
.gip-quick-links {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  margin-left: auto;
  margin-right: 0.4rem;
}
.gip-quick-link {
  display: inline-grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 0.45rem;
  color: var(--gip-muted);
  text-decoration: none;
}
.gip-quick-link svg {
  width: 1.15rem;
  height: 1.15rem;
}
.gip-quick-link:hover {
  background: var(--gip-surface-subtle);
  color: var(--gip-ink);
}
.gip-quick-link.active {
  background: var(--gip-surface-subtle);
  color: var(--gip-ink);
}
.gip-quick-link:focus-visible {
  outline: 2px solid var(--gip-primary);
  outline-offset: 2px;
}
.gip-sidebar-close {
  display: none;
  margin-left: auto;
  padding: 0 0.35rem;
  border: 0;
  background: transparent;
  color: var(--gip-muted);
  font-size: 1.5rem;
  line-height: 1;
}
.gip-sidebar-close:hover {
  color: var(--gip-ink);
}
.gip-navigation {
  flex: 1;
}
.gip-nav-group + .gip-nav-group {
  margin-top: 0.15rem;
}
.gip-nav-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  margin: 0;
  padding: 0.45rem 0.65rem;
  border: 0;
  background: transparent;
  color: var(--gip-muted);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.gip-nav-toggle:hover {
  color: var(--gip-ink);
}
.gip-nav-group.is-active .gip-nav-toggle {
  color: var(--gip-ink);
  background: var(--gip-surface-subtle);
}
.gip-nav-group.is-active .gip-nav-toggle span:first-child {
  font-weight: 750;
}
.gip-nav-chevron {
  transition: transform 0.15s ease;
}
.gip-nav-group.is-open .gip-nav-chevron {
  transform: rotate(90deg);
}
.gip-nav-collapse {
  /* max-height + overflow:hidden en vez de la técnica grid-template-rows 0fr/1fr: esta
     última tiene un bug conocido en Safari (incluido iOS) donde, al cerrar el acordeón,
     deja sin recortar una porción del contenido anterior (ej. el fondo oscuro del ítem
     activo) visible debajo del grupo ya colapsado. */
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.18s ease;
}
.gip-nav-group.is-open .gip-nav-collapse {
  /* Mayor a la altura de cualquier grupo real (el más largo, Comercial, ronda 34rem) —
     no limita el contenido abierto, solo habilita la animación de cierre. */
  max-height: 40rem;
}
.gip-nav-collapse > .gip-nav-list {
  padding: 0.15rem 0 0.45rem 0.72rem;
}
.gip-sidebar .nav-link {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 2.15rem;
  padding: 0.38rem 0.6rem;
  margin: 0.06rem 0;
  color: var(--gip-muted);
  font-size: 0.79rem;
  font-weight: 500;
  transition:
    background-color 0.15s,
    color 0.15s;
}
.gip-sidebar .nav-link:hover {
  color: var(--gip-ink);
  background: var(--gip-surface-subtle);
}
.gip-sidebar .nav-link.active {
  color: var(--gip-canvas);
  background: var(--gip-ink);
  box-shadow: none;
}
.gip-nav-section {
  display: flex;
  align-items: center;
  min-height: 1.55rem;
  margin: 0.42rem 0.6rem 0.12rem;
  padding-top: 0.35rem;
  border-top: 1px solid color-mix(in srgb, var(--gip-border) 58%, transparent);
  color: color-mix(in srgb, var(--gip-muted) 66%, transparent);
  font-size: 0.57rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  line-height: 1;
  text-transform: uppercase;
  opacity: 0.82;
}
.gip-nav-section:first-child {
  margin-top: 0;
  padding-top: 0.1rem;
  border-top: 0;
}
.gip-page-path {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.38rem;
  margin: 0 0 0.45rem;
  color: color-mix(in srgb, var(--gip-muted) 78%, transparent);
  font-size: 0.67rem;
  font-weight: 650;
  letter-spacing: 0.055em;
  line-height: 1.2;
  text-transform: uppercase;
}
.gip-page-title-context {
  min-width: 0;
}
.gip-page-title-context .gip-page-path {
  margin: 0.32rem 0 0;
}
.gip-page-path span[aria-current='page'] {
  color: var(--gip-muted);
}
.gip-page-path-separator {
  color: color-mix(in srgb, var(--gip-muted) 42%, transparent);
  font-weight: 400;
}
.gip-sidebar-footer {
  padding-top: 1rem;
  border-top: 1px solid var(--gip-border);
}
.gip-user {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-width: 0;
  margin-bottom: 0.75rem;
}
.gip-user div {
  min-width: 0;
}
.gip-user strong,
.gip-user small {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gip-user strong {
  font-size: 0.82rem;
}
.gip-user small {
  color: var(--gip-muted);
  font-size: 0.7rem;
}
.gip-avatar {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--gip-primary-soft);
  color: var(--gip-ink);
  font-weight: 650;
}
.gip-icon-button {
  width: 2.2rem;
  height: 2.2rem;
  display: inline-grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--gip-border);
  background: transparent;
  color: inherit;
}
.gip-mobile-header {
  position: fixed;
  inset: 0 0 auto 14rem;
  z-index: 1035;
  height: 3.75rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 1.25rem;
  background: color-mix(in srgb, var(--gip-surface) 94%, transparent);
  border-bottom: 1px solid var(--gip-border);
  backdrop-filter: blur(12px);
  transition: left 0.2s ease;
}
body.gip-sidebar-collapsed .gip-mobile-header {
  left: 0;
}
.gip-topbar-start {
  display: flex;
  align-items: center;
  gap: 0.8rem;
}
.gip-mobile-header .gip-icon-button {
  border-color: var(--gip-border);
  color: var(--gip-muted);
}
.gip-mobile-header .gip-icon-button:hover {
  color: var(--gip-ink);
  background: var(--gip-surface-subtle);
}
.gip-mobile-brand {
  display: none;
  align-items: center;
  gap: 0.5rem;
  color: var(--gip-ink);
  text-decoration: none;
}
.gip-mobile-brand strong {
  font-family: 'IBM Plex Sans', var(--bs-body-font-family);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.gip-account-menu {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.45rem;
}
.gip-quick-action {
  display: inline-grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  margin-right: 0.75rem;
  padding: 0;
  border: 1px solid var(--gip-primary);
  border-radius: 0.5rem;
  color: var(--gip-primary);
  font-size: 1.1rem;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
}
.gip-quick-action:hover {
  background: var(--gip-primary-soft);
  color: var(--gip-primary-hover);
}
.gip-account-trigger {
  display: flex;
  align-items: center;
  gap: 0;
  padding: 0.2rem;
  border: 0;
  border-radius: 0.55rem;
  background: transparent;
  color: var(--gip-ink);
  text-align: right;
}
.gip-account-trigger .gip-avatar {
  width: 2.1rem;
  height: 2.1rem;
}
.gip-account-trigger:hover,
.gip-account-trigger[aria-expanded='true'] {
  background: var(--gip-surface-subtle);
}
.gip-account-copy strong,
.gip-account-copy small {
  display: block;
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gip-dropdown-profile {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.55rem 0.65rem 0.7rem;
  text-align: left;
}
.gip-dropdown-profile .gip-avatar {
  width: 2rem;
  height: 2rem;
}
.gip-dropdown-profile strong,
.gip-dropdown-profile small {
  display: block;
}
.gip-dropdown-profile strong {
  font-size: 0.8rem;
}
.gip-dropdown-profile small {
  margin-top: 0.12rem;
  color: var(--gip-muted);
  font-size: 0.7rem;
}
.gip-account-copy strong {
  font-size: 0.78rem;
  font-weight: 650;
}
.gip-account-copy small {
  color: var(--gip-muted);
  font-size: 0.66rem;
}
.gip-account-trigger .gip-avatar {
  width: 2rem;
  height: 2rem;
  background: var(--gip-primary-soft);
  color: var(--gip-primary);
  font-size: 0.78rem;
}
.gip-chevron {
  color: var(--gip-muted);
  font-size: 0.8rem;
}
.gip-account-dropdown {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  z-index: 1050;
  width: 13.5rem;
  padding: 0.4rem;
  border: 1px solid var(--gip-border);
  border-radius: 0.65rem;
  background: var(--gip-surface);
  box-shadow: 0 12px 32px rgba(16, 24, 40, 0.14);
}
.gip-account-dropdown[hidden] {
  display: none;
}
.gip-account-dropdown button {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 2.35rem;
  padding: 0.45rem 0.65rem;
  border: 0;
  border-radius: 0.4rem;
  background: transparent;
  color: var(--gip-ink);
  font-size: 0.78rem;
  text-align: left;
}
.gip-account-dropdown button:hover {
  background: var(--gip-surface-subtle);
}
.gip-account-dropdown .gip-logout-action {
  color: var(--gip-negative);
}
.gip-toast-region {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 1100;
  display: grid;
  gap: 0.55rem;
  width: min(24rem, calc(100vw - 2rem));
}
.gip-toast {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.8rem 0.9rem;
  border: 1px solid var(--gip-border);
  border-left: 3px solid var(--gip-primary);
  border-radius: 0.6rem;
  background: var(--gip-surface);
  color: var(--gip-ink);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.14);
  font-size: 0.82rem;
}
.gip-toast-error {
  border-left-color: var(--gip-negative);
}
.gip-toast button {
  border: 0;
  background: transparent;
  color: var(--gip-muted);
  font-size: 1.1rem;
  line-height: 1;
}
.gip-confirm-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1090;
  display: grid;
  place-items: center;
  padding: 1rem;
  background: rgba(0, 0, 0, 0.58);
}
.gip-confirm-dialog {
  width: min(28rem, 100%);
  padding: 1.25rem;
  border: 1px solid var(--gip-border);
  border-radius: 0.75rem;
  background: var(--gip-surface);
  color: var(--gip-ink);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.25);
}
.gip-confirm-dialog h2 {
  margin: 0 0 0.5rem;
  font-size: 1.05rem;
}
.gip-confirm-dialog p {
  margin: 0;
  color: var(--gip-muted);
  font-size: 0.86rem;
}
.gip-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 1.25rem;
}
.gip-avatar {
  display: inline-grid;
  place-items: center;
  flex: none;
  border: none;
  padding: 0;
  border-radius: 0.4rem;
  overflow: hidden;
  background: var(--gip-ink);
  color: var(--gip-canvas);
  font-family: 'IBM Plex Sans', var(--bs-body-font-family);
  font-weight: 600;
}
.gip-avatar-sm {
  width: 2rem;
  height: 2rem;
  font-size: 0.85rem;
}
.gip-avatar-md {
  width: 3.5rem;
  height: 3.5rem;
  font-size: 1.4rem;
}
.gip-avatar-lg {
  width: 6rem;
  height: 6rem;
  font-size: 2.2rem;
}
.gip-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.gip-avatar-photo {
  cursor: pointer;
}
.gip-search-result {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.gip-search-result > div {
  flex: 1;
  min-width: 0;
}
.gip-photo-preview-dialog {
  position: relative;
  width: min(28rem, 100%);
  padding: 1.25rem;
  border: 1px solid var(--gip-border);
  border-radius: 0.75rem;
  background: var(--gip-surface);
  color: var(--gip-ink);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.25);
  text-align: center;
}
.gip-photo-preview-dialog img {
  max-width: 100%;
  max-height: 70vh;
  border-radius: 0.5rem;
}
.gip-photo-preview-dialog p {
  margin: 0.75rem 0 0;
  color: var(--gip-muted);
  font-size: 0.86rem;
}
.gip-photo-preview-dialog .btn-close {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
}
.gip-agenda-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--gip-border);
  border-radius: 0.65rem;
  background: var(--gip-surface);
  color: var(--gip-muted);
  font-size: 0.82rem;
}
.gip-agenda-list {
  display: grid;
  gap: 0.6rem;
}
.gip-agenda-list h2 {
  margin: 1.1rem 0 0.25rem;
  color: var(--gip-muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: capitalize;
}
.gip-agenda-event {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 1rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--gip-border);
  border-radius: 0.65rem;
  background: var(--gip-surface);
  color: var(--gip-ink);
  text-decoration: none;
}
.gip-agenda-event:hover {
  border-color: var(--gip-primary);
  background: var(--gip-primary-soft);
}
.gip-agenda-event-date {
  color: var(--gip-muted);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}
.gip-agenda-event-main {
  min-width: 0;
}
.gip-agenda-event-main strong,
.gip-agenda-event-main small {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gip-agenda-event-main strong {
  font-size: 0.86rem;
}
.gip-agenda-event-main small {
  margin-top: 0.2rem;
  color: var(--gip-muted);
  font-size: 0.74rem;
}
.gip-agenda-loading,
.gip-empty-state {
  padding: 2.25rem 1rem;
  border: 1px dashed var(--gip-border);
  border-radius: 0.65rem;
  text-align: center;
  color: var(--gip-muted);
}
.gip-empty-state strong,
.gip-empty-state span {
  display: block;
}
.gip-calendar {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  border: 1px solid var(--gip-border);
  border-radius: 0.7rem;
  overflow: hidden;
  background: var(--gip-surface);
}
.gip-calendar-heading {
  padding: 0.7rem 0.55rem;
  border-bottom: 1px solid var(--gip-border);
  background: var(--gip-surface-subtle);
  color: var(--gip-muted);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
}
.gip-calendar-day {
  min-height: 7.5rem;
  padding: 0.45rem;
  border-right: 1px solid var(--gip-border);
  border-bottom: 1px solid var(--gip-border);
}
.gip-calendar-day:nth-child(7n) {
  border-right: 0;
}
.gip-calendar-day.is-other-month {
  background: var(--gip-surface-subtle);
  color: var(--gip-muted);
}
.gip-calendar-day.is-today {
  background: var(--gip-primary-soft);
  box-shadow: inset 0 0 0 1px var(--gip-primary);
}
.gip-calendar-day-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.35rem;
}
.gip-calendar-day-number {
  display: block;
  color: var(--gip-muted);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
}
.gip-calendar-day.is-today .gip-calendar-day-number {
  color: var(--gip-primary);
  font-weight: 700;
}
.gip-calendar-today-badge {
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: var(--gip-primary);
  color: var(--gip-surface);
  font-size: 0.58rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.gip-calendar-event {
  display: block;
  margin: 0.2rem 0;
  padding: 0.25rem 0.35rem;
  border-left: 3px solid var(--gip-primary);
  border-radius: 0.25rem;
  background: var(--gip-primary-soft);
  color: var(--gip-ink);
  font-size: 0.68rem;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  width: 100%;
  text-align: left;
}
button.gip-calendar-event {
  cursor: pointer;
  font: inherit;
  border-top: none;
  border-right: none;
  border-bottom: none;
}
.gip-calendar-event.is-paid,
.gip-dashboard-agenda-item.is-paid {
  border-left-color: var(--gip-positive);
}
.gip-calendar-event.is-overdue,
.gip-dashboard-agenda-item.is-overdue {
  border-left-color: var(--gip-negative);
}
.gip-calendar-event.is-partial,
.gip-dashboard-agenda-item.is-partial {
  border-left-color: var(--gip-warning);
}
@media (max-width: 700px) {
  .gip-calendar-day {
    min-height: 5.5rem;
    padding: 0.25rem;
  }
  .gip-calendar-event {
    border-left-width: 2px;
    font-size: 0.68rem;
  }
  .gip-calendar-heading {
    font-size: 0.66rem;
  }
  .gip-calendar-day-head {
    margin-bottom: 0.2rem;
  }
  .gip-calendar-today-badge {
    padding: 0.03rem 0.25rem;
    font-size: 0.56rem;
    letter-spacing: 0;
  }
}
.gip-empty-state strong {
  color: var(--gip-ink);
}
.gip-empty-state span {
  margin-top: 0.35rem;
  font-size: 0.82rem;
}
.gip-dropdown-divider {
  height: 1px;
  margin: 0.3rem 0;
  background: var(--gip-border);
}
.gip-sidebar-backdrop {
  display: none;
}

.card {
  border: 1px solid var(--gip-border);
  border-radius: 0.75rem;
  background: var(--gip-surface);
  box-shadow: var(--gip-shadow);
  overflow: hidden;
}
.card-body {
  padding: 1.1rem;
}
.card h2.h6 {
  color: var(--gip-muted);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.045em;
  text-transform: uppercase;
}
.card .fs-4 {
  font-weight: 650;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.btn {
  border-radius: 0.5rem;
  font-weight: 600;
}
.btn-primary {
  --bs-btn-color: var(--gip-canvas);
  --bs-btn-bg: var(--gip-primary);
  --bs-btn-border-color: var(--gip-primary);
  --bs-btn-hover-color: var(--gip-canvas);
  --bs-btn-hover-bg: var(--gip-primary-hover);
  --bs-btn-hover-border-color: var(--gip-primary-hover);
  --bs-btn-active-color: var(--gip-canvas);
  --bs-btn-active-bg: var(--gip-primary-hover);
  --bs-btn-active-border-color: var(--gip-primary-hover);
  --bs-btn-disabled-color: var(--gip-canvas);
  --bs-btn-disabled-bg: var(--gip-primary);
  --bs-btn-disabled-border-color: var(--gip-primary);
}
.btn-outline-primary {
  --bs-btn-color: var(--gip-primary);
  --bs-btn-border-color: var(--gip-primary);
  --bs-btn-hover-color: var(--gip-canvas);
  --bs-btn-hover-bg: var(--gip-primary);
  --bs-btn-hover-border-color: var(--gip-primary);
  --bs-btn-active-color: var(--gip-canvas);
  --bs-btn-active-bg: var(--gip-primary);
  --bs-btn-active-border-color: var(--gip-primary);
  --bs-btn-disabled-color: var(--gip-primary);
  --bs-btn-disabled-border-color: var(--gip-primary);
}
.btn-sm {
  border-radius: 0.55rem;
}
.gip-table-actions {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.35rem;
  white-space: nowrap;
}
.gip-action-icon {
  display: inline-grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  min-width: 2rem;
  padding: 0;
  border-radius: 0;
}
.gip-action-icon svg {
  width: 1rem;
  height: 1rem;
}
.gip-action-icon:focus-visible {
  outline: 2px solid var(--gip-primary);
  outline-offset: 2px;
}
.form-control,
.form-select {
  min-height: 2.4rem;
  border-color: var(--gip-border);
  border-radius: 0.5rem;
  background-color: var(--gip-surface);
}
.form-control:focus,
.form-select:focus {
  border-color: var(--gip-primary);
  box-shadow: 0 0 0 0.22rem rgba(var(--bs-primary-rgb), 0.13);
}
.form-label {
  margin-bottom: 0.35rem;
  color: var(--gip-muted);
  font-size: 0.78rem;
  font-weight: 700;
}
form.row {
  padding: 1rem;
  border: 1px solid var(--gip-border);
  border-radius: 0.85rem;
  background: var(--gip-surface);
}

.table {
  --bs-table-bg: transparent;
  --bs-table-hover-bg: var(--gip-surface-subtle);
  --bs-table-color: var(--gip-ink);
  margin-bottom: 0;
  border: 1px solid var(--gip-border);
  border-radius: 0.7rem;
  overflow: hidden;
  box-shadow: var(--gip-shadow);
}
.table.bg-white,
.table > :not(caption) > * > * {
  color: var(--gip-ink);
  background-color: var(--gip-surface) !important;
  box-shadow: none !important;
}
.table > thead > tr > * {
  background-color: var(--gip-surface-subtle) !important;
}
.table-hover > tbody > tr:hover > * {
  color: var(--gip-ink);
  background-color: color-mix(in srgb, var(--gip-primary) 7%, var(--gip-surface)) !important;
}
.table > thead {
  background: var(--gip-surface-subtle);
}
.table > thead th {
  padding: 0.68rem 0.85rem;
  border-bottom-width: 1px;
  color: var(--gip-muted);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  white-space: nowrap;
}
.table > tbody td {
  padding: 0.72rem 0.85rem;
  border-color: var(--gip-border);
  vertical-align: middle;
  font-size: 0.82rem;
}
.table > tbody tr:last-child td {
  border-bottom: 0;
}
.table-responsive {
  overflow: visible;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.42rem 0.62rem;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.025em;
}
.badge::before {
  content: '';
  width: 0.38rem;
  height: 0.38rem;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 16%, transparent);
}
.text-bg-success {
  color: var(--gip-positive) !important;
  background: color-mix(in srgb, var(--gip-positive) 12%, var(--gip-surface)) !important;
  border-color: color-mix(in srgb, var(--gip-positive) 30%, var(--gip-surface));
}
.text-bg-danger {
  color: var(--gip-negative) !important;
  background: color-mix(in srgb, var(--gip-negative) 12%, var(--gip-surface)) !important;
  border-color: color-mix(in srgb, var(--gip-negative) 30%, var(--gip-surface));
}
.text-bg-warning {
  color: var(--gip-warning) !important;
  background: color-mix(in srgb, var(--gip-warning) 14%, var(--gip-surface)) !important;
  border-color: color-mix(in srgb, var(--gip-warning) 32%, var(--gip-surface));
}
.text-bg-info,
.text-bg-primary,
.text-bg-secondary {
  color: var(--gip-muted) !important;
  background: var(--gip-surface-subtle) !important;
  border-color: var(--gip-border);
}

.alert {
  border: 0;
  border-left: 4px solid currentColor;
  border-radius: 0.75rem;
  box-shadow: var(--gip-shadow);
}
.modal-content {
  border: 1px solid var(--gip-border);
  border-radius: 1rem;
  background: var(--gip-surface);
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.24);
}
.modal-header,
.modal-footer {
  border-color: var(--gip-border);
}
.list-group-item {
  border-color: var(--gip-border);
  background: transparent;
}
.progress {
  height: 0.55rem;
  border-radius: 999px;
  background: var(--gip-surface-subtle);
}
/* Barra sin connotación de estado (proporciones, o presupuesto dentro de lo previsto). Usa
   la tinta del tema y no el bg-dark de Bootstrap, que es un #212529 fijo: sobre el riel del
   tema oscuro quedaba prácticamente invisible. */
.gip-bar-neutral {
  background-color: var(--gip-ink);
}
/* El ancho vivía en un style inline del propio input, que la CSP bloquea (style-src 'self'),
   así que nunca se aplicaba y el campo ocupaba toda la celda. */
.cart-quantity {
  display: inline-block;
  width: 6rem;
  text-align: right;
}
.gip-progress-thin {
  height: 6px;
}
.gip-min-w-90 {
  min-width: 90px;
}
.gip-min-w-220 {
  min-width: 220px;
}
.gip-min-w-40 {
  min-width: 40px;
}
.gip-progress-medium {
  height: 8px;
}

/* Diagrama de flujo de módulos (página de Documentación). */
.gip-flow-diagram {
  touch-action: none;
}
.gip-flow-node {
  cursor: grab;
  user-select: none;
}
.gip-flow-node.gip-flow-dragging {
  cursor: grabbing;
}
.gip-flow-node rect {
  fill: var(--gip-surface);
  stroke: var(--gip-border);
  stroke-width: 1;
}
.gip-flow-node.gip-flow-hub rect {
  fill: var(--gip-primary-soft);
  stroke: var(--gip-primary);
}
.gip-flow-node.gip-flow-admin rect {
  stroke-dasharray: 4 3;
}
.gip-flow-node-title {
  fill: var(--gip-ink);
  font-size: 12px;
  font-weight: 600;
}
.gip-flow-node-desc {
  fill: var(--gip-muted);
  font-size: 10px;
}
.gip-flow-arrow {
  fill: none;
  stroke: var(--gip-muted);
  stroke-width: 1.25;
}
.gip-flow-arrow.gip-flow-arrow-admin {
  stroke-dasharray: 3 3;
}
.gip-flow-arrowhead {
  fill: var(--gip-muted);
}
.gip-amount-positive {
  color: var(--gip-positive);
  font-weight: 650;
}
.gip-amount-negative {
  color: var(--gip-negative);
  font-weight: 650;
}

body:not(:has(#app-shell)) {
  background: var(--gip-canvas);
}
.gip-login-card {
  width: 100%;
  max-width: 26rem;
}
.gip-login-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.85rem;
  margin: 0 0 0.75rem;
  font-family: 'IBM Plex Sans', var(--bs-body-font-family);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.gip-login-mark .gip-logo {
  width: 2.3rem;
  height: 2.3rem;
  font-size: 1.15rem;
}

@media (max-width: 991.98px) {
  .gip-dashboard-agenda-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* Apilado: el separador vertical pasa a ser horizontal para que las dos mitades de la
     banda de cobertura sigan leyéndose como bloques distintos. */
  .gip-coverage-body {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
  .gip-coverage-gauge {
    padding-left: 0;
    padding-top: 1rem;
    border-left: 0;
    border-top: 1px solid var(--gip-border);
  }
  .gip-month-grid {
    grid-template-columns: 1fr;
  }
  .gip-month-item {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    gap: 0.15rem 0.75rem;
  }
  .gip-month-value {
    text-align: right;
  }
  .gip-month-delta {
    grid-column: 1 / -1;
  }
  body:has(#app-shell) {
    padding-left: 0;
    padding-top: 3.75rem;
  }
  #app-shell:empty::before {
    width: 14rem;
  }
  #app-shell:empty::after {
    left: 0;
  }
  .gip-mobile-header {
    left: 0;
    padding: 0 1rem;
  }
  .gip-mobile-brand {
    display: flex;
  }
  .gip-mobile-brand .gip-logo {
    display: grid;
    width: 1.85rem;
    height: 1.85rem;
  }
  /* El menú en mobile se opera con el dedo, no con el mouse: la densidad que funciona en
     escritorio deja objetivos de ~34px, por debajo del mínimo táctil recomendado (44px), y
     rótulos de ~9px. Acá se agranda todo el bloque —sin tocar el escritorio— y se ensancha
     el panel para que entren etiquetas largas ("Informes de tareas", "Unidades de medida"),
     dejando ver algo del fondo para que siga siendo evidente que se cierra tocando afuera. */
  .gip-sidebar {
    width: min(17rem, 78vw);
    padding-bottom: 1.5rem;
    transform: translateX(-102%);
    transition: transform 0.22s ease;
    box-shadow: 18px 0 50px rgba(0, 0, 0, 0.24);
  }
  .gip-sidebar.is-open {
    transform: translateX(0);
  }
  .gip-sidebar-close {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    margin-right: -0.5rem;
  }
  /* Objetivo táctil algo mayor en el teléfono, sin llegar a empujar al resto de la barra. */
  .gip-quick-link {
    width: 2.25rem;
    height: 2.25rem;
  }
  .gip-quick-action {
    margin-right: 0.25rem;
  }
  .gip-nav-group + .gip-nav-group {
    margin-top: 0.35rem;
  }
  .gip-nav-toggle {
    min-height: 2.75rem;
    padding: 0.5rem 0.8rem;
    font-size: 0.8rem;
  }
  .gip-nav-chevron {
    font-size: 1.1rem;
  }
  .gip-sidebar .nav-link {
    min-height: 2.75rem;
    margin: 0.1rem 0;
    padding: 0.5rem 0.8rem;
    font-size: 0.95rem;
  }
  .gip-nav-section {
    min-height: 1.9rem;
    margin: 0.7rem 0.8rem 0.2rem;
    padding-top: 0.5rem;
    font-size: 0.7rem;
  }
  /* Con los ítems más altos, el grupo más largo (Comercial: 12 enlaces y 3 separadores)
     ronda los 39rem y quedaba al filo del tope anterior de 40rem, que lo habría recortado. */
  .gip-nav-group.is-open .gip-nav-collapse {
    max-height: 60rem;
  }
  body.gip-sidebar-collapsed .gip-sidebar.is-open {
    transform: translateX(0);
  }
  .gip-sidebar-backdrop.is-visible {
    position: fixed;
    inset: 0;
    z-index: 1039;
    display: block;
    background: rgba(0, 0, 0, 0.48);
    backdrop-filter: blur(2px);
  }
  main.container,
  main.container-fluid {
    padding: 1.4rem 1rem !important;
  }

  /* En pantallas chicas priorizamos legibilidad sobre densidad: la letra compacta que
     funciona con mouse/trackpad en escritorio queda incómoda en un teléfono, sin zoom del
     navegador disponible cuando la app está anclada a la pantalla de inicio (PWA). */
  .table > tbody td {
    font-size: 0.92rem;
  }
  .table > thead th {
    font-size: 0.74rem;
  }
  .badge {
    font-size: 0.76rem;
  }
  .form-label {
    font-size: 0.85rem;
  }
  .card h2.h6 {
    font-size: 0.8rem;
  }
  .gip-empty-state span {
    font-size: 0.88rem;
  }

  /* Tablas como fichas en mobile. Apilar etiqueta sobre valor campo por campo estiraba cada
     fila a media pantalla y dejaba la mitad derecha vacía; acá se reencuadra por importancia:
     qué fue y cuánto en la primera línea, el contexto en una segunda línea atenuada, y el
     estado como pastillas junto a las acciones. Las tablas que declaran data-card-role en sus
     <th> usan ese encuadre (ver enhanceResponsiveTables en layout.js); las que no, caen en el
     modo genérico compacto de más abajo: una línea por campo, etiqueta izquierda y valor
     derecha, en vez de dos líneas. */
  .gip-responsive-table {
    display: block;
    border: 0;
    box-shadow: none;
    background: transparent;
  }
  .gip-responsive-table thead {
    display: none;
  }
  .gip-responsive-table tbody {
    display: grid;
    gap: 0.5rem;
  }
  /* El encabezado de grupo no es una ficha sino el título de las que siguen: sin fondo, sin
     recuadro y sin la etiqueta "Cuenta" que el conversor le pondría a cualquier celda. */
  .gip-responsive-table tbody tr.gip-accounts-group-row {
    display: block;
    background: transparent;
    border: 0;
    border-left: 2px solid var(--gip-accent, var(--gip-border));
    box-shadow: none;
    padding: 0.35rem 0 0.15rem 0.5rem;
    margin-top: 0.35rem;
  }
  .gip-responsive-table tbody tr.gip-accounts-group-row td::before {
    content: none;
  }
  .gip-responsive-table tbody tr.gip-accounts-group-row td {
    display: block;
    padding: 0;
    border: 0;
    /* Las fichas centran su contenido; el título de grupo se alinea a la izquierda, que es
       donde el ojo lo busca al recorrer la lista. */
    text-align: left;
  }

  .gip-responsive-table tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.2rem 0.5rem;
    padding: 0.75rem 0.85rem;
    border: 1px solid var(--gip-border);
    border-radius: 0.7rem;
    background: var(--gip-surface);
    box-shadow: var(--gip-shadow);
  }
  .gip-responsive-table tbody td {
    min-width: 0;
    padding: 0;
    border: 0;
    text-align: left;
    overflow-wrap: anywhere;
  }

  /* Modo genérico (tablas sin data-card-role). */
  .gip-responsive-table tbody td:not([data-card-role]) {
    display: flex;
    flex: 1 1 100%;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    text-align: right;
  }
  .gip-responsive-table tbody td:not([data-card-role])::before {
    content: attr(data-label);
    flex: none;
    color: var(--gip-muted);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-align: left;
    text-transform: uppercase;
  }
  .gip-responsive-table tbody td:not([data-card-role]):last-child:has(.btn),
  .gip-responsive-table tbody td:not([data-card-role])[data-label='Acciones'] {
    justify-content: flex-end;
    gap: 0.35rem;
    margin-top: 0.15rem;
    padding-top: 0.55rem;
    border-top: 1px solid var(--gip-border);
  }
  .gip-responsive-table tbody td:not([data-card-role]):last-child:has(.btn)::before,
  .gip-responsive-table tbody td:not([data-card-role])[data-label='Acciones']::before {
    content: none;
  }

  /* Modo ficha (cada <th> declara el papel de su columna). */
  .gip-responsive-table tbody td[data-card-role]::before {
    content: none;
  }
  .gip-responsive-table tbody td[data-card-empty] {
    display: none;
  }
  /* La base porcentual es lo que mantiene identidad e importe en el mismo renglón: el
     quiebre de línea de flexbox se decide con los tamaños base, así que reservando 70% acá
     el resto de la línea queda ocupado y los datos de contexto bajan al renglón siguiente
     (un margin-left:auto en el importe no alcanza: se reparte después de armar la línea). */
  .gip-responsive-table tbody td[data-card-role='identity'] {
    order: 1;
    flex: 1 1 70%;
    color: var(--gip-ink);
    font-size: 0.95rem;
    font-weight: 600;
    line-height: 1.3;
  }
  .gip-responsive-table tbody td[data-card-role='amount'] {
    order: 2;
    flex: none;
    margin-left: auto;
    font-size: 0.95rem;
    font-weight: 650;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
  .gip-responsive-table tbody td[data-card-role='meta'] {
    order: 3;
    flex: 0 1 auto;
    color: var(--gip-muted);
    font-size: 0.8rem;
    line-height: 1.35;
  }
  /* Separador al final del dato y no al principio del siguiente: si el grupo de contexto
     se parte en dos renglones, un "·" colgando al final se lee como continuación, mientras
     que uno al principio del renglón siguiente queda huérfano. */
  .gip-responsive-table tbody td[data-card-role='meta'].gip-card-sep::after {
    content: '·';
    margin-left: 0.45rem;
    opacity: 0.55;
  }
  /* Miniatura (foto de producto): acompaña a la identidad en el primer renglón. */
  .gip-responsive-table tbody td[data-card-role='media'] {
    order: 0;
    display: flex;
    flex: none;
    align-self: center;
  }
  /* Sin importe que lo acompañe, la identidad se queda con el renglón entero: si conservara
     la base del 70% un dato de contexto corto podría colarse en el espacio sobrante. */
  .gip-responsive-table
    tbody
    tr:not(:has(td[data-card-role='amount']))
    td[data-card-role='identity'] {
    flex-basis: 100%;
  }
  /* Descontando el ancho de la miniatura más el gap, la identidad sigue reservando el resto
     del renglón: con base 100% no entraría al lado de la foto y bajaría sola al renglón
     siguiente, dejando la miniatura huérfana arriba. */
  .gip-responsive-table
    tbody
    tr:has(td[data-card-role='media']):not(:has(td[data-card-role='amount']))
    td[data-card-role='identity'] {
    flex-basis: calc(100% - 2.5rem);
  }
  .gip-responsive-table tbody td[data-card-role='badge'] {
    order: 4;
    flex: none;
  }
  /* Dato secundario que sin su etiqueta sería ambiguo (ej. el saldo de una venta a crédito
     junto al total): renglón propio, etiqueta a la izquierda y valor a la derecha. */
  .gip-responsive-table tbody td[data-card-role='field'] {
    order: 6;
    display: flex;
    flex: 1 1 100%;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    font-variant-numeric: tabular-nums;
  }
  .gip-responsive-table tbody td[data-card-role='field']::before {
    content: attr(data-label);
    flex: none;
    color: var(--gip-muted);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
  }
  .gip-responsive-table tbody td[data-card-role='actions'] {
    order: 5;
    display: flex;
    flex: none;
    gap: 0.3rem;
    margin-left: auto;
  }

  /* Fila de estado vacío / "Cargando…" (una sola celda con colspan). */
  .gip-responsive-table tbody td:only-child {
    flex: 1 1 100%;
    justify-content: center;
    text-align: center;
  }
  .gip-responsive-table tbody td:only-child::before {
    content: none;
  }
}

@media (max-width: 575.98px) {
  .gip-dashboard-hero {
    align-items: flex-start !important;
    flex-direction: column;
    gap: 0.75rem;
  }
  .gip-dashboard-meta {
    width: 100%;
    justify-content: space-between;
  }
  .gip-dashboard-agenda-list {
    grid-template-columns: 1fr;
  }
  h1.h3 {
    font-size: 1.22rem;
  }
  .gip-account-copy,
  .gip-chevron {
    display: none;
  }
  .gip-account-trigger {
    padding: 0.25rem;
  }
  main > .d-flex:first-child {
    align-items: flex-start !important;
    gap: 0.8rem;
  }
  main > .d-flex:first-child:has(.btn) {
    flex-wrap: wrap;
  }
  main > .d-flex:first-child > .btn,
  main > .d-flex:first-child > .d-flex {
    max-width: 100%;
  }
  .row.g-3 > .col-6 {
    width: 100%;
  }
  .gip-responsive-table tbody tr {
    padding: 0.7rem 0.75rem;
  }
  .modal-dialog {
    margin: 0.5rem;
  }
  .modal-body {
    padding: 1rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    transition: none !important;
  }
}

.table,
.table-responsive,
form.row,
.gip-dashboard-agenda,
.gip-dashboard-agenda-item {
  border-radius: 0 !important;
}
.badge {
  border-radius: 0.12rem !important;
}
.gip-icon-button {
  border-radius: 0 !important;
}
.gip-avatar {
  border-radius: 50% !important;
}
.gip-dashboard-footer {
  margin-top: 1.5rem;
  border-top: 1px solid var(--gip-border);
}
.gip-phrase {
  max-width: 640px;
  margin: 0 auto;
}
.gip-phrase p {
  font-size: 0.95rem;
  font-style: italic;
  color: var(--gip-muted);
}
.gip-phrase cite {
  display: block;
  font-style: normal;
  font-size: 0.78rem;
  color: var(--gip-primary);
}
.gip-phrase cite:empty {
  display: none;
}

.gip-pull-refresh {
  position: fixed;
  top: 0.6rem;
  left: 50%;
  z-index: 1051;
  display: grid;
  place-items: center;
  width: 2.3rem;
  height: 2.3rem;
  border-radius: 50%;
  background: var(--gip-surface);
  border: 1px solid var(--gip-border);
  box-shadow: var(--gip-shadow);
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 0);
}
.gip-pull-refresh.is-visible {
  opacity: 1;
}
.gip-pull-refresh-icon {
  display: inline-block;
  color: var(--gip-muted);
  font-size: 1.1rem;
  line-height: 1;
}
.gip-pull-refresh.is-armed .gip-pull-refresh-icon {
  color: var(--gip-primary);
}
.gip-pull-refresh.is-loading .gip-pull-refresh-icon {
  animation: gip-pull-spin 0.7s linear infinite;
}
@keyframes gip-pull-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .gip-pull-refresh.is-loading .gip-pull-refresh-icon {
    animation: none;
  }
}

.gip-cropper-dialog {
  width: min(22rem, 100%);
  padding: 1.25rem;
  border: 1px solid var(--gip-border);
  border-radius: 0.75rem;
  background: var(--gip-surface);
  color: var(--gip-ink);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.25);
  text-align: center;
}
.gip-cropper-dialog h2 {
  margin: 0 0 0.9rem;
  font-size: 1.05rem;
}
/* El tamaño va acá y no en un style inline: la CSP de la aplicación (style-src 'self', sin
   'unsafe-inline') bloquea los atributos style inyectados por innerHTML, y sin alto ni ancho
   el recuadro colapsaba a cero y —con overflow hidden— el ajuste quedaba a ciegas.
   Es redondo a propósito: encuadra exactamente el círculo que después muestra el avatar. */
.gip-cropper-viewport {
  position: relative;
  width: min(15rem, 62vw);
  aspect-ratio: 1;
  margin: 0 auto;
  overflow: hidden;
  border-radius: 50%;
  border: 1px solid var(--gip-border);
  background: var(--gip-surface-subtle);
  touch-action: none;
  cursor: grab;
}
.gip-cropper-viewport:active {
  cursor: grabbing;
}
.gip-cropper-image {
  position: absolute;
  top: 0;
  left: 0;
  max-width: none;
  user-select: none;
  pointer-events: none;
}
.gip-cropper-hint {
  margin: 0.7rem 0 0;
  color: var(--gip-muted);
  font-size: 0.8rem;
}
.gip-cropper-zoom-label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin: 0.85rem 0 0;
  font-size: 0.78rem;
  color: var(--gip-muted);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  text-align: left;
}
.gip-cropper-zoom {
  width: 100%;
}
.gip-profile-photo-button {
  position: relative;
  display: inline-block;
  border-radius: 0.4rem;
}
.gip-profile-photo-button .gip-avatar {
  cursor: pointer;
}

/* Embudo comercial: cuánto hay en juego en cada estado. El orden de las columnas sigue el
   recorrido real del presupuesto (borrador → enviado → aprobado), y lo cerrado va al final
   en tono atenuado, porque ya no exige acción. */
.gip-funnel {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.6rem;
}
/* Sin esquinas redondeadas a pedido: en una fila de cinco recuadros contiguos, el redondeo
   los separa visualmente más de lo que ayuda. La franja superior sigue distinguiendo abierto,
   ganado y perdido. */
.gip-funnel-step {
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--gip-border);
  border-top: 3px solid var(--gip-border);
}
.gip-funnel-step.is-open {
  border-top-color: var(--gip-ink);
}
.gip-funnel-step.is-won {
  border-top-color: var(--gip-positive);
}
.gip-funnel-step.is-lost {
  border-top-color: var(--gip-negative);
}
.gip-funnel-label {
  display: block;
  color: var(--gip-muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.gip-funnel-amount {
  display: block;
  margin-top: 0.15rem;
  font-size: 1.05rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.gip-funnel-count {
  display: block;
  color: var(--gip-muted);
  font-size: 0.78rem;
}

@media (max-width: 575.98px) {
  .gip-mobile-brand strong {
    display: none;
  }
  .gip-quick-links {
    gap: 0.1rem;
  }
}

/* iOS hace zoom automático al enfocar un campo cuya letra mide menos de 16px, y después deja
   la pantalla corrida. Las variantes -sm de Bootstrap quedan en 14px, así que en pantallas
   táctiles se fuerzan a 16px: es la única forma de evitarlo sin desactivar el zoom con
   maximum-scale/user-scalable, que impediría al usuario acercar la pantalla cuando lo
   necesita (y que iOS igual ignora desde hace varias versiones). */
@media (max-width: 991.98px) {
  .form-control,
  .form-select,
  .form-control-sm,
  .form-select-sm,
  input,
  select,
  textarea {
    font-size: 16px;
  }
}

/* La frase pasó del pie al encabezado: se lee al abrir el panel en vez de quedar al final de
   tres pantallas de scroll. Arriba tiene que pesar menos que las cifras, así que va alineada
   a la izquierda y con un filete al costado, en lugar del bloque centrado que era al cierre. */
.gip-phrase-top {
  max-width: none;
  margin: 0;
  padding-left: 0.9rem;
  border-left: 2px solid var(--gip-border);
  text-align: left;
}
/* En el panel la frase va arriba de todo, así que el alto importa: la mayoría son cortas y
   dejar el autor en su propia línea gastaba el doble de espacio para media línea de texto.
   Acá el párrafo y la firma van en línea, y solo cae abajo si la frase no entra a lo ancho. */
.gip-phrase-top p {
  display: inline;
  font-size: 0.9rem;
}
.gip-phrase-top cite {
  display: inline;
  margin-left: 0.4rem;
  white-space: nowrap;
}

/* Código QR para vincular WhatsApp. El tamaño se acota acá y no en el atributo style del
   elemento porque la política de seguridad de contenido descarta los estilos en línea. */
.gip-whatsapp-qr {
  display: block;
  width: 100%;
  max-width: 17rem;
  height: auto;
  background: #ffffff;
}

/* Encabezado de grupo en el saldo por cuenta, cuando las cuentas están clasificadas por
   finalidad. Se marca con un filete al costado en vez de teñir la fila entera: son separadores,
   no datos que compitan con los saldos. */
.gip-accounts-group-row td {
  border-top: 1px solid var(--gip-border);
  border-left: 2px solid var(--gip-accent, var(--gip-border));
  padding-top: 0.6rem;
  padding-left: 0.5rem;
}
.gip-accounts-group-row td > span {
  display: inline-block;
}
.gip-accounts-group-name {
  font-weight: 600;
}
.gip-accounts-group-total {
  float: right;
  font-weight: 600;
}
/* La deuda del grupo, en su propia línea debajo del disponible. Va con más especificidad que
   la regla de arriba (`td > span { display: inline-block }`) porque necesita ser un bloque
   propio: al lado del total se leería como si fuera parte del mismo número. */
.gip-accounts-group-row td > span.gip-accounts-group-note {
  display: block;
  clear: both;
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--gip-muted);
}

/* Anchos que antes vivían en atributos `style` del HTML. No pueden ir ahí: la política de
   seguridad de contenido (style-src 'self', sin 'unsafe-inline') descarta los estilos en
   línea, y el navegador deja el atributo en el HTML pero nunca lo aplica. El síntoma era una
   página "estirada": un <main class="container-fluid" style="max-width: 720px"> se dibujaba a
   todo el ancho, y el logo de la empresa a su tamaño natural de 1024 píxeles. */
/* Llevan el selector de elemento a propósito: `main.container-fluid` (más arriba en este
   archivo) fija 1520px, y una clase suelta perdería por especificidad. */
main.gip-page-narrow {
  max-width: 720px;
}
main.gip-page-medium {
  max-width: 900px;
}
main.gip-page-wide {
  max-width: 1100px;
}

.gip-w-100 {
  width: 100px;
}
.gip-w-120 {
  width: 120px;
}
.gip-min-w-160 {
  min-width: 160px;
}
.gip-min-w-200 {
  min-width: 200px;
}
.gip-max-w-140 {
  max-width: 140px;
}
.gip-max-w-160 {
  max-width: 160px;
}

/* Vista previa del logo de la empresa: recuadro fijo con la imagen contenida adentro. */
.gip-logo-preview {
  width: 96px;
  height: 96px;
  overflow: hidden;
}
.gip-logo-preview-img {
  max-width: 100%;
  max-height: 100%;
}

/* Vistas de semana y de día de la agenda. No son un componente aparte: reutilizan la grilla
   del mes (.gip-calendar) en una sola columna, así que comparten el recuadro, las líneas entre
   días, el resaltado de hoy y las pastillas de los eventos. Lo único que cambia es que acá hay
   lugar para el nombre del día y para el detalle de cada evento. */
.gip-calendar.is-stacked {
  grid-template-columns: minmax(0, 1fr);
}
.gip-calendar.is-stacked .gip-calendar-day {
  /* En una sola columna el borde derecho sobraría, y la altura mínima de la grilla dejaría un
     hueco enorme en los días con un solo compromiso. */
  border-right: 0;
  min-height: 0;
  padding: 0.6rem 0.7rem;
}
.gip-calendar.is-stacked .gip-calendar-day:last-child {
  /* El borde del contenedor ya cierra abajo: sin esto queda una línea doble. */
  border-bottom: 0;
}
.gip-calendar.is-stacked .gip-calendar-day-number {
  /* Acá el encabezado dice el día entero ("domingo 23 de agosto"), no un número suelto, y el
     locale lo devuelve en minúscula. */
  font-size: 0.85rem;
  font-weight: 600;
  text-transform: capitalize;
}
.gip-calendar-day-empty {
  margin: 0;
  color: var(--gip-muted);
  font-size: 0.8rem;
}
/* Con el ancho completo disponible, el evento entra entero en vez de recortarse, y muestra el
   detalle debajo en lugar de esconderlo en el title del navegador. */
.gip-calendar.is-stacked .gip-calendar-event {
  white-space: normal;
  padding: 0.35rem 0.5rem;
}
.gip-agenda-event-title {
  display: block;
}
.gip-agenda-event-detail {
  display: block;
  font-size: 0.78rem;
  opacity: 0.85;
}

/* Selector de vista y atajo a hoy. */
.gip-agenda-views {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

/* Zona para arrastrar y soltar imágenes (Mejora continua). Es un botón de verdad, no un div
   con onclick: así funciona con teclado y con lector de pantalla sin nada extra. El estado
   .is-over lo pone el JS mientras hay algo arrastrado encima. */
.gip-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  width: 100%;
  padding: 1.25rem 1rem;
  border: 1.5px dashed var(--gip-border);
  border-radius: 0.5rem;
  background: var(--gip-surface-subtle);
  color: var(--gip-ink);
  cursor: pointer;
  transition:
    border-color 0.2s ease,
    background-color 0.2s ease;
}
.gip-dropzone:hover,
.gip-dropzone:focus-visible {
  border-color: var(--gip-muted);
}
.gip-dropzone.is-over {
  border-color: var(--gip-ink);
  border-style: solid;
  background: var(--gip-primary-soft);
}
.gip-dropzone-text {
  font-weight: 500;
}

/* Galería de miniaturas. Cada una es el visor de fotos que ya existe (data-photo-preview-url)
   más una cruz para quitarla. Las que todavía no se subieron van marcadas. */
.gip-photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
  gap: 0.5rem;
}
.gip-photo-grid:empty {
  display: none;
}
.gip-photo-tile {
  position: relative;
  aspect-ratio: 1;
  border: 1px solid var(--gip-border);
  border-radius: 0.4rem;
  overflow: hidden;
  background: var(--gip-surface);
}
.gip-photo-tile > button.gip-photo-open {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
}
.gip-photo-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.gip-photo-tile .gip-photo-remove {
  position: absolute;
  top: 0.2rem;
  right: 0.2rem;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  line-height: 1;
  border-radius: 999px;
}
.gip-photo-tile.is-pending::after {
  content: 'pendiente';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0.1rem 0;
  font-size: 0.65rem;
  text-align: center;
  color: var(--gip-canvas);
  background: rgba(23, 22, 15, 0.7);
}
