/* VisualIQ V2 preview */

:root{
  --ground:#F2F4F5; --surface:#FFFFFF; --surface-2:#E9EDEE;
  --ink:#14181A; --ink-2:#3B4548; --muted:#657174;
  --line:#D6DCDD; --line-2:#B4BEC0;
  --accent:#0E5E63; --accent-soft:#DCEBEB; --accent-ink:#0A4448;
  --good:#1F6849; --good-soft:#DDEDE5;
  --warn:#8A5A10; --warn-soft:#F6EBD8;
  --crit:#9B2F27; --crit-soft:#F6E2E0;
  --radius:6px;
  --shadow:0 1px 2px rgba(20,24,26,.05), 0 10px 30px -20px rgba(20,24,26,.35);
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --ground:#0E1113; --surface:#161A1D; --surface-2:#1E2427;
    --ink:#E4E8E9; --ink-2:#B4BEC0; --muted:#8A9699;
    --line:#2A3134; --line-2:#3C4548;
    --accent:#4FA8AC; --accent-soft:#12302F; --accent-ink:#8FCFD1;
    --good:#5FB48D; --good-soft:#132B21;
    --warn:#D4A45A; --warn-soft:#33280F;
    --crit:#DB7A70; --crit-soft:#3A201D;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 10px 30px -20px rgba(0,0,0,.9);
  }
}
:root[data-theme="dark"]{
  --ground:#0E1113; --surface:#161A1D; --surface-2:#1E2427;
  --ink:#E4E8E9; --ink-2:#B4BEC0; --muted:#8A9699;
  --line:#2A3134; --line-2:#3C4548;
  --accent:#4FA8AC; --accent-soft:#12302F; --accent-ink:#8FCFD1;
  --good:#5FB48D; --good-soft:#132B21;
  --warn:#D4A45A; --warn-soft:#33280F;
  --crit:#DB7A70; --crit-soft:#3A201D;
}

*{box-sizing:border-box}
html,body{margin:0;padding:0}

/* hidden means hidden.
   The browser's own rule for [hidden] is display:none at the weakest possible
   weight, so any class that sets a display — .modal{display:grid}, .mini
   {display:inline-block} — silently beats it and the thing stays on screen.
   That has now been found three times, twice by a browser test and once on
   the site, so it is settled here once for everything rather than patched
   per class. Anything that wants to show a hidden element removes the
   attribute, which is what every script here already does. */
[hidden]{display:none !important}
body{
  background:var(--ground); color:var(--ink);
  font:15px/1.55 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  -webkit-font-smoothing:antialiased;
}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible{
  outline:2px solid var(--accent); outline-offset:2px; border-radius:3px;
}
code,.mono{font-family:ui-monospace,"Cascadia Mono",Consolas,monospace}
.tiny{font-size:11.5px}
.small{font-size:13px}
.muted{color:var(--muted)}
.break{word-break:break-all}
.num{text-align:right;font-variant-numeric:tabular-nums}

/* ---------- top bar ---------- */
.topbar{
  display:flex;align-items:center;gap:22px;flex-wrap:wrap;
  padding:10px 20px;background:var(--surface);border-bottom:1px solid var(--line);
  position:sticky;top:0;z-index:40;
}
.brand{display:flex;align-items:center;gap:10px;color:inherit}
.brand:hover{text-decoration:none}
.brand-mark{
  width:26px;height:26px;border-radius:5px;flex:none;
  background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 45%,#000));
}
.brand-text strong{display:block;font-size:14px;line-height:1.2}
.brand-text small{display:block;font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}
.mainnav{display:flex;gap:2px;flex-wrap:wrap}
.mainnav a{
  padding:6px 11px;border-radius:5px;color:var(--ink-2);font-size:13.5px;
}
.mainnav a:hover{background:var(--surface-2);text-decoration:none}
.mainnav a.on{background:var(--accent-soft);color:var(--accent-ink);font-weight:600}
.quicksearch{margin-left:auto}
.quicksearch input{
  width:280px;max-width:40vw;padding:7px 11px;border:1px solid var(--line);
  border-radius:5px;background:var(--ground);color:var(--ink);font-size:13.5px;
}
.who-name{font-weight:600}
.who-role{color:var(--muted)}
.signout{color:var(--muted)}

/* ---------- layout ---------- */
.page{min-height:60vh}
.wrap{max-width:1320px;margin:0 auto;padding:26px 20px 60px}
.split{display:grid;grid-template-columns:262px minmax(0,1fr);gap:0;align-items:start}
.side{
  border-right:1px solid var(--line);background:var(--surface);
  padding:20px 14px 40px;min-height:calc(100vh - 52px);position:sticky;top:52px;
  max-height:calc(100vh - 52px);overflow:auto;
}
.side-h{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin:0 0 12px 8px}
.main{padding:26px 24px 60px;min-width:0}

h1,h2,h3{line-height:1.2;text-wrap:balance}
.h-page{font-size:24px;margin:0 0 4px;letter-spacing:-.015em}
.h-sec{font-size:17px;margin:34px 0 12px;letter-spacing:-.01em}
/* The line under a page heading.
   It used to be capped at 70ch, which on a wide screen broke a one-sentence
   introduction after its second-to-last word — "…and how to reach" / "them."
   A measure is worth having for paragraphs somebody reads; these are labels,
   and they read better whole. Wide enough for a sentence, and balanced so that
   when one does wrap it wraps evenly rather than leaving an orphan. */
.lede{color:var(--ink-2);max-width:none;margin:6px 0 18px;text-wrap:balance}

/* ---------- tree ---------- */
.tree{list-style:none;margin:0;padding:0 0 0 12px}
.tree-root{padding-left:0}
.tree a{
  display:flex;align-items:center;gap:7px;padding:4px 8px;border-radius:5px;
  color:var(--ink-2);font-size:13.5px;
}
.tree a:hover{background:var(--surface-2);text-decoration:none}
.tree li.on>a{background:var(--accent-soft);color:var(--accent-ink);font-weight:600}
.t-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.t-count{margin-left:auto;font-size:11px;color:var(--muted);font-weight:400;font-variant-numeric:tabular-nums}
.lock{color:var(--warn);font-size:8px;font-style:normal}
.ico{width:13px;height:13px;flex:none;border-radius:2px;display:inline-block}
.ico-folder{background:var(--line-2)}
.ico-album{background:var(--accent);opacity:.65;border-radius:50%}

/* ---------- headers / controls ---------- */
.listhead{display:flex;justify-content:space-between;align-items:flex-end;gap:20px;flex-wrap:wrap;margin-bottom:16px}
.sorter{display:flex;align-items:center;gap:12px}
.sorter select,.bigsearch input,.bigsearch button{
  padding:7px 10px;border:1px solid var(--line);border-radius:5px;
  background:var(--surface);color:var(--ink);font-size:13.5px;
}
.chk{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--muted)}
.crumbs{font-size:12.5px;color:var(--muted);margin-bottom:14px;display:flex;gap:6px;flex-wrap:wrap}
.crumbs span{color:var(--line-2)}

