/* ============================================================================
   PawCheck creator hub — the signed-in shell.
   ----------------------------------------------------------------------------
   Structure is ValuScan's (fixed sidebar, one scrolling main, drawer under a
   breakpoint). Every colour, radius and font is PawCheck's, from site.css's
   token block — no literals except inside rgba() shadows, so the whole hub
   follows the light/dark toggle for free.

   ⚠️ THE SITE HEADER IS `position: fixed` AT 68px (site.css:158). The shell sits
   under it rather than replacing it, so the brand and theme toggle stay put.
   ========================================================================== */

:root { --hub-header: 68px; --hub-side-w: 248px; }

.hub-shell {
  display: flex;
  align-items: stretch;
  min-height: calc(100vh - var(--hub-header));
}

/* ---- Sidebar -------------------------------------------------------------- */
.hub-side {
  position: sticky;
  top: var(--hub-header);
  align-self: flex-start;
  height: calc(100vh - var(--hub-header));
  width: var(--hub-side-w);
  flex: none;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--line);
  background: var(--card);
}

.hub-side-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 22px 20px 10px;
}
.hub-eyebrow {
  font-size: 0.72rem;
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.hub-drawer-close {
  display: none;
  font-size: 1rem;
  color: var(--ink-soft);
  padding: 4px 8px;
}

.hub-nav { flex: 1; padding: 6px 12px; overflow-y: auto; }
.hub-nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  margin-bottom: 4px;
  border-radius: var(--radius-sm);
  font-weight: 800;
  font-size: 0.96rem;
  color: var(--ink-soft);
  border: 1px solid transparent;
  transition: background 0.15s ease, color 0.15s ease;
}
.hub-nav-item:hover { background: var(--card-sunken); color: var(--ink); }
/*  Active is a filled tint plus the brand blue, not a left-edge bar: at this
    width a 3px rule reads as a rendering artefact rather than a state. */
.hub-nav-item.is-active {
  background: var(--blue-tint);
  border-color: var(--blue);
  color: var(--blue-deep);
}
.hub-nav-icon { width: 19px; height: 19px; flex: none; display: grid; place-items: center; }
.hub-nav-icon svg { width: 19px; height: 19px; }
.hub-nav-label { flex: 1; }
.hub-badge {
  background: var(--score-red);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 900;
  border-radius: 999px;
  padding: 2px 7px;
  min-width: 20px;
  text-align: center;
}

.hub-side-foot { padding: 14px 16px 18px; border-top: 1px solid var(--line); }
.hub-whoami { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; }
.hub-avatar {
  width: 36px; height: 36px; flex: none;
  border-radius: 50%;
  background: var(--brand-gradient);
  color: #fff;
  display: grid; place-items: center;
  font-weight: 1000; font-size: 0.95rem;
}
.hub-whoami-text { min-width: 0; }
.hub-whoami-name {
  font-size: 0.92rem; font-weight: 900; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hub-whoami-plan { font-size: 0.78rem; font-weight: 800; color: var(--blue); }
.hub-signout { width: 100%; padding: 9px 14px; font-size: 0.88rem; }

/* ---- Main ----------------------------------------------------------------- */
.hub-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

/*  Only shown under the breakpoint — on desktop the sidebar IS the navigation
    and a second title bar would just repeat it. */
.hub-topbar {
  display: none;
  align-items: center;
  gap: 14px;
  height: 56px;
  padding: 0 18px;
  border-bottom: 1px solid var(--line);
  background: var(--card);
}
.hub-burger { font-size: 1.25rem; color: var(--ink); }
.hub-topbar-title { font-size: 1.05rem; font-weight: 1000; }

.hub-scroll { flex: 1; }

/*  ⚠️ WIDE AND CENTRED, NOT NARROW AND LEFT-PINNED.
    This was `max-width: 820px` with no `margin`, which on a laptop left roughly
    a third of the window empty on the RIGHT while every card crushed itself
    against the left edge — the page looked broken rather than roomy.

    Still capped rather than full-bleed: a line of body copy running the whole
    width of a 27" display is genuinely harder to read than one that does not.
    The cap is generous enough that the four stat tiles, the message thread and
    the payout table all get real room, and `margin: 0 auto` puts whatever is
    left over on BOTH sides instead of all of it on one. */
.hub-page {
  padding: 34px 40px 72px;
  max-width: 1180px;
  margin: 0 auto;
  width: 100%;
}

.hub-page h1 {
  font-size: clamp(1.7rem, 3.4vw, 2.3rem);
  line-height: 1.12;
  letter-spacing: -0.015em;
  margin-bottom: 8px;
}
.hub-page > p.sub { color: var(--ink-soft); font-size: 1.03rem; margin-bottom: 28px; }

.hub-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(10, 22, 34, 0.42);
  backdrop-filter: blur(2px);
}

