/* The signed-in console in the landing page's language. diorama.css holds every token and the
   shared pieces (world-head, status chips, address row, build card, steps, facts, buttons, cards);
   this file only fits the owner page's own markup to them. No second palette. */
.owner-stage { position: relative; top: auto; height: 240px; min-height: 0; max-height: none; margin-top: var(--space-5); border-radius: var(--radius); box-shadow: var(--shadow); }
.owner-stage[data-full] { height: 560px; }
.owner-stage .island-slot { display: block; padding: var(--space-4); }
.owner-stage img { width: 100%; height: 100%; object-fit: contain; filter: drop-shadow(0 25px 25px rgb(30 60 80 / .15)); }
.owner-stage .chip-status { left: var(--space-4); bottom: var(--space-4); }
.stage-toggle { right: var(--space-4); bottom: var(--space-4); padding: var(--space-2) var(--space-3); font: 600 var(--text-sm)/1 var(--font-body); color: #17191c; }
/* The server's name above "Settings" is one word when the owner typed no spaces: it wraps instead of widening the page. */
.intro { margin-bottom: var(--space-2); overflow-wrap: anywhere; }
.intro .display { margin-bottom: var(--space-2); }
.intro-note { color: var(--ink-2); font-size: var(--text-base); }
#app { display: grid; gap: var(--space-5); min-width: 0; }
#app[aria-busy="true"] { cursor: progress; }
/* The button an owner pressed, while its action runs (app.mjs run()): a small turning ring, and no second press. */
button[aria-busy="true"] { cursor: progress; pointer-events: none; }
button[aria-busy="true"]::after { content: ""; display: inline-block; width: .8em; height: .8em; margin-left: .5em; vertical-align: -.1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: spin .7s linear infinite; }
@media (prefers-reduced-motion: reduce) { button[aria-busy="true"]::after { animation-duration: 2.5s; } }
#app > .card, #app .card + .card { margin-top: 0; }
#app p { overflow-wrap: anywhere; }
#app .card p + p, #app .card p + form, #app .card form + p { margin-top: var(--space-3); }
#app .card h2, #app .card h3 { font: 700 var(--text-lg)/1.2 var(--font-display); letter-spacing: -.02em; margin-bottom: var(--space-2); }
/* Messages (app.mjs tell()) sit in a dock pinned to the bottom of the window, so they are seen wherever the owner
   pressed. The dock lets clicks through; only Dismiss (shown for errors) takes them. */
.notice-dock { position: fixed; z-index: 40; left: 50%; bottom: calc(16px + env(safe-area-inset-bottom)); transform: translateX(-50%); width: min(640px, calc(100vw - 32px)); display: flex; align-items: center; gap: var(--space-2); pointer-events: none; }
.notice-dock:has(#notice:empty) { display: none; }
/* Room under the page so the dock never covers the footer, the last row or the Delete button. */
body[data-view="owner"] #panel, body[data-view="admin"] #panel { padding-bottom: calc(140px + env(safe-area-inset-bottom)); }
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
#notice { flex: 1; min-width: 0; overflow-wrap: anywhere; }
#notice:not(:empty) { padding: var(--space-3) var(--space-4); border-radius: var(--radius); background: var(--accent-soft); color: var(--accent-ink); font-weight: 500; box-shadow: var(--shadow); }
.notice-close { pointer-events: auto; flex: none; padding: var(--space-2) var(--space-3); border: 1.5px solid var(--line-2); border-radius: var(--radius); background: var(--surface); color: var(--ink); font: 600 var(--text-sm)/1 var(--font-body); cursor: pointer; box-shadow: var(--shadow); }
#notice.error { background: var(--danger-soft); color: var(--danger); }

/* Forms and buttons from the module markup take the landing's inputs and edged buttons. */
#app form { display: grid; gap: var(--space-4); }
#app label { display: grid; gap: var(--space-2); font: 600 var(--text-sm)/1.3 var(--font-body); }
#app label:has(input[type="checkbox"]) { display: flex; align-items: flex-start; gap: var(--space-2); }
#app > *, #app .card, #app form, #app label { min-width: 0; }
#app input, #app select { min-width: 0; max-width: 100%; }
#app input[type="number"] { width: 100%; height: 52px; padding: 0 var(--space-4); border-radius: var(--radius); border: 1.5px solid var(--line-2); background: var(--surface-3); font-size: var(--text-base); }
/* File pickers (World ZIP, Data pack ZIP): the browser's own button drawn as .btn-secondary; the input itself stays the
   keyboard target and still shows the chosen file's name (the plain grey "Choose File" stood out, owner walk #1489). */
#app input[type="file"] { max-width: 100%; color: var(--ink-2); font: 500 var(--text-sm)/1.3 var(--font-body); }
#app input[type="file"]::file-selector-button { height: 46px; margin: 0 var(--space-3) var(--space-1) 0; padding: 0 var(--space-4); border-radius: var(--radius); border: 1.5px solid var(--line-2); background: var(--surface); color: var(--ink); box-shadow: 0 4px 0 var(--line-2); font: 600 var(--text-base)/1 var(--font-body); cursor: pointer; transition: background-color .2s, border-color .2s; }
#app input[type="file"]:hover:not(:disabled)::file-selector-button { background: var(--surface-3); border-color: var(--line-3); }
#app input[type="file"]:disabled { opacity: .5; } #app input[type="file"]:disabled::file-selector-button { cursor: not-allowed; }
#app input[type="checkbox"] { accent-color: var(--accent); margin-top: 3px; flex-shrink: 0; width: 18px; height: 18px; }
#app button:not(.btn):not(.text-link):not(.more-item) {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); justify-self: start;
  min-height: 46px; padding: 0 var(--space-4); border-radius: var(--radius); border: 0; cursor: pointer; margin-bottom: var(--space-1);
  font: 600 var(--text-base)/1 var(--font-body); background: var(--accent); color: var(--on-accent);
  box-shadow: 0 4px 0 var(--accent-edge), inset 0 1px 0 rgb(255 255 255 / .2);
  transition: transform .12s var(--ease), box-shadow .12s var(--ease), background-color .2s;
}
#app button:not(.btn):not(.text-link):not(.more-item):hover:not(:disabled) { background: var(--accent-hover); }
#app button:not(.btn):not(.text-link):not(.more-item):active:not(:disabled) { transform: translateY(3px); box-shadow: 0 1px 0 var(--accent-edge); }
#app button.quiet:not(.btn):not(.text-link) { background: var(--surface); color: var(--ink); border: 1.5px solid var(--line-2); box-shadow: 0 4px 0 var(--line-2); }
#app button.quiet:not(.btn):not(.text-link):hover:not(:disabled) { background: var(--surface-3); border-color: var(--line-3); }
#app button.danger:not(.btn), #app .btn-danger { background: var(--danger); color: var(--on-danger); box-shadow: 0 4px 0 var(--danger-edge); }
#app button:disabled { opacity: .5; cursor: not-allowed; }
#app .muted, .inline-note { color: var(--ink-3); font-size: var(--text-sm); line-height: 1.55; }
.field-help { font-size: var(--text-sm); font-weight: 400; color: var(--ink-3); line-height: 1.5; }
.field-error { margin: 0 !important; color: var(--danger); font-size: var(--text-sm); }
.availability-note { padding: var(--space-3) var(--space-4); border-radius: var(--radius); background: var(--warn-soft); color: var(--warn-ink); font-size: var(--text-sm); }
.badge { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--ink-2); background: var(--surface-2); border-radius: 999px; padding: var(--space-1) var(--space-2); font: 600 var(--text-xs)/1 var(--font-body); }
.badge.pending { background: var(--warn-soft); color: var(--warn-ink); }
.badge.error { background: var(--danger-soft); color: var(--danger); }
.section-top { display: flex; gap: var(--space-3); justify-content: space-between; align-items: center; flex-wrap: wrap; }

