/* Calendar view - see planning/calendar-view-plan.md. Loaded by index.php AFTER pabc.css whenever the
   ?calendar=1 flag is on - in BOTH views, since the List/Calendar tabs appear in both - so nothing here
   may touch the list view: page-level rules are scoped to body.calendarView, everything else to
   #gameList.calendar, and the tabs to #viewTabs. Deliberate exceptions, which the two views share so switching
   between them doesn't change the page: #body's width, and the typefaces.

   The markup is the list view's: a <div id=gameList class="calendar"> whose <div class="day"> elements (one per
   date, Sunday first, every week complete - Game::printCalendar()) hold ordinary Game::print() games, after a
   <div class="weekdays"> header. Everything here reshapes that with CSS alone.

   Every rule is scoped "#gameList.calendar ..." so the id outranks pabc.css's list rules, and the whole file is
   screen-only so pabc.css's print rules are untouched.

   A game row is .game, and its cells are found by class, not position (plan section 13.1): .dateCell, .timeCell,
   .nameCell (description + fee), .actionCell (buttons + reservation-list). */

@media screen {

/* ---------- page furniture ---------- */

/* The calendar view shows only at 801px and wider (plan section 13.3, decision 32): every rule that lays it out
   is inside @media (min-width:801px), so on a narrower screen - a phone rotated to portrait - #gameList.calendar
   renders exactly as the list, and rotating back brings the calendar back. pabc.css's list rules are deliberately
   light (:where(#gameList)) so these, scoped "#gameList.calendar", always override them. */

/* Not used in the calendar view. It stays in the DOM, hidden rather than removed: pabc.js wires #showMore
   unconditionally at DOMContentLoaded, and a null there would abort the rest of that handler. (The filter menu,
   #filterRow, isn't output at all with the calendar flag - the list's header bar replaces it.) */
@media (min-width:801px) {
    body.calendarView #showMore { display:none; }
}

/* Typefaces (the Google Fonts <link>s in index.php): Montserrat for the page title, Muli for everything
   else. Like #body's width below, deliberately NOT scoped to the calendar view: with the flag on, the list
   view uses them too, so switching tabs keeps the fonts consistent - and it previews the planned switch of
   the list view to these fonts for everyone. Flag-off pages never load this file, so they keep sans-serif.
   The form controls and table cells are named explicitly because pabc.css sets their font-family
   directly (body, input, td, th, button, select, textarea), so they don't inherit it from body. Screen
   only, like the rest of this file - anything printed keeps pabc.css's sans-serif. */
body,
body :is(input, td, th, button, select, textarea) { font-family:Muli, sans-serif; }
#topHeader { font-family:Montserrat, sans-serif; font-weight:800; }

/* Seven columns need more room than the list's usual 1000px (pabc.css). Deliberately NOT scoped to the
   calendar view: with the flag on, the list view gets the same width too, so switching between the two tabs
   doesn't jump the page's width (and its header) around. Flag-off pages never load this file, so they keep
   the 1000px list. */
#body {
    max-width:1800px;
    padding:0 8px;
    box-sizing:border-box;
}
/* ...but the list's table keeps its usual 1000px, left-aligned under the logo, rather than stretching its
   columns across the wider page. The "Show more games" bar below it matches. */
#gameList:not(.calendar),
body:not(.calendarView) #showMore { max-width:1000px; }

/* The calendar scrolls inside its own box rather than the page scrolling: the page header (logo, title,
   tabs) stays where it is, and the sticky weekday header below sticks to the top of the box - there's
   nothing above it to slide under, and no content passing beside the fixed help/menu icons.
   calendar.js measures where the box starts (--cal-scroll-top) and this fills the rest of the viewport;
   dvh (the visible viewport, so a tablet's collapsing browser toolbar doesn't hide the bottom) where
   supported, vh otherwise. The page itself is then no taller than the window, so body doesn't scroll. */
@media (min-width:801px) {
body.calendarView { overflow:hidden; }
body.calendarView #calendarScroll {
    clear:both;                     /* overflow:auto makes it a BFC: without this it sits beside the floated logo, not below it */
    height:calc(100vh - var(--cal-scroll-top, 140px) - 6px);
    height:calc(100dvh - var(--cal-scroll-top, 140px) - 6px);
    overflow:auto;
}
/* An OVERLAY scrollbar (macOS's default with a trackpad) takes no space, so the calendar runs the full width
   underneath it - and when it widens and shows its track (while scrolling, or with the pointer over it) it
   covers the right-most ~15px: Saturday's right-aligned date numbers and fees. A classic scrollbar takes its
   own width and never covers anything, and scrollbar-gutter:stable only reserves space for classic ones, so
   calendar.js detects the overlay case (the box scrolls but its scrollbar has no width) and sets this class.
   Mouse/trackpad devices only: a touch device's thin scrollbar doesn't widen over the content. */
@media (hover:hover) and (pointer:fine) {
    body.calendarView #calendarScroll.overlayScrollbar { padding-right:16px; }
}
/* ...except on short screens - phones in landscape, about 360-430px tall - where the fixed page header took
   nearly half the height and left little for the calendar. There the whole page scrolls instead, as in the list
   view; the weekday header then sticks to the top of the window, where the fixed help/menu icons cover the
   end of it (accepted). 600px: below iPads (768+ tall) and ordinary laptop windows. */
