/* ==========================================================================
   Eleições — per-template paint

   Geometry that a component has to compute (a polygon derived from a diagonal,
   a box whose x follows from its y) lives in js/geometry.js. Everything a
   designer would want to nudge — a colour, a shadow, a gradient — lives here.
   ========================================================================== */

/* ==========================================================================
   Projected Winner
   ========================================================================== */

.pw__field { background: var(--el-party); }

.pw__kicker,
.pw__title,
.pw__region,
.pw__subhead { color: var(--el-on-field); }

.pw__rule { color: var(--el-on-field); }

.pw__given   { color: var(--c-ink); }
.pw__surname { color: var(--el-party); }

/* ==========================================================================
   Two Candidate
   ========================================================================== */

/* Both halves carry a field element; only the winner's is painted. Keeping
   the loser's in the tree rather than swapping one element's clip is what lets
   each half be a single group that travels as a unit. */
.tc__field { display: none; }
.tc[data-party="dem"] .tc__field--l,
.tc[data-party="rep"] .tc__field--r { display: block; background: var(--el-party); }

/* The ground is not flat here: the approved frame has a soft vertical wash
   under the neutral half. */
.tc::before { background: var(--c-neutral); }

/* Header and footnote stand on opposite halves of the frame — the header up
   and right of the diagonal, the footnote down and left of it — so swapping
   which half is painted swaps both of them at once. This is the whole reason
   the REP variant could not just be a mirror. */
.tc[data-party="dem"] .tc__header { color: var(--el-on-ground); }
.tc[data-party="dem"] .tc__foot   { color: var(--c-white); }
.tc[data-party="rep"] .tc__header { color: var(--c-white); }
.tc[data-party="rep"] .tc__foot   { color: var(--el-on-ground); }
.tc__rule { background: currentColor; }

/* Two runs, each one baseline carrying two sizes — and the two ratios are
   different, which is why they are measured and not shared. The footnote's
   words are cap 24 under a cap 34 number; a candidate's percent SIGN is cap 71
   under cap 131 digits, sitting on the same baseline rather than raised. */
.tc__foot-num  { font-weight: 700; font-style: normal; }
.tc__foot-rest { font-weight: 400; font-style: normal;
                 font-size: calc(24 / var(--el-cap) * 1px); }

.tc__pct-num   { font-weight: 900; font-style: normal; }
.tc__pct-rest  { font-weight: 900; font-style: normal;
                 font-size: calc(71 / var(--el-cap) * 1px); }

.tc__pct-l, .tc__votes-l, .tc__name-l { color: var(--tc-ink-l); }
.tc__pct-r, .tc__votes-r, .tc__name-r { color: var(--tc-ink-r); }

.tc[data-party="dem"] { --tc-ink-l: var(--c-white); --tc-ink-r: var(--c-black); }
.tc[data-party="rep"] { --tc-ink-l: var(--c-black); --tc-ink-r: var(--c-white); }

/* The centre plate is a recess, not a card: dark on every inner edge, with the
   frame diagonal continuing across it as a change of tone.

   Two shadows rather than one because the artwork is not evenly lit — the top
   edge measures ~#A9 sixteen pixels in while the sides only reach ~#C7, so the
   light is above. One symmetric shadow cannot say that. */
.tc__plate { position: absolute; overflow: hidden; }

/* Clip on the half, paint on the fill inside it — the halves slide in from
   their far ends and the diagonal has to stay put while they do. Both on one
   element and the split becomes a moving edge instead of a mask. */
.tc__plate-half { position: absolute; inset: 0; }
.tc__plate-half--back  { clip-path: polygon(0 0, 100% 0, 100% 100%); }
.tc__plate-half--front { clip-path: polygon(0 0, 100% 100%, 0 100%); }
.tc__plate-fill { position: absolute; inset: 0; }

/* THE RECESS IS LIT FROM THE BOTTOM LEFT, and that is the whole shape of it.
   Measured inward from each true edge against a base of ~225:

       top     125 at 1px, 156 at 10, 184 at 20, gone by 45
       right   120 at 1px, 136 at 10, 153 at 20
       left    166 at 1px, 194 at 10, 213 at 20, gone by 35
       bottom  181 at 1px, 199 at 10, 216 at 20

   Top and right are nearly twice the depth of left and bottom, so one offset
   shadow says it: pushed DOWN and LEFT, which darkens the two edges it is
   pushed away from. A second, symmetric one seats the other two.

   Each fill is inset:0 on the full plate box and only its own triangle shows,
   so the strong pair lands in the back half and the mild pair in the front —
   which is exactly how the artwork divides. */
