/* Shared opt-in skeleton. Callers reserve their canonical value slot before first paint. */
.twoip-skeleton {
  display: block; inline-size: var(--twoip-skeleton-width, 70%); max-inline-size: 100%; block-size: 1lh;
  border-radius: var(--twoip-radius-small); background: var(--twoip-surface-container-high);
  animation: twoip-skeleton-pulse 1.6s ease-in-out infinite;
}
[data-twoip-async-state="loading"] .twoip-info-icon { opacity: .45; }
[data-twoip-async-state]:is([data-twoip-async-state="ready"],[data-twoip-async-state="error"]) .twoip-async-content {
  animation: twoip-info-ready 150ms ease-out;
}
@keyframes twoip-skeleton-pulse { 0%, 100% { opacity: .65; } 50% { opacity: 1; } }
@keyframes twoip-info-ready { from { opacity: 0; } to { opacity: 1; } }
/* The reserve retains established geometry; compact text owns clipping/tooltip.
   Real bottom padding keeps intrinsic inline-link borders inside the clip. */
body .ipblock .showip_mod [data-twoip-async-state] .twoip-async-content {
  display: block; box-sizing: border-box; padding-block-end: 2px; min-block-size: calc(1lh + 2px); max-block-size: var(--twoip-info-value-block-size);
}
body .ipblock .showip_mod [data-twoip-async-state] .twoip-async-content[hidden] { display: none; }
body .ipblock .showip_mod :is(.info1,.s-info1) { --twoip-skeleton-width: 80%; }
body .ipblock .showip_mod :is(.info4,.s-info4) { --twoip-skeleton-width: 85%; }
body .ipblock .showip_mod .info6 { --twoip-skeleton-width: 55%; }
@media (prefers-reduced-motion: reduce) {
  .twoip-skeleton, [data-twoip-async-state]:is([data-twoip-async-state="ready"],[data-twoip-async-state="error"]) .twoip-async-content { animation: none; }
}
