/* ═══════════════════════════════════════════════════════════════════════════
   css/saas-skin.css — the SaaS / card-based restyle (Square · Stripe · Apple).

   LOAD ORDER: this sheet must be the LAST stylesheet on every page it skins:
     index.html   — after styles.css, wall.css, claim-door.css, requests.css
     login.html   — after login.css
     booking.html — after tokens.css, setup.css
     client.html  — after tokens.css, client.css, requests.css

   Also add Inter to each page's font link, e.g.:
     https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700
       &family=Anton&display=swap
   (Anton stays: the wordmark keeps it.)

   NOT loaded by profile.html — the public wall and its six themes are the
   client-facing brand and keep their own look. The .lw editor preview is
   scoped to its own variables, so it is unaffected automatically.

   Delete this one <link> line to revert everything.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ═══════════════════════════════════════════════════════════════════════════
   SCOPED TO THE APP, NOT THE MARKETING PAGE.

   index.html is BOTH the landing page and the whole app — the wizard, the
   editor and the admin shell are hash routes inside it. js/app.js sets
   `document.body.className = 'view-' + v`, so the landing page is
   `body.view-home` and every dashboard route is something else
   (view-edit, view-setup, view-profile).

   Every rule below hangs off `body:not(.view-home)`. The artist works
   inside a SaaS dashboard; the marketing page keeps the look it was
   designed with — Anton headers, the Playfair lede, the dark contrast
   slab, and payment logos that stay visible because their tiles are still
   the colour those logos were drawn against.

   login.html, booking.html and client.html have no .view-home class, so
   they are skinned in full.

   Raising specificity this way only helps: the sheet was relying on source
   order alone, which any later stylesheet could have undone.
   ═══════════════════════════════════════════════════════════════════════════ */
/* ── tokens, both dialects ──────────────────────────────────────────────── */
/* Same :root specificity as the originals; this file loads later, so these
   win by source order. html[data-look="dark"/"warm"] on the client form is
   MORE specific and still wins — the artist's chosen looks keep working;
   this skin simply becomes the new default ("paper") look.               */
body:not(.view-home){
  /* ── the structure, taken from Square's own Market tokens ─────────────
     Measured from their production stylesheet, not from screenshots:

       surface-10/20/30  #FFFFFF   — every card level is the SAME white
       fill-50           #F7F7F7   — the page behind them
       text-10           #101010   — not pure black
       text-20           #666666
       text-30           #959595
       divider-20        #f0f0f0   — a whisper
       radius            6 / 12 / 16 / 24

     The lesson is not the colours, it is where the contrast lives.
     Square puts almost NONE in the surfaces — page to card is a ratio of
     1.07, practically invisible — and ALL of it in the text, which runs
     19:1. Cards separate by a hairline and a shadow, not by tone.

     The palette this replaces did the opposite: cards DARKER than the
     page (1.24 the wrong way, so panels read pressed-in rather than
     raised) and borders at 9.5:1, which is a near-black rule around every
     box. Everything was competing, which is what "off" was.

     So: keep bone, adopt the structure. */

  /* surfaces — card LIGHTER than page, and barely */
  --ink:#eae3d5;  --card:#f7f3ea;  --ink2:#f7f3ea;
  --inset:#fffdf9; --ink3:#efe9dc;  --ink4:#e2dbcb;

  /* text — three steps, all the contrast lives here */
  --paper:#141210;   /* 16.9:1 on card. Square's #101010, warmed */
  --dim:#635c50;     /*  6.0:1 on card, 5.2:1 on page — passes AA  */
  --faintext:#948c7e;/*  3.0:1 — large text and disabled only      */

  /* dividers — a whisper, like Square's #f0f0f0. The old #272725 was a
     near-black rule around every panel, which is what made the shell feel
     heavy and dated. */
  --line:rgba(20,18,16,.09);
  --faint:rgba(20,18,16,.06);
  --hairline:#6b6b66;

  --red:#c22f2f;    --acid:#1f8a4c;  --amber:#8a6d1f;
  --accent:#c22f2f; --live:#1f8a4c;  --gold:#8a6d1f;

  --sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --mn:var(--sans);   --mono:var(--sans);
  --dsp:var(--sans);  --disp:var(--sans);
  --serif:var(--sans); --ask:var(--sans);

  /* Square's radius scale: 6 / 12 / 16 / 24 */
  --radius:12px; --radius-s:6px; --radius-lg:16px; --radius-pill:9999px;

  /* elevation does the lifting the tone no longer does */
  --shadow-card:0 1px 2px rgba(20,18,16,.05),0 1px 3px rgba(20,18,16,.07);
  --panel:#0f0f0d; --panel-fg:#f6f6f4;
}