/* ---- Drawer --------------------------------------------------------------- */
@media (max-width: 960px) {
  .hub-topbar { display: flex; }
  .hub-drawer-close { display: block; }
  .hub-side {
    position: fixed;
    top: 0; left: 0;
    height: 100vh;
    z-index: 70;
    transform: translateX(-100%);
    transition: transform 0.24s ease;
    box-shadow: var(--shadow-lg);
  }
  .hub-side.is-open { transform: translateX(0); }
  .hub-page { padding: 24px 20px 64px; }
}

/* On the shell, the page frame's own top padding is the topbar's job. */
.creator-main:has(.hub-shell) { padding: var(--hub-header) 0 0; }
.creator-main:has(.hub-shell) > .container { max-width: none; padding: 0; }

/* ---- Onboarding walk ------------------------------------------------------
   ⚠️ The close control is ABSENT while a blocking step is unfinished, never
   dimmed. A disabled X invites the same tap forever; a missing one says the
   screen means it. */
.ob-root { position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; padding: 18px; }
.ob-scrim { position: absolute; inset: 0; background: rgba(10, 22, 34, 0.55); backdrop-filter: blur(3px); }
.ob-panel {
  position: relative;
  width: min(520px, 100%);
  max-height: min(90vh, 720px);
  overflow-y: auto;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: 26px 26px 20px;
}
.ob-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.ob-count { font-size: 0.75rem; font-weight: 900; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-faint); }
.ob-x { font-size: 1rem; color: var(--ink-soft); padding: 4px 8px; border-radius: 8px; }
.ob-x:hover { background: var(--card-sunken); }
.ob-dots { display: flex; gap: 6px; margin-bottom: 20px; }
.ob-dot { height: 4px; flex: 1; border-radius: 999px; background: var(--line); }
.ob-dot.is-done { background: var(--green); }
.ob-dot.is-now { background: var(--blue); }
.ob-panel h2 { font-size: 1.5rem; line-height: 1.15; margin-bottom: 10px; }
.ob-lede { color: var(--ink-soft); line-height: 1.55; margin-bottom: 14px; }
.ob-note { color: var(--ink-faint); font-size: 0.9rem; font-weight: 700; margin-bottom: 4px; }
.ob-list { color: var(--ink-soft); line-height: 1.7; padding-left: 18px; margin-bottom: 6px; font-size: 0.96rem; }
.ob-list strong { color: var(--ink); }
.ob-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 22px; }
.ob-foot-right { display: flex; gap: 8px; }
.ob-foot .btn { padding: 11px 20px; font-size: 0.94rem; }
/*  The password step's failure route and its quiet sign-out. No skip lives
    here, by design — see onboarding/dialog.js. */
.ob-recover { margin-top: 14px; }
.ob-recover .btn { padding: 9px 16px; font-size: 0.9rem; }
.ob-recover .ob-note { margin-top: 10px; color: var(--ink-soft); }
.ob-signout { margin-top: 14px; text-align: center; color: var(--ink-faint); font-size: 0.86rem; font-weight: 700; }
/*  Google/Apple accounts setting a creator password (owner's decision 2). */
.ob-note.ob-warn { color: var(--ink-soft); background: var(--sunshine-tint); border: 1px solid var(--sunshine); border-radius: 10px; padding: 10px 12px; margin: 12px 0 4px; }
/*  "You're still paying Apple" (onboarding/dialog.js billingNoticeHtml). */
.billing-notice { margin: 14px 0; padding: 16px 18px; }
.billing-notice h2 { font-size: 1.05rem; margin-bottom: 6px; }
.billing-notice p { font-size: 0.93rem; }

/* ---- Page furniture ------------------------------------------------------- */
.hub-cards { display: grid; gap: 14px; }
.hub-row { display: flex; justify-content: space-between; gap: 12px; padding: 11px 0; border-top: 1px solid var(--line); }
.hub-row:first-child { border-top: none; }
.hub-muted { color: var(--ink-faint); font-size: 0.85rem; }

