.vault-body{
  background-image:linear-gradient(180deg, rgba(255,248,240,.6), rgba(255,232,210,.9)), var(--img-hero);
  background-size:cover;background-position:center;background-attachment:fixed;
}
.vault-wrap{padding-top:36px;display:flex;justify-content:center;}
.vault-card{
  position:relative;
  background:var(--white);border:1px solid var(--line);border-radius:22px;
  padding:32px 24px;max-width:420px;width:100%;text-align:center;
  box-shadow:0 24px 55px rgba(26,43,76,.12);
  overflow:hidden;
}
.vault-icon{
  width:56px;height:56px;border-radius:50%;margin:0 auto 14px;
  background:linear-gradient(135deg,var(--orange),var(--orange-dark));
  display:flex;align-items:center;justify-content:center;
}
.vault-icon [data-lucide]{width:26px;height:26px;color:#fff;}
@keyframes spin{to{transform:rotate(360deg);}}
.btn-spin{animation:spin 1s linear infinite;}
.vault-title{font-size:23px;font-weight:800;color:var(--navy);margin-bottom:6px;}
.vault-sub{font-size:13px;color:var(--muted);line-height:1.55;margin-bottom:22px;}
.vault-hint{font-size:11.5px;color:var(--muted);margin-top:18px;line-height:1.5;}
/* The magic-link screen's "subukan ulit" retry sits inside a .vault-hint and
   was indistinguishable from the muted text around it — an action nobody can
   see is an action nobody takes. */
.vault-hint a{color:var(--orange);font-weight:700;text-decoration:underline;}
.vault-hint a:focus-visible{outline:2px solid var(--orange);outline-offset:2px;}

#unlock-btn{width:100%;}
#unlock-btn:disabled{opacity:.6;cursor:not-allowed;animation:none;}

.field-label{display:block;font-size:12px;font-weight:700;color:var(--navy);margin:12px 0 6px;}
.field-input{
  width:100%;padding:13px 14px;border-radius:10px;border:1.5px solid var(--line);
  font-family:inherit;font-size:14px;color:var(--navy);background:var(--cream);
}
.field-input:focus{outline:none;border-color:var(--orange);background:var(--white);}

/* ================= PREMIUM UNBOXING REVEAL ================= */
.vault-unlocked-card{
  background:linear-gradient(180deg,var(--white) 0%,var(--cream) 100%);
  border-color:rgba(255,210,76,.5);
  animation:cardReveal .5s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes cardReveal{
  from{opacity:0;transform:translateY(14px) scale(.97);}
  to{opacity:1;transform:translateY(0) scale(1);}
}

.unbox-seal{display:flex;justify-content:center;margin-bottom:10px;}
.unbox-ring{
  width:72px;height:72px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%, var(--yellow), var(--yellow-deep) 80%);
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 12px 28px rgba(219,163,0,.4);
  animation:sealPop .6s cubic-bezier(.34,1.56,.64,1) both;
}
.unbox-ring [data-lucide]{width:34px;height:34px;color:var(--navy);}
@keyframes sealPop{
  0%{transform:scale(.4) rotate(-25deg);opacity:0;}
  60%{transform:scale(1.12) rotate(8deg);opacity:1;}
  100%{transform:scale(1) rotate(0);}
}

.unlocked-badge{
  display:inline-flex;align-items:center;gap:6px;
  background:rgba(45,138,69,.12);color:#2d8a45;border:1px solid rgba(45,138,69,.3);
  font-size:11.5px;font-weight:800;padding:5px 12px;border-radius:20px;margin-bottom:12px;
  animation:fadeUp .5s .1s both;
}
.unlocked-badge [data-lucide]{width:13px;height:13px;}
@keyframes fadeUp{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:translateY(0);}}

/* confetti — pure CSS, no canvas/lib needed */
.confetti-zone{position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:0;}
.confetti-piece{
  position:absolute;top:-12px;width:8px;height:14px;border-radius:2px;opacity:.9;
  animation:confettiFall 1.6s ease-in forwards;
}
@keyframes confettiFall{
  0%{transform:translateY(0) rotate(0deg);opacity:1;}
  100%{transform:translateY(340px) rotate(540deg);opacity:0;}
}

