/*
    responsive.css - the only file in this repository that may hold a media query.

    Screen-size rules spread over several stylesheets are a trap : changing one breakpoint
    means finding every copy of it, and the copy that is missed keeps contradicting the
    others at a width nobody thinks to test. So every width-conditional rule the site has
    lives here, and this file is linked last on every page that carries the shared topbar,
    so that it wins on source order alone rather than by shouting.

    THE THREE WIDTHS

    1024px - navigation.
        Below it the topbar collapses to a hamburger drawer and the search bar becomes a
        text field plus a filter button. Chosen because the tabs plus the theme toggle
        stopped fitting on one row somewhere in the 900 to 1050 band when there were five
        of them - there are six now, so they stop fitting sooner - and 1024 is the
        conventional tablet-landscape edge.

    820px - card.
        A pre-existing component breakpoint, moved here unchanged from card.css. It widens
        the card from half width to full width and has nothing to do with the other two :
        a card at half of a 700px page is unreadably narrow well before the navigation
        needs to change.

    640px - gallery.
        Below it the grid drops to one column and the gem image spans the column. Above it
        the grid is exactly today's, so a 768px portrait tablet keeps the 300px grid.

    WHY THE RANGE SYNTAX

    The conditions are written (width <= N) rather than (max-width: N). The two forms mean
    exactly the same thing - both are inclusive, so a viewport of exactly N matches either
    of them - and the range form keeps each number searchable as a single literal instead
    of hiding it behind a property name.

    The honest consequence : a browser older than Safari 16.4 or Chrome 104 does not
    understand the range form and drops the whole block, so a visitor on one of those gets
    the desktop layout. That is a degradation to a page that still works, not a broken one.

    NO MIN-WIDTH COUNTERPART, EVER

    Nothing in this file may be written as a min-width condition. A rule that can only
    match below a width cannot change what a 1280px desktop renders, and that property is
    the whole acceptance argument for this work : the desktop is not allowed to move.

    Z-INDEX SCALE - every layer the site stacks, with its number

        (auto)  document flow
        900     the bar carrying a message from one page to the next, above the topbar
        1000    sticky search bar and the loading popup - today's values, unchanged
        1100    panel backdrop (the layer that dims the page behind the drawer, and behind the
                comment panel - the filter sheet has none, being drawn over the whole screen)
        1200    panel itself
        1300    the account menu hanging under the topbar's identity control

    The first and the last of those are declared in the topbar's own stylesheet rather than here,
    because both are drawn at every width and nothing about either is conditional on one. They are
    written down in this list all the same : the list is only worth having if it is the whole of
    what this site stacks.

    The message bar sits below everything else, and being below the panel is the half that matters.
    A panel is drawn over the whole page, so a bar painted above one would sit over that panel's own
    close cross and leave a menu nobody can shut - the same failure the backdrop's own rule records.
    Below the rest for a plainer reason : that bar covers nothing at all, since it is part of the
    flow and pushes whatever follows it down rather than being drawn on top of anything.

    1000 is kept rather than renumbered to something rounder. Renumbering would be a
    rendering change, and this file was introduced on the promise that it is not one.

    The width blocks below are in descending width order. All three conditions are "at most", so
    a 360px viewport matches all three ; reading them narrowest-last means the later block
    wins in exactly the order a reader expects.

    One block at the very end of the file is not about a width at all : it answers a visitor who
    has asked their device for less movement. It sits after the width blocks deliberately and its
    own comment says why that position is load-bearing.
*/

:root {
  /* JavaScript asks the responsive authority for this condition instead of owning a second
     breakpoint literal that could drift away from the phone layout. */
  --public-profile-bench-wide-condition: (min-width: 641px);
  --public-profile-side-by-side-condition: (min-width: 821px);
}

