/* Picture Lock · "Studio" theme (17 Sep, owner: "a billion-dollar brand UI, not a motion graphic"). Restraint over
   decoration: near-monochrome neutrals, ink-black primary actions, hairline structure, layered soft depth, precise type.
   Token-first and loaded last, so the base stylesheets keep working; delete this <link> to get the old dark theme.
   --amber (the old accent, used as text/icon colour in ~50 places) becomes ink, so nothing turns unreadable; the only
   colour left is status (pass/live), which is meaning, not decoration. */
:root {
  /* ⚠️ THE SAME NEUTRALS AS getpicturelock.com, and this is not only a branding tidy-up.
     The app's ramp was COOL (blue-grey: #0E0F11 / #4B4E56 / #8C8E95) and the site's is WARM
     (#121211 / #5F5D58 / #726F68). Side by side they read as two different products, which is
     the worst possible impression to give somebody in the ten seconds between clicking "Start
     free" on the site and landing in the app.
     ⚠️ And --mut measured 3.02:1 on the old ground — BELOW WCAG AA's 4.5 for body text, at the
     sizes it is actually used. The site hit this exact defect and fixed it (its own comment
     records darkening #918E88 to #726F68 for the same reason); the app had the same hole and
     nobody had measured it. #726F68 on #FBFAF8 is 4.81:1. Measured, not eyeballed. */
  --bg:      #FBFAF8;
  --rail:    #F5F3EF;
  --panel:   #FFFFFF;
  --panel-2: #FAF9F6;
  --raised:  #EFEDE8;
  --line:    #E8E6E1;
  --line-2:  #D8D5CE;
  --text:    #121211;
  --text-2:  #5F5D58;
  --mut:     #726F68;
  --amber:   #121211;
  --amber-d: #000000;
  --amber-t: rgba(18, 18, 17, .06);
  /* ⚠️ MEASURED AGAINST EVERY LIGHT SURFACE IT LANDS ON, not just white. #1E8A55 was 4.36:1 —
     under WCAG AA's 4.5 — and it carries the scores and prices people read: "7 · 7/10", "$0.002",
     "Used by hooks & the planner". #1A794B is 5.41 on white, 5.14 on the panel, 4.63 on raised. */
  --pass:    #1A794B;
  /* 3.97:1 before — the "live" and error states are exactly the text a person must not miss */
  --live:    #C03C24;
  --blue:    #2E5BD8;
  --display: 'Geist', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
  --sans:    'Geist', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
  --mono:    'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  /* ⚠️ THE APP'S CORNERS WERE SHARPER THAN THE SITE'S. Measured across all eleven screens against
     getpicturelock.com: the site's cards sit at 16-22px and the app's at 10px, with a tail of
     3/5/6px on smaller parts. Nobody reads a radius, but it is most of why two pages made of the
     same colours and the same face still feel like different software. 14px is the site's own
     middle step; pills (999px) and dots (50%) are untouched. */
  --r: 14px;
  --lift-1: 0 1px 1px rgba(14,15,17,.04), 0 2px 6px -2px rgba(14,15,17,.06);
  --lift-2: 0 1px 1px rgba(14,15,17,.04), 0 10px 28px -10px rgba(14,15,17,.12), 0 30px 70px -30px rgba(14,15,17,.14);
  color-scheme: light;
}
body { background: var(--bg); -webkit-font-smoothing: antialiased; }

/* type: tight, confident, never loud
   ⚠️ ALIGNED WITH THE SITE, 25 Sep. The app set its display at 600 / -.035em while the landing
   page sets 400 / -.055em, so the two read as different products by the time a visitor has
   clicked once. Measured off the category first — vercel 64px/400/-.06em, linear 64px/510/
   -.022em, opus clip 60px/600/-.03em, captions 48px/400 — and the light-and-very-tight end is
   where the premium sits. One setting, both surfaces. */
h1, h2, .engine-h, .gate-h { font-family: var(--display); font-weight: 400; letter-spacing: -.052em; }
.engine-h { font-size: clamp(2.1rem, 1.3rem + 3.1vw, 3.6rem); line-height: 1.02; }
.engine-h em, .gate-h em { font-style: normal; color: var(--mut); }
.engine-sub { color: var(--text-2); }
.lbl, .made-h { letter-spacing: .1em; }