@media (max-height:600px) {
    body.calendarView { overflow:visible; }
    body.calendarView #calendarScroll { height:auto; overflow:visible; }
}
}   /* end @media (min-width:801px) */

/* The List / Calendar tabs (index.php, only when ?calendar=1), on one line with the list view's filter:
   #viewBar wraps the two (only emitted with the flag) and puts the tabs at the left and the filter at the right
   edge of the list's 1000px table (the max-width below matches #gameList's). In the calendar view the filter is
   hidden and the bar spans the calendar. The tabs are hidden at 800px and narrower - the same breakpoint as the
   list's phone layout (pabc.css), and below most iPads' portrait widths (810-834px), so those still get them;
   the filter is then the only item, and sits at the left as it always has. (Not pabc.css's .nophone class,
   whose 1000px cutoff would hide them from iPads in portrait.) clear:left takes over from the filter row's own (inline) clear:left, which a flex item ignores:
   the bar starts below the floated logo. */
#viewBar {
    clear:left;
    display:flex;
    align-items:center;
    justify-content:space-between;
    column-gap:12px;
    /* if the two ever don't fit side by side, the filter drops to a line of its own rather than squeezing -
       which split its label from its menu when the label was the longer "Filter games:" (about 801-890px wide) -
       and margin-left:auto keeps it at the right, still aligned with the table's edge. With "Filter:" they fit
       on one line at every width the tabs show (801px up), so this is a safety net. */
    flex-wrap:wrap;
    row-gap:6px;
    max-width:1000px;
}
body.calendarView #viewBar { max-width:none; }
#viewTabs {
    font-size:0;                    /* no gap between the two inline-blocks */
}
@media (max-width:800px) {
    #viewTabs { display:none; }
}
#viewTabs a {
    display:inline-block;
    padding:3px 18px;
    font-size:20px;
    color:purple;
    text-decoration:none;
    background:white;
    border:1px solid purple;
}
#viewTabs a:first-child { border-radius:6px 0 0 6px; }
#viewTabs a:last-child  { border-radius:0 6px 6px 0; border-left-width:0; }
/* In the calendar view the tabs sit directly on the purple weekday row (see #gameList.calendar), so their
   bottom corners are square: rounded ones would leave notches against the straight edge. */
body.calendarView #viewTabs a:first-child { border-bottom-left-radius:0; }
body.calendarView #viewTabs a:last-child  { border-bottom-right-radius:0; }
/* ...and the same in the list view, whose header bar (#listHeader, below) they sit on in the same way */
body:has(#listHeader) #viewTabs a:first-child { border-bottom-left-radius:0; }
body:has(#listHeader) #viewTabs a:last-child  { border-bottom-right-radius:0; }

/* The list view's header bar (plan section 15, prototype): what the list shows - "All upcoming games", "Monday
   games", "John Smith's reservations" - with a ▾, and a click or tap anywhere on it opens the menu to change it.
   Styled like the calendar's weekday header, and sticky like it, to the top of the window as the list scrolls, so
   the current filter and the way to change it stay in reach. (The help/menu icons are fixed at the top right and
   sit over its right end once it's stuck there - as with the calendar's header on short screens.)
   The menu is a native <select> (index.php), stretched invisibly over the centered title and ▾ only - not the
   whole bar, so the menu opens from the title rather than the bar's left edge: a native menu on a desktop, the
   phone's own picker on a phone, keyboard handling for free. Its font-size is 16px so iOS doesn't zoom the page
   when it's focused. The title shows a white outline when the <select> has keyboard focus (tabbing to it), but not
   after the menu is opened by mouse or touch (.byPointer, set by pabc.js), which browsers would otherwise go on
   showing as keyboard focus once a choice is made. */
#listHeader {
    max-width:1000px;       /* the list's width, as #gameList's below - and its left border, so the two line up */
    border-left:thin solid lightgray;
    margin-top:10px;
    position:sticky;
    top:0;
    z-index:3;
    padding:0.25em 0;
    /* from the list's own text size (the page's viewport-based base font), not the calendar header's fixed 18px:
       the list's text runs from ~12px on a phone to ~29px on a desktop, and a fixed size looked thin above the one
       and oversized above the other */
    font-size:clamp(15px, 0.8em, 24px);
    font-weight:bold;
    text-align:center;
    color:white;
    background:purple;
}
#listHeaderTitle {
    position:relative;
    display:inline-block;
    padding:0 0.4em;
    border-radius:4px;
    cursor:pointer;
}
#listHeaderTitle:has(#listFilter:focus-visible:not(.byPointer)) { outline:2px solid white; outline-offset:1px; }
#listFilter {
    position:absolute;
    inset:0;
    width:100%;
    height:100%;
    opacity:0;
    font-size:16px;
    cursor:pointer;
}
/* The bar sits just above the list, outside it (#gameList is role=list, which may hold only list items): the list
   then starts right below it, with no gap or top border, and the tabs sit directly on the bar (where they show) */
body:has(#listHeader) #gameList { margin-top:0; border-top-width:0; }   /* (the calendar's are 0 too) */
@media (min-width:801px) {
    #listHeader { margin-top:0; }
}
/* each view's header: the bar in the list, the weekday row in the calendar (which shows it, below) */
@media (min-width:801px) {
    body.calendarView #listHeader { display:none; }
}
.weekdays { display:none; }
#viewTabs a.active {
    color:white;
    background:purple;
    cursor:default;
}
#viewTabs a:not(.active):hover { background:rgba(128, 0, 128, 0.12); }