/* ---------- grid ---------- */
.grid{
  display:grid;gap:12px;
  grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
}
.tile{
  display:block;position:relative;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);overflow:hidden;color:inherit;
}
.tile:hover{text-decoration:none;border-color:var(--accent);box-shadow:var(--shadow)}
.tile-img{
  display:block;width:100%;aspect-ratio:4/3;
  background:var(--surface-2) center/cover no-repeat;
}
.tile-meta{display:block;padding:8px 10px 10px}
.tile-name{
  display:block;font-size:13px;font-weight:500;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.tile-sub{display:block;font-size:11.5px;color:var(--muted);margin-top:2px}
.tile-badge{
  position:absolute;top:7px;left:7px;background:rgba(0,0,0,.68);color:#fff;
  font-size:10px;letter-spacing:.05em;text-transform:uppercase;padding:2px 6px;border-radius:3px;
}
.tile-badge.private{left:auto;right:7px;background:var(--warn)}
.tile-swatch{position:absolute;top:7px;right:7px;width:20px;height:20px;border-radius:4px;border:2px solid #fff}
.subfolders{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:18px}
.subfolder{
  display:flex;align-items:center;gap:8px;padding:7px 12px;border:1px solid var(--line);
  border-radius:20px;background:var(--surface);font-size:13px;color:var(--ink-2);
}
.subfolder:hover{border-color:var(--accent);text-decoration:none}
.subfolder b{color:var(--muted);font-weight:500;font-size:11.5px}

.empty{padding:40px 0;color:var(--muted);text-align:center}
.pager{display:flex;align-items:center;justify-content:center;gap:22px;margin:28px 0 0;font-size:13.5px}
.pager .off{color:var(--line-2)}
.pager-mid{color:var(--muted)}

/* ---------- cards ---------- */
.card{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  padding:16px 18px;margin-bottom:16px;
}
.card h2{font-size:14px;margin:0 0 12px;letter-spacing:-.005em}

/* A panel heading with its one control on the same line, pushed right: Versions
   and "Edit a copy", Notes and "Add", "Delete this asset" and its button. One
   rule rather than three so the three panels cannot drift apart — which is
   what they had done. The heading keeps the margin .card h2 gives it, so the
   row sits exactly where a plain heading would. */
.panel-head{display:flex;align-items:center;justify-content:space-between;
  gap:16px;margin:0 0 12px}
.panel-head h2{margin:0}
.panel-head .btn{flex:none}
.card.subtle{background:var(--surface-2)}
.cols-2{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:16px;margin:20px 0}

.stat-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;margin:18px 0}
.stat{background:var(--surface);padding:14px 16px}
.stat b{display:block;font-size:23px;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.stat span{display:block;font-size:11.5px;color:var(--muted);margin-top:2px}

.bar{display:flex;height:12px;border-radius:6px;overflow:hidden;background:var(--surface-2);margin:6px 0 12px}
.seg.good{background:var(--good)} .seg.warn{background:var(--warn)} .seg.crit{background:var(--crit)}
.legend{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:16px;font-size:13px}
.legend b{font-variant-numeric:tabular-nums}
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;margin-right:6px}
.dot.good{background:var(--good)} .dot.warn{background:var(--warn)} .dot.crit{background:var(--crit)}

.feed{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:9px}
.feed li{display:grid;grid-template-columns:auto 1fr auto;gap:9px;align-items:baseline;font-size:13px}
.feed-who{font-weight:600}
.feed-what{color:var(--ink-2);overflow:hidden;text-overflow:ellipsis}
.feed-when{color:var(--muted);font-size:11.5px;white-space:nowrap}

/* ---------- tags ---------- */
.tagcloud{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0}
.tag{
  display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:14px;
  background:var(--surface);border:1px solid var(--line);font-size:12.5px;color:var(--ink-2);
}
.tag:hover{border-color:var(--accent);text-decoration:none}
.tag b{color:var(--muted);font-weight:500;font-size:11px}
.tag.human{background:var(--accent-soft);border-color:var(--accent);color:var(--accent-ink)}
.tag.machine{border-style:dashed}
.tagcloud.tight .tag{padding:3px 8px;font-size:12px}

/* ---------- asset page ---------- */
.asset-page{max-width:1420px}
/* The picture column, and the stage inside it: the picture with its controls
   ON it rather than stacked around it. */
.asset-view{min-width:0}
.stage{position:relative;margin:0;display:block}

/* Google Maps injects its own <img> elements — tiles, the marker pin, the
   logo. The rule below used to catch every one of them and stretch it to the
   width of the column, which is why the marker arrived as what looked like a
   thumbnail of the photograph dropped on the map. Scoped to the picture. */
/* The picture itself. It is no longer wrapped in a link — the > a > form is
   kept because an album's share page still draws one — and either way this
   must not catch the images Google Maps injects, which is what the note
   below is about. */
.stage > img,.stage > a > img,.stage > video{
  width:100%;height:auto;border-radius:var(--radius);border:1px solid var(--line);
  background:var(--surface-2);display:block;
}
/* And this is all that is left of the counter-rule.
   It used to force width:auto;height:auto on every image inside the map as
   well — which was the reason the map came up grey on a phone with only the
   pin on it. Maps sizes its raster tiles with width and height ATTRIBUTES, a
   stylesheet beats an attribute, and on a high-density screen the tile's own
   pixel size is not its layout size: every tile collapsed and all that was
   left was the marker, which carries inline styles and so survived.
   max-width is the only thing worth defending against here. */
#map img{max-width:none}
.asset-layout{display:grid;grid-template-columns:minmax(0,1fr) 380px;gap:24px;margin-top:16px;align-items:start}

.doc-frame{padding:60px 20px;text-align:center;background:var(--surface-2);border-radius:var(--radius);border:1px solid var(--line)}
.btn{
  display:inline-block;padding:7px 14px;border-radius:5px;background:var(--accent);
  color:#fff;font-size:13.5px;border:1px solid var(--accent);
  font:inherit;font-size:13.5px;cursor:pointer;   /* also used on <button> */
}
.btn:hover{text-decoration:none;filter:brightness(1.08)}
.btn.ghost{background:transparent;color:var(--accent)}
.palette{display:flex;gap:0;margin:0 0 8px;border-radius:5px;overflow:hidden;border:1px solid var(--line)}
.sw{flex:1;height:34px;display:block}
.sw:hover{outline:2px solid var(--ink);outline-offset:-2px}

.kv{display:grid;grid-template-columns:auto 1fr;gap:5px 14px;margin:0;font-size:13px}
.kv dt{color:var(--muted)}
.kv dd{margin:0;overflow-wrap:anywhere}
.coords{font-family:ui-monospace,monospace;font-size:12.5px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.map{width:100%;height:220px;border-radius:5px;border:1px solid var(--line);margin-top:10px;background:var(--surface-2)}
.bigmap{width:100%;height:min(70vh,720px);border-radius:var(--radius);border:1px solid var(--line);background:var(--surface-2)}

/* What a pin shows when it is clicked. Inside Google's own info window, which
   brings its own white box and its own text colour — so this sets the layout
   and the picture and leaves the rest of it alone.

   The picture is sized here in CSS, never by touching the img's own width and
   height: the map draws its tiles with those attributes, and a stylesheet that
   overrides them is what turned the whole map grey on a phone once. */
.mappop{display:grid;gap:5px;max-width:230px;font:13px/1.4 system-ui,-apple-system,sans-serif;color:#1b2124}
.mappop img{display:block;width:100%;max-width:230px;height:auto;border-radius:4px;background:#e7ecec}
.mappop-sub{color:#5a6a6c;font-size:12px;font-style:normal}
.mappop a{color:#0E5E63;font-weight:600}

.pill{
  display:inline-block;padding:2px 8px;border-radius:11px;font-size:11px;
  background:var(--surface-2);color:var(--muted);border:1px solid var(--line);
  vertical-align:middle;font-weight:500;
}
.pill.good{background:var(--good-soft);color:var(--good);border-color:var(--good)}
.pill.warn{background:var(--warn-soft);color:var(--warn);border-color:var(--warn)}

/* ---------- tables ---------- */
.tablewrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--radius);background:var(--surface);margin:14px 0}
table{border-collapse:collapse;width:100%;font-size:13.5px;min-width:560px}
th,td{text-align:left;padding:9px 13px;border-bottom:1px solid var(--line)}
th{font-size:11px;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
   font-weight:500;background:var(--surface-2)}
tr:last-child td{border-bottom:none}

/* ---------- quality ---------- */
.quality-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px;margin:20px 0}
.qcard{
  display:block;padding:16px 18px;border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);color:inherit;
}
.qcard:hover{text-decoration:none;border-color:var(--accent);box-shadow:var(--shadow)}
.qcard b{display:block;font-size:27px;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.qcard span{display:block;font-size:13px;margin-top:2px}
.qcard small{display:block;font-size:11.5px;color:var(--muted);margin-top:7px;line-height:1.45}
.qcard.warn b{color:var(--warn)} .qcard.crit b{color:var(--crit)}

.colour-head{display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin:14px 0 6px}
.colour-chip{
  display:inline-flex;align-items:center;justify-content:center;width:112px;height:64px;
  border-radius:var(--radius);font-family:ui-monospace,monospace;font-size:12.5px;
  border:1px solid var(--line);
}
.colour-head .sorter{margin-left:auto}

/* ---------- login ---------- */
body.plain{display:grid;place-items:center;min-height:100vh;padding:24px}
.login-card{
  width:100%;max-width:388px;background:var(--surface);border:1px solid var(--line);
  border-radius:10px;padding:34px 32px;box-shadow:var(--shadow);
}
.login-mark{
  width:40px;height:40px;border-radius:9px;margin-bottom:16px;
  background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 40%,#000));
}
.login-card h1{font-size:25px;margin:0 0 6px;letter-spacing:-.02em}
.login-sub{color:var(--muted);font-size:13.5px;margin:0 0 22px;line-height:1.5}
.login-card label{display:block;font-size:12px;color:var(--muted);margin:14px 0 5px}
.login-card input{
  width:100%;padding:9px 11px;border:1px solid var(--line);border-radius:5px;
  background:var(--ground);color:var(--ink);font-size:14px;
}
.login-card button{
  width:100%;margin-top:20px;padding:10px;border:0;border-radius:5px;
  background:var(--accent);color:#fff;font-size:14px;font-weight:600;cursor:pointer;
}
.login-card button:hover{filter:brightness(1.08)}
.login-foot{margin:20px 0 0;font-size:11.5px;color:var(--muted);text-align:center}

/* The mark under the sign-in form, where the word used to be. Sized by the
   layout and capped against the card, so it cannot overflow a phone. */
.login-mark-img{display:block;margin:0 auto;height:40px;width:auto;
  max-width:min(266px,85%);object-fit:contain}
@media (max-width:600px){ .login-mark-img{height:35px} }
.alert{
  background:var(--crit-soft);border:1px solid var(--crit);color:var(--crit);
  padding:9px 12px;border-radius:5px;font-size:13px;margin-bottom:6px;
}
.notice{max-width:620px;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);padding:26px 28px}
.notice pre{white-space:pre-wrap;font-size:12.5px;color:var(--ink-2);overflow-x:auto}

/* ---------- footer ---------- */
/* Three parts, so the mark sits in the middle of the PAGE rather than in the
   middle of what is left over: two equal flexible sides and the picture
   between them. Every size here is the layout's, never the file's, so a new
   logo cannot change the shape of the footer. */
.foot{
  display:flex;align-items:center;gap:20px;flex-wrap:wrap;
  padding:16px 22px;border-top:1px solid var(--line);color:var(--muted);font-size:11.5px;
}
.foot-side{flex:1 1 0;min-width:0}
.foot-right{text-align:right}
.foot-mark{flex:0 0 auto;display:flex;justify-content:center;line-height:0}
.foot-mark img{height:48px;width:auto;max-width:400px;object-fit:contain;display:block}

/* Stacked and centred on a narrow screen — which is why the sides are flexible
   cells rather than one thing pushed left with a margin. */
@media (max-width:600px){
  .foot{flex-direction:column;text-align:center;gap:10px}
  .foot-side,.foot-right{flex:none;text-align:center}
  .foot-mark img{height:40px;max-width:70vw}
}

@media (max-width:1000px){
  .split{grid-template-columns:1fr}
  .side{position:static;max-height:none;min-height:0;border-right:0;border-bottom:1px solid var(--line)}
  .asset-layout{grid-template-columns:1fr}
  .quicksearch{order:10;margin-left:0;width:100%}
  .quicksearch input{width:100%;max-width:none}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* ---------- uploader ---------- */
.uploader{margin:22px 0}
.up-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:14px}
.up-controls label{font-size:12.5px;color:var(--muted)}
.up-controls select{padding:7px 10px;border:1px solid var(--line);border-radius:5px;
  background:var(--surface);color:var(--ink);font-size:13.5px;max-width:420px}
.dropzone{
  border:2px dashed var(--line-2);border-radius:8px;background:var(--surface);
  padding:44px 20px;text-align:center;cursor:pointer;transition:border-color .12s,background .12s;
}
.dropzone:hover,.dropzone:focus-visible{border-color:var(--accent);background:var(--surface-2)}
.dropzone.over{border-color:var(--accent);background:var(--accent-soft)}
.dz-inner strong{display:block;font-size:15px;margin-bottom:4px}
.dz-inner span{font-size:13px;color:var(--muted)}

.up-summary{margin:16px 0 8px}
.up-bar{height:6px;border-radius:3px;background:var(--surface-2);overflow:hidden}
.up-bar span{display:block;height:100%;width:0;background:var(--accent);transition:width .25s}
.up-counts{font-size:12.5px;color:var(--muted);margin-top:6px;font-variant-numeric:tabular-nums}

.up-list{list-style:none;margin:14px 0 0;padding:0;display:flex;flex-direction:column;gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:5px;overflow:hidden}
.up-list:empty{display:none}
.up-item{
  /* Five columns, and the last one is the cross. A grid with four tracks puts a
     fifth child on a row of its own, underneath the name, where it is neither
     use nor ornament — which is exactly what happened when cancelling was
     added. */
  display:grid;grid-template-columns:minmax(0,1fr) 80px 140px 190px 24px;
  gap:12px;align-items:center;
  padding:8px 12px;background:var(--surface);font-size:13px;
}
.up-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.up-size{color:var(--muted);font-size:11.5px;text-align:right;font-variant-numeric:tabular-nums}
.up-track{height:5px;border-radius:3px;background:var(--surface-2);overflow:hidden}
.up-fill{display:block;height:100%;width:0;background:var(--line-2);transition:width .2s}
.up-state{font-size:11.5px;color:var(--muted);text-align:right}
.up-item.is-busy   .up-fill{background:var(--accent)}
.up-item.is-queued .up-fill{background:var(--accent)}
.up-item.is-done   .up-fill{background:var(--good)}
.up-item.is-done   .up-state{color:var(--good)}
.up-item.is-dupe   .up-fill{background:var(--warn)}
.up-item.is-dupe   .up-state{color:var(--warn)}
.up-item.is-failed .up-fill{background:var(--crit)}
.up-item.is-failed .up-state{color:var(--crit)}

.callout{border:1px solid var(--line);border-left:3px solid var(--accent);background:var(--surface);
  padding:12px 16px;border-radius:0 5px 5px 0;margin:16px 0;font-size:13.5px}
.callout.warn{border-left-color:var(--warn);background:var(--warn-soft)}
.callout.crit{border-left-color:var(--crit);background:var(--crit-soft)}
/* A row, so the drawing sits against the words rather than above them. */
.flash{padding:10px 16px;border-radius:5px;margin:0 0 18px;font-size:13.5px;
  display:flex;align-items:center;gap:9px}
.flash-ico{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.flash.ok{background:var(--good-soft);color:var(--good);border:1px solid var(--good)}
.flash.warn{background:var(--warn-soft);color:var(--warn);border:1px solid var(--warn)}
details.card summary{cursor:pointer;font-size:13.5px}
details.card pre{background:var(--surface-2);padding:10px 12px;border-radius:4px;overflow-x:auto}

/* ============================================================ search v2 == */

.hint{margin:10px 0 14px;border:1px solid var(--line);border-radius:var(--radius);background:var(--surface)}
.hint summary{cursor:pointer;padding:8px 12px;color:var(--muted);font-size:13px;user-select:none}
.hint summary:hover{color:var(--ink)}
.hint-body{padding:4px 14px 12px;border-top:1px solid var(--line)}
.hint-body dl{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;margin:10px 0}
.hint-body dt{margin:0}
.hint-body dd{margin:0;color:var(--muted);font-size:13px}
.hint-body code{background:var(--surface-2);padding:1px 6px;border-radius:4px;font-size:12.5px}

.savedbar{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:0 0 14px}
.chip{
  display:inline-flex;gap:6px;align-items:center;padding:3px 10px;font-size:13px;
  border:1px solid var(--line);border-radius:999px;background:var(--surface);
  color:var(--ink);text-decoration:none;
}
.chip:hover{border-color:var(--accent);color:var(--accent)}
.chip b{font-weight:600;font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}

.results{display:grid;grid-template-columns:210px 1fr;gap:22px;align-items:start}
@media (max-width:900px){.results{grid-template-columns:1fr}}

.facets{position:sticky;top:14px;display:flex;flex-direction:column;gap:16px}
.facet h3{
  margin:0 0 6px;font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);
}
.facet ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.facet li a{
  display:flex;justify-content:space-between;gap:8px;padding:4px 7px;border-radius:4px;
  color:var(--ink);text-decoration:none;font-size:13.5px;
}
.facet li a:hover{background:var(--surface-2)}
.facet li a span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.facet li a b{font-weight:500;color:var(--muted);font-variant-numeric:tabular-nums}
.facet-off{
  display:inline-block;margin:0 4px 4px 0;padding:3px 9px;font-size:12.5px;
  border-radius:999px;background:var(--accent-soft);color:var(--accent-ink);text-decoration:none;
}

.hits-head{
  display:flex;flex-wrap:wrap;gap:10px;justify-content:space-between;align-items:center;
  margin:0 0 12px;
}
.hits-tools{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.sortform,.saveform{display:flex;gap:6px;align-items:center;margin:0}
.saveform input[type=text]{
  width:170px;padding:5px 9px;border:1px solid var(--line);border-radius:4px;
  background:var(--surface);color:var(--ink);font:inherit;font-size:13px;
}
.sortform select{
  padding:5px 8px;border:1px solid var(--line);border-radius:4px;
  background:var(--surface);color:var(--ink);font:inherit;font-size:13px;
}

/* ---- selection ---- */
/* Every wrapped tile is a positioning context, not only in a .selectable grid:
   the basket draws one without that class and hangs a × in the corner. */
.tile-wrap{position:relative}
.grid.selectable .tile-wrap{position:relative}
.tile-pick{
  position:absolute;top:7px;left:7px;z-index:2;width:22px;height:22px;
  display:grid;place-items:center;cursor:pointer;
}
.tile-pick input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.tile-pick span{
  width:18px;height:18px;border-radius:4px;border:1.5px solid #fff;
  background:rgba(20,24,26,.35);box-shadow:0 1px 3px rgba(0,0,0,.4);transition:background .12s;
}
.tile-pick input:checked + span{background:var(--accent);border-color:#fff}
.tile-pick input:checked + span::after{
  content:"";display:block;width:5px;height:9px;margin:1px auto 0;
  border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg);
}
.tile-pick input:focus-visible + span{outline:2px solid var(--accent);outline-offset:2px}
.tile-wrap.is-picked .tile{outline:2px solid var(--accent);outline-offset:-2px;border-radius:var(--radius)}

/* Take this one out. Opposite corner from the tick, so "choose several" and
   "drop that one" are never the same target, and quiet until the pointer is on
   the tile — it is a destructive control sitting on a photograph. */
.tile-drop{
  position:absolute;top:7px;right:7px;z-index:3;width:24px;height:24px;padding:0;
  display:grid;place-items:center;cursor:pointer;border-radius:6px;
  color:#fff;background:rgba(12,16,18,.5);border:1px solid rgba(255,255,255,.3);
  opacity:0;transition:opacity .15s,background .15s;
}
.tile-drop svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round}
.tile-wrap:hover .tile-drop,.tile-drop:focus-visible{opacity:1}
.tile-drop:hover{background:var(--crit);border-color:var(--crit)}

/* No hover on a touch screen, so it is simply there. */
@media (hover:none){ .tile-drop{opacity:.85} }

/* A form that exists only to carry a token and an action; its buttons live in
   the grid above it, attached through the form= attribute. */
.hidden-form{display:none}

.bulkbar{
  position:sticky;top:8px;z-index:5;display:flex;flex-wrap:wrap;gap:8px;align-items:center;
  padding:8px 12px;margin:0 0 10px;border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);box-shadow:var(--shadow);
}
.bulk-count{font-weight:600;font-variant-numeric:tabular-nums}
.bulk-sep{flex:1}
.bulk-btn{
  padding:5px 11px;font:inherit;font-size:13px;cursor:pointer;
  border:1px solid var(--line);border-radius:4px;background:var(--surface);color:var(--ink);
}
.bulk-btn:hover{border-color:var(--accent);color:var(--accent)}
.bulk-btn.danger:hover{border-color:var(--crit);color:var(--crit)}
.linkish{
  border:0;background:none;padding:0;font:inherit;font-size:13px;
  color:var(--accent);cursor:pointer;text-decoration:underline;
}
.bulk-panel{
  display:flex;flex-wrap:wrap;gap:8px;align-items:center;
  padding:10px 12px;margin:0 0 10px;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--surface-2);
}
.bulk-panel input[type=text],.bulk-panel select{
  padding:6px 10px;border:1px solid var(--line);border-radius:4px;
  background:var(--surface);color:var(--ink);font:inherit;font-size:13.5px;
}
.bulk-panel input[name=tags]{min-width:260px;flex:1}

