/* ============================================================================
   manage.css - page styles for /manage/ only.

   prospira.css carries .card, the .btn family, .eyebrow, the nav, the footer and
   the starfield. None of that is restated here.

   THE RULE THIS PAGE IS BUILT ON: colour appears only where it reports a
   condition. Two things on the page are lit - the state lamp (green working,
   amber needs a decision, grey nothing) and the device slot (royal when the
   licence is occupying it). Everything else, the price included, is navy and
   grey. A billing screen has to read as calm and exact; the moment the price
   glows it starts selling, and this page is not a selling surface.

   Data is set in the mono face with tabular figures - every amount, date, slot
   count and column head. That single typographic decision is what makes the page
   read as a record of what you own rather than a page about a product.
   ========================================================================== */

/* Top: the nav is sticky (top:14px, ~64-66px tall), so its bottom edge sits at
   about y=80 and content passes UNDER it on scroll - this padding only decides how
   the page looks at rest.

   Bottom is 0: the whole gap below the panel is owned by the #site-footer override
   further down, so it is one number to read instead of two to add up. */
.mg {
  padding: clamp(48px, 5vw, 64px) 18px 0;
}

/* The shared sheet gives #site-footer margin-top: var(--section-gap) - 128px on a
   desktop - and that is right for the landing pages, where tall scrolling sections
   need a real break before the footer. On a single-panel task page it stacked with
   this page's own bottom padding into 160px of nothing, under a panel sitting just
   64px below the nav. Same expression as the top padding above, so the panel has
   equal air on both sides. This wins over the shared rule on load order rather
   than force: equal specificity (1,0,0), and this sheet is linked second. */
#site-footer { margin-top: clamp(48px, 5vw, 64px); }

/* --- footer at the bottom on a short page --------------------------------
   The shared sheet gives body { min-height: 100vh }. On a block-level body that
   slack piles up AFTER the last child, so an empty cart - about 400px of content
   in a 1000px window - left 122px of bare background sitting BELOW the footer,
   which reads as a broken page rather than a short one.

   Making the body a column and letting <main> absorb the slack puts the footer at
   the bottom of the window with nothing after it. Scoped to this page rather than
   done in the shared sheet: the landing pages are always taller than the viewport,
   so min-height never bites there, and turning body into a flex container on all
   19 pages to fix two of them is a much bigger blast radius than the bug.

   Sticky positioning on the header is unaffected - a sticky flex item still sticks
   to the scroll container - and this was re-measured after the change. */
.page-manage { display: flex; flex-direction: column; }
.page-manage > main { flex: 1 0 auto; }


/* Wider than the 640px receipt on /cart/: this page carries a two-up row and a
   five-column table, and at 720px the halves were too narrow to stop competing. */
.mg-wrap {
  width: min(880px, 100%);
  margin-inline: auto;
}

/* --- header ---------------------------------------------------------------
   Centred, the same as every other page on the site. The reference this page was
   built from opens left-aligned, which is the convention for a dashboard - but
   consistency across the site wins over the convention borrowed from one
   reference, and the owner made that call.

   Spacing here is deliberately identical to /cart/: 26px below the block, 12px
   between eyebrow and title. */
.mg-head {
  margin-bottom: 26px;
  text-align: center;
}
/* .eyebrow is inline-flex in the shared sheet, so it is only as wide as its own
   text and text-align on the element itself does nothing - it has to be centred
   as a box. Making it block-level also drops the ~5px of line leading an
   inline-flex element carries: that is exactly why this page measured 69px below
   the nav where /cart/ measured 64px, so centring it and matching the spacing to
   /cart/ turn out to be one and the same fix. */
.mg > .mg-wrap > .mg-head > .eyebrow { display: flex; width: fit-content; margin-inline: auto; }
.mg-title {
  margin: 12px 0 0;
  font-size: clamp(1.9rem, 4.4vw, 2.5rem);
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 1.05;
  color: var(--text-hi);
}
/* The site's one type flourish, used the same way as everywhere else. */
.mg-title em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.012em;
  background: linear-gradient(115deg, #d4e2ff 8%, #7fa2ff 54%, var(--accent) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
/* auto side margins so the 52ch measure stays centred rather than hugging the
   left edge of an 880px column */
.mg-sub {
  margin: 10px auto 0;
  max-width: 52ch;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--text-mid);
}