/* "Tap a game to see options" (touch devices only - see .touchOnly in pabc.css), at the right of the tab bar.
   A fixed size: otherwise it inherits the page's viewport-based size, 28.8px on an iPad (bigger than the tabs)
   and 12px on a phone in landscape. */
/* calendar view only: on a list - or a calendar too narrow to show (see the top of this file) - nothing to tap */
body:not(.calendarView) #viewBar .tapHint { display:none; }
@media (max-width:800px) {
    #viewBar .tapHint { display:none; }
}
#viewBar .tapHint {
    font-size:15px;
    font-style:italic;
    /* next to the tabs, not at the far right: an auto right margin takes up the bar's free space, so anything
       after it (the planned "My reservations" checkbox, plan section 14) still goes to the right end */
    margin-right:auto;
}

/* "My reservations" (plan section 14): a checkbox at the right end of the bar. Output whenever the flag is on
   (index.php) and shown only in the calendar view - the list view has #dowFilter's own option. The checkbox is
   sized and scaled by pabc.css's global rule, like every other checkbox on the page.
   It shares #calendarTools with the icon key (below).
   A long name ("Thomas the Tank Engine's res…") is cut off rather than moving the checkbox to a line of its own:
   #viewBar wraps an item by its full width, so #calendarTools' basis is 0 - it always fits on the tabs' line - and
   it grows into whatever room is left. Inside it the checkbox's label takes its natural width first and the key
   gets the rest; only once the key is down to nothing does the label shrink, its text getting the ellipsis. The
   label is only as wide as its checkbox and text, so only those are clickable. Only the text clips: overflow:hidden
   on the whole label would also clip the checkbox, which pabc.css enlarges with a transform, beyond its layout box.
   (As a flex container the label drops the space character between checkbox and text, so the gap stands in for it.) */
#calendarTools {
    flex:1 1 0;
    min-width:0;
    display:flex;
    align-items:center;
    column-gap:16px;
}
body:not(.calendarView) #calendarTools { display:none; }
@media (max-width:800px) {
    #calendarTools { display:none; }    /* the calendar isn't showing there (see the top of this file) */
}
#myResToggle {
    flex:0 1 auto;
    min-width:0;
    display:flex;
    align-items:center;
    padding-right: 5px;
}
#myResToggle label {
    min-width:0;
    display:flex;
    align-items:center;
    gap:0.25em;
    font-size:17px;
    color:purple;
    white-space:nowrap;
    cursor:pointer;
    & input {margin-right:5px;}
}
#myResToggle input { flex:none; accent-color:purple; cursor:pointer; }   /* a checkbox's own cursor is the browser's default, not inherited */
#myResToggle span {
    min-width:0;
    overflow:hidden;
    text-overflow:ellipsis;
}

/* Key to the game icons (prototype, 2026-10-15) - touch screens have no tooltips. Fills the space between the tabs
   (and tap hint) and "My reservations", centered in it. Icons the size of the help dropdown's (pabc.css #help img);
   all four images are square, so an icon is --key-icon wide as well as tall.
   As room runs out it degrades in two stages, CSS only - an icon never shows without its description:
   1. every description shrinks, with an ellipsis, down to a floor of a couple of letters ("Re…") - 2em, low
      enough that all four entries still fit a phone in landscape with a game's icons showing (the key gets 296px
      on an 844x390 iPhone; four entries at the floor take 251px, where 3em needed 311px and dropped Partner);
   2. below that, whole entries drop from the end (List first).
   Stage 2 is "wrap and clip": a container one line tall with overflow:hidden, whose entries wrap onto a second
   line - out of sight - when they don't fit. Wrapping happens before shrinking, though (a wrapping flex line is
   filled by the items' starting sizes), so each entry starts at the smallest size it may show at - icon, gap and
   the floor - and grows toward its full width (max-width:max-content), its description's ellipsis in between. */
#iconKey {
    --key-icon:min(30px, 6vmin);
    flex:1 1 0;
    min-width:0;
    height:var(--key-icon);
    overflow:hidden;
    display:flex;
    flex-wrap:wrap;
    align-items:center;
    justify-content:center;
    column-gap:min(7px, 1.2vmin);   /* 7px on desktop; about 5px on a phone in landscape, like the icons following vmin */
    font-size:15px;
    color:rgb(85 85 85 / 0.8);
    white-space:nowrap;
}
#iconKey > span {
    --key-floor:2em;
    flex:1 1 calc(var(--key-icon) + 4px + var(--key-floor));
    min-width:calc(var(--key-icon) + 4px + var(--key-floor));
    max-width:max-content;
    display:flex;
    align-items:center;
    column-gap:4px;
}
#iconKey img {
    flex:none;
    width:var(--key-icon);
    height:var(--key-icon);
}
#iconKey span span {
    min-width:0;
    overflow:hidden;
    text-overflow:ellipsis;
}

/* The key shows only while some game's action icons are showing - it explains them, and is clutter otherwise - and
   on touch screens the tap hint ("Tap a game to see options") gives way to it at the same moment, so the two
   don't compete for the bar. The conditions are those that show the icon bar ([data-actions="reveal"] below):
   a game tapped or clicked (.actions-shown), its "View list" dropdown open (.open), or, with a mouse, a game
   hovered - but none of them while a popover is open (below). visibility rather than display, so the key keeps its space and
   "My reservations" doesn't jump sideways each time a game is hovered. */