/* ============================================================= admin ===== */
/* Nothing here redefines an existing class. The admin screens use the app's
   own .wrap / .card / .cols-2 / .stat-row / .feed vocabulary so they look like
   the rest of the application rather than like a second application. */

.crumbs a{color:var(--muted)}
.crumbs a:hover{color:var(--accent)}

/* a .stat that is also a link */
a.stat{text-decoration:none;color:var(--ink);transition:background .12s}
a.stat:hover{background:var(--surface-2)}
.stat.bad b{color:var(--crit)}
.stat.warn b{color:var(--warn)}

/* single-value progress bar — .bar is the dashboard's segmented one */
.meter{display:block;height:7px;border-radius:4px;background:var(--surface-2);overflow:hidden;margin:6px 0}
.meter>span{display:block;height:100%;background:var(--accent)}

/* .pill, .pill.good and .pill.warn are defined above — this only adds the third */
.pill.bad{background:var(--crit-soft);color:var(--crit);border-color:var(--crit)}

tr.is-dim td{opacity:.55}
table.tight th,table.tight td{padding:5px 8px;font-size:13.5px}
th.num,td.num{text-align:right;font-variant-numeric:tabular-nums}

/* .kv is a <dl> defined above; these only add to it */
.kv dd{font-variant-numeric:tabular-nums}
.kv dd b{font-weight:600}