.file-list{position:relative;z-index:1;display:flex;flex-direction:column;gap:10px;text-align:left;}
.file-row{
  display:flex;align-items:center;gap:12px;width:100%;
  background:var(--white);border:1px solid var(--line);border-radius:14px;
  padding:13px 14px;cursor:pointer;font-family:inherit;color:var(--navy);
  transition:.18s;text-decoration:none;
  animation:fileIn .4s both;
  box-shadow:0 4px 14px rgba(26,43,76,.04);
}
.file-row:nth-child(1){animation-delay:.15s;}
.file-row:nth-child(2){animation-delay:.25s;}
.file-row:nth-child(3){animation-delay:.35s;}
.file-row:nth-child(4){animation-delay:.45s;}
.file-row:nth-child(5){animation-delay:.55s;}
@keyframes fileIn{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}
.file-row:hover{border-color:var(--orange-light);transform:translateY(-2px);box-shadow:0 10px 22px rgba(249,115,22,.12);}

.file-icon{
  width:42px;height:42px;border-radius:11px;flex:none;
  display:flex;align-items:center;justify-content:center;
}
.file-icon [data-lucide]{width:20px;height:20px;color:#fff;}
.file-icon.xlsx{background:linear-gradient(135deg,#3fae5c,#2d8a45);}
.file-icon.pdf{background:linear-gradient(135deg,#e0473c,#b8352c);}

.file-meta{flex:1;display:flex;flex-direction:column;min-width:0;}
.file-meta b{font-size:13px;}
.file-meta span{font-size:10.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

/* Per-row caveat (2026-09-05). MUST override the ellipsis rule above — the
   rule it inherits is white-space:nowrap + text-overflow:ellipsis, which would
   clip this to a single truncated line and hide the half that matters ("hindi
   ito gumagana sa Excel"). A caveat you cannot finish reading is worse than no
   caveat, so it wraps. */
/* `.file-meta span` is (0,1,1) and there is an unrelated, older `.file-note`
   rule further down this file — a bare `.file-note` here loses to BOTH (equal
   specificity, later wins) and the caveat stays clipped to one line. Hence the
   distinct name AND the compound selector. Verified in the browser, not by
   reading: getComputedStyle reported white-space:nowrap before this. */
/* A caveat row is 2-3 lines taller than a normal one; centring a 40px icon
   against that reads as a misalignment bug. */
.file-row.has-caveat{align-items:flex-start;}
.file-meta span.file-caveat{
  white-space:normal;overflow:visible;text-overflow:clip;
  margin-top:4px;font-size:10px;line-height:1.45;font-weight:600;
  color:var(--orange-dark);background:rgba(249,115,22,.09);
  border-left:2px solid var(--orange-dark);border-radius:0 6px 6px 0;
  padding:5px 8px;
}

.file-dl{
  flex:none;display:flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;
  color:var(--orange-dark);background:rgba(249,115,22,.1);padding:7px 11px;border-radius:9px;
}
.file-dl [data-lucide]{width:13px;height:13px;}

.crosssell-banner{
  position:relative;z-index:1;margin-top:20px;padding:16px 40px 16px 16px;
  background:linear-gradient(135deg,var(--navy),var(--navy-light));border-radius:14px;
  display:flex;align-items:center;gap:12px;text-align:left;
}
.crosssell-dismiss{
  position:absolute;top:8px;right:8px;background:rgba(255,255,255,.12);border:none;border-radius:50%;
  width:22px;height:22px;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#fff;
}
.crosssell-dismiss [data-lucide]{width:12px;height:12px;}
.crosssell-dismiss:hover{background:rgba(255,255,255,.22);}
.crosssell-icon{
  flex:none;width:36px;height:36px;border-radius:10px;
  background:linear-gradient(135deg,var(--orange),var(--yellow));
  display:flex;align-items:center;justify-content:center;
}
.crosssell-icon [data-lucide]{width:18px;height:18px;color:var(--navy);}
.crosssell-copy{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}
.crosssell-copy b{font-size:12.5px;color:#fff;line-height:1.35;}
.crosssell-copy span{font-size:11px;color:rgba(255,255,255,.7);line-height:1.3;}
.crosssell-btn{
  flex:none;width:auto;white-space:nowrap;padding:9px 14px;font-size:12px;
  background:var(--orange);color:#fff;display:inline-flex;align-items:center;gap:5px;
}
.crosssell-btn [data-lucide]{width:13px;height:13px;}
@media(max-width:420px){
  .crosssell-banner{flex-wrap:wrap;}
  /* width:100% + margin-left together used to overflow the container by
     exactly the margin amount (basic box-model math) — confirmed via a real
     getBoundingClientRect() overflow check, not just visual guesswork.
     calc() accounts for the margin instead of adding on top of 100%. */
  .crosssell-btn{width:calc(100% - 48px);justify-content:center;margin-left:48px;}
}

.vault-footer-note{
  position:relative;z-index:1;margin-top:20px;display:flex;align-items:center;justify-content:center;gap:7px;
  font-family:'Caveat','Segoe Script',cursive;font-size:18px;color:var(--navy);font-weight:700;
}
.vault-footer-note [data-lucide]{width:16px;height:16px;color:var(--orange);}

.file-note{margin-top:16px;font-size:12px;color:var(--muted);background:var(--cream);border-radius:10px;padding:10px 12px;}


/* ================= Post-purchase upgrades (2026-09-02, Move 2D) =================
   Sits between the file list and the KahitPiso banner, on the LIGHT vault card
   (var(--white) background, var(--navy) text) — not a dark surface. Uses the
   same token vocabulary as .file-row so it reads as part of the same page, and
   is deliberately quieter than the checkout page's addon rows: this buyer has
   already paid, and the files they came for must stay the loudest thing here. */
.vault-upsell{
  position:relative;z-index:1;
  margin-top:24px;padding:16px;
  border:1px solid var(--line);border-radius:16px;
  background:var(--cream);
  text-align:left;
}
.vault-upsell[hidden]{display:none;}

.vault-upsell-head{display:flex;flex-direction:column;gap:3px;margin-bottom:13px;}
.vault-upsell-head b{font-size:14.5px;color:var(--navy);line-height:1.35;}
.vault-upsell-head span{font-size:12.5px;line-height:1.5;color:var(--muted);}

.upgrade-rows{display:flex;flex-direction:column;gap:9px;}

.upgrade-row{
  display:flex;align-items:center;gap:12px;
  padding:12px 13px;
  border:1px solid var(--line);border-radius:13px;
  background:var(--white);
  box-shadow:0 4px 14px rgba(26,43,76,.04);
}
.upgrade-row-copy{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;}
.upgrade-row-copy b{font-size:13.5px;color:var(--navy);line-height:1.35;}
.upgrade-row-copy span{font-size:11.5px;color:var(--muted);line-height:1.45;}

.upgrade-price{display:flex;flex-direction:column;align-items:flex-end;gap:0;flex:none;}
.upgrade-price .now{font-size:15px;font-weight:800;color:var(--orange);font-variant-numeric:tabular-nums;}
.upgrade-price .was{font-size:11px;color:var(--muted);text-decoration:line-through;font-variant-numeric:tabular-nums;}

.upgrade-btn{
  flex:none;padding:9px 14px;border-radius:10px;
  border:1.5px solid var(--orange);background:var(--white);
  color:var(--orange);font-size:12.5px;font-weight:800;cursor:pointer;
  font-family:inherit;transition:.18s;
}
.upgrade-btn:hover:not(:disabled){background:var(--orange);color:var(--white);}
.upgrade-btn:disabled{opacity:.55;cursor:not-allowed;}
.upgrade-btn:focus-visible{outline:2px solid var(--orange);outline-offset:2px;}

.upgrade-error{margin:11px 0 0;font-size:12.5px;line-height:1.5;color:var(--danger);}
.upgrade-error[hidden]{display:none;}

@media (max-width:420px){
  /* Stack rather than squeeze: the label is the part that must stay readable,
     and at this width the price + button no longer fit beside it. */
  .upgrade-row{flex-wrap:wrap;gap:9px;}
  .upgrade-row-copy{flex-basis:100%;}
  .upgrade-price{align-items:flex-start;flex:1;}
}
@media (prefers-reduced-motion:reduce){
  .upgrade-btn{transition:none;}
}


/* ================= OPISINA post-purchase block (2026-09-03) =================
   Given its own surface — navy, like the KahitPiso banner — rather than more
   rows in the light add-on card. It is a different product line at 3-10x the
   price of anything above it, and a ₱1,748 row sitting among ₱99 ones reads as
   a pricing error rather than an offer. */
.vault-opisina{
  position:relative;z-index:1;
  margin-top:20px;padding:18px 16px;
  border-radius:16px;
  background:linear-gradient(135deg,var(--navy),var(--navy-light));
  text-align:left;
}
.vault-opisina[hidden]{display:none;}

.vault-opisina-head{display:flex;flex-direction:column;gap:5px;margin-bottom:14px;}
.opisina-eyebrow{
  font-size:10px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  color:var(--yellow);
}
.vault-opisina-head b{font-size:15.5px;color:#fff;line-height:1.35;}
.opisina-sub{font-size:12.5px;line-height:1.55;color:rgba(255,255,255,.72);}

.opisina-rows{display:flex;flex-direction:column;gap:9px;}

.opisina-row{
  display:flex;align-items:center;gap:12px;
  padding:12px 13px;border-radius:12px;
  background:rgba(255,255,255,.07);
  border:1px solid rgba(255,255,255,.13);
}
.opisina-row-copy{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;}
.opisina-row-copy b{font-size:13.5px;color:#fff;line-height:1.35;}
.opisina-row-copy span{font-size:11.5px;color:rgba(255,255,255,.62);line-height:1.45;}

.opisina-price{display:flex;flex-direction:column;align-items:flex-end;gap:0;flex:none;}
.opisina-price .now{font-size:15px;font-weight:800;color:var(--yellow);font-variant-numeric:tabular-nums;}
.opisina-price .was{font-size:11px;color:rgba(255,255,255,.45);text-decoration:line-through;font-variant-numeric:tabular-nums;}
.opisina-price .cred{font-size:10.5px;color:var(--yellow);opacity:.85;font-variant-numeric:tabular-nums;white-space:nowrap;}

.opisina-btn{
  flex:none;padding:9px 14px;border-radius:10px;
  border:1.5px solid var(--yellow);background:transparent;
  color:var(--yellow);font-size:12.5px;font-weight:800;cursor:pointer;
  font-family:inherit;transition:.18s;
}
.opisina-btn:hover:not(:disabled){background:var(--yellow);color:var(--navy);}
.opisina-btn:disabled{opacity:.5;cursor:not-allowed;}
.opisina-btn:focus-visible{outline:2px solid var(--yellow);outline-offset:2px;}

.opisina-note{
  margin:12px 0 0;font-size:11.5px;line-height:1.5;color:rgba(255,255,255,.62);
}
.opisina-note[hidden]{display:none;}
.opisina-note.err{color:#ffb4a2;}

@media (max-width:420px){
  .opisina-row{flex-wrap:wrap;gap:9px;}
  .opisina-row-copy{flex-basis:100%;}
  .opisina-price{align-items:flex-start;flex:1;}
}
@media (prefers-reduced-motion:reduce){
  .opisina-btn{transition:none;}
}

/* ============================================================================
   LOCKED VAULT — "FILES MUNA, BAGO BAYAD" (2026-09-08)
   ----------------------------------------------------------------------------
   The reservation state. Everything here sits on `.vault-card`, which is LIGHT
   (var(--white) with var(--navy) text) — the same surface that once swallowed a
   whole upgrade block styled for a dark background while every DOM assertion
   passed. Screenshot after touching any of it.

   Design intent, so it doesn't get "cleaned up" into something softer: a locked
   row must read as a REAL file the buyer already owns the right to, temporarily
   held — not as a disabled control and not as a teaser. Hence: full opacity on
   the filename, muted only on the icon and the pill, and no blur. The buyer is
   meant to recognise their files.
   ------------------------------------------------------------------------- */

/* The obligation panel. Deliberately the loudest thing on the screen after the
   Pay button — it is the whole consideration the buyer is giving in exchange
   for early delivery, and burying it would make this flow dishonest rather than
   clever. */
.vault-locked{position:relative;z-index:1;margin:0 0 18px;text-align:left;}
.vl-trust{
  display:flex;gap:11px;align-items:flex-start;
  background:var(--fmbb-tint);border:2px solid var(--fmbb);
  border-radius:14px;padding:14px 15px;margin-bottom:14px;
}
.vl-trust-ico{
  width:34px;height:34px;border-radius:9px;flex:none;
  background:var(--fmbb);
  display:flex;align-items:center;justify-content:center;
}
.vl-trust-ico [data-lucide]{width:17px;height:17px;color:#fff;}
/* min-width:0 so the copy can shrink inside the flex row — see CLAUDE.md's
   nowrap/flex gotcha; without it long Tagalog words push the panel wide. */
.vl-trust-copy{display:flex;flex-direction:column;min-width:0;}
.vl-trust-copy b{font-size:13.5px;color:var(--navy);line-height:1.35;margin-bottom:3px;}
.vl-trust-copy span{font-size:11.5px;color:var(--navy);opacity:.82;line-height:1.55;}

.vl-pay-btn{width:100%;flex-direction:column;gap:2px;padding:14px 16px;}
.vl-pay-btn:disabled{opacity:.65;cursor:not-allowed;animation:none;}
.vl-pay-main{display:flex;align-items:center;gap:7px;font-size:15px;font-weight:800;}
.vl-pay-main [data-lucide]{width:17px;height:17px;}
.vl-pay-sub{font-size:10.5px;font-weight:600;opacity:.85;}

.vl-error{
  margin-top:10px;font-size:12px;font-weight:600;line-height:1.5;
  color:#b8352c;background:rgba(224,71,60,.09);
  border-radius:9px;padding:9px 11px;
}
/* `.vl-error` sets its own display via the block default, so the [hidden]
   override is required — the CLAUDE.md rule about toggling with the hidden
   attribute on anything that carries its own display value. */
.vl-error[hidden]{display:none;}

.vl-fineprint{
  display:flex;align-items:center;justify-content:center;gap:6px;
  margin-top:11px;font-size:10.5px;font-weight:600;color:var(--muted);text-align:center;
}
.vl-fineprint [data-lucide]{width:12px;height:12px;color:var(--orange-dark);}

/* A locked row is not a link and must not pretend to be one: no pointer, no
   lift on hover, no orange border. */
.file-row.locked{
  cursor:default;background:var(--cream);border-style:dashed;
  box-shadow:none;
}
.file-row.locked:hover{border-color:var(--line);transform:none;box-shadow:none;}
/* Muted icon, full-strength filename. The buyer must still be able to READ
   which file this is — that recognition is the entire mechanic. */
.file-row.locked .file-icon{filter:grayscale(1);opacity:.55;}
.file-row.locked .file-meta b{color:var(--navy);opacity:.75;}

.file-dl-locked{color:var(--muted);background:rgba(26,43,76,.07);}
/* The one free file. Green rather than orange so it separates at a glance from
   the locked pills around it — this row is the proof, and it has to be found
   in under a second. */
.file-dl-free{color:#2d8a45;background:rgba(63,174,92,.13);}
.file-row.free-preview{border-color:rgba(63,174,92,.45);}
.file-row.free-preview:hover{border-color:#3fae5c;box-shadow:0 10px 22px rgba(63,174,92,.14);}

/* No entrance stagger in the preview state. The unboxing animation is a
   celebration cue and this is not that moment; it also delays the padlocks,
   which briefly makes locked files look downloadable. */
.vault-unlocked-card.is-preview .file-row{animation:none;}

/* The four elements the preview state hides all set their own `display`, so the
   bare `hidden` ATTRIBUTE loses to their class rule and they keep rendering.
   This is the exact gotcha CLAUDE.md warns about, and it shipped once here
   before a screenshot caught it: `el.hidden` read true, `getComputedStyle`
   said `flex`, and an unpaid vault displayed an "Access Verified" badge and a
   celebration seal over locked files.

   Verify any future addition to this list with getComputedStyle, never with
   the .hidden DOM property. */
.unbox-seal[hidden],
.unlocked-badge[hidden],
.crosssell-banner[hidden],
.vault-footer-note[hidden]{display:none;}
