/* ============================================================
   profile.css

   Owns profile-page surfaces wherever they render:
     - Dashboard Profile column header (pr24 base, pr37 refresh)
     - Standalone Profile page at /user/<username> (pr38)
     - Pinned post block (pr16a wave 1)
     - Free-space cell — wave 2 base + wave 3 modes B/C + wave 4
       polish + Mode D linktree + Mode E post reference, plus
       pr25b media placeholders
     - Free-space linktree drag-to-reorder (pr25h)

   Reference: markdown/design/DESIGN_DECISIONS_CSS_REFACTOR.md
   ============================================================ */

/* ============================================================
   Profile column header (pr24)
   Larger profile-identity block at the top of the Profile
   column body. Distinct from .dashboard-column-header (the
   sticky type-label row) — this is the identity content per
   DESIGN_DECISIONS_VISUAL.md §3.3.
   ============================================================ */

.profile-column-header {
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    border-bottom: 1px solid var(--bs-border-color-translucent);
}

.profile-column-identity {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}

.profile-column-identity-text {
    flex: 1 1 auto;
    min-width: 0;
}

.profile-column-name-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
}

.profile-column-name {
    font-size: var(--font-size-body);
    min-width: 0;
}

.profile-column-handle {
    /* Inherits text-muted + small from utility classes;
       this rule just guarantees truncation room. */
    min-width: 0;
}

.profile-column-bio {
    font-size: var(--font-size-base);
    line-height: 1.5;
}

.profile-column-meta {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.profile-column-meta-stats {
    /* Group the post-count and join-date so they wrap together
       on narrow columns, leaving "view full profile →" on the
       right. */
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-1);
}

.profile-column-meta-sep {
    /* Subtle separator dot — slightly more muted than the
       surrounding text so it reads as punctuation, not content. */
    opacity: 0.6;
}

/* Pencil button — same visual language as .freespace-edit-btn
   but slimmer because it sits inline with the display name
   rather than absolute-positioned over a cell. */
.profile-column-edit-btn {
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;

    background: transparent;
    border: none;
    border-radius: var(--radius-pill);
    color: var(--color-text-muted);

    cursor: pointer;
    transition: background-color var(--transition-fast),
        color var(--transition-fast);
}

.profile-column-edit-btn:hover,
.profile-column-edit-btn:focus-visible {
    background-color: color-mix(in srgb, var(--color-accent) 12%, transparent);
    color: var(--color-accent);
    text-decoration: none;
}

/* ============================================================
   Pinned post (pr16a — wave 1)
   ============================================================ */

.pinned-post-wrapper {
    /* Subtle visual lift so the pinned post reads as elevated. The
       label above already telegraphs "this is pinned"; we just want
       a quiet styling cue, not a big highlight. */
}

.pinned-post-label {
    /* The "📌 pinned to profile" row above the post. Lives outside
       the .card so it doesn't fight post-card styling. */
    padding-left: var(--space-2);
}

/* ============================================================
   Profile free-space (pr16b wave 2)
   ============================================================
   The cell that sits next to the avatar block on a profile page.
   Visible to the profile owner always (with the empty-state CTA);
   visible to visitors only when a mode is configured.

   Visual treatment: light violet border with a subtle inner gradient
   fading the border into the surface near the corners. Same vibe as
   the compose modal — reads as "interactive container," not just
   a random rectangle.
   ============================================================ */

.profile-header-identity {
    /* Lets the column shrink as the row resizes. */
    min-width: 0;
}

.profile-header-freespace {
    /* Same — the cell content shouldn't force horizontal overflow. */
    min-width: 0;
}

