/* The hub's stylesheet, served at /hub.css to both pages that wear it.

   It lived inside templates/account.html until 2026-09-07, when the device-auth
   entitlement page — which had been moved onto this hub's domain and left
   looking like the developer page it started as — needed the same skin. A
   second inline copy would have been a second thing to keep in step, and the
   two pages sitting on one domain looking like different products is the defect
   being fixed, not a thing to reintroduce a month later.

   dreamhelper.dev's own tokens, so the hub and the site read as one product.
   Source: dreamhelper.dev-site/docs/design-system.md. Cyan dominates; the
   per-game colours are used only where a product is named. */
:root{
  --bg-deep:#050508; --bg-elevated:#0c0c14;
  --bg-surface:rgba(255,255,255,0.03); --bg-surface-hover:rgba(255,255,255,0.06);
  --border:rgba(255,255,255,0.06); --border-bright:rgba(255,255,255,0.12);
  --text-primary:#ececf0; --text-secondary:#7c7c8a; --text-muted:#44445a;
  --cyan:#22d3ee; --cyan-dim:rgba(34,211,238,0.12); --cyan-glow:rgba(34,211,238,0.20);
  --pink:#f472b6; --pink-dim:rgba(244,114,182,0.12);
  --purple:#a855f7; --purple-dim:rgba(168,85,247,0.12);
  --amber:#fbbf24; --red:#f87171;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--bg-deep); color:var(--text-primary);
  font-family:'Zen Kaku Gothic New',sans-serif; font-size:.9rem; line-height:1.7;
  min-height:100vh;
  background-image:
    radial-gradient(circle, rgba(255,255,255,0.04) 1px, transparent 1px);
  background-size:60px 60px;
}
/* One cyan orb, top of the page — the site's atmosphere without its weight. */
body::before{
  content:''; position:fixed; inset:-20% 0 auto 0; height:60vh; pointer-events:none;
  background:radial-gradient(ellipse at 50% 0%, var(--cyan-glow), transparent 65%);
  opacity:.5; z-index:0;
}
.wrap{position:relative; z-index:1; max-width:1120px; margin:0 auto; padding:2rem 1.25rem 5rem}
header.top{
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding:1rem 0 2rem; flex-wrap:wrap;
}
.brand{font-family:'Sora',sans-serif; font-weight:800; font-size:1.25rem; letter-spacing:-.01em}
.brand span{color:var(--cyan)}
h1{font-family:'Sora',sans-serif; font-weight:800; font-size:clamp(2rem,4vw,3rem); margin:.2rem 0 .4rem; letter-spacing:-.02em}
h2{font-family:'Sora',sans-serif; font-weight:600; font-size:1.25rem; margin:0 0 1rem}
p{margin:.4rem 0}
.muted{color:var(--text-secondary)}
.tag{
  font-family:'IBM Plex Mono',monospace; font-size:.7rem; font-weight:500;
  letter-spacing:.02em; padding:.25rem .6rem; border-radius:999px;
  border:1px solid var(--border-bright); color:var(--text-secondary);
  display:inline-block; white-space:nowrap;
}
.tag.ok{color:var(--cyan); border-color:var(--cyan); background:var(--cyan-dim)}
.tag.warn{color:var(--amber); border-color:rgba(251,191,36,.4); background:rgba(251,191,36,.1)}
.tag.bad{color:var(--red); border-color:rgba(248,113,113,.4); background:rgba(248,113,113,.1)}
.server-tags{display:inline-flex;gap:6px;margin:4px 0 0}
.platform-tag{display:inline-flex; align-items:center; gap:.35rem}
.platform-icon{width:1em; height:1em; flex:0 0 auto}
.platform-windows{color:#78b9ff}
.platform-android{color:#8bd34a}
.card{
  background:var(--bg-surface); border:1px solid var(--border); border-radius:16px;
  padding:2rem; margin-bottom:1rem; position:relative; overflow:hidden;
  transition:border-color .15s ease, background .15s ease;
}
.card:hover{border-color:var(--border-bright); background:var(--bg-surface-hover)}
.prose h3{font-family:'Sora',sans-serif; font-size:1rem; margin:1.5rem 0 .5rem}
.prose p{margin:.5rem 0; color:var(--text-secondary)}
.prose ol,.prose ul{margin:.5rem 0 .5rem 1.25rem; color:var(--text-secondary)}
.prose li{margin:.25rem 0}
.prose code{font-family:'IBM Plex Mono',monospace; font-size:.8rem;
  background:var(--bg-surface); padding:.1rem .35rem; border-radius:4px}
.prose img{max-width:100%; height:auto; border-radius:8px}
.card .blob{
  position:absolute; top:-40px; right:-40px; width:180px; height:180px; border-radius:50%;
  filter:blur(60px); opacity:.5; pointer-events:none;
}
.product-card{display:block; text-decoration:none; color:inherit}
/* One column at phone width, two or three once there is room — the same
   "cards equal height in a row" idea the stats `.grid` already uses above,
   just at a size that fits a heading, tags and a badge instead of a number. */
#products{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr));
  gap:16px; align-items:stretch;
}
#products .card{margin-bottom:0}
#products .group-heading{grid-column:1 / -1}
/* A family card (<section>, not <a> — see familyCard() in account.html) has
   no href of its own, so it does not get .product-card's link color reset;
   it still wants the same block/overflow shape as a card. */
.family-card{display:block}
/* Shared by a standalone product's one row and a family's one row per
   variant — see variantRow() in account.html. The hairline and padding match
   .dl-item's rhythm on the downloads tab, so the two tabs read as one page. */
.variant-row{
  display:flex; align-items:center; gap:10px; padding:8px 0;
  border-top:1px solid var(--border); text-decoration:none; color:inherit;
}
.variant-row-tail{display:flex; align-items:center; gap:8px; margin-left:auto}
.group-heading{margin:1.5rem 0 .75rem; font-size:1rem; color:var(--text-secondary)}
.blob.accent-cyan{background:var(--cyan)}
.blob.accent-pink{background:var(--pink)}
.blob.accent-purple{background:var(--purple)}
.blob.accent-amber{background:var(--amber)}
button, a.button{
  font-family:'Zen Kaku Gothic New',sans-serif; font-size:.85rem; font-weight:700;
  padding:.7rem 1.1rem; border-radius:10px; border:1px solid transparent;
  cursor:pointer; text-decoration:none; display:inline-block; transition:all .15s ease-out;
  background:linear-gradient(135deg,var(--cyan),#67e8f9); color:#04141a;
}
button:hover, a.button:hover{transform:translateY(-2px); box-shadow:0 8px 24px var(--cyan-glow)}
button:disabled{opacity:.45; cursor:not-allowed; transform:none; box-shadow:none}
button.secondary, a.button.secondary{
  background:transparent; border-color:var(--border-bright); color:var(--text-primary);
}
button.secondary:hover, a.button.secondary:hover{background:var(--bg-surface-hover); box-shadow:none}
/* Releasing another device is destructive to whoever is on it, so it does not
   get the cyan the ordinary actions wear. */
button.danger{
  background:transparent; color:var(--red);
  border:1px solid rgba(248,113,113,.4);
}
button.danger:hover{background:rgba(248,113,113,.1); box-shadow:none}
input{
  font-family:'IBM Plex Mono',monospace; font-size:.85rem;
  padding:.7rem .8rem; border-radius:10px; width:100%;
  border:1px solid var(--border-bright); background:#0a0a12; color:var(--text-primary);
}
input:focus{outline:none; border-color:var(--cyan); box-shadow:0 0 0 3px var(--cyan-dim)}
select{
  font-family:'Zen Kaku Gothic New',sans-serif; font-size:.85rem;
  padding:.7rem .8rem; border-radius:10px;
  border:1px solid var(--border-bright); background:#0a0a12; color:var(--text-primary);
}
.row{display:flex; gap:.75rem; align-items:center; flex-wrap:wrap}
.grow{flex:1; min-width:12rem}
.grid{display:grid; gap:1rem; grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}
table{width:100%; border-collapse:collapse; margin-top:.5rem}
th,td{text-align:left; padding:.7rem .5rem; border-bottom:1px solid var(--border); vertical-align:top}
th{
  font-family:'IBM Plex Mono',monospace; font-weight:500; font-size:.7rem;
  letter-spacing:.04em; text-transform:uppercase; color:var(--text-muted);
}
td.key{font-family:'IBM Plex Mono',monospace; font-size:.8rem; word-break:break-all}
.stat{font-family:'Sora',sans-serif; font-size:2rem; font-weight:800; line-height:1.1}
.stat-label{font-family:'IBM Plex Mono',monospace; font-size:.7rem; color:var(--text-muted); letter-spacing:.04em}
nav.tabs{display:flex; gap:.5rem; margin:0 0 1.25rem; flex-wrap:wrap}
nav.tabs button{
  background:transparent; border:1px solid var(--border); color:var(--text-secondary);
  font-weight:500; padding:.5rem .9rem; border-radius:999px;
}
nav.tabs button:hover{transform:none; box-shadow:none; background:var(--bg-surface-hover)}
nav.tabs button[aria-selected="true"]{
  border-color:var(--cyan); color:var(--cyan); background:var(--cyan-dim);
}
/* `hidden` is a UA `display:none`, so ANY author display rule beats it — and
   .row is display:flex, which is how the reason field for a second HWID reset
   was on screen before the server had asked for one. Author-level, on
   everything, once. */
[hidden]{display:none !important}
.panel[hidden]{display:none}
.notice{
  margin-top:.75rem; padding:.7rem .9rem; border-radius:10px; font-size:.85rem;
  border:1px solid var(--border-bright); background:var(--bg-surface); white-space:pre-wrap;
}
.notice.ok{border-color:var(--cyan); background:var(--cyan-dim)}
.notice.bad{border-color:rgba(248,113,113,.5); background:rgba(248,113,113,.1)}
.notice[hidden]{display:none}
.empty{color:var(--text-muted); padding:1rem 0}
.skeleton{color:var(--text-muted)}
.crumb{color:var(--text-secondary); text-decoration:none}
.crumb:hover{color:var(--text-primary)}
.mono{font-family:'IBM Plex Mono',monospace}
.download-explainer{margin:-.2rem 0 1.25rem}
.download-section + .download-section{margin-top:1.5rem}
.download-section h3{font-family:'Sora',sans-serif; font-size:1rem; margin:1.25rem 0 .75rem}
.download-section h4{
  color:var(--text-secondary); font-family:'IBM Plex Mono',monospace;
  font-size:.72rem; font-weight:500; letter-spacing:.05em; margin:1rem 0 .5rem;
  text-transform:uppercase;
}
.download-card{
  --product-accent:var(--cyan); --product-accent-dim:var(--cyan-dim);
  background:var(--bg-surface); border:1px solid var(--border); border-left:3px solid var(--product-accent);
  border-radius:12px; margin:.6rem 0; overflow:hidden;
}
.download-card.accent-pink{--product-accent:var(--pink); --product-accent-dim:var(--pink-dim)}
.download-card.accent-purple{--product-accent:var(--purple); --product-accent-dim:var(--purple-dim)}
.download-card.accent-amber{--product-accent:var(--amber); --product-accent-dim:rgba(251,191,36,.1)}
.download-card-head{display:flex; align-items:center; flex-wrap:wrap; gap:.5rem; padding:1rem 1rem .7rem}
.download-card h5{font-family:'Sora',sans-serif; font-size:1rem; margin:0; margin-right:auto}
.download-choices{display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:1px; background:var(--border)}
.download-choice{background:var(--bg-elevated); padding:.85rem 1rem}
.release{padding:1rem 0; border-bottom:1px solid var(--border)}
.release:last-child{border-bottom:0; padding-bottom:0}
.dl-release-name{font-family:'Sora',sans-serif; font-size:.9rem; font-weight:700; margin:0 0 .45rem}
.platform-download{
  align-items:center; background:transparent; border:1px solid currentColor; border-radius:9px;
  display:inline-flex; font-weight:700; gap:.5rem; padding:.55rem .75rem; text-decoration:none;
}
.platform-download:hover{background:var(--product-accent-dim); transform:translateY(-1px)}
.platform-download.platform-windows{color:#78b9ff}
.platform-download.platform-android{color:#8bd34a}
.platform-download .platform-icon{width:1.25em; height:1.25em}
.dl-meta{color:#9999aa; font-size:.8rem; margin:.45rem 0 .25rem}
.version-tag{color:#aaaabc; margin-top:.1rem}
.dl-hash{
  font-family:'IBM Plex Mono',monospace; font-size:.7rem; color:#9999aa;
  margin-top:.35rem;
}
.dl-hash summary{cursor:pointer; width:max-content}
.dl-hash code{display:block; margin-top:.35rem; overflow-wrap:anywhere; user-select:all}
.download-catalog{border-top:1px solid var(--border); margin-top:1.5rem; padding-top:1rem}
.download-catalog > summary{color:var(--text-secondary); cursor:pointer; font-weight:700}
.download-catalog[open] > summary{margin-bottom:.5rem}
footer{margin-top:3rem; color:var(--text-muted); font-size:.8rem; text-align:center}
footer a{color:var(--text-secondary)}
/* A five-column table does not survive a phone. Measured at 393px before this
   existed: the key column was 25px wide — a 19-character licence key in about
   two characters — and the headers were clipped to `製品 状態 キ 期限 トライ`.
   Purely additive, so the desktop layout above is untouched: below 640px each
   row becomes a small block and every cell states its own column via the
   `data-label` that `labelCells` copies off the header. */
@media (max-width:640px){
  .card{padding:1.25rem}
  th,td{padding:.55rem .35rem; font-size:.8rem}
  /* auto-fit at minmax(180px,1fr) puts one stat per row here, and three cards
     2rem tall each is the entire first screen of a phone spent on counts. */
  .grid{grid-template-columns:repeat(3,1fr); gap:.5rem}
  .grid .card{padding:.9rem .6rem; margin-bottom:0}
  .stat{font-size:1.4rem}
  .stat-label{font-size:.6rem; letter-spacing:0}
  table thead{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0)}
  table, table tbody, table tr, table td{display:block; width:100%}
  table tr{border-bottom:1px solid var(--border); padding:.5rem 0}
  table tr:last-child{border-bottom:none}
  table td{border:none; padding:.25rem 0; display:flex; gap:.75rem; align-items:baseline}
  table td::before{
    content:attr(data-label); flex:0 0 5.5rem; color:var(--text-muted);
    font-family:'IBM Plex Mono',monospace; font-size:.7rem; letter-spacing:.04em;
  }
  table td[colspan]::before{content:none}
  td.key{font-size:.75rem}
  .download-card-head{padding:.85rem}
  .download-choices{grid-template-columns:1fr}
}
