/* squeez.in home. Ink, starlight, paper, and one disc colour per black hole
   (--disc, set by home.js). Two families, self-hosted (squeezin.css): Funnel Display for display type and Instrument Sans for text.
   Geometry matches blackhole.js: the hole's centre and outer radius are
   --hx, --hy and --hr (px, set on <html> by the renderer; these are the fallbacks). */

:root {
  --night: #0E0C27;
  --dust: #2B2766;
  --field: #17143B;
  --starlight: #B9C3F2;
  --paper: #F4EEE2;
  --warm: #D7E4FF;
  --bad: #FFB3C6;
  --good: #C9F2D6;
  --disc: #BFD0FF;
  --disc-hot: #E9EEFF;
  --hx: 74vw;
  --hy: 47vh;
  --hr: min(27vw, 36vh);
  --display: 'Funnel Display', 'Instrument Sans', system-ui, sans-serif;
  --radius: 14px;
  --gutter: 6vw;
  color-scheme: dark;
}
@media (max-width: 899px) {
  :root { --hx: 50vw; --hy: 20vh; --hr: min(62vw, 27vh); --gutter: 16px; }
}

html, body { background: var(--night); color: var(--paper); }
body.hole-page {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  overflow-x: hidden;
  font-family: 'Instrument Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 17px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Layers: the lensing canvas, the orbit and squeeze canvas, then the page. */
#sky { position: fixed; inset: 0; width: 100%; height: 100%; display: block; z-index: 0; transition: opacity 0.35s ease; }
/* Behind the console, the result and the Preview (z-index 2): the destination letters must never cover text or controls. */
#fx { position: fixed; inset: 0; width: 100%; height: 100%; display: block; z-index: 1; pointer-events: none; }
.skip { position: absolute; left: 8px; top: -60px; z-index: 10; background: var(--paper); color: #10102A; padding: 10px 14px; border-radius: var(--radius); }
.skip:focus { top: 8px; }

:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; border-radius: 6px; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* The shorten card: centred in the viewport (both ways) on every width. It is a solid ink panel, so the
   text stays readable over the moving hole and the stars behind it. */
/* The console: no box. Its content floats over space, on a soft dark vignette (.h-copy::before) that
   keeps the body text readable over the bright disc. Four HUD corner brackets (.h-copy::after). */
.h-copy, .h-result {
  --hud: rgba(191, 208, 255, 0.55);
  position: absolute; z-index: 2;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(600px, calc(100vw - 2 * var(--gutter)));
  box-sizing: border-box; padding: 36px 40px 34px;
  background: none; border: 0; border-radius: 0; box-shadow: none;
}
/* The console itself runs twice as wide as the result card used to: the headline sits on one line
   and the command bar gets room for long links. */
.h-copy { width: min(1200px, calc(100vw - 2 * var(--gutter))); }
.h-copy::before, .h-result::before {
  content: ""; position: absolute; inset: -56px -80px; z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side, rgba(14, 12, 39, 0.9) 0%, rgba(14, 12, 39, 0.84) 58%, rgba(14, 12, 39, 0) 100%);
}
.h-copy::after, .h-result::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(var(--hud), var(--hud)) left top / 22px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) left top / 1.5px 22px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right top / 22px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right top / 1.5px 22px no-repeat,
    linear-gradient(var(--hud), var(--hud)) left bottom / 22px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) left bottom / 1.5px 22px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right bottom / 22px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right bottom / 1.5px 22px no-repeat;
}
/* Top row of the console: the id label on the left, the live scan chip on the right. */
.h-hud-top { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 12px 16px; margin: 0 0 26px; }
.h-hud-id { margin: 0; font: 500 11px/1.4 var(--mono); letter-spacing: 0.16em; color: var(--starlight); }
.h-status-chip {
  margin: 0; display: inline-flex; align-items: center; gap: 10px; max-width: 100%;
  padding: 6px 14px 6px 11px; border: 1px solid rgba(201, 242, 214, 0.34); border-radius: 999px;
  background: rgba(14, 12, 39, 0.5);
}
.h-status-dot {
  flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--good);
  box-shadow: 0 0 0 0 rgba(201, 242, 214, 0.6); animation: h-pulse 2.2s ease-out infinite;
}
@keyframes h-pulse {
  0% { box-shadow: 0 0 0 0 rgba(201, 242, 214, 0.6); }
  70%, 100% { box-shadow: 0 0 0 7px rgba(201, 242, 214, 0); }
}
.h-status-text { display: flex; flex-direction: column; min-width: 0; }
.h-status-main { font: 600 11px/1.3 var(--mono); letter-spacing: 0.14em; color: var(--good); }
.h-status-sub { font: 400 11px/1.4 var(--mono); letter-spacing: 0.03em; color: var(--starlight); }