#iconKey { visibility:hidden; }
/* Not while any popover is open (the reservation form, the seating chart, a dialog): by then the user is busy with it,
   and the key is only clutter - though the icon bar of the game it was opened from stays. */
body:not(:has(:popover-open)):has(#gameList.calendar .game:not(.noGame).actions-shown) #iconKey,
body:not(:has(:popover-open)):has(#gameList.calendar .actionCell.open) #iconKey {
    visibility:visible;
}
@media (hover:hover) and (pointer:fine) {
    body:not(:has(:popover-open)):has(#gameList.calendar[data-actions="reveal"] .game:not(.hoverStale, .noGame):hover) #iconKey {
        visibility:visible;
    }
}
/* (Separate selectors rather than one :is() list: "a game with something open" can't be :has(:popover-open) inside
   body:has() - a :has() inside another isn't allowed, and browsers silently drop it - so it's the open popover inside.) */
body:has(#gameList.calendar .game:not(.noGame).actions-shown) #viewBar .tapHint,
body:has(#gameList.calendar .game:not(.noGame) :popover-open) #viewBar .tapHint,
body:has(#gameList.calendar .actionCell.open) #viewBar .tapHint {
    display:none;
}
/* Below 1000px wide - phones in landscape, iPads in portrait - the full key and "My reservations" don't both fit (they
   need about 912px of window: the tabs take ~376px, the key ~364px, the checkbox and its gap 172px), and the key's
   labels would be cut short. So while the key shows, the checkbox makes way for it, coming back when the icons are
   put away. The same conditions as the key's, above. (A very long name can still cut the key short on a wider
   screen - "Constantine X. Sintoplertchaiyaku's reservations" - where its labels' ellipsis remains the fallback.)
   "Subscribe" (#calSubscribe, below) goes with it, so the two always come and go together. */
@media (max-width:999px) {
    body:not(:has(:popover-open)):has(#gameList.calendar .game:not(.noGame).actions-shown) :is(#myResToggle, #calSubscribe),
    body:not(:has(:popover-open)):has(#gameList.calendar .actionCell.open) :is(#myResToggle, #calSubscribe) {
        display:none;
    }
    @media (hover:hover) and (pointer:fine) {
        body:not(:has(:popover-open)):has(#gameList.calendar[data-actions="reveal"] .game:not(.hoverStale, .noGame):hover) :is(#myResToggle, #calSubscribe) {
            display:none;
        }
    }
}
/* "Subscribe" (planning/calendar-attachment.md section 10): the "My reservations" player's calendar feed, in either
   view while that filter is on - so outside #calendarTools, which the list view hides. At the right end of the bar
   (margin-left:auto, for the list view, where nothing else is there to fill the space), styled like #myResToggle.
   In the calendar it hides whenever #myResToggle does - below 1000px while the icon key shows (above). */
#calSubscribe {
    display:none;
    align-items:center;
    gap:0.3em;
    margin-left:auto;
    padding-right:5px;
    margin-right:5px;
    padding: 1px 3px;
    border:solid thin purple;
    border-radius: 4px;
    font-size:17px;
    color:purple;
    white-space:nowrap;
    text-decoration:none;
    & img { height:1.2em; }
}
body:has(#gameList[data-filter="my"]) #calSubscribe { display:flex; }

/* An icon before each tab's label: purple on the white (inactive) tab, white on the purple (active) one. A
   background rather than content:url(), because a background can be sized: the PNGs are ~239px tall, and a
   content image renders at its natural size. One text line tall, width set to keep each image's proportions
   (list 250x200, calendar 236x239). */
#viewTabs a::before {
    content:'';
    display:inline-block;
    height:1em;
    margin-right:0.35em;
    vertical-align:-0.15em;
    background:center / contain no-repeat;
}
#viewTabs a:first-child::before { width:1.25em; background-image:url(images/list.png); }
#viewTabs a:last-child::before  { width:0.99em; background-image:url(images/cal.png); }
#viewTabs a.active:first-child::before { background-image:url(images/list_white.png); }
#viewTabs a.active:last-child::before  { background-image:url(images/cal_white.png); }


/* ---------- the calendar grid ---------- */

@media (min-width:801px) {      /* through the end of the calendar's rules - see the top of this file */

#gameList.calendar {
    --cal-font: 15px;
    --cal-border: #d8d0d8;
    --cal-icon: 30px;               /* the largest an icon gets; see .actionCell below for how it shrinks */

    /* A grid container is a block formatting context, so unlike a plain table it would sit beside the
       floated logo rather than below it whenever the logo is taller than the header (wide windows)
       - and shift right by the logo's width. The list view never sees this because #filterRow
       (clear:left) sits between them; that row is hidden here. */
    clear:both;
    display:grid;
    /* minmax(0, 1fr), not plain 1fr: 1fr won't shrink below its content's min-width, so one
       long unbreakable string would widen its column and unbalance the week */
    grid-template-columns: repeat(7, minmax(0, 1fr));
    width:auto;
    /* No top margin and no top border: the header row sits directly under the List/Calendar tabs, so the
       active tab joins it (the tabs' bottom corners are squared to match - see #viewTabs). The weekday row
       is the calendar's top edge; cells supply the right and bottom edges, this the left. */
    margin-top:0;
    border:solid var(--cal-border);
    border-width:0 0 0 1px;
    -webkit-text-size-adjust:100%;  /* iPad: don't let iOS boost text per block */
    text-size-adjust:100%;
}

/* The weekday header: .weekdays generates no box, so its seven <div>s become ordinary grid items in the first
   grid row. Sticky so the weekday stays visible down a long calendar: to the top of #calendarScroll, its nearest
   scrolling ancestor. */
#gameList.calendar .weekdays { display:contents; }
#gameList.calendar .weekdays > div {
    position:sticky;
    top:0;
    z-index:3;
    font-size:calc(var(--cal-font) + 3px);    /* larger than the game text: these label the whole column */
    padding:4px 0;
    font-weight:bold;
    text-align:center;
    color:white;
    background:purple;
    border-right:1px solid rgba(255,255,255,0.35);
}

/* One day = one grid cell. Block, so the game rows inside simply stack. */
#gameList.calendar .day {
    position:relative;              /* for its alpha-list button, below */
    display:block;
    min-width:0;
    box-sizing:border-box;
    padding:0 3px 15px;
    border:solid var(--cal-border);
    border-width:0 1px 1px 0;
    /* deliberately NO min-height: an empty week then collapses to the height of its date-number
       line, which is what keeps a run of empty weeks compact */
}

/* the date number / "Oct 1", from the day's data-label */
#gameList.calendar .day::before {
    content:attr(data-label);
    display:block;
    margin-top:1px;
    text-align:right;
    font-size:calc(var(--cal-font) + 6px);
    font-weight:bold;
    line-height:1.4;
    color:#666;
    padding-right:2px;
}
#gameList.calendar .day.firstOfMonth::before { color:purple; }
#gameList.calendar .day.today::before {
    width:fit-content;
    margin-left:auto;
    padding:0 8px;
    border-radius:12px;
    color:white;
    background:purple;
}
#gameList.calendar .day.past { background:#f2f0f2; }
#gameList.calendar .day.past::before { color:#aaa; }


