/* Radial operation menu — the /play main-menu UI for the simplified game.
   Markup is generated by public/javascripts/menus/radial-menu.js; this file
   owns placement, colour and state.

   The menu lives inside #main-menu, which is a .container (the aspect-ratio
   locked 3:4 play area, container-type: size), so every cqh below is a
   percent of the play area's height — not the viewport's. */

#radial-menu {
  position: absolute;

  /* The free box, i.e. the play area minus the chrome. Container height is
     100cqh and the 3:4 lock makes its width 75cqh, so the chrome converts:

       header  7% tall + 1cqh border  -> free space starts at  8cqh
       sidebar  hidden                -> the full width is ours, from 0
       right edge                     -> 75cqh
       footer   hidden, but the two corner buttons sit in the bottom
                ~9cqh of the play area -> stop at 88cqh

     Written out rather than folded into one number so moving any piece of
     chrome is a one-line change here. The sidebar used to cost 7.5cqh off the
     left, which pushed the whole ring right of centre; with it gone the box is
     symmetric and so is the ring.

     The widest circle the box holds is min(75 - 0, 88 - 8) = 75cqh across, so
     width is the binding constraint. 68cqh leaves a 3.5cqh gutter either side
     — enough that the ring reads as sitting IN the scene rather than being
     cropped by it. */
  --radial-top: 8cqh;
  --radial-bottom: 88cqh;
  --radial-left: 0cqh;
  --radial-right: 75cqh;
  --radial-size: 68cqh;

  /* Nudge below the centre of the free box. Kept as its own offset rather than
     folded into --radial-top, which would misstate where the header ends.

     The box is 80cqh tall and the ring 68cqh, so there is 12cqh of slack —
     6cqh above and below when centred. That makes 6cqh the most this can be
     before the ring meets the bottom of the box, where the corner buttons
     start. */
  --radial-drop: 5.5cqh;

  /* Grey for a segment that is not available yet. */
  --radial-locked-color: #9aa0b5;

  width: var(--radial-size);
  height: var(--radial-size);
  /* Centred in the free box. With the sidebar gone that box is the full width
     of the play area, so this now agrees with centring on the play area — the
     calc is kept because the box is still the thing being centred in, and the
     header still shortens it vertically. */
  left: calc(
    (var(--radial-left) + var(--radial-right) - var(--radial-size)) / 2
  );
  top: calc(
    (var(--radial-top) + var(--radial-bottom) - var(--radial-size)) / 2 +
      var(--radial-drop)
  );

  /* Above the scene canvases (#overlay-canvas is 10, same as the old
     .main-menu-button) and below the sidebar (19), header and footer (20) —
     so the chrome always wins, including the sidebar's hover expansion. */
  z-index: 11;

  /* #main-menu is pointer-events: none (base.css); the segments opt back in
     individually so the gaps between them stay click-through to the scene. */
  pointer-events: none;
}

/* One wedge per operation. The only thing the wedge itself carries is the
   colour its three segments draw in. */
.radial-wedge-earth {
  --seg-color: var(--earth-color);
}
.radial-wedge-air {
  --seg-color: var(--air-color);
}
.radial-wedge-water {
  --seg-color: var(--water-color);
}
.radial-wedge-fire {
  --seg-color: var(--fire-color);
}

/* The three resting states, as tokens so the gap between "you can use this"
   and "not yet" is set in one place. An available segment sits well forward of
   an unavailable one — the ring should answer "what can I do right now?" at a
   glance, without reading the icons. */
#radial-menu {
  --seg-opacity-rest: 0.78;
  --seg-opacity-hover: 1;
  /* Unavailable, but still legible: the ring should read as a whole shape
     with parts of it asleep, not as a couple of segments floating in space.
     At roughly two-fifths of the resting opacity the gap still answers "what
     can I do right now?" without the dim wedges disappearing. */
  --seg-opacity-muted: 0.34;
}

.radial-segment {
  pointer-events: auto;
  opacity: var(--seg-opacity-rest);
  transition: opacity 0.15s ease-out;
}

.radial-segment:hover {
  opacity: var(--seg-opacity-hover);
}

/* The inner ring is reserved, so it draws nothing at all — an empty segment
   in full element colour pulls the eye to the one part of the wedge that does
   nothing. The shapes stay in the DOM with their geometry live, so giving the
   ring a job later is a matter of dropping these two lines.

   pointer-events also keeps the middle of the menu click-through to the scene,
   and is what stops :hover from lifting an invisible segment back into view —
   no hover override needed. */
