/* ── HTMX feedback ─────────────────────────────────────────────── */

.htmx-indicator { opacity: 0; transition: opacity .2s; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }
.htmx-swapping { opacity: .45; transition: opacity .12s; }
[aria-busy='true'] { cursor: progress; }

.spin {
  width: 13px; height: 13px; border: 2px solid var(--line);
  border-top-color: var(--blue-deep); border-radius: 50%;
  display: inline-block; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* app.js appends this to whichever control is waiting. The vendored Ákotí
   spinner ships in its own navy; retint it to our palette rather than fork it,
   and let a spinner on a dark button borrow the button's own colour. */
.busy-spin { margin-left: 8px; vertical-align: -2px; }
.akoti-spinner.busy-spin {
  border-color: color-mix(in srgb, var(--slate) 18%, transparent);
  border-top-color: var(--blue-deep);
}
.akoti-btn.primary .busy-spin, .pill.solid .busy-spin,
.tile-slate .busy-spin {
  border-color: rgba(255, 255, 255, .28); border-top-color: #fff;
}
/* Reduced motion: keep the state visible, drop the rotation. */
@media (prefers-reduced-motion: reduce) {
  .akoti-spinner, .akoti-dots i, .akoti-pulse, .spin { animation: none !important; }
  .akoti-spinner.busy-spin { border-top-color: var(--blue-deep); opacity: .75; }
}

/* ── Django messages ───────────────────────────────────────────── */

.msg-tile {
  display: flex; align-items: flex-start; gap: 10px;
  margin-bottom: 14px; font-size: 14.5px;
  border-left: 3px solid var(--bronze);
}
.msg-mark { width: 8px; height: 8px; margin-top: 8px; flex: none;
  border-radius: 5px 2px 5px 2px; background: var(--bronze); }
.msg-text { flex: 1; min-width: 0; overflow-wrap: break-word; }
.msg-close {
  flex: none; background: none; border: 0; cursor: pointer;
  color: var(--muted); font-size: 20px; line-height: 1;
  min-width: 32px; min-height: 32px; border-radius: 8px 3px 8px 3px;
}
.msg-close:hover { background: var(--surface-2); color: var(--ink); }

.msg-tile.tone-success { background: var(--ok-wash); border-left-color: var(--ok); }
.msg-tile.tone-success .msg-mark { background: var(--ok); }
.msg-tile.tone-info, .msg-tile.tone-debug { background: var(--blue-wash); border-left-color: var(--blue-deep); }
.msg-tile.tone-info .msg-mark, .msg-tile.tone-debug .msg-mark { background: var(--blue-deep); }
.msg-tile.tone-warning { background: var(--warn-wash); border-left-color: var(--warn); }
.msg-tile.tone-warning .msg-mark { background: var(--warn); }
.msg-tile.tone-error { background: var(--alert-wash); border-left-color: var(--alert); font-weight: 600; }
.msg-tile.tone-error .msg-mark { background: var(--alert); }

/* ── Toasts ────────────────────────────────────────────────────── */

/* Bottom-centre on a phone (thumb reach, clear of the browser chrome),
   bottom-right once there is a mouse. */
#toasts {
  position: fixed; z-index: 90;
  left: 12px; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 8px; align-items: stretch;
  pointer-events: none;
}
@media (min-width: 560px) {
  #toasts { left: auto; right: 18px; bottom: 18px; align-items: flex-end; }
}
.toast {
  background: var(--ink); color: #fff; padding: 11px 16px;
  border-radius: var(--brick-sm); font-size: 14px; font-weight: 600;
  box-shadow: 0 8px 24px rgba(34,34,42,.22); animation: rise .25s ease;
  pointer-events: auto;
}
.toast.ok { background: var(--ok); }
.toast.bad { background: var(--alert); }
@keyframes rise { from { transform: translateY(10px); opacity: 0; } }

/* Failure toasts stay until dismissed, so they need room and a close button. */
.toast { display: flex; align-items: flex-start; gap: 10px; }
.toast.sticky { max-width: 30rem; font-weight: 500; line-height: 1.5; }
.toast-close {
  flex: none; background: none; border: 0; cursor: pointer;
  color: inherit; opacity: .75; font-size: 19px; line-height: 1;
  min-width: 28px; min-height: 28px; border-radius: 8px 3px 8px 3px;
}
.toast-close:hover { opacity: 1; background: rgba(255, 255, 255, .16); }

/* ── Footer ────────────────────────────────────────────────────── */

/* Small on purpose. This is a personal platform, not a marketing site. */
.footly {
  border-top: 1px solid var(--line-soft);
  padding: 16px 0 calc(20px + env(safe-area-inset-bottom));
  color: var(--muted); font-size: 12.5px;
}
.footly-inner { display: flex; flex-direction: column; gap: 4px; }
@media (min-width: 700px) {
  .footly-inner { flex-direction: row; align-items: center; gap: 14px; }
  .footly-link { margin-left: auto; }
}
.footly-brand { font-family: var(--font-display); font-weight: 900; color: var(--ink); }
.footly-link { color: var(--blue-deep); min-height: 32px; display: inline-flex; align-items: center; }

@media print {
  .topbar, .toc, .nav, .field-foot, .assist, button, #toasts,
  .footly, .msg-tile { display: none !important; }
  .module-block, .tile { border: none; box-shadow: none; page-break-inside: avoid; }
  body { background: #fff; font-size: 11pt; }
}