/* Overview: the server is the page's title, like the mockup. */
.servers-top { display: flex; justify-content: space-between; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
.servers-top h2 { font: 600 var(--text-xs)/1.2 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); }
.owner-server + .owner-server { margin-top: var(--space-5); }
.owner-server .world-name { margin-bottom: var(--space-4); }
.owner-server .status-row + .availability-note { margin-top: var(--space-4); }
.owner-server .build-card, .owner-server .discord-card { margin-top: var(--space-5); }
.join-hint { margin-top: var(--space-3); }
.sleep-note { display: flex; gap: var(--space-2); margin-top: var(--space-3) !important; padding: var(--space-3) var(--space-4); border-radius: var(--radius); background: var(--night-soft); color: var(--night-ink); font-size: var(--text-sm); }
.server-actions { margin-top: var(--space-5); }
.owner-server .danger-zone { margin-top: var(--space-5); }
/* Overview of a built server (app.mjs serverCard): one panel ruled into rows, data before decoration (DESIGN-SYSTEM rule
   12), laid out as a hosting panel (Keiden, website owner, 2026-10-07). Row 1 the name with its status chip, the note, and
   on the right the state button and More; row 2 the address with Copy, and how to join; row 3 who can join; row 4 Players
   (add by username, the names); row 5 how it sleeps (beside the region or event engine when they apply); row 6 Discord.
   Nothing about recording here (DESIGN-SYSTEM rule 12). */
