/* /c/<hash> - public certificate page.
   The proof card itself is styled by track.css (which this
   page also loads). cert.css frames the card alongside a
   narrative explaining what the visitor is looking at and how
   to verify it independently. */

.cert-body { background: var(--bone); }

/* Sequential page-transition from /database into /c. The
   inline head script on /c sets html.cert-fading-in when the
   URL has ?fade=1 (set by /database's submit handler when a
   hash matches). cert.js paintProof flips the class to
   html.cert-faded-in once the cert has actually rendered, so
   the visitor sees: /database fades out → /c body invisible
   while loading → cert paints → /c body fades in. A 1.5 s
   failsafe in the inline script restores visibility if JS
   never gets there. The transition rule lives on the
   .cert-faded-in state, not the resting state, so unrelated
   class toggles elsewhere on the page don't accidentally
   trigger a fade. */
html.cert-fading-in body.cert-body { opacity: 0; }
html.cert-faded-in body.cert-body {
  opacity: 1;
  transition: opacity 300ms ease;
}

/* Outer page wrapper. Mobile: natural top-anchored flow with
   clamp-ed padding (the editorial grid stacks to a single
   column under 880 px and is too tall to centre vertically
   on a phone). Desktop: min-height: 100vh with safe centring
   so the grid sits vertically central above the fold. "safe"
   keeps the top of the cert visible if the viewport is ever
   shorter than the content (older laptops, browser chrome).
   var(--nav-height) keeps the visual centre below the sticky
   header rather than under it. */
.cert-page {
  padding:
    clamp(48px, 8vw, 96px)
    var(--rail-x)
    clamp(64px, 10vw, 128px);
  box-sizing: border-box;
}
@media (min-width: 881px) {
  /* .page-main (Base.astro's wrapper around <slot/>) already
     has min-height: calc(100svh - var(--nav-h)) and is a
     flex column. flex: 1 lets .cert-page fill that pre-
     computed visible-area-below-nav, so we don't duplicate
     the nav math here. Centring happens one level deeper on
     .cert-page-inner so the optional back-to-search chip can
     sit at the top of .cert-page without getting swept into
     the vertical-middle alongside the cert grid. */
  .cert-page {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding-top: clamp(32px, 4vw, 56px);
    padding-bottom: clamp(32px, 4vw, 56px);
  }
  .cert-page-inner {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: safe center;
  }
}
/* Back chip layout. Two jobs:
     1. Horizontal alignment with the cert-grid container so
        the chip's outdented chevron lines up with the
        "Mivia Certificate found" headline's left edge. Same
        max-width + auto margins as .cert-grid achieves that
        - both elements resolve to the same left edge whether
        the viewport is wide (max-width hits, both centred)
        or narrow (max-width slack, both full-width).
     2. A slightly larger bottom margin (12px vs the base
        .ta-back-row's 10px) since the chip sits above the
        big editorial headline rather than the smaller /track
        title - a touch more breathing room reads cleaner. */
.cert-back-row {
  width: 100%;
  max-width: calc(var(--rail) + 2 * var(--rail-x));
  margin: 0 auto 12px;
}

/* Editorial split: narrative paragraphs on the left, cert
   card on the right. Stacks to single-column at <= 880 px
   so on mobile the narrative reads first, then the cert.
   Hidden until cert.js has populated the dynamic narrative
   slots (title, creator, date) - showing an empty quoted
   title before the fetch resolves would look broken. */
