{"body":"<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Walkable</title>\n    <link\n      rel=\"icon\"\n      type=\"image/svg+xml\"\n      href=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' fill='none'%3E%3Ccircle cx='31' cy='31' r='4.6' fill='%233F5233' opacity='.32'/%3E%3Ccircle cx='23.5' cy='25' r='4.8' fill='%233F5233' opacity='.55'/%3E%3Ccircle cx='16.5' cy='17.5' r='5' fill='%234D6040' opacity='.8'/%3E%3Ccircle cx='11.5' cy='9.5' r='5.2' fill='%23C4983A'/%3E%3Ccircle cx='9.6' cy='7.2' r='1.5' fill='%23FFF8E7' opacity='.9'/%3E%3C/svg%3E\"\n    />\n    <style>\n:root {\n  --bg: #f5f2ec;\n  --nav-bg: #1f2a2e;\n  --nav-text: #f3efe8;\n  --accent: #e07a5f;\n  --muted: #a9a39b;\n  --shadow: 0 10px 30px rgba(0, 0, 0, 0.12);\n  --menu-shadow: 0 14px 40px rgba(0, 0, 0, 0.28);\n  --nav-height: 56px;\n  font-family: \"Avenir Next\", \"Avenir\", \"Futura\", \"Segoe UI\", sans-serif;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  margin: 0;\n  background: var(--bg);\n  color: #1f2a2e;\n}\n\n.shell {\n  min-height: 100vh;\n  display: flex;\n  flex-direction: column;\n}\n\n/* ---- The frame ---- */\n\n.frame {\n  height: var(--nav-height);\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 0 12px 0 8px;\n  background: var(--nav-bg);\n  color: var(--nav-text);\n  box-shadow: var(--shadow);\n}\n\n.zone {\n  display: flex;\n  align-items: center;\n  min-width: 0;\n}\n\n.zone-left {\n  gap: 4px;\n  flex: 0 0 auto;\n}\n\n.zone-center {\n  flex: 1 1 auto;\n  min-width: 0;\n  justify-content: flex-start;\n}\n\n.zone-right {\n  flex: 0 0 auto;\n  justify-content: flex-end;\n  gap: 4px;\n}\n\n/* ---- Left: waffle + wordmark ---- */\n\n.apps {\n  position: relative;\n}\n\n.waffle {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 36px;\n  height: 36px;\n  border-radius: 8px;\n  color: var(--nav-text);\n  text-decoration: none;\n  font-size: 1.2rem;\n  line-height: 1;\n}\n\n.waffle:hover,\n.waffle[aria-expanded=\"true\"] {\n  background: rgba(255, 255, 255, 0.1);\n}\n\n.wordmark {\n  font-weight: 700;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  font-size: 0.82rem;\n  color: var(--nav-text);\n  text-decoration: none;\n  white-space: nowrap;\n}\n\n/* ---- Center: the location sentence ---- */\n\n.lens {\n  position: relative;\n  display: flex;\n  min-width: 0;\n}\n\n.location {\n  display: inline-flex;\n  align-items: baseline;\n  gap: 8px;\n  min-width: 0;\n}\n\n/* The location is a <button> so it can open the lens (\"open with…\") menu on\n * lens routes; on plain app routes it stays disabled and looks like the old\n * inert sentence. */\n.lens-chip {\n  background: none;\n  border: 0;\n  padding: 4px 8px;\n  margin: -4px -8px;\n  font: inherit;\n  color: inherit;\n  border-radius: 8px;\n  /* Flex item of .lens: natural width normally, shrinkable (the app-name\n   * ellipsizes) when the frame gets tight. NOT max-width:100% — that resolves\n   * circularly against the shrink-wrapped parent and clamps the name. */\n  min-width: 0;\n}\n\n.lens-chip:disabled {\n  cursor: default;\n}\n\n.lens-chip:not(:disabled) {\n  cursor: pointer;\n}\n\n.lens-chip:not(:disabled):hover,\n.lens-chip[aria-expanded=\"true\"] {\n  background: color-mix(in srgb, currentColor 8%, transparent);\n}\n\n.lens-caret {\n  font-size: 0.7rem;\n  align-self: center;\n}\n\n.lens-caret[hidden] {\n  display: none;\n}\n\n.app-icon {\n  display: inline-flex;\n  align-items: center;\n  align-self: center;\n  flex: 0 0 auto;\n  font-size: 1.1rem;\n  line-height: 1;\n}\n\n.app-icon[hidden] {\n  display: none;\n}\n\n.app-icon img {\n  display: block;\n  height: 1.15rem;\n  width: 1.15rem;\n  object-fit: contain;\n  border-radius: 4px;\n}\n\n.app-name {\n  font-size: 1.1rem;\n  font-weight: 600;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.from {\n  font-size: 0.85rem;\n  color: var(--muted);\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  flex: 0 1 auto;\n}\n\n.from[hidden] {\n  display: none;\n}\n\n/* ---- Right: account chip + menu ---- */\n\n.account {\n  position: relative;\n}\n\n.account-chip {\n  display: inline-flex;\n  align-items: center;\n  gap: 8px;\n  background: transparent;\n  border: 1px solid transparent;\n  border-radius: 999px;\n  padding: 4px 10px 4px 4px;\n  color: var(--nav-text);\n  font: inherit;\n  font-size: 0.9rem;\n  cursor: pointer;\n  max-width: 220px;\n}\n\n.account-chip:hover,\n.account-chip[aria-expanded=\"true\"] {\n  background: rgba(255, 255, 255, 0.1);\n  border-color: rgba(255, 255, 255, 0.14);\n}\n\n.account[data-auth-state=\"signed-out\"] .account-chip {\n  border-color: rgba(255, 255, 255, 0.32);\n  padding: 6px 12px;\n  font-weight: 700;\n}\n\n.account[data-auth-state=\"signed-out\"] .account-chip:hover,\n.account[data-auth-state=\"signed-out\"] .account-chip:focus-visible {\n  background: rgba(255, 255, 255, 0.14);\n  border-color: rgba(255, 255, 255, 0.5);\n}\n\n.account[data-auth-state=\"signed-out\"] .avatar,\n.account[data-auth-state=\"signed-out\"] .caret {\n  display: none;\n}\n\n/* Persistent frame toggle for phantom users: shared synthetic accounts whose\n * pages are included or excluded by the user's private cred rating on each\n * host-provided phantom root. */\n.phantom-chip {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  gap: 7px;\n  min-width: 100px;\n  height: 34px;\n  flex: 0 0 auto;\n  background: rgba(226, 226, 226, 0.1);\n  color: rgba(243, 239, 232, 0.62);\n  border: 1px solid rgba(243, 239, 232, 0.22);\n  border-radius: 999px;\n  padding: 0 13px;\n  font: inherit;\n  font-size: 0.78rem;\n  font-weight: 800;\n  text-transform: uppercase;\n  cursor: pointer;\n  opacity: 0.78;\n  position: relative;\n  transition: background 120ms ease, border-color 120ms ease, color 120ms ease, opacity 120ms ease;\n}\n\n.phantom-chip[data-state=\"on\"] {\n  background: #b83f35;\n  color: #fff8f6;\n  border-color: rgba(255, 198, 185, 0.9);\n  opacity: 1;\n}\n\n.phantom-chip[data-busy=\"true\"] {\n  cursor: wait;\n  opacity: 0.68;\n}\n\n.phantom-chip[data-busy=\"true\"] .phantom-chip-short,\n.phantom-chip[data-busy=\"true\"] .phantom-chip-label {\n  visibility: hidden;\n}\n\n.phantom-chip::after {\n  content: \"\";\n  display: none;\n  position: absolute;\n  width: 14px;\n  height: 14px;\n  border: 2px solid currentColor;\n  border-right-color: transparent;\n  border-radius: 999px;\n}\n\n.phantom-chip[data-busy=\"true\"]::after {\n  display: block;\n  animation: phantom-spin 700ms linear infinite;\n}\n\n@keyframes phantom-spin {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .phantom-chip[data-busy=\"true\"]::after {\n    animation: none;\n  }\n}\n\n.phantom-chip:hover,\n.phantom-chip:focus-visible {\n  background: rgba(226, 226, 226, 0.18);\n  border-color: rgba(243, 239, 232, 0.36);\n  color: rgba(243, 239, 232, 0.84);\n}\n\n.phantom-chip[data-state=\"on\"]:hover,\n.phantom-chip[data-state=\"on\"]:focus-visible {\n  background: #cb4b3f;\n  border-color: rgba(255, 220, 212, 0.95);\n  color: #fff;\n}\n\n.phantom-chip-short {\n  display: none;\n  letter-spacing: 0;\n}\n\n.phantom-chip-label {\n  white-space: nowrap;\n}\n\n/* The Phantoms chip's positioning context. */\n.phantom {\n  position: relative;\n  display: inline-flex;\n}\n\n.modal-backdrop {\n  position: fixed;\n  inset: 0;\n  z-index: 100;\n  display: grid;\n  place-items: center;\n  padding: 18px;\n  background: rgba(16, 21, 23, 0.54);\n}\n\n.modal-backdrop[hidden] {\n  display: none;\n}\n\n.phantom-confirm-modal {\n  width: min(420px, 100%);\n  border-radius: 12px;\n  background: #fff;\n  color: #1f2a2e;\n  box-shadow: var(--menu-shadow);\n  padding: 18px;\n  display: grid;\n  gap: 12px;\n}\n\n.phantom-confirm-modal h2 {\n  margin: 0;\n  font-size: 1.12rem;\n  line-height: 1.25;\n}\n\n.phantom-confirm-modal p {\n  margin: 0;\n  color: #5f676b;\n  line-height: 1.45;\n}\n\n.phantom-confirm-body {\n  display: grid;\n  gap: 10px;\n}\n\n/* The toggle word (\"visible\" / \"invisible\") in the modal title, strongly\n   highlighted so the new state reads at a glance. */\n.phantom-confirm-state {\n  font-weight: 800;\n  color: #173a2a;\n  background: #ffe39a;\n  padding: 0 0.28em;\n  border-radius: 4px;\n}\n\n/* The collapsed \"more…\" detail under the lead paragraph. */\n.phantom-confirm-more > summary {\n  cursor: pointer;\n  color: #2e7d5b;\n  font-weight: 600;\n  list-style: none;\n}\n\n.phantom-confirm-more > summary::-webkit-details-marker {\n  display: none;\n}\n\n.phantom-confirm-more[open] > summary {\n  margin-bottom: 8px;\n}\n\n/* `.phantom-confirm-modal p` zeroes margins and only the body's direct children\n   get the grid gap, so stacked paragraphs (the two inside the disclosure) would\n   otherwise run together. Restore the break between consecutive paragraphs. */\n.phantom-confirm-body p + p {\n  margin-top: 0.7em;\n}\n\n.phantom-confirm-actions {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 12px;\n  flex-wrap: wrap;\n}\n\n.phantom-confirm-dismiss {\n  display: inline-flex;\n  align-items: center;\n  gap: 7px;\n  font-size: 0.9rem;\n  color: #5f676b;\n  cursor: pointer;\n}\n\n.phantom-confirm-dismiss input {\n  width: 15px;\n  height: 15px;\n  accent-color: #2e7d5b;\n  cursor: pointer;\n}\n\n.phantom-confirm-action {\n  min-height: 36px;\n  border-radius: 8px;\n  padding: 0 13px;\n  font: inherit;\n  font-weight: 700;\n  cursor: pointer;\n  border: 1px solid #1f2a2e;\n  background: #1f2a2e;\n  color: #fff;\n}\n\n.frame-extension-button {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  gap: 7px;\n  position: relative;\n  min-width: 34px;\n  height: 34px;\n  flex: 0 0 auto;\n  border: 1px solid rgba(185, 213, 188, 0.42);\n  border-radius: 999px;\n  padding: 0 12px;\n  background: rgba(106, 143, 112, 0.22);\n  color: rgba(244, 250, 241, 0.9);\n  font: inherit;\n  font-size: 0.8rem;\n  font-weight: 800;\n  cursor: pointer;\n  transition: background 120ms ease, border-color 120ms ease, color 120ms ease, box-shadow 120ms ease;\n}\n\n.frame-extension-button[hidden] {\n  display: none;\n}\n\n.frame-extension-label {\n  white-space: nowrap;\n}\n\n.frame-extension-icon,\n.frame-extension-icon svg {\n  width: 18px;\n  height: 18px;\n}\n\n.frame-extension-icon {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  color: rgba(218, 240, 214, 0.95);\n}\n\n.frame-extension-icon svg {\n  display: block;\n}\n\n.frame-extension-icon svg {\n  fill: none;\n  stroke: currentColor;\n  stroke-width: 1.9;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n}\n\n.frame-extension-badge {\n  position: absolute;\n  top: -4px;\n  right: -4px;\n  min-width: 18px;\n  height: 18px;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  border: 2px solid var(--nav-bg);\n  border-radius: 999px;\n  padding: 0 4px;\n  background: #8f3d2d;\n  color: #fff;\n  font-size: 0.66rem;\n  font-weight: 800;\n  line-height: 1;\n}\n\n.frame-extension-button:hover,\n.frame-extension-button:focus-visible {\n  background: rgba(126, 162, 130, 0.34);\n  border-color: rgba(214, 237, 209, 0.72);\n  color: #fff;\n}\n\n.frame-extension-button:focus-visible {\n  outline: 2px solid rgba(214, 237, 209, 0.55);\n  outline-offset: 2px;\n}\n\n.account-name {\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.caret {\n  font-size: 0.7rem;\n  opacity: 0.7;\n}\n\n.avatar {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 28px;\n  height: 28px;\n  flex: 0 0 28px;\n  border-radius: 50%;\n  background: var(--accent);\n  color: #fff;\n  font-size: 0.85rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  background-size: cover;\n  background-position: center;\n}\n\n.avatar.lg {\n  width: 40px;\n  height: 40px;\n  flex-basis: 40px;\n  font-size: 1.1rem;\n}\n\n/* ---- Frame dropdowns ---- */\n\n.apps-menu,\n.account-menu,\n.lens-menu {\n  position: absolute;\n  top: calc(100% + 8px);\n  min-width: 260px;\n  max-width: 320px;\n  background: #fff;\n  color: #1f2a2e;\n  border-radius: 12px;\n  box-shadow: var(--menu-shadow);\n  padding: 8px;\n  z-index: 50;\n}\n\n.apps-menu {\n  left: 0;\n  max-height: min(70vh, 520px);\n  overflow-y: auto;\n}\n\n.account-menu {\n  right: 0;\n}\n\n.lens-menu {\n  left: 50%;\n  transform: translateX(-50%);\n}\n\n.apps-menu[hidden],\n.account-menu[hidden],\n.lens-menu[hidden] {\n  display: none;\n}\n\n.account-active {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 10px 10px 12px;\n}\n\n.account-active-text {\n  min-width: 0;\n}\n\n.account-active-name {\n  font-weight: 600;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.account-active-sub {\n  font-size: 0.8rem;\n  color: #6b6b6b;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.menu-sep {\n  height: 1px;\n  background: #ececec;\n  margin: 6px 4px;\n}\n\n.menu-label {\n  font-size: 0.7rem;\n  letter-spacing: 0.06em;\n  text-transform: uppercase;\n  color: #9a9a9a;\n  padding: 4px 12px;\n}\n\n.menu-item {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  width: 100%;\n  padding: 9px 12px;\n  border: none;\n  border-radius: 8px;\n  background: transparent;\n  color: inherit;\n  font: inherit;\n  font-size: 0.9rem;\n  text-align: left;\n  text-decoration: none;\n  cursor: pointer;\n}\n\n.menu-item:hover {\n  background: #f3f0ea;\n}\n\n.menu-item[aria-current=\"page\"] {\n  background: #f3f0ea;\n  font-weight: 600;\n}\n\n.menu-item .avatar {\n  width: 26px;\n  height: 26px;\n  flex-basis: 26px;\n  font-size: 0.8rem;\n}\n\n.menu-item .menu-item-text {\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.menu-item .menu-glyph {\n  display: inline-flex;\n  width: 26px;\n  justify-content: center;\n  opacity: 0.7;\n}\n\n.menu-item.danger {\n  color: #b3261e;\n}\n\n/* \"Coming soon\" rows: visible roadmap, dimmed and non-interactive. */\n.menu-item.is-soon {\n  cursor: default;\n  opacity: 0.5;\n}\n\n.menu-item.is-soon:hover {\n  background: transparent;\n}\n\n.menu-soon-tag {\n  margin-left: auto;\n  flex: 0 0 auto;\n  font-size: 0.62rem;\n  letter-spacing: 0.05em;\n  text-transform: uppercase;\n  color: #8a8a8a;\n  border: 1px solid #e0e0e0;\n  border-radius: 999px;\n  padding: 1px 7px;\n}\n\n/* A switchable account paired with its \"open in a new window\" action.\n   The switch button flex-grows; the icon stays a fixed icon button (#342). */\n.account-other-row {\n  display: flex;\n  align-items: center;\n}\n\n.account-other-row .account-other {\n  flex: 1 1 auto;\n  min-width: 0;\n}\n\n.account-new-window {\n  flex: 0 0 auto;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 30px;\n  height: 30px;\n  margin-right: 6px;\n  border: none;\n  border-radius: 8px;\n  background: transparent;\n  color: inherit;\n  font: inherit;\n  font-size: 0.95rem;\n  opacity: 0.55;\n  cursor: pointer;\n}\n\n.account-new-window:hover {\n  background: #f3f0ea;\n  opacity: 1;\n}\n\n/* Shared role accounts: \"act as a role\" rows + the acting-as band. A role wears\n   a distinct marker so a borrowed identity is never mistaken for one's own. */\n.avatar.role {\n  background: #2f6f6a;\n  color: #fff;\n}\n\n.account-role .menu-item-text {\n  font-style: italic;\n}\n\n/* When the account chip / menu is currently acting as a role, tint its avatar. */\n.account[data-acting-as-role=\"true\"] .avatar {\n  box-shadow: 0 0 0 2px #2f6f6a;\n}\n\n.account-acting-band {\n  padding: 8px 12px;\n  background: #eef5f4;\n  border-radius: 8px;\n  margin: 4px 8px;\n}\n\n.account-acting-note {\n  font-size: 0.8rem;\n  opacity: 0.8;\n  margin-bottom: 6px;\n}\n\n.account-switch-back {\n  color: #2f6f6a;\n  font-weight: 600;\n}\n\n:root[data-theme=\"dark\"] .account-acting-band {\n  background: #1c2b2a;\n}\n\n/* A teaching-band row: primary item + trailing help link. */\n.menu-row {\n  display: flex;\n  align-items: center;\n  gap: 2px;\n}\n\n.menu-row > .menu-item {\n  flex: 1 1 auto;\n  min-width: 0;\n}\n\n.menu-info {\n  flex: 0 0 auto;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 30px;\n  height: 30px;\n  border-radius: 8px;\n  color: #9a9a9a;\n  text-decoration: none;\n  font-size: 0.95rem;\n}\n\n.menu-info:hover {\n  background: #f3f0ea;\n  color: #1f2a2e;\n}\n\n.primary-button,\n.secondary-button {\n  min-width: 72px;\n  height: 36px;\n  border-radius: 8px;\n  font: inherit;\n  font-size: 0.9rem;\n  cursor: pointer;\n}\n\n.primary-button {\n  border: 1px solid #1f2a2e;\n  background: #1f2a2e;\n  color: #fff;\n}\n\n.secondary-button {\n  border: 1px solid #d7d2ca;\n  background: #fff;\n  color: #1f2a2e;\n}\n\n.primary-button:disabled,\n.secondary-button:disabled {\n  cursor: default;\n  opacity: 0.55;\n}\n\n/* ---- Content ---- */\n\n.content {\n  flex: 1;\n  min-height: 0;\n  position: relative;\n}\n\n/* ---- Public (chromeless) mode ----\n   Public read-only sites (docs/decisions/public-read-only-sites.md) hide the frame\n   for anonymous visitors and offer Login as a slim footer link. The server sets\n   <html data-chrome=\"public\"> in <head> so the frame never flashes pre-paint; the\n   shell sets data-signed-in once auth resolves, restoring full chrome for a\n   signed-in curator. Inert (footer stays display:none) outside public mode. */\n.public-footer {\n  display: none;\n}\nhtml[data-chrome=\"public\"] .frame {\n  display: none;\n}\nhtml[data-chrome=\"public\"] .public-footer {\n  display: flex;\n  justify-content: flex-end;\n  align-items: center;\n  padding: 10px 16px;\n  border-top: 1px solid rgba(31, 42, 46, 0.1);\n  background: var(--bg);\n}\nhtml[data-chrome=\"public\"][data-signed-in=\"true\"] .frame {\n  display: flex;\n}\nhtml[data-chrome=\"public\"][data-signed-in=\"true\"] .public-footer {\n  display: none;\n}\n.public-footer-link {\n  color: var(--muted);\n  font-size: 13px;\n  text-decoration: none;\n}\n.public-footer-link:hover {\n  color: #1f2a2e;\n  text-decoration: underline;\n}\n\n.app-error-banner {\n  position: absolute;\n  z-index: 30;\n  top: 12px;\n  left: 12px;\n  right: 12px;\n  max-width: 880px;\n  margin: 0 auto;\n  display: flex;\n  align-items: flex-start;\n  gap: 14px;\n  padding: 12px;\n  border: 1px solid #d78a5c;\n  border-radius: 10px;\n  background: #fff8f0;\n  color: #31251d;\n  box-shadow: 0 12px 30px rgba(31, 42, 46, 0.18);\n}\n\n/* Trust referral, not a prompt: a quiet note that a shared link asked for a\n * below-horizon lens, pointing at TrustLamp where an informed decision is\n * possible (decisions/lens-switcher-network-tier.md). Deliberately calm —\n * neutral surface, no warning colors: nothing is wrong, something is hidden. */\n.lens-referral-banner {\n  position: absolute;\n  z-index: 29;\n  top: 12px;\n  left: 12px;\n  right: 12px;\n  max-width: 880px;\n  margin: 0 auto;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 10px 14px;\n  border: 1px solid #d5d5d0;\n  border-radius: 10px;\n  background: #fbfbf8;\n  color: #31353a;\n  font-size: 0.9rem;\n  box-shadow: 0 8px 22px rgba(31, 42, 46, 0.12);\n}\n\n.lens-referral-banner[hidden] {\n  display: none;\n}\n\n.lens-referral-text {\n  min-width: 0;\n}\n\n.lens-referral-link {\n  white-space: nowrap;\n  color: inherit;\n  font-weight: 600;\n}\n\n.lens-referral-dismiss {\n  margin-left: auto;\n  background: none;\n  border: 0;\n  cursor: pointer;\n  color: inherit;\n  font-size: 0.9rem;\n  padding: 4px;\n}\n\n:root[data-theme=\"dark\"] .lens-referral-banner {\n  background: var(--surface);\n  color: var(--on-surface);\n  border-color: rgba(255, 255, 255, 0.16);\n}\n\n/* Mirror-on-demand fetch progress: shown while the host caches a publisher app\n   the user just opened (decision app-references-are-urls-invisible-cache §4). */\n.mirror-fetch-banner {\n  position: absolute;\n  z-index: 29;\n  top: 12px;\n  left: 12px;\n  right: 12px;\n  max-width: 880px;\n  margin: 0 auto;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 10px 14px;\n  border: 1px solid #d5d5d0;\n  border-radius: 10px;\n  background: #fbfbf8;\n  color: #31353a;\n  font-size: 0.9rem;\n  box-shadow: 0 8px 22px rgba(31, 42, 46, 0.12);\n}\n\n.mirror-fetch-banner[hidden] {\n  display: none;\n}\n\n.mirror-fetch-spinner {\n  flex: 0 0 auto;\n  width: 14px;\n  height: 14px;\n  border: 2px solid #d5d5d0;\n  border-top-color: #31353a;\n  border-radius: 50%;\n  animation: mirror-fetch-spin 0.9s linear infinite;\n}\n\n.mirror-fetch-banner.mirror-fetch-error .mirror-fetch-spinner {\n  display: none;\n}\n\n@keyframes mirror-fetch-spin {\n  to { transform: rotate(360deg); }\n}\n\n:root[data-theme=\"dark\"] .mirror-fetch-banner {\n  background: var(--surface);\n  color: var(--on-surface);\n  border-color: rgba(255, 255, 255, 0.16);\n}\n\n:root[data-theme=\"dark\"] .mirror-fetch-spinner {\n  border-color: rgba(255, 255, 255, 0.24);\n  border-top-color: var(--on-surface);\n}\n\n.app-error-banner[hidden] {\n  display: none;\n}\n\n.app-error-copy {\n  flex: 1 1 auto;\n  min-width: 0;\n}\n\n.app-error-title {\n  font-weight: 800;\n  font-size: 0.95rem;\n  line-height: 1.25;\n}\n\n.app-error-message {\n  margin-top: 3px;\n  color: #5f4937;\n  font-size: 0.88rem;\n  line-height: 1.35;\n}\n\n.app-error-details {\n  margin-top: 6px;\n  color: #71563e;\n  font-size: 0.78rem;\n}\n\n.app-error-details summary {\n  width: fit-content;\n  cursor: pointer;\n  font-weight: 700;\n}\n\n.app-error-details p {\n  margin: 5px 0 0;\n  overflow-wrap: anywhere;\n}\n\n.app-error-actions {\n  display: flex;\n  flex: 0 0 auto;\n  gap: 8px;\n  align-items: center;\n}\n\n.app-error-reload,\n.app-error-dismiss {\n  min-height: 34px;\n  border-radius: 8px;\n  padding: 0 11px;\n  font: inherit;\n  font-size: 0.82rem;\n  font-weight: 800;\n  cursor: pointer;\n  white-space: nowrap;\n}\n\n.app-error-reload {\n  border: 1px solid #1f2a2e;\n  background: #1f2a2e;\n  color: #fff;\n}\n\n.app-error-dismiss {\n  border: 1px solid #d9b98e;\n  background: #fff;\n  color: #5f4937;\n}\n\n#app-frame {\n  width: 100%;\n  height: calc(100vh - var(--nav-height));\n  border: none;\n  display: block;\n  background: #fff;\n}\n\n/* ---- Mobile: stay a single row; shed the least-needed bits first ---- */\n\n@media (max-width: 600px) {\n  .wordmark {\n    display: none;\n  }\n  .account-name,\n  .caret {\n    display: none;\n  }\n  .account-chip {\n    padding: 4px;\n  }\n  .phantom-chip {\n    min-width: 34px;\n    width: 34px;\n    padding: 0;\n  }\n  .phantom-chip-short {\n    display: inline;\n  }\n  .phantom-chip-label {\n    display: none;\n  }\n  .frame-extension-label {\n    display: none;\n  }\n  .frame-extension-button {\n    width: 34px;\n    padding: 0;\n  }\n  .app-name {\n    font-size: 1rem;\n  }\n  .app-error-banner {\n    top: 8px;\n    left: 8px;\n    right: 8px;\n    flex-direction: column;\n    gap: 10px;\n  }\n  .app-error-actions {\n    width: 100%;\n  }\n  .app-error-reload,\n  .app-error-dismiss {\n    flex: 1 1 0;\n  }\n}\n\n/* ---- Dark theme ----\n   Applied when the shell sets <html data-theme=\"dark\"> from the reader's account\n   preference (app.ts applyTheme; pre-painted by the inline <head> script). The nav\n   bar is already dark in both themes; these rules darken the remaining light\n   surfaces — the popover menus, the Phantoms modal, buttons, the app-load backdrop,\n   and the data-error banner — so the whole shell, including the modal, follows the\n   setting. Light mode is the default and left untouched. */\n:root[data-theme=\"dark\"] {\n  --bg: #15191b;\n  --muted: #8b9398;\n  --shadow: 0 10px 30px rgba(0, 0, 0, 0.45);\n  --menu-shadow: 0 14px 40px rgba(0, 0, 0, 0.6);\n  --surface: #1f262a;\n  --surface-2: #283034;\n  --on-surface: #e9eef0;\n  --on-surface-muted: #9aa3a8;\n  --border-soft: #323b40;\n  --accent-green: #2e7d5b;\n  --accent-green-border: #3c8f6a;\n}\n\n:root[data-theme=\"dark\"] body {\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .apps-menu,\n:root[data-theme=\"dark\"] .account-menu,\n:root[data-theme=\"dark\"] .lens-menu {\n  background: var(--surface);\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .account-active-sub,\n:root[data-theme=\"dark\"] .menu-label,\n:root[data-theme=\"dark\"] .menu-info {\n  color: var(--on-surface-muted);\n}\n\n:root[data-theme=\"dark\"] .menu-sep {\n  background: var(--border-soft);\n}\n\n:root[data-theme=\"dark\"] .menu-item:hover,\n:root[data-theme=\"dark\"] .menu-item[aria-current=\"page\"],\n:root[data-theme=\"dark\"] .account-new-window:hover {\n  background: var(--surface-2);\n}\n\n:root[data-theme=\"dark\"] .menu-info:hover {\n  background: var(--surface-2);\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .menu-item.danger {\n  color: #f08a82;\n}\n\n:root[data-theme=\"dark\"] .menu-soon-tag {\n  color: var(--on-surface-muted);\n  border-color: var(--border-soft);\n}\n\n:root[data-theme=\"dark\"] .secondary-button {\n  background: var(--surface-2);\n  border-color: var(--border-soft);\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .primary-button {\n  border-color: var(--accent-green-border);\n  background: var(--accent-green);\n  color: #fff;\n}\n\n/* The Phantoms modal follows the theme. */\n:root[data-theme=\"dark\"] .phantom-confirm-modal {\n  background: var(--surface);\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .phantom-confirm-modal p {\n  color: var(--on-surface-muted);\n}\n\n:root[data-theme=\"dark\"] .phantom-confirm-more > summary {\n  color: #6fc69b;\n}\n\n:root[data-theme=\"dark\"] .phantom-confirm-dismiss {\n  color: var(--on-surface-muted);\n}\n\n:root[data-theme=\"dark\"] .phantom-confirm-action {\n  border-color: var(--accent-green-border);\n  background: var(--accent-green);\n  color: #fff;\n}\n\n/* The toggle-state highlight stays a warm chip; darken its text for contrast. */\n:root[data-theme=\"dark\"] .phantom-confirm-state {\n  color: #1a2e22;\n}\n\n:root[data-theme=\"dark\"] #app-frame {\n  background: var(--bg);\n}\n\n/* Data-error banner: warm-dark amber instead of cream. */\n:root[data-theme=\"dark\"] .app-error-banner {\n  background: #2a211a;\n  border-color: #8a5a36;\n  color: #f0e3d6;\n}\n\n:root[data-theme=\"dark\"] .app-error-message {\n  color: #d8c2ab;\n}\n\n:root[data-theme=\"dark\"] .app-error-details {\n  color: #c9b196;\n}\n\n:root[data-theme=\"dark\"] .app-error-reload {\n  border-color: var(--accent-green-border);\n  background: var(--accent-green);\n}\n\n:root[data-theme=\"dark\"] .app-error-dismiss {\n  background: var(--surface-2);\n  border-color: #8a5a36;\n  color: #f0e3d6;\n}\n\n:root[data-theme=\"dark\"] .public-footer-link:hover {\n  color: var(--on-surface);\n}\n\n</style><script>\n      // Pre-paint theme: apply the cached resolved scheme (or the OS preference) so a\n      // returning reader's dark mode never flashes light before app.js loads the\n      // authoritative account preference and reconciles. Mirror of THEME_PAINT_HINT_KEY\n      // / applyTheme in app.ts.\n      (function () {\n        try {\n          var hint = localStorage.getItem(\"wkbl.themeResolved\");\n          var resolved =\n            hint === \"light\" || hint === \"dark\"\n              ? hint\n              : window.matchMedia && window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n                ? \"dark\"\n                : \"light\";\n          document.documentElement.dataset.theme = resolved;\n        } catch (e) {}\n      })();\n    </script>\n  </head>\n  <body>\n    <div class=\"shell\">\n      <header class=\"frame\">\n        <div class=\"zone zone-left\">\n          <div class=\"apps\" id=\"apps\">\n            <a\n              class=\"waffle\"\n              id=\"waffle\"\n              href=\"/\"\n              title=\"Apps\"\n              aria-label=\"Apps\"\n              aria-haspopup=\"menu\"\n              aria-expanded=\"false\"\n              aria-controls=\"apps-menu\"\n            >\n              <span aria-hidden=\"true\">☰</span>\n            </a>\n            <div class=\"apps-menu\" id=\"apps-menu\" role=\"menu\" hidden></div>\n          </div>\n          <a class=\"wordmark\" id=\"home-link\" href=\"/\">Walkable</a>\n        </div>\n\n        <div class=\"zone zone-center\">\n          <div class=\"lens\" id=\"lens\">\n            <button\n              class=\"location lens-chip\"\n              id=\"lens-chip\"\n              type=\"button\"\n              disabled\n              aria-haspopup=\"menu\"\n              aria-expanded=\"false\"\n              aria-controls=\"lens-menu\"\n            >\n              <span class=\"app-icon\" id=\"app-icon\" aria-hidden=\"true\" hidden></span>\n              <span class=\"app-name\" id=\"app-name\">App</span>\n              <span class=\"from\" id=\"from-owner\" hidden></span>\n              <span class=\"caret lens-caret\" id=\"lens-caret\" aria-hidden=\"true\" hidden>▾</span>\n            </button>\n            <div class=\"lens-menu\" id=\"lens-menu\" role=\"menu\" hidden></div>\n          </div>\n        </div>\n\n        <div class=\"zone zone-right\">\n          <div class=\"phantom\" id=\"phantom\">\n            <button\n              class=\"phantom-chip\"\n              id=\"phantom-chip\"\n              type=\"button\"\n              title=\"Phantom users are hidden\"\n              aria-pressed=\"false\"\n              data-state=\"off\"\n            >\n              <span class=\"phantom-chip-short\" aria-hidden=\"true\">Ph</span>\n              <span class=\"phantom-chip-label\">Phantoms</span>\n            </button>\n          </div>\n          <div class=\"account\" id=\"account\" data-auth-state=\"signed-out\">\n            <button\n              class=\"account-chip\"\n              id=\"account-chip\"\n              aria-haspopup=\"false\"\n              aria-expanded=\"false\"\n              aria-label=\"Log in or sign up\"\n            >\n              <span class=\"avatar\" id=\"account-avatar\" aria-hidden=\"true\">·</span>\n              <span class=\"account-name\" id=\"user-label\">Log in / Sign up</span>\n              <span class=\"caret\" aria-hidden=\"true\">▾</span>\n            </button>\n            <div class=\"account-menu\" id=\"account-menu\" role=\"menu\" hidden></div>\n          </div>\n        </div>\n      </header>\n\n      <main class=\"content\">\n        <div class=\"lens-referral-banner\" id=\"lens-referral-banner\" role=\"status\" aria-live=\"polite\" hidden>\n          <span class=\"lens-referral-text\" id=\"lens-referral-text\"></span>\n          <a class=\"lens-referral-link\" id=\"lens-referral-link\" href=\"/apps/app-trustlamp\">See who trusts them</a>\n          <button class=\"lens-referral-dismiss\" id=\"lens-referral-dismiss\" type=\"button\" aria-label=\"Dismiss\">✕</button>\n        </div>\n        <div class=\"mirror-fetch-banner\" id=\"mirror-fetch-banner\" role=\"status\" aria-live=\"polite\" hidden>\n          <span class=\"mirror-fetch-spinner\" id=\"mirror-fetch-spinner\" aria-hidden=\"true\"></span>\n          <span class=\"mirror-fetch-text\" id=\"mirror-fetch-text\"></span>\n        </div>\n        <div class=\"app-error-banner\" id=\"app-error-banner\" role=\"status\" aria-live=\"polite\" hidden>\n          <div class=\"app-error-copy\">\n            <div class=\"app-error-title\" id=\"app-error-title\">This app could not load data</div>\n            <div class=\"app-error-message\" id=\"app-error-message\"></div>\n            <details class=\"app-error-details\">\n              <summary>Details</summary>\n              <p id=\"app-error-detail\"></p>\n            </details>\n          </div>\n          <div class=\"app-error-actions\">\n            <button class=\"app-error-reload\" id=\"app-error-reload\" type=\"button\">Reload app</button>\n            <button class=\"app-error-dismiss\" id=\"app-error-dismiss\" type=\"button\" aria-label=\"Dismiss app data warning\">Dismiss</button>\n          </div>\n        </div>\n        <iframe\n          id=\"app-frame\"\n          sandbox=\"allow-forms allow-scripts allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation\"\n          title=\"App\"\n        ></iframe>\n      </main>\n\n      <!-- Public read-only sites: the only chrome an anonymous visitor sees. Hidden\n           by default; shown by CSS only under <html data-chrome=\"public\"> while\n           signed out (see docs/decisions/public-read-only-sites.md). -->\n      <footer class=\"public-footer\" id=\"public-footer\">\n        <a class=\"public-footer-link\" id=\"public-login-link\" href=\"/login\">Log in</a>\n      </footer>\n\n      <div class=\"modal-backdrop\" id=\"phantom-confirm-backdrop\" hidden>\n        <section\n          class=\"phantom-confirm-modal\"\n          role=\"dialog\"\n          aria-modal=\"true\"\n          aria-labelledby=\"phantom-confirm-title\"\n          aria-describedby=\"phantom-confirm-lead\"\n        >\n          <h2 id=\"phantom-confirm-title\">\n            Phantom users are now <span class=\"phantom-confirm-state\" id=\"phantom-confirm-state\">visible</span>\n          </h2>\n          <div class=\"phantom-confirm-body\" id=\"phantom-confirm-body\"></div>\n          <div class=\"phantom-confirm-actions\">\n            <label class=\"phantom-confirm-dismiss\">\n              <input type=\"checkbox\" id=\"phantom-confirm-dismiss\" />\n              Don't show again\n            </label>\n            <button class=\"phantom-confirm-action\" id=\"phantom-confirm-action\" type=\"button\">OK</button>\n          </div>\n        </section>\n      </div>\n    </div>\n    <script type=\"module\" src=\"/apps/_shell-js?blob=true\"></script>\n  <script>\n(() => {\n  document.addEventListener(\"click\", (event) => {\n    if (!(event.target instanceof Element)) return;\n    const anchor = event.target.closest(\"a[href]\");\n    if (!(anchor instanceof HTMLAnchorElement)) return;\n    if (anchor.target && anchor.target !== \"_self\") return;\n    let url;\n    try {\n      url = new URL(anchor.href, window.location.href);\n    } catch {\n      return;\n    }\n    if (url.origin !== window.location.origin) return;\n    if (!url.pathname.startsWith(\"/apps/\")) return;\n    if (window.top && window.top !== window) {\n      event.preventDefault();\n      window.top.location.href = url.href;\n      return;\n    }\n    anchor.target = \"_top\";\n  }, true);\n})();\n</script>\n</body>\n</html>\n","html":"<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Walkable</title>\n    <link\n      rel=\"icon\"\n      type=\"image/svg+xml\"\n      href=\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' fill='none'%3E%3Ccircle cx='31' cy='31' r='4.6' fill='%233F5233' opacity='.32'/%3E%3Ccircle cx='23.5' cy='25' r='4.8' fill='%233F5233' opacity='.55'/%3E%3Ccircle cx='16.5' cy='17.5' r='5' fill='%234D6040' opacity='.8'/%3E%3Ccircle cx='11.5' cy='9.5' r='5.2' fill='%23C4983A'/%3E%3Ccircle cx='9.6' cy='7.2' r='1.5' fill='%23FFF8E7' opacity='.9'/%3E%3C/svg%3E\"\n    />\n    <style>\n:root {\n  --bg: #f5f2ec;\n  --nav-bg: #1f2a2e;\n  --nav-text: #f3efe8;\n  --accent: #e07a5f;\n  --muted: #a9a39b;\n  --shadow: 0 10px 30px rgba(0, 0, 0, 0.12);\n  --menu-shadow: 0 14px 40px rgba(0, 0, 0, 0.28);\n  --nav-height: 56px;\n  font-family: \"Avenir Next\", \"Avenir\", \"Futura\", \"Segoe UI\", sans-serif;\n}\n\n* {\n  box-sizing: border-box;\n}\n\nbody {\n  margin: 0;\n  background: var(--bg);\n  color: #1f2a2e;\n}\n\n.shell {\n  min-height: 100vh;\n  display: flex;\n  flex-direction: column;\n}\n\n/* ---- The frame ---- */\n\n.frame {\n  height: var(--nav-height);\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 0 12px 0 8px;\n  background: var(--nav-bg);\n  color: var(--nav-text);\n  box-shadow: var(--shadow);\n}\n\n.zone {\n  display: flex;\n  align-items: center;\n  min-width: 0;\n}\n\n.zone-left {\n  gap: 4px;\n  flex: 0 0 auto;\n}\n\n.zone-center {\n  flex: 1 1 auto;\n  min-width: 0;\n  justify-content: flex-start;\n}\n\n.zone-right {\n  flex: 0 0 auto;\n  justify-content: flex-end;\n  gap: 4px;\n}\n\n/* ---- Left: waffle + wordmark ---- */\n\n.apps {\n  position: relative;\n}\n\n.waffle {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 36px;\n  height: 36px;\n  border-radius: 8px;\n  color: var(--nav-text);\n  text-decoration: none;\n  font-size: 1.2rem;\n  line-height: 1;\n}\n\n.waffle:hover,\n.waffle[aria-expanded=\"true\"] {\n  background: rgba(255, 255, 255, 0.1);\n}\n\n.wordmark {\n  font-weight: 700;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  font-size: 0.82rem;\n  color: var(--nav-text);\n  text-decoration: none;\n  white-space: nowrap;\n}\n\n/* ---- Center: the location sentence ---- */\n\n.lens {\n  position: relative;\n  display: flex;\n  min-width: 0;\n}\n\n.location {\n  display: inline-flex;\n  align-items: baseline;\n  gap: 8px;\n  min-width: 0;\n}\n\n/* The location is a <button> so it can open the lens (\"open with…\") menu on\n * lens routes; on plain app routes it stays disabled and looks like the old\n * inert sentence. */\n.lens-chip {\n  background: none;\n  border: 0;\n  padding: 4px 8px;\n  margin: -4px -8px;\n  font: inherit;\n  color: inherit;\n  border-radius: 8px;\n  /* Flex item of .lens: natural width normally, shrinkable (the app-name\n   * ellipsizes) when the frame gets tight. NOT max-width:100% — that resolves\n   * circularly against the shrink-wrapped parent and clamps the name. */\n  min-width: 0;\n}\n\n.lens-chip:disabled {\n  cursor: default;\n}\n\n.lens-chip:not(:disabled) {\n  cursor: pointer;\n}\n\n.lens-chip:not(:disabled):hover,\n.lens-chip[aria-expanded=\"true\"] {\n  background: color-mix(in srgb, currentColor 8%, transparent);\n}\n\n.lens-caret {\n  font-size: 0.7rem;\n  align-self: center;\n}\n\n.lens-caret[hidden] {\n  display: none;\n}\n\n.app-icon {\n  display: inline-flex;\n  align-items: center;\n  align-self: center;\n  flex: 0 0 auto;\n  font-size: 1.1rem;\n  line-height: 1;\n}\n\n.app-icon[hidden] {\n  display: none;\n}\n\n.app-icon img {\n  display: block;\n  height: 1.15rem;\n  width: 1.15rem;\n  object-fit: contain;\n  border-radius: 4px;\n}\n\n.app-name {\n  font-size: 1.1rem;\n  font-weight: 600;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.from {\n  font-size: 0.85rem;\n  color: var(--muted);\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  flex: 0 1 auto;\n}\n\n.from[hidden] {\n  display: none;\n}\n\n/* ---- Right: account chip + menu ---- */\n\n.account {\n  position: relative;\n}\n\n.account-chip {\n  display: inline-flex;\n  align-items: center;\n  gap: 8px;\n  background: transparent;\n  border: 1px solid transparent;\n  border-radius: 999px;\n  padding: 4px 10px 4px 4px;\n  color: var(--nav-text);\n  font: inherit;\n  font-size: 0.9rem;\n  cursor: pointer;\n  max-width: 220px;\n}\n\n.account-chip:hover,\n.account-chip[aria-expanded=\"true\"] {\n  background: rgba(255, 255, 255, 0.1);\n  border-color: rgba(255, 255, 255, 0.14);\n}\n\n.account[data-auth-state=\"signed-out\"] .account-chip {\n  border-color: rgba(255, 255, 255, 0.32);\n  padding: 6px 12px;\n  font-weight: 700;\n}\n\n.account[data-auth-state=\"signed-out\"] .account-chip:hover,\n.account[data-auth-state=\"signed-out\"] .account-chip:focus-visible {\n  background: rgba(255, 255, 255, 0.14);\n  border-color: rgba(255, 255, 255, 0.5);\n}\n\n.account[data-auth-state=\"signed-out\"] .avatar,\n.account[data-auth-state=\"signed-out\"] .caret {\n  display: none;\n}\n\n/* Persistent frame toggle for phantom users: shared synthetic accounts whose\n * pages are included or excluded by the user's private cred rating on each\n * host-provided phantom root. */\n.phantom-chip {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  gap: 7px;\n  min-width: 100px;\n  height: 34px;\n  flex: 0 0 auto;\n  background: rgba(226, 226, 226, 0.1);\n  color: rgba(243, 239, 232, 0.62);\n  border: 1px solid rgba(243, 239, 232, 0.22);\n  border-radius: 999px;\n  padding: 0 13px;\n  font: inherit;\n  font-size: 0.78rem;\n  font-weight: 800;\n  text-transform: uppercase;\n  cursor: pointer;\n  opacity: 0.78;\n  position: relative;\n  transition: background 120ms ease, border-color 120ms ease, color 120ms ease, opacity 120ms ease;\n}\n\n.phantom-chip[data-state=\"on\"] {\n  background: #b83f35;\n  color: #fff8f6;\n  border-color: rgba(255, 198, 185, 0.9);\n  opacity: 1;\n}\n\n.phantom-chip[data-busy=\"true\"] {\n  cursor: wait;\n  opacity: 0.68;\n}\n\n.phantom-chip[data-busy=\"true\"] .phantom-chip-short,\n.phantom-chip[data-busy=\"true\"] .phantom-chip-label {\n  visibility: hidden;\n}\n\n.phantom-chip::after {\n  content: \"\";\n  display: none;\n  position: absolute;\n  width: 14px;\n  height: 14px;\n  border: 2px solid currentColor;\n  border-right-color: transparent;\n  border-radius: 999px;\n}\n\n.phantom-chip[data-busy=\"true\"]::after {\n  display: block;\n  animation: phantom-spin 700ms linear infinite;\n}\n\n@keyframes phantom-spin {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .phantom-chip[data-busy=\"true\"]::after {\n    animation: none;\n  }\n}\n\n.phantom-chip:hover,\n.phantom-chip:focus-visible {\n  background: rgba(226, 226, 226, 0.18);\n  border-color: rgba(243, 239, 232, 0.36);\n  color: rgba(243, 239, 232, 0.84);\n}\n\n.phantom-chip[data-state=\"on\"]:hover,\n.phantom-chip[data-state=\"on\"]:focus-visible {\n  background: #cb4b3f;\n  border-color: rgba(255, 220, 212, 0.95);\n  color: #fff;\n}\n\n.phantom-chip-short {\n  display: none;\n  letter-spacing: 0;\n}\n\n.phantom-chip-label {\n  white-space: nowrap;\n}\n\n/* The Phantoms chip's positioning context. */\n.phantom {\n  position: relative;\n  display: inline-flex;\n}\n\n.modal-backdrop {\n  position: fixed;\n  inset: 0;\n  z-index: 100;\n  display: grid;\n  place-items: center;\n  padding: 18px;\n  background: rgba(16, 21, 23, 0.54);\n}\n\n.modal-backdrop[hidden] {\n  display: none;\n}\n\n.phantom-confirm-modal {\n  width: min(420px, 100%);\n  border-radius: 12px;\n  background: #fff;\n  color: #1f2a2e;\n  box-shadow: var(--menu-shadow);\n  padding: 18px;\n  display: grid;\n  gap: 12px;\n}\n\n.phantom-confirm-modal h2 {\n  margin: 0;\n  font-size: 1.12rem;\n  line-height: 1.25;\n}\n\n.phantom-confirm-modal p {\n  margin: 0;\n  color: #5f676b;\n  line-height: 1.45;\n}\n\n.phantom-confirm-body {\n  display: grid;\n  gap: 10px;\n}\n\n/* The toggle word (\"visible\" / \"invisible\") in the modal title, strongly\n   highlighted so the new state reads at a glance. */\n.phantom-confirm-state {\n  font-weight: 800;\n  color: #173a2a;\n  background: #ffe39a;\n  padding: 0 0.28em;\n  border-radius: 4px;\n}\n\n/* The collapsed \"more…\" detail under the lead paragraph. */\n.phantom-confirm-more > summary {\n  cursor: pointer;\n  color: #2e7d5b;\n  font-weight: 600;\n  list-style: none;\n}\n\n.phantom-confirm-more > summary::-webkit-details-marker {\n  display: none;\n}\n\n.phantom-confirm-more[open] > summary {\n  margin-bottom: 8px;\n}\n\n/* `.phantom-confirm-modal p` zeroes margins and only the body's direct children\n   get the grid gap, so stacked paragraphs (the two inside the disclosure) would\n   otherwise run together. Restore the break between consecutive paragraphs. */\n.phantom-confirm-body p + p {\n  margin-top: 0.7em;\n}\n\n.phantom-confirm-actions {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 12px;\n  flex-wrap: wrap;\n}\n\n.phantom-confirm-dismiss {\n  display: inline-flex;\n  align-items: center;\n  gap: 7px;\n  font-size: 0.9rem;\n  color: #5f676b;\n  cursor: pointer;\n}\n\n.phantom-confirm-dismiss input {\n  width: 15px;\n  height: 15px;\n  accent-color: #2e7d5b;\n  cursor: pointer;\n}\n\n.phantom-confirm-action {\n  min-height: 36px;\n  border-radius: 8px;\n  padding: 0 13px;\n  font: inherit;\n  font-weight: 700;\n  cursor: pointer;\n  border: 1px solid #1f2a2e;\n  background: #1f2a2e;\n  color: #fff;\n}\n\n.frame-extension-button {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  gap: 7px;\n  position: relative;\n  min-width: 34px;\n  height: 34px;\n  flex: 0 0 auto;\n  border: 1px solid rgba(185, 213, 188, 0.42);\n  border-radius: 999px;\n  padding: 0 12px;\n  background: rgba(106, 143, 112, 0.22);\n  color: rgba(244, 250, 241, 0.9);\n  font: inherit;\n  font-size: 0.8rem;\n  font-weight: 800;\n  cursor: pointer;\n  transition: background 120ms ease, border-color 120ms ease, color 120ms ease, box-shadow 120ms ease;\n}\n\n.frame-extension-button[hidden] {\n  display: none;\n}\n\n.frame-extension-label {\n  white-space: nowrap;\n}\n\n.frame-extension-icon,\n.frame-extension-icon svg {\n  width: 18px;\n  height: 18px;\n}\n\n.frame-extension-icon {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  color: rgba(218, 240, 214, 0.95);\n}\n\n.frame-extension-icon svg {\n  display: block;\n}\n\n.frame-extension-icon svg {\n  fill: none;\n  stroke: currentColor;\n  stroke-width: 1.9;\n  stroke-linecap: round;\n  stroke-linejoin: round;\n}\n\n.frame-extension-badge {\n  position: absolute;\n  top: -4px;\n  right: -4px;\n  min-width: 18px;\n  height: 18px;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  border: 2px solid var(--nav-bg);\n  border-radius: 999px;\n  padding: 0 4px;\n  background: #8f3d2d;\n  color: #fff;\n  font-size: 0.66rem;\n  font-weight: 800;\n  line-height: 1;\n}\n\n.frame-extension-button:hover,\n.frame-extension-button:focus-visible {\n  background: rgba(126, 162, 130, 0.34);\n  border-color: rgba(214, 237, 209, 0.72);\n  color: #fff;\n}\n\n.frame-extension-button:focus-visible {\n  outline: 2px solid rgba(214, 237, 209, 0.55);\n  outline-offset: 2px;\n}\n\n.account-name {\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.caret {\n  font-size: 0.7rem;\n  opacity: 0.7;\n}\n\n.avatar {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 28px;\n  height: 28px;\n  flex: 0 0 28px;\n  border-radius: 50%;\n  background: var(--accent);\n  color: #fff;\n  font-size: 0.85rem;\n  font-weight: 700;\n  text-transform: uppercase;\n  background-size: cover;\n  background-position: center;\n}\n\n.avatar.lg {\n  width: 40px;\n  height: 40px;\n  flex-basis: 40px;\n  font-size: 1.1rem;\n}\n\n/* ---- Frame dropdowns ---- */\n\n.apps-menu,\n.account-menu,\n.lens-menu {\n  position: absolute;\n  top: calc(100% + 8px);\n  min-width: 260px;\n  max-width: 320px;\n  background: #fff;\n  color: #1f2a2e;\n  border-radius: 12px;\n  box-shadow: var(--menu-shadow);\n  padding: 8px;\n  z-index: 50;\n}\n\n.apps-menu {\n  left: 0;\n  max-height: min(70vh, 520px);\n  overflow-y: auto;\n}\n\n.account-menu {\n  right: 0;\n}\n\n.lens-menu {\n  left: 50%;\n  transform: translateX(-50%);\n}\n\n.apps-menu[hidden],\n.account-menu[hidden],\n.lens-menu[hidden] {\n  display: none;\n}\n\n.account-active {\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 10px 10px 12px;\n}\n\n.account-active-text {\n  min-width: 0;\n}\n\n.account-active-name {\n  font-weight: 600;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.account-active-sub {\n  font-size: 0.8rem;\n  color: #6b6b6b;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n\n.menu-sep {\n  height: 1px;\n  background: #ececec;\n  margin: 6px 4px;\n}\n\n.menu-label {\n  font-size: 0.7rem;\n  letter-spacing: 0.06em;\n  text-transform: uppercase;\n  color: #9a9a9a;\n  padding: 4px 12px;\n}\n\n.menu-item {\n  display: flex;\n  align-items: center;\n  gap: 10px;\n  width: 100%;\n  padding: 9px 12px;\n  border: none;\n  border-radius: 8px;\n  background: transparent;\n  color: inherit;\n  font: inherit;\n  font-size: 0.9rem;\n  text-align: left;\n  text-decoration: none;\n  cursor: pointer;\n}\n\n.menu-item:hover {\n  background: #f3f0ea;\n}\n\n.menu-item[aria-current=\"page\"] {\n  background: #f3f0ea;\n  font-weight: 600;\n}\n\n.menu-item .avatar {\n  width: 26px;\n  height: 26px;\n  flex-basis: 26px;\n  font-size: 0.8rem;\n}\n\n.menu-item .menu-item-text {\n  min-width: 0;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n.menu-item .menu-glyph {\n  display: inline-flex;\n  width: 26px;\n  justify-content: center;\n  opacity: 0.7;\n}\n\n.menu-item.danger {\n  color: #b3261e;\n}\n\n/* \"Coming soon\" rows: visible roadmap, dimmed and non-interactive. */\n.menu-item.is-soon {\n  cursor: default;\n  opacity: 0.5;\n}\n\n.menu-item.is-soon:hover {\n  background: transparent;\n}\n\n.menu-soon-tag {\n  margin-left: auto;\n  flex: 0 0 auto;\n  font-size: 0.62rem;\n  letter-spacing: 0.05em;\n  text-transform: uppercase;\n  color: #8a8a8a;\n  border: 1px solid #e0e0e0;\n  border-radius: 999px;\n  padding: 1px 7px;\n}\n\n/* A switchable account paired with its \"open in a new window\" action.\n   The switch button flex-grows; the icon stays a fixed icon button (#342). */\n.account-other-row {\n  display: flex;\n  align-items: center;\n}\n\n.account-other-row .account-other {\n  flex: 1 1 auto;\n  min-width: 0;\n}\n\n.account-new-window {\n  flex: 0 0 auto;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 30px;\n  height: 30px;\n  margin-right: 6px;\n  border: none;\n  border-radius: 8px;\n  background: transparent;\n  color: inherit;\n  font: inherit;\n  font-size: 0.95rem;\n  opacity: 0.55;\n  cursor: pointer;\n}\n\n.account-new-window:hover {\n  background: #f3f0ea;\n  opacity: 1;\n}\n\n/* Shared role accounts: \"act as a role\" rows + the acting-as band. A role wears\n   a distinct marker so a borrowed identity is never mistaken for one's own. */\n.avatar.role {\n  background: #2f6f6a;\n  color: #fff;\n}\n\n.account-role .menu-item-text {\n  font-style: italic;\n}\n\n/* When the account chip / menu is currently acting as a role, tint its avatar. */\n.account[data-acting-as-role=\"true\"] .avatar {\n  box-shadow: 0 0 0 2px #2f6f6a;\n}\n\n.account-acting-band {\n  padding: 8px 12px;\n  background: #eef5f4;\n  border-radius: 8px;\n  margin: 4px 8px;\n}\n\n.account-acting-note {\n  font-size: 0.8rem;\n  opacity: 0.8;\n  margin-bottom: 6px;\n}\n\n.account-switch-back {\n  color: #2f6f6a;\n  font-weight: 600;\n}\n\n:root[data-theme=\"dark\"] .account-acting-band {\n  background: #1c2b2a;\n}\n\n/* A teaching-band row: primary item + trailing help link. */\n.menu-row {\n  display: flex;\n  align-items: center;\n  gap: 2px;\n}\n\n.menu-row > .menu-item {\n  flex: 1 1 auto;\n  min-width: 0;\n}\n\n.menu-info {\n  flex: 0 0 auto;\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 30px;\n  height: 30px;\n  border-radius: 8px;\n  color: #9a9a9a;\n  text-decoration: none;\n  font-size: 0.95rem;\n}\n\n.menu-info:hover {\n  background: #f3f0ea;\n  color: #1f2a2e;\n}\n\n.primary-button,\n.secondary-button {\n  min-width: 72px;\n  height: 36px;\n  border-radius: 8px;\n  font: inherit;\n  font-size: 0.9rem;\n  cursor: pointer;\n}\n\n.primary-button {\n  border: 1px solid #1f2a2e;\n  background: #1f2a2e;\n  color: #fff;\n}\n\n.secondary-button {\n  border: 1px solid #d7d2ca;\n  background: #fff;\n  color: #1f2a2e;\n}\n\n.primary-button:disabled,\n.secondary-button:disabled {\n  cursor: default;\n  opacity: 0.55;\n}\n\n/* ---- Content ---- */\n\n.content {\n  flex: 1;\n  min-height: 0;\n  position: relative;\n}\n\n/* ---- Public (chromeless) mode ----\n   Public read-only sites (docs/decisions/public-read-only-sites.md) hide the frame\n   for anonymous visitors and offer Login as a slim footer link. The server sets\n   <html data-chrome=\"public\"> in <head> so the frame never flashes pre-paint; the\n   shell sets data-signed-in once auth resolves, restoring full chrome for a\n   signed-in curator. Inert (footer stays display:none) outside public mode. */\n.public-footer {\n  display: none;\n}\nhtml[data-chrome=\"public\"] .frame {\n  display: none;\n}\nhtml[data-chrome=\"public\"] .public-footer {\n  display: flex;\n  justify-content: flex-end;\n  align-items: center;\n  padding: 10px 16px;\n  border-top: 1px solid rgba(31, 42, 46, 0.1);\n  background: var(--bg);\n}\nhtml[data-chrome=\"public\"][data-signed-in=\"true\"] .frame {\n  display: flex;\n}\nhtml[data-chrome=\"public\"][data-signed-in=\"true\"] .public-footer {\n  display: none;\n}\n.public-footer-link {\n  color: var(--muted);\n  font-size: 13px;\n  text-decoration: none;\n}\n.public-footer-link:hover {\n  color: #1f2a2e;\n  text-decoration: underline;\n}\n\n.app-error-banner {\n  position: absolute;\n  z-index: 30;\n  top: 12px;\n  left: 12px;\n  right: 12px;\n  max-width: 880px;\n  margin: 0 auto;\n  display: flex;\n  align-items: flex-start;\n  gap: 14px;\n  padding: 12px;\n  border: 1px solid #d78a5c;\n  border-radius: 10px;\n  background: #fff8f0;\n  color: #31251d;\n  box-shadow: 0 12px 30px rgba(31, 42, 46, 0.18);\n}\n\n/* Trust referral, not a prompt: a quiet note that a shared link asked for a\n * below-horizon lens, pointing at TrustLamp where an informed decision is\n * possible (decisions/lens-switcher-network-tier.md). Deliberately calm —\n * neutral surface, no warning colors: nothing is wrong, something is hidden. */\n.lens-referral-banner {\n  position: absolute;\n  z-index: 29;\n  top: 12px;\n  left: 12px;\n  right: 12px;\n  max-width: 880px;\n  margin: 0 auto;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 10px 14px;\n  border: 1px solid #d5d5d0;\n  border-radius: 10px;\n  background: #fbfbf8;\n  color: #31353a;\n  font-size: 0.9rem;\n  box-shadow: 0 8px 22px rgba(31, 42, 46, 0.12);\n}\n\n.lens-referral-banner[hidden] {\n  display: none;\n}\n\n.lens-referral-text {\n  min-width: 0;\n}\n\n.lens-referral-link {\n  white-space: nowrap;\n  color: inherit;\n  font-weight: 600;\n}\n\n.lens-referral-dismiss {\n  margin-left: auto;\n  background: none;\n  border: 0;\n  cursor: pointer;\n  color: inherit;\n  font-size: 0.9rem;\n  padding: 4px;\n}\n\n:root[data-theme=\"dark\"] .lens-referral-banner {\n  background: var(--surface);\n  color: var(--on-surface);\n  border-color: rgba(255, 255, 255, 0.16);\n}\n\n/* Mirror-on-demand fetch progress: shown while the host caches a publisher app\n   the user just opened (decision app-references-are-urls-invisible-cache §4). */\n.mirror-fetch-banner {\n  position: absolute;\n  z-index: 29;\n  top: 12px;\n  left: 12px;\n  right: 12px;\n  max-width: 880px;\n  margin: 0 auto;\n  display: flex;\n  align-items: center;\n  gap: 12px;\n  padding: 10px 14px;\n  border: 1px solid #d5d5d0;\n  border-radius: 10px;\n  background: #fbfbf8;\n  color: #31353a;\n  font-size: 0.9rem;\n  box-shadow: 0 8px 22px rgba(31, 42, 46, 0.12);\n}\n\n.mirror-fetch-banner[hidden] {\n  display: none;\n}\n\n.mirror-fetch-spinner {\n  flex: 0 0 auto;\n  width: 14px;\n  height: 14px;\n  border: 2px solid #d5d5d0;\n  border-top-color: #31353a;\n  border-radius: 50%;\n  animation: mirror-fetch-spin 0.9s linear infinite;\n}\n\n.mirror-fetch-banner.mirror-fetch-error .mirror-fetch-spinner {\n  display: none;\n}\n\n@keyframes mirror-fetch-spin {\n  to { transform: rotate(360deg); }\n}\n\n:root[data-theme=\"dark\"] .mirror-fetch-banner {\n  background: var(--surface);\n  color: var(--on-surface);\n  border-color: rgba(255, 255, 255, 0.16);\n}\n\n:root[data-theme=\"dark\"] .mirror-fetch-spinner {\n  border-color: rgba(255, 255, 255, 0.24);\n  border-top-color: var(--on-surface);\n}\n\n.app-error-banner[hidden] {\n  display: none;\n}\n\n.app-error-copy {\n  flex: 1 1 auto;\n  min-width: 0;\n}\n\n.app-error-title {\n  font-weight: 800;\n  font-size: 0.95rem;\n  line-height: 1.25;\n}\n\n.app-error-message {\n  margin-top: 3px;\n  color: #5f4937;\n  font-size: 0.88rem;\n  line-height: 1.35;\n}\n\n.app-error-details {\n  margin-top: 6px;\n  color: #71563e;\n  font-size: 0.78rem;\n}\n\n.app-error-details summary {\n  width: fit-content;\n  cursor: pointer;\n  font-weight: 700;\n}\n\n.app-error-details p {\n  margin: 5px 0 0;\n  overflow-wrap: anywhere;\n}\n\n.app-error-actions {\n  display: flex;\n  flex: 0 0 auto;\n  gap: 8px;\n  align-items: center;\n}\n\n.app-error-reload,\n.app-error-dismiss {\n  min-height: 34px;\n  border-radius: 8px;\n  padding: 0 11px;\n  font: inherit;\n  font-size: 0.82rem;\n  font-weight: 800;\n  cursor: pointer;\n  white-space: nowrap;\n}\n\n.app-error-reload {\n  border: 1px solid #1f2a2e;\n  background: #1f2a2e;\n  color: #fff;\n}\n\n.app-error-dismiss {\n  border: 1px solid #d9b98e;\n  background: #fff;\n  color: #5f4937;\n}\n\n#app-frame {\n  width: 100%;\n  height: calc(100vh - var(--nav-height));\n  border: none;\n  display: block;\n  background: #fff;\n}\n\n/* ---- Mobile: stay a single row; shed the least-needed bits first ---- */\n\n@media (max-width: 600px) {\n  .wordmark {\n    display: none;\n  }\n  .account-name,\n  .caret {\n    display: none;\n  }\n  .account-chip {\n    padding: 4px;\n  }\n  .phantom-chip {\n    min-width: 34px;\n    width: 34px;\n    padding: 0;\n  }\n  .phantom-chip-short {\n    display: inline;\n  }\n  .phantom-chip-label {\n    display: none;\n  }\n  .frame-extension-label {\n    display: none;\n  }\n  .frame-extension-button {\n    width: 34px;\n    padding: 0;\n  }\n  .app-name {\n    font-size: 1rem;\n  }\n  .app-error-banner {\n    top: 8px;\n    left: 8px;\n    right: 8px;\n    flex-direction: column;\n    gap: 10px;\n  }\n  .app-error-actions {\n    width: 100%;\n  }\n  .app-error-reload,\n  .app-error-dismiss {\n    flex: 1 1 0;\n  }\n}\n\n/* ---- Dark theme ----\n   Applied when the shell sets <html data-theme=\"dark\"> from the reader's account\n   preference (app.ts applyTheme; pre-painted by the inline <head> script). The nav\n   bar is already dark in both themes; these rules darken the remaining light\n   surfaces — the popover menus, the Phantoms modal, buttons, the app-load backdrop,\n   and the data-error banner — so the whole shell, including the modal, follows the\n   setting. Light mode is the default and left untouched. */\n:root[data-theme=\"dark\"] {\n  --bg: #15191b;\n  --muted: #8b9398;\n  --shadow: 0 10px 30px rgba(0, 0, 0, 0.45);\n  --menu-shadow: 0 14px 40px rgba(0, 0, 0, 0.6);\n  --surface: #1f262a;\n  --surface-2: #283034;\n  --on-surface: #e9eef0;\n  --on-surface-muted: #9aa3a8;\n  --border-soft: #323b40;\n  --accent-green: #2e7d5b;\n  --accent-green-border: #3c8f6a;\n}\n\n:root[data-theme=\"dark\"] body {\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .apps-menu,\n:root[data-theme=\"dark\"] .account-menu,\n:root[data-theme=\"dark\"] .lens-menu {\n  background: var(--surface);\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .account-active-sub,\n:root[data-theme=\"dark\"] .menu-label,\n:root[data-theme=\"dark\"] .menu-info {\n  color: var(--on-surface-muted);\n}\n\n:root[data-theme=\"dark\"] .menu-sep {\n  background: var(--border-soft);\n}\n\n:root[data-theme=\"dark\"] .menu-item:hover,\n:root[data-theme=\"dark\"] .menu-item[aria-current=\"page\"],\n:root[data-theme=\"dark\"] .account-new-window:hover {\n  background: var(--surface-2);\n}\n\n:root[data-theme=\"dark\"] .menu-info:hover {\n  background: var(--surface-2);\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .menu-item.danger {\n  color: #f08a82;\n}\n\n:root[data-theme=\"dark\"] .menu-soon-tag {\n  color: var(--on-surface-muted);\n  border-color: var(--border-soft);\n}\n\n:root[data-theme=\"dark\"] .secondary-button {\n  background: var(--surface-2);\n  border-color: var(--border-soft);\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .primary-button {\n  border-color: var(--accent-green-border);\n  background: var(--accent-green);\n  color: #fff;\n}\n\n/* The Phantoms modal follows the theme. */\n:root[data-theme=\"dark\"] .phantom-confirm-modal {\n  background: var(--surface);\n  color: var(--on-surface);\n}\n\n:root[data-theme=\"dark\"] .phantom-confirm-modal p {\n  color: var(--on-surface-muted);\n}\n\n:root[data-theme=\"dark\"] .phantom-confirm-more > summary {\n  color: #6fc69b;\n}\n\n:root[data-theme=\"dark\"] .phantom-confirm-dismiss {\n  color: var(--on-surface-muted);\n}\n\n:root[data-theme=\"dark\"] .phantom-confirm-action {\n  border-color: var(--accent-green-border);\n  background: var(--accent-green);\n  color: #fff;\n}\n\n/* The toggle-state highlight stays a warm chip; darken its text for contrast. */\n:root[data-theme=\"dark\"] .phantom-confirm-state {\n  color: #1a2e22;\n}\n\n:root[data-theme=\"dark\"] #app-frame {\n  background: var(--bg);\n}\n\n/* Data-error banner: warm-dark amber instead of cream. */\n:root[data-theme=\"dark\"] .app-error-banner {\n  background: #2a211a;\n  border-color: #8a5a36;\n  color: #f0e3d6;\n}\n\n:root[data-theme=\"dark\"] .app-error-message {\n  color: #d8c2ab;\n}\n\n:root[data-theme=\"dark\"] .app-error-details {\n  color: #c9b196;\n}\n\n:root[data-theme=\"dark\"] .app-error-reload {\n  border-color: var(--accent-green-border);\n  background: var(--accent-green);\n}\n\n:root[data-theme=\"dark\"] .app-error-dismiss {\n  background: var(--surface-2);\n  border-color: #8a5a36;\n  color: #f0e3d6;\n}\n\n:root[data-theme=\"dark\"] .public-footer-link:hover {\n  color: var(--on-surface);\n}\n\n</style><script>\n      // Pre-paint theme: apply the cached resolved scheme (or the OS preference) so a\n      // returning reader's dark mode never flashes light before app.js loads the\n      // authoritative account preference and reconciles. Mirror of THEME_PAINT_HINT_KEY\n      // / applyTheme in app.ts.\n      (function () {\n        try {\n          var hint = localStorage.getItem(\"wkbl.themeResolved\");\n          var resolved =\n            hint === \"light\" || hint === \"dark\"\n              ? hint\n              : window.matchMedia && window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n                ? \"dark\"\n                : \"light\";\n          document.documentElement.dataset.theme = resolved;\n        } catch (e) {}\n      })();\n    </script>\n  </head>\n  <body>\n    <div class=\"shell\">\n      <header class=\"frame\">\n        <div class=\"zone zone-left\">\n          <div class=\"apps\" id=\"apps\">\n            <a\n              class=\"waffle\"\n              id=\"waffle\"\n              href=\"/\"\n              title=\"Apps\"\n              aria-label=\"Apps\"\n              aria-haspopup=\"menu\"\n              aria-expanded=\"false\"\n              aria-controls=\"apps-menu\"\n            >\n              <span aria-hidden=\"true\">☰</span>\n            </a>\n            <div class=\"apps-menu\" id=\"apps-menu\" role=\"menu\" hidden></div>\n          </div>\n          <a class=\"wordmark\" id=\"home-link\" href=\"/\">Walkable</a>\n        </div>\n\n        <div class=\"zone zone-center\">\n          <div class=\"lens\" id=\"lens\">\n            <button\n              class=\"location lens-chip\"\n              id=\"lens-chip\"\n              type=\"button\"\n              disabled\n              aria-haspopup=\"menu\"\n              aria-expanded=\"false\"\n              aria-controls=\"lens-menu\"\n            >\n              <span class=\"app-icon\" id=\"app-icon\" aria-hidden=\"true\" hidden></span>\n              <span class=\"app-name\" id=\"app-name\">App</span>\n              <span class=\"from\" id=\"from-owner\" hidden></span>\n              <span class=\"caret lens-caret\" id=\"lens-caret\" aria-hidden=\"true\" hidden>▾</span>\n            </button>\n            <div class=\"lens-menu\" id=\"lens-menu\" role=\"menu\" hidden></div>\n          </div>\n        </div>\n\n        <div class=\"zone zone-right\">\n          <div class=\"phantom\" id=\"phantom\">\n            <button\n              class=\"phantom-chip\"\n              id=\"phantom-chip\"\n              type=\"button\"\n              title=\"Phantom users are hidden\"\n              aria-pressed=\"false\"\n              data-state=\"off\"\n            >\n              <span class=\"phantom-chip-short\" aria-hidden=\"true\">Ph</span>\n              <span class=\"phantom-chip-label\">Phantoms</span>\n            </button>\n          </div>\n          <div class=\"account\" id=\"account\" data-auth-state=\"signed-out\">\n            <button\n              class=\"account-chip\"\n              id=\"account-chip\"\n              aria-haspopup=\"false\"\n              aria-expanded=\"false\"\n              aria-label=\"Log in or sign up\"\n            >\n              <span class=\"avatar\" id=\"account-avatar\" aria-hidden=\"true\">·</span>\n              <span class=\"account-name\" id=\"user-label\">Log in / Sign up</span>\n              <span class=\"caret\" aria-hidden=\"true\">▾</span>\n            </button>\n            <div class=\"account-menu\" id=\"account-menu\" role=\"menu\" hidden></div>\n          </div>\n        </div>\n      </header>\n\n      <main class=\"content\">\n        <div class=\"lens-referral-banner\" id=\"lens-referral-banner\" role=\"status\" aria-live=\"polite\" hidden>\n          <span class=\"lens-referral-text\" id=\"lens-referral-text\"></span>\n          <a class=\"lens-referral-link\" id=\"lens-referral-link\" href=\"/apps/app-trustlamp\">See who trusts them</a>\n          <button class=\"lens-referral-dismiss\" id=\"lens-referral-dismiss\" type=\"button\" aria-label=\"Dismiss\">✕</button>\n        </div>\n        <div class=\"mirror-fetch-banner\" id=\"mirror-fetch-banner\" role=\"status\" aria-live=\"polite\" hidden>\n          <span class=\"mirror-fetch-spinner\" id=\"mirror-fetch-spinner\" aria-hidden=\"true\"></span>\n          <span class=\"mirror-fetch-text\" id=\"mirror-fetch-text\"></span>\n        </div>\n        <div class=\"app-error-banner\" id=\"app-error-banner\" role=\"status\" aria-live=\"polite\" hidden>\n          <div class=\"app-error-copy\">\n            <div class=\"app-error-title\" id=\"app-error-title\">This app could not load data</div>\n            <div class=\"app-error-message\" id=\"app-error-message\"></div>\n            <details class=\"app-error-details\">\n              <summary>Details</summary>\n              <p id=\"app-error-detail\"></p>\n            </details>\n          </div>\n          <div class=\"app-error-actions\">\n            <button class=\"app-error-reload\" id=\"app-error-reload\" type=\"button\">Reload app</button>\n            <button class=\"app-error-dismiss\" id=\"app-error-dismiss\" type=\"button\" aria-label=\"Dismiss app data warning\">Dismiss</button>\n          </div>\n        </div>\n        <iframe\n          id=\"app-frame\"\n          sandbox=\"allow-forms allow-scripts allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation\"\n          title=\"App\"\n        ></iframe>\n      </main>\n\n      <!-- Public read-only sites: the only chrome an anonymous visitor sees. Hidden\n           by default; shown by CSS only under <html data-chrome=\"public\"> while\n           signed out (see docs/decisions/public-read-only-sites.md). -->\n      <footer class=\"public-footer\" id=\"public-footer\">\n        <a class=\"public-footer-link\" id=\"public-login-link\" href=\"/login\">Log in</a>\n      </footer>\n\n      <div class=\"modal-backdrop\" id=\"phantom-confirm-backdrop\" hidden>\n        <section\n          class=\"phantom-confirm-modal\"\n          role=\"dialog\"\n          aria-modal=\"true\"\n          aria-labelledby=\"phantom-confirm-title\"\n          aria-describedby=\"phantom-confirm-lead\"\n        >\n          <h2 id=\"phantom-confirm-title\">\n            Phantom users are now <span class=\"phantom-confirm-state\" id=\"phantom-confirm-state\">visible</span>\n          </h2>\n          <div class=\"phantom-confirm-body\" id=\"phantom-confirm-body\"></div>\n          <div class=\"phantom-confirm-actions\">\n            <label class=\"phantom-confirm-dismiss\">\n              <input type=\"checkbox\" id=\"phantom-confirm-dismiss\" />\n              Don't show again\n            </label>\n            <button class=\"phantom-confirm-action\" id=\"phantom-confirm-action\" type=\"button\">OK</button>\n          </div>\n        </section>\n      </div>\n    </div>\n    <script type=\"module\" src=\"/apps/_shell-js?blob=true\"></script>\n  <script>\n(() => {\n  document.addEventListener(\"click\", (event) => {\n    if (!(event.target instanceof Element)) return;\n    const anchor = event.target.closest(\"a[href]\");\n    if (!(anchor instanceof HTMLAnchorElement)) return;\n    if (anchor.target && anchor.target !== \"_self\") return;\n    let url;\n    try {\n      url = new URL(anchor.href, window.location.href);\n    } catch {\n      return;\n    }\n    if (url.origin !== window.location.origin) return;\n    if (!url.pathname.startsWith(\"/apps/\")) return;\n    if (window.top && window.top !== window) {\n      event.preventDefault();\n      window.top.location.href = url.href;\n      return;\n    }\n    anchor.target = \"_top\";\n  }, true);\n})();\n</script>\n</body>\n</html>\n"}