/* ============================================================
   shell_nightswim.css — th10 (#446)
   ============================================================
   The "nightswim" shell. Every rule in this file is scoped under
   [data-shell="nightswim"] and is purely additive: the "terminal"
   shell (the default, and every anonymous render) never matches a
   single selector here.

   Load order: LAST, after CSS_CORE and the route's CSS_MANIFEST
   files. See SHELL_CSS in app/common/css_manifest.py for why that
   matters — a shell rule only out-specifies its own base rule, so
   it has to win on source order against dashboard.css / feed.css /
   profile.css rather than restating their full ancestor chains.

   Design premise: zero borders. Every edge in here is one of
     - an inset light-leak (var(--shell-leak)),
     - a blur boundary,
     - or nothing at all.
   Depth replaces geometry. If you find yourself reaching for
   `border:` in this file, the answer is a leak or a fill instead.

   Values live in tokens.css (the single-token-source rule owns
   that); this file only consumes them via var().

   Performance note: real backdrop-filter is used on exactly two
   surfaces — the navbar capsule and the FAB dock. Both are small,
   fixed-position and compositor-friendly. Columns and cards use
   flat rgba fills that read as glass over the aurora WITHOUT
   sampling it, because a 3-column dashboard of live-blurred
   surfaces drops scroll below 60fps on older iPhones. Resist the
   urge to "fix" them by adding backdrop-filter.
   ============================================================ */

/* ============================================================
   The aurora field
   ============================================================ */

/* The page background becomes a fixed, slowly drifting field of
   three palette-derived hues. ::before is the field itself;
   ::after is a grain overlay so the gradients never band on wide
   displays. Both are pointer-events-free and sit behind all
   content via negative z-index. */
[data-shell="nightswim"] body::before {
    content: "";
    position: fixed;
    inset: -22vh -12vw;
    z-index: -2;
    pointer-events: none;
    background:
        radial-gradient(46vw 40vh at 14% 8%,
            color-mix(in srgb, var(--shell-aurora-1) calc(var(--shell-aurora-alpha) * 100%), transparent),
            transparent 62%),
        radial-gradient(52vw 46vh at 88% 22%,
            color-mix(in srgb, var(--shell-aurora-2) calc(var(--shell-aurora-alpha) * 75%), transparent),
            transparent 65%),
        radial-gradient(40vw 44vh at 62% 92%,
            color-mix(in srgb, var(--shell-aurora-3) calc(var(--shell-aurora-alpha) * 55%), transparent),
            transparent 66%);
    filter: blur(var(--shell-aurora-blur)) saturate(1.25);
    animation: nightswim-drift 34s ease-in-out infinite alternate;
}

/* Grain. Inlined SVG turbulence rather than a PNG so it costs no
   extra request and scales to any DPR. */
[data-shell="nightswim"] body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    opacity: 0.32;
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

@keyframes nightswim-drift {
    0% {
        transform: translate3d(0, 0, 0) scale(1);
    }

    50% {
        transform: translate3d(-3%, 2.5%, 0) scale(1.08);
    }

    100% {
        transform: translate3d(2.5%, -2%, 0) scale(1.04);
    }
}

/* ============================================================
   Navbar — a floating capsule dock
   ============================================================ */

[data-shell="nightswim"] .navbar {
    background: transparent;
    padding-inline: var(--space-4);
    margin-top: var(--space-3);
    display: flex;
    justify-content: center;
}

[data-shell="nightswim"] .nav-container {
    border-radius: var(--radius-pill);
    padding: var(--space-2) var(--space-2) var(--space-2) var(--space-5);
    background: color-mix(in srgb, var(--shell-navbar-tint) 55%, transparent);
    backdrop-filter: blur(26px) saturate(1.5);
    box-shadow: var(--shell-leak), var(--shell-lift);
    max-width: 1180px;
}

[data-shell="nightswim"] .nav-icon-btn {
    border-radius: var(--radius-pill);
    transition: transform var(--transition-medium), background var(--transition-medium), color var(--transition-medium);
}

[data-shell="nightswim"] .nav-icon-btn:hover {
    background: var(--shell-glass-strong);
    transform: translateY(-2px);
}