/* The headline: a slow light sweep across the letters. The solid colour is the fallback. */
.h-title {
  font-family: var(--display);
  margin: 0 0 16px;
  font-weight: 800; font-stretch: 125%;
  font-size: clamp(40px, 5.6vw, 80px);
  line-height: 0.96; letter-spacing: -0.03em;
  white-space: normal;
  color: var(--paper);
}
@supports ((background-clip: text) or (-webkit-background-clip: text)) {
  .h-title {
    color: transparent; -webkit-text-fill-color: transparent;
    background-image: linear-gradient(100deg, #F4EEE2 0%, #F4F8FF 28%, #BFD0FF 44%, #8F7CFF 52%, #F4F8FF 62%, #F4EEE2 80%);
    background-size: 240% 100%;
    background-position: 100% 0;
    -webkit-background-clip: text; background-clip: text;
    animation: h-sweep 9s ease-in-out infinite;
  }
}
@keyframes h-sweep { from { background-position: 100% 0; } to { background-position: -100% 0; } }
/* The small mono tagline: FREE · NO ADS · NO SIGNUP · NEVER SOLD. */
.h-tagline { margin: 0 0 30px; font: 500 12px/1.5 var(--mono); letter-spacing: 0.2em; text-transform: uppercase; color: var(--starlight); }
.h-sep { margin: 0 0.7em; color: var(--disc); }

.h-field-label {
  display: block; margin: 0 0 10px; font: 500 11px/1.4 var(--mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--starlight);
}
/* The command bar: one pill holding the input, Paste and Shorten. The rim is a rotating conic gradient
   (--rim-a) behind an ink fill, so the outline shimmers without any border box. */
.h-bar {
  position: relative; isolation: isolate;
  display: flex; gap: 0; align-items: center;
  min-height: 68px; padding: 0 6px 0 0; border-radius: 34px;
  transition: box-shadow 0.35s ease;
}
.h-bar::before, .h-bar::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; }
.h-bar::before {
  z-index: -2; opacity: 0.6; transition: opacity 0.35s ease;
  background: conic-gradient(from var(--rim-a), #BFD0FF, #8F7CFF, #F4F8FF, #8F7CFF, #BFD0FF);
  animation: h-rim 8s linear infinite;
}
.h-bar::after { z-index: -1; inset: 1.5px; background: var(--field); }
.h-bar:focus-within { box-shadow: 0 0 30px rgba(143, 124, 255, 0.35); }
.h-bar:focus-within::before { opacity: 1; }
.h-bar:has(input:focus-visible) { outline: 2px solid var(--paper); outline-offset: 4px; }
.h-bar:has(input[aria-invalid="true"])::before { background: var(--bad); animation: none; }
@keyframes h-rim { to { --rim-a: 360deg; } }
.h-field { position: relative; flex: 1; min-width: 0; align-self: stretch; display: flex; align-items: center; }
.h-field input {
  width: 100%; box-sizing: border-box; height: 66px;
  padding: 0 16px 0 26px;
  background: transparent; color: var(--paper); border: 0; border-radius: 34px 0 0 34px;
  font: 500 20px/1 'Instrument Sans', system-ui, sans-serif; letter-spacing: -0.005em;
}
.h-field input:focus { outline: none; box-shadow: none; }
.h-field input::placeholder { color: #8C94C7; opacity: 1; }
/* Room for the Paste button only while it shows (updatePaste in home.js). */
.h-field.has-paste input { padding-right: 118px; }
@media (min-width: 900px) {
  .h-go { min-width: 168px; padding: 0 20px; }
  /* A short window: the card scrolls inside itself rather than running off the top and bottom. */
  .h-copy, .h-result { max-height: calc(100vh - 32px); overflow-y: auto; overscroll-behavior: contain; }
}
.is-squeezing .h-field input { color: transparent; }
/* The warp canvas (assets/warps): an iris opens out of the hole (--wx, --wy, set by home.js), slow
   then fast, and the warp takes the screen. At the end the canvas turns transparent around the
   arriving link, so the page shows through before the canvas goes. Plain (reduced motion, no
   WebGL 2): a fade to ink and back. */
.h-warp {
  position: fixed; inset: 0; width: 100%; height: 100%; display: block; z-index: 4; pointer-events: none;
  clip-path: circle(0px at var(--wx, 50%) var(--wy, 50%));
  transition: clip-path 0.55s cubic-bezier(0.7, 0, 0.84, 0);
}
.h-warp.on { clip-path: circle(150vmax at var(--wx, 50%) var(--wy, 50%)); }
.h-warp.h-warp-plain { clip-path: none; background: var(--night); opacity: 0; transition: opacity 0.4s ease; }
.h-warp.h-warp-plain.on { opacity: 1; }
/* The black hole's squeeze (warp 1): once the link's characters have left the field, the copy
   and caption step back so the fall reads at full scale. They return if the squeeze fails. */
body.is-squeezing .h-copy { opacity: 0.12; transition: opacity 0.6s ease 0.45s; }
body.is-squeezing .h-caption { opacity: 0; transition: opacity 0.4s ease 0.3s; }
.h-copy, .h-caption { transition: opacity 0.35s ease; }
/* The arrival (home.js): the result is laid out but hidden while the warp brings the short link to
   its place; then the rest of the card settles in around it, one line after another. */
body.fx-result .h-result { animation: none; }
body.is-arriving .h-result > * { opacity: 0; transform: translateY(14px); }
body.is-arriving .h-result > .h-short { transform: none; }
body.is-landing .h-result > :not(.h-short) { transition: opacity 0.55s cubic-bezier(.2,.7,.2,1), transform 0.7s cubic-bezier(.16,1,.3,1); }
body.is-landing .h-result > :nth-child(1) { transition-delay: 0.04s; }
body.is-landing .h-result > :nth-child(3) { transition-delay: 0.1s; }
body.is-landing .h-result > :nth-child(4) { transition-delay: 0.16s; }
body.is-landing .h-result > :nth-child(5) { transition-delay: 0.22s; }
body.is-landing .h-result > :nth-child(n+6) { transition-delay: 0.3s; }
body.is-arriving.is-landing .h-result > :not(.h-short) { opacity: 1; transform: none; }
.h-paste {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%); height: 44px; padding: 0 14px 0 12px; overflow: hidden; isolation: isolate;
  display: inline-flex; align-items: center; gap: 8px;
  background: transparent; color: var(--paper); border: 1px solid rgba(185, 195, 242, 0.5); border-radius: 10px;
  font: 600 16px/1 'Instrument Sans', system-ui, sans-serif; cursor: pointer;
}
.h-paste-ico { flex: none; }
.h-paste:focus-visible { outline: 2px solid var(--disc); outline-offset: 2px; }
.h-paste::before {
  content: ""; position: absolute; inset: 0; z-index: -1; background: var(--paper);
  clip-path: circle(0% at 50% 50%); transition: clip-path 0.5s cubic-bezier(.2,.7,.2,1);
}
@media (hover: hover) { .h-paste:hover { color: #10102A; } .h-paste:hover::before { clip-path: circle(150% at 50% 50%); } }
.h-paste:focus-visible { color: #10102A; }
.h-paste:focus-visible::before { clip-path: circle(150% at 50% 50%); }

/* Primary: fills with paper from the centre on hover and focus (the disc colour is the resting state). */
.h-go {
  position: relative; isolation: isolate; overflow: hidden;
  height: 68px; min-width: 200px; padding: 0 28px;
  background: var(--disc); color: #10102A; border: 0; border-radius: var(--radius);
  font: 700 19px/1 'Instrument Sans', system-ui, sans-serif; letter-spacing: -0.005em; cursor: pointer;
}
.h-go::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--paper); clip-path: circle(0% at 50% 50%);
  transition: clip-path 0.5s cubic-bezier(.2,.7,.2,1);
}
@media (hover: hover) { .h-go:hover::before { clip-path: circle(150% at 50% 50%); } }
.h-go:focus-visible::before, .h-go:active::before { clip-path: circle(150% at 50% 50%); }
.h-go:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; }
.h-go:active { transform: scale(0.98); }
/* Disabled (the squeeze is running): a muted surface with starlight text, 8:1 on the dust surface. */
.h-go:disabled { cursor: progress; opacity: 1; background: var(--dust); color: var(--starlight); box-shadow: inset 0 0 0 1px rgba(185, 195, 242, 0.35); }
.h-go:disabled::before { display: none; }
.h-go[aria-busy="true"]::after {
  content: ""; position: absolute; right: 22px; top: 50%; width: 16px; height: 16px; margin-top: -8px;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: h-spin 0.8s linear infinite;
}
@keyframes h-spin { to { transform: rotate(360deg); } }
.h-go[aria-busy="true"]::before { clip-path: circle(0% at 50% 50%); }

/* The human check: its own row under the form, left-aligned with the input. It takes height only while
   Cloudflare asks for a click (before/after-interactive-callback in home.js), and never overlaps the form. */
.h-captcha { position: relative; z-index: 1; display: block; width: 100%; margin: 0; }
.h-captcha.is-interactive { min-height: 70px; margin-top: 14px; }
.h-captcha iframe { display: block; position: static !important; max-width: 100%; }

.h-msg { min-height: 26px; margin: 0; font-size: 15px; line-height: 1.45; color: var(--starlight); max-width: 56ch; }
.h-msg.h-hint { opacity: 0.92; }
.h-msg.h-ok { color: var(--good); }
.h-msg.h-err { color: var(--bad); }
.h-msg.h-warn { color: var(--warm); }
.h-msg.h-hint { color: var(--starlight); }
.h-msg-btn {
  background: none; border: 0; padding: 0 0 2px; color: var(--paper); font: inherit; cursor: pointer;
  text-decoration: underline; text-underline-offset: 4px; min-height: 44px;
}

/* Options: a panel under the bar on a wide screen; a sheet from the bottom on a phone. */
.h-opts { margin-top: 6px; }
.h-opt-toggle {
  background: linear-gradient(rgba(185, 195, 242, 0.22), rgba(185, 195, 242, 0.22)) 0 100% / 100% 1px no-repeat;
  border: 0; padding: 10px 0; min-height: 44px; color: var(--paper); font: 600 16px/1 'Instrument Sans', system-ui, sans-serif; cursor: pointer;
  transition: background-size 0.35s cubic-bezier(.2,.7,.2,1), color 0.2s;
}
@media (hover: hover) { .h-opt-toggle:hover { background-size: 100% 0.45em; color: var(--night); } }
.h-opt-toggle:focus-visible { background-size: 100% 0.45em; color: var(--night); }
.h-opt-toggle::after { content: ""; display: inline-block; width: 7px; height: 7px; margin-left: 10px; vertical-align: 3px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); }
.h-opt-toggle[aria-expanded="true"]::after { transform: rotate(-135deg); vertical-align: -1px; }
.h-options {
  margin-top: 10px; padding: 20px; box-sizing: border-box; width: 100%;
  background: rgba(43, 39, 102, 0.72); border: 1px solid rgba(185, 195, 242, 0.22); border-radius: 18px;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.h-options[hidden] { display: none; }
.h-opt { margin-bottom: 14px; }
.h-label { display: block; font-weight: 700; font-size: 16px; margin: 0 0 8px; }
fieldset.h-expiry-set { border: 0; margin: 0 0 14px; padding: 0; min-width: 0; }
fieldset.h-expiry-set legend { padding: 0; }
.h-ending { display: flex; align-items: center; background: var(--night); border: 1.5px solid rgba(185, 195, 242, 0.3); border-radius: var(--radius); height: 52px; }
.h-ending span { padding-left: 14px; color: var(--starlight); font-size: 16px; white-space: nowrap; }
.h-ending input { flex: 1; min-width: 0; height: 100%; background: transparent; border: 0; color: var(--paper); padding: 0 12px 0 2px; font: 500 18px 'Instrument Sans', system-ui, sans-serif; }
.h-ending input:focus { outline: none; }
.h-ending:focus-within { border-color: var(--paper); }
.h-field-msg { min-height: 22px; margin: 6px 0 0; font-size: 15px; color: var(--starlight); }
.h-field-msg.h-ok { color: var(--good); }
.h-field-msg.h-err { color: var(--bad); }
.h-radios { display: flex; gap: 4px; padding: 4px; border: 1.5px solid rgba(185, 195, 242, 0.3); border-radius: 14px; background: var(--night); }
.h-radios button {
  flex: 1 1 0; min-width: 0; min-height: 44px; padding: 0 10px; border-radius: 10px; cursor: pointer; border: 0;
  background: transparent; color: var(--paper); font: 600 15px 'Instrument Sans', system-ui, sans-serif; white-space: nowrap;
}
.h-radios button:hover { background: rgba(185, 195, 242, 0.14); }
.h-radios button:focus-visible { outline: 2px solid var(--disc); outline-offset: 2px; }
.h-radios button[aria-checked="true"] { background: var(--disc); color: #10102A; }
.h-easy { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.h-easy p { margin: 0; font-size: 16px; }
/* The switch's hit area is 44px tall; the track is drawn inside it. */
.h-switch {
  position: relative; flex: none; width: 64px; height: 44px; cursor: pointer; padding: 0; background: transparent; border: 0;
}
.h-switch::before {
  content: ""; position: absolute; left: 4px; right: 4px; top: 10px; bottom: 10px; border-radius: 999px;
  background: rgba(185, 195, 242, 0.25); border: 1.5px solid rgba(185, 195, 242, 0.5); box-sizing: border-box;
}
.h-switch[aria-checked="true"]::before { background: var(--disc); border-color: var(--disc); }
.h-switch:focus-visible { outline: 2px solid var(--disc); outline-offset: 2px; border-radius: 999px; }
.h-switch span { position: absolute; top: 14px; left: 10px; width: 16px; height: 16px; border-radius: 50%; background: var(--starlight); transition: transform 0.25s cubic-bezier(.2,.7,.2,1); }
.h-switch[aria-checked="true"] span { transform: translateX(24px); background: var(--night); }

/* Result: the same column, with the link the API returned. */
.h-result { display: none; }
body[data-phase="result"] .h-result { display: block; animation: h-in 0.6s cubic-bezier(.2,.7,.2,1) both; }
@keyframes h-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
body[data-phase="result"] #entry { display: none; }
.h-from { margin: 0; font-size: 16px; color: var(--starlight); word-break: break-word; }
.h-short {
  font-family: var(--display);
  margin: 12px 0 10px; font-weight: 800; font-stretch: 112%;
  font-size: clamp(34px, 4.3vw, 60px); line-height: 1.02; letter-spacing: -0.02em;
  color: var(--paper);
}
.h-short:focus { outline: none; }
.h-short:focus-visible { outline: 3px solid var(--paper); outline-offset: 6px; }
.h-pre { display: block; color: var(--starlight); font-weight: 600; }
.h-short #r-code { display: block; white-space: nowrap; }
.h-status { margin: 0; min-height: 24px; font-size: 16px; color: var(--starlight); }
.h-status.h-ok { color: var(--good); }
.h-note { margin: 4px 0 0; font-size: 15px; color: var(--starlight); }
.h-actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 26px 0 18px; align-items: center; }
.h-ghost {
  min-height: 56px; padding: 0 20px; border-radius: var(--radius); cursor: pointer;
  background: transparent; color: var(--paper); border: 1.5px solid rgba(185, 195, 242, 0.5);
  font: 600 17px/1 'Instrument Sans', system-ui, sans-serif;
}
.h-ghost:hover { border-color: var(--paper); }
.h-ghost:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; }
.h-actions .h-go { min-height: 56px; height: 56px; }
.h-link {
  background: none; border: 0; padding: 12px 0; min-height: 44px; color: var(--paper); font: 600 16px 'Instrument Sans', system-ui, sans-serif;
  text-decoration: underline; text-underline-offset: 4px; cursor: pointer;
}
.h-qr-panel { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; margin: 0 0 18px; color: var(--starlight); font-size: 15px; }
.h-qr-panel[hidden] { display: none; }
.h-qr-box { background: var(--paper); padding: 12px; border-radius: 12px; line-height: 0; }
.h-qr-box svg { display: block; }
.h-qr-panel p { margin: 0 0 10px; }

/* The hole's caption: sits under the disc, placed from the renderer's numbers. */
.h-hole { position: absolute; margin: 0; left: 0; top: 0; z-index: 2; pointer-events: none; }
/* The destination readout: no box. Corner brackets (::before), a faint dark gradient for contrast, and a
   thin scan line (::after) that sweeps once when a new destination arrives (home.js adds .is-scan). */
.h-caption {
  /* Bottom right of the viewport, clear of the centred console (on a phone it moves to the foot of the screen). */
  --hud: rgba(191, 208, 255, 0.6);
  position: fixed; right: var(--gutter); bottom: 24px; margin: 0;
  width: 300px; max-width: min(88vw, 320px); box-sizing: border-box; text-align: left;
  display: flex; flex-direction: column; gap: 6px; padding: 16px 18px 15px;
  background: linear-gradient(135deg, rgba(14, 12, 39, 0.66), rgba(14, 12, 39, 0.4) 70%, rgba(14, 12, 39, 0.2));
  font: 500 13px/1.35 var(--mono); letter-spacing: 0.02em; color: var(--starlight);
  overflow: hidden;
}
.h-caption::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(var(--hud), var(--hud)) left top / 16px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) left top / 1.5px 16px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right top / 16px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right top / 1.5px 16px no-repeat,
    linear-gradient(var(--hud), var(--hud)) left bottom / 16px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) left bottom / 1.5px 16px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right bottom / 16px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right bottom / 1.5px 16px no-repeat;
}
.h-caption::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px; pointer-events: none; opacity: 0;
  background: linear-gradient(90deg, rgba(191, 208, 255, 0), #BFD0FF 50%, rgba(191, 208, 255, 0));
  box-shadow: 0 0 10px rgba(191, 208, 255, 0.7);
}
.h-caption.is-scan::after { animation: h-hud-scan 0.9s cubic-bezier(.3,.6,.3,1) 1 both; }
@keyframes h-hud-scan {
  0% { top: 0; opacity: 1; }
  85% { opacity: 1; }
  100% { top: 100%; opacity: 0; }
}
.h-hud-kicker { display: flex; align-items: center; gap: 8px; font: 600 11px/1.2 var(--mono); letter-spacing: 0.16em; text-transform: uppercase; color: var(--disc); }
.h-hud-kicker svg { flex: none; }
.h-hud-name { font-family: var(--display); font-weight: 700; font-size: 22px; line-height: 1.12; color: var(--paper); letter-spacing: -0.01em; margin-top: 4px; }
/* RA and Dec as two aligned columns, a small label above each value. */
.h-hud-coords { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: 8px 16px; margin: 2px 0 4px; }
/* Mass: its own row under RA and Dec. Values may wrap rather than run into the next column or the box edge. */
.h-hud-mass { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; margin: 6px 0 0; min-width: 0; }
.h-hud-mass .h-hud-val { white-space: normal; overflow-wrap: anywhere; }
.h-hud-tag { font: 500 10px/1.2 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--starlight); opacity: 0.85; border: 1px solid var(--hud); padding: 2px 5px; }
.h-hud-col { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.h-hud-lab { font: 500 10px/1.2 var(--mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--starlight); opacity: 0.85; }
.h-hud-val { font: 500 13px/1.3 var(--mono); color: var(--paper); white-space: normal; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
/* The distance is the hero: a small "about", a big number, then the unit. */
.h-hud-dist { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 6px; margin-top: 6px; padding-top: 10px; border-top: 1px solid rgba(191, 208, 255, 0.22); color: var(--starlight); }
.h-hud-about { font: 500 12px/1 var(--mono); color: var(--starlight); }
.h-hud-num { font: 700 44px/0.95 var(--display); letter-spacing: -0.02em; color: var(--paper); font-variant-numeric: tabular-nums; }
.h-hud-unit { font: 500 13px/1.2 var(--mono); color: var(--starlight); }
.h-hud-note { flex-basis: 100%; font: 400 11px/1.4 var(--mono); color: var(--starlight); opacity: 0.9; }

/* Claim your name: a native dialog, centred, the same ink as the rest of the page. */
.h-claim-cta { margin: 12px 0 0; }
/* Claim your name: a native modal <dialog> (assets/dialog.js opens and closes it, with focus return and an
   animated close). Ink panel with a corner-bracket frame, a close button in the top-right corner, and the
   same pill and rim language as the command bar. On a phone it is a bottom sheet. */
.h-claim {
  --hud: rgba(191, 208, 255, 0.5);
  position: fixed; inset: 0; margin: auto; height: fit-content; max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px);
  width: min(460px, calc(100vw - 32px)); box-sizing: border-box; padding: 0; overflow: auto; overscroll-behavior: contain;
  background: radial-gradient(120% 90% at 50% 0%, #241F5C 0%, #1B1846 55%, #15123A 100%);
  color: var(--paper); border: 1px solid rgba(191, 208, 255, 0.3); border-radius: 22px;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(14, 12, 39, 0.6);
  animation: h-claim-in 0.34s cubic-bezier(.16, 1, .3, 1) both;
}
.h-claim::backdrop {
  background: rgba(6, 5, 20, 0.66); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  animation: h-claim-fade 0.3s ease both;
}
.h-claim.is-closing { animation: h-claim-out 0.26s cubic-bezier(.4, 0, 1, 1) both; }
.h-claim.is-closing::backdrop { animation: h-claim-fade-out 0.26s ease both; }
@keyframes h-claim-in { from { opacity: 0; transform: translateY(14px) scale(0.96); } to { opacity: 1; transform: none; } }
@keyframes h-claim-out { to { opacity: 0; transform: translateY(10px) scale(0.97); } }
@keyframes h-claim-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes h-claim-fade-out { to { opacity: 0; } }
html.claim-open { overflow: hidden; }
/* Corner brackets on the panel. */
.h-claim::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  background:
    linear-gradient(var(--hud), var(--hud)) left top / 18px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) left top / 1.5px 18px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right top / 18px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right top / 1.5px 18px no-repeat,
    linear-gradient(var(--hud), var(--hud)) left bottom / 18px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) left bottom / 1.5px 18px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right bottom / 18px 1.5px no-repeat,
    linear-gradient(var(--hud), var(--hud)) right bottom / 1.5px 18px no-repeat;
}
/* The close button: round, in the top-right corner of the panel, 40px hit area. It sits in a sticky bar at the top of the
   dialog (the dialog is the scroll box), so on a short landscape screen the x stays in view while the form scrolls. The
   bar takes no room in the flow; only the button takes pointer events. */
