/* =====================================================
   kid-companion.css — the one figure that is on screen the whole time.

   Loaded on abby-games.html and bella-games.html only, after css/fonts.css
   (the --font-* tokens), css/style.css (every colour token and the global
   reduced-motion flatten), css/kid-games.css (--kg-target, --kg-tint) and
   css/kid-motion.css (the easings, the durations and .kg-press). Everything
   this file introduces is prefixed kg-cmp-, per the rule the other two kid
   sheets follow: css/style.css is one flat namespace shared with the reader
   and the Arcade, and an unprefixed selector here would reach both.

   REDUCED MOTION IS NOT HANDLED HERE, ON PURPOSE, and for the same reason
   kid-hub.css does not handle it: css/style.css:559-562 already flattens every
   animation-duration and transition-duration globally, so every state below
   still lands, instantly. The one thing a stylesheet cannot flatten is an
   infinite keyframe — style.css parks it at whatever 100% happens to be, which
   for a bob is mid-air — so js/kidgames/companion.js never adds .kg-cmp-float
   at all when a child has asked for less motion.

   WHY THE ROW IS BETWEEN THE METER AND THE STAGE, rather than floating over
   the card: a companion positioned over the play area is a companion that can
   sit on top of a choice. On a 360px phone the games fill the card edge to
   edge, and there is no corner of it that is reliably empty in all of them.
   So the figure takes a row of its own that nothing else is in, above the
   card and below the meter — always visible without scrolling, and
   structurally unable to cover a target.
   ===================================================== */


/* ---------------- Size ----------------
   THE BUTTON IS A TARGET LIKE ANY OTHER. Brief §4 is 76 CSS px for Bella and
   57 for Abby, so the floor is --kg-target and not a number typed again here;
   the clamp only ever grows it.

   The drawing is EXACTLY the button and never larger. It was 1.14x for a
   while, on the reasoning that a notebook drawn at Abby's 57px floor is a
   smudge — but a drawing wider than its hit box means the top of the star and
   the rim of its plate are outside the tappable region, which is the opposite
   of the point of making the companion a target at all. The smudge was the
   viewBox, not the size: both are cropped to their own ink now, so the figure
   fills its box and the box is the target. */
.kg-cmp {
  --kg-cmp-size: clamp(var(--kg-target, 57px), 19vw, 88px);
  --kg-cmp-art: var(--kg-cmp-size);
  --kg-cmp-lean: 1;

  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
}


/* ---------------- The figure ----------------
   Three nested elements for three transforms, which is not tidiness: .kg-press
   animates transform on the button, the lean writes it on the figure and the
   bob owns it on its own node, and one element cannot carry three. The same
   split js/kidgames/hub.js makes for the travelling child. */
.kg-cmp-btn {
  flex: 0 0 auto;
  width: var(--kg-cmp-size);
  height: var(--kg-cmp-size);
  min-width: var(--kg-target, 44px);
  min-height: var(--kg-target, 44px);
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  border-radius: 50%;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.kg-cmp-btn:focus-visible {
  outline: 4px solid var(--accent, #7c4dff);
  outline-offset: 3px;
}

.kg-cmp-figure {
  display: block;
  width: var(--kg-cmp-art);
  height: var(--kg-cmp-art);
  transition: transform .3s var(--kg-ease-back, ease);
}
.kg-cmp-bob,
.kg-cmp-art-host {
  display: block;
  width: 100%;
  height: 100%;
}
.kg-cmp-art {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  filter: drop-shadow(0 5px 7px rgba(53, 49, 94, .26));
}

/* The one infinite animation on the page. Added by JS, and never at all when a
   child has asked for less motion. */
.kg-cmp-float { animation: kg-cmp-bob 3.6s ease-in-out infinite; }
@keyframes kg-cmp-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5%); }
}

/* Speaking. On its own node because .kg-cmp-float already owns transform on
   the one below it, and two animations writing one property is a race. */