.cert-grid {
  width: 100%;
  max-width: calc(var(--rail) + 2 * var(--rail-x));
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: clamp(40px, 6vw, 96px);
  align-items: center;
}
@media (max-width: 880px) {
  /* Mobile reflow: title → cert card → CTAs → paragraphs.
     Two levels of `display: contents` cascade the
     dissolution down through .cert-narrative AND each
     visible narrative block, so the headline, the cert
     card, the paragraphs, and the action row all become
     direct flex children of .cert-grid. `order` then
     interleaves them in the right sequence without
     touching the DOM.

     align-items: stretch overrides the desktop's
     align-items: center, which would otherwise size each
     item to its content and centre it horizontally - the
     specific failure mode that pulled the headline into
     the middle of the viewport on mobile. */
  .cert-grid {
    display: flex;
    flex-direction: column;
    gap: 14px;
    align-items: stretch;
  }
  /* track.css line 1676 sets .ta-proof { margin-top: 32px
     !important } at the same breakpoint as part of the
     /track hero stack rhythm. /c loads track.css too, so
     that rule leaks here and creates a 32px phantom strip
     above the cert card INSIDE .cert-frame. Override with
     higher specificity + !important to win the cascade. */
  .cert-frame .ta-proof {
    margin-top: 0 !important;
  }
  .cert-narrative {
    display: contents;
  }
  /* :not([hidden]) so the [hidden] attribute on the
     narrative blocks still wins when the block is
     deliberately hidden. Without the guard, display:
     contents (class selector, specificity 0,1,0) would
     tie with the UA's [hidden] (0,1,0) and our later rule
     would win, leaking the hidden block's children into
     the flex flow. With :not([hidden]) the rule simply
     doesn't match when hidden, and the UA's display:
     none kicks in cleanly. */
  .cert-narrative-public:not([hidden]),
  .cert-narrative-owner:not([hidden]) {
    display: contents;
  }
  .cert-headline {
    order: 1;
    /* !important wins the cascade against the base rule
       `.cert-headline { margin: 0 0 28px }` further down
       the file - same specificity, but the later rule
       would normally win. The mobile value is small (just
       enough to add a touch of air between the title and
       the cert card on top of the 14px flex gap), not the
       desktop's full 28px which would read as oversized
       on a phone. Net title→cert spacing: ~24px. */
    margin-bottom: 10px !important;
  }
  .cert-frame {
    order: 2;
  }
  /* Action rows sit between the cert card and the body
     paragraphs on mobile - cert is the artifact, CTA is
     the immediate next action, prose follows as
     supporting detail. margin-top is reset to 0 because
     the base rule's 28px (designed for desktop where the
     row sits beneath the last paragraph) would otherwise
     compound with the flex gap into oversized whitespace.
     nowrap keeps the buttons on a single row; the children
     share the available width via flex: 1 (with min-width:
     0 so the View-on-Bitcoin button can shrink below its
     intrinsic content size on very narrow viewports
     without overflowing the container). */
  .cert-narrative-public > .cert-actions,
  .cert-narrative-owner > .cert-actions {
    order: 3;
    /* Symmetric margin-y on top of the 14px flex gap gives
       the button row distinct breathing room (~34px above
       and below) versus the tighter 14px rhythm between
       the headline, cert card, and paragraphs. Overrides
       the desktop base `margin: 28px 0 0` which would
       otherwise compound oversized above and leave nothing
       below. */
    margin: 20px 0;
    flex-wrap: nowrap;
    gap: 8px;
  }
  .cert-narrative-public > .cert-actions > *,
  .cert-narrative-owner > .cert-actions > * {
    flex: 1;
    min-width: 0;
  }
  .cert-narrative-public > p,
  .cert-narrative-owner > p {
    order: 4;
  }
}

/* Narrative column. Type hierarchy matches the landing's
   .kc-section so the cert page reads as part of the same
   family. */
.cert-narrative {
  max-width: 56ch;
}
/* Title size matches the landing's .landing-section-title (§1
   Mark, §2 Resistance) so the cert page reads at the same
   hierarchy as the rest of the site's section heads. */
