/* ---------------------------------------------------------------------------
   phase6 — the profile page. Owned by its lane.

   Instagram's own measurements, in Peltify's paper: a 935px column, a 150px
   avatar on desktop and 77px on a phone, a 20px regular handle, a stat row of
   bold numbers, then an icon tab row whose ACTIVE tab is underlined ON TOP
   (Instagram puts the 1px rule above the icon, not below it), then a 3-column
   grid of square tiles with a 4px gutter.

   Nothing here redefines an `.ig-*` primitive — the buttons, the dialog, the
   person row and the empty state all come from ig.css, so a Follow button on
   this page is the same object as a Follow button on the feed.
   --------------------------------------------------------------------------- */

.pfp {
  max-width: 935px;
  margin: 0 auto;
  padding: 30px 20px 0;
}

/* ------------------------------------------------------------- the header -- */

.pfp-head {
  display: grid;
  grid-template-columns: 290px minmax(0, 1fr);
  align-items: start;
  padding-bottom: 44px;
}
.pfp-av { display: grid; place-items: center; }
.pfp-av .av { border-radius: 50%; overflow: hidden; }

.pfp-meta { display: grid; gap: 20px; min-width: 0; }

.pfp-row1 { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; }
.pfp-handle {
  margin: 0;
  font-size: 20px; font-weight: 400; line-height: 1.2;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.pfp-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pfp-gear { width: 32px; height: 32px; }
.pfp-gear svg { width: 22px; height: 22px; }

/* "Follows you" — a fact the server sent, drawn as a quiet grey pill. */
.pfp-pill {
  display: inline-flex; align-items: center;
  padding: 3px 8px; border-radius: 6px;
  background: var(--paper-deep); color: var(--ink-soft);
  font-size: 12px; font-weight: 700; line-height: 1.4;
}

/* The Following ▾ menu. Anchored to its button so it cannot drift. */
.pfp-menuwrap { position: relative; }
.pfp-menuwrap .ig-btn svg { margin-left: -2px; }
.pfp-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 20;
  min-width: 160px; padding: 4px;
  border-radius: 10px; border: 1px solid var(--line);
  background: var(--white); box-shadow: var(--shadow-md);
}
.pfp-menu-item {
  display: block; width: 100%; padding: 10px 12px;
  border: 0; border-radius: 7px; background: none;
  font: inherit; font-size: 14px; font-weight: 700; color: #c22;
  text-align: left; cursor: pointer;
}
.pfp-menu-item:hover { background: var(--paper); }

/* The three numbers. Bold number, plain word — Instagram's exact reading. */
.pfp-stats { display: flex; align-items: center; gap: 40px; flex-wrap: wrap; }
.pfp-stat {
  display: inline-flex; align-items: baseline; gap: 5px;
  border: 0; background: none; padding: 0;
  font: inherit; font-size: 16px; color: var(--ink);
  text-align: left;
}
button.pfp-stat { cursor: pointer; }
button.pfp-stat:hover span { color: var(--ink-soft); }
.pfp-stat b { font-weight: 700; }
.pfp-stat span { color: var(--ink); }

.pfp-about { display: grid; gap: 2px; font-size: 14px; line-height: 1.5; }
.pfp-name { margin: 0; font-weight: 700; color: var(--ink); }
/* THE LINE BREAKS ARE THE PERSON'S OWN — nothing parses the bio, so the only
   way to keep the shape they typed is to preserve the whitespace. */
.pfp-bio { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ink); }
.pfp-tag { margin: 2px 0 0; font-size: 12.5px; color: var(--ink-faint); }
.pfp-tag-id { opacity: .75; }

.pfp-notice {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 6px;
  margin: 12px 0 0; padding: 8px 12px;
  border-radius: 8px; background: var(--warn-wash);
  color: var(--warn); font-size: 13px; line-height: 1.45;
}

/* --------------------------------------------------------------- the tabs -- */