.freespace-cell {
    /* Border-as-gradient pattern: a translucent violet that's
       strongest in the middle of each edge and fades into the
       surface near the corners. We achieve this by layering a
       background-image (the gradient) on a transparent border —
       but a simpler-and-still-good approach is a soft solid
       border at the accent color with reduced opacity. We can
       upgrade to the gradient treatment in a polish pass. */
    border: 1px solid color-mix(in srgb, var(--color-accent) 35%, transparent);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    background-color: color-mix(in srgb, var(--color-accent) 3%, var(--color-bg-surface));
    min-height: 100%;

    /* The cell stretches to match the left column's height when the
       left is taller. When the cell is taller, the card grows. */
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.freespace-cell--unframed {
    border-color: transparent;
    background-color: transparent;
    padding: 0;
}

/* ============================================================
   Free-space cell — owner hover scale (pr16b wave 4 polish)
   ============================================================ */
.freespace-cell--unframed.freespace-cell--owner:hover,
.freespace-cell--unframed.freespace-cell--owner:focus-within {
    border-color: color-mix(in srgb, var(--color-accent) 20%, transparent);
    background-color: color-mix(in srgb, var(--color-accent) 2%, var(--color-bg-surface));
    padding: var(--space-2);
    transition: border-color var(--transition-medium),
        background-color var(--transition-medium),
        padding var(--transition-medium);
}

.freespace-cell--unframed {
    transition: border-color var(--transition-medium),
        background-color var(--transition-medium),
        padding var(--transition-medium);
}

.freespace-cell--owner {
    transform: scale(1);
    transform-origin: center;
    transition: transform var(--transition-medium),
        border-color var(--transition-medium),
        background-color var(--transition-medium),
        padding var(--transition-medium);
}

.freespace-cell--owner:hover,
.freespace-cell--owner:focus-within {
    transform: scale(1.02);
}

/* override the media image hover scale */
.freespace-cell--owner .freespace-media-link:hover .freespace-media-image,
.freespace-cell--owner .freespace-media-link:focus-visible .freespace-media-image {
    transform: none;
}

@media (prefers-reduced-motion: reduce) {

    .freespace-cell--owner,
    .freespace-cell--owner:hover,
    .freespace-cell--owner:focus-within {
        transform: none;
        transition: border-color var(--transition-medium),
            background-color var(--transition-medium),
            padding var(--transition-medium);
    }
}

/* Empty-state CTA (owner only) */
.freespace-empty-cta {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    min-height: 80px;
    color: var(--color-text-muted);
    text-decoration: none;
    font-size: var(--font-size-body);
    border-radius: var(--radius-sm);
    transition: color var(--transition-fast),
        background-color var(--transition-fast);
}

.freespace-empty-cta:hover,
.freespace-empty-cta:focus-visible {
    color: var(--color-accent);
    background-color: color-mix(in srgb, var(--color-accent) 6%, transparent);
}

.freespace-empty-plus {
    font-size: 1.5rem;
    font-weight: var(--font-weight-light);
    line-height: 1;
}

/* Mobile: the row already stacks via Bootstrap's col-md-* classes
   (cells become full width below 768px). Just clean up spacing. */
@media (max-width: 767.98px) {
    .freespace-cell {
        margin-top: var(--space-3);
    }
}

/* ---- Edit pencil button (owner only) -------------------- */

.freespace-cell {
    /* Re-declared here as a positioning context for the edit btn. */
    position: relative;
}

.freespace-edit-btn {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    width: 28px;
    height: 28px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;

    background: transparent;
    border: none;
    border-radius: var(--radius-pill);
    color: var(--color-text-muted);

    cursor: pointer;
    transition: background-color var(--transition-fast),
        color var(--transition-fast);

    /* Above the empty-state CTA so it's clickable through it. */
    z-index: 2;
}

.freespace-edit-btn:hover,
.freespace-edit-btn:focus-visible {
    background-color: color-mix(in srgb, var(--color-accent) 12%, transparent);
    color: var(--color-accent);
}

/* ---- Modal mode picker ---------------------------------- */

.freespace-mode-picker {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.freespace-mode-option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color var(--transition-fast);
}

.freespace-mode-option:hover {
    background-color: color-mix(in srgb, var(--color-accent) 6%, transparent);
}

.freespace-mode-option.is-disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

.freespace-mode-option.is-disabled:hover {
    background-color: transparent;
}

.freespace-mode-label {
    font-size: var(--font-size-body);
    color: var(--color-text);
}

.freespace-mode-option.is-disabled .freespace-mode-label {
    color: var(--color-text-muted);
}

/* The empty-state CTA was an <a> in step 3 but is now a <button>.
   Reset Bootstrap's button defaults so it visually matches what we
   had before. */
button.freespace-empty-cta {
    background: transparent;
    border: none;
    text-align: center;
}

/* ---- Mode A (text) rendering ---------------------------- */

.freespace-content-text {
    word-break: break-word;
    font-size: var(--font-size-body);
    color: var(--color-text);
    line-height: 1.5;
}

.freespace-content-text p {
    white-space: pre-wrap;
    margin-bottom: var(--space-2);
}

/* ---- Style presets for mode A (text) -------------------- */

/* Each preset overrides one or more aspects of the base
   .freespace-content-text styling. NULL preset uses base styling.

   Design intent: each preset is a coherent "voice" for the cell.
   Not a la carte. Users pick a preset, not individual controls. */

.freespace-preset-default {
    /* Identical to base styling. Declared explicitly so the picker
       UI has a "default" option that the user can select to revert
       from another preset without it being a no-op or confusing. */
    font-family: var(--font-sans);
    font-size: var(--font-size-body);
    font-weight: var(--font-weight-regular);
    line-height: 1.5;
    text-align: left;
}

.freespace-preset-emphasis {
    /* Pull-quote feel — large, light, centered, generous line-height.
       Geist Light (300) was self-hosted in pr6a so this works without
       a font-fetching delay. */
    font-family: var(--font-sans);
    font-size: 1.5rem;
    font-weight: var(--font-weight-light);
    line-height: 1.4;
    text-align: center;
    /* Slight letter-spacing tightening, which large light text usually
       benefits from. */
    letter-spacing: -0.01em;
}

.freespace-preset-tight {
    /* Info-density treatment. Smaller text, tight line-height.
       Useful for "fast facts" or bullet-list bios. */
    font-family: var(--font-sans);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-regular);
    line-height: 1.3;
    text-align: left;
}

.freespace-preset-mono {
    /* Computer voice. Geist Mono (self-hosted, pr6a). Body-size for
       readability — mono fonts are larger-feeling per character so
       keeping at body-size avoids visual heaviness. */
    font-family: var(--font-mono);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-regular);
    line-height: 1.5;
    text-align: left;
}

/* Markdown output styling — make sure rendered <strong>, <em>, etc.
   look right inside the cell regardless of preset. The base rules
   here are inherited by all presets unless overridden. */

.freespace-content-text strong {
    /* Use semibold rather than bold; reads cleaner alongside Geist's
       weight range. */
    font-weight: var(--font-weight-bold);
}

.freespace-content-text em {
    font-style: italic;
}

.freespace-content-text s {
    /* Strikethrough is fine at default; just make sure the line is
       visible against muted text. */
    text-decoration-color: currentColor;
}

.freespace-content-text code {
    font-family: var(--font-mono);
    font-size: 0.9em;
    background-color: var(--color-bg-subtle);
    padding: 1px 4px;
    border-radius: var(--radius-sm);
}