.cert-headline {
  font-size: clamp(1.75rem, 3.5vw, 2.75rem);
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.05;
  margin: 0 0 28px;
  color: var(--ink);
}
.cert-narrative p {
  font-size: 1.05rem;
  line-height: 1.55;
  color: var(--fg-dim);
  margin: 0 0 16px;
}
.cert-narrative p:last-child { margin-bottom: 0; }
.cert-narrative a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* Inline dynamic slots populated by cert.js paintProof.
   Solid ink keeps the title + creator visually anchored as
   the focal facts of the paragraph, even though they sit
   inside a fg-dim paragraph. Class selectors (not #id)
   because each slot exists twice in the DOM - once in the
   public narrative, once in the owner narrative - and id
   selectors only match the first. */
.cert-narr-title,
.cert-narr-creator,
.cert-narr-date,
.cert-narr-block {
  color: var(--ink);
  font-weight: 600;
}
/* Inline Mivia ID inside P2: rendered in mono uppercase so
   it visually reads as "the hex string from the cert card"
   rather than a wordy term. Sitewide mono = uppercase rule
   is already enforced by font choice; letter-spacing tuned
   to keep the 16-char hash from looking too airy in the
   middle of proportional copy. Slightly smaller so it doesn't
   tower over the surrounding paragraph text. */
.cert-narr-mivia-id {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.92em;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}

/* Label swap for the owner-row buttons. Desktop shows the
   full multi-word label; mobile swaps to a single-word
   variant so the three-button row (Library / Bitcoin /
   Share) fits on one line without per-button wrapping. The
   short variants are wrapped in their own span so we can
   hide one or the other purely in CSS - no JS, no aria
   churn (the visible text IS what screen readers narrate,
   and both variants point at the same destination so the
   announced label stays useful either way).
   Scoped to .cert-narrative-owner only - the public side
   has a single BTC button that has plenty of room for
   "View on Bitcoin" even at mobile widths. */
.cert-btn-short { display: none; }
@media (max-width: 880px) {
  .cert-narrative-owner .cert-btn-full { display: none; }
  .cert-narrative-owner .cert-btn-short { display: inline; }
}

/* Owner action row. Two side-by-side affordances at the end
   of the owner narrative:
     - View in library (.primary-button, ink fill — hand-off
       back to the owner's authenticated /track/<hash> view)
     - Share (.secondary-button, outline)
   Buttons reuse the landing-site .primary-button /
   .secondary-button styles from styles.css so the cert page
   matches site-wide button conventions on bone surfaces.
   flex-wrap lets the pair stack on very narrow viewports
   without overflowing the narrative column. */
.cert-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 28px 0 0;
}
/* Override the narrative's underline-all-links AND
   color-all-links-ink rules for the landing action pills.
   .cert-narrative a (0,2,0) was hijacking both:
     - text-decoration: underline → underlined button text
     - color: var(--ink)         → ink-on-ink primary button
                                   text (invisible)
   Specificity 0,2,1 here beats the narrative rule. The
   resting color is restored to the .primary-button's white
   default; hover keeps its own rule (.primary-button:hover,
   0,3,0) which already beats narrative-a so no override
   needed there. Secondary's color already matches
   narrative-a (var(--ink)) so it doesn't need re-asserting. */
.cert-narrative a.primary-button,
.cert-narrative a.secondary-button {
  text-decoration: none;
  text-underline-offset: 0;
}
.cert-narrative a.primary-button {
  color: var(--btn-ink-fg, var(--white));
}
/* Lightweight inline toast used when the Web Share API is
   unavailable and we fall back to copying the URL to the
   clipboard. Sits below the .cert-actions row (sibling, not
   child) so it doesn't wedge between Share and View buttons
   as a third flex item. Auto-clears after ~2 s. */
.cert-share-toast {
  display: block;
  margin: 12px 0 0;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink);
  opacity: 0.6;
}

/* Cert column. Same 380 px max-width as the live cert
   convention; justify-self: center so the card sits in the
   horizontal middle of its grid cell on desktop. */
.cert-frame {
  width: 100%;
  max-width: 380px;
  justify-self: center;
}
@media (max-width: 880px) {
  .cert-frame { justify-self: start; }
}

/* Standalone cert page doesn't need the sticky positioning
   the track-page sidebar uses. */
.cert-frame .ta-proof {
  position: relative;
}
/* The cert-card Mivia ID hex is a non-interactive span on
   /c (we're already on the cert page; the linked variant
   only makes sense on /track where it jumps you here).
   Same opacity as the "Mivia ID:" label so the row reads
   as one tone, no hover/underline since there's no link. */
