/* ============================================================================
   Noa & Brandon — noalovesbrandon.com

   ⚠ ALMOST NOTHING VISUAL IS CSS. Rules, frames, ovals, palms and ornaments are
     generated engraved plates keyed to alpha and painted through -webkit-mask,
     so the ink is a variable and the linework is real artwork.

   ⚠ THE LIGHT PLATES ARE DIFFERENT ART, NOT INVERTED ART. A gold-on-midnight
     plate keys on how far a pixel is brighter than its ground; reused on cream
     it paints ink into the highlights and reads as an inverted photograph.

   ⚠ THE OPENED STATE IS THE DEFAULT. The envelope is scoped to html.js, and
     app.js sets that class as its first statement. If the script 404s, is
     blocked, or throws, the guest still gets the whole invitation — a <noscript>
     block would not have covered a 404 or a throw, because those run WITH
     scripting enabled.

   ⚠ "TOO BARE" IS A SCALE PROBLEM, NOT A CONTENT PROBLEM. Half the page's
     vertical run was bare paper while the finest asset rendered 203px wide.
     The answer is enlarging what is here, not adding more of it.
   ========================================================================= */
:root{
  --paper:#F4F0E6;
  --paper-warm:#EDE7D8;
  --ink:#2C3A57;
  --ink-deep:#16223C;
  --text:#1B2740;
  --text-dim:#44506B;
  --hair:rgba(44,58,87,.22);

  /* sampled from the envelope clip's own backdrop, so it floats with no seam */
  --env-top:#D2CAC5; --env-mid:#C6B9B2; --env-bot:#AC9A8E;

  --ease:cubic-bezier(.22,.61,.25,1);
  --slow:cubic-bezier(.16,.84,.3,1);

  /* ---- one type scale, one ratio (1.28). Only the base scales with the
     viewport, so every relationship survives every breakpoint. Twelve
     independent sizes on no ratio is what read as "assembled, not set". */
  --t0:clamp(11px,2.6vmin,13.5px);
  --t1:calc(var(--t0) * 1.28);
  --t2:calc(var(--t1) * 1.28);
  --t3:calc(var(--t2) * 1.28);
  --t4:calc(var(--t3) * 1.28);
  --t5:calc(var(--t4) * 1.28);

  --gap:clamp(56px,9.5vmin,116px);
  --frame-trim:clamp(6px,1.2vmin,16px);
  --frame-band:clamp(15px,3.6vmin,38px);
  --edge:clamp(20px,5.5vw,64px);

  --fp:1;               /* frame progress: 0 = the card's border, 1 = the page trim */
  --f-open-h:40px;      /* set by app.js from the card's own rect */
  --f-open-v:18px;
}
*{margin:0;padding:0;box-sizing:border-box}
html{background:var(--paper);-webkit-text-size-adjust:100%}
body{
  background:var(--paper);color:var(--text);
  font-family:'Cormorant Garamond',Didot,Georgia,serif;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  min-height:100svh;
}
img,video{display:block;max-width:100%}
::selection{background:rgba(44,58,87,.18)}
a{color:var(--ink)}

/* ---- an engraved plate ---------------------------------------------------
   The ink drifts by two or three pixels over twenty-two seconds. You cannot
   see it while looking at it; you notice only when it is gone. Real intaglio
   catches light as a card is tilted.                                       */
@keyframes ink-breathe{
  0%,100%{background-position:50% 42%}
  50%    {background-position:51.4% 44.2%}
}
.plate{
  background:radial-gradient(66% 62% at 50% 42%,var(--ink-deep) 0%,var(--ink) 60%,var(--ink) 100%);
  background-size:104% 104%;
  animation:ink-breathe 22s ease-in-out infinite;
  -webkit-mask-repeat:no-repeat;-webkit-mask-size:100% 100%;-webkit-mask-position:center;
          mask-repeat:no-repeat;        mask-size:100% 100%;        mask-position:center;
}

/* ---- the frame: the card's border first, the page's second ---------------
   As a fixed overlay it put a rule through the cap of a "Y" and laid a navy
   guilloche band permanently across a golden-hour Pacific. Driven by scroll
   it opens outward from the card instead, and gets out of the way of the
   photography.                                                             */