/* ---------- a game inside a day ---------- */

/* pabc.css's cell rules (padding, borders, vmin sizes, per-column tweaks) are all reset here. The
   row becomes a small grid:
        time            fee   alpha
        name ............................
        action buttons ..................
   .dateCell and .nameCell use display:contents so their children (fee, name, the alpha-list button) become
   items of the row grid directly. */
#gameList.calendar .game {
    display:grid;
    grid-template-columns:1fr auto auto;
    grid-template-areas:
        "time    fee     alpha"
        "name    name    name"
        "actions actions actions";
    column-gap:4px;
    position:relative;
    margin-bottom:3px;
    padding:2px 4px 3px;
    border:1px solid rgba(128, 0, 128, 0.18);
    border-radius:5px;
    /* Universal background (decision 15): the tint the list's odd rows used to have, on every game - alternate rows
       restarted inside each day, which looked patchy rather than like real zebra striping. (The list has since
       dropped its alternate rows too, decision 26.)
       Hue and strength are separate properties (decision 28, plan section 14.1): the states below each set only
       the one they change - a tournament game changes the hue, "something open" and "My reservations" the
       strength - so they combine without a rule for every pairing. */
    --row-rgb:128 0 128;
    --row-alpha:0.05;
    background:rgb(var(--row-rgb) / var(--row-alpha));
    transition:background-color 0.7s ease;

}
/* .noGame ("No game" or "Closed" - Game::isClosed() in objects.php) has nothing to act on. .hoverStale: see
   calendar.js - Safari can leave a game :hover after the reservation form closes, so every hover-based rule here
   ignores a row carrying it. 
   Every hover-based rule also waits while any popover is open (body:not(:has(:popover-open))) - the reservation form,
   the seating chart, a dialog: games behind it would otherwise light up and show their icons (and the key flicker) as
   the pointer crossed them. The game whose form is open keeps its icons through its own :has(:popover-open) rules. */
body:not(:has(:popover-open)) #gameList.calendar .game:not(.noGame):not(.hoverStale):hover { border-color:rgba(128, 0, 128, 0.5); }

/* Rows that stand out (decision 16/17). Tournament games (the "Tournament games" filter's own name for them;
   details()->sectional): blue rather than the universal purple, and a little stronger. Provisional color
   (decision 17). "My reservations" is a generated rule, see filterMyReservations() in pabc.js. */
#gameList.calendar .game[data-sectional] { --row-rgb:0 0 128; --row-alpha:0.1; }

/* "Something is open for this row right now" - reservation-list's own seating-chart popover, or
   #newReservation reparented into the game's .actionCell for Reserve/Partner?/editing a reservation
   (ReservationPopover.js's show()/editReservation()). :has(:popover-open) is a live, continuously
   reevaluated relational selector - no .active class or JS bookkeeping needed, and nothing to get
   stuck on a soft-dismiss (Escape/light-dismiss/clicking a different action), since there's no state
   to clean up in the first place: the rule is just always correct, by construction.
   Only safe because ReservationPopover.js's 'toggle' listener always moves #newReservation back out
   to document.body once it closes (however it closes) - otherwise a row this had been reparented
   into, then later replaced (edited/deleted, or a sibling's capacity-propagation side effect), would
   detach it from the document entirely and break Reserve/Partner? for every game until a reload; see
   that listener's own comment for the full story.
   Sets only the strength, so a tournament game stays blue while open. Same (1,2,1) specificity as the
   tournament rule above, and placed after it, so its alpha wins there. */
