/*
Theme Name:  Downtown Arnprior
Description: The block theme for the downtown Arnprior business hub. Every
             template is a thin shell around one PHP pattern, because the
             eighteen page designs are bespoke and a pattern is a PHP file.
             See docs/decisions/0002.
Version:     0.81.2
Author:      Kingsbury Creative
Requires at least: 6.5
Tested up to: 7.1
Requires PHP: 8.2
Text Domain: dta
*/

/* ----------------------------------------------------------------
   THE FOUR STEPS

   620   two columns
   800   tablet
   1000  desktop

   And two that are not free to move:

   1024  the nav drawer, and system.js watches this exact number with
         matchMedia. Change one without the other and the body stays
         scroll-locked when the window grows.
   1180  the nav gap, and it is contentSize in theme.json.

   Nothing else. There were nineteen, because eighteen page designs
   each solved "wide enough for two columns" against their own ruler:
   600, 620 and 640 all meant the same thing, and so did 860, 900,
   940, 960 and 980.

   The numbers are written down in theme.json under custom.step as
   well, which is where a person looks for a design value. They are
   repeated here because @media cannot read a custom property: the
   query is evaluated before the cascade, so var() in a media
   condition is not a thing. Two copies, and this comment is the
   reason they have to be kept in step by hand.
   ----------------------------------------------------------------- */

/* ----------------------------------------------------------------
   TOKENS
   theme.json is the design system. Every colour, type size and
   spacing value lives there and nowhere else. This block only gives
   them the short names the design was written against, so the CSS
   ported from design/system.css reads as it was written.

   If you are about to type a hex into this file, put it in
   theme.json instead and alias it here.
   ---------------------------------------------------------------- */
:root{
  --paper:var(--wp--preset--color--paper);
  --paper-2:var(--wp--preset--color--paper-2);
  --ink:var(--wp--preset--color--ink);
  --ink-soft:var(--wp--preset--color--ink-soft);
  --yellow:var(--wp--preset--color--yellow);
  --card:var(--wp--preset--color--card);
  --danger:var(--wp--preset--color--danger);
  /* Open, closing soon and closed are green, yellow and grey. The colour is
     never the only carrier: the badge says the words as well. */
  --open:var(--wp--preset--color--open);

  --line:var(--wp--custom--line);
  --line-heavy:var(--wp--custom--line-heavy);

  /* The navy bands are the header, the drawer, the footer, the join
     board and every inverted section. They keep the same palette in
     light, dark and high contrast on purpose: the signboard does not
     change colour when the page does. */
  --navy:var(--wp--preset--color--navy);
  --on-navy:var(--wp--preset--color--on-navy);
  --on-navy-line:var(--wp--custom--on-navy-line);
  --photo-ink:var(--wp--preset--color--photo-ink);

  --disp:var(--wp--preset--font-family--display);
  --body:var(--wp--preset--font-family--body);

  --t-s:var(--wp--preset--font-size--s);
  --t-b:var(--wp--preset--font-size--b);
  --t-m:var(--wp--preset--font-size--m);
  --t-l:var(--wp--preset--font-size--l);
  --t-xl:var(--wp--preset--font-size--xl);

  --gut:var(--wp--preset--spacing--gut);
  --band:var(--wp--preset--spacing--band);

  /* the space under a heading, scaled to the same vw term the type itself
     uses. A fixed rem here is why the gap under a name reads fine on a
     phone and broken on a desktop: the heading grows, the gap does not. */
  --sp-3xs:var(--wp--custom--space--tight);
  --sp-2xs:var(--wp--custom--space--snug);
  --sp-xs:var(--wp--custom--space--open);
  --sp-s:var(--wp--custom--space--wide);
  /* The step the scale never had. It ran 3xs, 2xs, xs, s and then jumped
     straight to --band, so anything that needed "a new section starts here,
     but not a new band" reached for a hardcoded rem value instead. */
  --sp-m:var(--wp--custom--space--mid);

  --hdr-h:var(--wp--custom--header-height);

  /* Where a sticky sub-bar parks. The header is fixed and hides itself on
     the way down the page, so a bar parked at --hdr-h leaves an empty 62px
     strip with the page scrolling through it. system.js puts .hdr-away on
     the html element whenever the header is out, and this drops to zero. */
  --stick-top:var(--hdr-h);

  /* the layer scale. Nothing on this site sets a z-index that is not
     one of these five. Leaflet is why: it hard-codes up to 800 on its
     own panes, which beat a header at 50. */
  --z-map:var(--wp--custom--layer--map);
  --z-sticky:var(--wp--custom--layer--sticky);
  --z-hdr:var(--wp--custom--layer--header);
  --z-nav:var(--wp--custom--layer--nav);
  --z-find:var(--wp--custom--layer--find);
  --z-skip:var(--wp--custom--layer--skip);
}

/* derive the hairline from the ink so a theme only sets one colour.
   --line-heavy is the same hairline with enough of it to read as the edge of
   an object rather than as a rule between two. Measured at 390px: the owner
   form's shut section bars sit at 1.10:1 against the page they sit on, so the
   only thing making four of them four things is this border, and at 17% it
   was 1.38:1 and doing none of that work.

   The percentages are not a taste: each one is the smallest that puts the
   painted edge over 3:1 against the surface it edges, which is what WCAG
   asks of a boundary somebody has to see to tell one control from the next.
   Measured off the painted pixels in all four themes by the probe in #180,
   not read off these declarations, because alpha over an unknown backdrop is
   exactly what a computed value will not tell you. */
@supports (color:color-mix(in srgb,red 50%,white)){
  :root{--line:color-mix(in srgb,var(--ink) 17%,transparent);
    --line-heavy:color-mix(in srgb,var(--ink) 52%,transparent)}
}

/* the display-options bar sets these attributes; the head script restores
   them from localStorage before first paint so nothing flashes */
html[data-fs="1"]{font-size:112.5%}
html[data-fs="2"]{font-size:125%}
/* The three other theme states. Their palettes live in theme.json under
   settings.custom, same as every other colour on the site. */
:root[data-thr="dark"]{
  --paper:var(--wp--custom--dark--paper);
  --paper-2:var(--wp--custom--dark--paper-2);
  --ink:var(--wp--custom--dark--ink);
  --ink-soft:var(--wp--custom--dark--ink-soft);
  --line:var(--wp--custom--dark--line);
  --line-heavy:var(--wp--custom--dark--line-heavy);
  --card:var(--wp--custom--dark--card);
  /* The light danger red is #a3271b and it measures 1.97:1 against the dark
     card, so a field marked wrong in dark mode was marked in a colour nobody
     could see. Same failure as the focus ring, found the same way. */
  --danger:var(--wp--custom--dark--danger);
}
:root[data-hc="1"]{
  --paper:var(--wp--custom--contrast--paper);
  --paper-2:var(--wp--custom--contrast--paper-2);
  --ink:var(--wp--custom--contrast--ink);
  --ink-soft:var(--wp--custom--contrast--ink-soft);
  --line:var(--wp--custom--contrast--line);
  --line-heavy:var(--wp--custom--contrast--line-heavy);
  --card:var(--wp--custom--contrast--card);
}
:root[data-thr="dark"][data-hc="1"]{
  --paper:var(--wp--custom--dark-contrast--paper);
  --paper-2:var(--wp--custom--dark-contrast--paper-2);
  --ink:var(--wp--custom--dark-contrast--ink);
  --ink-soft:var(--wp--custom--dark-contrast--ink-soft);
  --line:var(--wp--custom--dark-contrast--line);
  --line-heavy:var(--wp--custom--dark-contrast--line-heavy);
  --card:var(--wp--custom--dark-contrast--card);
  --danger:var(--wp--custom--dark-contrast--danger);
}
@supports (color:color-mix(in srgb,red 50%,white)){
  :root[data-thr="dark"]{--line:color-mix(in srgb,var(--ink) 18%,transparent);
    --line-heavy:color-mix(in srgb,var(--ink) 40%,transparent)}
  :root[data-hc="1"],:root[data-thr="dark"][data-hc="1"]{
    --line:color-mix(in srgb,var(--ink) 40%,transparent);
    --line-heavy:color-mix(in srgb,var(--ink) 55%,transparent)}
}

/* ----------------------------------------------------------------
   BASE
   ---------------------------------------------------------------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--body);font-size:var(--t-b);line-height:1.65;
  overflow-x:clip;
}
/* height:auto or a browser honours the width and height attributes
   WordPress writes on every uploaded image, and squashes the picture
   the moment max-width has to do anything. */
img{max-width:100%;height:auto;display:block}
a{color:inherit}
/* A link that leaves this site says so and opens in its own tab, so the page
   somebody was reading is still there when they come back. The hook is the
   target attribute and not the href: matching the href means writing the
   site's domain into this file, and it is a different domain on a laptop than
   it is on the real one.

   Drawn as a mask rather than a character or a coloured image, so it takes
   whatever colour the link is. That matters in three places at once: the navy
   footer, dark mode, and both contrast modes. */