.freespace-content-text a {
    color: var(--color-accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.freespace-content-text a:hover {
    color: var(--color-accent-hover);
}

/* ---- Markdown list spacing (consolidated) -------------- 
   Markdown lists get fully-controlled spacing here to avoid the
   layered-margin-compounding problem (li margin + p margin inside
   li + ul vertical rhythm all stacking). Override everything and
   own the values explicitly. */

.freespace-content-text ul,
.freespace-content-text ol {
    padding-left: var(--space-5);
    margin: var(--space-2) 0 0 0;
}

.freespace-content-text li {
    margin: 0;
    line-height: 1.5;
}

.freespace-content-text li+li {
    margin-top: var(--space-1);
}

.freespace-content-text li p {
    margin: 0;
}

.freespace-content-text p:last-child {
    margin-bottom: 0;
}

/* When the free-space cell is rendering, the left column shrinks to
   col-md-4 — a narrow rail. Center its contents both horizontally
   AND vertically inside the rail so the identity block sits like a
   well-balanced "card" against the cell on its right. Forms inside
   the column (follow / unfollow) are block elements so text-align
   alone doesn't center them; flex centering picks them up. */
.profile-header-identity>.text-center {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 100%;
}

/* The follow/unfollow forms are block-level so they'd default to
   full column width. Constrain them to their natural button width
   so the centering above does its job. */
.profile-header-identity>.text-center>form {
    margin: 0;
}

/* ---- Toolbar above the textarea (pr16b wave 2) ---------- */

.freespace-toolbar {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-2);
    background-color: var(--color-bg-subtle);
    border: 1px solid var(--color-border);
    border-bottom: none;
    border-top-left-radius: var(--radius-sm);
    border-top-right-radius: var(--radius-sm);
}

.freespace-toolbar-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 28px;
    height: 28px;
    padding: 0 var(--space-2);

    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    font-size: var(--font-size-base);
    line-height: 1;
    cursor: pointer;

    transition: background-color var(--transition-fast),
        color var(--transition-fast);
}

.freespace-toolbar-btn:hover,
.freespace-toolbar-btn:focus-visible {
    background-color: color-mix(in srgb, var(--color-accent) 12%, transparent);
    color: var(--color-accent);
    outline: none;
}

/* The icon-based button (list) needs to vertically center the SVG;
   the text-based buttons (B/I/S) don't. Both end up looking right
   because the flex centering on the button itself handles either. */
.freespace-toolbar-btn svg {
    display: block;
}

/* The inline-code button shows "<>" which needs a slightly tighter
   font-size to match the visual weight of B/I/S. */
.freespace-toolbar-btn code {
    background: transparent;
    padding: 0;
    font-size: var(--font-size-sm);
    color: inherit;
}

/* Live character counter — sits in the form-text row beside the
   syntax hint. Subtle until you approach the limit, then warning,
   then danger. */
.freespace-char-count {
    flex-shrink: 0;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
    /* tabular-nums keeps digit widths fixed so the counter doesn't
       jitter as numbers change. Cheap polish. */
}

.freespace-char-count.is-warning {
    color: var(--color-warning);
}

.freespace-char-count.is-danger {
    color: var(--color-danger);
    font-weight: var(--font-weight-semibold);
}

/* Make the textarea sit flush below the toolbar — no top border,
   no top radii — so the toolbar and textarea read as one integrated
   input rather than two stacked controls. */
.freespace-textarea {
    border-top-left-radius: 0 !important;
    border-top-right-radius: 0 !important;
    /* The toolbar's bottom edge already provides the visual divider. */
    border-top: 1px solid var(--color-border);
    resize: vertical;
}

.freespace-textarea:focus {
    /* When the textarea is focused, ring the entire toolbar+textarea
       combo. The toolbar gets a sibling rule below for the top half. */
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 15%, transparent);
}

/* Wrap the toolbar's border color to match when the textarea is
   focused — keeps the visual integrity of "one input." */
.freespace-textarea:focus~* .freespace-toolbar,
.freespace-subform:focus-within .freespace-toolbar {
    border-color: var(--color-accent);
}

/* ============================================================
   Profile free-space — wave 3 (modes B and C)
   ============================================================ */

/* ---- Mode B (media) rendering --------------------------- */

