/**
 * DarkMatterVPN visitor IP card.
 *
 * Everything is scoped under .dm-ipcard so it cannot leak into the Elementor layout it
 * sits in. Colours follow the site's existing purple (#8a2be2); the red/green states are
 * semantic and deliberately separate from that accent so "exposed" reads as a warning
 * rather than as brand colour.
 */

.dm-ipcard {
    --dm-surface: #12121c;
    --dm-surface-2: #1a1a28;
    --dm-border: rgba(138, 43, 226, 0.22);
    --dm-accent: #8a2be2;
    --dm-text: #f2f2f7;
    --dm-muted: #9a9ab4;
    --dm-good: #22c55e;
    --dm-bad: #f43f5e;

    /* Tinted background / border / glow are carried as their own tokens rather than
       derived with color-mix(), which silently drops the whole declaration on browsers
       that do not support it - taking the pill's background with it. */
    --dm-state: var(--dm-muted);
    --dm-state-bg: rgba(154, 154, 180, 0.12);
    --dm-state-border: rgba(154, 154, 180, 0.38);
    --dm-state-glow: rgba(154, 154, 180, 0.55);

    box-sizing: border-box;
    max-width: 860px;
    margin: 0 auto;
    padding: clamp(20px, 3.2vw, 34px);
    border: 1px solid var(--dm-border);
    border-radius: 18px;
    background:
        radial-gradient(120% 140% at 0% 0%, rgba(138, 43, 226, 0.14) 0%, transparent 58%),
        linear-gradient(160deg, var(--dm-surface-2) 0%, var(--dm-surface) 100%);
    box-shadow: 0 18px 46px rgba(0, 0, 0, 0.42);
    color: var(--dm-text);
    text-align: left;
}

.dm-ipcard *,
.dm-ipcard *::before,
.dm-ipcard *::after { box-sizing: border-box; }

.dm-ipcard.dm-state-hidden {
    --dm-state: var(--dm-good);
    --dm-state-bg: rgba(34, 197, 94, 0.12);
    --dm-state-border: rgba(34, 197, 94, 0.40);
    --dm-state-glow: rgba(34, 197, 94, 0.55);
}

.dm-ipcard.dm-state-exposed {
    --dm-state: var(--dm-bad);
    --dm-state-bg: rgba(244, 63, 94, 0.12);
    --dm-state-border: rgba(244, 63, 94, 0.40);
    --dm-state-glow: rgba(244, 63, 94, 0.55);
}

/* ---------- status ---------- */

.dm-ipcard__status {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 7px 15px 7px 12px;
    border: 1px solid var(--dm-state-border);
    border-radius: 999px;
    background: var(--dm-state-bg);
    font-size: 0.95rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--dm-state);
}

.dm-ipcard__dot {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--dm-state);
    box-shadow: 0 0 0 0 var(--dm-state-glow);
}

/* Only the resolved states pulse - a pulsing "checking" dot competes with the spinner
   feeling of the page still loading. */
.dm-ipcard.dm-ready .dm-ipcard__dot { animation: dm-pulse 2.4s ease-out infinite; }

@keyframes dm-pulse {
    0%   { box-shadow: 0 0 0 0 var(--dm-state-glow); }
    70%  { box-shadow: 0 0 0 9px rgba(0, 0, 0, 0); }
    100% { box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
}

@media (prefers-reduced-motion: reduce) {
    .dm-ipcard.dm-ready .dm-ipcard__dot { animation: none; }
}

.dm-ipcard__substatus {
    margin: 14px 0 0;
    max-width: 62ch;
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--dm-muted);
}

/* ---------- headline IP ---------- */

.dm-ipcard__primary {
    margin-top: 22px;
    padding-top: 22px;
    border-top: 1px solid rgba(255, 255, 255, 0.07);

    /* The address is sized from THIS box's width, not the viewport's. The card sits in a
       hero column, so viewport units size it against a width it does not have - at 768px
       the column is ~286px while 5.2vw assumes 768px. Container units track the real box.
       Must stay paired with the cqw values below; if this declaration is ever lost, cqw
       falls back to the small viewport and the clamp max is all that limits the damage. */
    container-type: inline-size;
}

.dm-ipcard__label {
    display: block;
    margin-bottom: 7px;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--dm-muted);
}

/**
 * IPv4 is at most 15 characters; IPv6 is up to 39. They cannot share a type size, so the
 * script tags the element .dm-ip--v4 / .dm-ip--v6 from the version the edge reports.
 *
 * Sizes are in cqw against .dm-ipcard__primary. The coefficients assume the WIDEST font in
 * the stack: ui-monospace resolves to SF Mono on Apple, Roboto Mono on Android and
 * Liberation Mono on Linux, all 0.600em advance. Only Windows reaches Consolas at 0.550em.
 * Tuning against Consolas would overflow every non-Windows device by ~9%.
 *   v4 at 10cqw: 15 chars x 0.6 x 0.10 = 0.90 of the box - fits on one line at any width.
 *   v6 at  8cqw: 39 chars x 0.6 x 0.08 = 1.87 of the box - two lines, breaking at a colon.
 * Below a ~267px container the 1.5rem floor takes over and a very narrow phone can reach
 * three lines; that is a deliberate legibility floor, not an oversight.
 */