.tc__plate-fill--front {
  background: var(--c-neutral-hi);
  box-shadow:
    inset -4px 12px 40px rgba(0, 0, 0, 0.52),
    inset -4px 5px 14px rgba(0, 0, 0, 0.06),
    inset 0 0 26px rgba(0, 0, 0, 0.16);
}

/* The half the diagonal leaves ABOVE. Measured at ~#DF, not the ground grey —
   it is a shade of the plate, not a hole in it.

   THE CENTRE STEP LIVES ENTIRELY ON THIS SIDE. Sampled perpendicular to the
   diagonal, the front half reads a flat 225 right up to the line and the back
   half drops to 189 against its own base of 223, recovering over about 70px.
   So the division is not a line drawn between two tones — it is one half lying
   lower than the other, and only the lower half is shaded.

   It also deepens along the line: the back half's far field holds 223 down to
   the diagonal's midpoint and then falls to 170 at 78% of its length and 145
   at 86%. Two gradients say the two things. At 45deg the gradient line runs
   PERPENDICULAR to the split, and its 50% is exactly the split, because the
   diagonal joins two corners of a square box; at 135deg it runs ALONG the
   split, so its percentages are position down the line. */
.tc__plate-fill--back {
  background:
    linear-gradient(45deg,
      transparent 50%,
      rgba(0, 0, 0, 0.152) 50%,
      rgba(0, 0, 0, 0.072) 53%,      /* the middle stop bends the falloff: the
                                        artwork recovers fast then flattens,
                                        and two stops draw a straight ramp that
                                        stays ~10 too dark through the middle */
      rgba(0, 0, 0, 0) 60%),
    linear-gradient(135deg,
      rgba(0, 0, 0, 0) 45%, rgba(0, 0, 0, 0.42) 100%),
    #DFDFDF;
  box-shadow:
    inset -4px 12px 40px rgba(0, 0, 0, 0.52),
    inset -4px 5px 14px rgba(0, 0, 0, 0.06),
    inset 0 0 26px rgba(0, 0, 0, 0.16);
}

/* The tick on this frame is NOT the standard fraction of its box — the artwork
   scales it independently, at ~94% of the plate against 79.5% in a Projected
   Winner box. Wrapper carries the shadow; see stage.css.

   These fractions were re-derived when the plate was corrected. The previous
   set was fitted against a plate that was 11px high and 14px oversize, so it
   put the tick in the right place FOR THE WRONG BOX — which is exactly why the
   plate's own error had gone unnoticed. Ink measured at 751..1227 / 379..766
   against a plate of 506 at (731, 325). */
.tc__tick-wrap {
  position: absolute;
  inset: 0;
  filter: drop-shadow(26px 26px 55px rgba(0, 0, 0, 0.55));
}

.tc__tick {
  position: absolute;
  left: 3.95%;
  top: 10.67%;
  width: 94.27%;
  height: 76.68%;
  background: var(--el-party);
  clip-path: var(--tick-path);
}

/* ==========================================================================
   Race - Map
   ========================================================================== */

.rm::before { background: var(--el-ground); }

.rm__map { position: absolute; background: var(--c-white); }

/* The two corner triangles are lit from their own corner outward — a radial
   whose centre is the frame corner, measured at f 0.41 there and full colour
   by about 800px out. */
.rm__field-l {
  background: radial-gradient(
    circle 820px at 0% 100%,
    #07143E 0%, #0D2471 50%, #102E8F 100%
  );
}

.rm__field-r {
  background: radial-gradient(
    circle 820px at 100% 100%,
    #630A11 0%, #AE111D 50%, #DC1525 100%
  );
}

/* Everything that is not a triangle, painted with the ground gradient so the
   emblems drawn underneath are cut to the triangles without a mask. */
.rm__mask { background: var(--el-ground); }

