/* Page styles for index.html: the homepage / Objects index (catalog of every system page).
   Shell (panels, top bar, explorer, header, results list, activity, status bar, one-screen layout)
   comes from css/workspace.css; this file adds the home hero, the live preview and its own column layouts.
   Hologram effects come from css/index.css.
   Content is rendered by js/objects.js from its OBJECTS catalog. */

.g-objmeta .g-format { color: var(--tone); }

/* ---------- Host: subject (left) + comms and live dossier (right), over an animated grid floor ---------- */
.g-home-pane + .g-objhead { margin-top: var(--g-gap); }
.g-home {
    --holo: clamp(110px, 16vh, 200px);
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 12px 28px;
    padding: 12px 22px;
    overflow: hidden;
    isolation: isolate;
    background: radial-gradient(ellipse at 14% 55%, rgba(var(--sky-rgb, var(--g-blue-rgb)), 0.1) 0%, transparent 55%), rgba(0, 0, 0, 0.25);
}
.g-home .g-reticle { position: absolute; z-index: 3; }

/* Background: perspective grid floor scrolling toward the viewer + a slow scan beam. Both take the sky's colour
   (--sky-rgb, --sky-a from js/sky.js: cyan by day, coral at sunrise and sunset, indigo at night), and stars
   come out over the floor at night (--night). */
.g-op-fx {
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    overflow: hidden;
    contain: paint;
}
.g-op-floor {
    --cell: 56px;
    position: absolute;
    left: -50%;
    right: -50%;
    bottom: -10%;
    height: 140%;
    overflow: hidden;
    /* A gentler tilt than before, so the far lines don't bunch into a shimmering band */
    transform: perspective(420px) rotateX(58deg);
    transform-origin: 50% 100%;
    -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0.6) 40%, transparent 80%);
    mask-image: linear-gradient(to top, #000 0%, rgba(0, 0, 0, 0.6) 40%, transparent 80%);
}
/* The grid itself scrolls by transform (no per-frame repaint), one cell per loop, inside the fixed mask.
   The tilt squashes the rows to a third of a pixel near the horizon, where hard 1px lines blink on and off
   as they cross pixel rows; soft 3px rows (and 2px columns) keep their brightness steady while they move. */
.g-op-floor::before {
    content: '';
    position: absolute;
    inset: calc(var(--cell) * -1) 0 0;
    background:
        linear-gradient(transparent, rgba(var(--sky-rgb, var(--g-blue-rgb)), 0.42) 1px, rgba(var(--sky-rgb, var(--g-blue-rgb)), 0.42) 2px, transparent 3px) 0 0 / var(--cell) var(--cell),
        linear-gradient(90deg, rgba(var(--sky-rgb, var(--g-blue-rgb)), 0.38) 1px, transparent 2px) 0 0 / var(--cell) var(--cell);
    opacity: var(--sky-a, 1);
    will-change: transform;
    backface-visibility: hidden;
    animation: g-op-floor 2.8s linear infinite;
}
@keyframes g-op-floor { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(0, var(--cell), 0); } }
/* The beam sweeps by transform too (percentages of its own 22% width: -114% and 500% are -25% and 110%
   of the panel), so it never repaints over the grid */
.g-op-beam {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 22%;
    background: linear-gradient(90deg, transparent, rgba(var(--sky-rgb, var(--g-blue-rgb)), 0.09) 45%, rgba(var(--sky-rgb, var(--g-blue-rgb)), 0.16) 50%, rgba(var(--sky-rgb, var(--g-blue-rgb)), 0.09) 55%, transparent);
    transform: translateX(-114%);
    will-change: transform;
    animation: g-op-beam 7s ease-in-out infinite;
}
/* Stars: three scattered layers of dots in the upper part of the panel, faded in by --night; one layer
   breathes slowly (no blinking) */