.pageframe{
  position:fixed;
  inset:calc(var(--f-open-v) - (var(--f-open-v) - var(--frame-trim)) * var(--fp))
        calc(var(--f-open-h) - (var(--f-open-h) - var(--frame-trim)) * var(--fp));
  z-index:14;pointer-events:none;
  border-style:solid;
  border-width:calc(var(--frame-band) * (0.62 + 0.38 * var(--fp)));
  border-image-source:url(/assets/border-navy.png);
  border-image-slice:172;
  border-image-repeat:round;
  opacity:calc(0.9 - 0.62 * var(--fp));
  transition:opacity .5s var(--ease);
  animation:ink-breathe 26s ease-in-out infinite;
}
.pageframe.dim{opacity:.1}
html:not(.js) .pageframe{inset:var(--frame-trim);border-width:var(--frame-band);opacity:.3}

/* ---- ACT ONE : the envelope (only exists when the script runs) ----------- */
#stage{display:none}
html.js #stage{
  position:fixed;inset:0;z-index:30;display:grid;place-items:center;
  padding:calc(env(safe-area-inset-top) + 18px) 16px calc(env(safe-area-inset-bottom) + 18px);
  background:linear-gradient(178deg,var(--env-top) 0%,var(--env-mid) 52%,var(--env-bot) 100%);
}
html.js body{overflow:hidden}
html.js body.opened{overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch}
html.js #scroll{opacity:0}
html.js #scroll.live{opacity:1;transition:opacity 1s var(--ease) .12s}
#stage.gone{opacity:0;pointer-events:none;transition:opacity .65s var(--ease)}

