/**
 * Lineage — canvas styling. (rev 3)
 *
 * Deliberately the same visual language as Circuit: one bordered box with a
 * toolbar across the top, a grid surface, and cards on a transformed layer.
 * Colours resolve through Filament's own palette variables, so a panel theme
 * change carries through without touching this file, and chrome (buttons) is
 * rendered with Filament's own Blade components rather than styled here.
 *
 * Everything inside a card is a Filament schema and is already themed; this
 * file styles the surface the cards sit on, not their contents.
 */

.fi-lineage {
    --fi-lineage-surface: var(--gray-50, #f9fafb);
    --fi-lineage-border: var(--gray-200, #e5e7eb);
    --fi-lineage-grid: var(--gray-200, #e5e7eb);
    --fi-lineage-node-bg: #ffffff;
    --fi-lineage-text: var(--gray-950, #030712);
    --fi-lineage-edge: var(--gray-400, #9ca3af);
    --fi-lineage-muted: var(--gray-500, #6b7280);
    --fi-lineage-accent: var(--primary-600, #2563eb);
    /* The wash over whatever is out of view on the minimap. Grey in both
       themes rather than the panel background: over a light panel, the
       background would wash the cards out towards white instead of greying
       them, which reads as "blank" rather than "not looking at this". */
    --fi-lineage-scrim: color-mix(in srgb, var(--gray-500, #6b7280) 62%, transparent);

    display: flex;
    flex-direction: column;
    /* The canvas has no intrinsic width (its children are absolute), so
       content-sized contexts like the infolist entry grid would collapse it. */
    width: 100%;
    min-width: 0;
    /* The height lives on the box, not the surface below: the surface is a
       flex child, and `flex: 1` zeroes its basis, so a height set there is
       ignored and the canvas collapses. */
    height: var(--fi-lineage-height, 32rem);
    overflow: hidden;
    border: 1px solid var(--fi-lineage-border);
    border-radius: 0.75rem;
    background-color: var(--fi-lineage-surface);
    color: var(--fi-lineage-text);
}

:where(.dark) .fi-lineage {
    --fi-lineage-surface: var(--gray-900, #111827);
    --fi-lineage-border: var(--gray-700, #374151);
    --fi-lineage-grid: var(--gray-800, #1f2937);
    --fi-lineage-node-bg: var(--gray-800, #1f2937);
    --fi-lineage-text: var(--gray-50, #f9fafb);
    --fi-lineage-edge: var(--gray-600, #4b5563);
    --fi-lineage-muted: var(--gray-400, #9ca3af);
    --fi-lineage-scrim: color-mix(in srgb, var(--gray-950, #030712) 72%, transparent);
}

[x-cloak] {
    display: none !important;
}

/* ── Toolbar ──────────────────────────────────────────────────────────────── */

.fi-lineage-toolbar {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.5rem;
    border-bottom: 1px solid var(--fi-lineage-border);
    background-color: var(--fi-lineage-node-bg);
}

.fi-lineage-toolbar-spacer {
    flex: 1;
}

/* ── Surface ──────────────────────────────────────────────────────────────── */

.fi-lineage-surface {
    position: relative;
    flex: 1;
    min-height: 0;
    overflow: hidden;
    cursor: grab;
    touch-action: none;
    /* Without this, panning drags a text selection across the whole page. */
    user-select: none;
    -webkit-user-select: none;
}

.fi-lineage-surface-panning {
    cursor: grabbing;
}

.fi-lineage-grid {
    position: absolute;
    inset: 0;
    background-image:
        linear-gradient(to right, var(--fi-lineage-grid) 1px, transparent 1px),
        linear-gradient(to bottom, var(--fi-lineage-grid) 1px, transparent 1px);
    opacity: 0.6;
    pointer-events: none;
}

/*
 * No `will-change: transform` here, on purpose. Promoting this to its own
 * compositor layer makes the browser rasterise it once at the current scale
 * and then stretch that bitmap, so every card and label goes soft as soon as
 * you zoom. Without the hint the layer is re-rasterised at the new scale and
 * text stays sharp.
 *
 * Promoting it only while panning was tried, on the theory that a pan holds
 * the scale steady so the cached bitmap would be sharp anyway. It measured as
 * noise (38ms against 40ms at 76 cards): the per-frame cost here is not paint
 * but style and layout across the mounted cards, which compositing does not
 * touch. What actually moves that number is mounting fewer cards — see the
 * `x-if` on each node in tree.blade.php.
 */
.fi-lineage-layer {
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0 0;
}

/* ── Edges ────────────────────────────────────────────────────────────────── */

.fi-lineage-edges {
    position: absolute;
    inset-inline-start: 0;
    inset-block-start: 0;
    overflow: visible;
    pointer-events: none;
}

.fi-lineage-edge {
    stroke: var(--fi-lineage-edge);
    stroke-width: 1.5;
    stroke-linejoin: round;
}

/* ── Nodes ────────────────────────────────────────────────────────────────── */

.fi-lineage-node {
    position: absolute;
    inset-inline-start: 0;
    inset-block-start: 0;
}

/*
 * Overflow is visible so a node schema can deliberately break the box — an
 * avatar straddling the top edge, most obviously. Keeping content inside the
 * declared height is the schema's job (`lineClamp`), because the layout is
 * pure arithmetic and has no measuring pass to notice a card that grew.
 */
.fi-lineage-card {
    display: block;
    width: 100%;
    height: 100%;
    padding: 0.75rem;
    border-radius: 0.75rem;
    border: 1px solid var(--fi-lineage-border);
    background-color: var(--fi-lineage-node-bg);
    box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
    text-decoration: none;
    color: inherit;
    transition: border-color 100ms ease;
}

a.fi-lineage-card:hover {
    border-color: var(--fi-lineage-accent);
}

a.fi-lineage-card:focus-visible {
    outline: 2px solid var(--fi-lineage-accent);
    outline-offset: 2px;
}

/*
 * Opt-in for a node entry that should break the card's box — an avatar
 * straddling the top edge being the obvious case. Filament's text item is a
 * `-webkit-box` with `overflow: hidden` (its line-clamp machinery) and clips
 * such content by default; freeing every entry instead would break
 * `lineClamp()` everywhere, so this is deliberately opt-in per entry:
 *
 *     TextEntry::make('avatar')->extraAttributes(['class' => 'fi-lineage-bleed'])
 */
.fi-lineage-card .fi-lineage-bleed,
.fi-lineage-card .fi-lineage-bleed .fi-in-text {
    overflow: visible;
}

/*
 * Optional avatar slot: a circle straddling the card's top edge, which is also
 * what hides the end of the connector coming down from the parent — edges are
 * drawn beneath the cards, so the ring in the card's own colour masks the join.
 *
 * Geometry lives here rather than in the consuming app because it is measured
 * against the card: the rise is the overhang plus the card's own padding, since
 * the margin starts at the content box, not the border.
 */
.fi-lineage-avatar {
    --fi-lineage-avatar-overhang: 0.75rem;

    display: flex;
    justify-content: center;
    margin-block-start: calc(-1 * (var(--fi-lineage-avatar-overhang) + 0.75rem));
    margin-block-end: 0.25rem;
}

.fi-lineage-avatar > * {
    border-radius: 9999px;
    box-shadow: 0 0 0 3px var(--fi-lineage-node-bg);
}

/*
 * Initials, for the records with no picture. Sized to match Filament's own
 * `lg` avatar so the two stand in for each other without the row shifting,
 * and coloured from the panel palette so it follows a theme change.
 */
.fi-lineage-avatar-fallback {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    background-color: var(--fi-lineage-avatar-bg, var(--primary-400, #a5b4fc));
    color: var(--fi-lineage-avatar-color, var(--gray-800, #1f2937));
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1;
}

.fi-lineage-toggle {
    position: absolute;
    inset-block-end: -0.6875rem;
    inset-inline-start: 50%;
    translate: -50% 0;
    display: grid;
    place-items: center;
    width: 1.375rem;
    height: 1.375rem;
    border-radius: 9999px;
    border: 1px solid var(--fi-lineage-border);
    background-color: var(--fi-lineage-node-bg);
    font-size: 0.875rem;
    line-height: 1;
    color: var(--gray-600, #4b5563);
    transition: border-color 100ms ease, color 100ms ease;
}

:where(.dark) .fi-lineage-toggle {
    color: var(--gray-300, #d1d5db);
}

.fi-lineage-toggle:hover {
    border-color: var(--fi-lineage-accent);
    color: var(--fi-lineage-accent);
}

.fi-lineage-toggle:focus-visible {
    outline: 2px solid var(--fi-lineage-accent);
    outline-offset: 1px;
}

/* ── Minimap ──────────────────────────────────────────────────────────────── */

.fi-lineage-minimap {
    position: absolute;
    inset-inline-end: 0.75rem;
    bottom: 0.75rem;
    width: var(--fi-lineage-minimap-w, 160px);
    height: var(--fi-lineage-minimap-h, 110px);
    border: 1px solid var(--fi-lineage-border);
    border-radius: 0.5rem;
    background-color: color-mix(in srgb, var(--fi-lineage-node-bg) 85%, transparent);
    overflow: hidden;
    pointer-events: none;
}

/* Explicit width and height rather than `inset`: an <svg> with neither falls
   back to its intrinsic 300x150 and spills out of the frame. The 10px inset
   matches the margin the blocks carry, so a line ends at a block's centre. */
.fi-lineage-minimap-edges {
    position: absolute;
    top: 10px;
    left: 10px;
    width: calc(var(--fi-lineage-minimap-w, 160px) - 22px);
    height: calc(var(--fi-lineage-minimap-h, 110px) - 22px);
    stroke: var(--fi-lineage-edge);
    stroke-width: 1;
    stroke-linecap: round;
    opacity: 0.85;
}

.fi-lineage-minimap-node {
    position: absolute;
    margin: 10px;
    border-radius: 2px;
    background-color: var(--fi-lineage-accent);
    opacity: 0.75;
}

/* What the viewer is looking at — marked by fading everything that is NOT.
   The element is the clear window; the huge spread shadow is the scrim over
   the rest, clipped by the minimap's own rounded frame. Tinting the window
   instead would fade the very cards the frame exists to point at. */
.fi-lineage-minimap-viewport {
    position: absolute;
    margin: 10px;
    border: 1px solid var(--fi-lineage-muted);
    border-radius: 2px;
    box-shadow: 0 0 0 9999px var(--fi-lineage-scrim);
    pointer-events: none;
}
