/* ==========================================================
   MY PORTFOLIO
   ==========================================================
   The one page whose job is to answer "what do I actually own", so the layout is
   built around numbers being read, not admired: each section is a card with a
   labelled title bar, a large figure where there is one, and a definition list
   under it for everything else. Fonts and tokens are the site's own — nothing new
   is introduced here.

   The background is the Knight's Hall art under a heavy scrim. It is deliberate
   and it is quiet: a portfolio is dense with figures, and the art sits behind a
   scrim dark enough that no number is ever read against the chandelier. The two
   media queries below drop the art entirely on narrow screens, where the cards
   already fill the width and the picture would only cost bytes.
   ========================================================== */

.portfolio-page {
    position: relative;
}

/* The page's own art, not the Knight's Hall's. It used to borrow `knight-hall.webp`, which meant
   the Portfolio looked like a room belonging to the roster — the two are different places and now
   have different pictures.

   The scrim is set the way the hall panels' was: against the art's own mean luma rather than by
   eye. Measured with `ffmpeg -vf signalstats` (YAVG), this picture is **79 of 255** where the
   Knight's Hall's is 74 and the Summoning Chamber's 71 — the brightest of the three, and the one
   wearing the most type, which is why its scrim is the heaviest of the three (0.50 → 0.86 against
   the panels' 0.45 → 0.82): it stays plainly visible while the numbered rows read over it. */
.portfolio-page::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 0;
    background-image:
        linear-gradient(180deg, rgba(13, 10, 8, 0.5) 0%, rgba(13, 10, 8, 0.86) 100%),
        url('/assets/hall/portfolio.webp');
    background-size: cover;
    background-position: center;
    pointer-events: none;
}

.portfolio-page > * {
    position: relative;
    z-index: 1;
}

/* ------------------------------------------------------------------ the main column */
.pf-main {
    flex: 1;
    overflow-y: auto;
    padding: 22px 26px 40px;
    display: flex;
    flex-direction: column;
    gap: 16px;
    max-width: 1180px;
    width: 100%;
    margin: 0 auto;
}

/* --------------------------------------------------------------------- identity strip */
.pf-identity {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    padding: 10px 14px;
    background: var(--bg-panel);
    border: 1px solid var(--border-base);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

.pf-identity-who {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.pf-address {
    font-family: var(--font-heading);
    font-size: 14px;
    letter-spacing: 1px;
    color: var(--text-primary);
    white-space: nowrap;
}

.pf-copy {
    font-family: var(--font-body);
    font-size: 10.5px;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--text-muted);
    background: transparent;
    border: 1px solid var(--border-dim);
    border-radius: var(--radius-sm);
    padding: 2px 7px;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
}

.pf-copy:hover {
    color: var(--accent-gold);
    border-color: var(--border-gold);
}

.pf-identity-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font-body);
    font-size: 11.5px;
    color: var(--text-muted);
}

.pf-dot-sep {
    color: var(--border-base);
}

.pf-refresh {
    margin-left: 4px;
    font-family: var(--font-heading);
    font-size: 10.5px;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    color: var(--accent-gold);
    background: transparent;
    border: 1px solid var(--border-bronze);
    border-radius: var(--radius-sm);
    padding: 4px 10px;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.pf-refresh:hover:not(:disabled) {
    background: var(--bg-hover);
}

.pf-refresh:disabled {
    opacity: 0.45;
    cursor: default;
}

/* ------------------------------------------------------------------------- the grid */
.pf-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
}

.pf-card {
    background: var(--bg-panel);
    border: 1px solid var(--border-base);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    padding: 14px 16px 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-width: 0;
}

.pf-card-wide {
    grid-column: 1 / -1;
}

.pf-card-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font-heading);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 1.4px;
    text-transform: uppercase;
    color: var(--accent-gold);
    border-bottom: 1px solid var(--border-dim);
    padding-bottom: 8px;
}

.pf-card-icon {
    width: 20px;
    height: 20px;
    image-rendering: pixelated;
    flex-shrink: 0;
}

.pf-card-icon-round {
    border-radius: 50%;
}

.pf-card-count {
    margin-left: auto;
    font-family: var(--font-display);
    font-size: 14px;
    letter-spacing: 0;
    color: var(--text-primary);
}

/* ------------------------------------------------------------------------ hero figure */
.pf-hero {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
}

.pf-hero-value {
    font-family: var(--font-display);
    font-size: 30px;
    line-height: 1;
    color: var(--accent-gold);
    text-shadow: 0 0 24px rgba(212, 175, 55, 0.18);
}

.pf-hero-label {
    font-family: var(--font-body);
    font-size: 11px;
    letter-spacing: 0.6px;
    color: var(--text-muted);
}

/* ------------------------------------------------------------------------ data rows */
.pf-rows {
    display: flex;
    flex-direction: column;
    gap: 0;
}

.pf-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 6px 0;
    border-bottom: 1px solid var(--border-dim);
}

.pf-row:last-child {
    border-bottom: none;
}