.opener-wrap{position:relative;display:grid;place-items:center}
.opener{
  position:relative;display:block;background:none;border:0;padding:0;cursor:pointer;
  width:min(90vw,min(68svh,500px));-webkit-tap-highlight-color:transparent;
}
.opener:focus-visible{outline:2px solid rgba(44,58,87,.7);outline-offset:20px}
.opener video{
  width:100%;height:auto;
  /* ⚠ NO EDGE MARKS. The backdrop in the clip graduates, so a flat page colour
     would seam along the video's border however carefully it were matched. */
  -webkit-mask-image:linear-gradient(to right,transparent 0,#000 8%,#000 92%,transparent 100%),
                     linear-gradient(to bottom,transparent 0,#000 6%,#000 94%,transparent 100%);
  -webkit-mask-composite:source-in;
          mask-image:linear-gradient(to right,transparent 0,#000 8%,#000 92%,transparent 100%),
                     linear-gradient(to bottom,transparent 0,#000 6%,#000 94%,transparent 100%);
          mask-composite:intersect;
}
.mouth{position:absolute;left:0;right:0;top:0;height:24%;overflow:hidden;pointer-events:none}
.mouth img{position:absolute;left:19%;width:62%;top:100%;
  box-shadow:0 18px 40px rgba(80,64,54,.28);transform:translateY(0)}
.mouth img.up{transform:translateY(-64%);transition:transform .95s var(--slow)}
.hint{
  margin-top:clamp(18px,4vmin,34px);text-align:center;
  font-size:max(12px,var(--t0));letter-spacing:.5em;text-indent:.5em;
  text-transform:uppercase;color:rgba(44,58,87,.88);
  animation:breathe 3.8s ease-in-out infinite;
}
.hint.gone{opacity:0;transition:opacity .45s}
@keyframes breathe{0%,100%{opacity:.78}50%{opacity:1}}

/* ---- the card ------------------------------------------------------------
   The card lies on the same taupe surface the envelope was photographed on,
   so the film hands off to an object on a table rather than to type on cream. */
.sheetbox{position:relative;width:100%;min-height:100svh;
  padding:clamp(30px,7vmin,80px) var(--edge);
  display:grid;place-items:center;
  background:linear-gradient(178deg,var(--env-top) 0%,var(--env-mid) 62%,#BCADA4 100%)}

.sheet{
  container-type:inline-size;
  position:relative;width:min(100%,600px);
  display:flex;flex-direction:column;align-items:center;
  padding:7% 7.5% 6.5%;
  background:#FBF8F1;--shade:#FBF8F1;
  box-shadow:0 30px 70px -30px rgba(70,54,38,.5), 0 2px 6px rgba(70,54,38,.14);
}
.sheet .eyebrow{font-weight:500;font-size:max(11px,3.4cqw);letter-spacing:.52em;
  text-indent:.52em;color:var(--ink-deep);text-transform:uppercase}
.sheet .vig{position:relative;width:100%;aspect-ratio:1300/872;margin-top:4%}
.sheet .vig .p{position:absolute;inset:0}
.sheet .vig #i2{left:19.3%;right:19.3%;top:19.6%;bottom:19.4%}

.foot-block{display:flex;flex-direction:column;align-items:center;width:100%;margin-top:6%}
.wordmark{width:74%;aspect-ratio:2000/237;margin-bottom:4.4%}
.sheet .rule{width:23%;aspect-ratio:1300/520;margin-bottom:4.8%}
.ln{font-weight:500;text-transform:uppercase;color:var(--text);
  letter-spacing:.30em;text-indent:.30em;line-height:1.5;text-align:center}
.l-date {font-size:max(16px,5.5cqw)}
.venue  {margin-top:4.4%}
.l-venue{font-size:max(16px,5.5cqw)}
.l-city {font-size:max(13px,4.4cqw);letter-spacing:.24em;text-indent:.24em;
  color:var(--text-dim);margin-top:.35em}
.l-cal  {font-size:max(11.5px,3.7cqw);letter-spacing:.34em;text-indent:.34em;margin-top:6.5%;
  color:var(--ink);text-decoration:none;border-bottom:1px solid rgba(44,58,87,.32);
  padding-bottom:.35em;transition:color .25s,border-color .25s}
.l-cal:hover{color:var(--ink-deep);border-bottom-color:var(--ink)}
.l-foot {font-size:max(11px,3.5cqw);letter-spacing:.34em;text-indent:.34em;
  color:#3B4864;margin-top:5.2%}
@media (min-width:680px){ .sheet{aspect-ratio:4/5} .foot-block{flex:1;justify-content:center;margin-top:0} }

/* ---- the ink being laid down --------------------------------------------
   A press travelling across the sheet at a slight angle, then striking.

   ⚠ THIS CANNOT BE DONE WITH clip-path IN THIS RENDERING PATH. Three attempts —
     clip-path on the masked element, clip-path plus will-change, clip-path on a
     wrapper with the mask moved to a child — all ended the same way: the
     transition completed, every computed style was correct on all four plates,
     and the card painted nothing until an unrelated style was touched, at which
     point all four appeared at once. Delaying past the parent's opacity
     transition did not help either. A transform on a covering shade always
     repaints, so the reveal is a shade sliding off the plate rather than a clip
     closing in on it. The plate itself never animates; it only ever paints.   */
.ink{position:relative;overflow:hidden;display:block}
.ink > .plate{display:block;width:100%;height:100%;
  -webkit-mask-image:var(--art);mask-image:var(--art)}
.ink > .shade{
  position:absolute;top:-25%;bottom:-25%;left:-40%;width:190%;
  background:var(--shade,var(--paper));
  transform:translateX(0) skewX(-7deg);
  transition:transform 1.05s var(--slow);
}
.ink.on > .shade{transform:translateX(160%) skewX(-7deg)}
.ink.struck > .plate{animation:strike .1s var(--ease)}
@keyframes strike{0%{transform:scale(1.004)}100%{transform:scale(1)}}

/* ---- sections ------------------------------------------------------------ */
.act{padding:var(--gap) var(--edge);position:relative;overflow:hidden}
.act.tint{background:var(--paper-warm);--shade:var(--paper-warm)}
.mid{width:100%;max-width:min(96vw,820px);margin:0 auto;text-align:center;
  position:relative;z-index:2}
.mid.narrow{max-width:min(94vw,560px)}

/* one micro-label class, not two doing the identical job */
.caps{font-size:var(--t0);letter-spacing:.42em;text-indent:.42em;
  text-transform:uppercase;color:var(--text-dim);line-height:2.3}
.caps.hi{color:var(--ink)}
/* ⚠ ROMAN, NOT ITALIC. Every display line on the page was italic at weight 300,
   so there was no weight anywhere — a large part of the owner's "too bare". */
h2.h{font-size:var(--t4);font-weight:400;font-style:normal;
  line-height:1.22;letter-spacing:.002em;margin:.4em auto .1em;max-width:16ch;
  text-wrap:balance}
h2.h.big{font-size:var(--t5);max-width:13ch}
/* measure in em, not ch: ch resolves to Cormorant's narrow zero and under-reports */
.say{margin:1em auto 0;max-width:22em;font-size:var(--t2);
  font-weight:400;line-height:1.62;color:var(--text-dim);text-wrap:pretty}
.say.lead{max-width:15em;font-size:var(--t3);font-style:italic;color:var(--text)}

.orn{margin:0 auto;display:block;width:100%}
.orn.coupes {max-width:min(76vw,420px);aspect-ratio:1100/1042}
.orn.wreath {max-width:min(66vw,360px);aspect-ratio:1100/963}
.orn.divider{max-width:min(72vw,420px);aspect-ratio:1300/520}
.orn.table  {max-width:min(100%,1100px);aspect-ratio:1500/953}

.photo{position:relative;width:100%;height:100svh;min-height:460px;overflow:hidden;
  background:var(--paper-warm);background-size:cover;background-position:center}
.photo img{width:100%;height:100%;object-fit:cover}
html.js .photo img{opacity:0;transition:opacity .6s var(--ease)}
html.js .photo img.shown{opacity:1}
.photo figcaption{
  position:absolute;left:0;right:0;bottom:0;padding:26vmin var(--edge) clamp(30px,7vmin,66px);
  background:linear-gradient(180deg,rgba(244,240,230,0) 0%,rgba(244,240,230,.55) 34%,
              rgba(244,240,230,.94) 64%,var(--paper) 90%);
  text-align:center;
}
.photo figcaption .h{margin-bottom:0}
/* over photography the micro-caps need the full text colour, not the dim one */
.photo figcaption .caps,.coda .caps{color:var(--text)}
.photo figcaption .say{margin-left:auto;margin-right:auto}

.cameo{position:relative;width:min(94vw,min(66svh,560px));aspect-ratio:1400/1730;margin:0 auto}
.cameo .fr{position:absolute;inset:0}
.cameo .win{position:absolute;left:24.3%;right:24.9%;top:21.2%;bottom:22.3%;overflow:hidden;
  -webkit-mask-image:radial-gradient(ellipse 50% 50% at 50% 48%,#000 66%,rgba(0,0,0,.6) 84%,transparent 97%);
          mask-image:radial-gradient(ellipse 50% 50% at 50% 48%,#000 66%,rgba(0,0,0,.6) 84%,transparent 97%)}
.cameo .win .p{position:absolute;width:125%;left:-20%;top:6%;aspect-ratio:1400/1689}

/* ---- the palms: one watermark behind a whole section, overrunning the trim
   Two boxed plates left a corridor of bare cream down the middle and a hard
   cut on every edge. Run past the trim on both sides and no edge is ever
   on screen to be cut.                                                     */
.palms{position:absolute;inset:-6% -12%;z-index:1;pointer-events:none;opacity:0;
  transform:scale(1.06)}
.palms.in{opacity:.26;transform:scale(1);
  transition:opacity 1.8s var(--ease),transform 2.4s var(--slow)}
.palms .pl{position:absolute;top:0;bottom:0;width:52%;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-size:contain;mask-size:contain}
.palms .pl.a{left:0;-webkit-mask-position:left center;mask-position:left center}
.palms .pl.b{right:0;-webkit-mask-position:right center;mask-position:right center}
@media (max-width:620px){ .palms.in{opacity:.2} .palms .pl{width:64%} }

/* ---- the sub-page links -------------------------------------------------- */
.links{display:grid;gap:13px;margin-top:clamp(28px,6vmin,52px);text-align:left}
@media (min-width:640px){ .links{grid-template-columns:1fr 1fr} }
.links a{
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  border:1px solid var(--hair);border-radius:2px;padding:20px 24px;
  text-decoration:none;color:var(--ink);background:rgba(255,255,255,.44);
  font-size:var(--t1);transition:background .28s,border-color .28s,transform .28s;
}
.links a:hover{background:rgba(255,255,255,.9);border-color:rgba(44,58,87,.44);transform:translateY(-1px)}
.links a span{font-size:var(--t0);letter-spacing:.3em;text-transform:uppercase;color:var(--text-dim)}

/* ---- the form ------------------------------------------------------------ */
form{text-align:left;display:grid;gap:16px;margin-top:clamp(26px,5vmin,46px)}
.f{position:relative}
label{display:block;font-size:var(--t0);letter-spacing:.28em;text-indent:.28em;
  text-transform:uppercase;color:var(--text-dim);margin-bottom:8px}
label .opt{text-transform:none;letter-spacing:0;text-indent:0;font-style:italic}
input,textarea{
  width:100%;background:rgba(255,255,255,.62);color:var(--text);
  border:1px solid var(--hair);border-radius:2px;
  padding:15px 16px;font:400 17px/1.45 'Cormorant Garamond',Georgia,serif;
  transition:border-color .25s,background .25s}
input:hover,textarea:hover{border-color:rgba(44,58,87,.38)}
input:focus,textarea:focus{outline:0;border-color:var(--ink);background:#fff}
input::placeholder,textarea::placeholder{color:rgba(44,58,87,.56)}
textarea{resize:vertical;min-height:88px}
.help{margin-top:7px;font-size:var(--t0);font-style:italic;letter-spacing:0;
  text-transform:none;color:var(--text-dim);line-height:1.5}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.sugg{position:absolute;left:0;right:0;top:calc(100% + 5px);z-index:40;
  background:#fff;border:1px solid rgba(44,58,87,.3);border-radius:2px;
  box-shadow:0 22px 50px rgba(44,58,87,.18);display:none;
  max-height:min(44svh,290px);overflow-y:auto;-webkit-overflow-scrolling:touch}
.sugg.open{display:block}
.sugg [role="option"]{display:block;width:100%;text-align:left;cursor:pointer;
  padding:13px 16px;color:var(--text);font:400 16px/1.4 'Cormorant Garamond',Georgia,serif;
  border-bottom:1px solid rgba(44,58,87,.10)}
.sugg [role="option"]:last-child{border-bottom:0}
.sugg [role="option"]:hover,.sugg [role="option"][aria-selected="true"]{
  background:var(--ink);color:#F6F2E8}

button.send{margin-top:8px;width:100%;background:var(--ink);color:#F6F2E8;
  border:1px solid var(--ink);border-radius:2px;padding:18px 20px;cursor:pointer;
  font:500 13px/1 'Cormorant Garamond',Georgia,serif;letter-spacing:.42em;text-indent:.42em;
  text-transform:uppercase;transition:background .3s,opacity .3s,transform .1s}
button.send:hover{background:var(--ink-deep)}
button.send:active{transform:translateY(1px)}
button.send:disabled{opacity:.55;cursor:default}
.msg{min-height:1.4em;font-size:var(--t1);font-style:italic;color:var(--text-dim);text-align:center}
.msg.bad{color:#8E3527}
.done{display:none;opacity:0}
.done.on{display:block;opacity:1;transition:opacity .8s var(--ease)}

/* the page closes on a gesture, not on six input fields */
.coda{min-height:100svh;display:grid;place-items:center;padding:var(--gap) var(--edge)
  calc(var(--gap) + env(safe-area-inset-bottom));text-align:center;position:relative;
  overflow:hidden}
.coda-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}
/* the closing gesture sits over the water it happens above; the scrim is heavy enough
   that navy engraving and navy caps both clear the contrast floor on the busiest part */
.coda-scrim{position:absolute;inset:0;z-index:1;
  background:linear-gradient(180deg,var(--paper) 0%,rgba(244,240,230,.90) 26%,
             rgba(244,240,230,.88) 58%,rgba(244,240,230,.95) 88%,var(--paper) 100%)}

.reveal{opacity:0;transform:translateY(14px)}
.reveal.in{opacity:1;transform:none;
  transition:opacity var(--dur,.9s) var(--ease) var(--dly,0ms),
             transform var(--dur,.9s) var(--ease) var(--dly,0ms)}

/* ---- sub-pages ----------------------------------------------------------- */
.back{display:inline-block;font-size:var(--t0);letter-spacing:.28em;
  text-transform:uppercase;color:var(--text-dim);text-decoration:none;
  border-bottom:1px solid transparent;padding-bottom:3px;transition:color .25s,border-color .25s}
.back:hover{color:var(--ink);border-bottom-color:var(--hair)}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition-duration:.001ms!important}
  .ink > .shade{transform:translateX(160%) skewX(-7deg)}
  .reveal{opacity:1;transform:none}
  .palms{opacity:.22;transform:none}
  .photo img{opacity:1}
}