/* chrome: quiet rail, frosted top bar */
.side { background: var(--rail); border-right-color: var(--line); }
.top { background: rgba(255,255,255,.78); backdrop-filter: saturate(180%) blur(14px); -webkit-backdrop-filter: saturate(180%) blur(14px); border-bottom-color: var(--line); }
.nav button { color: var(--text-2); border-radius: 9px; }
.nav button[aria-current="true"] { background: var(--panel); color: var(--text); box-shadow: var(--lift-1), inset 0 0 0 1px var(--line); }
.nav button[aria-current="true"] svg { color: var(--text); }
@media (hover:hover) and (pointer:fine) { .nav button:hover { background: rgba(14,15,17,.04); } }
.dot { background: var(--text); }
.credits .bar { background: var(--line); }

/* the composer is the product: white, generous, softly lifted, a calm focus ring */
.engine-box { background: var(--panel); border: 1px solid var(--line); border-radius: 20px; box-shadow: var(--lift-2); }
.engine-box:focus-within { border-color: var(--line-2); box-shadow: var(--lift-2), 0 0 0 4px rgba(14,15,17,.05); }
.engine-in { padding: 20px 20px 12px; }
.engine-bar { border-top-color: var(--line); padding: 12px 14px 14px; }
.engine-way { background: var(--panel-2); border-color: var(--line); color: var(--text-2); }
.engine-way svg { color: var(--text-2); }
@media (hover:hover) and (pointer:fine) { .engine-way:hover { border-color: var(--line-2); color: var(--text); background: var(--panel); } }
.engine-eg button { border-color: var(--line); color: var(--text-2); background: rgba(255,255,255,.6); }
@media (hover:hover) and (pointer:fine) { .engine-eg button:hover { background: var(--panel); border-color: var(--line-2); color: var(--text); } }

/* actions: one ink button per view; secondary buttons are hairline */
.btn--go, .engine-go { background: var(--text) !important; color: #FFFFFF !important; border-color: var(--text) !important;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14), 0 1px 2px rgba(14,15,17,.2);
  transition: transform 140ms cubic-bezier(.23,1,.32,1), background-color 140ms cubic-bezier(.23,1,.32,1); }