.pf-row dt {
    font-family: var(--font-body);
    font-size: 11.5px;
    color: var(--text-secondary);
    flex-shrink: 0;
}

.pf-row dd {
    font-family: var(--font-heading);
    font-size: 13px;
    color: var(--text-primary);
    text-align: right;
    min-width: 0;
}

.pf-row-hint {
    display: block;
    font-family: var(--font-body);
    font-size: 10px;
    letter-spacing: 0.3px;
    color: var(--text-muted);
    margin-top: 1px;
}

/* -------------------------------------------------------------------- supply progress */
.pf-supply {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding-top: 4px;
    border-top: 1px solid var(--border-dim);
}

.pf-supply-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}

.pf-supply-label {
    font-family: var(--font-body);
    font-size: 11px;
    color: var(--text-secondary);
}

.pf-supply-numbers {
    font-family: var(--font-heading);
    font-size: 12px;
    color: var(--text-primary);
}

.pf-supply-track {
    height: 4px;
    background: var(--bg-void);
    border: 1px solid var(--border-dim);
    border-radius: 2px;
    overflow: hidden;
}

.pf-supply-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--accent-gold-dim), var(--accent-gold));
}

.pf-bands {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 4px;
}

.pf-bands li {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: 10px;
    align-items: baseline;
    font-family: var(--font-body);
    font-size: 10.5px;
    color: var(--text-muted);
    padding: 2px 0;
}

.pf-band-name {
    color: var(--text-secondary);
}

.pf-band-range {
    letter-spacing: 0.3px;
}

.pf-band-left {
    font-family: var(--font-heading);
    color: var(--text-primary);
    min-width: 58px;
    text-align: right;
}

/* --------------------------------------------------------------------- activity list */
.pf-activity {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0;
}

.pf-activity li {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto auto;
    gap: 12px;
    align-items: baseline;
    padding: 7px 0;
    border-bottom: 1px solid var(--border-dim);
    font-family: var(--font-body);
    font-size: 11.5px;
}

.pf-activity li:last-child {
    border-bottom: none;
}

/* Points rows: when, what, how much. Three slots because there is no knight to name — the fourth
   column the dungeon rows used was a rarity and an id that a points entry does not have. */
.pf-activity-points li {
    grid-template-columns: auto minmax(0, 1fr) auto;
}

.pf-act-what {
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pf-act-dungeon {
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pf-act-knight {
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pf-act-reward {
    font-family: var(--font-heading);
    color: var(--accent-gold);
    white-space: nowrap;
}

/* The one row that is negative. Muted rather than gold: a spend is not a reward, and colouring it
   like one would make a redeemed card read as an earning on the same list. */
.pf-act-reward.is-spend {
    color: var(--text-muted);
}

.pf-act-when {
    color: var(--text-muted);
    font-size: 10.5px;
    white-space: nowrap;
    min-width: 56px;
    text-align: right;
}

/* -------------------------------------------------- panels that are not live yet
   The $DNG and Genesis panels read real things that are not open to players yet. Blurring the
   body keeps the shape of what is coming — a panel with figures in it — while making it plain
   that nothing there is meant to be read or acted on. The title stays crisp so the panel is
   still identifiable, and `pointer-events: none` means the buttons inside it do nothing: a
   dead link to a gated page is worse than an obviously disabled panel. */
.pf-soon {
    position: relative;
    border-style: dashed;
}

.pf-soon-body,
.pf-blur {
    filter: blur(5px);
    opacity: 0.5;
    pointer-events: none;
    user-select: none;
}

/* `pf-blur` is the same treatment without the chip, for things that are not "coming soon" —
   just not for a visitor: the chain and read stamp, and the two buttons that lead into the
   password-gated game. */
.pf-blur {
    opacity: 0.45;
}

.pf-soon-chip {
    margin-left: auto;
    font-family: var(--font-body);
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--accent-gold);
    background: rgba(212, 175, 55, 0.12);
    border: 1px solid var(--border-gold, var(--border-base));
    border-radius: 999px;
    padding: 2px 8px;
    white-space: nowrap;
}

.pf-soon-note {
    font-family: var(--font-body);
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-muted);
}

/* The sample squad's own label, and deliberately not `.pf-soon-chip` recoloured: that chip means
   the panel is *shut*, and this one means the panel is open and what is in it is a fixture. Same
   size, same corner, a dashed border and the muted end of the palette — provisional chrome should
   read as provisional rather than as one more status. */
.pf-sample-chip {
    margin-left: auto;
    font-family: var(--font-body);
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, 0.03);
    border: 1px dashed var(--border-base);
    border-radius: 999px;
    padding: 2px 8px;
    white-space: nowrap;
}

.pf-sample-note {
    margin-bottom: 12px;
    font-family: var(--font-body);
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-muted);
}

/* --------------------------------------------------------- notes, warnings and links */
.pf-note {
    font-family: var(--font-body);
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-muted);
}

