:root {
    --ball-size: 50px;
    --tube-border-size: 3px;
    --tube-padding-size: 3px;
    --gap-size: 6px;
}
/** ensure buttons have a reasonable minimum size so they are easy to click on */
.ActionButton {
    min-height: 30pt;
    min-width: 40pt;
    margin: 5px;
}

.AuxStuff {
    /* leave a gap between buttons and link to instructions */
    margin-top: 1em;
    display: flex;
    /* get the info box to wrap, mainly so long messages go on next line if they can't fit on same line as buttons. */
    flex-flow: row wrap;
}
body:has(#FeedbackSimple:checked) .UndoUntilLiveButton {
    display: none;
}
/* info box for non blocking alerts will be absolute screen positioned */
.InfoBox {
    position: fixed;
    z-index: 10;
    bottom: 2em;/* just in case there is a phone UI blocking the message */
    left: 0px;
    max-width: 100vw;
    padding: 0.5em;
    background: light-dark(burlywood, darkblue);
}
.InfoBox:empty {
    display: none;
}
#settingsForm input[type="number" i] {
    max-width: 4.5ch;
    text-align: end;
    padding-inline-end: 1ch;
}
/* menu used for settings newlines should not display like a ul */
menu {
    list-style: none;
    padding-inline-start:unset;
}
menu.newGameSettings > li {
    margin-bottom: 0.5em;
}
/* disable labels for hidden inputs */
li:has(input[type=hidden]) {
    display: none;
}
#levelCode {
    max-width: 100%;
    width: 60ch;
}
/** TODO: probably do better labeling of solvable or impossible than just shoving some text in the grid */
.GameBoard.solvable::after  { content: "solvable";}
.GameBoard.solvable.ended.dead::after  { content: "solvable (from start)";}
.GameBoard.impossible::after {content: "impossible";}

.GameBoard {
    display: grid;
    /* this should get immidiately overridden by the javascript resize listener, if it fails to get the proper number this is the effective default */
    --n-rows: 10;
    --n-columns: auto-fill;
    /* the main sizing constraints are here */
    /* the column size maximum is the main width of the tubes including margin, the target is the minimum size for smaller screens and 
    and the min is based on the width of the label + margins and border and padding etc to overrule the minimum screen portion if it would cutoff the labels */
    grid-template-columns: repeat(var(--n-columns), clamp(2ch, 18vw, calc(var(--ball-size) + 2 * (var(--tube-border-size) + var(--tube-padding-size)))));
    /* the rows roughly specify the height of the balls with some offset due to margins and borders of the tubes, the minimum is just a bit more than the height of the label to try to account for marigns
    the default is some fraction of the screen divided by the calculated number of rows to keep the whole grid on the screen, and the maximum is the size that should be taken up if there is plenty of room on the screen
    and the game doesn't need the full height (typical on desktop) */
    grid-auto-rows: clamp(1.5em, calc(90vh / var(--n-rows)), var(--ball-size));
    grid-auto-flow: dense;
    /* put good spaces around the tubes  */
    justify-content: space-evenly;
    /* take up full width of body (or other container) */
    width: 100%;
    column-gap: var(--gap-size);
}

.Tube {
    /* transparency of border, set to 1 for focussed tube */
    --border-transp: 0.7;
    /* tube spans over rows based on capacity */
    grid-row-end: span var(--capacity);
    /* display balls from bottom to top */
    display: flex;
    flex-direction: column-reverse;

    padding: var(--tube-padding-size);
    border-width: var(--tube-border-size);
    border-style: solid;
    --border: light-dark(rgba(0,0,0, var(--border-transp)), rgba(255,255,255, var(--border-transp)));
    border-color: var(--border);
    outline-color: var(--border);
    outline-style: solid;
    outline-width: 0px;
    /* will take up available space in width and height */
    width: auto;
    height: auto;
    /* since tubes are all different heights we use margin-top for the vertical gaps instead of within grid, applying grid gaps would effectively put gaps between balls although because the balls are within tubes it just messes up spacing */
    margin-top: var(--gap-size);
}

/* the following is logic for tube border highlighting */


/* clicking makes border extend inward, I'd like to also do hover for desktop but mobile annoyingly
  treats "you just clicked on that thing" as hovering over it */