.server-panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 0; }
/* Not overflow: hidden, which would cut off the More menu; the last row rounds its own corners instead. */
.server-panel > :last-child { border-bottom-left-radius: var(--radius); border-bottom-right-radius: var(--radius); }
.server-panel > * { padding: var(--space-4) var(--space-5); }
.server-panel > * + * { border-top: 1px solid var(--line); }
.server-bar { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--space-3) var(--space-5); }
.server-bar .eyebrow { margin-bottom: var(--space-1); }
.owner-server .server-bar .world-name { margin: 0; }
.server-title { flex: 1 1 320px; min-width: 0; }
.server-name-row { gap: var(--space-2) var(--space-3); }
#app .server-title > .server-note { margin: var(--space-2) 0 0; }
#app .server-title > .availability-note { margin: var(--space-3) 0 0; }
.server-bar > .server-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; }
.building > .server-bar + .build-card { margin-top: var(--space-4); }
/* More (owner-server.mjs moreMenu): a quiet button that opens a short list of actions under it, right-aligned. */
.server-more { position: relative; }
.more-caret { font-size: .8em; margin-left: var(--space-1); }
.more-menu { position: absolute; right: 0; top: calc(100% + var(--space-1)); z-index: 20; min-width: 248px; display: grid; padding: var(--space-1); background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--radius); box-shadow: var(--shadow); }
.more-menu[hidden] { display: none; }
#app .more-item { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--space-2); width: 100%; min-height: 44px; margin: 0; padding: var(--space-2) var(--space-3); border: 0; border-radius: calc(var(--radius) - 4px); background: none; box-shadow: none; color: var(--ink); font: 600 var(--text-sm)/1.3 var(--font-body); text-align: left; text-decoration: none; cursor: pointer; align-content: center; }
#app .more-item:hover:not(:disabled), #app .more-item:focus-visible { background: var(--surface-2); }
#app .more-item:disabled { opacity: .5; cursor: not-allowed; }
.more-item .referral-left { font-weight: 500; }
.server-controls { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3) var(--space-4); }
.server-address { flex: 1 1 420px; min-width: 0; }
.server-address .facts-label { display: block; margin-bottom: var(--space-2); }
/* A usual address (about 30 characters) broke at its hyphen at 390 px under Geist Mono (#1403): on phones it uses the
   smaller size so it fits on one line down to 360 px. A longer one still wraps, so all of it stays readable. */
@media (max-width: 400px) { .address-row .address { font-size: var(--text-sm); } }
/* A two-line address (dev2, owner walk #1483) made the address row taller than its Copy: Copy shares its bottom edge. */
.server-address .address-row { align-items: flex-end; }
.server-controls > .join-hint { flex-basis: 100%; margin: 0; }
/* Who can join on the server card (owner-settings.mjs joinChoice): two options side by side, the chosen one filled; the
   radios stay real (arrow keys, a screen reader's radio group) and only look like a segmented control. */
