@import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,300;0,6..72,400;0,6..72,500;0,6..72,600;1,6..72,300;1,6..72,400&family=Public+Sans:wght@300;400;500;600;700&display=swap');

/* ============================================================
   FLY CALEDONIA — design tokens
   ============================================================ */
:root{
  /* surfaces */
  --ink:            #0e1620;   /* deepest, hero + footer            */
  --ink-2:          #14202c;   /* section surface                   */
  --ink-3:          #1b2a3a;   /* raised card                       */
  --paper:          #f5f2ed;   /* warm light section                */
  --paper-2:        #ebe6dd;

  /* text */
  --on-dark:        #f2efe9;
  --on-dark-mute:   #a3b1bf;
  --on-light:       #14202c;
  --on-light-mute:  #5d6b78;

  /* brand */
  --accent:         #c1703a;   /* display use: large type, rules, numerals */
  --accent-hi:      #d8874e;   /* accent on dark surfaces                  */
  --accent-text:    #9d5623;   /* small text on light  (4.96:1 on paper)   */
  --accent-solid:   #a85c28;   /* button fill, white text (4.97:1)         */
  --accent-ink:     #ffffff;

  /* lines */
  --line-dark:      rgba(242,239,233,.14);
  --line-light:     rgba(20,32,44,.14);

  /* type */
  --display: "Newsreader", Georgia, "Times New Roman", serif;
  --sans:    "Public Sans", -apple-system, "Segoe UI", system-ui, sans-serif;

  /* spacing scale (8pt) */
  --s1:.5rem; --s2:1rem; --s3:1.5rem; --s4:2rem;
  --s5:3rem;  --s6:4rem; --s7:6rem;  --s8:8rem;

  --shell: 1280px;
  --gutter: clamp(1.25rem, 4vw, 3.5rem);
  --radius: 4px;
  --frame-radius: 14px;   /* the hero frame's corner. Anything that sits against
                             that white line copies it rather than guessing. */
  --frame-inset: clamp(.75rem, 2.2vw, 2rem);  /* and its distance from the edge */
  --frame-gap: 1rem;      /* even breathing room inside that line */

  --ease: cubic-bezier(.22,.61,.36,1);
}

*,*::before,*::after{ box-sizing:border-box; }
html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  /* clip, not hidden: hidden on html/body creates a scroll container,
     which breaks window.scrollY, position:fixed and IntersectionObserver */
  overflow-x:clip;
}
@media (prefers-reduced-motion: reduce){ html{ scroll-behavior:auto; } }

body{
  margin:0;
  background:var(--ink);
  color:var(--on-dark);
  font-family:var(--sans);
  font-size:17px;
  line-height:1.65;
  font-weight:300;
  -webkit-font-smoothing:antialiased;
}

img,video{ display:block; max-width:100%; height:auto; }
a{ color:inherit; text-decoration:none; }
button{ font:inherit; cursor:pointer; }

:focus-visible{
  outline:2px solid var(--accent-hi);
  outline-offset:3px;
  border-radius:2px;
}

.shell{
  width:100%; max-width:var(--shell);
  margin-inline:auto;
  padding-inline:var(--gutter);
}

.skip{
  position:absolute; left:-9999px; top:0; z-index:200;
  background:var(--accent-solid); color:#fff; padding:.75rem 1.25rem;
}
.skip:focus{ left:1rem; top:1rem; }

/* ---------- type ---------- */
h1,h2,h3,h4{
  font-family:var(--display);
  font-weight:400;
  line-height:1.08;
  letter-spacing:-.015em;
  margin:0;
  font-variation-settings:"opsz" 60;
}
h2{ font-size:clamp(2rem, 4.2vw, 3.1rem); }
h3{ font-size:clamp(1.3rem, 2vw, 1.6rem); line-height:1.2; }
p{ margin:0 0 1.15em; }
p:last-child{ margin-bottom:0; }

.eyebrow{
  font-family:var(--sans);
  font-size:.72rem;
  font-weight:600;
  letter-spacing:.22em;
  text-transform:uppercase;
  color:var(--accent-hi);
  margin:0 0 var(--s3);
}
.eyebrow.on-light{ color:var(--accent-text); }

.lede{
  font-size:clamp(1.02rem,1.35vw,1.18rem);
  color:var(--on-dark-mute);
  max-width:62ch;
}
.lede.on-light{ color:var(--on-light-mute); }

/* ---------- buttons ---------- */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:.6rem;
  min-height:52px; padding:.9rem 1.9rem;
  font-family:var(--sans); font-size:.83rem; font-weight:600;
  letter-spacing:.13em; text-transform:uppercase;
  border-radius:var(--radius); border:1px solid transparent;
  transition:background .22s var(--ease), border-color .22s var(--ease),
             color .22s var(--ease), transform .22s var(--ease);
  /* every .btn on the site used to be an <a>, so nothing ever reset the
     background a real <button> gets from the browser. The admin has real
     buttons, and a ghost one came out as a solid grey box without this. */
  appearance:none; background:none; cursor:pointer;
}
.btn-primary{ background:var(--accent-solid); color:var(--accent-ink); }
.btn-primary:hover{ background:var(--accent); transform:translateY(-2px); }
.btn-ghost{ border-color:rgba(242,239,233,.38); color:var(--on-dark); }
.btn-ghost:hover{ border-color:var(--on-dark); background:rgba(242,239,233,.08); }
.btn-ghost.on-light{ border-color:rgba(20,32,44,.3); color:var(--on-light); }
.btn-ghost.on-light:hover{ background:rgba(20,32,44,.06); border-color:var(--on-light); }

.textlink{
  display:inline-flex; align-items:center; gap:.5rem;
  font-size:.83rem; font-weight:600; letter-spacing:.13em; text-transform:uppercase;
  color:var(--accent-hi); padding-block:.4rem;
}
.textlink svg{ transition:transform .22s var(--ease); }
.textlink:hover svg{ transform:translateX(5px); }
.textlink.on-light{ color:var(--accent-text); }

/* ============================================================
   HEADER
   ============================================================ */
.masthead{
  position:absolute; inset:0 0 auto 0; z-index:60;
  padding-block:var(--s4);
}
.masthead.is-stuck{
  position:fixed; padding-block:var(--s2);
  background:rgba(14,22,32,.9);
  backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line-dark);
  animation:drop .35s var(--ease);
}
@keyframes drop{ from{ transform:translateY(-100%);} to{ transform:translateY(0);} }

.masthead .shell{ display:flex; align-items:center; justify-content:space-between; gap:var(--s4); }

.wordmark{
  font-family:var(--display); font-size:1.32rem; font-weight:500;
  letter-spacing:.16em; text-transform:uppercase; white-space:nowrap;
}
.wordmark span{ color:var(--accent-hi); }

.nav{ display:flex; align-items:center; gap:var(--s4); }
.nav-list{ display:flex; align-items:center; gap:clamp(.85rem,1.9vw,2rem); list-style:none; margin:0; padding:0; }
.nav-list > li{ position:relative; }
.nav-list a{
  display:block; padding:.6rem 0;
  font-size:.83rem; font-weight:500; letter-spacing:.05em;
  /* without this every two-word label breaks over two lines as soon as the row
     gets tight, which is what adding Pricing did */
  white-space:nowrap;
  border-bottom:2px solid transparent;
  transition:border-color .2s var(--ease), color .2s var(--ease);
}
.nav-list a:hover, .nav-list a[aria-current="page"]{ border-bottom-color:var(--accent); }
.has-sub > a::after{
  content:""; display:inline-block; width:5px; height:5px; margin-left:.5rem;
  border-right:1.5px solid currentColor; border-bottom:1.5px solid currentColor;
  transform:translateY(-2px) rotate(45deg); opacity:.6;
}

.submenu{
  position:absolute; top:100%; left:50%; translate:-50% 0;
  min-width:260px; margin:0; padding:.5rem; list-style:none;
  background:var(--ink-3); border:1px solid var(--line-dark);
  border-radius:var(--radius); box-shadow:0 24px 48px rgba(0,0,0,.45);
  opacity:0; visibility:hidden; transform:translateY(8px);
  transition:opacity .2s var(--ease), transform .2s var(--ease), visibility .2s;
}
.has-sub:hover .submenu, .has-sub:focus-within .submenu{
  opacity:1; visibility:visible; transform:translateY(0);
}
.submenu a{
  padding:.7rem .9rem; border:0; border-radius:3px;
  font-size:.88rem; font-weight:400; letter-spacing:0; line-height:1.35;
}
.submenu a:hover{ background:rgba(242,239,233,.07); color:var(--accent-hi); }
.submenu .sub-note{ display:block; font-size:.72rem; color:var(--on-dark-mute); margin-top:.15rem; }

.nav-cta{ min-height:36px; padding:.45rem 1.3rem; font-size:.72rem; white-space:nowrap; }

.burger{
  display:none; width:44px; height:44px; align-items:center; justify-content:center;
  background:none; border:1px solid var(--line-dark); border-radius:var(--radius); color:inherit;
}
.burger span{ display:block; width:19px; height:1.5px; background:currentColor; position:relative; }
.burger span::before,.burger span::after{
  content:""; position:absolute; left:0; width:19px; height:1.5px; background:currentColor;
}
.burger span::before{ top:-6px; } .burger span::after{ top:6px; }

/* ============================================================
   HERO
   ============================================================ */
.hero{ position:relative; min-height:100svh; display:grid; }
.hero-media{ position:absolute; inset:0; overflow:hidden; background:var(--ink); }
.hero-media video, .hero-media img{
  width:100%; height:100%; object-fit:cover;
}
.hero-media::after{
  content:""; position:absolute; inset:0;
  background:
    linear-gradient(to right, rgba(10,16,24,.82) 0%, rgba(10,16,24,.52) 46%, rgba(10,16,24,.28) 100%),
    linear-gradient(to top, rgba(10,16,24,.78) 0%, rgba(10,16,24,0) 42%);
}

/* the Airpixel inset frame — margin lives here, padding lives on the inner
   shell, so the two never add up to more than the viewport */
.hero-frame{
  position:relative; z-index:2;
  margin:var(--frame-inset);
  border:1px solid rgba(242,239,233,.4);
  border-radius:var(--frame-radius);
  display:grid;
  min-width:0;
}
.hero-inner{
  display:grid; grid-template-rows:1fr auto;
  padding-block:clamp(3.5rem,8vh,5.5rem) clamp(1.5rem,3vh,2.25rem);
  min-width:0;
}

/* the marquee made the foot much taller, so the copy is pulled off dead
   centre and up towards the top third, where it was before */
.hero-body{ align-self:center; padding-block:var(--s4) var(--s6); min-width:0; }
.hero h1{
  font-size:clamp(2.9rem, 7.2vw, 6rem);
  max-width:16ch;
  margin-bottom:var(--s3);
  text-wrap:balance;
}
.hero h1 em{ font-style:italic; color:var(--accent-hi); }
.hero .lede{ max-width:52ch; margin-bottom:var(--s4); font-size:clamp(1.02rem,1.4vw,1.2rem); }
.hero-actions{ display:flex; flex-wrap:wrap; gap:var(--s2); }

/* "As featured in" marquee across the foot of the hero. This replaced the
   CAA / insurance / award text line: a masthead someone recognises is believed
   faster than a claim, and the licence is still stated in the footer and on
   every service page. */