.pfp-tabs {
  display: flex; justify-content: center; align-items: stretch; gap: 60px;
  border-top: 1px solid var(--line);
}
.pfp-tab {
  display: inline-grid; place-items: center;
  min-width: 44px; min-height: 52px; padding: 0 8px;
  margin-top: -1px;                    /* so the active rule lands ON the border */
  border: 0; border-top: 1px solid transparent;
  background: none; color: var(--ink-faint); cursor: pointer;
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.pfp-tab:hover { color: var(--ink-soft); }
.pfp-tab.is-on { color: var(--ink); border-top-color: var(--ink); }
.pfp-tab svg { width: 22px; height: 22px; }

.pfp-body { padding: 24px 0 64px; }
.pfp-loading { margin: 40px 0; text-align: center; color: var(--ink-faint); font-size: 14px; }

/* -------------------------------------------------------- the posts grid -- */

.pfp-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 4px;
}
.pfp-tile {
  position: relative;
  aspect-ratio: 1 / 1;
  display: block; width: 100%; padding: 0;
  border: 0; border-radius: 0; overflow: hidden;
  background: var(--paper); color: var(--ink);
  cursor: pointer;
}
.pfp-tile-art { width: 100%; height: 100%; object-fit: cover; display: block; }
/* A text-only post is a paper tile with its own words in the middle — never a
   blank square, and never a picture it does not have. */
.pfp-tile-paper {
  display: grid; place-items: center; width: 100%; height: 100%;
  padding: 14px; background: var(--paper);
  border: 1px solid var(--line-soft);
}
.pfp-tile-words {
  font-family: var(--font-serif); font-size: 15px; line-height: 1.35;
  text-align: center; color: var(--ink);
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden;
}
.pfp-tile-badge {
  position: absolute; top: 6px; right: 6px;
  display: inline-flex; align-items: center; gap: 3px;
  padding: 3px 6px; border-radius: 6px;
  background: rgba(20, 12, 28, 0.62); color: #fff;
  font-size: 11px; font-weight: 700; line-height: 1;
}
.pfp-tile-badge svg { width: 12px; height: 12px; }
/* A friends-only tile carries a small lock in the top-left corner, on the same
   soft dark disc the star badge sits on, so the two never collide. */
.pfp-tile-lock {
  position: absolute; top: 6px; left: 6px;
  display: inline-grid; place-items: center;
  width: 24px; height: 24px; border-radius: 50%;
  background: rgba(20, 12, 28, 0.62); color: #fff;
  line-height: 0;
}
.pfp-tile-lock svg { width: 14px; height: 14px; }
.pfp-tile-hover {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center; gap: 26px;
  background: rgba(20, 12, 28, 0.45); color: #fff;
  opacity: 0; transition: opacity var(--fast) var(--ease);
}
.pfp-tile-hover span { display: inline-flex; align-items: center; gap: 6px; font-size: 15px; font-weight: 700; }
.pfp-tile-hover svg { fill: currentColor; }
.pfp-tile:hover .pfp-tile-hover, .pfp-tile:focus-visible .pfp-tile-hover { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .pfp-tile-hover { transition: none; }
}
/* A touch device has no hover, so the overlay would only ever flash on tap. */
@media (hover: none) {
  .pfp-tile-hover { display: none; }
}

.pfp-more { display: grid; place-items: center; padding: 24px 0 0; }
.pfp-first-ic {
  display: inline-grid; place-items: center;
  width: 62px; height: 62px; margin: 0 auto 12px;
  border: 2px solid var(--ink); border-radius: 50%;
  color: var(--ink);
}

/* ------------------------------------------------------ palette · board --- */

/* The palette is the public page's own sections, drawn by friends.js. Only the
   spacing is this file's business — the sections keep their own look, which is
   what makes the preview and the real page the same thing. */
.pfp-palette .pp-sections { display: grid; gap: 28px; }

.pfp-private {
  display: flex; align-items: center; gap: 6px;
  margin: 0 0 14px;
  color: var(--ink-faint); font-size: 12.5px;
}
.pfp-board-body { min-width: 0; }
/* The board arrives with the Dashboard's own pane padding; on the profile page
   the page already owns the margin. */
.pfp-board-body .db-board > .tb { padding: 0; }

/* ---------------------------------------------------------- edit profile -- */

.pfp-edit { --ig-dialog-w: 680px; }
.pfp-editor { padding: 8px 16px 20px; }

/* --------------------------------------------------- followers/following -- */