.radial-segment-inner {
  opacity: 0;
  pointer-events: none;
}

/* Outline solid, background well under it — the ring reads as line art laid
   over the flower rather than as a panel covering it. */
.radial-segment-shape {
  fill: var(--seg-color);
  fill-opacity: 0.22;
  stroke: var(--seg-color);
  stroke-opacity: 0.95;
  stroke-width: 1.1;
}

.radial-segment-icon {
  /* The icon is decoration; hovering and clicking belong to the segment. */
  pointer-events: none;
}

.radial-segment-op-icon {
  fill: #ffffff;
}

/* The element icon carries the wedge's colour, so switching a segment to the
   locked grey below greys the icon with it. */
.radial-segment-element-icon {
  fill: var(--seg-color);
}

.radial-segment-math:not(.is-locked) {
  cursor: pointer;
}

/* A chargeable element segment is held, not clicked. `grab` says "put your
   finger on this and keep it there" where `pointer` would promise a click that
   does something on release. touch-action stops a hold on mobile turning into
   a scroll or a long-press selection partway through. */
.radial-segment-element:not(.is-disabled) {
  cursor: grab;
  touch-action: none;
}

.radial-segment-element:not(.is-disabled):active {
  cursor: grabbing;
}

/* Not available yet: an op the player has not unlocked, or an element whose
   round has not been won today. Redefining --seg-color inside the segment
   overrides the wedge's colour for the shape and the element icon alike.

   Everything here pushes the segment back rather than just draining its
   colour: it drops to a fifth of the resting opacity, the fill all but
   disappears and the outline thins, so an unavailable wedge reads as a faint
   groove in the ring instead of a button that happens to be grey. */
.radial-segment.is-locked,
.radial-segment.is-disabled {
  --seg-color: var(--radial-locked-color);
  opacity: var(--seg-opacity-muted);
  cursor: default;
}

.radial-segment.is-locked .radial-segment-shape,
.radial-segment.is-disabled .radial-segment-shape {
  fill-opacity: 0.14;
  stroke-opacity: 0.75;
  stroke-width: 0.9;
}

.radial-segment.is-locked:hover,
.radial-segment.is-disabled:hover {
  /* Inert: no hover lift, so "greyed out" also means "does nothing". */
  opacity: var(--seg-opacity-muted);
}

.radial-segment.is-locked .radial-segment-op-icon {
  fill: var(--radial-locked-color);
}

/* Enabling fanfare — played once, when a segment crosses from locked to
   available while the menu is on screen (radial-menu.js holds it back if the
   end-game tally is still up). Fades in from nothing through a bright flare
   and settles at the segment's resting 50%. */
/* Ends on the token, not a literal: the animation runs with `both`, so it
   holds its last frame until the class is removed. Any drift between that
   frame and the resting opacity shows up as a pop at the end of the fanfare. */
@keyframes radial-segment-wake {
  0% {
    opacity: 0;
  }
  18% {
    opacity: 1;
  }
  60% {
    opacity: 0.9;
  }
  100% {
    opacity: var(--seg-opacity-rest);
  }
}

@keyframes radial-segment-wake-flare {
  0% {
    transform: scale(0.93);
    filter: brightness(2.6);
  }
  30% {
    transform: scale(1.04);
    filter: brightness(2);
  }
  100% {
    transform: scale(1);
    filter: brightness(1);
  }
}

.radial-segment.is-awakening {
  animation: radial-segment-wake 1.2s ease-out both;
}

/* transform-box/origin spelled out so the flare scales about the middle of
   the ring (the viewBox is centred on 0,0) rather than about each path's own
   bounding box, which would make every wedge pulse in a different direction. */
.radial-segment.is-awakening .radial-segment-shape,
.radial-segment.is-awakening .radial-segment-icon {
  transform-box: view-box;
  transform-origin: 50% 50%;
  animation: radial-segment-wake-flare 1.2s ease-out both;
}

/* ---- hold-to-charge meters -----------------------------------------------
   One per element, parked where the thing it affects lives so a charge
   particle flying in reads as going somewhere. Built by element-meters.js into
   #flower-container, which is the aspect-locked play area — so cqh here is a
   percent of that, the same units the radial itself uses.

   Hidden unless the element has an action available: an empty meter for
   something you cannot do yet is a puzzle, not a goal. */