.h-claim-bar {
  position: sticky; top: 0; z-index: 2; height: 0; display: flex; justify-content: flex-end; pointer-events: none;
}
.h-claim-x {
  pointer-events: auto; flex: none; margin: 16px 16px 0 0;
  width: 40px; height: 40px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid rgba(185, 195, 242, 0.35); border-radius: 50%; background: rgba(14, 12, 39, 0.5);
  color: var(--paper); cursor: pointer; transition: background 0.2s, border-color 0.2s;
}
.h-claim-x:hover { background: rgba(185, 195, 242, 0.16); border-color: var(--paper); }
.h-claim-x:focus-visible { outline: 3px solid var(--disc); outline-offset: 2px; }
.h-claim-x svg { display: block; }
.h-claim-form { padding: 34px 30px 26px; margin: 0; }
.h-claim-kicker { margin: 0 44px 14px 0; font: 500 11px/1.4 var(--mono); letter-spacing: 0.16em; color: var(--starlight); }
.h-claim-h { margin: 0 0 14px; font-family: var(--display); font-weight: 700; font-size: 30px; line-height: 1.1; letter-spacing: -0.01em; }
/* The price, the hero of the dialog. */
.h-claim-hero { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; margin: 0 0 14px; }
.h-claim-price { font: 700 56px/0.9 var(--display); letter-spacing: -0.03em; color: var(--paper); font-variant-numeric: tabular-nums; }
.h-claim-hero-text { font: 500 13px/1.3 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--starlight); }
.h-claim-p { margin: 0 0 12px; font-size: 16px; line-height: 1.5; color: var(--starlight); }
.h-claim-p b { color: var(--paper); font-weight: 700; }
.h-claim-rules { margin-bottom: 20px; font-size: 15px; }
.h-claim-rules a { color: var(--paper); text-underline-offset: 3px; }
.h-claim .h-claim-label { color: var(--starlight); margin-bottom: 10px; }
/* The name field: a pill, like the command bar, with a disc focus ring. */
.h-claim-form .h-claim-ending {
  height: 58px; box-sizing: border-box; padding-left: 4px; background: var(--night);
  border: 1.5px solid rgba(185, 195, 242, 0.38); border-radius: 999px;
}
.h-claim-form .h-claim-ending:focus-within { border-color: var(--disc); box-shadow: 0 0 0 3px rgba(191, 208, 255, 0.22); }
.h-claim-form .h-claim-ending span { padding-left: 18px; color: var(--starlight); font-size: 17px; }
.h-claim-form .h-claim-ending input { font-size: 18px; color: var(--paper); caret-color: var(--disc); padding-right: 22px; }
.h-claim-form .h-claim-ending input::placeholder { color: #8C94C7; opacity: 1; }
.h-claim-form .h-claim-ending input:focus { outline: none; }
.h-claim-form .h-claim-ending.is-err { border-color: var(--bad); }
/* Live preview of the link the name would give. */
.h-claim-live { margin: 10px 0 0; font: 500 13px/1.4 var(--mono); color: var(--starlight); word-break: break-all; }
.h-claim-live b { color: var(--paper); font-weight: 500; }
.h-claim-form .h-field-msg { min-height: 40px; margin: 8px 0 16px; font-size: 15px; color: var(--starlight); }
.h-claim-form .h-field-msg.h-ok { color: var(--good); }
.h-claim-form .h-field-msg.h-err { color: var(--bad); }
.h-claim-actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.h-claim-go { width: 100%; height: 58px; min-width: 0; font-size: 18px; border-radius: 999px; }
.h-claim-go[disabled], .h-claim-go[aria-disabled="true"] { background: var(--dust); color: var(--starlight); box-shadow: inset 0 0 0 1px rgba(185, 195, 242, 0.35); }
.h-claim-go[disabled]::before { display: none; }
.h-claim-wait { display: inline-flex; align-items: center; justify-content: center; width: 100%; box-sizing: border-box; min-height: 52px; color: var(--night); background: var(--paper); border-color: var(--paper); text-decoration: none; border-radius: 999px; }
.h-claim-wait:hover { background: #FFFFFF; color: var(--night); border-color: #FFFFFF; }
.h-claim-form .h-note { margin: 18px 0 0; font-size: 13px; color: var(--starlight); display: flex; gap: 8px; align-items: flex-start; }
.h-claim-polar .h-lock { flex: none; margin-top: 3px; }
.h-claim .h-ghost:focus-visible, .h-claim .h-go:focus-visible, .h-claim a:focus-visible { outline: 3px solid var(--disc); outline-offset: 3px; }
.h-claim-signin { margin: -6px 0 16px; }
.h-claim-signin a { color: var(--paper); }
@media (max-width: 599px) {
  .h-claim {
    inset: auto 0 0 0; margin: 0; width: 100%; max-width: none; max-height: 88vh; max-height: 88dvh;
    border-radius: 24px 24px 0 0; padding-bottom: env(safe-area-inset-bottom);
    animation-name: h-sheet-in;
  }
  .h-claim.is-closing { animation-name: h-sheet-out; }
  .h-claim::after { border-radius: 24px 24px 0 0; }
  .h-claim-form { padding: 30px 22px calc(22px + env(safe-area-inset-bottom)); }
  .h-claim-h { font-size: 26px; }
  .h-claim-price { font-size: 48px; }
  .h-claim-kicker { margin-right: 48px; }
}
@keyframes h-sheet-in { from { transform: translateY(40px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes h-sheet-out { to { transform: translateY(40px); opacity: 0; } }

.h-preview-panel { margin: 16px 0 0; max-width: 560px; }
.h-preview-more { margin: 10px 0 0; }
.h-preview-more a { color: var(--starlight); }

/* Phone: one header row (logo, Claim a name, menu). The hole sits behind the header, the text column and
   the footer are in normal flow (no absolute positioning over the form), and the links open in a sheet. */
@media (max-width: 899px) {
  :root { --head: 64px; }
  /* The sky and its scrim are anchored to the top of the page on a phone, so the headline and form never
     scroll over the bright disc. (Fixed on desktop, where the copy does not scroll.) */
  #sky { position: absolute; top: 0; left: 0; height: 100vh; height: 100lvh; transition: none; }
  /* The WebGL canvas is created by blackhole.js with an inline fixed position; this moves it with the page. */
  canvas[data-render-scale] { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100vh !important; height: 100lvh !important; }
  .hole-page::before { position: absolute; height: 100vh; height: 100lvh; }
  /* The scrim under the header row (home only; the sky is behind it). */
  .h-top::before {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: calc(var(--head) + 64px); z-index: -1; pointer-events: none;
    background: linear-gradient(rgba(14, 12, 39, 0.94) 0%, rgba(14, 12, 39, 0.88) 40%, rgba(14, 12, 39, 0.62) 62%,
      rgba(14, 12, 39, 0.3) 80%, rgba(14, 12, 39, 0.1) 92%, rgba(14, 12, 39, 0) 100%);
  }
  .hole-page .h-top { background: none; }
  /* The copy, the caption and the footer are in normal flow: the margin places the headline under the hole's caption. */
  .h-copy, .h-result {
    width: calc(100vw - 2 * var(--gutter)); max-width: 560px; padding: 24px 20px 20px; border-radius: 20px;
  }
  .h-result { top: calc(var(--head) + 12px); transform: none; }
  .h-title { font-size: clamp(34px, 10vw, 54px); margin-bottom: 10px; }
  .h-quiet { font-size: 16px; margin-bottom: 8px; }
  .h-copy > .h-quiet + .h-quiet { margin-bottom: 16px; }
  .h-bar { flex-direction: column; gap: 10px; }
  .h-field input { height: 60px; font-size: 19px; }
  .h-field.has-paste input { padding-right: 118px; }
  .h-paste { height: 44px; }
  .h-go { width: 100%; height: 60px; min-width: 0; }
  .h-msg { min-height: 44px; margin-top: 10px; font-size: 15px; }
  .h-opts { margin-top: 14px; }
  .h-options {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; margin: 0; z-index: 5;
    max-height: 72svh; max-height: 72dvh; overflow-y: auto; border-radius: 22px 22px 0 0;
    padding: 22px 20px calc(22px + env(safe-area-inset-bottom));
    background: #1B1846; backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  .h-short { font-size: clamp(30px, 9vw, 42px); }
  /* On a phone the readout sits at the foot of the screen, clear of the centred card. */
  .h-caption { position: fixed; top: auto; right: auto; left: 50%; transform: translateX(-50%); bottom: calc(12px + env(safe-area-inset-bottom)); min-width: 0; max-width: calc(100vw - 2 * var(--gutter)); font-size: 12px; }
  .h-hud-name { font-size: 16px; }
}

@media (max-height: 700px) and (min-width: 900px) {
  .h-title { font-size: clamp(32px, 4vw, 56px); }
}

@media (prefers-reduced-motion: reduce) {
  #sky, .h-go::before, .h-switch span { transition: none; }
  body[data-phase="result"] .h-result { animation: none; }
  .h-go:active { transform: none; }
}

/* A scrim under the copy: a soft ink gradient between the moving hole and the text, so
   text contrast holds over the lensing halo (the canvas is under this layer). */
.hole-page::before {
  content: ""; position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(ellipse 58% 72% at 22% 46%, rgba(14, 12, 39, 0.92) 0%, rgba(14, 12, 39, 0.8) 42%, rgba(14, 12, 39, 0) 78%);
}
@media (max-width: 899px) {
  .hole-page::before {
    background: linear-gradient(to bottom, rgba(14, 12, 39, 0) 40vh, rgba(14, 12, 39, 0.88) 56vh, rgba(14, 12, 39, 0.9) 100%);
  }
}

/* Custom endings are Pro: the field shows locked, with the upgrade path beside it. */
.h-pro-badge { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px; font-size: 12px; background: rgba(185, 195, 242, 0.22); color: var(--paper); vertical-align: 1px; }
.h-pro-lock .h-ending input:disabled { opacity: 0.6; cursor: not-allowed; }
.h-pro-cta { margin: 10px 0 0; font-size: 15px; }
.h-pro-note { color: var(--starlight); }
.h-claim-signin { margin: -6px 0 16px; }
.h-claim-signin a { color: var(--paper); }
/* The claim success page. */
.claim-status { max-width: 560px; margin: 0 auto; padding: 48px 16px 64px; }
.claim-status h1 { font-family: var(--display); font-size: 36px; line-height: 1.1; margin: 0 0 14px; }
.claim-status p { font-size: 17px; }
.claim-status #claim-link-wrap, .claim-status #claim-signin { margin: 22px 0 0; }

/* Result screen. The short link is one line in a box that hugs it (fitShort in home.js shrinks it to fit);
   the meta line, the one row of actions, and the claim card sit under it. Element ids and the arrival order are unchanged. */
.h-result { display: none; }
body[data-phase="result"] .h-result { display: block; }
@media (min-width: 900px) { .h-result { width: min(620px, 44vw); } }
.h-short {
  display: block; width: fit-content; max-width: 100%; box-sizing: border-box;
  margin: 10px 0 14px; padding: 12px 20px 13px; border-radius: 18px;
  border: 1px solid rgba(191, 208, 255, 0.34);
  background: linear-gradient(180deg, rgba(43, 39, 102, 0.55), rgba(23, 20, 59, 0.78));
  box-shadow: 0 0 0 1px rgba(191, 208, 255, 0.05), 0 0 34px rgba(127, 155, 255, 0.16);
  font-family: var(--display); font-weight: 800; font-stretch: 112%;
  font-size: clamp(28px, 4.3vw, 60px); line-height: 1.1; letter-spacing: -0.02em;
  white-space: nowrap; color: var(--paper);
}
@media (max-width: 899px) { .h-short { font-size: clamp(28px, 8.4vw, 60px); } }
.h-short .h-pre { display: inline; color: var(--starlight); font-weight: 600; }
.h-short #r-code { display: inline; white-space: nowrap; color: var(--paper); }
.h-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 22px; }
.h-meta > p { margin: 0; }
.h-from, .h-note, .h-status {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%; min-height: 30px; margin: 0; padding: 5px 12px;
  border-radius: 999px; background: rgba(43, 39, 102, 0.5); border: 1px solid rgba(185, 195, 242, 0.24);
  color: var(--starlight); font-size: 14px; line-height: 1.35;
}
.h-from { word-break: break-all; }
.h-status:empty, .h-note:empty { display: none; }
.h-status.h-ok { color: var(--good); border-color: rgba(201, 242, 214, 0.45); }
.h-actions { display: flex; flex-wrap: wrap; align-items: stretch; gap: 10px; margin: 0 0 22px; }
.h-actions > button {
  flex: 0 0 auto; height: 52px; min-height: 52px; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
  gap: 9px; padding: 0 18px; border-radius: 14px; font: 600 16px/1 'Instrument Sans', system-ui, sans-serif; white-space: nowrap; cursor: pointer;
}
.h-actions > .h-ghost { background: rgba(43, 39, 102, 0.3); border: 1.5px solid rgba(185, 195, 242, 0.42); color: var(--paper); }
.h-actions > .h-ghost:hover { border-color: var(--disc); background: rgba(43, 39, 102, 0.6); }
.h-actions > .h-ghost[aria-expanded="true"] { border-color: var(--disc); background: rgba(191, 208, 255, 0.14); }
.h-actions > .h-ghost:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; }
.h-actions > .h-go { flex: 1 1 200px; min-width: 0; height: 52px; min-height: 52px; padding: 0 24px; gap: 10px; border-radius: 14px; font-size: 17px; }
.h-actions > .h-go.is-copied { background: var(--good); }
.h-actions > .h-go:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; }
.h-ico { width: 18px; height: 18px; flex: none; }
.h-ico-check { display: none; }
.h-go.is-copied .h-ico-copy { display: none; }
.h-go.is-copied .h-ico-check { display: inline-block; }
@media (max-width: 899px) {
  .h-actions > .h-go { flex: 0 0 100%; }
  .h-actions > .h-ghost { flex: 1 1 0; min-width: 0; padding: 0 8px; font-size: 15px; }
}
.h-claim-card {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px 20px; margin: 0 0 14px; padding: 18px 20px;
  border-radius: 18px; background: linear-gradient(135deg, rgba(43, 39, 102, 0.72), rgba(23, 20, 59, 0.9));
  border: 1px solid rgba(191, 208, 255, 0.32); box-shadow: 0 0 36px rgba(127, 155, 255, 0.14);
}
.h-claim-card-text { margin: 0; flex: 1 1 240px; font-size: 16px; line-height: 1.45; color: var(--starlight); }
.h-claim-card-text b { color: var(--paper); }
.h-claim-pill {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 10px; min-height: 52px; padding: 0 20px 0 24px; border: 0; border-radius: 999px; cursor: pointer;
  background: linear-gradient(135deg, #F2F5FF 0%, var(--disc) 55%, #A9BEFF 100%); color: #10102A;
  font: 700 17px/1 'Instrument Sans', system-ui, sans-serif;
  box-shadow: 0 0 0 1px rgba(233, 238, 255, 0.7), 0 0 18px rgba(191, 208, 255, 0.55), 0 0 42px rgba(127, 155, 255, 0.32);
}
.h-claim-pill:hover { box-shadow: 0 0 0 1px #FFFFFF, 0 0 26px rgba(191, 208, 255, 0.8), 0 0 60px rgba(127, 155, 255, 0.45); }
.h-claim-pill:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; }
.h-claim-pill .h-claim-tag { margin-left: 0; }
.h-again { margin: 0; }
.h-again .h-link { min-height: 44px; padding: 0 2px; font-size: 15px; font-weight: 500; color: var(--starlight); text-decoration: none; }
.h-again .h-link:hover { color: var(--paper); text-decoration: underline; }
.h-again .h-link:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; }


/* ---- Console v2: command bar, options row, readout and phone rules (these follow the rules above) ---- */
@property --rim-a { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
/* The Shorten button sits inside the pill, at its right end. The arrow is a CSS background, so JS can
   replace the button's text (setBusy) without losing it. */
.h-bar .h-go {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  flex: none; height: 56px; min-height: 56px; min-width: 178px; margin: 0 6px 0 0; padding: 0 22px 0 26px;
  border-radius: 999px; font-size: 18px;
  transform: translate(var(--mx, 0px), var(--my, 0px));
  transition: transform 0.35s cubic-bezier(.2, .7, .2, 1);
}
.h-bar .h-go:not([aria-busy="true"])::after {
  content: ""; flex: none; width: 18px; height: 18px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M4 10h11M11 5l5 5-5 5' fill='none' stroke='%2310102A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: transform 0.35s cubic-bezier(.2, .7, .2, 1);
}
.h-bar .h-go:not([aria-busy="true"]):hover::after { transform: translateX(3px); }
.h-bar .h-go[aria-busy="true"] { min-width: 178px; }
.h-bar .h-go:disabled { transform: none; }
/* The input is borderless and sits in the pill; the Paste button is inside the field (see .h-paste). */
.h-field.has-paste input { padding-right: 124px; }
.h-paste { right: 6px; height: 40px; }

/* The row under the command bar: the hint (role=status) on the left, the Options chip on the right. */
.h-under { display: flex; align-items: center; justify-content: space-between; gap: 12px 16px; margin-top: 14px; min-height: 44px; }
.h-under .h-msg { flex: 1 1 auto; min-width: 0; }
.h-under .h-opt-toggle {
  flex: none; min-height: 36px; margin: 0; padding: 0 14px; display: inline-flex; align-items: center;
  background: transparent; border: 1px solid rgba(185, 195, 242, 0.4); border-radius: 999px; color: var(--paper);
  font: 600 14px/1 'Instrument Sans', system-ui, sans-serif; cursor: pointer;
  transition: background-color 0.2s, border-color 0.2s;
}
.h-under .h-opt-toggle::after { margin-left: 9px; vertical-align: 2px; }
.h-under .h-opt-toggle[aria-expanded="true"]::after { vertical-align: -1px; }
@media (hover: hover) { .h-under .h-opt-toggle:hover { background: rgba(185, 195, 242, 0.16); border-color: var(--paper); color: var(--paper); } }
.h-under .h-opt-toggle:focus-visible { background: rgba(185, 195, 242, 0.16); color: var(--paper); outline: 3px solid var(--paper); outline-offset: 3px; }
/* The options panel is a block under the row (a bottom sheet on a phone). */
.h-options { margin-top: 14px; }
/* Desktop shorter than 1000px: the custom ending and the expiry sit side by side, and the Easy row spans the
   panel below them, so the whole open panel (its switch too) stays on screen at 1280x800 and 1440x900. */
@media (min-width: 900px) and (max-height: 1000px) {
  .h-options { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 32px; align-items: start; padding: 16px 20px; }
  .h-options > .h-easy { grid-column: 1 / -1; margin-bottom: 0; }
  /* With Options open the console starts closer to the header; the stage padding is equal top and bottom, so a card
     that fits stays centred on the screen. */
  body.hole-page:has(#options:not([hidden])) .h-main { padding-top: calc(var(--head) + 12px); padding-bottom: calc(var(--head) + 12px); }
}
/* Shorter still (a 1280x800 laptop): the card cannot fit, so it starts under the header, the stage keeps no bottom
   padding and the vignette stops at the card's edge. The page does not scroll for the panel's bottom edge. */
@media (min-width: 900px) and (max-height: 840px) {
  body.hole-page:has(#options:not([hidden])) .h-main { padding-bottom: 0; }
  body.hole-page:has(#options:not([hidden])) .h-copy { padding-bottom: 12px; }
  body.hole-page:has(#options:not([hidden])) .h-copy::before { inset: -20px -40px 0; }
}

/* Magnetic hover (home.js sets --mx/--my; desktop with a fine pointer only). */
@media (hover: hover) and (pointer: fine) {
  .h-bar .h-go { will-change: transform; }
}

/* The destination readout on a phone: a compact box at the foot of the screen. */
@media (max-width: 899px) {
  .h-copy, .h-result { padding: 24px 20px 22px; }
  .h-copy::before, .h-result::before { inset: -28px -20px; }
  .h-copy::after, .h-result::after { --hud: rgba(191, 208, 255, 0.5); }
  .h-hud-top { margin-bottom: 20px; }
  .h-title { font-size: clamp(38px, 11vw, 56px); margin-bottom: 12px; }
  .h-tagline { margin-bottom: 20px; font-size: 11px; letter-spacing: 0.16em; }
  .h-bar { flex-direction: column; align-items: stretch; gap: 0; min-height: 0; padding: 0 0 6px; border-radius: 28px; }
  .h-bar::before { border-radius: 28px; }
  .h-bar::after { border-radius: 26.5px; }
  .h-bar:has(input:focus-visible) { outline-offset: 3px; }
  .h-field { flex: none; align-self: auto; }
  .h-field input { height: 60px; font-size: 19px; border-radius: 28px 28px 0 0; padding: 0 18px 0 22px; }
  .h-field.has-paste input { padding-right: 130px; }
  .h-paste { top: 10px; right: 10px; transform: none; height: 40px; }
  .h-bar .h-go { width: calc(100% - 12px); min-width: 0; height: 56px; min-height: 56px; margin: 0 6px; }
  .h-under { margin-top: 12px; }
  .h-under .h-msg { font-size: 14px; }
  .h-caption {
    width: calc(100vw - 2 * var(--gutter)); max-width: 420px; padding: 12px 16px 12px;
    bottom: calc(12px + env(safe-area-inset-bottom)); font-size: 12px;
  }
  .h-caption::before { background-size: 12px 1.5px, 1.5px 12px, 12px 1.5px, 1.5px 12px, 12px 1.5px, 1.5px 12px, 12px 1.5px, 1.5px 12px; }
  .h-hud-name { font-size: 16px; }
  .h-hud-coords { gap: 4px 14px; margin: 0; }
  .h-hud-mass { margin-top: 4px; }
  .h-hud-val { font-size: 12px; }
  .h-hud-num { font-size: 28px; }
  .h-hud-about, .h-hud-unit { font-size: 12px; }
  .h-hud-kicker { font-size: 10px; }
  .h-hud-dist { margin-top: 2px; padding-top: 6px; }
  .h-hud-note { font-size: 10px; }
  .h-claim-hero { margin-bottom: 12px; }
}

/* Reduced motion: no sweep, no rim rotation, no pulse, no scan line, no magnetic offset, no dialog animation. */
@media (prefers-reduced-motion: reduce) {
  .h-title { animation: none; }
  .h-bar::before, .h-status-dot, .h-caption.is-scan::after { animation: none; }
  .h-bar, .h-bar::before, .h-bar .h-go, .h-bar .h-go::after, .h-under .h-opt-toggle { transition: none; }
  .h-bar .h-go { transform: none; }
  .h-bar:has(input:focus-visible) { outline-width: 3px; }
  .h-claim, .h-claim::backdrop, .h-claim.is-closing, .h-claim.is-closing::backdrop { animation: none; }
  .h-claim-x { transition: none; }
}

/* ---- Result screen v2: centred, wider on desktop, the security row under the From line, and the
   Preview beside the result on wide screens. ---- */
.h-result { --hud: rgba(191, 208, 255, 0.5); }
@media (min-width: 900px) {
  .h-result { width: min(1040px, calc(100vw - 2 * var(--gutter))); padding: 40px 48px 36px; }
  .h-result::before { inset: -64px -96px; }
  .h-short { font-size: clamp(44px, 6.2vw, 88px); padding: 14px 26px 15px; margin: 6px 0 18px; }
  .h-actions { flex-wrap: nowrap; }
  .h-actions > button { flex: 0 0 auto; white-space: nowrap; }
  .h-actions > .h-go { flex: 1 1 0; min-width: 0; }
}
/* The row under the From line: the security chips. */
.h-security { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; flex-basis: 100%; margin: 0; min-height: 28px; }
.h-security[hidden] { display: none; }
.h-security .pv-chip { font-size: 12px; }
.h-security .pv-chip-ico { display: inline-flex; }
.h-security .pv-chip.pv-warn { color: var(--bad); border-color: rgba(255, 179, 198, 0.55); background: rgba(255, 179, 198, 0.08); }
.h-security .pv-chip[title] { cursor: help; }
.h-sec-load {
  display: inline-block; padding: 3px 12px; border-radius: 999px; font: 500 12px/1.3 var(--mono);
  color: var(--starlight); border: 1px solid rgba(185, 195, 242, 0.3);
  background: linear-gradient(100deg, rgba(43, 39, 102, 0.45) 30%, rgba(191, 208, 255, 0.22) 50%, rgba(43, 39, 102, 0.45) 70%) 200% 0 / 220% 100% no-repeat;
  animation: h-shimmer 1.4s linear infinite;
}
@keyframes h-shimmer { to { background-position: -20% 0; } }
@media (prefers-reduced-motion: reduce) { .h-sec-load { animation: none; } }
/* On a phone the vignette stays inside the gutters, so it never adds to the page width. */
@media (max-width: 899px) {
  .h-copy::before, .h-result::before { inset: -16px 0; }
}
/* The result card is wide and centred, so the destination readout steps back while it shows (home.js still fills it). */
body[data-phase="result"] .h-caption { opacity: 0; pointer-events: none; }
/* The result is centred with insets and auto margins, not with a transform: its entrance animation
   (h-in, fill mode both) sets transform, which would otherwise knock the card off centre. */
.h-result { left: 0; right: 0; top: 0; bottom: 0; margin: auto; transform: none; height: fit-content; max-height: calc(100vh - 32px); }
@media (max-width: 899px) {
  .h-result { top: var(--head); bottom: 0; height: auto; max-height: none; margin: 0 auto; padding-bottom: 28px; }
}
/* The vignette reaches past the box, so the console and the result do not scroll inside themselves. */
.h-copy, .h-result { overflow: visible; }
.h-copy, .h-result { max-height: none; }
/* Action buttons keep their labels: the copy button takes the spare width, the others stay at their size. */
@media (min-width: 900px) {
  .h-actions > button { flex: 0 0 auto; }
  .h-actions > .h-go { flex: 1 1 auto; min-width: 176px; }
}
/* With Preview open on a wide screen, the preview sits beside the result (a column to its right), and the
   pair is centred together. On a narrower screen the preview stays below the result, as before. */
@media (min-width: 1100px) {
  .h-result:has(#r-preview-panel:not([hidden])) { width: 620px; left: -258px; right: 258px; }
  .h-result:has(#r-preview-panel:not([hidden])) .h-short { font-size: clamp(40px, 4.6vw, 58px); }
  .h-result:has(#r-preview-panel:not([hidden])) > .h-preview-panel {
    position: absolute; left: 100%; top: 0; margin: 0 0 0 36px; width: 480px; max-width: none;
  }
}
/* On a phone the result card covers the page, so the footer is hidden while the result shows. */
@media (max-width: 899px) { body[data-phase="result"] .h-foot { display: none; } }
/* The home page is one screen: on a wide screen its footer sits at the foot of the viewport, as before. */
@media (min-width: 900px) { body.hole-page .h-foot { position: absolute; left: 0; right: 0; bottom: 14px; margin-top: 0; padding-top: 0; padding-bottom: 0; } }
/* Phone: the console is centred in the first screen and the footer sits below it (main is at least one screen tall). */
@media (max-width: 899px) {
  .h-main { min-height: 100vh; min-height: 100svh; }
  .h-copy::before { inset: -24px -8px; background: radial-gradient(closest-side, rgba(14, 12, 39, 0.94) 0%, rgba(14, 12, 39, 0.9) 62%, rgba(14, 12, 39, 0.5) 86%, rgba(14, 12, 39, 0) 100%); }
}
/* The top-row label keeps its contrast over the bright disc: a small ink backing. */
.h-hud-id { display: inline-block; padding: 3px 8px; border-radius: 6px; background: rgba(14, 12, 39, 0.78); }

/* Wide console: a bigger headline to fill it, and a solid Options chip so it reads over the bright disc. */
@media (min-width: 900px) { .h-copy .h-title { font-size: clamp(56px, 7.4vw, 112px); } }
.h-under .h-opt-toggle { background: rgba(10, 10, 32, 0.86); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); color: var(--paper); }

   HUD toggle switches it off (home.js). The slow zoom is off under reduced motion. */
/* The HUD: the source credit line. */
.h-hud-credit { flex-basis: 100%; min-width: 0; overflow-wrap: anywhere; font: 400 11px/1.4 var(--mono); color: var(--starlight); opacity: 0.9; }

/* The result screen matches the console's width, so the page does not jump narrower after shortening. */
@media (min-width: 900px) { .h-result { width: min(1200px, calc(100vw - 2 * var(--gutter))); } }

/* background-clip:text paints only inside the box; with line-height under 1 the descenders (the y)
   fall outside it. Pad the box down by the descender depth and pull the next line back up. */
.h-title { padding-bottom: 0.14em; margin-bottom: calc(16px - 0.14em); }

/* ---- Layout QA pass (one centring model per width; desktop unchanged) ----
   The orbit canvas (#fx) sits under the content and the scrim, so its glyphs never cross a control. */
/* Phone: the page's <main> is the stage. The console, the result and the destination readout are in normal
   flow inside it, centred by equal top and bottom padding (the header row plus the readout's reserve), so the
   card is centred on the viewport whenever it fits, and starts under the header with clearance when it does not. */
@media (max-width: 899px) {
  body.hole-page .h-top { position: absolute; top: 0; left: 0; right: 0; z-index: 6; }
  body.hole-page .h-main {
    --cap: 116px;
    position: relative; box-sizing: border-box; display: flex; flex-direction: column; min-height: 100vh; min-height: 100svh;
    padding: calc(var(--head) + var(--cap) + env(safe-area-inset-top, 0px)) 0 calc(var(--head) + var(--cap) + env(safe-area-inset-bottom, 0px));
  }
  body.hole-page[data-phase="result"] .h-main { --cap: 0px; }
  body.hole-page .h-copy, body.hole-page .h-result {
    position: relative; inset: auto; transform: none; margin: 0 auto; box-sizing: border-box;
    width: calc(100% - 2 * var(--gutter)); max-width: 560px; height: auto; max-height: none; overflow: visible;
    margin-block: auto;
  }
  body.hole-page .h-copy::before, body.hole-page .h-result::before { inset: -16px -8px; }
  /* The destination readout: one compact line at the foot of the first screen (name and distance). */
  body.hole-page .h-hole {
    position: absolute; left: 0; right: 0; top: auto; bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    margin: 0; padding: 0 var(--gutter); box-sizing: border-box; z-index: 2; pointer-events: none;
  }
  body.hole-page[data-phase="result"] .h-hole { display: none; }
  body.hole-page .h-caption {
    position: relative; left: auto; right: auto; bottom: auto; top: auto; transform: none;
    width: 100%; max-width: 560px; min-width: 0; margin: 0 auto; padding: 9px 14px;
    flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; font-size: 12px;
  }
  body.hole-page .h-caption .h-hud-kicker,
  body.hole-page .h-caption .h-hud-coords,
  body.hole-page .h-caption .h-hud-mass,
  body.hole-page .h-caption .h-hud-note,
  body.hole-page .h-caption .h-hud-credit { display: none; }
  body.hole-page .h-caption .h-hud-name { font-size: 15px; margin: 0; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  body.hole-page .h-caption .h-hud-dist { flex: none; margin: 0; padding: 0; border: 0; }
  body.hole-page .h-caption .h-hud-num { font-size: 22px; }
  body.hole-page .h-caption .h-hud-unit, body.hole-page .h-caption .h-hud-about { font-size: 12px; }
}
/* Narrow phones: the header row and the result's three buttons keep their labels on a 320px screen. */
@media (max-width: 400px) {
  body.hole-page .h-top .brand { font-size: 15px; }
  body.hole-page .h-top .h-claim-link { font-size: 14px; padding: 0 10px 0 12px; }
  body.hole-page .h-top .h-claim-tag { min-width: 28px; }
  body.hole-page .h-result .h-actions > .h-ghost { padding: 0 6px; font-size: 14px; }
  body.hole-page .h-result .h-actions > .h-ghost .h-ico { display: none; }
}
/* The vignettes stay inside the page width, so the console never adds a sideways scroll. */
@media (min-width: 900px) {
  .h-copy::before, .h-result::before { inset: -56px -40px; }
  .h-result::before { inset: -64px -40px; }
}
/* Narrow phones: the four expiry choices fit their row without clipping their labels. */
@media (max-width: 400px) {
  .h-radios button { padding: 0 4px; font-size: 13px; }
}
/* Desktop below 1920x1100: the destination readout is the same compact strip as on a phone (name and distance),
   so it fits in the space under the console and never covers it or the Options button. */
@media (min-width: 900px) and (max-height: 1100px), (min-width: 900px) and (max-width: 1919px) {
  body.hole-page .h-caption {
    width: 340px; max-width: 340px; padding: 10px 14px; flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; font-size: 12px;
  }
  body.hole-page .h-caption .h-hud-kicker, body.hole-page .h-caption .h-hud-coords, body.hole-page .h-caption .h-hud-mass,
  body.hole-page .h-caption .h-hud-note, body.hole-page .h-caption .h-hud-credit { display: none; }
  body.hole-page .h-caption .h-hud-name { font-size: 15px; margin: 0; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  body.hole-page .h-caption .h-hud-dist { flex: none; margin: 0; padding: 0; border: 0; }
  body.hole-page .h-caption .h-hud-num { font-size: 22px; }
}
/* The footer steps back while a panel grows the console or the result (as it does on a phone), so nothing sits on it. */
@media (min-width: 900px) {
  body.hole-page[data-phase="result"] .h-foot, body.hole-page:has(#options:not([hidden])) .h-foot { visibility: hidden; }
}
/* Narrow phones: the four expiry choices wrap to two rows rather than clip their labels. */
@media (max-width: 400px) {
  .h-radios { flex-wrap: wrap; }
  .h-radios button { flex: 1 1 calc(50% - 4px); white-space: normal; }
}
/* With Options open on a wide screen the readout steps back, as it does for a result, so the panel never sits on it. */
@media (min-width: 900px) {
  body.hole-page:has(#options:not([hidden])) .h-caption { opacity: 0; pointer-events: none; }
}
/* The expiry choices may wrap their labels to two lines rather than clip them. */
.h-radios button { white-space: normal; line-height: 1.15; }
/* Long destinations (the squeezed link's own URL): shown in at most two lines, broken only where needed, never wider
   than their box. The full address is in the element's title attribute (home.js, preview.js). */
.h-from, .pv-url, .pv-node, .pv-flow .pv-dest {
  max-width: 100%; min-width: 0; box-sizing: border-box;
  overflow-wrap: anywhere; word-break: normal;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.h-from { display: -webkit-box; }

/* ---- Long From line, Copy full link, and the desktop stage ----
   The From line: the destination in the middle-ellipsis form (assets/url-display.js) with a real two-line clamp on its
   text, and the pill grows with those two lines (a flex row: the label, then the clamped text). The full address is in
   the title and in Copy full link, so nothing is lost. */
.h-from {
  display: flex; align-items: flex-start; gap: 6px; width: fit-content; max-width: 100%; box-sizing: border-box;
  min-height: 30px; padding: 5px 12px; border-radius: 999px; word-break: normal; overflow-wrap: normal; overflow: visible;
}
.h-from-lead { flex: none; line-height: 1.35; }
.h-from-text {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  min-width: 0; max-width: 100%; line-height: 1.35; overflow-wrap: anywhere;
}
.h-copy-full {
  align-self: center; min-height: 36px; padding: 0 14px; border-radius: 999px; cursor: pointer; white-space: nowrap;
  font: 600 14px/1 'Instrument Sans', system-ui, sans-serif; color: var(--paper);
  background: rgba(43, 39, 102, 0.3); border: 1.5px solid rgba(185, 195, 242, 0.42);
}
.h-copy-full:hover { border-color: var(--disc); background: rgba(43, 39, 102, 0.6); }
.h-copy-full:focus-visible { outline: 3px solid var(--paper); outline-offset: 3px; }
.h-copy-full.is-copied { color: var(--good); border-color: rgba(201, 242, 214, 0.45); }
.h-copy-full[hidden] { display: none; }

/* The desktop stage (same model as the phone). The header is an overlay row at the top, and the console, the result
   and the options sit in <main> with equal top and bottom padding (the header and a gap). The card is centred on the
   screen when it fits. When it is taller than the screen under the header, its auto margins fall to zero: it starts
   below the header with the gap, and the page scrolls. */
@media (min-width: 900px) {
  body.hole-page .h-top { position: absolute; top: 0; left: 0; right: 0; z-index: 6; }
  body.hole-page .h-main {
    position: relative; box-sizing: border-box; display: flex; flex-direction: column;
    min-height: 100vh; min-height: 100svh;
    padding: calc(var(--head) + 36px) 0 calc(var(--head) + 36px);
  }
  body.hole-page .h-copy, body.hole-page .h-result {
    position: relative; left: auto; right: auto; top: auto; bottom: auto; transform: none; margin: auto;
    height: auto; max-height: none; overflow: visible; flex: none;
  }
}