.g-op-stars {
    position: absolute;
    inset: 0 0 35% 0;
    opacity: var(--night, 0);
    background:
        radial-gradient(1px 1px at 23px 17px, #fff, transparent) 0 0 / 137px 89px,
        radial-gradient(1px 1px at 71px 52px, rgba(255, 255, 255, 0.8), transparent) 0 0 / 191px 113px,
        radial-gradient(1.5px 1.5px at 113px 31px, rgba(200, 210, 255, 0.9), transparent) 0 0 / 263px 127px;
    -webkit-mask-image: linear-gradient(to bottom, #000 30%, transparent);
    mask-image: linear-gradient(to bottom, #000 30%, transparent);
}
.g-op-stars::after {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(1px 1px at 51px 9px, #fff, transparent) 0 0 / 157px 71px;
    animation: g-op-stars 9s ease-in-out infinite alternate;
}
@keyframes g-op-stars { from { opacity: 0.35; } to { opacity: 1; } }
@keyframes g-op-beam {
    0% { transform: translateX(-114%); }
    55%, 100% { transform: translateX(500%); }
}

/* Subject: remilio inside a radar scope */
.g-op-subject {
    position: relative;
    margin: 0;
    justify-self: center;
    /* As wide as the scope rings (~1.22x the image height), or as the tag and status line under the image
       when those are wider (a small hologram, "ONLINE · DESKTOP · MOBILE"): the column then makes room,
       instead of them running into the comms and dossier */
    width: max-content;
    min-width: calc(var(--holo) * 1.26);
    display: grid;
    justify-items: center;
    gap: 6px;
}
.g-op-scope {
    position: absolute;
    left: 50%;
    top: calc(var(--holo) / 2);
    width: calc(var(--holo) * 1.12);
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
    pointer-events: none;
}
.g-op-scope i {
    position: absolute;
    border-radius: 50%;
}
.g-op-sweep {
    inset: 4%;
    background: conic-gradient(from 0deg, rgba(var(--g-blue-rgb), 0.28), rgba(var(--g-blue-rgb), 0.06) 18%, transparent 30%);
    -webkit-mask-image: radial-gradient(circle, transparent 30%, #000 31%);
    mask-image: radial-gradient(circle, transparent 30%, #000 31%);
    animation: g-op-spin 4s linear infinite;
}
.g-op-ring.r1 { inset: 0; border: 1px dashed rgba(var(--g-blue-rgb), 0.4); animation: g-op-spin 40s linear infinite; }
.g-op-ring.r2 {
    inset: 8%;
    border: 2px solid transparent;
    border-top-color: rgba(var(--g-blue-rgb), 0.7);
    border-bottom-color: rgba(var(--g-blue-rgb), 0.35);
    filter: drop-shadow(0 0 6px rgba(var(--g-blue-rgb), 0.6));
    animation: g-op-spin 9s linear infinite reverse;
}
.g-op-ring.r3 { inset: -5%; border: 1px dotted rgba(var(--g-blue-rgb), 0.25); }
@keyframes g-op-spin { to { transform: rotate(360deg); } }
.g-home .hologram-container { max-width: none; z-index: 1; }
.g-home .hologram-image {
    display: block;
    max-width: none;
    width: auto;
    height: var(--holo);
}
.g-op-tag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--g-mono);
    font-size: 9.5px;
    letter-spacing: 0.14em;
    color: var(--g-muted);
    background: rgba(0, 0, 0, 0.55);
    border: 1px solid var(--g-border);
    border-radius: 4px;
    padding: 1px 8px;
    white-space: nowrap;
    z-index: 1;
}
/* Under the tag: online state or last seen (js/presence.js), in the status colour */
.g-op-seen {
    margin-top: -3px;
    font-family: var(--g-mono);
    font-size: 8.5px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--tone, var(--g-dim));
    text-shadow: 0 0 8px color-mix(in srgb, var(--tone, transparent) 50%, transparent);
    z-index: 1;
}
.g-op-seen[hidden] { display: none; }
.g-op-seen.is-gaming { --tone: rgb(189, 147, 249); }

/* Power-up, while the workspace assembles (html.boot-reveal, index.html): the floor grid draws in from the
   horizon toward the viewer, the beam makes one fast pass, the scope rings spin up into place, and the
   hologram materialises bottom-up behind a bright scan line. The scan line ends last; the class comes off
   when it does (index.html) and the usual loops carry on. */
.g-op-materialize { display: none; }
html.boot-reveal .g-op-floor { animation: g-floor-in 0.9s cubic-bezier(0.3, 0.6, 0.2, 1) 0.2s both; }
@keyframes g-floor-in { from { clip-path: inset(0 0 100% 0); opacity: 0.4; } to { clip-path: inset(0); } }
html.boot-reveal .g-op-beam { animation: g-op-beam-fast 0.8s cubic-bezier(0.4, 0, 0.2, 1) 0.35s both; }
@keyframes g-op-beam-fast { from { transform: translateX(-114%); } to { transform: translateX(500%); } }
html.boot-reveal .g-op-stars { animation: g-stars-in 1.2s ease 0.4s both; }
@keyframes g-stars-in { from { opacity: 0; } }
html.boot-reveal .g-op-scope { animation: g-scope-in 0.9s cubic-bezier(0.2, 0.7, 0.2, 1) 0.15s both; }
@keyframes g-scope-in { from { opacity: 0; transform: translate(-50%, -50%) scale(0.55) rotate(-90deg); } }
html.boot-reveal .g-home .hologram-container { animation: g-holo-in 0.75s cubic-bezier(0.3, 0.6, 0.2, 1) 0.3s both; }
@keyframes g-holo-in {
    from { clip-path: inset(100% -25% -25% -25%); filter: brightness(2.2) saturate(0.4); }
    70% { filter: brightness(1.35) saturate(0.8); }
    to { clip-path: inset(-25%); filter: none; }
}
/* The scan line rides the wipe's edge: same timing and path, from the hologram's foot to just above its top */
html.boot-reveal .g-op-materialize {
    display: block;
    position: absolute;
    left: 50%;
    top: 0;
    width: calc(var(--holo) * 0.95);
    height: 2px;
    margin-left: calc(var(--holo) * -0.475);
    border-radius: 2px;
    background: linear-gradient(90deg, transparent, rgba(var(--g-blue-rgb), 0.9) 20%, #fff 50%, rgba(var(--g-blue-rgb), 0.9) 80%, transparent);
    box-shadow: 0 0 12px rgba(var(--g-blue-rgb), 0.9), 0 0 28px rgba(var(--g-blue-rgb), 0.5);
    z-index: 2;
    pointer-events: none;
    animation: g-holo-line 0.75s cubic-bezier(0.3, 0.6, 0.2, 1) 0.3s both;
}
@keyframes g-holo-line {
    from { transform: translateY(var(--holo)); opacity: 1; }
    85% { opacity: 1; }
    to { transform: translateY(calc(var(--holo) * -0.25)); opacity: 0; }
}

/* Right side: labelled rows */
.g-home-links {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 6px;
    min-width: 0;
}
.g-op-label {
    display: flex;
    align-items: center;
    gap: 10px;
    font-family: var(--g-mono);
    font-size: 9.5px;
    letter-spacing: 0.2em;
    color: var(--g-blue);
}
.g-op-label::after { content: ''; flex: 1; height: 1px; background: linear-gradient(90deg, var(--g-border-strong), transparent); }
.g-op-label span { color: var(--g-dim); letter-spacing: 0.1em; order: 2; }
.g-op-label + * { margin-bottom: 4px; }

/* Comms: one tile per channel, glowing in its brand colour */
.g-chans {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 8px;
}
.g-chan {
    --chan: var(--g-blue-rgb);
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 8px 4px 7px;
    border: 1px solid rgba(var(--chan), 0.22);
    border-radius: 10px;
    background: radial-gradient(ellipse at 50% 0%, rgba(var(--chan), 0.12), transparent 70%), rgba(0, 0, 0, 0.45);
    color: rgb(var(--chan));
    text-decoration: none;
    text-align: center;
    overflow: hidden;
    transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}
.g-chan::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(105deg, transparent 35%, rgba(var(--chan), 0.18) 50%, transparent 65%);
    transform: translateX(-100%);
    transition: transform 0.6s ease;
}
.g-chan:hover,
.g-chan.is-ambient,
.g-chan:focus-visible {
    border-color: rgba(var(--chan), 0.85);
    box-shadow: 0 0 18px rgba(var(--chan), 0.3), inset 0 0 14px rgba(var(--chan), 0.1);
    transform: translateY(-2px);
}
.g-chan:hover::before,
.g-chan.is-ambient::before { transform: translateX(100%); }
.g-chan-icon {
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: rgba(var(--chan), 0.1);
    box-shadow: 0 0 0 1px rgba(var(--chan), 0.3);
}
.g-chan-icon svg { width: 17px; height: 17px; }
.g-chan-icon img { width: 21px; height: 21px; filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.55)); }
.g-chan:hover .g-chan-icon,
.g-chan.is-ambient .g-chan-icon { box-shadow: 0 0 0 1px rgb(var(--chan)), 0 0 12px rgba(var(--chan), 0.6); }
.g-chan-text { display: flex; flex-direction: column; min-width: 0; max-width: 100%; line-height: 1.25; }
.g-chan-text b {
    font-weight: 500;
    font-size: 11.5px;
    color: var(--g-text);
    letter-spacing: 0.04em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.g-chan-text small {
    font-family: var(--g-mono);
    font-size: 9.5px;
    color: var(--g-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.g-chan:hover .g-chan-text small,
.g-chan.is-ambient .g-chan-text small { color: rgb(var(--chan)); }

/* Dossier: one live card at a time (Discord, RemiNET stats, GitHub), stacked in one grid cell and
   cross-faded by js/index.js; the cell is as tall as the tallest loaded card, so switching never shifts the panel */
.g-dossier {
    container: dossier / inline-size;
    display: grid;
    min-width: 0;
}
.g-dossier > [data-slide] {
    grid-area: 1 / 1;
    /* Frosted: the floor grid behind reads as glass, not lines through the text */
    backdrop-filter: blur(5px);
    -webkit-backdrop-filter: blur(5px);
    opacity: 0;
    visibility: hidden;
    transform: translateY(6px);
    transition: opacity 0.35s ease, transform 0.35s ease, visibility 0s linear 0.35s;
}
.g-dossier > .is-active {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition: opacity 0.35s ease, transform 0.35s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}
/* Which card is up, beside its source in the DOSSIER label */
.g-dossier-dots { display: flex; gap: 5px; }
.g-dossier-dots button {
    width: 6px;
    height: 6px;
    padding: 0;
    border: 1px solid rgba(var(--g-blue-rgb), 0.5);
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
}
.g-dossier-dots button[aria-selected="true"] { background: var(--g-blue); box-shadow: 0 0 6px rgba(var(--g-blue-rgb), 0.8); }
/* RemiNET stats (js/index.js fills [data-stat]) */
.g-op-stats {
    min-width: 0;
    display: grid;
    grid-template-columns: 1.7fr repeat(4, minmax(0, 1fr));
    border: 1px solid var(--g-border);
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.45);
    text-decoration: none;
    overflow: hidden;
    transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.g-op-stats:hover { border-color: rgba(189, 147, 249, 0.6); box-shadow: 0 0 16px rgba(189, 147, 249, 0.2); }
.g-op-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 6px 4px;
    line-height: 1.15;
    min-width: 0;
}
.g-op-stat + .g-op-stat { border-left: 1px solid rgba(var(--g-blue-rgb), 0.08); }
.g-op-stat b {
    font-family: var(--g-mono);
    font-weight: 400;
    font-size: 15px;
    color: var(--g-blue);
    text-shadow: 0 0 8px rgba(var(--g-blue-rgb), 0.35);
}
.g-op-stat small {
    font-size: 8.5px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--g-dim);
    white-space: nowrap;
}
.g-op-stat.is-credit {
    background: radial-gradient(ellipse at 50% 0%, rgba(189, 147, 249, 0.2), transparent 75%);
}
.g-op-stat.is-credit b {
    font-size: 19px;
    color: #fff;
    text-shadow: 0 0 10px rgba(189, 147, 249, 0.95), 0 0 22px rgba(189, 147, 249, 0.5);
}
.g-op-stat.is-credit small { color: rgb(189, 147, 249); }
.g-op-stats.ready b { animation: g-op-in 0.8s ease-out; }
@keyframes g-op-in { 0% { opacity: 0; filter: blur(4px); } 100% { opacity: 1; filter: none; } }
.g-op-stats.offline b { color: var(--g-dim); text-shadow: none; }

/* Waiting for a live source (js/objects.js Results rows, the RemiNET stats before they land): dim text with
   a slow sheen passing through it. js/arrive.js then decodes or counts the real value in. */
.g-acquire,
.g-op-stats:not(.ready):not(.offline) b {
    color: transparent;
    text-shadow: none;
    background: linear-gradient(90deg, var(--g-dim) 0%, var(--g-dim) 35%, var(--g-text) 50%, var(--g-dim) 65%, var(--g-dim) 100%) 0 0 / 250% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    animation: g-acquire 2.2s linear infinite;
}
.g-acquire { font-style: italic; letter-spacing: 0.04em; }
@keyframes g-acquire { from { background-position: 100% 0; } to { background-position: -150% 0; } }

/* Presence: live Discord status beside the stats (js/presence.js). Art with a status pip, a small label row
   (LISTENING / PLAYING + time), title and subtitle; Spotify adds a progress bar along the bottom edge. */
.g-presence {
    --pres: var(--tone, var(--g-muted));
    position: relative;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 5px 12px 5px 6px;
    border: 1px solid color-mix(in srgb, var(--pres) 35%, transparent);
    border-radius: 10px;
    background: radial-gradient(ellipse at 0% 50%, color-mix(in srgb, var(--pres) 14%, transparent), transparent 70%), rgba(0, 0, 0, 0.45);
    color: var(--g-text);
    text-decoration: none;
    overflow: hidden;
    transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.g-presence[hidden] { display: none; }
.g-presence.is-spotify { --pres: #1db954; }
/* Music card (js/music.js): the presence card's layout in TIDAL's cyan, dimmer for the last track played; a
   note glyph sits behind the cover art (and shows when there isn't any), and a small equalizer while playing */
.g-music { --pres: color-mix(in srgb, #33e1ed 55%, var(--g-muted)); }
.g-music.is-playing { --pres: #33e1ed; }
.g-music-note { position: absolute; inset: 0; display: grid; place-items: center; font-style: normal; font-size: 18px; color: var(--pres); opacity: 0.6; }
.g-music .g-presence-art img { position: relative; }
.g-music-eq { display: inline-flex; align-items: flex-end; gap: 1px; height: 8px; }
.g-music-eq i { width: 2px; height: 100%; background: var(--pres); transform-origin: 50% 100%; animation: g-music-eq 0.9s ease-in-out infinite alternate; }
.g-music-eq i:nth-child(2) { animation-duration: 0.7s; animation-delay: -0.3s; }
.g-music-eq i:nth-child(3) { animation-duration: 1.1s; animation-delay: -0.6s; }
@keyframes g-music-eq { from { transform: scaleY(0.25); } to { transform: scaleY(1); } }
/* The artist rides on the title line where the compact layout drops the subtitle */
.g-music-by { display: none; color: var(--g-muted); }

/* Live connections in Results (js/objects.js, type 'live'): their icons are line art, so they get a little
   room; selected, the preview shows the Host dossier card, enlarged, with a link out under it */
.g-res-icon[src$=".svg"],
.g-objimg[src$=".svg"] { padding: 4px; box-sizing: border-box; }
.g-preview-screen.is-native .g-live-mirror {
    min-height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
    padding: 24px 18px;
    box-sizing: border-box;
}
.g-live-mirror-card {
    container: dossier / inline-size; /* the cards' own container queries */
    width: min(100%, 560px);
    zoom: 1.45;
}
.g-live-mirror-card > [data-slide] { margin: 0; }
.g-live-mirror-card .g-preview-wait { position: static; }

/* Status-bar ticker (js/ticker.js): takes most of the bar's free space on home (the status items shed from
   their end first), fades in and out at its edges, and crawls by transform */
footer.g-statusbar .g-ticker {
    flex: 2.2 1 0;
    min-width: 0;
    height: 20px;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}
footer.g-statusbar .g-ticker-track { display: flex; align-items: center; width: max-content; height: 100%; will-change: transform; }
footer.g-statusbar .g-ticker-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
    color: var(--sb-text);
    text-decoration: none;
}
footer.g-statusbar .g-ticker-item::before { content: ''; width: 5px; height: 5px; border-radius: 50%; flex: none; background: var(--tone, var(--sb-dim)); box-shadow: 0 0 6px var(--tone, transparent); }
footer.g-statusbar .g-ticker-item::after { content: '◆'; margin: 0 16px; font-size: 7px; color: var(--sb-dim); }
footer.g-statusbar a.g-ticker-item:hover { color: #fff; }
@media (max-width: 760px) {
    footer.g-statusbar .g-ticker { display: none; }
}
.g-presence.is-activity { --pres: #8a93f8; }
.g-presence:hover,
.g-presence:focus-visible { border-color: var(--pres); box-shadow: 0 0 16px color-mix(in srgb, var(--pres) 30%, transparent); }
.g-presence-art { position: relative; flex: none; width: 40px; height: 40px; border-radius: 8px; background: rgba(var(--g-blue-rgb), 0.08); }
.g-presence-art img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }
.g-presence.is-custom .g-presence-art,
.g-presence.is-idle .g-presence-art { border-radius: 50%; }
.g-presence-status {
    position: absolute;
    right: -3px;
    bottom: -3px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--tone, var(--g-muted));
    box-shadow: 0 0 0 3px #05080a, 0 0 8px var(--tone, transparent);
}
.g-presence-text { flex: 1 1 200px; display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
/* Profile card (status / idle): the Discord nameplate faded in behind the right side, the display name in its
   own colours (brightened for the dark card) with badges, and a custom emoji before the status text */
.g-presence-plate {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: auto;
    opacity: 0.3;
    pointer-events: none;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 45%);
    mask-image: linear-gradient(90deg, transparent, #000 45%);
}
.g-presence > :not(.g-presence-plate):not(.g-presence-bar) { position: relative; }
.g-presence-name.is-styled {
    background: linear-gradient(90deg, color-mix(in srgb, var(--name) 55%, #fff), color-mix(in srgb, var(--name2) 55%, #fff));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    filter: drop-shadow(0 0 6px color-mix(in srgb, var(--name) 60%, transparent));
}
.g-presence-badge,
.g-presence-emoji { width: 14px; height: 14px; vertical-align: -3px; }
.g-presence-badge { margin-left: 5px; }
.g-presence-emoji { margin-right: 4px; }
/* Right-hand stats (js/presence.js), in the card's own colour; long values (the album) truncate */
.g-presence-side { flex: 0 1 auto; min-width: 0; display: flex; }
.g-presence .g-soc-stat b { color: var(--pres); }
.g-presence .g-soc-stat.is-text { min-width: 0; max-width: 170px; }
.g-presence .g-soc-stat.is-text b { max-width: 100%; font-family: inherit; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g-presence-clock b { font-variant-numeric: tabular-nums; }
.g-presence-text small { display: flex; gap: 8px; font-family: var(--g-mono); font-size: 8.5px; letter-spacing: 0.14em; color: var(--pres); }
.g-presence-text small .g-presence-time { color: var(--g-dim); letter-spacing: 0.04em; font-variant-numeric: tabular-nums; }
.g-presence-text b,
.g-presence-text em { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g-presence-text b { font-weight: 500; font-size: 12px; }
.g-presence-text em { font-style: normal; font-size: 10px; color: var(--g-muted); }
.g-presence-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: rgba(255, 255, 255, 0.08); }
.g-presence-bar i { position: absolute; inset: 0; background: var(--pres); transform-origin: 0 50%; transform: scaleX(0); transition: transform 1s linear; }
.g-presence.tone-green .g-presence-status { animation: g-presence-pulse 2.4s ease-in-out infinite; }
@keyframes g-presence-pulse { 50% { box-shadow: 0 0 0 3px #05080a, 0 0 14px var(--tone); } }

/* GitHub: contributions heatmap (newest week on the right, older weeks clipped to fit) and three totals */
.g-gh {
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 5px 6px 5px 12px;
    border: 1px solid var(--g-border);
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.45);
    color: var(--g-text);
    text-decoration: none;
}
.g-gh[hidden] { display: none; }
.g-gh:hover,
.g-gh:focus-visible { border-color: rgba(var(--g-blue-rgb), 0.6); box-shadow: 0 0 16px rgba(var(--g-blue-rgb), 0.18); }
/* The map takes its natural width (a year of weeks); when the card is too narrow it shrinks, and rtl keeps
   the newest week in view while the oldest clip */
.g-gh-map { flex: 0 1 auto; min-width: 0; display: flex; direction: rtl; justify-content: flex-start; overflow: hidden; }
.g-gh-grid { --cell: 5px; flex: none; direction: ltr; display: grid; grid-template-rows: repeat(7, var(--cell)); grid-auto-flow: column; grid-auto-columns: var(--cell); gap: 1px; }
.g-gh-grid i { border-radius: 1px; background: rgba(var(--g-blue-rgb), 0.08); }
.g-gh-grid i.l1 { background: rgba(var(--g-blue-rgb), 0.3); }
.g-gh-grid i.l2 { background: rgba(var(--g-blue-rgb), 0.55); }
.g-gh-grid i.l3 { background: rgba(var(--g-blue-rgb), 0.8); }
.g-gh-grid i.l4 { background: var(--g-blue); box-shadow: 0 0 4px rgba(var(--g-blue-rgb), 0.8); }
.g-gh-stats { flex: 1 1 auto; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
.g-gh-stat { display: flex; flex-direction: column; align-items: center; padding: 0 8px; line-height: 1.15; }
.g-gh-stat + .g-gh-stat { border-left: 1px solid rgba(var(--g-blue-rgb), 0.08); }
.g-gh-stat b { font-family: var(--g-mono); font-weight: 400; font-size: 15px; color: var(--g-blue); text-shadow: 0 0 8px rgba(var(--g-blue-rgb), 0.35); }
.g-gh-stat small { font-size: 8.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--g-dim); white-space: nowrap; }

/* X and Steam cards (js/socials.js): art, a label row, title and subtitle like the presence card, then stats
   (X) or recent game capsules (Steam) on the right */
.g-xcard,
.g-steam {
    --soc: 230, 230, 230;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 5px 8px 5px 6px;
    border: 1px solid rgba(var(--soc), 0.25);
    border-radius: 10px;
    background: radial-gradient(ellipse at 0% 50%, rgba(var(--soc), 0.1), transparent 70%), rgba(0, 0, 0, 0.45);
    color: var(--g-text);
    text-decoration: none;
}
.g-steam { --soc: 102, 192, 244; }
.g-xcard[hidden],
.g-steam[hidden] { display: none; }
.g-xcard:hover,
.g-xcard:focus-visible,
.g-steam:hover,
.g-steam:focus-visible { border-color: rgba(var(--soc), 0.75); box-shadow: 0 0 16px rgba(var(--soc), 0.2); }
.g-soc-art { position: relative; flex: none; width: 40px; height: 40px; border-radius: 50%; background: rgba(var(--soc), 0.1); }
.g-soc-art img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }
.g-steam .g-soc-art { border-radius: 8px; }
.g-steam .g-soc-art.is-capsule { width: 92px; height: 34px; border-radius: 4px; }
.g-soc-pip {
    position: absolute;
    right: -3px;
    bottom: -3px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--tone, var(--g-muted));
    box-shadow: 0 0 0 3px #05080a, 0 0 8px var(--tone, transparent);
}
.g-steam.tone-steam { --tone: rgb(102, 192, 244); }
.g-soc-text { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.g-soc-text small { display: flex; gap: 8px; font-family: var(--g-mono); font-size: 8.5px; letter-spacing: 0.14em; color: rgb(var(--soc)); }
.g-steam.tone-green .g-soc-text small > span:first-child { color: var(--g-green); }
.g-steam.tone-muted .g-soc-text small > span:first-child { color: var(--g-muted); }
.g-soc-dim { color: var(--g-dim); letter-spacing: 0.04em; }
.g-soc-text b,
.g-soc-text em { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g-soc-text b { font-weight: 500; font-size: 12px; }
.g-soc-text em { font-style: normal; font-size: 10px; color: var(--g-muted); }
.g-soc-stats { flex: none; display: flex; }
.g-soc-stat { display: flex; flex-direction: column; align-items: center; padding: 0 12px; line-height: 1.15; }
.g-soc-stat + .g-soc-stat { border-left: 1px solid rgba(var(--g-blue-rgb), 0.08); }
.g-soc-stat b { font-family: var(--g-mono); font-weight: 400; font-size: 15px; color: rgb(var(--soc)); }
.g-soc-stat small { font-size: 8.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--g-dim); }
/* Recent games: store capsules (184x69) at half size, total hours in the corner */
.g-steam-games { flex: none; display: flex; gap: 5px; }
.g-steam-game { position: relative; width: 92px; height: 34px; border-radius: 4px; overflow: hidden; background: rgba(var(--soc), 0.1); box-shadow: 0 0 0 1px rgba(var(--soc), 0.2); }
.g-steam-game img { width: 100%; height: 100%; object-fit: cover; display: block; }
.g-steam-game small { position: absolute; right: 2px; bottom: 2px; padding: 0 4px; border-radius: 3px; background: rgba(0, 0, 0, 0.75); font-family: var(--g-mono); font-size: 9px; color: var(--g-text); }

/* Dossier cards follow the dossier's own width, not the window's: wide cards fill out with more stats and games,
   narrow ones shed them, least important first */
/* Steam's hours sit in its stat cells on wide cards and in the label line once those cells go */
.g-steam .g-soc-dim { display: none; }
@container dossier (max-width: 860px) {
    .g-gh-stat:nth-child(n + 4) { display: none; }
    .g-presence-side > :nth-child(n + 3) { display: none; }
    .g-steam-game:nth-child(n + 4) { display: none; }
    .g-steam .g-soc-stats { display: none; }
    .g-steam .g-soc-dim { display: inline; }
}
@container dossier (max-width: 620px) {
    .g-presence-side > :nth-child(n + 2) { display: none; }
    .g-steam-game:nth-child(n + 3) { display: none; }
}
@container dossier (max-width: 460px) {
    .g-presence-side { display: none; }
}
@container dossier (max-width: 480px) {
    .g-gh-stat:nth-child(n + 3) { display: none; }
    .g-steam-game:nth-child(n + 2) { display: none; }
    .g-soc-stats .g-soc-stat:last-child { display: none; }
    .g-soc-stat,
    .g-gh-stat { padding: 0 6px; }
}
@container dossier (max-width: 360px) {
    .g-steam-games,
    .g-soc-stats { display: none; }
}

/* Results: a live row's telemetry line, led by a pulsing dot in its source's tone */
.g-res-desc.is-live { color: var(--g-muted); }
.g-live-dot {
    display: inline-block;
    width: 5px;
    height: 5px;
    margin-right: 6px;
    vertical-align: 1px;
    border-radius: 50%;
    background: var(--tone, var(--g-green));
    box-shadow: 0 0 6px var(--tone, var(--g-green));
    animation: g-live-pulse 2s ease-in-out infinite;
}
@keyframes g-live-pulse { 50% { opacity: 0.35; } }

/* ---------- Live preview ---------- */
.g-preview-pane { margin-top: var(--g-gap); }
.g-preview {
    position: relative;
    height: 460px;
    overflow: hidden;
    background:
        radial-gradient(ellipse at center, rgba(var(--g-blue-rgb), 0.06) 0%, transparent 65%),
        linear-gradient(rgba(var(--g-blue-rgb), 0.035) 1px, transparent 1px) 0 0 / 32px 32px,
        linear-gradient(90deg, rgba(var(--g-blue-rgb), 0.035) 1px, transparent 1px) 0 0 / 32px 32px;
}
.g-preview .g-reticle { position: absolute; z-index: 2; }
/* The page renders on a 1440px-wide "screen" that js/objects.js makes as tall as the panel's shape and
   scales to fill it edge to edge (height and --view-h set there) */
.g-preview-screen {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 1440px;
    height: 900px;
    transform-origin: 50% 50%;
    background: #000;
    border: 2px solid rgba(var(--g-blue-rgb), 0.4);
    box-shadow: 0 0 60px rgba(var(--g-blue-rgb), 0.18), 0 30px 80px rgba(0, 0, 0, 0.7);
    overflow: hidden;
}
.g-preview-screen iframe {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.4s ease;
}
.g-preview-screen.ready iframe { opacity: 1; }
/* Scanlines over the framed page so it reads as a preview, not the page itself:
   fine CRT lines plus a soft band sweeping down the screen (screen-scale px, so they scale with it) */
.g-preview-screen::before,
.g-preview-screen::after {
    content: '';
    position: absolute;
    pointer-events: none;
    z-index: 1;
}
.g-preview-screen::before {
    inset: 0;
    background: repeating-linear-gradient(180deg, transparent 0 2px, rgba(0, 0, 0, 0.3) 2px 4px);
    box-shadow: inset 0 0 120px rgba(var(--g-blue-rgb), 0.08);
}
.g-preview-screen::after {
    left: 0;
    right: 0;
    top: 0;
    height: 160px;
    background: linear-gradient(180deg, transparent, rgba(var(--g-blue-rgb), 0.07) 70%, rgba(var(--g-blue-rgb), 0.18) 98%, transparent);
    animation: g-preview-sweep 4.5s linear infinite;
}
@keyframes g-preview-sweep {
    from { transform: translateY(-160px); }
    to { transform: translateY(var(--view-h, 900px)); }
}
.g-preview-wait {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-family: var(--g-mono);
    font-size: 40px;
    letter-spacing: 0.3em;
    color: var(--g-blue);
    text-shadow: 0 0 20px rgba(var(--g-blue-rgb), 0.6);
    animation: g-wait 1.2s ease-in-out infinite;
}
@keyframes g-wait { 50% { opacity: 0.35; } }
/* Click-through to the real page; shows an OPEN tag on hover */
.g-preview-open {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    text-decoration: none;
    background: transparent;
    transition: background 0.25s ease;
    z-index: 2; /* above the preview scanlines */
}
.g-preview-open span {
    font-family: var(--g-mono);
    font-size: 44px;
    letter-spacing: 0.2em;
    color: var(--g-blue);
    border: 2px solid var(--g-blue);
    border-radius: 16px;
    padding: 14px 40px;
    background: rgba(0, 0, 0, 0.75);
    box-shadow: 0 0 40px rgba(var(--g-blue-rgb), 0.4);
    opacity: 0;
    transform: translateY(10px);
    transition: opacity 0.25s ease, transform 0.25s ease;
}
.g-preview-open:hover { background: rgba(0, 0, 0, 0.35); }
.g-preview-open:hover span,
.g-preview-open:focus-visible span { opacity: 1; transform: none; }
/* External hosts: no frame, a "preview disabled" card at screen scale */
.g-preview-screen.offline {
    background: radial-gradient(ellipse at center, rgba(var(--g-blue-rgb), 0.08), rgba(0, 0, 0, 0.9) 70%);
}
.g-preview-off {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 28px;
    text-align: center;
}
.g-preview-off-icon {
    width: 180px;
    height: 180px;
    border-radius: 36px;
    object-fit: contain;
    font-size: 80px;
    opacity: 0.85;
    filter: drop-shadow(0 0 30px rgba(var(--g-blue-rgb), 0.35));
}
.g-preview-off strong {
    font-family: var(--g-mono);
    font-weight: 400;
    font-size: 46px;
    letter-spacing: 0.2em;
    color: var(--g-blue);
    text-shadow: 0 0 24px rgba(var(--g-blue-rgb), 0.6);
}
.g-preview-off span {
    font-family: var(--g-mono);
    font-size: 24px;
    letter-spacing: 0.08em;
    color: var(--g-muted);
}
.g-preview-off .g-visit {
    font-size: 26px;
    height: auto;
    padding: 12px 30px;
    border-radius: 12px;
}

/* ---------- Featured pages (FEATURED in js/objects.js), in place of the preview for tylerirl.com ----------
   One card design that scales continuously with its own box (container units), instead of
   per-breakpoint variants. The grid picks 1/2/3/6 columns from the panel's shape so the six
   cards keep a comfortable aspect; each card then switches between a row layout (wide or short
   cards) and a stacked one (roomy cards), with every size interpolated in between. */
.g-preview[hidden],
.g-featured[hidden] { display: none; }
.g-featured {
    container: featured / inline-size;
    display: flex;
    flex-direction: column;
    padding: clamp(6px, 0.7vw, 12px);
    background:
        linear-gradient(rgba(var(--g-blue-rgb), 0.035) 1px, transparent 1px) 0 0 / 32px 32px,
        linear-gradient(90deg, rgba(var(--g-blue-rgb), 0.035) 1px, transparent 1px) 0 0 / 32px 32px;
}
.g-feat-grid {
    --cols: 1;
    --card-ratio: 2.8;
    display: grid;
    grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
    gap: clamp(6px, 1.1cqi, 12px);
}
/* Flowing layouts (tablet, phone, short desktop): columns follow the panel width and each card
   keeps a fixed shape, so its height scales smoothly with its width */
@container featured (min-width: 500px) {
    .g-feat-grid { --cols: 2; --card-ratio: 2.7; }
}
@container featured (min-width: 860px) {
    .g-feat-grid { --cols: 3; --card-ratio: 1.6; }
}
.g-feat {
    --feat: var(--g-blue-rgb);
    container: feat / size;
    position: relative;
    display: block;
    min-width: 0;
    aspect-ratio: var(--card-ratio);
    border: 1px solid rgba(var(--feat), 0.35);
    border-radius: clamp(8px, 4cqmin, 14px);
    background: radial-gradient(ellipse at 12% 50%, rgba(var(--feat), 0.15), transparent 60%), rgba(0, 0, 0, 0.35);
    color: var(--g-text);
    text-decoration: none;
    overflow: hidden;
    transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}
.g-feat:hover,
.g-feat.is-ambient,
.g-feat:focus-visible {
    border-color: rgb(var(--feat));
    box-shadow: 0 0 24px rgba(var(--feat), 0.3), inset 0 0 24px rgba(var(--feat), 0.08);
    transform: translateY(-2px);
}

/* Row layout (default): art | name / desc / cta. Sizes track the card's height and width. */
.g-feat-body {
    --art: clamp(28px, min(52cqh, 20cqw), 100px);
    height: 100%;
    display: grid;
    /* Same art column on every card (sized for the wide sprite) so captions line up */
    grid-template-columns: min(calc(var(--art) * 1.4), 30cqw) minmax(0, 1fr);
    grid-template-areas: "art name" "art desc" "art cta";
    align-content: center;
    align-items: center;
    justify-items: start;
    column-gap: clamp(8px, 4cqw, 20px);
    row-gap: clamp(2px, 2.5cqh, 6px);
    padding: clamp(6px, 6cqh, 16px) clamp(10px, 5cqw, 20px);
}
.g-feat-text,
.g-feat-head { display: contents; }
.g-feat-art {
    grid-area: art;
    justify-self: center;
    width: var(--art);
    height: var(--art);
    display: flex;
    align-items: center;
    justify-content: center;
}
.g-feat-art img {
    display: block;
    height: 100%;
    width: auto;
    max-width: 100%;
    object-fit: contain;
    filter: drop-shadow(0 0 18px rgba(var(--feat), 0.45));
    transition: transform 0.3s ease;
}
/* Round art (the guest book glyph) gets a ring in the card accent */
.g-feat-art.is-round img { border-radius: 50%; box-shadow: 0 0 0 2px rgba(var(--feat), 0.8), 0 0 22px rgba(var(--feat), 0.35); }
/* RadGotchi sprites (black on transparent, 144x44): inverted and tinted cyan like the status-bar pet.
   Wider box but the same visual weight as the square art. */
.g-feat-art.is-sprite { width: calc(var(--art) * 1.4); max-width: 100%; }
.g-feat-art.is-sprite img {
    height: auto;
    width: 100%;
    image-rendering: pixelated;
    filter: invert(1) sepia(1) saturate(6) hue-rotate(160deg) brightness(1.2)
            drop-shadow(0 0 4px rgba(var(--feat), 0.9)) drop-shadow(0 0 14px rgba(var(--feat), 0.45));
}
/* KoL avatar (100x100, black ink on white): the character sits in the left part (outfits can reach x 58, like
   a merman's tail fin) and the "Kingdom of Loathing" caption starts at x 61, with any held items further
   right, so the image covers a 60x100 box from the left: the whole character, and a blank column before
   the caption so scaling never blends its first letter in.
   Inverted and tinted; #brightnessToAlpha (index.html) drops the inverted, now black, page */
.g-feat-art.is-badge img {
    height: 100%;
    width: auto;
    aspect-ratio: 60 / 100;
    object-fit: cover;
    object-position: 0 50%;
    filter: invert(1) sepia(1) saturate(5) hue-rotate(155deg) brightness(1.2) url(#brightnessToAlpha)
            drop-shadow(0 0 6px rgba(var(--feat), 0.6));
}
.g-feat:hover .g-feat-art img,
.g-feat.is-ambient .g-feat-art img { transform: scale(1.06) rotate(-2deg); }
.g-feat:hover .g-feat-art.is-sprite img,
.g-feat.is-ambient .g-feat-art.is-sprite img,
.g-feat:hover .g-feat-art.is-badge img,
.g-feat.is-ambient .g-feat-art.is-badge img { transform: scale(1.06); }
.g-feat-name {
    grid-area: name;
    max-width: 100%;
    font-size: clamp(13px, min(11.5cqh, 5.8cqw), 19px);
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1.2;
    text-shadow: 0 0 14px rgba(var(--feat), 0.35);
}
.g-feat-desc {
    grid-area: desc;
    font-size: clamp(10.5px, min(8.5cqh, 4cqw), 12.5px);
    line-height: 1.45;
    color: var(--g-muted);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
}
.g-feat-cta {
    grid-area: cta;
    display: inline-flex;
    align-items: center;
    margin-top: clamp(0px, 2cqh, 6px);
    height: auto;
    padding: 0.55em 0.9em;
    white-space: nowrap;
    font-family: var(--g-mono);
    font-size: clamp(9.5px, min(8cqh, 3.6cqw), 12px);
    letter-spacing: 0.12em;
    border-color: rgba(var(--feat), 0.6);
    color: rgb(var(--feat));
}
.g-feat:hover .g-feat-cta,
.g-feat.is-ambient .g-feat-cta { background: rgba(var(--feat), 0.15); }
/* Short cards drop the description first, then trim it to one line */
@container feat (max-height: 104px) {
    .g-feat-desc { display: none; }
}
@container feat (min-height: 105px) and (max-height: 139px) {
    .g-feat-desc { -webkit-line-clamp: 1; line-clamp: 1; }
}

/* Stacked layout for roomy cards: art over a centred caption */
@container feat (max-aspect-ratio: 21/10) and (min-height: 150px) {
    .g-feat-body {
        --art: clamp(40px, min(36cqh, 40cqw), 150px);
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas: "art" "name" "desc" "cta";
        justify-items: center;
        text-align: center;
        row-gap: clamp(3px, 2.4cqh, 10px);
        padding: clamp(10px, 6cqh, 22px) clamp(12px, 6cqw, 24px);
    }
    .g-feat-name { font-size: clamp(14px, min(7.5cqh, 6.8cqw), 22px); }
    .g-feat-desc {
        font-size: clamp(10.5px, min(4.6cqh, 4cqw), 13px);
        /* Reserve a line so names and buttons line up across a row of cards */
        min-height: 1.45em;
        -webkit-line-clamp: 2;
        line-clamp: 2;
    }
    .g-feat-cta { font-size: clamp(10px, min(4.6cqh, 3.8cqw), 12px); }
}


/* ---------- Three columns: recently viewed fills the rail below the explorer ----------
   The rail stretches to the row height and the size-contained panel takes whatever is left,
   scrolling its list, so it never grows the page as entries are added. */
@media (min-width: 1360px) {
    .g-body { align-items: stretch; }
    .g-activity-pane {
        flex: 1 1 0;
        min-height: 150px;
        contain: size;
        display: flex;
        flex-direction: column;
    }
    .g-activity {
        flex: 1 1 0;
        min-height: 0;
        overflow-y: auto;
        scrollbar-width: thin;
        scrollbar-color: rgba(139, 233, 253, 0.25) transparent;
    }
}

/* ---------- Main column fills its height (900px and up) ----------
   The main column stretches to the row (window or rail height), and the preview pane takes
   whatever is below the operator and object header: the live preview grows to it, and the
   featured grid fills it, picking its column count from the pane's shape. */
@media (min-width: 900px) {
    .g-main {
        display: flex;
        flex-direction: column;
    }
    .g-objhead,
    .g-home-pane { flex: none; }
    .g-preview-pane { flex: 1 0 auto; }
    .g-preview,
    .g-featured {
        flex: 1 1 auto;
        height: auto;
    }
    .g-preview { min-height: 360px; }
    .g-featured {
        container: featured / size;
        min-height: 300px;
    }
    .g-feat-grid {
        --cols: 2;
        --rows: 3;
        flex: 1 1 0;
        min-height: 0;
        grid-template-rows: repeat(var(--rows), minmax(72px, 1fr));
        overflow-y: auto;
        scrollbar-width: thin;
        /* The scroller clips its edges: room for a hovered card's lift and rim, without moving the layout */
        padding: 4px;
        margin: -4px;
    }
    .g-feat { aspect-ratio: auto; }
    /* Columns that keep each cell nearest a ~1.6:1 card: cell aspect = pane aspect * 6 / cols^2 */
    @container featured (max-aspect-ratio: 53/100) { .g-feat-grid { --cols: 1; --rows: 6; } }
    @container featured (min-aspect-ratio: 16/10)  { .g-feat-grid { --cols: 3; --rows: 2; } }
    @container featured (min-aspect-ratio: 48/10)  { .g-feat-grid { --cols: 6; --rows: 1; } }
}

/* ---------- One-screen layout (desktop): this page's panels ----------
   Everything fits the window, so the preview pane shrinks as well as grows. */
@media (min-width: 1360px) and (min-height: 600px) {
    .g-preview-pane {
        flex: 1 1 0;
        min-height: 200px;
    }
    .g-preview,
    .g-featured {
        flex: 1 1 0;
        min-height: 0;
    }
    .g-results-pane { flex: 1 1 0; }
    .g-results {
        flex: 1 1 0;
        min-height: 0;
        overflow-y: auto;
        scrollbar-width: thin;
        scrollbar-color: rgba(139, 233, 253, 0.25) transparent;
    }
}

/* Short screens: a slimmer Host panel so the live preview keeps its room */
@media (min-width: 1360px) and (min-height: 600px) and (max-height: 820px) {
    .g-home { --holo: 96px; padding: 8px 20px; gap: 6px 22px; }
    .g-op-label { display: none; }
    .g-home-links { gap: 6px; }
    .g-chan { flex-direction: row; justify-content: center; gap: 6px; padding: 5px 4px; }
    .g-chan-icon { width: 24px; height: 24px; flex: none; }
    .g-chan-icon svg { width: 14px; height: 14px; }
    .g-chan-icon img { width: 17px; height: 17px; }
    .g-chan-text small { display: none; }
    .g-op-stat { padding: 3px 4px; }
    .g-op-stat b { font-size: 13px; }
    .g-op-stat.is-credit b { font-size: 16px; }
    .g-op-tag,
    .g-op-seen { display: none; }
    .g-presence { padding-block: 3px; }
    .g-presence-art { width: 32px; height: 32px; }
    .g-presence-text em { display: none; }
    .g-music-by { display: inline; }
    .g-gh { padding-block: 3px; }
    .g-gh-grid { --cell: 4px; }
}

/* ---------- Responsive: this page's panels ---------- */
/* Two columns: the rail carries explorer, results, then recently viewed. The workspace fills at
   least the window; results take a viewport-based height and recently viewed runs from there
   down to the bottom, whichever is lower of the window or the main column. Both lists scroll. */
@media (min-width: 900px) and (max-width: 1359px) {
    .gotham { flex: 1 0 auto; }
    .g-body {
        flex: 1 0 auto;
        grid-template-columns: clamp(190px, 22%, 260px) minmax(0, 1fr);
        grid-template-rows: auto clamp(220px, 40vh, 480px) minmax(150px, 1fr);
        align-items: stretch;
    }
    .g-rail,
    .g-side { display: contents; }
    .g-explorer { grid-column: 1; grid-row: 1; }
    .g-side > .g-results-pane { grid-column: 1; grid-row: 2; } /* beats the shell's .g-side > :only-child span */
    .g-activity-pane { grid-column: 1; grid-row: 3; }
    .g-main { grid-column: 2; grid-row: 1 / span 3; }
    /* Size containment: list length never stretches the rows; the grid sets their height */
    .g-results-pane,
    .g-activity-pane {
        contain: size;
        display: flex;
        flex-direction: column;
        min-height: 0;
        overflow: hidden;
    }
    .g-results,
    .g-activity {
        flex: 1 1 0;
        min-height: 0;
        max-height: none;
        overflow-y: auto;
        scrollbar-width: thin;
        scrollbar-color: rgba(139, 233, 253, 0.25) transparent;
    }
}

@media (max-width: 1359px) {
    .g-home { --holo: 112px; gap: 12px 18px; padding: 12px 16px; }
}

@media (max-width: 899px) {
    .g-results-pane { order: 3; }
    .g-preview { height: 58vw; min-height: 220px; }
    .g-home-pane .g-meta { display: none; }
    /* Comms: three per row (a 6-track grid: three tiles of two tracks, then two of three) */
    .g-chans { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; }
    .g-chan { grid-column: span 2; padding: 6px 4px; }
    .g-chan:nth-child(n + 4) { grid-column: span 3; }
    .g-chan-icon { width: 26px; height: 26px; }
    .g-chan-icon svg { width: 15px; height: 15px; }
    .g-chan-icon img { width: 18px; height: 18px; }
    .g-results { max-height: 60vh; overflow-y: auto; }
    .g-activity { max-height: 260px; overflow-y: auto; }
}

@media (max-width: 639px) {
    .g-home {
        --holo: clamp(140px, 40vw, 180px);
        grid-template-columns: minmax(0, 1fr);
        justify-items: stretch;
        padding: 14px 12px;
    }
}

/* In the narrow two-column workspace, wrap comms instead of truncating names. */
@media (min-width: 900px) and (max-width: 959px) {
    .g-home { --holo: 96px; }
    .g-chans { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
    .g-chan { flex-direction: row; justify-content: flex-start; text-align: left; gap: 6px; padding: 6px 8px; }
    .g-chan:last-child { grid-column: 1 / -1; }
    .g-chan-icon { flex: none; width: 24px; height: 24px; }
    .g-chan-icon svg { width: 14px; height: 14px; }
    .g-chan-icon img { width: 17px; height: 17px; }
}

/* ---------- Hover effects ----------
   Replaces the old scanline sweep. Panels and featured cards get a soft light that follows the
   pointer (js/objects.js sets --mx/--my on the hovered .g-spot elements) plus a brighter rim;
   rows, chips and stats respond individually. Mostly transforms, colour and opacity, so it stays cheap. */
.g-spot {
    position: relative;
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.g-spot::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    background: radial-gradient(260px circle at var(--mx, 50%) var(--my, 50%), rgba(var(--spot, var(--g-blue-rgb)), 0.09), transparent 70%);
    mix-blend-mode: screen;
    opacity: 0;
    transition: opacity 0.35s ease;
    z-index: 1;
}
.g-spot:hover::after,
.g-spot.is-ambient::after { opacity: 1; }
:is(.g-rail, .g-side) > section.g-spot:hover,
:is(.g-rail, .g-side) > section.g-spot.is-ambient,
.g-objhead.g-spot:hover,
.g-objhead.g-spot.is-ambient,
.g-card.g-spot:hover,
.g-card.g-spot.is-ambient {
    border-color: rgba(139, 233, 253, 0.42);
    box-shadow:
        0 4px 24px rgba(0, 0, 0, 0.3),
        0 0 22px rgba(139, 233, 253, 0.08),
        inset 0 1px 0 rgba(255, 255, 255, 0.1),
        inset 0 0 24px rgba(139, 233, 253, 0.05);
}
.g-spot:hover > .g-panel-title,
.g-spot.is-ambient > .g-panel-title { color: #c9f6ff; text-shadow: 0 0 10px rgba(var(--g-blue-rgb), 0.45); }
.g-panel-title { transition: color 0.3s ease, text-shadow 0.3s ease; }

/* Featured cards: the light takes the card's accent and a glint sweeps across */
.g-feat.g-spot { --spot: var(--feat); }
.g-feat.g-spot::after { background: radial-gradient(200px circle at var(--mx, 50%) var(--my, 50%), rgba(var(--feat), 0.18), transparent 70%); }
.g-feat::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(115deg, transparent 35%, rgba(255, 255, 255, 0.07) 50%, transparent 65%);
    transform: translateX(-120%);
    z-index: 1;
}
.g-feat:hover::before,
.g-feat.is-ambient::before { transform: translateX(120%); transition: transform 0.8s ease; }
.g-feat:hover .g-feat-name,
.g-feat.is-ambient .g-feat-name { text-shadow: 0 0 18px rgba(var(--feat), 0.7); }

/* Object explorer: accent rail slides in, the dot lights up */
.g-obj-btn { transition: background 0.2s ease, border-color 0.2s ease, padding-left 0.2s ease; }
.g-obj-btn:not([aria-current="true"]):hover {
    border-left-color: rgba(var(--g-blue-rgb), 0.45);
    padding-left: 32px;
}
.g-obj-btn .g-dot { transition: box-shadow 0.2s ease, transform 0.2s ease; }
.g-obj-btn:hover .g-dot { transform: scale(1.35); box-shadow: 0 0 8px var(--tone, var(--g-blue)); }

/* Results: rail, icon pop and a nudge of the text */
.g-res { transition: background 0.2s ease, border-color 0.2s ease; }
.g-res:not([aria-current="true"]):hover { border-left-color: rgba(var(--g-blue-rgb), 0.45); background: rgba(var(--g-blue-rgb), 0.07); }
.g-res-icon,
.g-res-text { transition: transform 0.2s ease, box-shadow 0.2s ease, color 0.2s ease; }
.g-res:hover .g-res-icon { transform: scale(1.12); box-shadow: 0 0 12px rgba(var(--g-blue-rgb), 0.35); }
.g-res:hover .g-res-text { transform: translateX(3px); }
.g-res:hover .g-res-title { color: var(--g-blue); }

/* Recently viewed: row tint, dot glow, time brightens */
.g-activity li { transition: background 0.2s ease; }
.g-activity li:hover { background: rgba(var(--g-blue-rgb), 0.05); }
.g-activity .g-dot,
.g-activity time { transition: transform 0.2s ease, box-shadow 0.2s ease, color 0.2s ease; }
.g-activity li:hover .g-dot { transform: scale(1.35); box-shadow: 0 0 8px var(--tone, var(--g-blue)); }
.g-activity li:hover time { color: var(--g-blue); }

/* Comms: cards lift on top of their existing sweep and icon ring */
.g-chan { transition: transform 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease, background 0.25s ease; }
.g-chan:hover,
.g-chan.is-ambient { transform: translateY(-2px); }

/* Dossier: each stat lights up on its own */
.g-op-stat { transition: background 0.2s ease; }
.g-op-stat b { transition: text-shadow 0.2s ease, transform 0.2s ease; }
.g-op-stat:hover,
.g-op-stat.is-ambient { background: rgba(var(--g-blue-rgb), 0.07); }
.g-op-stat:hover b,
.g-op-stat.is-ambient b { transform: scale(1.08); text-shadow: 0 0 12px currentColor; }

/* Object header: the icon tilts toward you */
.g-objicon { transition: transform 0.3s ease, box-shadow 0.3s ease; }
.g-objhead:hover .g-objicon,
.g-objhead.is-ambient .g-objicon { transform: perspective(200px) rotateY(-14deg) scale(1.05); box-shadow: 0 0 16px rgba(var(--g-blue-rgb), 0.35); }

/* ---------- Ambient mode (js/objects.js): idle-screen effects ----------
   Most effects reuse the hover styles above through their .is-ambient twins; these are its own. */
.g-ambient-tag { color: var(--g-blue); }
.g-ambient-tag[hidden] { display: none; }
.g-ambient-tag .g-dot { background: var(--g-blue); box-shadow: 0 0 6px var(--g-blue); animation: g-ambient-blink 1.6s steps(1) infinite; }
@keyframes g-ambient-blink { 50% { opacity: 0.2; } }
/* A scan line sweeping one panel */
.g-ambient-scan { position: relative; }
.g-ambient-scan::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 48px;
    pointer-events: none;
    z-index: 6;
    background: linear-gradient(transparent, rgba(var(--g-blue-rgb), 0.07) 70%, rgba(var(--g-blue-rgb), 0.16));
    border-bottom: 1px solid rgba(var(--g-blue-rgb), 0.5);
    box-shadow: 0 6px 14px rgba(var(--g-blue-rgb), 0.12);
    animation: g-ambient-scan 2.6s linear both;
}
@keyframes g-ambient-scan {
    from { top: -48px; }
    to { top: 100%; }
}

@media (prefers-reduced-motion: reduce) {
    .g-feat::before { display: none; }
    .g-ambient-tag .g-dot { animation: none; }
    .g-res:hover .g-res-icon,
    .g-res:hover .g-res-text,
    .g-chan:hover,
    .g-chan.is-ambient,
    .g-op-stat:hover b,
    .g-op-stat.is-ambient b,
    .g-objhead:hover .g-objicon,
    .g-objhead.is-ambient .g-objicon { transform: none; }
    .g-obj-btn:not([aria-current="true"]):hover { padding-left: 28px; }
    .g-preview-wait,
    .g-op-floor::before,
    .g-op-stars::after,
    .g-op-beam,
    .g-op-sweep,
    .g-op-ring,
    .g-op-stats.ready b { animation: none; }
    .g-acquire,
    .g-op-stats:not(.ready):not(.offline) b { animation: none; background-position: 50% 0; }
    .g-presence-status { animation: none !important; }
    .g-music-eq i { animation: none; transform: scaleY(0.6); }
    .g-presence-bar i { transition: none; }
    .g-live-dot { animation: none; }
    .g-dossier > [data-slide] { transition: none; transform: none; }
    .g-op-beam { display: none; }
    .g-preview-screen::after { animation: none; display: none; }
    .g-preview-screen iframe,
    .g-preview-open,
    .g-preview-open span { transition: none; }
}

/* ---------- Embedded pages (OBJECTS embed: true, js/embed.js) ----------
   The page fills the preview unscaled and takes input, so no screen bezel, CRT lines or click-through
   cover; the panel's grid shows through the page's glass. Its side panels sit under Results. */
.g-preview-screen.is-embed {
    inset: 0;
    width: auto;
    height: auto;
    transform: none;
    background: transparent;
    border: 0;
    box-shadow: none;
}
.g-preview-screen.is-embed::before { display: none; }
.g-preview-screen.is-embed iframe { pointer-events: auto; }
.g-embed-side[hidden] { display: none; }
.g-embed-side {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-height: 0;
}
.g-embed-host {
    display: flex;
    flex-direction: column;
    min-height: 0;
}
.g-embed-label {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 4px;
    font-family: var(--g-mono);
    font-size: 9.5px;
    letter-spacing: 0.2em;
    color: var(--g-dim);
    text-transform: uppercase;
}
.g-embed-label b { color: var(--g-blue); font-weight: 400; }
.g-embed-label::after { content: ''; flex: 1; height: 1px; background: linear-gradient(90deg, var(--g-border-strong), transparent); }
@media (min-width: 1360px) and (min-height: 600px) {
    .g-embed-side { flex: 1.4 1 0; }
    .g-embed-host { flex: 1 1 0; }
    /* The page's left column is as tall as its panels (shrinking, and scrolling, only when they don't fit),
       and Recently Viewed fills whatever is left, so it always starts right below them */
    .g-embed-rail,
    .g-embed-rail .g-embed-host { flex: 0 1 auto; min-height: 0; }
    .g-rail:has(> .g-embed-rail:not([hidden])) .g-activity-pane { flex: 1 1 0; min-height: 110px; }
}
/* Below the one-screen layout nothing sizes the column, so long panels scroll in a box of their own */
@media (max-width: 1359px) {
    .g-embed-host {
        max-height: 70vh;
        overflow-y: auto;
        scrollbar-width: thin;
        scrollbar-color: rgba(139, 233, 253, 0.25) transparent;
    }
}
/* Two columns: both under Results in the rail (left column panels, then side panels), with recently viewed
   pushed down. The page gets a taller frame, as it's laid out for a narrow window here and runs down rather
   than across. */
@media (min-width: 900px) and (max-width: 1359px) {
    .g-preview.is-embed { min-height: min(760px, 80vh); }
    .g-embed-side { grid-column: 1; grid-row: 4; }
    .g-embed-rail { grid-row: 3; }
    .g-body:has(.g-embed-side:not([hidden])) { grid-template-rows: auto clamp(220px, 40vh, 480px) auto auto minmax(150px, 1fr); }
    .g-body:has(.g-embed-side:not([hidden])) .g-activity-pane { grid-row: 5; }
    .g-body:has(.g-embed-side:not([hidden])) .g-main { grid-row: 1 / span 5; }
}
@media (max-width: 899px) {
    .g-embed-side { order: 3; }
    .g-embed-rail { order: 4; }
    .g-preview.is-embed { height: min(130vw, 75vh); }
}

/* ---------- Channel change (js/objects.js tune / tuned) ----------
   While the next object connects the screen shows static; when it's ready it switches on like a CRT: a bright
   line that opens to the full picture. The side panels it hands over slide in with it. */
.g-preview.is-tuning::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 3;
    pointer-events: none;
    background:
        repeating-linear-gradient(0deg, rgba(var(--g-blue-rgb), 0.07) 0 1px, transparent 1px 3px),
        repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.025) 0 2px, transparent 2px 5px),
        radial-gradient(ellipse at center, rgba(var(--g-blue-rgb), 0.08), transparent 70%);
    animation: g-static 0.18s steps(3) infinite;
}
@keyframes g-static {
    0% { background-position: 0 0, 0 0, 0 0; opacity: 0.9; }
    50% { background-position: 0 1px, 3px 0, 0 0; opacity: 0.6; }
    100% { background-position: 0 2px, -2px 0, 0 0; opacity: 1; }
}
.g-preview.is-on .g-preview-screen { animation: g-switch-on 0.5s cubic-bezier(0.2, 0.7, 0.2, 1); }
@keyframes g-switch-on {
    0% { clip-path: inset(49.6% 8% 49.6% 8%); filter: brightness(3) saturate(0.4); }
    35% { clip-path: inset(49.6% 0 49.6% 0); filter: brightness(2.4) saturate(0.6); }
    100% { clip-path: inset(0); filter: none; }
}
.g-embed-side:not([hidden]) { animation: g-embed-in 0.45s cubic-bezier(0.2, 0.7, 0.2, 1); }
.g-embed-rail { --embed-from: -14px; } /* slides in from its own side */
@keyframes g-embed-in {
    from { opacity: 0; transform: translateX(var(--embed-from, 14px)); }
    to { opacity: 1; transform: none; }
}

/* ---------- The preview as an instrument ----------
   Live pages get no sweep band over their text. Corner marks frame the screen and light up, with a rim
   glow, while the embedded page has the keyboard; the chip in the title bar says where keys go and hands
   them over on a click. */
.g-preview-screen.is-embed::after { display: none; }
.g-preview .g-reticle {
    width: 14px;
    height: 14px;
    border: 0 solid rgba(var(--g-blue-rgb), 0.45);
    transition: border-color 0.25s ease, transform 0.25s ease, filter 0.25s ease;
}
.g-preview .g-reticle.tl { top: 6px; left: 6px; border-top-width: 1px; border-left-width: 1px; }
.g-preview .g-reticle.tr { top: 6px; right: 6px; border-top-width: 1px; border-right-width: 1px; }
.g-preview .g-reticle.bl { bottom: 6px; left: 6px; border-bottom-width: 1px; border-left-width: 1px; }
.g-preview .g-reticle.br { bottom: 6px; right: 6px; border-bottom-width: 1px; border-right-width: 1px; }
.g-preview.has-keys .g-reticle {
    border-color: var(--g-blue);
    border-width: 2px 0 0 2px;
    filter: drop-shadow(0 0 4px rgba(var(--g-blue-rgb), 0.8));
}
.g-preview.has-keys .g-reticle.tl { transform: translate(-2px, -2px); }
.g-preview.has-keys .g-reticle.tr { border-width: 2px 2px 0 0; transform: translate(2px, -2px); }
.g-preview.has-keys .g-reticle.bl { border-width: 0 0 2px 2px; transform: translate(-2px, 2px); }
.g-preview.has-keys .g-reticle.br { border-width: 0 2px 2px 0; transform: translate(2px, 2px); }
.g-preview { transition: box-shadow 0.3s ease; }
.g-preview.has-keys { box-shadow: inset 0 0 0 1px rgba(var(--g-blue-rgb), 0.35), inset 0 0 28px rgba(var(--g-blue-rgb), 0.08); }
.g-keys {
    margin-left: auto;
    font-family: var(--g-mono);
    font-size: 10px;
    letter-spacing: 0.12em;
    color: var(--g-dim);
    background: none;
    border: 1px solid var(--g-border);
    border-radius: 999px;
    padding: 1px 8px;
    transition: color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.g-keys[hidden] { display: none; }
.g-keys:hover { color: var(--g-blue); border-color: var(--g-border-strong); }
.g-keys[aria-pressed="true"] {
    color: var(--g-blue);
    border-color: var(--g-blue);
    box-shadow: 0 0 10px rgba(var(--g-blue-rgb), 0.35);
}

/* A row in Results pings when its embedded page reports news (js/objects.js TirlEmbedHost.event) */
.g-res.is-ping { animation: g-res-ping 2.4s ease-out; }
.g-res.is-ping > .g-dot { animation: g-dot-ping 0.6s ease-out 3; }
@keyframes g-res-ping {
    0% { background: rgba(var(--g-blue-rgb), 0.28); box-shadow: inset 2px 0 0 var(--g-blue); }
    100% { box-shadow: inset 2px 0 0 transparent; }
}
@keyframes g-dot-ping {
    0% { box-shadow: 0 0 0 0 rgba(var(--g-blue-rgb), 0.7); }
    100% { box-shadow: 0 0 0 7px transparent; }
}

/* ---------- Command palette (js/palette.js) ---------- */
.g-search input { cursor: pointer; }
.g-pal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: grid;
    justify-items: center;
    align-items: start;
    padding: min(14vh, 120px) 16px 16px;
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    animation: g-pal-fade 0.16s ease-out;
}
.g-pal[hidden] { display: none; }
@keyframes g-pal-fade { from { opacity: 0; } }
.g-pal-box {
    width: min(640px, 100%);
    max-height: min(560px, 76vh);
    display: flex;
    flex-direction: column;
    background: rgba(6, 10, 14, 0.88);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border: 1px solid var(--g-border-strong);
    border-radius: 14px;
    box-shadow: 0 24px 80px rgba(0, 0, 0, 0.6), 0 0 40px rgba(var(--g-blue-rgb), 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.08);
    overflow: hidden;
    animation: g-pal-in 0.22s cubic-bezier(0.2, 0.7, 0.2, 1);
}
@keyframes g-pal-in { from { opacity: 0; transform: translateY(-8px) scale(0.98); } }
.g-pal-search {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--g-border);
}
.g-pal-search svg { width: 15px; height: 15px; fill: var(--g-blue); flex: none; }
.g-pal-search input {
    flex: 1;
    min-width: 0;
    background: none;
    border: 0;
    outline: none;
    color: var(--g-text);
    font: inherit;
    font-size: 15px;
    caret-color: var(--g-blue);
}
.g-pal-search input::placeholder { color: var(--g-dim); }
.g-pal kbd {
    font-family: var(--g-mono);
    font-size: 10px;
    color: var(--g-muted);
    border: 1px solid var(--g-border-strong);
    border-radius: 4px;
    padding: 0 5px;
    line-height: 16px;
}
.g-pal-meta {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 6px 14px;
    font-family: var(--g-mono);
    font-size: 10px;
    letter-spacing: 0.14em;
    color: var(--g-dim);
    background: var(--g-panel-2);
    border-bottom: 1px solid var(--g-border);
}
.g-pal-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    margin: 0;
    padding: 4px 0;
    list-style: none;
    scrollbar-width: thin;
    scrollbar-color: rgba(var(--g-blue-rgb), 0.25) transparent;
}
.g-pal-item {
    width: 100%;
    display: grid;
    grid-template-columns: 26px minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
    padding: 6px 14px;
    background: none;
    border: 0;
    border-left: 2px solid transparent;
    color: inherit;
    text-align: left;
    font: inherit;
}
[aria-selected="true"] > .g-pal-item {
    background: linear-gradient(90deg, rgba(var(--g-blue-rgb), 0.16), transparent);
    border-left-color: var(--g-blue);
}
.g-pal-icon {
    width: 26px;
    height: 26px;
    border-radius: 6px;
    object-fit: cover;
    display: grid;
    place-items: center;
    background: var(--g-well);
    color: var(--g-blue);
    font-family: var(--g-mono);
}
.g-pal-text { display: flex; flex-direction: column; min-width: 0; }
.g-pal-name,
.g-pal-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.g-pal-name { color: var(--g-text); font-size: 12.5px; }
[aria-selected="true"] > .g-pal-item .g-pal-name { color: var(--g-blue); }
.g-pal-name mark { background: none; color: var(--g-blue); text-shadow: 0 0 8px rgba(var(--g-blue-rgb), 0.6); }
.g-pal-sub { color: var(--g-dim); font-size: 10.5px; }
.g-pal-chip {
    font-family: var(--g-mono);
    font-size: 9.5px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--g-muted);
    border: 1px solid var(--g-border);
    border-radius: 999px;
    padding: 1px 8px;
    white-space: nowrap;
}
.g-pal-empty {
    padding: 28px 14px;
    text-align: center;
    font-family: var(--g-mono);
    font-size: 11px;
    letter-spacing: 0.08em;
    color: var(--g-dim);
}
.g-pal-foot {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    padding: 8px 14px;
    border-top: 1px solid var(--g-border);
    font-size: 10.5px;
    color: var(--g-dim);
}
.g-pal-foot span { display: inline-flex; align-items: center; gap: 4px; }
@media (max-width: 639px) {
    .g-pal { padding-top: 12px; }
    .g-pal-chip { display: none; }
    .g-pal-foot { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .g-preview.is-tuning::after,
    .g-preview.is-on .g-preview-screen,
    .g-embed-side:not([hidden]),
    .g-res.is-ping,
    .g-res.is-ping > .g-dot,
    .g-pal,
    .g-pal-box { animation: none; }
}

/* ---------- Player link (js/player.js), on the home object ----------
   Signed out: the games' two sign-ins, on the right of the header beside the description. Signed in: who you are
   in the top bar (#g-whoami), and a card per game in its own color under the header's description. */
.g-objsplit {
    display: flex;
    align-items: flex-end;
    gap: 10px 24px;
}
.g-objinfo { flex: 1 1 auto; min-width: 0; }
.g-player-sso {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
}
.g-player-sso[hidden] { display: none; }
.g-player-note { font-size: 10.5px; color: var(--g-red); }
.g-player:empty { display: none; }
.g-player {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--g-border);
}
.g-player-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 10px;
}
.g-player-label {
    font-family: var(--g-mono);
    font-size: 10px;
    letter-spacing: 0.2em;
    color: var(--g-blue);
}
.g-player-head small { color: var(--g-dim); font-size: 10.5px; }
/* Top bar, signed in: pushes the search box over */
.g-whoami {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.g-whoami[hidden] { display: none; }
.g-whoami:not([hidden]) + .g-search { margin-left: 0; }
.g-whoami .g-btn { padding: 2px 8px; font-size: 10px; }
.g-sso-row { display: flex; align-items: center; gap: 8px; }
.g-sso-row > i { color: var(--g-dim); font-style: normal; font-size: 10.5px; }
.g-sso {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 5px 12px;
}
.g-sso img,
.g-sso svg { width: 15px; height: 15px; flex: none; }
.g-sso:not(.ur):hover { border-color: rgba(255, 222, 23, 0.6); box-shadow: 0 0 12px rgba(255, 222, 23, 0.2); }
.g-sso.ur { color: #6fb0ff; }
.g-sso.ur:hover { border-color: rgba(111, 176, 255, 0.6); box-shadow: 0 0 12px rgba(111, 176, 255, 0.25); }
/* The verified name: Remilia's mark in gold, Urbit's in blue, as on the games' boards */
.g-ptag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 1px 9px 1px 4px;
    border: 1px solid var(--g-border-strong);
    border-radius: 999px;
    font-family: var(--g-mono);
    font-size: 11px;
    text-decoration: none;
}
.g-ptag img,
.g-ptag svg { width: 15px; height: 15px; }
.g-ptag.rm { color: #ffde17; border-color: rgba(255, 222, 23, 0.35); }
.g-ptag.ur { color: #6fb0ff; border-color: rgba(111, 176, 255, 0.4); }
.g-ptag b { font-weight: 400; }
.g-player-wait {
    padding: 12px;
    text-align: center;
    font-family: var(--g-mono);
    font-size: 11px;
    letter-spacing: 0.14em;
    color: var(--g-blue);
    border: 1px dashed var(--g-border);
    border-radius: 10px;
    animation: g-wait 1.2s ease-in-out infinite;
}
.g-player-wait.is-error { color: var(--g-muted); animation: none; }
.g-player-wait .g-btn { margin-left: 6px; }
.g-games {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 10px;
}
.g-game {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
    padding: 9px 12px 10px;
    border: 1px solid rgba(var(--game), 0.25);
    border-radius: 10px;
    background: linear-gradient(135deg, rgba(var(--game), 0.07), transparent 60%), var(--g-well);
    color: inherit;
    text-decoration: none;
    transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.g-game:hover {
    border-color: rgba(var(--game), 0.6);
    box-shadow: 0 0 18px rgba(var(--game), 0.15);
    transform: translateY(-1px);
}
.g-game-head {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.g-game-head img { width: 22px; height: 22px; border-radius: 5px; object-fit: cover; flex: none; }
.g-game-head > b {
    color: rgb(var(--game));
    font-family: var(--g-mono);
    font-weight: 400;
    font-size: 12px;
    letter-spacing: 0.06em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.g-game-head em {
    font-style: normal;
    font-family: var(--g-mono);
    font-size: 9.5px;
    letter-spacing: 0.1em;
    color: #ffde17;
    border: 1px solid rgba(255, 222, 23, 0.45);
    border-radius: 999px;
    padding: 0 7px;
    white-space: nowrap;
}
.g-game-go {
    margin-left: auto;
    font-family: var(--g-mono);
    font-size: 10px;
    letter-spacing: 0.12em;
    color: var(--g-dim);
    white-space: nowrap;
}
.g-game:hover .g-game-go { color: rgb(var(--game)); }
.g-pstats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 6px;
}
.g-pstat {
    display: flex;
    flex-direction: column;
    min-width: 0;
    padding: 5px 8px;
    border-radius: 7px;
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.05);
}
.g-pstat b {
    font-family: var(--g-mono);
    font-weight: 400;
    font-size: 14px;
    color: var(--g-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-variant-numeric: tabular-nums;
}
.g-pstat.drip b { color: rgb(var(--game)); }
.g-pstat small {
    font-size: 9.5px;
    color: var(--g-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.g-pstats .good,
.g-game-last .good { color: var(--g-green); }
.g-pstats .bad,
.g-game-last .bad { color: var(--g-red); }
.g-game-last {
    display: flex;
    align-items: baseline;
    gap: 10px;
    font-size: 10.5px;
    color: var(--g-muted);
    min-width: 0;
}
.g-game-last > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.g-game-last b { color: var(--g-text); font-weight: 400; }
.g-game-last i { margin-left: auto; font-style: normal; color: var(--g-dim); white-space: nowrap; }
@media (max-width: 899px) {
    .g-whoami { order: 2; }
    .g-whoami .g-player-label { display: none; }
}
@media (max-width: 639px) {
    .g-pstats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .g-objsplit { flex-direction: column; align-items: stretch; }
    .g-player-sso { align-items: flex-start; }
}
@media (prefers-reduced-motion: reduce) {
    .g-player-wait { animation: none; }
    .g-game { transition: none; }
}
