/* ====================================================================
   THE SWEET IN THE DISC, ITS EIGHT GROUNDS, AND THE SHUFFLE
   ====================================================================

   Everything about what a candy face LOOKS like is here, and nothing about
   what it is: the catalogue of eleven and eight is a shared contract
   (shared/src/candy-faces.ts) and the paths are a module of their own
   (admin/src/views/candy-face-art.ts). This file is the third of the three
   and the only one allowed to name a colour or a duration.

   FOUR ROLES, NOT FOUR COLOURS. Every path in every drawing wears one of
   `body`, `shade`, `deep` or `line`, and each of those is derived from the
   ground rather than chosen beside it. That is what makes eleven drawings
   hold on eight grounds without eighty-eight decisions: there is one
   decision, the ground, and the other three follow it. It is also the only
   way to stay inside rule 13, since the brand supplies eight pastels and no
   palette to invent a twelfth accent from.

   THE GROUNDS ARE THE CATEGORY TINTS, mapped here and named for the colour
   they are. They are the brand's only pastel set, they are already
   theme-aware in both directions (console-theme.css keeps `--cbc-cat-*`
   pointing at the right variant), and the shared catalogue deliberately does
   not borrow their audit names: nothing about somebody's profile picture is
   about local visibility. This block is the whole of the translation.
   ==================================================================== */

.cb-sweet[data-tint="coral"],
.cb-face-burst[data-tint="coral"] {
  --cb-face-ground: var(--cbc-cat-performance);
}
.cb-sweet[data-tint="amber"],
.cb-face-burst[data-tint="amber"] {
  --cb-face-ground: var(--cbc-cat-accessibility);
}
.cb-sweet[data-tint="lime"],
.cb-face-burst[data-tint="lime"] {
  --cb-face-ground: var(--cbc-cat-seo);
}
.cb-sweet[data-tint="mint"],
.cb-face-burst[data-tint="mint"] {
  --cb-face-ground: var(--cbc-cat-aiVisibility);
}
.cb-sweet[data-tint="aqua"],
.cb-face-burst[data-tint="aqua"] {
  --cb-face-ground: var(--cbc-cat-design);
}
.cb-sweet[data-tint="periwinkle"],
.cb-face-burst[data-tint="periwinkle"] {
  --cb-face-ground: var(--cbc-cat-techStack);
}
.cb-sweet[data-tint="lilac"],
.cb-face-burst[data-tint="lilac"] {
  --cb-face-ground: var(--cbc-cat-localVisibility);
}
.cb-sweet[data-tint="bubblegum"],
.cb-face-burst[data-tint="bubblegum"] {
  --cb-face-ground: var(--cbc-cat-marketingFootprint);
}

/* THE BURST IS TINTED THE SAME WAY, AND IT HAS TO BE ASKED SEPARATELY. The
   scraps of sugar hang on the chip rather than inside the disc, because the
   disc clips to a circle and sugar that stops at the rim is not sugar leaving.
   That puts them outside `.cb-sweet`, so they cannot inherit its ground and are
   given the same attribute instead. One mapping, two readers.

   The other three roles, derived from the ground rather than chosen beside it.
   Mixed towards the ink, so each carries the ground's own hue and no pairing
   can go muddy: a sweet on lime and a sweet on lilac are the same drawing in
   two families, which is what makes eighty-eight faces read as one set. */
.cb-sweet,
.cb-face-burst {
  --cb-face-body: var(--cb-color-white);
  --cb-face-shade: color-mix(in srgb, var(--cb-color-blackcurrant) 20%, var(--cb-face-ground));
  --cb-face-deep: color-mix(in srgb, var(--cb-color-blackcurrant) 38%, var(--cb-face-ground));
  --cb-face-line: color-mix(in srgb, var(--cb-color-blackcurrant) 62%, var(--cb-face-ground));
}
.cb-sweet {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--cb-face-ground);
}
.cb-sweet svg {
  /* Eighty per cent, so the sweet has ground around it on every side. A
     drawing that touches the rim of a disc reads as cropped, and the one thing
     these must not look like is a photograph that did not fit. */
  inline-size: 80%;
  block-size: 80%;
  overflow: visible;
}

/* The four roles, applied, AND THEY SET `color` RATHER THAN `fill`.
   
   That is the whole of the fix for a bug worth writing down. A class setting
   `fill` beats a `fill="none"` ATTRIBUTE, because CSS wins over presentation
   attributes, so every stroked path in every drawing quietly filled itself in
   solid: the lollipop's spiral vanished under its own fill and the sweet came
   out as a plain white circle on a stick, which is a magnifying glass. Setting
   `color` here and saying `currentColor` on each mark leaves the attribute in
   charge of whether a shape is filled, stroked, or both. */