@media (hover:hover) and (pointer:fine) { .btn--go:hover, .engine-go:hover { background: #2A2C31 !important; } }
.btn--go:active, .engine-go:active { transform: scale(.97); }
.btn--go:disabled, .engine-go[disabled] { opacity: 1 !important; background: #ECEBE7 !important; color: #A2A4AA !important; border-color: #ECEBE7 !important; box-shadow: none; cursor: not-allowed; }
.btn { background: var(--panel); border-color: var(--line); color: var(--text); box-shadow: var(--lift-1); }
@media (hover:hover) and (pointer:fine) { .btn:hover { border-color: var(--line-2); } }
.btn:active { transform: scale(.97); }

/* surfaces and status */
.card { background: var(--panel); border-color: var(--line); border-radius: 14px; box-shadow: var(--lift-1); }
.pill--amber { color: var(--text); border-color: var(--line-2); background: var(--panel-2); }
.chip { background: var(--panel); border-color: var(--line); }
.src[aria-pressed="true"] { background: var(--amber-t); border-color: var(--text); }

/* conversation */
.t-you { background: var(--text) !important; color: #FFFFFF !important; border-radius: 16px 16px 4px 16px; }
.t-stages li.is-now .t-what::after { background: var(--live); }

:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
/* Captions / Look pickers in the composer bar: same pill language as the other ways in */
.engine-pick { display: inline-flex; align-items: center; gap: 6px; padding: 5px 6px 5px 11px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--panel-2); font-size: var(--s--2); color: var(--text-2); }
.engine-pick select { appearance: none; -webkit-appearance: none; border: 0; background: var(--panel); border-radius: 999px; padding: 3px 22px 3px 9px;
  font: inherit; color: var(--text); box-shadow: inset 0 0 0 1px var(--line);
  background-image: linear-gradient(45deg, transparent 50%, var(--mut) 50%), linear-gradient(135deg, var(--mut) 50%, transparent 50%);
  background-position: calc(100% - 11px) 55%, calc(100% - 7px) 55%; background-size: 4px 4px; background-repeat: no-repeat; }
.engine-pick select:focus-visible { outline: 2px solid var(--text); outline-offset: 1px; }

/* ═══ Picture Lock identity: the viewfinder (17 Sep, owner: "our own distinct UI… carry it through everything") ═══
   The name is a film term: picture lock = the cut is final. So the signature is a camera viewfinder, never decoration:
   - crop-mark corners frame the ONE thing you're working on (the composer, the video, the chosen plan, the current screen)
   - a red tally light means live/recording (the brand dot, running jobs), nothing else is red
   - labels read like slate/timecode metadata (mono, tracked)
   Corners are drawn with background gradients on a pseudo-element: no extra DOM, animate with transform/opacity only. */
:root { --tally: #C73225; --vf: var(--text); --vf-len: 16px; --vf-w: 1.5px; }
.vf-corners, .engine-box { position: relative; }   /* NOT .hero-stage: it is absolutely positioned 9:16, relative broke it */
/* ⚠️ The crop marks frame THE ONE THING you are working on — the composer and the video. They were
   also drawn around the current nav item, which at 56px collapsed is a pair of brackets around a
   40px icon: the motif applied where there is nothing to frame, which reads as clutter rather than
   as framing and cheapens it everywhere else. The active item is marked by weight and colour. */
.engine-box::before, .studio .hero-stage::after {
  content: ""; position: absolute; pointer-events: none;
  background:
    linear-gradient(var(--vf), var(--vf)) top left / var(--vf-len) var(--vf-w) no-repeat,
    linear-gradient(var(--vf), var(--vf)) top left / var(--vf-w) var(--vf-len) no-repeat,
    linear-gradient(var(--vf), var(--vf)) top right / var(--vf-len) var(--vf-w) no-repeat,
    linear-gradient(var(--vf), var(--vf)) top right / var(--vf-w) var(--vf-len) no-repeat,
    linear-gradient(var(--vf), var(--vf)) bottom left / var(--vf-len) var(--vf-w) no-repeat,
    linear-gradient(var(--vf), var(--vf)) bottom left / var(--vf-w) var(--vf-len) no-repeat,
    linear-gradient(var(--vf), var(--vf)) bottom right / var(--vf-len) var(--vf-w) no-repeat,
    linear-gradient(var(--vf), var(--vf)) bottom right / var(--vf-w) var(--vf-len) no-repeat;
}
/* the composer: corners sit just outside, quiet at rest, pull in and sharpen when you start typing (it "locks on") */
.engine-box::before { inset: -10px; opacity: .22; transform: scale(1.012);
  transition: opacity 240ms cubic-bezier(.23,1,.32,1), transform 240ms cubic-bezier(.23,1,.32,1); }
.engine-box:focus-within::before { opacity: .9; transform: scale(1); }
/* the video you're editing is framed like a monitor */
.studio .hero-stage::after { inset: 10px; --vf: rgba(255,255,255,.85); --vf-len: 20px; opacity: .8; z-index: 3; }
/* current screen in the rail: corners instead of a filled pill */
.nav button[aria-current="true"] { box-shadow: none; background: transparent; }
.nav button[aria-current="true"]::after { inset: 0; --vf-len: 7px; --vf-w: 1.5px; }
.nav button { position: relative; }
/* tally light: the brand dot, and any running job */
.dot { background: var(--tally); box-shadow: 0 0 0 3px color-mix(in srgb, var(--tally) 18%, transparent); }
.t-stages li.is-now .t-what::after { background: var(--tally); }
.brand .mk { color: var(--text); }
.brand .mk rect { fill: var(--tally); }
/* slate/timecode labels */
.lbl, .eyebrow { font-family: var(--mono); letter-spacing: .14em; text-transform: uppercase; }
@media (prefers-reduced-motion: reduce) { .engine-box::before { transition: none; transform: none; } }
/* tested recipes grid */
.vrecipes { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
.vrecipe { display: grid; grid-template-columns: auto 1fr auto; grid-template-rows: auto auto; column-gap: 10px; align-items: center; text-align: left;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; box-shadow: var(--lift-1);
  transition: transform 140ms cubic-bezier(.23,1,.32,1), border-color 140ms cubic-bezier(.23,1,.32,1); }
.vrecipe:active { transform: scale(.97); }
@media (hover:hover) and (pointer:fine) { .vrecipe:hover { border-color: var(--line-2); } }
.vr-sw { grid-row: span 2; width: 22px; height: 22px; border-radius: 6px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.1); }
.vrecipe b { font-size: var(--s--1); font-weight: 600; }
.vrecipe .mut { grid-column: 2; font-size: var(--s--2); }
.vr-score { grid-row: 1 / span 2; grid-column: 3; font-family: var(--mono); font-size: var(--s--2); color: var(--pass); }


/* ── the top bar, at the website's weight ────────────────────────────────────────────────────
   getpicturelock.com's nav is a wordmark, a few quiet links and exactly ONE filled button. The app
   had a title plus four controls of near-equal weight, so nothing led and the whole strip read as
   chrome. Same parts, ranked. */
.top { gap: 8px; }
.top h1 { letter-spacing: -0.01em; }
.top .btn--sm[data-open-pal] {
  border-color: transparent; background: none; color: var(--mut);
  font-family: var(--mono); font-size: .68rem; letter-spacing: .04em; padding: 6px 8px;
}
@media (hover: hover) and (pointer: fine) {
  .top .btn--sm[data-open-pal]:hover { color: var(--text); background: var(--raised); }
}
.top .btn--sm[data-go="account"] { border-color: transparent; background: none; color: var(--text-2); }
@media (hover: hover) and (pointer: fine) {
  .top .btn--sm[data-go="account"]:hover { color: var(--text); background: var(--raised); }
}
/* the connection pill is status, not a control: it should never compete with the one real button */
.top .pill[data-conn] { font-family: var(--mono); font-size: 0.66rem; letter-spacing: .06em;
  border-color: var(--line); color: var(--mut); background: none; }


/* ── the composer, at the website's weight ───────────────────────────────────────────────────
   Measured: getpicturelock.com's hero box is 678x62. This one was 720x198 with 19 controls. The
   controls stay in the DOM (screens.js and thread.js read the pickers by querySelector) and move
   behind the two affordances the site already uses. */
.engine-plus, .engine-opts {
  appearance: none; border: 1px solid var(--line); background: var(--panel);
  color: var(--text-2); border-radius: 999px; cursor: pointer; font: 500 .8rem/1 var(--sans);
  display: inline-flex; align-items: center; gap: 6px; padding: 8px 13px; flex: 0 0 auto;
  transition: transform 120ms cubic-bezier(.23,1,.32,1), border-color 140ms cubic-bezier(.23,1,.32,1);
}
.engine-plus { width: 34px; height: 34px; padding: 0; justify-content: center; font-size: 1.05rem; }
.engine-plus:active, .engine-opts:active { transform: scale(.97); }
@media (hover: hover) and (pointer: fine) {
  .engine-plus:hover, .engine-opts:hover { border-color: var(--line-2); color: var(--text); }
}
.engine-plus[aria-expanded="true"], .engine-opts[aria-expanded="true"] {
  background: var(--raised); color: var(--text); border-color: var(--line-2);
}
/* the two rows are closed until asked for. display:none, not height:0 — a hidden control that
   still takes a tab stop is worse than a visible one. */
.engine-box:not([data-open~="src"]) .engine-way,
.engine-box:not([data-open~="src"]) .engine-style,
.engine-box:not([data-open~="opt"]) .engine-pick { display: none; }
.engine-bar { flex-wrap: wrap; align-items: center; }


/* ── a legibility floor, measured against the website ────────────────────────────────────────
   ⚠️ 841 elements in this app rendered under 11px, on a ladder down to EIGHT — the "creator" plan
   pill, 44 of them. The website's smallest step is 10.5px and it never goes below it. Small type
   is how a dense tool signals density; unreadable type is just unreadable, and it was being used
   for plan names and scores that people are meant to act on. Nothing renders below 10.5px now. */


/* ── the composer IS the website's hero box ──────────────────────────────────────────────────
   Measured side by side, three things still differed after the palette match:
     the textarea was 17.28px against the site's 16px
     the box stood 159px tall against 122px
     the shadow was still tinted with the OLD cool ink, rgba(14,15,17,…) vs rgba(18,18,17,…)
   The last one is the kind of difference nobody can name and everybody feels. */
.engine-box {
  box-shadow: 0 1px 2px 0 rgba(18,18,17,.05), 0 10px 28px -12px rgba(18,18,17,.10),
              0 30px 70px -40px rgba(18,18,17,.12);
}
.engine-box [data-engine-say] { font-size: 1rem; line-height: 1.5; }
.engine-in { padding: 18px 20px 4px; }
.engine-bar { padding: 8px 14px 12px; gap: 8px; }


/* ── legibility on the dark surfaces ─────────────────────────────────────────────────────────
   ⚠️ .ed-src sat in --text-2 (#5F5D58) on the editor's near-black stage: 3.02:1. That token is a
   LIGHT-theme secondary; on a dark surface the ramp has to invert or the label is a smudge. */
.ed-stage .ed-src, .ed-src { color: #96948F; }

/* ── nothing may push a phone sideways ───────────────────────────────────────────────────────
   ⚠️ The comparison tables on /f/styles and /f/formats, and the API code block on Connect, all
   ran past a 390px screen. Wide tables and code are the two things allowed to scroll in their own
   container — but only if they are GIVEN one. */
.conn-c pre, pre.apicode, .apicode { overflow-x: auto; max-width: 100%; }
.conn-c { min-width: 0; }
@media (max-width: 560px) {
  table { display: block; overflow-x: auto; max-width: 100%; }
}


/* ── syntax colours on the DARK code block ───────────────────────────────────────────────────
   ⚠️ .k rendered in rgb(18,18,17) — near black — inside pre.code whose background is rgb(11,10,10).
   Measured 1.05:1: the keywords in the API example were effectively invisible, on the one screen
   that exists to show developers how to call us. A light-theme syntax colour on a dark surface. */
pre.code .k, .conn-c .k { color: #E8E6E1; }
pre.code .s, .conn-c .s { color: #E9C46A; }
pre.code .c, .conn-c .c { color: #9A958A; }
pre.code, .conn-c pre { overflow-x: auto; max-width: 100%; }

/* the "pass" pill tints its own background, which eats the contrast the token was chosen for */
.pill--pass { color: #14633D; }


/* ⚠️ a button that is telling you WHY it cannot act is still text somebody has to read. "Needs a
   server" measured 2.09:1. Disabled controls are exempt from AA, but the exemption is for controls
   you are meant to ignore — not for the one explaining what is wrong. */
/* ⚠️ !important, because theme-studio's own .btn--go rule uses it — without matching that the
   disabled colour never applied and the button stayed white-on-near-white at 2.09:1. */
.btn--go:disabled, .btn--go[disabled], .btn[disabled], .engine-go:disabled {
  color: #4F4C47 !important; background: #E7E4DE !important;
  border-color: var(--line-2) !important; opacity: 1 !important; }


/* ── the app reads like the website ──────────────────────────────────────────────────────────
   Measured side by side: a section on getpicturelock.com sits in a 1160px centred column with
   112px of vertical air and a 44px heading. An app screen was full-bleed, 22px from the top, with
   a 23px heading. Same colours, same face, same corners — and still obviously a different piece
   of software, because the RHYTHM was different. This is that rhythm, at a tool's scale rather
   than a landing page's: content stops sprawling, screens breathe, headings lead.
   ⚠️ .screen--flush is the editor. It is a timeline and a monitor and it must stay full-bleed. */
.screen:not(.screen--flush) {
  max-width: 1160px;
  margin-inline: auto;
  padding: 52px 24px 88px;
}
/* ⚠️ the headings live inside a .head wrapper, so a direct-child selector matched nothing and the
   padding changed while the type did not. Scoped to the screen's leading heading, not every h2. */
.screen:not(.screen--flush) > h2,
.screen:not(.screen--flush) > .head > h2,
.screen:not(.screen--flush) > .head > .h2,
.screen:not(.screen--flush) .h2 {
  font-size: clamp(1.7rem, 2.4vw, 2.1rem);
  line-height: 1.12;
  letter-spacing: -0.03em;
  text-wrap: balance;
  margin-bottom: 10px;
}
/* the site's eyebrow: mono, uppercase, tracked, quiet */
.screen:not(.screen--flush) > .head .lbl,
.screen:not(.screen--flush) > .head .eyebrow,
.screen:not(.screen--flush) > .eyebrow {
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--mut);
}
/* sections inside a screen get the site's spacing between them, not a dashboard's */
.screen:not(.screen--flush) > * + .head,
.screen:not(.screen--flush) > * + h2 { margin-top: 64px; }
.screen:not(.screen--flush) > .head { margin-bottom: 26px; max-width: 46ch; }
.screen:not(.screen--flush) > .head > .lbl { display: block; margin-bottom: 12px; }

/* ── the fold: the site's hero light, brought into the Create screen ──────────────────────────
   27 Sep. Screenshotted getpicturelock.com against app.getpicturelock.com side by side. Measured,
   both already share ground (#FBFAF8), ink (#121211) and face (Geist) — the palette fold was done.
   What made them still read as two different products was LIGHT: the site's hero sits under a wash
   and the app's Create screen was flat, so the site looked photographed and the app looked printed.
   ⚠️ A WASH, NOT A RAINBOW — the same two-hue decision the site's own comment argues for: the warm
   of tungsten and the cool of daylight, the one colour call a colourist makes all day.
   ⚠️ THE FIRST VERSION OF THIS PASSED ITS OWN GATE AND LOOKED BROKEN. The site's hero is full-bleed
   so its wash has no visible edge; .engine is an 801px column inside .main, so the identical recipe
   painted a RECTANGLE — a hard vertical seam down both sides, a hard cut under the header, and a
   blue block rather than daylight. The colour gate measured hue and said OK, because it sampled
   three points and a rectangle is the right colour in the middle. Two fixes, both required:
     1. a RADIAL mask, so the wash falls to nothing on every edge instead of only the bottom. One
        mask image, no mask-composite — intersect support is the kind of thing that silently no-ops.
     2. the blobs pulled inside the box and the opacity dropped .50 → .34, because a wash confined
        to a column concentrates what the site spreads across the viewport.
   The gate now measures the EDGE as well as the middle. */
.engine { position: relative; isolation: isolate; }
.engine::before {
  content: ""; position: absolute; left: -16%; right: -16%; top: -10%; height: 86%;
  z-index: -1; pointer-events: none; opacity: .34;
  background:
    radial-gradient(40% 48% at 22% 20%, rgba(233,168,80,.72) 0%, rgba(233,168,80,0) 72%),
    radial-gradient(42% 50% at 80% 28%, rgba(86,150,178,.62) 0%, rgba(86,150,178,0)  74%),
    radial-gradient(54% 40% at 50% 4%,  rgba(214,200,178,.5)  0%, rgba(214,200,178,0) 72%);
  filter: blur(52px);
  -webkit-mask-image: radial-gradient(68% 60% at 50% 24%, #000 0%, #000 30%, transparent 76%);
          mask-image: radial-gradient(68% 60% at 50% 24%, #000 0%, #000 30%, transparent 76%);
}
/* ⚠️ KNOWN, MEASURED, NOT FIXED: the wash bands. Amplified ×8 against the control it shows
   concentric contour rings, and one scanline through the warm blob holds just 21 distinct values.
   That is 8-bit quantisation, and blur(52px) is what stretches each step wide enough to see.
   I built the standard fix — an feTurbulence dither on .engine::after — and MEASURED IT AS INERT:
   mean high-pass went 5.68 → 5.88 levels, i.e. nothing, and it stayed nothing at every opacity
   from .035 to .35. So it was removed rather than shipped: a pseudo-element, a blend mode and a
   data URI that do not do the job are worse than the artefact. At 1:1 the contours sit at about
   one level, right at threshold, and getpicturelock.com's hero ships the identical recipe — so
   the app and the site band the same way, which is the point of the fold. If it ever has to go,
   the lever is a real dither (a pre-rendered noise tile), not this one. */
/* ⚠️ THE BLEED CAUSED A HORIZONTAL SCROLLBAR ON EVERY NARROW SCREEN. On desktop .engine is an
   801px column inside a 1384px main, so -16% either side stays comfortably inside and the desktop
   check passed at 1920 with 0px over. At 390px and 768px .engine fills the viewport, so the same
   -16% hangs 31px and 91px off the right edge and the page scrolls sideways — on phones, where a
   sideways-scrolling page is most obvious and least forgivable. Measured against a wash-off
   control at both widths: scrollWidth was exactly clientWidth with the wash off and over it with
   the wash on, with no other offender on the page, so this was the wash and nothing else.
   The bleed only ever bought spread it has room for, so it is dropped where there is no room.
   ⚠️ NOT overflow-x:clip on an ancestor: that is the precise tool but it is dropped wholesale by
   older Safari, which would hand those users the scrollbar back silently. A width guard works
   everywhere. The radial mask still fades the wash to nothing well inside the element's own edge,
   so zeroing the bleed costs softness, not the soft edge. */
@media (max-width: 1180px) {
  .engine::before { left: 0; right: 0; }
}

/* ── the front door ──────────────────────────────────────────────────────────────────────────
   ⚠️ I SPENT A DAY ON THE SCREEN BEHIND THIS ONE. The wash, the composer, the crop marks — all
   on the Create screen, which sits BEHIND the sign-in. Almost nobody reaching app.getpicturelock.com
   has an account yet, so THIS is the product's first impression and it was a generic SaaS login:
   a hard #0E0E0D slab against warm cream, a card marooned in a field of empty page, and a heavy
   drop shadow. Nothing of the site it came from.
   Four things, all measured against getpicturelock.com rather than invented:
     1. the site's own two-hue wash, behind the GATE, where a first-time visitor can see it
     2. the dark half in the site's INK (#121211) warmed, not a blue-black slab
     3. the site's lift, not a 2014 drop shadow
     4. the card higher on the page, because dead space under a login reads as an unfinished page */
/* ⚠️ DO NOT SET position HERE. .gate is `position: fixed; inset: 0` — the whole sign-in is an
   OVERLAY. Setting position:relative took it out of that and let it flow into the document, so
   the card rendered BELOW the Create screen with the page footer and a hard seam above it. It is
   already a positioned ancestor, so ::before needs nothing from us. */
[data-gate] { isolation: isolate; }
[data-gate]::before {
  content: ""; position: absolute; left: -10%; right: -10%; top: -18%; height: 92%;
  z-index: -1; pointer-events: none; opacity: .40;
  background:
    radial-gradient(42% 50% at 20% 18%, rgba(233,168,80,.72) 0%, rgba(233,168,80,0) 72%),
    radial-gradient(44% 52% at 82% 26%, rgba(86,150,178,.62) 0%, rgba(86,150,178,0)  74%),
    radial-gradient(56% 42% at 50% 2%,  rgba(214,200,178,.5)  0%, rgba(214,200,178,0) 72%);
  filter: blur(54px);
  -webkit-mask-image: radial-gradient(70% 62% at 50% 26%, #000 0%, #000 32%, transparent 78%);
          mask-image: radial-gradient(70% 62% at 50% 26%, #000 0%, #000 32%, transparent 78%);
}
@media (max-width: 1180px) { [data-gate]::before { left: 0; right: 0; } }

/* ⚠️ THE CARD SAT DEAD-CENTRE IN 100vh. On a 900px window that is ~230px of nothing above and
   ~270px below — the page reads as unfinished rather than composed. Sit it in the upper third,
   which is where a form belongs and where the wash actually falls. */
/* ⚠️ place-items, not align-items: .gate sets the shorthand, so a longhand declared elsewhere is
   a coin toss. The card sat dead-centre of 100vh — ~230px of nothing above it and ~270px below,
   which reads as an unfinished page rather than a composed one. */
[data-gate] { place-items: start center !important; padding-top: clamp(32px, 10vh, 110px) !important; }

.gate-split {
  border-radius: 20px;
  box-shadow: var(--lift-2);
  border: 1px solid var(--line);
}
/* the dark half: the SITE'S ink, warmed — not a blue-black slab borrowed from a dashboard */
.gate-right {
  background: linear-gradient(158deg, #1A1815 0%, #121211 58%, #16140F 100%) !important;
  color: #EDE9E1 !important;
}
.gr-s, .gr-n span { color: #ADA79C !important; }
.gr-f { color: #8A857B !important; border-top-color: #2A2723 !important; }
/* the brass the numbers deserve — the one accent the site allows itself */
.gr-n b { color: #E9A850 !important; }