.join-choice { border: 0; margin: 0; min-width: 0; }
.server-panel > .join-choice { border-top: 1px solid var(--line); }
#app .join-choice legend { display: block; float: left; width: 100%; margin: 0 0 var(--space-2); padding: 0; }
.segmented { clear: both; display: inline-grid; grid-template-columns: 1fr 1fr; max-width: 100%; padding: 3px; gap: 3px; border-radius: var(--radius); background: var(--surface-2); border: 1px solid var(--line); }
#app .segment { position: relative; display: flex; align-items: center; justify-content: center; min-height: 40px; padding: 0 var(--space-4); border-radius: calc(var(--radius) - 3px); font: 600 var(--text-sm)/1.2 var(--font-body); color: var(--ink-2); text-align: center; cursor: pointer; }
#app .segment input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
#app .segment:has(input:checked) { background: var(--surface); color: var(--ink); box-shadow: 0 1px 0 var(--line-2), 0 0 0 1px var(--line-2); }
#app .segment:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
#app .segment:has(input:disabled) { opacity: .5; cursor: not-allowed; }
#app .join-choice > .field-help { margin: var(--space-2) 0 0; }
.join-choice-confirm { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: var(--space-3); }
#app .join-choice-confirm .availability-note { flex-basis: 100%; margin: 0; }
/* Players on the server card (owner-settings.mjs card()): the heading line with the count and Player settings, the add
   form, then one row per name with its one action. */
.players-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-4); }
#app .players-head h3 { margin: 0; }
#app .players-head p { margin: 0; }
.players-who { font-size: var(--text-sm); color: var(--ink-2); }
.players-head .text-link { margin-left: auto; font-size: var(--text-sm); }
#app .server-players form[data-player-add] { margin: var(--space-3) 0 0; gap: var(--space-2) var(--space-3); }
.building > .join-choice, .building > .server-players { margin-top: var(--space-5); }
.building > .join-choice { padding: 0; }
/* "Next steps" under Players (owner-checklist.mjs): the heading with Hide, then the shared .steps list (diorama.css), a check
   on each done step and the link to its control under each open one. */
.checklist-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-4); }
#app .checklist-head h3 { margin: 0; }
.checklist-head .text-link { margin-left: auto; font-size: var(--text-sm); }
#app .server-checklist .steps { margin: var(--space-2) 0 0; }
.server-checklist .steps li { align-items: center; padding: var(--space-1) 0; }
.server-checklist .steps li[data-s="next"] { color: var(--ink); }
.server-checklist .checklist-item { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-3); font-size: var(--text-base); }
.server-checklist .checklist-item b { display: inline; }
.server-checklist .checklist-item .text-link { font-size: var(--text-sm); }
.server-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); padding: 0 !important; }
.server-body > * { padding: var(--space-4) var(--space-5); align-content: center; }
.server-join { display: grid; } .server-join .sleep-note { margin-top: 0 !important; }
.server-body > :only-child { grid-column: 1 / -1; }   /* no facts row to show: the sleep note takes the full width */
.server-body > * + * { border-left: 1px solid var(--line); }
.server-panel > .discord-card { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); margin: 0; border: 0; border-top: 1px solid var(--line); border-radius: 0; box-shadow: none; }
.server-panel > .discord-card > * { margin: 0 !important; }
.server-panel > .discord-card .cta-row { margin-left: auto !important; }
/* The facts table: a mono label column, the value, and any Copy at the end of its row. */
.facts-label { font: 500 var(--text-xs)/1.6 var(--font-mono); letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }
/* Building: one live state from Create until the server has been online once (owner-server.mjs). */
.build-line { color: var(--ink-2); font-size: var(--text-base); line-height: 1.55; margin-bottom: var(--space-4); }
.progress-bar[data-step="1"] { width: 30%; }
.progress-bar[data-step="2"] { width: 72%; }
.build-card .steps { margin-top: var(--space-4); }
.build-card .steps li { align-items: center; }
.early-ip .address { display: flex; color: var(--ink-2); }
.address-input { display: flex; align-items: center; gap: var(--space-1); } .address-input input { flex: 1; min-width: 0; } .address-suffix { color: var(--ink-2); white-space: nowrap; }
/* Settings → Server address: the current address with Copy, then New address. On a phone the suffix took the row and left
   the field about 120 px wide (owner walk #1483): the suffix goes under the field there. */