.pl { --ig-dialog-w: 400px; display: grid; grid-template-rows: auto auto auto 1fr; }
.pl-count { font-weight: 400; color: var(--ink-faint); }
.pl-searchwrap { padding: 10px 14px; border-bottom: 1px solid var(--line-soft); }
.pl-search {
  width: 100%; min-height: 36px; padding: 0 12px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--paper); color: var(--ink);
  font: inherit; font-size: 14px;
}
.pl-search:focus { outline: 2px solid var(--violet); outline-offset: 1px; }
.pl-rows { padding: 6px 14px 14px; overflow-y: auto; max-height: 58vh; }
.pl-rows .ig-person { gap: 12px; }
.pl-av { border: 0; background: none; padding: 0; cursor: pointer; line-height: 0; }
.pl-acts { display: flex; align-items: center; gap: 6px; flex: none; }
.pl-more { display: block; width: 100%; margin: 10px 0 0; padding: 10px;
           border: 0; background: none; color: var(--spine);
           font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; }
.pl-loading { margin: 12px 0 0; text-align: center; color: var(--ink-faint); font-size: 13px; }
.pl-msg {
  display: flex; align-items: center; gap: 6px;
  margin: 0; padding: 10px 14px;
  background: var(--warn-wash); color: var(--warn);
  font-size: 13px; line-height: 1.45;
}

/* ------------------------------------------------------------ the phone --- */

/* Instagram's phone profile, to the letter: a small avatar beside the three-up
   stat strip, the name and bio underneath at full width, then the buttons at
   full width, then the tab row spread edge to edge.

   `display: contents` on the identity row is what makes that possible without a
   second DOM: the handle, the "Follows you" pill and the button row become
   direct children of the meta column, so each one can take its own order. One
   markup, two formats — which is the rule for the whole phase6 shell. */
@media (max-width: 735px) {
  .pfp { padding: 14px 0 0; }
  .pfp-head {
    grid-template-columns: 108px minmax(0, 1fr);
    gap: 0 4px;
    padding: 0 16px 14px;
  }
  .pfp-meta { display: flex; flex-direction: column; gap: 12px; }
  .pfp-row1 { display: contents; }
  .pfp-handle { order: 1; font-size: 18px; }
  .pfp-pill { order: 1; align-self: start; }
  .pfp-stats { order: 2; }
  .pfp-about { order: 3; }
  .pfp-acts { order: 4; }

  .pfp-stats {
    justify-content: space-around;
    gap: 0;
    padding: 4px 0 0;
  }
  .pfp-stat {
    flex: 1 1 0;
    flex-direction: column; align-items: center; gap: 1px;
    min-height: 44px; justify-content: center;
    font-size: 12.5px;
  }
  .pfp-stat b { font-size: 15px; }
  .pfp-stat span { color: var(--ink-soft); }

  /* Full-width buttons, 44px tall — a touch target, not a desktop pill. */
  .pfp-acts { width: 100%; gap: 6px; }
  .pfp-acts .ig-btn { flex: 1 1 0; min-height: 44px; }
  .pfp-acts .pfp-menuwrap { flex: 1 1 0; display: flex; }
  .pfp-acts .pfp-menuwrap .ig-btn { width: 100%; }
  .pfp-gear { flex: none; width: 44px; height: 44px; }

  .pfp-tabs { gap: 0; }
  .pfp-tab { flex: 1 1 0; min-height: 48px; }
  .pfp-body { padding: 12px 0 72px; }
  .pfp-grid { gap: 2px; }
  .pfp-palette, .pfp-board { padding: 0 16px; }
  .pfp-editor { padding: 8px 12px 20px; }
  .pl-rows { max-height: none; }
  /* The dialog is a sheet on a phone (ig.css); its rows need the room. */
  .pl { grid-template-rows: auto auto auto minmax(0, 1fr); }
}

/* Nothing on this page may make the page scroll sideways. */
.pfp, .pfp-head, .pfp-meta, .pfp-body { min-width: 0; max-width: 100%; }
.pfp-gridwrap { min-width: 0; }

/* ------------------------------------------------------------ Your things ----
   13.6 (2026-09-08, library answers 4-6). The library as the profile's third
   tab: a lock that states which it is, the one add box, groups by kind with a
   platform badge per item, and Scan at the foot with the graph beside it.

   No pills and no boxes where nothing is tappable (rule 6): the group headings
   and the badges are typography.
   --------------------------------------------------------------------------- */
