/* ───────────────────────────── fonts ───────────────────────────── */
/* The site is set in Nunito, with DM Mono for addresses and anything pasted.
   Archivo and Martian Mono are declared because the canvas renderer draws the
   image with them. All are SIL Open Font License; see /fonts/OFL-*.txt. */
@font-face { font-family: "Nunito"; font-weight: 200 1000; font-display: swap; src: url("/fonts/Nunito-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Nunito"; font-weight: 200 1000; font-display: swap; src: url("/fonts/Nunito-latin-ext.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "DM Mono"; font-weight: 400; font-display: swap; src: url("/fonts/DMMono-Regular-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "DM Mono"; font-weight: 400; font-display: swap; src: url("/fonts/DMMono-Regular-latin-ext.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "DM Mono"; font-weight: 500; font-display: swap; src: url("/fonts/DMMono-Medium-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "DM Mono"; font-weight: 500; font-display: swap; src: url("/fonts/DMMono-Medium-latin-ext.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "Archivo"; font-weight: 400; font-display: swap; src: url("/fonts/Archivo-Regular.woff2") format("woff2"); }
@font-face { font-family: "Archivo"; font-weight: 500; font-display: swap; src: url("/fonts/Archivo-Medium.woff2") format("woff2"); }
@font-face { font-family: "Archivo"; font-weight: 700; font-display: swap; src: url("/fonts/Archivo-Bold.woff2") format("woff2"); }
@font-face { font-family: "Archivo Expanded"; font-weight: 800; font-display: swap; src: url("/fonts/ArchivoExpanded-ExtraBold.woff2") format("woff2"); }
@font-face { font-family: "Archivo Condensed"; font-weight: 600; font-display: swap; src: url("/fonts/ArchivoCondensed-SemiBold.woff2") format("woff2"); }
@font-face { font-family: "Archivo Condensed"; font-weight: 700; font-display: swap; src: url("/fonts/ArchivoCondensed-Bold.woff2") format("woff2"); }
@font-face { font-family: "Martian Mono"; font-weight: 400; font-display: swap; src: url("/fonts/MartianMonoCondensed-Regular.woff2") format("woff2"); }
@font-face { font-family: "Martian Mono"; font-weight: 500; font-display: swap; src: url("/fonts/MartianMonoCondensed-Medium.woff2") format("woff2"); }

/* ───────────────────────────── tokens ───────────────────────────── */
:root {
  /* The site: a pale green ground with white panels on it. */
  --ground: #EAF2EC;
  --panel: #FFFFFF;
  --ink: #15231B;
  --muted: #5E6E64;
  --line: #D7E3DA;
  /* Mint fills carry dark lettering. The deep green is for rings, outlines and text on white. */
  --accent: #A6E3B8;
  --accent-ink: #0E3A22;
  --accent-deep: #1F7A4D;
  --accent-soft: #E4F5E9;
  --ring: var(--accent-deep);
  --danger: #B3352A;
  --good: #1F7A4D;
  --lift: 0 14px 34px -16px rgba(18, 60, 36, 0.38);

  /* A page's own colours. The sheet in the editor and each public page set
     these from the page's theme; everywhere else they follow the site. */
  --p-ground: var(--panel);
  --p-panel: var(--panel);
  --p-ink: var(--ink);
  --p-muted: var(--muted);
  --p-line: var(--line);

  /* Headings and text share a face; they are named apart because they are set apart. */
  --sans: "Nunito", system-ui, -apple-system, "Segoe UI", sans-serif;
  --display: var(--sans);
  --mono: "DM Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --r: 18px;
  --r-s: 12px;
  --gutter: clamp(16px, 4vw, 40px);
  color-scheme: light;
}

/* ───────────────────────────── base ───────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  --wash: color-mix(in srgb, var(--ink) 6%, transparent);
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--ground);
  color: var(--ink);
  font: 400 1rem/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
}
main { flex: 1; }
img, canvas, svg { display: block; max-width: 100%; }
svg { width: 16px; height: 16px; flex: none; }
a { color: inherit; text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
h1, h2, h3, p { margin: 0; }
[hidden] { display: none !important; }
:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
::selection { background: var(--accent); color: var(--accent-ink); }

.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.title { font: 800 clamp(1.7rem, 3.4vw, 2.3rem) / 1.08 var(--display); letter-spacing: -0.015em; }
.heading { font: 700 1.3rem/1.2 var(--display); letter-spacing: -0.02em; }
.marking { font: 700 0.95rem/1.3 var(--sans); }
.mono { font: 400 0.8rem/1.4 var(--mono); }
.muted { color: var(--muted); }
.lede { color: var(--muted); font-size: 1.06rem; text-wrap: pretty; }

/* ───────────────────────────── the bar ───────────────────────────── */
/* The same height on every page, whatever it holds. */
.wrap { width: 100%; max-width: 1200px; margin-inline: auto; padding-inline: var(--gutter); }

.bar { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px; min-height: 72px; padding: 14px clamp(16px, 3vw, 28px); }
.brand { display: inline-flex; align-items: center; gap: 9px; justify-self: start; color: inherit; text-decoration: none; font: 700 1.12rem/1 var(--display); letter-spacing: -0.01em; }
.brand svg { width: 24px; height: 24px; }
.addr, .addr form { display: flex; align-items: center; gap: 6px; }
.addr { grid-column: 2; min-width: 0; max-width: 100%; min-height: 44px; padding: 0 16px; background: var(--panel); border: 1px solid var(--line); border-radius: 999px; font: 400 0.86rem/1 var(--mono); white-space: nowrap; overflow: hidden; }
/* A button at the end of the pill sits closer to its edge than text does. */
.addr:has(> .icon-btn, > .btn, > form) { padding-right: 6px; }
.addr .host { color: var(--muted); }
.addr b { font-weight: 500; margin-left: -6px; }
.addr a { display: inline-flex; gap: 6px; color: inherit; text-decoration: none; }
.addr a:hover b { text-decoration: underline; }
.addr input { border: 0; outline: 0; background: transparent; font: 500 0.86rem/1 var(--mono); width: 10ch; padding: 6px 0; margin-left: -6px; }
.addr input + input { width: 11ch; margin-left: 0; padding-left: 10px; border-left: 1px solid var(--line); font-family: var(--sans); }
.addr input::placeholder { color: color-mix(in srgb, var(--muted) 70%, transparent); }
.addr input[aria-invalid="true"] { color: var(--danger); }
.addr .btn { font-family: var(--sans); }
/* As tall as the account button whether or not it is there, so the bar never changes height. */
.right { grid-column: 3; justify-self: end; display: flex; align-items: center; gap: 10px; min-height: 38px; }
.me { width: 38px; height: 38px; display: grid; place-items: center; padding: 0; border: 0; border-radius: 50%; overflow: hidden; background: var(--ink); color: var(--panel); font: 700 0.95rem/1 var(--display); text-decoration: none; }
.me svg { width: 18px; height: 18px; }
.me img { width: 100%; height: 100%; object-fit: cover; }

.foot { padding-block: 28px; display: flex; flex-wrap: wrap; gap: 6px 18px; align-items: center; color: var(--muted); font-size: 0.88rem; }
.foot a { text-decoration: none; }
.foot a:hover { color: var(--ink); }
.foot .spacer { flex: 1; }

/* The account menu, and the panel it opens. */
.menu-wrap { position: relative; display: flex; }
.menu { position: absolute; z-index: 30; right: 0; top: calc(100% + 8px); min-width: 180px; padding: 6px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--lift); display: grid; }
.menu button { border: 0; background: transparent; text-align: left; padding: 9px 12px; border-radius: 10px; font-weight: 500; font-size: 0.95rem; }
.menu button:hover { background: var(--accent-soft); }
.menu hr { width: 100%; border: 0; border-top: 1px solid var(--line); margin: 6px 0; }

.drawer { margin: 0 0 0 auto; width: min(460px, 100vw); max-width: none; height: 100dvh; max-height: none; padding: 0; border: 0; border-left: 1px solid var(--line); background: var(--ground); color: var(--ink); overflow-y: auto; }
.drawer::backdrop, .modal::backdrop, dialog.image::backdrop { background: rgba(16, 30, 22, 0.42); }
.drawer-head { position: sticky; top: 0; z-index: 3; display: flex; align-items: center; justify-content: space-between; padding: 14px 14px 14px 22px; background: var(--ground); border-bottom: 1px solid var(--line); }
.drawer-body { padding: 22px; }

/* A question before something that cannot be undone, and other small dialogs. */
.modal { width: min(400px, calc(100vw - 32px)); max-width: none; padding: 0; border: 1px solid var(--line); border-radius: 26px; background: var(--panel); color: var(--ink); box-shadow: 0 30px 80px -30px rgba(14, 50, 30, 0.5); animation: modal-in 0.18s ease-out; }
.modal::backdrop { animation: fade-in 0.18s ease-out; }
.modal form, .modal .modal-body { display: grid; gap: 8px; padding: 24px; }
.modal p { color: var(--muted); text-wrap: pretty; }
.modal-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; padding-top: 14px; }
@keyframes modal-in { from { opacity: 0; transform: translateY(8px) scale(0.98); } }
@keyframes fade-in { from { opacity: 0; } }

/* ───────────────────────────── controls ───────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 40px; padding: 0 16px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); color: var(--ink);
  font: 600 0.92rem/1 var(--sans); text-decoration: none; white-space: nowrap;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
.btn:hover { border-color: color-mix(in srgb, var(--muted) 55%, var(--line)); }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { background: color-mix(in srgb, var(--accent) 88%, var(--accent-ink)); border-color: transparent; }
.btn-ink { background: var(--ink); color: var(--panel); border-color: transparent; }
.btn-ink:hover { background: color-mix(in srgb, var(--ink) 86%, var(--panel)); border-color: transparent; }
.btn-quiet { border-color: transparent; background: transparent; color: var(--muted); padding-inline: 12px; }
.btn-quiet:hover { background: var(--accent-soft); border-color: transparent; color: var(--accent-deep); }
/* On the sheet a quiet button is in the page's own inks, whatever colours the page wears. */
.sheet .btn-quiet { color: var(--p-muted); }
.sheet .btn-quiet:hover, .sheet .btn-quiet[aria-expanded="true"] { background: color-mix(in srgb, var(--p-ink) 8%, transparent); color: var(--p-ink); }
.btn-danger { color: var(--danger); }
.btn-danger-solid { background: var(--danger); color: #fff; border-color: transparent; }
.btn-danger-solid:hover { background: color-mix(in srgb, var(--danger) 88%, #000); border-color: transparent; }
.btn-small { min-height: 32px; padding-inline: 13px; font-size: 0.84rem; }
.btn-big { min-height: 50px; padding-inline: 28px; font-size: 1rem; }

.icon-btn { display: grid; place-items: center; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--muted); }
.icon-btn:hover { background: var(--accent-soft); color: var(--accent-deep); }

.switch { display: inline-flex; align-items: center; gap: 8px; border: 0; background: none; padding: 6px; font-size: 0.88rem; font-weight: 600; color: var(--muted); }
.switch i { position: relative; width: 34px; height: 20px; border-radius: 999px; background: color-mix(in srgb, var(--muted) 34%, var(--line)); transition: background-color 0.15s; }
.switch i::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: transform 0.15s; }
.switch[aria-pressed="true"] { color: var(--ink); }
.switch[aria-pressed="true"] i { background: var(--accent-deep); }
.switch[aria-pressed="true"] i::after { transform: translateX(14px); }

/* A choice between a few things. */
.seg { display: inline-flex; gap: 2px; padding: 3px; border-radius: 999px; background: color-mix(in srgb, var(--p-ink) 7%, transparent); }
.seg button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 28px; min-width: 32px; padding: 0 11px; border: 0; border-radius: 999px; background: transparent; color: var(--p-muted); font-size: 0.82rem; font-weight: 600; }
.seg button:hover { color: var(--p-ink); }
.seg button[aria-pressed="true"] { background: var(--panel); color: var(--accent-ink); }

.field { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; }
.field > label, .field > .label { font-weight: 600; font-size: 0.9rem; }
.field .hint, .hint { font-size: 0.86rem; color: var(--muted); }
.error { font-size: 0.9rem; color: var(--danger); font-weight: 500; }
.input, .textarea, .select { width: 100%; min-height: 44px; padding: 10px 14px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-s); }
.textarea { resize: vertical; line-height: 1.5; }
.input::placeholder, .textarea::placeholder { color: color-mix(in srgb, var(--muted) 70%, transparent); }
.input:focus-visible, .textarea:focus-visible, .select:focus-visible { outline: 0; border-color: var(--ring); }
.input[aria-invalid="true"] { border-color: var(--danger); }
.input.mono, .textarea.mono { font: 400 0.86rem/1.7 var(--mono); }

/* Input with a fixed prefix: the site address before a handle. */
.affix { display: flex; align-items: stretch; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-s); overflow: hidden; }
.affix:focus-within { border-color: var(--ring); }
.affix > span { display: flex; align-items: center; padding-left: 14px; color: var(--muted); font: 400 0.86rem/1 var(--mono); white-space: nowrap; }
.affix > input { flex: 1; min-width: 0; border: 0; background: transparent; padding: 10px 14px 10px 2px; min-height: 44px; font: 500 0.9rem/1 var(--mono); outline: none; }

.check { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; font-size: 0.95rem; }
.check input { width: 17px; height: 17px; margin: 3px 0 0; accent-color: var(--accent-deep); flex: none; }
.check span { display: block; }
.check small { display: block; color: var(--muted); font-size: 0.86rem; }

.notice { padding: 14px 16px; border-radius: var(--r-s); border: 1px solid var(--line); background: var(--panel); font-size: 0.93rem; text-align: left; }
.notice-bad { border-color: var(--danger); color: var(--danger); }
.notice-good { border-color: var(--good); }

/* Above everything, open dialogs included: the script shows it as a popover.
   The browser gives a popover a height of fit-content, and Safari stretches a
   fixed grid with that height to the whole window. Hence height: auto. */
.toasts { position: fixed; z-index: 50; inset: auto auto 22px 50%; transform: translateX(-50%); margin: 0; padding: 0; border: 0; background: none; color: inherit; overflow: visible; display: grid; gap: 8px; justify-items: center; pointer-events: none; width: max-content; height: auto; max-width: calc(100vw - 24px); }
body:is([data-view="first"], [data-view="edit"]) .toasts { bottom: 92px; }
.toast { pointer-events: auto; display: flex; align-items: center; gap: 14px; background: var(--ink); color: var(--panel); padding: 10px 18px; border-radius: 999px; font-weight: 500; font-size: 0.9rem; animation: toast-in 0.18s ease-out; }
.toast button { border: 0; background: none; padding: 0; color: var(--accent); font-weight: 700; }
.toast-bad { background: var(--danger); color: #fff; }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }

.spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid color-mix(in srgb, var(--p-ink) 18%, transparent); border-top-color: var(--p-ink); animation: spin 0.7s linear infinite; flex: none; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ───────────────────────────── the page being made ───────────────────────────── */
/* A sheet in the page's own colours, with the site's controls on top of it. */
body[data-view] > main { padding: 0 clamp(10px, 3vw, 28px); }
.sheet {
  --wash: color-mix(in srgb, var(--p-ink) 5%, transparent);
  max-width: 1120px; margin: 4px auto 0; padding: clamp(28px, 5vw, 64px) clamp(16px, 4vw, 56px) clamp(28px, 4vw, 52px);
  background: var(--p-ground); color: var(--p-ink); border: 1px solid var(--line); border-radius: 32px;
  transition: background-color 0.25s, color 0.25s;
}
/* Editing is for getting things done, so the sheet gives up its wide margins. */
[data-view="edit"] .sheet { padding: clamp(16px, 2.6vw, 28px) clamp(14px, 3vw, 34px) clamp(22px, 3vw, 36px); }
/* Room for the dock to float clear of whatever the page ends with. */
body:is([data-view="first"], [data-view="edit"]) { padding-bottom: 96px; }

/* Who it belongs to. Visitors get it large and centred; while editing it is a strip. */
.who { display: grid; justify-items: center; text-align: center; gap: 8px; padding-bottom: clamp(28px, 4vw, 48px); }
.avatar { width: 72px; height: 72px; flex: none; border-radius: 50%; display: grid; place-items: center; overflow: hidden; padding: 0; border: 0; background: var(--p-ink); color: var(--p-ground); font: 700 1.7rem/1 var(--display); object-fit: cover; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar svg { width: 42%; height: 42%; }
button.avatar:hover { opacity: 0.86; }
.id { display: contents; }
.name { margin-top: 8px; font: 800 clamp(2rem, 5vw, 3.2rem)/1.02 var(--display); letter-spacing: -0.015em; overflow-wrap: anywhere; }
.bio { color: var(--p-muted); font-size: 1.1rem; max-width: 52ch; text-wrap: pretty; }
.meta { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin-top: 8px; font-size: 0.88rem; }
.meta > * { display: inline-flex; align-items: center; gap: 4px; height: 30px; padding: 0 12px; border: 1px solid var(--p-line); border-radius: 999px; background: transparent; color: inherit; text-decoration: none; white-space: nowrap; }
.meta > a:hover { border-color: var(--p-muted); }
.meta .count { border-color: transparent; background: color-mix(in srgb, var(--p-ink) 8%, transparent); font-weight: 600; }
/* While editing, a link carries its own way of being removed, and an empty slot asks for the next. */
.meta .link { padding-right: 4px; }
.meta .link a { color: inherit; text-decoration: none; max-width: 28ch; overflow: hidden; text-overflow: ellipsis; }
.meta .link a:hover { text-decoration: underline; }
.meta .link button { display: grid; place-items: center; width: 22px; height: 22px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--p-muted); }
.meta .link button:hover { background: color-mix(in srgb, var(--p-ink) 10%, transparent); color: var(--p-ink); }
.meta .link svg { width: 12px; height: 12px; }
.meta .add-link { border-style: dashed; border-color: color-mix(in srgb, var(--p-ink) 28%, transparent); color: var(--p-muted); font-size: inherit; }
.meta .add-link:hover { border-style: solid; border-color: var(--ring); color: var(--p-ink); }
.meta .add-link svg { width: 13px; height: 13px; }
.meta form { padding: 0 4px 0 12px; border-color: var(--ring); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.meta form input { width: 19ch; border: 0; outline: 0; background: transparent; font: 400 0.84rem/1 var(--mono); }
.meta form input::placeholder { color: var(--p-muted); }
/* What could not be taken stays in the box, in red, until it is put right: the same as in the boxes beside it. */
.meta form input[aria-invalid="true"] { color: var(--danger); }
/* A link to X or GitHub wears that place's own mark, in the badge's ink: in
   its box while editing, and on its link as visitors get it. */
.meta svg.brand { flex: none; width: 14px; height: 14px; }
.meta > a > svg.brand { margin-right: 2px; }
.meta .link a svg.brand { display: inline-block; vertical-align: -2px; margin-right: 6px; }
.meta .preset svg.brand { margin-right: 7px; }
/* X and GitHub are asked for by name: the address, with a blank for the handle. */
.preset { cursor: text; font: 400 0.84rem/1 var(--mono); }
.preset span { color: var(--p-muted); }
.preset input { min-width: 0; padding: 0; border: 0; outline: 0; background: transparent; color: inherit; font: inherit; font-weight: 500; }
.preset input::placeholder { color: color-mix(in srgb, var(--p-muted) 70%, transparent); font-weight: 400; }
.preset input[aria-invalid="true"] { color: var(--danger); }
/* A long handle scrolls inside its box instead of pushing the row wider than the page. */
.meta .preset { gap: 0; max-width: 100%; }
.meta .preset.is-empty { border-style: dashed; border-color: color-mix(in srgb, var(--p-ink) 28%, transparent); }
.meta .preset:hover { border-color: var(--p-muted); }
.meta .preset:focus-within { border-style: solid; border-color: var(--ring); background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* Save has the line under the badges, and is only there while something typed
   has not been saved by hand. Beside the picture that line is kept for it, so
   nothing moves under whoever is typing; on a narrow screen it closes up. */
[data-view="edit"] .who-save { grid-column: 2; }
.who-save:not([disabled]) { animation: drop-in 0.14s ease-out; }
.who-save[disabled] { visibility: hidden; }

[data-view="edit"] .who { grid-template-columns: auto minmax(0, 1fr); justify-items: start; align-items: center; text-align: left; gap: 10px 14px; padding: 0; }
[data-view="edit"] .avatar { width: 60px; height: 60px; font-size: 1.45rem; }
[data-view="edit"] .id { display: grid; justify-items: start; gap: 3px; min-width: 0; }
[data-view="edit"] .name { margin: 0; font-size: clamp(1.55rem, 2.7vw, 2.05rem); line-height: 1.08; }
[data-view="edit"] .bio { font-size: 1rem; }
[data-view="edit"] .meta { grid-column: 2; justify-self: stretch; justify-content: flex-start; margin: 0; }
[data-view="edit"] [contenteditable="true"] { border-radius: 8px; outline: 1.5px dashed transparent; outline-offset: 4px; cursor: text; }
[data-view="edit"] [contenteditable="true"]:hover { outline-color: color-mix(in srgb, var(--p-ink) 28%, transparent); }
[data-view="edit"] [contenteditable="true"]:focus { outline: 2px solid var(--ring); }
/* An empty name or bio says what belongs there. */
[contenteditable="true"]:empty::before { content: attr(data-placeholder); color: color-mix(in srgb, var(--p-muted) 80%, transparent); }
/* Where that is taken from their X profile it is what visitors will read, so it is not greyed out. */
[contenteditable="true"].is-stand-in:empty::before { color: inherit; }

/* ── the top of the sheet while editing ──
   Three columns, the same as the cards below: the strip takes two, the picture
   takes the third and lines up as one more card. The third is never narrower
   than the line of controls under the picture needs. */
.top { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)) minmax(280px, 1fr); gap: 22px; }
.top .who { grid-column: span 2; align-self: center; }

/* The small picture, and under it one line for what can be done with it: its
   options on the left; on the right taking it away and, last, starting the
   page over. Whatever the picture's shape, it is fitted inside the stage with
   a margin round it. The whole thing sits a level above the list, so its
   panel opens over the apps. */
.shot { position: relative; z-index: 1; display: grid; grid-template: "stage stage" auto "opts acts" auto / minmax(0, 1fr) auto; align-content: start; gap: 8px 4px; min-width: 0; container-type: inline-size; }
.shot-stage { grid-area: stage; position: relative; display: grid; place-items: center; width: 100%; aspect-ratio: 600 / 315; padding: 10px; border: 1px solid var(--p-line); border-radius: 18px; background: var(--wash); overflow: hidden; cursor: zoom-in; transition: border-color 0.15s; }
.shot-stage:hover { border-color: color-mix(in srgb, var(--p-ink) 34%, transparent); }
.shot-stage canvas { max-width: none; border-radius: 4px; box-shadow: 0 0 0 1px color-mix(in srgb, var(--p-ink) 12%, transparent); }
/* That it opens larger is said when it is pointed at, not all the time. */
.zoom { position: absolute; top: 8px; right: 8px; width: 28px; height: 28px; display: grid; place-items: center; border-radius: 50%; background: var(--panel); color: var(--ink); opacity: 0; transition: opacity 0.15s; }
.shot-stage:hover .zoom, .shot-stage:focus-visible .zoom { opacity: 1; }
.shot-opts { grid-area: opts; justify-self: start; }
.shot-opts > .btn { gap: 3px; padding-right: 9px; }
.shot-opts > .btn svg { width: 14px; height: 14px; transition: transform 0.15s; }
.shot-opts > .btn[aria-expanded="true"] svg { transform: rotate(180deg); }
.shot-acts { grid-area: acts; display: flex; align-items: center; gap: 4px; }
/* Copy is a word where there is room for one and its icon where there is not. */
.shot-acts [data-image="copy"] svg { display: none; }
@container (max-width: 300px) {
  .shot-acts [data-image="copy"] { width: 32px; padding: 0; }
  .shot-acts [data-image="copy"] svg { display: block; }
  .shot-acts [data-image="copy"] span { display: none; }
}
/* Clear all ends the line as an icon, and says what it is when it is pointed
   at. It takes every app off the page, so under the pointer it goes red: a
   red that leans towards the page's ink, to show on a dark page too. */
.shot-acts .shot-clear { width: 32px; padding: 0; }
.shot-acts .shot-clear svg { width: 15px; height: 15px; }
.sheet .shot-acts .shot-clear:hover { background: color-mix(in srgb, var(--danger) 14%, transparent); color: color-mix(in srgb, var(--danger) 72%, var(--p-ink)); }
/* The options, opened under the picture. One of the site's own panels, so it
   keeps the site's colours on a page of any colour. In a narrow column it is
   wider than the picture, and when it is scrolled to it stops clear of the dock.
   Its last line leads to the large picture, which has the rest of them. */
.shot-pop { --pop: max(100%, 300px); position: absolute; top: calc(100% + 8px); left: 50%; width: var(--pop); margin-left: calc(var(--pop) / -2); padding: 16px; border: 1px solid var(--line); border-radius: 22px; background: var(--panel); color: var(--ink); box-shadow: var(--lift); --ring: var(--accent-deep); color-scheme: light; scroll-margin-bottom: 96px; animation: drop-in 0.14s ease-out; }
@keyframes drop-in { from { opacity: 0; transform: translateY(-4px); } }
.shot-all { justify-self: start; gap: 6px; padding-right: 11px; }
.shot-all svg { width: 13px; height: 13px; }

.statline { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; margin-top: 18px; padding: 12px 16px; border-radius: 16px; background: color-mix(in srgb, var(--p-ink) 6%, transparent); font-size: 0.95rem; }
.statline span { color: var(--p-muted); font-size: 0.86rem; margin-left: auto; }
.statline button { border: 0; background: none; padding: 0; color: inherit; font-size: 0.86rem; font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }

/* Above the list: how it is shown, then a word on working with it. */
.list-head { display: flex; align-items: center; gap: 12px; min-height: 34px; margin: 20px 0 8px; }
.list-head .hint { color: var(--p-muted); }
.list-foot { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px 18px; margin-top: 14px; font-size: 0.86rem; }
.list-foot button { border: 0; background: none; padding: 2px 0; color: var(--p-muted); text-decoration: underline; text-underline-offset: 3px; }
.list-foot button:hover { color: var(--p-ink); }

/* ── the apps ──
   One element per app. Rows and cards are the same markup laid out two ways,
   so the switch between them is a single attribute on the list. A public page
   is whichever of the two its builder has showing, with each app a link. */
.apps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 30px 22px; }
.apps[data-list="rows"] { grid-template-columns: minmax(0, 1fr); gap: 4px; }
.app { position: relative; display: grid; align-items: center; color: inherit; text-decoration: none; transition: transform 0.15s, opacity 0.15s; }
.art { position: relative; aspect-ratio: 600 / 315; border: 1px solid var(--p-line); background: var(--p-panel); overflow: hidden; display: grid; place-items: center; }
.art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.art img.is-shot { object-position: top; }
.art.lettering { background: var(--c); border-color: transparent; }
.word { padding: 0 12%; font: 800 clamp(1.2rem, 2.6vw, 1.9rem)/1 var(--display); letter-spacing: -0.015em; color: var(--f); text-align: center; overflow-wrap: anywhere; }
.ico { width: 32px; height: 32px; border-radius: 10px; display: grid; place-items: center; overflow: hidden; background: var(--c); color: var(--f); font-weight: 700; object-fit: cover; }
img.ico, .ico:has(img) { background: #fff; }
.ico img { width: 100%; height: 100%; object-fit: cover; }
.txt { min-width: 0; }
.txt b { display: block; width: fit-content; max-width: 100%; font-weight: 600; line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.txt small { display: block; color: var(--p-muted); font-size: 0.88rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.txt small.is-bad { color: var(--danger); }
.txt [contenteditable="true"] { outline-offset: 3px; }
.clicks { padding: 6px 9px; border-radius: 999px; background: color-mix(in srgb, var(--p-ink) 8%, transparent); font: 500 0.74rem/1 var(--mono); white-space: nowrap; }
.pill { position: absolute; top: 10px; left: 10px; display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 10px; border-radius: 999px; background: #fff; color: #15231B; font-size: 0.74rem; font-weight: 700; }
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: 0.55; }
/* The handle an app is moved by. Touch has to be left to the drag, not to scrolling. */
.grip { width: 22px; height: 36px; display: grid; place-items: center; border-radius: 8px; color: var(--p-muted); opacity: 0.55; cursor: grab; touch-action: none; }
.grip:hover, .grip:focus-visible { opacity: 1; }
.grip:active { cursor: grabbing; }
.app.is-hidden :is(.art img, .word, .ico, .txt, .clicks) { opacity: 0.32; }
.app.is-sunset .art img { filter: grayscale(1) opacity(0.78); }
.app.is-dragging { z-index: 5; opacity: 0.94; box-shadow: 0 14px 30px -12px rgba(20, 30, 26, 0.45); }
.is-pending .art { border-style: dashed; background: var(--wash); animation: pulse 1s ease-in-out infinite alternate; }
.is-pending :is(.tools, .grip) { visibility: hidden; }
@keyframes pulse { to { opacity: 0.55; } }
a.app:hover { transform: translateY(-3px); }

/* The paste box comes first in the list: a row among the rows, a tile among the cards. */
.add .box { border: 2px dashed color-mix(in srgb, var(--p-ink) 24%, transparent); transition: border-color 0.15s, background-color 0.15s; }
.add .box:focus-within { border-style: solid; border-color: var(--ring); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.add textarea { display: block; width: 100%; padding: 0 2px; border: 0; outline: 0; resize: none; background: transparent; font: 400 0.86rem/1.6 var(--mono); }
.add textarea::placeholder { color: var(--p-muted); }
.add .row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.add .hint { padding-left: 2px; font-size: 0.8rem; color: var(--p-muted); white-space: nowrap; }
/* What could not be added, and why, straight after the box it was pasted into. */
.apps .note { grid-column: 1 / -1; display: flex; align-items: flex-start; gap: 12px; padding: 12px 8px 12px 16px; border: 1px solid var(--p-line); border-radius: 14px; font-size: 0.9rem; }
.note > div { flex: 1; min-width: 0; }
.note ul { margin: 4px 0 0; padding-left: 1.1em; color: var(--p-muted); }
.note li { overflow-wrap: anywhere; }

/* rows: everything on one line, every control in view */
[data-list="rows"] .app { grid-template-columns: 22px 68px minmax(0, 1fr) auto auto; gap: 12px; padding: 7px 8px 7px 4px; border-radius: 14px; background: var(--wash); }
[data-list="rows"] .app:hover { background: color-mix(in srgb, var(--p-ink) 8%, transparent); }
[data-list="rows"] .app > * { grid-row: 1; }
[data-list="rows"] .app > .grip { grid-column: 1; }
[data-list="rows"] .art { grid-column: 2; border-radius: 8px; }
[data-list="rows"] .txt { grid-column: 3; }
[data-list="rows"] .clicks { grid-column: 4; }
[data-list="rows"] .tools { grid-column: 5; display: flex; align-items: center; gap: 2px; }
[data-list="rows"] :is(.ico, .pill, .tools .grip) { display: none; }
[data-list="rows"] .word { padding: 0; font-size: 0; }
[data-list="rows"] .word::before { content: attr(data-initial); font: 700 0.95rem/1 var(--sans); letter-spacing: 0; }
[data-list="rows"] .txt small { font-size: 0.82rem; }
[data-list="rows"] .app.is-hidden .art { opacity: 0.32; }
[data-list="rows"] .tools button { width: 32px; height: 32px; display: grid; place-items: center; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--p-muted); }
[data-list="rows"] .tools button:hover { background: color-mix(in srgb, var(--p-ink) 10%, transparent); color: var(--p-ink); }
[data-list="rows"] .tools .chip { width: auto; height: 28px; margin-right: 4px; padding: 0 11px; border: 1.5px solid var(--p-line); border-radius: 999px; font-size: 0.8rem; font-weight: 600; }
[data-list="rows"] .tools .chip:not([data-s="live"]) { border-color: var(--p-muted); color: var(--p-ink); }
/* One line, as tall as a row. It grows with what is pasted into it. */
[data-list="rows"] .add .box { display: flex; align-items: flex-end; gap: 12px; padding: 9px 9px 9px 14px; border-radius: 14px; }
[data-list="rows"] .add textarea { flex: 1; min-width: 0; height: 32px; padding: 5px 2px; }
[data-list="rows"] .add textarea::placeholder { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
[data-list="rows"] .add .row { flex: none; height: 32px; }

/* cards: the page as visitors get it, with the controls on the picture */
[data-list="cards"] .app { grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "art art art" "ico txt clicks"; gap: 12px 10px; }
[data-list="cards"] .art { grid-area: art; border-radius: 18px; }
[data-list="cards"] .ico { grid-area: ico; margin-left: 2px; }
[data-list="cards"] .txt { grid-area: txt; }
[data-list="cards"] .clicks { grid-area: clicks; margin-right: 2px; }
[data-list="cards"] .app > .grip { display: none; }
[data-list="cards"] .add .box { display: grid; grid-template-rows: minmax(0, 1fr) auto; gap: 8px; aspect-ratio: 600 / 315; padding: 14px; border-radius: 18px; }
[data-list="cards"] .add textarea { height: 100%; }
[data-list="cards"] .tools { position: absolute; top: 8px; right: 8px; display: flex; align-items: center; gap: 2px; padding: 3px; border-radius: 999px; background: #fff; color: #15231B; box-shadow: 0 6px 16px -8px rgba(0, 0, 0, 0.5); opacity: 0; transform: translateY(-3px); transition: opacity 0.12s, transform 0.12s; }
[data-list="cards"] .app:hover .tools, [data-list="cards"] .app:focus-within .tools { opacity: 1; transform: none; }
[data-list="cards"] .tools :is(button, .grip) { height: 30px; min-width: 30px; width: auto; padding: 0 8px; border: 0; border-radius: 999px; background: transparent; color: inherit; opacity: 1; display: inline-grid; place-items: center; font-size: 0.78rem; font-weight: 700; }
[data-list="cards"] .tools :is(button, .grip):hover { background: var(--accent-soft); color: var(--accent-deep); }
[data-view="edit"] [data-list="cards"] .app:not(.is-dragging):hover { transform: translateY(-2px); }
/* Nothing hovers on a touch screen, so the controls stay out. */
@media (hover: none) { [data-list="cards"] .tools { opacity: 1; transform: none; } }

/* With no apps: the pitch and one box, with the page it will become ghosted below. */
.hero { display: none; justify-items: center; text-align: center; gap: 14px; padding: clamp(4px, 3vw, 28px) 0 clamp(30px, 4vw, 48px); }
.hero h1 { max-width: 13ch; font: 800 clamp(2.3rem, 5.6vw, 3.8rem)/0.98 var(--display); letter-spacing: -0.02em; text-wrap: balance; }
.hero p { color: var(--p-muted); font-size: 1.14rem; }
.paste { width: min(560px, 100%); display: grid; justify-items: center; gap: 12px; margin-top: 10px; }
.paste textarea { width: 100%; min-height: 128px; padding: 16px 18px; border: 1.5px solid var(--p-line); border-radius: 22px; background: var(--p-panel); outline: 0; resize: vertical; font: 400 0.92rem/1.7 var(--mono); }
.paste textarea:focus { border-color: var(--ring); }
/* Under the big box, a small one for the builder's X handle. */
.paste .preset { display: inline-flex; align-items: center; height: 40px; padding: 0 16px; border: 1.5px solid var(--p-line); border-radius: 999px; background: var(--p-panel); font-size: 0.86rem; }
.paste .preset input { width: 15ch; }
.paste .preset:focus-within { border-color: var(--ring); }
.ghost { display: none; }
.ghost .art { border: 2px dashed color-mix(in srgb, var(--p-ink) 16%, transparent); border-radius: 18px; background: transparent; }
.ghost i { display: block; width: 55%; height: 10px; margin-top: 14px; border-radius: 5px; background: color-mix(in srgb, var(--p-ink) 9%, transparent); }
/* Where the site has examples of its own, they stand in for the blank cards:
   who each one is on top, where it is read first, and its picture fading out
   underneath. Three show at a time and the script turns them over one by one. */
.eg { display: none; grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "ico txt" "art art"; align-items: center; gap: 12px 10px; transition: opacity 0.4s ease; }
.eg .ico { grid-area: ico; margin-left: 2px; }
.eg .txt { grid-area: txt; }
.eg .art { grid-area: art; border-radius: 18px; }
.eg.is-out { opacity: 0; }
[data-view="first"] .hero { display: grid; }
[data-view="first"] .ghost { display: block; }
[data-view="first"] .eg { display: grid; }
[data-view="first"] :is(.top, .list-head, .list-foot, .add, .app, .apps .note, .statline) { display: none; }
/* The dock is there from the start, with the colours and nothing else until there is an app. */
[data-view="first"] .dock > :not(#dock-theme) { display: none; }
[data-view="first"] .apps { pointer-events: none; -webkit-mask-image: linear-gradient(#000 10%, transparent 92%); mask-image: linear-gradient(#000 10%, transparent 92%); }
/* Examples keep their names clear of the fade; it is their pictures that go. */
[data-view="first"] .apps[data-eg] { -webkit-mask-image: linear-gradient(#000 32%, transparent 96%); mask-image: linear-gradient(#000 32%, transparent 96%); }
[data-view="visit"] .top { display: block; }
[data-view="visit"] :is(.add, .shot, .list-head, .list-foot, .note, .tools, .grip, .dock, .app.is-hidden, .statline, .meta .link button) { display: none; }
[data-view="visit"] .meta .link { padding-right: 12px; }

/* ── the dock and what it opens ── */
.dock { position: fixed; z-index: 20; left: 50%; bottom: 22px; transform: translateX(-50%); display: flex; gap: 2px; padding: 6px; background: var(--panel); border: 1px solid var(--line); border-radius: 999px; box-shadow: var(--lift); }
.dock button { display: inline-flex; align-items: center; gap: 8px; height: 44px; padding: 0 16px; border: 0; border-radius: 999px; background: transparent; font-weight: 600; font-size: 0.92rem; white-space: nowrap; }
.dock button:hover { background: var(--accent-soft); }
.dock button[aria-expanded="true"], .dock button[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); }
.dot { width: 18px; height: 18px; border-radius: 50%; border: 1px solid rgba(0, 0, 0, 0.15); background: linear-gradient(var(--g) 50%, var(--a) 50%); }
.pop { position: fixed; z-index: 21; left: 50%; bottom: 90px; transform: translateX(-50%); width: min(440px, calc(100vw - 24px)); padding: 16px; background: var(--panel); border: 1px solid var(--line); border-radius: 24px; box-shadow: var(--lift); }
.pop h3 { margin: 0 2px 12px; font: 700 1rem/1.2 var(--display); }
.pop .note { margin: -4px 2px 12px; color: var(--muted); font-size: 0.88rem; }
.swatches { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.sw { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border: 1px solid var(--line); border-radius: 14px; background: var(--panel); font-weight: 600; font-size: 0.9rem; }
.sw[aria-pressed="true"] { border-color: var(--accent-deep); box-shadow: 0 0 0 1px var(--accent-deep); }
.sw .dot { width: 24px; height: 24px; }
.share-row { display: grid; grid-template-columns: 58px minmax(0, 1fr) auto; gap: 10px; align-items: center; padding: 10px 2px; border-top: 1px solid var(--line); font-weight: 600; font-size: 0.9rem; }
.share-row code { font: 400 0.78rem/1.4 var(--mono); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pop .more { margin: 10px 2px 0; }

/* The picture at full size. The small one in the header opens this too. */
dialog.image { width: min(1060px, calc(100vw - 24px)); max-width: none; max-height: calc(100dvh - 24px); padding: 0; border: 0; border-radius: 28px; background: var(--panel); color: var(--ink); box-shadow: 0 30px 80px -30px rgba(14, 50, 30, 0.5); overflow: auto; }
.image-grid { display: grid; grid-template-columns: minmax(0, 1fr) 310px; }
.image-stage { display: grid; place-items: center; min-height: 440px; max-height: calc(100dvh - 24px); padding: 26px; background: var(--ground); overflow: auto; }
.image-stage canvas { max-width: none; border-radius: 10px; box-shadow: 0 16px 36px -20px rgba(14, 50, 30, 0.55); }
.image-side { display: grid; align-content: start; gap: 20px; padding: 22px; }
.image-side h2 { display: flex; align-items: center; justify-content: space-between; font: 700 1.3rem/1.1 var(--display); letter-spacing: -0.02em; }
/* The layouts and sizes, each drawn and named. The same in the dialog and under the small picture. */
.image-opts { display: grid; gap: 20px; }
.image-opts .opt { display: grid; gap: 8px; }
.image-opts .opt > span { font-size: 0.82rem; font-weight: 700; color: var(--muted); }
.image-opts [data-opt="layout"] .choices { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.image-opts [data-opt="layout"] button { display: grid; justify-items: center; gap: 6px; padding: 10px 4px 8px; border: 1px solid var(--line); border-radius: 14px; background: var(--panel); font-size: 0.82rem; font-weight: 600; }
.image-opts [data-opt="layout"] svg { width: 44px; height: 30px; color: var(--muted); }
.image-opts [data-opt="size"] .choices { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 8px; }
.image-opts [data-opt="size"] button { display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--panel); font-size: 0.84rem; font-weight: 600; white-space: nowrap; }
/* Each outline sits in the middle of the same small square, so the names beside them line up. */
.image-opts [data-opt="size"] i { display: block; flex: none; width: var(--w); height: var(--h); margin-inline: calc((16px - var(--w)) / 2); border: 1.5px solid currentColor; border-radius: 2px; }
.image-opts .choices button { transition: border-color 0.12s ease, background-color 0.12s ease; }
.image-opts .choices button:hover { border-color: color-mix(in srgb, var(--muted) 55%, var(--line)); }
.image-opts .choices button[aria-pressed="true"] { border-color: var(--accent-deep); background: var(--accent-soft); color: var(--accent-ink); }
.image-opts .choices button[aria-pressed="true"] svg { color: var(--accent-deep); }
.image-more { display: grid; gap: 10px; }
/* Plainly a button: outlined like the others in the dialog, with an arrow that turns when it is open. */
.image-more > button { justify-self: start; gap: 4px; padding-right: 10px; }
.image-more > button svg { width: 14px; height: 14px; transition: transform 0.15s; }
.image-more > button[aria-expanded="true"] svg { transform: rotate(180deg); }
/* Which bio the image would show, said under the box that shows or hides it. */
.image-more .check small { overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.image-more .more { display: grid; gap: 10px; }
.save, .save-acts { display: grid; gap: 8px; }
.save .btn { width: 100%; min-height: 46px; }
.save .btn svg.brand { width: 14px; height: 14px; }
.save p { color: var(--muted); font-size: 0.84rem; }
.save code { font: 500 0.78rem/1.4 var(--mono); color: var(--ink); overflow-wrap: anywhere; }

/* One app's details: what a row has no room for. */
.detail { width: min(460px, calc(100vw - 32px)); }
.detail .modal-body { gap: 16px; }
.detail h2 { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.detail h2 span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.detail .row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.detail .row .spacer { flex: 1; }

/* ───────────────────────────── the panels ───────────────────────────── */
.tab-panel { display: grid; grid-template-columns: minmax(0, 1fr); gap: 22px; }
.group { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; padding-bottom: 22px; border-bottom: 1px solid var(--line); }
.group:last-child { border-bottom: 0; padding-bottom: 0; }
.avatar-row { display: flex; align-items: center; gap: 12px; }
.avatar-row .avatar { width: 60px; height: 60px; font-size: 1.2rem; }
.options { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: center; }
.code { display: block; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-s); padding: 12px 14px; font: 400 0.76rem/1.65 var(--mono); white-space: pre-wrap; overflow-wrap: anywhere; }
.copy-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.copy-row .input { flex: 1 1 200px; width: auto; font: 400 0.8rem/1 var(--mono); }
.recovery { font: 500 1.05rem/1 var(--mono); letter-spacing: 0.06em; background: var(--accent-soft); padding: 18px 16px; border-radius: var(--r-s); text-align: center; user-select: all; }

/* Stats */
.stat-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.tile { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-s); padding: 12px 14px; display: grid; gap: 2px; align-content: start; }
.tile b { font: 800 1.5rem/1.15 var(--display); letter-spacing: -0.02em; }
.tile span { font-size: 0.82rem; color: var(--muted); }
.stats-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; transition: opacity 0.15s ease; }
.stats-body.is-loading { opacity: 0.5; }

/* Chart. Series colours are the validated steps for a light surface. */
.viz { margin: 0; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-s); padding: 14px 14px 10px; --series-1: #2a78d6; --series-2: #eb6834; --viz-grid: #E8EFEA; }
.viz-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; padding-bottom: 8px; }
.viz-legend { display: flex; gap: 14px; margin-left: auto; font-size: 0.85rem; color: var(--muted); }
.viz-key { display: inline-flex; align-items: center; gap: 6px; }
.viz-key i, .viz-tip-row i { width: 14px; height: 2px; border-radius: 2px; flex: none; }
.viz-frame { position: relative; }
.viz-plot { border-radius: 6px; touch-action: pan-y; cursor: crosshair; }
.viz-plot svg { display: block; width: 100%; height: auto; overflow: visible; }
.viz-grid { stroke: var(--viz-grid); stroke-width: 1; }
.viz-axis { stroke: var(--line); stroke-width: 1; }
.viz-hair { stroke: var(--muted); stroke-width: 1; }
.viz-tick { fill: var(--muted); font: 400 11px/1 var(--sans); font-variant-numeric: tabular-nums; }
.viz-label { fill: var(--ink); font: 700 12px/1 var(--sans); }
.viz-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.viz-dot { stroke: var(--panel); stroke-width: 2; }
.viz-tip { position: absolute; z-index: 2; pointer-events: none; background: var(--ink); color: var(--panel); border-radius: 10px; padding: 8px 10px; font-size: 0.82rem; min-width: 120px; }
.viz-tip-day { opacity: 0.75; padding-bottom: 4px; white-space: nowrap; }
.viz-tip-row { display: flex; align-items: center; gap: 7px; white-space: nowrap; }
.viz-tip-row b { font-weight: 700; font-variant-numeric: tabular-nums; }
.viz-tip-row span { opacity: 0.8; }
.viz-table { margin-top: 6px; font-size: 0.875rem; }
.viz-table summary { cursor: pointer; color: var(--muted); padding: 4px 0; width: fit-content; }
.viz-table-scroll { max-height: 260px; overflow: auto; margin-top: 6px; }
.table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.table th { text-align: left; white-space: nowrap; font: 500 0.8rem/1.2 var(--sans); color: var(--muted); padding: 6px 8px; border-bottom: 1px solid var(--line); }
.table td { padding: 8px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.table tr:last-child td { border-bottom: 0; }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }
.table td:first-child, .table th:first-child { padding-left: 0; }
.table td:last-child, .table th:last-child { padding-right: 0; }
.table .clip { max-width: 0; width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ───────────────────────────── a public page ───────────────────────────── */
/* The whole window is the page, in its own colours. */
html[data-theme] body { background: var(--p-ground); color: var(--p-ink); }
.page { --wash: color-mix(in srgb, var(--p-ink) 5%, transparent); width: 100%; max-width: 1120px; margin: 0 auto; padding: clamp(28px, 5vw, 64px) clamp(16px, 4vw, 56px) 0; }
.page .foot { padding-block: clamp(32px, 6vw, 64px) 28px; color: var(--p-muted); }
.page .foot a:hover { color: var(--p-ink); }
.empty { border: 2px dashed color-mix(in srgb, var(--p-ink) 16%, transparent); border-radius: var(--r); padding: 24px; color: var(--p-muted); text-align: center; }

/* An upvote: an arrow and how many the app has had. It is a button of its own
   beside the card's link, at the end of the line with the app's name, and it
   lifts with the card. Filled once this visitor has given theirs. */
.has-vote { position: relative; }
.has-vote .txt { padding-right: 74px; }
.has-vote:hover > a.app { transform: translateY(-3px); }
.vote { position: absolute; right: 2px; bottom: 4px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; height: 32px; min-width: 42px; padding: 0 12px; border: 1px solid var(--p-line); border-radius: 999px; background: var(--p-ground); color: var(--p-ink); font: 700 0.84rem/1 var(--sans); transition: transform 0.15s, background-color 0.12s, border-color 0.12s, color 0.12s; }
.vote svg { width: 14px; height: 14px; }
.vote span:empty { display: none; }
.vote:hover { border-color: var(--p-ink); }
.has-vote:hover .vote { transform: translateY(-3px); }
.vote[aria-pressed="true"] { border-color: var(--p-ink); background: var(--p-ink); color: var(--p-ground); }

/* In the editor the upvote is a mock-up, and only in Visitor view, where the
   page is shown as visitors get it. It stands where the real one does. */
.app > .vote { display: none; }
[data-view="visit"] .app > .vote { display: inline-flex; }
[data-view="visit"] .app .txt { padding-right: 74px; }
[data-view="visit"] .clicks { display: none; }
.is-pending > .vote { visibility: hidden; }

/* Rows as visitors get them, on the page itself and in Visitor view. There is
   nothing to move an app by and no tools, so the picture leads the line and
   the upvote ends it, in the middle of the row's height. An app that is not
   simply live says so just before the upvote, where a card says it on its
   picture. A row does not lift under the pointer as a card does; it darkens. */
.state { display: none; }
:is(.page, [data-view="visit"]) [data-list="rows"] .app { grid-template-columns: 68px minmax(0, 1fr) auto; gap: 12px; padding: 8px 96px 8px 8px; }
:is(.page, [data-view="visit"]) [data-list="rows"] .art { grid-column: 1; display: grid; }
:is(.page, [data-view="visit"]) [data-list="rows"] .txt { grid-column: 2; padding-right: 0; }
:is(.page, [data-view="visit"]) [data-list="rows"] .state { grid-column: 3; display: inline-flex; align-items: center; height: 26px; padding: 0 10px; border: 1.5px solid var(--p-muted); border-radius: 999px; font-size: 0.78rem; font-weight: 600; white-space: nowrap; }
/* In the editor the upvote is one of the row's own children, and a place in
   the row's grid would measure "the middle" from inside the row's padding. */
:is(.page, [data-view="visit"]) [data-list="rows"] .vote, [data-list="rows"] .has-vote:hover .vote { grid-row: auto; top: 50%; bottom: auto; right: 10px; transform: translateY(-50%); }
[data-list="rows"] a.app:hover, [data-list="rows"] .has-vote:hover > a.app { transform: none; }

/* ───────────────────────────── explore ───────────────────────────── */
.section { padding-top: clamp(20px, 4vw, 44px); display: grid; gap: 18px; }
.builders { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: 16px; list-style: none; margin: 0; padding: 0; }
.builder { display: grid; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; text-decoration: none; transition: transform 0.16s ease; }
.builder:hover { transform: translateY(-3px); }
.builder-thumbs { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--line); aspect-ratio: 1200 / 630; }
.builder-thumbs > * { min-height: 0; background: var(--ground); display: grid; place-items: center; overflow: hidden; font: 700 0.8rem/1.1 var(--sans); text-align: center; padding: 6px; overflow-wrap: anywhere; }
.builder-thumbs img { object-fit: cover; margin: -6px; max-width: none; width: calc(100% + 12px); height: calc(100% + 12px); }
.builder-thumbs[data-n="1"] { grid-template-columns: 1fr; }
.builder-info { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 10px; align-items: center; padding: 12px 14px; }
.builder-info > div { min-width: 0; }
.builder-info img, .builder-info .avatar { width: 36px; height: 36px; border-radius: 50%; font-size: 0.9rem; }
.builder-name { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.builder-count { font-size: 0.86rem; color: var(--muted); white-space: nowrap; }
.app-domain { font: 400 0.72rem/1.3 var(--mono); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.search { display: flex; gap: 8px; max-width: 440px; }
.search .btn { min-height: 44px; }
.pager { display: flex; gap: 10px; align-items: center; justify-content: center; padding-top: 10px; }

/* ───────────────────────────── account pages and plain ones ───────────────────────────── */
.formcard { max-width: 420px; margin: clamp(20px, 7vw, 80px) auto 0; background: var(--panel); border: 1px solid var(--line); border-radius: 26px; padding: clamp(22px, 4vw, 32px); display: grid; gap: 18px; }
.formcard .heading { font-size: 1.5rem; }
.formcard form { display: grid; gap: 16px; }
.formcard form .btn { min-height: 46px; }
.formcard .alt { color: var(--muted); font-size: 0.92rem; }
.prose { display: grid; gap: 14px; max-width: 64ch; }
.prose h2 { margin-top: 16px; }
.prose ul { margin: 0; padding-left: 1.2em; display: grid; gap: 6px; }
.prose code { font: 400 0.84em/1 var(--mono); background: var(--wash); padding: 2px 6px; border-radius: 6px; }
.errorpage { min-height: 56vh; display: grid; place-content: center; gap: 12px; text-align: center; justify-items: center; }

/* ───────────────────────────── narrower screens ───────────────────────────── */
@media (max-width: 900px) {
  .apps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .top { grid-template-columns: minmax(0, 1fr) minmax(0, 280px); }
  .top .who { grid-column: auto; }
  .image-grid { grid-template-columns: minmax(0, 1fr); }
  .image-stage { min-height: 0; max-height: 46dvh; }
}
@media (max-width: 860px) {
  .bar { grid-template-columns: 1fr auto; }
  .right { grid-column: 2; }
  .addr { grid-column: 1 / -1; grid-row: 2; justify-self: center; }
}
@media (max-width: 760px) {
  .dock button { padding: 0 12px; }
  [data-list="rows"] .add .hint { display: none; }
}
@media (max-width: 640px) {
  .top { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .shot-stage { aspect-ratio: 5 / 2; }
  .who-save[disabled] { display: none; }
  /* The switch has the line to itself. */
  .list-head .hint { display: none; }
  /* 16px stops iOS zooming the page when a field takes focus. */
  .input, .textarea, .select, .affix > input { font-size: 16px; }
  .modal-actions .btn { flex: 1 1 auto; }
}
@media (max-width: 520px) {
  .apps { grid-template-columns: minmax(0, 1fr); }
  /* With every tool in the dock there is room for the icons only. */
  [data-view="edit"] .dock button span { display: none; }
  .swatches { grid-template-columns: repeat(2, 1fr); }
  [data-view="edit"] .avatar { width: 48px; height: 48px; font-size: 1.2rem; }
  /* The small picture gives its room to the name. */
  [data-list="rows"] .app { grid-template-columns: 22px 0 minmax(0, 1fr) auto auto; gap: 6px; }
  [data-list="rows"] .art { display: none; }
  /* Claiming on a phone: the two fields share the width, and the site's name makes way for them. */
  .addr:has(> form) { width: 100%; }
  .addr form { flex: 1; min-width: 0; }
  .addr form .host span { display: none; }
  .addr form input, .addr form input + input { flex: 1; width: auto; min-width: 0; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after, ::backdrop { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}