.address-card .address-row { margin-bottom: var(--space-4); }
@media (max-width: 560px) { .address-input { flex-wrap: wrap; } .address-input input { flex-basis: 100%; } }

/* Create: "Create your server", shown while the owner is under the server limit; first on the page with no server. */
.create-card .form-card { margin-top: var(--space-5) !important; }
.found-us { display: grid; gap: var(--space-2); }
#create .opt { font-weight: 500; color: var(--ink-3); }   /* "Optional" beside the server name and How did you find us? */
#app .found-us label[hidden] { display: none; }   /* the Other box with its label, until Other is chosen; #app label sets grid */
/* Address, world and region keep their defaults under one closed "More options" line (app.mjs). */
.create-more { border-top: 1px solid var(--line); }
.create-more > summary { cursor: pointer; padding: var(--space-3) 0 0; font: 600 var(--text-sm)/1.3 var(--font-body); }
.create-more-hint { font-weight: 500; color: var(--ink-3); margin-left: var(--space-1); }
.create-more-body { display: grid; gap: var(--space-4); padding-top: var(--space-4); }
.create-card .lede { margin-top: -6px; }
.create-card .field-help { margin-top: -8px !important; }
.create-card .facts li { gap: var(--space-3); }
/* A phone's first screen (390 x 664) holds the whole Create form down to Create server (#1316). With the self-hosted
   fonts drawing, Geist's metrics put the button about 80 px below that fold, so on a phone the form is tighter: the
   heading one step down the scale, 46 px fields (the button height), --space-3 between rows. */
@media (max-width: 560px) {
  .create-card .display { font-size: var(--text-xl); margin-bottom: var(--space-2); }
  .create-card .lede { font-size: var(--text-base); }
  .create-card .form-card { margin-top: var(--space-4) !important; }
  #app .create-card form { gap: var(--space-3); }
  #app .create-card label { gap: var(--space-1); }
  .create-card .field-help { margin-top: calc(-1 * var(--space-1)) !important; }
  .create-card form input:not([type="checkbox"]):not([type="radio"]):not([type="file"]), .create-card form select { height: 46px; }
  .create-more > summary { padding-top: var(--space-2); white-space: nowrap; }
  .create-more-hint { font-size: var(--text-xs); }
}
/* Create: server size, shown only to an account given large or event servers (app.mjs sizeHtml). */
.size-choice { border: 0; padding: 0; margin: 0 0 var(--space-4); }
.size-choice legend { font-weight: 600; margin-bottom: var(--space-2); }
.size-choice .size-option { display: block; margin-bottom: var(--space-2); }
.create-card .size-choice .field-help { display: block; margin: 2px 0 0 var(--space-5) !important; }
/* Create: a new world, or "Start from my world (.zip)" (create-world.mjs). */
.world-choice { border: 0; margin: 0; padding: 0; display: grid; gap: var(--space-2); min-width: 0; }
.world-choice legend { font: 600 var(--text-sm)/1.3 var(--font-body); margin-bottom: var(--space-2); padding: 0; }
#app .world-choice label.choice { display: flex; align-items: center; gap: var(--space-2); font-weight: 500; }
.world-choice input[type="radio"] { width: 18px; height: 18px; margin: 0; accent-color: var(--accent); flex: none; }
[data-world-new], [data-world-upload-create] { display: grid; gap: var(--space-4); min-width: 0; }
[data-world-new][hidden], [data-world-upload-create][hidden] { display: none; }
[data-world-upload-create] input[type="file"] { max-width: 100%; }
[data-create-progress-box] progress { width: 100%; }
.fact-mark { width: 20px; color: var(--accent-ink); text-align: center; flex: none; }

