/* Art by McCarthy.
   Gallery logic: the wall is quiet, the work is loud. Everything here is
   built to get out of the way of a photograph -- no borders on the art, no
   shadows, no cropping. The one saturated colour in the whole stylesheet is
   the red dot, which in a gallery means sold and means nothing else. */
:root{
  --wall:#FAFAF7;
  --paper:#FFFFFF;
  --ink:#171A18;
  --muted:#6E736E;
  --line:#E4E3DD;
  --rule:#CFCEC6;
  --accent:#2F4A57;      /* slate blue, for links and the one button */
  --on-accent:#fff;      /* what is legible ON that slate; it flips in dark */
  --dot:#C0392B;         /* sold. used nowhere else. */
  --focus:#2F4A57;

  /* Surfaces that used to be hardcoded, named so a theme can move them. Each
     is a shade OF the paper, not a colour: a hovered row and a table head are
     the same sheet caught at a different angle. */
  /* The rail is dark in BOTH themes, so these do not move -- but the arc that
     folds into the page has to match them exactly, and two hardcoded copies of
     a colour eventually stop being the same colour. */
  --rail-bg:#141A18;
  --rail-edge:rgba(255,255,255,.10);   /* the hairline down the rail's edge */
  --paper-hi:#FBFBF9;    /* a row under the cursor, a message already sent */
  --panel-soft:#F1F0EA;  /* a quiet panel ON the wall: Previous/Next */
  --paper-lo:#FCFCFA;    /* the holding pen, a draft of a surface */
  --head:#F7F7F4;        /* the heading row of a table */
  --admin-bg:#F3F4F2;    /* the studio's ground, a shade off the shop's */
  --draft-stripe:#F7F4EE;/* the barber's pole on an unpublished row */
  /* The three things a screen ever has to say back. Kept as background/edge
     pairs so a theme moves both together and never leaves dark text on a dark
     tint. */
  --accent-hi:#253c47;   /* the slate, pressed. In dark it goes LIGHTER, not darker */
  --danger:#A3372B;      /* delete, failed to send. Not the sold dot. */
  --pick-on:#F4FCFD;     /* the chosen one in a row of choices */
  --warn-bg:#FFF6E5;  --warn-line:#E8D5A8;
  --err-bg:#FBE9E7;   --err-line:#E0B4AE;
  --ok-bg:#E6F5F2;    --ok-line:#B7DED6;  --ok-ink:#0E6E60;

  /* The landing palette is sampled from the collage on the front page --
     the turquoise ground, the chartreuse line, the yellow lens, the plum, and
     the near-black of the shadow box. It is used on the dark entry and in the
     viewer ONLY; the wall below stays quiet, and red is still just "sold". */
  --ground:#16232E;        /* lifted off black: the entry is dim, not unlit */
  --ground-deep:#0A121A;
  --turq:#09BBD5;
  --turq-lo:#1ECAE1;
  --chart:#BED566;
  --lens:#F8F3C6;
  --plum:#7C6779;
  --gold:#F2B134;        /* the fish's tail, the moon */

  /* Mount colours for the offset blocks. Deliberately NOT the collage colours
     at full strength -- those belong in the paintings, and a saturated slab of
     them behind a frame reads as a sticker. These are mat-board tones: the same
     hues, desaturated and darkened to sit under the work rather than shout. */
  --mat-teal:#7FA5AC;
  --mat-sage:#A6B189;
  --mat-plum:#9C8598;    /* was an ochre; it read as brown, which is in nothing she paints */
}

/* The mark is a photograph of a painted tile, so it carries its own light
   ground wherever it goes. On a dark page that reads as a bright chip floating
   free; the same hairline the dark entry already gives it seats it again.
   Written twice for the same reason the token blocks are -- see above. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .wordmark .mark,
  :root:not([data-theme="light"]) .adminbrand img{box-shadow:0 0 0 1px rgba(237,241,240,.16)}
}
:root[data-theme="dark"] .wordmark .mark,
:root[data-theme="dark"] .adminbrand img{box-shadow:0 0 0 1px rgba(237,241,240,.16)}

/* ---------- the "this is a copy" band ----------
   Hazard stripes, because this is the one thing on the site that must not be
   mistaken for anything else and the palette has no colour to spare -- red is
   sold, turquoise is available, gold is a deadline. Diagonal stripes belong to
   no painting and mean "works in progress" to everyone.

   It sits at the very top of the page, above the wordmark and above the rail,
   on the shop and in the studio alike. It is not dismissible on purpose. */
.envband{
  display:flex; align-items:center; gap:8px 14px; flex-wrap:wrap;
  padding:7px clamp(12px,4vw,20px);
  font:600 11.5px/1.5 "Karla",sans-serif; letter-spacing:.06em;
  color:#2A1F12; border-bottom:1px solid #8A6A2E;
  background:repeating-linear-gradient(135deg,#F6D98A,#F6D98A 11px,#EFC95F 11px,#EFC95F 22px);
}
.envband strong{text-transform:uppercase; letter-spacing:.16em;
  background:#2A1F12; color:#F6D98A; padding:3px 9px; border-radius:2px}
.envband span{margin-right:auto; font-weight:500; letter-spacing:.02em; text-transform:none}
.envband a{color:#2A1F12; font-weight:700; white-space:nowrap}
/* The stripes stay the same in the dark theme: a hazard band that changed with
   the lights would be a hazard band you stop noticing. */
@media print{ .envband{display:none} }

/* ---------- the dark-mode button ----------
   One button, one glyph, and the glyph is the ACTION: a moon while the page is
   light, a sun while it is dark. They swap on press, turning a little as they
   go, which is the only thing on the page that says the press landed.

   Quiet on purpose: it inherits the colour of the nav it sits in and is no
   bigger than a line of text beside it. A theme control is a preference, not a
   feature, and it should not compete with the work. */
.tsw{
  -webkit-appearance:none; appearance:none; border:0; margin:0; padding:7px;
  background:none; color:inherit; cursor:pointer; line-height:0; border-radius:50%;
  display:inline-flex; align-items:center; justify-content:center;
  position:relative; width:32px; height:32px; box-sizing:border-box;
  opacity:.6; transition:opacity .15s ease, background .15s ease;
}
/* The hover wash is a TINT of the button's own colour, never the colour: a
   filled circle behind a glyph drawn in the same ink would swallow it. No
   fallback declaration on purpose -- where color-mix is not understood the
   line is dropped and the button simply brightens, which is the right way to
   fail. A solid disc would not be. */
.tsw:hover{opacity:1; background:color-mix(in srgb, currentColor 12%, transparent)}
.tsw svg{
  width:17px; height:17px; position:absolute;
  transition:opacity .2s ease, transform .28s cubic-bezier(.34,1.56,.64,1);
}
/* Only the one it is about to do is drawn. Both are in the markup so the swap
   is a fade between two things already there, not a repaint. */
.tsw[data-goes=dark]  .tsw-sun {opacity:0; transform:rotate(-60deg) scale(.6)}
.tsw[data-goes=dark]  .tsw-moon{opacity:1; transform:none}
.tsw[data-goes=light] .tsw-moon{opacity:0; transform:rotate(60deg) scale(.6)}
.tsw[data-goes=light] .tsw-sun {opacity:1; transform:none}
/* Before the script runs there is no data-goes. The moon is the honest guess:
   most readers are on a light page, and a glyph is better than a blank. */
.tsw:not([data-goes]) .tsw-sun{opacity:0}
.tsw:focus-visible{outline:2px solid var(--focus); outline-offset:2px; opacity:1}
@media (prefers-reduced-motion:reduce){
  .tsw svg{transition:opacity .2s ease}
  .tsw[data-goes=dark] .tsw-sun,.tsw[data-goes=light] .tsw-moon{transform:none}
}
/* In the studio rail it shares the top row with the logo; on the dark entry it
   takes the same soft white as the nav links beside it. */
.home .masthead .tsw{color:rgba(244,246,245,.7)}
/* The home page's "Light mode -- the pages below are light" note; see
   theme.js. Hangs just under the sticky header (--navh is published by the
   inline script in base.html), on the dark entry, in the entry's own type. */
.tsw-note{
  position:fixed; z-index:40; right:clamp(16px,5vw,56px); top:calc(var(--navh,80px) + 6px);
  margin:0; padding:8px 12px; border:1px solid rgba(9,187,213,.55); border-radius:2px;
  background:rgba(10,18,26,.92); color:#F4F6F5;
  font:600 12px/1.2 "Karla",sans-serif; letter-spacing:.05em;
  opacity:0; transform:translateY(-4px); pointer-events:none;
  transition:opacity .35s ease, transform .35s ease;
}
.tsw-note.show{opacity:1; transform:none}
@media (prefers-reduced-motion:reduce){.tsw-note{transition:none; transform:none}}
@media (max-width:560px){.tsw-note{left:16px; right:16px; text-align:center}}
.home .masthead .tsw:hover{color:var(--turq-lo)}

/* ---------- dark ----------
   THE TWO BLOCKS BELOW MUST STAY IDENTICAL. The first follows the machine's
   own setting, the second follows hers when she has overruled it, and CSS has
   no way to share one body of declarations between two selectors. They are
   written from one source; if you edit one, edit the other.

   The system block is guarded by :not([data-theme="light"]) so that choosing
   light on a machine set to dark actually sticks -- without the guard the
   media query would win every time and the toggle would only appear to work in
   one direction. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
  /* A GALLERY AT NIGHT, not an inverted document. The greens and blues are the
     same family as the dark entry the landing page already opens with
     (--ground / --ground-deep), so the shop does not become a different site
     after dark -- it becomes the same room with the lights down.

     The work is still the loud thing. Every surface here is a shade of the
     same near-black so a photograph sitting on it is the brightest object on
     the screen, which is the whole argument of the light theme too. */
  --wall:#0F161A;
  --paper:#171F25;
  --paper-hi:#1D262D;
  --panel-soft:#1A232A;
  --paper-lo:#141B21;
  --head:#1A232A;
  --admin-bg:#0B1115;
  --ink:#E6EBE9;
  --muted:#94A19C;
  --line:#253037;
  --rule:#3B4750;
  /* Slate cannot be a link colour on near-black, so it lifts to the pale end
     of the same hue -- and the text ON it goes dark, which is what --on-accent
     is for. */
  --accent:#7EB8C9;
  --on-accent:#07161C;
  --focus:#7EB8C9;
  /* Still only sold, just lifted enough to read as red rather than as brown. */
  --dot:#E2604E;
  --plum:#AC93A9;
  --accent-hi:#9ACBD9;
  --danger:#F08A7A;
  --pick-on:#12262C;
  --draft-stripe:#1C2620;
  --warn-bg:#2A2414;  --warn-line:#5A4A22;
  --err-bg:#331F1C;   --err-line:#5F352F;
  --ok-bg:#12302B;    --ok-line:#2B6157;  --ok-ink:#79D3C1;
  }
}
:root[data-theme="dark"]{
  /* A GALLERY AT NIGHT, not an inverted document. The greens and blues are the
     same family as the dark entry the landing page already opens with
     (--ground / --ground-deep), so the shop does not become a different site
     after dark -- it becomes the same room with the lights down.

     The work is still the loud thing. Every surface here is a shade of the
     same near-black so a photograph sitting on it is the brightest object on
     the screen, which is the whole argument of the light theme too. */
  --wall:#0F161A;
  --paper:#171F25;
  --paper-hi:#1D262D;
  --panel-soft:#1A232A;
  --paper-lo:#141B21;
  --head:#1A232A;
  --admin-bg:#0B1115;
  --ink:#E6EBE9;
  --muted:#94A19C;
  --line:#253037;
  --rule:#3B4750;
  /* Slate cannot be a link colour on near-black, so it lifts to the pale end
     of the same hue -- and the text ON it goes dark, which is what --on-accent
     is for. */
  --accent:#7EB8C9;
  --on-accent:#07161C;
  --focus:#7EB8C9;
  /* Still only sold, just lifted enough to read as red rather than as brown. */
  --dot:#E2604E;
  --plum:#AC93A9;
  --accent-hi:#9ACBD9;
  --danger:#F08A7A;
  --pick-on:#12262C;
  --draft-stripe:#1C2620;
  --warn-bg:#2A2414;  --warn-line:#5A4A22;
  --err-bg:#331F1C;   --err-line:#5F352F;
  --ok-bg:#12302B;    --ok-line:#2B6157;  --ok-ink:#79D3C1;
}

/* Tell the browser which way round we are, so form controls, scrollbars and
   the caret follow the page instead of staying light on a dark sheet. */
:root{color-scheme:light}
@media (prefers-color-scheme:dark){ :root:not([data-theme="light"]){color-scheme:dark} }
:root[data-theme="dark"]{color-scheme:dark}
:root[data-theme="light"]{color-scheme:light}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; scroll-padding-top:calc(var(--navh, 113px) + 14px)}
/* "Available work" on the landing page drops the reader past the hero to the
   wall. Jumping there cuts -- the page simply becomes a different page and you
   have to work out what moved. Gliding shows the hero leaving, so the wall
   arrives as somewhere you travelled to.

   GUARDED, and not as a formality: for some readers animated scrolling is
   nausea, not polish. Without the guard this would be smooth for everyone and
   there is no way to opt out. The jump still works, instantly, for them.

   One known wart: this also animates the Skip to content link. Focus still
   lands immediately and correctly -- only the scroll is animated -- and
   scroll-behavior belongs to the scrolling box, so there is no way to smooth
   one anchor and not another in CSS alone. Not worth a script. */
@media (prefers-reduced-motion:no-preference){
  html{scroll-behavior:smooth}
}
/* Land with air above the heading rather than flush against the top edge --
   "Available" hard against the viewport reads as a page that has been cut off
   rather than one that has arrived. */
.wall[id]{scroll-margin-top:clamp(14px,4vh,40px)}

/* ---------- moving between pages ----------
   This is a multi-page app, so navigation is a real document load. Where the
   browser supports it, the cross-document View Transition dissolves one page
   into the next and there is no white flash at all; where it does not, JS
   marks the document .no-vt and a short fade stands in. Both are decoration
   and neither delays anything. */
@view-transition{navigation:auto}
::view-transition-old(root){animation:vt-out .16s ease both}
::view-transition-new(root){animation:vt-in .3s ease both}
@keyframes vt-out{to{opacity:0}}
@keyframes vt-in{from{opacity:0; transform:translateY(6px)} to{opacity:1; transform:none}}
.no-vt main{animation:vt-in .3s ease both}
/* The light/dark switch borrows the same machinery for a plain dissolve --
   no slide, the page is not going anywhere. See abmTheme() in theme.js. */
html.theme-vt::view-transition-old(root){animation:none}
html.theme-vt::view-transition-new(root){animation:theme-in .5s ease both}
@keyframes theme-in{from{opacity:0}}
html.theme-ease *,html.theme-ease *::before,html.theme-ease *::after{
  transition:background-color .45s ease, color .45s ease, border-color .45s ease,
             fill .45s ease, stroke .45s ease !important;
}
body{
  margin:0; background:var(--wall); color:var(--ink);
  font:400 17px/1.6 "Karla","Helvetica Neue",Arial,sans-serif;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%; height:auto; display:block}
a{color:var(--accent); text-underline-offset:2px}
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{
  outline:2px solid var(--focus); outline-offset:2px;
}
.skip{position:absolute; left:-9999px}
.skip:focus{left:12px; top:12px; background:var(--paper); padding:8px 12px; z-index:40}

/* ---------- masthead ---------- */
.masthead{
  display:flex; flex-wrap:wrap; align-items:center; gap:12px 26px;
  padding:24px clamp(18px,5vw,56px) 22px; border-bottom:1px solid var(--line);
  /* Opaque, so the fixed pattern layer behind the page stops at the rule under
     the wordmark instead of running up through the logo and the nav. */
  background:var(--wall);
}
/* The mark is the face out of the collage -- the glasses, the mouth, the
   turquoise -- cropped square. Cropping is forbidden for the WORK on the wall;
   a logo is a different object, and this one has to survive at 32px. */
.wordmark{
  display:flex; align-items:center; gap:16px;
  text-decoration:none; color:var(--ink); margin-right:auto; line-height:1;
}
.wordmark .mark{
  width:56px; height:56px; display:block; border-radius:2px;
}
/* The bug sits left of the copy stack, so the stack's own optical centre --
   not its line-box, which the swashes and descenders blow out on both sides --
   is what has to line up with the middle of the mark. */
.wordmark-txt{display:flex; flex-direction:column; padding-top:4px}
/* "Art by" is a label; the name is signed. Script faces carry a small
   x-height, so this is set far larger than a serif would be at the same
   optical weight, and the line-box is trimmed back to sit tight under it. */
.wordmark-pre{
  font:700 11px/1 "Karla",sans-serif; letter-spacing:.28em; text-transform:uppercase;
  color:var(--muted); margin-bottom:7px;
}
.wordmark-name{
  font-family:"Mrs Saint Delafield","Snell Roundhand","Apple Chancery",cursive;
  font-size:54px; line-height:1; font-weight:400; white-space:nowrap;
  /* The swashes on the M and C climb well above the cap line, so there is no
     room to steal at the top -- only the descenders below are trimmed back. */
  margin:0 0 -14px; padding-left:1px;
}
.wordmark:hover .wordmark-name{opacity:.82}

/* ---- the signature, drawn ----
   One <path> per letter, each with pathLength="1" so a single dash rule works
   whatever the real outline length is. The dash runs off to reveal the letter's
   outline in writing order, then the fill arrives underneath and the tracing
   stroke leaves -- otherwise the finished name would sit a hair heavier than
   the face actually is. Letters carry their own --t (start) and --d (duration)
   from make_signature.py, weighted by width, so the M takes its time and the t
   is over quickly. */
.wordmark-sig{
  display:block; height:54px; width:auto; overflow:visible;
  margin:0 0 -10px; color:currentColor;
}
.wordmark-sig .sg{
  stroke-dasharray:1; stroke-dashoffset:1; fill-opacity:0;
}
.signing .wordmark-sig .sg{
  animation:
    sig-draw var(--d) linear var(--t) forwards,
    sig-ink .28s ease-out calc(var(--t) + var(--d) * .72) forwards;
}
@keyframes sig-draw{to{stroke-dashoffset:0}}
@keyframes sig-ink{to{fill-opacity:1; stroke-opacity:0}}
/* Not signing -- a repeat visit, no JS, or reduced motion: the name is simply
   already there. This is the resting state, so it must not depend on the
   animation having run. */
.wordmark-sig .sg{fill-opacity:1; stroke-opacity:0; stroke-dashoffset:0}
.signing .wordmark-sig .sg{fill-opacity:0; stroke-opacity:1; stroke-dashoffset:1}
@media (prefers-reduced-motion:reduce){
  .signing .wordmark-sig .sg{
    animation:none; fill-opacity:1; stroke-opacity:0; stroke-dashoffset:0;
  }
}
@media (max-width:560px){
  .wordmark{gap:13px}
  .wordmark .mark{width:46px; height:46px}
  /* Less to trim at the smaller size, and the nav wraps directly beneath it. */
  .wordmark-name{font-size:44px; margin-bottom:-7px}
  .wordmark-sig{height:44px; margin-bottom:-6px}
  .wordmark-pre{font-size:10px; letter-spacing:.24em; margin-bottom:5px}
}
/* Squared boxes, not pills, and each one lifts off a hard offset block in a mat
   colour -- the same trick that mounts the portrait and the enquiry forms. The
   box-shadow is declared transparent at rest so the button never changes size
   when the block appears; only the colour does. */