.formgrid{display:grid;grid-template-columns:1fr 1fr;gap:12px 16px;margin:6px 0 4px}
@media (max-width:700px){.formgrid{grid-template-columns:1fr}}
.formgrid-wide{grid-column:1 / -1}
.formgrid label{display:flex;flex-direction:column;gap:4px;font-size:12.5px;color:var(--muted)}
.formgrid input,.formgrid select,.formgrid textarea{
  padding:7px 10px;border:1px solid var(--line);border-radius:4px;
  background:var(--surface);color:var(--ink);font:inherit;font-size:14px;
}
.formgrid input:disabled,.formgrid textarea:disabled,.formgrid select:disabled{
  background:var(--surface-2);color:var(--muted);cursor:not-allowed;
}
.formgrid textarea{resize:vertical}
.inline-check{flex-direction:row!important;align-items:center;gap:6px;color:var(--ink)!important;font-size:13.5px!important}

.stack{display:flex;flex-direction:column;gap:7px;align-items:stretch;margin:0 0 10px}
.stack select,.stack input{
  padding:6px 9px;border:1px solid var(--line);border-radius:4px;
  background:var(--surface);color:var(--ink);font:inherit;font-size:13.5px;
}
form.inline{display:inline-flex;gap:4px;align-items:center;margin:0}

/* .btn and .btn.ghost are defined above — these only add variants */
.btn.danger{background:var(--crit);border-color:var(--crit)}
.btn:disabled{opacity:.4;cursor:default;filter:none}

.mini{
  display:inline-block;padding:3px 8px;font:inherit;font-size:12px;cursor:pointer;
  border:1px solid var(--line);border-radius:4px;background:var(--surface);
  color:var(--ink);text-decoration:none;
}
.mini:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}
.mini:disabled{opacity:.35;cursor:default}

.danger-zone{
  margin-top:14px;padding:12px;border:1px solid var(--crit);border-radius:var(--radius);
  background:var(--crit-soft);display:flex;flex-wrap:wrap;gap:10px;align-items:center;
}

.h-min{margin:16px 0 4px;font-size:12px;text-transform:uppercase;letter-spacing:.07em;color:var(--muted)}

/* A heading INSIDE a panel that reads as a panel heading: same size, same
   colour, same sentence case as the <h2> above it, with room over it because
   it is coming after something rather than starting the card. Used for
   Histogram, which sits at the foot of Capture metadata and was in the small
   grey uppercase used for form sub-headings — a different kind of label
   entirely. */
.h-panel{margin:20px 0 10px;font-size:14px;font-weight:bold;letter-spacing:-.005em;
  text-transform:none;color:var(--ink)}
.toolrow{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:0 0 14px}
.toolrow form{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:0}
.toolrow input[type=search],.toolrow input[type=text],.toolrow input[type=date],.toolrow select{
  padding:6px 10px;border:1px solid var(--line);border-radius:4px;
  background:var(--surface);color:var(--ink);font:inherit;font-size:13.5px;
}
.toolrow .spacer{flex:1}

.actingbar{
  display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:space-between;
  padding:8px 20px;background:var(--warn-soft);color:var(--warn);
  border-bottom:1px solid var(--warn);font-size:13px;
}
.actingbar strong{color:var(--warn)}
.actingbar form{margin:0}
.actingbar button{
  padding:4px 10px;font:inherit;font-size:12.5px;cursor:pointer;border-radius:4px;
  border:1px solid var(--warn);background:transparent;color:var(--warn);
}
.actingbar button:hover{background:var(--warn);color:var(--surface)}

.snippet{
  margin:6px 0 10px;padding:9px 11px;border-radius:4px;overflow-x:auto;
  background:var(--surface-2);border:1px solid var(--line);
  font-family:ui-monospace,monospace;font-size:12.5px;line-height:1.5;
}

.listhead-tools{display:flex;align-items:center;gap:10px;margin-left:auto;margin-right:12px}
.listhead-tools .chk{cursor:pointer;user-select:none}

/* ---------- share viewer ----------
   A sealed surface: no navigation, no search, nothing that leads anywhere
   except the photographs the link names. It does not use body.plain because
   that centres a single card, and a gallery needs the width. */
body.share-page{min-height:100vh;padding:0;background:var(--ground)}
.share-wrap{max-width:1180px;margin:0 auto;padding:34px 24px 64px}

.share-head{margin:0 0 26px;padding-bottom:18px;border-bottom:1px solid var(--line)}
.share-head h1{font-size:24px;margin:0 0 6px;letter-spacing:-.02em}
.share-quiet{color:var(--muted);font-size:13px;margin:0}

.share-msg{
  max-width:420px;margin:12vh auto 0;background:var(--surface);
  border:1px solid var(--line);border-radius:10px;padding:32px;
  box-shadow:var(--shadow);text-align:center;
}
.share-msg h1{font-size:21px;margin:0 0 10px;letter-spacing:-.02em}
.share-msg p{color:var(--muted);font-size:13.5px;line-height:1.55;margin:0 0 14px}
.share-msg label{display:block;font-size:12px;color:var(--muted);margin:16px 0 5px;text-align:left}
.share-msg input{
  width:100%;padding:9px 11px;border:1px solid var(--line);border-radius:5px;
  background:var(--ground);color:var(--ink);font-size:14px;
}
.share-alert{
  background:var(--crit-soft);border:1px solid var(--crit);color:var(--crit);
  padding:9px 12px;border-radius:5px;font-size:13px;margin:0 0 12px;
}
.share-btn{
  display:inline-block;margin-top:16px;padding:10px 18px;border:0;border-radius:5px;
  background:var(--accent);color:#fff;font-size:14px;font-weight:600;
  cursor:pointer;text-decoration:none;
}
.share-btn:hover{filter:brightness(1.08)}

/* The share gallery is the library's grid, so it inherits .grid/.tile and the
   justified layout with it. What is left here is only what differs: no
   selection, and a photograph served as an <img> rather than a background,
   because a public page wants lazy loading and real alt text. */
.share-grid{gap:14px}
.share-tile{background:var(--surface)}
.share-tile:hover{border-color:var(--accent)}
img.tile-img{object-fit:cover;background:var(--ground)}

.share-back{margin:0 0 16px;font-size:13px}
.share-back a{color:var(--muted);text-decoration:none}
.share-back a:hover{color:var(--accent)}
.share-figure{margin:0;background:var(--surface);border:1px solid var(--line);border-radius:9px;overflow:hidden}
.share-figure img{display:block;width:100%;height:auto;max-height:76vh;object-fit:contain;background:var(--ground)}
.share-meta{margin-top:22px;max-width:660px}
.share-meta h1{font-size:20px;margin:0 0 8px;letter-spacing:-.02em}
.share-meta p{font-size:13.5px;line-height:1.6;color:var(--muted);margin:0 0 14px}
.share-meta dl{display:grid;grid-template-columns:120px 1fr;gap:6px 16px;margin:0 0 18px;font-size:13px}
.share-meta dt{color:var(--muted)}
.share-meta dd{margin:0}

/* ---------- share panel (album and asset pages) ---------- */
.share-panel .share-made{
  background:var(--ok-soft);border:1px solid var(--ok);border-radius:var(--radius);
  padding:12px 13px;margin-bottom:16px;
}
.share-panel .share-made label{display:block;font-size:12px;color:var(--muted);margin-bottom:5px}
.share-panel .share-made input{
  width:100%;padding:8px 10px;border:1px solid var(--line);border-radius:5px;
  background:var(--ground);color:var(--ink);font-family:ui-monospace,monospace;font-size:12.5px;
}
.share-list{width:100%;border-collapse:collapse;margin-bottom:16px;font-size:12.5px}
.share-list th{
  text-align:left;font-weight:600;color:var(--muted);font-size:11px;
  text-transform:uppercase;letter-spacing:.05em;padding:0 8px 6px 0;
}
.share-list td{padding:7px 8px 7px 0;border-top:1px solid var(--line);vertical-align:top}
/* Share, in a pop-over rather than a panel: the contents alone, stacked, with
   room for the link it makes. Wider than the other two boxes because a share
   row is two fields side by side and a URL is long. */
.share-inner{display:flex;flex-direction:column;gap:14px}
#shareModal .modal-card{width:min(620px,100%)}

.share-form .share-row{display:flex;gap:14px;margin-bottom:12px;flex-wrap:wrap}
.share-form .share-row label{flex:1;min-width:150px;display:block}
/* The caption over a field is a block — but ONLY the caption. This used to
   catch every span inside the label, which put "(for your own reference)" on a
   line of its own underneath the word it belongs to. A span inside the caption
   is part of the sentence, so it stays inline. */
.share-form .share-row label > span{display:block;font-size:12px;color:var(--muted);margin-bottom:5px}
.share-form .share-row label > span span{display:inline;margin:0;font-size:inherit}
.share-form input[type=text],.share-form input[type=number],.share-form select{
  width:100%;padding:8px 10px;border:1px solid var(--line);border-radius:5px;
  background:var(--ground);color:var(--ink);font-size:13.5px;
}
.share-form .share-checks{flex-direction:column;gap:8px}
.share-form .share-checks label{flex:none;min-width:0}
.tickcol{width:28px;padding-right:0}
.copy-link{white-space:nowrap}

/* ---------- settings forms (email, and anything like it) ---------- */
.wrap.narrow{max-width:760px}
.settings-form .card{margin-bottom:18px}
.field-row{display:flex;gap:14px;margin-bottom:14px;flex-wrap:wrap}
.field-row label,.test-form label{display:block}
.field-row label span,.test-form label span{
  display:block;font-size:12px;color:var(--muted);margin-bottom:5px;
}
.field-row .grow,.test-form .grow{flex:1;min-width:190px}
.field-row .narrow-f{width:130px;flex:none}
.field-row input,.field-row select,.test-form input{
  width:100%;padding:8px 10px;border:1px solid var(--line);border-radius:5px;
  background:var(--ground);color:var(--ink);font-size:13.5px;
}
.form-actions{margin:18px 0 26px}
.test-form{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;margin-bottom:18px}
.transcript{
  margin:0 0 18px;border:1px solid var(--line);border-radius:var(--radius);
  background:var(--surface);
}
.transcript summary{padding:9px 13px;cursor:pointer;font-size:13px;color:var(--muted)}
.transcript pre{
  margin:0;padding:12px 14px;border-top:1px solid var(--line);overflow-x:auto;
  font-family:ui-monospace,monospace;font-size:12px;line-height:1.55;color:var(--ink);
}