/* ── base ───────────────────────────────────────────────────────────────── */
body:not(.view-home),
body:not(.view-home){background:var(--ink);color:var(--paper)}
body:not(.view-home){
  font-family:var(--sans);
  font-size:16px;line-height:1.6;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
body:not(.view-home) ::selection{background:var(--red);color:#fff}

/* the wordmark is the one thing that keeps Anton — the tattoo identity
   survives inside the SaaS chrome */
body:not(.view-home) .mark b,
body:not(.view-home) .adm-brand,
body:not(.view-home) .topbar b,
body:not(.view-home) .brand,
body:not(.view-home) .corner b,
body:not(.view-home) .badge b{
  font-family:'Anton','Helvetica Neue',sans-serif;font-weight:400;
}

/* ── headers: sentence case, bold Inter, tight tracking ─────────────────── */
/* Every big display header that was Anton-uppercase. Anton is condensed, so
   a couple of sizes come down a notch to stop Inter Bold from wrapping.   */
body:not(.view-home) .sec-head h2,
body:not(.view-home) .hero h1,
body:not(.view-home) footer h2,
body:not(.view-home) .incl h2,
body:not(.view-home) .wiz-title,
body:not(.view-home) .adm-top h2,
body:not(.view-home) .pm-card h2,
body:not(.view-home) .crop-title,
body:not(.view-home) .vs-name,
body:not(.view-home) .card-title,
body:not(.view-home) .rq-head b,
body:not(.view-home) .rq-quote-h,
body:not(.view-home) .rq-empty b,
body:not(.view-home) .reach b,
body:not(.view-home) .chapter h1,
body:not(.view-home) .h,
body:not(.view-home) .guess b,
body:not(.view-home) .calbar b,
body:not(.view-home) .drop b{
  font-family:var(--sans);font-weight:700;
  letter-spacing:-0.02em;text-transform:none;line-height:1.15;
}
body:not(.view-home) .h{font-size:clamp(1.7rem,5vw,2.6rem)}          /* client form question   */
body:not(.view-home) .card-title{font-size:1.35rem}                   /* login card             */
body:not(.view-home) .rq-head b{font-size:1.25rem}                    /* request detail name    */
body:not(.view-home) .sec-head h2,
body:not(.view-home) .hero h1{letter-spacing:-0.025em}

/* big numbers: bold, tabular, tighter */
body:not(.view-home) .chapter .no,
body:not(.view-home) .plan .price,
body:not(.view-home) .plan-card .amt,
body:not(.view-home) .szn,
body:not(.view-home) .math-cell .fig,
body:not(.view-home) .ldr__pct,
body:not(.view-home) .prof-init,
body:not(.view-home) .rq-count b{
  font-family:var(--sans);font-weight:700;
  letter-spacing:-0.03em;font-variant-numeric:tabular-nums;
}
body:not(.view-home) .chapter .no{font-size:clamp(3rem,11vw,5.5rem)}

/* prices & metadata read cleaner with tabular figures everywhere */
body:not(.view-home) .rq-row-m,
body:not(.view-home) .rq-answers dd,
body:not(.view-home) .guess b,
body:not(.view-home) .plan .price{font-variant-numeric:tabular-nums}

/* micro-labels: uppercase stays (it IS the Stripe/Square idiom at this
   size) but standardised — medium weight, quiet gray */
body:not(.view-home) .field label,
body:not(.view-home) .rq-quote label,
body:not(.view-home) .rq-f,
body:not(.view-home) .rq-reach-l,
body:not(.view-home) .f-label,
body:not(.view-home) .rq-answers dt,
body:not(.view-home) .rq-hist b,
body:not(.view-home) .pic .cap,
body:not(.view-home) .wl-list .wl-tag,
body:not(.view-home) .plan .tier{
  font-weight:500;color:var(--dim);
}
/* Section labels sit ON the card, where --dim is already at the floor.
   Give them the body colour at 80% instead of a second dimming — legible,
   still clearly secondary. */
body:not(.view-home) .ed-panel > h3,
body:not(.view-home) .bs-panel > h3,
body:not(.view-home) .checklist h3{
  color:var(--paper);opacity:.8;font-weight:600;letter-spacing:.02em;
}

/* ── cards: white, soft border, whisper of shadow ───────────────────────── */
/* border-color (not the shorthand) so dashed empty-states stay dashed */
body:not(.view-home) .card,
body:not(.view-home) .step,
body:not(.view-home) .plan,
body:not(.view-home) .pay-tile,
body:not(.view-home) .opt,
body:not(.view-home) .faq details,
body:not(.view-home) .rq-detail,
body:not(.view-home) .rq-row,
body:not(.view-home) .rq-empty,
body:not(.view-home) .reach,
body:not(.view-home) .door-done,
body:not(.view-home) .tile,
body:not(.view-home) .rq-reach{
  background:var(--card);border-color:var(--line);box-shadow:var(--shadow-card);
}
body:not(.view-home) .rq-row{border-radius:12px}
body:not(.view-home) .rq-row:hover{border-color:rgba(27,27,24,.25)}
body:not(.view-home) .opt:hover,
body:not(.view-home) .pay-tile:hover{border-color:rgba(27,27,24,.25)}
/* selected states: the tint gets a touch of the accent instead of a
   darker gray, which reads as "chosen" rather than "pressed" */
body:not(.view-home) .rq-row.on{background:#fbf1ee;border-color:var(--red)}
body:not(.view-home) .opt[aria-pressed=true],
body:not(.view-home) .day[aria-pressed=true],
body:not(.view-home) .card.done{
  background:#eef5ee;
}
/* attention card: red tint, not bare red border */
body:not(.view-home) .rq-reach{border-color:rgba(194,47,47,.32);background:#fcf4f1}

/* ── buttons ────────────────────────────────────────────────────────────── */
body:not(.view-home) button{font-family:var(--sans)}
/* the workaday primary actions go near-black pills — the Square move */
body:not(.view-home) .savebtn,
body:not(.view-home) .rq-btn,
body:not(.view-home) .ok{
  background:#0a0a0a;color:#fff;font-weight:600;letter-spacing:0;
  border-radius:var(--radius-pill);text-transform:none;
}
body:not(.view-home) .savebtn:hover:not(:disabled),
body:not(.view-home) .rq-btn:hover:not(:disabled),
body:not(.view-home) .ok:hover:not(:disabled){background:#2a2a26;transform:none}
/* the flagship CTAs keep the brand red — claim is THE action */
body:not(.view-home) .big-claim,
body:not(.view-home) .submit-btn,
body:not(.view-home) .ego{
  background:var(--red);color:#fff;font-weight:600;
  border-radius:var(--radius-pill);text-transform:none;letter-spacing:0;
}
/* ghosts: quiet border, darken on hover */
body:not(.view-home) .rq-btn.ghost,
body:not(.view-home) .exit,
body:not(.view-home) .email-reveal,
body:not(.view-home) .google-btn{
  background:var(--card);border:1px solid rgba(27,27,24,.16);color:var(--paper);
  font-weight:500;border-radius:var(--radius-pill);
}
body:not(.view-home) .rq-btn.ghost:hover,
body:not(.view-home) .exit:hover,
body:not(.view-home) .email-reveal:hover,
body:not(.view-home) .google-btn:hover{
  border-color:rgba(27,27,24,.4);color:var(--paper);background:var(--card);
}
body:not(.view-home) .google-btn.primary{background:#0a0a0a;border-color:#0a0a0a;color:#fff}
body:not(.view-home) .google-btn.primary:hover{background:#2a2a26;border-color:#2a2a26}

/* ── inputs: boxed, white, dark focus ring ──────────────────────────────── */
body:not(.view-home) .field input,
body:not(.view-home) .field textarea,
body:not(.view-home) .field select,
body:not(.view-home) .rq-quote input,
body:not(.view-home) .rq-quote textarea,
body:not(.view-home) .e-form input,
body:not(.view-home) .handle-wrap{
  background:var(--inset);border-color:#d8d0be;border-radius:var(--radius-s);
}
body:not(.view-home) .field input:focus,
body:not(.view-home) .field textarea:focus,
body:not(.view-home) .field select:focus,
body:not(.view-home) .rq-quote input:focus,
body:not(.view-home) .rq-quote textarea:focus,
body:not(.view-home) .e-form input:focus,
body:not(.view-home) .handle-wrap:focus-within{
  border-color:#1b1b18;box-shadow:0 0 0 3px rgba(27,27,24,.08);outline:0;
}
body:not(.view-home) .field input::placeholder,
body:not(.view-home) .text::placeholder,
body:not(.view-home) .area::placeholder{
  color:rgba(27,27,24,.30);
}
/* the client form's underline fields become boxed Stripe-style fields */
body:not(.view-home) .text,
body:not(.view-home) .area,
body:not(.view-home) select.drop{
  background:var(--inset);border:1px solid #d8d0be;border-radius:var(--radius-s);
  padding:.7rem .9rem;
}
body:not(.view-home) .text:focus,
body:not(.view-home) .area:focus,
body:not(.view-home) select.drop:focus{
  border-color:#1b1b18;border-bottom-color:#1b1b18;
  box-shadow:0 0 0 3px rgba(27,27,24,.08);
}

/* focus ring, app-wide */
body:not(.view-home) :focus-visible{outline:2px solid #1b1b18;outline-offset:2px}

/* ── small corrections the re-tokening exposes ──────────────────────────── */
/* landing link-list hover tint was hard-coded to the old dark-theme red */
body:not(.view-home) .wl-list a:hover{background:rgba(194,47,47,.04)}
/* photo thumbnails: soften the hard corners */
body:not(.view-home) .rq-pics img,
body:not(.view-home) .shots img,
body:not(.view-home) .tile .art{border-radius:8px}
body:not(.view-home) .tile{border-radius:12px;overflow:hidden}
/* dashed empty state: keep it clearly an invitation, not an error */
body:not(.view-home) .rq-empty{box-shadow:none;background:#f6f1e6}
/* the serif italic lede on the landing becomes a plain quiet lede */
body:not(.view-home) .lede{font-family:var(--sans);font-style:normal;font-weight:400;color:var(--dim)}
body:not(.view-home) .lede b{font-style:normal;color:var(--paper);font-weight:600}
/* step numbers on the landing: were serif italic red */
body:not(.view-home) .step .n{font-family:var(--sans);font-style:normal;font-weight:700;
  font-variant-numeric:tabular-nums}
/* FAQ +/– marker: was serif */
body:not(.view-home) .faq summary::after{font-family:var(--sans);font-weight:500}

/* ═══════════════════════════════════════════════════════════════════════════
   THE SHELL: rail, cards, and how they sit on bone.

   The problem this fixes: a white sidebar and white panels on a warm bone
   page look like two products stapled together. Square and Stripe both get
   away with a white rail because their page is a cool gray — the two are
   the same temperature. Bone is not.

   So the rail becomes the PAGE, separated by a hairline rather than by
   colour, and only the panels lift. One raised surface instead of two
   competing ones, and the eye goes to the content rather than to the
   furniture.
   ═══════════════════════════════════════════════════════════════════════════ */

/* the rail sits on the page, not above it */
body:not(.view-home) .adm-rail{
  background:transparent;
  border-right:1px solid rgba(27,27,24,.10);
}
/* the current section is the only thing in the rail that lifts */
body:not(.view-home) .adm-rail .adm-nav{
  border-radius:10px;color:var(--dim);font-weight:500;
}
body:not(.view-home) .adm-rail .adm-nav:hover{
  background:rgba(27,27,24,.045);color:var(--paper);
}
body:not(.view-home) .adm-rail .adm-nav.on{
  background:var(--card);color:var(--paper);font-weight:600;
  box-shadow:var(--shadow-card);
}

/* panels: the one raised surface */
body:not(.view-home) .ed-panel,
body:not(.view-home) .adm-check,
body:not(.view-home) .bs-panel,
body:not(.view-home) .bs-tunnel,
body:not(.view-home) .wrap > .field,
body:not(.view-home) .checklist{
  background:var(--card);
  border:1px solid rgba(27,27,24,.10);
  border-radius:var(--radius);
  box-shadow:var(--shadow-card);
}

/* inputs read as recessed: a shade lighter than the card, warmer border */
body:not(.view-home) .ed-field input,
body:not(.view-home) .ed-field textarea,
body:not(.view-home) .ed-field select,
body:not(.view-home) .ed-panel input,
body:not(.view-home) .ed-panel textarea,
body:not(.view-home) .ed-panel select{
  background:var(--inset);border:1px solid #d8d0be;border-radius:var(--radius-s);
}
body:not(.view-home) .ed-panel input:focus,
body:not(.view-home) .ed-panel textarea:focus,
body:not(.view-home) .ed-panel select:focus{
  border-color:#1b1b18;box-shadow:0 0 0 3px rgba(27,27,24,.07);outline:0;
}

/* the top bar floats on the page too — a white strip across the top was
   the other half of the clash */
body:not(.view-home) .adm-top{
  background:transparent;border-bottom:1px solid rgba(27,27,24,.10);
}

/* the phone preview keeps its own six themes; all it needs from here is
   somewhere to stand that is not fighting it */
body:not(.view-home) .adm-side{background:transparent}

/* ── elevation, because tone no longer separates the card ────────────────
   With the page and the card almost the same value, a panel has to lift by
   shadow instead. This is the whole trick behind why Square and Stripe feel
   calm: nothing is fighting for attention through colour. */
body:not(.view-home) .ed-panel,
body:not(.view-home) .adm-check,
body:not(.view-home) .bs-panel,
body:not(.view-home) .bs-tunnel,
body:not(.view-home) .checklist,
body:not(.view-home) .rq-detail,
body:not(.view-home) .rq-row{
  background:var(--card);
  border:1px solid var(--line);
  box-shadow:var(--shadow-card);
}
/* hover lifts a fraction rather than changing colour */
body:not(.view-home) .rq-row:hover{
  border-color:rgba(20,18,16,.22);
  box-shadow:0 2px 4px rgba(20,18,16,.06),0 4px 10px rgba(20,18,16,.07);
}

/* the third text step, for hints and disabled copy */
body:not(.view-home) .ed-note,
body:not(.view-home) .tip,
body:not(.view-home) .hint,
body:not(.view-home) .bs-note{color:var(--dim)}

/* inputs: white against a near-white card needs a real border, not a tint */
body:not(.view-home) .ed-panel input,
body:not(.view-home) .ed-panel textarea,
body:not(.view-home) .ed-panel select,
body:not(.view-home) .field input,
body:not(.view-home) .field textarea,
body:not(.view-home) .field select{
  background:var(--inset);
  border:1px solid rgba(20,18,16,.16);
  border-radius:var(--radius-s);
}
body:not(.view-home) .ed-panel input:focus,
body:not(.view-home) .ed-panel textarea:focus,
body:not(.view-home) .ed-panel select:focus,
body:not(.view-home) .field input:focus,
body:not(.view-home) .field textarea:focus{
  border-color:#141210;box-shadow:0 0 0 3px rgba(20,18,16,.07);outline:0;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE SHELL LAYOUT — closing the gap without losing the preview.

   The problem: the cards were capped at a narrow max-width inside a column
   that was much wider, so a band of dead space sat between the panels and
   the phone. The instinct is to delete the right-hand column; the better
   answer is to let the panels use the room and pin the phone.

   The preview is not decoration on the sections where it changes as you
   type — it is the thing that makes the editor feel like building rather
   than form-filling, and it is why an artist keeps going. But it IS
   decoration on Requests, Account and Settings, so it goes there and those
   screens take the whole width. The inbox needed it most: a list-and-detail
   layout does not belong in a third of a screen.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (min-width:1161px){
  /* NOT centred. Capping .adm and auto-margining it pushed the rail away
     from the left edge on a wide screen, which is what made the sidebar
     look like it was floating in from nowhere. A navigation rail belongs
     against the edge of the window — every dashboard does this, and it is
     why Square's rail runs to the browser chrome. Cap the CONTENT
     instead; see the max-width on .adm-main further down. */
  body:not(.view-home) .adm{
    display:grid;
    grid-template-columns:264px minmax(0,1fr) 372px;
    gap:0 30px;
    max-width:none;
    margin:0;
    align-items:start;
  }
  /* the rail runs to the edge and takes the page colour, with its own
     breathing room inside rather than a gap outside */
  body:not(.view-home) .adm-rail{
    padding:22px 18px 22px 22px;
    min-height:100vh;
  }
  /* panels take the room that was empty */
  body:not(.view-home) .adm-main{max-width:none;min-width:0}
  body:not(.view-home) .adm-main .ed-panel,
  body:not(.view-home) .adm-main .ed-panel-plain{max-width:none}

  /* the phone follows you down the page instead of scrolling away at the
     exact moment you start editing the thing it is showing */
  body:not(.view-home) .adm-side{
    position:sticky;
    top:22px;
    align-self:start;
    max-height:calc(100vh - 44px);
  }

  /* ── where the preview does not earn its place ─────────────────────── */
  body:not(.view-home) .adm[data-sec="requests"] .adm-side,
  body:not(.view-home) .adm[data-sec="account"]  .adm-side,
  body:not(.view-home) .adm[data-sec="settings"] .adm-side{display:none}

  body:not(.view-home) .adm[data-sec="requests"],
  body:not(.view-home) .adm[data-sec="account"],
  body:not(.view-home) .adm[data-sec="settings"]{
    grid-template-columns:264px minmax(0,1fr);
  }
  /* the inbox finally gets a real detail pane */
  body:not(.view-home) .adm[data-sec="requests"] .rq{
    grid-template-columns:minmax(280px,360px) minmax(0,1fr);
    gap:24px;
    max-width:1280px;   /* big, but not 200-character answer lines */
  }
  /* .adm-body's 640px cap was written for forms; an inbox is a
     list-and-detail layout and needs the room. THIS was the narrow
     column with a desert to its right. */
  body:not(.view-home) .adm[data-sec="requests"] .adm-body{max-width:none}

  /* ── the detail card, now that it has space to be a page ────────────── */
  body:not(.view-home) .rq-detail{padding:26px 28px}
  /* answers pair up: two label/value columns per row instead of one long
     ladder. The dl alternates dt/dd, so a four-track grid does it with
     no markup change. */
  body:not(.view-home) .rq-detail .rq-answers{
    grid-template-columns:max-content minmax(0,1fr) max-content minmax(0,1fr);
    column-gap:26px;
  }
  body:not(.view-home) .rq-detail .rq-answers dt{padding-right:0}
  body:not(.view-home) .rq-detail .rq-answers dd{font-size:.9rem}
  /* photos become the visual centrepiece rather than postage stamps */
  body:not(.view-home) .rq-detail .rq-pics img{width:148px;height:148px}
  /* contact card reads at arm's length */
  body:not(.view-home) .rq-detail .rq-mail,
  body:not(.view-home) .rq-detail .rq-tel{font-size:1.05rem}
  body:not(.view-home) .rq-detail .rq-head b{font-size:1.6rem}
}

/* a very wide screen should not stretch a form to 1100px — text lines get
   unreadable past about 75 characters */
@media (min-width:1600px){
  body:not(.view-home) .adm-main .ed-panel,
  body:not(.view-home) .adm-main .ed-panel-plain{max-width:860px}
  /* …but the requests mount is not a form; keep the inbox at full width
     (the .rq rule above still caps it at a readable 1280px) */
  body:not(.view-home) .adm[data-sec="requests"] .ed-panel-plain{max-width:none}
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE UPGRADE BADGE — one component, used everywhere something is locked.

   Square's "Upgrade to Plus" chip works because it appears AT the locked
   control, at the moment somebody wanted it, rather than on a pricing page
   they have to go and find. The intent and the offer are in the same place.

   Two rules that stop it becoming spam:
     · it labels a control that is VISIBLE and disabled, never a blank space
       where a feature would be. Seeing the thing you would be buying beats
       being told a feature exists.
     · one per panel. A page peppered with upgrade chips reads as a demo,
       not a product.
   ═══════════════════════════════════════════════════════════════════════════ */
body:not(.view-home) .up-tag{
  display:inline-flex;align-items:center;gap:.35em;flex:none;
  padding:3px 8px;border-radius:999px;
  border:1px solid var(--amber);color:var(--amber);
  background:color-mix(in srgb,var(--amber) 8%,transparent);
  font-size:9.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  white-space:nowrap;cursor:pointer;text-decoration:none;
}
body:not(.view-home) .up-tag:hover{
  background:color-mix(in srgb,var(--amber) 16%,transparent);
}
/* a whole row that is locked: the control stays visible and legible, just
   clearly not yours yet */
body:not(.view-home) .up-row{
  display:flex;align-items:flex-start;gap:14px;
  padding:.75rem 0;border-top:1px solid var(--faint);
}
body:not(.view-home) .up-row > span{flex:1;min-width:0}
body:not(.view-home) .up-row b{display:block;font-size:.88rem;font-weight:500}
body:not(.view-home) .up-row small{
  display:block;font-size:.74rem;color:var(--dim);margin-top:2px;line-height:1.5;
}
body:not(.view-home) .up-row input,
body:not(.view-home) .up-row select{
  opacity:.5;pointer-events:none;
}

/* ── optimistic feedback ─────────────────────────────────────────────────
   The save chip already exists. What it lacked was the instant half: the
   tick should appear on the click, not on the round trip. Everything the
   editor saves is local-first anyway — Firestore is a mirror — so showing
   success immediately is honest, not a lie. If the write fails the chip
   turns red and says so, which is the only case where waiting mattered. */
body:not(.view-home) .saved,
body:not(.view-home) #adm-saved{
  transition:opacity .18s ease,color .18s ease;
}
body:not(.view-home) .saved.bad,
body:not(.view-home) #adm-saved.bad{color:var(--red)}
body:not(.view-home) .save-panel.did,
body:not(.view-home) .savebtn.did{
  background:var(--acid);border-color:var(--acid);color:#fff;
}

/* ── empty states as onboarding ──────────────────────────────────────────
   A blank panel tells an artist nothing. A dashed panel with the next
   action in it tells them what this screen is for. The requests inbox
   already does this; these are the shared bones so the others can match. */
body:not(.view-home) .empty{
  border:1px dashed rgba(20,18,16,.22);border-radius:var(--radius);
  background:transparent;box-shadow:none;
  padding:26px 22px;text-align:center;
}
body:not(.view-home) .empty b{
  display:block;font-size:1.05rem;font-weight:600;margin-bottom:6px;
}
body:not(.view-home) .empty p{
  font-size:.82rem;color:var(--dim);line-height:1.6;
  max-width:32rem;margin:0 auto 12px;
}