.freespace-content-media {
    /* The cell takes the image's natural aspect ratio — no
       letterboxing or cropping. The browser computes the
       intrinsic ratio from the <img>'s width/height attributes,
       so cell height = (cell width × image-height / image-width),
       capped at max-height below. */
    width: 100%;
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.freespace-media-link {
    display: block;
    /* Width fills the cell; height collapses to the image's
       natural height (bounded by max-height on the img). */
    width: 100%;
    cursor: pointer;
    /* Anchor inherits the wrapper's overflow:hidden so the
       hover-zoom transform stays clipped to the cell. */
    overflow: hidden;
    border-radius: var(--radius-sm);
}

.freespace-media-image {
    /* Fill the width of the cell. Height auto-scales from the image's intrinsic ratio (via the width/height HTML
       attributes the template renders). max-height caps very tall portraits so they don't dominate the header on
       desktop — adjust to taste. */
    width: 100%;
    height: auto;
    max-height: 460px;
    /* When max-height clamps a tall image, object-fit:contain keeps the whole image visible inside the clamped box 
    rather than cropping. For images that aren't clamped, object-fit has no effect (the box matches the image). */
    object-fit: contain;
    display: block;
    transition: transform var(--transition-base);
}

/* ---- Mode B (video) rendering — pr25b ----
   Sized to match the image-mode container. Native controls bar 
   provided by the browser; we just constrain dimensions and match the rounded-corner styling. */
.freespace-media-video {
    display: block;
    width: 100%;
    height: auto;
    max-height: 460px;
    border-radius: var(--radius-sm);
    background-color: #000;
}

.freespace-media-link:hover .freespace-media-image,
.freespace-media-link:focus-visible .freespace-media-image {
    /* Subtle zoom-on-hover affordance — signals interactivity without being noisy. Matches the visual language used on
       post-card media hover. */
    transform: scale(1.02);
}

.freespace-media-link:focus-visible {
    /* Keyboard focus needs an explicit ring since the <img> itself isn't the focus target — the <a> is. */
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

/* ---- Mode C (single link) rendering --------------------- */

.freespace-content-link {
    /* Caption (if any) renders below the card. The wrapper just provides a vertical stack context. */
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.freespace-link-card {
    /* The whole card is one clickable anchor. Flex layout: image on the left (when present), body on the right.
       On narrow viewports the card stacks vertically — image on top, body below — see the mobile media query at the bottom of this block. */
    display: flex;
    align-items: stretch;
    gap: 0;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    background-color: var(--color-bg-surface);
    transition: border-color var(--transition-fast),
        background-color var(--transition-fast);
}

.freespace-link-card:hover,
.freespace-link-card:focus-visible {
    border-color: var(--color-accent);
    background-color: color-mix(in srgb, var(--color-accent) 4%, var(--color-bg-surface));
    outline: none;
}

.freespace-link-card-image {
    /* Fixed-width image rail on the left of the card. Image fills its container with object-fit: cover so it never distorts. */
    flex: 0 0 120px;
    background-color: var(--color-bg-subtle);
    overflow: hidden;
}

.freespace-link-card-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.freespace-link-card-body {
    flex: 1 1 auto;
    padding: var(--space-3);
    /* Vertical centering when the body is shorter than the image, which is the common case for OG cards with just title + URL. */
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--space-1);
    min-width: 0;
    /* min-width: 0 fixes flex-item-overflow-with-ellipsis — without it, long titles can push the card wider than its container. */
}

.freespace-link-card-title {
    font-weight: var(--font-weight-semibold);
    color: var(--color-text);
    line-height: 1.3;
    /* Clamp the title to two lines max — long page titles otherwise balloon the card height. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.freespace-link-card-description {
    /* One-line clamp on description; full text is on the linked page itself, so truncating in the card is fine. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.freespace-link-card-url {
    /* URL is the tertiary visual element — small, single-line, truncated with ellipsis. Tells the user where the link goes without dominating the card. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Stack the link card vertically on narrow viewports — the horizontal layout starts looking cramped below ~520px. 
The cell itself is full-width below the md breakpoint (768px), so the card has room until the viewport itself gets narrow. */
@media (max-width: 519.98px) {
    .freespace-link-card {
        flex-direction: column;
    }

    .freespace-link-card-image {
        flex: 0 0 auto;
        height: 140px;
        width: 100%;
    }
}

/* ---- Caption (shared by modes B and C) ----------------- */

.freespace-caption {
    /* Same visual treatment as the bio — small, muted, with mentions/links inheriting their accent color from the global anchor rules. */
    line-height: 1.4;
}

.freespace-caption a {
    color: var(--color-accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.freespace-caption a:hover {
    color: var(--color-accent-hover);
}

/* ============================================================
   Free-space Mode E — post reference (pr16b wave 4)
   ============================================================ */

/* ----- Picker UI (inside modal) ----- */

.freespace-post-picker[data-state="searching"] .freespace-post-picker-selected {
    display: none;
}

.freespace-post-picker[data-state="selected"] .freespace-post-picker-search {
    display: none;
}

.freespace-post-results {
    max-height: 320px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    /* No outer border — each row is its own card. */
    background: transparent;
    padding: 0.25rem;
}

.freespace-post-result-row {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    width: 100%;
    padding: 0.75rem;
    text-align: left;
    color: var(--bs-body-color);
    background: var(--bs-tertiary-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease;
}

/* Make sure text descendants honor the row's body color rather than
   inheriting the modal's darker palette. */
.freespace-post-result-row .freespace-post-result-display-name,
.freespace-post-result-row .freespace-post-result-preview {
    color: var(--bs-body-color);
}

.freespace-post-result-row .freespace-post-result-handle {
    /* Keep the handle muted relative to the display name. */
    color: var(--bs-secondary-color);
}

.freespace-post-result-row:hover,
.freespace-post-result-row:focus-visible {
    background: var(--bs-secondary-bg);
    border-color: var(--bs-emphasis-color);
    outline: none;
}

.freespace-post-result-row.is-selected-preview {
    cursor: default;
}

.freespace-post-result-row.is-selected-preview:hover {
    background: var(--bs-tertiary-bg);
    border-color: var(--bs-border-color);
}

.freespace-post-result-row.is-selected-preview {
    cursor: default;
}

.freespace-post-result-row.is-selected-preview:hover {
    background: transparent;
}

.freespace-post-result-author {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.35rem;
}

.freespace-post-result-avatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    object-fit: cover;
}

.freespace-post-result-author-text {
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
    min-width: 0;
}

.freespace-post-result-display-name {
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.freespace-post-result-body {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    min-width: 0;
}

.freespace-post-result-preview {
    flex: 1;
    min-width: 0;
    font-size: 0.9rem;
    line-height: 1.35;
    overflow-wrap: anywhere;
}

.freespace-post-result-thumbnail {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: var(--bs-border-radius-sm);
    object-fit: cover;
}

/* ----- Rendered post on the profile page ----- */

.freespace-content-post {
    display: flex;
    flex-direction: column;
}

.freespace-post-caption {
    /* spec §3 Mode E: caption above the post, like Twitter quote-tweet
       commentary. Same tone as the bio — small, muted. */
    line-height: 1.4;
}

.freespace-post-attribution {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--bs-border-color-translucent);
    font-size: 0.9rem;
}

.freespace-post-attribution .avatar-link {
    /* Prevent the avatar from being stretched by the flex layout. */
    flex-shrink: 0;
    line-height: 0;
}

.freespace-post-attribution-text {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
    min-width: 0;
}

.freespace-post-attribution-name {
    font-weight: 500;
    color: var(--bs-body-color);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.freespace-post-attribution-name:hover {
    text-decoration: underline;
}

.freespace-post-attribution-handle {
    text-decoration: none;
}

.freespace-post-body {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.freespace-post-text {
    font-size: 0.95rem;
    line-height: 1.4;
    overflow-wrap: anywhere;
}

.freespace-post-media {
    text-align: center;
    /* center images */
}

.freespace-post-media img {
    display: block;
    max-width: 100%;
    max-height: 240px;
    width: auto;
    height: auto;
    margin: 0 auto;
    border-radius: var(--bs-border-radius-sm);
    object-fit: contain;
}

.freespace-post-view-link {
    color: var(--bs-link-color);
    text-decoration: none;
    font-weight: 500;
}

.freespace-post-view-link:hover {
    text-decoration: underline;
}

.freespace-content-post-empty {
    /* Owner-only state when the featured post is no longer visible. */
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

/* ---- Modal additions for wave 3 ------------------------- */

/* Image input preview inside the modal: a thumbnail strip below
   the file input that shows either the existing stored image or
   the newly-selected file (JS swaps the src). */

.freespace-media-preview-wrap {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: var(--space-2);
    background-color: var(--color-bg-subtle);
}

.freespace-media-preview {
    display: block;
    max-width: 100%;
    max-height: 200px;
    margin: 0 auto;
    border-radius: var(--radius-sm);
    object-fit: contain;
    /* In the preview we DO want contain rather than cover —
       the user is reviewing the actual image, not the cropped
       version that'll render on their profile. */
}

.freespace-media-preview-label {
    text-align: center;
    color: var(--color-text-muted);
}

.freespace-media-edit-btn {
    position: absolute;
    top: var(--space-2);
    left: var(--space-2);
    z-index: 1;
}

/* Link fetch-preview button + error banner inside the modal. */

.freespace-link-fetch-btn {
    /* Matches the .btn-outline-secondary look but lives inside an
       input-group, so it shares borders with the URL input. No
       extra rules needed — Bootstrap input-group handles geometry. */
}

.freespace-link-fetch-error {
    color: var(--color-danger);
}

/* Hide the carried-through hidden fields (preview_img,
   og_image_width, og_image_height) from view. HiddenField renders
   <input type="hidden"> which is already invisible — but we apply
   classes for JS targeting. No visual rules needed. */

/* ============================================================
   Free-space Mode D — linktree (pr16b wave 4)
   ============================================================ */

/* ---- Editor (inside modal) ---- */

.freespace-linktree-row {
    /* hidden attribute on the row handles visibility; no rule needed */
}

.freespace-linktree-row .freespace-linktree-label {
    flex: 0 0 35%;
    min-width: 100px;
}

.freespace-linktree-row .freespace-linktree-url {
    flex: 1 1 auto;
    min-width: 0;
}

.freespace-linktree-row .freespace-linktree-remove-btn {
    flex: 0 0 auto;
    padding: 0.25rem 0.5rem;
}

.freespace-linktree-add-btn:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

/* ---- Rendered on profile ---- */

.freespace-content-linktree {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.freespace-linktree-link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-3);
    background-color: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    text-decoration: none;
    color: var(--color-text);
    font-weight: var(--font-weight-medium);
    transition: border-color var(--transition-fast),
        background-color var(--transition-fast),
        transform var(--transition-fast);
}

.freespace-linktree-link:hover,
.freespace-linktree-link:focus-visible {
    border-color: var(--color-accent);
    background-color: color-mix(in srgb, var(--color-accent) 4%, var(--color-bg-surface));
    color: var(--color-text);
    transform: translateY(-1px);
    outline: none;
}

.freespace-linktree-link-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.freespace-linktree-link-icon {
    flex: 0 0 auto;
    color: var(--color-text-muted);
    transition: color var(--transition-fast), transform var(--transition-fast);
}

.freespace-linktree-link:hover .freespace-linktree-link-icon,
.freespace-linktree-link:focus-visible .freespace-linktree-link-icon {
    color: var(--color-accent);
    transform: translateX(2px);
}

/* ============================================================
   Free-space media placeholders (pr25b)
   ============================================================
   Rendered in place of <video> when transcode_status is "pending"
   or "failed". Same outer dimensions as .freespace-media-video so
   the cell doesn't reflow when JS swaps the placeholder for a
   working <video> after transcode completion (SSE-driven).

   Structure:
     .freespace-media-placeholder            outer container, has poster
                                             as background-image
       .freespace-media-placeholder-overlay  dark layer + centered content
         .freespace-media-placeholder-spinner  (pending only)
         .freespace-media-placeholder-text     copy
         .freespace-media-placeholder-remove-btn (failed only)
   ============================================================ */

.freespace-media-placeholder {
    /* Match the working video's geometry so swapping in a real <video> doesn't shift the layout. */
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    max-height: 460px;
    border-radius: var(--radius-sm);

    /* Poster image background. Set inline via style="background-image: url(...)" in the template, so this rule just sets layout. Fallback color
       for when no poster exists. */
    background-color: #000;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;

    /* Position context for the overlay. */
    position: relative;
    overflow: hidden;
}

.freespace-media-placeholder-overlay {
    /* Cover the entire placeholder with a dark layer so the copy is readable against any poster image. Stronger
       than .post-detail-panel-backdrop's 0.4 — the poster image's brightness varies a lot and we need readable text on the
       worst case. */
    position: absolute;
    inset: 0;
    background-color: rgba(0, 0, 0, 0.6);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    padding: var(--space-4);
    text-align: center;
}

.freespace-media-placeholder-text {
    /* Light text against dark overlay. Same size as body copy so it reads as a clear status message rather than fine print. */
    color: var(--color-text-on-accent);
    font-size: var(--font-size-body);
    font-weight: var(--font-weight-medium);
    margin: 0;
    /* Soft text shadow improves legibility on edge cases (e.g. light poster image bleeding through low-opacity overlay corners). */
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

/* ---- Spinner (pending state) ----
   Pure CSS spinner — no SVG dependency, no JS, no font-awesome. A border-trick circle that rotates indefinitely. */

.freespace-media-placeholder-spinner {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 3px solid rgba(255, 255, 255, 0.25);
    border-top-color: rgba(255, 255, 255, 0.9);
    animation: freespace-spinner-rotate 800ms linear infinite;
}

@keyframes freespace-spinner-rotate {
    to { transform: rotate(360deg); }
}

/* Reduced-motion respect — kill the spin animation. The spinner still renders as a static circle (which is fine — the
   text below still communicates "in progress"). */
@media (prefers-reduced-motion: reduce) {
    .freespace-media-placeholder-spinner {
        animation: none;
    }
}

/* ---- Remove button (failed state) ----
   .btn-outline-light is Bootstrap's light-on-dark outline button. Sized small via .btn-sm in the template. We just need
   light spacing here. */

.freespace-media-placeholder-remove-btn {
    margin-top: var(--space-1);
}

/* ===================================================================
 * === Free-space linktree drag-to-reorder (pr25h)                 ===
 * === Drag handle on each linktree row; SortableJS-driven reorder ===
 * =================================================================== */

.freespace-linktree-drag-handle {
    color: var(--color-text-muted);
    cursor: grab;
    border: 0;
    background: transparent;
    align-self: stretch;
    display: inline-flex;
    align-items: center;
    padding: 0 0.25rem;
}

.freespace-linktree-drag-handle:hover {
    color: var(--color-text);
}

.freespace-linktree-drag-handle:active,
.freespace-linktree-row--drag .freespace-linktree-drag-handle {
    cursor: grabbing;
}

.freespace-linktree-row--ghost {
    opacity: 0.4;
}

.freespace-linktree-row--ghost > div {
    outline: 1px dashed var(--color-border, rgba(0, 0, 0, 0.15));
    outline-offset: 2px;
    border-radius: 0.375rem;
}

.freespace-linktree-row--drag {
    opacity: 0.95;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
    border-radius: 0.375rem;
}

@media (prefers-reduced-motion: reduce) {
    .freespace-linktree-row {
        transition: none !important;
    }
}

/* === Profile column header (pr37) ================================
   Visual refresh of the dashboard Profile column header. Last in the
   family established by pr31 (notifications), pr33 (feed cards),
   pr34 (messages rows), pr35 (links tiles), pr36 (media tiles).

   This is the "identity-forward" treatment: bigger avatar (64px),
   centred identity stack, pill-shape stat row. Pencil tucks into
   the avatar's bottom-right corner.

   Scope: every rule prefixed with
     `.dashboard-grid .dashboard-column-body`
   so the pr24 base rules (~line 1578) remain the default for any
   future non-dashboard surface that consumes the partial. The
   standalone profile page at /user/<username> is unaffected — its
   refresh lands in pr38.
   ================================================================ */

.dashboard-grid .dashboard-column-body .profile-column-header {
    padding: var(--space-5) var(--space-4) var(--space-4);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    text-align: center;
    border-bottom: 1px solid var(--color-border-subtle);
}

/* Avatar wrap — positioning context for the pencil overlay. */
.dashboard-grid .dashboard-column-body .profile-column-avatar-wrap {
    position: relative;
}

/* Pencil chip — 22px circle anchored to the avatar's bottom-right.
   1px page-color ring + 1px border-color ring matches the chip
   pattern from .notification-glyph and .messages-group-glyph. */
.dashboard-grid .dashboard-column-body .profile-column-edit-btn {
    position: absolute;
    bottom: -2px;
    right: -2px;
    width: 22px;
    height: 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    color: var(--color-text-muted);
    cursor: pointer;
    text-decoration: none;
    transition: color var(--transition-fast),
                border-color var(--transition-fast),
                background-color var(--transition-fast);
}

.dashboard-grid .dashboard-column-body .profile-column-edit-btn:hover {
    color: var(--color-accent);
    border-color: var(--color-accent);
    background-color: var(--color-bg-surface);
}

.dashboard-grid .dashboard-column-body .profile-column-edit-btn:focus-visible {
    outline: none;
    color: var(--color-accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-accent) 40%, transparent);
}

.dashboard-grid .dashboard-column-body .profile-column-name {
    font-size: 1.05rem;
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.01em;
    margin-top: var(--space-3);
    color: var(--color-text);
}

.dashboard-grid .dashboard-column-body .profile-column-handle {
    color: var(--color-text-muted);
    font-size: 0.85rem;
}

.dashboard-grid .dashboard-column-body .profile-column-bio {
    font-size: var(--font-size-body);
    line-height: 1.5;
    margin-top: var(--space-2);
    max-width: 280px;
}

/* Stat pill — bold numbers + muted labels, separated by a thin
   vertical divider. Single inline-flex row centred via the parent's
   align-items: center. */
.dashboard-grid .dashboard-column-body .profile-column-stats {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-3);
    padding: 6px 14px;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-pill);
    font-size: 0.78rem;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

.dashboard-grid .dashboard-column-body .profile-column-stats strong {
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
    margin-right: 2px;
}

.dashboard-grid .dashboard-column-body .profile-column-stats-sep {
    width: 1px;
    height: 12px;
    background: var(--color-border);
}

/* View-full link — accent-coloured, lives on its own line below the
   stat pill. Replaces the pr24 inline placement on the right of the
   meta row. */
.dashboard-grid .dashboard-column-body .profile-column-view-full {
    margin-top: var(--space-2);
    color: var(--color-accent);
    font-weight: var(--font-weight-medium);
    font-size: 0.82rem;
    transition: color var(--transition-fast);
}

.dashboard-grid .dashboard-column-body .profile-column-view-full:hover {
    color: var(--color-accent-hover);
}

/* Posts region — gentle top padding to separate from the header's
   bottom-border. The .pinned-post-wrapper and .profile-column-empty
   own their own bottom spacing. */
.dashboard-grid .dashboard-column-body .profile-column-posts {
    padding-top: var(--space-3);
}

/* Pinned-post label — UPPERCASE tracked, matches the family meta
   treatment (cf .links-column-tile-domain in pr35). */
.dashboard-grid .dashboard-column-body .pinned-post-wrapper {
    margin-bottom: var(--space-3);
}

.dashboard-grid .dashboard-column-body .pinned-post-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-bottom: var(--space-2);
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
}

/* Empty state — promoted from the inline Bootstrap utility chain
   into a real class structure, matching how pr34 did .messages-empty
   and pr35 did .links-column-empty. */
.dashboard-grid .dashboard-column-body .profile-column-empty {
    text-align: center;
    padding: var(--space-5) var(--space-4);
    color: var(--color-text-muted);
}

.dashboard-grid .dashboard-column-body .profile-column-empty-title {
    margin: 0 0 var(--space-1) 0;
    font-weight: var(--font-weight-semibold);
    font-size: 0.95rem;
    color: var(--color-text);
}

.dashboard-grid .dashboard-column-body .profile-column-empty-cta {
    margin: 0;
    font-size: var(--font-size-base);
}

/* Reduced-motion: kill the pencil's hover transitions. The static
   states still differ; we just don't animate between them. */
@media (prefers-reduced-motion: reduce) {
    .dashboard-grid .dashboard-column-body .profile-column-edit-btn,
    .dashboard-grid .dashboard-column-body .profile-column-view-full {
        transition: none;
    }
}

/* === Profile page (pr38) ========================================
   Visual refresh of the standalone /user/<username> page. Editorial-
   split layout: identity column + freespace column joined by a CSS
   grid, no outer card chrome, hairline divider above the posts feed.

   pr33's post-card-chrome rules (line ~5323) are extended in-place
   with a `.profile-feed .card` sibling selector so profile-page
   posts inherit the same transparent surface + violet hover halo
   as dashboard column posts.

   Freespace cell (.freespace-cell, .freespace-content-*, etc.) is
   byte-identical to pre-pr38. This section only adds the wrapping
   chrome around it.

   Companion rules elsewhere in this file:
     - .freespace-cell (~line 2774) — unchanged
     - pr16b wave 2 freespace internals (~line 2750–3777) — unchanged
     - pr33 card refresh (~line 5323) — selectors extended to .profile-feed
     - pr37 .pinned-post-label (~line ~) — referenced; rules need to
       become unscoped so both .dashboard-grid AND .profile-feed
       inherit the same pinned-label treatment. See "scope lift" note
       in the rules below.
   ================================================================ */

/* ---- Header region ---- */

.profile-header {
    display: grid;
    grid-template-columns: 1fr 1.8fr;
    gap: var(--space-5);
    align-items: start;
    padding-bottom: var(--space-5);
    margin-bottom: var(--space-4);
    border-bottom: 1px solid var(--color-border-subtle);
}

/* Single-column case — no freespace cell rendered. Identity fills
   the row. */
.profile-header:not(:has(.profile-freespace-col)) {
    grid-template-columns: 1fr;
    max-width: 480px;
}

/* Mobile: stack the two columns and tighten spacing. */
@media (max-width: 767.98px) {
    .profile-header {
        grid-template-columns: 1fr;
        gap: var(--space-4);
    }
}

/* ---- Identity column ---- */

.profile-identity {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    align-items: flex-start;
}

.profile-identity-avatar-wrap {
    position: relative;
    align-self: flex-start;
}

/* Pencil chip — same idiom as pr37's .profile-column-edit-btn but
   bigger (28px vs 22px) since the hero avatar is 96px not 64px and
   the chip needs to stay legible proportionally. */
.profile-identity-edit-pencil {
    position: absolute;
    bottom: -2px;
    right: -2px;
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    color: var(--color-text-muted);
    text-decoration: none;
    cursor: pointer;
    transition: color var(--transition-fast),
                border-color var(--transition-fast);
}

.profile-identity-edit-pencil:hover {
    color: var(--color-accent);
    border-color: var(--color-accent);
}

.profile-identity-edit-pencil:focus-visible {
    outline: none;
    color: var(--color-accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-accent) 40%, transparent);
}

.profile-identity-name {
    font-size: 1.6rem;
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.025em;
    line-height: 1.1;
    color: var(--color-text);
}

/* Geist Mono handle — ties back visually to the FriendZone wordmark
   in the nav. Small typographic move; the editorial-split signature. */
.profile-identity-handle {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-top: -4px;  /* tighten against the name above */
}

.profile-identity-bio {
    font-size: var(--font-size-body);
    line-height: 1.5;
    color: var(--color-text);
}

.profile-identity-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px;
    font-size: 0.78rem;
    color: var(--color-text-muted);
}

.profile-identity-meta strong {
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
    font-variant-numeric: tabular-nums;
}

.profile-identity-meta-sep {
    opacity: 0.5;
}

.profile-identity-actions {
    margin-top: var(--space-1);
}

.profile-identity-actions form {
    margin: 0;
}

/* ---- Action button family ----
   Shared component for follow/unfollow/edit-profile, draft-banner
   buttons, and the muted-notice unmute button. Three modifiers:
   --primary, --outline, --danger; one size modifier --sm. */

.profile-action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 8px 18px;
    border-radius: var(--radius-md);
    font-family: inherit;
    font-size: 0.85rem;
    font-weight: var(--font-weight-medium);
    text-decoration: none;
    cursor: pointer;
    border: 1px solid transparent;
    transition: color var(--transition-fast),
                background-color var(--transition-fast),
                border-color var(--transition-fast);
}

.profile-action-btn--primary {
    background: var(--color-accent);
    color: var(--color-text-on-accent);
    border-color: var(--color-accent);
}

.profile-action-btn--primary:hover {
    background: var(--color-accent-hover);
    border-color: var(--color-accent-hover);
    color: var(--color-text-on-accent);
}

.profile-action-btn--outline {
    background: transparent;
    color: var(--color-text);
    border-color: var(--color-border);
}

.profile-action-btn--outline:hover {
    color: var(--color-accent);
    border-color: var(--color-accent);
}

.profile-action-btn--danger {
    background: transparent;
    color: var(--color-danger);
    border-color: var(--color-border);
}

.profile-action-btn--danger:hover {
    color: var(--color-text-on-accent);
    background: var(--color-danger);
    border-color: var(--color-danger);
}

.profile-action-btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.profile-action-btn--sm {
    padding: 6px 12px;
    font-size: 0.8rem;
}

.profile-action-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-accent) 40%, transparent);
}