@media (width <= 1024px) {
  /* Desktop keeps the source sentence and click hint on one line with the count underneath. At
     this width the introduction returns to its earlier flow: source text first, then a wrapping
     row containing the click hint and count. */
  .gallery-intro {
    display: block;
  }

  .gallery-intro-status {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: center;
    column-gap: 10px;
  }

  /* navigation : topbar and drawer */

  /* The two numbers the whole navigation is built on, written down once and read from here by
     everything else. The height of the closed row, the width the menu panel slides in at, and
     anything that later has to leave room for either of them all have to agree on the same
     figure ; two numbers that must agree are one number, and a second copy of either is a
     rendering fault waiting for the day somebody edits only one of them. */
  #nav-drawer {
    --drawer-width: min(80%, 300px);
    --topbar-height: 50px;

    /* Three columns whose outer two are equal, so the site's name sits optically in the middle
       of the window whatever the control to its left ends up costing : both 1fr columns take the
       same share of the leftover space, and the right-hand one is meant to stay empty.

       The alternative was taking the control out of the flow with position: absolute and centring
       what remains. It is refused, and the theme toggle is the demonstration : it is positioned
       that way today, and it is therefore drawn on top of this row instead of beside anything. A
       box that has left the flow reserves no space, so nothing can be centred against it.

       min-height and not height : a control sized to be comfortable under a finger must be free
       to push this row taller than the figure above rather than spill out of it. */
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    min-height: var(--topbar-height);

    /* The three declarations that let the menu slide in from the left instead of appearing. They
       only work together, and none of them means what it looks like on its own :

       - taking the row out of positioning is the load-bearing one. The shared stylesheet makes
         this row a positioned box, so without this line it would honour the offset below and
         paint itself - background, menu button and site name - a menu's width to the left of
         where it belongs, on every page that carries the row, at every width under the one this
         block answers. Out of positioning, the offset beside it has no effect whatever while the
         row is part of the flow, and it is safe to take out because the one box under this width
         that needed the row to be its anchor is the theme toggle, which is put back into the
         flow further down.
       - the offset is where the menu comes from, and it has to be a length. Moving between a
         length and "no offset at all" is not a movement a browser can be halfway through, so it
         jumps - and it jumps while a duration sits beside it looking perfectly correct in any
         tool that lists the declarations.
       - the duration is the movement itself.

       Only the opening is animated, and that is deliberate rather than missing. Whether a box is
       part of the flow is another thing a browser cannot be halfway through, so the moment the
       open state is dropped this is the row again in the same frame and no closing slide is ever
       seen. Holding the panel over the page for half of its exit and then snapping it back is
       worse than not animating the exit at all. */
    position: static;
    left: calc(-1 * var(--drawer-width));
    transition: left 200ms ease;
  }

  /* Both are placed by hand instead of being left to fall into the next free cell. When the menu
     opens, the hamburger is swapped for a close cross and automatic placement would slide the
     site name into the first column on that one state - the name would jump sideways as the menu
     opened. Naming the cell each one lives in makes the two states identical.

     justify-self keeps the hamburger the width of its own glyph : a block-level grid item with an
     automatic width is stretched to fill its column by default, which would spread the button
     across a third of the row and draw its glyph in the middle of that instead of at the edge. */
  .nav-toggle {
    display: block;
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
  }

  .site-name {
    display: block;
    grid-column: 2;
    grid-row: 1;
    text-align: center;
    font-weight: 600;
  }

  /* The way in and out of an account takes the third column, which is the one the row above was
     built with and left empty for it. Named rather than left to fall there on its own for the same
     reason the two controls above are named : it is the only child of this row that is drawn in
     both states, so the cell it lives in has to be the same cell whether the menu is shut or open.

     The first two declarations are a deliberate pair and neither means anything without the other.
     The wide row pins this control to its right edge, out of the flow ; a box out of the flow
     reserves no space, so left pinned here it would occupy no cell at all and be painted over the
     site's name. That is the theme toggle's own lesson, written a few rules further down, and it
     cost a menu that could not be opened. It is put back into the flow relatively and not
     statically, so that it goes on being the box the account menu is placed against : the menu
     hangs under this control at every width, and under a statically positioned one it would be
     placed against the whole page instead and land at the top left corner of the window. The
     offset beside it is what makes "relative" mean "exactly where the flow put it".

     THE WIDTH IT MAY NOT EXCEED IS THE LOAD-BEARING PART OF THIS RULE. Each outer column of this
     row is one share of what is left over, and a share is a *minimum* of what the column holds -
     so the two outer columns are equal only while their contents fit inside their share. An
     account name may be twenty-four characters long, and one of that length would widen this
     column past its share, take the width from the column on the other side, and carry the site's
     name off the centre the whole row is built around. Cutting the name short instead costs
     nothing anybody has to look for : the menu it opens carries what a visitor came for.

     The width is a share of the window and not a percentage, which looks like the same thing and
     is not. A percentage on a grid item is measured against the cell it sits in, and the size of
     that cell is what this declaration exists to hold down - during the sizing of the columns such
     a percentage counts as no limit at all, so the column would grow exactly as if nothing had
     been written here.

     WHAT MAY NEVER BE ADDED HERE IS A CLIPPED OVERFLOW, and it was here until a phone was read.
     Cutting the name short is done on the name itself, in the topbar's own stylesheet, one box
     further in ; done on this box it also cuts away the menu that hangs out of it, and an account
     menu that opens into nothing looks exactly like a button that does not work. */
  #identity-control {
    position: relative;
    right: auto;
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    max-width: 30vw;
  }

  /* The account button is inset less in the closed row than anywhere else on this site, and the
     figure comes from a measurement rather than from taste. The whole control may be 108 pixels
     wide at the narrowest width this site is laid out for ; carrying the entries' own inset of 24
     on each side, 48 of those 108 are blank and about five characters of a name are left, which is
     not enough of a name to recognise. At 10 the same control shows about ten. The height is
     untouched, so the box a finger lands on is the 108 by 49 it was.

     Only while the menu is shut. Opened, this control becomes one of the entries and lines up with
     them, and their inset is the one thing that alignment is made of. */
  #nav-drawer:not(.is-open) .identity-menu-button {
    padding-left: 10px;
    padding-right: 10px;
  }

  /* The six navigation entries are drawn by the opened menu and by nothing else. Written as the
     closed row hiding them, rather than as this width hiding them and the open state putting them
     back : the open state then needs no counter-rule at all, and there is no pair of selectors
     whose relative strength has to go on being right. */
  #nav-drawer:not(.is-open) .tab {
    display: none;
  }

  /* The theme toggle leaves the closed row with them, and not for tidiness. It is positioned
     absolutely against the row's left edge, so its box lies over the menu button's : asked at
     the exact point a tap on that button lands, the page hands the tap to the toggle, and the
     menu cannot be opened at all while both are drawn. It comes back with the entries when the
     menu is opened, which is where it belongs anyway. */
  #nav-drawer:not(.is-open) #mode-switch {
    display: none;
  }

  /* The opened menu : the same element as the row above, pinned to the left edge of the window
     and as tall as it. Being the same element is the point rather than an economy - the site's
     name and the control beside it stay in the cells the closed row put them in, so the top line
     does not rearrange itself as the menu opens.

     Nothing here moves this element with a transform, a scale, a rotation, a filter, containment
     or a hint naming any of those. Every one of them makes this element the anchor of every
     box inside it that was declared against the window, so such a box would silently start
     following the menu instead of staying where the window put it. Driven on this page at a
     phone width : a window-anchored box planted inside the menu read a left edge of 0, and with
     a translation on the menu it read the translation instead. Animating the offset has no such
     effect, which is why the offset is what is animated.

     A list too long for the screen scrolls inside the menu, and the page behind it is not
     allowed to take over once that list reaches its end. The containment is asked for in its
     physical spelling on purpose : the spelling that follows the writing direction is not
     implemented in the browser this line exists for. */
  #nav-drawer.is-open {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: var(--drawer-width);
    align-content: start;
    grid-auto-flow: row;
    overflow-y: auto;
    overscroll-behavior-y: contain;
  }

  /* The close cross takes the menu button's own cell instead of sitting in the far corner. The
     site's name is a child of this element and travels with it, so putting the cross exactly
     where the button was is what keeps the top line from visibly jumping as the menu opens - and
     it leaves the cross under the thumb that just opened it. Held to the start of its column for
     the same reason the menu button is : a grid item with an automatic width is stretched to
     fill its column, which would draw the glyph a third of the way across the line rather than
     at its edge. */
  #nav-drawer.is-open .nav-toggle {
    display: none;
  }

  #nav-drawer.is-open .nav-close {
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
  }

  /* First of everything in the menu, above the entries and shaped like them rather than like the
     small control it is in the closed row.
     It reads first because wanting to sign in is a likely reason to have opened the menu at all.
     Two declarations put it there and the pair was arrived at by measuring rather than by reading :
     the number alone leaves it in the very first line of the menu, drawn across the close cross and
     the site's name, which share that line and are placed there by hand. The line is named
     explicitly for that reason - the second line of the menu, under the cross and above the six
     entries, which then fill the lines after it. The number stays beside it because it is what
     keeps this control ahead of the entries and far ahead of the theme toggle, which is carried to
     the very end with a positive one : anything left unnumbered counts as zero, and the order this
     element is written in is the order the closed row needs rather than the order the menu wants.

     A rule of its own rather than an amendment to the one above is not a style : every other child
     of this element has an explicit rule for the opened state, and the one left without it keeps
     what the closed row gave it - a third of the first line of a panel three hundred pixels wide,
     jammed against the site's name, while everything around it has become a full-width row.

     The width limit is released here because the panel is not the row : the line is the whole width
     of the panel, there is no second column beside it whose size depends on this one, and a panel
     three hundred pixels wide holds a name at the longest length this site allows without cutting
     anything. The name goes on being told to cut rather than wrap, which is what makes this control
     behave the same way in both places instead of two ways nobody can describe in one sentence -
     and a wrapped name would make this row two lines tall while every entry under it is one.

     A hairline under it, of the kind the theme toggle carries at the other end of this menu, is
     deliberately not drawn : the line the toggle needs is what says it is not one of the entries,
     and this control is meant to read as one of them. Space alone is what sets it apart, and space
     needs no second rule for the other theme. */
  #nav-drawer.is-open #identity-control {
    grid-column: 1 / -1;
    grid-row: 2;
    order: -1;
    justify-self: stretch;
    max-width: none;
    margin-bottom: 8px;
  }

  /* One entry per line, each spanning the whole menu, so the list reads as a list rather than as
     a stack of buttons. The padding the links already carry is deliberately left alone : it is
     what makes the whole strip a target instead of just the words on it. */
  #nav-drawer.is-open .tab {
    grid-column: 1 / -1;
  }

  #nav-drawer.is-open .tab a {
    text-align: left;
  }

  /* The page currently being looked at, marked twice on purpose.

     The light theme declares its own version of this mark with a selector carrying an element
     name and two classes, and that outweighs a pair of classes however late the file holding it
     is loaded. So a single rule written with two classes would be obeyed in the dark theme and
     quietly ignored in the light one : the accent would stay under the entry, drawn in the other
     theme's colour, and neither a picture of one theme nor a sweep of screen widths would show
     it. Each theme therefore gets its own rule, each shaped to outweigh the theme rule it
     answers.

     The accent moves from the bottom edge to the left edge because in a vertical list a line
     under an entry reads as a divider between two of them rather than as a mark on one. */
  #nav-drawer.is-open .tab.active {
    background-color: #3a3a3a;
    border-bottom: none;
    border-left: 3px solid #4db6ff;
  }

  body.light-mode #nav-drawer.is-open .tab.active {
    background-color: #dcdcdc;
    border-bottom: none;
    border-left: 3px solid #007acc;
  }

  /* The theme toggle, put back into the flow. Everywhere else it is pinned to the left edge of
     the row it lives in, which is what makes it overlap whatever else is drawn at that corner :
     a box out of the flow reserves no space, so nothing in this menu could be laid out around
     it and it would be painted straight over the first entry. The offset is written out rather
     than left to be ignored, so that the two declarations read as one deliberate pair. */
  #mode-switch {
    position: static;
    left: auto;
  }

  /* Last of everything in the menu, under a hairline that separates it from the entries, and
     shaped like the entries above it rather than like the small bordered button it is on a wide
     screen. It is the FIRST child of this element in the document - the script that binds it
     finds it by id, and this row is the menu itself - so the ordering is what carries it to the
     end. The bottom margin keeps it clear of the very foot of the screen, where a phone draws
     its own gesture bar over whatever is there. */
  #nav-drawer.is-open #mode-switch {
    grid-column: 1 / -1;
    order: 1;
    justify-self: stretch;
    text-align: left;
    border: none;
    border-top: 1px solid #444;
    border-radius: 0;
    margin: 8px 0 24px;
    padding: 14px 24px;
  }

  body.light-mode #nav-drawer.is-open #mode-switch {
    border-top-color: #ccc;
  }

  /* The dimmed page fades in over the same time as the menu slides, so the two read as one
     movement rather than as one thing arriving on top of another.

     The dim layer is drawn from not being drawn at all, and a box that was not being drawn has
     no earlier value for a fade to start from - which is what the starting value below supplies.
     Without it the layer is simply there on the first frame, with the fade sitting in the file
     looking correct.

     Nothing is declared to hold the layer on screen while it leaves : it stops being drawn the
     instant the open state is dropped, which is exactly what the menu beside it does, so the two
     go away together. A later change that made only one of them linger would pull them apart. */
  .nav-backdrop {
    opacity: 0;
    transition: opacity 200ms ease;
  }

  .nav-backdrop.is-open {
    opacity: 1;
  }

  @starting-style {
    .nav-backdrop.is-open {
      opacity: 0;
    }
  }

  /* navigation : toolbar and filter panel */

  /* Below this width the toolbar keeps only the filter toggle and gem count. Every search control
     is drawn by the panel, and the closed panel draws nothing at all - which is what empties a band
     that otherwise stacks seven labelled groups of fields above the gems on every screen, all the
     time, wrapping like words on a 360px phone.

     This rule and the one below it are a pair, and their relative strength is the whole of how the
     pair works : this one is a single class, 0-1-0, and the open state is that class plus another
     on the same element, 0-2-0. The open state therefore wins wherever the two happen to sit in
     the file, and it goes on winning if somebody later reorders them. Read in the inspector rather
     than assumed. */
  .filter-panel {
    display: none;
    width: auto;
    margin: 0;
    gap: 0;
    text-align: inherit;
  }

  /* The four desktop grouping boxes disappear at this width. Their children remain the same flex
     items the mobile panel already orders, so the full-screen sheet keeps its established sequence. */
  .filter-block {
    display: contents;
  }

  /* The opened panel : a sheet over the whole screen, and the thing that scrolls, both at once.
     No wrapper is added to carry the scrolling, because every field inside is written once in the
     document and shares its markup with the wide screen's toolbar - a wrapper here would mean a
     second copy of something.

     NO HEIGHT IS WRITTEN ANYWHERE IN IT, and that is load-bearing rather than tidy. A box told to
     be as tall as the viewport is told to be as tall as the viewport with the phone's own toolbars
     retracted, which is not the height the page arrives at : the box hangs below the bottom of the
     screen by the height of those toolbars, and the band that ends up down there is the one
     carrying the two buttons. Pinning all four edges asks the browser for the answer instead, and
     it stays the right answer while the toolbars come and go.

     About the layer number, because it does not say what a reader will take it to say. The toolbar
     this panel lives in is a sticky box, and a sticky box creates a stacking context always,
     whatever its own layer is ; this panel is a descendant of it. So 1200 is a rank INSIDE the
     toolbar, and against the rest of the page the panel paints at the toolbar's own 1000. What
     actually keeps it above everything else is that nothing at body level goes over 1000 while it
     is open. The number is written on the same scale as every other layer in this file all the
     same, because inventing a second scale for one box is worse than the ambiguity ; this note is
     here so nobody reads it as a guarantee against a body-level layer above 1000.

     The colours are the toolbar's own, and the background is written into a property rather than
     into the rule because three bands further down have to carry exactly the same background or
     the fields scroll visibly through them. Two copies of a colour that must match is one of them
     changed on its own later.

     The containment is asked for in its physical spelling on purpose : the spelling that follows
     the writing direction is not implemented in the browser this line exists for. Without it, a
     drag continued past the end of the field list scrolls the gallery behind the panel.

     Side padding only. Padding at the top or the bottom would sit between the sheet's edge and the
     bands pinned to it, and a strip of panel with the fields sliding through it is exactly what
     those bands exist to prevent. */
  .filter-panel.is-open {
    --panel-background: #2a2a2a;

    display: flex;
    flex-wrap: wrap;
    align-content: start;
    position: fixed;
    inset: 0;
    z-index: 1200;
    overflow-y: auto;
    overscroll-behavior-y: contain;
    padding: 0 12px;
    background-color: var(--panel-background);
    color: #eaeaea;
  }

  body.light-mode .filter-panel.is-open {
    --panel-background: #e5e5e5;
    color: #1f1f1f;
  }

  /* The button that opens it. Nothing else here reveals it, and without it the panel cannot be
     opened at all at this width : the controller asks the stylesheet whether this button is drawn
     before it opens anything, rather than holding a copy of the width itself. How big it is and
     where it sits on the toolbar's row are decided further on ; here it only has to exist. */
  button.filters-toggle {
    display: block;
  }

  /* THE FIRST BAND : the heading and the cross, on one row at the top of the sheet.
     Both are pinned there rather than scrolled away with the fields, because this is the one
     screen in the site where the content is long enough to push everything else out of sight, and
     a visitor who cannot see the way out of a full-screen panel has no way out of it. Both carry
     the sheet's own background : a transparent pinned row shows the fields sliding underneath it. */
  .filter-panel.is-open .panel-title {
    order: 0;
    flex: 1 1 auto;
    position: sticky;
    top: 0;
    padding: 12px 0;
    font-weight: 600;
    background-color: var(--panel-background);
  }

  /* The cross takes its text colour from the panel rather than from the browser : a button does
     not inherit colour the way ordinary text does, so with the sheet's dark background behind it
     and the browser's own button colour on it the glyph would be black on near-black. */
  .filter-panel.is-open .panel-close {
    order: 0;
    flex: 0 0 auto;
    position: sticky;
    top: 0;
    background-color: var(--panel-background);
    color: inherit;
  }

  /* THE SECOND BAND : the render choice and the order the gallery is shown in, a row each, then the
     text search, then the seven filter groups, one per row.
     Neither of those two is a filter - the render choice hides no gem, it only picks which picture
     of the same gem is shown, and choosing an order hides none either - and nothing on screen
     otherwise says which of the three pictures is being shown or what the gallery is ordered by. So
     they come first, above the filters, with a rule under the pair saying that neither of them is
     one. That rule is still the only border in the whole panel, which is what lets it mean that : it
     is drawn under the row that closes the band instead of under every row in it, so a band of two
     says exactly what a band of one said about everything above the line.

     Both rows share the layout below and both keep the same order value, so the band stays above the
     text search and the groups and is laid out in document order inside itself - the render choice
     first, the ordering second. What tells the two apart is the class only the last of them carries,
     which is where the line and the space that closes the band live. */
  .filter-panel.is-open .filter-row {
    order: 1;
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 8px;
    row-gap: 4px;
    margin-bottom: 12px;
  }

  /* The end of the band : the space under its last row and the one line in the sheet. Written
     against the class rather than against a position, because a rule that picked the last row out
     by counting children starts drawing the line somewhere else the day a row is added. */
  .filter-panel.is-open .filter-row-band-end {
    padding-bottom: 12px;
    border-bottom: 1px solid #444;
  }

  body.light-mode .filter-panel.is-open .filter-row-band-end {
    border-bottom-color: #ccc;
  }

  /* The refusal line, on a line of its own inside the row it belongs to. Each row of this sheet is
     itself a wrapping flex container, so with no basis of its own the sentence would be laid out
     beside the field it is about and squeezed into whatever was left of that line. Nothing else about
     it is decided here : its colours, its box and the reason it is drawn as a box at all belong to
     the sheet that owns the toolbar, which is the only place that has to know either. */
  .filter-panel.is-open .ordering-notice {
    flex-basis: 100%;
  }

  /* The text search, drawn under the one line in this sheet and above the seven filter groups.
     Under the line because that border means "the rows above it are not filters", so anything that
     is one has to sit below it ; above the groups because this is the field people came to use, and
     a sheet that opens on six numeric ranges before its text search puts the rare case in front of
     the common one.

     Its label needs no width declaration of its own : this panel already gives every label a full
     line, so the name sits above the field exactly as it does for every other control here. That is
     also why the toolbar's old rule hiding this one label off-screen was deleted rather than
     narrowed - the row it was saving 55 pixels on no longer holds this field, and a sheet where
     every control but one is named is worse than those pixels ever were. The field needs no width
     declaration either : this panel gives every input inside it a zero flex basis, and a flex basis
     decides a flex item's main size in place of the definite width the shared stylesheet puts on
     this particular field.

     Weights, so that nobody has to count them again : the label's selector is 0-3-1 and the field's
     is 0-3-0. Neither has anything to outweigh, since nothing else in the site sets an order on
     either element - they only have to reach. */
  .filter-panel.is-open label[for="inTextValue"],
  .filter-panel.is-open .filter-text {
    order: 2;
  }

  /* One row each, in the order the document lists them, and separated from each other by space
     rather than by a line. The one border in this panel is the one closing the band above them, and
     that is what lets it read as "those two are not filters" instead of as a divider like any
     other.

     Each of these is itself a wrapping row of its own, which is what the two rules below lay out
     inside. Worth knowing while reading them : the wide screen's stylesheet gives these same
     elements a display value that removes their box entirely, at one class, and every rule here
     carries three, so they reach without anything having to shout. */
  .filter-panel.is-open .filter-group {
    order: 3;
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 8px;
    row-gap: 4px;
    margin-bottom: 12px;
  }

  /* A label opens its own line and its fields follow on the line underneath. That shape is what
     makes the fault this whole width was rewritten for impossible by construction rather than by
     luck : on a 360px screen the old row wrapped between a label and the field it names, three
     times over, leaving three fields on the page with no visible name and three names pointing at
     nothing. A label that always takes the full width cannot be separated from what follows it. */
  .filter-panel.is-open label {
    flex: 0 0 100%;
  }

  /* The four range labels are the exception : each stays beside its own field, so Min and Max sit
     on one line at about half the row each and the pair reads as the range it is. Named one by one
     rather than picked out by their position in the group - a rule that counts children silently
     starts naming something else the day a field is added. They have to carry the panel's own
     shape as well as their names, because the rule above them is written against the panel too and
     would otherwise win. */
  .filter-panel.is-open label[for="minLw"],
  .filter-panel.is-open label[for="maxLw"],
  .filter-panel.is-open label[for="minRI"],
  .filter-panel.is-open label[for="maxRI"],
  .filter-panel.is-open label[for="minFacetCount"],
  .filter-panel.is-open label[for="maxFacetCount"] {
    flex: 0 0 auto;
  }

  /* The fields share the line their label opened, in equal shares of whatever is left of it.
     The zero basis and the minimum width are both load-bearing rather than decoration. The wide
     screen gives these fields definite widths of 40, 50 and 120 pixels, which a phone must be
     allowed to beat ; and a flex item refuses by default to shrink below the width its own content
     needs, so without the minimum a field would keep a width nothing in this file asked for and
     the row would run off the side of the screen. Together they beat the desktop widths without an
     override that shouts and without touching an id. */
  .filter-panel.is-open input,
  .filter-panel.is-open select {
    flex: 1 1 0;
    min-width: 0;
    margin: 0 4px;
  }

  /* A facet refusal belongs below both bounds rather than being squeezed beside the maximum. */
  .filter-panel.is-open .facet-count-notice {
    flex-basis: 100%;
  }

  /* THE THIRD BAND : Apply and Reset, on one row at the foot of the sheet.
     Sticky, and sticky INSIDE the panel's own scroller - never fixed, and that is the single most
     important line in this block. When a phone raises its on-screen keyboard it shrinks only the
     part of the page the visitor is looking through, not the page itself, and the browsers that
     can be asked to shrink the page instead do not include the one on every iPhone. A row fixed to
     the bottom of the window therefore ends up underneath the keyboard with no gesture that can
     reach it : the visitor types into a filter and can no longer press the button that applies it.
     A sticky row gives up its pin at the end of the content it belongs to, so scrolling the panel
     always brings it back into view.

     They sit flush against the foot of the sheet on purpose : a margin below a pinned row is a
     strip the fields would be seen sliding through. A phone that draws its own gesture bar over
     the last few pixels therefore covers the bottom few pixels of a 44px-tall control, which
     leaves the rest of it comfortably tappable - the failure is cosmetic instead of functional,
     which is the whole reason for the height.

     Both carry the sheet's background and its text colour, for the same two reasons the pinned row
     at the top does. */
  .filter-panel.is-open .panel-apply,
  .filter-panel.is-open .panel-reset {
    order: 4;
    position: sticky;
    bottom: 0;
    min-height: 44px;
    background-color: var(--panel-background);
    color: inherit;
  }

  /* Roughly two to one, and said as two shares of the row rather than as two widths, so the
     proportion holds at every screen width without a second number to keep in step. Reset wipes
     seven groups of filters at once and takes effect the moment it is pressed, so it does not get
     the same weight as the button people opened the panel to press - but it stays plainly a button
     rather than a link hidden in a corner. */
  .filter-panel.is-open .panel-apply {
    flex: 2 1 0;
  }

  .filter-panel.is-open .panel-reset {
    flex: 1 1 0;
  }

  /* The closed mobile toolbar contains only the centred button that opens the full-screen filter
     panel. The result count now belongs to the introduction above and scrolls away with it. */
  .search-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    column-gap: 8px;
    row-gap: 6px;
    padding-left: 10px;
    padding-right: 10px;
  }

  /* The button is sized to its own text and never shrinks or grows, which is what keeps it at its
     own width inside a centred row : an item allowed to grow would take the whole line and there
     would be nothing left to centre, and one allowed to shrink would be narrower than its label
     whenever the label is long. So the box a finger aims at is exactly as wide as the words on it,
     and the number the button gains once filters are applied is always fully readable. */
  .filters-toggle {
    flex: 0 0 auto;
  }

  /* Search is not drawn at this width, and that is worth saying out loud rather than leaving to be
     discovered : no text search is reachable here at all until the panel is opened, which is how it
     was asked for and confirmed rather than a side effect of moving the field. Once the panel is
     open there are two ways to run one, and they are the only two : the field asks the phone for a
     Search key instead of a Return key and pressing it runs a search, and the panel's own Apply runs
     the same search alongside the filters. */
  .search-button {
    display: none;
  }

  /* One hairline under the bar, in each theme's own border colour - the pair the topbar already
     uses, so no new colour enters the site on this account. Once the introduction above has scrolled
     away this bar floats directly over a single large picture of a gem, and with nothing marking its
     lower edge the two read as one surface. A shadow would separate them just as well and would be
     the only shadow anywhere on the site, so the edge is a line.

     Written twice at the same shape rather than once with an exception underneath it. The two rules
     then weigh the same as each other and neither has to go on outweighing the other as either is
     edited ; and both are keyed off the class the page body carries, never the document element,
     which is where the same class is also written and where a rule would work by accident today.

     It costs one pixel of the very band this width exists to shrink. That is deliberate, and it is
     why the bar measures one pixel more than the arithmetic of the things inside it. */
  body:not(.light-mode) .search-bar {
    border-bottom: 1px solid #444;
  }

  body.light-mode .search-bar {
    border-bottom: 1px solid #ccc;
  }

  /* navigation : touch targets and input font size */

  /* The smallest box a finger lands on reliably is about 44 by 44, and the box that has to be that
     size is the control's OWN border box - the box the tap is delivered to. Padding is inside it
     and counts towards the 44 ; margin is outside it and does not, so space around a control makes
     it look roomier without making it any easier to hit.

     The other way of reaching 44 is refused deliberately. An invisible pseudo-element stretched
     past a control's edges does enlarge the area a finger can use, and it is invisible to every
     tool that measures a box : the control would go on reporting the size it always had, so the
     one piece of evidence anybody can check would contradict what is being claimed. Everything
     written here grows the element itself.

     Floors rather than fixed sizes, so a control whose own text needs more room than 44 takes it
     instead of spilling out of a box held at exactly 44.

     No display value is written alongside them, and that is what keeps this rule out of the
     question of which controls are drawn : a minimum size is inert on a control nothing draws, so
     the close cross stays hidden on the closed row and the menu button stays hidden while the menu
     is open, exactly as the rules above leave them.

     Three of the controls a finger meets at this width are deliberately not named here, because
     each already carries its size where its own shape is decided : the six menu entries carry the
     padding that makes the whole strip of each one tappable rather than just the words on it, and
     the panel's Apply and Reset carry their height on the row that pins them to the foot of the
     sheet. A second copy of any of those three is a number somebody later changes in one place
     only, and the two copies then disagree with nothing on screen saying so.

     The panel's own close cross IS named, and it is the one that is easy to miss. It is already as
     tall as the row it shares with the heading, so only its width was ever short - a cross is a
     narrow glyph and the button around it was the width of that glyph. A control that is tall
     enough and too narrow looks perfectly reasonable in a picture and is still a small target, and
     this is the way out of a sheet that covers the whole screen.

     One control on this row is not a target at all, rather than being a small one : Search is not
     drawn at this width. The rule that hides it says so out loud and names the two routes that
     replace it.

     The button that opens the filter panel is named here with the rest of them, and it used to
     carry a height floor of its own with no width floor beside it. That exception existed because
     the button shared its row with the text search : it was the one item on that row that never
     shrank, so every pixel of width floored on it was a pixel taken from the field, and the field's
     ability to give width up was what kept the two on one line. The field is not on that row any
     more - it is a row of the panel now - and the button is the only thing on it, so the reason for
     the exception is gone and the button takes the same pair of floors as every other target. The
     width floor still changes nothing on its own : the label is a word or two and a word already
     costs more than 44 pixels here. What it buys is that nobody has to work out, later, why one
     control in this list was missing half of it.

     The height is paid in the one band of the page that is never scrolled away, so it is worth
     saying rather than discovering : the row is as tall as this button, and the band is as tall as
     the row plus the count under it. That is what a control a finger can actually hit costs here,
     and it is the trade being made rather than an oversight. */
  button.nav-toggle,
  button.nav-close,
  button.panel-close,
  button.filters-toggle,
  #mode-switch {
    min-width: 44px;
    min-height: 44px;
  }

  /* Every field on this page, at the size below which a phone zooms the page in the moment one of
     them takes the focus - and leaves it zoomed, with the layout the visitor was reading now wider
     than the screen and no gesture that obviously undoes it. The zoom is the browser deciding the
     text is too small to type into ; the only thing that stops it is the text not being too small.

     All three kinds are named, and the dropdowns are named for the same reason as the text fields
     rather than for symmetry : the same zoom applies to a dropdown gaining the focus, so a select
     left out is a page that still zooms, on the one control nobody thought to check. The panel's
     own fields need no separate rule - every filter control on this page lives inside the toolbar
     in the document and is only drawn elsewhere by the panel - so this one rule reaches all twelve
     of them.

     There is a second way to stop the zoom, which is to pin the page's maximum scale in the
     document's viewport declaration, and it is refused. It does not make the text readable ; it
     takes zoom away from everybody, including the visitor who was pinching to read a caption and
     has nothing to do with any field.

     The rule lives inside this width block and must stay there. Set everywhere, it would be
     larger glyphs inside the fixed 28-pixel box a wide screen draws, and a visibly different
     toolbar on a machine that never zooms anything and never had the problem.

     Two declarations, not one, and the second is what makes the first work. The base stylesheet
     gives these fields a fixed height of 28 pixels under a border-box sizing, which leaves 18
     pixels of content once the border and the vertical padding are taken out - less than a 16
     pixel line box needs, so the text would be clipped rather than shown. Handing the height back
     to automatic is what releases the box ; the floor beside it then brings the field up to the
     size a finger needs, and the field is free to be taller still if its own text asks for it. */
  .search-bar input[type="text"],
  .search-bar input[type="number"],
  .search-bar select {
    height: auto;
    min-height: 44px;
    font-size: 16px;
  }
}