/* --- loading -------------------------------------------------------------
   The shape of the answer is known before it arrives, so the page holds it.
   A spinner would collapse the layout and then push it back down. */
.mg-skeleton {
  display: grid;
  gap: 14px;
  grid-template-columns: 1fr 1fr;
}
.mg-sk {
  display: block;
  border-radius: var(--r-panel);
  background: linear-gradient(180deg, rgba(16, 22, 44, 0.5), rgba(10, 14, 30, 0.36));
  border: 1px solid var(--border-soft);
}
.mg-sk-lic { grid-column: 1 / -1; height: 224px; }
.mg-sk-half { height: 148px; }
@media (prefers-reduced-motion: no-preference) {
  .mg-sk { animation: mg-breathe 1.5s var(--ease-out) infinite alternate; }
  /* opacity only: a moving gradient would repaint a 700px box every frame */
  @keyframes mg-breathe { from { opacity: 0.55; } to { opacity: 0.85; } }
}
@media (max-width: 720px) {
  .mg-skeleton { grid-template-columns: 1fr; }
}

.mg-panel + .mg-panel,
.mg-licence + .mg-grid,
.mg-grid + .mg-panel { margin-top: 14px; }
.mg-body { padding: 20px 22px 22px; }
.mg-center { text-align: center; padding: 32px 22px 30px; }

.mg-h2 {
  margin: 0;
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--text-hi);
}
.mg-note {
  margin: 8px 0 0;
  font-size: 0.92rem;
  line-height: 1.65;
  color: var(--text-mid);
}
.mg-center .mg-note { margin: 8px auto 20px; max-width: 42ch; }

/* screen-reader-only, for the table's action column head */
.mg-sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- the licence ---------------------------------------------------------
   The one panel that gets weight: a lit top rim and a state-tinted wash, so the
   thing you actually own is unmistakably the subject of the page. */
.mg-licence {
  position: relative;
  padding: 22px 24px 24px;
  overflow: hidden;
  background:
    radial-gradient(76% 128% at 50% -16%, rgba(var(--mg-accent, 42, 91, 255), 0.2), transparent 66%),
    linear-gradient(180deg, rgba(18, 26, 56, 0.5), rgba(10, 14, 30, 0.3));
}
.mg-licence::after {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 52%;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.9) 50%, transparent);
  box-shadow: 0 0 12px 1px rgba(var(--mg-accent, 42, 91, 255), 0.7);
  pointer-events: none;
}

/* Green when it is working, amber when it needs a decision, grey when there is
   nothing. The lamp fill and glow are declared here too, so a state's whole
   appearance lives in ONE selector list - it used to be set from JS as well,
   which meant every state existed twice and adding one meant remembering both.
   The grey group declares neither and keeps the fallbacks, which is also why
   nothing lights up before the first render. */
.mg-licence[data-state="active"],
.mg-licence[data-state="lifetime"] {
  --mg-accent: 74, 222, 128;
  --mg-lamp: #6ee7a0;
  --mg-lamp-glow: 0 0 0 3px rgba(var(--mg-accent), 0.16), 0 0 11px -1px rgba(var(--mg-accent), 0.9);
}
.mg-licence[data-state="canceling"],
.mg-licence[data-state="past_due"] {
  --mg-accent: 245, 180, 95;
  --mg-lamp: #f5c069;
  --mg-lamp-glow: 0 0 0 3px rgba(var(--mg-accent), 0.16), 0 0 11px -1px rgba(var(--mg-accent), 0.9);
}
.mg-licence[data-state="none"] { --mg-accent: 120, 140, 190; }

.mg-lic-state {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 16px;
}
.mg-lamp {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  flex-shrink: 0;
  background: var(--mg-lamp, rgba(150, 170, 210, 0.5));
  box-shadow: var(--mg-lamp-glow, none);
}
.mg-state {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-low);
}