/* ---- Freespace column wrapper ----
   The column that contains .freespace-cell. The cell itself is
   unchanged; this is just the grid item wrapper. */

.profile-freespace-col {
    min-width: 0;  /* lets the cell content shrink without overflow */
}

/* ---- Draft banner ----
   Replaces the inline style="background-color: #cfe2ff; …" pattern
   from pre-pr38. Subtle accent-tinted surface; buttons use the
   .profile-action-btn family. */

.freespace-draft-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-4);
    background: color-mix(in srgb, var(--color-accent) 6%, var(--color-bg-surface));
    border: 1px solid color-mix(in srgb, var(--color-accent) 25%, transparent);
    border-radius: var(--radius-md);
}

.freespace-draft-banner-text {
    font-size: 0.85rem;
    color: var(--color-text);
    flex: 1 1 auto;
    min-width: 0;
}

.freespace-draft-banner-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.freespace-draft-banner-actions form {
    margin: 0;
    display: inline;
}

/* ---- Muted notice ----
   Replaces Bootstrap .alert .alert-info. Same content shape: a text
   line + an unmute button. Subtle accent-tinted surface matching
   the draft banner family. */

.profile-muted-notice {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-4);
    background: var(--color-bg-subtle);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-md);
}

.profile-muted-notice-text {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    flex: 1 1 auto;
    min-width: 0;
}