.pfp-things { display: flex; flex-direction: column; gap: 4px; padding: 4px 0 8px; }
.th-head { display: flex; align-items: baseline; gap: 10px; }
.th-head-h { margin: 0; font-family: var(--serif, Georgia, "Times New Roman", serif);
  font-size: 19px; letter-spacing: -.01em; }
.th-lock { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px;
  font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-faint); }
.th-lock.is-open { color: var(--violet, #6d28d9); }
.th-count { margin-left: auto; font-size: 12.5px; color: var(--ink-faint); }

/* 18.6: the add door is a button in the tools row, not a full-width box of
   its own — it was 60px of height before the first shelf. */
.th-group { display: flex; align-items: baseline; gap: 7px; margin: 6px 0 2px;
  font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink-faint); }
.th-group-n { font-size: 11px; font-weight: 600; color: var(--line-strong); letter-spacing: .02em; }
/* When a kind is longer than one shelf draws, the heading says so — there is
   no popup holding the rest (entry 15: "No popups at all"). */
.th-group-more { margin-left: auto; font-size: 11px; font-weight: 600;
  letter-spacing: 0; text-transform: none; color: var(--ink-faint); }
/* THE COLUMN CAP (13.16, 2026-09-08) — Nick: "SCARILY horizontal. Too many
   items." A shelf never runs wider than `--shelf-cols` tiles (shelf.js writes
   the number), so a short section sits in the first slots of the same grid
   the long ones use rather than in tiles half their size. */
.th-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
.th-grid.shelf, .lp-grid.shelf {
  grid-template-columns: repeat(auto-fill,
    minmax(max(150px, calc((100% - (var(--shelf-cols, 6) - 1) * 10px) / var(--shelf-cols, 6))), 1fr));
}
.th-songs { align-self: start; margin-top: 2px; padding: 0; border: 0; background: none;
  font: inherit; font-size: 11.5px; font-weight: 700; color: var(--spine); cursor: pointer; }
.th-songlist { margin: 4px 0 0; padding: 0 0 0 2px; list-style: none; }
.th-song { font-size: 12px; color: var(--ink-soft); line-height: 1.45;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 18.6: Songs is the one section that still grows downwards — it is a list of
   albums, not a shelf of covers. It gets three rows and scrolls, so a big
   music library cannot be the reason the tab is "sooo large". */
.th-songgroups { display: flex; flex-direction: column; gap: 2px;
  max-height: 108px; overflow-y: auto; overscroll-behavior: contain; }
.th-songgroup-b { display: flex; align-items: baseline; gap: 8px; width: 100%;
  padding: 7px 2px; border: 0; border-bottom: 1px solid var(--line); background: none;
  font: inherit; text-align: left; color: var(--ink); cursor: pointer; }
.th-songgroup-n { font-size: 11.5px; font-weight: 600; color: var(--ink-faint); }
.th-songgroup-w { font-size: 11px; color: var(--ink-faint); font-style: italic; }
.th-item { position: relative; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.th-art { width: 100%; aspect-ratio: 1; padding: 0; border: 1px solid var(--line);
  border-radius: 10px; overflow: hidden; background: var(--paper-2, #f4f1ec);
  display: grid; place-items: center; color: var(--ink-faint); cursor: pointer; }
.th-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.th-name { font-size: 12.5px; font-weight: 600; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 18.6 — ONE line under the name: which platform said this, or that you added
   it. The creator was the other line and is on the cover's label now. */
.th-meta { display: flex; align-items: center; min-width: 0;
  font-size: 10.5px; color: var(--ink-faint); }
.th-meta.is-mine .th-badge { color: var(--violet, #6d28d9); }
.th-badge { display: inline-flex; align-items: center; gap: 3px; min-width: 0;
  font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.th-rate { align-self: start; padding: 0; border: 0; background: none; font: inherit;
  font-size: 11.5px; color: var(--ink-faint); cursor: pointer;
  opacity: 1; animation: th-fade 6s ease forwards; }
@keyframes th-fade { 0%, 55% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
@media (prefers-reduced-motion: reduce) { .th-rate { animation: none; } }

.th-empty { display: flex; flex-direction: column; align-items: flex-start; gap: 12px;
  margin: 18px 0; padding: 22px; border: 1px solid var(--line); border-radius: 16px;
  background: var(--white); }
.th-empty-h { margin: 0; font-family: var(--serif, Georgia, serif); font-size: 20px; }
.th-empty-p { margin: 0; max-width: 46ch; font-size: 13.5px; color: var(--ink-soft); }
.th-empty-add, .th-connect { display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 15px; border: 1px solid var(--ink); border-radius: 11px; background: var(--ink);
  color: var(--paper); font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; }
.th-connect { border-color: var(--line); background: var(--white); color: var(--ink); }
.th-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.th-chip { padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--paper); font: inherit; font-size: 12.5px; cursor: pointer; }
.th-chip:hover { border-color: var(--ink); }

.th-warn, .th-wait, .th-more { margin: 14px 0 0; font-size: 13px; color: var(--ink-soft);
  display: flex; align-items: center; gap: 6px; }
/* 14.7 (library entry 7) — SCAN AND THE GRAPH AT THE TOP of the tab. */
.th-tools { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 8px; }
.th-scan, .th-graph, .th-add { display: inline-flex; align-items: center; gap: 8px; padding: 8px 13px;
  border: 1px solid var(--line); border-radius: 11px; background: var(--white);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.th-scan:hover, .th-graph:hover, .th-add:hover { border-color: var(--ink); }

/* 14.7 (entries 10-11) — the tab's two views: what you have, and what you are. */
.th-seg { margin: 0 0 4px; }
.th-taste { margin: 6px 0 0; }
.th-taste .db-board { padding: 0; }

@media (max-width: 560px) {
  /* 14.7 (entry 8, "At the very least I thought these would be more
     optimized!"): two enormous squares became three real ones, and the type
     under them comes down with the tile. */
  /* the 6-column shelf cap has a 150px floor, which on a 390px phone IS the
     two-tile sheet Nick called out — the floor comes down with the screen */
  .th-grid, .th-grid.shelf, .lp-grid.shelf {
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
  .th-art { border-radius: 8px; }
  .th-name { font-size: 12px; }
  .th-by, .th-badge { font-size: 10.5px; }
  .th-tools .th-scan, .th-tools .th-graph, .th-tools .th-add { flex: 1 1 auto; justify-content: center; }
}
@media (max-width: 400px) {
  .th-grid, .th-grid.shelf, .lp-grid.shelf {
    grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 7px; }
}

/* 13.11 — edit mode is the body, not a layer over it (public-profile SPEC
   2026-09-08, answer 26: "no form, no preview"). The dialog it replaces was
   still a form somewhere else; this is the profile, editable in place. */
.pfp-editing { padding: 4px 0 24px; }
.pfp-editing .ep { padding: 0; }

/* ===========================================================================
   17.5 — A'S SHELVES WITH C'S SORTING (library/SPEC.md 7, 8, 14).

   Built to the sketches at `~/peltify-shots/ig-desktop-16.3-A-shelves.png` and
   `-C-table.png`, because three builds failed here before there were pictures.
   One shelf per kind, every kind on screen at once, each shelf scrolling
   sideways; the filter box, the kind dropdown and the sort above them. The
   page is now the same height with 17 things or 1,700.
   =========================================================================== */
.th-filters { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 12px 0 4px; }
.th-filter-q, .th-filter-k, .th-filter-s {
  font: inherit; font-size: 13px; padding: 8px 12px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--white); color: var(--ink);
}
.th-filter-q { flex: 1 1 200px; min-width: 0; max-width: 260px; }
.th-filter-q:focus, .th-filter-k:focus, .th-filter-s:focus { outline: none; border-color: var(--line-strong); }
.th-filter-n { font-size: 12.5px; color: var(--ink-faint); margin-left: auto; }

/* THE SHELF. `overflow-x: auto` and nothing else — the tile keeps every rule it
   already had, it simply stops wrapping. */
.th-strip {
  display: flex; gap: 8px; overflow-x: auto; overflow-y: hidden;
  padding: 2px 2px 4px; scroll-snap-type: x proximity; scrollbar-width: thin;
}
/* 17.5 drew the sketch's 132px tile. 18.6 takes it to 96 — "just sooo large.
   its too vertical": the tile is SQUARE, so its width is what sets every
   shelf's height, and 132 → 72 takes 45% off every one of them. */
.th-strip > .th-item { flex: 0 0 72px; scroll-snap-align: start; }
.th-strip::-webkit-scrollbar { height: 8px; }
.th-strip::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
/* 18.6 — THE "OPEN ALL" PANEL AND ITS TILE ARE GONE (entry 15: "No popups at
   all."). The shelf holds the kind and scrolls sideways; the filter and the
   kind dropdown above are how a long one is narrowed. */
/* 19.8 (`library/SPEC.md` entry 16) — "I need an obvious and easy way that I
   can open ALL of it at once." The same shelf, WRAPPED rather than scrolled:
   nothing opens, nothing has to be dismissed, and the page simply gets as
   long as the library is — which is the one moment being long is the point. */
.th-strip.is-all {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
  overflow: visible; scroll-snap-type: none;
}
.th-strip.is-all > .th-item { flex: none; width: auto; }
.th-every {
  flex: none; height: 34px; padding: 0 12px;
  border: 1px solid var(--line-strong); border-radius: var(--r-pill);
  background: var(--white); color: var(--ink); font: inherit; font-size: 13px; font-weight: 700;
  cursor: pointer;
}
.th-every:hover { border-color: var(--ink); }
.th-every[aria-pressed="true"] { background: var(--violet-wash); border-color: var(--violet); color: var(--spine); }

@media (max-width: 640px) {
  .th-strip > .th-item { flex: 0 0 68px; }
  .th-filter-q { max-width: none; }
  .th-filter-n { margin-left: 0; }
}


/* ===========================================================================
   19.8 (`public-profile/SPEC.md` entry 5) — EDIT PROFILE IS A POPUP.

   Nick: "Edit profile should be a neat easy popup too. Otherwise its too
   vertical." It was the body of the page: the tabs went quiet, the page grew
   as long as the form, and the thing being edited scrolled off the top. It is
   the app's own scrim + dialog now, capped, scrolling inside itself.
   =========================================================================== */
.pfp-edit-card {
  width: min(560px, calc(100vw - 32px));
  max-height: min(86dvh, 760px);
  display: flex; flex-direction: column;
  padding: 0; overflow: hidden;
}
.pfp-edit-head {
  flex: none; display: flex; align-items: center; gap: 10px;
  padding: 14px 16px; border-bottom: 1px solid var(--line-soft);
}
.pfp-edit-t { flex: 1 1 auto; font-size: 16px; font-weight: 700; }
.pfp-edit-x { flex: none; }
.pfp-edit-card .pfp-editing {
  min-height: 0; flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain;
  padding: 14px 16px 18px;
}
@media (max-width: 640px) {
  .pfp-edit-card { width: calc(100vw - 16px); max-height: calc(100dvh - 96px); }
  .pfp-edit-card .pfp-editing { padding: 12px 12px 16px; }
}

/* 21.6 (public-profile/SPEC.md 6) — skeleton first, then ONE fade. Shapes
   only: a skeleton states nothing about anybody. */
.pfp.is-loading .pfp-tabs, .pfp.is-loading .pfp-body { visibility: hidden; }
.pfp.is-in { animation: pfp-in 240ms var(--soft, cubic-bezier(.22,.61,.36,1)) both; }
@keyframes pfp-in { from { opacity: 0; } to { opacity: 1; } }
.pfp-skel .skel { display: block; background: color-mix(in srgb, var(--ink) 7%, var(--paper)); border-radius: 8px;
                  animation: skel-pulse 1.2s ease-in-out infinite; }
.pfp-skel .skel-av { width: 150px; height: 150px; border-radius: 50%; }
.pfp-skel .pfp-meta { display: grid; gap: 12px; align-content: center; }
.pfp-skel .skel-line { height: 14px; }
.pfp-skel .is-w40 { width: 40%; height: 20px; } .pfp-skel .is-w70 { width: 70%; } .pfp-skel .is-w55 { width: 55%; }
@keyframes skel-pulse { 50% { opacity: .55; } }
@media (max-width: 900px) { .pfp-skel .skel-av { width: 77px; height: 77px; } }
@media (prefers-reduced-motion: reduce) { .pfp.is-in, .pfp-skel .skel { animation: none; } }

/* 21.7 — your archive, on your own page only. */
.pfp-archived { display: grid; gap: 18px; max-width: 640px; margin: 0 auto; padding-top: 8px; }
.pfp-archived-note { margin: 0; font-size: 13px; color: var(--ink-faint); text-align: center; }
