/* ==========================================================================
   Charles Schwab - Playable End Card EC1
   CM360 / Studio rich media port of Cavai creative 95896.

   ======================= REVISION 2026-09-03 ==============================
   The creative is now drawn end to end - see the long note in index.html for
   why the five baked exports were retired. That changed the vertical system,
   so the numbers below are no longer measurements off those exports; they are
   a budget, and the budget is the thing to keep consistent:

     70 .. 220     header      Schwab mark left, "Question N / 4" right
    250 .. 346     banner band correct / incorrect, question view only
    396            headline    question view, top anchored
    660 .. 1510    pills       question view, solved by the ladder in flow.js
    400 .. 1230    band        insight / result content, centred
   1310 .. 1465    single CTA  intro / insight / result
   1540 .. 1920    footer      RESERVED, empty - the client's lockup

   The INTRO is the one exception, because it is composed as a title card the
   way the retired g1-intro.png export was - see .cs-intro-mark below:

     96 ..  296    mark        200 square, CENTRED; .cs-head is hidden
    356 ..  560    lockup      the "Booyah!" crop, 760 wide
    630 .. 1250    band        the full 870 column, headline at 104

   The two rules that hold it together:

     1. Nothing on any view may cross 1510. The footer band below it belongs
        to the "You might know more than you think." lockup, which is not in
        our hands yet; anything that grows into it will collide the day the
        artwork lands.
     2. The banner band is reserved whether or not a banner is showing. The
        client's flow frames show the question headline in the same place
        before and after answering, and it only holds still if the space is
        always there.

   Retained from the Cavai port, because the engine is still worth not
   repeating: on Cavai the step switch was five css_N operators, each appending
   its own <style> to head, and they ACCUMULATED - so every hide rule had to tie
   the previous step's show rule on specificity or frames stacked up on screen.
   Owning the DOM means one attribute does it and that trap cannot occur.
   ========================================================================== */

@font-face{
  font-family:"Charles Modern";
  src:url("../assets/fonts/CharlesModern-500.woff2") format("woff2");
  font-weight:500; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Charles Modern";
  src:url("../assets/fonts/CharlesModern-700.woff2") format("woff2");
  font-weight:700; font-style:normal; font-display:swap;
}