.hero-foot{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:var(--s4); min-width:0;
}
.hero-logos{ flex:1; min-width:0; }
.hero-logos-label{
  margin:0 0 .9rem; font-size:.72rem; font-weight:600;
  letter-spacing:.16em; text-transform:uppercase; color:var(--on-dark-mute);
}
.logo-marquee{
  overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 4%, #000 88%, transparent);
  mask-image:linear-gradient(90deg, transparent, #000 4%, #000 88%, transparent);
}
.logo-track{
  display:flex; width:max-content; gap:clamp(2.5rem,5vw,4.5rem);
  animation:logo-scroll 42s linear infinite;
}
.logo-set{
  display:flex; align-items:center; gap:clamp(2.5rem,5vw,4.5rem);
  flex:none;
}
.logo-set img{
  height:clamp(26px,3.4vw,40px); width:auto; flex:none;
  /* white artwork over a bright loch needs more than the usual muted-logo
     treatment, or the finer wordmarks disappear */
  opacity:.85; transition:opacity .25s var(--ease);
  filter:drop-shadow(0 1px 6px rgba(10,16,24,.75));
}
.logo-set img:hover{ opacity:1; }
.logo-marquee:hover .logo-track{ animation-play-state:paused; }
/* the gap is duplicated between the two sets, so half the track plus one gap */
@keyframes logo-scroll{ to{ transform:translateX(calc(-50% - clamp(1.25rem,2.5vw,2.25rem))); } }
@media (prefers-reduced-motion: reduce){
  .logo-track{ animation:none; width:auto; flex-wrap:wrap; }
  .logo-set[aria-hidden]{ display:none; }
  .logo-marquee{ -webkit-mask-image:none; mask-image:none; }
}
.socials{ display:flex; gap:.15rem; list-style:none; margin:0; padding:0; }
.socials a{
  width:38px; height:38px; display:grid; place-items:center;
  border-radius:50%;
  /* same ink as the nav links beside them: at 18px an icon has far less mass
     than a word, so the muted grey that works for text read as disabled here */
  color:var(--on-dark);
  transition:color .2s var(--ease), background .2s var(--ease);
}
.socials a:hover{ color:var(--accent-hi); background:rgba(242,239,233,.1); }
.socials svg{ width:18px; height:18px; }
/* in the header the icons sit between the links and the CTA, which is why
   the nav gap tightens: the row has to hold two more items without the
   links running under the wordmark at 1100px */
.nav-socials{ margin-left:clamp(.35rem,.9vw,1rem); }

.scroll-cue{
  display:grid; place-items:center; width:46px; height:46px;
  border:1px solid rgba(242,239,233,.32); border-radius:50%;
  color:var(--on-dark); animation:bob 2.6s var(--ease) infinite;
}
.scroll-cue:hover{ background:rgba(242,239,233,.1); }
@keyframes bob{ 0%,100%{ transform:translateY(0);} 50%{ transform:translateY(7px);} }
@media (prefers-reduced-motion: reduce){ .scroll-cue{ animation:none; } }

/* ============================================================
   SECTIONS
   ============================================================ */
.section{ padding-block:clamp(4rem,9vw,7.5rem); }
.section-light{ background:var(--paper); color:var(--on-light); }
.section-mid{ background:var(--ink-2); }

.section-head{ max-width:64ch; margin-bottom:var(--s6); }
/* wider measure for section heads that sit above a full-width table or grid,
   so the heading and the standfirst line up with the block underneath rather
   than stopping two thirds of the way across. The lede still keeps a readable
   measure - full 1280px body text would be miserable to read. */
.section-head-wide{ max-width:none; }
.section-head-wide .lede{ max-width:78ch; }
.section-head h2{ margin-bottom:var(--s3); }

/* proof bar */
.proof{ background:var(--ink-2); border-block:1px solid var(--line-dark); }
.proof-grid{
  display:grid; grid-template-columns:repeat(4,1fr);
  padding-block:var(--s5);
}
.proof-item{ padding-inline:var(--s3); border-left:1px solid var(--line-dark); }
.proof-item:first-child{ border-left:0; padding-left:0; }
.proof-num{
  font-family:var(--display); font-size:clamp(1.7rem,3vw,2.4rem);
  color:var(--accent-hi); line-height:1; margin-bottom:.5rem; display:block;
}
.proof-label{ font-size:.83rem; color:var(--on-dark-mute); line-height:1.4; }

/* service cards */
.cards{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s3); }
.card{
  position:relative; display:flex; flex-direction:column;
  border:1px solid var(--line-light); border-radius:var(--radius);
  overflow:hidden; background:#fff;
  transition:transform .3s var(--ease), box-shadow .3s var(--ease);
}
.card:hover{ transform:translateY(-5px); box-shadow:0 18px 40px rgba(20,32,44,.13); }
/* same reason as .rail-media: cover the card's 1px border rather than leave a
   hairline of it above and beside the picture. .card already clips. */
.card-img{
  aspect-ratio:4/3; overflow:hidden; background:var(--paper-2);
  margin:-1px -1px 0;
}
.card-img img{ width:100%; height:100%; object-fit:cover; transition:transform .6s var(--ease); }
.card:hover .card-img img{ transform:scale(1.045); }
.card-body{ padding:var(--s3); display:flex; flex-direction:column; gap:.75rem; flex:1; }
.card-body h3{ color:var(--on-light); }
.card-body p{ color:var(--on-light-mute); font-size:.95rem; margin:0; flex:1; }

/* work grid */
.work{ display:grid; grid-template-columns:repeat(12,1fr); gap:var(--s2); }
.tile{
  position:relative; overflow:hidden; border-radius:var(--radius);
  background:var(--ink-3); display:block;
}
.tile img{ width:100%; height:100%; object-fit:cover; transition:transform .7s var(--ease); }
.tile:hover img{ transform:scale(1.05); }
.tile figcaption{
  position:absolute; inset:auto 0 0 0; padding:var(--s3);
  background:linear-gradient(to top, rgba(10,16,24,.88), transparent);
  font-size:.9rem; letter-spacing:.03em;
  /* the caption sits on the photograph, so it is always light text, even
     when the tiles are inside a light section */
  color:var(--on-dark);
}
.tile figcaption b{ display:block; font-family:var(--display); font-size:1.1rem; font-weight:400; }
.tile figcaption span{ color:var(--on-dark-mute); font-size:.78rem; letter-spacing:.1em; text-transform:uppercase; }
.t-lg{ grid-column:span 6; aspect-ratio:3/2; }
.t-md{ grid-column:span 6; aspect-ratio:3/2; }
.t-sm{ grid-column:span 4; aspect-ratio:4/3; }
.t-wide{ grid-column:span 12; aspect-ratio:21/7; }

/* press */
.press-list{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s2) var(--s4); list-style:none; margin:0; padding:0; }
.press-set{ grid-template-columns:1fr; gap:0; }
.press-list li{
  padding:var(--s3) 0; border-top:1px solid var(--line-dark);
  display:flex; flex-direction:column; gap:.3rem;
}
.press-name{ font-family:var(--display); font-size:1.12rem; }
.press-note{ font-size:.85rem; color:var(--on-dark-mute); }

.pull{
  border-left:3px solid var(--accent);
  padding:var(--s2) 0 var(--s2) var(--s3);
  font-family:var(--display); font-size:clamp(1.2rem,2.1vw,1.6rem);
  line-height:1.4; font-style:italic; max-width:52ch;
}
.pull cite{ display:block; margin-top:var(--s2); font-family:var(--sans); font-style:normal;
  font-size:.78rem; letter-spacing:.14em; text-transform:uppercase; color:var(--on-dark-mute); }

/* split feature */
.split{ display:grid; grid-template-columns:1.05fr .95fr; gap:clamp(2rem,5vw,4.5rem); align-items:center; }
.split-start{ align-items:start; }
.split-media img{ width:100%; border-radius:var(--radius); }

/* CTA band */
.cta-band{ position:relative; overflow:hidden; background:var(--ink-2); }
.cta-band .shell{ position:relative; z-index:2; text-align:center; }
/* the Stirling panorama sits behind the commissions band. It is heavily
   scrimmed on purpose: this is a background, the buttons on top of it have to
   stay legible, and a 5:1 panorama in a 3:1 band is cropped either way. */
.cta-media{ position:absolute; inset:0; z-index:0; }
.cta-media img{ width:100%; height:100%; object-fit:cover; object-position:50% 58%; }
.cta-media::after{
  content:""; position:absolute; inset:0;
  background:
    radial-gradient(110% 75% at 50% 52%, rgba(14,22,32,.45) 0%, rgba(14,22,32,.82) 100%),
    linear-gradient(to bottom, rgba(14,22,32,.22), rgba(14,22,32,.45));
}
.cta-band h2{ margin-bottom:var(--s3); }
.cta-band .lede{ margin-inline:auto; margin-bottom:var(--s4); }
.cta-actions{ display:flex; gap:var(--s2); justify-content:center; flex-wrap:wrap; }

/* ============================================================
   FOOTER
   ============================================================ */
.footer{
  background:var(--ink); border-top:1px solid var(--line-dark);
  /* the extra bottom padding keeps the legal line, and the Savvy Website
     credit on the right of it, out from under the fixed WhatsApp button */
  padding-block:var(--s6) calc(var(--s4) + 56px + clamp(1rem,2.5vw,2rem));
}
.footer-grid{ display:grid; grid-template-columns:1.4fr repeat(4,1fr); gap:var(--s4); margin-bottom:var(--s5); }
.footer h4{
  font-family:var(--sans); font-size:.74rem; font-weight:600;
  letter-spacing:.16em; text-transform:uppercase; color:var(--on-dark);
  margin:0 0 var(--s2);
}
.footer ul{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.55rem; }
.footer li a{ font-size:.9rem; color:var(--on-dark-mute); transition:color .18s var(--ease); }
.footer li a:hover{ color:var(--accent-hi); }
.footer-brand p{ font-size:.9rem; color:var(--on-dark-mute); max-width:34ch; margin-top:var(--s2); }
.footer-creds{
  font-size:.78rem; letter-spacing:.06em; color:var(--on-dark-mute);
  padding-top:var(--s2); border-top:1px solid var(--line-dark); max-width:34ch;
}
/* icons rather than two more text links: they were competing with the sitemap
   columns for attention and they are navigation off the site, not into it */
/* .footer ul is a column and outranks .socials, so the row has to be restated
   at the same specificity or the two icons stack */
.footer .footer-socials{
  flex-direction:row; gap:.15rem;
  margin-top:var(--s2); margin-left:-9px;
}
.footer-socials a{ width:36px; height:36px; }
.footer-socials svg{ width:17px; height:17px; }
.footer-legal{
  border-top:1px solid var(--line-dark); padding-top:var(--s3);
  display:flex; flex-wrap:wrap; gap:var(--s2) var(--s4); justify-content:space-between;
  font-size:.8rem; color:var(--on-dark-mute);
}

/* ============================================================
   REVEAL
   ============================================================ */