.element-meter {
  position: absolute;
  display: none;
  width: 22cqh;
  /* Above the scene canvases, below the chrome — same band as the radial. */
  z-index: 12;
  pointer-events: none;
  text-align: center;
}

/* Two conditions, both required. `is-visible` means the element has an action
   to charge toward at all; `is-shown` means the player is pointing at it (or
   holding it). A meter is an answer to "what would this button do?", so it
   appears when the question is being asked and stays out of the scene the rest
   of the time. */
.element-meter.is-visible.is-shown {
  display: block;
}

.element-meter-track {
  position: relative;
  height: 2.2cqh;
  border-radius: 2.2cqh;
  background: rgba(0, 0, 0, 0.45);
  border: 0.25cqh solid rgba(255, 255, 255, 0.35);
  overflow: hidden;
}

.element-meter-fill,
.element-meter-over {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  width: 0;
  transition: width 0.12s linear;
}

.element-meter-fill {
  background: var(--meter-color, #ffffff);
}

/* The surplus past the milestone, drawn over the full bar in a darker shade —
   this is what over-watering looks like. */
.element-meter-over {
  background: var(--meter-over-color, rgba(0, 0, 0, 0.45));
}

.element-meter-label {
  margin-top: 0.4cqh;
  font-size: 2cqh;
  font-weight: 700;
  color: #f7f3ec;
  text-shadow: 0 0.2cqh 0.4cqh rgba(0, 0, 0, 0.8);
}

/* Positions. Percentages of the play area, matching where each thing sits in
   the scene rather than being lined up as a HUD. */
.element-meter-earth {
  /* Down in the soil, well below the ground line.

     For reference, the horizon is canvasHeight * 0.7 (getGroundTopScreen in
     environment.js), so the ground line sits 30cqh up from the bottom and its
     midpoint to the floor is 15cqh. This sits lower again — the footer used to
     occupy the bottom 15cqh and no longer does, so there is room down here and
     the meter stays clear of the flower rather than sitting behind it.

     A chosen value, not a derived one: adjust this single number to taste. */
  left: 50%;
  bottom: 7cqh;
  transform: translateX(-50%);
  --meter-color: var(--earth-color);
}

.element-meter-water {
  /* Soil moisture, immediately above the ground meter. */
  left: 50%;
  bottom: 26cqh;
  transform: translateX(-50%);
  --meter-color: var(--water-color);
  /* Over-watering: the surplus goes a deeper blue rather than just dark. */
  --meter-over-color: #003a63;
}

.element-meter-fire {
  /* The sun, upper left. */
  left: 12%;
  top: 14cqh;
  --meter-color: var(--fire-color);
}

.element-meter-wind {
  /* Dead centre of the ring, in the hole the inner segments leave. */
  left: 50%;
  top: 46cqh;
  transform: translateX(-50%);
  --meter-color: var(--air-color);
}

/* ---- charge particles ----------------------------------------------------
   One per unit spent, from the pointer to the meter. Positioned in
   #flower-container's own coordinates (element-hold.js subtracts its rect), so
   they travel with the play area rather than the viewport. */
.element-charge-particle {
  position: absolute;
  width: 1.6cqh;
  height: 1.6cqh;
  margin: -0.8cqh 0 0 -0.8cqh;
  border-radius: 50%;
  opacity: 0.95;
  z-index: 13;
  pointer-events: none;
  transition: left 0.45s ease-in, top 0.45s ease-in, opacity 0.45s ease-in;
}

.element-charge-particle-earth {
  background: var(--earth-color);
}
.element-charge-particle-wind {
  background: var(--air-color);
}
.element-charge-particle-water {
  background: var(--water-color);
}
.element-charge-particle-fire {
  background: var(--fire-color);
}

/* While a commit is in flight the ring is inert: the player has already seen
   the effect play, and a second hold against a balance the server has not
   confirmed would compound the guess. */
#radial-menu.is-committing .radial-segment {
  pointer-events: none;
  opacity: var(--seg-opacity-muted);
}

/* ---- ability fanfare -----------------------------------------------------
   A meter filled and its ability fired. Everything the player was interacting
   with clears out so the effect has the screen to itself, and fades back in
   once it has landed (element-hold.js holds the class for the effect's
   duration plus a beat).

   Applied to the two containers rather than to their parts: fading the <svg>
   and each meter as a whole means the segments' own opacity states — resting,
   muted, mid-wake — are untouched underneath and come back exactly as they
   were, with no need to remember them.

   The fade out is quicker than the fade in. Getting out of the way should feel
   prompt; coming back should feel like arriving. */