/* Live status: the page asks the owner API by itself (app.mjs poll); this says when it last heard back. */
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; }
#app .live { display: inline-flex; align-items: center; gap: var(--space-2); margin: 0; font: 500 var(--text-xs) var(--font-mono); letter-spacing: .02em; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.live[data-active="true"] .live-dot { animation: live-pulse 1.6s ease-out infinite; }
.live[data-active="false"] .live-dot { background: var(--muted-dot); }
@keyframes live-pulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); } 100% { box-shadow: 0 0 0 9px transparent; } }
@media (prefers-reduced-motion: reduce) { .live[data-active="true"] .live-dot { animation: none; } }

.owner-footer { display: flex; flex-wrap: wrap; gap: var(--space-4); justify-content: space-between; align-items: center; margin-top: var(--space-6); }
.owner-footer .small-print { font-size: var(--text-xs); }
.community-link { display: inline-flex; align-items: center; gap: var(--space-1); }
.check-email-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-4) 0 var(--space-3); }
.check-email-inboxes { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); margin: var(--space-3) 0; font-weight: 600; }
/* World: one tidy block under the IP and Discord cards. */
.server-facts { display: grid; font-size: var(--text-sm); color: var(--ink); }
.server-facts:empty { display: none; }
.server-facts p { display: grid; grid-template-columns: 112px minmax(0, 1fr) auto; gap: var(--space-3); align-items: baseline; margin: 0 !important; padding: var(--space-2) 0; border-top: 1px solid var(--line); }
.server-facts p:first-child { border-top: 0; padding-top: 0; }
.server-facts p:last-child { padding-bottom: 0; }
.server-facts [data-server-engine] { display: block; }
.discord-card .cta-row { margin-top: var(--space-3); }
.discord-card .discord-row { display: block; margin-top: var(--space-4); font-size: var(--text-sm); color: var(--ink-2); }
.server-facts button.text-link, .discord-card button.text-link { border: 0; padding: 0; background: none; font: inherit; cursor: pointer; }
.seed-line { overflow-wrap: anywhere; }
.seed-line code.seed { font: 500 .95em var(--font-mono); color: var(--ink); user-select: all; }
.world-settings .world-facts { margin: 0 0 var(--space-4); }
.admin-link { display: inline-block; color: var(--accent-ink); font-size: var(--text-sm); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
/* Two columns need room: below 760 px the facts table, how it sleeps and the Players columns stack (a 112 px label
   column in half of a tablet's width squeezed the values, review-1363). */
@media (max-width: 760px) {
  .server-body { grid-template-columns: minmax(0, 1fr); } .server-body > * + * { border-left: 0; border-top: 1px solid var(--line); }
}
@media (max-width: 560px) {
  .topbar-row { gap: var(--space-2); } #account-label { display: none; }
  .server-panel > *, .server-body > * { padding: var(--space-4); }
  .server-facts p { grid-template-columns: 96px minmax(0, 1fr) auto; }
  .server-address .address-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }   /* Copy alone, full width, under the address */
  /* A phone: the state button and More share one row under the name, each half of it; the menu opens under More. */
  .server-bar > .server-actions { flex: 1 1 100%; flex-wrap: nowrap; }
  .server-bar > .server-actions > .btn, .server-bar > .server-actions > .server-more { flex: 1 1 0; min-width: 0; }
  .server-bar > .server-actions .more-toggle { width: 100%; }
  .segmented { display: grid; width: 100%; } #app .segment { padding: 0 var(--space-2); }
  #app .card { padding: var(--space-4); } .address-row { flex-direction: column; } .address-row .btn { width: 100%; }
  .server-actions .btn { flex: 1; }
}

/* Settings: the same shell, full width; each part is one card. */
body[data-view="admin"] .owner-stage { display: none; }
/* Phones: the island is decoration; the server card (status and address), or with no server the Create form, comes
   first on screen (with no server the island filled a phone's first screen after the invite claim, walk 2026-10-06). */