/* The terminal wordmark's hard block cursor becomes a soft pulsing
   dot — same signal, no corners. */
[data-shell="nightswim"] .navbar-brand-cursor {
    width: 7px;
    height: 7px;
    border-radius: var(--radius-pill);
    margin-left: 7px;
    box-shadow: 0 0 12px 2px color-mix(in srgb, var(--color-accent) 70%, transparent);
    animation: nightswim-pulse 2.4s ease-in-out infinite;
}

@keyframes nightswim-pulse {
    50% {
        opacity: 0.35;
        transform: scale(0.75);
    }
}

/* ============================================================
   Dashboard — unframed planes
   ============================================================ */

/* The 2px accent frame is the single loudest box in the app today.
   It goes entirely; separation comes from a soft top-edge wash,
   the leak, and an independent hover lift. overflow is released
   from hidden so the lift's shadow isn't clipped. */
[data-shell="nightswim"] .dashboard-column {
    /* dashboard.css draws this frame as `2px solid var(--color-accent)`,
       straight from the accent rather than through a border token, so
       the kill-switch in tokens.css can't reach it. It's the loudest
       box in the app and the main thing this shell exists to remove.
       Neutralised rather than set to `none` so the 2px of inset
       geometry is preserved and nothing reflows between shells. */
    border-color: transparent;
    border-radius: var(--shell-radius-xl);
    padding: var(--space-2) var(--space-1) var(--space-3);
    overflow: visible;
    background:
        radial-gradient(120% 55% at 50% -6%, var(--shell-glass-strong), transparent 62%),
        var(--shell-glass);
    box-shadow: var(--shell-leak), var(--shell-lift);
    transition: transform var(--transition-slow), box-shadow var(--transition-slow);
}

[data-shell="nightswim"] .dashboard-column:hover {
    transform: translateY(-3px);
    box-shadow:
        var(--shell-leak),
        0 0 0 1px color-mix(in srgb, rgb(var(--color-halo-rgb)) 12%, transparent),
        var(--shell-lift);
}

[data-shell="nightswim"] .dashboard-column-header {
    padding: var(--space-3) var(--space-4);
}

/* A phosphor dot replaces the header's divider rule as the "this
   is a section" signal. */
[data-shell="nightswim"] .dashboard-column-title {
    letter-spacing: 0.2em;
}

[data-shell="nightswim"] .dashboard-column-title::before {
    content: "";
    display: inline-block;
    width: 5px;
    height: 5px;
    border-radius: var(--radius-pill);
    background: var(--color-accent);
    margin-right: 9px;
    vertical-align: middle;
    box-shadow: 0 0 9px color-mix(in srgb, var(--color-accent) 80%, transparent);
}

[data-shell="nightswim"] .dashboard-column-body {
    padding: 0 var(--space-2);
}

[data-shell="nightswim"] .column-menu-btn,
[data-shell="nightswim"] .dashboard-column-move-btn {
    border-radius: var(--radius-pill);
}

[data-shell="nightswim"] .add-column-btn {
    border-radius: var(--radius-pill);
    background: var(--shell-glass);
    box-shadow: var(--shell-leak);
    transition: transform var(--transition-medium), background var(--transition-medium), color var(--transition-medium);
}

[data-shell="nightswim"] .add-column-btn:hover {
    color: var(--color-accent);
    background: var(--shell-glass-strong);
    transform: scale(1.08);
}

/* ============================================================
   Cards
   ============================================================ */

[data-shell="nightswim"] .card {
    background: transparent;
    border-radius: var(--shell-radius-lg);
    transition: transform var(--transition-medium), background var(--transition-medium), box-shadow var(--transition-medium);
}

/* Standalone card surfaces (post detail, feed page, settings) need
   a real pane; in-column cards stay transparent until hovered so
   the column reads as one continuous plane. */
[data-shell="nightswim"] .content-narrow > .card {
    background: var(--shell-glass);
    box-shadow: var(--shell-leak);
}

[data-shell="nightswim"] .dashboard-column-body .card:hover,
[data-shell="nightswim"] .profile-feed .card:hover,
[data-shell="nightswim"] .drafts-list .card:hover {
    background: linear-gradient(180deg, var(--shell-glass-strong), var(--shell-glass));
    box-shadow: var(--shell-leak);
    transform: translateY(-1px);
}

