/* ============================================================================
   DetectiveCheck — report rendering
   Shared by the in-app report (results.html) and the public sample
   (dashboard.html). Both surfaces call DC.renderReport(), so a change to how
   a finding looks lands on both without either page being edited.
   Layout only; every token and component underneath comes from app.css.
   ============================================================================ */
/* Report layout: findings left, evidence rail right. */
    /* minmax(0, …), not 1fr. `1fr` is `minmax(auto, 1fr)` and the automatic
       minimum is the content's min-content width — so on a 358px phone column
       this laid out a 993px track and two thirds of every card sat outside the
       viewport, invisible and unreachable because html carries overflow: clip.
       The page reported no horizontal scroll the whole time. */
    .rep { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 1.5rem; align-items: start; }
    .rail { position: sticky; top: calc(var(--topbar-h) + 1rem); display: flex; flex-direction: column; gap: 1rem; }

    /* Verdict — the answer before any detail. */
    .verdict { background: var(--ink); color: var(--text-onink); border: 2px solid var(--line); border-radius: var(--r-panel); box-shadow: var(--neo-3); padding: clamp(1.25rem, 2.5vw, 1.875rem); }
    .verdict .vq { display: flex; align-items: center; gap: .75rem; margin-bottom: 1rem; flex-wrap: wrap; }
    .verdict .vq code { font-family: var(--font-display); font-size: clamp(1.0625rem, 1rem + .5vw, 1.375rem); font-weight: 650; background: rgba(255,255,255,.1); border: 1.5px solid rgba(255,255,255,.22); border-radius: var(--r-pill); padding: .3rem .875rem; word-break: break-all; }
    .verdict h1 { font-size: clamp(1.375rem, 1.1rem + 1.2vw, 1.875rem); line-height: 1.25; }
    .verdict p { color: rgba(242,245,250,.78); margin-top: .75rem; max-width: 62ch; }
    .verdict .vmeta { display: flex; gap: 1.5rem; flex-wrap: wrap; margin-top: 1.25rem; padding-top: 1.25rem; border-top: 1px solid rgba(255,255,255,.16); }
    .verdict .vmeta div span { display: block; font-size: .72rem; text-transform: uppercase; letter-spacing: .1em; color: rgba(242,245,250,.55); }
    .verdict .vmeta div strong { font-family: var(--font-display); font-size: 1.25rem; }
    .verdict .meter > i { border-color: rgba(255,255,255,.3); background: rgba(255,255,255,.14); }
    .verdict .meter > i.on { background: #fff; border-color: #fff; }

    .sec { margin-top: 1.5rem; }
    .sec > h2 { font-size: 1.1875rem; margin-bottom: .875rem; display: flex; align-items: center; gap: .5rem; }
    .sec > h2 .count { font-size: .75rem; font-weight: 650; color: var(--text-muted); background: var(--bg-sunken); border: 1.5px solid var(--border-strong); border-radius: var(--r-pill); padding: .1rem .5rem; }

    /* ── Result cards ─────────────────────────────────────────────────────
       Each card was a name, a domain and up to five fields, because the
       renderer read five fixed keys off the account and ignored profile_data
       entirely — which is where whois puts its registrar and name servers,
       Google puts its account id and app list, Duolingo puts a streak, and
       xposedornot puts a password-strength breakdown. Ten cards of almost
       nothing, from an engine that had returned a great deal.

       Now the card holds a table of whatever the checker actually sent, under
       the site's own logo. That changes the constraint from "fit more boxes on
       a screen" to "be wide enough to read a table in". */
    .acc-grid {
        /* Columns, not a grid. A grid puts cards in rows, so a row is as tall
           as its tallest card and every shorter card in it leaves dead space
           underneath — with cards that run from three fields to fifteen, that
           was most of the page.

           CSS columns flow each card into whichever column has room, which is
           what "fill the space that exists" means. `break-inside: avoid` keeps
           a card whole; without it a card splits across a column boundary and
           its table finishes somewhere else entirely.

           DOM order is untouched, so a screen reader still reads #1, #2, #3 in
           order; only the visual order goes down a column before across. */
        /* Two columns, however many rows that takes. Three fitted, and three
           made every card narrow enough that a label like PASSWORD STRENGTH
           wrapped onto two lines next to its value. Two gives the table room.
           One below 900px, where two would be 200px each. */
        /* Both together: at most two columns, and only where each gets at
           least 26rem. With column-count alone and a viewport media query the
           layout ignored its own container — the report column narrows for
           the rail long before the window does, and a 360px container was
           still laying out two 486px columns and overflowing. Setting a width
           as well makes the browser drop to one when two will not fit, with
           no breakpoint to keep in sync. */
        /* Width only, no count. With both set, Chrome kept two columns on a
           358px phone and made them 169px each — narrower than the label
           column of the table inside them. A width on its own lets the browser
           fit as many as will fit: one on a phone, two at every width this
           layout actually reaches, because three 26rem columns need more room
           than the report column ever has. */
        column-width: 26rem;
        column-gap: 1.25rem;
    }
    .acc-grid > * {
        break-inside: avoid;
        /* Safari honours the older name and not the standard one. */
        -webkit-column-break-inside: avoid;
        margin-bottom: 1.25rem;
    }
    .acc {
        background: var(--bg); border: 2px solid var(--line);
        border-radius: var(--r-card); box-shadow: var(--neo-2);
        overflow: hidden; display: flex; flex-direction: column;
    }
    .ahead {
        display: flex; align-items: center; gap: .875rem;
        padding: 1.125rem 1.25rem; border-bottom: 2px solid var(--line);
    }
    .atitle { flex: 1; min-width: 0; }
    .atitle h3 {
        font-family: var(--font-display); font-weight: 700; font-size: 1.125rem;
        letter-spacing: -.02em; line-height: 1.2; overflow-wrap: anywhere;
    }
    .atitle p { font-size: .8125rem; color: var(--text-muted); margin-top: .1875rem; overflow-wrap: anywhere; }
    .anum { flex-shrink: 0; align-self: flex-start; font-family: var(--font-mono); font-size: .75rem; font-weight: 600; color: var(--text-muted); }

    /* The brand mark. --brand is the logo's own colour behind it at low alpha,
       so Spotify reads green and Adobe reads red with no per-brand rule. */
    .amark {
        flex-shrink: 0; width: 44px; height: 44px;
        display: inline-flex; align-items: center; justify-content: center;
        border: 2px solid var(--line); border-radius: 12px; box-shadow: var(--neo-1);
        background: color-mix(in srgb, var(--brand, var(--accent)) 14%, var(--bg));
    }
    .amark img { display: block; }

    /* The category mark sits behind the logo and is revealed only when the
       image fails. Both are in the flow, one hidden, so nothing moves when the
       swap happens — a layout shift on a failed image is worse than the
       failure. */
    .abrand { display: inline-flex; flex-shrink: 0; }
    .amark-fallback { display: none; }
    .abrand.nologo .amark:not(.amark-fallback) { display: none; }
    .abrand.nologo .amark-fallback { display: inline-flex; }

    .apic { display: flex; align-items: center; gap: .875rem; padding: 1rem 1.25rem; border-bottom: 1px solid var(--border); }
    .apic img { width: 56px; height: 56px; object-fit: cover; border: 2px solid var(--line); border-radius: 10px; background: var(--bg-sunken); }
    .apic span { font-size: .8125rem; color: var(--text-secondary); }

    /* Label left, value right, one row per fact: the shape the eye already
       knows from a spec sheet. */
    .afields { display: flex; flex-direction: column; }
    .afields > div {
        display: grid; grid-template-columns: minmax(6rem, 38%) 1fr; gap: 1rem;
        padding: .75rem 1.25rem; border-bottom: 1px solid var(--border); align-items: baseline;
    }
    .afields > div:last-child { border-bottom: 0; }
    .afields dt { font-size: .6875rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--text-muted); overflow-wrap: anywhere; }
    .afields dd { font-size: .9375rem; color: var(--text-primary); overflow-wrap: anywhere; min-width: 0; }
    .afields dd a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

    .anone { padding: 1rem 1.25rem; font-size: .875rem; color: var(--text-secondary); }
    .aconf { padding: .625rem 1.25rem; font-size: .75rem; color: var(--text-muted); border-top: 1px solid var(--border); }

    .alink {
        display: flex; align-items: center; justify-content: center; gap: .5rem;
        margin: 1rem 1.25rem 1.25rem; padding: .8125rem 1rem;
        font-family: var(--font-display); font-weight: 650; font-size: .9375rem;
        border: 2px solid var(--line); border-radius: var(--r-control);
        box-shadow: var(--neo-1); background: var(--bg-sunken);
        transition: box-shadow .2s var(--ease), transform .2s var(--ease-spring);
    }
    .alink:hover { box-shadow: var(--neo-2); transform: translate(-1px,-1px); }
    .alink:active { box-shadow: var(--neo-press); transform: translate(2px,2px); }

    /* A breach card is an account card with a paragraph and a badge list
       instead of a link. Same shell on purpose: to a reader they are the same
       kind of thing — a source, a heading, and what it knows. */
    .bdesc { padding: 1rem 1.25rem; font-size: .875rem; line-height: 1.6; color: var(--text-secondary); border-bottom: 1px solid var(--border); }
    .bclasses { padding: 1rem 1.25rem; display: flex; flex-wrap: wrap; gap: .375rem; }
    .bclabel {
        flex-basis: 100%; margin-bottom: .25rem;
        font-size: .6875rem; font-weight: 700; letter-spacing: .07em;
        text-transform: uppercase; color: var(--text-muted);
    }
    /* The breach mark is a warning glyph, not a brand: there is no logo for
       "the Wattpad breach" and inventing one would dress up bad news. */
    .brc .amark { background: color-mix(in srgb, var(--danger) 14%, var(--bg)); }

    /* Sources that returned something, as chips. The rail answers "was X
       checked"; this answers "what did we get", which is the question people
       ask first — and a row of logos answers it without reading. */
    .schips { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1rem; }
    .schip {
        display: inline-flex; align-items: center; gap: .5rem;
        padding: .3125rem .75rem .3125rem .3125rem;
        background: var(--bg); border: 2px solid var(--line);
        border-radius: var(--r-pill); box-shadow: var(--neo-1);
        font-size: .8125rem; font-weight: 600; white-space: nowrap;
    }
    /* The chip reuses .amark so a brand looks the same wherever it appears,
       at the size a chip needs. */
    .schip .amark { width: 24px; height: 24px; border-width: 1.5px; border-radius: 7px; box-shadow: none; }
    .schip .amark img, .schip .amark svg { width: 15px; height: 15px; }

    /* Category filter. The count is in the tab because a filter that hides how
       much it is hiding makes people click each one to find out. */
    .cattabs { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1.125rem; }
    .cattab {
        display: inline-flex; align-items: center; gap: .4375rem;
        padding: .4375rem .875rem; cursor: pointer;
        background: var(--bg); color: var(--text-secondary);
        border: 2px solid var(--line); border-radius: var(--r-pill);
        box-shadow: var(--neo-1);
        font-family: var(--font-display); font-weight: 650; font-size: .8125rem;
        transition: box-shadow .2s var(--ease), transform .2s var(--ease-spring);
    }
    .cattab span {
        font-size: .6875rem; font-weight: 700;
        background: var(--bg-sunken); border: 1.5px solid var(--border-strong);
        border-radius: var(--r-pill); padding: .05rem .4rem;
    }
    .cattab:hover { box-shadow: var(--neo-2); transform: translate(-1px,-1px); }
    .cattab.on { background: var(--accent); color: #fff; }
    .cattab.on span { background: rgba(255,255,255,.22); border-color: transparent; color: #fff; }

    /* Export. Built in the browser from the report already in memory, so it
       works on a report opened from history with the engine unreachable. */
    .expbar {
        display: flex; flex-wrap: wrap; align-items: center; gap: .625rem;
        margin-top: 1.5rem; padding: 1rem 1.125rem;
        background: var(--bg); border: 2px solid var(--line);
        border-radius: var(--r-card); box-shadow: var(--neo-1);
    }
    .expbar .t-muted { font-size: .8125rem; margin-left: auto; }
    @media (max-width: 560px) { .expbar .t-muted { margin-left: 0; flex-basis: 100%; } }

    /* Breach rows */
    .breach { display: flex; gap: .875rem; padding: .875rem 0; border-bottom: 1px solid var(--border); }
    .breach:last-child { border-bottom: 0; }
    .breach .bico { width: 40px; height: 40px; flex-shrink: 0; border-radius: var(--r-control); border: 2px solid var(--line); background: var(--danger-soft); color: var(--danger); display: flex; align-items: center; justify-content: center; }
    .breach h3 { font-size: .9375rem; }
    .breach p { font-size: .8125rem; color: var(--text-secondary); }
    .dclasses { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .4rem; }

    /* Timeline */
    .tl { position: relative; padding-left: 1.75rem; }
    .tl::before { content: ''; position: absolute; left: 7px; top: 6px; bottom: 6px; width: 2px; background: var(--border-strong); }
    .tl-item { position: relative; padding-bottom: 1.125rem; }
    .tl-item:last-child { padding-bottom: 0; }
    .tl-item::before { content: ''; position: absolute; left: -1.75rem; top: 5px; width: 16px; height: 16px; border-radius: 50%; background: #fff; border: 2px solid var(--line); }
    .tl-item.is-breach::before { background: var(--danger); }
    .tl-item.is-account::before { background: var(--accent); }
    .tl-date { font-family: var(--font-display); font-weight: 700; font-size: .78rem; color: var(--text-muted); }
    .tl-desc { font-size: .875rem; }

    /* Sources */
    .src { display: flex; align-items: center; gap: .625rem; padding: .5rem 0; font-size: .8125rem; border-bottom: 1px solid var(--border); }
    .src:last-child { border-bottom: 0; }
    .src .sname { }
    .src .shit { margin-left: auto; font-weight: 650; font-size: .75rem; }
    .src .shit.yes { color: var(--success); }
    .src .shit.no { color: var(--text-muted); }

    /* Relationship graph, drawn from graph.nodes/edges */
    .graph { width: 100%; height: 260px; display: block; }
    .graph .edge { stroke: var(--border-strong); stroke-width: 1.5; }
    .graph .n-account circle { fill: var(--accent-soft); stroke: var(--accent); }
    .graph .n-breach circle { fill: var(--danger-soft); stroke: var(--danger); }
    .graph .n-query circle { fill: var(--ink); stroke: var(--ink); }
    .graph circle { stroke-width: 2; }
    .graph text { font-family: var(--font-sans); font-size: 9px; fill: var(--text-secondary); text-anchor: middle; }
    .graph .n-query text { fill: var(--text-primary); font-weight: 700; }

    .actions { display: flex; gap: .5rem; flex-wrap: wrap; }

    /* Loading skeleton mirrors the real layout, not a spinner in a box. */
    .load .sk { margin-bottom: .75rem; }

    @media (max-width: 1024px) {
        .rep { grid-template-columns: minmax(0, 1fr); }
        .rail { position: static; }
    }
    @media print {
        /* Export PDF is a button on this page, so what comes out of it is a
           product surface. Before this it inherited the screen theme: a reader
           on dark mode got a document of near-black rectangles with white text,
           which is unreadable on paper and a cartridge on an inkjet.

           The tokens are overridden rather than the elements, so every card,
           chip, badge and table below comes right without a rule of its own —
           the same reason the dark theme is a token swap and not a second
           stylesheet. */
        :root, :root[data-theme="dark"] {
            --bg: #fff; --bg-soft: #fff; --bg-sunken: #f4f6fa;
            --ink: #000; --line: #333;
            --text-primary: #111; --text-secondary: #333; --text-muted: #555;
            --text-onink: #fff;
            --border: #ccc; --border-strong: #999;
        }
        body { background: #fff; }
        body::before { display: none; }

        /* Chrome drops background colours by default. The brand marks and the
           breach badges carry meaning in their fill, so ask for them. */
        * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

        .sidebar, .topbar, .actions, .sidebar-scrim { display: none !important; }
        .app { grid-template-columns: 1fr; }
        .app-body { max-width: none; padding: 0; }

        /* Controls do not belong on paper: a filter nobody can click and an
           export bar inside the exported file. */
        .cattabs, .expbar { display: none !important; }

        /* One column. The rail sits under the report rather than beside it —
           320px of sidebar on an A4 page leaves the cards too narrow to hold
           their tables. */
        .rep { grid-template-columns: minmax(0, 1fr); }
        .rail { position: static; }
        .acc-grid { column-width: auto; column-count: 1; }

        /* Shadows are the neomorphism, and on paper they are grey smudges. */
        .acc, .card, .panel, .verdict, .schip, .expbar { box-shadow: none; }
        .acc, .card, .panel { break-inside: avoid; }
        .sec > h2 { break-after: avoid; }
        .verdict { background: #fff; color: #000; border: 2px solid #333; }
        .verdict p, .verdict .vmeta div span { color: #333; }

        /* A link in print is text; the address is the useful part. */
        .afields dd a::after { content: " (" attr(href) ")"; font-size: .8em; color: #555; }
        .alink { display: none; }
    }