.reveal{ opacity:0; transform:translateY(22px); transition:opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.in{ opacity:1; transform:none; }
@media (prefers-reduced-motion: reduce){
  .reveal{ opacity:1; transform:none; transition:none; }
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width:1024px){
  .proof-grid{ grid-template-columns:repeat(2,1fr); gap:var(--s4) 0; }
  .proof-item:nth-child(3){ border-left:0; padding-left:0; }
  .cards{ grid-template-columns:repeat(2,1fr); }
  .press-list{ grid-template-columns:repeat(2,1fr); }
  .footer-grid{ grid-template-columns:1fr 1fr 1fr; }
  .split{ grid-template-columns:1fr; }
}

/* The full nav needs about 1120px: "Drone Photography Services" is the long
   one, and shortening "About Francis" back to "About" bought a hundred pixels.
   Below that it becomes the burger, rather than overflowing the page or
   wrapping each label onto two lines. Measured, not guessed - if a label
   changes, re-measure. */
@media (max-width:1120px){
  .nav-list, .nav-cta, .nav-socials{ display:none; }
  .burger{ display:flex; }
  .nav-cta::before{ display:none; }

  /* The whole nav becomes the panel, not just the list. The socials and the
     CTA are siblings of the list, so when only the list was positioned they
     stayed in the header row, widened it, and pushed the burger off the right
     edge of a phone — leaving no way to close the menu. */
  .nav.is-open{
    position:absolute; top:calc(100% + .75rem); left:var(--gutter); right:var(--gutter);
    flex-direction:column; align-items:stretch; gap:0;
    background:var(--ink-3); border:1px solid var(--line-dark); border-radius:var(--radius);
    padding:var(--s2); box-shadow:0 24px 48px rgba(0,0,0,.5);
    max-height:calc(100vh - 8rem); overflow-y:auto;
  }
  .nav.is-open .nav-list{
    display:flex; flex-direction:column; align-items:stretch; gap:0;
  }
  .nav.is-open .nav-list a{ padding:.85rem .75rem; border:0; }
  .nav.is-open .nav-socials{
    display:flex; flex-direction:row; justify-content:center;
    margin:var(--s2) 0 0;
  }
  .nav.is-open .nav-cta{ display:inline-flex; margin:var(--s2) 0 0; width:100%; }
  .nav.is-open .submenu{
    position:static; opacity:1; visibility:visible;
    /* transform:none does NOT cancel the standalone translate property that
       centres this under its parent on desktop. Without translate:none every
       submenu sits half its own width off the left of the screen. */
    transform:none; translate:none;
    box-shadow:none; border:0; background:transparent;
    padding:0 0 .5rem .75rem; min-width:0;
  }
  .nav.is-open .submenu .sub-note{ display:none; }
  .has-sub > a::after{ display:none; }
}

@media (max-width:820px){
  .t-lg,.t-md,.t-sm{ grid-column:span 12; aspect-ratio:3/2; }
  .t-wide{ aspect-ratio:16/9; }
  .cards{ grid-template-columns:1fr; }
  .hero-foot{ align-items:center; }
}

@media (max-width:560px){
  body{ font-size:16px; }
  .proof-grid{ grid-template-columns:1fr; }
  .proof-item{ border-left:0; padding-left:0; }
  .press-list{ grid-template-columns:1fr; }
  .footer-grid{ grid-template-columns:1fr; }
  .hero-actions .btn{ width:100%; }
  .socials a{ width:40px; height:40px; }
}

/* ============================================================
   NAV CTA — room to breathe away from the links
   ============================================================ */
.nav{ gap:0; }
.nav-cta{
  margin-left:clamp(1rem,2.2vw,2.75rem);
  /* deliberately shorter than the nav links (43px) so the pill sits centred
     BELOW them. The hero frame's top hairline lands at y=32 on wide screens,
     which is exactly where the header row starts, and a full-height pill met
     that line edge to edge. Losing ~12px lets the line pass cleanly above it. */
  padding:.45rem 1.3rem;
  min-height:36px;
  font-size:.72rem;
  white-space:nowrap;      /* "GET A QUOTE" wrapped to two lines 821-1030px */
  position:relative;
}
.nav-cta::before{                       /* hairline separating it from the links */
  content:""; position:absolute; left:calc(-1 * clamp(.75rem,1.5vw,1.4rem));
  top:50%; translate:0 -50%; width:1px; height:22px;
  background:var(--line-dark);
}

/* ============================================================
   PAGE HERO (interior pages)
   ============================================================ */
.pagehero{ position:relative; padding:calc(var(--s8) + var(--s5)) 0 var(--s7); overflow:hidden; }
.pagehero-media{ position:absolute; inset:0; }
.pagehero-media img{ width:100%; height:100%; object-fit:cover; }
.pagehero-media::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(to right, rgba(10,16,24,.92) 0%, rgba(10,16,24,.72) 55%, rgba(10,16,24,.5) 100%),
             linear-gradient(to top, rgba(10,16,24,.9) 0%, rgba(10,16,24,.1) 55%);
}
.pagehero-body{ position:relative; z-index:2; }
.pagehero h1{ font-size:clamp(2.2rem,5vw,4rem); max-width:18ch; margin-bottom:var(--s3); text-wrap:balance; }

/* ============================================================
   SERVICES RAIL
   ============================================================ */
.rail{ display:grid; grid-template-columns:repeat(5,1fr); gap:.85rem; }
.rail-card{
  display:flex; flex-direction:column; gap:.65rem;
  padding:var(--s3) 1.15rem var(--s4);
  background:#fff; border:1px solid var(--line-light); border-radius:var(--radius);
  overflow:hidden;   /* the media bleeds over the border, so clip the corners */
  transition:transform .3s var(--ease), box-shadow .3s var(--ease);
}
.rail-card:hover{ transform:translateY(-5px); box-shadow:0 16px 36px rgba(20,32,44,.12); }

/* sample image at the head of each service card. Negative margins pull it out
   through the card padding so it sits flush to the card edge; the flex gap
   below it does the spacing, hence no margin-bottom. */
.rail-media{
  display:block;
  /* pull out through the padding AND the card's own 1px border. Without the
     extra pixel that border paints a hairline of card colour above and beside
     every image, which reads as the pictures being misaligned with each other. */
  margin:calc(-1 * var(--s3) - 1px) calc(-1.15rem - 1px) 0;
  aspect-ratio:4/3; overflow:hidden; background:var(--paper-2);
  border-top-left-radius:inherit; border-top-right-radius:inherit;
}
.rail-media picture{ display:block; width:100%; height:100%; }
.rail-media img{
  width:100%; height:100%; object-fit:cover;
  transition:transform .6s var(--ease);
}
.rail-card:hover .rail-media img{ transform:scale(1.05); }
.rail-num{
  font-family:var(--display); font-size:1.5rem; color:var(--accent);
  line-height:1; opacity:.55;
}
.rail-card h3{ color:var(--on-light); font-size:1.06rem; line-height:1.25; letter-spacing:-.01em; }
.rail-card p{ color:var(--on-light-mute); font-size:.9rem; margin:0; flex:1; line-height:1.55; }
.rail-go{
  display:inline-flex; align-items:center; gap:.45rem; margin-top:.4rem;
  font-size:.74rem; font-weight:600; letter-spacing:.13em; text-transform:uppercase;
  color:var(--accent-text);
}
.rail-go svg{ transition:transform .22s var(--ease); }
.rail-card:hover .rail-go svg{ transform:translateX(4px); }

/* rail on dark sections */
.section-mid .rail-card,
.section:not(.section-light) .rail-card{ background:var(--ink-3); border-color:var(--line-dark); }
.section-mid .rail-card h3,
.section:not(.section-light) .rail-card h3{ color:var(--on-dark); }
.section-mid .rail-card p,
.section:not(.section-light) .rail-card p{ color:var(--on-dark-mute); }
.section-mid .rail-go,
.section:not(.section-light) .rail-go{ color:var(--accent-hi); }

/* ============================================================
   PRICES
   ============================================================ */
.price-list{ border-top:1px solid var(--line-dark); }
.section-light .price-list{ border-top-color:var(--line-light); }
.price-row{
  display:grid; grid-template-columns:1.1fr auto 1.6fr; gap:var(--s3) var(--s4);
  align-items:baseline; padding:var(--s3) 0;
  border-bottom:1px solid var(--line-dark);
}
.section-light .price-row{ border-bottom-color:var(--line-light); }
.price-what h3{ margin-bottom:.2rem; }
.price-what span{ font-size:.82rem; color:var(--on-dark-mute); letter-spacing:.06em; text-transform:uppercase; }
.section-light .price-what span{ color:var(--on-light-mute); }
.price-fig{
  font-family:var(--display); font-size:clamp(1.35rem,2.4vw,1.9rem);
  color:var(--accent-hi); white-space:nowrap;
}
.section-light .price-fig{ color:var(--accent); }
.price-note{ margin:0; font-size:.93rem; color:var(--on-dark-mute); }
.section-light .price-note{ color:var(--on-light-mute); }

/* ============================================================
   TICK LIST + STEPS
   ============================================================ */
.ticks{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:.75rem; }
.ticks li{ position:relative; padding-left:1.9rem; font-size:.98rem; color:var(--on-dark-mute); }
.section-light .ticks li{ color:var(--on-light-mute); }
.ticks li::before{
  content:""; position:absolute; left:0; top:.42em;
  width:11px; height:6px;
  border-left:2px solid var(--accent-hi); border-bottom:2px solid var(--accent-hi);
  transform:rotate(-45deg);
}
.section-light .ticks li::before{ border-color:var(--accent-text); }

.steps{ list-style:none; margin:0; padding:0; display:grid; grid-template-columns:repeat(2,1fr); gap:var(--s4); }
.steps li{ display:flex; gap:var(--s3); align-items:flex-start; }
.step-n{
  font-family:var(--display); font-size:1.6rem; color:var(--accent-hi);
  line-height:1; opacity:.6; flex:none; padding-top:.15rem;
}
.steps h3{ margin-bottom:.45rem; }
.steps p{ margin:0; color:var(--on-dark-mute); font-size:.95rem; }

/* ============================================================
   PRINT WALL  (prints-framed.html)
   Frank's own photographs of the mounted prints. The white mount is part
   of the file, so no cropping and no object-fit: the shadow does the
   lifting and the print sits on the paper as it would on a wall.
   align-items:end hangs each row off a shared bottom line, which is how
   prints are actually hung, and keeps the captions on one line per row.
   ============================================================ */
.prints-wall{
  display:grid; grid-template-columns:repeat(3,1fr);
  gap:var(--s6) var(--s4); align-items:end;
}
.prints-wall figure{ margin:0; text-align:center; }
.prints-wall img{
  display:block; width:auto; max-width:100%; max-height:25rem;
  margin-inline:auto;
  box-shadow:0 22px 44px -20px rgba(20,32,44,.5), 0 2px 6px rgba(20,32,44,.10);
}
.prints-wall figcaption{
  margin-top:.9rem; font-size:.78rem; font-weight:600;
  letter-spacing:.14em; text-transform:uppercase; color:var(--on-light-mute);
}
/* seven prints: the last one hangs on its own, centred rather than orphaned left */
.prints-wall figure:last-child:nth-child(3n+1){ grid-column:2; }
.prints-note{
  margin-top:var(--s6); padding-top:var(--s3);
  border-top:1px solid var(--line-light);
  color:var(--on-light-mute); font-size:.95rem; max-width:60ch;
}

/* a clip sitting where a photograph normally would, in a .split.
   The poster does the work until main.js decides the download is worth it,
   so this has to look finished with no video loaded at all. */
.split-media video{
  display:block; width:100%; height:auto; border-radius:var(--radius);
  background:var(--ink-3);
}
.video-note{
  margin-top:.9rem; font-size:.78rem; font-weight:600;
  letter-spacing:.14em; text-transform:uppercase; color:var(--on-dark-mute);
}
.on-light + .video-note, .section-light .video-note{ color:var(--on-light-mute); }

/* ============================================================
   GALLERY GRID
   Justified rows, so every photograph is shown whole at its own shape.
   pages.py cuts each gallery into rows whose shapes add up to about the
   same width; each figure takes a share of its row in proportion to its
   aspect ratio (--r), so every photograph in a row is the same height and
   the row fills the width exactly. No cropping, no object-fit, and no
   photograph left small beside an empty space.
   ============================================================ */
.ggrid{ display:flex; flex-direction:column; gap:var(--s5); }
.grow{ display:flex; gap:var(--s3); align-items:flex-start; }
.gph{ flex-grow:var(--r, 1.5); flex-shrink:1; flex-basis:0; min-width:0; margin:0; }
.gph-open{ display:block; width:100%; padding:0; border:0; background:var(--ink-3); cursor:zoom-in; }
.gph-open picture, .gph-open img{ display:block; width:100%; height:auto; }
.gph-open img{ transition:filter .4s var(--ease); }
.gph-open:hover img{ filter:brightness(1.08); }
.gph-open:focus-visible{ outline:2px solid currentColor; outline-offset:4px; }
.gph figcaption{ padding-top:.85rem; }
.gph-title{ margin:0 0 .35rem; font-family:inherit; font-size:1rem; font-weight:600; letter-spacing:0; line-height:1.35; color:var(--on-dark); }
.gph-text{ margin:0; font-size:.88rem; line-height:1.6; color:var(--on-dark-mute); }

/* ---- lightbox: any gallery photograph, full size ------------------------
   The X, Escape, or a click anywhere that is not the photograph closes it. */
.lightbox{
  position:fixed; inset:0; width:100%; height:100%; max-width:none; max-height:none;
  margin:0; padding:0; border:0; background:rgba(8,12,18,.985); color:var(--on-dark);
  overscroll-behavior:contain;
}
.lightbox[open]{ display:flex; align-items:center; justify-content:center; }
.lightbox::backdrop{ background:rgba(8,12,18,.7); }
body:has(.lightbox[open]){ overflow:hidden; }
.lb-fig{ margin:0; padding:4.5rem 5.5rem 1.25rem; display:flex; flex-direction:column; align-items:center; gap:1rem; max-width:100%; max-height:100%; }
.lb-img{
  display:block; width:auto; height:auto; max-width:100%;
  max-height:calc(100vh - 10.5rem); max-height:calc(100dvh - 10.5rem);
  box-shadow:0 30px 80px -30px rgba(0,0,0,.85); transition:opacity .25s;
}
.lb-img.is-loading{ opacity:.35; }
.lb-cap{ display:flex; flex-wrap:wrap; align-items:center; justify-content:center; gap:.6rem 1.25rem; text-align:center; }
.lb-title{ font-weight:600; font-size:1.02rem; }
.lb-count{ font-size:.85rem; color:var(--on-dark-mute); font-variant-numeric:tabular-nums; }
.lb-close, .lb-nav{
  position:absolute; z-index:2; display:grid; place-items:center; width:48px; height:48px;
  border-radius:50%; border:1px solid rgba(242,239,233,.3); background:rgba(242,239,233,.1);
  color:var(--on-dark); cursor:pointer; transition:background .2s;
}
.lb-close:hover, .lb-nav:hover{ background:rgba(242,239,233,.24); }
.lb-close{ top:1rem; right:1rem; }
.lb-nav{ top:50%; margin-top:-24px; }
.lb-prev{ left:1rem; }
.lb-next{ right:1rem; }
.lb-nav[hidden]{ display:none; }