/* ---------- charts ----------
   Two categorical colours, validated rather than eyeballed: both inside the
   lightness band, above the chroma floor, separated by dE 16.4 under
   deuteranopia against the card. The dark pair is re-derived against the dark
   surface, not lightened from the light one. Text never wears a series colour —
   identity is carried by the mark beside it, plus the legend and tooltips. */
:root{
  --s0:#00919B; --s1:#A65310;
  --chart-grid:#E4E9EA; --chart-axis:#C9D2D3;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --s0:#2FA7B1; --s1:#C47F3C;
    --chart-grid:#232A2D; --chart-axis:#394245;
  }
}
:root[data-theme="dark"]{
  --s0:#2FA7B1; --s1:#C47F3C;
  --chart-grid:#232A2D; --chart-axis:#394245;
}

/* Drawn at a nominal 640px wide and scaled proportionally. Below that the
   card scrolls rather than shrinking the labels into illegibility. */
.chart-wrap{overflow-x:auto}
/* The SVG carries its natural width/height; this caps it at the container and
   lets it shrink proportionally, never grow. Below the min-width the wrapper
   scrolls rather than shrinking the labels into illegibility. */
.chart{max-width:100%;height:auto;display:block;min-width:440px}
.chart-grid{stroke:var(--chart-grid);stroke-width:1}
.chart-axis{stroke:var(--chart-axis);stroke-width:1}
.chart-tick,.chart-xlab{fill:var(--muted);font-size:11px;font-family:inherit}
.chart-limitlab{fill:var(--crit);font-size:11px;font-family:inherit}

.chart-bar{transition:opacity .12s}
.chart-bar.s0{fill:var(--s0)}
.chart-bar.s1{fill:var(--s1)}
.chart:hover .chart-bar{opacity:.45}
.chart .chart-bar:hover{opacity:1}

.chart-line{fill:none;stroke:var(--s0);stroke-width:2;
  stroke-linejoin:round;stroke-linecap:round}
.chart-area{fill:var(--s0);opacity:.12;stroke:none}
/* A 2px surface ring keeps a marker legible where it overlaps the line. */
.chart-dot{fill:var(--s0);stroke:var(--surface);stroke-width:2}
.chart-limit{stroke:var(--crit);stroke-width:1.5;stroke-dasharray:5 4;opacity:.85}

.chart-legend{display:flex;gap:16px;margin-top:10px;font-size:12.5px;color:var(--muted)}
.chart-key{display:inline-flex;align-items:center;gap:6px}
.chart-key i{width:10px;height:10px;border-radius:2px;display:inline-block}
.chart-key i.s0{background:var(--s0)}
.chart-key i.s1{background:var(--s1)}
.chart-empty{color:var(--muted);font-size:13px;margin:14px 0;text-align:center}

/* Ranked horizontal bars — one series, so one hue and no legend. */
.ranked{display:flex;flex-direction:column;gap:7px;margin-top:4px}
.ranked-row{display:grid;grid-template-columns:minmax(90px,1.4fr) 3fr auto;
  gap:10px;align-items:center;font-size:13px}
.ranked-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink-2)}
.ranked-track{background:var(--surface-2);border-radius:3px;height:10px;overflow:hidden}
.ranked-fill{display:block;height:100%;background:var(--s0);border-radius:3px}
.ranked-value{text-align:right;font-variant-numeric:tabular-nums;color:var(--ink-2);white-space:nowrap}
.ranked-value small{display:block;color:var(--muted);font-size:11px}

.report-cols{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media (max-width:860px){ .report-cols{grid-template-columns:1fr} }
.csv{float:right;font-size:11.5px;font-weight:400;text-transform:uppercase;
  letter-spacing:.06em;color:var(--muted)}
.csv:hover{color:var(--accent)}
.stat b small{font-size:13px;font-weight:400;color:var(--muted)}

/* ---------- collections, basket and notes ---------- */
.coll-grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(230px,1fr))}
.coll-card{
  display:block;text-decoration:none;color:inherit;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
}
.coll-card:hover{border-color:var(--accent)}
.coll-cover{
  display:block;height:112px;background:var(--surface-2) center/cover no-repeat;
  border-bottom:1px solid var(--line);
}
.coll-body{display:block;padding:11px 13px}
.coll-body b{display:inline-block;font-size:14px;margin-right:6px}
.coll-desc{display:block;font-size:12.5px;color:var(--ink-2);margin:4px 0 6px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.basket-link{
  display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--muted);
  text-decoration:none;padding:5px 9px;border-radius:5px;white-space:nowrap;
}
.basket-link:hover,.basket-link.on{color:var(--accent);background:var(--accent-soft);text-decoration:none}
.basket-count{
  background:var(--accent);color:#fff;border-radius:9px;padding:1px 6px;
  font-size:11px;font-weight:600;font-variant-numeric:tabular-nums;
}

.notes{list-style:none;margin:0 0 14px;padding:0;display:flex;flex-direction:column;gap:12px}
.note{border-left:2px solid var(--line);padding-left:11px}
.note-head{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}
.note-head b{font-size:13px}
.note-body{font-size:13.5px;line-height:1.55;margin-top:3px;white-space:pre-wrap;word-break:break-word}
.note-tools{display:flex;gap:12px;align-items:center;margin-top:5px}
.note-tools summary{font-size:12px;color:var(--muted);cursor:pointer}
.note-edit textarea,.note-add textarea{
  width:100%;margin:6px 0;padding:8px 10px;border:1px solid var(--line);border-radius:5px;
  background:var(--ground);color:var(--ink);font:inherit;font-size:13.5px;resize:vertical;
}
/* The box to write in. Its Add button is up on the heading line — see
   .panel-head — so this is a box and nothing else. */
.note-add{margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}
.note-add textarea{margin-bottom:0}

/* ---------- my account ---------- */
.who{display:flex;align-items:center;gap:9px;font-size:12.5px;text-decoration:none;
  color:inherit;padding:4px 8px;border-radius:6px}
.who:hover,.who.on{background:var(--surface-2);text-decoration:none}
.who-text{display:flex;flex-direction:column;line-height:1.25}
.avatar-sm{width:28px;height:28px;border-radius:50%;object-fit:cover;flex:none;
  background:var(--surface-2)}
.avatar-lg{width:96px;height:96px;border-radius:50%;object-fit:cover;flex:none;
  background:var(--surface-2);border:1px solid var(--line)}
.avatar-row{display:flex;gap:18px;align-items:flex-start;flex-wrap:wrap}
.tight-grid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
a.stat{text-decoration:none;color:inherit}
a.stat:hover{border-color:var(--accent);text-decoration:none}

/* ---------- justified gallery, lightbox, histogram ---------- */
/*
 * `data-justify` in the markup says this grid MAY be laid out in rows. Nothing
 * below keys off that: the rules wait for the `is-justified` class, which
 * gallery.js adds only once it has measured the container and sized the tiles.
 *
 * That is what makes the fallback real rather than claimed. With no script —
 * or on a phone, where the script hands the layout back — the class is never
 * added, and the gallery is the ordinary auto-fill grid defined above. Keying
 * these off the markup instead would give a browser with JavaScript turned off
 * a flex row of unsized tiles, each shrunk to the width of its caption.
 */
.grid.is-justified{display:flex;flex-wrap:wrap;gap:12px;align-content:flex-start}
.grid.is-justified>*{flex:none}
.grid.is-justified .tile{height:100%;display:flex;flex-direction:column}
.grid.is-justified .tile-img{flex:1;aspect-ratio:auto;min-height:0}
.grid.is-justified .tile-meta{flex:none}

.histogram{
  display:block;width:100%;max-width:512px;height:90px;margin-top:18px;
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--radius);
}

body.lb-on{overflow:hidden}
.lightbox{
  position:fixed;inset:0;z-index:100;background:rgba(8,10,11,.975);
  display:flex;align-items:center;justify-content:center;padding:48px 64px;
}
/* display:flex overrides the browser's own rule for the hidden attribute, so
   without this the closed lightbox stays a full-screen invisible sheet over
   the page and swallows every click. */