:root{--icon-out:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6' /%3E %3Cpath d='M10 14 21 3' /%3E %3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6' /%3E%3C/svg%3E")}
a[target="_blank"]::after{content:"";display:inline-block;width:.72em;height:.72em;
  margin-left:.34em;vertical-align:-.02em;background:currentColor;
  -webkit-mask:var(--icon-out) center/contain no-repeat;
  mask:var(--icon-out) center/contain no-repeat}
h1,h2,h3{margin:0;font-family:var(--disp);font-weight:800;line-height:1.06;letter-spacing:-.01em}
/* balance keeps a two-line headline from leaving one orphan word.
   Ignored where unsupported, which costs nothing. */
h1,h2,h3,.lede,.kick{text-wrap:balance}
p{margin:0 0 1em;text-wrap:pretty}
::selection{background:var(--yellow);color:var(--ink)}

/* The focus ring, and it is two rings on purpose.

   One colour cannot do this. The ring has to read on paper, on the navy
   bands, over a photograph and on a yellow button, and the brightness
   windows those demand do not overlap: navy needs a light ring and
   yellow needs a dark one. There is no single value that clears 3:1
   against both.

   This rule used to be one ink outline. --ink and --navy are the same
   hex in light mode, so a keyboard user tabbing into the footer got an
   outline at 1:1 against the band behind it, which is to say nothing at
   all. Measured on /contact/, not noticed by looking.

   So: ink outside, paper inside, the inner ring filling the gap the
   offset opens. Those two are opposite ends of the palette in all four
   theme states, so whichever one loses its contrast against the thing
   behind it, the other one has it. Same trick the browser's own default
   ring uses. */
:focus-visible{
  outline:3px solid var(--ink);
  outline-offset:3px;
  box-shadow:0 0 0 3px var(--paper);
}

/* anything an in-page link points at clears the sticky header */
:target,[id]{scroll-margin-top:calc(var(--hdr-h) + 1rem)}

.wrap{width:min(100% - (var(--gut)*2),1160px);margin-inline:auto}
.skip{position:absolute;left:-999px}
.skip:focus{left:.5rem;top:.5rem;z-index:var(--z-skip);background:var(--ink);color:var(--paper);
  padding:0 1.1rem;min-height:44px;display:inline-flex;align-items:center}

/* small caps label, used sparingly */
.kick{
  font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.12em;color:var(--ink-soft);
}

/* ----------------------------------------------------------------
   DISPLAY OPTIONS : text size, contrast, colours
   ---------------------------------------------------------------- */
/* This started as six pills in a band above the header, which put roughly a
   hundred pixels of chrome before any of the words on every page. Then it was
   one button in that band, which is a whole strip across the site for one
   control and looked it.

   It is a button in the header now, beside the menu, and the panel drops out
   of the header rather than pushing the page down. The band is gone.

   The button says Aa. It was a faders icon and it did not survive being 18px
   on a navy ground: three tracks with knobs is a shape you decipher, and the
   people most likely to want this button are the ones least able to do that
   at that size. Two letters in the display face read at a glance and say the
   thing the panel mostly does. */
.dispbtn{
  background:none;color:var(--on-navy);border:1px solid #eef1ef55;border-radius:4px;
  width:44px;height:44px;flex:none;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  transition:background .15s,color .15s,border-color .15s;
}
.dispbtn-aa{font-family:var(--disp);font-weight:800;font-size:1.02rem;line-height:1;
  letter-spacing:-.01em}
/* The on-navy fill. Shared with the drawer's close button and the ghost
   button: everything drawn transparent on navy fills the same way. Was
   three identical blocks. */
.dispbtn:hover,.dispbtn[aria-expanded="true"],.navx:hover,.ghost:hover{
  background:var(--on-navy);color:var(--navy);border-color:var(--on-navy)}

/* ---- the search panel behind the magnifier (#45) ----
   Its own layer above the drawer, because the magnifier sits outside the
   drawer and either can be open. A dialog rather than a dropdown: at 390
   a dropdown is a dropdown that covers the page anyway, and this way the
   same markup is the same thing at every width. */
.findbtn{
  background:none;color:var(--on-navy);border:1px solid #eef1ef55;border-radius:4px;
  width:44px;height:44px;flex:none;cursor:pointer;text-decoration:none;
  display:inline-flex;align-items:center;justify-content:center;
  transition:background .15s,color .15s,border-color .15s;
}
.findbtn:hover,.findbtn:focus-visible{background:var(--on-navy);color:var(--navy);border-color:var(--on-navy)}
.findbtn .icon{width:20px;height:20px}

/* THE ACCOUNT CORNER. #194.
   It takes .findbtn wholesale rather than restating it, so the three buttons
   in this row are one size by construction and not by three sets of numbers
   agreeing. What is left here is the parts a magnifier does not have: a
   summary has a marker, a details needs somewhere to hang its panel from, and
   there is a dot. */
.navacct{position:relative;flex:none}
.navacctbtn{position:relative}
.navacct > summary{list-style:none}
.navacct > summary::-webkit-details-marker{display:none}

/* Signed in, and readable without opening anything. Yellow because that is
   what this site uses for "live", the same as the sale card's rule and the
   pressed pill. Against the navy bar it measures 8.6:1, and it carries a ring
   of the bar's own colour so it reads as a badge rather than as a speck of
   dust on the screen. */
.navacct-dot{
  position:absolute;top:6px;right:6px;width:9px;height:9px;border-radius:50%;
  background:var(--yellow);box-shadow:0 0 0 2px var(--navy);
}
.navacctbtn:hover .navacct-dot,.navacct > summary:focus-visible .navacct-dot{box-shadow:0 0 0 2px var(--on-navy)}

/* The panel. Right-aligned to the button, because it is the last thing in the
   row and a left-aligned panel would run off the screen on a phone. Its own
   z-index sits above the bar; the bar does not clip it, which was checked
   rather than assumed, because a container with a z-index traps everything
   inside it at that container's place in the paint order. */
.navacct-menu{
  position:absolute;top:calc(100% + .5rem);right:0;z-index:var(--z-nav);
  min-width:15rem;padding:.35rem;
  background:var(--card);color:var(--ink);
  border:1px solid var(--line);border-radius:8px;
  box-shadow:0 18px 50px #16283e40;
}
.navacct-who{
  margin:0;padding:.55rem .7rem .5rem;font-size:var(--t-s);color:var(--ink-soft);
  border-bottom:1px solid var(--line);
}
.navacct-who b{color:var(--ink)}
.navacct-menu a{
  display:flex;align-items:center;min-height:44px;padding:0 .7rem;
  color:var(--ink);text-decoration:none;border-radius:5px;font-weight:700;
  font-size:var(--t-s);
}
.navacct-menu a:hover,.navacct-menu a:focus-visible{background:var(--paper-2)}
.navacct-out{border-top:1px solid var(--line);margin-top:.3rem;color:var(--ink-soft)}

/* THE WASH SLIDER, in the sale form. #164.
   A range and a preview, and the preview is the real card's own rules on a
   smaller box so what it shows is what gets drawn. The slider's minimum is the
   floor measured off the photograph, which is why there is no "too light"
   error anywhere: the control cannot express one. */
.owash{margin:var(--sp-s) 0 0}
.owash-row{display:flex;align-items:center;gap:.9rem;margin:.2rem 0 var(--sp-xs)}
.owash-slider{flex:1 1 auto;min-width:0;height:44px;accent-color:var(--navy);cursor:pointer}
.owash-num{flex:none;min-width:4ch;font-weight:700;font-variant-numeric:tabular-nums}
.owash-eye{max-width:26rem;min-height:9rem}
.owash-eye h3{font-size:var(--t-m)}

.find{
  position:fixed;inset:0;z-index:var(--z-find);
  background:#101824b8;
  display:flex;justify-content:center;
  padding:calc(var(--gut)*.75) var(--gut) var(--gut);
  overscroll-behavior:contain;
}
@supports (color:color-mix(in srgb,red 50%,white)){
  .find{background:color-mix(in srgb,var(--photo-ink) 72%,transparent)}
}
/* No backdrop-filter. It was here and it tore: Chromium re-samples the
   blurred backdrop when anything above it repaints, and the row highlight
   repaints on every pointer move, which drew diagonal banding across the
   whole panel. The scrim is already 72% opaque and the sheet on top of it
   is not transparent at all, so the blur was buying almost nothing. */
.find[hidden]{display:none}
/* the page underneath does not scroll while the sheet is open */
.find-open,.find-open body{overflow:hidden}

.find-sheet{
  width:min(100%,42rem);max-height:100%;align-self:flex-start;
  display:flex;flex-direction:column;
  background:var(--card);color:var(--ink);
  border:1px solid var(--line);border-radius:6px;
  /* The yellow rule the header already draws under itself. One property,
     and it is the difference between a white card that could belong to
     any site and a panel that belongs to this one. */
  border-top:3px solid var(--yellow);
  box-shadow:0 18px 50px #16283e40;overflow:hidden;
  /* its own stacking context, so a row lighting up under the pointer
     repaints inside the sheet. translateZ(0) was here as a layer hint and
     came out again: it computes to a flat 2D matrix, so it was promoting
     nothing and only saying it did. */
  isolation:isolate;
}
@media (min-width:700px){ .find-sheet{margin-top:6vh} }

/* It used to appear. An animation rather than a transition, because the
   panel goes from display:none to shown and a transition has nothing to
   run from. Short: this is in the way of somebody who came here to type. */
@keyframes find-scrim{from{opacity:0}to{opacity:1}}
@keyframes find-sheet{from{opacity:0;transform:translateY(-.55rem)}to{opacity:1;transform:none}}
.find:not([hidden]){animation:find-scrim .13s ease-out}
.find:not([hidden]) .find-sheet{animation:find-sheet .18s cubic-bezier(.2,.7,.3,1)}
@media (prefers-reduced-motion:reduce){
  .find:not([hidden]),.find:not([hidden]) .find-sheet{animation:none}
}

/* Proximity and whitespace, deliberately. This panel is the one place on the
   site somebody arrives having already decided to do something, so the field
   gets room around it rather than the tightest box it fits in. */
.find-top{display:flex;align-items:center;gap:.75rem;padding:.9rem 1.1rem;
  border-bottom:1px solid var(--line)}
.find-mark{display:flex;color:var(--ink-soft)}
.find-in{
  flex:1;min-width:0;border:0;background:none;color:var(--ink);
  font:inherit;font-size:var(--t-m);min-height:52px;padding:0 .2rem;
}
/* L12 in the session 22 audit wanted a focus ring back on this. Two tries at
   it both looked like a bug rather than an indicator: the sheet clips its
   own overflow for the rounded corners and this row is flush to the edge, so
   an outline on the row or on the bare input inside it is cut off on three
   sides and draws as a heavy dark bar across the divider. The audit rates it
   low on the grounds that the caret is a conforming indicator for a text
   field under WCAG 2.4.7, and M6's announcement layer was the real gap on
   this control. Left as it is on purpose, with a screenshot behind the
   decision rather than an argument. */
.find-in:focus{outline:none}
.find-in::placeholder{color:var(--ink-soft)}
/* the browser's own clear button, which sits under ours */
.find-in::-webkit-search-cancel-button{display:none}
.find-x{
  background:none;border:1px solid var(--line);border-radius:4px;color:var(--ink-soft);
  width:40px;height:40px;flex:none;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
}
.find-x:hover{background:var(--ink);color:var(--paper);border-color:var(--ink)}

.find-body{overflow:auto;overscroll-behavior:contain;padding:.9rem 0 1.1rem}

/* Two groups are further apart than two rows inside one group, and a hairline
   between them says so twice. That difference is the whole of why a list can
   be scanned instead of read. */
.find-grp{padding:.9rem 0}
.find-grp + .find-grp{border-top:1px solid var(--line)}
/* The same yellow rule the register on the shops page draws under a group
   letter. Four groups used to run into one another because the headings
   and the rows shared a left edge and nothing separated them. */
.find-grp h3{
  font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.09em;
  color:var(--ink-soft);margin:0 1.1rem .75rem;padding:0 0 .4rem;
  border-bottom:2px solid var(--yellow);
}
/* Wide enough for the group name to have its own column, which is what the
   shops page does with its letters, and the reason four groups read as four
   things there. Not a phone: 9rem out of a 390 screen is most of the row. */
@media (min-width:560px){
  .find-grp{
    display:grid;grid-template-columns:10rem 1fr;gap:0 1.4rem;align-items:start;
    padding:1rem 1.1rem 1rem 0;
  }
  .find-grp h3{margin:0 0 0 1.1rem;padding:.6rem 0 .4rem}
  .find-hit{padding:.7rem .9rem;border-radius:4px}
  .find-chips{padding:0}
}
.find-hit{
  display:flex;align-items:center;gap:1rem;
  padding:.75rem 1.1rem;text-decoration:none;color:inherit;
}
.find-hit-i{display:flex;color:var(--ink-soft);flex:none}
.find-hit-i .icon{width:1.15rem;height:1.15rem}
.find-hit-t{display:grid;min-width:0}
.find-hit b{font-family:var(--disp);font-weight:800;font-size:var(--t-b);line-height:1.2;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.find-hit span{font-size:var(--t-s);color:var(--ink-soft);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The letters that actually matched, in the site's yellow. Solid rather
   than a wash, and it carries its own navy: a translucent yellow over the
   card is a different colour in dark mode and the text on it stops being
   legible. Yellow with navy on it reads the same in both, which is why the
   fixed pair is the right answer here and not a shortcut. */
.find-hit mark{
  background:var(--yellow);color:var(--navy);
  border-radius:2px;padding:0 .08em;
}
/* one highlight for the pointer and the arrow keys both, so there is never
   a row that looks picked and a different row that Enter would open */
.find-hit.on{background:var(--paper-2)}
.find-hit.on .find-hit-i{color:var(--ink)}

.find-chips{display:flex;flex-wrap:wrap;gap:.5rem;padding:0 1.1rem}
.find-chip{
  font:inherit;font-size:var(--t-s);cursor:pointer;
  background:none;color:var(--ink-soft);
  border:1px solid var(--line);border-radius:999px;padding:.3rem .8rem;min-height:36px;
}
.find-chip:hover{background:var(--ink);color:var(--paper);border-color:var(--ink)}

.find-say{padding:1.1rem;color:var(--ink-soft);margin:0;max-width:46ch}
.find-say b{display:block;font-family:var(--disp);color:var(--ink)}

.find-foot{
  display:flex;flex-wrap:wrap;gap:.2rem 1rem;margin:0;
  padding:.5rem 1rem;border-top:1px solid var(--line);
  font-size:.72rem;color:var(--ink-soft);
}
.find-foot kbd{
  font:inherit;border:1px solid var(--line);border-radius:3px;
  padding:0 .3rem;margin-right:.25rem;
}
/* no keyboard hints on a phone, which has no keyboard and needs the room.
   Off by default and switched on at the breakpoint, never the other way
   round: this site is min-width only. */
.find-foot{display:none}
@media (min-width:700px){ .find-foot{display:flex} }

/* The panel hangs off the header, which is sticky and therefore already a
   containing block, so this needs no wrapper of its own. The insets repeat
   what .wrap does, because the panel has to line up with the page's right
   edge and not the window's. */
.disp{
  display:none;position:absolute;top:100%;z-index:var(--z-hdr);
  left:max(var(--gut),calc((100% - 1160px) / 2));
  right:max(var(--gut),calc((100% - 1160px) / 2));
  background:var(--card);color:var(--ink);
  border:1px solid var(--line);border-top:0;border-radius:0 0 4px 4px;
  padding:.85rem;box-shadow:0 12px 26px #16283e2e;
}
.disp.open{display:flex;flex-wrap:wrap;align-items:center;gap:.45rem .6rem}
/* On a phone the pills wrap, and a label that wraps with them ends up in the
   middle of a row belonging to the group above it. Each label takes its own
   line, so the panel reads as three labelled groups rather than one bag. */
.disp b{font-weight:700;font-size:.7rem;text-transform:uppercase;letter-spacing:.1em;
  color:var(--ink-soft);flex-basis:100%}
/* wide enough for the pills to sit on one line, so the panel stops spanning
   the page and becomes what it looks like: a drop-down under its button */
@media (min-width:800px){
  .disp{left:auto}
  .disp b{flex-basis:auto}
}
/* .abtn is one of the pill family. Its base, sizes, hover and pressed
   fill all live under BUTTONS AND PILLS below. */

/* ----------------------------------------------------------------
   HEADER : painted signboard. Navy stays navy in every theme.
   ---------------------------------------------------------------- */
/* Fixed, and it slides out of the way going down the page and comes back the
   moment you go up. Rob's call, taking Sembr's marketing nav as the shape.

   Fixed rather than sticky, and that is the fix as much as the behaviour:
   sticky was declared here and did nothing. Scrolled with a real wheel, the
   header left the top of the screen on every page of this site. Fixed cannot
   be quietly defeated by an ancestor the way sticky can.

   The transform is what moves it, not top, so it is one composited property
   and the paint does not thrash. Reduced motion gets a header that never
   hides at all rather than one that jumps. */
.hdr{
  position:fixed;top:0;left:0;right:0;z-index:var(--z-hdr);
  background:var(--navy);color:var(--on-navy);border-bottom:2px solid var(--yellow);
  transform:translateY(0);
  transition:transform 360ms cubic-bezier(.4,0,.2,1);
}
.hdr.is-hidden{transform:translateY(-100%)}
@media (prefers-reduced-motion:reduce){
  .hdr{transition:none}
  .hdr.is-hidden{transform:none}
}
/* Everything that parks under the header has to travel with it. The class
   goes on the html element rather than the header so a sticky bar four
   sections down the page can see it. Same 360ms and same curve, so the two
   move as one thing instead of one chasing the other. */
:root.hdr-away{--stick-top:0px}
.ctl,.filt,.rail,.reads,.prev,.grp-lab,.mon-lab{
  transition:top 360ms cubic-bezier(.4,0,.2,1);
}
@media (prefers-reduced-motion:reduce){
  :root.hdr-away{--stick-top:var(--hdr-h)}
  .ctl,.filt,.rail,.reads,.prev,.grp-lab,.mon-lab{transition:none}
}
/* The header is out of the flow now, so the page has to start below it. Plus
   2px, because --hdr-h is the min-height of the row inside and the yellow
   rule sits outside that: measured, the header occupies 62px and a 60px
   spacer left the weather band's first two pixels under it forever. */
.wp-site-blocks{padding-top:calc(var(--hdr-h) + 2px)}
/* Base is the narrowest phone, so the gaps start tight and open up rather
   than starting comfortable and overflowing. Measured at 320: the wordmark,
   the two tool buttons and the menu button wanted 402px inside 285px, and
   .hdr-end ran 100px past the right edge on every page. min-width:0 on the
   flex children is what lets the wordmark give ground instead of forcing it. */
.hdr-in{display:flex;align-items:center;justify-content:space-between;gap:.5rem;
  min-height:var(--hdr-h)}
.hdr-in > *{min-width:0}
@media (min-width:420px){.hdr-in{gap:1rem}}
/* the nav, the display-options button and the menu button travel together at
   the right-hand end, so .hdr-in stays two children and space-between works */
.hdr-end{display:flex;align-items:center;gap:.35rem;flex:none}
@media (min-width:420px){.hdr-end{gap:.6rem}}
/* Six links and two icon buttons at one gap read as eight things in a row,
   which is what made 1024 to 1180 feel packed. The tools are their own pair
   now. The hairline that separates them from the links is on .nav, down in
   the desktop block below, because .nav's own padding is set there and a
   later rule of equal specificity wins whatever this one says. */
@media (min-width:1024px){.hdr-end{gap:.5rem}}
/* "Downtown" and the Arnprior span are two flex items, and neither can
   shrink, so at 320 to 380 the name ran under the search button. Wrapping
   stacks them on two lines when the row is short and puts them back on one
   from about 390. */
.mark{font-family:var(--disp);font-weight:800;font-size:.94rem;line-height:1.05;
  text-decoration:none;display:flex;flex-wrap:wrap;align-content:center;min-height:44px}
@media (min-width:420px){.mark{font-size:1.12rem}}
.mark span{color:var(--yellow)}

/* the menu button, and the close button that replaces it when open */
.navbtn{
  font-family:var(--body);font-weight:700;font-size:var(--t-s);letter-spacing:.06em;
  text-transform:uppercase;background:none;color:var(--on-navy);border:1px solid #eef1ef55;
  border-radius:4px;padding:0 .8rem;min-width:44px;min-height:44px;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:.6rem;
}
/* The word goes on the narrowest screens and the icon carries the button.
   The button keeps its name for a screen reader either way: aria-label is on
   it in header.php, so this is only about what is drawn. */
.navbtn-t{display:none}
@media (min-width:420px){
  .navbtn{padding:0 1.15rem}
  .navbtn-t{display:inline}
}
.navbtn svg{width:15px;height:15px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round}

/* ---- the drawer ----
   Covers the whole screen including the header, so there is never a
   half-lit page behind it. Items sit at the bottom left, which is
   where a thumb already is on a phone. */
/* display:none can't transition, so the drawer stays display:flex always
   and hides through transform/visibility instead -- fixed positioning
   takes it out of flow either way, so an always-present drawer costs
   nothing when it is not open. No opacity: a fade is exactly as
   see-through as it sounds for the length of the transition, and this
   drawer already covers the whole screen specifically so nothing shows
   through it -- a fade would work against the one thing it is for.
   Transform only, so it is opaque throughout and reads as a physical
   drawer sliding down rather than a thing materializing. visibility flips
   at the end of the close transition (the 0s duration, .28s delay) rather
   than with it, so a hidden drawer still cannot be tabbed into or read by
   a screen reader one frame after it looks gone. Opening reverses the
   delay to zero, so it becomes interactive the instant it starts moving. */
.nav{
  display:flex;flex-direction:column;justify-content:flex-end;
  position:fixed;inset:0;z-index:var(--z-nav);
  background:var(--navy);color:var(--on-navy);
  padding:var(--gut);
  padding-bottom:max(var(--gut),env(safe-area-inset-bottom));
  height:100vh; height:100dvh;          /* dvh second: old browsers keep vh */
  overflow:auto;overscroll-behavior:contain;
  visibility:hidden;pointer-events:none;transform:translateY(-100%);
  transition:transform .32s cubic-bezier(.4,0,.2,1),visibility 0s linear .32s;
}
.nav.open{
  visibility:visible;pointer-events:auto;transform:translateY(0);
  transition:transform .32s cubic-bezier(.4,0,.2,1),visibility 0s linear 0s;
}
@media (prefers-reduced-motion:reduce){.nav{transition:visibility 0s linear .01s;transform:none}}
.nav::before{                       /* the yellow rule the header had */
  content:"";position:absolute;inset:0 0 auto;height:2px;background:var(--yellow);
}
/* Room between the items, not just around the words. A drawer on a phone is
   thumbed at speed and the rules were 60px apart, which is close enough that
   the wrong one gets hit. */
.nav a{
  display:flex;align-items:center;min-height:74px;text-decoration:none;
  font-family:var(--disp);font-weight:700;
  font-size:clamp(1.75rem,8vw,2.6rem);line-height:1.1;
  border-bottom:1px solid var(--on-navy-line);
}
.nav a:first-child{border-top:1px solid var(--on-navy-line)}
.nav a:hover,.nav a:focus-visible{color:var(--yellow)}
.nav a[aria-current]{color:var(--yellow)}
/* close sits top right of the drawer, thumb-reachable and obvious */
.navx{
  position:absolute;top:calc(var(--gut) - .3rem);right:calc(var(--gut) - .3rem);
  width:52px;height:52px;border-radius:50%;
  background:none;border:1px solid #eef1ef55;color:var(--on-navy);cursor:pointer;
  display:none;align-items:center;justify-content:center;
}
.nav.open .navx{display:flex}
.navx svg{width:20px;height:20px;stroke:currentColor;stroke-width:2.4;fill:none;stroke-linecap:round}
/* the hover is the shared on-navy fill, up with .dispbtn */
.nav-foot{margin-top:1.6rem;font-size:var(--t-s);color:#eef1ef9e}

/* Seven items plus the wordmark need more room than five did, so the drawer
   holds on until 1024 rather than 920. Adding the display-options button put
   the row 8px over its own wrap at exactly 1024; tightening the nav's
   tracking took it back under, so this stays at 1024 rather than becoming a
   fifteenth breakpoint. Checked at 1024, 1060, 1100 and up: no overflow and
   no horizontal scroll anywhere. */
@media (min-width:1024px){
  .navbtn,.navx{display:none}
  /* padding-right and the hairline are the group break between the pages
     and the two tools. On .nav rather than on a wrapper because .nav is
     already a 44px flex row that ends at the last link, so the rule is the
     right height without anything being added to the markup for it. */
  .nav,.nav.open{display:flex;flex-direction:row;align-items:center;
    position:static;inset:auto;height:auto;
    background:none;padding:0 1.1rem 0 0;margin-right:.85rem;
    border-right:1px solid var(--on-navy-line);
    gap:1.45rem;overflow:visible;z-index:auto;
    visibility:visible;pointer-events:auto;transform:none;transition:none}
  .nav::before{display:none}
  /* .02em, not .04em. Seven uppercase items and a button filled the row, and
     tracking is the cheapest thing in it to give back. Measured, halving it
     returns 17px across the whole nav, not the forty I guessed, which is
     still enough to let the row go back to fitting at 1024. */
  .nav a,.nav a:first-child{border:0;font-family:var(--body);font-weight:700;font-size:var(--t-s);
    letter-spacing:.02em;text-transform:uppercase;min-height:44px;white-space:nowrap}
  /* A real underline rather than a box-shadow along the bottom edge. The
     link's box is 44px tall for the thumb, so an inset shadow drew a thick
     yellow bar down at the bottom of that box with a gap between it and the
     word. This is the same underline the breadcrumbs use on hover, and it
     hugs the text. Not colour alone: colour is not a signal on its own for
     anyone who cannot see it. */
  /* Thickness and offset only. The colour, the solid rule and the underline
     itself come from the shared block up in "the yellow underline". This one
     is thinner and sits much lower on purpose: it is a nav marker under a
     word, not a rule under a heading. */
  .nav a[aria-current]{color:inherit;text-decoration-thickness:2px;
    text-underline-offset:.42em}
  .nav-foot{display:none}
}
@media (min-width:1180px){.nav,.nav.open{gap:1.9rem}}

/* ----------------------------------------------------------------
   BUTTONS AND PILLS
   ---------------------------------------------------------------- */
.cta{
  display:inline-flex;align-items:center;justify-content:center;min-height:52px;
  font-weight:700;font-size:var(--t-s);letter-spacing:.06em;text-transform:uppercase;
  text-decoration:none;padding:0 1.6rem;background:var(--ink);color:var(--paper);
  border:0;border-radius:6px;margin-top:1.6rem;transition:background .15s;
  font-family:var(--body);cursor:pointer;text-align:center;
}
/* the hover has to come from the ink token, not from a fixed navy.
   A hardcoded #0e1c2e is invisible in dark mode, where the button is
   already light with dark type on it, and it breaks high contrast
   the same way. Mixing toward the paper reads as pressed into the
   page and stays legible in all four themes. The literal is the
   fallback and is correct in the default one. */
.cta:hover{background:#0e1c2e}
.cta:active{background:#0a1524}
@supports (color:color-mix(in srgb,red 50%,white)){
  .cta:hover{background:color-mix(in srgb,var(--ink) 86%,var(--paper))}
  .cta:active{background:color-mix(in srgb,var(--ink) 74%,var(--paper))}
}
.cta.yellow{background:var(--yellow);color:var(--navy)}
.cta.yellow:hover{background:#e5a51f}
.cta.yellow:active{background:#d2960f}
/* the literals above are the fallback. Mixing toward the navy keeps the
   pressed look in all four themes, the same as the ink button does. The
   hero and the statement band used to each declare this again. */
@supports (color:color-mix(in srgb,red 50%,white)){
  .cta.yellow:hover{background:color-mix(in srgb,var(--yellow) 88%,var(--navy))}
  .cta.yellow:active{background:color-mix(in srgb,var(--yellow) 76%,var(--navy))}
}

/* ---- the pill family ----
   One base for every pill on the site: the display options (.abtn),
   the read-more link (.more), the category rows (.cats), the shop
   page's action row (.state .acts a) and the visit map's layer
   toggles (.lbtn). Each site keeps only its own sizes and spacing.
   This was five near-identical bases and six identical ink fills. */
.abtn,.more,.cats a,.cats button,.cats span,.state .acts a,.lbtn{
  font-family:var(--body);font-weight:700;font-size:var(--t-s);color:var(--ink);
  background:var(--card);border:1px solid var(--line);border-radius:999px;
  text-decoration:none;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
  transition:background .15s,color .15s,border-color .15s;
}
/* the display options run smaller than the pill's t-s */
.abtn{font-size:.82rem;min-height:44px;min-width:44px;padding:0 .9rem}
/* the two toggles darken their edge on hover and fill when pressed;
   the plain links fill straight away */
.abtn:hover,.lbtn:hover{border-color:var(--ink)}
.abtn[aria-pressed="true"],.lbtn[aria-pressed="true"],
.more:hover,.cats a:hover,.cats button:hover,
.cats [aria-current],.cats [aria-pressed="true"],
.state .acts a:hover{background:var(--ink);border-color:var(--ink);color:var(--paper)}

.more{letter-spacing:.05em;text-transform:uppercase;padding:0 1.25rem;min-height:46px;
  white-space:nowrap}

/* pill link rows: shop categories, filters */
.cats{display:flex;flex-wrap:wrap;gap:.6rem;margin-bottom:1.6rem}
.cats a,.cats button{letter-spacing:.03em;padding:0 1.15rem;min-height:44px}

/* segmented control: two or three exclusive options */
.seg{display:flex;border:1px solid var(--line);border-radius:999px;background:var(--card);overflow:hidden}
.seg button{
  font-family:var(--body);font-weight:700;font-size:var(--t-s);color:var(--ink);
  background:none;border:0;min-height:46px;padding:0 1.05rem;cursor:pointer;white-space:nowrap;
  transition:background .15s,color .15s;
}
.seg button + button{border-left:1px solid var(--line)}
.seg button[aria-pressed="true"]{background:var(--ink);color:var(--paper)}

/* ----------------------------------------------------------------
   SECTIONS
   ---------------------------------------------------------------- */
.band{padding:var(--band) 0}
.hd{display:flex;align-items:flex-end;justify-content:space-between;gap:1.2rem;flex-wrap:wrap;margin-bottom:1.9rem}
.hd h2{font-size:var(--t-l);max-width:20ch}
.hd .kick{display:block;margin-bottom:var(--sp-3xs)}

/* alternate band: notice-board grey */
.on{background:var(--paper-2);border-block:1px solid var(--line)}

/* breadcrumb line above a page title */
.crumb{font-size:var(--t-s);color:var(--ink-soft);padding:.9rem 0 0}
.crumb a{display:inline-flex;align-items:center;min-height:44px;color:var(--ink-soft);text-decoration:none}
.crumb a:hover{color:var(--ink)}
.crumb span[aria-hidden]{padding:0 .45rem}

/* ---- the yellow underline ----
   The one hover on this site: whatever names a place you can go gets
   the 3px yellow rule under it. Every hover underline lives in this
   list and the pages declare none of their own; it was thirteen
   identical blocks. The always-on underlines (.rail a.link, .p-site,
   .gate .aside a, .hrow.now dt) stay at their sites: same rule,
   different trigger, and two of them differ in offset. */
/* EVERY YELLOW UNDERLINE ON THE SITE, HOVER OR ALWAYS ON, IN ONE PLACE.

   Rob spotted the tell: a small heading cut its yellow line around the
   descenders and a big one did not. The reason was that the hover list here
   had been given text-decoration-skip-ink:none and the five always-on rules
   had not, because each of those carried its own copy of the same four
   declarations at its own site. One underline, two behaviours, sometimes on
   the same page. The file's own comment already said this started as thirteen
   identical blocks; it was five more than anybody thought.

   The rule breaks around descenders, with clear space either side of the
   stem. That is skip-ink on its default, and it is Rob's call: a rule driven
   straight through a y or a g looks like an overlap, and the gap around the
   stem is what makes it look drawn on purpose. The first fix here went the
   other way, forcing the rule solid, which made every heading consistent in
   the wrong direction.

   Offset is the only thing that legitimately differs per site, so it is the
   only thing left at the call sites below. */
.crumb a:hover,.ev:hover h3,.qa > summary:hover h3,
a.item:hover h3,.item a:hover h3,.tries a:hover b,
.row:hover h3,.door:hover h2,.tn:hover h2,
.eb:hover h3,.shop-offer:hover h3,.kind:hover h3,
.note:hover h3,.dr:hover h2,.names li a:hover,
.nav a[aria-current],.hrow.now dt,.rail a.link,.p-site,.gate .aside a{
  text-decoration:underline;text-decoration-color:var(--yellow);
  text-decoration-thickness:3px;text-underline-offset:.2em;
}

/* The display headings pull the rule in closer. At a 48px headline .2em put
   it 9.6px below its own words and 1px above the next line, so it read as
   belonging to the line underneath. A rule should sit nearer the thing it
   underlines than the thing it does not. Body-size links keep .2em above,
   where .14em would sit on the baseline. */
.ev:hover h3,.qa > summary:hover h3,a.item:hover h3,.item a:hover h3,
.row:hover h3,.door:hover h2,.tn:hover h2,.eb:hover h3,
.shop-offer:hover h3,.kind:hover h3,.note:hover h3,.dr:hover h2{
  text-underline-offset:.14em;
}

/* ROOM FOR THAT UNDERLINE TO LAND IN.

   Measured, session 35, on a three-line offer headline Rob sent: the rule
   drawn under line one was ending 1.8px BELOW the bottom of its own line box,
   which puts it inside line two. On a one-line heading nothing shows. On a
   multi-line one the headline looks struck through.

   The arithmetic. The baseline sits about 0.8em below the top of the text,
   the rule starts 0.2em under that and is 3px thick, and half the leading
   sits above the text. Solve for the line box that contains all of it and
   everything cancels to: line-height must be at least font-size + 6px. The
   +8px here is that minimum with 2px to spare.

   Written as calc rather than a ratio because the answer is a fixed number of
   pixels, not a proportion: a 48px headline needs 1.13, a 20px one needs 1.3,
   and a single ratio cannot be right for both. Every heading in the list
   above failed this at both 390 and 1280, by 3.6px to 4.8px. The only one
   that passed was the breadcrumb, which runs at body leading.

   The leading goes on the heading, never on the hover: changing line-height
   on hover would reflow the card under the pointer. */
.ev h3,.qa > summary h3,a.item h3,.item a h3,.tries a b,
.row h3,.door h2,.tn h2,.eb h3,.shop-offer h3,.kind h3,
.note h3,.dr h2,.names li a{line-height:calc(1em + 8px)}

/* ----------------------------------------------------------------
   PAGE HEADS

   Nine of these, one per page design, each with its own class and
   its own type scale, all doing the same job: a crumb, a kicker, a
   headline and sometimes a lede. The nine repeated the same three
   rules with the numbers nudged, so changing the crumb meant
   editing nine places and finding out later which one was missed.

   Three grounds, because three of them really are different: paper,
   navy, and a photograph. Everything above the ground is written
   once, here.

   Four of the nine are gone entirely: the shops, news, one notice
   and about pages are the plain head with a modifier. What is left
   on .season, .post, .win, .vhead and .hero is only the part that is
   theirs: the market strip, the date set big, the photograph and its
   scrim, and the home page's taller poster.
   ---------------------------------------------------------------- */
.phead .kick{display:block;margin:var(--sp-3xs) 0}
.phead h1{font-size:clamp(2.4rem,1.7rem + 3.2vw,4.4rem)}
.phead h1 em{font-style:normal;color:var(--ink-soft)}
.phead .lede{font-size:var(--t-m);line-height:1.45;max-width:34ch;margin:.9rem 0 0}

/* ---- on paper ----
   The navy and photograph heads put their padding on an inner
   element, because they have a background to bleed and the inner is
   what holds it off the edge. A paper head has no such thing, so the
   padding sits on the head itself. */
.phead-plain{padding:calc(var(--band)*.7) 0 calc(var(--band)*.5)}
.phead-plain .lede{color:var(--ink-soft)}
.phead-tint{background:var(--paper-2);border-bottom:1px solid var(--line)}
.phead-rule{border-bottom:1px solid var(--line)}
/* one notice, at reading measure, with its body starting straight after */
.phead-narrow{width:min(100% - (var(--gut)*2),42rem);margin-inline:auto;padding-bottom:0}
.phead-sm h1{font-size:clamp(2.1rem,1.55rem + 2.6vw,3.5rem);max-width:20ch}

/* ---- on navy ----
   The join head and the members head are this same signboard, so the
   band itself is written once. Their own type lives at their sites. */
.phead-navy,.jhead,.ihead{background:var(--navy);color:var(--on-navy);border-bottom:2px solid var(--yellow)}
.phead-navy .crumb,.phead-navy .crumb a{color:#eef1efb5}
.phead-navy .crumb a:hover{color:#fff}
.phead-navy .kick{color:var(--yellow)}
.phead-navy h1{color:#fff}
@supports (color:color-mix(in srgb,red 50%,white)){
  .phead-navy .crumb,.phead-navy .crumb a{color:color-mix(in srgb,var(--on-navy) 71%,transparent)}
}

/* ---- over a photograph ----
   The shadows are not decoration. Six photographs by six people have
   six exposures, and a white headline on a bright sky is the one
   thing a scrim alone cannot promise. */
.phead-photo{position:relative;background:var(--photo-ink);color:var(--on-navy);overflow:clip}
.phead-photo .crumb,.phead-photo .crumb a{color:#eef1efc4}
.phead-photo .crumb a:hover{color:#fff}
.phead-photo .kick{color:var(--yellow);text-shadow:0 1px 4px #101824cc}
.phead-photo h1{color:#fff;text-shadow:0 2px 8px #101824e6,0 1px 2px var(--photo-ink)}
.phead-photo .lede{color:#eef1efe6;text-shadow:0 1px 4px #101824cc}
/* no featured image: the shadows come off, because over flat ink they
   read as a smudge rather than as depth */
.phead-bare .kick,.phead-bare h1,.phead-bare .lede{text-shadow:none}

/* ----------------------------------------------------------------
   DIRECTORY ROWS : the register
   ---------------------------------------------------------------- */

/* ----------------------------------------------------------------
   EVENT ROWS : the notice board
   ---------------------------------------------------------------- */
.ev{
  display:grid;grid-template-columns:auto 1fr;gap:var(--sp-2xs) 1.2rem;align-items:baseline;
  border-top:1px solid var(--line);padding:1.35rem 0;
  text-decoration:none;
}
.ev-date{
  font-family:var(--disp);font-weight:800;font-size:var(--t-m);line-height:1;
  white-space:nowrap;
}
.ev-date small{display:block;font-family:var(--body);font-weight:700;font-size:var(--t-s);
  text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);margin-top:var(--sp-2xs)}
.ev h3{font-size:var(--t-m)}
.ev .d{grid-column:2;color:var(--ink-soft);font-size:var(--t-b);margin:0;max-width:58ch}
.ev .flag{grid-column:2;font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.08em;color:var(--ink-soft);justify-self:start}
@media (min-width:800px){.ev{grid-template-columns:8.5rem 1fr}}

/* ----------------------------------------------------------------
   Q & A : native disclosure, no JavaScript
   A <details> is keyboard accessible, findable by browser search in
   modern browsers, and works with JS off. It replaces the old
   always-open block and the hand-rolled toggles.
   ---------------------------------------------------------------- */
.faq{border-top:1px solid var(--line)}
.qa{border-bottom:1px solid var(--line)}
.qa > summary{
  list-style:none;cursor:pointer;display:grid;position:relative;
  grid-template-columns:1fr 2rem;gap:1rem;align-items:start;
  padding:1.25rem 0;min-height:44px;
}
.qa > summary::-webkit-details-marker{display:none}
.qa > summary :is(h2,h3){font-size:var(--t-m);max-width:30ch}
/* The plus that turns into a minus.

   The prototype drew it with a <span class="mk"> inside the summary. That
   does not survive an editor: wpautop wraps a lone span in a <p> and the
   marker drops out of the summary's grid. Drawn from the summary's own
   pseudo-elements instead, so the accordion works from the simplest markup
   anybody could type, which is <summary><h3>Question</h3></summary>. */
.qa > summary::before,.qa > summary::after{
  content:"";position:absolute;right:0;top:50%;width:20px;height:2px;
  background:var(--ink);transform:translateY(-50%);transition:transform .18s ease;
}
.qa > summary::after{transform:translateY(-50%) rotate(90deg)}
.qa[open] > summary::after{transform:translateY(-50%) rotate(0)}
.qa .a{padding:0 0 1.35rem;max-width:62ch}
.qa .a p{margin:0 0 .8em}
.qa .a p:last-child{margin:0}
/* The answer used to sit in a second column on a wide screen, pulled
   level with the question by a negative margin. It cost a dead left
   column, squeezed the answer to about forty characters, and once the
   disclosure got its slide the negative margin was clipped by the
   overflow. The answer now runs under its question at every width,
   which is also what it does on a phone. Give the question more room
   before it wraps and that is the whole difference. */
@media (min-width:1000px){
  .qa > summary h3{max-width:44ch}
}

/* the answer slides rather than snapping. Height cannot be animated
   from 0 to auto without interpolate-size, so the whole thing sits
   behind @supports: browsers without it open instantly, which is
   what they did before, and nothing is unreadable either way. */
@supports (interpolate-size:allow-keywords){
  :root{interpolate-size:allow-keywords}
  .qa::details-content{
    block-size:0;overflow:hidden;
    transition:block-size .3s ease,content-visibility .3s allow-discrete;
  }
  .qa[open]::details-content{block-size:auto}
}

@media (prefers-reduced-motion:reduce){
  /* the marker is drawn on the summary itself now; this rule named the
     .mk span it used to live in, so reduced motion stopped reaching it */
  .qa > summary::before,.qa > summary::after{transition:none}
  .qa::details-content{transition:none}
}

/* ----------------------------------------------------------------
   NEWS ITEMS
   ---------------------------------------------------------------- */
.news{display:grid;gap:1.2rem}
@media (min-width:800px){.news{grid-template-columns:1fr 1fr;gap:2.5rem}}
@media (min-width:1000px){.news{grid-template-columns:repeat(3,1fr)}}
.item{border-top:1px solid var(--line);padding:1.2rem 0 1.35rem}
/* the date label is shared with the pinned notice on the news page */
.item time,.pin-note time{font-weight:700;font-size:var(--t-s);text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft)}
.item h3{font-size:var(--t-m);margin:.55rem 0 .5rem}
.item p{color:var(--ink-soft);margin:0}
/* the whole card is often the link, so kill the underline on the
   element itself as well as on any anchor inside it */
a.item,.item a{text-decoration:none}

/* ----------------------------------------------------------------
   THE STATEMENT BAND

   Says plainly what something is, and backs it with a short ledger
   of facts. The contact page uses it to say what the group is not,
   because everyone assumes Chamber of Commerce. The home page uses
   it to ask the shops to get listed.

   One band, one function that prints it, dta_statement(). The two
   had drifted to two layouts, two ledgers and two type scales.

   Full bleed, on the photograph ink rather than the navy. The join
   band was inset for a while because it was navy and the footer is
   navy, so the bottom of the home page was fifteen hundred pixels of
   one colour. A different dark solves that without giving up the
   full width.

   It takes a photograph if the page has a featured image and reads
   as a flat band if it does not, which is what both pages do today.
   ---------------------------------------------------------------- */
.statement{position:relative;background:var(--photo-ink);color:var(--on-navy);overflow:clip}
.statement-in{position:relative;padding:var(--band) 0;max-width:44rem}

.statement h2{color:#fff;font-size:var(--t-l);max-width:18ch;text-shadow:0 2px 6px #101824d9}
.statement h2 em{font-style:normal;color:var(--yellow)}
.statement p{color:#eef1efe0;max-width:46ch;margin:1rem 0 0;text-shadow:0 1px 3px #101824cc}
.statement .cta{margin-top:1.6rem}

/* the photograph, and the wash that guarantees a value behind the text */
.statement-bg{position:absolute;inset:0}
.statement-bg img{width:100%;height:100%;object-fit:cover;object-position:center 45%}
.statement-bg::after{content:"";position:absolute;inset:0;background:
  linear-gradient(82deg,#101824f0 0%,#101824cc 46%,#10182466 78%,#10182422 100%)}
@supports (color:color-mix(in srgb,red,blue)){
  .statement-bg::after{background:linear-gradient(82deg,
    color-mix(in srgb,var(--photo-ink) 94%,transparent) 0%,
    color-mix(in srgb,var(--photo-ink) 80%,transparent) 46%,
    color-mix(in srgb,var(--photo-ink) 40%,transparent) 78%,
    color-mix(in srgb,var(--photo-ink) 13%,transparent) 100%)}
}
/* High contrast drops the photograph rather than dimming it further. A
   picture behind text is the first thing to go when somebody has asked
   for contrast. */
:root[data-hc="1"] .statement-bg img{display:none}
:root[data-hc="1"] .statement-bg::after{background:var(--photo-ink)}
.statement-cr{position:absolute;right:.9rem;bottom:.6rem;font-size:.7rem;color:#eef1ef8c}
/* No featured image: the text drops its shadows, which over flat ink
   read as a smudge rather than as depth. */
.statement-bare .kick,.statement-bare h2,.statement-bare p{text-shadow:none}

/* ---- the ledger ----
   A short list of plain facts under a heading. It was written three times,
   once for the join board, once for the contact page's band and once for
   the detail pages, with three different column widths and the same
   everything else. One list now; the dark bands change two colours. */
.facts{margin:0;border-top:1px solid var(--line)}
.facts .drow{display:grid;grid-template-columns:6.8rem 1fr;border-bottom:1px solid var(--line);align-items:baseline}
@media (min-width:620px){.facts .drow{grid-template-columns:8.5rem 1fr}}
.facts .drow:last-child{border-bottom:0}
.facts dt{font-weight:700;font-size:var(--t-s);text-transform:uppercase;letter-spacing:.08em;padding:.85rem 0}
.facts dd{margin:0;font-size:var(--t-b);padding:.85rem 0}
/* on navy or over a photograph */
.facts-dark{border-top-color:var(--on-navy-line);margin-top:1.8rem}
.facts-dark .drow{border-bottom-color:var(--on-navy-line)}
.facts-dark dt{color:var(--yellow)}
/* a value that is only a link is a tap target, not prose. Give it the
   full 44px without pushing the row open. */
.facts dd > a:only-child{display:inline-flex;align-items:center;min-height:44px;margin:-.7rem 0}

/* a heading that introduces a bordered list needs its own air, or it
   sits on the rule. Measured: this was at -1px before. */
.listhead{font-size:var(--t-m);margin:0 0 .95rem}

/* ----------------------------------------------------------------
   THE YELLOW RULE : two widths, and only two
   A short note beside the text gets 4px. A block that carries its
   own headline gets 6px, because a hairline beside 3rem type reads
   as an accident. Four separate widths had grown across the pages
   for the same three jobs; these are the ones that survived.
   ---------------------------------------------------------------- */

/* a small aside: a caveat, a "we are still checking this" */
.sidenote{
  font-size:var(--t-s);color:var(--ink-soft);
  border-left:4px solid var(--yellow);
  padding:.35rem 0 .35rem 1.05rem;margin:1.5rem 0 0;max-width:58ch;
}
.sidenote p{margin:0 0 .55em}
.sidenote p:last-child{margin:0}

/* a tinted panel that summarises what is under it */
.panel{
  background:var(--paper-2);border-left:4px solid var(--yellow);
  padding:1.3rem clamp(1.15rem,4vw,1.5rem) 1.4rem;margin:2rem 0 2.4rem;
}

/* a block whose own headline is the point */
.feature-rule{border-left:6px solid var(--yellow);padding-left:clamp(1.1rem,3vw,2.2rem)}

/* ----------------------------------------------------------------
   THE LOGO MARK
   A business's sign or logo, wherever it appears on a public page.
   One box, one set of rules, so eighteen logos of eighteen different
   shapes and qualities still line up. Contained, never cropped, on a
   neutral tile so a transparent PNG and a white JPG look alike.
   Falls back to the first letter of the name when there is no file,
   which there often will not be.
   ---------------------------------------------------------------- */
.logo-mark{
  width:var(--mark-size,56px);height:var(--mark-size,56px);flex:none;
  border:1px solid var(--line);border-radius:4px;background:var(--on-navy);
  display:grid;place-items:center;overflow:hidden;
  font-family:var(--disp);font-weight:800;
  color:color-mix(in srgb,var(--navy) 75%,var(--on-navy));
  font-size:calc(var(--mark-size,56px) * .5);line-height:1;
}
/* #166. Artwork drawn in white is invisible on the light plate, measured at
   about 1.1:1, and the tile just looks broken. The shop picks light or dark
   and the default comes from dta_logo_ink(), which measures the file. The
   navy is the same fixed pair as the plate above: both sides of this are
   fixed-palette colours, so neither flips with the theme and the contrast
   holds in light and dark. */
.logo-mark[data-ground="dark"]{background:var(--navy);border-color:#eef1ef4a}
.logo-mark img{width:100%;height:100%;object-fit:contain;padding:6px}
/* #181. A logo you cannot see through arrived with a background of its own, so
   it takes the tile edge to edge and the plate behind it never shows. The 6px
   above is breathing room for artwork floating on a plate WE chose; on a file
   that brought its own it reads as a picture shrunk inside a box for no
   reason. Still contained and still never cropped, so a wide one letterboxes
   rather than losing its ends.

   Which of the three grounds a file gets is measured, not asked: the alpha
   channel is walked and the mere presence of one does not count, because a PNG
   saved out of anything carries a channel whether it uses it or not. See
   dta_logo_ground(). */
.logo-mark[data-ground="own"] img{padding:0}
.logo-mark.lg{--mark-size:clamp(72px,11vw,104px)}
/* A FIXED LIGHT PLATE IN BOTH THEMES, not the surface colour. This tile used
   to take var(--card), which is white in light and navy in dark, and the
   comment above promised that a transparent PNG and a white JPG would look
   alike on it. In dark mode they did not: a shop whose logo is a white JPG
   got a white square, the shop next to it with a transparent PNG got navy
   with its dark ink vanishing into it, and the same shop looked like two
   different shops depending on the reader's theme.

   Every platform that hosts other people's logos landed here for the same
   reason. Google Play refuses transparent icons because they take the colour
   of whatever is behind them, Apple fills alpha with black, and LinkedIn
   plates transparent company logos onto white in dark mode. A shop has one
   logo, made for white paper, and there is no second file to ask them for.

   --on-navy is that plate and it already existed: the rule below this one has
   been using it over the photograph bands since long before dark mode, for
   exactly this reason. It is now the rule rather than the exception, so the
   line below only has the border left to say. The fallback letter takes a
   fixed navy mixed back toward the plate, because --ink-soft is light in dark
   mode and was already unreadable on the tiles over photographs. */
.win .logo-mark{border-color:#eef1ef4a}

/* ----------------------------------------------------------------
   FORMS : big targets, plain labels, one column
   ---------------------------------------------------------------- */
/* wp_login_form prints its own p.login-username and p.login-password
   boxes and nothing can put .field or .in on them from outside, so
   core's class names ride along here rather than getting a wholesale
   copy of these rules down at the members gate. */
/* A CONTAINER THAT SETS A GAP OWNS THE SPACING INSIDE IT.
   Measured, session 35. .field says gap:.4rem, which is 6px, and the help
   text under a label is a <p>, and p carries margin:0 0 1em from the base
   rules. So the real distance from the help line to the box below it was
   6 + 15 = 21px, a number nobody chose and nobody wrote down. Between two
   whole questions it was 20px. Two different questions sat closer together
   than the parts of one question, which is proximity telling the eye the
   opposite of the truth, and it is why the forms read as crowded.

   The same doubling hit the directory rows: a.row sets gap:2px and p.meta
   brought 15px, so the address line sat 17px under the shop name instead of
   against it.

   The fix is the rule, not the numbers: inside anything that manages its own
   spacing with gap, children do not bring their own block margins. Found with
   a probe that walked every grid and column flex on ten pages looking for a
   child with a margin; it found seven, and these are the ones that showed. */
.field > *,.gate .login-username > *,.gate .login-password > *,
.row > *,.note > *,.p-row > *{margin-block:0}

/* margin-bottom is --sp-s, the step above the one used inside a field, so
   the gap between two questions is always bigger than any gap within one.
   It was a flat 1.25rem, which at a wide screen lost that race. */
.field,.gate .login-username,.gate .login-password{display:grid;grid-template-columns:minmax(0,1fr);gap:.4rem;margin:0 0 var(--sp-s)}
/* A display value silently beats the hidden attribute, and .field sets one.
   Without this line, hiding a field with hidden does nothing and says nothing.
   Fifth time on this project. */
.field[hidden]{display:none}
/* A .field holding prose rather than a question. Its rows are paragraphs, and
   paragraphs want reading space, not the 6px that separates a label from its
   box. This exists because the join form had three stacked help paragraphs
   carrying inline margins of .6rem, hand-tuning around the base p margin that
   the gap was already fighting. An inline style is the one thing no rule can
   beat, so it is the one place a spacing scale cannot reach. */
.fieldnote{gap:var(--sp-2xs)}
/* The small-caps label. Also the shop page's hours ledger, the join
   form's day column and the forecast drawer's day names, which are
   the same label on other ledgers: one rule, not five. */
.field label,.field .lab,.gate .login-username label,.gate .login-password label,
.hrow dt,.wday,.nowfc-when{font-weight:700;font-size:var(--t-s);text-transform:uppercase;letter-spacing:.08em}
.field .help{font-size:var(--t-s);color:var(--ink-soft)}
.in,.gate .login-username input,.gate .login-password input{
  font-family:var(--body);font-size:var(--t-b);color:var(--ink);
  background:var(--card);border:1px solid var(--ink-soft);border-radius:4px;
  padding:.6rem .9rem;min-height:52px;width:100%;
}
textarea.in{min-height:9rem;line-height:1.55;resize:vertical}
/* grows with what is typed, where the browser can do it */
@supports (field-sizing:content){
  textarea.in{field-sizing:content;max-height:26rem}
}
select.in{cursor:pointer}

/* SHOW WHAT YOU TYPED. #185, and pw.js is what puts this in the page.
   Inside the box rather than under it, because under it the button is one
   more thing in a column of questions and reads as one of them.

   THE ROOM ON THE RIGHT IS THE WHOLE RULE. Without it the last characters of
   a long password type underneath the button, which was measured rather than
   noticed: the probe read 14px of padding where the button starts 326px in.
   The gate's own box is styled as `.gate .login-password input`, two classes
   and an element, so a plain `.pw-wrap input` loses to it however late it
   sits in the file. Hence the second selector. Specificity, not order.

   The button is a full 44px tall like every other target here, and it is
   underlined text rather than an eye, because an eye means "show" to people
   who already know it means show. */
.pw-wrap{position:relative;display:block}
.pw-wrap > input,.gate .login-password .pw-wrap > input{padding-right:5.5rem}
.pw-see{
  position:absolute;right:.35rem;top:50%;transform:translateY(-50%);
  min-height:44px;padding:0 .75rem;border:0;background:none;cursor:pointer;
  font:inherit;font-size:var(--t-s);font-weight:700;color:var(--ink-soft);
  text-decoration:underline;text-underline-offset:3px;
}
.pw-see:hover{color:var(--ink)}
.pw-see:focus-visible{outline:3px solid var(--yellow);outline-offset:2px;border-radius:2px}
.in::placeholder{color:var(--ink-soft);opacity:.75}
.in:focus-visible{border-color:var(--ink)}
/* Chrome paints an autofilled field in its own colours and its own font, and
   its rules carry !important, so the only way in is the inset shadow for the
   background and text-fill-color for the letters. Without this a saved
   password turns the field pale blue on a page that has no blue in it.

   What is deliberately NOT here is a font rule. Before the first click lands
   anywhere on the document, Chrome paints a filled value at roughly its own
   default input size and the type visibly grows once you click. That is not
   reachable from CSS: measured in DevTools, the computed font on this input
   is Atkinson at 18.832px both before and after the click, identical, while
   the paint differs. Longhands and the font shorthand were both tried and
   both did nothing, which is what a paint-time override looks like from the
   cascade. Other sites carry the same override and do not show it because
   they style inputs near Chrome's own ~13px; this one asks for 18.8px, so
   the same gap is visible. Leave it. */
.in:-webkit-autofill,
.gate .login-username input:-webkit-autofill,
.gate .login-password input:-webkit-autofill{
  -webkit-text-fill-color:var(--ink);
  -webkit-box-shadow:0 0 0 100px var(--card) inset;
  box-shadow:0 0 0 100px var(--card) inset;
  caret-color:var(--ink);
}
.in:-webkit-autofill:focus-visible,
.gate .login-username input:-webkit-autofill:focus-visible,
.gate .login-password input:-webkit-autofill:focus-visible{border-color:var(--ink)}

/* ----------------------------------------------------------------
   THE MAP : Leaflet, theme-aware tiles, pulsing pins
   ---------------------------------------------------------------- */
/* isolation opens a stacking context without needing a position, so
   Leaflet's hard-coded panes (400) and controls (800) are measured
   against each other inside here and never against the header. The
   positioned z-index is the fallback for anything without isolation. */
.lmap{
  height:clamp(340px,44vw,500px);border-radius:4px;background:var(--paper-2);
  position:relative;z-index:var(--z-map);isolation:isolate;
}
.lmap .leaflet-container{font-family:var(--body)}
.pin{display:block;width:15px;height:15px;background:var(--yellow);
  border:2px solid var(--ink);border-radius:50%;position:relative}
.pin .ring{position:absolute;inset:-7px;border:2px solid var(--yellow);
  border-radius:50%;animation:ping 2.8s ease-out infinite;opacity:0}
@keyframes ping{0%{transform:scale(.5);opacity:.9}70%{transform:scale(1.15);opacity:0}100%{opacity:0}}
.leaflet-popup-content-wrapper{border-radius:4px;color:var(--navy)}
.leaflet-popup-content b{font-family:var(--disp);font-size:1.05rem}
.leaflet-popup-content span{color:#43536a;font-size:.85rem;display:block;margin-top:.15rem}
.leaflet-popup-content .pop-name{display:inline-block;font-family:var(--disp);font-weight:800;
  font-size:1.05rem;color:var(--navy);text-decoration:underline;text-decoration-color:#f7b32b;
  text-decoration-thickness:3px;text-underline-offset:.22em;padding:.1rem 0 .15rem}
.leaflet-popup-content .pop-name:hover{text-decoration-color:var(--navy)}
.map-cap{display:flex;justify-content:space-between;gap:.4rem 1rem;flex-wrap:wrap;
  border-top:1px solid var(--line);padding:.65rem 0;font-size:var(--t-s);color:var(--ink-soft)}
.map-cap b{color:var(--ink)}
/* the OSM and CARTO credit, moved off the map and into the caption.
   Required by ODbL and by CARTO's terms; it is not decoration. */
.map-credline,.map-dir{margin:.5rem 0 1rem}
.map-cred{font-size:.78rem;color:var(--ink-soft);opacity:.85}
.map-cred a{color:inherit;text-decoration:underline;text-underline-offset:.15em}
.map-cred a:hover{color:var(--ink)}

/* album mount: a print under four flat black corners. The one licensed
   piece of skeuomorphism; do not re-embellish the corners. */
.mount{position:relative;display:inline-block;line-height:0}
.mount img{display:block;max-width:100%;width:auto;height:auto;
  border:10px solid #f8f8f4;box-shadow:0 3px 14px #0a0f1440}
.pc{position:absolute;width:clamp(30px,3.8vw,44px);height:clamp(30px,3.8vw,44px);
  overflow:visible;filter:drop-shadow(1px 1px 2px #0a0f1440)}
.pc path{fill:#12161d}
:root[data-thr="dark"] .pc path{fill:#0b0e13}
.pc.tl{top:-6px;left:-6px}
.pc.tr{top:-6px;right:-6px;transform:rotate(90deg)}
.pc.br{bottom:-6px;right:-6px;transform:rotate(180deg)}
.pc.bl{bottom:-6px;left:-6px;transform:rotate(270deg)}

/* carousel / pager arrows */
.arrb{background:none;border:0;cursor:pointer;padding:0;color:var(--ink);
  font-size:1.7rem;line-height:1;min-width:44px;min-height:44px;
  display:inline-flex;align-items:center;justify-content:center;transition:transform .15s}
.arrb:hover{transform:translateX(2px)}
.arrb.prev:hover{transform:translateX(-2px)}
.arrb.pause{font-size:var(--t-s);font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  min-width:auto;padding-inline:.4rem;margin-left:auto}
.arrb.pause:hover{transform:none}

/* ----------------------------------------------------------------
   FOOTER
   ---------------------------------------------------------------- */
.ft{background:var(--navy);color:var(--on-navy);padding:var(--band) 0 2rem;border-top:1px solid var(--on-navy-line)}
.ft-grid{display:grid;gap:2.2rem}
@media (min-width:1000px){.ft-grid{grid-template-columns:1.3fr 1fr 1fr}}
.ft h2{font-size:clamp(2.2rem,8vw,4.6rem);line-height:1;margin-bottom:2rem}
.ft h2 span{color:var(--yellow)}
.ft h3{font-weight:700;font-size:var(--t-s);text-transform:uppercase;letter-spacing:.11em;
  color:var(--yellow);margin:0 0 .6rem;font-family:var(--body)}
.ft a{display:flex;align-items:center;min-height:44px;text-decoration:none;color:var(--on-navy)}
/* The way in. It goes first in the column and it is the only footer link that
   opens a door rather than a page. No underline and no weight on it: Rob's
   call, session 36, after seeing it with both. Its position is what marks it. */
.ft a:hover{color:var(--yellow)}
.ft address{font-style:normal;color:#eef1efc9;line-height:1.7}
.ft-note{margin-top:1rem;color:#eef1efc9}
@supports (color:color-mix(in srgb,red 50%,white)){
  .ft address,.ft-note{color:color-mix(in srgb,var(--on-navy) 79%,transparent)}
}
.fine{border-top:1px solid var(--on-navy-line);margin-top:2.4rem;padding-top:1.3rem;
  font-size:var(--t-s);color:#eef1efb0;line-height:1.8}
/* Centred on a desktop only. Rob's call. On a phone these two lines wrap
   to four and centred wrapped text is harder to read than ragged right,
   so the small screen keeps the left edge everything else on the page
   is lined up on. */
@media (min-width:1000px){
  .fine{text-align:center}
  .legal{justify-content:center;text-align:center}
}

/* ---- back to top ----
   Under the legal line, centred, quiet. A link and not a button so it
   works with scripts off, and it is the last thing in the footer because
   that is where somebody who has read to the bottom is. */
.totop{display:flex;width:fit-content;align-items:center;gap:.5rem;
  margin:1.6rem auto 0;min-height:44px;padding:0 1rem;
  font-size:var(--t-s);font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:#eef1efb5;text-decoration:none;border:1px solid var(--on-navy-line);
  border-radius:999px;transition:color .15s,border-color .15s}
.totop:hover,.totop:focus-visible{color:#fff;border-color:#eef1ef6b}
.totop svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2.2;
  stroke-linecap:round;stroke-linejoin:round}
@supports (color:color-mix(in srgb,red 50%,white)){
  .totop{color:color-mix(in srgb,var(--on-navy) 71%,transparent)}
}

.legal{display:flex;flex-wrap:wrap;gap:.4rem 2rem;align-items:center;
  border-top:1px solid var(--on-navy-line);margin-top:1.4rem;padding-top:1.1rem;
  font-size:.75rem;color:#eef1efb0}
.legal nav{display:flex;gap:1.6rem}
.legal a{display:inline-flex;align-items:center;min-height:44px;color:#eef1efd8;text-decoration:none}
.legal a:hover{color:var(--yellow)}

/* ----------------------------------------------------------------
   ERROR AND EMPTY PAGES
   One layout, different copy. 404, 403, 500, 503 and no-results all
   use it, so a stranger who hits any of them lands somewhere that
   looks like the site and offers a way out.
   ---------------------------------------------------------------- */
/* The column that pins the thin footer to the bottom goes on
   .wp-site-blocks and not on the body. A block theme wraps the whole
   template in that div, so the body's only flex child is the wrapper and
   `main` is a grandchild: putting the column on the body leaves main with
   nothing to fill and the footer floating fifty pixels short of the bottom
   on any screen taller than the content. Ported from a prototype that had
   no wrapper, found by measuring the footer instead of reading it. */
.errpage .wp-site-blocks{display:flex;flex-direction:column;min-height:100vh;min-height:100dvh}
/* On a phone these are one column, and centring them in the leftover
   height put each page's heading at a different distance from the
   header depending on how many links it offered. Start them all at
   the same place instead, and only centre once the layout is two
   columns and the block is short enough for centring to mean
   something. */
/* grid, not flex: a flex item would have to be told to fill, and a
   width on .lost beats the gutter .wrap sets and drops the type onto
   the page edge. align-content does the same job with nothing to
   undo. */
.errpage main{flex:1;display:grid;align-content:start;padding:clamp(2.2rem,6vw,3rem) 0}
@media (min-width:1000px){
  .errpage main{align-content:center;padding:clamp(3rem,7vw,4.5rem) 0}
}
.lost{display:grid;gap:2.2rem}
@media (min-width:1000px){.lost{grid-template-columns:1fr 1fr;gap:4.5rem;align-items:center}}
.lost h1{font-size:clamp(2.3rem,1.6rem + 3.2vw,4.2rem);max-width:14ch;margin-top:.5rem}
.lost h1 em{font-style:normal;color:var(--ink-soft)}
.lost .say{font-size:var(--t-m);line-height:1.45;max-width:34ch;margin:1.1rem 0 0;color:var(--ink-soft)}
.tries{margin:0;border-top:2px solid var(--ink)}
.tries a{display:grid;grid-template-columns:1fr auto;gap:.2rem 1.2rem;align-items:center;
  border-bottom:1px solid var(--line);padding:.95rem 0;text-decoration:none}
.tries b{font-family:var(--disp);font-weight:800;font-size:var(--t-m);line-height:1.15}
.tries span{grid-column:1;color:var(--ink-soft);font-size:var(--t-s)}
/* the column has to be stated: a definite grid-row alone gets this
   placed before the auto items and it lands in column 1 */
.tries em{grid-row:1 / span 2;grid-column:2;font-style:normal;font-size:1.4rem;color:var(--ink-soft)}
.tries a:hover em{color:var(--ink)}

/* search results, grouped by what the thing is (#45). The register's own
   group heading is reused rather than a second one written. */
.hits{display:grid;gap:2.2rem}
.hits .grp-lab{margin-bottom:0}
.hits .tries{border-top:0}
.thin{background:var(--navy);color:#eef1efb0;border-top:2px solid var(--yellow);font-size:var(--t-s)}
.thin-in{display:flex;flex-wrap:wrap;gap:.4rem 2rem;align-items:center;padding:.9rem 0}
.thin a{display:inline-flex;align-items:center;min-height:44px;color:#eef1efd8;text-decoration:none}
.thin a:hover{color:var(--yellow)}

/* ----------------------------------------------------------------
   MOTION : gentle. Gated on .js and reduced-motion.
   ---------------------------------------------------------------- */
.js .rise{opacity:0;transform:translateY(14px)}
@media (prefers-reduced-motion:reduce){
  .js .rise{opacity:1;transform:none}
  .pin .ring{animation:none}
  *{transition:none!important}
}

/* ================================================================
   PAGES

   Everything above is the shared system, ported from
   design/system.css. Everything below is one page's own CSS, ported
   from that page's <style> block, in the order the pages were built.

   The convention set above holds here too: where a colour is an alpha
   or a shade of a palette colour, the hex is the fallback and the
   real value is the color-mix inside the @supports beside it.
   ================================================================ */

/* ----------------------------------------------------------------
   THE SHOPS : the directory list
   design/shops.html
   ---------------------------------------------------------------- */

/* ---- the controls: search and view. .seg comes from the system.
   Shared sticky band with .filt below; the -in layout differs (grid
   here, wrapping flex there), so only the band itself is shared. ---- */
.ctl,.filt{position:sticky;top:var(--stick-top);z-index:var(--z-sticky);background:var(--paper);
  border-bottom:1px solid var(--line);padding:.75rem 0}
.ctl-in{display:grid;gap:.7rem}
@media (min-width:800px){.ctl-in{grid-template-columns:1fr auto;align-items:center;gap:1.2rem}}
.search{position:relative}
.search .in{padding-left:2.6rem}
.search svg{position:absolute;left:.9rem;top:50%;transform:translateY(-50%);
  width:18px;height:18px;stroke:var(--ink-soft);fill:none;stroke-width:2.2}
.count{font-size:var(--t-s);color:var(--ink-soft);white-space:nowrap}
.count b{color:var(--ink)}

/* ---- the register itself ---- */
.reg{padding:var(--band) 0}
.grp{margin-bottom:2.6rem}
.grp:last-child{margin-bottom:0}
/* the group letter sits in its own column on desktop so the list runs
   as one continuous register instead of restarting per block */
@media (min-width:1000px){
  .grp{display:grid;grid-template-columns:14rem 1fr;gap:0 3rem;align-items:start}
  /* scoped to the register: .grp-lab is reused as the search page's
     group heading, where a sticky heading in a short column is wrong */
  .grp .grp-lab{position:sticky;top:calc(var(--stick-top) + 5.2rem)}
}
/* the group is display:grid above, and a display value beats the
   hidden attribute, so a filtered-out group needs saying twice */
.grp[hidden]{display:none}
.grp-lab{font-family:var(--disp);font-weight:800;font-size:var(--t-m);line-height:1.1;
  padding-bottom:.7rem;border-bottom:2px solid var(--yellow);margin-bottom:.4rem}

.row{
  display:grid;gap:.15rem;border-top:1px solid var(--line);
  padding:1.15rem 0;text-decoration:none;
}

/* "Sale on" beside a shop's name in the directory. Not .badge: that one is
   built for the navy photograph band and carries its own dark background,
   which is unreadable on this paper list. Same shape, page's own colours,
   and justify-self:start so the grid row does not stretch it to full width. */
.row-on{justify-self:start;display:inline-block;
  padding:.15rem .55rem;border-radius:999px;margin:.15rem 0;
  font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.08em;background:var(--yellow);color:var(--navy)}
/* .lead is the first row showing, not the first row in the markup.
   PHP puts it on the first one and the filter script moves it, so the
   hairline under the yellow rule is right in both states. */
.grp .row.lead{border-top:0}
@media (min-width:1000px){.grp .row.lead{border-top:1px solid var(--line)}}
.row h3{font-size:var(--t-m)}
.row .meta{color:var(--ink-soft);font-size:var(--t-s);margin:var(--sp-2xs) 0 0}
/* the bold lead-in is shared with the notice byline */
.row .meta b,.byline b{font-weight:700;color:var(--ink);text-transform:uppercase;letter-spacing:.08em}
.row .meta b + span::before{content:"\00a0\00b7\00a0"}
.row[hidden]{display:none}

.empty{padding:3rem 0;text-align:center;color:var(--ink-soft)}
.empty b{display:block;font-family:var(--disp);font-size:var(--t-m);color:var(--ink);margin-bottom:.4rem}

/* ---- map view ---- */
.mapview[hidden]{display:none}
.mapview{padding:var(--band) 0}

/* ---- the ask, at the bottom of a long list. Shared with .yours below,
   which is the same band on a different page. ---- */
.missing,.yours{background:var(--paper-2);border-block:1px solid var(--line)}
.missing-in,.yours-in{display:grid;gap:1.2rem;padding:calc(var(--band)*.7) 0;align-items:center}
@media (min-width:800px){.missing-in,.yours-in{grid-template-columns:1fr auto;gap:2.5rem}}
.missing h2,.yours h2{font-size:var(--t-l);max-width:22ch}
.missing p,.yours p{color:var(--ink-soft);margin:.7rem 0 0}
.missing p{max-width:52ch}
.yours p{max-width:54ch}
.missing .cta,.yours .cta{margin-top:0;white-space:nowrap}

/* ----------------------------------------------------------------
   ONE SHOP : the business listing
   design/shop.html
   ---------------------------------------------------------------- */

/* ---- the window: photo, name across the glass ---- */
.win{min-height:min(56vh,480px);display:flex;align-items:flex-end}
.win-bg{position:absolute;inset:0}
.win-bg img{width:100%;height:100%;object-fit:cover;object-position:center 42%}
/* Two scrims: one down from the top so the breadcrumb reads, one up
   from the bottom so the name does. The literals are the fallback,
   the color-mix beside them is the real value, same as everywhere
   else in this file. */
.win-bg::after{content:"";position:absolute;inset:0;background:
  linear-gradient(to bottom,#101824bd 0%,#1018246e 15%,#10182400 33%),
  linear-gradient(to top,#101824f2 0%,#101824b0 34%,#10182430 70%,#10182412 100%)}
@supports (color:color-mix(in srgb,red 50%,white)){
  .win-bg::after{background:
    linear-gradient(to bottom,
      color-mix(in srgb,var(--photo-ink) 74%,transparent) 0%,
      color-mix(in srgb,var(--photo-ink) 43%,transparent) 15%,
      transparent 33%),
    linear-gradient(to top,
      color-mix(in srgb,var(--photo-ink) 95%,transparent) 0%,
      color-mix(in srgb,var(--photo-ink) 69%,transparent) 34%,
      color-mix(in srgb,var(--photo-ink) 19%,transparent) 70%,
      color-mix(in srgb,var(--photo-ink) 7%,transparent) 100%)}
}
:root[data-hc="1"] .win-bg img{display:none}
:root[data-hc="1"] .win-bg::after{background:var(--photo-ink)}
/* no width here: this element is also .wrap and a width would beat
   the gutter that .wrap sets, dropping the type onto the page edge */
.win-in{position:relative;padding:0 0 clamp(1.6rem,4vw,2.6rem)}

.win h1{font-size:clamp(2.3rem,1.6rem + 3.4vw,4.6rem);max-width:16ch}
/* the sign and the name, on one baseline. The mark drops above the
   name on a phone rather than stealing a third of the line. */
.win-name{display:flex;flex-direction:column;align-items:flex-start;gap:.9rem}
@media (min-width:800px){.win-name{flex-direction:row;align-items:center;gap:1.4rem}}
.win-cr{position:absolute;right:.9rem;top:.6rem;font-size:.7rem;color:#eef1efc9;letter-spacing:.04em;
  text-shadow:0 1px 3px var(--photo-ink)}
@supports (color:color-mix(in srgb,red 50%,white)){
  .win-cr{color:color-mix(in srgb,var(--on-navy) 79%,transparent)}
}

/* ---- a shop's own sale or promotion, #22 ---- */
.offers{background:var(--paper-2);border-bottom:1px solid var(--line)}
.offers-in{display:grid;gap:1.4rem;padding:1.4rem 0}
/* AS MANY SALES AS THE SHOP HAS ON. #186, and the old rule could not do it:
   auto-fit with a 16rem floor put four columns on a desk, so four sales were
   four cramped columns and a dozen would have been three rows of them. The
   floor goes up and the count is capped at three, so a card keeps a readable
   line length whatever the shop is running, and the rows grow downward the way
   a list should. One column on a phone, which is the base above. */
@media (min-width:700px){
  .offers-in{grid-template-columns:repeat(auto-fit,minmax(18rem,1fr));
    grid-template-columns:repeat(auto-fit,minmax(max(18rem,(100% - 2.8rem)/3),1fr))}
}
.offers-rest{grid-column:1 / -1;margin:0;color:var(--ink-soft);font-size:var(--t-s)}
/* The fold holding the rest of them. #186. It sits inside the same tinted
   band, so it reads as more of the same list rather than as a new section, and
   its own grid is the one above: the cards inside are the cards outside.
   .a caps at 62ch for prose, which would put the grid in a column half the
   width of the one it is continuing. */
.offers-fold{border-bottom:0}
.offers-fold > .a{max-width:none;padding-bottom:1.4rem}
.offers-fold .offer h3{font-size:var(--t-m)}

/* WHAT THIS SHOP HAS SAID, above its sales. #188.
   Same tinted band as the sales under it, so the top of a shop's page reads as
   one run of what is true today, with the yellow edge marking which part is
   the shop talking rather than the shop selling. */
.shop-notes{background:var(--paper-2);border-bottom:1px solid var(--line)}
.shop-notes-in{display:grid;gap:1.4rem;padding:1.4rem 0}
@media (min-width:700px){
  .shop-notes-in{grid-template-columns:repeat(auto-fit,minmax(min(22rem,100%),1fr))}
}
.shop-note{border-left:3px solid var(--yellow);padding-left:1rem}
.shop-note h2{font-size:var(--t-m);margin:.2rem 0 .35rem}
.shop-note h2 a{color:inherit;text-decoration:none}
.shop-note h2 a:hover{text-decoration:underline;text-decoration-color:var(--yellow);
  text-decoration-thickness:2px;text-underline-offset:3px}
.shop-note p{margin:0;color:var(--ink-soft)}
.shop-note .when{margin-top:.4rem;font-size:var(--t-s);font-weight:700;
  text-transform:uppercase;letter-spacing:.06em}

.offer .kick{display:block;margin-bottom:var(--sp-3xs)}
.offer h2{font-size:var(--t-m)}
.offer p{color:var(--ink-soft);margin:.4rem 0 0}
.offer .when{font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.08em;color:var(--ink)}

/* A SALE WITH A PICTURE ON IT, on the shop's own page. #164.
   The same photograph and the same stored wash strength the calendar's card
   uses, because a sale that is a photograph over there and three lines of grey
   text over here is two answers to one question.

   AFTER the .offer rules above, not before them, and that is load-bearing.
   `.offer p` and `.offer .when` set their own colours, and `.offer .when` ties
   with `.offer-pic .when` on specificity, so above them the date stayed navy
   ink on a dark photograph. Later wins on a tie. Measured, not guessed: a
   probe read the computed colour of every element in the card and found the
   summary at ink-soft and the date at ink while the stylesheet said otherwise.

   Mobile first: a photograph you read words off, tall enough at 390 that the
   picture is not a stripe. The words need position and a z-index of their own
   to sit above it, because a positioned element with any z-index paints over
   non-positioned content whatever the DOM order says. Session 15, still true. */
.offer-pic{
  position:relative;overflow:hidden;border-radius:8px;
  min-height:11rem;padding:1.2rem;display:flex;
  align-items:center;justify-content:center;text-align:center;
  background:var(--photo-ink);color:var(--on-navy);
}
.offer-pic .offer-say{position:relative;z-index:1;max-width:28ch}
/* NOTHING A SHOP TYPES MAY BLOW THE CARD OPEN. Rob, session 36, on a card
   whose summary ran to the edges. Two guards, because they fail differently.

   The width is in ch and always was: 28 characters is a readable measure and
   it does not care what the font is doing. What it cannot do is stop a long
   line growing DOWNWARD, and the cap in the form is a browser attribute that
   a crafted post ignores, so the last defence is here: three lines for the
   words, three for the title, and the rest is cut with an ellipsis.

   -webkit-line-clamp with -webkit-box is the prefixed spelling every browser
   including Firefox actually implements; the unprefixed `line-clamp` is not
   safe to rely on yet. The fallback if it is ever dropped is a visible
   overflow, not a broken card, because the card clips its own overflow. */
.offer-pic h2,.offer-pic h3,.offer-pic p,
.offer h2,.offer h3,.offer p{
  display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;
  -webkit-line-clamp:3;
}
.offer-pic .kick,.offer-pic .when,.offer .kick,.offer .when{
  display:block;-webkit-line-clamp:none;overflow:visible;
}

/* THE WAY BACK IN, on the shop's own page. #193. A quiet pill under the name,
   not a call to action: the visitor this page is written for is not the person
   pressing it, and it appears for exactly one signed-in person in the town. */
.win-mine{margin:var(--sp-xs) 0 0}
.win-mine .more{background:color-mix(in srgb,var(--on-navy) 14%,transparent);
  border-color:#eef1ef4a;color:var(--on-navy)}
.win-mine .more:hover{background:var(--on-navy);color:var(--navy);border-color:var(--on-navy)}
.offer-pic h2,.offer-pic h3,.offer-pic p,
.offer-pic .kick,.offer-pic .when{color:var(--on-navy)}
@media (min-width:700px){.offer-pic{min-height:13rem}}

/* THE CALENDAR CARD'S WASH DOES NOT FIT THIS CARD, and the measurement is why.
   That one is a plateau with ramps top and bottom plus a horizontal mask from
   620px up, all of it solved for a card 22rem tall with the words floating in
   the middle of a wide band. This card is a third of the height and its words
   run nearly edge to edge, so they sit ON the ramps: measured over a pure white
   photograph, the brightest ground under them came back 206 of 255 at 1280,
   which is 1.37:1. Unreadable, and the shape was the whole reason.

   So here the wash is flat. Same strength, same --offer-wash so a measured
   value on the attachment still lowers it, no ramps and no mask: a small card
   has no room to fade anything out over. The literal is the fallback and the
   color-mix beside it is the real value, the same way this file does it
   everywhere else. */
.offer-pic .shop-offer-pic::after{background:#101824a1;mask-image:none}
@supports (color:color-mix(in srgb,red 50%,white)){
  .offer-pic .shop-offer-pic::after{
    background:color-mix(in srgb,var(--photo-ink) calc(var(--offer-wash) * 100%),transparent);
  }
}


/* the shop's own events on its page (#22). The calendar's .eb row, so the
   only thing needed here is the space around it and the first row's rule,
   which .mon supplies on the calendar and there is no .mon here. */
.shop-events{padding:calc(var(--band)*.5) 0 0}
.shop-events h2{font-size:var(--t-l);margin-bottom:var(--sp-2xs)}
.shop-events .eb.lead{border-top:0}

/* ---- open or closed, right now. The one fact worth the space. ---- */
/* Shared with .till below: the two status bands. */
.state,.till{background:var(--paper-2);border-bottom:1px solid var(--line)}
.state-in,.till-in{display:flex;flex-wrap:wrap;padding:1rem 0}
.state-in{align-items:center;gap:.6rem 1.4rem}
/* The dot on the state band is the same dot the badges carry; its rules
   are with .badge below. Only its size and its outline are here.

   The outline stays. Measured on the light band: the soon yellow is 1.47:1
   against --paper-2 and 1.60:1 in high contrast, so on those two grounds
   the fill is not what makes the dot visible, the ink ring around it is.
   Green is 3.37:1 and would carry itself; one rule for all three states is
   worth more than saving two pixels on one of them. */
.state .pip{width:13px;height:13px;border:2px solid var(--ink)}
/* the bold fact. .p-row .n, the preview card's shop name, is the same set */
.state b,.till b,.p-row .n{font-family:var(--disp);font-weight:800;font-size:var(--t-m);line-height:1.1}
.state span,.till span{color:var(--ink-soft);font-size:var(--t-s)}
.state .acts{margin-left:auto;display:flex;flex-wrap:wrap;gap:.6rem}
/* one of the pill family: base and ink fill live under BUTTONS AND
   PILLS. Here is only its size, and the yellow call-first variant. */
.state .acts a{letter-spacing:.04em;padding:0 1.15rem;min-height:46px}
.state .acts a.pri{background:var(--yellow);border-color:var(--yellow);color:var(--navy)}
.state .acts a.pri:hover{background:#e5a51f;border-color:#e5a51f;color:var(--navy)}
@supports (color:color-mix(in srgb,red 50%,white)){
  .state .acts a.pri:hover{
    background:color-mix(in srgb,var(--yellow) 88%,var(--navy));
    border-color:color-mix(in srgb,var(--yellow) 88%,var(--navy))}
}

/* ---- body: the words on the left, the facts on the right ----
   The same working layout carries the contact page (.msg) and the
   join page (.work); only the column split and the wide gap differ,
   and those live at each page's own site. */
.body,.msg,.work{display:grid;grid-template-columns:minmax(0,1fr);gap:2.6rem;padding:var(--band) 0}
@media (min-width:1000px){
  .body,.msg,.work{gap:4.5rem;align-items:start}
  .body{grid-template-columns:1.25fr .75fr}
}
.about p{font-size:clamp(1.12rem,1.03rem + .4vw,1.32rem);line-height:1.62;max-width:60ch}
.about p:first-of-type{font-size:clamp(1.25rem,1.1rem + .7vw,1.6rem);line-height:1.5}

/* ---- the hours ledger: today gets the yellow underline ---- */
/* The rule that opens the block is on the heading now, because the heading
   moved out of the dl: a dl may only hold dt/dd, so an h2 inside it was an
   invalid content model. Same line in the same place on screen. */
.hours-h{margin:2.4rem 0 0;padding:var(--sp-xs) 0 var(--sp-2xs);border-top:1px solid var(--line);
  font-size:var(--t-m)}
.hours{margin:0}
.hours-note{font-size:var(--t-s);color:var(--ink-soft);margin-top:.9rem}

/* the holidays a shop has ticked (#30). Under the week rather than in it:
   an exception to the ledger, not a row of it. */
.shut-days{list-style:none;margin:.9rem 0 0;padding:0;display:grid;gap:.35rem}
.shut-days li{font-size:var(--t-s);color:var(--ink-soft)}
.shut-days b{color:var(--ink);font-weight:700}
.hrow{display:grid;grid-template-columns:6.6rem 1fr;border-bottom:1px solid var(--line);
  align-items:baseline;position:relative;padding:.7rem 0}
/* the day column is the shared small-caps label, up with .field */
.hrow dd{margin:0;font-size:var(--t-b);font-variant-numeric:tabular-nums}
.hrow.shut dd{color:var(--ink-soft)}
/* The yellow used to be a bar down the left of the row, which needed .9rem
   of padding to sit in, so today's day name started 14.4px right of the
   other six. This page is a ledger and the left edge is the whole point of
   it.

   Now it underlines the day name instead, which is the same yellow rule the
   category rows use on hover. It costs no layout, so all seven days keep the
   same x, and it marks the word rather than the row, so there is nothing to
   work out about which side of a line today is on. */
.hrow.now dt{text-underline-offset:.25em}
/* The marker sits under the day name at every width. There used to be a
   second version above 480 that put it on the same line as a "· today",
   but the day column is 8.5rem there and WEDNESDAY is already most of it,
   so it wrapped anyway and printed a bare dot at the start of the line.

   inline-block, not block: the yellow underline on the dt would otherwise
   propagate into this and draw a second rule under the word "today". A
   decoration does not cross into an atomic inline. */
.hrow.now dt::after{content:"today";display:inline-block;width:100%;margin-top:.15rem;
  color:var(--ink-soft);font-weight:400;letter-spacing:0;text-transform:none;
  text-decoration:none}
@media (min-width:620px){
  .hrow{grid-template-columns:8.5rem 1fr}
}

/* ---- the facts rail ---- */
@media (min-width:1000px){.rail{position:sticky;top:calc(var(--stick-top) + 1.4rem)}}
.rail h2{font-size:var(--t-m);margin-bottom:.9rem}
.rail .lmap{height:clamp(240px,32vw,320px);margin-bottom:1.2rem}
.rail a.link{color:var(--ink);word-break:break-word}
.rail a.link:hover{text-decoration-color:var(--ink)}

/* ---- walk on: the doors either side of this one ----
   The news page's next-and-back (.thumb/.tn) is this same two-door
   band, so the doors are written once. .tn's own measure and its
   time line live at its site. */
.walk{background:var(--paper-2);border-block:1px solid var(--line)}
.walk-in,.thumb-in{display:grid;gap:0}
@media (min-width:800px){.walk-in,.thumb-in{grid-template-columns:1fr 1fr}}
.door,.tn{display:block;text-decoration:none;padding:calc(var(--band)*.5) 0;border-top:1px solid var(--line)}
@media (min-width:800px){
  .door,.tn{border-top:0;padding:calc(var(--band)*.55) 0}
  .door + .door,.tn + .tn{border-left:1px solid var(--line);padding-left:2.5rem}
}
.door .kick,.tn .kick{display:block;margin-bottom:var(--sp-3xs)}
.door h2,.tn h2{font-size:var(--t-m)}
.door p{color:var(--ink-soft);font-size:var(--t-s);margin:.3rem 0 0}
.door.next,.tn.fwd{text-align:left}
@media (min-width:800px){.door.next,.tn.fwd{text-align:right}}
/* A listing with no photograph yet. The design assumed one; without it
   the band is four hundred pixels of flat navy above the name, which
   reads as a picture that failed to load rather than as a shop that
   has not sent one in. Same band, sized to its type. */
.win-bare{min-height:0;padding-top:calc(var(--band)*.55)}

/* ----------------------------------------------------------------
   WHAT'S ON : the calendar
   design/events.html
   ---------------------------------------------------------------- */

/* ---- the season strip: the Sundays, drawn ---- */
.season-in{padding:calc(var(--band)*.6) 0 calc(var(--band)*.5)}
/* the calendar's hero over a photograph (#22). The wash, the scale and the
   credit chip are the visit band's, shared rather than copied: it is the
   same shape of band with different words in it.

   Measured, both ways round, at 390 over the Elgin Street storefronts: the
   visit band's wash puts the h1 at 2.79:1 and the home hero's at 1.65:1,
   because the home wash is directional for type in a left-hand column and
   this type runs the width. Neither number is the whole story, and that is
   worth knowing before anybody "fixes" it: the home hero itself measures
   1.00:1 on its kick and 1.53:1 on its h1 at the same width. Every photo
   band on this site carries its type on text-shadow rather than on the
   wash, by design, because a wash heavy enough to carry it alone erases
   the photograph. So the bar here is the other two bands, and this one
   sits between them. */
.phead-photo.season{min-height:min(56vh,480px);display:flex;align-items:flex-end}
.phead-photo .season-in{position:relative;padding-bottom:clamp(1.6rem,4vw,2.6rem)}
/* no photograph, no five hundred pixels of flat navy waiting for one */
.season-bare{min-height:0}
/* .season-shops is gone. It was the "three shops have a sale on" line in this
   hero and in the front page's; the calendar now opens on the shops' own
   cards, so a sentence there pointed at what the reader was already looking
   at, and the front page turned its copy into a button. The directory keeps
   the sentence and styles it as .shops-on, further down this file. */

/* The hero's two buttons. It was this sentence with a small "See what" link
   under it, which put a count in front of the thing somebody actually wants
   to press. The shops' own posts are the reason a business would keep this
   site up to date, so they get a button instead. The second one draws only
   when a shop has something on, and .hero-go is a plain flex row, so the
   first button simply sits on its own when it does not.

   .cta carries a 1.6rem top margin that breaks a flex row until cleared. */
.hero-go{display:flex;flex-wrap:wrap;align-items:center;gap:.8rem;margin:var(--sp-xs) 0 0}
.hero-go .cta{margin-top:0}

/* The quieter of the two: outlined on the photograph rather than filled, so
   "See the shops" stays the one the eye lands on first. */
.hero-go-2{background:transparent;color:var(--on-navy);
  box-shadow:inset 0 0 0 2px var(--on-navy-line)}
.hero-go-2:hover,.hero-go-2:focus-visible{background:var(--yellow);color:var(--navy);
  box-shadow:none}

/* And the directory carries it over a tinted paper band, where none of the
   on-navy colours above apply. Same sentence, page's own ink. */
.shops-on{font-size:var(--t-s);font-weight:700;text-transform:uppercase;
  letter-spacing:.06em;margin:var(--sp-xs) 0 0;color:var(--ink-soft)}
.shops-on a{color:var(--ink);text-underline-offset:3px;
  box-shadow:inset 0 -2px 0 var(--yellow)}
.shops-on a:hover,.shops-on a:focus-visible{background:var(--yellow);color:var(--navy)}
/* white, not a theme colour: this band stays navy in all four themes */
.season h1{max-width:16ch}
/* One lede, like every other head on the site. It used to be two bold
   lines, an event and the market, each shouting to be the headline. A
   head leads with one thing; when both apply they share a sentence. */
.season .lede{font-size:var(--t-m);line-height:1.45;max-width:40ch;margin:var(--sp-s) 0 0;color:#eef1efe0}
/* Keeps its own thickness and offset: this rule runs under prose inside a
   band, not under a heading. The break around descenders is the default and
   is left alone, same as everywhere else. Not reproducible locally, because
   the season band is conditional, so this one was matched by reading the
   file rather than by measuring the page. */
.season .lede a{color:inherit;text-decoration-color:var(--yellow);
  text-underline-offset:3px}
.season .lede a:hover{color:#fff}
@supports (color:color-mix(in srgb,red 50%,white)){
  .season .crumb,.season .crumb a{color:color-mix(in srgb,var(--on-navy) 71%,transparent)}
  .season .lede{color:color-mix(in srgb,var(--on-navy) 88%,transparent)}
}


/* ---- the toggle strip: see .ctl above for the shared band ---- */
.filt-in{display:flex;flex-wrap:wrap;align-items:center;gap:.6rem 1.2rem}
.filt .count{margin-left:auto;font-size:var(--t-s);color:var(--ink-soft);white-space:nowrap}
.filt .count b{color:var(--ink)}

/* ---- the wall of months ---- */
.wall{padding:var(--band) 0}
.mon{margin-bottom:2.8rem}
.mon:last-child{margin-bottom:0}
@media (min-width:1000px){
  .mon{display:grid;grid-template-columns:13rem 1fr;gap:0 3rem;align-items:start}
  .mon-lab{position:sticky;top:calc(var(--stick-top) + 5rem)}
}
/* the month is display:grid above, and a display value beats the hidden
   attribute, so a month with nothing showing needs saying twice */
.mon[hidden]{display:none}
.mon-lab{font-family:var(--disp);font-weight:800;font-size:clamp(1.7rem,1.3rem + 1.4vw,2.5rem);
  line-height:1;border-bottom:2px solid var(--yellow);padding-bottom:.6rem;margin-bottom:.4rem}
.mon-lab small{display:block;font-family:var(--body);font-weight:400;font-size:var(--t-s);
  color:var(--ink-soft);margin-top:.45rem;letter-spacing:0}
/* one event: the date block, then everything else */
.eb{display:grid;grid-template-columns:4.4rem 1fr;gap:.35rem 1.3rem;
  border-top:1px solid var(--line);padding:1.4rem 0;text-decoration:none;align-items:start}
@media (min-width:620px){.eb{grid-template-columns:5.6rem 1fr;gap:.35rem 2rem}}
.eb[hidden]{display:none}
/* .lead is the first row showing, same as the shops register */
.mon .eb.lead{border-top:0}
@media (min-width:1000px){.mon .eb.lead{border-top:1px solid var(--line)}}
.eb-d{grid-row:1 / span 3;text-align:center;border:1px solid var(--line);border-radius:4px;
  background:var(--card);padding:.6rem .2rem .55rem;line-height:1}
.eb-d b{display:block;font-family:var(--disp);font-weight:800;
  font-size:clamp(1.5rem,1.2rem + 1vw,2.1rem);font-variant-numeric:tabular-nums}
/* the label had the body's 1.65 line-height, so about six pixels of
   empty box sat under three capital letters and the tile read
   bottom-heavy. Measured: ten above the numerals, sixteen below. */
.eb-d span{display:block;font-weight:700;font-size:.72rem;text-transform:uppercase;
  letter-spacing:.09em;color:var(--ink-soft);margin-top:.32rem;line-height:1}
.eb.on .eb-d{background:var(--yellow);border-color:var(--yellow)}
.eb.on .eb-d b,.eb.on .eb-d span{color:var(--navy)}
.eb h3{font-size:var(--t-m);grid-column:2}
.eb .when{grid-column:2;font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.08em;color:var(--ink-soft)}
.eb .d{grid-column:2;color:var(--ink-soft);margin:.2rem 0 0;max-width:60ch}
:is(.eb,.ev) .tags{grid-column:2;display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.7rem}
.ev .tags{margin-top:0}
:is(.eb,.ev) .tags span{font-weight:700;font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;
  border:1px solid var(--line);border-radius:999px;padding:.28rem .7rem;color:var(--ink-soft)}
.eb .tags span.free{border-color:var(--ink);color:var(--ink)}

/* the plain link between the calendar's two halves (#50). A button here
   would compete with the one the page actually wants pressed. */
.tail{padding:0 0 calc(var(--band)*.4);font-size:var(--t-s);font-weight:700;
  text-transform:uppercase;letter-spacing:.08em}


/* ---- the Kingsbury Creative note, members page only (#33). Paper, not
   navy: it is a note from a person, and the navy bands on this site are
   the site talking about itself. ---- */
.kc{background:var(--paper-2);border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);padding:calc(var(--band)*.6) 0}
.kc-in{display:grid;gap:var(--sp-xs)}
@media (min-width:900px){
  .kc-in{grid-template-columns:1fr auto;gap:3rem;align-items:end}
  /* The two-column version works because the members page's note is two
     lines: the buttons sit level with the last of them. /why/ puts four
     paragraphs in the same block, so the second column stays empty for the
     whole height and the button lands alone at the bottom of a dead half
     page. This stacks it back to one column and lets the actions follow the
     text. Only the properties that differ, and only above 900, because the
     base is already a single column. */
  .kc-in.kc-stack{grid-template-columns:1fr;gap:var(--sp-xs);align-items:start}
}
.kc h2{font-size:var(--t-l);max-width:22ch;margin-bottom:var(--sp-2xs)}
.kc p{color:var(--ink-soft);max-width:60ch;margin:0 0 .8rem}
.kc p:last-child{margin-bottom:0}
.kc-acts{display:flex;flex-wrap:wrap;align-items:center;gap:.8rem}
/* .cta carries a top margin that breaks a flex row until it is cleared */
.kc-acts .cta{margin-top:0}
.kc-acts form{margin:0}

/* ---- what the shops have on, #22. ABOVE the town's own wall since session
   22, because a shop whose sale lands under three screens of market dates has
   no reason to post a second one. Their posts lead this page. ----

   THE CARDS ARE FULL SIZE AND OPAQUE, and both of those are load bearing.
   events.js pins each one and slides the next over the top, so a card that is
   see-through shows the card it is covering, and a card too short to fill the
   screen shows the join between two of them. Every rule below is what the
   page looks like with no JavaScript at all: a plain column of big cards.
   The pinning adds nothing to the layout and takes nothing away. */
.shop-offers{padding:calc(var(--band)*.6) 0 0}
.shop-offers .kick{display:block;margin-bottom:var(--sp-3xs)}
/* The heading needs real air under it here, not the usual heading gap: the
   first card pins right below it and a tight gap reads as the card being
   part of the heading rather than the first of a set. */
.shop-offers h2{font-size:var(--t-l);margin-bottom:var(--band);max-width:20ch}

/* perspective on the row, not the card: rotationX on a child needs its
   parent to hold the vanishing point, or the tip reads as a flat squash. */
.shop-offers-in{list-style:none;margin:0;padding:0}
.shop-offer-row{perspective:800px;margin-bottom:var(--sp-xs)}
.shop-offer-row:last-child{margin-bottom:0}

/* Centred, and the card is a panel rather than a page. Left-aligned text in
   a 28rem-tall card leaves a large empty right half, which is what made the
   first version read as a big empty box with a sentence in the corner. */
/* One height token per breakpoint. The column uses it as a floor; the stack
   uses it as an exact height, because stacked cards have to be identical or
   the taller one behind shows below the one in front. */
.shop-offers-in{--offer-h:22rem}
.shop-offer{position:relative;display:flex;flex-direction:column;
  align-items:center;justify-content:center;text-align:center;
  overflow:hidden;min-height:var(--offer-h);text-decoration:none;
  border:1px solid var(--line);border-radius:8px;background:var(--card);
  padding:var(--band) var(--sp-s);margin:0 auto;width:100%}

.shop-offer-say{position:relative;z-index:1;max-width:32ch;
  display:flex;flex-direction:column;align-items:center;gap:.2rem}
.shop-offer .kick{display:block;margin-bottom:var(--sp-2xs)}
/* The title is capped rather than let loose. At --t-xl a three-line title
   filled a 480px card corner to corner and the padding stopped meaning
   anything, which is what "the cards have no padding" was: the type was
   eating it. The card is the fixed thing here; the words fit inside it. */
/* No line-height here. It said 1.05, which is the same specificity as the
   leading rule up in the underline block and 1000 lines later, so it won and
   the yellow rule under line one sat inside line two. Leading for anything
   that can take that underline lives in one place now. */
.shop-offer h3{font-size:var(--t-l);max-width:16ch;text-wrap:balance}

/* Renamed off .who, which the about page's navy band also claims. That rule
   is later in this file and sets background and border-top, neither of which
   the specific rule here overrode, so the shop's name rendered as dark type
   on a navy chip with a yellow line over it. Third bare class name to collide
   on this site; the name is the fix. */
.shop-offer .offer-who{font-weight:700;font-size:var(--t-m);color:var(--ink);
  margin:.6rem 0 0;box-shadow:inset 0 -2px 0 var(--yellow);display:inline-block}
.shop-offer .d{color:var(--ink-soft);margin:.9rem 0 0;max-width:46ch}
.shop-offer .when{display:block;font-weight:700;font-size:var(--t-s);
  text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);
  margin-top:var(--sp-xs)}

/* The shop's photograph, full bleed behind centred type, with a wash over it
   heavy enough to read on. A wash strong enough for text contrast is strong
   enough to erase a photograph, which this site has learned before, so it is
   a scrim rather than a flood: the picture still reads as a picture.

   THE STRENGTH IS SOLVED, NOT PICKED. A photograph can arrive anywhere from
   near black to near white, so there is no one safe tint and a slider with a
   guessed floor is the same problem in one dimension. .63 is the opacity that
   puts --on-navy at 4.5:1 over a PURE WHITE photograph under this overlay,
   solved to 0.6268 and rounded up. Pure white is the lightest a photograph
   can be, so .63 is enough for any of them, and a value measured for one
   picture and written on its attachment can only ever come in under it. */
.shop-offer-pic{position:absolute;inset:0;z-index:0;--offer-wash:.63}
.shop-offer-pic img{width:100%;height:100%;object-fit:cover}

/* A plateau with ramps, and NOT .win-bg's shape. That one clears the middle
   because the name it protects sits at the bottom; this card's words sit in
   the middle, so the same gradient upside down would wash exactly the part
   that needs it least. Measured on the running page, the text box never
   reaches above 9.3% or below 90.7% of the card at any width from 360 to
   1800, so the plateau runs 6% to 94% and the ramps are what is left. The
   literals are the fallback, the color-mix beside them is the real value,
   same as everywhere else in this file. */
.shop-offer-pic::after{content:"";position:absolute;inset:0;background:
  linear-gradient(to bottom,#10182400 0%,#101824a1 6%,#101824a1 94%,#10182400 100%)}
@supports (color:color-mix(in srgb,red 50%,white)){
  .shop-offer-pic::after{background:
    linear-gradient(to bottom,
      transparent 0%,
      color-mix(in srgb,var(--photo-ink) calc(var(--offer-wash)*100%),transparent) 6%,
      color-mix(in srgb,var(--photo-ink) calc(var(--offer-wash)*100%),transparent) 94%,
      transparent 100%)}
}

/* At 620 and up the text box stops being the card. It is 32ch inside a card
   that keeps widening: 20.1% to 79.9% of the card at 620, and down to 30.6%
   to 69.4% by 1440. So the wash ramps out to the left and right edges too and
   the photograph comes back at the sides. On a phone the box is 89% of the
   card and a horizontal ramp would buy nothing, which is why this starts at a
   breakpoint instead of at the base.

   THIS IS A MASK, NOT A SECOND GRADIENT. Two stacked alpha gradients
   composite to 1-(1-a)(1-a), so the plateau would land at .86 instead of the
   .63 that was solved for: more picture eaten, no contrast gained. A mask
   multiplies the alpha instead, which leaves the plateau exactly where the
   arithmetic put it. Behind @supports so a browser without masks gets the
   vertical fade on its own, which is more wash than this and never less. */
@supports (mask-image:linear-gradient(to right,black,black)){
  @media (min-width:620px){
    .shop-offer-pic::after{mask-image:
      linear-gradient(to right,transparent 0%,black 16%,black 84%,transparent 100%)}
  }
}

/* Light type, because what is under it is dark now. Same token the navy bands
   and .phead-photo use, at full strength: --on-navy softened to 90% the way
   .vhead .lede is measures 4.04:1 over the heaviest wash, so the hierarchy on
   this card is size and weight rather than tint. No text-shadow either. The
   shadows on .phead-photo are there because a flat scrim cannot promise a
   white headline over a bright sky; here the wash is computed to promise it,
   and a shadow would only be cover for a wash that was too light.

   The kicker stays light rather than going yellow the way .phead-photo's
   does. The shop name already carries a yellow underline and the hovered
   title takes a yellow rule, and three yellow marks on one card is the thing
   the note below is about. */
.shop-offer:not(.shop-offer-bare){background:var(--photo-ink);color:var(--on-navy)}
.shop-offer:not(.shop-offer-bare) .kick,
.shop-offer:not(.shop-offer-bare) .offer-who,
.shop-offer:not(.shop-offer-bare) .d,
.shop-offer:not(.shop-offer-bare) .when{color:var(--on-navy)}

/* Same as every other photograph on this site in high contrast: the picture
   goes and the ink floods, mask included, or the ramps would leave the card
   corners bare. */
:root[data-hc="1"] .shop-offer-pic img{display:none}
:root[data-hc="1"] .shop-offer-pic::after{background:var(--photo-ink);mask-image:none}

/* No hover colour on the shop name. The shared rule near the top of this file
   already underlines a hovered card's h3 in yellow, and both firing together
   put three yellow marks on one card. One signal. */
.shop-offer:focus-visible{outline:3px solid var(--yellow);outline-offset:3px}

@media (min-width:700px){
  .shop-offers-in{--offer-h:26rem}
  .shop-offer{padding:var(--band) calc(var(--sp-s)*1.6)}
  .shop-offer-say{max-width:36ch}
}
/* 80vw at the wide end, capped so it does not become a billboard on a big
   monitor, and centred by the auto margins above. The section drops its own
   .wrap gutter for the list at this width, because 80vw inside a wrap is
   80% of an already-narrowed column. */
@media (min-width:1000px){
  .shop-offers-in{--offer-h:30rem}
  .shop-offer{width:min(80vw,74rem)}
  .shop-offer h3{max-width:20ch}
}

/* The calendar's "show me more" button. Centred under the wall, and hidden
   by the `hidden` attribute until events.js finds something outside the
   first sixty days, so a display value must not be set on .wall-more itself
   or it would beat the attribute. It is set on the wrapper's shown state
   instead, which is what :not([hidden]) is doing here. */
.wall-more:not([hidden]){display:flex;justify-content:center;
  margin:var(--sp-s) 0 0}

/* ---- the stack, once events.js has taken it over ----

   ONE PIN, NOT ONE PER CARD. The first version pinned every row separately
   with pinSpacing:false. Each row then released on its own clock and dropped
   back into its own place in the flow, which put an earlier card below a
   later one at the bottom of the section and let a pinned row hang over the
   calendar underneath. Both of those were the same bug wearing two hats.

   Pinning the list once and stacking the cards inside it means exactly one
   box on this page has a scroll-dependent position, and it is a box that
   ends where the section ends. Nothing can outlive it.

   All of it is scoped to .is-stacked, which events.js adds only after it has
   decided to run, so the no-JavaScript page keeps the plain column above and
   never meets an absolutely positioned card. */
/* THE CARDS STAY IN THE FLOW. Two versions of this were built before this
   one and both were wrong in the same way: they moved the incoming card.
   Slide a card up into a clipped box and every moment between two cards
   shows a card cut in half, because the thing arriving is only half in the
   box. GreenSock's own stacking pen never moves the arriving card. It pins
   the one underneath and lets the next scroll up over it at ordinary scroll
   speed, which is why it is readable the whole way down.

   So: normal flow, ordinary margins, and events.js pins each card as it
   reaches the top. Nothing here is clipped and nothing is animated except
   the shrink on the card being covered. */
.shop-offers-in.is-stacked{position:relative}
.shop-offers-in.is-stacked .shop-offer-row{margin-bottom:var(--sp-xs)}
.shop-offers-in.is-stacked .shop-offer-row:last-child{margin-bottom:0}

/* EVERY STACKED CARD IS EXACTLY --offer-h TALL, and that is not tidiness.
   Unstacked they are min-height, so a two-line title makes a taller card,
   which is right in a column. Stacked, one card sits directly on another and
   a taller card behind shows its own bottom edge sticking out below the one
   in front. That was the "card bottoms appear mid-scroll" bug: the heights,
   not the pinning.

   overflow-y:auto and not hidden, so an offer whose words genuinely do not
   fit can still be scrolled to. Nothing here may cut off a shop's own text
   to keep a layout neat. */
/* EVERY STACKED CARD IS EXACTLY --offer-h TALL. Unstacked they are
   min-height, so a two-line title makes a taller card, which is right in a
   column. Stacked, one card is pinned directly under another and a taller
   card behind shows its own bottom edge below the one in front. That was the
   "card bottoms appear mid-scroll" bug: the heights, not the pinning. */
.shop-offers-in.is-stacked .shop-offer{height:var(--offer-h);min-height:0;
  box-shadow:0 -14px 34px -24px var(--photo-ink)}

/* Room under the pile before the calendar starts. */
.shop-offers{padding-bottom:var(--band)}

/* The line under the stack saying how many shops did not fit in it.

   margin-top, NOT the margin shorthand. This element also carries .wrap,
   which centres itself with margin-inline:auto, and `margin: x 0 0` sets the
   side margins to zero and throws that away. This rule is later in the file,
   so it won, and the line sat flush against the left edge of the window
   while everything around it was in the container. Same shape of bug as the
   .who collision above: one rule quietly overwriting a property another
   class owns. */
.shop-offers-rest{margin-top:var(--sp-s);font-size:var(--t-s);font-weight:700;
  text-transform:uppercase;letter-spacing:.06em;color:var(--ink-soft)}
.shop-offers-rest a{color:var(--ink);text-underline-offset:3px;
  box-shadow:inset 0 -2px 0 var(--yellow)}
.shop-offers-rest a:hover,.shop-offers-rest a:focus-visible{background:var(--yellow);color:var(--navy)}

/* ---- add your own: see .missing above, same band ---- */

/* ----------------------------------------------------------------
   ONE EVENT : the poster
   design/event.html
   ---------------------------------------------------------------- */
.post{position:relative;overflow:clip}
.post::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(135deg,#eef1ef00 0 22px,#eef1ef08 22px 24px)}
:root[data-hc="1"] .post::before{display:none}
.post-in{position:relative;padding:calc(var(--band)*.55) 0 calc(var(--band)*.7)}
/* the kicker has the date block under it, so it needs the extra air */
.post .kick{margin-bottom:1.4rem}
@supports (color:color-mix(in srgb,red 50%,white)){
  .post .crumb,.post .crumb a{color:color-mix(in srgb,var(--on-navy) 71%,transparent)}
}

/* the date, set as the picture, and read top to bottom the way you
   would say it: month, then the days, then which days those are */
.big{border-top:2px solid var(--yellow);border-bottom:2px solid var(--yellow);padding:1.3rem 0 1.5rem}
.big .mm{font-family:var(--disp);font-weight:800;color:var(--yellow);
  font-size:clamp(1.3rem,1rem + 1.6vw,2.2rem);letter-spacing:.02em;margin:0 0 .35rem}
.big .dd{font-family:var(--disp);font-weight:800;line-height:.86;color:#fff;
  font-size:clamp(4rem,1.9rem + 13vw,12rem);letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;margin:0}
.big .dd em{font-style:normal;color:var(--yellow);font-size:.62em;
  vertical-align:.12em;padding:0 .06em}
.big .wk{display:flex;flex-wrap:wrap;gap:.35rem 1.5rem;align-items:baseline;margin:.85rem 0 0}
.big .wk b{font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.14em;color:#fff}
.big .wk span{font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.1em;color:#eef1efb5}
@supports (color:color-mix(in srgb,red 50%,white)){
  .big .wk span{color:color-mix(in srgb,var(--on-navy) 71%,transparent)}
}

.post h1{margin-top:1.5rem;max-width:15ch}
.post .sub{font-size:var(--t-m);line-height:1.45;max-width:38ch;margin:1rem 0 0;color:#eef1efdd}
@supports (color:color-mix(in srgb,red 50%,white)){
  .post .sub{color:color-mix(in srgb,var(--on-navy) 87%,transparent)}
}

.acts{display:flex;flex-wrap:wrap;gap:.7rem;margin-top:1.9rem}
.acts .cta{margin-top:0}
/* Unscoped from .acts in session 9: the members header wants exactly this
   button and a second copy of it under another name is how two navy buttons
   end up looking slightly different. */
.ghost{
  display:inline-flex;align-items:center;justify-content:center;min-height:52px;
  font-weight:700;font-size:var(--t-s);letter-spacing:.06em;text-transform:uppercase;
  text-decoration:none;padding:0 1.6rem;background:none;color:var(--on-navy);
  border:1px solid #eef1ef6b;border-radius:6px;cursor:pointer;font-family:var(--body);
  transition:background .15s,border-color .15s,color .15s;
}
/* the hover is the shared on-navy fill, up with .dispbtn */
.saved{font-size:var(--t-s);color:#eef1efb5;margin:.8rem 0 0}
@supports (color:color-mix(in srgb,red 50%,white)){
  .ghost{border-color:color-mix(in srgb,var(--on-navy) 42%,transparent)}
  .saved{color:color-mix(in srgb,var(--on-navy) 71%,transparent)}
}

/* AFTER .ghost, and that is the whole of why this works. Both are one class,
   so they tie on specificity and the later rule wins. The first attempt at
   this landed at line 609 while .ghost sits near 1971, so it lost silently
   and the button stayed unreadable. Pinned gotcha, walked into anyway.
   Session 33. */
/* .ghost on a light band. It is a navy-band button: its text is --on-navy
   and its edge is a translucent white, which on the Kingsbury block put
   near-white words on pale grey and made the button unreadable. Rob caught
   it in a screenshot, session 33, and asked whether it was deliberate. It
   was not.

   Its own class rather than scoping to .kc, because the next light band
   that wants this button will otherwise reintroduce the same bug. */
.ghost-light{color:var(--ink);border-color:var(--line)}
.ghost-light:hover{background:var(--ink);border-color:var(--ink);color:var(--paper)}

/* ---- the countdown strip ---- */
/* the band, b and span are shared with .state above */
.till-in{align-items:baseline;gap:.4rem 1.4rem}

/* ---- what happens ----
   .body is shared with the shop page and is defined there. What is here is
   the typography of editor-written content sitting inside it. */
.body .lede{font-size:clamp(1.25rem,1.1rem + .7vw,1.65rem);line-height:1.5;max-width:38ch}
.body > div > p{max-width:60ch;font-size:clamp(1.1rem,1.02rem + .35vw,1.28rem);line-height:1.62}

/* The running order: a timetable, not bullets.

   A plain definition list, which is what a volunteer typing in the editor
   produces, lays itself out on the grid below. There was a second path for
   markup wrapping each pair in a div, carried over from the prototype, and
   nothing on the site or in the editor has ever produced it.

   Scoped to .whathappens, which is only ever the event page's editor
   content. It used to read .body > div > dl, and .body is shared with the
   shop page, so it also caught the shop's hours ledger and laid seven days
   out on a two-column grid on top of each other. */
.order,.whathappens > dl{margin:2.4rem 0 0;border-top:1px solid var(--line)}
.order h2{font-size:var(--t-m);padding:0 0 .9rem}
.whathappens > dl{display:grid;grid-template-columns:5.9rem 1fr;gap:0 1.2rem;align-items:baseline}
@media (min-width:620px){.whathappens > dl{grid-template-columns:7.5rem 1fr}}
.order dt,.whathappens > dl dt{font-weight:700;font-size:var(--t-s);letter-spacing:.04em;
  font-variant-numeric:tabular-nums;color:var(--ink-soft);
  border-bottom:1px solid var(--line);padding:.85rem 0}
.order dd,.whathappens > dl dd{margin:0;border-bottom:1px solid var(--line);padding:.85rem 0}
.order dd b,.whathappens > dl dd b{display:block;font-family:var(--disp);font-weight:800;font-size:var(--t-b)}
.order dd span,.whathappens > dl dd span{color:var(--ink-soft);font-size:var(--t-s)}

/* ---- also on ---- */
.also{background:var(--paper-2);border-block:1px solid var(--line)}
.also-in{padding:calc(var(--band)*.7) 0}
.also h2{font-size:var(--t-l);margin-bottom:var(--sp-xs)}

/* ----------------------------------------------------------------
   HOME
   design/home.html
   ---------------------------------------------------------------- */

/* ---- hero: the street itself is the picture ---- */
.hero{border-bottom:1px solid var(--line);
  min-height:min(78vh,640px);min-height:min(78dvh,640px);display:flex;align-items:center}
@media (min-width:800px){.hero{min-height:min(86vh,760px);min-height:min(86dvh,760px)}}
/* no photograph, no six hundred pixels of flat navy waiting for one */
.hero-bare{min-height:0}
.hero-bg{position:absolute;inset:0}
.hero-bg img{width:100%;height:100%;object-fit:cover;object-position:center 38%}

/* Every photo hero parallaxes on scroll now (system.js), home included --
   one rule rather than repeating it in .hero-bg/.win-bg/.vhead-bg's own
   blocks, since all three need exactly the same buffer for exactly the
   same reason. 1.4, not a smaller number: sliding an image on scroll needs
   real headroom past the frame or it shows its own edge partway down the
   page, and the amount system.js moves it by is matched to this scale --
   raise one, raise the other. */
.hero-bg img,.win-bg img,.vhead-bg img,.season-bg img{transform:scale(1.4);will-change:transform}
/* A shop's own page drifts less. Rob, session 46: at 1.4 a shopfront photo
   is cut to a sliver, and the owner's framing has nowhere to go. Dianne
   Rodger's sign, a portrait photo in a banner three times wider than it is
   tall, was 90 percent of the banner's width. 1.1 buys 5 percent each side,
   and --drift tells system.js to move 4 rather than 15 so it never shows
   the picture's edge. */
.win-bg{--drift:4}
.win-bg img{transform:scale(1.1)}
/* localised navy glass: keeps the type readable without killing the
   photograph. Literals are the fallback, the color-mix is the value. */
.hero-bg::after{content:"";position:absolute;inset:0;background:
  linear-gradient(78deg,#101824e0 0%,#101824b8 42%,#10182440 68%,#10182412 100%),
  linear-gradient(to bottom,#10182480 0%,transparent 30%),
  linear-gradient(to top,#101824d6 0%,transparent 36%)}
@supports (color:color-mix(in srgb,red 50%,white)){
  .hero-bg::after{background:
    linear-gradient(78deg,
      color-mix(in srgb,var(--photo-ink) 88%,transparent) 0%,
      color-mix(in srgb,var(--photo-ink) 72%,transparent) 42%,
      color-mix(in srgb,var(--photo-ink) 25%,transparent) 68%,
      color-mix(in srgb,var(--photo-ink) 7%,transparent) 100%),
    linear-gradient(to bottom,color-mix(in srgb,var(--photo-ink) 50%,transparent) 0%,transparent 30%),
    linear-gradient(to top,color-mix(in srgb,var(--photo-ink) 84%,transparent) 0%,transparent 36%)}
}
:root[data-hc="1"] .hero-bg img{display:none}
:root[data-hc="1"] .hero-bg::after{background:var(--photo-ink)}
.hero-cr{position:absolute;right:.9rem;bottom:.6rem;z-index:1;
  font-size:.7rem;color:#eef1ef8c;letter-spacing:.04em}
@supports (color:color-mix(in srgb,red 50%,white)){
  .hero-cr{color:color-mix(in srgb,var(--on-navy) 55%,transparent)}
}
/* the max-width lives on an inner box, never on the .wrap itself.
   A narrower max-width on .wrap keeps its auto margins and centres
   the whole block, which put the hero type 212px right of the
   wordmark and every heading below it at 1440. */
.hero-wrap{position:relative;padding:clamp(3rem,10vw,6rem) 0 clamp(3.2rem,9vw,5.5rem)}
.hero-in{max-width:46rem}
/* the poster headline: tighter leading than the shared one, because it
   is set in two lines on purpose */
.hero h1{font-size:clamp(2.5rem,1.7rem + 3.4vw,4.4rem);line-height:1.05}
/* the line break in the headline is a wide-screen refinement; on a
   phone the words wrap where they land */
.hero h1 br{display:none}
@media (min-width:620px){.hero h1 br{display:inline}}
.hero .today-top,.hero .today-body b,.hero .today-body span{text-shadow:0 1px 3px #101824cc}

/* today card: plain words, real logic */
.today{max-width:34rem;margin-top:1.8rem;border-top:1px solid #eef1ef3d;padding:1.05rem 0 0}
.today-top{display:flex;gap:.55rem;color:#ccd5e0;padding:0 0 .4rem}
.today-top span::before{content:"\00b7\00a0"}
.today-top b{font-weight:700;font-size:var(--t-s);text-transform:uppercase;letter-spacing:.1em}
.today-top span{font-size:var(--t-s)}
.today-body{margin-top:var(--sp-2xs)}
.today-body b{font-family:var(--disp);font-weight:800;font-size:var(--t-m);line-height:1.15;display:block;color:#fff}
.today-body span{color:#c3cddb;font-size:var(--t-s)}
@supports (color:color-mix(in srgb,red 50%,white)){
  .today{border-top-color:color-mix(in srgb,var(--on-navy) 24%,transparent)}
  .today-top{color:color-mix(in srgb,var(--on-navy) 82%,transparent)}
  .today-body span{color:color-mix(in srgb,var(--on-navy) 76%,transparent)}
}

/* ---- shop of the day ----
   A poster, not a card. The photograph is the band and the band is tall
   enough to be a photograph rather than a letterboxed strip of one. Type
   centred over it, one accent on it, and the accent is the button.

   The wash is a flat veil plus a soft pool behind the middle. A
   one-directional gradient guarantees nothing where centred type actually
   sits: the first version darkened the left edge and the type sat in the
   middle over a bright sky. Measured against the real photographs at 390,
   800 and 1280 before this line was written. */
.feat{
  position:relative;display:grid;align-content:center;
  min-height:clamp(26rem,74vh,34rem);
  --feat-fg:var(--on-navy);
  background:var(--photo-ink);color:var(--feat-fg);overflow:clip;
  border-block:1px solid var(--line);
  text-align:center;
}
@media (min-width:800px){.feat{min-height:clamp(32rem,78vh,44rem)}}

.feat-bg{position:absolute;inset:0}
.feat-bg img{width:100%;height:100%;object-fit:cover;object-position:center}

/* A photograph its shop has framed (owner/photos.php). --fx and --fy are the
   point that has to stay in view, --fz the zoom. object-position keeps that
   point at the same place in the picture's own box, and the box is made --fz
   times the frame and slid so the point lands at the same place in the frame
   too: (1 - z) * fx is exactly that offset. Sizing the box rather than
   scaling it leaves transform alone, which the parallax in system.js owns,
   along with the scale(1.4) headroom it needs. Every frame listed here is
   already position:absolute and inset:0. */
:is(.hero-bg,.win-bg,.vhead-bg,.feat-bg,.shop-offer-pic,.oframe-pic) img[data-frame]{
  position:absolute;
  left:calc((1 - var(--fz)) * var(--fx));top:calc((1 - var(--fz)) * var(--fy));
  width:calc(var(--fz) * 100%);height:calc(var(--fz) * 100%);max-width:none;
  object-position:var(--fx) var(--fy)}
.feat-bg::after{content:"";position:absolute;inset:0;background:
  radial-gradient(ellipse 78% 76% at 50% 52%,#101824e0 0%,#10182499 58%,#10182433 100%),
  linear-gradient(#10182459,#10182459)}
@supports (color:color-mix(in srgb,red,blue)){
  .feat-bg::after{background:
    radial-gradient(ellipse 78% 76% at 50% 52%,
      color-mix(in srgb,var(--photo-ink) 88%,transparent) 0%,
      color-mix(in srgb,var(--photo-ink) 60%,transparent) 58%,
      color-mix(in srgb,var(--photo-ink) 20%,transparent) 100%),
    linear-gradient(color-mix(in srgb,var(--photo-ink) 35%,transparent),
                    color-mix(in srgb,var(--photo-ink) 35%,transparent))}
}
/* A picture behind text is the first thing to go when somebody has asked
   for contrast. */
:root[data-hc="1"] .feat-bg img{display:none}
:root[data-hc="1"] .feat-bg::after{background:var(--photo-ink)}

/* No photograph: the shop's own category, drawn twice. Tiled small across
   the whole band, which is what stops a bare band being a flat field, and
   once enormous and low off the right edge. Every category gets a different
   ground and none of them is a grid.

   --feat-pattern comes off the section as an inline custom property,
   because the drawing is chosen per shop and a stylesheet cannot know
   which. */
/* #182. One drawing stretched over the band, not a tile repeated across it.
   The old one tiled the shop's category icon at 176px and read as a page still
   loading, which is Rob's whole objection: a fallback must not look like a
   missing picture. What is here now is a field of lines bending around two
   rises, seeded from the shop so it never changes under them. */
.feat-bare{background-image:var(--bare-pattern,none);background-size:cover;
  background-position:center}
/* The same drawing on a sale card with no photograph, in ink rather than in
   paper because that card is white. #182. Both places set --bare-pattern and
   both get it from dta_bare_pattern(), so a shop's band and its sale card are
   the same drawing rather than two ideas. */
.shop-offer-bare{background-image:var(--bare-pattern,none);background-size:cover;
  background-position:center}
:root[data-hc="1"] .shop-offer-bare{background-image:none}
:root[data-hc="1"] .feat-bare{background-image:none}

.feat-in{position:relative;padding:calc(var(--band)*.9) 0;max-width:40rem;
  margin-inline:auto;display:grid;justify-items:center;gap:0}

.feat-tag{font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.16em;margin:0 0 1rem;
  color:color-mix(in srgb,var(--feat-fg) 72%,transparent)}
.feat h2{font-size:clamp(2.4rem,1.6rem + 3.4vw,4.2rem);line-height:1.02;
  max-width:16ch;color:#fff;text-shadow:0 2px 10px #101824a6;margin:0}

/* what they call themselves, then where they are. Drawn separators, so a
   shop with no address does not leave a hanging dot. */
.feat-meta{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:.3rem .8rem;margin:1rem 0 0;font-weight:700;font-size:var(--t-s);
  text-transform:uppercase;letter-spacing:.1em;
  color:color-mix(in srgb,var(--feat-fg) 76%,transparent);text-shadow:0 1px 4px #101824a6}
.feat-meta:empty{display:none}
.feat-meta span+span::before{content:"·";margin-right:.8rem;opacity:.55}

.feat-d{font-size:var(--t-m);line-height:1.5;max-width:34ch;margin:1.2rem 0 0;
  color:color-mix(in srgb,var(--feat-fg) 94%,transparent);text-shadow:0 1px 4px #101824a6}

/* The shop's own mark, when they have one. Never the letter tile: over a
   photograph at this size that is a grey square with a letter in it. */
.feat-logo{width:auto;height:clamp(52px,7vw,76px);max-width:min(60%,15rem);
  object-fit:contain;margin:0 0 1rem;
  filter:drop-shadow(0 2px 8px #101824a6)}

/* One yellow thing on the band. The second link is a quiet one beside it.

   Both are the same height and both centre their own text, so the label in
   the button and the label in the link sit on one line. They did not: the
   link was 44px against a button that is taller, and the underline pulled
   the eye down again. The rule is drawn as a box-shadow rather than a
   border, so it hangs under the text without changing the box. */
/* align-items:center, and both boxes the same height. Stretch made the link
   78px against a 52px button, so its centred label sat twelve pixels above
   the button's. Measured, both ways. line-height:1 on the row keeps a stray
   text node from setting the height instead. */
.feat-go{display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:.8rem 1.6rem;margin:1.9rem 0 0;line-height:1}
.feat-go .cta,.feat-cat{display:inline-flex;align-items:center;justify-content:center;
  height:52px;min-height:52px;margin:0}
.feat-cat{font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.09em;color:var(--feat-fg);text-decoration:none;
  box-shadow:inset 0 -1px 0 color-mix(in srgb,var(--feat-fg) 45%,transparent)}
.feat-cat:hover,.feat-cat:focus-visible{box-shadow:inset 0 -2px 0 var(--yellow)}

/* ---- badges ----
   Open, closing soon, closed. Green, yellow, grey, and the colour is never
   the only carrier: the badge says the words. Built as a row rather than a
   single chip because #22 puts a sale, a late night and an event tonight
   next to it, and they all come out of dta_business_badges(). */
.badges{display:flex;flex-wrap:wrap;justify-content:center;gap:.5rem;
  margin:1rem 0 0;padding:0;list-style:none}
.badge{display:inline-flex;align-items:center;gap:.5rem;
  padding:.3rem .8rem;border-radius:999px;
  font-weight:700;font-size:var(--t-s);text-transform:uppercase;letter-spacing:.08em;
  background:#101824a6;color:var(--on-navy);
  border:1px solid color-mix(in srgb,var(--on-navy) 26%,transparent)}
/* One dot, two places it mounts: inside a badge, where the state is on the
   badge, and on its own as .pip on the shop page's state band, where the
   state is on the dot itself. Closed mixes currentColor rather than naming
   a colour, so the same rule reads right on navy and on paper. */
.badge i,.pip{width:9px;height:9px;border-radius:50%;flex:none;background:currentColor}
.badge[data-state="open"] i,.pip[data-state="open"]{background:var(--open)}
.badge[data-state="soon"] i,.pip[data-state="soon"]{background:var(--yellow)}
.badge[data-state="closed"] i,.pip[data-state="closed"]{background:color-mix(in srgb,currentColor 55%,transparent)}
/* the ring is the live one, and it stops for anybody who asked for no motion */
.badge[data-state="open"] i,.pip[data-state="open"]{animation:badge-ping 2.4s ease-out infinite}
@keyframes badge-ping{
  0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--open) 65%,transparent)}
  70%,100%{box-shadow:0 0 0 7px color-mix(in srgb,var(--open) 0%,transparent)}
}
@media (prefers-reduced-motion:reduce){.badge i,.pip{animation:none}}

/* .badges centers by default for the featured band; the event poster is
   left-set like everything else in it. Placed after .badges itself on
   purpose -- same specificity, and a copy of this sitting earlier in the
   file lost the cascade to .badges's own justify-content and margin,
   which is why this badge sat centred and hard against the date block's
   top border instead of left-aligned with breathing room under it. */
.post-badges{justify-content:flex-start;margin:0 0 1.4rem}
.badge[data-state="community"] i,.badge[data-state="shop"] i{background:var(--yellow)}

/* over a flat ground a shadow reads as a smudge rather than as depth */
.feat-bare h2,.feat-bare .feat-d,.feat-bare .feat-meta{text-shadow:none}
/* No photograph, so no reason to be dark. It sits straight under the hero,
   and navy under navy was one long blue block (Rob, session 46). Paper-2
   keeps it apart from the paper sections either side. */
.feat-bare{--feat-fg:var(--ink);background-color:var(--paper-2)}
.feat-bare h2{color:var(--ink)}
.feat-bare .badge{background:var(--card);color:var(--ink);border-color:var(--line-heavy)}

/* Measured at 2.94:1 over the bright corner of a real photograph at 1280,
   which is not enough for seven-tenths of a rem. Opaque, with the same
   shadow the rest of the band carries, and its own small pool under it. */
.feat-cr{position:absolute;right:0;bottom:0;padding:.5rem .9rem .45rem 1.6rem;
  font-size:.7rem;color:color-mix(in srgb,var(--on-navy) 88%,transparent);
  text-shadow:0 1px 3px #101824;
  background:radial-gradient(120% 160% at 100% 100%,#101824b3 0%,#10182400 72%)}
/* ---- the six kinds of place ----
   A ledger, like every other block on this site. Six rows that do not move,
   do not pick anybody, and answer the question somebody actually landed
   here with. */
/* auto-fit, not a column count. It was 1 column, then 2 at 620, then 3 at
   1000, which snaps at two widths and does nothing between them: at 900px
   seven categories sat in two columns with one stranded on its own row, and
   at 1180 they broke awkwardly again. auto-fit reflows continuously and fits
   whatever the width allows, so there is no width where the row is wrong.

   18rem is the floor because the longest category name here is "Food and
   drink" beside a 24px icon, and narrower than that it wraps mid-name. */
.kinds{display:grid;gap:0 3.5rem;border-top:1px solid var(--line);margin-top:1.8rem;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr))}
.kind{display:flex;align-items:center;gap:.95rem;text-decoration:none;
  min-height:60px;padding:1.15rem 0;border-bottom:1px solid var(--line)}
/* The two nth-child rules that used to sit here cancelled a border-top that
   .kind never sets, so they did nothing at either breakpoint. They also could
   not survive auto-fit, which has no column count to count against. */
.kind h3{font-size:var(--t-m);margin:0}
/* Beside the name, not over it. Rob's call, session 12. It costs the left
   edge the rest of the page is built on, and it buys a row you read in one
   pass instead of two: the drawing and the words it belongs to are on the
   same line, the way they are on every other ledger on this site. */
/* every Lucide mark on the site (#81). A 24 box that takes its size from
   the words beside it and its ink from currentColor, so one rule covers
   light, dark and both contrast modes. Anything wanting a different size
   sets it on its own class, the way .kicon does below. */
.icon{display:inline-block;width:1.25em;height:1.25em;flex:none;vertical-align:-.2em}
.kicon{display:block;width:26px;height:26px;flex:none;
  fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
  color:var(--ink-soft);transition:color .15s}
.kind:hover .kicon{color:var(--ink)}

/* ---- from the archives: the one licensed piece of skeuomorphism ---- */
.archb{display:grid;gap:1.6rem}
@media (min-width:1000px){.archb{grid-template-columns:1.1fr .9fr;gap:3rem;align-items:start}}
.arch-img{margin:0;background:var(--paper-2);border-radius:4px;
  display:grid;place-items:center;height:clamp(280px,38vw,430px);padding:1.4rem}
.arch-img .mount img{max-height:calc(clamp(280px,38vw,430px) - 3.2rem)}
.arch-txt h3{font-size:var(--t-m)}
.arch-txt p{max-width:52ch;margin:.6rem 0 0}
.arch-cr{font-size:var(--t-s);color:var(--ink-soft)}
/* Back, Next and Pause do nothing without script, so they are not there
   without it. Gated on body.js the same way .rise is, and gated with display
   rather than the hidden attribute because a display value beating hidden is
   a mistake this stylesheet has already made more than once. The photograph,
   its caption and the credit are all server-rendered and stay. */
.arch-nav{display:none}
.js .arch-nav{display:flex;align-items:center;gap:.6rem;margin-top:1.2rem}
.arch-count{font-size:var(--t-s);color:var(--ink-soft);min-width:4rem;text-align:center}


/* ----------------------------------------------------------------
   NEWS : the notices
   design/news.html
   ---------------------------------------------------------------- */

/* one column, reading measure, centred. The whole page is this. */
.col{width:min(100% - (var(--gut)*2),44rem);margin-inline:auto}

/* ---- the one at the top: newest or pinned, in full ---- */
/* the tint needs real padding or it runs right to the text. Pulling
   the panel out by the same amount it pads keeps the words on the
   same left edge as everything else in the page, so the notice
   reads as pinned over the page rather than indented inside it. The
   photo, when there is one, bleeds to that same pulled-out edge --
   padding lives on .pin-note-body instead of the card itself so the
   image isn't fighting an inset it doesn't want. */
.pin-note{border-top:3px solid var(--yellow);border-bottom:1px solid var(--line);
  --pad:min(var(--gut),1.7rem);
  margin:0 calc(var(--pad) * -1);
  background:var(--paper-2)}
.pin-note-img{aspect-ratio:16/9;overflow:hidden}
.pin-note-img img{display:block;width:100%;height:100%;object-fit:cover}
.pin-note-body{padding:1.7rem var(--pad) 2rem}
@media (min-width:800px){
  .pin-note:not(.pin-note-bare){display:grid;grid-template-columns:minmax(0,42%) 1fr;align-items:stretch}
  .pin-note:not(.pin-note-bare) .pin-note-img{aspect-ratio:auto}
}
.pin-note .flag{font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.1em;color:var(--ink-soft)}
.pin-note h2{font-size:clamp(1.7rem,1.3rem + 1.8vw,2.7rem);margin:.55rem 0 .3rem;max-width:22ch}
/* the time is the same date label the news cards use: see .item time */
.pin-note p{font-size:clamp(1.12rem,1.03rem + .4vw,1.3rem);line-height:1.62;margin:1rem 0 0}
/* WHITE ON WHITE ON HOVER, and the :not() is the whole fix. #187.
   The pill is white so it stands off this card's tinted ground, and the shared
   hover fill up at .more:hover is ink with paper text. Both selectors weigh
   the same, this one is a thousand lines later, so on hover the background
   stayed white while the text went white with it. The label vanished.

   Two selectors tied on specificity: the later one wins. It is written down in
   pinned-decisions and it still cost a hover state nobody could read. Scoping
   this to the rested state takes it out of the fight instead of adding a
   louder copy of the hover fill. */
.pin-note .more{margin-top:1.3rem}
.pin-note .more:not(:hover):not(:focus-visible){background:var(--card)}

/* ---- the stack: date in the margin, one line each ---- */
.stack{padding:0 0 var(--band)}
.yr{display:flex;align-items:baseline;gap:1.1rem;margin:2.6rem 0 0}
.yr h2{font-family:var(--disp);font-weight:800;font-size:var(--t-m);white-space:nowrap}
.yr::after{content:"";flex:1;height:2px;background:var(--yellow)}
.yr span{font-size:var(--t-s);color:var(--ink-soft);white-space:nowrap}

.note{display:grid;grid-template-columns:1fr;gap:.25rem;
  border-bottom:1px solid var(--line);padding:1.15rem 0;text-decoration:none}
@media (min-width:620px){
  .note{grid-template-columns:6.4rem 1fr;gap:.25rem 1.6rem;align-items:baseline}
  .note time{grid-row:1 / span 2}
  .note:not(.note-bare){grid-template-columns:6.4rem 4rem 1fr}
  .note:not(.note-bare) .note-thumb{grid-row:1 / span 2}
}
.note time{font-weight:700;font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.07em;color:var(--ink-soft);white-space:nowrap;font-variant-numeric:tabular-nums}
.note-thumb{width:3.5rem;height:3.5rem;object-fit:cover;border-radius:4px;margin-bottom:.35rem}
.note h3{font-size:clamp(1.2rem,1.1rem + .5vw,1.5rem);max-width:34ch}
.note p{color:var(--ink-soft);margin:.2rem 0 0;max-width:52ch;font-size:var(--t-s)}

/* ----------------------------------------------------------------
   ONE NOTICE : the memo
   design/article.html
   ---------------------------------------------------------------- */
.byline{display:flex;flex-wrap:wrap;gap:.3rem 1.5rem;align-items:baseline;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  margin-top:1.5rem;padding:.85rem 0;font-size:var(--t-s);color:var(--ink-soft)}
/* .byline b is the register rows' bold lead-in: see .row .meta b */

/* ---- the short version: the box that does the work ---- */
.short h2{font-size:var(--t-s);text-transform:uppercase;letter-spacing:.1em;
  font-family:var(--body);font-weight:700;color:var(--ink-soft);margin-bottom:.7rem}
.short ul{margin:0;padding:0;list-style:none}
.short li{display:grid;grid-template-columns:auto 1fr;gap:.8rem;align-items:baseline;
  padding:.42rem 0;font-size:clamp(1.1rem,1.02rem + .35vw,1.24rem);line-height:1.5}
.short li::before{content:"";width:9px;height:9px;background:var(--ink);border-radius:50%;
  transform:translateY(-.1em)}

/* ---- the notice itself ---- */
.text p{font-size:clamp(1.14rem,1.05rem + .42vw,1.34rem);line-height:1.68;margin:0 0 1.15em}
.text p:first-of-type{font-size:clamp(1.28rem,1.12rem + .75vw,1.68rem);line-height:1.5}
.text h2{font-size:var(--t-m);margin:2.1rem 0 .7rem}
.text ul{padding-left:1.15rem;margin:0 0 1.15em}
.text li{margin-bottom:.4em;font-size:clamp(1.14rem,1.05rem + .42vw,1.34rem);line-height:1.6}
.text ::marker{color:var(--yellow)}
.text strong{font-weight:700}

/* a photograph on a notice: the one at the top and anything the editor put
   in the body. Inside the reading column, not breaking out of it, because
   the notice is a memo and a full-bleed photo halfway down reads as a
   different page. #25 */
.note-lead{margin:1.6rem 0 2rem}
.text figure{margin:1.8rem 0}
.note-lead img,.text img{border-radius:4px}
.note-lead figcaption,.text figcaption{font-size:var(--t-s);color:var(--ink-soft);
  margin-top:.55rem;line-height:1.45}

/* The numbers table, because a notice about money should show it.
   A definition list typed in the editor lands here, same as the
   running order on an event, so nobody has to learn a class name. */
.text > dl{margin:2rem 0;border-top:1px solid var(--line);
  display:grid;grid-template-columns:1fr auto;gap:0 1rem;align-items:baseline}
.text > dl dt{font-size:var(--t-b);border-bottom:1px solid var(--line);padding:.75rem 0}
.text > dl dd{margin:0;font-family:var(--disp);font-weight:800;
  font-variant-numeric:tabular-nums;border-bottom:1px solid var(--line);padding:.75rem 0}

/* ---- who wrote it ---- */
.sign{border-top:2px solid var(--yellow);margin-top:2.6rem;padding:1.4rem 0 0}
.sign p{margin:0 0 .5em;color:var(--ink-soft)}
.sign b{display:block;font-family:var(--disp);font-weight:800;font-size:var(--t-m);color:var(--ink)}
.sign .more{margin-top:1.1rem}

/* ---- next and back ----
   The band and the doors are the shop page's walk-on (.walk/.door);
   what is here is only .tn's own: a capped headline measure, and a
   time where a door has a category line. The band itself is also the
   privacy page's .nots, one band-and-margin rule for both. */
.thumb,.nots{background:var(--paper-2);border-block:1px solid var(--line);margin-top:var(--band)}
.tn h2{max-width:24ch}
.tn time{display:block;color:var(--ink-soft);font-size:var(--t-s);margin-top:.35rem}

/* ----------------------------------------------------------------
   ABOUT : who the group is
   design/about.html
   ---------------------------------------------------------------- */

/* ---- opening statement, no photograph ----
   The one page whose headline is the whole point, so it keeps a short
   measure. Everything else about it is the shared plain head. */
.phead-tight h1{max-width:15ch}
.phead-tight .lede{max-width:40ch;margin-top:1.3rem}

/* ---- what it does: four plain statements ---- */
.does{background:var(--paper-2);border-bottom:1px solid var(--line)}
.does-in{padding:var(--band) 0}
.does h2{font-size:var(--t-l);max-width:22ch;margin-bottom:var(--sp-s)}
/* the bordered ledger grid, shared with the visit page's distance
   list (.miles) and the contact page's doors (.doors). The two-column
   steps and gaps differ and live at each site. */
.jobs,.miles,.doors{display:grid;gap:0;border-top:1px solid var(--line)}
@media (min-width:1000px){.jobs{grid-template-columns:1fr 1fr;column-gap:4rem}}
.job{border-bottom:1px solid var(--line);padding:1.4rem 0}
@media (min-width:1000px){.job:nth-child(-n+2){border-top:0}}
.job h3{font-size:var(--t-m);max-width:20ch}
.job p{color:var(--ink-soft);margin:var(--sp-2xs) 0 0;max-width:46ch}

/* ---- the timeline: a spine down the page ---- */
.line{padding:var(--band) 0}
.line-kick{display:block;margin-bottom:var(--sp-3xs)}
.line > .wrap > h2{font-size:var(--t-l);max-width:20ch;margin-bottom:var(--sp-2xs)}
.line > .wrap > p{color:var(--ink-soft);max-width:52ch;margin:0 0 2.6rem}

/* the year drives both the dot and the spine. It is a clamp, so the
   old fixed `top` for the dot drifted with the viewport and was never
   actually level with the digits: too low at 820, too high at 1440.
   Both now sit on the middle of the year's own line box, plus one
   measured nudge for where digits sit inside it. */
.stop{position:relative;padding:0 0 2.8rem 0;--yr:clamp(1.6rem,1.3rem + 1.2vw,2.2rem)}
@media (min-width:800px){
  .stop{display:grid;grid-template-columns:7rem 1fr;gap:0 3rem;padding-bottom:3.4rem}
  /* the spine runs the full height of a stop so it joins the next one.
     Only the ends stop short: the first starts at its dot, the last
     finishes at its dot. */
  .stop::before{content:"";position:absolute;left:calc(7rem + 1.5rem - 1px);
    top:0;bottom:0;width:2px;background:var(--line)}
  .stop:first-of-type::before{top:calc(var(--yr) * .5)}
  .stop:last-of-type::before{bottom:auto;height:calc(var(--yr) * .5)}
  /* box-sizing is border-box, so the dot is 14px across including its
     border, not 18px. Half of 14 is 7. Measured at 1280 it sat 2px left
     of the spine for as long as this rule has existed. */
  .stop::after{content:"";position:absolute;left:calc(7rem + 1.5rem - 7px);
    top:calc(var(--yr) * .5);transform:translateY(-50%);
    width:14px;height:14px;border-radius:50%;background:var(--yellow);border:2px solid var(--ink)}
}
.stop-yr{font-family:var(--disp);font-weight:800;font-size:var(--yr);
  line-height:1;border-bottom:2px solid var(--yellow);padding-bottom:.5rem;margin-bottom:1rem;
  font-variant-numeric:tabular-nums}
@media (min-width:800px){.stop-yr{border-bottom:0;padding:0;margin:0;text-align:right}}
.stop-b h3{font-size:var(--t-m);max-width:26ch}
.stop-b p{margin:.6rem 0 0;max-width:56ch;font-size:clamp(1.08rem,1.02rem + .3vw,1.24rem);line-height:1.62}
.stop-b .cr{font-size:var(--t-s);color:var(--ink-soft);margin-top:.5rem}

/* mounted print inside a stop */
.stop figure{margin:1.3rem 0 0}
.stop .mount img{max-height:clamp(190px,26vw,300px)}
.stop.wide .mount img{max-height:clamp(210px,30vw,360px)}

/* ---- who runs it ---- */
.who{background:var(--navy);color:var(--on-navy);border-top:2px solid var(--yellow)}
.who-in{padding:var(--band) 0}
/* white, not a theme colour: this band stays navy in all four themes */
.who h2{font-size:var(--t-l);color:#fff;max-width:20ch}
.who > .who-in > .wrap > p{max-width:52ch;color:#eef1efdd;margin:1rem 0 0}
.who-note{margin-top:1.8rem}
/* the on-navy ledger grid, shared with the join board's terms. The
   column steps differ and live at each site. */
.roles,.terms{display:grid;gap:0;margin-top:2rem;border-top:1px solid var(--on-navy-line)}
@media (min-width:1000px){.roles{grid-template-columns:1fr 1fr;column-gap:4rem}}
.role{border-bottom:1px solid var(--on-navy-line);padding:1.2rem 0}
@media (min-width:1000px){.role:nth-child(-n+2){border-top:0}}
.role b{display:block;font-family:var(--disp);font-weight:800;font-size:var(--t-m);color:#fff}
.role span{display:block;color:#eef1efc4;margin-top:.35rem}
@supports (color:color-mix(in srgb,red 50%,white)){
  .who > .who-in > .wrap > p{color:color-mix(in srgb,var(--on-navy) 87%,transparent)}
  .role span{color:color-mix(in srgb,var(--on-navy) 77%,transparent)}
}

/* ---- the owned fact ---- */
.fact{padding:var(--band) 0}
.fact h2{font-size:clamp(1.8rem,1.4rem + 2vw,3.2rem);max-width:22ch}
.fact p{font-size:clamp(1.14rem,1.05rem + .42vw,1.34rem);line-height:1.65;max-width:58ch;margin:1.1rem 0 0}
.fact .src{font-size:var(--t-s);color:var(--ink-soft);margin-top:1.2rem}

/* ----------------------------------------------------------------
   PLAN A VISIT
   design/visit.html
   ---------------------------------------------------------------- */

/* ---- header over the water ---- */
.vhead{min-height:min(52vh,440px);display:flex;align-items:flex-end}
/* no photograph, no four hundred pixels of flat navy waiting for one */
.vhead-bare{min-height:0;padding-top:calc(var(--band)*.55)}
.vhead-bg,.season-bg{position:absolute;inset:0}
.vhead-bg img,.season-bg img{width:100%;height:100%;object-fit:cover;object-position:center 55%}
.vhead-bg::after,.season-bg::after{content:"";position:absolute;inset:0;background:
  linear-gradient(to bottom,#101824bd 0%,#1018246e 15%,#10182400 33%),
  linear-gradient(to top,#101824f0 0%,#101824a8 38%,#10182428 72%,#1018240f 100%)}
@supports (color:color-mix(in srgb,red 50%,white)){
  .vhead-bg::after,.season-bg::after{background:
    linear-gradient(to bottom,
      color-mix(in srgb,var(--photo-ink) 74%,transparent) 0%,
      color-mix(in srgb,var(--photo-ink) 43%,transparent) 15%,
      transparent 33%),
    linear-gradient(to top,
      color-mix(in srgb,var(--photo-ink) 94%,transparent) 0%,
      color-mix(in srgb,var(--photo-ink) 66%,transparent) 38%,
      color-mix(in srgb,var(--photo-ink) 16%,transparent) 72%,
      color-mix(in srgb,var(--photo-ink) 6%,transparent) 100%)}
}
:root[data-hc="1"] .vhead-bg img,:root[data-hc="1"] .season-bg img{display:none}
:root[data-hc="1"] .vhead-bg::after,:root[data-hc="1"] .season-bg::after{background:var(--photo-ink)}
/* no width here: this element is also .wrap and a width would beat
   the gutter that .wrap sets, dropping the type onto the page edge */
.vhead-in{position:relative;padding:0 0 clamp(1.6rem,4vw,2.6rem)}
.vhead h1{max-width:15ch}
.vhead .lede{margin-top:1rem}
/* Bottom right and dim, the same as .hero-cr on the front page. It used to be
   top right and near-white here, which read as a label on the photograph
   rather than the credit it is, and put three pages' worth of the same line
   in two different places. A credit is a debt being paid, not a caption. */
.vhead-cr,.season-cr{position:absolute;right:.9rem;bottom:.6rem;font-size:.7rem;color:#eef1ef8c;
  text-shadow:0 1px 3px var(--photo-ink)}
@supports (color:color-mix(in srgb,red 50%,white)){
  .vhead .crumb,.vhead .crumb a{color:color-mix(in srgb,var(--on-navy) 77%,transparent)}
  .vhead .lede{color:color-mix(in srgb,var(--on-navy) 90%,transparent)}
  .vhead-cr,.season-cr{color:color-mix(in srgb,var(--on-navy) 55%,transparent)}
}

/* ---- how far: a plain distance ledger ---- */
.far{background:var(--paper-2);border-bottom:1px solid var(--line)}
.far-in{padding:calc(var(--band)*.6) 0}
.far h2{font-size:var(--t-l);margin-bottom:var(--sp-xs)}
.far-note{font-size:var(--t-s);color:var(--ink-soft);margin-top:1.1rem}
/* the grid itself is shared: see .jobs on the about page */
@media (min-width:800px){.miles,.doors{grid-template-columns:1fr 1fr;column-gap:3.5rem}}
.mrow{display:grid;grid-template-columns:1fr auto auto;gap:.4rem 1.2rem;align-items:baseline;
  border-bottom:1px solid var(--line);padding:.85rem 0;margin:0}
@media (min-width:800px){.mrow:nth-child(-n+3){border-top:0}}
.mrow b{font-weight:400;font-size:var(--t-b)}
.mrow .t{font-family:var(--disp);font-weight:800;font-size:var(--t-m);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.mrow .k{font-size:var(--t-s);color:var(--ink-soft);white-space:nowrap;min-width:4.5rem;text-align:right}

/* ---- the map with layers ---- */
.mapsec{padding:var(--band) 0}
.layers{display:flex;flex-wrap:wrap;gap:.6rem;margin-bottom:1.1rem}
/* one of the pill family: base, hover and pressed fill live under
   BUTTONS AND PILLS. Here is only its size and the gap for its key dot. */
.lbtn{min-height:46px;padding:0 1.15rem;gap:.55rem}
.lbtn i{width:11px;height:11px;border-radius:50%;flex:none;border:2px solid currentColor}
.lbtn[data-l="shops"] i{background:var(--yellow)}
.lbtn[data-l="parking"] i{background:var(--wp--preset--color--parking);border-radius:2px}
.lbtn[data-l="washroom"] i{background:var(--wp--preset--color--washroom)}
.lbtn[data-l="water"] i{background:none}
.mapsec .lmap{height:clamp(380px,52vw,560px)}

/* the non-shop pins. Their two colours are in theme.json beside every
   other colour on the site; they are map keys, not decoration, and a
   blue square has to stay a blue square in dark mode and in contrast. */
.pin.parking{background:var(--wp--preset--color--parking);border-radius:2px}
.pin.washroom{background:var(--wp--preset--color--washroom)}
.pin.water{background:none;border-color:var(--yellow)}
.pin.parking .ring,.pin.washroom .ring{display:none}

/* ---- three plans by how long you have ---- */
.plans{background:var(--paper-2);border-block:1px solid var(--line)}
.plans-in{padding:var(--band) 0}
.plans-kick{display:block;margin-bottom:var(--sp-3xs)}
.plans h2{font-size:var(--t-l);max-width:22ch}
.plans > .plans-in > .wrap > p{color:var(--ink-soft);max-width:52ch;margin:.8rem 0 0}
.pgrid{display:grid;gap:2.2rem;margin-top:2.2rem}
@media (min-width:1000px){.pgrid{grid-template-columns:repeat(3,1fr);gap:3rem}}
.plan{border-top:3px solid var(--ink)}
.plan:nth-child(2){border-top-color:var(--yellow)}
.plan h3{font-size:var(--t-m);padding-top:1.1rem}
.plan .sub{font-size:var(--t-s);color:var(--ink-soft);margin:.35rem 0 0}
.steps{margin:1.2rem 0 0;padding:0;list-style:none}
.steps li{display:grid;grid-template-columns:3.6rem 1fr;gap:.9rem;align-items:baseline;
  border-top:1px solid var(--line);padding:.8rem 0}
.steps li b{font-weight:700;font-size:var(--t-s);color:var(--ink-soft);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.steps li span{font-size:var(--t-b);line-height:1.5}

/* ---- good to know, expanded ---- */
.know{padding:var(--band) 0}
.know-kick{display:block;margin-bottom:var(--sp-3xs)}
.know h2{font-size:var(--t-l);max-width:24ch;margin-bottom:var(--sp-s)}

/* ----------------------------------------------------------------
   PRIVACY AND TERMS
   design/privacy.html. Terms of use was never drawn and uses this
   same layout, which is what the design says to do.

   Renamed on the way in, twice, because both names were taken:
   the design's .big is this theme's event date block, and its
   .yours is the paper-2 band on the shops page.
   ---------------------------------------------------------------- */

/* the answer, big, before anything else */
.answer{padding:calc(var(--band)*.7) 0 var(--band);border-bottom:1px solid var(--line)}
.answer h1{font-size:clamp(1.6rem,1.3rem + 1.2vw,2.2rem);color:var(--ink-soft);
  font-family:var(--body);font-weight:700;letter-spacing:0;max-width:24ch;margin-top:.6rem}
.answer .huge{font-family:var(--disp);font-weight:800;line-height:1.05;letter-spacing:-.01em;
  font-size:clamp(2.6rem,1.7rem + 4.4vw,5rem);margin:.7rem 0 0;max-width:16ch}
/* the answer sits in the ink colour like every other heading. An earlier
   draft set it in yellow with a navy stroke to force the contrast, which
   read as an outline effect rather than as type. The yellow does its
   normal job on the rule under it instead. */
.answer .huge em{font-style:normal;box-shadow:inset 0 -.14em 0 var(--yellow);padding:0 .05em}
.answer p{font-size:var(--t-m);line-height:1.45;max-width:38ch;margin:1.3rem 0 0;color:var(--ink-soft)}
.stamp{font-size:var(--t-s);color:var(--ink-soft);margin-top:1.8rem;padding-top:1rem;
  border-top:1px solid var(--line)}

/* the ledger: what it is, why, how long. Half a band, because the answer
   block above already ends on a full one. */
.what{padding:calc(var(--band)*.55) 0 0}
/* the same h2 carries the not-list below and the join page's
   what-happens-next band */
.what h2,.nots h2,.after h2{font-size:var(--t-l);max-width:22ch;margin-bottom:var(--sp-xs)}
.pitem{border-top:1px solid var(--line);padding:1.4rem 0}
.pitem:last-of-type{border-bottom:1px solid var(--line)}
.pitem h3{font-size:var(--t-m);max-width:28ch}
.pitem p{margin:.6rem 0 0;max-width:56ch;line-height:1.6}
.pitem dl{margin:1rem 0 0;display:grid;gap:.3rem 1.4rem;grid-template-columns:auto 1fr;
  font-size:var(--t-s)}
.pitem dt{font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft)}
.pitem dd{margin:0}

/* what it deliberately does not do. The band itself is written with
   the news page's .thumb; the h2 is up with .what. */
.nots-in{padding:var(--band) 0}
/* the bare bordered list is also the members page's .gaps */
.nots ul,.gaps{margin:0;padding:0;list-style:none;border-top:1px solid var(--line)}
.nots li{display:grid;grid-template-columns:1.7rem 1fr;gap:1rem;align-items:baseline;
  border-bottom:1px solid var(--line);padding:1rem 0}
.nots li::before{content:"\00d7";font-family:var(--disp);font-weight:800;font-size:1.5rem;
  color:var(--yellow);line-height:1}
/* The cross is a grid item too, so both of these have to name the column
   or the span drops back into the narrow first one. The join page's
   .after list sets its rows the same way, so the pairs are shared. */
.nots li b,.after li b{grid-column:2;font-family:var(--disp);font-weight:800;font-size:var(--t-b)}
.nots li span,.after li span{grid-column:2;color:var(--ink-soft)}
.nots-after{margin-top:1.6rem;color:var(--ink-soft);max-width:52ch}

/* your shop's listing: the bit that matters to a member */
.owner{padding:var(--band) 0}
.owner h2{font-size:var(--t-l);max-width:24ch;margin-bottom:var(--sp-xs)}
.owner p{max-width:58ch;line-height:1.62;margin:0 0 1em}
.owner .cta{margin-top:.8rem}

/* ----------------------------------------------------------------
   CONTACT
   design/contact.html

   A router before it is a form. Four doors first, because most of
   what gets typed into a form like this is already answered on
   another page, and the form is what is left over.
   ---------------------------------------------------------------- */
.chead{padding:calc(var(--band)*.7) 0 calc(var(--band)*.45)}
.chead h1{font-size:clamp(2.4rem,1.7rem + 3.2vw,4.4rem);max-width:14ch}
.chead-kick{display:block;margin:.4rem 0 .6rem}
.chead .lede{font-size:var(--t-m);line-height:1.45;max-width:38ch;margin:1rem 0 0;color:var(--ink-soft)}

/* ---- the router: four doors, pick one ---- */
.router{background:var(--paper-2);border-block:1px solid var(--line)}
.router-in{padding:calc(var(--band)*.7) 0}
.doors{display:grid;gap:0;border-top:1px solid var(--line)}
@media (min-width:800px){.doors{grid-template-columns:1fr 1fr;column-gap:3.5rem}}
.dr{display:block;text-decoration:none;border-bottom:1px solid var(--line);padding:1.4rem 0;
  position:relative}
@media (min-width:800px){.dr:nth-child(-n+2){border-top:0}}
.dr .kick{display:block;margin-bottom:var(--sp-3xs)}
.dr h2{font-size:var(--t-m);max-width:22ch}
.dr p{color:var(--ink-soft);margin:.5rem 0 0;max-width:44ch}
.dr .go{display:inline-flex;align-items:center;min-height:44px;margin-top:.35rem;
  font-weight:700;font-size:var(--t-s);text-transform:uppercase;letter-spacing:.06em}
.dr .go::after{content:"\00a0\2192"}

/* ---- the form, for what's left ----
   The band layout is shared with .body on the shop page; here is only
   the split this page and the join page (.work) use. */
@media (min-width:1000px){.msg,.work{grid-template-columns:1.1fr .9fr}}
.msg h2{font-size:var(--t-l);max-width:20ch}
.msg > div > p{color:var(--ink-soft);max-width:48ch;margin:.8rem 0 1.9rem}
.msg form,.work form{max-width:34rem}

/* What the form says back after a post. Yellow for the one that worked,
   because it is a receipt rather than an alarm, and the danger colour for
   the four that did not. Not colour alone: the wording carries it. */
.said{max-width:34rem;margin:0 0 1.6rem;padding:.9rem 1rem;line-height:1.5;
  border-left:4px solid var(--yellow);background:var(--paper-2)}
.said-no{border-left-color:var(--danger)}

/* ---- the preview lock, drawn ----
   The form is still on the page during a demo, greyed and switched off, so a
   room full of shopkeepers can see what it asks for. The refusal itself is a
   403 from the plugin and has nothing to do with any of this; if every rule
   below were deleted the site would be exactly as shut.

   The note stays at full strength above the form. Fading the explanation
   along with the thing it explains is how a person ends up reading neither. */
.formlock-said{border-left-color:var(--ink-soft)}

/* Base is the phone: opacity alone. A grayscale filter over twenty-one fields
   is a repaint a cheap phone pays for on every scroll, and the form is not
   there to be read closely. The filter is a desktop enhancement, min-width
   only, same as every other rule in this file. */
.formlock{position:relative;opacity:.6}

/* No pointer-events here: the disabled fieldset already stops every control
   in every browser, keyboard included, and a pointer-events rule would only
   add a second half-answer on top of a whole one. */
.formlock-set{border:0;margin:0;padding:0;min-width:0}

/* A disabled fieldset greys its own controls in most browsers already; this
   is only so the labels and help text go with them rather than sitting bright
   over dead fields. */
.formlock-set label,.formlock-set .help,.formlock-set legend{color:var(--ink-soft)}

@media (min-width:520px){.formlock{opacity:.55;filter:grayscale(1)}}

/* The honeypot, off the side of the screen rather than display:none. Some
   form fillers skip a hidden input and none of them skip this one. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* who reads it, plain */
@media (min-width:1000px){.reads{position:sticky;top:calc(var(--stick-top) + 1.4rem)}}
.reads h3{font-size:var(--t-m);margin-bottom:.9rem}
.speed{margin:0;border-top:2px solid var(--ink)}
.srow{display:grid;grid-template-columns:1fr auto;gap:.3rem 1.2rem;align-items:baseline;
  border-bottom:1px solid var(--line);padding:.85rem 0}
.srow dt{font-size:var(--t-b)}
.srow dt span{display:block;font-size:var(--t-s);color:var(--ink-soft);margin-top:.2rem}
.srow dd{margin:0;font-family:var(--disp);font-weight:800;white-space:nowrap;
  font-variant-numeric:tabular-nums;color:var(--ink)}

/* ---- what this is and is not, over the theatre ---- */

/* ----------------------------------------------------------------
   GET LISTED
   From design/join.html. Mobile first: one column, the preview
   under the form, the offer band above both.
   ---------------------------------------------------------------- */

/* ---- the offer, stated once ----
   The navy board itself is the shared page head: see .phead-navy. */
.jhead-in{padding:calc(var(--band)*.65) 0}
.jhead .crumb,.jhead .crumb a{color:color-mix(in srgb,var(--on-navy) 71%,transparent)}
.jhead .crumb a:hover{color:var(--on-navy)}
.jhead .kick{color:var(--yellow);display:block;margin:var(--sp-3xs) 0}
.jhead h1{color:var(--on-navy);font-size:clamp(2.4rem,1.7rem + 3.2vw,4.4rem);max-width:15ch}
.jhead h1 em{font-style:normal;color:var(--yellow)}
.jhead .lede{font-size:var(--t-m);line-height:1.45;max-width:36ch;margin:1rem 0 0;
  color:color-mix(in srgb,var(--on-navy) 88%,transparent)}

/* the four terms. One per row on a phone, four across once there is
   room. The grid itself is shared: see .roles on the about page. */
.term{border-bottom:1px solid var(--on-navy-line);padding:1rem 0}
.term b{display:block;font-family:var(--disp);font-weight:800;font-size:var(--t-m);color:var(--on-navy)}
.term span{display:block;font-size:var(--t-s);margin-top:.25rem;
  color:color-mix(in srgb,var(--on-navy) 77%,transparent)}
@media (min-width:800px){
  .terms{grid-template-columns:repeat(4,1fr);column-gap:2rem;border-top:0}
  .term{border-top:1px solid var(--on-navy-line);border-bottom:0}
}

/* ---- the two columns: fill it in, watch it build ----
   The band layout is shared with .body, the split with .msg; only the
   wide gap is this page's own. .said already caps itself at 34rem. */
@media (min-width:1000px){.work{gap:4rem}}

fieldset{border:0;margin:0 0 2.4rem;padding:0;min-width:0}
legend{padding:0;margin-bottom:var(--sp-2xs);font-family:var(--disp);font-weight:800;
  font-size:var(--t-m);line-height:1.15}
.leg-note{font-size:var(--t-s);color:var(--ink-soft);margin:0 0 1.4rem;max-width:52ch}
.two{display:grid;gap:0 1.2rem}
@media (min-width:620px){.two{grid-template-columns:1fr 1fr}}

/* a box the form is complaining about. Not colour alone: it also carries
   aria-invalid, and the sentence at the top of the page names the field */
.in[aria-invalid="true"]{border-color:var(--danger);border-width:2px}

/* consent, said plainly. A 44px target, because this is the one control
   on the page that has to be hit first time */
.agree{display:grid;grid-template-columns:auto 1fr;gap:1.5rem .9rem;align-items:start;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:1.3rem 0;margin:0 0 1.8rem;max-width:34rem}
.agree input{width:44px;height:44px;margin:0;accent-color:var(--ink);flex:none}
.agree input[aria-invalid="true"]{outline:2px solid var(--danger);outline-offset:2px}
.agree label{font-size:var(--t-b);line-height:1.55}
.agree .help{display:block;font-size:var(--t-s);color:var(--ink-soft);margin-top:var(--sp-2xs)}

/* ---- the week: seven rows of two times and a tick ----
   Mobile is a stack: the day, then the times, then Closed. At 620 the row
   turns into one line, which is where a week starts being readable as a
   week rather than as twenty-one controls. */
.week{border-top:1px solid var(--line)}
.wrow{display:grid;grid-template-columns:minmax(0,1fr);gap:.5rem .9rem;padding:.9rem 0;border-bottom:1px solid var(--line)}
@media (min-width:620px){
  .wrow{grid-template-columns:7.5rem 1fr auto;align-items:center;gap:.9rem}
}
/* .wday is the shared small-caps label, up with .field */
.wtimes{display:flex;align-items:center;gap:.6rem}
.wtime{width:auto;flex:1 1 auto;min-width:0;padding:.5rem .6rem;min-height:48px;
  font-variant-numeric:tabular-nums}
@media (min-width:620px){.wtime{flex:0 1 9rem}}
.wto{font-size:var(--t-s);color:var(--ink-soft)}
/* The tick and its word are one target, and the target is 44px tall. */
.wshut{display:inline-flex;align-items:center;gap:.6rem;min-height:44px;cursor:pointer;
  font-size:var(--t-s);text-transform:uppercase;letter-spacing:.08em;font-weight:700;
  color:var(--ink-soft)}
/* the same tick box carries the sign-in's remember-me */
.wshut input,.gate .login-remember input{width:24px;height:24px;margin:0;accent-color:var(--ink);flex:none}
.wshut:has(input:checked){color:var(--ink)}
/* A label that only a screen reader gets. Not the honeypot's .hp: that one is
   off the side of the screen on purpose and means something else. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}

/* ---- the preview rail ----
   Absent until a script has run, so there is no dead placeholder column
   with JavaScript off. Nothing here sets `display`, because a display
   value silently beats the hidden attribute and that has cost this
   project four bugs. */
@media (min-width:1000px){.prev{position:sticky;top:calc(var(--stick-top) + 1.4rem)}}
.prev > h2{font-size:var(--t-m);margin-bottom:.4rem}
.prev > p{font-size:var(--t-s);color:var(--ink-soft);margin:0 0 1.2rem;max-width:44ch}
.card{border:1px solid var(--line);border-radius:4px;background:var(--card);overflow:hidden}
.card + .card{margin-top:1.4rem}
.card-bar{margin:0;background:var(--paper-2);border-bottom:1px solid var(--line);
  padding:.5rem .9rem;font-size:var(--t-s);font-weight:700;text-transform:uppercase;
  letter-spacing:.09em;color:var(--ink-soft)}
.card-in{padding:1.2rem 1.1rem 1.4rem}
.card .logo-mark{margin-bottom:.9rem}
.p-cat{margin:0;font-size:var(--t-s);font-weight:700;text-transform:uppercase;
  letter-spacing:.09em;color:var(--ink-soft)}
.p-name{font-family:var(--disp);font-weight:800;font-size:var(--t-m);line-height:1.1;margin:.35rem 0 0}
/* The design called this marker .empty. This theme already has an .empty,
   the nothing-here block on the shops and events lists, and it centres its
   text. Third class collision of its kind on this project: check style.css
   before inventing a short class. */
.p-empty{color:var(--ink-soft)}
.p-addr{font-size:var(--t-s);color:var(--ink-soft);margin:.3rem 0 0}
.p-desc{margin:.9rem 0 0;line-height:1.55}
.p-site{margin:.4rem 0 0;word-break:break-word}
.p-site[hidden]{display:none}
.p-hours{margin:1.1rem 0 0;border-top:1px solid var(--line);padding-top:.7rem;
  font-size:var(--t-s);color:var(--ink-soft)}
.p-row{display:flex;flex-direction:column;gap:.15rem}
/* .p-row .n is the shop page's bold fact: see .state b */
.p-row .m{font-size:var(--t-s);color:var(--ink-soft);margin-top:.25rem}

/* ---- what happens next ---- */
.after{background:var(--paper-2);border-block:1px solid var(--line)}
.after-in{padding:calc(var(--band)*.8) 0}
/* the h2 is shared with the privacy page: see .what h2 */
.after ol{margin:0;padding:0;list-style:none;counter-reset:n}
.after li{display:grid;grid-template-columns:2.6rem 1fr;gap:1.2rem;align-items:baseline;
  border-top:1px solid var(--line);padding:1.15rem 0;max-width:58rem}
.after li::before{counter-increment:n;content:counter(n);font-family:var(--disp);
  font-weight:800;font-size:var(--t-m);color:var(--yellow);line-height:1}
/* the counter is a grid item too; the b and span pairs that name the
   column are shared with the privacy page's not-list, at .nots li b */
.after-note{margin-top:1.8rem;color:var(--ink-soft);max-width:56ch}

/* ----------------------------------------------------------------
   FOR MEMBERS
   From design/members.html. Two states in one stylesheet: the
   sign-in a stranger sees and the desk an owner sees.

   The design defined .ghost, .listhead, .facts and .more of its
   own. All four already existed here, so this file adds none of
   them back. .who was taken too, by the navy band on the about
   ---------------------------------------------------------------- */

/* ---- the sign-in ---- */
.gate{display:grid;place-items:center;padding:var(--band) 0;min-height:58vh;min-height:58dvh}
.gate-box{width:min(100%,30rem)}
.gate .kick{display:block;margin-bottom:var(--sp-3xs)}
.gate h1{font-size:clamp(2rem,1.5rem + 2.4vw,3.2rem);max-width:16ch}
.gate > .gate-box > p{color:var(--ink-soft);margin:.9rem 0 1.8rem;max-width:38ch}
.gate .said{max-width:none}

/* core's markup, our shapes. wp_login_form prints p.login-username,
   p.login-password, p.login-remember and p.login-submit, so those are
   the hooks rather than anything this file gets to name. The box, its
   label and its input reuse .field and .in's rules up in FORMS, and
   the remember tick reuses the join form's .wshut input, rather than
   carrying a wholesale copy of each down here. */
.gate .login-remember{display:flex;align-items:center;gap:.7rem;margin:0 0 1.4rem;
  font-size:var(--t-s);color:var(--ink-soft)}
.gate .login-remember label{display:flex;align-items:center;gap:.7rem;min-height:44px}
.gate .login-submit{margin:0}
/* core's submit is <input type=submit>, so .cta cannot be put on it from
   the outside. Same shape, said once. */
.gate .login-submit input{
  display:block;width:100%;min-height:56px;border:0;border-radius:6px;cursor:pointer;
  font-family:var(--body);font-weight:700;font-size:var(--t-s);letter-spacing:.06em;
  text-transform:uppercase;background:var(--ink);color:var(--paper);
  transition:background .15s;
}
.gate .login-submit input:hover{background:var(--ink-soft)}

.gate .aside{font-size:var(--t-s);color:var(--ink-soft);border-top:1px solid var(--line);
  margin-top:1.8rem;padding-top:1.2rem;line-height:1.9}
/* Vertical padding on an inline element grows the hit area without growing
   the line box, so these stay tappable and the paragraph keeps its leading.
   min-height would have pushed the lines apart. */
.gate .aside a{padding-block:.88rem}

/* ---- signed in ----
   The navy board itself is the shared page head: see .phead-navy. */
/* Grid, not a wrapping flex row. As a flex row the whole right-hand group
   wrapped onto its own line the moment the shop name ran long, which put
   Sign out halfway down the band instead of in the corner. A grid keeps the
   two columns wherever the name ends, and the name wraps inside its own.

   Base is the phone, where they stack and the button sits under the name. */
/* Measured, session 35, tools/walk-owner.mjs at 390x844: this band was 192px
   and the tab strip under it another 186px, so 494px of a phone screen, 59
   percent of it, was gone before the shop's own work started. The band is the
   worst of that because it repeats on all eleven views to say a thing the
   person already knows: they are signed in, and which shop is theirs.

   So on a phone it is one tight row. The h1 stays an h1, because it is the
   page's only one and the accessibility harness checks that; it is just no
   longer set at thirty pixels. A wider screen has the room and keeps the
   roomier band, which is why the 620 block below restores the gap and the
   padding rather than the phone inheriting a desktop shape. */
.ihead-in{display:grid;gap:.45rem;padding:calc(var(--band)*.26) 0}
@media (min-width:620px){
  .ihead-in{grid-template-columns:1fr auto;align-items:start;gap:1rem 2rem;padding:calc(var(--band)*.5) 0}
}
.ihead .kick{color:var(--yellow);display:block;margin-bottom:0}
@media (min-width:620px){.ihead .kick{margin-bottom:var(--sp-3xs)}}
.ihead h1{color:var(--on-navy);font-size:clamp(1.45rem,1.1rem + 1.8vw,3.1rem)}
.ihead .out{display:flex;flex-wrap:wrap;gap:.6rem;align-items:center;justify-content:flex-start}
@media (min-width:620px){.ihead .out{justify-content:flex-end}}

/* ---- the two jobs ---- */
.jobs2{padding:var(--band) 0 calc(var(--band)*.5)}
.jobs2 > .wrap > h2{font-size:var(--t-l);max-width:20ch}
.jobs2 > .wrap > p{color:var(--ink-soft);max-width:50ch;margin:.8rem 0 0}
.deck{display:grid;gap:1.4rem;margin-top:2rem}
@media (min-width:800px){.deck{grid-template-columns:1fr 1fr;gap:2.5rem}}
.job2{border-top:3px solid var(--ink);padding-top:1.2rem}
.job2:nth-child(2){border-top-color:var(--yellow)}
.job2 h3{font-size:var(--t-m)}
.job2 p{color:var(--ink-soft);margin:.6rem 0 0;max-width:44ch}
.job2 ul{margin:1.2rem 0 0;padding:0;list-style:none;border-top:1px solid var(--line)}
.job2 li{border-bottom:1px solid var(--line);padding:.75rem 0;font-size:var(--t-b);
  display:grid;grid-template-columns:1.5rem 1fr;gap:.7rem;align-items:baseline}
.job2 li::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--yellow);
  border:2px solid var(--ink);transform:translateY(-.15em)}
.job2 .cta{margin-top:1.4rem}

/* ---- your listing, as it stands ---- */
.mine{background:var(--paper-2);border-block:1px solid var(--line)}
.mine-in{padding:var(--band) 0}
.mine h2{font-size:var(--t-l);margin-bottom:var(--sp-2xs)}
.mine > .wrap > p{color:var(--ink-soft);max-width:52ch;margin:0 0 1.7rem}
.sheet{display:grid;gap:2.4rem}
@media (min-width:1000px){.sheet{grid-template-columns:1.15fr .85fr;gap:3.5rem;align-items:start}}
.state-row{display:flex;flex-wrap:wrap;align-items:center;gap:.6rem 1.2rem;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:.9rem 0;margin-bottom:1.5rem}
.chip{display:inline-flex;align-items:center;gap:.5rem;font-weight:700;font-size:var(--t-s);
  text-transform:uppercase;letter-spacing:.08em}
.chip i{width:11px;height:11px;border-radius:50%;background:var(--yellow);
  border:2px solid var(--ink);flex:none}
.state-row .when{font-size:var(--t-s);color:var(--ink-soft);margin-left:auto}

/* What is missing from your own listing. The only nagging an owner
   gets. The list itself is the privacy page's .nots ul, shared there. */
.gaps li{display:grid;grid-template-columns:1.6rem 1fr auto;gap:.9rem;align-items:baseline;
  border-bottom:1px solid var(--line);padding:.8rem 0}
.gaps li b{font-weight:400}
.gaps li em{font-style:normal;font-size:var(--t-s);color:var(--ink-soft);white-space:nowrap}
/* Not colour alone: the tick and the dot are different shapes, and the word
   beside them says Done or Still missing either way. */
.gaps .ok::before{content:"\2713";color:var(--ink);font-weight:700}
.gaps .no::before{content:"\2022";color:var(--yellow);font-size:1.6rem;line-height:.55}

/* ---- your account ---- */
.acct{padding:var(--band) 0}
.acct h2{font-size:var(--t-l);margin-bottom:var(--sp-2xs)}
.acct > .wrap > p{color:var(--ink-soft);max-width:54ch;margin:0 0 1.7rem}
.acct-grid{display:grid;gap:0}
@media (min-width:1000px){.acct-grid{grid-template-columns:1fr 1fr;column-gap:4rem}}
.acct-row{border-bottom:1px solid var(--line);padding:1.15rem 0;
  display:grid;grid-template-columns:1fr auto;gap:.3rem 1.2rem;align-items:center}
@media (min-width:1000px){.acct-row:nth-child(-n+2){border-top:1px solid var(--line)}}
.acct-row b{font-family:var(--disp);font-weight:800;font-size:var(--t-b);grid-column:1}
.acct-row span{grid-column:1;color:var(--ink-soft);font-size:var(--t-s);max-width:40ch}
.acct-row .more{grid-row:1 / span 2;grid-column:2}

/* ---- who's on the site ---- */
.roll{background:var(--paper-2);border-block:1px solid var(--line)}
.roll-in{padding:var(--band) 0}
.roll h2{font-size:var(--t-l);margin-bottom:var(--sp-2xs)}
.roll > .wrap > p{color:var(--ink-soft);max-width:52ch;margin:0 0 1.6rem}
.names{columns:2;column-gap:2.5rem;margin:0;padding:0;list-style:none;border-top:1px solid var(--line)}
@media (min-width:1000px){.names{columns:3}}
.names li{break-inside:avoid;border-bottom:1px solid var(--line);padding:.7rem 0;font-size:var(--t-b)}
.names li a{display:block;text-decoration:none}
.roll-note{font-size:var(--t-s);color:var(--ink-soft);margin-top:1.4rem}

/* ----------------------------------------------------------------
   THE WEATHER
   A thin strip under the header on every page. Server-rendered and
   cached; see dta_weather() for why it is not an embed.

   The drawings are masks over currentColor, the same trick the
   external-link arrow uses, so the icon takes whatever colour the
   band is using and never has to be drawn twice.
   ---------------------------------------------------------------- */
/* position:relative is for the forecast drawer, which hangs off the bottom
   edge of this band. The band is in normal flow under the fixed header, not
   sticky itself, so there is nothing above it to clip the drawer. */
.nowout{position:relative;background:var(--paper-2);border-bottom:1px solid var(--line)}

/* ---- the band shows the sky ----
   A photograph, under a wash, and the photograph has to be readable as
   weather from six feet away: a sun in the sky for clear, cumulus with the
   light behind them for partly cloudy, a curtain of rain, flakes across the
   frame, trees going into fog, a lightning bolt. The first set was chosen
   for "sky only, no landmark" and came out as six abstract colour fields,
   which is a set with nothing to say.

   The wash does two jobs. It guarantees a value behind the text, which a
   photograph cannot promise on its own: six pictures by six photographers
   have six different exposures. And it is what makes six strangers'
   photographs read as one set.

   It is a two-part wash. A flat veil over the whole band keeps the set
   consistent and is deliberately light, so the weather still shows. A second
   pass fades in from the left, over the third of the band the temperature
   and the condition sit on, and that is what actually carries the contrast.
   The far end stays clear, which is where the next few hours are going.

   One wash for daylight, one for after dark, and the picture underneath is
   the same either way. So the night sky is the day sky behind darker glass
   and there is no second set of six to keep in step.

   Fixed hex rather than theme tokens, deliberately, the same as the navy
   bands: the wash has to hold its value when the page turns dark, or the
   thing it is guaranteeing stops being guaranteed. */
.nowout[data-sky]{
  --veil:linear-gradient(#eef1ef2e,#eef1ef1f);
  --read:linear-gradient(90deg,#eef1efed 0%,#eef1efe0 24%,#eef1ef70 48%,#eef1ef00 66%);
  background-color:#dfe4e1;
  color:#16283e;border-bottom-color:#16283e2e;
}
.nowout[data-sky][data-day="0"]{
  --veil:linear-gradient(#0c162280,#0c162270);
  --read:linear-gradient(90deg,#0c1622f2 0%,#0c1622e6 24%,#0c16227a 48%,#0c162200 66%);
  background-color:#121d2b;
  color:#eef1ef;border-bottom-color:#eef1ef2e;
}
/* The photo and the wash used to be one background-image list on .nowout
   itself, three layers swapped in one instant tick when the hour turned
   over. Split into their own layers so the photo alone can fade -- the
   wash's job is guaranteeing contrast, not something that needs to move,
   and re-compositing it every tick would risk the exact contrast this band
   was built and measured against.
   .nowout-in needs position:relative and a z-index above these two, full
   stop -- a non-positioned element always paints below ANY positioned one
   regardless of DOM order, so without it the wash painted over the
   temperature and the icon instead of behind them.

   That alone is not the whole fix. Giving .nowout ITSELF a z-index (to make
   it a stacking context, so these two could use negative z-index instead)
   was tried and reverted: a stacking context traps its contents at its own
   position in the page's paint order, so .nowfc-panel -- which has to cover
   the section below this band when the drawer opens -- could no longer
   escape upward no matter what z-index it carried internally. .nowout
   stays a plain positioned element, not a stacking context, so its
   children's z-index competes against the rest of the page normally and
   the drawer can still paint over what comes after it.

   The remaining side effect -- .nowout-in becoming the *nearest* positioned
   ancestor, ahead of .nowout, for anything absolutely positioned inside it
   -- is real and is fixed at .nowfc-panel directly (below), not here. */
.nowout-photo{position:absolute;top:0;right:0;bottom:-1px;left:0;z-index:0;
  background-image:var(--sky-photo,none);background-size:cover;background-position:center;
  background-repeat:no-repeat;opacity:0;transition:opacity .8s ease}
.nowout-photo.on{opacity:1}
.nowout-wash{position:absolute;top:0;right:0;bottom:-1px;left:0;z-index:1;
  background-image:var(--read),var(--veil);background-size:cover;background-position:center}
.nowout-in{position:relative;z-index:2}
@media (prefers-reduced-motion:reduce){.nowout-photo{transition:none}}

.nowout[data-sky="sun"]  {--sky-photo:url("assets/img/sky/sun.jpg")}
.nowout[data-sky="cloud"]{--sky-photo:url("assets/img/sky/cloud.jpg")}
.nowout[data-sky="rain"] {--sky-photo:url("assets/img/sky/rain.jpg")}
.nowout[data-sky="snow"] {--sky-photo:url("assets/img/sky/snow.jpg")}
.nowout[data-sky="fog"]  {--sky-photo:url("assets/img/sky/fog.jpg")}
.nowout[data-sky="storm"]{--sky-photo:url("assets/img/sky/storm.jpg")}
/* a clear night is the clear day picture with the night wash over it */
.nowout[data-sky="moon"] {--sky-photo:url("assets/img/sky/sun.jpg")}

/* High contrast is somebody telling us they need the text to win. A sky
   behind it is decoration, so it comes off entirely and the band goes back
   to being paper with ink on it. */
:root[data-hc] .nowout[data-sky]{
  background:var(--paper-2);color:var(--ink);border-bottom-color:var(--line)}
:root[data-hc] .nowout-photo,:root[data-hc] .nowout-wash{display:none}
/* and with no photograph there is nothing for the chips to sit on top of, so
   the pill would be a grey blob on a plain band */
:root[data-hc] .nowout-next{background:none;padding-inline:0}
/* Centre, not baseline. The temperature is set at line-height 1 and
   everything beside it is body text at 1.6, so sharing a baseline put the
   number's box three pixels above the middle of the band. Measured. */
.nowout-in{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem .8rem;padding:.75rem 0}
.nowout-deg{font-family:var(--disp);font-weight:800;font-size:var(--t-m);line-height:1;
  font-variant-numeric:tabular-nums}
.nowout-say{font-size:var(--t-b)}
/* ---- the next few hours ----
   Six chips at the far end of the band, in the room the observed note used to
   take. One line, no wrapping, no scrolling: a strip under the header is not
   a thing to swipe, so the ones that do not fit are simply not shown.

   Which is why the count is done in CSS rather than in PHP. The server always
   sends six and the width decides how many are readable, so there is no
   breakpoint in the markup and nothing to keep in step. */
/* The chips carry their own ground, as a pill.

   They sit at the far end of the band, which is exactly where the readability
   wash was faded out on purpose to let the photograph show. Measured over the
   real pixels, the hours came out at 1.05:1 on a rainy afternoon and failed
   in five of the six daylight skies. Invisible.

   A pill rather than widening the wash. Widening it to cover six chips at
   1280 leaves the photograph showing nowhere, and the pill fixes the exact
   width the text occupies at any screen size with no stops to tune. The
   radius is the one the display-options buttons already use. */
.nowout-next{
  display:flex;align-items:center;gap:.9rem;
  margin:0 0 0 auto;padding:.22rem .85rem;list-style:none;
  font-variant-numeric:tabular-nums;
  background:#eef1efdb;border-radius:999px;
}
.nowout[data-day="0"] .nowout-next{background:#0c1622d1}
.nowout-next li{display:flex;align-items:center;gap:.35rem;white-space:nowrap}
/* Celsius or Fahrenheit. The letter against the number, 25°C, in the same
   display face at the same size, so it reads as part of the reading rather
   than as a control parked beside one. A dotted underline says it does
   something.

   Both sit inside .nowout-deg, which is line-height 1, and both are plain
   inline text, so they share a baseline without being told to. The
   padding makes the letter a thumb-sized target (24px was failing at
   phone widths) and the negative margin gives the space straight back, so
   the reading does not move. The underline is text-decoration, not a
   border, because a border would ride down with the padding. */
.nowout-unit{
  font:inherit;color:inherit;
  text-decoration:underline dotted 1px;text-underline-offset:.12em;
  padding:.3em .35em;margin:-.3em -.35em -.3em -.3em;
  opacity:.62;transition:opacity .15s;
}
.nowout-unit:hover,.nowout-unit:focus-visible{opacity:1}
.nowout-at{font-size:var(--t-s);opacity:.72}
.nowout-t{font-weight:700;font-size:var(--t-s)}
.nowout-next .nowout-sky{width:1.05rem;height:1.05rem}
/* A phone gets the current temperature and what it is doing, and that is the
   whole answer at that width. The hours start at the two-column step.

   This used to have five stops of its own, at 420, 620, 820, 980 and 1120,
   because the chips were fitted one at a time against a ruler. They are on
   the site's four steps now. What that costs is one chip on a large phone
   held sideways, which used to show two hours between 420 and 620 and now
   shows none. Measured at each step: three chips clear the condition text by
   79px at 620 and four wrap it, four clear by 137px at 800, and six fit at
   1180. */
.nowout-next li+li{display:none}
@media (min-width:620px){.nowout-next li:nth-child(-n+3){display:flex}}
@media (min-width:800px){.nowout-next li:nth-child(-n+4){display:flex}}
@media (min-width:1000px){.nowout-next li:nth-child(-n+5){display:flex}}
/* :nth-child again rather than a bare li, so this keeps the same specificity
   as the rules above it and actually wins over the li+li that hides them */
@media (min-width:1180px){.nowout-next li:nth-child(-n+6){display:flex}}

/* ---- five days, in a drawer under the band ----
   Absent below the tablet step, not hidden: a phone gets the temperature and
   what it is doing, which is the whole answer at that width, and the chips it
   would sit beside are not there either.

   `details` does the opening and closing, so this works with no script at
   all. The panel hangs off the bottom edge rather than pushing the page down,
   because the band is the first thing under the header and shoving the hero
   forty pixels down the moment somebody presses a chevron reads as the page
   breaking. */
.nowfc{display:none}
@media (min-width:800px){.nowfc{display:block;margin-left:.5rem}}
/* The toggle sits at the far right of the band, past where --read guarantees
   a value behind it (see the comment above .nowout[data-sky]): that wash
   fades to nothing by two-thirds across, on purpose, so the next-few-hours
   strip stays a photograph. A control can't rely on that fade-out the way
   plain text can, so it carries its own always-on chip -- opaque enough that
   no sky photo underneath can break its contrast with the icon on top. */
.nowfc > summary{
  display:flex;align-items:center;justify-content:center;
  width:34px;height:34px;border-radius:999px;cursor:pointer;
  list-style:none;background:#eef1efd9;transition:background-color .15s;
  user-select:none;-webkit-user-select:none;
}
.nowout[data-day="0"] .nowfc > summary{background:#0c1622d9}
/* Safari puts its own triangle on a summary and this is the only way off it */
.nowfc > summary::-webkit-details-marker{display:none}
.nowfc > summary:hover,.nowfc > summary:focus-visible{background:#eef1eff2}
.nowout[data-day="0"] .nowfc > summary:hover,
.nowout[data-day="0"] .nowfc > summary:focus-visible{background:#0c1622f2}
.nowfc-chev{
  width:1.15rem;height:1.15rem;background:currentColor;
  transition:transform .25s cubic-bezier(.4,0,.2,1);
  -webkit-mask:var(--chev) center/contain no-repeat;
  mask:var(--chev) center/contain no-repeat;
  --chev:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6' /%3E%3C/svg%3E");
}
.nowfc[open] .nowfc-chev{transform:rotate(180deg)}
@media (prefers-reduced-motion:reduce){.nowfc-chev{transition:none}}

/* left:0;right:0 anchors to the nearest positioned ancestor, and that is
   .nowout-in (the centred, 1160px-max .wrap) now, not .nowout -- see the
   note above .nowout-photo. Full-bleed by width and a horizontal transform
   instead, which reaches .nowout's actual edges regardless of which
   ancestor ends up positioned.
   No CSS animation here any more, and no opacity: a fade is see-through
   for the length of the transition, which put the hero text underneath
   showing through the panel while it opened. system.js drives a real
   drawer instead -- height animated from 0 with overflow:hidden clipping
   it, so the panel is opaque throughout and looks like it slides rather
   than materializes. height:0 is the closed state so there is never a
   flash of full-height content before JS measures it on first open. */
.nowfc-panel{
  position:absolute;left:50%;top:100%;width:100vw;z-index:1;
  transform:translateX(-50%);
  height:0;overflow:hidden;
  background:var(--paper-2);color:var(--ink);
  border-bottom:1px solid var(--line);
  box-shadow:0 14px 24px -18px #0a0f1459;
}
/* With no script this is the entire show: native <details> toggles height
   between 0 and auto and nothing animates, same as before this session.
   With script, system.js sets an explicit inline pixel height for the
   length of the animation and clears it on finish -- an inline height wins
   over this rule by specificity while the animation owns the value, then
   this takes back over for anything that changes the panel's natural
   height afterwards (a window resize, a font load). */
.nowfc[open] .nowfc-panel{height:auto}
.nowfc-wrap{padding:.9rem 0}
.nowfc-days{
  display:grid;grid-template-columns:repeat(5,1fr);gap:.6rem;
  margin:0;padding:0;list-style:none;font-variant-numeric:tabular-nums;
}
.nowfc-days li{
  display:grid;justify-items:center;gap:.25rem;text-align:center;
  padding:.15rem .3rem;
}
.nowfc-days li+li{border-left:1px solid var(--line)}
/* .nowfc-when is the shared small-caps label, up with .field */
.nowfc-say{font-size:var(--t-s);color:var(--ink-soft)}
.nowfc-hi{font-family:var(--disp);font-weight:800;font-size:var(--t-m);line-height:1}
.nowfc-lo{font-size:var(--t-s);color:var(--ink-soft)}
.nowfc-days .nowout-sky{width:1.6rem;height:1.6rem}

.nowout-sky{
  width:1.5rem;height:1.5rem;flex:none;align-self:center;
  background:currentColor;
  -webkit-mask:var(--sky) center/contain no-repeat;
  mask:var(--sky) center/contain no-repeat;
}
/* The seven skies, as Lucide icons (#81). Generated from the same files
   dta_icon() serves, by tools/gen-sky.php, rather than transcribed: a
   mask needs a literal stroke colour where the inline version uses
   currentColor, and that is the only difference between them.

   A mask rather than inline SVG here on purpose. These sit in the weather
   band, which the browser refreshes hourly and which prints the same mark
   in three sizes, so one rule per sky beats one element per chip. */
.nowout-sun{--sky:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='4' /%3E %3Cpath d='M12 2v2' /%3E %3Cpath d='M12 20v2' /%3E %3Cpath d='m4.93 4.93 1.41 1.41' /%3E %3Cpath d='m17.66 17.66 1.41 1.41' /%3E %3Cpath d='M2 12h2' /%3E %3Cpath d='M20 12h2' /%3E %3Cpath d='m6.34 17.66-1.41 1.41' /%3E %3Cpath d='m19.07 4.93-1.41 1.41' /%3E%3C/svg%3E")}
.nowout-moon{--sky:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401' /%3E%3C/svg%3E")}
.nowout-cloud{--sky:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z' /%3E%3C/svg%3E")}
.nowout-rain{--sky:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242' /%3E %3Cpath d='M16 14v6' /%3E %3Cpath d='M8 14v6' /%3E %3Cpath d='M12 16v6' /%3E%3C/svg%3E")}
.nowout-snow{--sky:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242' /%3E %3Cpath d='M8 15h.01' /%3E %3Cpath d='M8 19h.01' /%3E %3Cpath d='M12 17h.01' /%3E %3Cpath d='M12 21h.01' /%3E %3Cpath d='M16 15h.01' /%3E %3Cpath d='M16 19h.01' /%3E%3C/svg%3E")}
.nowout-fog{--sky:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242' /%3E %3Cpath d='M16 17H7' /%3E %3Cpath d='M17 21H9' /%3E%3C/svg%3E")}
.nowout-storm{--sky:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 16.326A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 .5 8.973' /%3E %3Cpath d='m13 12-3 5h4l-3 5' /%3E%3C/svg%3E")}

/* ----------------------------------------------------------------
   THE OWNER AREA (#153)
   Signed in, on the front of the site. A shop owner never sees
   wp-admin, so everything they do wears the site's own clothes.
   ---------------------------------------------------------------- */

/* The section strip. Eight sections is a lot of tabs, and most of the
   people using this are on a phone, so they are the site's existing
   .cats pills rather than a row of underlined words: a 44px target with
   its own outline, wrapping onto three lines as something deliberate
   instead of a ragged sentence. The pressed state comes free from the
   pill family, which already fills on [aria-current].

   Base is the phone, per the project rule, and nothing below changes the
   layout for a wider screen. The row simply stops needing to wrap. */
.odesk-nav{background:var(--paper-2);border-bottom:1px solid var(--line)}
.oset{
  width:min(100% - (var(--gut)*2),1160px);margin-inline:auto;
  gap:.55rem;padding-block:1.1rem;margin-bottom:0;
}
/* .cats pills run to t-s, which at eight of them is wider than a phone
   wants. These carry less text each, so they can afford to be smaller and
   still clear 44px, which is the measurement that actually matters. */
/* min-height here and not inherited. `.cats a,.cats button` carries the 44px
   and does not name span, so the current pill was only as tall as its line
   box and got away with it by stretching to match its neighbours. On the
   account page at 390px it wraps onto the last row alone, has nothing to
   stretch to, and collapses. Caught by the spacing check, which measures
   the gap between the words and the edge of the box. */
/* Session 35: the horizontal padding, not the type, is what decided the row
   count. At 1rem a side the eight pills needed three rows on a 390px screen
   and the strip was 186px tall. At .62rem they take two, and 44px of height
   is untouched, which is the measurement a thumb actually cares about. */
.oset a,.oset span{font-size:.82rem;letter-spacing:.04em;padding:0 .62rem;min-height:44px}
/* The one they are on. Not a link, so it never picks up the hover fill,
   and the yellow says "here" without competing with the ink-filled
   pressed state the filters use elsewhere. */
/* --navy, not --ink. The yellow is a fixed-palette colour that does not flip
   with the theme, and --ink does, so in dark mode this painted near-white
   words on yellow at 1.56:1. Same pairing the search panel already uses.
   Found by tools/check-owner-a11y.mjs, not by looking. */
.oset .on{background:var(--yellow);border-color:var(--yellow);color:var(--navy);cursor:default}

@media (min-width:800px){
  .oset{gap:.65rem;padding-block:1.3rem}
  .oset a,.oset span{padding:0 1.15rem}
}
.owork{padding:var(--band) 0}
.owork > .wrap > h2{font-size:var(--t-l);margin-bottom:var(--sp-2xs)}
.olede{color:var(--ink-soft);max-width:54ch;margin:0 0 2rem}

/* The form's sections reuse .qa, the accordion from the why page, so
   there is one disclosure on this site rather than two that drift. What
   changes here is only what a form needs that a question does not: a
   rule between the folds, and room under the last one for the button.

   A closed fold still posts its fields, which is what makes a six-part
   form submit whole. The fold holding a refused field is opened by the
   view, because a message about a box nobody can see is not a message. */
/* No borders on the fold itself any more. These drew the hairline rules that
   separated the folds back when a fold was a row; the summary is a bordered
   box now and draws its own edges, so a top border here put a second line
   against the box's own and the last fold carried a third underneath it. */
.ofold > summary :is(h2,h3){font-size:var(--t-m);max-width:none}
/* Enough room under the last field that the boundary between two sections is
   bigger than the boundary between two fields inside one. It was .6rem, which
   lost that race the moment the fields themselves moved up to --sp-s. */
.ofold .a{padding-bottom:var(--sp-xs);max-width:none}
/* The last field in a fold already carries its own bottom margin, so the
   fold does not add a second one underneath it. */
.ofold .a > .field:last-child,.ofold .a > .two:last-child{margin-bottom:0}

.osave{margin-top:2rem}

/* A shop's own events, sales or notices.

   Base is the phone: everything stacks, and the two buttons sit under the
   thing they act on rather than beside it, because a row that squeezes a
   title and two 44px buttons onto 390px gives the title four words. */
.orows{list-style:none;margin:2rem 0 0;padding:0;border-top:1px solid var(--line)}
.orow{display:grid;gap:.9rem;padding:1.25rem 0;border-bottom:1px solid var(--line)}
.orow-what{display:grid;gap:.25rem}
.orow-what b{font-size:var(--t-m);font-weight:700;line-height:1.25}
.orow-when{color:var(--ink-soft);font-size:var(--t-s)}
/* The state is the one thing somebody scans this list for, so it gets the
   small-caps treatment the site uses for a label rather than another grey
   line that reads as more date. */
.orow-state{
  font-size:var(--t-s);text-transform:uppercase;letter-spacing:.07em;font-weight:700;
}
.orow-acts{display:flex;flex-wrap:wrap;gap:.6rem;align-items:center}
/* .more is a pill, and a <button> in the pill family needs its form not to
   add a margin under it. */
.orow-acts form{margin:0}

.onone{color:var(--ink-soft);max-width:50ch;margin-top:1.6rem}

@media (min-width:800px){
  /* Room for the buttons beside the thing now, right-aligned so the eye runs
     down one column of actions instead of hunting for them. */
  .orow{grid-template-columns:1fr auto;align-items:center;gap:1.5rem}
  .orow-acts{justify-content:flex-end}
}


/* Two columns once there is room for two, same breakpoint the join form
   uses for the same pair of boxes. */
@media (min-width:620px){
  .owork .two{grid-template-columns:1fr 1fr}
}

/* The cap sentence on the sales page, and the small headings the owner
   area reuses from the members desk. */

/* #168. A fold you can press and a label you cannot were drawn identically.
   Measured with tools/walk-owner.mjs: on the photos page "Your logo" opens a
   fold and "Logo library" does nothing, and both were --t-m at weight 800.
   The only thing telling them apart was a 20px plus sign pinned to the far
   right edge, about 300px from the words it belonged to on a phone.

   Two changes, and they pull in opposite directions on purpose. The fold
   header gets a surface, so it reads as a bar you press rather than as text.
   The label loses weight and size and becomes the same quiet uppercase the
   field labels already use, so it reads as a caption over what follows.
   Neither of them is a new colour or a new type size.

   Scoped to .ofold, not .qa, because .qa is also the public question and
   answer accordion on the privacy and about pages, and nothing is wrong with
   that one. */
/* The bars were butted edge to edge with 0px between them, five bordered
   boxes making one slab, and the borders of two neighbours drew a 2px seam.
   That was this afternoon's doing: the summary got a surface and a border and
   the fold never got a margin. As hairline rows they did not need one. As
   boxes they do, and .qa's own bottom border is the one that has to go,
   because the box already draws that line. */
/* #180. Rob: "they just look like they all merge into each other." Measured
   at 390px before touching anything: four shut bars, 66px each, --paper-2 at
   1.10:1 against the page showing through the 9px between them, and a --line
   border at 1.38:1. Nothing there separates one bar from the next, so the eye
   reads one grey slab with words on it. He was right, and the accordion was
   never the problem.

   Two levers, both on the separation and neither on the disclosure: the edge
   goes to --line-heavy, and the gap goes from --sp-2xs to --sp-xs, which is
   9px to 15px on a phone. */
.ofold{border-bottom:0;margin-bottom:var(--sp-xs)}
.ofold > summary{
  background:var(--paper-2);border:1px solid var(--line-heavy);border-radius:4px;
  padding:1.05rem 3rem 1.05rem 1.05rem;
}
.ofold[open] > summary{border-bottom-left-radius:0;border-bottom-right-radius:0}
/* The marker is absolutely positioned against the summary's padding box, so
   the padding-right above already walks it in from the edge to sit beside
   the words instead of out at the rim of the phone. */
.ofold > summary::before,.ofold > summary::after{right:1.05rem}
/* color-mix rather than a --paper-3, because there is no --paper-3: the first
   draft of this rule named one, fell through to its own fallback and changed
   nothing at all on hover. Mixing the two tokens that do exist keeps the
   darkening honest in light and in dark. */
.ofold > summary:hover,.ofold > summary:focus-visible{
  background:color-mix(in srgb,var(--paper-2) 90%,var(--ink));
}
.ofold .a{padding-inline:1.05rem}
/* --sp-m, the step that did not exist until this session. This is exactly the
   job it was added for: a new section starting inside a band, which is more
   than the space between two fields and less than a whole band. It was 2.8rem
   hardcoded, which is what reaching for a missing step looks like. */
.ophead{margin-top:var(--sp-m);font-size:var(--t-s);text-transform:uppercase;
  letter-spacing:.1em;font-weight:700;color:var(--ink-soft)}
.ophelp{max-width:60ch;margin:.4rem 0 1.2rem}
.onum{font-size:var(--t-m);margin:0 0 1.2rem}
.onumhead{margin-top:2.4rem}
/* The numbers page puts a whole sentence in the dd, not a count, so the
   label column cannot stay at the 6.8rem the facts list uses elsewhere. */
.facts-wide .drow{grid-template-columns:1fr}
.facts-wide dt{padding-bottom:0}
.facts-wide dd{padding-top:.2rem;color:var(--ink-soft)}

.oupload{margin:0 0 1rem;padding-bottom:1.6rem;border-bottom:1px solid var(--line)}

/* Choosing a picture is choosing between pictures, so the grid shows them
   rather than a list of file names: a row reading IMG_2938.jpg tells
   somebody nothing about which one is the front of their shop.

   Base is two columns, which on a 390px phone is a thumbnail big enough to
   recognise with room for its buttons underneath. Every photograph appears
   exactly once; the first version had a picker and a separate delete list
   showing the same pictures twice. */
.ogrid{
  list-style:none;margin:1.4rem 0 0;padding:0;
  display:grid;grid-template-columns:repeat(2,1fr);gap:1.6rem .9rem;
}
.ocell{display:grid;gap:.45rem;align-content:start}
/* A TILE IS THE SHAPE OF THE JOB IT IS FOR, and that is the whole of this.
   Square and contained is right for the sign, because a shop's sign is
   usually a wordmark and a square crop cuts the ends off it: the public
   listing already draws a logo contained, so a cropping thumbnail was showing
   something no visitor ever sees. It is wrong for the picture behind a sale,
   which is a wide band that really does crop, and a square tile promising the
   whole photograph there is the same lie the other way round.

   So the base is square and contained, on the same fixed plate the public
   sign tile uses so the picture does not change with the theme, and the sale
   form's grids take .ogrid-wide below.

   Measured on the running site rather than guessed: the sale card is 388x350
   at a 390px viewport and 1022x478 at 1280, so it has no single ratio to copy
   and 16/9 is the honest middle. It says "this gets used wide and it will be
   cropped", which is the thing somebody needs to know before they press it,
   and no number could say more than that when the card itself changes shape
   between a phone and a desk. #160. */
.oshot{
  display:block;width:100%;aspect-ratio:1;object-fit:contain;
  border:2px solid var(--line);border-radius:2px;background:var(--on-navy);
}
/* The sale form's two grids: the shop's own pictures and the library results.
   One override each, the property that differs and nothing else. */
.ogrid-wide .oshot,
.ogrid-wide .onone-shot{aspect-ratio:16/9}
.ogrid-wide .oshot{object-fit:cover}
/* The one on the listing. A yellow edge rather than a tick in a corner,
   because an edge is visible at thumbnail size on a phone and a 16px tick
   is not. */
.ocell-sign .oshot{border-color:var(--yellow);border-width:4px}
/* #166. The preview shows the ground the public tile will use, so a shop
   whose logo is drawn in white sees what a visitor sees rather than a tile
   that looks fine only in here. Same fixed navy as .logo-mark. */
.ocell[data-ground="dark"] .oshot{background:var(--navy)}
/* The two ground buttons, drawn only for a logo you can see through. #181.
   A row, because they are one setting with two states rather than a list of
   actions, and the one already in force reads as pressed rather than being
   hidden, so the pair still reads as a pair.

   The owner preview needs no rule of its own for a logo with its own
   background: .oshot carries no padding, so it already draws what the public
   tile draws. */
/* Framing the main picture. Three previews at the real shapes, measured on
   the live site (owner/photos.php has the numbers). The two shop-page ones
   carry what the page itself does to the picture as it loads: scale(1.1) for
   the parallax headroom, and system.js's starting yPercent of -4. Without
   those the preview would promise a crop the page does not draw. */
.oframes{display:grid;gap:var(--sp-s);margin:0 0 var(--sp-s)}
@media (min-width:620px){.oframes{grid-template-columns:1fr 2fr}.oframe-win{grid-column:1/-1}}
.oframe-shot{margin:0}
.oframe-shot figcaption{font-size:var(--t-s);color:var(--ink-soft);margin-top:.35rem}
.oframe-pic{position:relative;overflow:hidden;border:2px solid var(--line);
  background:var(--navy);cursor:grab;touch-action:none}
.oframe-pic:active{cursor:grabbing}
.oframe-win .oframe-pic{aspect-ratio:1280/448}
.oframe-phone .oframe-pic{aspect-ratio:390/473}
.oframe-feat .oframe-pic{aspect-ratio:1280/759}
.oframe-pic img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  user-select:none;-webkit-user-drag:none}
.oframe-win img,.oframe-phone img{transform:translateY(-4%) scale(1.1)}
.oframe-go{display:flex;flex-wrap:wrap;gap:.6rem;align-items:center}
.oframe{margin-bottom:var(--sp-s)}

.oground{display:flex;flex-wrap:wrap;gap:.6rem;margin:0 0 1.2rem}
.oground button[aria-pressed="true"]{
  background:var(--navy);border-color:var(--navy);color:var(--on-navy);
}
.oused{font-size:var(--t-s);color:var(--ink-soft);line-height:1.35}
.ocell-sign .oused{color:var(--ink);font-weight:700}
/* The buttons stack on a phone and sit in a row the moment there is width
   for two of them, because a 44px pill and a 44px pill side by side need
   about 300px and a two-column grid at 390 gives each cell about 170. */
.ocell-acts{display:flex;flex-wrap:wrap;gap:.5rem}
.ocell-acts form{margin:0}
.ocell-acts .more{padding:0 .9rem;min-height:44px}

/* The picker inside the sale form. Same grid as the photos page, but these
   tiles are radio buttons because they are part of one bigger form that gets
   saved in one press, where the photos page acts on each press straight away.

   The input is visually hidden and never display:none, which would take it
   out of the tab order and off a screen reader. */
.opick{display:block;cursor:pointer;position:relative}
.opick input{position:absolute;width:1px;height:1px;opacity:0;margin:0}
.opick:hover .oshot{border-color:var(--ink)}
.opick input:checked ~ .oshot{border-color:var(--yellow);border-width:4px}
.opick input:focus-visible ~ .oshot{outline:3px solid var(--ink);outline-offset:2px}
/* The "no picture" tile, which has no picture in it. Same box, same size, so
   the grid does not go ragged. */
.onone-shot{
  display:grid;place-items:center;text-align:center;padding:.5rem;
  aspect-ratio:1;border:2px solid var(--line);border-radius:2px;
  background:var(--card);font-size:var(--t-s);color:var(--ink-soft);
}

/* The picture library inside the sale form. #158.

   Hidden in the markup and revealed by the script, because searching somebody
   else's library is a request and there is no version of this that works with
   JavaScript off. Everything else in the owner area does. */
.oblib{margin-top:1.6rem}
/* The box and its button on one line from the start: this fold is full width,
   so even at 390 there is room for a text box and a short pill beside it. It
   wraps rather than squashing if a translation ever makes the word longer. */
.oblib-ask{display:flex;flex-wrap:wrap;gap:.6rem;align-items:stretch}
.oblib-ask .in{flex:1 1 12rem;margin:0}
.oblib-ask .more{flex:none;padding:0 1.2rem;min-height:44px}
/* Empty until the script has something to say, and it must not reserve a line
   while it is empty or the grid jumps down the moment anybody searches. */
.oblib-said:empty{display:none}
.oblib-said{margin-top:.7rem}
.oblib-grid{margin-top:.9rem}
/* A result is a button, so it needs the button taken off it. */
.obhit{
  display:block;width:100%;padding:0;border:0;background:none;
  cursor:pointer;font:inherit;color:inherit;
}
.obhit:hover .oshot{border-color:var(--ink)}
.obhit:focus-visible .oshot{outline:3px solid var(--ink);outline-offset:2px}
.obhit.is-on .oshot{border-color:var(--yellow);border-width:4px}
.opick input:checked ~ .onone-shot{border-color:var(--yellow);border-width:4px}
@media (min-width:620px){
  .ogrid{grid-template-columns:repeat(3,1fr)}
}
@media (min-width:1000px){
  .ogrid{grid-template-columns:repeat(5,1fr)}
}

/* The back link out of a form. It was a bare <a> at 21px tall, which is half
   a thumb, and it is the control somebody reaches for when they have changed
   their mind on a phone. Padded to a real target without turning it into a
   third button competing with Save. */
.owork .link{
  display:inline-flex;align-items:center;min-height:44px;
  margin-bottom:.4rem;
}

/* The roll of shop names on the desk. Two columns of 28px rows, which is a
   miss on a phone and pre-dates the owner area. The list is links to other
   shops' pages, so it is a real target and not decoration. */
.names a{display:inline-flex;align-items:center;min-height:44px}

/* ----------------------------------------------------------------
   HOW YOUR PAGE WORKS : the owners' guide (#179)

   Not drawn in the prototype. It is built out of pieces this
   stylesheet already has: the join page's navy head, the .jobs grid
   for the three kinds of thing, and the legal page's ledger rows for
   the contents. What is new here is the step list and the note, and
   both of those are shapes nothing else on the site needed.

   READ ON A PHONE, IN A SHOP, WITH ONE HAND. Every row in the
   contents is a 44px target, the steps are numbered in type big
   enough to find your place again after you have looked away, and
   the whole thing prints, because some of these owners want a sheet
   of paper beside the till.
   ---------------------------------------------------------------- */

/* the contents. Rows rather than a bulleted list: the second line is
   when you would use the thing, which is what somebody scanning for
   their own job actually matches on. */
.hcon{padding:var(--band) 0;background:var(--paper-2);border-block:1px solid var(--line)}
.hcon h2{font-size:var(--t-l);max-width:22ch;margin-bottom:var(--sp-xs)}
.htoc{margin:0;padding:0;list-style:none;border-top:1px solid var(--line)}
.htoc li{border-bottom:1px solid var(--line)}
.htoc a{
  display:flex;flex-direction:column;gap:.15rem;
  min-height:44px;justify-content:center;padding:.9rem 0;
  text-decoration:none;color:inherit;
}
.htoc a b{font-family:var(--disp);font-weight:800;font-size:var(--t-b)}
.htoc a span{color:var(--ink-soft);font-size:var(--t-s);line-height:1.45}
.htoc a:hover b,.htoc a:focus-visible b{box-shadow:inset 0 -.12em 0 var(--yellow)}
.htoc-sign{margin-top:var(--sp-xs);color:var(--ink-soft);max-width:52ch}

/* one job. The rule between them is the divider; the last one runs
   into the stuck band, which has its own ground. */
.hjob{padding:var(--band) 0 0}
.hjob-in > h2{font-size:var(--t-l);max-width:20ch;margin-bottom:var(--sp-xs)}
.hjob + .hjob .hjob-in{border-top:1px solid var(--line);padding-top:var(--band)}
.hjob + .hjob{padding-top:0}

/* what it is for, and when. Two headings that are labels rather than
   type: the same quiet uppercase the owner forms use for a field. */
.hwhat{display:grid;gap:var(--sp-xs);margin-bottom:var(--sp-m)}
.hwhat h3{
  font-family:var(--body);font-size:var(--t-s);font-weight:700;
  text-transform:uppercase;letter-spacing:.08em;color:var(--ink-soft);
  margin:0 0 .3rem;
}
.hwhat p{margin:0;max-width:52ch;line-height:1.62}

/* the steps. The numeral is the thing you find your place with after
   you have looked away at the shop, so it is display weight and it
   sits in its own column rather than hanging off the text. */
.hsteps{margin:0;padding:0;list-style:none;counter-reset:hstep}
.hsteps li{
  counter-increment:hstep;
  display:grid;grid-template-columns:2.2rem 1fr;gap:.2rem 1rem;
  padding:.75rem 0;max-width:58ch;line-height:1.55;
}
.hsteps li + li{border-top:1px solid var(--line)}
.hsteps li::before{
  content:counter(hstep);
  font-family:var(--disp);font-weight:800;font-size:1.35rem;line-height:1.3;
  color:var(--yellow);
}
/* The button names are quoted off the screen, so they are drawn as
   the screen draws them rather than as emphasis in a sentence. */
.hsteps b,.hnote b{font-weight:700;color:var(--ink)}

.hnote{
  margin:var(--sp-m) 0 0;padding:.9rem 0 .9rem 1.1rem;
  border-left:3px solid var(--yellow);
  max-width:56ch;line-height:1.62;color:var(--ink-soft);
}
.hgo{margin:var(--sp-xs) 0 0}

.hstuck{padding:var(--band) 0}
.hstuck h2{font-size:var(--t-l);max-width:22ch;margin-bottom:var(--sp-xs)}
.hstuck p{max-width:56ch;line-height:1.62;margin:0 0 1em}

@media (min-width:620px){
  .hwhat{grid-template-columns:1fr 1fr;gap:var(--sp-m)}
  .hsteps li{grid-template-columns:2.6rem 1fr}
}

/* ----------------------------------------------------------------
   PRINT : the guide on paper, and nothing else

   Some of these owners want a sheet beside the till. The only page
   on the site anybody would print is this one, so the rules are
   written for it and scoped to nothing: a print of any other page
   simply loses the chrome, which is an improvement there too.

   Backgrounds are dropped rather than forced. A browser prints
   without backgrounds by default unless the person ticks the box,
   and a design that only works with the box ticked does not work.
   ---------------------------------------------------------------- */
@media print{
  /* The weather band is its own element outside the header, so hiding
     .hdr alone left 17 degrees and a cloud on the top of the sheet.
     Found in a print screenshot, not by reading this list. */
  .hdr,.nowout,.ft,.thin,.crumb,.hgo,.jhead .lede,.htoc-sign,.hstuck .cta,
  .skip,.find,.abar{display:none}

  html,body{background:#fff;color:#000}
  .jhead{background:#fff;color:#000;border-bottom:2px solid #000}
  .jhead h1{color:#000;font-size:2rem;max-width:none}
  .jhead h1 em{box-shadow:none}
  .jhead .kick{color:#000}
  .hcon{background:#fff}

  /* .rise is opacity:0 until the scroll observer clears it, and a printed
     page does not scroll. !important because GSAP writes the fade as an
     inline style the moment it builds the tween, and a stylesheet cannot
     beat an inline style any other way. Measured before and after: 14 of
     the 46 risers on this page printed blank without it. */
  .js .rise{opacity:1!important;transform:none!important}

  /* Paper is not a phone. The screen sizes are generous because a thumb
     needs room; a printed sheet needs none of that, and every rem of it
     is another page somebody has to carry. */
  body{font-size:10pt;line-height:1.4}
  .hjob,.hcon,.hstuck{padding:.35cm 0;break-inside:avoid}
  .hjob + .hjob .hjob-in{padding-top:.35cm;border-top:1px solid #000}
  .hjob-in > h2,.hcon h2,.hstuck h2{font-size:13pt;margin-bottom:.15cm}
  .hwhat{grid-template-columns:1fr 1fr;gap:.4cm;margin-bottom:.25cm}
  .hwhat h3{font-size:8pt;margin-bottom:.05cm}
  .hwhat p,.hsteps li,.hnote,.hstuck p{max-width:none}
  .htoc a{min-height:0;padding:.08cm 0}
  .htoc a b{font-size:10pt}
  .htoc a span{font-size:9pt}
  .hsteps li{padding:.08cm 0;grid-template-columns:1.4rem 1fr}
  .hsteps li::before{font-size:11pt}
  .hnote{margin-top:.25cm;padding:.15cm 0 .15cm .4cm}
  .does-in,.jhead-in{padding:.35cm 0}
  .jobs{gap:.3cm}
  .hsteps li::before{color:#000}
  .hnote{border-left:2px solid #000;color:#000}
  a{text-decoration:none;color:#000}

  /* The quiet greys are a screen device. On paper they photocopy away,
     and half of this page is help text set in one of them. */
  .hwhat p,.hsteps li,.hnote,.hstuck p,.htoc a span,.job p,.hwhat h3{color:#000}

  /* Three across instead of one down the page. The screen keeps these
     stacked until 1000px because a phone has one column; a sheet of A4
     is 794px wide and has room for all three. */
  .jobs{grid-template-columns:repeat(3,1fr);column-gap:.5cm}
}

/* The shop's own words for what it sells (#198). A quiet row under the
   description, not pills: a pill on this site is something you press, and
   these are not links. Making them look pressable would be a promise the
   page does not keep. */
.shop-tags{
  display:flex;flex-wrap:wrap;gap:.4rem .9rem;
  margin:0 0 var(--sp-m);padding:0;list-style:none;
  font-size:var(--t-s);color:var(--ink-soft);
}
.shop-tags li{position:relative}
.shop-tags li + li::before{
  content:"";position:absolute;left:-.48rem;top:50%;
  width:3px;height:3px;margin-top:-1.5px;border-radius:50%;
  background:var(--yellow);
}

/* THE STEP'S WORDS. The <li> is a grid and the numeral is its first item, so
   the words need one box of their own: without it every <b> naming a button
   became a grid item and broke one word per line. */
.hsteps li > span{display:block}

/* ----------------------------------------------------------------
   THE FLOATING CONTENTS (#179)

   Somebody eight jobs down wants the one they came for, and the
   contents list is nine screens above them.

   A <details>, so it works with no script: the browser owns the
   open state, the keyboard gets it free, and there is no class for
   anybody to keep in step. On a phone it is a 44px button in the
   corner; from 1000px there is room to leave it open down the side.

   Fixed, not sticky. Sticky needs a positioned parent that spans
   the scroll, and this page is a stack of full-width sections with
   no such wrapper. --z-sticky is the layer under the header, so the
   header and the search panel still come over the top of it.
   ---------------------------------------------------------------- */
.hjump{
  position:fixed;right:var(--gut);bottom:var(--gut);z-index:var(--z-sticky);
  max-width:calc(100vw - (var(--gut)*2));
}
.hjump summary{
  display:inline-flex;align-items:center;justify-content:center;gap:.4rem;
  min-height:44px;padding:0 1.1rem;cursor:pointer;list-style:none;
  font-family:var(--body);font-weight:700;font-size:var(--t-s);
  letter-spacing:.04em;text-transform:uppercase;
  color:var(--paper);background:var(--ink);
  border:1px solid var(--ink);border-radius:999px;
  box-shadow:0 2px 10px rgb(0 0 0 / .18);
}
.hjump summary::-webkit-details-marker{display:none}
.hjump summary::after{content:"\25B2";font-size:.6em;line-height:1}
.hjump[open] summary::after{content:"\25BC"}
/* --on-navy goes with it, not --paper. The navy pair is fixed-palette and
   does not flip with the theme; --paper does, so in dark mode this painted
   near-black words on navy at 1.18:1. The contrast walk found it, hovered,
   which is the state nothing used to be measured in. */
.hjump summary:hover,.hjump summary:focus-visible{
  background:var(--navy);border-color:var(--navy);color:var(--on-navy);
}

.hjump-in{
  margin-bottom:.5rem;padding:.6rem .2rem;
  background:var(--card);border:1px solid var(--line);border-radius:14px;
  box-shadow:0 6px 24px rgb(0 0 0 / .16);
  max-height:min(60vh,26rem);overflow-y:auto;
}
/* The panel is drawn ABOVE the button on a phone, so it opens away from the
   thumb rather than under it. Source order is panel then button for the same
   reason a fold's own content follows its summary; column-reverse puts the
   button back on the bottom without moving it in the DOM. */
.hjump[open]{display:flex;flex-direction:column-reverse;align-items:flex-end}
.hjump-list{margin:0;padding:0;list-style:none;min-width:14rem}
.hjump-list a{
  display:flex;align-items:center;min-height:44px;padding:0 1rem;
  font-size:var(--t-s);line-height:1.3;color:var(--ink);text-decoration:none;
}
.hjump-list a:hover,.hjump-list a:focus-visible{background:var(--paper-2)}

@media print{
  .hjump{display:none}
}

/* ----------------------------------------------------------------
   THE GUIDE ON A WIDE SCREEN

   Rob, session 37, on the first version: you have the whole wrapper
   for width, use it. He was right. Every block was one narrow column
   down the left of 1160px with the right half empty, because the
   measures were capped for a phone and never released.

   So from 1000px a job is two columns: what it is and when you would
   use it on the left, the steps and the note on the right. Both
   columns keep a readable line, which is the reason the caps existed;
   what was wrong was stacking them in a space wide enough for both.
   ---------------------------------------------------------------- */
@media (min-width:1000px){
  .hjob-in{
    display:grid;grid-template-columns:22rem 1fr;
    column-gap:var(--band);align-items:start;
  }
  /* The heading spans both, so a job reads as one thing rather than
     two columns that happen to be next to each other. */
  .hjob-in > h2{grid-column:1 / -1;max-width:28ch}

  /* Stacked inside its own column now, not side by side in the page. */
  .hwhat{grid-template-columns:1fr;gap:var(--sp-m);margin-bottom:0}
  .hwhat p{max-width:none}

  .hsteps{grid-column:2}
  .hsteps li{max-width:none;grid-template-columns:2.6rem 1fr}
  .hnote{grid-column:2;max-width:none;margin-top:var(--sp-m)}

  /* Two columns, filled DOWN and then across rather than across and
     then down. A contents list is read in order, and row-major puts
     the second job at the top of the right column where somebody
     reading the left column straight down never meets it. */
  .htoc{
    display:grid;grid-template-columns:1fr 1fr;column-gap:var(--band);
    grid-template-rows:repeat(5,auto);grid-auto-flow:column;
  }
  .hcon h2,.hstuck h2,.does-in h2{max-width:28ch}
  .hstuck p,.htoc-sign{max-width:70ch}
}