.masthead nav{display:flex; flex-wrap:wrap; gap:10px}
.masthead nav a{
  position:relative; display:inline-block;
  padding:9px 15px; border:1.5px solid var(--rule); border-radius:2px;
  background:var(--paper); color:var(--muted); text-decoration:none;
  font:600 12.5px/1 "Karla",sans-serif; letter-spacing:.09em; text-transform:uppercase;
  box-shadow:3px 3px 0 0 transparent;
  transition:color .18s ease, border-color .18s ease, background .18s ease,
             box-shadow .18s ease, transform .18s ease;
}
.masthead nav a:hover{
  color:var(--ink); border-color:var(--ink);
  box-shadow:3px 3px 0 0 var(--mat-sage); transform:translate(-1px,-1px);
}
.masthead nav a.on{
  background:var(--ground); border-color:var(--ground); color:#F4F6F5;
  box-shadow:4px 4px 0 0 var(--mat-teal);
}
.masthead nav a.on:hover{box-shadow:4px 4px 0 0 var(--mat-teal); transform:none}

/* On the dark entry the same buttons run in reverse: outlined on the ground
   rather than sitting on paper. */
.home .masthead nav a{
  background:transparent; border-color:rgba(237,241,240,.26); color:rgba(244,246,245,.74);
}
.home .masthead nav a:hover{
  color:#fff; border-color:var(--turq); box-shadow:3px 3px 0 0 rgba(9,187,213,.45);
}
.home .masthead nav a.on{
  background:rgba(255,255,255,.09); border-color:var(--turq); color:#fff;
  box-shadow:4px 4px 0 0 rgba(9,187,213,.5);
}
/* GALLERY is an action, not a page, so it is the one filled button in the
   row: collage turquoise with dark type, lifting off an ink block instead of
   a mat colour. Same square shape and offset trick as its neighbours, so it
   still belongs to the set. margin-left:auto only bites on a phone, where
   the nav is full-width and it drifts to the right edge on its own. The
   extra .home in the selectors outranks the dark-entry overrides below. */
.masthead nav a.gal,.home .masthead nav a.gal{
  display:inline-flex; align-items:center; gap:7px; margin-left:auto;
  background:var(--turq); border-color:var(--turq); color:#07161C;
  box-shadow:3px 3px 0 0 var(--ink);
}
.home .masthead nav a.gal{box-shadow:3px 3px 0 0 rgba(0,0,0,.55)}
.masthead nav a.gal:hover,.home .masthead nav a.gal:hover{
  background:var(--turq-lo); border-color:var(--turq-lo); color:#07161C;
  box-shadow:4px 4px 0 0 var(--ink); transform:translate(-1px,-1px);
}
.home .masthead nav a.gal:hover{box-shadow:4px 4px 0 0 rgba(0,0,0,.6)}
.masthead nav a.gal svg{flex:0 0 auto; margin-top:-1px}
@media (prefers-reduced-motion:reduce){
  .masthead nav a,.gm-tab{transition:none}
  .masthead nav a:hover,.gm-tab:hover{transform:none}
}
@media (max-width:560px){
  .masthead nav{gap:8px}
  .masthead nav a{padding:8px 11px; font-size:11.5px; letter-spacing:.07em}
  /* The switch rides the top row with the wordmark and the nav drops beneath.
     `order` rather than moving it in the markup, so the reading order on a
     wide screen is untouched; width:100% on the nav makes the break certain
     instead of leaving it to whether four links happen to fit. */
  .masthead .tsw{order:1}
  .masthead nav{order:2; width:100%}
}
/* The narrowest phones: Gallery's icon made the four buttons one hair too
   wide for a single row at 360px. Tighter padding keeps them on one line. */
@media (max-width:389px){
  .masthead nav{gap:6px}
  .masthead nav a{padding:8px 8px; letter-spacing:.05em}
  .masthead nav a.gal{gap:5px}
}

main{padding:clamp(28px,5vw,56px) clamp(18px,5vw,56px) 72px}

/* ---------- intro ---------- */
.intro{max-width:60ch; margin:0 0 clamp(28px,5vw,48px)}
.intro h1,.prose h1,.facts h1,.co-title{
  font-family:"Newsreader",Georgia,serif; font-weight:400;
  font-size:clamp(2rem,4.5vw,2.9rem); line-height:1.1; margin:0 0 .5rem;
  letter-spacing:-.01em;
}
.lede{color:var(--muted); margin:0; font-size:1.05rem}

/* ---------- the wall ----------
   Columns, not a row grid: paintings are not one aspect ratio and cropping
   them to make a tidy grid is the one thing a gallery site must not do. */
.grid{columns:3 300px; column-gap:clamp(18px,2.6vw,34px)}
/* THE FRONT PAGE HANGS THEM AT A FIXED SIZE. Everywhere else the wall is a
   column layout that fills whatever width it is given, which is right for a
   collection of unknown length; on the landing page the pieces are the first
   thing anyone sees and they are all one size in life -- 10.5in square -- so
   they are hung 550px square and the wall is centred under them rather than
   stretched to the window.
   550 is a CEILING, not a fixed size, and the count of columns is set by hand
   rather than by auto-fit. Auto-fit decides how many tracks to make from their
   MINIMUM, which ties the two decisions together: a minimum small enough for
   three on a laptop also gives three narrow ones on a wide screen, and one big
   enough to hold 550 drops to two the moment three no longer fit. Settled
   separately here -- three columns from 1100px and two from 880px, each
   shrinking below 550 as the window narrows, and 550 square once there is room
   for it. One painting at the width of the screen below that. */
.home .grid{
  columns:auto;
  display:grid;
  grid-template-columns:minmax(0, 550px);
  justify-content:center;
  column-gap:clamp(18px,2.6vw,34px);
  row-gap:clamp(22px,3vw,40px);
}
@media (min-width:880px){
  .home .grid{grid-template-columns:repeat(2, minmax(0, 550px))}
}
@media (min-width:1100px){
  .home .grid{grid-template-columns:repeat(3, minmax(0, 550px))}
}
.home .tile{margin:0}
.tile{
  break-inside:avoid; display:block; margin:0 0 clamp(22px,3vw,40px);
  text-decoration:none; color:inherit;
}
/* A SQUARE BOX ON THE WALL. This body of work is square -- 25 of the 26 are
   10.5 x 10.5 and the stored photographs are 600x600 with it -- so a fixed 1:1
   box makes the wall line up exactly instead of one stray 8px-short file
   nudging everything below it down its column.
   CONTAIN, not COVER, and that matters: the wall has never cropped a painting,
   and the day she hangs something that is not square it letterboxes inside the
   box rather than losing its edges. The rule survives, its premise just is not
   true of this particular collection. */
.tile img{
  width:100%; aspect-ratio:1; object-fit:contain;
  background:var(--paper); transition:opacity .3s ease;
}
.tile:hover img{opacity:.88}
.tile .cap{
  display:flex; justify-content:space-between; gap:14px;
  padding-top:10px; font-size:14px;
}
.tile .cap .t{font-family:"Newsreader",Georgia,serif; font-size:16px}
.tile .cap .p{color:var(--muted); white-space:nowrap}
.tile:hover .cap .t{text-decoration:underline}
.cap .dot::before{
  content:""; display:inline-block; width:8px; height:8px; border-radius:50%;
  background:var(--dot); margin-right:6px; vertical-align:1px;
}
.noimg{
  background:var(--paper); border:1px dashed var(--rule); color:var(--muted);
  padding:48px 16px; text-align:center; font-size:13px; letter-spacing:.06em;
}
.tile .noimg{
  aspect-ratio:1; padding:0; display:flex; align-items:center; justify-content:center;
}
.noimg.tall{padding:140px 16px}

/* ---------- one work ---------- */
/* STRETCH, not start. The details column is now full-height on purpose, which
   is what gives the stepper a bottom to hang off -- see .facts below. */
.work{display:grid; grid-template-columns:auto minmax(300px,1fr); gap:clamp(28px,4vw,60px); align-items:stretch}
/* THE PLATES: one photograph at a time, thumbnails in a column to its right
   (2026-09-26). The photograph is capped so a square one fits the screen
   under the header -- it used to run the full column, ~830px on a laptop.
   The stage shrinks to the photograph, so the thumbnails sit against its
   edge rather than out at the end of the column. Every photograph keeps its
   whole frame: contain, never cropped, same rule as the wall. */
.plates{display:flex; align-items:flex-start; gap:14px}
/* ONE FRAME SIZE FOR EVERY PHOTOGRAPH (Paul, 2026-09-26). Photographs of
   different proportions used to resize the stage as you clicked through,
   which slid the thumbnails and the whole details column sideways. The
   stage is now a fixed square -- --plate, sized to fit the screen -- and each
   photograph is fitted inside it (contain: nothing cropped), centred. */
.plates{--plate:min(640px, calc(100vh - var(--navh,90px) - 80px)); min-width:0}
.plate-stage{
  flex:0 1 auto; width:var(--plate); min-width:0;
  display:flex; flex-direction:column; gap:clamp(18px,2.5vw,30px);
}
.plates figure{margin:0}
.plate-stage .plate-img{width:100%; aspect-ratio:1}
.plate-stage picture{display:block; width:100%; height:100%}
.plate-stage img{width:100%; height:100%; object-fit:contain}
.plates.picking .plate{display:none}
.plates.picking .plate.on{display:block; animation:plate-in .25s ease both}
@keyframes plate-in{from{opacity:0}}
.plate-thumbs{flex:0 0 auto; display:flex; flex-direction:column; gap:8px}
.plate-thumb{
  width:64px; height:64px; padding:3px; cursor:pointer;
  border:1.5px solid var(--rule); border-radius:2px; background:var(--paper);
  opacity:.7; transition:opacity .2s ease, border-color .2s ease;
}
.plate-thumb img{width:100%; height:100%; object-fit:contain}
.plate-thumb:hover{opacity:1; border-color:var(--ink)}
.plate-thumb.on{opacity:1; border-color:var(--accent)}
@media (prefers-reduced-motion:reduce){
  .plates.picking .plate.on{animation:none}
  .plate-thumb{transition:none}
}
@media (max-width:560px){
  .plates{gap:8px}
  .plate-thumbs{gap:6px}
  .plate-thumb{width:46px; height:46px; padding:2px}
}
.plates figcaption{padding-top:8px; font-size:13px; color:var(--muted)}
/* Previous/Next is PINNED TO THE FOOT of the column rather than following the
   details. It used to sit directly under the buy panel, which meant it landed
   at a different height on every painting -- a long story pushed it down, a
   sold piece with no price pulled it up -- so cycling through the run made it
   hop about under the cursor. Hung off the bottom it stays put, and since
   every piece is one square photograph the column is the same height on every
   page, so "the bottom" is the same place each time.

   The details keep their sticky behaviour inside .facts-inner; the aside
   itself can no longer be sticky because it is now as tall as its container
   and would have nothing to travel through. */
.facts{display:flex; flex-direction:column; min-height:100%}
/* WHERE THE STICKY DETAILS ARE ALLOWED TO GET TO. position:sticky travels
   inside its containing block, and its containing block used to be the whole
   aside -- so on a long page the details kept going and rode down over the
   Previous/Next at the foot of the column. This holds them: it takes the
   height the stepper does not, so the travel ends exactly where the stepper
   begins. min-height:0 because a flex item will not shrink below its content
   without it. */
.facts-hold{flex:1 1 auto; min-height:0}
.facts-inner{position:sticky; top:calc(var(--navh, 113px) + 22px)}
.stepper{margin-top:auto}
.facts h1{font-size:clamp(1.6rem,3vw,2.1rem)}
.facts dl{display:grid; grid-template-columns:auto 1fr; gap:6px 18px; margin:18px 0 0; font-size:15px}
.facts dt{color:var(--muted)}
.facts dd{margin:0}
.story{margin:20px 0 0; color:var(--ink)}
/* Her writing arrives as paragraphs now rather than as one block, here and
   wherever a collection's About box is shown. */
.story p, .lede p{margin:0 0 .75em}
.story p:last-child, .lede p:last-child{margin-bottom:0}
.buy{margin-top:26px; border-top:1px solid var(--line); padding-top:20px}
.price{font-family:"Newsreader",Georgia,serif; font-size:1.6rem; margin:0 0 14px}
.soldline{font-size:1.05rem; margin:0 0 10px}
/* "Added to your cart": the turquoise of the cart badge, so the banner and the
   number in the header read as the same event. On a fresh add it pops in and
   the button beneath pulses twice; coming back to the page it just sits. */
.added{margin:0 0 12px; font-weight:600; font-size:1.02rem; color:var(--ink)}
/* Copy only, then gone: it fades out and the space closes up behind it. */
.added.is-new{overflow:hidden; animation:addedFade 3.4s ease forwards}
/* Not the dark "Add to cart" button any more: the Gallery button's filled
   turquoise with dark type and an ink block, so the button itself changes. */
.cta.cta-cart{background:var(--turq); border-color:var(--turq); color:#07161C;
  box-shadow:3px 3px 0 0 var(--ink)}
.cta.cta-cart:hover{background:var(--turq-lo); border-color:var(--turq-lo)}
.added.is-new + .cta{animation:ctaPulse 1.1s .45s ease-out 2}
@keyframes addedFade{0%{opacity:0; max-height:2em} 10%,70%{opacity:1; max-height:2em; margin-bottom:12px} 100%{opacity:0; max-height:0; margin-bottom:0}}
@keyframes ctaPulse{0%{box-shadow:3px 3px 0 0 var(--ink), 0 0 0 0 rgba(9,187,213,.6)} 100%{box-shadow:3px 3px 0 0 var(--ink), 0 0 0 16px rgba(9,187,213,0)}}
@media (prefers-reduced-motion: reduce){
  .added.is-new + .cta{animation:none}
}
.reddot{display:inline-block; width:11px; height:11px; border-radius:50%; background:var(--dot); margin-right:8px}
.fine{color:var(--muted); font-size:13.5px; line-height:1.5; margin:12px 0 0}

.cta{
  -webkit-appearance:none; appearance:none; display:inline-block;
  background:var(--accent); color:var(--on-accent); border:1px solid var(--accent);
  border-radius:2px; padding:13px 26px; font:600 15px/1 "Karla",sans-serif;
  letter-spacing:.04em; cursor:pointer; text-decoration:none;
}
.cta:hover{background:var(--accent-hi)}
.cta{transition:background .2s ease, border-color .2s ease, color .2s ease}

/* ---------- cart ----------
   One row per original: the picture, what it is, how long it is held, the
   price. The hold reads in the muted voice until the last two minutes, then
   takes the accent -- never the sold red, which means something else here. */
.cart .cartnote{margin:0 0 22px}
.cart form + .fine{margin-top:16px}
.cartlist{list-style:none; margin:0 0 8px; padding:0; border-top:1px solid var(--line)}
.cartrow{
  display:grid; grid-template-columns:72px 1fr auto; grid-template-rows:auto auto;
  column-gap:16px; align-items:start; padding:16px 0; border-bottom:1px solid var(--line);
}
.cartimg{grid-row:1 / span 2; display:block; width:72px; height:72px; background:var(--panel-soft)}
.cartimg img{width:100%; height:100%; object-fit:contain; display:block}
.cartwhat{grid-row:1 / span 2; min-width:0}
.carttitle{font-family:"Newsreader",Georgia,serif; font-size:1.15rem; color:var(--ink); text-decoration:none}
.carttitle:hover{text-decoration:underline}
.cartwhat .hold{display:inline-block; margin-top:6px; font-size:12.5px; color:var(--muted);
  font-variant-numeric:tabular-nums; letter-spacing:.02em}
.cartwhat .hold.low{color:var(--accent); font-weight:600}
.cartprice{font-family:"Newsreader",Georgia,serif; font-size:1.1rem; white-space:nowrap; text-align:right}
.cartrm{grid-column:3; justify-self:end; margin:0}
.cartrm .link{font-size:12.5px}
.carttotals{display:grid; grid-template-columns:1fr auto; row-gap:8px; margin:18px 0 22px}
.carttotals dd{padding-left:16px}
.carttotals dt,.carttotals dd{margin:0}
.carttotals dd{text-align:right; font-variant-numeric:tabular-nums}
.carttotals .tot{border-top:1px solid var(--line); padding-top:10px;
  font-family:"Newsreader",Georgia,serif; font-size:1.25rem}
/* The cart is a drawing, not a button: no frame, no fill, no offset block --
   just the inked tote with its swipe of turquoise, which tips on hover the
   way a hung picture does when you brush past it. The count rides on its
   shoulder. */
.masthead nav a.cartlink,.home .masthead nav a.cartlink,
.masthead nav a.cartlink.on,.home .masthead nav a.cartlink.on{
  position:relative; display:inline-flex; align-items:center; justify-content:center;
  padding:2px 6px; border:0; background:none; box-shadow:none; color:var(--ink);
  transform-origin:50% 10%}