/* Name left, price right - the shape a statement uses, and the reason the two
   facts that matter can be read in one glance instead of two. */
.mg-lic-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px 26px;
  flex-wrap: wrap;
}
.mg-lic-name { flex: 1 1 22ch; min-width: 0; }
.mg-plan {
  margin: 0;
  font-size: clamp(1.45rem, 3.6vw, 1.9rem);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.1;
  color: var(--text-hi);
}
.mg-lic-name .mg-note { max-width: 46ch; }

/* Big, tabular, and deliberately without colour. */
.mg-price {
  margin: 0;
  flex: 0 0 auto;
  text-align: right;
  line-height: 1;
}
.mg-price-amount {
  display: block;
  font-family: var(--font-mono);
  font-size: clamp(1.5rem, 3.6vw, 1.95rem);
  font-weight: 600;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--text-hi);
}
.mg-price-term {
  display: block;
  margin-top: 7px;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-low);
}

/* --- facts strip ---------------------------------------------------------
   Label above value, both in mono: a date on a billing screen is data to check,
   not a sentence to read. */
.mg-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 40px;
  margin: 20px 0 0;
  padding-top: 17px;
  border-top: 1px solid var(--border-soft);
}
.mg-fact { min-width: 0; }
.mg-fact dt {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-low);
}
.mg-fact dd {
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-hi);
}

.mg-lic-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 22px;
}

/* Loud on purpose, and only ever shown for a declined card - the one state where
   doing nothing quietly costs the person their access. */
.mg-alert {
  margin: 18px 0 0;
  padding: 12px 14px;
  border-radius: 11px;
  border: 1px solid rgba(245, 180, 95, 0.34);
  background: rgba(245, 165, 65, 0.08);
  font-size: 0.89rem;
  line-height: 1.55;
  color: #f0dcc0;
}

.mg-error {
  margin: 0 0 18px;
  padding: 12px 14px;
  border-radius: 11px;
  border: 1px solid rgba(255, 120, 120, 0.34);
  background: rgba(255, 80, 80, 0.07);
  font-size: 0.89rem;
  line-height: 1.55;
  color: #ffc2c2;
}

/* --- the two halves ------------------------------------------------------ */
.mg-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.mg-grid > .mg-panel + .mg-panel { margin-top: 0; }
@media (max-width: 720px) {
  .mg-grid { grid-template-columns: 1fr; }
}

/* --- THE SIGNATURE: the device slot -------------------------------------
   A Prospira licence runs on exactly one machine. That is the defining
   constraint of the product and it used to be a sentence of prose next to a
   pill. Here it is the thing itself: one bay, filled or empty.

   Royal, not green - the lamp above already owns "is this healthy", so the slot
   speaks about occupancy instead. Two lights on the page, two different
   questions, no overlap. */