/* ---- the piece about each place, under the gallery ---------------------- */
.place-grid{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.75fr); gap:var(--s4) var(--s6); align-items:start; }
.place-head{ position:sticky; top:6.5rem; }
.place-head h2{ margin-bottom:var(--s3); }
.place-body h3{ font-size:1.3rem; margin:var(--s5) 0 .6rem; color:var(--on-light); }
.place-body > h3:first-child{ margin-top:0; }
.place-body p{ color:var(--on-light-mute); line-height:1.75; margin:0 0 1rem; max-width:68ch; }
.place-body a{ color:var(--on-light); text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px; }
.faq{ margin-top:var(--s6); }
.faq-item{ padding:var(--s3) 0; border-top:1px solid var(--line-light); }
.faq-item:last-child{ border-bottom:1px solid var(--line-light); }
.faq-item h4{ margin:0 0 .45rem; font-size:1.02rem; color:var(--on-light); }
.faq-item p{ margin:0; }
.place-more{ margin-top:var(--s5) !important; font-size:.92rem; }

/* dark card variant, used on the gallery and prints index pages */
.card-dark{ background:var(--ink-3); border-color:var(--line-dark); }
.card-dark .card-body h3{ color:var(--on-dark); }
.card-dark .card-body p{ color:var(--on-dark-mute); }

/* the featured-in strip repeated inside a section rather than the hero needs
   its own air and a rule to sit under, or it reads as part of the buttons */
.section .hero-logos{
  margin-top:var(--s7); padding-top:var(--s4);
  border-top:1px solid var(--line-dark);
}

/* ============================================================
   PRESS TICKER  (home recognition block)
   The five credits used to be a tall static column that pushed the section
   past the fold. Now it is a short window with the set drifting slowly
   upward, same trick as the logo marquee: two identical stacks, translate by
   exactly half the track, so the loop has no seam. Slow on purpose - this is
   something to notice, not something to read against a clock.
   ============================================================ */
.press-ticker{
  height:19rem; overflow:hidden; align-self:center;
  -webkit-mask-image:linear-gradient(to bottom, transparent, #000 14%, #000 86%, transparent);
  mask-image:linear-gradient(to bottom, transparent, #000 14%, #000 86%, transparent);
}
.press-track{
  display:flex; flex-direction:column;
  animation:press-scroll 34s linear infinite;
}
.press-set{ display:flex; flex-direction:column; flex:none; }
.press-ticker:hover .press-track{ animation-play-state:paused; }
@keyframes press-scroll{ to{ transform:translateY(-50%); } }
@media (prefers-reduced-motion: reduce){
  .press-ticker{ height:auto; -webkit-mask-image:none; mask-image:none; }
  .press-track{ animation:none; }
  .press-set[aria-hidden]{ display:none; }
}
@media (max-width:1024px){
  .press-ticker{ height:15rem; }
}
.footer-legal a{ color:var(--on-dark-mute); text-decoration:underline; text-underline-offset:2px; }
.footer-legal a:hover{ color:var(--accent-hi); }

/* ============================================================
   WHATSAPP
   Green on purpose. The site palette is ink and terracotta, but this button
   is only useful if it is recognised in the first half second, and the one
   thing everybody recognises is WhatsApp's own green.
   ============================================================ */
.wa-float{
  position:fixed;
  /* inside the hero frame's line with the same gap on both sides, rather than
     sitting exactly on the corner of it */
  right:calc(var(--frame-inset) + var(--frame-gap));
  bottom:calc(var(--frame-inset) + var(--frame-gap));
  z-index:70; display:inline-flex; align-items:center; gap:.6rem;
  height:56px; padding:0 1.1rem 0 .95rem;
  background:#25d366; color:#0b3d24;
  /* same corner as the hero frame it sits against, not a pill */
  border-radius:var(--frame-radius);
  box-shadow:0 10px 28px rgba(10,16,24,.45);
  font-size:.84rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  transition:transform .22s var(--ease), box-shadow .22s var(--ease);
}
.wa-float:hover{ transform:translateY(-3px); box-shadow:0 16px 34px rgba(10,16,24,.5); }
.wa-float svg{ width:26px; height:26px; flex:none; }
@media (max-width:560px){
  /* on a phone the label is dead weight: the glyph is unmistakable */
  .wa-float{ height:54px; width:54px; padding:0; justify-content:center; border-radius:var(--frame-radius); }
  .wa-float-label{ display:none; }
}
@media (prefers-reduced-motion: reduce){ .wa-float{ transition:none; } }

/* desktop fallback: wa.me redirects to a QR wall on a computer, so offer the
   number itself instead of sending people somewhere they cannot use */
.wa-panel{
  position:fixed; inset:0; z-index:90; display:grid; place-items:center;
  background:rgba(10,16,24,.7); padding:var(--gutter);
}
.wa-panel[hidden]{ display:none; }
.wa-card{
  background:var(--ink-2); border:1px solid var(--line-dark); border-radius:10px;
  padding:var(--s5); max-width:26rem; width:100%; text-align:center;
  box-shadow:0 30px 70px rgba(0,0,0,.5);
}
.wa-card h3{ margin-bottom:.6rem; }
.wa-card p{ color:var(--on-dark-mute); font-size:.94rem; }
.wa-number{
  display:block; margin:var(--s3) 0; font-family:var(--display);
  font-size:clamp(1.7rem,3.2vw,2.2rem); color:var(--on-dark); letter-spacing:.02em;
}
.wa-actions{ display:flex; flex-direction:column; gap:.6rem; margin-top:var(--s3); }
.wa-actions .btn{ width:100%; }
.wa-close{
  margin-top:var(--s3); background:none; border:0; color:var(--on-dark-mute);
  font:inherit; font-size:.85rem; text-decoration:underline; cursor:pointer;
}
.wa-close:hover{ color:var(--on-dark); }

/* ============================================================
   PRODUCTS  (the catalogue, and the pages for each product)
   Product shots are photographed on white, so every product frame is white and
   uses contain, never cover: a framed print must not be cropped to fit a card.
   ============================================================ */
.cat-bar{ background:var(--ink-2); border-bottom:1px solid var(--line-dark); padding-block:var(--s3); }
.cat-bar .shell{ display:flex; flex-wrap:wrap; gap:.6rem; }
.cat-bar a{
  display:inline-flex; align-items:center; min-height:44px; padding:.5rem 1.1rem;
  border:1px solid rgba(242,239,233,.32); border-radius:999px;
  font-size:.78rem; font-weight:600; letter-spacing:.1em; text-transform:uppercase;
  color:var(--on-dark); transition:background .2s var(--ease), border-color .2s var(--ease);
}
.cat-bar a:hover{ background:rgba(242,239,233,.1); border-color:var(--on-dark); }
.cat-bar a span{ margin-left:.5rem; color:var(--on-dark-mute); font-weight:500; }

.product-grid{ display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:var(--s3); }
.product-card{
  display:flex; flex-direction:column; overflow:hidden;
  background:#fff; border:1px solid var(--line-light); border-radius:var(--radius);
  color:var(--on-light);
  transition:transform .3s var(--ease), box-shadow .3s var(--ease);
}
.product-card:hover{ transform:translateY(-4px); box-shadow:0 16px 36px rgba(20,32,44,.12); }
.product-card:focus-visible{ outline:2px solid var(--accent-solid); outline-offset:3px; }
/* -1px pulls the media over the card's own border, the same fix as
   .rail-media, or the hover photograph shows a hairline of card round it */
.product-media{
  position:relative; display:block; aspect-ratio:1/1; background:#fff;
  margin:-1px -1px 0;
}
.product-media picture{ display:contents; }
.product-media img{ position:absolute; inset:0; width:100%; height:100%; object-fit:contain; }
/* the room shot fades in on hover: it shows the product in use, and it is the
   cue that there is more than one photograph behind the card */
.product-media .product-alt{ object-fit:cover; opacity:0; transition:opacity .45s var(--ease); }
@media (hover:hover){ .product-card:hover .product-alt{ opacity:1; } }
.product-body{
  display:flex; flex-direction:column; gap:.3rem; flex:1;
  padding:var(--s2) 1.1rem var(--s3); border-top:1px solid var(--line-light);
}
.product-cat{ font-size:.7rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--accent-text); }
.product-name{ font-family:var(--display); font-size:1.15rem; line-height:1.25; color:var(--on-light); }
.product-meta{ font-size:.85rem; color:var(--on-light-mute); flex:1; }
.product-go{
  display:inline-flex; align-items:center; gap:.45rem; margin-top:.55rem;
  font-size:.72rem; font-weight:600; letter-spacing:.13em; text-transform:uppercase;
  color:var(--accent-text);
}
.product-go svg{ transition:transform .22s var(--ease); }
.product-card:hover .product-go svg{ transform:translateX(4px); }
.product-more{ margin-top:var(--s4); }

/* one product, every photograph of it as its own tile (the calendar page) */
.view-strip{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--s3); }
.view-tile{ display:flex; flex-direction:column; gap:.6rem; color:var(--on-light); }
.view-media{
  position:relative; display:block; aspect-ratio:4/3; overflow:hidden;
  background:#fff; border-radius:var(--radius);
  transition:transform .3s var(--ease), box-shadow .3s var(--ease);
}
.view-media picture{ display:contents; }
.view-media img{ position:absolute; inset:0; width:100%; height:100%; object-fit:contain; }
.view-tile:hover .view-media{ transform:translateY(-3px); box-shadow:0 14px 30px rgba(20,32,44,.12); }
.view-label{ font-size:.76rem; font-weight:600; letter-spacing:.12em; text-transform:uppercase; color:var(--on-light-mute); }

@media (max-width:1100px){
  .product-grid{ grid-template-columns:repeat(3,minmax(0,1fr)); }
}
@media (max-width:820px){
  .product-grid, .view-strip{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s2); }
  .product-body{ padding:.85rem .85rem 1.1rem; }
  .product-name{ font-size:1rem; }
}

/* the catalogue's section links land under a fixed header without this */
.product-section{ scroll-margin-top:5.5rem; }
/* tiles on a dark section keep readable labels */
.section-mid .view-tile{ color:var(--on-dark); }
.section-mid .view-label{ color:var(--on-dark-mute); }

/* "5 PHOTOGRAPHS ->" broke across two lines in a two-column phone grid */
.product-go{ white-space:nowrap; }
@media (max-width:820px){ .product-go{ font-size:.66rem; letter-spacing:.08em; } }

/* retail price on the card, with Frank's postage under it: he quotes his
   prices as "£11 plus postage £3.50", so the two belong together */
.product-price{ margin-top:.35rem; font-size:.95rem; font-weight:600; color:var(--on-light); }
.product-post{ font-size:.78rem; color:var(--on-light-mute); }
.strip-price{ margin:0 0 var(--s3); font-weight:600; color:var(--on-light); }
.strip-price span{ font-weight:400; color:var(--on-light-mute); }
.section-mid .strip-price{ color:var(--on-dark); }
.section-mid .strip-price span{ color:var(--on-dark-mute); }
#stockists{ scroll-margin-top:5.5rem; }

/* ---- the viewer ---------------------------------------------------------
   A native <dialog>, built by main.js on first use. showModal() gives focus
   trapping, Escape and a backdrop without any of it being hand-rolled. */