.dm-ipcard__ip {
    display: block;
    max-width: 100%;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
    font-size: clamp(1.5rem, 10cqw, 2.85rem);
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.015em;
    font-variant-numeric: tabular-nums;
    color: #fff;
    /* NOT break-all: the script inserts <wbr> after each colon, so an IPv6 address breaks
       between hextets. break-all would ignore those and tear mid-group instead. */
    overflow-wrap: break-word;
}

.dm-ipcard__ip.dm-ip--v6 {
    font-size: clamp(1.5rem, 8cqw, 2.6rem);
    letter-spacing: -0.02em;
}

/* Pre-container-query engines (Chrome <105, Safari <16, Firefox <110). cqw cannot be
   emulated, so rather than tuned vw values that are wrong at some widths, take a fixed
   conservative size that fits the narrowest column the card is used in. */
@supports not (font-size: 1cqw) {
    .dm-ipcard__ip            { font-size: 1.75rem; }
    .dm-ipcard__ip.dm-ip--v6  { font-size: 1.35rem; }
}

.dm-ipcard__ipmeta {
    display: inline-block;
    margin-top: 10px;
    padding: 3px 10px;
    border: 1px solid var(--dm-border);
    border-radius: 6px;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--dm-accent);
}

/* ---------- detail grid ---------- */

.dm-ipcard__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
    gap: 22px 26px;
    margin-top: 26px;
    padding-top: 24px;
    border-top: 1px solid rgba(255, 255, 255, 0.07);
}

.dm-ipcard__value {
    display: block;
    font-size: 1.02rem;
    font-weight: 600;
    line-height: 1.4;
    color: var(--dm-text);
    overflow-wrap: anywhere;
}

/* Placeholder text stays visibly provisional until the fetch lands, so a stalled request
   never reads as a real answer.
   Browser, OS and device are exempt: they come from the browser itself, are final before
   any network call, and are painted immediately. Dimming a finished answer for the length
   of a round trip would read as "still loading". */
.dm-ipcard:not(.dm-ready) .dm-ipcard__ip,
.dm-ipcard:not(.dm-ready) .dm-ipcard__value:not(.dm-ipcard__value--local) {
    color: var(--dm-muted);
    opacity: 0.55;
}

@media (max-width: 480px) {
    .dm-ipcard { padding: 20px 17px; border-radius: 14px; }
    .dm-ipcard__grid { gap: 18px; }
}

/* ---------- self-hosted location map ----------
 *
 * Replaces two Google Maps iframes that sent the visitor's coordinates, IP and Referer to
 * Google on every page view. The land silhouette is dm-world.svg, generated from Natural
 * Earth 4.1.0 (public domain), served first-party. No tiles, no keys, no third party.
 *
 * The map is deliberately coarse. Cloudflare gives a city centroid at best, so a
 * street-level map would imply precision that does not exist.
 */

/* Its own copy of the state tokens: the map is a sibling of the card, not a child, so
   nothing cascades into it. Values match .dm-ipcard exactly. */
.dm-map                    { --dm-state: #8a2be2; }
.dm-map.dm-state-hidden    { --dm-state: #22c55e; }
.dm-map.dm-state-exposed   { --dm-state: #f43f5e; }

.dm-map {
    position: relative;
    width: 100%;
    aspect-ratio: 2 / 1;
    margin-top: 22px;
    border: 1px solid var(--dm-border, rgba(138, 43, 226, 0.22));
    border-radius: 14px;
    background: #0b0b14;
    overflow: hidden;
}

/* aspect-ratio is well supported, but a fallback keeps the box from collapsing to zero
   height on older engines - which would also divide by zero when scaling the pin. */
@supports not (aspect-ratio: 2 / 1) {
    .dm-map { height: 220px; }
}

.dm-map__svg {
    display: block;
    width: 100%;
    height: 100%;
}

.dm-map__land {
    fill: #33334d;
}

.dm-map__pin {
    fill: var(--dm-state, #8a2be2);
    stroke: #fff;
    stroke-width: 0;
}

.dm-map__halo {
    fill: var(--dm-state, #8a2be2);
    opacity: 0.22;
}

.dm-map--ready .dm-map__halo {
    animation: dm-map-pulse 2.8s ease-out infinite;
    transform-box: fill-box;
    transform-origin: center;
}

@keyframes dm-map-pulse {
    0%   { opacity: 0.30; transform: scale(0.75); }
    70%  { opacity: 0;    transform: scale(1.5); }
    100% { opacity: 0;    transform: scale(1.5); }
}

@media (prefers-reduced-motion: reduce) {
    .dm-map--ready .dm-map__halo { animation: none; }
}

.dm-map__caption {
    margin: 8px 2px 0;
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--dm-muted, #9a9ab4);
}