.mg-seat {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 14px;
}
.mg-seat-bay {
  position: relative;
  width: 42px;
  height: 24px;
  flex-shrink: 0;
  border-radius: 7px;
  border: 1px solid var(--border-mid);
  background: rgba(4, 6, 16, 0.5);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.5);
  overflow: hidden;
}
.mg-seat-fill {
  position: absolute;
  inset: 3px;
  border-radius: 4px;
  background: transparent;
  transition: background var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.mg-seat[data-state="on"] .mg-seat-bay { border-color: rgba(120, 160, 255, 0.42); }
.mg-seat[data-state="on"] .mg-seat-fill {
  background: linear-gradient(180deg, rgba(120, 160, 255, 0.95), var(--accent));
  box-shadow: 0 0 14px -2px rgba(42, 91, 255, 0.85), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.mg-seat[data-state="locked"] .mg-seat-bay { border-color: rgba(245, 180, 95, 0.42); }
.mg-seat[data-state="locked"] .mg-seat-fill {
  background: repeating-linear-gradient(115deg, rgba(245, 180, 95, 0.8) 0 5px, rgba(120, 80, 20, 0.55) 5px 10px);
}
.mg-seat-read {
  font-family: var(--font-mono);
  font-size: 0.86rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  color: var(--text-mid);
}
.mg-seat[data-state="on"] .mg-seat-read { color: var(--text-hi); }

.mg-bill-btn { margin-top: 16px; }

/* Covers the licence panel's primary as well: in the past-due and canceling
   states that anchor IS the action, and it lives outside any action group.
   Anchors ignore the disabled property, so the pointer has to be taken away. */
.mg .btn.is-busy {
  opacity: 0.72;
  cursor: progress;
  pointer-events: none;
}

/* Link in weight, button in behaviour. The global button reset already clears
   background, border, padding, font and cursor, so only the differences are here.
   Padded to a real tap target: at 0.86rem with no padding this was a 23px strip,
   and it is one of the two destructive controls on the page. It only turns red on
   approach, so the page is not shouting a warning at someone who came to check a
   date. */
.mg-quiet {
  margin-left: auto;
  padding: 10px 4px;
  font-size: 0.86rem;
  color: var(--text-low);
  transition: color var(--dur-1) var(--ease-out), opacity var(--dur-1) var(--ease-out);
}
.mg-quiet:hover { color: #ffb4b4; text-decoration: underline; }
.mg-quiet:disabled { opacity: 0.5; cursor: progress; text-decoration: none; }

/* --- sessions table -----------------------------------------------------
   Hairline rows, not a stack of boxes: a column of money should be readable
   straight down, which is the whole reason this is a table. */
.mg-table-wrap {
  margin-top: 14px;
  overflow-x: auto;
  /* the table has a floor width, so on a narrow phone this scrolls rather than
     crushing the amount column into two lines */
  -webkit-overflow-scrolling: touch;
}
.mg-table {
  width: 100%;
  min-width: 460px;
  border-collapse: collapse;
  text-align: left;
}
.mg-table th {
  padding: 0 14px 10px 0;
  border-bottom: 1px solid var(--border-soft);
  font-family: var(--font-mono);
  font-size: 0.65rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--text-low);
  white-space: nowrap;
}
.mg-table td {
  padding: 13px 14px 13px 0;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.9rem;
  color: var(--text-mid);
  vertical-align: middle;
}
.mg-table tr:last-child td { border-bottom: 0; }
.mg-table th:last-child,
.mg-table td:last-child { padding-right: 0; }
/* Amounts and dates in mono so the columns line up digit under digit. */
.mg-table .mg-num {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.mg-table td.mg-num { color: var(--text-hi); font-weight: 500; }
.mg-table .mg-when {
  font-family: var(--font-mono);
  font-size: 0.84rem;
  color: var(--text-low);
  white-space: nowrap;
}
.mg-table .mg-what { color: var(--text-hi); font-weight: 500; }
/* The session time goes under the package name instead of into a fifth column: the
   table already scrolls sideways below its 460px floor, and one more column would
   push the amount off a phone completely. */
.mg-table .mg-sub {
  display: block;
  margin-top: 4px;
  font-weight: 400;
}
/* The link stacks under the time rather than beside it. Both are nowrap, and side
   by side they widen the first column enough to push the amount past the edge of a
   phone - a table cell cannot be capped with max-width without table-layout:fixed,
   so the fix is a line break, not a width. */
.mg-table .mg-sub .mg-ticket {
  display: block;
  margin-top: 3px;
}
/* Same amber the status column uses for refunded and disputed - a clash is the same
   kind of thing: paid, but needing a person. */
.mg-clash {
  font-size: 0.84rem;
  color: #f0cfa0;
}
.mg-ticket {
  font-size: 0.84rem;
  color: #8ea8ff;
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 1px solid transparent;
}
.mg-ticket:hover { border-bottom-color: currentColor; }

/* Status reads as a word with a dot, not a filled capsule: on a page whose only
   colour is state, a row of bright pills would out-shout the licence above. */
.mg-st {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-low);
  white-space: nowrap;
}
.mg-st::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 999px;
  flex-shrink: 0;
  background: currentColor;
}
.mg-st[data-tone="on"] { color: #7ddba6; }
.mg-st[data-tone="warn"] { color: #f0cfa0; }

/* --- tags (device pill in the signed-out and other small spots) ---------- */
.mg-tag {
  display: inline-flex;
  align-items: center;
  padding: 4px 11px 5px;
  border-radius: 999px;
  border: 1px solid var(--border-mid);
  background: rgba(150, 180, 255, 0.06);
  font-size: 0.78rem;
  color: var(--text-mid);
  white-space: nowrap;
}

/* --- cancel dialog --------------------------------------------------------- */
.mg-modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  place-items: center;
  height: 100vh;
  height: 100dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 16px;
  background: rgba(2, 3, 10, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.mg-modal-panel {
  width: min(480px, 100%);
  max-height: calc(100dvh - 32px);
  display: flex;
  flex-direction: column;
  border-radius: var(--r-panel);
  border: 1px solid var(--border-strong);
  /* No backdrop-filter: the overlay already blurs the page and this panel is
     nearly opaque, so a second readback would buy nothing. */
  background:
    radial-gradient(120% 90% at 10% 0%, rgba(42, 91, 255, 0.14), transparent 58%),
    linear-gradient(180deg, rgba(13, 18, 38, 0.97), rgba(8, 12, 26, 0.98));
  box-shadow: 0 40px 90px -40px rgba(0, 0, 0, 0.9);
}
.mg-modal-body { padding: 22px 22px 4px; overflow-y: auto; }
.mg-modal-title {
  margin: 0 0 12px;
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-hi);
}
.mg-modal-body .mg-note + .mg-note { margin-top: 12px; }
.mg-modal-foot {
  display: grid;
  gap: 10px;
  padding: 20px 22px 22px;
}
.mg-modal-foot .btn { width: 100%; }
/* The way out sits last and quiet, under the two ways to stay - present, never
   hidden, and never dressed up as the obvious choice. */
.mg-quiet-danger {
  margin: 0 auto;
  padding: 12px 16px;
  color: var(--text-low);
}

/* --- phone ----------------------------------------------------------------- */
@media (max-width: 560px) {
  .mg-licence { padding: 20px 18px 22px; }
  .mg-body { padding: 18px; }
  .mg-center { padding: 28px 18px 26px; }
  /* Price under the name rather than beside it: at this width a right-aligned
     price left the plan name on two lines with a hole between them. */
  .mg-lic-head { flex-direction: column; gap: 14px; }
  /* The flex above has to be reset with the direction. `flex: 1 1 22ch` on
     .mg-lic-name means "at least 22 characters wide" only while the row runs
     horizontally - once this container becomes a column, flex-basis is a HEIGHT,
     so the name block was pinned to 233px tall for 82px of text and grow kept it
     there. That was 151px of dead space between the note and the price. */
  .mg-lic-name { flex: 0 0 auto; }
  .mg-price { text-align: left; }
  .mg-lic-actions { gap: 10px; }
  .mg-lic-actions .btn { width: 100%; }
  /* auto margin pushes it to its own line once the row wraps */
  .mg-quiet { margin-left: 0; }
  .mg-facts { gap: 14px 28px; }
}

/* -- Panel entrance -------------------------------------------------------
   Every licence panel is swapped in by JS once the API answers, so the scroll reveal
   cannot drive it. An IntersectionObserver never fires for a display:none
   element, and by the time the script un-hides one the observer has already
   disconnected - the panel would sit at opacity 0 permanently. Keying the
   animation to :not([hidden]) runs it exactly when the panel appears, whenever
   that is, and something already on screen can never be left waiting on a
   script. Same keyframe and same tokens as .reveal-item and .hero-item: one
   system, a third trigger for content that arrives on its own schedule. */
.mg-panel:not([hidden]),
.mg-error:not([hidden]) {
  animation: flow-card-in var(--reveal-dur) var(--reveal-ease) both;
}
@media (prefers-reduced-motion: reduce) {
  .mg-panel:not([hidden]), .mg-error:not([hidden]) { animation: none; }
}