.lightbox[hidden]{display:none}
.lb-figure{margin:0;max-width:100%;max-height:100%;display:flex;flex-direction:column;gap:12px}
.lb-figure img{
  max-width:100%;max-height:calc(100vh - 150px);object-fit:contain;
  display:block;margin:0 auto;border-radius:4px;
}
.lb-figure figcaption{
  color:#cfd6d7;font-size:13px;text-align:center;display:flex;gap:16px;
  justify-content:center;align-items:baseline;flex-wrap:wrap;
}
.lb-open{color:#8FCFD1}
.lightbox button{
  position:absolute;background:rgba(255,255,255,.1);color:#fff;border:0;
  cursor:pointer;border-radius:50%;width:44px;height:44px;font-size:26px;line-height:1;
}
.lightbox button:hover{background:rgba(255,255,255,.2)}
.lb-close{top:16px;right:16px;font-size:30px}
.lb-prev{left:14px;top:50%;transform:translateY(-50%)}
.lb-next{right:14px;top:50%;transform:translateY(-50%)}
@media (max-width:700px){
  .lightbox{padding:56px 12px}
  .lb-prev{left:6px} .lb-next{right:6px}
}

/* ---------- the platform console ---------- */
.subnav{
  display:flex;flex-wrap:wrap;gap:4px;margin:0 0 18px;padding-bottom:12px;
  border-bottom:1px solid var(--line);
}
.subnav a{
  padding:5px 11px;border-radius:5px;font-size:13px;color:var(--ink-2);
  border:1px solid transparent;
}
.subnav a:hover{background:var(--surface-2);text-decoration:none;border-color:var(--line)}

/* A row that is still listed but no longer live — a deleted account, a stopped
   notice. Dimmed rather than hidden: it is there because somebody went looking
   for it. */
tr.is-gone td{opacity:.55}
.bad{color:var(--crit);font-weight:600}
.warnish{color:var(--warn);font-weight:600}
.pill.crit{background:var(--crit-soft);color:var(--crit);border-color:var(--crit)}
.good-note{
  padding:12px 14px;border-radius:var(--radius);margin:14px 0;
  background:var(--good-soft);color:var(--good);border:1px solid var(--good);font-size:13.5px;
}
.mini.danger{border-color:var(--crit);color:var(--crit)}
.mini.danger:hover{background:var(--crit);color:var(--surface)}

/* Wearing somebody else's account is louder than merely being inside their
   organisation, because it is easier to forget and harder to undo. */
.actingbar.impersonating{background:var(--crit-soft);color:var(--crit);border-bottom-color:var(--crit)}
.actingbar.impersonating strong{color:var(--crit)}
.actingbar.impersonating button{border-color:var(--crit);color:var(--crit)}
.actingbar.impersonating button:hover{background:var(--crit);color:var(--surface)}

/* ---------- platform notices ---------- */
.sitenotice{
  display:flex;align-items:flex-start;gap:14px;justify-content:space-between;
  padding:10px 20px;font-size:13.5px;border-bottom:1px solid var(--line);
  background:var(--surface-2);color:var(--ink-2);
}
.sitenotice strong{color:var(--ink);margin-right:8px}
.sitenotice a{text-decoration:underline}
.sitenotice form{margin:0}
.sitenotice-warn{background:var(--warn-soft);color:var(--warn);border-bottom-color:var(--warn)}
.sitenotice-warn strong{color:var(--warn)}
.sitenotice-critical{background:var(--crit-soft);color:var(--crit);border-bottom-color:var(--crit)}
.sitenotice-critical strong{color:var(--crit)}
.sitenotice-x{
  background:none;border:0;color:inherit;font-size:20px;line-height:1;
  cursor:pointer;padding:0 4px;opacity:.6;
}
.sitenotice-x:hover{opacity:1}

/* ---------- versions ---------- */
/* A panel like every other panel: the rule-and-heading version read as the
   bottom of the picture rather than a thing in its own right. .card supplies
   the background, border and radius; this only keeps the spacing. */
.versions{padding-bottom:6px}
.version-strip{
  display:flex;gap:14px;overflow-x:auto;padding:14px 2px 6px;
  scroll-snap-type:x proximity;
}
.version{
  flex:0 0 216px;margin:0;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);overflow:hidden;scroll-snap-align:start;
}
.version img{display:block;width:100%;height:150px;object-fit:cover;background:var(--surface-2)}
.version figcaption{display:block;padding:9px 11px 11px;font-size:12.5px}
.version figcaption strong{display:block;margin-bottom:2px}
.version figcaption .tiny{display:block}
.version-acts{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:7px}
.version-acts form{margin:0}
/* An icon on a panel-head button — the palette on "Edit a copy". The same
   drawing sits on the picture at the stage-button size; here it rides beside
   the words. */
.panel-head .btn svg{
  width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;
  display:inline-block;vertical-align:-3px;margin-right:6px;
}

/* ---------- the editor ---------- */
body.ed-on{overflow:hidden}
.editor{
  position:fixed;inset:0;z-index:120;background:var(--ground);
  display:grid;grid-template-rows:auto minmax(0,1fr) auto;
}
.editor[hidden]{display:none}

.ed-bar{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:10px 18px;background:var(--surface);border-bottom:1px solid var(--line);
}
.ed-title{flex:1;font-size:13.5px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ed-bar button{
  font:inherit;font-size:14px;cursor:pointer;padding:7px 16px;border-radius:6px;
  border:1px solid var(--line);background:var(--surface);color:var(--ink-2);
}
.ed-bar .ed-save{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
.ed-bar .ed-save:disabled{opacity:.6;cursor:default}

.ed-stage{
  display:flex;align-items:center;justify-content:center;
  padding:20px;background:var(--surface-2);min-height:0;
  /* The picture is whatever size it is. Without this it grows past its grid
     row and sits on top of the sliders — which does not merely look wrong: the
     canvas then swallows every click meant for a control underneath it. */
  overflow:hidden;
}
.ed-canvas-wrap{
  position:relative;line-height:0;
  max-width:100%;max-height:100%;display:flex;
  /* A flex item's automatic minimum size is its content's own size, and that
     beats max-width — so without these two the canvas keeps its full pixel
     width and the picture runs off the side of a phone. */
  min-width:0;min-height:0;
}
.ed-canvas{
  max-width:100%;max-height:100%;min-width:0;width:auto;height:auto;
  object-fit:contain;border-radius:4px;box-shadow:var(--shadow);
}

/* The crop rectangle: a bright frame with the thirds drawn in, which is what
   makes a crop a decision rather than a guess. */
.ed-crop{
  position:absolute;border:1.5px solid #fff;box-shadow:0 0 0 4000px rgba(0,0,0,.45);
  cursor:move;touch-action:none;
}
.ed-crop::before,.ed-crop::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  background-repeat:no-repeat;
}
.ed-crop::before{
  background-image:linear-gradient(to right,rgba(255,255,255,.4) 1px,transparent 1px),
                   linear-gradient(to right,rgba(255,255,255,.4) 1px,transparent 1px);
  background-size:1px 100%;background-position:33.33% 0,66.66% 0;
}
.ed-crop::after{
  background-image:linear-gradient(to bottom,rgba(255,255,255,.4) 1px,transparent 1px),
                   linear-gradient(to bottom,rgba(255,255,255,.4) 1px,transparent 1px);
  background-size:100% 1px;background-position:0 33.33%,0 66.66%;
}
.ed-handle{
  position:absolute;width:22px;height:22px;border:3px solid #fff;background:transparent;
  touch-action:none;
}
.ed-handle[data-corner="nw"]{top:-2px;left:-2px;border-right:0;border-bottom:0;cursor:nwse-resize}
.ed-handle[data-corner="ne"]{top:-2px;right:-2px;border-left:0;border-bottom:0;cursor:nesw-resize}
.ed-handle[data-corner="sw"]{bottom:-2px;left:-2px;border-right:0;border-top:0;cursor:nesw-resize}
.ed-handle[data-corner="se"]{bottom:-2px;right:-2px;border-left:0;border-top:0;cursor:nwse-resize}

.ed-panel{
  background:var(--surface);border-top:1px solid var(--line);
  padding:12px 18px 18px;max-height:42vh;overflow-y:auto;
}
.ed-crop-row{display:flex;align-items:center;gap:10px 14px;flex-wrap:wrap;margin-bottom:12px}
/* Straightening is geometry, so it belongs beside Crop and not in among the
   light and colour sliders — the same place an iPhone puts it. */
.ed-straighten{
  flex:1 1 230px;min-width:190px;max-width:360px;
  padding:0 0 0 14px;margin-left:4px;border-left:1px solid var(--line);
}
.ed-tool{
  font:inherit;font-size:13px;cursor:pointer;padding:6px 12px;border-radius:20px;
  border:1px solid var(--line);background:var(--surface);color:var(--ink-2);
}
.ed-tool.on{background:var(--accent);border-color:var(--accent);color:#fff}
.ed-reset-all{margin-left:auto;color:var(--muted)}
.ed-ratios{display:flex;gap:4px}
.ed-ratios button{
  font:inherit;font-size:12px;cursor:pointer;padding:5px 10px;border-radius:14px;
  border:1px solid transparent;background:var(--surface-2);color:var(--ink-2);
}
.ed-ratios button.on{border-color:var(--accent);color:var(--accent-ink)}

.ed-sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:4px 26px}
.ed-row{display:flex;align-items:center;gap:12px;padding:5px 0}
.ed-icon{
  flex:none;width:34px;height:34px;border-radius:50%;cursor:pointer;
  border:1px solid var(--line);background:var(--surface-2);color:var(--muted);
  display:flex;align-items:center;justify-content:center;
}
.ed-row.touched .ed-icon{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}
.ed-slider{flex:1;min-width:0}
.ed-label{display:flex;justify-content:space-between;font-size:12px;color:var(--muted);margin-bottom:1px}
.ed-row.touched .ed-label{color:var(--ink-2)}
.ed-label output{font-variant-numeric:tabular-nums}
/*
 * The sliders.
 *
 * accent-color fills a range from its left-hand end, which on a control that is
 * centred on zero draws a half-full bar when the setting is none — and the eye
 * believes that before it reads the number beside it. So the track is drawn by
 * hand: --a and --b are the two ends of the filled segment, written by the
 * script on every move, and the fill therefore grows out of the middle in
 * whichever direction the slider was pushed. Sharpen starts at zero on the left
 * and fills from there, because its origin IS its left-hand end.
 */
.ed-slider input[type="range"]{
  --a:50%;--b:50%;
  -webkit-appearance:none;appearance:none;
  width:100%;margin:0;height:20px;background:transparent;cursor:pointer;
}
.ed-slider input[type="range"]:focus{outline:none}
.ed-slider input[type="range"]::-webkit-slider-runnable-track{
  height:4px;border-radius:2px;
  background:linear-gradient(to right,var(--surface-2) 0 var(--a),
             var(--accent) var(--a) var(--b),var(--surface-2) var(--b) 100%);
}
.ed-slider input[type="range"]::-moz-range-track{
  height:4px;border-radius:2px;
  background:linear-gradient(to right,var(--surface-2) 0 var(--a),
             var(--accent) var(--a) var(--b),var(--surface-2) var(--b) 100%);
}
.ed-slider input[type="range"]::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;
  width:16px;height:16px;border-radius:50%;margin-top:-6px;
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow);
}
.ed-slider input[type="range"]::-moz-range-thumb{
  width:16px;height:16px;border-radius:50%;border:1px solid var(--line);
  background:var(--surface);box-shadow:var(--shadow);
}
.ed-slider input[type="range"]:focus-visible::-webkit-slider-thumb{
  border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft);
}
.ed-slider input[type="range"]:focus-visible::-moz-range-thumb{
  border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft);
}
/* Sits under its own slider, not on a row of its own, so it reads as belonging
   to the control it is about. */
.ed-note{margin:3px 0 0;font-size:11.5px;color:var(--muted)}

.ed-name{display:flex;align-items:center;gap:10px;margin-top:14px;font-size:13px;color:var(--muted)}
.ed-name input{
  flex:1;max-width:420px;padding:7px 11px;border:1px solid var(--line);border-radius:5px;
  background:var(--ground);color:var(--ink);font-size:13.5px;
}
.ed-foot{margin:8px 0 0;font-size:12px;color:var(--muted)}