:root{
  --blue:#009DDB;
  --cta:#459AD5;          /* pill blue, sampled off the client frames */
  --chev:#55AADC;         /* answer chevron */
  --pick:#91CCEA;         /* answer pressed state */
  --hair:#A6A6A6;         /* answer outline */
  --slate:#233441;
  --ink:#000000;
  --card:#FFFFFF;
  --paper:#FFFFFF;        /* the exports' own ground */
  --band:#000000;         /* desktop letterbox surround - see .cs-wrap below */

  /* Correct / incorrect. The banner colours carry white text, so they are set
     dark enough to clear 4.5:1 - #147A3D is 5.6:1 and #B02318 is 6.9:1. The
     pill tints are the pale washes the client's flow frames show, and they
     keep BLACK body text, so they are deliberately near-white. */
  --ok:#147A3D;
  --ok-fill:#DCF3E5;
  --ok-line:#4FA974;
  --no:#B02318;
  --no-fill:#FADEDC;
  --no-line:#D98781;
  --no-ink:#8C1A12;

  --s: min(100vw, 56.25vh);
  --u: calc(var(--s) / 1080);

  /* Question-view type scale, in design units. These are the ladder's top
     rung; js/flow.js steps them down per question until the headline and the
     four pills fit between the banner band and the floor. See FIT in flow.js. */
  --qsize: 76;
  --asize: 44;
  --agap:  70;
  --arow: 160;   /* answer button height - equalised by flow.js fit() */

  /* Band views (intro / insight / result) shrink as ONE proportional block
     rather than on a per-element ladder: they are short, fixed, hand-written
     copy, so the only failure mode is a long tier or insight paragraph
     overflowing the band, and stepping the whole block down keeps the internal
     rhythm the design was drawn with. flow.js writes this. */
  --fscale: 1;

  --font: "Charles Modern", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

*{ box-sizing:border-box; }

html, body{
  width:100%; height:100%;
  margin:0; padding:0;
  overflow:hidden;
  background:transparent;
}

/* ---------------------------------------------------------------- stage --*/

/* The letterbox layer, and the only one that can carry its colour.

   Which colour is right depends on WHERE the letterbox falls, and --s decides
   that. Below 9:16 - a normal phone, taller than the design - --s resolves to
   100vw, the stage is full width, and because it is bottom anchored the only
   leftover band sits ABOVE it. Against a white creative a white band there is
   invisible, so portrait stays --paper.

   From 9:16 and wider - desktop, tablet, any landscape - --s resolves to
   56.25vh instead, the stage is exactly 100vh tall and the leftover moves to
   the two SIDES. There white would let the phone-shaped creative dissolve
   into the page, so those bands go back to black and give it a visible edge.
   The breakpoint is the same comparison --s already makes: min-aspect-ratio
   9/16 is exactly "56.25vh is the smaller of the two". */
.cs-wrap{
  position:relative;
  width:100%; height:100%;
  overflow:hidden;
  background-color:var(--paper);
  font-family:var(--font);
  -webkit-font-smoothing:antialiased;
}

@media (min-aspect-ratio: 9/16){
  .cs-wrap{ background-color:var(--band); }
}

/* Bottom anchored, not centred: it keeps the tap targets a fixed distance
   from the bottom of the screen on every device instead of moving ~84px
   between a 667px SE and a 932px Pro Max.

   It paints its own ground. With no image layer behind it any more, the black
   desktop letterbox on .cs-wrap would otherwise show through the whole ad. */
.cs-stage{
  position:absolute;
  width:var(--s);
  height:calc(var(--u) * 1920);
  left:50%; right:auto;
  top:auto; bottom:0;
  transform:translateX(-50%);
  overflow:hidden;
  background:var(--paper);
  pointer-events:none;      /* taps belong to the pills; everything else falls through */
}

.cs-frame{
  position:absolute;
  left:0; right:0; top:0; bottom:0;
  overflow:hidden;
  text-align:center;
}

/* ----------------------------------------------------------- view switch --*/

.cs-frame{ display:none; }

[data-view="intro"]    .cs-frame[data-frame="intro"],
[data-view="question"] .cs-frame[data-frame="question"],
[data-view="insight"]  .cs-frame[data-frame="insight"],
[data-view="result"]   .cs-frame[data-frame="result"]{ display:block; }

/* ==========================================================================
   Header - Schwab mark left, question counter right
   --------------------------------------------------------------------------
   Outside the frames, so it is drawn once and cannot drift between views.
   The counter is emptied by flow.js on the intro and result, but it is hidden
   here as well: an empty span still reserves its line box, and a stray space
   in the copy deck would otherwise be enough to nudge the mark.
   ==========================================================================*/

.cs-head{
  position:absolute;
  left:calc(var(--u) * 105);
  right:calc(var(--u) * 105);
  top:calc(var(--u) * 70);
  height:calc(var(--u) * 150);
  display:flex;
  align-items:center;
  justify-content:space-between;
}

.cs-mark{
  display:block;
  width:calc(var(--u) * 150);
  height:calc(var(--u) * 150);
  user-select:none;
  -webkit-user-drag:none;
}

.cs-count{
  font-size:calc(var(--u) * 40);
  line-height:1.2;
  font-weight:700;
  color:var(--ink);
  white-space:nowrap;
}

[data-view="result"] .cs-count{ display:none; }

/* The intro carries its own large centred mark instead - see .cs-intro-mark.
   Hiding the whole header, not just the counter, is what makes it read as a
   title card rather than as another screen of the quiz. */
[data-view="intro"] .cs-head{ display:none; }

/* ==========================================================================
   Correct / incorrect banner
   --------------------------------------------------------------------------
   Reserved band, revealed by data-phase. Hidden with visibility rather than
   display so the band keeps its exact height in the layout at all times - the
   headline below it must not move when the banner appears.

   The glyph sits in its own darker cell at the left, as the client's frames
   draw it, and the label centres in what is left. The label is centred in the
   FULL banner, not in the remainder, so "Correct" and "Incorrect" both land on
   the banner's centre line rather than shifting with the word length; the
   icon cell is absolutely positioned over the top for that reason.
   ==========================================================================*/

.cs-banner{
  position:absolute;
  left:calc(var(--u) * 170);
  right:calc(var(--u) * 170);
  top:calc(var(--u) * 250);
  height:calc(var(--u) * 96);
  display:flex;
  align-items:center;
  justify-content:center;
  border-radius:calc(var(--u) * 14);
  color:#FFFFFF;
  visibility:hidden;
}

.cs-banner-icon{
  position:absolute;
  left:0; top:0; bottom:0;
  width:calc(var(--u) * 96);
  display:flex;
  align-items:center;
  justify-content:center;
  border-radius:calc(var(--u) * 14) 0 0 calc(var(--u) * 14);
  background:rgba(0,0,0,.16);
  font-size:calc(var(--u) * 46);
  font-weight:700;
  line-height:1;
}

.cs-banner-label{
  font-size:calc(var(--u) * 46);
  font-weight:700;
  line-height:1.2;
  letter-spacing:calc(var(--u) * 0.5);
}

[data-view="question"][data-phase="correct"] .cs-banner{
  visibility:visible;
  background:var(--ok);
}

[data-view="question"][data-phase="wrong"] .cs-banner{
  visibility:visible;
  background:var(--no);
}

/* ==========================================================================
   Question headline
   --------------------------------------------------------------------------
   Top anchored at 396, immediately under the banner band. Top anchoring is
   what lets a 2-line and a 4-line headline start on the same line; bottom
   anchoring would float the short one down.

   The size is a variable, not a constant. The trivia questions run from 29
   characters ("When you buy a bond, you are:") to 89 ("When your portfolio
   suffers a big loss, what do you need to get back to where you started?"),
   which is 1 line against 4, so flow.js steps this down until the headline and
   the pills both fit above the floor.

   The 740 column (1080 - 170 - 170) is inherited from the approved frames. No
   text-wrap:balance - it is unsupported before Safari 17.4, so it would
   rebreak these lines on some iPhones and not others.
   ==========================================================================*/

.cs-q-h1{
  position:absolute;
  left:calc(var(--u) * 170);
  right:calc(var(--u) * 170);
  top:calc(var(--u) * 396);
  margin:0;
  text-align:center;
  font-weight:700;
  font-size:calc(var(--u) * var(--qsize));
  line-height:1.27;
  letter-spacing:calc(var(--u) * -1);
  color:var(--ink);
}

/* ==========================================================================
   The band - intro, insight and result content
   --------------------------------------------------------------------------
   One geometry for all three so the three screens share a rhythm: a fixed box
   from 400 to 1230, with its content centred in it. Centring rather than top
   anchoring is what lets a 2-line result headline and a 6-line insight both
   look deliberate in the same box.

   overflow is visible on purpose. If a copy revision ever outgrows the band
   even at the bottom of the --fscale ladder, it should be visibly wrong in
   review rather than silently clipped mid-sentence.
   ==========================================================================*/

.cs-band{
  position:absolute;
  left:calc(var(--u) * 170);
  right:calc(var(--u) * 170);
  top:calc(var(--u) * 400);
  height:calc(var(--u) * 830);
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  text-align:center;
}

.cs-band > *{ margin:0; }

/* ---- intro -------------------------------------------------------------- */

/* The intro is composed the way the retired g1-intro.png export was: a large
   centred mark as a title card, then the headline at display size, then the
   subhead. Three numbers come off that export - the mark is 252 square at 170,
   the type is set much larger than on the other views, and the copy runs in
   the FULL 870 column (105 either side) rather than the 740 the question
   headline uses. That column is what keeps "Test Your Financial Know-How" on
   two lines at 104; in the 740 column it would break to three and stop
   reading as a title.

   Its band therefore differs from the shared one - it starts below the mark,
   at 470, and ends on the same 1230. flow.js measures each band's own height
   rather than assuming the shared 830, so this needs no counterpart there.

   NOT reproduced from that export: the "Booyah!" gradient lockup. It is the
   Money Momentum campaign's, which the client asked to remove entirely, and
   the reserved footer band is about to carry a gradient "more" lockup on every
   screen that a second wordmark here would compete with. */

.cs-intro-mark{
  position:absolute;
  left:50%;
  top:calc(var(--u) * 96);
  width:calc(var(--u) * 200);
  height:calc(var(--u) * 200);
  transform:translateX(-50%);
  user-select:none;
  -webkit-user-drag:none;
}

/* The "Booyah!" lockup, cropped from the retired g1-intro.png export at its
   native 842 x 226 and set here at 760 wide - 0.90 of the export's size. It is
   scaled down rather than reproduced at 1:1 because the intro now carries a
   subhead more than three times the length of the one that export was drawn
   around (159 characters against 46), and something had to give: the artwork
   scales cleanly, the client's copy does not.

   The height is written out rather than left to `auto` so the band below it
   cannot shift while the PNG is still decoding. */
.cs-intro-lockup{
  position:absolute;
  left:50%;
  top:calc(var(--u) * 356);
  width:calc(var(--u) * 760);
  height:calc(var(--u) * 204);
  transform:translateX(-50%);
  user-select:none;
  -webkit-user-drag:none;
}

[data-view="intro"] .cs-band{
  left:calc(var(--u) * 105);
  right:calc(var(--u) * 105);
  top:calc(var(--u) * 630);
  height:calc(var(--u) * 620);
}

.cs-intro-h1{
  font-size:calc(var(--u) * var(--fscale) * 104);
  line-height:1.16;
  font-weight:700;
  letter-spacing:calc(var(--u) * -2);
  color:var(--ink);
}

.cs-intro-sub{
  margin-top:calc(var(--u) * var(--fscale) * 56) !important;
  padding:0 calc(var(--u) * 40);
  font-size:calc(var(--u) * var(--fscale) * 52);
  line-height:1.36;
  font-weight:500;
  color:var(--slate);
}

/* ---- insight ------------------------------------------------------------ */

/* "Answer:" is a label, not a headline - it stays small and quiet so the
   answer itself is what the eye lands on. */
.cs-ins-label{
  font-size:calc(var(--u) * var(--fscale) * 42);
  line-height:1.2;
  font-weight:500;
  color:var(--slate);
}

.cs-ins-answer{
  margin-top:calc(var(--u) * var(--fscale) * 22) !important;
  font-size:calc(var(--u) * var(--fscale) * 68);
  line-height:1.22;
  font-weight:700;
  letter-spacing:calc(var(--u) * -1);
  color:var(--ink);
}

.cs-ins-body{
  margin-top:calc(var(--u) * var(--fscale) * 46) !important;
  font-size:calc(var(--u) * var(--fscale) * 44);
  line-height:1.38;
  font-weight:500;
  color:var(--slate);
}

/* ---- result ------------------------------------------------------------- */

.cs-tick{
  width:calc(var(--u) * var(--fscale) * 128);
  height:calc(var(--u) * var(--fscale) * 128);
  flex:none;
}

.cs-res-h1{
  margin-top:calc(var(--u) * var(--fscale) * 54) !important;
  font-size:calc(var(--u) * var(--fscale) * 76);
  line-height:1.22;
  font-weight:700;
  letter-spacing:calc(var(--u) * -1);
  color:var(--ink);
}

.cs-rule{
  margin-top:calc(var(--u) * var(--fscale) * 52) !important;
  display:flex;
  align-items:center;
  justify-content:center;
  gap:calc(var(--u) * 31);
  flex:none;
}

.cs-rule-line{
  display:block;
  width:calc(var(--u) * 160);
  height:calc(var(--u) * 3);
  background:var(--slate);
}

.cs-rule-dot{
  display:block;
  width:calc(var(--u) * 20);
  height:calc(var(--u) * 20);
  border-radius:50%;
  background:var(--blue);
}

.cs-res-body{
  margin-top:calc(var(--u) * var(--fscale) * 52) !important;
  font-size:calc(var(--u) * var(--fscale) * 50);
  line-height:1.32;
  font-weight:500;
  color:var(--slate);
}

/* ==========================================================================
   Choice stack
   ==========================================================================*/

/* The four answer buttons are always the SAME height, whether their copy runs
   to one line or two, so the text centres in a button of consistent size.

   That height is --arow, and js/flow.js computes it: it measures each pill's
   natural height and writes the tallest back. It is NOT done with
   grid-auto-rows:1fr, which is the obvious way and was the first attempt.
   Blink resolves fr track sizing in a second layout pass, so inside the fit
   loop getBoundingClientRect() returns the un-equalised min-height and the
   ladder accepts a rung that then grows past the floor - which is exactly the
   bug this replaced. Measuring natural heights and writing one number back is
   deterministic in one pass, and does not lean on fr behaviour that older
   in-app webviews get wrong anyway. */
.cs-choices{
  position:absolute;
  left:0; right:0;
  top:calc(var(--u) * 660);
  display:flex;
  flex-direction:column;
  align-items:stretch;
  flex-wrap:nowrap;
  gap:calc(var(--u) * var(--agap));
  padding:0 calc(var(--u) * 107);
  margin:0;
}

/* The intro, insight and result stacks are a single CTA at one fixed
   position - 1310 puts its baseline of 1465 just clear of the reserved
   footer band, on all three views alike. */
[data-view="intro"]   .cs-choices,
[data-view="insight"] .cs-choices,
[data-view="result"]  .cs-choices{ top:calc(var(--u) * 1310); }

/* ---- the answer pill, the default look for every choice ----------------- */
/* 866 wide x 160 tall between x=105 and x=971, fully rounded: at mid height
   the client frame's card is at full width and it curves the whole way to
   both ends, so the radius is half the height, not a soft corner. */
.cs-pill{
  position:relative;
  width:100%;
  min-height:calc(var(--u) * var(--arow));
  margin:0;
  padding:calc(var(--u) * 24) calc(var(--u) * 130) calc(var(--u) * 24) calc(var(--u) * 55);
  display:flex;
  align-items:center;
  justify-content:flex-start;
  text-align:left;
  background:var(--card);
  border:calc(var(--u) * 3) solid var(--hair);
  border-radius:calc(var(--u) * 80);
  color:var(--ink);
  font-family:var(--font);
  font-weight:500;
  font-size:calc(var(--u) * var(--asize));
  line-height:1.24;
  text-decoration:none;
  -webkit-appearance:none;
  appearance:none;
  cursor:pointer;
  transition:background-color .16s ease-out, border-color .16s ease-out;
  pointer-events:auto;
  -webkit-tap-highlight-color:transparent;
}

/* pressed state - the tint the client frames show on the picked answer */
.cs-pill:active{
  background:var(--pick);
  border-color:var(--pick);
}

/* chevron, drawn rather than shipped so CSS can tint it */
.cs-pill::after{
  content:"";
  position:absolute;
  right:calc(var(--u) * 57);
  top:50%;
  width:calc(var(--u) * 20); height:calc(var(--u) * 20);
  border-right:calc(var(--u) * 6) solid var(--chev);
  border-top:calc(var(--u) * 6) solid var(--chev);
  transform:translateY(-50%) rotate(45deg);
}

/* ---- answered state ----------------------------------------------------- */

/* Once the question is answered the correct pill is always marked, and the
   picked one is marked too when it was wrong - which is exactly what the
   client's two flow frames show. The chevron goes: it invites a tap the pill
   no longer accepts, and the stack is inert from here until the view changes.

   :active is suppressed in the same breath. Without that, a tap-and-hold on a
   locked pill still paints the blue pressed state over the red or green. */
[data-phase] .cs-pill{ pointer-events:none; }
[data-phase] .cs-pill::after{ content:none; }
[data-phase] .cs-pill:active{ background:var(--card); border-color:var(--hair); }

.cs-pill.is-correct,
[data-phase] .cs-pill.is-correct:active{
  background:var(--ok-fill);
  border-color:var(--ok-line);
}

.cs-pill.is-wrong,
[data-phase] .cs-pill.is-wrong:active{
  background:var(--no-fill);
  border-color:var(--no-line);
  color:var(--no-ink);
}

/* ---- CTA pill: same component, different job --------------------------- */
/* Used by the intro ("Start Quiz"), the insight ("Continue") and the result
   ("Visit Schwab.com"). */
.cs-cta{
  width:calc(var(--u) * 596);
  align-self:center;
  min-height:calc(var(--u) * 155);
  padding:0 calc(var(--u) * 40);
  justify-content:center;
  text-align:center;
  background:var(--cta);
  border:0;
  color:#fff;
  font-weight:700;
  font-size:calc(var(--u) * 50);
  border-radius:calc(var(--u) * 78);
}
.cs-cta:active{ background:var(--cta); border-color:transparent; }
.cs-cta::after{ content:none; }

/* ---- accessibility ------------------------------------------------------ */

.cs-pill:focus-visible{
  outline:calc(var(--u) * 6) solid var(--blue);
  outline-offset:calc(var(--u) * 4);
}

@media (prefers-reduced-motion: reduce){
  .cs-pill{ transition:none; }
}

/* ==========================================================================
   Reserved footer band
   --------------------------------------------------------------------------
   1540 to the bottom of the frame, held empty for the "You might know more
   than you think." lockup the client is supplying separately. Nothing is
   allowed to grow into it; FIT.floor in flow.js is what enforces that.

   When the artwork arrives: drop an <img> into .cs-footer in index.html and
   nothing here or in flow.js needs to move.
   ==========================================================================*/

.cs-footer{
  position:absolute;
  left:0; right:0;
  top:calc(var(--u) * 1540);
  bottom:0;
  pointer-events:none;
}