/* The quoted post keeps its "this is someone else's" signal, but
   as a single left light-leak rather than a filled inset box. */
[data-shell="nightswim"] .shared-post-card {
    background: linear-gradient(180deg, var(--shell-glass-strong), var(--shell-glass));
    border-radius: var(--shell-radius-sm);
    box-shadow:
        inset 1px 0 0 color-mix(in srgb, var(--shell-aurora-2) 55%, transparent),
        var(--shell-leak);
}

/* Containers whose ONLY visual boundary was a border. With the token
   kill-switch in tokens.css they'd otherwise become invisible -- a
   poll option would read as loose text, a links tile as a floating
   favicon. Each gets the fill that the border used to imply. If you
   add another border-only container, it belongs in this list. */
[data-shell="nightswim"] .link-preview,
[data-shell="nightswim"] .poll-card-option,
[data-shell="nightswim"] .links-column-tile,
[data-shell="nightswim"] .freespace-link-card {
    border-radius: var(--shell-radius-sm);
    background: var(--shell-glass);
    box-shadow: var(--shell-leak);
}

[data-shell="nightswim"] .post-media,
[data-shell="nightswim"] .media-column-tile {
    border-radius: var(--shell-radius-sm);
}

/* ============================================================
   List rows — pill hover, no dividers
   ============================================================ */

[data-shell="nightswim"] .dashboard-update-row,
[data-shell="nightswim"] .dashboard-reminder-row,
[data-shell="nightswim"] .people-row,
[data-shell="nightswim"] .post-comment-row {
    border-radius: var(--shell-radius-sm);
    padding-inline: var(--space-3);
    transition: transform var(--transition-medium), background var(--transition-medium);
}

[data-shell="nightswim"] .dashboard-update-row:hover,
[data-shell="nightswim"] .dashboard-reminder-row:hover,
[data-shell="nightswim"] .people-row:hover,
[data-shell="nightswim"] .post-comment-row:hover {
    background: linear-gradient(90deg,
        color-mix(in srgb, rgb(var(--color-halo-rgb)) 9%, transparent),
        var(--shell-glass));
    transform: translateX(3px);
}

/* ============================================================
   Profile
   ============================================================ */

[data-shell="nightswim"] .editorial-split {
    border-radius: var(--shell-radius-xl);
    padding: var(--space-5);
    background:
        radial-gradient(90% 120% at 12% 0%,
            color-mix(in srgb, var(--shell-aurora-1) 13%, transparent), transparent 58%),
        radial-gradient(80% 110% at 95% 20%,
            color-mix(in srgb, var(--shell-aurora-2) 10%, transparent), transparent 58%),
        var(--shell-glass);
    box-shadow: var(--shell-leak), var(--shell-lift);
}

/* Only the FRAMED cell (text mode) gets a surface. In terminal that
   mode has a real border + fill, so glass is the honest translation. */
[data-shell="nightswim"] .freespace-cell {
    /* Same story as .dashboard-column: profile.css draws this from
       color-mix() on the accent, so the token kill-switch misses it.
       The glass fill below replaces it. */
    border-color: transparent;
    border-radius: var(--shell-radius-lg);
    background: linear-gradient(155deg, var(--shell-glass-strong), var(--shell-glass));
    box-shadow: var(--shell-leak);
}

/* ...and the unframed modes (media, link, post, linktree, music,
   event, pet, astrology) explicitly get it taken back off.

   Two reasons. The design one: each of those modes already carries
   its own boundary -- a photo, a hero card, a link card -- so a pane
   around it is a container inside a container, which is the exact
   boxiness this shell exists to delete.

   The bug one: profile.css zeroes padding on --unframed (at (0,1,0))
   precisely because there's meant to be no surface to inset from.
   The rule above is (0,2,0), so it won on `background` but left
   `padding: 0` standing -- giving a visible rounded pane with its
   caption and card flush against the corner, reading as content
   bleeding out of the frame. Restoring transparency fixes it at the
   cause rather than papering over it with a padding override. */
[data-shell="nightswim"] .freespace-cell--unframed {
    background: none;
    box-shadow: none;
}