.cb-fa-body {
  color: var(--cb-face-body);
}
.cb-fa-shade {
  color: var(--cb-face-shade);
}
.cb-fa-deep {
  color: var(--cb-face-deep);
}
.cb-fa-line {
  color: var(--cb-face-line);
}

/* THE INITIALS ARE STILL THERE AND NOTHING HAS TO HIDE THEM. The sweet is a
   positioned layer with an opaque ground filling the whole disc, and a
   positioned child paints over its parent's own text whatever the source order
   says, so the letter is behind the sweet rather than turned off. That matters
   for the one case worth designing for: a browser that cannot paint the SVG
   still has a disc with a person's initials in it. The photograph, appended
   after the sweet, paints over both. */

/* ---- The shuffle, opposite the cross ----

   TOP-LEFT, BECAUSE THE CROSS IS TOP-RIGHT. The two things you can do to a
   picture you did not choose are take it off and try another, and giving each
   its own corner means neither is ever where the other was a moment ago. It is
   pinned to the set, like the cross, so it moves nothing by being here.

   IT IS NEVER TAKEN AWAY, unlike the cross, and that difference is the design.
   The cross has nothing to undo until there is something to undo; the shuffle
   always has another sweet to offer, so on the one screen where the result
   would be hidden under somebody's own photograph it is turned off with the
   reason on it (rule 15) rather than vanishing. */
.cb-face-shuffle {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-3xl);
  block-size: var(--cb-space-3xl);
  padding: 0;
  border: 1px solid var(--cbc-line-strong);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-raised);
  /* IT HAS TO BE FINDABLE, WHICH IS NOT THE SAME AS LOUD. A hairline chip in
     the raised colour is invisible on a light card: this sat on a white ground
     in muted ink behind a white border and read as a smudge on the picture. The
     shadow is what separates it from the disc, and the ink is full strength
     because the whole control is one small icon. */
  box-shadow: var(--cb-shadow-card);
  color: var(--cbc-ink);
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-face-shuffle:hover {
  background: var(--cbc-accent);
  border-color: var(--cbc-accent);
  color: var(--cbc-on-accent);
}
/* THE ICON DOES NOT TURN ON HOVER, and it used to. A quarter turn was meant to
   say "this rearranges something", and it says nothing of the sort: a shuffle
   icon is two crossed arrows, turning it ninety degrees just points them
   somewhere else, and the eye reads a control that fidgets when approached.
   The row of arrows is already the affordance. What hover does here is what
   hover does everywhere else in this console, which is fill the control in. */
.cb-face-shuffle .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}
.cb-face-shuffle[aria-disabled="true"] {
  cursor: default;
  opacity: 0.4;
}
.cb-face-shuffle[aria-disabled="true"]:hover {
  background: var(--cbc-raised);
  border-color: var(--cbc-line-strong);
  color: var(--cbc-ink);
}

/* ---- The three steps ----

   THE TWO DURATIONS ARE DECLARED HERE AND READ BY THE SCRIPT. face-shuffle.ts
   asks the button for these two custom properties at the press and swaps the
   sweet on their schedule, so the keyframes below and the moment the drawing
   changes cannot drift apart: one number, read twice. It is also why neither
   of them is a calc(), which an unregistered custom property hands back
   unresolved. */
.cb-face-shuffle,
.cb-face-disc {
  --cb-face-wind: var(--cb-motion-slow);
  --cb-face-pop: var(--cb-motion-slow);
}

/* 1. WIND. It rattles, turns against itself and tightens to four-fifths,
      easing INTO the hold. A reveal with no anticipation is a swap. */
@keyframes cb-face-wind {
  0% {
    transform: scale(1) rotate(0deg);
  }
  22% {
    transform: scale(0.96) rotate(-7deg);
  }
  44% {
    transform: scale(0.93) rotate(6deg);
  }
  64% {
    transform: scale(0.9) rotate(-5deg);
  }
  82% {
    transform: scale(0.87) rotate(4deg);
  }
  100% {
    transform: scale(0.83) rotate(-1deg);
  }
}
/* 2 and 3. POP, then SETTLE, which is the spring's own tail rather than a
      third animation: overshoot, undershoot, home. */
@keyframes cb-face-pop {
  0% {
    transform: scale(0.83) rotate(-1deg);
  }
  36% {
    transform: scale(1.16) rotate(2deg);
  }
  62% {
    transform: scale(0.96) rotate(0deg);
  }
  100% {
    transform: scale(1) rotate(0deg);
  }
}
.cb-face-disc[data-turn="wind"] {
  animation: cb-face-wind var(--cb-face-wind) var(--cb-motion-ease) both;
}
.cb-face-disc[data-turn="pop"] {
  animation: cb-face-pop var(--cb-face-pop) var(--cbc-spring) both;
}