#gameList.calendar .game:has(:popover-open) { --row-alpha:0.2; }

#gameList.calendar :where(.game) > div {     /* :where(): no heavier than the "td" this was, so the per-cell rules below still win */
    display:block;
    padding:0;
    border:0;
    font-size:var(--cal-font);
    text-wrap:wrap;
}

/* .dateCell: the date - now the column header and the day's own label, so all of it is hidden except
   the sectional alpha-list button that lives in it */
#gameList.calendar .dateCell { display:contents; }
#gameList.calendar .dateCell > div { display:none; }
/* The alpha-list button: one per day, the day's own (Game::printCalendar()), at the left end of the date line -
   centered on it: the line is (--cal-font + 6px) * 1.4 tall (.day::before), the button 20px (pabc.css). Directors
   only (body.director, as .directorOnly would - this selector outweighs that rule), and only on days with a
   tournament game, so it comes and goes with them, without JS. The games' own buttons are the list's. */
#gameList.calendar .dateCell > .alphaListButton { display:none; }
body.director #gameList.calendar .day:has(> .game[data-sectional]) > .dayAlpha {
    display:block;
    position:absolute;
    top:calc(1px + ((var(--cal-font) + 6px) * 1.4 - 20px) / 2);
    left:3px;
    margin:0;
}

/* .timeCell: start time(s) */
#gameList.calendar .timeCell {
    grid-area:time;
    font-size:calc(var(--cal-font) - 1px);
    color:purple;
    /* wrap, not nowrap: a game with several start times is one line of "6:45pm/7:00pm" here, and in a
       narrow column the second time may need to drop to a line of its own rather than push the column
       wider. A single time has nowhere to break, so it never does. */
    text-wrap:wrap;
}
/* Several start times: each is a <span> (Game::tableRow(), ReservationList.updateGameTimes()), which
   pabc.css stacks one per line in the list view. A calendar column has no room for a stack, so they run
   together, separated by slashes ("6:45pm/7:00pm", the same form Game::time() uses). The slash goes on the
   end of every time but the last (the generated text is part of the span, so it stays with its time), and
   the <wbr> between the spans in the markup is where the line may break - no space, so nothing shows
   between the slash and the next time. */
#gameList.calendar .timeCell span { display:inline; }
#gameList.calendar .timeCell span:not(:last-child)::after { content:"/"; }
#gameList.calendar .timeCell wbr { display:inline; }   /* pabc.css hides it for the list's stacked blocks */

/* .nameCell: fee + name/special */
#gameList.calendar .nameCell { display:contents; }
#gameList.calendar .nameCell > :not(.tableFee):not(.gameName) { display:none; }   /* localhost-only sectional diagnostic */
#gameList.calendar .tableFee {
    grid-area:fee;
    position:static;
    padding:0;
    font-size:calc(var(--cal-font) - 1px);
    color:purple;
}
#gameList.calendar .gameName {
    grid-area:name;
    height:auto;
    /* no overflow:hidden anywhere on the game or day: the right-click "Edit game" menu is appended
       inside .gameName and would be clipped */
}
#gameList.calendar .gameName > div,
#gameList.calendar .gameName > div:not(.contextMenu) { padding-right:0; }   /* pabc.css reserves room for the fee here */
#gameList.calendar .gameName > div:first-child { font-weight:600; line-height:1.2; }
#gameList.calendar .special { display:block; font-size:calc(var(--cal-font) - 1px); line-height:1.2; }

/* .actionCell: the action buttons, and the reservation-list dropdown that is positioned against this cell */
#gameList.calendar .actionCell {
    grid-area:actions;
    position:relative;              /* containing block for the non-director reservation-list dropdown */
    /* Four equal tracks, each at most one full-size icon (plus the button's 2px padding either side)
       wide. Two properties fall out of that, both wanted:
       - never wraps: when the column is too narrow for four full-size icons the tracks shrink together
         (an icon is at most its track, so it scales down) instead of the fourth dropping to a new line;
       - same size everywhere: a track is a quarter of the row whether it holds one of four buttons or
         one of two. A game with a custom reservation URL only has Reserve and Director (objects.php
         omits Partner? and View list), and its icons come out exactly the size of a four-button game's. */
    display:grid;
    grid-template-columns:repeat(4, minmax(0, calc(var(--cal-icon) + 4px)));
    column-gap:1px;                 /* the minimum: what the gap is while the icons are still shrinking */
    /* Spacing that opens up only once the icons have stopped growing. The tracks reach their maximum
       (--cal-icon + 4px) first; any room beyond that is leftover space, which space-between shares
       out on top of the 1px minimum - and max-width is exactly four full-size tracks plus three 4px
       gaps, so the leftover can never push the gap past 4px. (Wider than that, the group simply
       stops growing and sits at the left.) The 4 tracks exist for two-button games too, so their
       icons stay in the same places at the same spacing. */
    justify-content:space-between;
    width:100%;
    max-width:calc(4 * (var(--cal-icon) + 4px) + 3 * 4px);
    margin-top:2px;
}

/* the four buttons: icon only. The icon/label markup is built by CustomButton.js, so this is pure
   CSS - the label (2nd div) is hidden and remains available as each button's title= tooltip. */