/* The owner-only hover affordance on an unframed cell is a 1px accent
   border in profile.css, drawn from --color-accent directly rather
   than through --bs-border-color, so the kill-switch in tokens.css
   doesn't reach it. Neutralise it and say the same thing in this
   shell's language: a glass pane that fades up under the content. */
[data-shell="nightswim"] .freespace-cell--unframed.freespace-cell--owner:hover,
[data-shell="nightswim"] .freespace-cell--unframed.freespace-cell--owner:focus-within {
    border-color: transparent;
    background: var(--shell-glass);
    box-shadow: var(--shell-leak);
}

[data-shell="nightswim"] .freespace-linktree-link {
    border-radius: var(--radius-pill);
    background: var(--shell-glass-strong);
    transition: transform var(--transition-medium), background var(--transition-medium);
}

[data-shell="nightswim"] .freespace-linktree-link:hover {
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--shell-aurora-1) 20%, transparent),
        color-mix(in srgb, var(--shell-aurora-2) 12%, transparent));
    transform: translateX(5px);
}

[data-shell="nightswim"] .profile-pill-btn {
    border-radius: var(--radius-pill);
    background: var(--shell-glass);
    box-shadow: var(--shell-leak);
    transition: transform var(--transition-medium), background var(--transition-medium), color var(--transition-medium);
}

[data-shell="nightswim"] .profile-pill-btn:hover {
    background: var(--shell-glass-strong);
    transform: translateY(-2px);
}

/* The primary pill keeps a solid accent fill — it's the one place
   in the shell where a hard edge is the point. */
[data-shell="nightswim"] .profile-pill-btn--primary {
    background: var(--color-accent);
    box-shadow: 0 0 26px -6px color-mix(in srgb, var(--color-accent) 60%, transparent);
}

/* ============================================================
   FAB dock — a floating island
   ============================================================ */

[data-shell="nightswim"] .fab-expandable-container {
    border-radius: var(--radius-pill);
    padding: 7px var(--space-2);
    background: color-mix(in srgb, var(--shell-navbar-tint) 60%, transparent);
    backdrop-filter: blur(24px) saturate(1.5);
    box-shadow: var(--shell-leak), var(--shell-lift);
}

[data-shell="nightswim"] .fab {
    box-shadow: none;
    transition: transform var(--transition-medium), background var(--transition-medium), color var(--transition-medium);
}

[data-shell="nightswim"] .fab:hover {
    transform: translateY(-3px);
}

[data-shell="nightswim"] .fab-toggle {
    box-shadow: 0 0 26px -4px color-mix(in srgb, var(--color-accent) 65%, transparent);
}

/* ============================================================
   Bordered Bootstrap components
   ============================================================ */

/* --bs-border-color is transparent under this shell (see the
   kill-switch comment in tokens.css). Everything that relied on a
   Bootstrap border to be visible needs a surface instead, or it
   disappears. Adding a new bordered component? It needs a rule
   here too. */
[data-shell="nightswim"] .form-control,
[data-shell="nightswim"] .form-select {
    background: var(--shell-glass-strong);
    border-radius: var(--shell-radius-sm);
    box-shadow: var(--shell-leak);
}

[data-shell="nightswim"] .form-control:focus,
[data-shell="nightswim"] .form-select:focus {
    background: var(--shell-glass-strong);
    box-shadow:
        var(--shell-leak),
        0 0 0 3px color-mix(in srgb, rgb(var(--color-halo-rgb)) 32%, transparent);
}

[data-shell="nightswim"] .dropdown-menu,
[data-shell="nightswim"] .modal-content {
    border-radius: var(--shell-radius-lg);
    background: color-mix(in srgb, var(--color-bg-surface) 88%, transparent);
    backdrop-filter: blur(22px) saturate(1.4);
    box-shadow: var(--shell-leak), var(--shell-lift);
}

[data-shell="nightswim"] .dropdown-item {
    border-radius: var(--radius-sm);
}

/* The dropdown/modal hairline dividers have no color left to
   resolve, so give them one that doesn't depend on --bs-border-color. */
[data-shell="nightswim"] .dropdown-divider,
[data-shell="nightswim"] .modal-header {
    box-shadow: inset 0 -1px 0 var(--shell-glass-strong);
}