.home .masthead nav a.cartlink,.home .masthead nav a.cartlink.on{color:#F4F6F5}
.masthead nav a.cartlink:hover,.home .masthead nav a.cartlink:hover,
.masthead nav a.cartlink.on:hover{
  border:0; background:none; box-shadow:none; color:var(--ink); transform:rotate(-7deg)}
.home .masthead nav a.cartlink:hover{color:#fff}
.carticon{display:block; overflow:visible}
.carticon .ink{fill:none; stroke:currentColor; stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round}
.carticon .ink.thin{stroke-width:.9; opacity:.55}
.carticon .swatch{fill:var(--turq); opacity:.9}
.masthead nav a.cartlink.on .carticon .swatch{opacity:1}
/* The photograph flying into the cart: square like every frame on the site,
   lifted by a soft shadow, arcing slightly on the way (the overshooting
   curve). Then the Cart button bumps to say it landed. */
.cart-tracer{position:fixed; z-index:3000; object-fit:cover; pointer-events:none;
  box-shadow:0 12px 32px rgba(0,0,0,.28);
  transition:transform .8s cubic-bezier(.5,-0.25,.3,1.15), opacity .8s ease;
  will-change:transform, opacity}
.cart-bump{animation:cartBump .5s ease}
@keyframes cartBump{0%{transform:rotate(0)} 30%{transform:rotate(-12deg) scale(1.15)} 65%{transform:rotate(6deg)} 100%{transform:rotate(0)}}

/* ---------- checkout ----------
   Her page around Stripe's fields: the form on the left in numbered steps,
   the order beside it. The step numbers are small squares in the accent --
   her buttons are square, so her numbers are too. */
.checkout{display:grid; grid-template-columns:minmax(0,1fr) 340px; gap:48px; align-items:start;
  max-width:1060px; margin:0 auto; padding:40px clamp(18px,5vw,56px) 64px}
.checkout .co-title{grid-column:1 / -1; margin:0 0 -18px}
.co-step{padding:0 0 26px; margin:0 0 26px; border-bottom:1px solid var(--line)}
.co-step h2{display:flex; align-items:center; gap:12px; margin:0 0 6px;
  font:500 1.3rem/1.2 "Newsreader",Georgia,serif}
.co-n{display:inline-grid; place-items:center; width:26px; height:26px; flex:0 0 26px;
  background:var(--accent); color:var(--on-accent); font:600 13px/1 "Karla",sans-serif;
  box-shadow:2px 2px 0 0 var(--mat-teal)}
.co-step .fine{margin:0 0 16px}
.co-el{min-height:44px}
.co-msg{margin:0 0 16px; padding:12px 14px; border:1.5px solid var(--danger); color:var(--danger);
  background:var(--paper); font-size:14.5px}
.co-pay{width:100%; text-align:center; font-size:16px; padding:16px 26px}
.co-pay:disabled{opacity:.45; cursor:not-allowed}
.co-pay.is-busy{opacity:.7; cursor:progress}
.co-secure{display:flex; gap:8px; align-items:flex-start}
.co-secure svg{flex:0 0 auto; margin-top:3px}
/* Sticks BELOW the masthead (which is itself sticky), the same offset the
   work page's details use; a long cart scrolls inside the box rather than
   running off the bottom of the screen. */
.co-summary{position:sticky; top:calc(var(--navh, 113px) + 22px);
  max-height:calc(100vh - var(--navh, 113px) - 44px); overflow-y:auto; background:var(--paper); border:1px solid var(--line);
  padding:24px; box-shadow:4px 4px 0 0 var(--panel-soft)}
.co-summary h2{margin:0 0 14px; font:500 1.2rem/1.2 "Newsreader",Georgia,serif}
.co-items{list-style:none; margin:0; padding:0; border-top:1px solid var(--line)}
.co-items li{display:grid; grid-template-columns:52px 1fr auto; gap:12px; align-items:center;
  padding:12px 0; border-bottom:1px solid var(--line)}
.co-thumb{width:52px; height:52px; background:var(--panel-soft); display:block}
.co-thumb img{width:100%; height:100%; object-fit:contain; display:block}
.co-what{font-family:"Newsreader",Georgia,serif; font-size:1rem; min-width:0}
.co-what .sub{font-family:"Karla",sans-serif; margin-top:2px}
.co-price{font-family:"Newsreader",Georgia,serif; white-space:nowrap}
.co-summary .carttotals{margin:14px 0 16px}
.co-promo{margin:0 0 14px; font-size:14px}
.co-promo summary{cursor:pointer; color:var(--accent); font-weight:600; list-style:none;
  text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px; width:max-content}
.co-promo summary::-webkit-details-marker{display:none}
.co-promo summary:hover{color:var(--ink)}
.co-promo-row{display:flex; gap:8px; align-items:center; margin-top:10px}
.co-promo-row input{flex:1; min-width:0; text-transform:uppercase; letter-spacing:.04em}
.co-promo-row input::placeholder{text-transform:none; letter-spacing:0}
.co-promo-msg{margin:8px 0 0; color:var(--danger); font-size:13px}
.co-promo-rm{font-size:12.5px; min-height:0; padding:0 0 0 6px; font-weight:500}
.co-hold{font-size:13px; color:var(--muted); font-variant-numeric:tabular-nums; margin:0 0 8px}
.co-hold.low{color:var(--accent); font-weight:600}
@media (max-width:860px){
  .checkout{grid-template-columns:1fr; gap:28px; padding-top:24px}
  .checkout .co-title{margin:0; order:-2}
  .co-summary{position:static; order:-1; max-height:none; overflow:visible}
}
.cartn{position:absolute; top:-6px; right:-6px; min-width:18px; padding:3px 5px; border-radius:9px;
  background:var(--turq); color:#07161C; font:700 11px/1 "Karla",sans-serif; letter-spacing:0;
  text-align:center; box-shadow:0 0 0 2px var(--wall)}
.cartn:empty{display:none}
.home .cartn{box-shadow:none}
@media (max-width:560px){
  .cartrow{grid-template-columns:56px 1fr auto; column-gap:12px}
  .cartimg{width:56px; height:56px}
}

/* ---------- about ----------
   Copy left, portrait right. The portrait column is capped well below the
   width a painting gets: on a gallery's About page the artist is the caption,
   not the exhibit. */
/* The collage sits behind everything and takes no clicks. It bleeds past the
   section so the shapes run off the page rather than stopping at a tidy edge. */
.page-bg{
  position:fixed; inset:0; width:100%; height:100%; z-index:0; pointer-events:none;
}
/* Named children only. A `.split > *` rule also matches the background svg,
   outranks .page-bg's position:absolute, and drops it back into the flow --
   which silently added 1800px to the page height. */
main, .foot{position:relative; z-index:1}
/* ---------- THE NAV FOLLOWS YOU DOWN ----------
   Sticky on every page. Above the fixed pattern layer and everything in the
   flow (z-index 30), under the gallery overlay (1000), which must still cover
   it. --navh is measured at runtime and published on :root -- the header is
   113px on a wide screen but the nav wraps under the wordmark on a phone, so
   anything that needs to clear it has to ask rather than assume. */
.masthead{position:sticky; top:0; z-index:30}
/* Only once it has left the top of the page: at rest it is part of the page,
   and on the home page that means staying invisible over the dark entry. */
.masthead.stuck{box-shadow:0 10px 24px rgba(23,26,24,.07)}
.home .masthead.stuck{
  background:var(--ground-deep);
  border-bottom-color:rgba(237,241,240,.14);
  box-shadow:0 10px 26px rgba(0,0,0,.35);
}
/* A phone gives up a lot of screen to a 150px header that never leaves, so it
   draws itself in once it is travelling. */
@media (max-width:700px){
  .masthead.stuck{padding-top:11px; padding-bottom:9px}
  .masthead.stuck .wordmark .mark{width:38px; height:38px}
}
.about > .prose, .about > .portrait,
.split > .prose, .split > .panel,
.panelled > .prose, .panelled > .panel{position:relative; z-index:1}

/* ---------- a form, mounted ----------
   The enquiry forms were a stack of inputs on an empty page. They are boxed
   now, in the same white-mat-in-a-dark-box the portrait uses, because that is
   the one piece of furniture this site already owns. */
.pane .panel{
  /* The portrait's mount, exactly: white mat, dark box, the inset shadow that
     gives a shadow box its depth, and a block of the collage turquoise offset
     behind it. There it is an absolutely positioned ::before, which works
     because the figure itself has no background; a form does, so the block is
     drawn as a HARD-EDGED box-shadow instead -- same offset, same colour, and
     it cannot end up painted over the panel's own white. */
  position:relative;
  /* The offset block's colour is a variable so the mount is written once and
     each panel just names its own -- the portrait keeps the turquoise. */
  --block:var(--mat-teal);
  background:var(--paper); border:9px solid var(--ground); padding:clamp(20px,3vw,30px);
  box-shadow:20px 20px 0 0 var(--block),
             0 20px 46px rgba(13,22,30,.26),
             inset 0 3px 12px rgba(13,22,30,.17);
}
.pane-message .panel{--block:var(--mat-sage)}
.pane-commission .panel{--block:var(--mat-plum)}
.pane .panel .stack{margin-top:0; max-width:none}
.pane .panel h2{
  font-family:"Newsreader",Georgia,serif; font-weight:400; font-size:1.35rem;
  margin:0 0 4px; letter-spacing:-.01em;
}
.pane .panel .fine{margin:0 0 18px}
.pane .panel .cta{width:100%; text-align:center}

/* copy on one side, the form on the other */
.split{
  position:relative;
  display:grid; grid-template-columns:minmax(0,48ch) minmax(280px,24rem);
  justify-content:start; gap:clamp(28px,4vw,56px); align-items:start;
}
.panelled{position:relative}

/* ---------- contact + commissions, one page ----------
   Two radio inputs drive the panes. They come first in the source because the
   sibling combinator can only look forwards; they are hidden by clipping
   rather than display:none so they stay focusable from the keyboard. */
.enq{position:relative}
.tabin{position:absolute; width:1px; height:1px; opacity:0; pointer-events:none}
.tabs{display:flex; flex-wrap:wrap; gap:10px; margin:0 0 clamp(24px,3.5vw,36px)}
/* Squared to match the nav, and mounted on the same hard offset block. Each
   pane's block is the colour of the form it opens, so the toggle previews
   where it is taking you. */
.tabs label{
  cursor:pointer; user-select:none;
  font:600 12px/1 "Karla",sans-serif; letter-spacing:.1em; text-transform:uppercase;
  color:var(--muted); background:var(--paper);
  border:1.5px solid var(--rule); border-radius:2px; padding:12px 20px;
  box-shadow:3px 3px 0 0 transparent;
  transition:color .2s, border-color .2s, background .2s, box-shadow .2s, transform .2s;
}
.tabs label:hover{
  border-color:var(--ink); color:var(--ink); transform:translate(-1px,-1px);
}
.tabs label[for="mode-message"]:hover{box-shadow:3px 3px 0 0 var(--mat-sage)}
.tabs label[for="mode-commission"]:hover{box-shadow:3px 3px 0 0 var(--mat-plum)}
/* Switching panes is a display change, which is not animatable on its own --
   hence `display` in the transition with allow-discrete, plus @starting-style
   to give the incoming pane something to animate FROM. Browsers without those
   simply cut between panes, which is the old behaviour. Stacking both panes in
   one grid cell and cross-fading was the other option, and it was worse: the
   container would always stand as tall as the longer pane. */
.pane{
  /* NO transition on the way out. Transitioning `display` with allow-discrete
     keeps the outgoing pane in the layout for the whole fade, so both panes
     stack and the page briefly doubles in height -- measured at 927px -> 1712px
     -> 1065px. The outgoing pane now leaves the flow at once and only the
     incoming one animates, from the @starting-style below. */
  display:none; opacity:0; transform:translateY(7px);
}
.pane .prose{max-width:52ch}
#mode-message:checked ~ .tabs label[for="mode-message"],
#mode-commission:checked ~ .tabs label[for="mode-commission"]{
  background:var(--ground); border-color:var(--ground); color:#F4F6F5;
  transform:none;
}
#mode-message:checked ~ .tabs label[for="mode-message"]{box-shadow:4px 4px 0 0 var(--mat-sage)}
#mode-commission:checked ~ .tabs label[for="mode-commission"]{box-shadow:4px 4px 0 0 var(--mat-plum)}
@media (prefers-reduced-motion:reduce){
  .tabs label{transition:none}
  .tabs label:hover{transform:none}
}
/* Keyboard focus has to be visible on the label, since the input it belongs to
   is clipped out of sight. */
.tabin:focus-visible ~ .tabs label[for="mode-message"],
.tabin:focus-visible ~ .tabs label[for="mode-commission"]{outline:2px solid var(--focus); outline-offset:2px}
#mode-message:checked ~ .pane-message,
#mode-commission:checked ~ .pane-commission{
  display:grid; grid-template-columns:minmax(0,48ch) minmax(280px,24rem);
  justify-content:start; gap:clamp(28px,4vw,56px); align-items:start;
  opacity:1; transform:none;
  transition:opacity .3s ease, transform .3s ease;
}
@starting-style{
  #mode-message:checked ~ .pane-message,
  #mode-commission:checked ~ .pane-commission{opacity:0; transform:translateY(7px)}
}
@media (prefers-reduced-motion:reduce){
  .pane{transition:none; transform:none}
}
.pane > .prose, .pane > .panel{position:relative; z-index:1}
@media (max-width:860px){
  #mode-message:checked ~ .pane-message,
  #mode-commission:checked ~ .pane-commission{grid-template-columns:1fr}
  /* the offset block pulls in so it cannot run off the side of a phone */
  .pane .panel{
    box-shadow:12px 12px 0 0 var(--block),
               0 14px 32px rgba(13,22,30,.22),
               inset 0 3px 10px rgba(13,22,30,.15);
  }
}
.panelled .prose{max-width:52ch}
.panelled .panel{max-width:30rem; margin-top:26px}
@media (max-width:860px){
  .split{grid-template-columns:1fr}
}

.about{
  position:relative;
  /* The copy column is sized to the measure the prose actually uses, not to a
     1fr track -- otherwise the portrait is pushed to the far edge of a wide
     screen and reads as belonging to nothing. */
  display:grid; grid-template-columns:minmax(0,62ch) minmax(220px,23rem);
  justify-content:start; gap:clamp(28px,4vw,56px); align-items:start;
}
.about.noportrait{display:block}
/* Dropped to sit level with the first line of copy rather than with the
   heading. Derived from the h1's own metrics -- font-size x line-height plus
   its bottom margin -- so it stays true as the clamp resizes the heading. */
.portrait{position:relative; margin:calc(clamp(2rem,4.5vw,2.9rem) * 1.1 + .5rem) 0 0}
/* Her own phrase for the frames is "black and white treasure boxes", so the
   artist is mounted in one: white mat, dark box, and the inset shadow that
   gives a shadow box its depth. A block of the collage's turquoise sits behind
   it, offset, the way a torn sheet sits under the next layer. This frames the
   PERSON, not the work -- the paintings on the wall still get nothing. */
.portrait::before{
  content:""; position:absolute; z-index:0;
  left:20px; right:-20px; top:20px; bottom:-20px;
  background:var(--mat-teal);
}
.portrait picture{
  position:relative; z-index:1; display:block;
  background:var(--paper); padding:15px; border:9px solid var(--ground);
  box-shadow:0 20px 46px rgba(13,22,30,.26), inset 0 3px 12px rgba(13,22,30,.17);
}
.portrait img{width:100%; display:block}
.portrait figcaption{
  padding-top:10px; font-size:13.5px; line-height:1.5; color:var(--muted);
}
@media (max-width:860px){
  .about{grid-template-columns:1fr}
  /* Read the copy first on a phone; the portrait follows it, and there is no
     heading beside it to line up with. */
  .portrait{max-width:21rem; order:2; margin:4px 0 20px}
  .portrait::before{left:12px; right:-12px; top:12px; bottom:-12px}
  .portrait picture{padding:11px; border-width:7px}
}

/* ---------- stepping between works ---------- */
/* ---------- stepping between paintings ----------
   IN THE DETAILS COLUMN, not across the foot of the page. It sits where the
   reader already is once they have read the size and the price, and it leaves
   the whole left-hand column to the painting.

   Which means it now lives in roughly 300px, not the full width, so the row of
   three becomes a grid: the count on its own line and the two steps sharing
   the one below. The two are equal columns rather than content-width, because
   "Previous" next to a piece called "Face" would otherwise give the two ends
   wildly different sizes for no reason a reader could name. */
.stepper{
  display:grid; grid-template-columns:1fr 1fr; gap:0 clamp(12px,2.5vw,22px);
  /* margin-top is auto (see .facts) so it is pushed to the foot; this is the
     floor, for the short-viewport case where there is no spare room to push
     through and the two blocks would otherwise touch. */
  padding-top:clamp(26px,3.5vw,40px);
  /* And the same underneath: pushed to the foot, the titles sat ON the bottom
     edge of the column, level with the cut of the photograph and with nothing
     under them. A gallery label does not end at the frame. Plus a further
     100px of plain air below that, asked for so the pair sits well clear of
     the bottom of the page rather than close to it. */
  padding-bottom:calc(clamp(20px,2.6vw,32px) + 100px);
}
/* NO BOX. A bordered card with an offset shadow is a web control; this is a
   gallery, and the catalogue convention is a rule and some type. Each step
   gets its own hairline rather than one rule across the pair, so the two ends
   read as two objects without either being drawn as a button.

   The rule is the only chrome, which means it also has to be the hover
   target -- it darkens to ink, the title underlines, and the arrow steps
   outward, away from the piece you are on. */
.step{
  display:flex; flex-direction:column; gap:4px; min-width:0;
  /* A QUIET PANEL, not a button. The pair was a hairline and some type on a
     wall almost the same colour, which is right in a catalogue and wrong at
     the foot of a long page: they were easy to scroll past without seeing.
     A ground a shade off the wall is enough to find them by -- still no
     border, no shadow, no rounded corners, so the convention holds and they
     do not turn into a web control. The hairline stays: it is the catalogue's
     mark and the thing that darkens under the cursor. */
  padding:12px 14px 14px; border:0; border-top:1px solid var(--rule);
  border-radius:0; background:var(--panel-soft); box-shadow:none;
  text-decoration:none; color:var(--ink);
  transition:border-color .18s ease, background .18s ease;
}
/* Lifts towards the paper under the cursor rather than darkening: the page is
   light, so the thing being pointed at should come forward. */
.step:hover{border-top-color:var(--ink); background:var(--paper-hi)}
.step:hover .step-t{text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px}
.step:hover .step-dir{color:var(--ink)}
/* Without the box there is nothing to outline, so focus needs saying. */
.step:focus-visible{outline:2px solid var(--focus); outline-offset:4px}
/* Outward: left for Previous, right for Next. The .step-next rule must follow,
   since both selectors carry the same weight and it is overriding the first. */
.arw{display:inline-block; transition:transform .18s ease}
.step:hover .arw{transform:translateX(-3px)}
.step-next:hover .arw{transform:translateX(3px)}
@media (prefers-reduced-motion:reduce){
  .arw{transition:none}
  .step:hover .arw,.step-next:hover .arw{transform:none}
}
.step-next{text-align:right; align-items:flex-end}
/* Spans both columns and is pulled above them. Kept in the middle of the
   markup on purpose -- between Previous and Next is where it belongs when the
   page is read aloud, and `order` moves only the picture. */
.step-count{grid-column:1/-1; order:-1; margin-bottom:13px}
.step-dir{
  font:600 11px/1 "Karla",sans-serif; letter-spacing:.12em; text-transform:uppercase;
  color:var(--muted); transition:color .18s ease;
}
/* A long title in a half-column has to be allowed to break, or it pushes the
   card wider than its track and the two ends stop matching. */
.step-t{
  font-family:"Newsreader",Georgia,serif; font-size:14.5px; line-height:1.25;
  overflow-wrap:anywhere;
}
.step-count{
  font-size:12.5px; letter-spacing:.1em; text-transform:uppercase; color:var(--muted);
  white-space:nowrap;
}
@media (prefers-reduced-motion:reduce){.step{transition:none} .step:hover{transform:none}}
@media (max-width:640px){
  .stepper{gap:10px}
  /* The panel fills its half of the row now. It used to be capped, which only
     mattered while there was nothing to see but the words. */
  .step{padding:10px 12px 12px}
  .step-t{font-size:14px}
  .step-count{display:none}
}

/* ---------- prose & forms ---------- */
.prose{max-width:62ch}
.prose.narrow{max-width:46ch}
.prose p{margin:0 0 1rem}
/* Policies: two sections, each a serif h2 with a rule above it, and small
   caps-weight h3s so a buyer can scan straight to "Returns". The anchors land
   below the masthead rather than under it. */
.policies .prose h2{font:400 clamp(1.5rem,2.6vw,1.9rem)/1.2 "Newsreader",Georgia,serif;
  margin:2.6rem 0 .4rem; padding-top:1.4rem; border-top:1px solid var(--rule); scroll-margin-top:110px}
.policies .prose h1 + h2{border-top:0; padding-top:0; margin-top:1.2rem}
.policies .prose h3{font:700 .95rem/1.3 "Karla",sans-serif; letter-spacing:.02em; margin:1.5rem 0 .35rem}
.foot-links{white-space:nowrap}
.stack{display:flex; flex-direction:column; gap:14px; margin-top:22px; max-width:34rem}
label{display:flex; flex-direction:column; gap:5px; font-size:14px; color:var(--muted)}
input,select,textarea{
  font:400 16px/1.4 "Karla",sans-serif; color:var(--ink); background:var(--paper);
  border:1px solid var(--rule); border-radius:2px; padding:10px 11px; width:100%;
}
textarea{resize:vertical}
/* The honeypot. Positioned off-screen rather than display:none or [hidden] —
   the cruder bots skip anything hidden those ways, and the point is that they
   fill it in. A real person never reaches it: no tab stop, no label on screen. */