.cert-frame .ta-proof-hash-static {
  opacity: 0.5;
}

/* Loading + error fallbacks - sit centred under the cert
   slot. Visible by default; cert.js hides them once data
   resolves. */
.cert-loading,
.cert-error {
  margin: 0;
  padding: 32px 0;
  text-align: center;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink);
  opacity: 0.5;
}
.cert-error { opacity: 0.7; }

/* Invalid / not-found certificate state. Mirrors the
   site-wide /404 page (not-found.css) so the two
   "doesn't-exist" pages share a typographic family - same
   display title scale, same subtitle treatment, same
   horizontal link row. Lives inside .cert-page-inner so
   desktop's safe-centring still applies and the block lands
   in the vertical middle of the visible area below the nav.
   Mobile hides the link row to match the 404 behaviour
   (header nav already covers every destination). */
.cert-invalid {
  width: 100%;
  text-align: center;
  padding: clamp(40px, 6vw, 80px) 0;
  box-sizing: border-box;
}
.cert-invalid-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  max-width: 720px;
  margin: 0 auto;
}
/* Title matches .drop-headline (/sign) and .db-headline
   (/database) exactly so the three "page-as-card" surfaces
   read as a typographic family - same scale, same tracking,
   same line-height. Smaller than the /404 title scale,
   which is intentionally bolder since it's the only signal
   on its page. */
.cert-invalid-title {
  font-size: clamp(2.25rem, 6vw, 3.75rem);
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.05;
  color: var(--ink);
  margin: 0;
}
.cert-invalid-sub {
  font-size: clamp(1rem, 1.4vw, 1.125rem);
  line-height: 1.5;
  color: var(--ink);
  opacity: 0.78;
  margin: 0;
  /* 48ch keeps the two-sentence description on ~2 lines at
     desktop widths rather than stretching wide enough to
     read as a paragraph. */
  max-width: 48ch;
}
/* Single-line prompt that takes the visitor to /database
   to look up a different Mivia ID. Mirrors the styling of
   .auth-card .auth-footer (the "Have the audio file? Scan
   it instead." line on /database) for exact visual
   continuity: small dim copy with the whole sentence as an
   ink-underlined link. */
.cert-invalid-prompt {
  margin: 10px 0 0;
  font-size: 0.85rem;
  color: var(--fg-dim);
  font-weight: 500;
  text-align: center;
}
.cert-invalid-prompt a {
  color: var(--ink);
  text-decoration: none;
}
@media (hover: hover) {
  .cert-invalid-prompt a:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
  }
}
@media (max-width: 720px) {
  .cert-invalid-sub { max-width: none; }
}

/* Mobile vertical-centring for the invalid block ONLY.
   Desktop already centres via .cert-page-inner's safe-center
   rule; mobile defaults to top-aligned (because the cert
   grid wants top-aligned reading rhythm). Using :has() to
   flip the layout only when .cert-invalid is the visible
   content keeps the cert-grid path untouched.

   Override page-main's base `min-height: calc(100svh -
   nav-h)` with 100dvh - nav-h for the invalid case. On
   iOS Safari and a handful of other mobile browsers, 100svh
   resolves to the LARGER viewport height (chrome hidden)
   rather than the actual visible area; the page-main ends
   up taller than the screen, so flex-centring lands the
   content below the visible centre. 100dvh follows the
   current visible viewport exactly, so the centred block
   sits at the optical centre of what the user sees.

   :has() is supported in Chrome 105+, Safari 15.4+, and
   Firefox 121+. Older browsers fall through to the existing
   top-aligned layout, which is an acceptable degradation. */
@media (max-width: 880px) {
  .page-main:has(.cert-invalid:not([hidden])) {
    min-height: calc(100vh - var(--nav-h));
    min-height: calc(100dvh - var(--nav-h));
  }
  .cert-page:has(.cert-invalid:not([hidden])) {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding-top: 0;
    padding-bottom: 0;
  }
  .cert-page:has(.cert-invalid:not([hidden])) .cert-page-inner {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}