@media (max-width: 860px) { :root[data-view="owner"] .owner-stage { display: none; } }
body[data-view="owner"] .layout, body[data-view="admin"] .layout { display: block; max-width: 1120px; }
.admin-heading nav { color: var(--ink-3); font-size: var(--text-sm); overflow-wrap: anywhere; }
.admin-heading nav a { color: var(--accent-ink); font-weight: 600; text-decoration: none; }
.admin-heading .live { margin-top: var(--space-2) !important; }
.admin-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
@media (max-width: 700px) { body[data-view="owner"] .layout, body[data-view="admin"] .layout { padding: var(--space-4) var(--space-4); } }
.players-card form[data-player-add], .server-players form[data-player-add] { grid-template-columns: minmax(0, 1fr) auto; align-items: end; }
.players-card form[data-player-add] .field-error, .server-players form[data-player-add] .field-error { grid-column: 1 / -1; grid-row: 2; }
.player-list { list-style: none; margin: var(--space-4) 0 0; padding: 0; }
.player-row { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); justify-content: flex-start; align-items: center; padding: var(--space-3) 0; border-top: 1px solid var(--line); }
/* The name takes the free space and the buttons sit together at the end (space-between spread Make operator, Remove and
   Block across the card, owner walk #1483). */
.player-row > .player-name { margin-right: auto; }
.player-name { font: 600 var(--text-base)/1.25 var(--font-body); overflow-wrap: anywhere; }
.player-confirm { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-2); align-items: center; font-size: var(--text-sm); }
@media (max-width: 560px) { .players-card form[data-player-add], .server-players form[data-player-add] { grid-template-columns: minmax(0, 1fr); } .players-card form[data-player-add] .field-error, .server-players form[data-player-add] .field-error { grid-row: auto; } }
/* World settings: one row per schema key, grouped; a setting already active says nothing. */
.world-group { border: 0; margin: 0; padding: 0; min-width: 0; }
.world-group + .world-group { margin-top: var(--space-3); }
.world-group legend { padding: 0; margin-bottom: var(--space-1); font: 700 var(--text-base)/1.3 var(--font-display); }
/* One compact row per setting: name (and any "applies at next start") on the left, the control on the right,
   help in small print below; two columns of rows on a wide screen, one on a phone. */
.world-group { column-gap: var(--space-6); }
@media (min-width: 900px) { .world-group { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
.world-setting { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-1) var(--space-4); align-items: center; padding: var(--space-2) 0; border-top: 1px solid var(--line); min-width: 0; }
.world-setting > * { grid-column: 1 / -1; }
.world-setting > .setting-head { grid-column: 1; }
.world-setting > input:not([type=checkbox]), .world-setting > select { grid-column: 2; grid-row: 1; width: 10rem; min-height: 40px; padding-block: var(--space-1); }
.world-setting > input[type=text] { grid-column: 1 / -1; grid-row: auto; width: 100%; }
.setting-head { display: flex; flex-wrap: wrap; gap: 2px var(--space-2); align-items: baseline; }
@media (max-width: 560px) { .world-setting > input:not([type=checkbox]), .world-setting > select { width: 7.5rem; } }
.setting-name { font-size: var(--text-base); font-weight: 600; }
.setting-class { font-size: var(--text-xs); color: var(--ink-3); white-space: nowrap; }
.world-setting[data-setting-class="restart-now"] .setting-class { color: var(--warn-ink); }
.world-setting .field-help { margin: 0 !important; font-size: var(--text-sm); line-height: 1.4; }
.world-setting .setting-state { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); align-items: center; margin: 0 !important; }
.world-setting .setting-state:has([data-setting-state]:empty) { display: none !important; }
.world-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* The few settings an owner changes first; everything else under one "Advanced settings" disclosure, closed. */
.advanced-settings { margin: var(--space-3) 0 var(--space-1); border-top: 1px solid var(--line); }
.advanced-settings > summary { cursor: pointer; padding: var(--space-3) 0; font: 700 var(--text-base)/1.3 var(--font-display); }
.advanced-settings[open] > summary { margin-bottom: var(--space-1); }
/* Delete server: a plain section at the bottom of the settings page and a typed-name dialog. */
.danger-zone { border: 2px solid var(--danger); margin-top: var(--space-6) !important; scroll-margin-top: 16px; }
.danger-link { align-self: center; color: var(--danger); font-weight: 600; font-size: var(--text-sm); text-underline-offset: 3px; }
/* The top of Settings: the server's status and its Start or Stop, the same markup as the server card. */
.settings-status { margin-top: var(--space-3); display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; justify-content: space-between; }
.settings-status .cta-row { margin: 0; }
.settings-status .admin-actions { gap: var(--space-3); align-items: center; }
/* Inside Players and World settings, rows are --space-4 apart with a --line rule between them (DESIGN-SYSTEM rule 8):
   the summary, Who can join, the add field and Operators sat flush against each other (owner walk #1489). */