/* ============================================================================
   PAGE PRIMITIVES — the pieces js/pages/* build out of.
   ----------------------------------------------------------------------------
   Added when the five routed pages landed. Everything above this line is the
   frame; everything below is what goes inside it.

   ⚠️ `.unknown` IS THE MOST IMPORTANT RULE IN THIS FILE. It is what makes an
   em dash look like an absence rather than a number. The backend goes to real
   trouble to keep "zero" and "we could not measure it" apart, format.js keeps
   them apart in text, and this keeps them apart on sight.
   ========================================================================== */

/* The frame's own footer is the sidebar's job once the shell is up. */
body:has(.hub-shell) .site-footer { display: none; }

.hub-section { margin-bottom: 34px; }
.hub-section > h2 {
  font-size: 1.18rem;
  font-weight: 1000;
  margin-bottom: 12px;
  letter-spacing: -0.01em;
}
.hub-section .panel + .panel { margin-top: 12px; }
.section-lede { color: var(--ink-soft); margin-bottom: 14px; font-size: 0.95rem; line-height: 1.55; }

.hub-row-v { font-weight: 900; white-space: nowrap; text-align: right; }
.hub-more { margin-top: 14px; font-weight: 800; }
.hub-more a { color: var(--blue); }

.badge-note.center { text-align: center; }
.badge-note.warn { color: var(--score-amber); }

/*  An unmeasured figure is dimmed and lightened, so a column of numbers with one
    em dash in it reads as "this one is missing" rather than "this one is zero". */
.stat-value.unknown { color: var(--ink-faint); font-weight: 800; }
.stat-note { font-size: 0.78rem; color: var(--ink-faint); margin-top: 4px; font-weight: 700; }

.big-figure {
  font-size: 1.9rem;
  font-weight: 1000;
  color: var(--green-deep);
  display: inline-block;
  margin-right: 8px;
  line-height: 1;
}
.money-explainer p + p { margin-top: 10px; }

.code-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 16px; }

.step-card h3 { font-size: 1.05rem; font-weight: 1000; margin-bottom: 6px; }
.step-card p { color: var(--ink-soft); line-height: 1.55; font-size: 0.95rem; }
.step-action { margin-top: 14px; }

/* ---- the Pro requirement bars --------------------------------------------
   ⚠️ THREE STATES, AND `is-unknown` IS NOT A FAILURE. An unmeasured bar is grey,
   never red: on PawCheck today every creator is `unproven` because the
   conversion pipeline has nothing in it yet, and painting our own missing data
   as their shortfall would threaten people over a number we never read.
   -------------------------------------------------------------------------- */
.bar-row { padding: 12px 0; border-top: 1px solid var(--line); }
.bar-row:first-child { border-top: none; padding-top: 0; }
.bar-head {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 0.94rem; font-weight: 800; margin-bottom: 8px;
}
.bar-track { height: 7px; border-radius: 999px; background: var(--card-sunken); overflow: hidden; }
.bar-fill { height: 100%; border-radius: 999px; background: var(--ink-faint); transition: width 0.3s ease; }
.bar-row.is-pass .bar-fill { background: var(--green); }
.bar-row.is-fail .bar-fill { background: var(--score-amber); }
.bar-row.is-unknown .bar-fill { background: var(--ink-faint); opacity: 0.45; }
.bar-row.is-unknown .bar-head strong { color: var(--ink-faint); font-weight: 800; }
.bar-head strong { white-space: nowrap; }

/* ---- Support --------------------------------------------------------------
   The thread is the page. Given the width back, it should use it — a chat
   column 820px wide inside a 1400px window was the worst offender.
   -------------------------------------------------------------------------- */
.thread-panel { display: flex; flex-direction: column; }
.thread {
  max-height: min(58vh, 620px);
  min-height: 180px;
  overflow-y: auto;
  margin-bottom: 16px;
  padding-right: 4px;
  overscroll-behavior: contain;
}
.thread-status { padding: 24px 0; text-align: center; }
.thread-empty { text-align: center; padding: 26px 12px; }
/*  ⚠️ SCOPED TO ITS OWN CLASS, NOT `.thread-empty span`. A bare descendant
    selector also catches the `.btn-label` span inside the retry button, which
    rendered "Try again" at 2rem — the same shape of mistake as a `.hero-scene
    img` rule burying the sparkles. Style the element you mean. */