.profile-muted-notice form {
    margin: 0;
}

/* ---- Posts feed wrapper ----
   The .profile-feed div wraps both the pinned-post block and the
   chronological posts loop. pr33's card refresh selectors are
   extended (in place, in their original section) to include
   .profile-feed .card so cards on this page inherit the family
   chrome. No rules needed here beyond a small bottom-spacing tweak. */

.profile-feed {
    /* Reserved for any feed-level rules (none yet); class exists
       primarily as a scope hook for pr33's extended selectors. */
}

/* ---- Pinned post label scope lift ----
   pr37 declared .pinned-post-label scoped to
   `.dashboard-grid .dashboard-column-body .pinned-post-label`.
   pr38 wants the same label on the profile page. Three options:
     (a) duplicate the rules under .profile-feed
     (b) lift the original rules to unscoped (they're specific
         enough to .pinned-post-label that there's no collision risk)
     (c) add .profile-feed as a comma-sibling to the existing scope
   Doing (b) during implementation — cheapest and matches how
   .freespace-cell is unscoped today. Strike the pr37 scope, move
   the rules into the global scope, add a one-line note at the pr37
   banner pointing at this. */

/* ---- Posts empty state ----
   Promoted from the inline `text-center text-muted py-4` chain
   into the family structure mirroring .profile-column-empty,
   .messages-empty, .links-column-empty, etc. */