#radial-menu,
.element-meter {
  transition: opacity 0.45s ease-in;
}

/* body.sun-rising rides along with the fanfare rules. flower.js sets it for
   exactly as long as sunRiseTime is counting down, so a sunrise that did NOT
   come from a hold — a reconcile, or a refresh that brings up a level bought
   elsewhere — clears the sky the same way. Where both apply they simply
   agree. */
body.element-fanfare #radial-menu,
body.element-fanfare .element-meter,
body.sun-rising #radial-menu,
body.sun-rising .element-meter {
  opacity: 0;
  transition: opacity 0.2s ease-out;
}

/* The segments, not just their container. #radial-menu is already
   pointer-events: none and each segment opts back in, so muting the parent
   does nothing — an invisible ring would stay clickable.
   .is-committing covers the start of the fanfare but is lifted the moment the
   response lands, which is well before the celebration ends. */
body.element-fanfare .radial-segment,
body.sun-rising .radial-segment {
  pointer-events: none;
}

/* ---- an operation opening ------------------------------------------------
   Creating the ground opens subtraction, multiplication and division at once,
   announced after the ability fanfare has cleared and the ring has faded back
   in (radial-menu.js does the timing and the stagger).

   Deliberately unlike the element wake above. That one is a soft swell — the
   segment fades up and settles. This is a strike: the wedge lands from
   slightly outside the ring, overshoots inward, and rings out with a hard
   white flash. Two different events should not read as the same animation. */
@keyframes radial-segment-unlock {
  0% {
    opacity: 0;
  }
  12% {
    opacity: 1;
  }
  100% {
    opacity: var(--seg-opacity-rest);
  }
}

@keyframes radial-segment-unlock-strike {
  0% {
    transform: scale(1.18);
    filter: brightness(4) saturate(0.2);
  }
  12% {
    transform: scale(0.97);
    filter: brightness(3.2);
  }
  34% {
    transform: scale(1.03);
    filter: brightness(1.6);
  }
  100% {
    transform: scale(1);
    filter: brightness(1);
  }
}

.radial-segment.is-unlocking {
  /* --unlock-delay is set per segment so the three arrive in sequence. */
  animation: radial-segment-unlock 0.85s ease-out var(--unlock-delay, 0ms) both;
}

.radial-segment.is-unlocking .radial-segment-shape,
.radial-segment.is-unlocking .radial-segment-icon {
  /* Same transform-box reasoning as the wake: scale about the middle of the
     ring, not each path's own box, or every wedge strikes in a different
     direction. */
  transform-box: view-box;
  transform-origin: 50% 50%;
  animation: radial-segment-unlock-strike 0.85s cubic-bezier(0.2, 0.9, 0.3, 1)
    var(--unlock-delay, 0ms) both;
}

/* Stowed: a summoned seed is on the ground waiting to be planted or discarded,
   and the ring stands down until it is dealt with. Driven from the inventory
   in radial-menu.js, so it survives a page refresh — the player who reloads
   mid-decision comes back to the same cleared stage.

   Separate from the fanfare above, which is a timed celebration. This one has
   no timer: it lasts exactly as long as the seed does. */
#radial-menu.is-stowed {
  opacity: 0;
}

/* The segments, for the same reason the fanfare needs it: #radial-menu is
   already pointer-events: none and each segment opts back in, so muting the
   parent alone leaves an invisible ring clickable. */
#radial-menu.is-stowed .radial-segment {
  pointer-events: none;
}

/* ---- flower view ---------------------------------------------------------
   A toggle in the lower right that clears every piece of chrome away so the
   scene can be looked at on its own, and puts it back.

   The button is a direct child of body rather than of the play area, so it
   survives everything it hides. It positions itself against .container's box
   via the same min() the aspect lock uses, which keeps it inside the
   letterboxed play area instead of drifting to the window corner on a wide
   screen. */
.corner-icon-button {
  /* How far up from the corner this button sits, in whole button-and-gap
     steps: 0 for the one in the corner itself, 9vh (7vh tall + a 2vh gap) for
     the next one stacked above it. One variable so the inset formula below is
     written once. */
  --corner-button-stack: 0vh;

  position: fixed;
  /* The play area is min(100vw, 75svh) wide and min(100svh, 133.333vw) tall,
     centred — so half the leftover on each side is its inset from the window
     edge. Adding a margin inside that puts the button in the play area's own
     corner rather than the window's. */
  bottom: calc(
    (100svh - min(100svh, 133.333vw)) / 2 + 2vh + var(--corner-button-stack)
  );

  width: 7vh;
  height: 7vh;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* These are <button>s and <a>s both; the link must not draw an underline. */
  text-decoration: none;

  border: 0.25vh solid rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #f7f3ec;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.2s ease-out;

  /* Above the header and footer (20) and the sidebar (19): it is the way out
     of flower view, so nothing may cover it. */
  z-index: 40;
}