.thread-empty .thread-empty-icon { font-size: 2rem; display: block; margin-bottom: 10px; }
.thread-empty p { color: var(--ink-soft); line-height: 1.55; margin-bottom: 8px; }
.thread-empty .btn { margin-top: 10px; }

.msg { display: flex; margin-bottom: 12px; }
.msg-mine { justify-content: flex-end; }
.msg-team { justify-content: flex-start; }
.msg-bubble { max-width: min(72%, 720px); border-radius: var(--radius-sm); padding: 11px 14px; background: var(--card-sunken); }
.msg-mine .msg-bubble { background: var(--blue-tint); }
.msg-meta { font-size: 0.75rem; font-weight: 800; color: var(--ink-faint); margin-bottom: 3px; }
.msg-body { white-space: pre-wrap; font-size: 0.94rem; line-height: 1.5; word-break: break-word; }

.payout-form { margin-top: 16px; }


/* ============================================================================
   USING THE WIDTH — layouts that earn the extra room rather than just stretching
   ========================================================================== */

/*  Two columns of cards once there is genuinely room for two. Below that they
    stack, which is what they already did. */
@media (min-width: 1100px) {
  .hub-cards.two-up { grid-template-columns: 1fr 1fr; }
}

/*  The number tiles were `repeat(4, 1fr)` at every size, so on a narrow window
    four tiles shared 820px and the figures wrapped. `auto-fit` + a floor lets
    them reflow instead of squeezing. */
.hub-page .stat-grid {
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

/*  The code plate is the first thing on Overview and the thing a creator
    actually came for. It should not be a full-bleed banner on a wide screen. */
.hub-page .code-card { max-width: 720px; margin-left: auto; margin-right: auto; }

/* ---- Forms inside the hub -------------------------------------------------
   ⚠️ The password and payout inputs inherit the marketing form's styling, which
   was written for a centred 680px card and looks stark at hub width. These keep
   them from running the full width of a wide panel, which reads as a text area
   rather than a field.
   -------------------------------------------------------------------------- */
.hub-page .field { max-width: 460px; }
/*  ⚠️ A COMPOSER IS NOT A FIELD. The 460px cap above keeps a password or a
    PayPal address from stretching into something that looks like a text area on
    a wide screen — but the Support message box IS a text area, and capping it
    put a narrow column inside a full-width panel, which was the exact "why is
    everything squashed to the left" complaint one level down. */
.hub-page .field.field-wide { max-width: none; }
.hub-page .field.field-wide textarea { min-height: 130px; }

/* ---- The password field ---------------------------------------------------
   ⚠️ THIS SCREEN IS A GIFT, NOT A SECURITY CHECKPOINT, and it used to look like
   the second one: a bare masked box with a rule underneath it, on a step that
   cannot be dismissed. Three changes, all aimed at the same feeling —
   a bigger, softer field; a Show toggle so nothing is happening in the dark;
   and a length cue that fills up rather than grading anybody.
   -------------------------------------------------------------------------- */
.field-pw { max-width: 460px; }
.pw-wrap { position: relative; display: block; }
.pw-wrap input {
  width: 100%;
  /*  Room for the Show button, which sits inside the field rather than beside
      it — a control outside the box reads as a second, unrelated thing. */
  padding-right: 88px;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
}
.pw-reveal {
  position: absolute;
  top: 50%;
  right: 10px;
  transform: translateY(-50%);
  /*  Sized to sit INSIDE the field's 2px border with air around it. It used to
      be 35px tall against a 24px unstyled input — taller than the box it was
      supposed to be inside. */
  height: 34px;
  display: grid;
  place-items: center;
  padding: 0 13px;
  border-radius: 10px;
  font-size: 0.84rem;
  font-weight: 900;
  color: var(--blue-deep);
  background: var(--blue-tint);
  border: none;
  cursor: pointer;
}
.pw-reveal:hover { background: var(--card-sunken); }

.pw-meter {
  height: 5px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
  margin-top: 10px;
}
.pw-meter span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 999px;
  background: var(--blue);
  transition: width 0.18s ease, background 0.18s ease;
}
/*  Green at the threshold. There is no red state, deliberately — see above. */
.pw-meter.is-ok span { background: var(--green); }