.pf-warn {
    font-family: var(--font-body);
    font-size: 11px;
    line-height: 1.6;
    color: var(--danger-hover);
    background: rgba(192, 57, 43, 0.08);
    border-left: 2px solid var(--danger);
    border-radius: var(--radius-sm);
    padding: 6px 9px;
}

.pf-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: auto;
    padding-top: 8px;
    border-top: 1px solid var(--border-dim);
}

.pf-link {
    font-family: var(--font-heading);
    font-size: 10.5px;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    color: var(--accent-gold);
    text-decoration: none;
    border: 1px solid var(--border-bronze);
    border-radius: var(--radius-sm);
    padding: 5px 10px;
    transition: background 0.15s, border-color 0.15s;
}

.pf-link:hover {
    background: var(--bg-hover);
    border-color: var(--border-gold);
}

.pf-footnote {
    font-family: var(--font-body);
    font-size: 10.5px;
    line-height: 1.6;
    color: var(--text-muted);
    text-align: center;
    max-width: 720px;
    margin: 0 auto;
}

/* -------------------------------------------------------------------- disconnected */
.pf-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    text-align: center;
    max-width: 520px;
    margin: auto;
    padding: 40px 24px;
}

.pf-empty-art {
    width: 96px;
    height: 96px;
    object-fit: contain;
    image-rendering: pixelated;
    opacity: 0.9;
}

.pf-empty-title {
    font-family: var(--font-display);
    font-size: 24px;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--accent-gold);
}

.pf-empty-text {
    font-family: var(--font-body);
    font-size: 12.5px;
    line-height: 1.8;
    color: var(--text-secondary);
}

/* ------------------------------------------------------------------- My capsules
   The one card on this page whose subject is a picture. Everything else here is a number read off
   a chain; this is a box with something glowing inside it, and the art has to carry it — so the
   turn gets the top of the card, the count sits under it in the same `pf-hero` the points panel
   uses, and the two read as one object. */
.pf-capsules {
    position: relative;
    overflow: hidden;
}

/* A warm pool behind the capsule, so the object looks lit rather than pasted onto a dark card. */
.pf-capsules::before {
    content: '';
    position: absolute;
    top: -40px;
    left: 50%;
    width: 260px;
    height: 260px;
    transform: translateX(-50%);
    background: radial-gradient(closest-side, rgba(212, 168, 67, 0.14), transparent 72%);
    pointer-events: none;
}

/* The turn itself.

   Deliberately capped rather than allowed to fill the column: at 220px the capsule reads as an
   object you can pick up, and the reward for a wider screen is the rest of the page rather than a
   bigger box. `touch-action: none` is what lets a finger drag it without the page scrolling
   underneath, and `user-select: none` stops a drag from selecting the picture. */
.pf-spin {
    position: relative;
    width: 100%;
    max-width: 220px;
    aspect-ratio: 1 / 1;
    margin: 2px auto 10px;
    border-radius: 50%;
    cursor: grab;
    touch-action: none;
    user-select: none;
}

.pf-spin.is-dragging {
    cursor: grabbing;
}

.pf-spin:focus-visible {
    outline: 1px solid var(--accent-gold);
    outline-offset: 6px;
}

.pf-spin-frame {
    display: block;
    width: 100%;
    height: 100%;
    /* The frames are cut with alpha, so the shadow has to come from the picture rather than from a
       box: this is what stops the capsule looking like a sticker on a rectangle. */
    filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.55));
    -webkit-user-drag: none;
}

/* There is no caption under the capsule, and its absence is the decision. "Drag to spin" was
   printed there for a while — it faded to 30% once you had spun it once — and it came off for the
   reason a caption under a prize always should: the gesture is discoverable by trying it, and the
   words read as a label on a toy rather than a thing you own. A reader who cannot see the page
   still gets the instruction, in the element's `aria-label`, which is the only place it belongs. */

.pf-capsule-count {
    justify-content: center;
    text-align: center;
}

/* --------------------------------------------------------------------------- phone */
@media (max-width: 900px) {
    /* This used to drop the art entirely and paint a flat scrim — which is what a phone visitor
       got instead of the room they were standing in. It is the small file now (79 KB against the
       desktop's 254 KB), scrimmed harder because a phone reads the same cards through a much
       narrower column. */
    .portfolio-page::before {
        background-image:
            linear-gradient(180deg, rgba(13, 10, 8, 0.64) 0%, rgba(13, 10, 8, 0.9) 100%),
            url('/assets/hall/portfolio-mobile.webp');
    }

    .pf-main {
        padding: 16px 14px 32px;
        gap: 12px;
    }

    .pf-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .pf-hero-value {
        font-size: 25px;
    }

}

@media (max-width: 560px) {
    .pf-identity {
        align-items: flex-start;
        flex-direction: column;
        gap: 8px;
    }

    .pf-identity-meta {
        flex-wrap: wrap;
    }

    .pf-activity li {
        grid-template-columns: minmax(0, 1fr) auto;
    }

    .pf-act-knight,
    .pf-act-when {
        display: none;
    }
}