/* ============================================================
   Accessibility guards
   ============================================================ */

/* The drift is ambient, not informational — killing it costs
   nothing. The field itself stays. */
@media (prefers-reduced-motion: reduce) {
    [data-shell="nightswim"] body::before {
        animation: none;
    }

    [data-shell="nightswim"] .navbar-brand-cursor {
        animation: none;
    }

    [data-shell="nightswim"] .dashboard-column:hover,
    [data-shell="nightswim"] .freespace-linktree-link:hover,
    [data-shell="nightswim"] .profile-pill-btn:hover,
    [data-shell="nightswim"] .fab:hover {
        transform: none;
    }
}

/* Users who've asked the OS to reduce transparency get flat opaque
   surfaces instead of glass. The shell still reads as borderless —
   it just stops being see-through. */
@media (prefers-reduced-transparency: reduce) {
    [data-shell="nightswim"] .nav-container,
    [data-shell="nightswim"] .fab-expandable-container,
    [data-shell="nightswim"] .dropdown-menu,
    [data-shell="nightswim"] .modal-content,
    [data-shell="nightswim"] .tutorial-popover {
        background: var(--color-bg-surface);
        backdrop-filter: none;
    }

    [data-shell="nightswim"] body::after {
        display: none;
    }
}

/* ============================================================
   Tutorial popover (onb1, #537)
   ============================================================
   #526 tokenized the popover so it follows the viewer's theme,
   palette and font pack, and explicitly decided against per-shell
   rules. That was the right call while nightswim was an opt-in
   minority. onb1 makes nightswim the default for every account,
   which turns the popover into the one bordered object in a shell
   whose entire premise is that nothing has borders -- on the first
   screen a new user ever sees, since the tutorial queue fires on
   the onboarding page.

   So this is the exception to #526's no-shell-rules decision, and
   the only one. Everything here is the standard nightswim
   translation: the 1px accent hairline becomes a leak, the hard
   3px radius softens, and the geometry-drawn arrow goes away
   entirely because depth is doing that job instead.

   Colors stay on the #526 tokens throughout -- this file changes
   materials, never palette. No backdrop-filter: the popover is
   small but it sits OVER the aurora and can be up during a
   three-column scroll, which is exactly the case the file header
   warns about.
   ============================================================ */

[data-shell="nightswim"] .tutorial-popover {
    background: var(--shell-glass-strong);
    border: none;
    border-radius: var(--shell-radius-lg);
    box-shadow: var(--shell-leak), var(--shell-lift);
}

/* The two-layer arrow is drawn entirely out of borders, which is
   the one thing this shell doesn't do. Hidden rather than
   restyled -- the same treatment .is-detached already gets, and
   the anchor halo is what points at the target anyway. */
[data-shell="nightswim"] .tutorial-popover::before,
[data-shell="nightswim"] .tutorial-popover::after {
    display: none;
}

/* Title bar: accent hairline out, leak in. */
[data-shell="nightswim"] .tutorial-popover__bar {
    background: var(--shell-glass);
    border-bottom: none;
    box-shadow: var(--shell-leak);
}

/* Keystroke chips lose their outlines and become fills. The
   primary chip already IS a fill, so only its radius moves. */
[data-shell="nightswim"] .tutorial-popover__key {
    background: var(--shell-glass);
    border: none;
    border-radius: var(--shell-radius-sm);
}

[data-shell="nightswim"] .tutorial-popover__key--primary,
[data-shell="nightswim"] .tutorial-popover__key:hover,
[data-shell="nightswim"] .tutorial-popover__key:focus-visible {
    background: var(--color-accent);
    border-radius: var(--shell-radius-sm);
}

/* Anchor halo: outline is geometry, so drop it and let the pulse
   box-shadow (base.css, already on --color-halo-rgb) carry it. */
[data-shell="nightswim"] .tutorial-target-active {
    outline: none;
}

/* Mobile bottom sheet: base.css punches a 1px surface-colored ring
   to separate the sheet from the page. Nothing to separate from
   here -- the lift does it. */
@media (max-width: 640px) {
    [data-shell="nightswim"] .tutorial-popover {
        box-shadow: var(--shell-leak), var(--shell-lift);
    }
}