.rm__band {
  background: linear-gradient(to bottom, #7A7A7A 0%, #545454 100%);
}

.rm__chip { background: var(--c-ink-deep); }
.rm__bar  { background: var(--c-white); }

.rm__header,
.rm__subhead,
.rm__reporting,
.rm__reporting-sym,
.rm__reporting-cap { color: var(--c-ink-deep); }

.rm__rule { color: var(--c-ink-deep); height: 4px; }

.rm .el-t--onfield { color: var(--c-white); }

/* ==========================================================================
   Senate Flips
   ========================================================================== */

.sf::before { background: var(--el-ground); }

/* The Capitol cutout. It sits UNDER the corner triangles and over the ground,
   which is the order the artwork uses — the building's wings run behind the
   party fields. */
.sf__photo {
  position: absolute;
  background-size: 100% 100%;
  background-repeat: no-repeat;
}

/* The field gradient runs PERPENDICULAR to the hypotenuse — full colour along
   the diagonal edge, half strength at the far corner. 225deg is that axis for
   a triangle whose hypotenuse falls to the right. */
.sf__field-l {
  background: linear-gradient(225deg, #164BAE 0%, #0E2779 50%, #081645 100%);
}
.sf__field-r {
  background: linear-gradient(135deg, #F04859 0%, #BA121F 50%, #6B0A12 100%);
}

.sf__band-l { background: linear-gradient(to bottom, #274090 0%, #182E77 100%); }
.sf__band-r { background: linear-gradient(to bottom, #E41E2E 0%, #C61926 100%); }

.sf__title { color: var(--c-black); }
.sf__rule-l { position: absolute; background: var(--c-blue); }
.sf__rule-r { position: absolute; background: var(--c-red); }

.sf__pill      { position: absolute; }
.sf__pill-name { position: absolute; inset: 0; }
.sf__pill-abbr {
  position: absolute;
  left: 0; top: 0;
  height: 100%;
  box-shadow: inset -4px 12px 40px rgba(0, 0, 0, 0.52);
}

.sf__pill[data-party="dem"] .sf__pill-name {
  background: linear-gradient(to bottom, #284191 0%, #071C5F 100%);
}
.sf__pill[data-party="dem"] .sf__pill-abbr { background: #0F3099; }
.sf__pill[data-party="rep"] .sf__pill-name {
  background: linear-gradient(to bottom, #E21E2D 0%, #A4131E 100%);
}
.sf__pill[data-party="rep"] .sf__pill-abbr { background: #B91320; }

.sf__pill-name { box-shadow: 5px 7px 16px rgba(0, 0, 0, 0.20); }

.sf .el-t--onfield { color: var(--c-white); }

/* The seat tally: the number at full size, the party name at half, sharing a
   baseline. The gap is a margin rather than a space character because a space
   at either size is wider than the 6px the artwork leaves. */
.sf__tally-num   { font-weight: 900; font-style: normal; }
.sf__tally-party {
  font-weight: 700;
  font-style: normal;
  font-size: calc(39 / var(--el-cap) * 1px);
}
.sf__tally--l .sf__tally-party { margin-left: 6px; }
.sf__tally--r .sf__tally-party { margin-right: 6px; }

/* ==========================================================================
   Balance of Power
   ========================================================================== */

.bp::before { background: var(--el-ground); }

.bp__field-l {
  background: linear-gradient(225deg, #1740A6 0%, #102E8F 50%, #081748 100%);
}
.bp__field-r {
  background: linear-gradient(135deg, #EC5462 0%, #DC1525 50%, #6E0B13 100%);
}

.bp__band-l { background: linear-gradient(to bottom, #102F92 0%, #0D2574 100%); }
.bp__band-r { background: linear-gradient(to bottom, #E51626 0%, #BF1220 100%); }

.bp__divider { position: absolute; background: var(--c-divider); }

.bp__control {
  background: linear-gradient(to bottom, var(--c-data) 0%, var(--c-data-shade) 100%);
}

.bp__ind {
  position: absolute;
  background: #333333;
  box-shadow: inset -4px 12px 40px rgba(0, 0, 0, 0.52);
}

.bp__title,
.bp__subhead,
.bp__seats,
.bp__seats-cap,
.bp__control-num,
.bp__control-cap { color: var(--c-black); }

.bp .el-t--onfield { color: var(--c-white); }

.bp__rule { position: absolute; background: var(--c-black); }

/* ==========================================================================
   The hemicycle
   ========================================================================== */

/* The canvas covers the whole stage rather than the slot, so the 3D camera
   projects straight into stage coordinates and the diff harness can be
   trusted about where a seat landed. It sits below the corner triangles for
   the same reason the artwork does: the flanks of the arc run under them. */
.hemi {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.hemi__canvas {
  position: absolute;
  left: 0;
  top: 0;
  width: 1920px;
  height: 1080px;
}

/* The clock. Not decoration and not a debug aid: this is the animation the
   playout engine seeks. `motion.js` pauses every animation under the root and
   sets `currentTime`; the renderer reads this one's position instead of a wall
   clock, so a non-real-time render produces the right frame. Removing it does
   not stop the motion — it stops the motion being reproducible. */
.hemi__clock {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

[data-motion="on"] .hemi__clock {
  animation: hemi-fill var(--hemi-dur) linear both;
}

@keyframes hemi-fill {
  from { opacity: 0; }
  to   { opacity: 0.01; }
}

/* The slot stays as the fallback for a box with no WebGL — a graphic that
   renders nothing at all is worse than one that says what is missing. */
.bp__hemicycle { z-index: 0; }