/* The new sweet arrives INSIDE a disc that has already popped, a beat behind
   it, small and turned. That order is the whole difference between a reveal and
   two things moving at once (rule 14). */
@keyframes cb-face-arrive {
  0% {
    opacity: 0;
    transform: scale(0.45) rotate(-24deg);
  }
  55% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: scale(1) rotate(0deg);
  }
}
.cb-sweet[data-arrived] svg {
  animation: cb-face-arrive var(--cb-motion-slow) var(--cbc-spring) both;
}

/* ---- The burst ----

   Twelve scraps of sugar, hung on the chip rather than inside the disc, because
   the disc clips its contents to a circle and sugar that stops at the rim is
   not sugar leaving. Each carries its own angle, distance and turn from the
   script; everything else about them is here. */
/* THE CHIP AROUND THE DISC STOPS CLIPPING WHILE THIS IS HERE. `.cb-ask-chip`
   hides its overflow, which is right for a chip holding an icon and fatal for a
   burst: the sugar stopped dead at the edge of the disc. Safe, because the disc
   inside it clips itself, being a circle with `overflow: hidden` of its own. */
.cb-face-chip {
  overflow: visible;
}
.cb-face-burst {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.cb-face-bit {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  inline-size: var(--cb-space-md);
  block-size: var(--cb-space-md);
  margin-block-start: calc(var(--cb-space-md) / -2);
  margin-inline-start: calc(var(--cb-space-md) / -2);
  border-radius: var(--cb-radius-xxs);
  animation: cb-face-bit var(--cb-motion-slow) var(--cb-motion-ease) both;
}
/* Three colours cycled rather than rolled, so every burst carries some of each.
   The ground and its two derivations, which is the sweet that just arrived
   coming apart rather than a fourth palette turning up for half a second. */
.cb-face-bit[data-bit="0"] {
  background: var(--cb-face-ground);
}
.cb-face-bit[data-bit="1"] {
  background: var(--cb-face-body);
}
.cb-face-bit[data-bit="2"] {
  background: var(--cb-face-shade);
}

/* The shockwave: one ring, out through the disc and gone. It is the release of
   the wind-up made visible, which is why there is exactly one of them: a ring
   that repeats is a pulse, and a pulse says something is still happening. */
.cb-face-ring {
  position: absolute;
  inset: 0;
  border: 2px solid var(--cb-face-ground);
  border-radius: var(--cb-radius-full);
  animation: cb-face-ring var(--cb-motion-slow) var(--cb-motion-ease) both;
}
@keyframes cb-face-ring {
  0% {
    opacity: 0.9;
    transform: scale(0.86);
  }
  100% {
    opacity: 0;
    transform: scale(1.7);
  }
}

/* Out along its own angle, turning, shrinking to nothing. The distance is a
   multiple of a spacing step rather than a length, so the script can vary it
   without writing a pixel (rule 13). */
@keyframes cb-face-bit {
  0% {
    opacity: 1;
    transform: rotate(var(--cb-bit-turn)) translateY(0) rotate(0deg) scale(0.6);
  }
  30% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: rotate(var(--cb-bit-turn))
      translateY(calc(var(--cb-space-6xl) * var(--cb-bit-far) * -1)) rotate(var(--cb-bit-spin))
      scale(0.2);
  }
}

/* NOTHING MOVES WHERE SOMEBODY HAS ASKED FOR STILLNESS. The script already
   refuses to run the sequence at all under either switch, so this is the
   belt-and-braces half: a burst left over from a preference changed mid-press
   holds still rather than finishing. */
@media (prefers-reduced-motion: reduce) {
  .cb-face-disc[data-turn],
  .cb-sweet[data-arrived] svg,
  .cb-face-bit,
  .cb-face-ring {
    animation: none;
    transition: none;
    transform: none;
  }
}
:root[data-motion="reduced"] .cb-face-disc[data-turn],
:root[data-motion="reduced"] .cb-sweet[data-arrived] svg,
:root[data-motion="reduced"] .cb-face-bit,
:root[data-motion="reduced"] .cb-face-ring {
  animation: none;
  transition: none;
  transform: none;
}

/* NOTHING PER-SURFACE, AND THAT IS DELIBERATE. The first run's sentence and the
   profile panel both blow the disc up from the forty pixels a row uses, and
   this control stays where and what it is on all of them: pinned to the top
   corner of the set, at a size a pointer can hit. A cross or a shuffle that
   grew with the picture would be a target the size of somebody's chin. */