#app .players-card > * + * { margin-top: var(--space-4); }
#app .players-card > .section-top { padding-bottom: var(--space-4); border-bottom: 1px solid var(--line); }   /* a fieldset's own top border would run through its legend */
#app .players-card > :is(form[data-player-add], .operators-note) { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--line); }
#app .players-card > .join-mode + p { margin-top: var(--space-2); }
#app .players-card > .player-list { margin-top: var(--space-2); }
.operators-note .field-help { margin-top: var(--space-1) !important; }
.world-settings .world-facts { margin: var(--space-4) 0; }
.world-settings .world-facts p:first-child { border-top: 1px solid var(--line); padding-top: var(--space-2); }
.delete-dialog { max-width: min(460px, calc(100vw - 32px)); border: 0; border-radius: var(--radius); padding: var(--space-5); background: var(--surface); color: var(--ink); box-shadow: var(--shadow); }
.delete-dialog::backdrop { background: rgb(0 0 0 / .35); }
.delete-dialog h3 { margin-top: 0; overflow-wrap: anywhere; }
.dialog-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: flex-end; margin-top: var(--space-4); }
.feedback-dialog { width: min(460px, calc(100vw - 32px)); }
.feedback-dialog label { display: grid; gap: var(--space-2); margin-top: var(--space-3); font: 600 var(--text-sm)/1.3 var(--font-body); }
.feedback-dialog label:has(input[type="checkbox"]) { display: flex; align-items: center; gap: var(--space-2); font-weight: 500; }
.feedback-dialog textarea { width: 100%; box-sizing: border-box; resize: vertical; }
.feedback-dialog input[type="checkbox"] { accent-color: var(--accent); width: 18px; height: 18px; }
.deleted-line { margin-top: var(--space-4) !important; color: var(--ink-3); font-size: var(--text-sm); }
.deleted-line .text-link, .server-checklist button.text-link { border: 0; padding: 0; background: none; color: var(--accent-ink); font: inherit; font-weight: 600; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
/* Owner's invite link ("Give a friend their own server", right under the server card once one exists): the ready-made
   message with Copy message (and Share on phones), then the one link with Copy link. The server card's More has "Give a
   friend an invite", which scrolls here. */
#app [data-referral-invites] .referral-message { margin: var(--space-2) 0; padding: var(--space-2) var(--space-3); background: var(--surface-2); border-radius: var(--radius); font-size: var(--text-sm); color: var(--ink); overflow-wrap: anywhere; }
.referral-share { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-2); }
.invite-share { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.invite-link { font: 500 .95em var(--font-mono); color: var(--ink); user-select: all; overflow-wrap: break-word; }
.referral-left { font-size: var(--text-xs); color: var(--ink-3); }
/* Who can join (owner-settings.mjs): a plain group of radios like the Create form's world choice, not a browser-default box. */
.join-mode { border: 0; margin: 0; padding: 0; min-width: 0; display: grid; gap: var(--space-2); }
.join-mode legend { font: 600 var(--text-sm)/1.3 var(--font-body); margin-bottom: var(--space-2); padding: 0; }
#app label:has(input[type="radio"]) { display: flex; align-items: center; gap: var(--space-2); font-weight: 500; }
#app input[type="radio"] { width: 18px; height: 18px; margin: 0; accent-color: var(--accent); flex: none; }