@media (width <= 820px) {
  /* card : the pre-existing component breakpoint, moved from card.css */
  .card { width: 100%; }

  /* Public profile upper panels share document order at tablet widths. The browser enhancement
     keeps the one bench disclosure open above the phone boundary; CSS changes only its layout. */
  .public-profile-upper {
    grid-template-columns: minmax(0, 1fr);
  }

  .public-profile-bench {
    display: block;
    height: auto;
    min-height: 0;
    overflow: visible;
  }

  .public-profile-bench-content {
    height: auto;
    overflow-y: visible;
  }
}

@media (width <= 640px) {
  /* gallery : single column, and the gallery's comment panel drawn on the whole screen

     Two subjects share this block and each is labelled where it starts. The gallery's own rules are
     the older ones and none of them moved ; the comment panel's were added the day that panel was
     first drawn, and they are here rather than in that panel's own stylesheet for the reason the
     head of this file gives - each of the three widths this site is designed around opens exactly
     one block, and a second block at 640 would be a second place somebody has to remember. */

  /* gallery : single column */

  .wall-photograph-slot-list {
    grid-template-columns: 1fr;
  }

  /* Completion and edit forms keep their document order while dense pairs become one column. */

  .todo-form-card {
    padding: 24px;
  }

  .todo-form-heading {
    align-items: flex-start;
    flex-direction: column;
  }

  .todo-form-design {
    grid-template-columns: minmax(0, 1fr);
  }

  .todo-form-design-wireframe {
    width: 100%;
  }

  .todo-form-fields {
    grid-template-columns: minmax(0, 1fr);
  }

  .todo-form-field-wide {
    grid-column: auto;
  }

  /* public profile and wall : one semantic column with complete media in stored order */

  .public-profile-shell,
  .public-profile-upper {
    gap: 24px;
  }

  .public-profile-card,
  .public-profile-bench,
  .public-wall {
    padding: 24px;
  }

  /* completed-cut feed : compact context above the unchanged canonical wall card */

  .completed-cut-feed-shell {
    gap: 24px;
  }

  .completed-cut-feed-filter {
    grid-template-columns: 64px minmax(0, 1fr);
  }

  .completed-cut-feed-filter > img {
    width: 64px;
    height: 64px;
  }

  .completed-cut-feed-show-all {
    grid-column: 1 / -1;
    width: 100%;
  }

  .public-wall-toolbar {
    align-items: flex-start;
    flex-direction: column;
    gap: 8px;
  }

  .public-wall-filter {
    align-items: flex-start;
  }

  /* My Todos : compact identity first, then a full-width two-control action row. */

  .my-todos-section {
    padding: 24px;
  }

  .my-todos-pending-row,
  .my-todos-completed-row {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
  }

  .my-todos-actions,
  .my-todos-completed-actions {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 100%;
    max-width: none;
  }

  .todo-confirmation,
  .todo-confirmation-slot,
  .my-todos-edit-deadline {
    min-width: 0;
  }

  .my-todos-edit-deadline {
    grid-column: 1 / -1;
  }

  .wall-item {
    padding: 16px;
  }

  .wall-photo-grid-1,
  .wall-photo-grid-2,
  .wall-photo-grid-3,
  .wall-photo-grid-4 {
    grid-template-columns: minmax(0, 1fr);
  }

  .wall-photo-viewer {
    width: calc(100% - 32px);
    padding: 16px;
  }

  /* The one place the gem picture's width is written for a narrow screen, and the only one : the
     caption above the picture and the info box at the foot of the card already read this same
     value, so the three boxes end up the same width by construction rather than because somebody
     kept three numbers in step.

     The cap is exact rather than round. It is one narrow phone's column - a 360 pixel screen less
     the card's own two 10 pixel margins - so on that screen the picture takes everything there is,
     and on anything wider it stops there rather than growing into a picture nobody asked for. The
     percentage beside the cap is what makes it stop at the screen instead of running off it, and
     it is measured against the card, which is what the rule below is for. */
  .gem-gallery {
    --gem-image-width: min(100%, 340px);
  }

  /* The card takes the whole row, and without this line the width above is inert. The wide screen
     gives the card a preferred width of 320 pixels ; the percentage above is measured against the
     card and not against the screen, so left at 320 the picture would resolve to 320, the cap
     would never be reached and the number would appear nowhere on the page. A preferred width of
     the whole row overshoots it by the card's own margins and shrinks straight back to exactly the
     width the cap was cut for.

     The space DECLARED under a card drops by 20 pixels, which is 20 pixels of empty screen
     recovered on every gem in a list people scroll through hundreds of. What is DRAWN between two
     cards is this figure plus the next card's own top margin : the cards are flex items, and a
     flex item's margins never collapse against anything. So a declaration of 30 draws 40, exactly
     as the previous declaration of 50 drew 60, and the two quantities are not to be confused when
     either one is next measured. The top margin is deliberately left alone - zeroing it to make
     the two numbers agree would also close up the space above the very first card and above every
     row of them. */
  .gif {
    flex-basis: 100%;
    min-width: 0;
    margin-bottom: 30px;
  }

  /* The caption is given a width of its own, and this is the one declaration in this block that
     looks redundant and is not. It already carries a cap of the same value the picture uses, but a
     cap is not a width : the card is a column that centres what is inside it, so a child with no
     width of its own is drawn exactly as wide as its own text - measured at 167 pixels under a 300
     pixel picture, nowhere near a cap of 300. Given a definite width it fills the card, and the
     cap beside it then does what it always meant to and holds it to the picture's width on a
     screen wider than the cap. The text inside stays centred either way ; what changes is the box,
     and the box is what has to match the picture.

     This is the only thing this file says about the caption. Where it is drawn is not decided here
     and is not decided anywhere : it is drawn where the document lists it, which is above the
     picture, at every width. */
  .gif-name {
    width: 100%;
  }

  /* THE CARD IS ONE DOCUMENT ORDER AT EVERY WIDTH, and nothing here decides where anything in it is
     drawn any more. Four declarations used to stand at this spot numbering the picture, the caption,
     the badges and the info box so that a narrow screen drew the picture first and its name under it.
     They are deleted rather than adjusted, so a narrow screen now draws what the document lists and
     what a wide screen has always drawn : the caption, then the row of controls, then the picture,
     then the badges, then the info box a text search produces.

     Why they went, said properly, because this is the kind of thing somebody restores later meaning to
     help. A card used to hold exactly one thing that could take the focus - the link on the name - and
     the comment that stood here said in as many words that a second one would end the property those
     numbers relied on : the keyboard travels a card in the order the document lists it and never in
     the order these numbers drew it, so with a second control the two could disagree and one of them
     would have to be settled against the other by hand. A card now holds four such controls. Deleting
     the numbers settles it by construction instead, and what the eye meets going down a card is what
     the keyboard reaches going through it. It is also the order that was asked for outright : the
     caption above the picture on a phone, consistent with the desktop.

     Restoring the phone-only reversal later would be a regression and not a fix.

     What is left in this block about the card's shape is one thing, and it is unrelated : THE BADGES
     ARE ALLOWED A SECOND LINE HERE. Four of them measure about 250 pixels today and sit comfortably on
     one, so most cards will never use it ; a gem carrying a longer value wraps onto a second line
     instead of having its last badge cut off with an ellipsis, at a cost of about 20 pixels of card
     height on that card alone. Those values are the gem's own cut data, which is what a reader came
     for. */
  .gem-badges {
    flex-wrap: wrap;
  }

  /* report dialog : the same full-screen ownership used by the gallery's other phone panel */

  .gem-report-dialog.is-open {
    width: auto;
    height: auto;
    max-height: none;
    margin: 0;
    border: none;
    border-radius: 0;
  }

  .gem-report-shape-comparison {
    grid-template-columns: minmax(0, 1fr);
  }

  .gem-report-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }

  .gem-report-actions > button {
    width: 100%;
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* administrator dashboard : one readable column and one viewport-owned resolution surface */

  .administrator-dashboard-card {
    padding: 24px;
  }

  .administrator-report-tile {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
    min-width: 0;
    padding: 16px;
  }

  .administrator-report-wireframe-box {
    width: min(220px, 100%);
    height: auto;
    aspect-ratio: 1 / 1;
    justify-self: center;
  }

  .administrator-report-comparison {
    grid-template-columns: minmax(0, 1fr);
  }

  .administrator-report-actions {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
  }

  .administrator-report-actions > button {
    width: 100%;
    min-width: 0;
    min-height: 44px;
    overflow-wrap: anywhere;
  }

  .administrator-report-copy,
  .administrator-report-header,
  .administrator-report-heading,
  .administrator-report-facts,
  .administrator-report-fact,
  .administrator-report-fact > div,
  .administrator-report-fact dd,
  .administrator-report-address-text,
  .administrator-report-address-link,
  .administrator-report-explanation,
  .administrator-resolution-dialog-heading,
  .administrator-resolution-dialog-body,
  .administrator-resolution-form,
  .administrator-resolution-message,
  .administrator-resolution-message-preview,
  .administrator-resolution-effect {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  .administrator-resolution-dialog.is-open {
    inset: 0;
    width: auto;
    height: auto;
    max-height: none;
    margin: 0;
    border: none;
    border-radius: 0;
  }

  .administrator-resolution-dialog-header {
    position: sticky;
    top: 0;
    z-index: 1;
  }

  .administrator-resolution-dialog-body {
    overflow-x: hidden;
    overflow-y: auto;
  }

  .administrator-resolution-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }

  .administrator-resolution-actions > button {
    width: 100%;
    min-width: 0;
    min-height: 44px;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  /* wall moderation : compact evidence and one viewport-owned confirmation surface */

  .wall-moderation-dashboard {
    padding: 24px;
  }

  .wall-moderation-history-filter:not([hidden]) {
    grid-template-columns: minmax(0, 1fr);
  }

  .wall-moderation-history-filter > div,
  .wall-moderation-history-filter > button,
  .wall-moderation-history-filter > a {
    width: 100%;
    min-width: 0;
  }

  .wall-moderation-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .wall-moderation-actions > button {
    width: 100%;
    min-width: 0;
  }

  .wall-moderation-staff-header,
  .wall-moderation-staff-footer,
  .wall-moderation-timeline,
  .wall-moderation-timeline-event,
  .wall-moderation-owner,
  .wall-moderation-owner a,
  .wall-moderation-rejection-reason {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  .wall-moderation-dialog.is-open {
    width: auto;
    height: auto;
    max-height: none;
    margin: 0;
    border: none;
    border-radius: 0;
  }

  .wall-moderation-dialog-header {
    position: sticky;
    top: 0;
    z-index: 1;
  }

  .wall-moderation-dialog-body {
    overflow-x: hidden;
    overflow-y: auto;
  }

  /* comments : the panel on the whole screen, one picture at a time, what the opening screen holds,
     and the tile rearranged */

  /* The strip the scroll lock holds for a hidden scrollbar is given back at this width, and the
     panel below is the reason. Every panel this site opens here covers the whole screen, so a strip
     held down the side of one is a strip of page beside a sheet that is supposed to have no side -
     measured at fifteen pixels of it on this machine. What the strip is there to prevent costs
     nothing here either : the page behind steps sideways as the panel opens, under a panel that is
     covering it, and steps back the moment it closes with nobody having seen either.

     On a real handset this declaration and the one it overrides are both inert : a phone draws its
     scrollbar over the page rather than beside it, and a gutter only exists where a scrollbar takes
     width. It is a narrow window on a desktop that this answers for. */
  html.panel-open {
    scrollbar-gutter: auto;
  }

  /* The panel stops being a centred box and becomes the screen. All four of its edges are already
     pinned by the rule this overrides - that is how the wider screen centres it - so what is taken
     away here is everything that held it short of them : the width, the shrink-to-fit, the ceiling
     over that, and the frame a box floating on a backdrop needs and a full screen does not. The
     automatic margins that centred it resolve to nothing once there is no width left to centre.

     NO HEIGHT IS WRITTEN HERE AND THE ONE DECLARATION THAT MENTIONS HEIGHT IS TAKING ONE AWAY. A
     box told to be as tall as the viewport is told to be as tall as the viewport with the phone's
     own toolbars retracted, which is not the height the page arrives at : it hangs below the bottom
     of the screen by the height of those toolbars, and what ends up down there is the writing box.
     Four pinned edges ask the browser for the answer instead, and it stays the right answer while
     the toolbars come and go. The filter sheet's own rule records the same thing, and it is the
     same failure. */
  .gem-comment-panel.is-open {
    width: auto;
    height: auto;
    max-height: none;
    border: none;
    border-radius: 0;
  }

  .wall-comment-parent {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }

  .wall-comment-parent-media-box {
    flex: 0 0 72px;
    width: 72px;
    height: 72px;
  }

  .wall-comment-parent-copy {
    flex: 1 1 160px;
  }

  .wall-comment-parent-photograph-strip {
    height: 40px;
  }

  .wall-comment-parent .comment-share-row {
    flex: 0 0 auto;
    margin-inline-start: auto;
  }

  /* The three pictures become one picture at a time, swiped. Each is exactly the strip wide, so a
     swipe lands on one of them and never between two, and the strip is held to that by the snap.

     The alignment is put back to the start and is not decoration : a flex row that centres its
     children and overflows puts the first of them off the left-hand end, where nothing can scroll
     back to it. The gap goes for the same kind of reason - a strip of panel between two pictures
     that are meant to be one screen each.

     The last declaration is what stops a swipe carried past the last picture dragging the panel,
     and through it the gallery, along behind it. Asked for in its physical spelling on purpose :
     the spelling that follows the writing direction is not implemented in the browser it exists
     for. */
  .comment-picture-strip {
    justify-content: flex-start;
    gap: 0;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
  }

  .comment-picture-strip > * {
    flex: 0 0 100%;
    scroll-snap-align: center;
  }

  /* And the three dots under it, which are the only thing saying which of the three is being
     looked at once they are no longer side by side. What each one looks like is drawn where the
     panel's other colours are ; this is only where they are turned on. */
  .comment-picture-dots {
    display: flex;
    justify-content: center;
  }

  /* WHAT A PHONE SHOWS BEFORE ANYBODY SCROLLS, WHICH IS THE WHOLE REASON THESE FOUR RULES EXIST.
     Opened as this panel stood on 2026-08-12, a phone showed a picture as wide as the screen and
     therefore as tall, its three dots, the count and a pager - and not one line of a comment.
     Somebody who tapped a bubble to read what people had said met a picture they were already
     looking at, with nothing on the screen saying there was anything under it.

     Measured on three handset viewports rather than guessed. At 360 by 800 the first comment began
     106 pixels above the writing box ; at 390 by 664 it began 60 pixels BELOW that box and at 412 by
     730 it began 16 below - so on the two shorter screens the first comment was not merely under the
     fold, it was behind the box you type into, which is stuck over the foot of the same scrolling
     column.

     THE HALVED MARGINS ARE THE PART THAT WAS ASKED FOR AND THE PICTURE'S HEIGHT IS THE PART THAT
     ANSWERS IT. Halving the three pairs - the strip's twelve, the count line's eight and the pager's
     eight - buys 24 pixels, which is real and is not the 60 the worst of the three viewports is short
     by. So the picture is given a height at this width instead of taking its own width, and 226 is
     where that number comes from : on the shortest of the three, with the margins already halved, the
     writing box's top sits at 508, everything above the first comment other than the picture takes
     178 of that, and the first line of a comment's words ends 104 pixels into its tile. 508 less 178
     less 104 is 226. A smaller number puts more of the comment on the opening screen and costs more
     of the picture ; that trade is the whole of this decision, and the arithmetic above is how to
     move it for a reason rather than by taste.

     The ratio has to be given up for a height to mean anything, which is what the first declaration
     does : a box carrying both a ratio and a height is a box whose ratio is ignored. WHAT THE PICTURE
     KEEPS IS ITS SLOT - the strip's children are still one screen width each, so the swipe, the
     snapping and the three dots are untouched. It is drawn whole and centred inside that slot with
     empty bands at its sides, by the fitted-whole rule it already carries, exactly as a diagram that
     is not square is already drawn with bands above and below. Shrinking the slot instead is what
     would break the snapping.

     WHAT ACTUALLY SHRINKS IS NOT WHAT A READER WOULD EXPECT, and it is worth knowing before this
     number is moved. Measured on the drawn pixels rather than on the box, on a design whose diagram
     is half again as wide as it is tall : the diagram is letterboxed inside its square box already,
     so on a 360-wide screen it comes out at exactly the size it had - 336 by 224 before and after,
     not one pixel lost - and on the two wider screens it gives up a fourteenth and an eighth. The
     render, which is square and fills its box, and the graph, which nearly does, are the two that
     pay : between a third and two fifths of their drawn size across the three widths. So this
     number costs the renders far more than it costs the cutting instructions. */
  .comment-picture {
    aspect-ratio: auto;
    height: 226px;
  }

  .comment-picture-strip {
    margin: 6px 0;
  }

  .comment-design-evidence {
    margin: 4px 0;
    column-gap: 4px;
  }

  .comment-design-evidence-separator {
    margin-left: 4px;
  }

  .comment-pager {
    margin: 4px 0;
  }

  /* And the row of two controls above the pictures, halved with them and for the same reason. It is
     the one thing added to this screen since the numbers above were taken, and it is not free : the
     row is as tall as a thumb needs, so what it costs the opening screen is its own height plus what
     is above and below it. Halving those margins is the whole of what can be given back without
     taking the controls below the size a finger can hit.

     WHAT IT ACTUALLY COST, measured at the same three viewports on 2026-08-12 with the row and
     without it in one browser run : 48 pixels at every one of them, which is the row's 44 and the
     four above it, the four below being swallowed by the strip's six where two margins meet. The
     room the first comment lands in goes from 240 / 104 / 170 to 192 / 56 / 122 at 360x800, 390x664
     and 412x730. All three are still positive, so the first tile still begins on the opening screen
     at every one of them - but 56 is less than the 104 the first line of a comment's own words sits
     at inside its tile, so at 390x664 what now lands on that screen is the author's name and not yet
     the words. The number to move if that is not wanted is the picture's height above, and the
     arithmetic for moving it is written there. */
  .comment-share-row {
    margin: 4px 0;
  }

  /* THE TILE'S SECOND ARRANGEMENT, AND IT IS THE SAME NODES. The picture and the name on a line
     above, the instant under the name beside the picture, and the comment itself across the whole
     width below - rather than indented past a picture's gutter, which on a three-hundred-pixel
     column costs a fifth of every line of every comment.

     The middle box is dissolved rather than laid out, which is the whole trick : the name, the
     instant, the marker, the words and the controls are its children, and they cannot be arranged
     against the picture while a box stands between them and it. Dissolving it makes all six the
     children of one grid, so the picture takes the first column and spans the two rows beside it
     while the words take a row of their own from edge to edge. Nothing is moved in the document
     and nothing is drawn twice. */
  .comment-tile {
    display: grid;
    grid-template-columns: auto auto 1fr;
    align-items: center;
    column-gap: 8px;
    row-gap: 2px;
  }

  .comment-tile-body {
    display: contents;
  }

  .comment-avatar {
    grid-column: 1;
    grid-row: 1 / 3;
  }

  .comment-author {
    grid-column: 2 / -1;
    grid-row: 1;
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    width: fit-content;
    min-width: 0;
    max-width: 100%;
    overflow-wrap: anywhere;
  }

  /* The space the instant carries from the name on the wider screen is taken back : here it opens
     a line rather than following one, and eight pixels of it would sit the instant out of line
     with the name above it. */
  .comment-date {
    grid-column: 2;
    grid-row: 2;
    margin-left: 0;
  }

  .comment-edited {
    grid-column: 3;
    grid-row: 2;
  }

  .comment-text,
  .comment-edit-box,
  .comment-tile-controls {
    grid-column: 1 / -1;
  }

  /* discussions : the design name and the relative date each take a line of their own here. Side by
     side in a tile this narrow, the name is left a column too short to read. */
  .discussion-line-heading {
    align-items: flex-start;
    flex-direction: column;
    gap: 4px;
  }

  /* private messages : keep identity beside copy while the copy and actions use the narrow column */
  .private-message-inbox-card,
  .private-message-compose-card,
  .private-message-detail-card,
  .private-message-block-card {
    padding: 24px;
  }

  .private-message-conversation-heading {
    align-items: flex-start;
    flex-direction: column;
    gap: 8px;
  }

  .private-message-conversation-actions {
    display: grid;
    justify-items: start;
  }

  .private-message-conversation-actions [aria-hidden="true"] {
    display: none;
  }

  .private-message-profile-action {
    display: block;
  }

  .public-profile-message-actions,
  .public-profile-message-action,
  .public-profile-message-action-form {
    width: 100%;
  }

  .private-message-textarea {
    min-height: 176px;
    font-size: 16px;
  }

  .private-message-detail-header {
    grid-template-columns: 64px minmax(0, 1fr) auto;
    gap: 8px;
  }

  .private-message-detail-block,
  .private-message-detail-unblock-form {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
  }

  .private-message-detail-block,
  .private-message-detail-unblock {
    width: auto;
    padding: 6px 10px;
  }

  .private-message-history-message {
    max-width: 88%;
  }

  .private-message-conversation-copy,
  .private-message-conversation-actions,
  .private-message-correspondent-link,
  .private-message-conversation-link,
  .private-message-correspondent-name,
  .private-message-conversation-excerpt,
  .private-message-recipient-copy,
  .private-message-recipient-name,
  .private-message-detail-correspondent,
  .private-message-history,
  .private-message-history-message,
  .private-message-history-date,
  .private-message-body,
  .private-message-block-warning,
  .private-message-block-cancel,
  .private-message-block-submit,
  .private-message-profile-back,
  .private-message-back-link,
  .private-message-not-found-guidance {
    overflow-wrap: anywhere;
  }
}

/*
    SOMEBODY WHO ASKED THEIR DEVICE FOR LESS MOVEMENT GETS NONE

    This block is the LAST thing in the file, and that placement is the only reason it works. A
    media condition contributes no weight of its own to the rules inside it, so each rule here
    weighs exactly as much as the one it exists to overrule - an id against an id for the menu,
    a class against a class for the dim layer - and between two rules of equal weight the later
    one in the file wins. Filed above the width blocks, which is where a reader following their
    descending order would naturally put it, both declarations lose : the preference is silently
    not honoured and the block goes on looking perfectly correct. Nothing else in this repository
    reads a motion preference, so nothing would ever report it.

    Both halves are named. Honouring the preference on the menu alone would still leave the page
    behind it fading, which is half an animation rather than none.
*/
@media (prefers-reduced-motion: reduce) {
  #nav-drawer,
  .nav-backdrop {
    transition: none;
  }

  /* The shine crossing Get the design runs on its own for as long as the panel is open, rather than
     once in answer to something the visitor did, which is the kind a motion preference is most
     plainly about. Removed rather than slowed : there is no slow version of it that is not still a
     thing moving on its own in the corner of somebody's eye.

     Taken away outright rather than by stopping its animation, which is what the rules around this
     one do. Those act on things that are in the right place when they are still ; this one is a band
     that is only ever off the edge of the button because a keyframe is holding it there, so stopping
     the animation leaves it standing wherever it was declared, in view. */
  .comment-share-open::after {
    display: none;
  }

  .administrator-dashboard :is(a, button),
  .administrator-resolution-dialog :is(button, input, textarea) {
    transition: none;
  }

  .wall-moderation-dashboard :is(a, button, input, select, summary),
  .wall-moderation-dialog,
  .wall-moderation-dialog :is(button, textarea) {
    animation: none;
    transition: none;
  }

  .identity-logout-confirm {
    animation: none;
  }

  .discussion-line {
    transition: none;
  }

  .public-profile-shell *,
  .completed-cut-feed-shell *,
  .wall-photo-viewer {
    animation: none;
    transition: none;
  }

  .todo-confirmation-slot {
    animation: none;
    transition: none;
  }

  .todo-form-shell * {
    animation: none;
    transition: none;
  }

  /* The two pseudo-element selectors are not padding : the universal selector does not reach a pseudo-element, and
     the four blinking triplets on this page draw two of their three dots with one each — so without these lines two
     dots in every three would keep moving for somebody who asked for no movement. */
  .my-submissions-shell *,
  .my-submissions-shell *::before,
  .my-submissions-shell *::after {
    animation: none;
    transition: none;
  }
}