.profile-empty {
    text-align: center;
    padding: var(--space-6) var(--space-4);
    color: var(--color-text-muted);
}

.profile-empty-title {
    margin: 0 0 var(--space-1) 0;
    font-weight: var(--font-weight-semibold);
    font-size: 0.95rem;
    color: var(--color-text);
}

.profile-empty-cta {
    margin: 0;
    font-size: var(--font-size-base);
}

/* ---- Reduced motion ---- */

@media (prefers-reduced-motion: reduce) {
    .profile-identity-edit-pencil,
    .profile-action-btn {
        transition: none;
    }
}



/* ============================================================
   Folded in from style.css during css-refactor commit 8.
   se15 — Audience curation (close friends + content blocks)
   ============================================================ */

/* ===================================================================
   se15 — Audience curation (close friends + content blocks)
   ===================================================================
   Two surfaces:
     1. Profile action cluster — the add/remove close-friend and
        content-block link-buttons under the follow/report row.
     2. Settings editor (/settings/audience) — removable member rows
        and the add-picker forms.
   Both lean on existing token vocabulary and the .inline-editor /
   .editor-row / .summary-pill family from the visibility page. */

/* Profile cluster — stack the small audience link-buttons with a
   little breathing room above, matching .profile-identity-report's
   muted-link treatment. The buttons themselves inherit Bootstrap's
   .btn-link .text-muted; this just owns the wrapper rhythm. */
.profile-identity-audience {
    gap: var(--space-1);
}

.profile-identity-audience .btn-link {
    font-size: var(--font-size-sm);
    text-decoration: none;
}

.profile-identity-audience .btn-link:hover {
    color: var(--color-accent);
}

/* Settings editor — current-member rows. A quiet list with hairline
   dividers between rows, same idiom as inbox/following lists. */
.audience-list-row {
    border-top: 1px solid var(--color-border);
}

.audience-list-row:first-child {
    border-top: none;
}

.audience-list-name {
    font-weight: var(--font-weight-medium);
}

/* The add-picker form sits above each list; give it a touch of room
   below so the picker dropdown doesn't crowd the member rows. */
.audience-add-form {
    position: relative;
}