.viewer{
  width:min(1180px, 96vw); height:min(880px, 92dvh);
  max-width:none; max-height:none; margin:auto; padding:0; border:0;
  border-radius:10px; background:var(--paper); color:var(--on-light); overflow:hidden;
}
.viewer[open]{ display:flex; flex-direction:column; }
.viewer::backdrop{ background:rgba(10,16,24,.82); }
.viewer-top{
  display:flex; align-items:center; gap:var(--s2);
  padding:var(--s2) var(--s3); border-bottom:1px solid var(--line-light);
}
.viewer-title{ flex:1; min-width:0; }
.viewer-cat{ display:block; font-size:.7rem; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--accent-text); }
.viewer-name{ margin:.15rem 0 0; font-size:clamp(1.2rem,2.2vw,1.65rem); line-height:1.2; color:var(--on-light); }
.viewer-count{ font-size:.85rem; color:var(--on-light-mute); white-space:nowrap; font-variant-numeric:tabular-nums; }
.viewer-close{
  flex:none; width:44px; height:44px; display:grid; place-items:center; cursor:pointer;
  border:1px solid var(--line-light); border-radius:50%; background:#fff; color:var(--on-light);
}
.viewer-close:hover{ border-color:var(--on-light); }
.viewer-stage{ position:relative; flex:1; min-height:0; background:#fff; touch-action:pan-y; }
.viewer-img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:contain;
  transition:opacity .25s var(--ease);
}
.viewer-img.is-loading{ opacity:.35; }
.viewer-nav{
  position:absolute; top:50%; translate:0 -50%;
  width:48px; height:48px; display:grid; place-items:center; cursor:pointer;
  border:0; border-radius:50%; background:rgba(20,32,44,.78); color:#fff;
  transition:background .2s var(--ease);
}
.viewer-nav:hover{ background:var(--accent-solid); }
.viewer-prev{ left:var(--s2); }
.viewer-next{ right:var(--s2); }
/* display:grid above outranks the UA's [hidden] rule, so restate it */
.viewer-nav[hidden], .viewer-thumbs[hidden]{ display:none; }
.viewer-foot{
  display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center;
  gap:.75rem var(--s3); padding:var(--s2) var(--s3) var(--s3);
  border-top:1px solid var(--line-light);
}
.viewer-label{ grid-column:1 / -1; margin:0; font-weight:600; color:var(--on-light); }
.viewer-price{ grid-column:1 / -1; display:flex; flex-wrap:wrap; align-items:baseline; gap:.3rem 1.4rem; }
.viewer-price-row{ display:inline-flex; align-items:baseline; gap:.5rem; }
.viewer-price-row .k{ font-size:.7rem; font-weight:600; letter-spacing:.13em; text-transform:uppercase; color:var(--on-light-mute); }
.viewer-price-row .v{ font-size:1rem; font-weight:600; color:var(--on-light); }
.viewer-price small{ flex-basis:100%; font-size:.8rem; color:var(--on-light-mute); }
.viewer-thumbs{ display:flex; gap:.5rem; overflow-x:auto; padding:2px; scrollbar-width:thin; }
.viewer-thumbs button{
  flex:none; width:64px; height:64px; padding:0; cursor:pointer; overflow:hidden;
  border:2px solid transparent; border-radius:6px; background:#fff; opacity:.7;
  transition:opacity .2s var(--ease), border-color .2s var(--ease);
}
.viewer-thumbs button:hover{ opacity:1; }
.viewer-thumbs button[aria-current="true"]{ border-color:var(--accent-solid); opacity:1; }
.viewer-thumbs img{ width:100%; height:100%; object-fit:cover; display:block; }
body:has(.viewer[open]){ overflow:hidden; }
@media (max-width:640px){
  .viewer{ width:100vw; height:100dvh; border-radius:0; }
  .viewer-top{ padding:.75rem 1rem; }
  .viewer-foot{ grid-template-columns:1fr; padding:.75rem 1rem 1rem; }
  .viewer-ask{ width:100%; }
  .viewer-nav{ width:42px; height:42px; }
  .viewer-prev{ left:.5rem; }
  .viewer-next{ right:.5rem; }
}
@media (prefers-reduced-motion: reduce){ .viewer-img{ transition:none; } }

/* ============================================================
   FORMS
   ============================================================ */
/* the on-page quote form, used on every service page and under the services
   rail on the home page */
.quote-grid{
  display:grid; grid-template-columns:1.15fr .85fr;
  gap:clamp(2rem,4vw,3.5rem); align-items:start;
}
.quote-form{ max-width:none; margin-top:0; }
/* .form is a stretch flex column, which would make the button span the
   whole measure and stop looking like a button */
.form button[type="submit"]{ align-self:flex-start; }
.field-row{ display:grid; grid-template-columns:1fr 1fr; gap:var(--s3); }
.quote-side{ display:flex; flex-direction:column; gap:var(--s4); }
.quote-media{
  position:relative;
  aspect-ratio:4/3; overflow:hidden; border-radius:var(--radius);
  background:var(--ink-3);
}
/* every frame stacked, each fading up in turn. --cycle is the whole loop and
   the inline animation-delay on each image is its slot in it, so adding or
   removing a photograph in build.py needs no CSS change.
   The responsive rewriter wraps each frame in <picture> and leaves the delay on
   the <img> inside it, so the picture is made display:contents and the image
   itself is the animated, positioned box. */
.quote-media picture{ display:contents; }
.quote-media img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  opacity:0; animation:quote-fade var(--cycle, 30s) linear infinite;
}
@keyframes quote-fade{
  0%    { opacity:0; }
  2%    { opacity:1; }
  16.6% { opacity:1; }
  18.6% { opacity:0; }
  100%  { opacity:0; }
}
@media (prefers-reduced-motion: reduce){
  .quote-media img{ animation:none; opacity:0; }
  .quote-media > :first-child img, .quote-media > img:first-child{ opacity:1; }
}

/* honeypot: off-screen rather than display:none, because some bots skip
   anything that is not rendered */
.hp{
  position:absolute; left:-9999px; width:1px; height:1px;
  overflow:hidden; margin:0;
}