/* ---- Signed-out: password only --------------------------------------------
   The link/password tab strip that lived here is gone with the "Email me a
   link" form (2026-09-14). `.signin-notice` explains why the sign-in page
   opened instead of the dashboard (an expired "Open my dashboard" link, or one
   for an account that already has a password).
   -------------------------------------------------------------------------- */
.signin-notice {
  margin: -18px 0 18px;
  padding: 13px 15px;
  border-radius: var(--radius-sm);
  background: var(--blue-tint);
  border: 1px solid var(--line);
}
.signin-notice p { color: var(--ink); font-weight: 800; font-size: 0.94rem; line-height: 1.5; }
.signin-notice.is-warn { background: var(--rose-tint); border-color: var(--score-red); }
/*  "Email me a way back in" scrolls the email field to the top of the screen
    (boot.js openRecovery); clear the 68px fixed site header when it does. */
#f-pw-email, #pw-reset-send { scroll-margin-top: 88px; }

/*  ⚠️ `.field .pw-wrap input`, NOT `.pw-wrap input`, FOR THE SHOW BUTTON'S ROOM.
    creator.css's `.field input[type="password"]` is (0,2,1) and sets the
    `padding` shorthand, which beat `.pw-wrap input` (0,1,1) — so the 88px right
    padding above never applied and long passwords ran underneath the Show
    button. Same specificity here, and this file loads after creator.css. The
    `text` type is covered too because Show flips the input to it. */
.field .pw-wrap input { padding-right: 88px; }

/*  A whole-form message inside the password card. Not a .panel: a full panel
    inside a card is a box in a box, and this is one sentence. */
.signin-alert {
  margin-top: 18px;
  padding: 13px 15px;
  border-radius: var(--radius-sm);
  background: var(--rose-tint);
  border: 1px solid var(--score-red);
}
.signin-alert p { color: var(--ink); font-weight: 800; font-size: 0.94rem; line-height: 1.5; }
.signin-alert .signin-alert-sub { color: var(--ink-soft); font-weight: 700; font-size: 0.88rem; margin-top: 4px; }

/*  A button that reads as a link. Real <button>s because these do things on
    this page (open the recovery box) rather than go anywhere. */
.text-btn {
  font: inherit;
  font-weight: 800;
  color: var(--blue);
  background: none;
  border: none;
  padding: 2px 0;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.text-btn:hover { color: var(--blue-deep); }

.signin-aux { text-align: center; margin-top: 14px; font-size: 0.92rem; }

.signin-reset {
  margin-top: 14px;
  padding: 16px 18px;
  border-radius: var(--radius-sm);
  background: var(--card-sunken);
}
.signin-reset p { color: var(--ink-soft); font-size: 0.92rem; line-height: 1.55; font-weight: 600; }
.signin-reset p strong { color: var(--ink); }
.signin-reset a { color: var(--blue); font-weight: 800; }
/*  btn-quiet IS card-sunken — the same as this box — so it would vanish into it. */
.signin-reset .btn-quiet { background: var(--card); border-color: var(--line); }
.signin-reset .btn-quiet:hover:not(:disabled) { background: var(--blue-tint); }
.signin-reset .signin-reset-status { margin-top: 12px; color: var(--ink); font-weight: 700; }

@media (max-width: 480px) {
  .signin-reset .btn { width: 100%; }
}

/* ---- Loading, so a cold Cloud Function does not look like a hang -----------
   ⚠️ THE HUB USED TO AWAIT `creatorStats` BEFORE ITS FIRST PAINT, so a cold
   start showed a spinner on a blank page for as long as the round trip took.
   The shell and the page frame now paint immediately and the numbers arrive
   into these placeholders.
   -------------------------------------------------------------------------- */
@keyframes hub-shimmer { 0% { background-position: -420px 0; } 100% { background-position: 420px 0; } }
.is-loading .stat-value,
.skeleton {
  color: transparent !important;
  border-radius: 8px;
  background: linear-gradient(90deg, var(--card-sunken) 0%, var(--line) 50%, var(--card-sunken) 100%);
  background-size: 420px 100%;
  animation: hub-shimmer 1.1s linear infinite;
  user-select: none;
}
.skeleton-line { height: 13px; margin: 9px 0; }
.skeleton-line.w60 { width: 60%; }
.skeleton-line.w40 { width: 40%; }
@media (prefers-reduced-motion: reduce) {
  .is-loading .stat-value, .skeleton { animation: none; }
}