.hp{position:absolute !important; left:-9999px !important; width:1px; height:1px;
  overflow:hidden; opacity:0; pointer-events:none}
.empty{color:var(--muted)}
.flash{background:var(--warn-bg); border:1px solid var(--warn-line); padding:10px 14px; margin-bottom:20px}

/* ---------- footer ---------- */
/* The sign-up sits in the footer of every page, which is where someone who has
   just scrolled a painting to the end actually is. Quiet by design: this is an
   invitation, not a demand, and a gallery that shouts for your email is not one
   you trust with a $385 painting. */
.signup{
  flex:1 1 100%;
  display:flex; flex-wrap:wrap; gap:18px 32px; align-items:center;
  justify-content:space-between; padding-bottom:22px; margin-bottom:22px;
  border-bottom:1px solid var(--line);
}
.signup-say{display:flex; flex-direction:column; gap:3px; max-width:34rem}
.signup-say strong{
  font-family:"Newsreader",Georgia,serif; font-weight:400; font-size:1.22rem;
  letter-spacing:-.01em; color:var(--ink);
}
.signup-say span{font-size:.86rem; color:var(--muted)}
.signup-do{display:flex; flex-wrap:wrap; gap:9px; align-items:center; flex:0 1 26rem}
.signup-do input[type=email]{
  flex:1 1 13rem; min-width:0; padding:11px 14px; font-size:16px;
  border:1px solid var(--rule); border-radius:2px; background:var(--paper); color:var(--ink);
}
.signup-do input[type=email]:focus-visible{outline:2px solid var(--focus); outline-offset:1px}
.btn-ghost{
  -webkit-appearance:none; appearance:none; flex:0 0 auto; cursor:pointer;
  background:transparent; color:var(--accent); border:1px solid var(--accent);
  border-radius:2px; padding:11px 20px; font:600 14px/1 "Karla",sans-serif;
  letter-spacing:.03em; transition:background .2s ease, color .2s ease;
}
.btn-ghost:hover{background:var(--accent); color:var(--on-accent)}
/* On the dark entry the footer sits on the cream wall like everywhere else, so
   nothing special is needed there — but the home page paints its own ground
   under .foot, and the field would otherwise vanish into it. */
.home .signup-do input[type=email]{background:var(--paper)}
@media (max-width:720px){
  .signup{gap:14px}
  .signup-do{flex:1 1 100%}
  .signup-do input[type=email]{flex:1 1 100%}
  .btn-ghost{flex:1 1 100%; text-align:center}
}

.foot{
  border-top:1px solid var(--line); padding:34px clamp(18px,5vw,56px) 56px;
  display:flex; flex-wrap:wrap; gap:24px 60px; align-items:end;
}
.foot .fine{flex:1 1 16rem; text-align:left}

@media (max-width:860px){
  .work{grid-template-columns:1fr}
  /* Stacked, the details are no longer beside a tall picture, so there is
     nothing to stick to and no spare column height to push the stepper
     through. It simply follows the buy panel, which is where it belongs when
     the page is one column. */
  .facts-inner{position:static}
  .facts{min-height:0}
  .stepper{margin-top:clamp(26px,3.5vw,40px)}
}

/* ================= admin =================
   Deliberately plain. It is a working surface, used by one person, and it
   should look nothing like the shop so there is never a doubt which is which. */
body.admin{background:var(--admin-bg); font-size:15px}
/* The nav is a LEFT RAIL, not a top bar. There are five sections and they read
   as a list rather than as tabs across the top; a rail also hands the whole
   width of the page to the tables, which are the only thing in here that is
   ever short of room. The section you are on is marked with a turquoise edge,
   so the nav says where you are and not just where you could go. */
/* 206 -> 228px. The labels were already at their natural width; the icons
   added 27px in front of them, and without the extra the longest word in
   the rail (Opportunities) sat hard against the edge. */
/* The rail's width lives here rather than in two places: the arc below has to
   land exactly on the seam, and a column that drifted from it would leave the
   curve floating in the middle of the page. */
.adminshell{--rail:228px; --arc:34px;
  display:grid; grid-template-columns:var(--rail) minmax(0,1fr); position:relative}
/* WHERE THE RAIL MEETS THE PAGE. Purely decorative: the dark runs a little way
   along the top of the main area and curves back down to the seam, so the two
   halves read as one surface folding rather than two panels butted together.
   The shape is a square with a disc bitten out of its far corner -- the bite is
   the arc -- and it tapers to nothing within the main column's own top padding,
   so it never touches anything anyone has to read.
   Hidden from assistive tech by being a pseudo-element with no content, and
   pointer-events:none so it cannot swallow a click meant for the page. */