.kg-cmp[data-talking="on"] .kg-cmp-art-host {
  animation: kg-cmp-talk .42s var(--kg-ease-back, ease) 2;
}
@keyframes kg-cmp-talk {
  0%, 100% { transform: scale(1); }
  40%      { transform: scale(1.05, .96); }
  70%      { transform: scale(.98, 1.03); }
}


/* ---------------- The moods ----------------
   Every variant of every part is in the markup at all times and exactly one of
   each is shown. A mood change is therefore one attribute write on the root:
   no re-parse, no reflow of the drawing, and no frame in which the figure has
   two mouths or none. */
.kg-cmp-var { display: none; }

/* eyes — dots are canon; `up` is BOTH raised, never one closed, because one
   closed reads as a wink and a wink at a child who just got something wrong
   reads as being teased. */
.kg-cmp[data-mood="idle"]  .kg-cmp-eyes-dots,
.kg-cmp[data-mood="point"] .kg-cmp-eyes-dots,
.kg-cmp[data-mood="again"] .kg-cmp-eyes-dots,
.kg-cmp[data-mood="think"] .kg-cmp-eyes-up,
.kg-cmp[data-mood="cheer"] .kg-cmp-eyes-arcs { display: block; }

/* mouths — four, and not one of them turns down. */
.kg-cmp[data-mood="idle"]  .kg-cmp-mouth-smile,
.kg-cmp[data-mood="point"] .kg-cmp-mouth-smile,
.kg-cmp[data-mood="think"] .kg-cmp-mouth-hm,
.kg-cmp[data-mood="again"] .kg-cmp-mouth-soft,
.kg-cmp[data-mood="cheer"] .kg-cmp-mouth-grin { display: block; }

/* marks — the shared pictogram vocabulary, on the star's plate and on the
   notebook's page. */
.kg-cmp[data-mood="think"] .kg-cmp-mark-think,
.kg-cmp[data-mood="point"] .kg-cmp-mark-point,
.kg-cmp[data-mood="cheer"] .kg-cmp-mark-cheer,
.kg-cmp[data-mood="again"] .kg-cmp-mark-again { display: block; }

/* The star's plate is only there to hold a mark, so it goes when there is
   none. The notebook has no plate: its page is the plate, and the page's rules
   are what it shows at rest. */
.kg-cmp[data-mood="idle"] .kg-cmp-mark-plate { display: none; }
.kg-cmp[data-mood="idle"] .kg-cmp-page-idle { display: block; }

/* The arrow turns to face the thing it is pointing at. --kg-cmp-lean is
   written by JS from the two bounding boxes; transform-box is what makes a CSS
   transform on an SVG group resolve against the group's own box rather than
   the whole viewport. */
.kg-cmp-mark-point {
  transform: scaleX(var(--kg-cmp-lean, 1));
  transform-box: fill-box;
  transform-origin: center;
}

/* The lean itself. `point` also steps toward the target; `think` and `again`
   only tilt, because leaning away from the screen while apologising for a
   miss reads as backing off. */
.kg-cmp[data-mood="think"] .kg-cmp-figure {
  transform: rotate(calc(var(--kg-cmp-lean, 1) * -8deg));
}
.kg-cmp[data-mood="again"] .kg-cmp-figure {
  transform: rotate(calc(var(--kg-cmp-lean, 1) * -5deg));
}
.kg-cmp[data-mood="point"] .kg-cmp-figure {
  transform: rotate(calc(var(--kg-cmp-lean, 1) * 7deg))
             translateX(calc(var(--kg-cmp-lean, 1) * 3px));
}
.kg-cmp[data-mood="cheer"] .kg-cmp-figure {
  transform: translateY(-6%) scale(1.06);
}


/* ---------------- The bubble ----------------
   aria-hidden in the markup: every game already writes its sentence into its
   own polite live region, so this is a second COPY for a child who cannot
   hear, not a second announcement for a child who cannot see. */