#gameList.calendar :is(reserve-button, partner-finder-button, view-reservations, contact-button) {
    width:auto;                     /* fills its grid track */
    min-width:0;
    max-width:none;
    margin:0;
    padding:2px;
    border-radius:6px;
    /* pabc.css also alternates by row - tr:nth-child(even) & gives the buttons their own extra
       tint (0.1) on top of whatever the row itself has - which restarts every day exactly like the
       row background did (decision 15), so it'd be just as patchy once the row alternation is gone.
       This selector already outranks that rule on specificity (an id wins outright, regardless of
       the rest), so restating the idle (odd-row) value here - transparent, letting the row's own
       flat tint (decision 15) show through instead - makes it uniform too. */
    background-color:rgba(128, 0, 128, 0);
}
/* The idle rule above has an id in it, so on its own it would ALSO outrank pabc.css's un-scoped
   &:hover rule (a bare reserve-button:hover, no id) - freezing the hover color at the idle value
   above instead of the darker one, since a browser resolves each PROPERTY by specificity across
   every matching rule, hover or not. Restating :hover here, at a still-higher specificity (one more
   pseudo-class), is what keeps the darkening pabc.css already gives on hover - same value (0.2) on
   every row, since that part was already row-independent and didn't need "fixing", only re-winning. */
#gameList.calendar :is(reserve-button, partner-finder-button, view-reservations, contact-button):hover {
    background-color:rgba(128, 0, 128, 0.2);
}
#gameList.calendar [data-sectional] :is(reserve-button, partner-finder-button, view-reservations, contact-button):hover {
    background-color:rgba(0,0,128,0.15);
}
#gameList.calendar :is(reserve-button, partner-finder-button, view-reservations, contact-button) > div:nth-child(2) { display:none; }
/* No `display` here, on purpose: each button holds two images and pabc.css hides whichever doesn't
   apply (.reserved / .not-reserved on Reserve, .director on View list) with display:none. Anything
   this selector sets - it has an id, so it wins - would show both. The buttons' own text-align:center
   centres a narrower-than-track icon. */
#gameList.calendar :is(reserve-button, partner-finder-button, view-reservations, contact-button) img {
    width:100%;                     /* the track's width, so it scales down with the track... */
    max-width:var(--cal-icon);      /* ...and never grows past the chosen size */
    height:auto;
    transform:none;
}

/* The reservation list inherits its text size through the DOM from .actionCell. The non-director dropdown
   sits in the calendar and should match the text around it, so it takes the calendar's text size.
   The director's seating chart is a centred top-layer popover (reservation-list[popover], set by
   showSeatingChart()) that wants to be big, so it gets back the size pabc.css's base rule would
   have given it. */
#gameList.calendar reservation-list:not([popover]) { font-size:var(--cal-font); }
#gameList.calendar reservation-list[popover] { font-size:var(--base-font); }

/* Same leak, same fix, for #newReservation now that show()/editReservation() (ReservationPopover.js)
   can reparent it into a game's .actionCell too: its own <input>/<button>/<select> elements are
   unaffected (pabc.css sets their font-size directly, and an element's own explicit rule always
   wins over an inherited one, regardless of the ancestor rule's specificity) - but every plain label
   ("Name:", "Partner:", ...) has no explicit size of its own and would otherwise inherit the small
   --cal-font from the td, next to inputs/selects/buttons still at their normal large size. */
#gameList.calendar #newReservation { font-size:var(--base-font); }

/* Third instance of the same leak, but it turns out NOT one shared fix: pabc.js's
   createContextMenu() is reused by every right-click menu in the app - ~8 call sites,
   EditClubPopover.js/CustomPopover.js/ReservationList.js/ReservationDiv.js/pabc.js - and a blanket
   `.contextMenu` rule (tried first, both as a flat restore and as a flat 140%) doesn't work because
   its callers land in two different starting contexts, not one:
   - "Edit game" (.gameName, .nameCell) and the non-director "Cancel reservation"/"Contact player" menu
     (ReservationDiv.js's contextMenu(), appended into the reservation-div - itself always inside
     reservation-list:not([popover]), the small non-director dropdown, since a director always gets
     the seating-chart popover instead, never this dropdown) both inherit the calendar's small
     --cal-font with no fix of their own - too small, and 140% of "too small" reads right.
   - The director's own reservation menu ("Get info"/"Edit reservation"/"Cancel reservation"), same
     append-into-the-reservation-div code, only ever appears inside reservation-list[popover] (a
     director right-clicking a reservation only happens within the seating chart) - already reading
     correctly, by inheriting that popover's own already-fixed large size, with no fix of its own
     needed. 140% on top of that overshot badly (measured: 28.8px -> 40.32px).
   So the two contexts that actually needed something are named explicitly instead of covered by one
   class selector - deliberately NOT matching reservation-list[popover] .contextMenu, which stays
   exactly as its ancestor already sets it. */
#gameList.calendar .gameName .contextMenu,
#gameList.calendar reservation-list:not([popover]) .contextMenu {
    font-size:140%;
}

/* Non-director dropdown: pabc.css anchors its right edge 15px in from the cell's right edge and it
   grows leftward, which would run off the left of the screen from the first columns. Sun-Wed open
   rightward from the game's left edge, Thu-Sat leftward from its right edge. */
#gameList.calendar reservation-list:not([popover]) { top:100%; }