.Tube:active {
    padding: 0px;
    border-width: calc(var(--tube-border-size) + var(--tube-padding-size));
}
/* keyboard focus applies outline to match border, effectively extending border outward */
.Tube:focus-visible {
    outline-width: var(--tube-padding-size);
}
/* make the border more vibrant when selected a legal move with either method */
.Tube:not(.unmovable):active, .Tube:not(.unmovable):focus-visible {
    --border-transp: 1;
}
/* if selected with either method on an unmovable tube make the border dashed */
.Tube.unmovable:active, .Tube.unmovable:focus-visible {
    border-style: dashed;
}
/* empty tubes should be "greyed out" to make them easier to differentiate */
.Tube.empty {
    background-color: rgba(128,128,128, 0.2);
    --border: rgba(128,128,128, var(--border-transp));
}
/* as long as we aren't on 'simple' borders also fade non legal moves */
body:has(#FeedbackSimple:not(:checked)) .Tube.unmovable {
    --border: rgba(128,128,128, var(--border-transp));
}

/* mark with red border when:
 - we are in dead end state
 - non-simple mode and move goes to visited dead state
 - verbose mode and move goes to calculated dead state (could be unvisited)*/
.GameBoard.ended.dead .Tube,
body:has(#FeedbackSimple:not(:checked)) .Tube.visited.dead,
body:has(#FeedbackVerbose:checked) .Tube.dead:not(.unmovable) {
    /* does not affect --border or outline logic, leave that alone */
    border-color: rgba(255,0,0,var(--border-transp));
}
/* mark with teal border when:
 - we are in won end state
 - non-simple mode and move goes to visited winning state
 - verbose mode and move goes to viable winning state */
.GameBoard.ended.winning .Tube,
body:has(#FeedbackSimple:not(:checked)) .Tube.visited.winning,
body:has(#FeedbackVerbose:checked) .Tube.winning {
    /* does not affect --border or outline logic, leave that alone */
    border-color: rgba(0,240,150,var(--border-transp));
}
/* verbose mode settings, change border style for different types of moves */
body:has(#FeedbackVerbose:checked) .Tube.drain {
    border-style: dotted;
}
body:has(#FeedbackVerbose:checked) .Tube.pure {
    border-style: double;
}
body:has(#FeedbackVerbose:checked) .Tube.normal {
    --border-transp: 0.85;
}

@keyframes ballDrop {
    from {
        opacity: 0.1;
        transform: translateY(-100%);
    }
    to {
        opacity: inherit;
        transform: translateY(0);
    }
}
.Tube > .Ball {
    display: flex;
    /* center span containing label as much as possible */
    justify-content: center; 
    align-items: center; 
    /* take up the full width of the tube and the fraction of the tube based on how many fit into this tube */
    width: 100%; 
    height: calc(100%/var(--capacity));
    /* do a simple animation upon creation to draw attention to where they go. */
    animation: ballDrop 0.5s ease-out forwards;
    /* in the event the padding of the label would overlap with other balls hide it */
    overflow: hidden;
}
/* set a slightly transparent background over the label to improve contrast */
.Ball > span {
    background-color: light-dark(rgba(255,255,255,0.5), rgba(0,0,0,0.5));
    padding: 0.25em;
}
/* shroud logic, if there is a body with a checkbox with id #disableFog that is not checked then balls with shroud get this*/
body:has(#disableFog:not(:checked)) .Ball.shroud:before {
    content: "?";
    background-color: light-dark(lightgrey, #555);
    display:flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
}
body:has(#disableFog:not(:checked)) .Ball.shroud > span {
    /* hide the span labeling the ball when it is shrouded, the :before
   would cover it visually but copy/paste or accessibility stuff could
   still interact with it */
    display: none;
}

.Ball.A { background-color: red; }
.Ball.B { background-color: blue; }
.Ball.C { background-color: lime; }
.Ball.D { background-color: magenta; }
.Ball.E { background-color: orange; }
.Ball.F { background-color: yellow; }
.Ball.G { background-color: lightseagreen; }
.Ball.H { background-color: darkgreen; }
.Ball.I { background-color: indigo; }
.Ball.J { background-color: pink; }
.Ball.K { background-color: mediumpurple; }
.Ball.L { background-color: maroon; }
.Ball.M { background-color: saddlebrown; }
.Ball.N { background-color: slategrey; }
.Ball.O {background-color: tan;}
.Ball.P {background-color: aqua;}
.Ball.Q {background: linear-gradient(to bottom, lime, blue);}
.Ball.R {background: linear-gradient(to bottom right, aqua, magenta);}
.Ball.S {background: linear-gradient(to right, blue, red);}
.Ball.T {background: linear-gradient(to bottom left, magenta, yellow);}
.Ball.U {background: linear-gradient(to right, red, lime);}
.Ball.V {background: linear-gradient(to bottom left, yellow, aqua);}
.Ball.W {background: linear-gradient(to bottom right, orange, purple);}
.Ball.X {background: linear-gradient(to bottom, pink, indigo);}
.Ball.Y {background: linear-gradient(to right ,magenta, red);}
.Ball.Z {background: linear-gradient(to bottom left, green, purple);}