.kg-cmp-bubble {
  flex: 0 1 auto;
  min-width: 0;
  margin: 0;
  position: relative;
  padding: 10px 14px;
  border-radius: 18px;
  background: #fff;
  color: var(--ink, #35315e);
  font-size: clamp(.92rem, 3.4vw, 1.05rem);
  line-height: 1.3;
  box-shadow: 0 4px 12px rgba(53, 49, 94, .12);
  border: 2px solid var(--kg-tint, #f4efff);
  /* Two lines at the bubble's own size, then it stops. The full sentence is
     always still spoken and always still in the game's live region. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: opacity .22s var(--kg-ease-enter, ease), transform .22s var(--kg-ease-enter, ease);
}
/* The tail, pointing back at whoever said it. */
.kg-cmp-bubble::before {
  content: "";
  position: absolute;
  left: -9px;
  top: 50%;
  width: 16px;
  height: 16px;
  transform: translateY(-50%) rotate(45deg);
  background: #fff;
  border-left: 2px solid var(--kg-tint, #f4efff);
  border-bottom: 2px solid var(--kg-tint, #f4efff);
  border-bottom-left-radius: 4px;
}
/* No line, no bubble and no space held for one. It only ever appears and
   disappears at a screen boundary — inside a game the companion reacts and
   never writes, because the card is already printing every sentence — so this
   can never move a target under a finger mid-round. */
.kg-cmp[data-line="off"] .kg-cmp-bubble {
  display: none;
}
.kg-cmp[data-line="on"] .kg-cmp-bubble {
  opacity: 1;
  transform: none;
}


/* ---------------- Per child ----------------
   The bubble borrows the child's own palest wash so it belongs to her page
   rather than to a component library. */
body.theme-bella .kg-cmp-bubble { border-color: #ffd9e8; }
body.theme-abby  .kg-cmp-bubble { border-color: #e3d9fb; }


@media (max-width: 560px) {
  /* Two lines of bubble at a phone's text size is most of a small screen's
     width, so the figure gives some of its bleed back. */
  .kg-cmp { gap: 10px; margin-top: 10px; }
  .kg-cmp-bubble { padding: 9px 12px; border-radius: 16px; }
}

@media (min-width: 720px) {
  .kg-cmp { gap: 16px; }
}

/* No :hover rule on the figure. It would be a transform on the same element
   the lean writes one to, at the same specificity and later in the file, so a
   mouse resting anywhere on the button would silently cancel a point. */


/* ---------------- The row ----------------
   The companion and the way out of a game share one row, and that is the whole
   reason a game screen pays nothing for this feature: #kg-mountbar was already
   a full-width row with one button in it and a great deal of nothing beside
   it. On the hub the mount bar is hidden and the companion has the row to
   itself, which is where the bubble goes.

   Both are siblings of the two screens rather than children of either, because
   transitionScreens() detaches the outgoing section. */
.kg-topbar {
  width: 100%;
  max-width: 820px;
  margin: 8px auto 0;
  display: flex;
  align-items: center;
  /* 24px, not the 10 the other rows use: the way out of a game is the one
     destructive control on the page and the companion is deliberately the most
     attractive thing on it. WCAG 2.2 SC 2.5.8 wants the clearance and a
     five-year-old reaching for a smiling star wants it more. The slot below
     takes the remaining width, so in a game they are at opposite ends. */
  gap: 24px;
}
.kg-companion-slot {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
}
/* The button keeps its size and its place above the card; only the row it is
   in is shared now, so the margin that used to separate it from the meter
   belongs to the row. */
.kg-topbar > .kg-mountbar {
  flex: 0 0 auto;
  width: auto;
  max-width: none;
  margin: 0;
}


/* ---------------- On the hub, and in a game ----------------
   The bob is added and removed by js/kidgames/companion.js rather than
   switched here, for the reason kid-hub.css gives about .kg-float: an infinite
   keyframe is the one thing a stylesheet cannot flatten, so it must not exist
   while a child is being asked to watch something else. This attribute is what
   the two states are named by, and what a test can see. */
.kg-cmp[data-playing="on"] .kg-cmp-bubble { display: none; }