/* Everything that floats over the calendar's cells must stay UNDER the sticky weekday header (th, z-index 3)
   as it scrolls up past it: the non-director dropdown (pabc.css gives it 5) and the right-click menus (10).
   Lowered here rather than the header raised: the fixed help panel and main menu (#menuHolder, 10) drop down
   over the top of the calendar, and a header at 11+ would paint over them. 2 still clears the neighbouring
   game rows and day cells, which are positioned or not but have no z-index of their own. */
#gameList.calendar reservation-list:not([popover]),
#gameList.calendar .contextMenu { z-index:2; }
#gameList.calendar .day:is([data-dow="0"], [data-dow="1"], [data-dow="2"], [data-dow="3"]) reservation-list:not([popover]) {
    left:0;
    right:auto;
}
#gameList.calendar .day:is([data-dow="4"], [data-dow="5"], [data-dow="6"]) reservation-list:not([popover]) {
    left:auto;
    right:0;
}

/* pabc.css's .game.noGame rule dims the cells and hides .actionCell's contents; an empty .actionCell would still
   leave its top margin behind */
#gameList.calendar .game.noGame .actionCell { display:none; }
/* (Its dimming reaches the calendar through the name cell's contents, not the cell itself - .nameCell is
   display:contents here, which has no box for opacity to apply to. See that rule in pabc.css.) */


/* ---------- the action icons: hidden until wanted (#gameList's data-actions="reveal", in index.php's markup) ----------
   Hidden until the game is hovered (mouse/trackpad) or tapped/clicked (any device); they then float under the game in
   a bar, so nothing below it moves. (A dev panel once offered other modes - always, tiered, hover-only, click - for
   comparison; "reveal" was chosen, decision 36.)
   Whatever hides the icon cell (.actionCell) must keep it shown while anything opened from it is open - and not just
   for the icons: the seating chart (reservation-list) lives in that cell, and #newReservation is moved into it
   while open (ReservationPopover.js show()/editReservation()). display:none on an ancestor hides even a
   top-layer popover, so tr:has(:popover-open) is part of every "show" rule below. */

/* reveal: the floating bar */
#gameList.calendar[data-actions="reveal"] .actionCell {
    grid-area:auto;                 /* out of the row grid: positioned against the row itself */
    display:none;
    position:absolute;
    left:-1px;
    right:-1px;
    top:100%;
    z-index:1;                      /* under the right-click menu (2) and the sticky weekday header (3) - see the dropdown's rule above */
    margin:0;
    padding:3px 4px;
    background:white;
    border:1px solid rgba(128, 0, 128, 0.5);
    border-radius:0 0 6px 6px;
    box-shadow:0 3px 6px rgba(0, 0, 0, 0.25);
}
/* Tapped/clicked (.actions-shown, calendar.js), something open, or the non-director list open (td.open). The bar
   is a descendant of the row and starts right at its bottom edge (top:100%), so moving the mouse onto it keeps
   the row :hover. */
#gameList.calendar[data-actions="reveal"] .game:is(.actions-shown, :has(:popover-open)) .actionCell,
#gameList.calendar[data-actions="reveal"] .actionCell.open {
    display:grid;
}
@media (hover:hover) and (pointer:fine) {
    /* .noGame excluded here rather than by the override below: :not() adds specificity, so the override would lose */
    body:not(:has(:popover-open)) #gameList.calendar[data-actions="reveal"] .game:not(.hoverStale, .noGame):hover .actionCell { display:grid; }
}
/* While the bar shows, the game's lower-left corner is square, so the two join: the bar hangs from the game's
   left edge, and it's only as wide as its icons (.actionCell's max-width), so the lower-right corner stays rounded.
   Same conditions as the rules showing the bar. */
#gameList.calendar[data-actions="reveal"] .game:is(.actions-shown, :has(:popover-open), :has(> .actionCell.open)) {
    border-bottom-left-radius:0;
}
@media (hover:hover) and (pointer:fine) {
    body:not(:has(:popover-open)) #gameList.calendar[data-actions="reveal"] .game:not(.hoverStale, .noGame):hover { border-bottom-left-radius:0; }
}
#gameList.calendar[data-actions="reveal"] .game.noGame { border-bottom-left-radius:5px; }

/* a .noGame ("No game"/"Closed") day has no actions: never show an empty bar for it */
#gameList.calendar[data-actions="reveal"] .game.noGame .actionCell { display:none; }
/* touch devices: iOS only delivers taps as clicks on elements that look clickable */
@media (hover:none) {
    #gameList.calendar[data-actions="reveal"] .game:not(.noGame) { cursor:pointer; }
}


/* Where the icons are hidden, the "you're reserved" check on the Reserve icon is hidden with them -
   so mark the game itself, with a purple outline just inside its border. (On trial, 2026-09-29; a strip down
   the left edge alone was too subtle.)
   - Not for directors (decision 29: they never see checkmarks - a director's browser reserves for other
     people, so nearly every game would be marked).
   - An inset box-shadow rather than a thicker border-left: drawn inside the existing box, so the game's
     contents don't move (a 3px border pushed them 2px right).
   - Not while "My reservations" is on: like the checkmark (pabc.css), it gives way to the chosen player's own
     marker, an outline of the same kind (filterMyReservations() in pabc.js) - both mean "a reservation here". */
body:not(.director) #gameList.calendar:not([data-filter="my"])[data-actions="reveal"] .game:has(reserve-button.reserved) {
    box-shadow:inset 0 0 0 2px purple;
}


}   /* end @media (min-width:801px), the calendar's rules */

}   /* end @media screen */