.form-note{ margin:0; font-size:.9rem; color:var(--on-dark-mute); }
.form-note.is-error{ color:#ff9b7a; }
.section-light .form-note{ color:var(--on-light-mute); }
.section-light .form-note.is-error{ color:#a3341a; }

.form-done{
  padding:var(--s4); border:1px solid var(--line-dark); border-radius:var(--radius);
  background:rgba(242,239,233,.05);
}
.form-done h3{ margin-bottom:.6rem; }
.form-done p{ margin:0; color:var(--on-dark-mute); font-size:.95rem; }
.form-done a{ color:var(--accent-hi); text-decoration:underline; }
.section-light .form-done{ border-color:var(--line-light); background:rgba(20,32,44,.04); }
.section-light .form-done h3{ color:var(--on-light); }
.section-light .form-done p{ color:var(--on-light-mute); }
.section-light .form-done a{ color:var(--accent-text); }

@media (max-width:900px){
  .quote-grid{ grid-template-columns:1fr; }
  .quote-side{ order:-1; }
  .quote-media{ aspect-ratio:16/9; }
}
@media (max-width:560px){
  .field-row{ grid-template-columns:1fr; }
}

.form{ display:flex; flex-direction:column; gap:var(--s3); max-width:34rem; margin-top:var(--s4); }
.field{ display:flex; flex-direction:column; gap:.45rem; }
.field label{ font-size:.82rem; font-weight:600; letter-spacing:.1em; text-transform:uppercase; }
.field .opt{ font-weight:400; text-transform:none; letter-spacing:0; color:var(--on-dark-mute); }
.field input, .field select, .field textarea{
  font:inherit; font-size:1rem; color:var(--on-dark);
  background:var(--ink-2); border:1px solid var(--line-dark);
  border-radius:var(--radius); padding:.85rem 1rem; min-height:50px;
  transition:border-color .2s var(--ease), background .2s var(--ease);
}
.field textarea{ resize:vertical; min-height:120px; line-height:1.6; }
.field input:focus, .field select:focus, .field textarea:focus{
  border-color:var(--accent); background:var(--ink-3); outline:none;
}
.field .help{ font-size:.83rem; color:var(--on-dark-mute); }

.contact-list{ list-style:none; margin:var(--s3) 0 0; padding:0; display:flex; flex-direction:column; gap:var(--s2); }
.contact-list li{
  display:flex; flex-direction:column; gap:.2rem;
  padding-bottom:var(--s2); border-bottom:1px solid var(--line-dark);
}
.contact-list span{ font-size:.74rem; letter-spacing:.14em; text-transform:uppercase; color:var(--on-dark-mute); }
.contact-list a{ color:var(--accent-hi); }
.contact-list a:hover{ text-decoration:underline; }

/* ============================================================
   RESPONSIVE for the new blocks
   ============================================================ */
@media (max-width:1100px){
  .rail{ grid-template-columns:repeat(3,1fr); }
}
@media (max-width:820px){
  .hero-foot{ display:block; }
  .hero-foot .scroll-cue{ display:none; }
  .prints-wall{ grid-template-columns:repeat(2,1fr); gap:var(--s5) var(--s3); }
  .prints-wall figure:last-child:nth-child(3n+1){ grid-column:auto; }
  .nav-cta::before{ display:none; }
  .rail{
    grid-auto-flow:column; grid-auto-columns:74%; grid-template-columns:none;
    overflow-x:auto; scroll-snap-type:x mandatory;
    padding-bottom:var(--s2);
    margin-inline:calc(-1 * var(--gutter)); padding-inline:var(--gutter);
    scrollbar-width:thin;
  }
  .rail-card{ scroll-snap-align:start; }
  .price-row{ grid-template-columns:1fr auto; }
  .price-note{ grid-column:1 / -1; }
  .steps{ grid-template-columns:1fr; }
}
@media (max-width:560px){
  .prints-wall{ grid-template-columns:1fr; }
  .prints-wall img{ max-height:none; }
  .rail{ grid-auto-columns:86%; }
  .price-row{ grid-template-columns:1fr; gap:.5rem; }
  .pagehero{ padding-top:calc(var(--s7) + var(--s5)); }
}

/* ============================================================
   UPLOAD PAGE (private)
   ============================================================ */
.up-err{
  margin:0 0 var(--s2); padding:.75rem 1rem; border-radius:var(--radius);
  background:rgba(168,68,58,.16); border:1px solid rgba(168,68,58,.45);
  color:#f0c9c4; font-size:.9rem;
}

.up-controls{
  display:grid; grid-template-columns:minmax(0,18rem) minmax(0,1fr);
  gap:var(--s3); margin-top:var(--s5);
}

/* the big friendly target */
.up-drop{
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:.4rem; text-align:center; cursor:pointer;
  margin-top:var(--s4); padding:var(--s6) var(--s3);
  border:2px dashed var(--line-dark); border-radius:8px;
  background:var(--ink-2); color:var(--on-dark);
  transition:border-color .2s var(--ease), background .2s var(--ease);
}
.up-drop:hover, .up-drop:focus-within{ border-color:var(--accent); background:var(--ink-3); }
.up-drop svg{ color:var(--accent-hi); margin-bottom:.4rem; }
.up-drop b{ font-family:var(--display); font-size:1.35rem; font-weight:400; }
.up-drop span{ font-size:.9rem; color:var(--on-dark-mute); }

.up-status{ margin:var(--s3) 0 0; font-size:.95rem; color:var(--on-dark-mute); min-height:1.5em; }
.up-status.is-ok{ color:#8fbf9f; }
.up-status.is-warn{ color:#e2a06a; }

/* one line per file, while it goes up */
.up-queue{ list-style:none; margin:var(--s3) 0 0; padding:0; display:flex; flex-direction:column; gap:.5rem; }
.up-row{
  display:grid; grid-template-columns:56px minmax(0,1fr) auto;
  gap:var(--s2); align-items:center;
  padding:.6rem; border:1px solid var(--line-dark); border-radius:var(--radius);
  background:var(--ink-2);
}
.up-thumb{
  width:56px; height:44px; border-radius:3px; overflow:hidden;
  background:var(--ink-3); display:block;
}
.up-thumb img{ width:100%; height:100%; object-fit:cover; }
.up-row-name{
  font-size:.9rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.up-row-state{ font-size:.78rem; letter-spacing:.08em; text-transform:uppercase; color:var(--on-dark-mute); }
.up-row.is-ok .up-row-state{ color:#8fbf9f; }
.up-row.is-fail{ border-color:rgba(168,68,58,.5); }
.up-row.is-fail .up-row-state{ color:#e29b93; text-transform:none; letter-spacing:0; font-size:.85rem; }

/* what is already live */
.up-recent{
  list-style:none; margin:var(--s3) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:var(--s2);
}
.up-recent-item{ position:relative; border-radius:var(--radius); overflow:hidden; background:var(--ink-3); }
.up-recent-item img{ width:100%; aspect-ratio:4/3; object-fit:cover; display:block; }
.up-del{
  position:absolute; inset:auto 0 0 0;
  border:0; padding:.5rem; min-height:40px;
  font-size:.72rem; font-weight:600; letter-spacing:.12em; text-transform:uppercase;
  color:#fff; background:rgba(14,22,32,.82); backdrop-filter:blur(4px);
  opacity:0; transition:opacity .2s var(--ease);
}
.up-recent-item:hover .up-del, .up-del:focus-visible{ opacity:1; }
.up-empty{ grid-column:1/-1; color:var(--on-dark-mute); font-size:.93rem; }

@media (max-width:700px){
  .up-controls{ grid-template-columns:1fr; }
  .up-drop{ padding:var(--s5) var(--s3); }
  /* touch has no hover, so the remove button is always visible on a phone */
  .up-del{ opacity:1; }
  .up-recent{ grid-template-columns:repeat(auto-fill,minmax(120px,1fr)); }
}

/* ============================================================
   ADMIN  (/admin, Frank's own page)
   Two tabs: the photographs and their words, and the prices.
   Dark like the upload page it grew out of, because it sits on the
   site's own shell and nobody but Frank ever sees it.
   ============================================================ */
.adm-tabs{
  display:flex; gap:.4rem; margin-top:var(--s5);
  border-bottom:1px solid var(--line); flex-wrap:wrap;
}
.adm-tab{
  appearance:none; background:none; border:0; cursor:pointer;
  padding:.7rem 1.1rem; margin-bottom:-1px;
  font-size:.78rem; font-weight:600; letter-spacing:.13em; text-transform:uppercase;
  color:var(--on-dark-mute); border-bottom:2px solid transparent;
}
.adm-tab:hover{ color:var(--on-dark); }
.adm-tab[aria-selected="true"]{ color:var(--accent-hi); border-bottom-color:var(--accent); }
.adm-tab:focus-visible{ outline:2px solid var(--accent-solid); outline-offset:2px; }

/* a note Frank needs to read before he changes a price */
.adm-warn{
  margin-top:var(--s3); padding:var(--s3);
  border:1px solid var(--line); border-left:3px solid var(--accent);
  border-radius:var(--radius); background:var(--ink-3);
  color:var(--on-dark-mute); font-size:.92rem; max-width:62ch;
}

/* one photograph, its picture beside the three fields that describe it */
.adm-photos{ list-style:none; margin:var(--s3) 0 0; padding:0; display:grid; gap:var(--s3); }
.adm-photo{
  display:grid; grid-template-columns:200px 1fr; gap:var(--s3);
  padding:var(--s3); border:1px solid var(--line); border-radius:var(--radius);
  background:var(--ink-3);
}
.adm-photo-media img{
  display:block; width:100%; aspect-ratio:4/3; object-fit:cover;
  border-radius:calc(var(--radius) - 2px); background:var(--ink-2);
}
.adm-photo-body{ display:grid; gap:.8rem; align-content:start; }
.adm-photo-body .field{ margin:0; }
.adm-photo-actions{ display:flex; align-items:center; gap:.7rem; flex-wrap:wrap; }
.adm-note{ font-size:.85rem; color:var(--on-dark-mute); }
.btn-sm{ padding:.5rem .95rem; min-height:0; font-size:.72rem; }

/* the price form: one fieldset per category */
.adm-price-group{
  margin:var(--s3) 0 0; padding:var(--s3); max-width:34rem;
  border:1px solid var(--line); border-radius:var(--radius); background:var(--ink-3);
}
.adm-price-group legend{
  padding:0 .5rem; font-size:.72rem; font-weight:600;
  letter-spacing:.14em; text-transform:uppercase; color:var(--accent-text);
}
.adm-price-field{ display:grid; grid-template-columns:1fr 8rem; align-items:center; gap:var(--s3); margin:0 0 .7rem; }
/* the pound sign belongs in the box, so the field reads as a price */
/* min-width:0 because this is the grid item now: without it the input keeps
   its intrinsic width and pushes straight out through the panel's right edge */
.adm-money{ position:relative; display:block; min-width:0; }
.adm-money input{ width:100%; }
.adm-money::before{
  content:"£"; position:absolute; left:.85rem; top:50%; transform:translateY(-50%);
  color:var(--on-dark-mute); font-size:.95rem; pointer-events:none;
}
.adm-money input{ padding-left:1.9rem; }
/* the spinner arrows crowd a two-character number and nobody uses them */
.adm-money input::-webkit-outer-spin-button,
.adm-money input::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
.adm-money input{ -moz-appearance:textfield; appearance:textfield; }
.adm-price-field:last-child{ margin-bottom:0; }
.adm-price-field label{ margin:0; }
/* postage sits slightly back from the price it belongs to */
.adm-price-field.is-postage label{ color:var(--on-dark-mute); font-weight:400; }

@media (max-width:700px){
  .adm-photo{ grid-template-columns:1fr; }
  .adm-photo-media img{ aspect-ratio:16/9; }
  .adm-price-field{ grid-template-columns:1fr; gap:.3rem; }
}

/* gallery on a phone: one photograph per line, still whole; the place piece stacks */
@media (max-width:900px){
  .place-grid{ grid-template-columns:1fr; }
  .place-head{ position:static; }
}
@media (max-width:760px){
  .grow{ flex-direction:column; gap:var(--s5); }
  .gph{ flex:none; width:100%; }
  .lb-fig{ padding:4.25rem .75rem 5.5rem; }
  .lb-img{ max-height:calc(100vh - 13rem); max-height:calc(100dvh - 13rem); }
  .lb-nav{ top:auto; bottom:1rem; margin-top:0; }
}

/* gallery index: each cover photograph whole, in justified rows like the
   galleries themselves (gallery_text.gallery_cards). Overrides the fixed 4:3
   crop and the hover zoom that .card uses elsewhere. */
.card-rows{ display:flex; flex-direction:column; gap:var(--s3); }
.card-row{ display:flex; gap:var(--s3); align-items:stretch; }
.card-whole{ flex-grow:var(--r, 1.33); flex-shrink:1; flex-basis:0; min-width:0; }
.card-whole .card-img{ aspect-ratio:auto; }
.card-whole .card-img picture, .card-whole .card-img img{ display:block; width:100%; height:auto; }
.card-whole .card-img img{ object-fit:fill; }
.card-whole:hover .card-img img{ transform:none; }
@media (max-width:760px){
  .card-row{ flex-direction:column; }
  .card-whole{ flex:none; width:100%; }
}

/* ============================================================
   HERO FITS THE SCREEN
   On a laptop the whole home hero has to be visible without scrolling: the
   "As featured in" strip and the bottom of the thin frame line included.
   Everything that decides its height (the header, the headline, the lede,
   the gaps, the logos) is sized by the window's height as well as its width
   (min(vw, vh)), so a wide but short laptop screen gets a slightly smaller
   headline instead of a hero that runs off the bottom of the screen.
   Phones and small tablets (820px and under) keep their own layout above.
   ============================================================ */
@media (min-width:821px){
  /* the header keeps its own spacing: it floats over the hero, so it costs no
     height, and it has to sit just inside the frame line, not across it */
  .hero-inner{ padding-block:clamp(4.25rem, 11vh, 6rem) clamp(.9rem, 2.4vh, 2rem); }
  .hero-body{ padding-block:clamp(.25rem, 1.6vh, 1.25rem) clamp(.75rem, 3vh, 2.5rem); }
  .hero .eyebrow{ margin-bottom:clamp(.6rem, 2vh, 1.5rem); }
  .hero h1{
    font-size:clamp(2.5rem, min(7.2vw, 10.6vh), 6rem);
    margin-bottom:clamp(.75rem, 2.2vh, 1.5rem);
  }
  .hero .lede{
    font-size:clamp(.95rem, min(1.4vw, 2.35vh), 1.2rem);
    margin-bottom:clamp(1rem, 3vh, 2rem);
  }
  .hero-logos-label{ margin-bottom:clamp(.4rem, 1.2vh, .9rem); }
  .logo-set img{ height:clamp(22px, min(3.4vw, 4.8vh), 40px); }
}
/* the WhatsApp button is pinned to the bottom right of the screen, which is
   where the hero's logo strip ends at every size, so the strip stops short of
   it rather than sliding underneath (the scroll arrow that sat there has gone
   for the same reason). Measured: the labelled button needs about 155px
   cleared, the square one on a phone about 61px. */
.hero .hero-logos{ margin-right:10rem; }
@media (max-width:560px){ .hero .hero-logos{ margin-right:4.25rem; } }

/* ============================================================
   SHOP: header basket button, buy panel in the viewer, basket drawer
   All of it is hidden until /api/basket says the shop is on (main.js).
   ============================================================ */
.masthead .shell > .nav{ margin-left:auto; }
.basket-btn{
  position:relative; flex:none; width:44px; height:44px; display:grid; place-items:center;
  background:none; border:1px solid var(--line-dark); border-radius:var(--radius);
  color:inherit; cursor:pointer; transition:border-color .2s var(--ease);
}
.basket-btn[hidden]{ display:none; }
.basket-btn:hover{ border-color:var(--on-dark); }
.basket-count{
  position:absolute; top:-7px; right:-7px; min-width:20px; height:20px; padding:0 5px;
  display:grid; place-items:center; border-radius:10px;
  background:var(--accent-solid); color:#fff; font-size:.7rem; font-weight:700; line-height:1;
}
/* below 1120 the closed nav is an empty flex item that still takes a gap, so
   with the basket showing there are three 2rem gaps and a 375px phone ran out
   of room: the burger shrank to 33px and hung off the right edge. Tighter
   gaps only while the basket is in the row; the burger must never shrink. */
@media (max-width:1120px){
  .burger{ flex:none; }
  .masthead .shell:has(.basket-btn:not([hidden])){ gap:.6rem; }
}

/* the buy panel: option chips, then the button */
.viewer-buy{
  grid-column:1 / -1; display:flex; flex-wrap:wrap; align-items:flex-end; gap:.75rem var(--s3);
}
.viewer-buy[hidden]{ display:none; }
.buy-opt{ border:0; margin:0; padding:0; display:flex; flex-wrap:wrap; gap:.4rem; min-width:0; }
.buy-opt legend{
  float:left; width:100%; margin-bottom:.35rem; padding:0;
  font-size:.7rem; font-weight:600; letter-spacing:.13em; text-transform:uppercase; color:var(--on-light-mute);
}
.chip{ position:relative; cursor:pointer; }
.chip input{ position:absolute; opacity:0; width:1px; height:1px; }
.chip span{
  display:inline-flex; align-items:center; min-height:40px; padding:.4rem .95rem;
  border:1px solid rgba(20,32,44,.25); border-radius:999px; background:#fff;
  font-size:.86rem; font-weight:500; color:var(--on-light);
  transition:border-color .15s var(--ease), background .15s var(--ease), color .15s var(--ease);
}
.chip:hover span{ border-color:var(--on-light); }
.chip input:checked + span{ background:var(--on-light); border-color:var(--on-light); color:#fff; }
.chip input:focus-visible + span{ outline:2px solid var(--accent-solid); outline-offset:2px; }
.buy-act{ margin-left:auto; display:flex; flex-direction:column; align-items:flex-end; gap:.3rem; }
.buy-add{ min-height:46px; padding:.7rem 1.5rem; white-space:nowrap; }
.buy-added{ margin:0; min-height:1.2em; font-size:.82rem; color:var(--on-light-mute); }
.buy-view{
  padding:0; border:0; background:none; cursor:pointer; font:inherit; font-weight:600;
  color:var(--accent-text); text-decoration:underline; text-underline-offset:2px;
}
.buy-test, .basket-test{ margin:0; font-size:.75rem; font-weight:600; color:#8a5a00; }
@media (max-width:640px){
  .viewer-buy{ gap:.6rem; }
  .buy-act{ margin-left:0; width:100%; align-items:stretch; }
  .buy-add{ width:100%; }
  .chip span{ min-height:38px; padding:.35rem .8rem; }
}

/* the basket: a panel down the right-hand side */
.basket{
  width:min(440px, 100vw); height:100dvh; max-height:none; max-width:none;
  margin:0 0 0 auto; padding:0; border:0; border-radius:0;
  background:var(--paper); color:var(--on-light);
}
.basket[open]{ display:flex; flex-direction:column; animation:basket-in .28s var(--ease); }
@keyframes basket-in{ from{ transform:translateX(40px); opacity:0; } to{ transform:none; opacity:1; } }
.basket::backdrop{ background:rgba(10,16,24,.6); }
.basket-top{
  display:flex; align-items:center; justify-content:space-between; gap:var(--s2);
  padding:var(--s2) var(--s3); border-bottom:1px solid var(--line-light);
}
.basket-top h2{ margin:0; font-size:1.5rem; color:var(--on-light); }
.basket-close{
  flex:none; width:44px; height:44px; display:grid; place-items:center; cursor:pointer;
  border:1px solid var(--line-light); border-radius:50%; background:#fff; color:var(--on-light);
}
.basket-close:hover{ border-color:var(--on-light); }
.basket-body{ flex:1; min-height:0; overflow-y:auto; padding:var(--s2) var(--s3); }
.basket.is-busy .basket-lines, .basket.is-busy .basket-sums{ opacity:.55; transition:opacity .2s; }
.basket-lines{ list-style:none; margin:0; padding:0; }
.basket-line{
  display:grid; grid-template-columns:64px minmax(0,1fr) auto; gap:.9rem; align-items:start;
  padding:.9rem 0; border-bottom:1px solid var(--line-light);
}
.basket-line:first-child{ padding-top:0; }
.basket-thumb{ width:64px; height:64px; object-fit:cover; border-radius:6px; background:#fff; }
.basket-text{ display:flex; flex-direction:column; gap:.15rem; min-width:0; }
.basket-name{ font-weight:600; line-height:1.3; }
.basket-option{ font-size:.85rem; color:var(--on-light-mute); }
.basket-row{ display:flex; align-items:center; gap:.9rem; margin-top:.45rem; }
.qty{ display:inline-flex; align-items:center; border:1px solid rgba(20,32,44,.25); border-radius:999px; background:#fff; }
.qty-btn{
  width:34px; height:34px; display:grid; place-items:center; padding:0; cursor:pointer;
  border:0; background:none; font-size:1.1rem; color:var(--on-light); border-radius:50%;
}
.qty-btn:disabled{ opacity:.35; cursor:default; }
.qty-n{ min-width:1.6rem; text-align:center; font-weight:600; font-variant-numeric:tabular-nums; }
.basket-remove{
  padding:0; border:0; background:none; cursor:pointer; font:inherit; font-size:.82rem;
  color:var(--on-light-mute); text-decoration:underline; text-underline-offset:2px;
}
.basket-remove:hover{ color:var(--on-light); }
.basket-price{ font-weight:600; font-variant-numeric:tabular-nums; }
.basket-empty{ margin:var(--s3) 0; color:var(--on-light-mute); }
.basket-foot{ padding:var(--s2) var(--s3) var(--s3); border-top:1px solid var(--line-light); background:var(--paper-2); }
.basket-foot[hidden]{ display:none; }
.basket-sums{ margin:0 0 .6rem; }
.basket-sums div{ display:flex; justify-content:space-between; padding:.2rem 0; }
.basket-sums dt{ color:var(--on-light-mute); }
.basket-sums dd{ margin:0; font-variant-numeric:tabular-nums; }
.basket-sums .b-total-row{ margin-top:.3rem; padding-top:.5rem; border-top:1px solid var(--line-light); font-size:1.15rem; font-weight:700; }
.basket-sums .b-total-row dt{ color:var(--on-light); }
.basket-note, .basket-secure{ margin:0 0 .7rem; font-size:.8rem; color:var(--on-light-mute); }
.basket-secure{ margin:.6rem 0 0; text-align:center; }
.basket-error{ margin:0 0 .7rem; padding:.6rem .8rem; border-radius:var(--radius); background:rgba(168,68,58,.12); color:#7a2a20; font-size:.88rem; }
.basket-error[hidden]{ display:none; }
.basket-go{ width:100%; }
.basket-go:disabled{ opacity:.7; cursor:progress; transform:none; }
@media (prefers-reduced-motion: reduce){ .basket[open]{ animation:none; } }

/* ============================================================
   POLICY PAGES: delivery & returns, terms, privacy (policies.py)
   ============================================================ */
.legal-head{ padding:calc(var(--s8) + var(--s4)) 0 var(--s5); }
.legal-page{ padding-top:var(--s3); }
.legal{ max-width:46rem; }
.legal h1{ font-size:clamp(2.2rem,4.5vw,3.2rem); }
.legal-updated{ margin:.6rem 0 var(--s3); font-size:.85rem; color:var(--on-dark-mute); }
.legal h2{ margin:var(--s5) 0 var(--s2); font-size:clamp(1.4rem,2.4vw,1.8rem); color:var(--on-light); }
.legal-page p, .legal-page li{ color:#2a3642; line-height:1.65; }
.legal ul, .legal ol{ margin:0 0 1.15em; padding-left:1.3rem; }
.legal li{ margin-bottom:.45rem; }
.legal a{ color:var(--accent-text); text-decoration:underline; text-underline-offset:2px; }
.legal-table{ width:100%; border-collapse:collapse; margin:0 0 1.15em; font-size:.95rem; }
.legal-table th, .legal-table td{ text-align:left; vertical-align:top; padding:.6rem .75rem .6rem 0; border-bottom:1px solid var(--line-light); }
.legal-table th{ font-size:.72rem; font-weight:600; letter-spacing:.12em; text-transform:uppercase; color:var(--on-light-mute); }
.legal-nav{ display:flex; flex-wrap:wrap; gap:.5rem 1.5rem; margin-top:var(--s5); padding-top:var(--s3); border-top:1px solid var(--line-light); }
.legal-nav a{ font-size:.8rem; font-weight:600; letter-spacing:.12em; text-transform:uppercase; text-decoration:none; }
@media (max-width:560px){
  .legal-table{ font-size:.86rem; }
  .legal-table th, .legal-table td{ padding-right:.5rem; }
}
.footer-policies a{ text-decoration:underline; text-underline-offset:2px; }
.basket-note a{ color:var(--accent-text); text-decoration:underline; }

/* ============================================================
   RECENT WORK: a gallery of jobs (main.js draws it from /api/projects)
   A card per project, and a viewer that opens that job's own pictures.
   ============================================================ */
.projects-loading{ color:var(--on-light-mute); margin:0; }
.proj-cards{
  display:grid; grid-template-columns:repeat(auto-fill, minmax(300px, 1fr)); gap:var(--s3);
}
.proj-card{
  display:flex; flex-direction:column; text-align:left; padding:0; border:0; cursor:pointer;
  background:none; color:inherit; font:inherit; border-radius:var(--radius); overflow:hidden;
}
.proj-card.is-feature{ grid-column:span 2; }
.proj-card-media{
  position:relative; display:block; aspect-ratio:3/2; overflow:hidden;
  border-radius:var(--radius); background:var(--paper-2);
}
.proj-card.is-feature .proj-card-media{ aspect-ratio:2/1; }
.proj-card-media img{
  width:100%; height:100%; object-fit:cover; display:block;
  transition:transform .5s var(--ease);
}
.proj-card:hover .proj-card-media img{ transform:scale(1.03); }
.proj-card:focus-visible{ outline:2px solid var(--accent-solid); outline-offset:3px; }
.proj-card-play, .pv-thumb-play{
  position:absolute; inset:auto auto 10px 10px; display:grid; place-items:center;
  width:34px; height:34px; border-radius:50%; background:rgba(14,22,32,.78); color:#fff;
  font-size:.75rem; padding-left:2px;
}
.proj-card-body{ display:block; padding:.85rem .1rem 0; }
.proj-card-name{
  display:block; font-family:var(--display); font-size:1.35rem; line-height:1.15;
  color:var(--on-light);
}
.proj-card-sum{ display:block; margin-top:.35rem; color:var(--on-light-mute); font-size:.92rem; line-height:1.45; }
.proj-card-go{
  display:block; margin-top:.5rem; font-size:.7rem; font-weight:600; letter-spacing:.13em;
  text-transform:uppercase; color:var(--accent-text);
}
@media (max-width:820px){
  .proj-cards{ grid-template-columns:1fr; }
  .proj-card.is-feature{ grid-column:span 1; }
  .proj-card.is-feature .proj-card-media{ aspect-ratio:3/2; }
}

/* the viewer: one job's photographs and clips */
.pviewer{
  width:min(1180px, 96vw); height:min(880px, 92dvh); max-width:none; max-height:none;
  margin:auto; padding:0; border:0; border-radius:10px;
  background:var(--paper); color:var(--on-light); overflow:hidden;
}
.pviewer[open]{ display:flex; flex-direction:column; }
.pviewer::backdrop{ background:rgba(10,16,24,.85); }
.pv-top{
  display:flex; align-items:center; gap:var(--s2);
  padding:var(--s2) var(--s3); border-bottom:1px solid var(--line-light);
}
.pv-title{ flex:1; min-width:0; }
.pv-meta{
  display:block; font-size:.68rem; font-weight:600; letter-spacing:.14em;
  text-transform:uppercase; color:var(--accent-text);
}
.pv-meta:empty{ display:none; }
.pv-name{ margin:.15rem 0 0; font-size:clamp(1.2rem,2.2vw,1.65rem); line-height:1.2; color:var(--on-light); }
.pv-count{ font-size:.85rem; color:var(--on-light-mute); white-space:nowrap; font-variant-numeric:tabular-nums; }
.pv-close{
  flex:none; width:44px; height:44px; display:grid; place-items:center; cursor:pointer;
  border:1px solid var(--line-light); border-radius:50%; background:#fff; color:var(--on-light);
}
.pv-close:hover{ border-color:var(--on-light); }
.pv-stage{ position:relative; flex:1; min-height:0; background:#0e1620; touch-action:pan-y; }
.pv-img, .pv-video{
  position:absolute; inset:0; width:100%; height:100%; object-fit:contain; background:#0e1620;
}
.pv-nav{
  position:absolute; top:50%; translate:0 -50%; z-index:2;
  width:48px; height:48px; display:grid; place-items:center; cursor:pointer;
  border:0; border-radius:50%; background:rgba(20,32,44,.78); color:#fff;
  transition:background .2s var(--ease);
}
.pv-nav:hover{ background:var(--accent-solid); }
.pv-prev{ left:var(--s2); }
.pv-next{ right:var(--s2); }
.pv-nav[hidden], .pv-thumbs[hidden]{ display:none; }
.pv-foot{
  display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center;
  gap:.75rem var(--s3); padding:var(--s2) var(--s3) var(--s3);
  border-top:1px solid var(--line-light);
}
.pv-sum{ grid-column:1 / -1; margin:0; color:var(--on-light-mute); font-size:.92rem; }
.pv-sum:empty{ display:none; }
.pv-thumbs{ display:flex; gap:.5rem; overflow-x:auto; padding:2px; scrollbar-width:thin; }
.pv-thumbs button{
  position:relative; flex:none; width:64px; height:64px; padding:0; cursor:pointer; overflow:hidden;
  border:2px solid transparent; border-radius:6px; background:#fff; opacity:.7;
  transition:opacity .2s var(--ease), border-color .2s var(--ease);
}
.pv-thumbs button:hover{ opacity:1; }
.pv-thumbs button[aria-current="true"]{ border-color:var(--accent-solid); opacity:1; }
.pv-thumbs img{ width:100%; height:100%; object-fit:cover; display:block; }
.pv-thumb-play{ inset:auto 4px 4px auto; width:20px; height:20px; font-size:.6rem; }
body:has(.pviewer[open]){ overflow:hidden; }
@media (max-width:640px){
  .pviewer{ width:100vw; height:100dvh; border-radius:0; }
  .pv-top{ padding:.75rem 1rem; }
  .pv-foot{ grid-template-columns:1fr; padding:.75rem 1rem 1rem; }
  .pv-ask{ width:100%; }
  .pv-nav{ width:42px; height:42px; }
  .pv-prev{ left:.5rem; }
  .pv-next{ right:.5rem; }
}

/* ============================================================
   RECENT WORK: the admin tab
   ============================================================ */
.adm-proj-top{ display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap; margin-top:var(--s3); }
.adm-projects{ list-style:none; margin:var(--s4) 0 0; padding:0; display:grid; gap:var(--s4); }
.adm-project{
  border:1px solid var(--line-dark); border-radius:var(--radius);
  padding:var(--s3); background:var(--ink-3);
}
.adm-proj-head{ display:grid; grid-template-columns:repeat(auto-fit, minmax(230px, 1fr)); gap:var(--s2) var(--s3); }
.adm-proj-head .field{ margin:0; }
.adm-proj-head .field:nth-child(n+5){ grid-column:1 / -1; }
.adm-proj-actions{
  display:flex; align-items:center; gap:.7rem; flex-wrap:wrap;
  margin-top:var(--s3); padding-top:var(--s3); border-top:1px solid var(--line-dark);
}
.adm-check{ display:inline-flex; align-items:center; gap:.5rem; font-size:.9rem; margin:0; }
.adm-check input{ width:18px; height:18px; }
.adm-small{ min-height:38px; padding:.45rem 1rem; font-size:.7rem; }
.adm-tiny{ min-height:32px; padding:.3rem .7rem; font-size:.68rem; letter-spacing:.08em; }
.adm-media{
  list-style:none; margin:var(--s3) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fill, minmax(215px, 1fr)); gap:var(--s3);
}
.adm-media-item{
  position:relative; border:1px solid var(--line-dark); border-radius:var(--radius);
  padding:.6rem; background:var(--ink-2); display:grid; gap:.5rem; align-content:start;
}
.adm-media-item img{ width:100%; aspect-ratio:3/2; object-fit:cover; border-radius:3px; background:#000; }
.adm-media-item .field{ margin:0; }
.adm-media-item label{ font-size:.68rem; }
.adm-media-item input{ min-height:40px; padding:.4rem .6rem; font-size:.85rem; }
.adm-cover-badge{
  justify-self:start; padding:.2rem .5rem; border-radius:3px;
  background:var(--accent-solid); color:#fff; font-size:.62rem; font-weight:700;
  letter-spacing:.12em; text-transform:uppercase;
}
.adm-media-badge{
  position:absolute; top:1rem; left:1rem; padding:.2rem .5rem; border-radius:3px;
  background:rgba(14,22,32,.85); color:var(--on-dark); font-size:.68rem; font-weight:600;
}
.adm-media-actions{ display:flex; align-items:center; gap:.35rem; flex-wrap:wrap; }
.adm-proj-add{ display:flex; align-items:center; gap:.7rem; flex-wrap:wrap; margin-top:var(--s3); }

/* ============================================================
   THE HERO FILM'S SOUND TAB
   Slides out of the right edge once the film is running, then tucks back to
   the speaker alone. Deliberately clear of the WhatsApp button, which owns the
   bottom right corner.
   ============================================================ */
.hero-sound{
  /* it stops at the hero frame's own line rather than the screen edge, or it
     reads as something that has fallen off the page */
  position:absolute; top:50%; right:calc(var(--frame-inset) + var(--frame-gap));
  translate:0 -50%; z-index:40;
  display:flex; align-items:center; gap:.65rem; cursor:pointer;
  padding:1rem 1.3rem 1rem 1.1rem;
  border:1px solid rgba(242,239,233,.22);
  border-radius:var(--radius);
  background:rgba(14,22,32,.72); backdrop-filter:blur(10px);
  color:var(--on-dark); font-family:var(--sans); font-size:.82rem; font-weight:600;
  letter-spacing:.1em; text-transform:uppercase; white-space:nowrap;
  box-shadow:0 10px 30px rgba(0,0,0,.35);
  transform:translateX(calc(100% + var(--frame-inset) + var(--frame-gap))); opacity:0;
  transition:transform .55s var(--ease), opacity .45s var(--ease),
             background .25s var(--ease), border-color .25s var(--ease);
}
.hero-sound[hidden]{ display:none; }
.hero-sound.is-in{ transform:translateX(0); opacity:1; }
.hero-sound:hover{ background:rgba(168,92,40,.92); border-color:transparent; }
.hero-sound:focus-visible{ outline:2px solid var(--accent-hi); outline-offset:3px; }
.hero-sound-icon{ display:grid; place-items:center; flex:none; color:var(--accent-hi); }
.hero-sound:hover .hero-sound-icon{ color:#fff; }
.hero-sound-label{
  display:block; max-width:14rem; overflow:hidden;
  transition:max-width .45s var(--ease), opacity .3s var(--ease), margin .45s var(--ease);
}
/* tucked away: the speaker alone, until it is hovered or focused again */
.hero-sound.is-tight .hero-sound-label{ max-width:0; opacity:0; margin-left:-.6rem; }
/* On a phone it goes UP, under the menu button. Down at the bottom right it sat
   on top of the WhatsApp button and crowded "Get a quote" and "See the work",
   which is exactly where a thumb is. */
@media (max-width:820px){
  .hero-sound{
    top:calc(var(--s6) + 1.35rem); bottom:auto; translate:0 0;
    padding:.75rem .95rem; font-size:.72rem; gap:.5rem;
  }
  .hero-sound-icon svg{ width:20px; height:20px; }
}
@media (prefers-reduced-motion: reduce){
  .hero-sound{ transition:opacity .2s linear; transform:none; }
  .hero-sound-label{ transition:none; }
}

/* the photograph the hero film comes to rest on, so it never ends on black */
.hero-still{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  opacity:0; transition:opacity 1.4s var(--ease); pointer-events:none;
}
.hero-media.is-ended .hero-still{ opacity:1; }
.hero-media.is-ended video{ opacity:0; transition:opacity 1.4s var(--ease); }
@media (prefers-reduced-motion: reduce){
  .hero-still, .hero-media.is-ended video{ transition:none; }
}

/* ============================================================
   THE GALLERY MANAGER  (/admin, Photographs tab, 12 Oct 2026)
   Every photograph in one gallery as a grid of tiles, in the order
   the website shows them. Drag one to move it; the number in the
   corner is its place.
   ============================================================ */
.adm-sort{
  list-style:none; margin:var(--s3) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(auto-fill, minmax(190px, 1fr)); gap:var(--s2);
}
.adm-sort-item{
  position:relative; display:grid; gap:.5rem; align-content:start;
  padding:.5rem; border:1px solid var(--line-dark); border-radius:var(--radius);
  background:var(--ink-2);
  transition:border-color .15s var(--ease), box-shadow .15s var(--ease), opacity .15s var(--ease);
}
.adm-sort-pic{
  position:relative; border-radius:3px; overflow:hidden; background:#000;
  cursor:grab; user-select:none; -webkit-user-select:none;
}
.adm-sort-pic img{
  display:block; width:100%; aspect-ratio:3/2; object-fit:cover; pointer-events:none;
}
.adm-sort-num{
  position:absolute; top:.4rem; left:.4rem; min-width:1.7rem; padding:.15rem .4rem;
  border-radius:3px; background:rgba(14,22,32,.85); color:var(--on-dark);
  font-size:.75rem; font-weight:700; text-align:center; pointer-events:none;
}
/* the grip: the only part of the tile a finger can drag by, so touching a
   photograph still scrolls the page. touch-action:none is what stops the
   browser scrolling instead of dragging. */
.adm-sort-grip{
  position:absolute; top:.4rem; right:.4rem; width:2.4rem; height:2.4rem;
  border-radius:3px; background:rgba(14,22,32,.85); cursor:grab; touch-action:none;
  background-image:radial-gradient(circle, var(--on-dark) 1.5px, transparent 1.6px);
  background-size:8px 8px; background-position:center; background-repeat:space;
  background-clip:content-box; padding:.55rem;
}
.adm-sort-name{
  margin:0; font-size:.85rem; line-height:1.35; color:var(--on-dark);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.adm-sort-name.is-blank{ color:var(--on-dark-mute); font-style:italic; }
.adm-sort-actions{ display:flex; gap:.3rem; flex-wrap:wrap; }
.adm-sort-actions .btn{ padding-inline:.6rem; }
.adm-sort-actions .btn:disabled{ opacity:.3; cursor:default; }
.adm-sort-del{ margin-left:auto; }

.adm-sort-item.is-dragging{
  border-color:var(--accent-hi); box-shadow:0 0 0 2px var(--accent-hi), 0 14px 30px rgba(0,0,0,.45);
  z-index:2;
}
.adm-sort-item.is-dragging .adm-sort-pic{ cursor:grabbing; }
.adm-sort-item.is-busy{ opacity:.4; }
/* while a drag is on, nothing else on the page selects or reacts */
body.adm-dragging{ user-select:none; -webkit-user-select:none; cursor:grabbing; }
body.adm-dragging .adm-sort-item:not(.is-dragging){ opacity:.75; }

/* what has been taken off the website: plainly not live */
.adm-sort-off .adm-sort-pic{ cursor:default; }
.adm-sort-off .adm-sort-pic img{ filter:grayscale(1); opacity:.6; }

@media (max-width:700px){
  .adm-sort{ grid-template-columns:repeat(2, minmax(0, 1fr)); }
  .adm-sort-del{ margin-left:0; }
}

/* the words for one photograph */
.adm-dialog{
  width:min(34rem, calc(100vw - 2rem)); max-height:calc(100dvh - 2rem); overflow:auto;
  padding:var(--s4); border:1px solid var(--line-dark); border-radius:var(--radius);
  background:var(--ink-2); color:var(--on-dark);
}
.adm-dialog::backdrop{ background:rgba(8,12,18,.75); }
.adm-dialog #wordsBody{ display:grid; gap:.9rem; }
.adm-dialog .field{ margin:0; }
.adm-dialog-pic{ display:block; width:100%; max-height:14rem; object-fit:contain; background:#000; border-radius:3px; }

/* a gallery page waiting to hear how Frank has arranged it (main.js) */
.ggrid{ transition:opacity .3s var(--ease); }
.ggrid.is-pending{ opacity:0; }
.ggrid-empty{ color:var(--on-dark-mute); max-width:52ch; }
/* the short explanations over the manager: a note, not a headline */
.adm-panel > p.help, #removedWrap > p.help{ font-size:.92rem; line-height:1.6; color:var(--on-dark-mute); }

/* the gallery switcher: above everything, and plainly not a form field */
.adm-pick{
  margin-top:var(--s4); padding:var(--s3);
  border:1px solid var(--line-dark); border-radius:var(--radius); background:var(--ink-2);
}
.adm-pick-label{
  margin:0 0 .8rem; font-size:.82rem; font-weight:600; letter-spacing:.1em;
  text-transform:uppercase; color:var(--on-dark);
}
.adm-pills{ display:flex; flex-wrap:wrap; gap:.5rem; }
.adm-pill{
  appearance:none; cursor:pointer; min-height:44px; padding:.55rem 1rem;
  border:1px solid rgba(242,239,233,.38); border-radius:999px; background:none;
  color:var(--on-dark); font:inherit; font-size:.92rem; font-weight:500;
  transition:background .15s var(--ease), border-color .15s var(--ease);
}
.adm-pill:hover{ border-color:var(--on-dark); background:rgba(242,239,233,.08); }
.adm-pill[aria-pressed="true"]{
  background:var(--accent-solid); border-color:var(--accent-solid); color:var(--accent-ink); font-weight:600;
}
.adm-pill:focus-visible{ outline:2px solid var(--accent-hi); outline-offset:2px; }
.up-controls-one{ grid-template-columns:1fr; }

/* the move arrows: big enough to read and to hit with a thumb */
.adm-sort-actions .adm-arrow{
  min-width:46px; min-height:42px; padding:.2rem .7rem;
  font-size:1.35rem; line-height:1; letter-spacing:0; font-weight:400;
}
.adm-sort-actions .btn{ min-height:42px; }

/* the upload draft: its buttons, and a Remove on each waiting photograph */
.up-actions{ display:flex; gap:.7rem; flex-wrap:wrap; align-items:center; margin-top:var(--s3); }
.up-actions .btn:disabled{ opacity:.4; cursor:not-allowed; transform:none; }
.up-row{ grid-template-columns:56px minmax(0,1fr) auto auto; }
.up-row-x[hidden]{ display:none; }
@media (max-width:700px){
  .up-row{ grid-template-columns:56px minmax(0,1fr) auto; }
  .up-row-state{ grid-column:2; }
  .up-row-x{ grid-column:3; grid-row:1 / span 2; }
}