.corner-icon-button-left {
  left: calc((100vw - min(100vw, 75svh)) / 2 + 2vh);
}

.corner-icon-button-right {
  right: calc((100vw - min(100vw, 75svh)) / 2 + 2vh);
}

/* One step up from the corner, for a second button in the same column. */
.corner-icon-button-stacked {
  --corner-button-stack: 9vh;
}

/* `display: flex` above beats the UA stylesheet's [hidden] { display: none },
   so the attribute needs saying again here — otherwise a button marked hidden
   in the markup (the garden, until the first flower blooms) draws anyway. */
.corner-icon-button[hidden] {
  display: none;
}

/* Present but not yet usable — the flower view before there is a flower to
   look at (user-data.js updateFlowerViewToggle). Faint enough to read as "not
   yet" rather than "broken", and out of hit-testing entirely so a tap on it
   does nothing rather than something invisible. */
.corner-icon-button.is-inert {
  opacity: 0.2;
  pointer-events: none;
}

.corner-icon-button.is-inert:hover {
  opacity: 0.2;
}

.corner-icon-button:hover {
  opacity: 1;
}

.corner-icon-button-icon,
.flower-view-toggle-icon {
  width: 60%;
  height: 60%;
  fill: currentColor;
}

/* Settings survives flower view. The sidebar it used to live in is hidden, so
   this is the only way into audio, display and the rest — and hiding the way
   out of a mode is how a player gets stuck. */
body.flower-view #settings-corner-button {
  opacity: 0.25;
}

/* One icon at a time: the flower to go and look at it, the ring to come back. */
.flower-view-toggle-radial,
body.flower-view .flower-view-toggle-flower {
  display: none;
}

body.flower-view .flower-view-toggle-radial {
  display: block;
}

/* Flower view itself. Everything the player might interact with goes, leaving
   the canvases and the toggle.

   visibility rather than display, for the reason the footer uses it: several
   of these are measured by animations (the end-game tally flies elements to
   the header icons, effects.js reads footer button rects) and a display:none
   element measures 0×0 at the page origin. */
body.flower-view #menu-header,
body.flower-view #menu-sidebar,
body.flower-view #radial-menu,
body.flower-view #garden-corner-button,
body.flower-view .element-meter {
  visibility: hidden;
}

/* Belt and braces on the ring, whose segments opt into pointer-events
   individually — visibility: hidden already stops hit-testing, but the two
   have to stay in step if this ever becomes an opacity fade. */
body.flower-view #radial-menu .radial-segment {
  pointer-events: none;
}

/* ---- inner-ring balance readouts -----------------------------------------
   How much of an element the player holds, shown while its segment is
   hovered. Bare text: no shape, no outline, no background. It shares the inner
   ring's space but not its segments — those stay hidden, and this is a sibling
   of them rather than a child, so nothing of theirs applies to it.

   Fades rather than appears, and is inert to the pointer so it cannot break
   the hover that summoned it. */
.radial-balance {
  /* The wedge's own colour. --seg-color is set on the parent .radial-wedge-*,
     so each number picks up its element without a rule per element — and a
     wedge whose segments have gone grey does NOT drag the number with it: the
     locked/disabled override redefines --seg-color inside the segment, which
     this is a sibling of, not a child. */
  fill: var(--seg-color);
  stroke: none;
  font-family: "Outfit", sans-serif;
  font-size: 11px;
  font-weight: 700;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease-out;
  /* The number changes with every unit spent during a hold; without this the
     digits jostle sideways as their widths change. */
  font-variant-numeric: tabular-nums;

  /* A shadow rather than an outline, on purpose. Coloured text needs help
     holding its edge against the scene — water's blue in particular sits close
     to the sky — but an outline would give these the very thing the middle of
     the ring is meant to be free of. This lifts them off the background
     without drawing a line around them. */
  filter: drop-shadow(0 0 1.5px rgba(0, 0, 0, 0.9));
}

.radial-balance.is-shown {
  opacity: 1;
}