/* The slider icons. Drawn in CSS rather than fetched, because this has to work
   on a server with no outbound internet access — the same reason there is no
   icon font anywhere else in here. */
.ed-i{display:block;width:16px;height:16px;position:relative}
.ed-i-sun{border-radius:50%;border:2px solid currentColor;
  box-shadow:0 -7px 0 -6px,0 7px 0 -6px,-7px 0 0 -6px,7px 0 0 -6px}
.ed-i-contrast{border-radius:50%;border:2px solid currentColor;
  background:linear-gradient(to right,transparent 50%,currentColor 50%)}
.ed-i-high{border-radius:50%;background:currentColor;opacity:.9;
  clip-path:polygon(0 0,100% 0,100% 55%,0 55%)}
.ed-i-shadow{border-radius:50%;background:currentColor;opacity:.9;
  clip-path:polygon(0 45%,100% 45%,100% 100%,0 100%)}
.ed-i-warmth{border-radius:50%;
  background:linear-gradient(120deg,#4a9fd8 0 50%,#e0a33c 50% 100%)}
.ed-i-tint{border-radius:50%;
  background:linear-gradient(120deg,#5fb48d 0 50%,#c06ab8 50% 100%)}
.ed-i-sat{border-radius:50%;
  background:conic-gradient(#d05a4e,#d8b13c,#5fb48d,#4a9fd8,#8a6fc0,#d05a4e)}
.ed-i-sharpen{border:2px solid currentColor;transform:rotate(45deg);width:12px;height:12px;margin:2px}
.ed-i-rotate{border:2px solid currentColor;border-radius:50%;border-top-color:transparent;
  border-right-color:transparent}
.ed-i-crop{
  border-left:2px solid currentColor;border-bottom:2px solid currentColor;
  width:12px;height:12px;display:inline-block;vertical-align:-2px;margin-right:6px;
  box-shadow:3px -3px 0 -1px currentColor;
}

@media (max-width:700px){
  .ed-canvas{max-height:50vh}
  .ed-panel{max-height:48vh}
  .ed-sliders{grid-template-columns:1fr}
  /* Three buttons and a title do not fit across a phone at full size. */
  .ed-bar{padding:9px 12px;gap:8px}
  .ed-bar button{padding:7px 11px;font-size:13px}
  /* Straighten takes a line of its own rather than squeezing the ratios. */
  .ed-straighten{flex:1 1 100%;max-width:none;margin:2px 0 0;padding:0;border-left:0}
}

/* Named so the check that every class a page uses is defined can see them:
   .ed-cancel takes the bar's button styling, .ed-form only ever carries hidden
   fields, and .version-edit is a link-styled button in the strip. */
/* (the Cancel button's own rule is further down, with the editor tools) */
/* Press and hold to see the photograph as it came in — the one gesture from a
   phone's editor that people reach for without being told it is there. */
.ed-compare{color:var(--muted)}
.ed-compare.on{background:var(--surface-2);border-color:var(--accent);color:var(--accent-ink)}
.ed-form{display:none}
.version-edit{color:var(--accent)}

/* ---------- the processing log ---------- */
.filterbar{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:14px 0 12px;
}
.filterbar input[type="search"],.filterbar select{
  font:inherit;font-size:13.5px;padding:7px 11px;border:1px solid var(--line);
  border-radius:6px;background:var(--surface);color:var(--ink);
}
.filterbar input[type="search"]{min-width:min(340px,100%);flex:1 1 240px}

.worklog{font-size:13px}
.worklog td{vertical-align:top}
/* The message is the widest thing here and the least predictable, so it is the
   column allowed to wrap; everything else stays on one line. */
.worklog .detail{color:var(--ink-2);word-break:break-word;min-width:220px}
.worklog .filecell{max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nowrap{white-space:nowrap}

.rawlog{
  margin:10px 0 0;padding:12px 14px;border-radius:var(--radius);
  background:var(--surface-2);border:1px solid var(--line);
  font-family:ui-monospace,"Cascadia Mono",Consolas,monospace;font-size:12px;line-height:1.55;
  max-height:420px;overflow:auto;white-space:pre;color:var(--ink-2);
}

/* ---------- dragging in a folder ---------- */
/* Where a file is bound for, when it is not the album that was chosen. Shown
   under its name, small, so a long tree does not take over the row. */
.up-where{
  display:block;font-size:11.5px;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.up-where:empty{display:none}
.planNote{margin:10px 0 0}
/* The sentence explaining what a dropped folder does belongs on its own line,
   and reads better at about a sentence's width than across the whole page. */
.dz-note{display:block;max-width:60ch;margin:9px auto 0;line-height:1.5}

/* The cross that cancels one file. Quiet until the row is hovered or it is
   focused from the keyboard, because a row of crosses down the page reads as
   an error list rather than a list of files going up. */
.up-cancel{
  border:0;background:none;cursor:pointer;padding:0;
  font-size:18px;line-height:1;color:var(--muted);opacity:.45;
  justify-self:end;
}
.up-item:hover .up-cancel,.up-cancel:focus-visible{opacity:1;color:var(--crit)}
.up-item.is-cancelled{opacity:.55}
.up-item.is-cancelled .up-fill{background:var(--muted)}

/* ---------- people, and one person ---------- */

/* A card that stands on its own as a section of a page rather than sitting in
   a .cols-2 grid. Without this the Recent activity panel on the overview sat
   hard against the bottom of the gallery above it, which read as though the
   two belonged together. Matches the space .h-sec leaves above a heading. */
.card-sec{margin-top:30px}

.avatar-xs{width:26px;height:26px;border-radius:50%;object-fit:cover;flex:none;
  background:var(--surface-2);display:block}
.avatar-xl{width:120px;height:120px;border-radius:50%;object-fit:cover;flex:none;
  background:var(--surface-2);border:1px solid var(--line)}

/* The picture column: as narrow as its contents and never the one that grows. */
th.avcol,td.avcol{width:1%;padding-right:0}

.visually-hidden{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/* A table whose rows are links. The anchor in the name cell does the real
   work — keyboard, middle click, "open in new tab" — and the row follows it
   for a plain click. The cursor and the hover tint are what tell somebody the
   row is clickable at all. */
.rowlinks tbody tr{cursor:pointer}
.rowlinks tbody tr:hover{background:var(--surface-2)}
.rowlinks tbody tr:focus-within{background:var(--surface-2);outline:2px solid var(--accent);outline-offset:-2px}
.rowlinks td.strong{font-weight:500}
.rowlinks td.strong a{color:inherit;text-decoration:none}
.rowlinks tbody tr:hover td.strong a{color:var(--accent)}
.rowlinks td.strong .sub{display:block;font-size:11.5px;color:var(--muted);font-weight:400}

/* One person's card. The picture, then everything about them beside it, and on
   a phone the two stack rather than squeezing the text into a column. */
.personhead{display:flex;gap:22px;align-items:flex-start;flex-wrap:wrap}
.personhead-main{flex:1 1 320px;min-width:0}
.personhead h1{margin:0 0 4px}
.personhead-sub{margin:0;color:var(--muted);font-size:13.5px}
.personhead-about{margin:10px 0 0;max-width:70ch;line-height:1.55}
.personhead-note{margin-top:12px}
.personlinks{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
.personlinks .chip.out{border-style:dashed}

.albumgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:12px}
.albumcard{
  display:block;border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden;background:var(--surface);color:inherit;text-decoration:none;
}
.albumcard:hover{border-color:var(--accent);box-shadow:var(--shadow);text-decoration:none}
.albumcard-img{
  display:block;aspect-ratio:4/3;background:var(--surface-2) center/cover no-repeat;
}
.albumcard-meta{display:block;padding:9px 11px}
.albumcard-name{display:block;font-size:13.5px;font-weight:500;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.albumcard-sub{display:block;font-size:11.5px;color:var(--muted);margin-top:2px}

/* The map on an asset page, which lives under the picture rather than in the
   rail beside it — a coordinate is worth looking at, and 220px in a 380px
   column was not enough to see where anything was. Tall enough to place a
   photograph, capped so it never pushes everything below it off the screen. */
.map-tall{height:clamp(320px,52vh,620px);margin-top:12px}

/* The histogram is a panel of its own now. Its own top margin was for when it
   sat loose under the picture; inside a card the heading provides that. */
.card .histogram{margin-top:0;max-width:none}

/* "Or send it to somebody" on the basket page: a fold-out rather than a form
   sitting open, because most zips are for the person building them. */
.sendzip{margin:6px 0 18px}
.sendzip .hint-body{max-width:520px}

/* Return. On the page whether the basket is empty or full, and solid rather
   than outlined — it is the way out of a page somebody came to for one thing,
   and an outline against the page was not reading as a button. */
.basket-back{margin:22px 0 10px}
.basket-return{display:inline-flex;align-items:center;gap:8px;padding:8px 16px}
.basket-return svg{
  width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
}

/* ---------- the asset stage ---------- */

/* Controls that sit on the picture. Semi-transparent and quiet until the
   pointer is near, because the photograph is the point and a row of solid
   buttons across it is not. Each is a real link underneath. */
.stage-btn,.stage-arrow{
  position:absolute;display:grid;place-items:center;z-index:2;
  color:#fff;background:rgba(12,16,18,.42);border:1px solid rgba(255,255,255,.22);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  text-decoration:none;opacity:.55;transition:opacity .15s,background .15s,transform .15s;
}
.stage-btn svg,.stage-arrow svg{
  width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
}
.stage:hover .stage-btn,.stage:hover .stage-arrow{opacity:.9}
.stage-btn:hover,.stage-arrow:hover,
.stage-btn:focus-visible,.stage-arrow:focus-visible{
  opacity:1;background:rgba(12,16,18,.7);text-decoration:none;color:#fff;
}

/* The row over the top left: download, basket, collection. One container so
   they are spaced by the layout rather than by three sets of coordinates that
   have to be kept in step by hand. */
.stage-tools{
  position:absolute;top:14px;left:14px;z-index:2;
  display:flex;align-items:center;gap:8px;
}
.stage-tools form{display:flex;margin:0}
.stage-tools .stage-btn,.stage-btn.stage-get{
  position:static;width:40px;height:40px;border-radius:10px;padding:0;cursor:pointer;
}
/* Already in the basket: the same button, wearing the answer. */
.stage-tools .stage-btn.is-on{
  background:rgba(31,104,73,.72);border-color:rgba(255,255,255,.45);opacity:.85;
}
.stage:hover .stage-tools .stage-btn.is-on{opacity:1}
.stage-arrow.prev,.stage-arrow.next{
  top:50%;transform:translateY(-50%);width:44px;height:64px;border-radius:12px;
}
.stage-arrow.prev{left:14px}
.stage-arrow.next{right:14px}
.stage-arrow.prev:hover{transform:translateY(-50%) translateX(-2px)}
.stage-arrow.next:hover{transform:translateY(-50%) translateX(2px)}

/* On a touch screen there is no hover to reveal them, and a swipe does the
   same job — so they sit a little quieter and out of thumb's way. */
@media (hover:none){
  .stage-btn,.stage-arrow{opacity:.75}
  .stage-arrow{width:38px;height:54px}
}

/* Title on the left, where it belongs to the picture; album and views on the
   right, where somebody goes next. */
.stage-foot{
  display:flex;align-items:flex-end;justify-content:space-between;gap:16px;
  flex-wrap:wrap;margin-top:10px;
}
.stage-title{
  margin:0;font-size:19px;font-weight:600;letter-spacing:-.01em;line-height:1.3;
  min-width:0;overflow-wrap:anywhere;
}
.stage-side{display:flex;align-items:center;gap:12px;flex-wrap:wrap}

/* The one thing somebody is likely to do next from this page, so it is a solid
   button rather than an outline that disappeared into the page. */
.stage-album{box-shadow:0 1px 2px rgba(12,16,18,.18)}
.stage-album:hover{filter:brightness(1.1)}

/* ---------- placing a photograph on the map ---------- */

.geo-line{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:10px 0 0}
.geo-pt{font-variant-numeric:tabular-nums}

/* "No coordinates were recorded in this file." — red, because it is the one
   line under a map that is reporting something missing rather than describing
   what is there, and it sits where the coordinates would otherwise be. Uses
   the same red as every other warning on the site, so it follows the dark
   theme with them. */
.geo-none{color:var(--crit)}

/* The controls live at the right-hand end of the coordinate line: the numbers
   and the thing that changes them, on one line under the map. */
.geo-tools{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-left:auto}
.geo-place{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0}

/* The same button as "Open … Album": solid, small, with its icon beside the
   words. It was the small outlined .mini used for table-row controls, which
   read as a different class of thing from the buttons around it. */
.geo-btn{display:inline-flex;align-items:center;gap:7px}
.geo-ico{
  width:15px;height:15px;flex:none;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;
}

/* One button, three states. Blue says "I am listening to the map", green says
   "press me and it is saved" — the colours people already read that way, which
   is why there is no sentence of instructions next to it any more. */
.geo-btn.is-picking{
  background:#1D63C8;border-color:#1D63C8;color:#fff;font-weight:600;
}
.geo-btn.is-picking:hover{background:#1a59b4;border-color:#1a59b4;color:#fff}
.geo-btn.is-ready{
  background:var(--good);border-color:var(--good);color:#fff;font-weight:600;
}
.geo-btn.is-ready:hover{background:var(--good);border-color:var(--good);color:#fff;opacity:.9}
/* The secondary of the pair, so it is the outlined one. */
.geo-cancel{color:var(--accent);display:inline-flex;align-items:center;gap:7px}
.geo-label{display:inline-block}
.map.is-picking{outline:2px solid var(--accent);outline-offset:2px;cursor:crosshair}
.map.is-empty{opacity:.9}

/* ---------- editing tags ---------- */

.iconbtn{
  border:1px solid var(--line);background:var(--surface);border-radius:6px;
  width:26px;height:26px;display:inline-grid;place-items:center;cursor:pointer;
  color:var(--muted);vertical-align:middle;margin-left:6px;padding:0;
}
.iconbtn:hover{border-color:var(--accent);color:var(--accent)}
.iconbtn svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}

.tag-editable{display:inline-flex;align-items:center;gap:6px}
.tag-x{
  border:0;background:none;padding:0;margin:0;cursor:pointer;font-size:15px;
  line-height:1;color:inherit;opacity:.55;
}
.tag-x:hover{opacity:1;color:var(--crit)}

/* A modal, used by the tag editor and available to anything else that needs
   one. Fixed, so it is not affected by wherever the page is scrolled to. */
/* hidden must win over display:grid — a class and an attribute have the same
   specificity, so without this the modal is open the moment it exists. */
.modal[hidden]{display:none}
.modal{
  position:fixed;inset:0;z-index:80;display:grid;place-items:center;padding:20px;
  background:rgba(10,14,16,.55);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);
}
.modal-card{
  width:min(520px,100%);max-height:min(80vh,700px);display:flex;flex-direction:column;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:0 20px 60px rgba(0,0,0,.35);
}
.modal-head{display:flex;align-items:center;justify-content:space-between;
  padding:14px 18px;border-bottom:1px solid var(--line)}
.modal-head h2{margin:0;font-size:15px}
.modal-x{border:0;background:none;font-size:22px;line-height:1;cursor:pointer;
  color:var(--muted);padding:0 2px}
.modal-x:hover{color:var(--ink)}
.modal-body{padding:16px 18px;overflow:auto;display:flex;flex-direction:column;gap:12px}
.modal-foot{display:flex;gap:10px;justify-content:flex-end;padding:12px 18px;
  border-top:1px solid var(--line)}
body.modal-open{overflow:hidden}

/* ---------- deleting one asset ---------- */

/* Last panel in the column, and the only one that is not about changing
   something — so it is marked rather than blended in, and it is a long way
   from the controls somebody uses every day. */
.danger-card{border-color:var(--crit)}
.danger-card h2{color:var(--crit)}

/* A list that reads as sentences rather than a bulleted form. */
ul.plain{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:7px;color:var(--ink-2)}
#deleteModal .modal-foot{justify-content:space-between}

/* ---------- the collections box ---------- */

/* One row per collection, the whole row a target: a tick when it holds this
   photograph, a plus when it does not, and a click to change its mind. Easier
   to hit than a checkbox and it says what it will do before it is pressed. */
.coll-list{display:flex;flex-direction:column;gap:6px;max-height:min(46vh,340px);overflow:auto}
.coll-row{
  display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  padding:8px 10px;border:1px solid var(--line);border-radius:6px;
  background:var(--surface);color:var(--ink);font:inherit;cursor:pointer;
}
.coll-row:hover{border-color:var(--accent)}
.coll-row.is-in{border-color:var(--good);background:var(--good-soft)}
.coll-tick{
  width:20px;height:20px;display:grid;place-items:center;flex:0 0 auto;
  border-radius:50%;border:1px solid var(--line);color:var(--muted);
  font-size:12px;line-height:1;
}
.coll-row.is-in .coll-tick{background:var(--good);border-color:var(--good);color:#fff}
.coll-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* "Send a link" folded into a table cell: narrow, and never wider than its
   column however long the address is. */
.sendagain{margin-top:6px}
.sendagain input[type=email]{width:min(240px,100%)}

/* The second half of the name, a step darker so the join between "Visual" and
   "IQ" is visible — the l and the I are the same stroke otherwise. */
.iq{color:color-mix(in srgb,currentColor 68%,#000);font-weight:700}

/* An organisation's own logo, where the coloured square would otherwise be.
   Height-constrained rather than width-constrained: logos are all shapes, and
   a tall one must not push the header about. */
.brand-logo{height:26px;width:auto;max-width:180px;object-fit:contain;display:block;flex:none}
.logo-preview{
  display:block;height:48px;width:auto;max-width:240px;object-fit:contain;
  background:var(--surface-2);border:1px solid var(--line);border-radius:6px;padding:6px;
}
.logo-preview.is-empty{width:120px}
.login-logo{display:block;max-height:56px;max-width:230px;margin:0 auto 14px;object-fit:contain}

/* "Emailed to …" — a statement of fact, so it reads as one rather than as an
   address sitting in a column. */
.sent-yes{font-weight:500}

/* ---------- the editor: turns, dehaze, auto, and a visible Cancel ---------- */

/* Dehaze: a hazy horizon — a flat band over a darker one, which is what the
   slider takes out. Built from gradients like the others rather than an image,
   so it inherits the colour and costs nothing. */
.ed-i-haze{
  border-radius:3px;
  background:linear-gradient(to bottom,
    color-mix(in srgb,currentColor 25%,transparent) 0 48%,
    color-mix(in srgb,currentColor 80%,transparent) 52% 100%);
}

.ed-turns{display:inline-flex;gap:4px}
.ed-turns .ed-tool{padding:6px 8px}
.ed-turns svg,.ed-cancel svg{
  width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;display:block;
}

/* Auto is the one control that changes several things at once, so it says so
   by being the only tool with the accent on it — and a wand, which is what
   every photograph application on a phone puts there. */
.ed-auto{
  display:inline-flex;align-items:center;gap:7px;
  border-color:var(--accent);color:var(--accent);font-weight:600;
}
.ed-auto:hover{background:var(--accent);color:#fff}
.ed-auto svg{
  width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;display:block;
}

/* Cancel was a grey word among grey words, which is a poor way to mark the way
   out of a full-screen editor over somebody's photograph. Filled, in the
   colour this application uses for "this undoes something", so it reads at a
   glance from the other side of the bar to Save. */
.ed-bar .ed-cancel{
  display:inline-flex;align-items:center;gap:7px;
  background:var(--crit);border-color:var(--crit);color:#fff;font-weight:600;
}
.ed-bar .ed-cancel:hover{
  background:color-mix(in srgb,var(--crit) 82%,#000);
  border-color:color-mix(in srgb,var(--crit) 82%,#000);
  color:#fff;
}
.ed-bar .ed-cancel svg{stroke-width:2.2}

/* The list of what may be uploaded: a label and a run of extensions, which
   wants the extensions to wrap under their own label rather than beside it. */
.accepts dd{overflow-wrap:anywhere}