.adminshell::before{
  content:""; position:absolute; top:0; left:var(--rail); z-index:1;
  width:var(--arc,34px); height:var(--arc,34px);
  background:var(--rail-bg); pointer-events:none;
  /* FEATHERED, and this is the whole trick. A hard stop at the circle's edge
     is a sub-pixel transition, so the browser stair-steps it -- and where the
     arc meets the top of the page it runs almost horizontally, which is
     precisely where a stair-step reads as a stray line of background cutting
     across the dark. A 1.6px blend either side of the radius gives the edge
     something to antialias into and the line goes away.

     Stops in px rather than percentages so the blend stays 1.6px whatever the
     arc is sized to; at 99%/100% it would shrink with the radius. */
  -webkit-mask:radial-gradient(circle at 100% 100%,
      #0000 calc(var(--arc,34px) - 0.8px), #000 calc(var(--arc,34px) + 0.8px));
          mask:radial-gradient(circle at 100% 100%,
      #0000 calc(var(--arc,34px) - 0.8px), #000 calc(var(--arc,34px) + 0.8px));
}
/* AND THE HAIRLINE ROUND THE CURVE. The rail draws a 1px light line down its
   own right edge, which in the dark theme is the only thing separating it from
   the page -- the two grounds are 1.08 contrast apart, so the wedge alone is
   invisible there. Without this the seam was a line that ran the height of the
   page and then simply stopped for the top 34px.

   A ring segment rather than a border: a 1px band of the same colour laid on
   the arc's radius, feathered at both edges so it antialiases like the wedge
   below it. It sits just INSIDE the curve, where the rail's own line sits just
   inside its edge. */
.adminshell::after{
  content:""; position:absolute; top:0; left:var(--rail); z-index:2;
  width:var(--arc,34px); height:var(--arc,34px); pointer-events:none;
  /* Centred ON the radius, not inside it. The rail's stripe sits in the last
     pixel INSIDE the rail, so a ring tucked inside the curve met it a pixel to
     the right and the join showed as a jog. Straddling the radius puts the two
     on the same pixel where they meet, at the bottom of the arc. */
  background:radial-gradient(circle at 100% 100%,
      transparent calc(var(--arc,34px) - 0.9px),
      var(--rail-edge) calc(var(--arc,34px) - 0.4px),
      var(--rail-edge) calc(var(--arc,34px) + 0.5px),
      transparent calc(var(--arc,34px) + 1.0px));
}
.adminside{
  /* align-self:start, NOT the grid default of stretch: a stretched item fills
     the row and sticky then has nothing left to travel through. */
  align-self:start; position:sticky; top:0;
  height:100vh; height:100dvh; overflow:auto;
  display:flex; flex-direction:column;
  color:#EAEDE9;
  /* The seam line is a 1px background stripe down the right edge that STARTS
     at the bottom of the arc -- not an inset box-shadow, which had no way to
     stop. It ran the full height of the rail, so above the fold you could see
     a straight white line carrying on next to a curve that had already left.
     background-attachment defaults to `scroll`, which for a scroll container
     means fixed to the element and NOT moving with its contents, so the stripe
     stays put if the nav ever needs to scroll. */
  background:
    linear-gradient(var(--rail-edge),var(--rail-edge))
      no-repeat right 0 top var(--arc,34px) / 1px calc(100% - var(--arc,34px)),
    var(--rail-bg);
}
/* The same bug as the public masthead, so the admin is visibly part of the
   site rather than a generic panel. It links home to the artwork list. */
/* The rail's top row. The logo and the switch share it at every width, which
   is the point: laid down as a bar on a phone the switch cannot be pushed onto
   a second line, because it is inside the same element as the logo rather than
   down among the utilities. Logo left, switch hard right. */
.railtop{display:flex; align-items:center; justify-content:space-between; gap:8px; padding-right:12px}
.railtop .adminbrand{padding-right:0; margin-right:auto}
.adminbrand{
  display:flex; align-items:center; gap:11px; padding:18px 18px 16px;
  text-decoration:none; color:#fff;
  font:600 13px/1 "Karla",sans-serif; letter-spacing:.13em; text-transform:uppercase;
}
.adminbrand img{
  width:32px; height:32px; display:block; border-radius:2px;
  box-shadow:0 0 0 1px rgba(255,255,255,.16);
}
.adminbrand:hover img{opacity:.85}
.adminside nav{display:flex; flex-direction:column; padding-top:6px}
.adminside nav a{
  display:flex; align-items:center; padding:12px 18px;
  color:#CBD5CE; text-decoration:none;
  font:600 12.5px/1 "Karla",sans-serif; letter-spacing:.08em; text-transform:uppercase;
  border-left:3px solid transparent;
  transition:color .15s, background .15s, border-color .15s;
}
.adminside nav a:hover{color:#fff; background:rgba(255,255,255,.07)}
/* ---------- section marks ----------
   The glyph sits dimmer than its label at rest so the rail still reads as a
   list of words rather than a row of pictures, and comes up to full strength
   with the label on hover or when the section is the current one. */
.adminside nav a{gap:11px}
.adminside nav a svg{
  width:17px; height:17px; flex:none; opacity:.62;
  transition:opacity .15s;
  /* The pulse scales about the glyph's own middle, not the corner. */
  transform-origin:50% 50%;
}
.adminside nav a:hover svg,.adminside nav a.on svg,.adminside nav a.grp.open svg{opacity:1}
.adminside nav a span{min-width:0}
/* One beat, not a loop: a rail that throbs continuously is a rail nobody can
   read past. It fires on hover and settles back, and it is scale only --
   animating anything that reflows would shift the label beside it. */
@keyframes navpulse{
  0%{transform:scale(1)}
  40%{transform:scale(1.22)}
  70%{transform:scale(.97)}
  100%{transform:scale(1)}
}
.adminside nav a:hover svg{animation:navpulse .42s cubic-bezier(.34,1.56,.64,1)}
@media (prefers-reduced-motion:reduce){
  .adminside nav a:hover svg{animation:none}
}
/* Artwork is a group heading that is also a link. It never takes the turquoise
   edge itself -- that belongs to whichever child you are actually on, or the
   rail would mark two places at once. */
.adminside nav a.grp.open{color:#fff}
.adminside .subnav{display:flex; flex-direction:column; padding-bottom:4px}
.adminside .subnav a{
  display:flex; align-items:center; padding:9px 18px 9px 34px;
  color:#9DABA4; text-decoration:none;
  font:500 12.5px/1 "Karla",sans-serif; letter-spacing:.02em; text-transform:none;
  border-left:3px solid transparent;
  transition:color .15s, background .15s, border-color .15s;
}
/* The three children carry glyphs now, for the phone's single icon row. In the
   rail they stay indented words under their parent -- a second column of icons
   inside an indent would read as a different kind of thing, and the indent is
   what says these belong to Artwork. */
.adminside .subnav a svg{display:none}
.adminside .subnav a:hover{color:#fff; background:rgba(255,255,255,.07)}
.adminside .subnav a.on{color:#fff; background:rgba(255,255,255,.10); border-left-color:var(--turq)}
.adminside nav a.on{color:#fff; background:rgba(255,255,255,.10); border-left-color:var(--turq)}
/* Pinned to the foot of the rail: leaving and signing out are not sections. */
.adminside .util{
  margin-top:auto; display:flex; flex-direction:column; gap:10px;
  padding:20px 18px; box-shadow:inset 0 1px 0 rgba(255,255,255,.10);
}
.adminside .util a{color:#93A29A; text-decoration:none; font-size:13px; white-space:nowrap; transition:color .15s}
/* Settings sits in the rail on a wide screen and among the utilities on a
   narrow one. Two anchors rather than one moved by CSS, because it changes
   parent and no property can do that. */
.adminside .util a.only-narrow{display:none}
.adminside .util a:hover{color:#fff}
.adminside .util a.out{color:#D79B93}
.adminside :focus-visible{outline:2px solid var(--turq); outline-offset:-3px}
/* A 228px rail out of a 390px screen is most of the screen, so on a phone it
   lies back down as a bar and the current-section edge moves to the bottom. */
@media (max-width:760px){
  .adminshell{grid-template-columns:1fr}
  /* The rail is a horizontal bar down here, so there is no seam to fold. */
  .adminshell::before,.adminshell::after{display:none}
  .adminside{
    position:static; height:auto; overflow:visible;
    flex-direction:row; flex-wrap:wrap; align-items:center; padding:0 12px;
    /* Flat colour again -- the vertical seam stripe belongs to the rail only. */
    background:var(--rail-bg);
    box-shadow:inset 0 -1px 0 var(--rail-edge);
  }
  .railtop{width:100%; padding-right:0}
  .adminbrand{padding:10px 16px 6px 0}
  /* ICONS, NOT WORDS, AND ONE ROW OF THEM.
     Seven labels wrapped to two rows and the header ate 200px -- a quarter of
     the screen -- before a single painting. Seven glyphs at a full 44px touch
     target is 308px, which fits one row on a 360px phone.

     NOT a burger. This is a tool one person opens twenty times a day; hiding
     the sections behind a press costs a tap on every move and takes away the
     mark that says which section she is in. The glyphs were already drawn for
     the rail -- they were only switched off down here. */
  /* width:100%, not margin-right:auto -- the rail's margin makes the nav
     shrink-wrap its contents, and seven icons at flex-basis 0 would then
     huddle in the left third of the bar instead of spreading across it. */
  .adminside nav{flex-direction:row; flex-wrap:nowrap; padding-top:0;
    margin-right:0; width:100%}
  .adminside nav a{
    padding:11px 0 8px; border-left:0; border-bottom:3px solid transparent;
    justify-content:center; flex:1 1 0; min-width:38px;
  }
  .adminside nav a svg{display:block; width:21px; height:21px; opacity:.72}
  /* The WORD STAYS, clipped rather than removed: it is the link's accessible
     name, and a row of seven unlabelled paths is nothing to a screen reader. */
  .adminside nav a > span{
    position:absolute; width:1px; height:1px; overflow:hidden;
    clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;
  }
  .adminside nav a.on{border-bottom-color:var(--turq)}
  .adminside nav a.on svg,.adminside nav a.grp.open svg{opacity:1}
  /* ONE ROW OF NINE. The hierarchy the rail draws -- Artwork, and three ways
     into it -- needs an indent to mean anything, and a bar has no indent to
     give. So down here it flattens: the group heading goes (it only ever led
     to Pieces, which is now in the row itself) and its three children join
     their siblings. display:contents dissolves the wrapper so they become
     items of the same flex row rather than a row of their own.

     Nine across a 390px phone is 40px each. Under the 44px ideal, and the
     honest alternative was a second row of words on three pages out of nine. */
  .adminside nav a.grp,.adminside nav a.only-wide{display:none}
  .adminside .util a.only-narrow{display:inline}
  .adminside .util a.only-narrow.on{color:#fff; font-weight:700}
  .adminside .subnav{display:contents}
  .adminside .subnav a{
    display:flex; justify-content:center; align-items:center;
    padding:11px 0 8px; flex:1 1 0; min-width:34px;
    border-left:0; border-bottom:3px solid transparent;
  }
  .adminside .subnav a svg{display:block; width:21px; height:21px; opacity:.72; flex:none}
  .adminside .subnav a > span{
    position:absolute; width:1px; height:1px; overflow:hidden;
    clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;
  }
  .adminside .subnav a.on{border-bottom-color:var(--turq)}
  .adminside .subnav a.on svg{opacity:1}
  .adminside .util{
    margin-top:0; flex-direction:row; gap:18px; padding:6px 0 8px; width:100%;
    box-shadow:none; justify-content:flex-end; font-size:12.5px;
  }
}
/* Centred in the column beside the rail rather than pushed up against it:
   at 1600px the old left-aligned block left a third of the screen empty on
   the right and the tables looked like they had slipped. */
.adminmain{padding:clamp(16px,3vw,30px); box-sizing:border-box;
  width:min(1240px,100%); margin-inline:auto}
.adminmain.narrowbox{max-width:22rem; margin:12vh auto}
.adminhead{display:flex; align-items:center; gap:16px; margin-bottom:18px}
.adminhead h1{font-family:"Newsreader",Georgia,serif; font-weight:400; font-size:1.7rem; margin:0; margin-right:auto}
/* ADD A PAINTING. The one action the studio exists for, and it used to be the
   same slate rectangle as Save and Build a packet -- findable only by reading
   every button on the page.

   Two things make it unlike anything else here. The glyph is an empty square
   with a plus in it: the shape of the work, 10.5 by 10.5, waiting to be filled.
   And the colour is the turquoise the LANDING PAGE already gives its one real
   action, borrowed inward -- so the studio's primary button and the shop's are
   the same button, and it is the only turquoise one in here. Red is still only
   sold; this takes nothing that means something else. */
.cta.newwork{
  display:inline-flex; align-items:center; gap:9px;
  background:var(--turq); border-color:var(--turq); color:#06222A;
  padding-left:20px; padding-right:24px;
}
.cta.newwork:hover{background:var(--turq-lo); border-color:var(--turq-lo); color:#06222A}
.cta.newwork svg{width:17px; height:17px; flex:none; opacity:.85}
.cta.newwork:hover svg{opacity:1}
.cta.newwork:focus-visible{outline:2px solid var(--accent); outline-offset:3px}
/* The plus grows out of the frame on hover -- a small nod that something is
   about to be put inside it. Held back for anyone who does not want motion. */
@media (prefers-reduced-motion:no-preference){
  .cta.newwork svg path{transition:transform .18s cubic-bezier(.34,1.56,.64,1); transform-origin:center}
  .cta.newwork:hover svg path{transform:scale(1.18)}
}
.alerts{display:flex; gap:14px; margin-bottom:16px; font-size:14px}
/* Naming a collection is two fields' worth of work, so it happens on the list
   itself rather than behind a New screen. */
.newrow{display:flex; gap:10px; align-items:stretch; margin:0 0 18px; max-width:34rem}
.newrow input{flex:1; box-sizing:border-box; font-size:16px}
.newrow .cta{white-space:nowrap}
.sr{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap}
/* A line of facts under a heading -- how many, how recent, where from. Reads
   as one sentence, so the separators are middots rather than boxes. */
.statline{margin:0 0 14px; color:var(--muted); font-size:14px}
.statline strong{color:var(--ink); font-size:16px}
.statline .sep{opacity:.45; padding:0 3px}
.tag{
  display:inline-block; font-size:11px; letter-spacing:.07em; text-transform:uppercase;
  color:var(--muted); border:1px solid var(--rule); border-radius:2px; padding:2px 7px;
}
.listacts{display:flex; flex-wrap:wrap; align-items:baseline; gap:6px 16px; margin:0 0 16px}
.addresses summary{
  cursor:pointer; color:var(--accent); font:600 13.5px "Karla",sans-serif;
  text-decoration:underline; text-underline-offset:3px; min-height:34px;
  display:flex; align-items:center;
}
.addresses textarea{
  width:100%; box-sizing:border-box; margin-top:8px; padding:10px 11px; font-size:14px;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; line-height:1.5;
  background:var(--paper); border:1px solid var(--line); border-radius:3px; color:var(--ink);
  resize:vertical;
}
.addresses{flex:1 1 100%; min-width:0}
.err{background:var(--err-bg); border:1px solid var(--err-line); padding:9px 12px}

/* ---------- the working surface ----------
   Still not the shop: this is a tool, and it should never be mistaken for the
   gallery. But "plain" had become "unstyled", and on a phone the tables ran
   off the side of the screen. The palette below is the site's own tokens --
   slate for actions, turquoise for the live edge, the sold red only for sold
   -- so the studio looks related to the shop without imitating it. */
.grid-t{
  width:100%; border-collapse:collapse; background:var(--paper);
  border:1px solid var(--line); border-radius:3px; overflow:hidden;
}
.grid-t th,.grid-t td{padding:11px 13px; border-bottom:1px solid var(--line); text-align:left; vertical-align:middle}
.grid-t tbody tr:last-child td{border-bottom:0}
.grid-t tbody tr:hover{background:var(--paper-hi)}
.grid-t thead th{
  font-size:11px; letter-spacing:.09em; text-transform:uppercase; color:var(--muted);
  font-weight:600; background:var(--head); border-bottom:1px solid var(--rule);
}
/* Pieces carries every painting she owns, so its rows are tightened: the
   title and its line of detail sit closer, and the cell padding comes in.
   Scoped to that table -- orders and contacts are short lists and read better
   with the room. The thumbnail still sets the row height. */
.grid-t.tight td{padding-top:7px; padding-bottom:7px; line-height:1.35}
.grid-t.tight .sub{margin-top:1px}
.grid-t.tight .thumb{width:60px}
.grid-t.tight .thumb img{width:50px; height:50px}
/* A right-aligned column needs its HEADING right-aligned too, or the word
   sits at the left edge of the column with its numbers stacked under the
   other end of it. `.grid-t th,.grid-t td` sets text-align:left and outweighs
   a bare `.num`, so the heading has to be named here explicitly -- the same
   specificity trap that left the show checklist's headings adrift. */
.grid-t th.num,.grid-t td.num{text-align:right; font-variant-numeric:tabular-nums}
.grid-t th.right,.grid-t td.right{text-align:right}
.grid-t .thumb{width:66px}
/* Same rule as the wall: a square box, and CONTAIN so nothing is cropped.
   cover was quietly trimming the edges off a photograph that was not exactly
   square -- which is most of a shadow-box frame's margin. */
.grid-t .thumb img{
  width:56px; height:56px; object-fit:contain; background:var(--paper);
  border-radius:2px; display:block;
}
.sub{display:block; color:var(--muted); font-size:12.5px}
.addr{max-width:15rem}
.inline{display:inline-flex; gap:6px; align-items:center}
.shipmail{flex-direction:row; align-items:center; gap:5px; font-size:12.5px; white-space:nowrap}
.shipmail input{width:auto; margin:0; padding:0}
/* 16px minimum on every control that can be focused. Below it, iOS zooms the
   whole page in on focus and leaves it there -- which is most of what "renders
   badly on mobile" turned out to mean. */
.grid-t select,.inline input{padding:8px 9px; font-size:16px; width:auto; max-width:100%}
/* Status carries a colour, so a list of paintings answers "what can somebody
   buy right now" without reading a word. Every hue is already in the palette
   and RED IS STILL ONLY SOLD -- available takes the turquoise live edge,
   reserved the gold, not-for-sale the plum because it is a decision rather
   than a failure, and a draft is dashed because it is not published at all.
   The class is rendered by the server from the stored status, so the border
   is correct on load and correct again after the form posts. */
/* EVERY dropdown gets the status column's left edge, in ONE colour.
   A select and a text box are the same grey rectangle until you click one, and
   the status column had already solved that -- a thick left edge says "this
   opens" before you touch it. So the device is now on all of them.

   In slate, and only on the LEFT EDGE, while the status selects keep their
   fully tinted border. That difference is the point: on the status column the
   colour MEANS something -- turquoise available, gold reserved, red sold -- and
   if every dropdown on the page were tinted too, the ones that carry meaning
   would stop standing out. This is chrome; those are signal.

   Specificity note for anyone editing below: `.admin select` and
   `select.st-available` weigh exactly the same, so this rule has to come FIRST
   or it would win and flatten every status colour to slate. */
.admin select{border-left-width:4px; border-left-color:var(--accent)}
/* Disabled ones are not openable, so they do not get the edge that promises
   they are. */
.admin select:disabled{border-left-color:var(--line)}

select.st{border-left-width:4px}
select.st-available{border-color:var(--turq)}
select.st-reserved{border-color:var(--gold)}
select.st-sold{border-color:var(--dot)}
select.st-nfs{border-color:var(--plum)}
select.st-draft{border-color:var(--muted); border-left-style:dashed}
.link{
  background:none; border:0; color:var(--accent); cursor:pointer;
  font:600 13.5px "Karla",sans-serif; padding:6px 2px; min-height:34px;
  text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px;
}
.link:hover{color:var(--ink)}
.link.danger{color:var(--danger)}

.formgrid{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:12px 14px;
  background:var(--paper); padding:clamp(13px,2.4vw,18px); border:1px solid var(--line);
  border-radius:3px;
}
/* label/field pairs were 5px apart inside a 16px grid gap, so the page read as
   one long ladder. Tighter inside, same outside. */
.formgrid label{gap:4px}
.formgrid .wide{grid-column:1/-1}
.formgrid fieldset{grid-column:1/-1; border:1px solid var(--accent); border-radius:3px; padding:13px 14px; margin:0}
.formgrid legend{
  font-size:11px; letter-spacing:.09em; text-transform:uppercase; color:var(--accent);
  padding:0 7px; font-weight:600;
}
.formgrid .check{flex-direction:row; align-items:center; gap:9px; color:var(--ink); min-height:40px}
.formgrid .check input{width:auto; min-width:20px; min-height:20px}
.formgrid input,.formgrid select,.formgrid textarea{box-sizing:border-box; max-width:100%}
/* Outline, not solid. Move it / Record / Choose Files are local "add a row"
   controls inside their own panels; filling all three made them compete with
   Save, which is the page's one real commitment. */
.admin .panel .cta,
.formgrid input[type=file]::file-selector-button,
.setform input[type=file]::file-selector-button{
  background:transparent; color:var(--accent); border:1px solid var(--accent);
}
.admin .panel .cta:hover,
.formgrid input[type=file]:hover::file-selector-button,
.setform input[type=file]:hover::file-selector-button{
  background:var(--accent); color:var(--on-accent);
}

/* The file input was the one raw browser control left in the studio — a grey
   "Choose Files" chip that matched nothing else on the page. ::file-selector-button
   styles the button half while leaving the native picker and its "no file
   chosen" text alone, which a hidden-input-plus-label trick would throw away
   along with the file names after a selection. */
.formgrid input[type=file],
.setform input[type=file]{
  padding:9px 11px; background:var(--wall); border:1px solid var(--accent);
  border-radius:3px; font:400 14px "Karla",sans-serif; color:var(--muted);
  cursor:pointer; width:100%;
}
.formgrid input[type=file]::file-selector-button,
.setform input[type=file]::file-selector-button{
  -webkit-appearance:none; appearance:none;
  background:var(--accent); color:var(--on-accent); border:1px solid var(--accent);
  border-radius:2px; padding:9px 18px; margin-right:12px;
  font:600 13.5px/1 "Karla",sans-serif; letter-spacing:.04em; cursor:pointer;
  transition:background .2s ease, border-color .2s ease;
}
.formgrid input[type=file]:hover::file-selector-button,
.setform input[type=file]:hover::file-selector-button{background:var(--accent-hi); border-color:var(--accent-hi)}
.formgrid input[type=file]:focus-visible,
.setform input[type=file]:focus-visible{outline:2px solid var(--focus); outline-offset:2px}
.rates{display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:12px}
/* A dollar sign sitting inside the field, so the unit is visible while typing
   and not only in the legend above. */
.money{position:relative; display:block}
.money::before{
  content:"$"; position:absolute; left:11px; top:50%; transform:translateY(-50%);
  color:var(--muted); font-size:15px; pointer-events:none;
}
.money input{padding-left:24px}
.shots{display:grid; grid-template-columns:repeat(auto-fill,minmax(132px,1fr)); gap:12px; margin-top:12px}
.shots figure{margin:0; background:var(--paper); border:1px solid var(--line); border-radius:3px; padding:8px}
.shots img{
  width:100%; aspect-ratio:1; height:auto; object-fit:contain;
  background:var(--paper); border-radius:2px; display:block;
}
.shots figcaption{font-size:12px; color:var(--muted); padding:6px 0}

/* Ordering controls, now only on the photographs of a single work -- the
   artwork table itself no longer reorders row by row. Quiet until you reach
   for them. The touch target is bigger than the glyph: this is used on a
   phone, one-handed, standing in a studio. */
.mover{
  font-size:16px; line-height:1; padding:7px 8px; color:var(--muted);
  min-height:34px; min-width:32px; text-decoration:none; border-radius:2px;
}
.mover:hover:not([disabled]){color:var(--accent); background:var(--paper-hi)}
.mover[disabled]{opacity:.22; cursor:default}
/* A SAMPLE ORDER, marked so it can never be read as a sale. The same red the
   sold dot uses, because this is the one other place on the site where getting
   it wrong costs real money -- and unlike the dot it is a warning, not a fact
   about a painting. */
.grid-t tr.is-sample{background:var(--err-bg)}
.grid-t tr.is-sample:hover{background:var(--err-bg)}
.samptag{
  display:inline-block; margin-left:8px; font-size:10px; font-weight:700;
  letter-spacing:.09em; text-transform:uppercase; color:var(--dot);
  border:1px solid var(--dot); border-radius:2px; padding:1px 6px; vertical-align:middle;
}
/* A draft is not on the wall, and the row says so at a glance rather than
   making you read the status menu. */
.grid-t tr.is-draft{background:repeating-linear-gradient(135deg,var(--paper),var(--paper) 9px,var(--draft-stripe) 9px,var(--draft-stripe) 18px)}
.grid-t tr.is-draft td[data-label="Title"] a::after{
  content:"draft"; font-size:10px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--plum); border:1px solid var(--plum); border-radius:2px;
  padding:2px 6px; margin-left:9px; vertical-align:middle; white-space:nowrap;
}
/* The lead photograph is the one the whole site uses; mark it where the choice
   is made rather than explaining it in prose alone. */
.shots figure.lead{outline:2px solid var(--turq); outline-offset:-1px}
.shots figure.lead figcaption{color:var(--accent); font-weight:600}
.shot-acts{display:flex; align-items:center; gap:8px; justify-content:space-between; flex-wrap:wrap}

/* ---------- Your site: the settings page ----------
   It used to be one form of nineteen fields under one Save, with a shipping
   rate sitting at the same weight as the first sentence on the front page and
   nothing on screen showing what any of them changed. That is a configuration
   screen, and it made writing a line of her own site feel like filing a form.

   So the page is now a stack of PLACES -- the front page, the About page, the
   band about the boxes -- each one carrying a true miniature of itself built
   from the stored values, and each saving alone. The chrome goes with it: no
   bordered fieldsets, no uppercase legends. A serif name, a sentence of plain
   English, the thing itself, then the fields. */
.setlede{margin:-6px 0 18px; color:var(--muted); font-size:14.5px; max-width:64ch}
.setcard{
  background:var(--paper); border:1px solid var(--line); border-radius:4px;
  padding:clamp(16px,2.4vw,24px); margin:0 0 18px;
  /* so a jump link does not tuck the card's name under the top edge -- and so
     the redirect back to #front after a save lands with air above it. */
  scroll-margin-top:18px;
}
/* A CARD THAT FOLDS. The four that edit a page she looks at stay open, because
   the miniature inside them is the point of this screen. The five that are set
   once and forgotten -- her name, the application texts, the shipping rates,
   the backdrop, the password -- close, so the page is four editors and five
   lines instead of thirty-nine fields in a column.

   The heading IS the summary rather than sitting above one, so a folded card
   is exactly one line tall and reads as the same object either way. */
.setfold{padding-top:0; padding-bottom:0}
.setfold > summary{
  cursor:pointer; list-style:none; display:flex; align-items:center; gap:11px;
  padding:clamp(14px,2vw,18px) 0; margin:0;
}
.setfold > summary::-webkit-details-marker{display:none}
.setfold > summary h2{
  font-family:"Newsreader",Georgia,serif; font-weight:400; font-size:1.35rem;
  margin:0; margin-right:auto; color:var(--ink); letter-spacing:-.005em;
}
/* Drawn, so it can turn. Same chevron as the folds on a call and a painting --
   one disclosure in the studio, not three. */
.setfold > summary::after{
  content:""; flex:none; width:8px; height:8px; margin-right:2px;
  border-right:1.6px solid var(--muted); border-bottom:1.6px solid var(--muted);
  transform:rotate(45deg) translate(-2px,-2px); transition:transform .18s ease;
}
.setfold[open] > summary::after{transform:rotate(-135deg) translate(-3px,-3px)}
.setfold > summary:hover h2{color:var(--accent)}
.setfold > summary:hover::after{border-color:var(--accent)}
.setfold > summary:focus-visible{outline:2px solid var(--focus); outline-offset:3px}
.setfold > .setsay{margin-top:0}
.setfold > .setform,.setfold > .setsay + .setform{margin-bottom:clamp(14px,2vw,18px)}
.setfold > .setform:last-child{margin-bottom:clamp(14px,2vw,18px)}
@media (prefers-reduced-motion:reduce){ .setfold > summary::after{transition:none} }

.setcard-head{display:flex; align-items:baseline; gap:14px; flex-wrap:wrap}
.setcard-head h2{
  font-family:"Newsreader",Georgia,serif; font-weight:400; font-size:1.35rem;
  margin:0; margin-right:auto; color:var(--ink); letter-spacing:-.005em;
}
.setsay{margin:7px 0 0; color:var(--muted); font-size:14px; line-height:1.55; max-width:66ch}

/* The miniature. Same ground, same serif, same order as the real page: this is
   the part that turns a field called hero_sub into "the line under it". It is
   deliberately not interactive -- it is a photograph of her site, not a copy
   of it to fiddle with. */
.setprev{
  margin:16px 0 4px; border:1px solid var(--line); border-radius:3px;
  padding:16px 18px; background:var(--wall); overflow:hidden;
}
.setprev.dark{
  background:linear-gradient(160deg,var(--ground),var(--ground-deep));
  border-color:#0A121A; color:#EDF1F0;
}
.setprev-note{margin:12px 0 0; font-size:12.5px; color:rgba(237,241,240,.55)}
.setprev-hero,.setprev-box,.setprev-about{display:grid; gap:16px; align-items:center}
.setprev-hero{grid-template-columns:132px minmax(0,1fr)}
.setprev-box{grid-template-columns:118px minmax(0,1fr)}
.setprev-about{grid-template-columns:minmax(0,1fr) 118px; align-items:start}
.setprev-hero.noart,.setprev-box.noart,.setprev-about.noart{grid-template-columns:1fr}
.setprev img{
  width:100%; aspect-ratio:1/1; object-fit:contain; display:block; border-radius:2px;
}
.setprev.dark img{filter:drop-shadow(0 10px 22px rgba(0,0,0,.5))}
.setprev figure{margin:0}
.setprev figcaption{margin-top:7px; font-size:11.5px; color:rgba(237,241,240,.55); line-height:1.4}
.setprev:not(.dark) figcaption{color:var(--muted)}
.pv-eyebrow{
  margin:0 0 7px; font-size:9.5px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--turq); font-weight:700;
}
.pv-head{
  font-family:"Newsreader",Georgia,serif; font-weight:400; font-size:1.45rem;
  line-height:1.08; letter-spacing:-.015em; margin:0 0 .35rem; color:#F7F9F8;
}
.pv-sub{margin:0; font-size:13px; line-height:1.5; color:rgba(237,241,240,.72)}
.setprev:not(.dark) .pv-sub{color:var(--muted)}
.pv-cap{
  margin:12px 0 0; padding-top:9px; border-top:1px solid rgba(237,241,240,.16);
  font-size:11.5px; color:rgba(237,241,240,.6);
}
.pv-quote{
  font-family:"Newsreader",Georgia,serif; font-size:1.02rem; line-height:1.45;
  margin:0 0 6px; color:#F3F6F5;
}
.pv-quote::before{content:"\201C"} .pv-quote::after{content:"\201D"}
.pv-cite{margin:0 0 10px; font-size:11.5px; letter-spacing:.06em; color:rgba(237,241,240,.6)}
.pv-prose p{margin:0 0 7px; font-size:12.5px; line-height:1.55; color:var(--ink)}
.pv-prose p:last-child{margin-bottom:0}
.pv-prose .pv-h1{font-family:"Newsreader",Georgia,serif; font-size:1.2rem; margin:0 0 8px; color:var(--ink)}
.pv-empty{color:var(--muted); font-style:italic}
/* Four lines of an About page is a preview; the whole thing is the page. */
.setprev-about .pv-prose{max-height:9.5rem; overflow:hidden; position:relative;
  -webkit-mask-image:linear-gradient(#000 72%,transparent); mask-image:linear-gradient(#000 72%,transparent)}

/* The fields. Same grid as the studio's other forms, but with no panel of its
   own -- the card is already the panel, and a box inside a box is the look
   this page was trying to get away from. */
.setform{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:14px 16px; margin-top:18px;
}
.setform .wide{grid-column:1/-1}
.setform label{gap:5px}
.setform label > .sub{margin-top:1px; line-height:1.45}
.setform .check{flex-direction:row; align-items:center; gap:9px; color:var(--ink); min-height:40px}
.setform .check input{width:auto; min-width:20px; min-height:20px}
.setform input,.setform select,.setform textarea{box-sizing:border-box; max-width:100%}
.setform textarea{font:400 15px/1.55 "Karla",sans-serif}
/* Counts the words in an application box, because the call for entry is what
   asks for a number. Sits under the field, right, out of the reading line. */
.wordcount{align-self:flex-end; font-size:11.5px; color:var(--muted); letter-spacing:.02em}

/* Save belongs to its card and says which card it is. Above a hairline so the
   fields end somewhere, and right-aligned because that is where a hand leaves
   a form. */
.setsave{
  grid-column:1/-1; display:flex; align-items:center; justify-content:flex-end;
  gap:12px; flex-wrap:wrap; border-top:1px solid var(--line); padding-top:15px; margin-top:2px;
}
/* Nothing changed yet, so the button is an offer rather than a pending job --
   nine filled buttons down a page all read as nine things you have left undone.
   It fills the moment the card is touched. JS-set: with the script missing
   every button stays filled and working, which is the right way round. */
.setsave .cta.idle{background:transparent; color:var(--accent)}
.setsave .cta.idle:hover{background:var(--accent); color:var(--on-accent)}
.savedpill{
  font:600 12.5px "Karla",sans-serif; color:var(--ok-ink); letter-spacing:.02em;
  background:var(--ok-bg); border:1px solid var(--ok-line); border-radius:99px; padding:4px 12px;
  transition:opacity .5s ease;
}
.savedpill::before{content:"\2713\00a0"}
.savedpill.gone{opacity:0}

/* Four named backdrops, each with its sentence. A dropdown hid three of the
   four behind a click and described none of them. */
.setpick{display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr)); gap:10px}
.setpick .pick{
  flex-direction:row; align-items:flex-start; gap:10px; margin:0;
  border:1px solid var(--line); border-radius:3px; padding:11px 13px;
  background:var(--wall); cursor:pointer; transition:border-color .15s, background .15s;
}
.setpick .pick:hover{border-color:var(--rule); background:var(--paper)}
.setpick .pick.on{border-color:var(--accent); background:var(--paper)}
.setpick .pick input{width:auto; min-width:18px; min-height:18px; margin-top:2px}
.setpick .pick strong{display:block; color:var(--ink); font-size:14px; font-weight:600}
.setpick .pick .sub{margin-top:2px}
@media (max-width:760px){
  .setform{grid-template-columns:1fr}
  .setprev-hero,.setprev-box{grid-template-columns:96px minmax(0,1fr); gap:12px}
  /* The portrait leads on a phone, the way it does on the About page itself. */
  .setprev-about{grid-template-columns:1fr}
  .setprev-about figure{order:-1; max-width:130px}
  .setsave{justify-content:stretch}
  .setsave .cta{flex:1; text-align:center}
}

/* ---------- the studio on a phone ----------
   A six-column table cannot be read at 390px, and the old rule was that there
   was no rule: the table simply ran off the side and took the page's
   horizontal scroll with it. Each row becomes a card, and every cell carries
   its column name in data-label because the heading row is gone. */
@media (max-width:760px){
  .grid-t,.grid-t tbody,.grid-t tr,.grid-t td{display:block; width:100%}
  .grid-t{border:0; background:none; border-radius:0; overflow:visible}
  .grid-t thead{display:none}
  .grid-t tbody tr{
    background:var(--paper); border:1px solid var(--line); border-radius:4px;
    padding:12px 14px; margin:0 0 12px; box-shadow:0 1px 2px rgba(23,26,24,.04);
  }
  .grid-t tbody tr:hover{background:var(--paper)}
  .grid-t td,.grid-t tbody tr:last-child td{border-bottom:0; padding:5px 0}
  .grid-t td[data-label]:not([data-label=""])::before{
    content:attr(data-label) " "; display:inline-block; min-width:5.2rem;
    color:var(--muted); font-size:10.5px; letter-spacing:.09em; text-transform:uppercase;
    font-weight:600; vertical-align:baseline;
  }
  /* A work with no photograph yet has an empty thumbnail cell; in card
     layout that is a stray gap rather than a column. */
  .grid-t td:empty{display:none}
  /* In card layout the column name is printed inline before the cell. A .sub
     that IS the whole cell should sit beside it rather than wrapping to its
     own line; one that follows a title is a genuine second line, which is why
     this is :only-child and not every .sub. */
  .grid-t td > .sub:only-child{display:inline}
  .grid-t td.num,.grid-t td.right{text-align:left}
  /* The thumbnail leads the card and the row controls close it, so the middle
     reads as a plain list of facts. */
  .grid-t td.thumb{width:auto; padding-bottom:8px}
  .grid-t td.thumb img,.grid-t.tight td.thumb img{width:88px; height:88px}
  .grid-t.tight td.thumb{width:auto}
  .grid-t td.right{padding-top:8px; border-top:1px solid var(--line); margin-top:8px}
  .grid-t select{width:100%; max-width:none}
  .grid-t .inline{display:flex; flex-wrap:wrap; width:100%}
  .grid-t .inline input{flex:1 1 8rem}
  .adminhead{flex-wrap:wrap; gap:10px}
  .adminhead .cta{width:100%; text-align:center}
  .alerts{flex-direction:column; gap:8px}
  .formgrid{grid-template-columns:1fr}
  .shots{grid-template-columns:repeat(auto-fill,minmax(130px,1fr))}
  .msg header{gap:6px 12px}
}

/* Three folders across the top of Messages. Squared and underlined rather than
   boxed: this is a place marker inside a page, not a second navigation bar
   competing with the rail on the left. */
.folders{display:flex; flex-wrap:wrap; gap:22px; margin:0 0 20px;
  border-bottom:1px solid var(--rule); padding-bottom:0}
.folders a{
  text-decoration:none; color:var(--muted); padding:0 0 10px; margin-bottom:-1px;
  font:600 13px/1 "Karla",sans-serif; letter-spacing:.08em; text-transform:uppercase;
  border-bottom:2px solid transparent; display:inline-flex; align-items:center; gap:7px;
}
.folders a:hover{color:var(--ink)}
.folders a.on{color:var(--ink); border-bottom-color:var(--accent)}
/* The waiting count. Only drawn when there is one, so an empty folder is a
   plain word rather than a permanent zero nobody reads. */
.folders .n{
  background:var(--accent); color:var(--on-accent); border-radius:999px; min-width:18px;
  padding:2px 6px; font-size:11px; letter-spacing:0; text-align:center;
}
.back{display:inline-block; margin-bottom:14px; font-size:13.5px; color:var(--muted);
  text-decoration:none}
.back:hover{color:var(--ink)}

.msgs{display:flex; flex-direction:column; gap:12px}
.msg .who{font-weight:700; color:var(--ink); text-decoration:none}
.msg .who:hover{text-decoration:underline; text-underline-offset:3px}
.msg .subj{font-weight:700; margin-bottom:4px}
.msg.open{border-color:var(--ink)}
/* A reply that has already gone, shown under the message it answers. Indented
   and quieter than the incoming mail so the two sides of the exchange are
   distinguishable at a glance without colour doing all the work. */
.msg.sent-note{background:var(--paper-hi); margin-left:clamp(0px,4vw,34px)}
.msg .answered{color:var(--accent)}
.msg .failed{color:var(--danger)}
.sendfail{background:var(--err-bg); border:1px solid var(--err-line); padding:10px 14px; margin:0 0 14px}

.compose{display:flex; flex-direction:column; gap:12px; max-width:46rem;
  background:var(--paper); border:1px solid var(--line); padding:clamp(14px,2.4vw,20px)}
.compose textarea{font:400 15px/1.55 "Karla",sans-serif}
.compose-acts{display:flex; flex-wrap:wrap; align-items:center; gap:16px}
.compose-acts .spacer{flex:1}
.fineprint{margin:0; font-size:12.5px; color:var(--muted); line-height:1.5}
.msg{background:var(--paper); border:1px solid var(--line); padding:13px 15px}
.msg.done{opacity:.55}
.msg header{display:flex; flex-wrap:wrap; gap:10px; align-items:baseline; margin-bottom:8px}
.msg .kind{font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); border:1px solid var(--rule); padding:2px 6px}
.msg p{margin:0 0 8px; white-space:pre-wrap}
/* Two controls on a message now, so they sit in a row rather than stacking. */
/* Two controls in one table cell. Table cells cannot be flex containers
   without breaking the row, so the pair sits in a box of its own. */
.rowacts{display:flex; flex-wrap:wrap; align-items:center; justify-content:flex-end; gap:16px}
.msg-acts{display:flex; flex-wrap:wrap; align-items:center; gap:18px}
/* "in contacts" sits where the button was. A STATE, not a control: it must not
   look clickable, and it must not look like an error either. */
.msg-acts .noted{color:var(--muted); font-size:13.5px}
/* Addresses that asked to come off. Present, plainly secondary, and never
   mistakable for the live list above them. */
.offhead{
  font-family:"Newsreader",Georgia,serif; font-weight:400; font-size:1.2rem;
  margin:34px 0 6px; color:var(--muted);
}
.grid-t.off{background:transparent}
.grid-t.off tbody tr{background:var(--paper-hi)}
.grid-t.off td[data-label="Email"]{color:var(--muted); text-decoration:line-through}

/* ---------- the schedule ----------
   Not a table: a show is a date and a name, and the date is the thing you are
   scanning for, so it leads its own column and the rest hangs off it. */
.offhead.up{margin-top:26px}
.sched{list-style:none; margin:0 0 10px; padding:0}
.sched li{
  display:grid; grid-template-columns:13rem minmax(0,1fr); gap:4px 20px;
  align-items:baseline; padding:14px 16px; background:var(--paper);
  border:1px solid var(--line); border-left:3px solid var(--rule);
  border-radius:3px; margin-bottom:8px;
}
.sched li.on-now{border-left-color:var(--turq)}
.sched .dates{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:13.5px;
  color:var(--ink); font-variant-numeric:tabular-nums;
}
.sched .nowtag{
  display:inline-block; margin-left:8px; font-size:10px; font-weight:700;
  letter-spacing:.09em; text-transform:uppercase; color:var(--accent);
  border:1px solid var(--turq); border-radius:2px; padding:1px 6px;
}
.sched .what a{font-weight:600}
.sched.past li{background:var(--paper-hi)}
.sched.past .dates{color:var(--muted)}

/* Ticking paintings into a show. A grid of real thumbnails, because she knows
   the work by sight long before she knows what I called it. */
.pickers{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(190px,1fr)); gap:8px;
  max-height:22rem; overflow-y:auto; padding:4px;
  border:1px solid var(--line); border-radius:3px; background:var(--wall);
}
.pick{
  flex-direction:row; align-items:center; gap:9px; padding:7px 9px; margin:0;
  background:var(--paper); border:1px solid var(--line); border-radius:3px;
  color:var(--ink); font-size:14px; cursor:pointer; min-height:52px;
}
.pick:hover{border-color:var(--rule)}
.pick input{width:auto; min-width:18px; min-height:18px; margin:0; flex:none}
.pick img{
  width:38px; height:38px; object-fit:contain; background:var(--paper);
  border-radius:2px; flex:none;
}
.pick span{min-width:0; overflow-wrap:anywhere}
.pick:has(input:checked){border-color:var(--turq); background:var(--pick-on)}
.danger-row{margin-top:22px}

/* One row of filters above the list. Wraps rather than scrolls; every control
   is the same height so the row reads as a single instrument. */
.filters{
  display:flex; flex-wrap:wrap; gap:9px; align-items:center;
  margin:0 0 14px; padding:12px 14px;
  background:var(--paper); border:1px solid var(--line); border-radius:3px;
}
.filters input[name=q]{flex:1 1 16rem; min-width:0; font-size:16px}
.filters select{flex:0 0 auto; width:auto; font-size:16px; max-width:14rem}
.filters .cta{flex:none}
.filters .link{flex:none}
/* ---------- the three output buttons ----------
   Labels / Price list / Packet each MAKE something out of whatever the filter
   is currently showing. They are buttons now rather than underlined links,
   because they do as much work as Search does -- but OUTLINE, not solid: four
   filled buttons in one row and nothing tells you which one acts on the words
   you just typed.

   Sized to match .cta exactly (min-height, not padding arithmetic) so the row
   still reads as one instrument rather than two sets of controls. */
.filters .outgroup{
  display:flex; flex-wrap:wrap; gap:8px; align-items:center;
  margin-left:auto; padding-left:14px;
  /* The rule is the whole point of the grouping: filters on one side of it,
     things you produce on the other. */
  border-left:1px solid var(--line);
}
.btn-out{
  -webkit-appearance:none; appearance:none;
  display:inline-flex; align-items:center; justify-content:center; flex:none;
  min-height:43px; padding:0 17px; box-sizing:border-box;
  background:transparent; color:var(--accent); border:1px solid var(--accent);
  border-radius:2px; font:600 14px/1 "Karla",sans-serif; letter-spacing:.03em;
  text-decoration:none; cursor:pointer; white-space:nowrap;
  transition:background .2s ease, color .2s ease;
}
.btn-out:hover{background:var(--accent); color:var(--on-accent)}
/* Small variant, for an outline button that sits in a row of text links --
   the full-height one is a toolbar control and towers over them. */
.btn-out.sm{min-height:32px; padding:0 12px; font-size:13px; letter-spacing:.02em}
@media (max-width:640px){
  .filters{padding:10px 12px}
  .filters input[name=q]{flex:1 1 100%}
  .filters select{flex:1 1 8rem; max-width:none}
  .filters .cta{flex:1 1 100%; text-align:center}
  /* On a phone the row has already stacked, so the rule and the auto margin
     have nothing left to separate -- they would just indent the group. The
     three share a line instead, which is the width they need. */
  .filters .outgroup{
    width:100%; margin-left:0; padding-left:0; border-left:0;
    padding-top:10px; border-top:1px solid var(--line);
  }
  .btn-out{flex:1 1 auto; min-height:40px; font-size:13.5px; padding:0 12px}
}

/* ---------- the panels below the work form ----------
   The page had become one very tall column: form, then four full-width boxes,
   then the photographs. These sit two-across where there is room, and each
   carries a compact header instead of a serif h2 with its own margins — which
   is most of the height saved. */
.admin .panels{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(370px,1fr));
  gap:14px; margin:18px 0 26px; align-items:start;
}
/* SCOPED TO .admin, AND box-shadow:none.
   `.panel` was already taken -- it is the shadow-box mount on the contact
   page, a 9px dark frame with a 20px offset slab of mat board behind it and
   the inset shadow that gives a real shadow box its depth. This rule sits
   after it and weighs the same, so it was overriding background, border and
   padding while quietly INHERITING all three shadows. The three cards on a
   painting's page have been wearing a mount built for one enquiry form,
   complete with a turquoise slab sticking out behind each of them.
   Scoping it under .admin settles the collision, and the reset is explicit so
   nobody has to know the other rule exists. */
.admin .panel{
  background:var(--paper); border:1px solid var(--line); border-radius:3px;
  padding:16px 18px 18px; min-width:0;
  box-shadow:none; position:static;
}
.admin .panel-wide{grid-column:1/-1}
.admin .panel > h3{
  margin:0 0 12px; font-family:"Karla",sans-serif;
  font-size:11px; letter-spacing:.09em; text-transform:uppercase;
  color:var(--accent); font-weight:600;
}
.admin .panel .now{margin:0 0 2px; font-size:15px}
.admin .panel .now strong{font-weight:600}
/* Care wants its whole row of fields; on a half-width panel that means the
   controls wrap rather than squeeze to nothing. */
.moverow{display:flex; flex-wrap:wrap; gap:8px; margin:14px 0 0; align-items:center}
.moverow input{flex:1 1 9rem; min-width:0; font-size:16px}
.moverow input[type=date]{flex:0 0 9.5rem}
.moverow input[name=cost]{flex:0 0 6rem}
.moverow .cta{flex:1 1 auto; min-width:6rem}
/* The trail is a list of facts: date, place, note. Newest first, so "where is
   it" is the first line read. */
.trail{list-style:none; margin:14px 0 0; padding:0; border-top:1px solid var(--line)}
.trail li{
  display:flex; flex-wrap:wrap; align-items:baseline; gap:3px 10px;
  padding:8px 0; border-bottom:1px solid var(--line);
}
.trail li:last-child{border-bottom:0}
.trail .mdate{
  flex:0 0 6rem; font-size:12px; color:var(--muted);
  font-variant-numeric:tabular-nums;
}
.trail .mplace{font-weight:600; font-size:14px}
.trail .sub{display:inline; flex:1 1 8rem; font-size:12.5px}
.trail .mcost{font-variant-numeric:tabular-nums; color:var(--ink); font-size:13.5px}
.trail form{margin-left:auto}
.qrrow{display:flex; flex-wrap:wrap; gap:14px; align-items:center}
.qrrow img{flex:none; border:1px solid var(--line); border-radius:3px; background:#fff;
  width:110px; height:110px}
.qrrow > div{flex:1 1 11rem; min-width:0}
.qrrow p{margin:0 0 7px; font-size:13px}
.qrrow .codeurl{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:11.5px; overflow-wrap:anywhere}
@media (max-width:560px){
  .trail .mdate{flex:1 1 100%}
  .trail form{margin-left:0}
}
.trail .mcost{font-variant-numeric:tabular-nums; color:var(--ink); font-size:14px}
.trail form{margin-left:auto}
@media (max-width:560px){
  .trail .mdate{flex:1 1 100%}
  .trail form{margin-left:0}
}
@media (max-width:560px){
  .sched li{grid-template-columns:1fr; gap:2px}
}


/* ================= the landing =================
   A dark entry, then the lit room. The photograph on the front is a shadow
   box with a black frame, so on a dark ground the frame falls away and the
   painting floats -- which is the whole reason this page is not the cream
   wall the rest of the shop is. */
/* The masthead and the hero are ONE painted surface, so the gradient is on the
   body and both of them are transparent over it. Painting them separately
   leaves a step where they meet -- measured at #16232E against #133744 -- and
   no amount of matching the base colour fixes it, because the washes land at
   different strengths on either side of the join. The lit section below paints
   its own ground back over the top. */
body.home{
  background-color:var(--ground-deep);
  background-image:
    /* THE LAST LAYER MATTERS AS MUCH AS THE FIRST, and it is listed first
       because that is the one painted on top. The box is one viewport tall and
       the dark region is TALLER than that on a wide window -- measured at
       1920x913, the hero ends at 880 and the box band runs to 1194, so the
       gradient stopped 33px inside the band and the copy was cut in half by a
       hard edge where the washes gave way to flat --ground-deep. This fades the
       whole thing to exactly that colour before the box ends, so wherever the
       edge falls there is nothing to see. Do not remove it and cap the washes
       instead: a wash centred near the bottom is strongest at the cut. */
    linear-gradient(to bottom, rgba(10,18,26,0) 72%, var(--ground-deep) 97%),
    radial-gradient(115% 80% at 14% 22%, rgba(9,187,213,.22) 0%, rgba(9,187,213,.08) 40%, transparent 66%),
    radial-gradient(80% 85% at 94% 50%, rgba(124,103,121,.24) 0%, transparent 62%),
    radial-gradient(90% 70% at 34% 100%, rgba(190,213,102,.12) 0%, transparent 58%),
    radial-gradient(70% 60% at 68% 86%, rgba(9,187,213,.09) 0%, transparent 60%),
    linear-gradient(168deg, var(--ground) 0%, #101A23 44%, var(--ground-deep) 100%);
  background-repeat:no-repeat;
  /* One viewport tall. Anything past it lands on --ground-deep, which is the
     colour the gradient ends on anyway, so a taller hero simply continues. */
  background-size:100% 100vh;
}
.home .masthead{
  /* The rule is made INVISIBLE rather than removed, and the padding is left
     alone, so the masthead box is the same 113px on every page. Dropping the
     border and 4px of padding here made the home header 5px shorter, which
     moved the whole page under it and read as the nav sitting in a different
     place from one page to the next. */
  background:transparent; border-bottom-color:transparent;
}
.home .masthead .wordmark{color:#F4F6F5}
.home .wordmark-pre{color:var(--turq)}
/* On the dark entry the mark is the only lit thing up there; a hairline keeps
   its black frame edge from bleeding into the ground. */
.home .wordmark .mark{box-shadow:0 0 0 1px rgba(237,241,240,.14)}
.home .masthead nav a{color:rgba(244,246,245,.62)}
.home .masthead nav a:hover{color:var(--turq-lo)}
.home main{padding:0}
.home .flash{margin:0 clamp(18px,5vw,56px)}

.hero{
  background:transparent;      /* painted by body.home, see above */
  color:#EDF1F0;
  display:grid; grid-template-columns:minmax(0,1fr) minmax(280px,26rem);
  justify-content:center; gap:clamp(26px,4vw,56px); align-items:center;
  padding:clamp(18px,3vw,40px) clamp(18px,5vw,56px) clamp(52px,7vw,88px);
}
.hero-art{display:flex; justify-content:flex-end}
.hero-art img,.hero-art .noimg{
  width:auto; max-width:100%; max-height:70vh; object-fit:contain;
  /* The frame's own shadow is in the photograph. This one seats it on the page. */
  filter:drop-shadow(0 30px 70px rgba(0,0,0,.55));
}

/* The hero's photographs, stacked so they can cross-dissolve.
   The first <picture> is left in flow and is what gives the stack its size --
   so the hero measures exactly as it did when there was only ever one image.
   The others are laid over it at that size and fade in turn.
   The drop-shadow moves up to the stack: left on each image it would be drawn
   twice mid-fade, and the seam shows on a dark ground. */
/* The photographs and the painting's name, as one column. .hero-art stays a
   row with a single child, so its existing right-align on a laptop and centre
   on a phone keep working untouched. */
.hero-figure{
  display:flex; flex-direction:column; align-items:center;
  gap:16px; margin:0; min-width:0;
}
.hero-piece{
  margin:0; line-height:1.4; text-align:center;
  font-size:.95rem; letter-spacing:.01em;
}
.hero-piece a{
  color:rgba(237,241,240,.82); text-decoration:none;
  border-bottom:1px solid rgba(237,241,240,.28); padding-bottom:2px;
  transition:color .2s ease, border-color .2s ease;
}
.hero-piece a:hover{color:var(--turq); border-bottom-color:var(--turq)}

.hero-art .hero-stack{
  position:relative; display:inline-block; line-height:0;
  filter:drop-shadow(0 30px 70px rgba(0,0,0,.55));
}
.hero-art .hero-stack img{filter:none}
.hero-shot{display:block; margin:0}
.hero-shot + .hero-shot{position:absolute; inset:0}
.hero-shot + .hero-shot img{width:100%; height:100%; object-fit:contain}

/* Only a stack that is actually rotating hides anything. A single-photograph
   hero never gets `.rotating`, so it cannot be left invisible by a script that
   failed to run -- and neither can a rotating one, because `.on` is printed
   into the markup rather than added on load. */
.hero-stack.rotating .hero-shot{opacity:0; transition:opacity 1100ms ease-in-out}
.hero-stack.rotating .hero-shot.on{opacity:1}

/* Asked not to animate: no dissolve, no rotation. The script checks the same
   query and stops scheduling, so the hero simply stays on the first. */
@media (prefers-reduced-motion:reduce){
  .hero-stack.rotating .hero-shot{transition:none}
}
.hero-say{max-width:34rem}
.eyebrow{
  margin:0 0 14px; font-size:11.5px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--turq); font-weight:700;
}
.hero-say h1{
  font-family:"Newsreader",Georgia,serif; font-weight:400;
  font-size:clamp(2.1rem,5vw,3.4rem); line-height:1.06; letter-spacing:-.015em;
  margin:0 0 .6rem; color:#F7F9F8;
}
.hero-say .lede{color:rgba(237,241,240,.72); font-size:1.06rem; margin:0}
/* The hero's thumbnails: under the photograph, flush with its right edge
   (align-self works because the stack is the widest thing in the figure).
   Whole photograph (contain, never cropped -- the same rule as the wall) on
   the entry's own ground; the one on screen takes the turquoise edge. The
   negative top margin pulls them in under the image, closer than the
   figure's gap, so they read as belonging to it. */
.hero-thumbs{
  display:flex; flex-wrap:wrap; justify-content:flex-end; gap:8px;
  flex:0 1 auto; margin-left:auto;
}
/* The row under the photograph: name left, thumbnails right, stretched to
   the photograph's width (the stack is the widest thing in the figure). On a
   phone the name wraps to two lines before the thumbnails leave the row. A
   hero with one photograph has only the name, centred as it always was. */
.hero-foot{
  display:flex; align-items:center; gap:14px; align-self:stretch;
  margin:-4px 0 0;
}
.hero-foot .hero-piece{flex:0 1 auto; min-width:0; text-align:left}
.hero-foot.solo{justify-content:center; margin-top:0}
.hero-foot.solo .hero-piece{text-align:center}
/* Phones: smaller thumbnails that never wrap -- the name gives way instead.
   Scoped under .hero-foot so it outranks the base .hero-thumb rule below. */
@media (max-width:560px){
  .hero-foot{gap:10px}
  .hero-foot .hero-thumbs{gap:5px; flex-wrap:nowrap}
  .hero-foot .hero-thumb{width:34px; height:34px; padding:2px; flex:0 0 auto}
}
.hero-thumb{
  width:48px; height:48px; padding:3px; border:1.5px solid rgba(237,241,240,.22);
  border-radius:2px; background:rgba(0,0,0,.25); cursor:pointer;
  opacity:.62; transition:opacity .2s ease, border-color .2s ease;
}
.hero-art .hero-thumb img{width:100%; height:100%; max-height:none; object-fit:contain; filter:none}
.hero-thumb:hover{opacity:1; border-color:rgba(237,241,240,.55)}
.hero-thumb.on{opacity:1; border-color:var(--turq)}
@media (prefers-reduced-motion:reduce){.hero-thumb{transition:none}}
.hero-do{display:flex; flex-wrap:wrap; gap:12px; margin-top:30px}
.hero-do .cta{background:var(--turq); border-color:var(--turq); color:#06222A}
.hero-do .cta:hover{background:var(--turq-lo); border-color:var(--turq-lo)}
.hero-do .cta.ghost{
  background:transparent; color:#EDF1F0; border-color:rgba(237,241,240,.35);
}
.hero-do .cta.ghost:hover{border-color:var(--chart); color:var(--chart)}
.hero-cap{
  margin:26px 0 0; padding-top:16px; border-top:1px solid rgba(237,241,240,.16);
  font-size:14px; color:rgba(237,241,240,.6); line-height:1.55;
}
.home .hero a:focus-visible,.home .hero button:focus-visible,
.gm-overlay :focus-visible{outline:2px solid var(--turq-lo); outline-offset:3px}

/* ---------- the box band, still on the dark entry ---------- */
/* Transparent like the hero, so it sits on the same ground -- past 100vh the
   gradient has ended on --ground-deep, which is the colour it continues in.
   A black shadow box photographed against a dark page loses its frame and
   floats, which is the whole reason the entry is dark in the first place. */
.boxband{
  background:transparent; color:#EDF1F0;
  display:grid; grid-template-columns:minmax(0,38%) minmax(0,1fr);
  gap:clamp(24px,4vw,56px); align-items:center;
  padding:clamp(36px,5vw,62px) clamp(18px,5vw,56px) clamp(52px,7vw,88px);
  border-top:1px solid rgba(237,241,240,.10);
}
/* Until she photographs a box from an angle there is one thing to read, so it
   takes the middle rather than sitting in a column beside an empty one. */
.boxband.nophoto{grid-template-columns:1fr}
.boxband.nophoto .boxband-say{max-width:44rem; margin-inline:auto; text-align:center}

.boxband-shot{margin:0}
.boxband-shot img{
  display:block; width:100%; height:auto;
  filter:drop-shadow(0 26px 60px rgba(0,0,0,.55));
}
.boxband-shot figcaption{margin-top:12px; font-size:13px; color:rgba(237,241,240,.55)}

/* Her own sentence, so it is set as a quotation and not as body copy: serif,
   near the size of the h1 above it, with her name under it. No quote marks --
   the attribution does that work, and glyphs would fight the hero's type. */
.boxband-say blockquote{margin:0; padding:0; border:0}
.boxband-say blockquote p{
  font-family:"Newsreader",Georgia,serif; font-weight:400;
  font-size:clamp(1.2rem,2.2vw,1.65rem); line-height:1.4;
  letter-spacing:-.01em; color:#F7F9F8; margin:0;
}
.boxband-say cite{
  display:block; margin-top:14px; font-style:normal; font-size:11.5px;
  letter-spacing:.2em; text-transform:uppercase; color:rgba(237,241,240,.5);
}
/* The measurements are mine and factual, so they are set as the hero caption
   is -- under a hairline, quiet, clearly not part of the quotation. */
.boxband-spec{
  margin:22px 0 0; padding-top:16px;
  border-top:1px solid rgba(237,241,240,.16);
  font-size:14px; line-height:1.55; color:rgba(237,241,240,.62);
}

@media (max-width:900px){
  /* Stacked, the photograph leads and the words follow it. */
  .boxband{grid-template-columns:1fr; gap:clamp(20px,5vw,32px)}
  .boxband-shot img{max-height:56vh; width:auto; margin-inline:auto}
}

/* ---------- the wall, below the entry ---------- */
/* Opaque on purpose: this is what covers the dark entry painted on the body. */
/* The sign-up row where the home page carries it: between the dark entry and
   the wall, on the cream ground the wall starts. It is the first thing under
   the hero because that is where interest actually is -- the same row at the
   foot of the home page sits beneath every painting she owns.

   Same side padding as .wall so the two read as one column, and the rule
   underneath does the separating, which is why the form's own footer rule is
   taken off below: two hairlines a few pixels apart is a mistake, not a band. */
.signup-band{
  background:var(--wall);
  padding:clamp(26px,4vw,46px) clamp(18px,5vw,56px);
  border-bottom:1px solid var(--line);
}
.signup-band .signup{padding-bottom:0; margin-bottom:0; border-bottom:0}
/* The entry above is dark, so the field would otherwise pick up the ground
   rather than sit on paper -- the same reason the footer needed this. */
.home .signup-band input[type=email]{background:var(--paper)}

.wall{background:var(--wall); padding:clamp(40px,6vw,72px) clamp(18px,5vw,56px) 72px}
.home .foot{background:var(--wall)}
.wall-head{
  display:flex; flex-wrap:wrap; align-items:baseline; gap:10px 22px;
  margin:0 0 clamp(24px,3.5vw,38px); padding-bottom:14px;
  border-bottom:1px solid var(--line);
}
.wall-head h2{
  font-family:"Newsreader",Georgia,serif; font-weight:400; font-size:1.5rem;
  margin:0; margin-right:auto; letter-spacing:-.01em;
}
.wall-head .fine{margin:0}
/* The collection the wall is showing, on its own line under "Available" --
   full width, because it wraps the heading row rather than sitting in it. The
   name is set as the hero's eyebrow is, but in the link colour: the entry's
   turquoise is for a dark ground and would go pale on this one. */
.wall-coll{flex:1 0 100%; margin:6px 0 0}
.wall-coll > a{
  font-size:11.5px; letter-spacing:.2em; text-transform:uppercase; font-weight:700;
  color:var(--accent); text-decoration:none;
  border-bottom:1px solid transparent;
}
.wall-coll > a:hover{border-bottom-color:currentColor}
/* The rest of what she wrote is on the collection's own page. Set as a link in
   the sentence, not as another eyebrow. */
.wall-coll .more{color:var(--accent); white-space:nowrap}
.wall-coll p{
  margin:7px 0 0; max-width:56ch;
  color:var(--muted); font-size:14.5px; line-height:1.6;
}

@media (max-width:900px){
  .hero{grid-template-columns:1fr; padding-bottom:clamp(38px,8vw,60px)}
  .hero-art{justify-content:center}
  .hero-art img{max-height:62vh}
  .hero-say{max-width:none}
}
@media (max-width:560px){
  /* Stacked, they should be one column of equal buttons, not two ragged ones. */
  .hero-do{flex-direction:column; align-items:stretch}
  .hero-do .cta{text-align:center}
}

/* ================= cinematic viewer =================
   Borrowed wholesale in shape from the Bayview gallery -- dark stage, arrows,
   filmstrip, keys and swipe. What differs is the caption: in a shop it has to
   read like a gallery label and get you to the work's own page. */
/* OFF THE BOTTOM CORNER, BUT NOT MID-SCREEN (2026-09-09, Paul, in two passes).
   It began in the corner where chat bubbles and cookie bars live — the one
   place people have trained themselves not to look. Dead centre was too far:
   it sat over the middle of the artwork. This is the compromise, measured from
   the BOTTOM so it stays put on a short screen, and high enough to read as part
   of the page rather than as a widget stuck to the glass. Anchored to the
   bottom rather than the top on purpose: the masthead wraps to two rows on a
   phone, so any top offset either collides with the nav or floats low. */
/* ---------- opening the viewer from a work page ----------
   The floating capsule that used to sit here is gone; see the note in
   _gallery.html. In its place the lead photograph IS the control, which is
   both the convention everywhere else people look at pictures and strictly
   better than what it replaced -- the button always opened at slide zero,
   this opens at the painting already on screen.

   The whole image is the target, with cursor:zoom-in doing the telling. The
   chip is a hint for people who need one, not the hit area. */
.plate-img{position:relative; display:block}
.plate-zoom{
  position:absolute; inset:0; width:100%; height:100%;
  display:flex; align-items:flex-end; justify-content:flex-end;
  padding:clamp(10px,1.6vw,16px);
  background:none; border:0; cursor:zoom-in;
}
.zoomchip{
  display:inline-flex; align-items:center; gap:7px;
  background:rgba(13,22,30,.82); color:#EDF1F0;
  border:1px solid rgba(237,241,240,.22); border-radius:2px;
  padding:8px 12px; font:600 11.5px/1 "Karla",sans-serif;
  letter-spacing:.08em; text-transform:uppercase;
  opacity:0; transform:translateY(5px);
  transition:opacity .18s ease, transform .18s ease, border-color .18s ease;
}
.zoomchip svg{width:14px; height:14px; color:var(--turq)}
.plate-zoom:hover .zoomchip,
.plate-zoom:focus-visible .zoomchip{opacity:1; transform:none; border-color:var(--turq)}
.plate-zoom:focus-visible{outline:2px solid var(--turq); outline-offset:-2px}
/* Nothing hovers on a touch screen, so there the hint simply stands. */
@media (hover:none){
  .zoomchip{opacity:1; transform:none}
}
@media (prefers-reduced-motion:reduce){
  .zoomchip{transition:none}
}

.gm-overlay{
  position:fixed; inset:0; z-index:1000; display:flex; align-items:center; justify-content:center;
  background:radial-gradient(125% 125% at 50% 0%, rgba(13,22,30,.9) 0%, rgba(4,8,11,.975) 72%);
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  opacity:0; transition:opacity .35s ease;
}
.gm-overlay.is-open{opacity:1}
.gm-overlay[hidden]{display:none}
.gm-stage{
  position:relative; width:100%; max-width:1180px; height:100%;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:18px;
  padding:max(20px,env(safe-area-inset-top)) 24px max(20px,env(safe-area-inset-bottom));
  transform:translateY(14px) scale(.987); transition:transform .4s cubic-bezier(.22,.61,.36,1);
}
.gm-overlay.is-open .gm-stage{transform:none}
.gm-x{
  position:fixed; top:max(14px,env(safe-area-inset-top)); right:max(14px,env(safe-area-inset-right));
  z-index:6; width:46px; height:46px; border-radius:50%; border:0; cursor:pointer; color:#fff;
  background:rgba(255,255,255,.13); font-size:1.7rem; line-height:1;
  display:flex; align-items:center; justify-content:center;
  -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px); transition:background .2s;
}
.gm-x:hover{background:rgba(255,255,255,.28)}
/* The same square buttons as the masthead, in the same dark-ground form the
   home page uses -- capsules were a second, competing shape for what is the
   same kind of control. Kept in sync with `.home .masthead nav a`. */
.gm-tabs{display:flex; flex-wrap:wrap; gap:10px; justify-content:center; flex:0 0 auto}
.gm-tabs[hidden]{display:none}  /* piece mode swaps the tabs for "Whole gallery" */
.gm-tab{
  position:relative; display:inline-block;
  font:600 12.5px/1 "Karla",sans-serif; letter-spacing:.09em; text-transform:uppercase;
  color:rgba(244,246,245,.74); background:transparent;
  border:1.5px solid rgba(237,241,240,.26); border-radius:2px; padding:9px 15px;
  cursor:pointer; box-shadow:3px 3px 0 0 transparent;
  transition:color .18s ease, border-color .18s ease, background .18s ease,
             box-shadow .18s ease, transform .18s ease;
}
.gm-tab:hover{
  color:#fff; border-color:var(--turq);
  box-shadow:3px 3px 0 0 rgba(9,187,213,.45); transform:translate(-1px,-1px);
}
.gm-tab.active{
  background:rgba(255,255,255,.09); border-color:var(--turq); color:#fff;
  box-shadow:4px 4px 0 0 rgba(9,187,213,.5);
}
.gm-tab.active:hover{box-shadow:4px 4px 0 0 rgba(9,187,213,.5); transform:none}
.gm-frame{position:relative; flex:1 1 auto; width:100%; min-height:0;
  display:flex; align-items:center; justify-content:center}
/* No aspect-ratio box and no object-fit:cover here: paintings are not one
   shape and cropping one to fit a stage is the one thing a gallery must not do.
   The stage takes a DEFINITE height and shrink-wraps to whatever width the
   picture then works out to -- height first, width fit-content. Sizing the
   other way round leaves the box at the image's intrinsic width while
   max-height shrinks the picture inside it, and the caption, which is
   positioned against the box, ends up hanging off the side of the painting. */
.gm-stagewrap{
  position:relative; display:block; height:66vh; width:fit-content;
  max-width:min(1100px,88vw);
}
.gm-photo{
  height:100%; width:auto; max-width:100%; display:block; object-fit:contain;
  box-shadow:0 34px 90px rgba(0,0,0,.6);
}
/* The whole stage dissolves, picture and label together, rather than the
   picture alone: the box is sized to each painting, so fading one image over
   another would animate the frame's width at the same time. The new file is
   preloaded before the fade starts, so nothing is ever caught half-drawn. */
.gm-stagewrap{transition:opacity .18s ease}
.gm-stagewrap.swapping{opacity:0}
.gm-caption{
  position:absolute; left:0; right:0; bottom:0; z-index:2;
  display:flex; flex-direction:column; gap:3px;
  padding:52px 20px 16px; color:#fff; text-align:left;
  background:linear-gradient(to top, rgba(4,8,11,.86), transparent);
}
.gm-caption .gm-t{font-family:"Newsreader",Georgia,serif; font-size:1.25rem; line-height:1.2}
.gm-caption .gm-m{font-size:13px; color:rgba(255,255,255,.66)}
.gm-caption .gm-p{font-size:14px; margin-top:5px; color:rgba(255,255,255,.9)}
.gm-caption .gm-sold::before{
  content:""; display:inline-block; width:9px; height:9px; border-radius:50%;
  background:var(--dot); margin-right:7px; vertical-align:1px;
}
.gm-link{color:var(--turq-lo); text-decoration:none; margin-left:10px; font-size:13px}
.gm-link:hover{text-decoration:underline}
.gm-arrow{
  position:absolute; top:50%; transform:translateY(-50%); z-index:4;
  width:52px; height:52px; border-radius:50%; border:0; cursor:pointer; color:#fff;
  background:rgba(255,255,255,.13); font-size:2rem; line-height:1;
  display:flex; align-items:center; justify-content:center;
  -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px); transition:background .2s;
}
.gm-arrow:hover{background:rgba(255,255,255,.3)}
.gm-arrow[hidden]{display:none}
.gm-prev{left:max(8px,calc(50% - 588px))}
.gm-next{right:max(8px,calc(50% - 588px))}
.gm-filmstrip{
  flex:0 0 auto; display:flex; gap:10px; max-width:100%; overflow-x:auto;
  padding:4px 6px 8px; scrollbar-width:thin; -webkit-overflow-scrolling:touch;
  scroll-snap-type:x proximity;
}
.gm-filmstrip::-webkit-scrollbar{height:6px}
.gm-filmstrip::-webkit-scrollbar-thumb{background:rgba(255,255,255,.25); border-radius:6px}
.gm-thumb{
  flex:0 0 auto; width:72px; height:72px; overflow:hidden; padding:0; background:none;
  border:2px solid transparent; opacity:.55; cursor:pointer; scroll-snap-align:center;
  transition:opacity .25s,border-color .25s,transform .25s;
}
.gm-thumb:hover{opacity:.9}
.gm-thumb.active{opacity:1; border-color:#fff; transform:translateY(-2px)}
/* The filmstrip is already a 72px square, so contain costs nothing on this
   work and keeps the strip honest about a piece that is not square. The dark
   ground shows through the letterbox, which suits the viewer. */
.gm-thumb img{width:100%; height:100%; object-fit:contain; display:block}
@media (prefers-reduced-motion:reduce){
  .gm-overlay,.gm-stage,.gm-thumb,.gm-stagewrap,.tile img,.cta{transition:none}
  .gm-stagewrap.swapping{opacity:1}
  ::view-transition-old(root),::view-transition-new(root){animation:none}
  .no-vt main{animation:none}
}
@media (max-width:640px){
  .gm-stage{gap:12px; padding-left:12px; padding-right:12px}
  .gm-stagewrap{height:56vh; max-width:92vw}
  .gm-arrow{width:40px; height:40px; font-size:1.5rem}
  .gm-prev{left:6px} .gm-next{right:6px}
  .gm-thumb{width:58px; height:58px}
  .gm-caption{padding:40px 14px 12px}
  .gm-caption .gm-t{font-size:1.08rem}
}

/* ---------- opportunities ----------
   The same list shape as the schedule, with a third column for where the
   application stands. Deadline leads, because the whole reason this screen
   exists is that a date is coming and something has to happen before it.

   RED IS STILL ONLY "SOLD". An urgent deadline wants an alarm colour and red
   is the obvious one, which is exactly why it cannot be used here -- a red
   edge anywhere on this site means a painting has gone. Gold carries urgency
   instead, the same gold that already means "reserved" in the status column,
   and the countdown chip is filled rather than outlined so it still shouts. */
.sched.opps li{grid-template-columns:13rem minmax(0,1fr) auto auto}
.sched.opps li.u-soon{border-left-color:var(--gold)}
.sched.opps li.u-month{border-left-color:var(--turq)}
.sched.opps li.u-later,.sched.opps li.u-undated{border-left-color:var(--rule)}
.sched.opps li.u-gone{border-left-color:var(--line)}
.sched .countdown{
  display:inline-block; margin-left:8px; font-size:10px; font-weight:700;
  letter-spacing:.09em; text-transform:uppercase;
  border:1px solid var(--rule); border-radius:2px; padding:1px 6px; color:var(--muted);
}
.u-soon .countdown{background:var(--gold); border-color:var(--gold); color:#2A2205}
.u-month .countdown{border-color:var(--turq); color:var(--accent)}

/* Where the application stands. Quiet chips: this column is scanned, not read,
   and five saturated badges down the right-hand edge would out-shout the
   deadlines, which are the point. */
.ostat{
  align-self:center; justify-self:end; white-space:nowrap;
  font-size:10.5px; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
  border:1px solid var(--rule); border-radius:2px; padding:2px 7px; color:var(--muted);
}
.ostat.s-watching{border-color:var(--turq); color:var(--accent)}
.ostat.s-applied{border-color:var(--plum); color:var(--plum)}
.ostat.s-accepted{border-color:var(--accent); background:var(--accent); color:var(--on-accent)}
@media (max-width:640px){
  .sched.opps li{grid-template-columns:minmax(0,1fr)}
  .ostat{justify-self:start; margin-top:4px}
  .miles{justify-self:start}
  .oppfilter{gap:6px}
}

/* HOW FAR. Beside the deadline because the two are read together: a call two
   hours away is a different proposition from one across the county, and the
   answer to "should I bother" is both dates and miles at once.

   Banded rather than shaded by degree -- the question is not how many miles,
   it is whether this is a morning, a day, or a trip. Turquoise is the live
   edge used everywhere else in the studio; far is simply quiet, because far
   is not a warning, it is just further. */
.miles{
  align-self:center; justify-self:end; white-space:nowrap;
  font-size:11.5px; font-weight:700; letter-spacing:.03em;
  font-variant-numeric:tabular-nums; color:var(--muted);
  border:1px solid var(--rule); border-radius:2px; padding:2px 7px;
}
.miles.n-here{border-color:var(--turq); background:var(--turq); color:#06222A}
.miles.n-close{border-color:var(--turq); color:var(--accent)}
.miles.n-day{border-color:var(--rule); color:var(--ink)}
.miles.n-far{border-color:var(--line); color:var(--muted)}

/* The radius control. Links, not a form control: each one is a URL, so a
   filtered view can be bookmarked and the back button behaves. */
.oppfilter{display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:0 0 14px}
.oppfilter-t{font-size:13px; color:var(--muted)}
.oppfilter .sub{display:inline}
.oppfilter-none{margin:0 0 14px}
.chip{
  font-size:12.5px; color:var(--muted); text-decoration:none; white-space:nowrap;
  border:1px solid var(--line); border-radius:99px; padding:5px 12px;
  background:var(--paper); transition:color .15s, border-color .15s;
}
.chip:hover{color:var(--accent); border-color:var(--rule)}
.chip.on{border-color:var(--accent); color:var(--accent); font-weight:600}
.chip.clear{border-style:dashed}
/* The call's own page. Quiet next to the title -- it is a way out of the
   studio, not the thing being named. */
.outlink{
  margin-left:10px; font-size:12px; color:var(--muted); text-decoration:none;
  white-space:nowrap; border-bottom:1px solid var(--line);
}
.outlink:hover{color:var(--accent); border-bottom-color:var(--accent)}

/* ---------- calls from outside ----------
   A holding pen above her real list, and it must never be mistaken for it. So:
   no paper, no rule down the left edge, a dotted border instead -- the visual
   language of a draft. These were published by somebody else and have not been
   judged yet; the two buttons say the only two things she can do about one. */
.finder{
  border:1px dashed var(--rule); border-radius:4px;
  padding:clamp(13px,2vw,18px); margin:0 0 22px; background:var(--paper-lo);
}
.finder-head{display:flex; align-items:baseline; gap:14px; flex-wrap:wrap}
.finder-head .offhead{margin:0; margin-right:auto}
.finder-go{display:flex; flex-wrap:wrap; gap:10px; align-items:center; margin:12px 0 0}
.finder-go .sub{flex:1 1 18rem; min-width:0; line-height:1.5}
.finder-note{margin:10px 0 0; line-height:1.55; max-width:70ch}
/* Something is happening that she did not start and cannot hurry. A slow pulse
   rather than a spinner: a spinner says "wait for this", and she should not
   wait for this -- the page is entirely usable while it fills in. */
.placing{display:flex; align-items:center; gap:9px; margin:12px 0 0}
.placing-dot{
  flex:none; width:8px; height:8px; border-radius:50%; background:var(--turq);
}
@media (prefers-reduced-motion:no-preference){
  .placing-dot{animation:placing-pulse 1.6s ease-in-out infinite}
}
@keyframes placing-pulse{
  0%,100%{opacity:.35; transform:scale(.8)}
  50%    {opacity:1;   transform:scale(1.15)}
}
/* TWO LINES A ROW, and rows that touch. Thirty suggestions sitting under her
   own list is a lot of page, and the description was most of the height -- it
   lives on the call's own page, which the title opens, and stays on the row as
   a tooltip. Hairlines between rather than a card each: this is a list to scan
   down, and thirty bordered boxes read as thirty separate decisions. */
.finds{
  list-style:none; margin:14px 0 0; padding:0; background:var(--paper);
  border:1px solid var(--line); border-radius:3px; overflow:hidden;
}
.finds li{
  display:grid; grid-template-columns:minmax(0,1fr) 7.5rem 4.5rem auto;
  gap:2px 14px; align-items:center; padding:9px 13px;
  border-bottom:1px solid var(--line);
}
.finds li:last-child{border-bottom:0}
.finds li:hover{background:var(--paper-hi)}
/* On its way out. Collapsing the row rather than just fading it means the ones
   below move up into the space, so the list closes over it instead of leaving
   a hole that the next repaint swallows. */
.finds li.going{
  opacity:0; transform:translateX(-10px);
  transition:opacity .18s ease, transform .18s ease;
}
@media (prefers-reduced-motion:reduce){
  .finds li.going{transition:opacity .12s ease; transform:none}
}
.find-what{min-width:0}
/* The title IS the way out to the call, so the row loses its separate link. */
.find-t{
  display:block; font-weight:600; color:var(--ink); line-height:1.3;
  text-decoration:none; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
a.find-t:hover{color:var(--accent); text-decoration:underline; text-underline-offset:3px}
.finds .sub{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.find-when{text-align:right; white-space:nowrap}
.find-when .dates{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12.5px;
  color:var(--ink); font-variant-numeric:tabular-nums; display:block;
}
.find-when .countdown{margin:2px 0 0; padding:1px 5px}
.finds .miles{justify-self:end; align-self:center; padding:1px 6px; font-size:11px}
.find-do{display:flex; align-items:center; gap:6px; white-space:nowrap}
.find-do form{margin:0}
.find-do .cta{padding:7px 15px; font-size:13.5px}
/* Dismiss is a cross, not a sentence. On a two-line row "not for me" was wider
   than the button that matters, and the tooltip and aria-label carry the word. */
.link.x{font-size:17px; line-height:1; padding:5px 8px; min-height:0; color:var(--muted);
  text-decoration:none}
.link.x:hover{color:var(--danger)}
@media (max-width:760px){
  .finds li{grid-template-columns:minmax(0,1fr) auto; gap:4px 10px; padding:11px 12px}
  .find-what{grid-column:1/-1}
  .find-when{text-align:left}
  .find-when .dates{display:inline-block}
  .find-when .countdown{margin:0 0 0 6px}
  .finds .miles{justify-self:start}
  .find-do{grid-column:2; justify-self:end}
  .finder-go .cta{width:100%}
}

/* ---------- where to look ----------
   A short list of places, not a feature. Reads as a set of doors rather than a
   toolbar: the name is the link, the sentence under it says what is behind it,
   because "CaFE" tells a person nothing. */
.lookhere{margin:16px 0 0}
.lookhere > summary{
  cursor:pointer; color:var(--accent); font:600 13px "Karla",sans-serif;
  text-decoration:underline; text-underline-offset:3px; min-height:32px;
  display:flex; align-items:center;
}
.looks{list-style:none; margin:12px 0 0; padding:0; display:grid;
  grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:10px}
.looks li{
  background:var(--paper); border:1px solid var(--line); border-radius:3px;
  padding:11px 13px; min-width:0;
}
.look{
  display:block; font-weight:600; color:var(--accent); text-decoration:none;
  line-height:1.3;
}
.look:hover{color:var(--ink); text-decoration:underline; text-underline-offset:3px}
.looks .sub{margin-top:3px; line-height:1.45}
@media (max-width:760px){ .looks{grid-template-columns:1fr} }


/* The one line at the top of a call's own page. Same urgency vocabulary as
   the list, so moving between the two teaches nothing new. */
.deadlinebar{
  margin:0 0 16px; padding:9px 14px; border-radius:3px; font-size:14.5px;
  background:var(--paper); border:1px solid var(--line); border-left:3px solid var(--rule);
}
.deadlinebar.u-soon{border-left-color:var(--gold); background:var(--warn-bg)}
.deadlinebar.u-month{border-left-color:var(--turq)}
.deadlinebar.u-gone{color:var(--muted)}

/* Deadlines on the studio home. Stacked, not inline like the order/message
   alerts beside them: a deadline is a sentence, and three of them wrapped
   into one row is unreadable. */
.alerts.due{flex-direction:column; gap:6px; margin-bottom:14px}
.alerts.due a{
  padding:8px 13px; background:var(--warn-bg); border:1px solid var(--line);
  border-left:3px solid var(--gold); border-radius:3px; color:var(--ink);
  text-decoration:none;
}
.alerts.due a:hover{border-color:var(--gold)}

/* A grid of fields nested inside a fieldset. The outer .formgrid already
   supplies the card, so this one is only the column behaviour. */
.formgrid .bare{
  background:none; border:0; padding:0; border-radius:0;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
}
/* Fieldsets that group a form by subject rather than by accident. The border
   is the only thing that changes, which is enough to say "these belong
   together" without adding another box inside the box. */
.formgrid fieldset.accent-dates{border-color:var(--turq)}
.formgrid fieldset.accent-dates legend{color:var(--accent)}
.formgrid fieldset.accent-spec{border-color:var(--plum)}
.formgrid fieldset.accent-spec legend{color:var(--plum)}
.formgrid fieldset.accent-stat{border-color:var(--gold)}
select.os-watching{border-color:var(--turq)}
select.os-applied{border-color:var(--plum)}
select.os-accepted{border-color:var(--accent)}

/* A painting with no photograph cannot be submitted. Shown greyed rather than
   hidden, so the gap is visible from the packet screen instead of being
   discovered at the deadline. */
.pick.nopic{opacity:.5; cursor:not-allowed}
.pick.nopic span .sub{display:block; font-size:11.5px}

/* ---------- collections that hold collections ----------
   A subcategory is shown as a row indented under the collection it sits in,
   not as a second table: the list is short, and two tables would ask you to
   work out which one a name is in. The arrow is decorative -- the row also
   says "inside <name>" in words, which is what a screen reader reads. */
.nestmark{color:var(--muted); margin-right:7px}
/* A heading that carries the collection it sits under on the line above it.
   The auto margin has to move from the h1 to the stack, or the link that was
   sitting at the far right of the header row comes back in beside it. */
.adminhead > div{margin-right:auto}
.adminhead .crumb{margin-bottom:4px}
/* Two ways back on one line -- the list, and the collection this one sits
   inside. Separated by a rule rather than a bullet, so neither reads as the
   continuation of the other. */
.crumbs{display:flex; flex-wrap:wrap; align-items:baseline; gap:2px 12px; margin-bottom:4px}
.crumbs .crumb{margin-bottom:0}
.crumbs .crumb + .crumb{padding-left:12px; border-left:1px solid var(--line)}
/* On a phone the card layout puts a lone .sub back on the label's line. Here
   every .sub is a fact of its own -- what it sits inside, what its
   subcategories add -- so each keeps its own line. */
.grid-t.tree td > .sub{display:block; line-height:1.3; margin-top:1px}
/* A subcategory's row is set TIGHTER and indented, so the nesting is visible
   down the left edge of the table rather than depending on reading the arrow
   and the words. Same trick as .grid-t.tight: less leading, less padding. */
.grid-t.tree tr.is-sub td{padding-top:6px; padding-bottom:6px; line-height:1.3}
.grid-t.tree tr.is-sub td:first-child{padding-left:30px}
/* The "inside" menu shares the add-a-collection row. It must be allowed to
   SHRINK: left at its intrinsic width it eats the whole 34rem row and the name
   field collapses to a sliver. */
.newrow:has(select){max-width:46rem; flex-wrap:wrap}
.newrow select{flex:0 1 15rem; min-width:9rem; font-size:16px; box-sizing:border-box}
/* SCOPED to the row this was written for. Unscoped it also hit the add rows on
   Opportunities and Exhibitions, which carry a name field AND a date field and
   do not wrap -- two 11rem floors plus a button is 460px of minimum in a row
   that had 390 to live in, and the page scrolled sideways because of it. */
.newrow:has(select) input{min-width:11rem}
/* On a phone every add row wraps: the name takes a line of its own, and the
   date and the button share the one under it. Written for ALL of them rather
   than for the one that overflowed -- they are the same control in four
   places, and the next one added would have had the same fault.

   min-width:0 has to beat the :has() rule above, which is why the selector is
   repeated rather than just `.newrow > input`. */
@media (max-width:560px){
  .newrow,.newrow:has(select){flex-wrap:wrap; max-width:none}
  .newrow > input,.newrow > select,.newrow:has(select) > input{min-width:0}
  .newrow > input:not([type=date]),.newrow > select{flex:1 1 100%}
  .newrow > input[type=date]{flex:1 1 8rem}
  .newrow > .cta{flex:1 1 auto}
}
/* A FOLD. Things typed once and then never again -- an image spec, the dates
   that only matter if she gets in -- kept on the page but not in the way.
   Opened by default when there is something inside, because a fold that hides
   filled-in fields is a fold that loses them.

   The count on the summary is the point: it says whether opening is worth it,
   which is the one thing a collapsed section normally cannot tell you. */
.fold{
  border:0; border-top:1px solid var(--line); padding:12px 0 2px; margin:0;
}
.fold > summary{
  cursor:pointer; list-style:none; display:flex; align-items:center; gap:9px;
  font:600 11px/1 "Karla",sans-serif; letter-spacing:.09em; text-transform:uppercase;
  color:var(--muted); padding:4px 0; min-height:28px;
}
.fold > summary::-webkit-details-marker{display:none}
/* The disclosure arrow is drawn rather than inherited, so it can turn. */
.fold > summary::before{
  content:""; flex:none; width:7px; height:7px; margin-right:1px;
  border-right:1.5px solid currentColor; border-bottom:1.5px solid currentColor;
  transform:rotate(-45deg); transition:transform .18s ease;
}
.fold[open] > summary::before{transform:rotate(45deg)}
.fold > summary:hover{color:var(--accent)}
.fold > summary:focus-visible{outline:2px solid var(--focus); outline-offset:3px}
.foldn{
  font-size:10px; font-weight:700; letter-spacing:.06em; color:var(--accent);
  border:1px solid var(--rule); border-radius:99px; padding:1px 7px; text-transform:none;
}
.fold > .formgrid.bare,.fold > .pickers,.fold > .sub{margin-top:10px}
@media (prefers-reduced-motion:reduce){ .fold > summary::before{transition:none} }

.plainlist{list-style:none; margin:0 0 14px; padding:0; display:flex;
  flex-wrap:wrap; gap:8px 18px}
.plainlist a{color:var(--accent)}
/* On the site: a subcategory's pieces sit under their own heading inside the
   parent's page, with the heading a link to that subcategory's own page. */
.subhead{
  display:flex; align-items:baseline; gap:14px; flex-wrap:wrap;
  margin:44px 0 14px; padding-bottom:8px; border-bottom:1px solid var(--rule);
}
.subhead h2{margin:0; font-size:22px}
.subhead a{font:600 12px/1 "Karla",sans-serif; letter-spacing:.08em;
  text-transform:uppercase; color:var(--muted); text-decoration:none}
.subhead a:hover{color:var(--accent)}
.crumb{
  display:block; font:600 12px/1.4 "Karla",sans-serif; letter-spacing:.08em;
  text-transform:uppercase; color:var(--muted); text-decoration:none;
  margin-bottom:10px;
}
.crumb:hover{color:var(--accent)}

/* ------------------------------------------------------------- visitors
   A signpost to Umami rather than a dashboard: the free plan issues no API
   key, so the figures cannot be read in here. Only the two rules the page
   still uses are kept -- the tile and bar rules went with the dashboard. */
.vfoot{margin-top:24px; max-width:60ch}
.vneed{margin:0 0 14px; padding-left:18px}
.vneed li{margin:3px 0}
