:root{
  /* How far every top bar, heading and row starts from the edge of the page. One value,
     so the logo, the account menu, "Browse by Category", "All Templates" and the cards all
     line up — and still line up when it shrinks on a narrow screen. Change it here only. */
  --edge:40px;
  /* THE HEADER'S HEIGHT, IN TWO PARTS. --navh is the top row and is a number because that row
     is a fixed height. --headh is the WHOLE header, both rows, and is measured from the page
     itself by syncHeadHeight() — the second row holds a search bar whose height depends on the
     text in it, and a guessed number would be wrong the first time anything about it changed.
     Everything else on the site that sticks stops below --headh. The fallback here is only for
     the moment before the script runs. */
  --navh:68px; --headh:146px;
  --bg:#171717; --panel:#232323; --ink:#F2F2F2; --muted:#8C8C8C; --line:#333333;
  --accent:#3E6BE6; --accent-ink:#5C82FF; --soft:#1A1A1A; --danger:#E0483F;
  /* The admin blue and its variations. Defined once, here, so changing the admin colour
     is a one-line edit rather than a hunt through the file. */
  --admin:#3765EE; --admin-dark:#2B54D4; --admin-soft:rgba(55,101,238,.12); --admin-tint:#E8EDFD;
  /* WHAT SOMEONE IS, WRITTEN ONCE. Free, Pro and Admin each show up in several places — a
     badge, a switch, and the filter that shows only admins — and they all have to agree, or
     the same fact reads as different things in one glance. Nothing below names these
     colours itself; everything reads them. */
  /* Destructive, and the one warning that has to look like one. Written here with the rest
     so the delete button, the warning line and the failed-load text cannot drift apart. */
  --danger:#B4443C; --danger-dark:#9C382F;
  --plan-free:#1F7A4D; --plan-free-tint:#DFF3E6;
  --plan-pro:#B02A67;  --plan-pro-tint:#FCE4EF;
  --role-admin:#9A4507; --role-admin-tint:#FDF0DC; --role-admin-tint-h:#FAE6CA;
  /* THE SAME TWO COLOURS, BRIGHTER, FOR SOLID BLOCKS — asked for 28 August 2026, looking at the
     Pro and Admin switches.
     WHY THEY ARE SEPARATE AND NOT SIMPLY BRIGHTER VERSIONS OF THE ABOVE. Those two are worn in
     two quite different ways: as a SOLID BLOCK on a switch, where deep reads as drab, and as
     TEXT on a pale tint in the plan and role badges, where bright reads as illegible. Measured
     rather than judged by eye — as badge text the bright pair falls to 3.6 and 2.7 against
     their tints, from 5.2 and 5.8, which is below the line for text that size.
     So: the deep pair keeps the words readable, the bright pair fills the switches. Both are
     checked against the white knob, which has to stay visible on top of them (4.9 and 3.7). */
  --plan-pro-vivid:#DB0A72;
  --role-admin-vivid:#E35A00;
  /* Templette's own colour and its soft tint. Every orange in the interface used to be a
     literal repeated in seven places; they all read these two now, so changing the brand
     colour is a one-line edit. */
  --brand:#F40628; --brand-soft:#FDE8EB;
  /* The highlight you get when you drag across text on the public site. Deliberately NOT the
     brand colour: dragging over a page painted every heading in the same red as the buttons,
     so half the page read as if it had been flagged for a reason. A pale blue is what a text
     selection looks like everywhere else, which is exactly why it goes unnoticed.
     The ink is dark BECAUSE the highlight is pale — and it is forced rather than left alone,
     since the site has white text on dark cards, which would otherwise vanish into it.
     The editor and the admin area are not touched — they never used this. */
  --sel:#BBD6FB; --sel-ink:#16161A;
  --field:#1A1A1A; --field-bd:#3D3D3D; --hover:#303030; --dim:#B3B3B3; --bdh:#4D4D4D; --dots:#6E6E6E; --rtrack:#3A3A3A; --accent-soft:rgba(92,130,255,.20);
  /* The resting surface of every control in the right panel, and the same one lifted for
     hover. Filled rather than outlined since 26 August 2026 — see the block by .field. */
  --ctl-bg:#2A2A2A; --ctl-bg-h:#333333;
  --radius:11px; --card-radius:18px; --shadow:0 6px 24px rgba(0,0,0,.35), 0 1px 2px rgba(0,0,0,.25); --canvas:#5E5E5E;
  /* Every window that floats over the page reads this one line: menus, popups, dialogs, the
     two toolbars. They each had their own before — eleven different shadows, some of them
     four times the weight of the others — which is what made them look like parts of
     different programs. Changing the depth of the whole app is now a one-line change. */
  /* HALVED ON 26 AUGUST 2026, at Mounir's word: every distance in it — offset, blur and the
     inward pull — is half what it was. The opacities are untouched, because he asked for a
     smaller shadow and not a fainter one. Both floating bars read this one variable, which is
     why they were already identical when he asked whether they were; halving it here moves
     the two together, along with every menu and popover in the editor. */
  --float-shadow:0 10px 24px -6px rgba(0,0,0,.60), 0 4px 9px -3px rgba(0,0,0,.45), 0 1px 2px rgba(0,0,0,.40);
  font-family:'Google Sans Flex','Schibsted Grotesk','Inter',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
}
*{ margin:0; padding:0; box-sizing:border-box; }
body{ background:var(--bg); color:var(--ink); -webkit-font-smoothing:antialiased; }
.hidden{ display:none !important; }
/* The `hidden` ATTRIBUTE, made to actually work.
   Browsers hide it via their own [hidden]{display:none}, but any author rule that sets a
   display — .tf-dd a{display:block}, .pager{display:flex} — beats it, and the element
   stays on screen with hidden set. Nothing in the code looks wrong when that happens: the
   line that hides it is there and running. This stylesheet had grown eight separate
   one-off patches for it before this rule replaced them. */
[hidden]{ display:none !important; }
button{ font-family:inherit; cursor:pointer; }

/* ---------- EDITOR (dark warm theme) ---------- */
/* ================= EDITOR THEME =================================================
   Colour and type for the editor only. Home, projects, profile and admin keep their
   own accents — these variables are declared on .editor, so they apply to it and
   everything inside it and stop at its edge.

   ACCENT — blue. One colour does all the work: the primary button, the sliders, the active
   tab, selection handles, focus rings. Nothing else in the editor is coloured, which is what
   makes the accent mean something when it appears.
     It was an indigo-violet, #6C5CE7, until 26 August 2026. Mounir moved the selected-button
     state to #3B5FE0 and then pointed at the slider and the Download button: use the same
     blue. One accent, so they cannot disagree — which is the whole reason it is one variable.
     ON 6 SEPTEMBER 2026 HE SENT A LIGHTER, BRIGHTER BLUE — #3D95FF — and asked for the whole
     editor to take it. It is a good deal lighter than the one it replaces, and that costs
     something worth writing down: white on #3D95FF measures 3.0:1, where white on #3B5FE0
     measured 5.3:1. Three to one is the line for a control's own shape and for large text; it
     is under the 4.5:1 line for ordinary-sized words. So the Download button's label and a
     selected button's label are now readable rather than comfortably readable. He was told,
     and it is his call. If it is ever to be fixed without losing the colour, the fix is to
     keep #3D95FF for fills, rings and handles and use a darker step of it behind small white
     words — the variable is already separate (--accent-ink), so it is a one-line change.

   TYPE — Inter, and five sizes under 16px, nothing in between:
     10  micro: keyboard hints, badges, pixel readouts
     11  small labels above a field
     12  secondary text: hints, counts, muted descriptions
     13  the default — buttons, menu items, field values, layer names
     14  emphasis: panel headings, section titles
   Sizes of 16 and above are headings and dialog titles and are left alone.
   tools/test-editortheme.cjs fails if a size outside that set appears. */
.editor{
  /* --accent-ink is the hover. It was a LIGHTER violet, which on a filled button reads as
     the button switching off. Darkening is the convention for a solid primary button. */
  --accent:#3D95FF; --accent-ink:#3480DB; --accent-soft:rgba(61,149,255,.22);
  /* pink, not the accent blue, for putting a picture into a frame — see the note by the rules
     that use it. Asked for 10 September 2026. */
  --drop-accent:#DB2777; --drop-accent-wash:rgba(219,39,119,.18);
  /* WHAT A SELECTED BUTTON LOOKS LIKE, asked for 26 August 2026: white text on a solid fill,
     everywhere in the editor. Black first, blue an hour later, from the swatch Mounir sent.
     Two variables rather than colours written into a dozen rules, so the next selected state
     cannot come out a slightly different blue.
     THE EDGE IS GONE WITH THE BLACK. It existed because black on a near-black panel had
     nothing to separate it from the panel; a blue this bright separates itself, and an edge
     on it reads as a stray outline. */
  --sel-bg:#3D95FF; --sel-fg:#FFFFFF; --sel-edge:none;
  /* PROPERTIES PANEL — proportions taken from the reference field. Five numbers, and
     every control in the panel is built from them, so nothing can end up a pixel out.
       --ctl-h   how tall a control is: field, dropdown, colour swatch, hex box, all of it
       --ctl-r   how round
       --ctl-x   from a control's edge to its text (about a third of its height)
       --pp-x    from the panel's edge to everything inside it — the left/right margin
       --pp-gap  between two controls sitting side by side */
  --ctl-h:36px; --ctl-r:4px; --ctl-x:12px; --pp-x:14px; --pp-gap:10px;
  /* One size for every icon in the panel that MEANS something — align, stroke alignment,
     fill and gradient, the stroke-style rows, and the letters and marks inside fields.
     They were drawn at 12, 13, 14, 15, 16, 18 and 19px depending on when each was written.
     Chevrons and ticks are deliberately not included: they are furniture, not content, and
     matching them to the content would make them shout. */
  --pico:19px;
  /* LEFT TOOL RAIL. Five things have to move with it: the canvas gutter, the scroll
     indicator, the two slide-out panels and the file menu. They keep their old distances
     from the rail's edge rather than their old absolute positions, so changing this one
     number moves the whole set correctly. Tried 60px; back to 46. */
  --rail-w:46px;
  font-family:'Inter','Schibsted Grotesk',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
  height:100vh; display:flex; flex-direction:column; background:var(--bg); color:var(--ink); color-scheme:dark; accent-color:var(--accent); }
/* On the light theme the tint is a flat colour rather than a transparency, so it does not
   darken whatever it happens to sit on. */
body.light .editor{ --accent-soft:#E8F2FF;
  /* Every border in the editor comes from one of these three. --bdh is a step darker so a
     hover is still visible once the resting state is this light. */
  --line:var(--ed-line); --field-bd:var(--ed-line); --bdh:var(--ed-line-h);
  --hover:var(--ed-hover); }
.ed-top{ display:flex; align-items:center; gap:14px; padding:8px 14px; background:var(--panel); border-bottom:1px solid var(--line); position:relative; }
.ed-title{ position:absolute; left:50%; transform:translateX(-50%); font-weight:700; font-size:13px; color:var(--ink); background:none; border:none; padding:7px 14px; display:flex; align-items:center; gap:8px; max-width:40%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ed-title::before{ content:""; width:6px; height:6px; border-radius:50%; background:#5FBF6E; }
.ed-actions{ display:flex; align-items:center; gap:8px; margin-left:auto; }
/* ---- Floating bottom island: undo/redo · zoom · theme ---- */
.float-island{ position:fixed; left:50%; transform:translateX(-50%); bottom:18px; z-index:120; display:flex; align-items:center; gap:5px; padding:5px 7px; background:var(--panel); border:1px solid var(--line); border-radius:12px; box-shadow:var(--float-shadow); }
.fi-group{ display:flex; align-items:center; gap:2px; }
/* With the separator lines gone, the space between groups is what tells them apart. */
.float-island > .fi-group + .fi-group,
.float-island > .fi-group + .fi-btn,
.float-island > .fi-btn + .fi-btn,
.float-island > .fi-btn + .fi-group{ margin-left:8px; }
/* THE SEPARATOR LINES BETWEEN THE ISLAND'S ICONS ARE GONE, asked for 26 August 2026. The
   groups are still groups — they keep their own tighter spacing, and the gap between groups
   does the separating now, which is what the line was doing badly. The rule stays as a
   no-op guard: any .fi-sep left in the markup by a future edit shows nothing. */
.fi-sep{ display:none; }
.fi-btn{ width:27px; height:27px; display:grid; place-items:center; border:none; background:transparent; border-radius:8px; color:var(--dim); cursor:pointer; transition:background .12s,color .12s; }
.fi-btn:hover{ background:var(--hover); color:var(--ink); }
.fi-btn:disabled{ opacity:.35; cursor:default; }
/* APPS YOU HAVE OPENED BEFORE, at the left end of the island — 11 September 2026.
   Ordinary island buttons, so they are the same size and take the same hover as fit-to-screen
   and the theme switch; only what is inside them is different.
   IT TAKES NO ROOM AT ALL WHILE IT IS EMPTY, and that needs no rule here: the stylesheet has
   one [hidden] rule for the whole site, strong enough to beat a display set on a group. A
   second one written here would be the ninth one-off patch of the kind that rule replaced. */
.fi-recent{ gap:2px; }
/* The picture fills more of the button than a drawn icon does, because it is a rounded square
   with its own edge rather than a line drawing that needs air around it. */
.fi-app-art{ display:block; width:21px; height:21px; border-radius:6px; object-fit:contain; }
/* An app's own colour, for the outline shown until its picture arrives. The hover still comes
   from .fi-btn, so these behave like every other button on the island. */
.fi-app svg{ display:block; }
/* THE CROSS THAT TAKES A SHORTCUT OFF, shown only while the pointer is on it — 1 November 2026. */
.fi-app{ position:relative; }
.fi-app-x{ position:absolute; top:-6px; right:-6px; z-index:2; width:16px; height:16px; display:grid; place-items:center;
  border-radius:50%; background:var(--panel); color:var(--ink); box-shadow:0 0 0 1px var(--line), 0 1px 3px rgba(0,0,0,.18);
  opacity:0; transform:scale(.8); pointer-events:none; transition:opacity .12s ease, transform .12s ease; cursor:pointer; }
.fi-app:hover .fi-app-x{ opacity:1; transform:none; pointer-events:auto; }
/* THE NAME, in a small dark pill above the icon — the Stageshow tooltip: 22px tall, 11px words,
   fading up a couple of pixels. 1 November 2026, asked for, in place of the browser's own. */
.fi-app-tip{ position:absolute; left:50%; bottom:calc(100% + 10px); z-index:3; display:flex; align-items:center; height:22px; padding:0 8px;
  border-radius:6px; background:#26262a; color:#fff; font-size:11px; font-weight:500; white-space:nowrap; pointer-events:none;
  opacity:0; transform:translate(-50%,2px); transition:opacity .1s ease, transform .1s ease; }
.fi-app:hover .fi-app-tip, .fi-app:focus-visible .fi-app-tip{ opacity:1; transform:translate(-50%,0); transition-duration:.15s; }

.fi-zoom{ gap:10px; padding:0 4px; }
.fi-zoom input[type=range]{ width:102px; cursor:pointer; }
/* No stroke, and the same size as every other icon on the island — it is one of them now
   rather than a word in a box. */
.fi-fit{ border:none; background:transparent; }
.sep{ width:1px; height:22px; background:var(--line); margin:0 4px; }
.ghost{ background:transparent; border:1px solid var(--field-bd); color:var(--dim); padding:8px 13px; border-radius:9px; font-size:13px; font-weight:600; }
.ghost:hover{ background:var(--hover); color:var(--ink); }
.primary{ background:var(--accent); color:#fff; border:none; padding:9px 15px; border-radius:9px; font-size:13px; font-weight:700; }
.primary:hover{ background:var(--accent-ink); }

/* THE EDITOR'S OWN TOP BAR — 11 September 2026, Adobe Express as the reference.
 *
 * Empty on purpose. Things get moved into it one at a time, from here on.
 *
 * BLACK IN BOTH THEMES, which is not an oversight. Adobe's is black whichever way round the
 * rest of their app is, and a bar that flips to white on the light theme stops being the fixed
 * dark edge the whole layout is read against. One variable, so softening it later is one number.
 *
 * It is a flex row already, with the space handed out by the gaps rather than by margins on
 * whatever goes in, so the first thing added does not have to bring its own spacing.
 */
/* 48 to start with, then up 40% on 11 September 2026 at his word. Everything in the bar is
   centred on its height, so this one number is the whole change. */
.editor{ --edbar-h:67px; --edbar-bg:#000000; }
.ed-bar{ flex:none; height:var(--edbar-h); display:flex; align-items:center; gap:10px;
  padding:0 12px; background:var(--edbar-bg); color:#fff; position:relative; z-index:60;
  user-select:none; }
body.light .editor{ --edbar-bg:#000000; }
/* THE LOGO, first thing in the bar and hard against the left. 11 September 2026.
   A separate file from the site header's logo because the word in that one is near-black and
   would vanish here; same shapes, the word turned white, the mark still red.
   It is a link, styled as one is not: no underline, no link colour. */
.ed-logo{ flex:none; display:flex; align-items:center; height:100%; padding:0 4px;
  text-decoration:none; color:inherit; }
.ed-logo img{ display:block; height:19px; width:auto; }
.ed-logo:hover{ opacity:.85; }
/* A BUTTON IN THE BAR: a word, and a chevron if it opens something. 11 September 2026.
   One rule for all of them, so the second and third to be moved up here need no rules of
   their own and cannot come out a different size from File.
   THE HOVER IS AS LIGHT AS IT CAN GO. He asked for light grey. A real light grey — the
   #D0D0D0 sort — would leave white letters sitting on top of it with almost nothing between
   them, so this is white at low strength, which on black reads as grey and keeps the word
   readable. Say the word and it goes lighter. */
.ed-btn{ flex:none; display:inline-flex; align-items:center; gap:5px; height:28px;
  padding:0 9px; border:none; border-radius:7px; background:transparent; color:#fff;
  font-size:13px; font-weight:600; line-height:1; cursor:pointer; white-space:nowrap;
  transition:background .12s; }
.ed-btn:hover{ background:rgba(255,255,255,.18); }
/* Open looks like hover held down, so it is plain which menu belongs to which button. */
.ed-btn.active{ background:rgba(255,255,255,.24); }
.ed-btn .ed-chev{ display:block; opacity:.7; margin-right:-1px; }
/* The icon it had in the tool rail, kept, and on the left of the word — asked for
   11 September 2026. Full strength, unlike the chevron: the icon says what the button is,
   the chevron only says it opens. */
.ed-btn .ed-ico{ display:block; }
/* An icon that is a PICTURE rather than a drawing — Apps, 11 September 2026. Sized in the rule
   as well as on the tag so it holds its place before the file has arrived, and never squashed
   if the file is not perfectly square (his is 50 by 49). */
.ed-btn .ed-ico-art{ width:19px; height:19px; object-fit:contain; }
/* DOUBLE THE GAP AFTER THE LOGO, asked for the same day. The bar hands out 10px between
   everything; this adds a second 10 so the logo reads as its own thing and File as the first
   of the controls. Written as "the button after the logo" rather than on File itself, so
   whatever ends up first in the bar gets the same air. */
.ed-logo + .ed-btn{ margin-left:10px; }
/* A BUTTON IN THE BAR THAT IS ONLY AN ICON — undo and redo, 11 September 2026. Square, and
   the same height as the ones with words in them, so a row of both sits on one line rather
   than stepping up and down. Same hover, from the same rule below.
   THE SPACING IS THE BAR'S OWN AND NOTHING ELSE. Asked for in the same breath: "keep spacing
   between elements consistent". The 10px gap on .ed-bar is the only thing that separates
   anything in here, and the one exception is the double gap after the logo, which is
   deliberate and written above. No margins on individual buttons. */
.ed-ibtn{ flex:none; display:grid; place-items:center; width:28px; height:28px; border:none;
  border-radius:7px; background:transparent; color:#fff; cursor:pointer;
  transition:background .12s,opacity .12s; }
.ed-ibtn:hover{ background:rgba(255,255,255,.18); }
/* Nothing left to undo: still there, still in place, plainly not available. */
.ed-ibtn:disabled{ opacity:.35; cursor:default; background:transparent; }
/* A FILLED PILL IN THE BAR. Asked for on Apps, 11 September 2026, from a picture of a
   "Download" button: bigger than the others, no edge, a light grey fill, fully rounded ends.
   It replaced an outline asked for an hour earlier.
   A separate class rather than a rule on Apps itself, so the next button that should stand
   out this way does not get its own. */
/* The gap is its own, wider than the 5px the small buttons use: at this size the icon and the
   word were touching. Asked for on 11 September 2026. */
.ed-pill{ height:36px; padding:0 16px; border:none; border-radius:999px;
  background:#35353A; font-size:14px; gap:9px; }
.ed-pill:hover{ background:#43434A; }
.ed-pill.active{ background:#4C4C54; }
/* APPS SITS IN THE MIDDLE OF THE BAR, and centred on the WINDOW rather than on whatever room
   is left between the buttons either side. Taken out of the row and pinned, so adding or
   removing anything to its left or right cannot nudge it off centre. */
.ed-apps{ position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); }

/* THE DESIGN'S NAME IN THE BAR — 11 September 2026, moved up from the properties panel.
   Still click-and-type. On black now, so it needs its own colours, and it needs a width it
   cannot grow past: a long name would otherwise push Apps off the middle of the bar.
   16em is about thirty characters, and anything longer ends in an ellipsis until you click
   into it, when it opens out to show the whole thing. */
.ed-name{ flex:0 1 auto; max-width:16em; min-width:4em; padding:6px 10px; border-radius:8px;
  font-size:14px; font-weight:600; color:#fff; cursor:text; outline:none;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; transition:background .12s; }
.ed-name:hover{ background:rgba(255,255,255,.12); }
.ed-name:focus{ background:rgba(255,255,255,.16); box-shadow:0 0 0 1px var(--accent);
  max-width:none; overflow:visible; text-overflow:clip; }
/* The saving light beside it. Its own greys were picked against a pale panel; on black the
   two quiet states are unreadable, so they are lifted. Saving amber, saved green and the red
   of a failure are left exactly as they were — those are the ones that have to be recognised
   at a glance, and they read on black already. */
.ed-bar .save-status{ flex:none; color:#9A958A; }
.ed-bar .save-status[data-state="saved"]{ color:#8FBF89; }

/* SHARE AND DOWNLOAD IN THE BLACK BAR — 11 September 2026, from the picture he sent.
 *
 * They moved here whole, out of the top of the properties panel, and they keep the rules that
 * make them work — the wrapper both menus hang off, and everything inside those menus. What
 * changes here is only how the two BUTTONS look, because they are now on black rather than on
 * a panel that follows the theme.
 *
 * SPACING: the wrapper is one item in the row, so the bar's 10px puts it away from its
 * neighbours, and the 10px inside it separates the two buttons. One number, used twice.
 */
.ed-bar .exp-wrap{ flex:none; display:flex; align-items:center; gap:10px; margin-left:auto; }
/* Share: the same height as Download and the same quiet grey it has always had beside it.
   IT WAS A SQUARE WITH JUST THE ICON until he asked for the word on 11 September 2026, so the
   width is no longer fixed — the padding and the word decide it, the way Download's does. The
   gap is the same 9 the Apps pill uses, so the two labelled buttons in the bar match.
   Round ends too, asked for a moment later, so it and Download are the same shape. */
.ed-bar .share-btn{ display:inline-flex; align-items:center; justify-content:center; gap:9px;
  height:40px; padding:0 16px; border:none; border-radius:999px; font-size:14px; font-weight:600;
  background:rgba(255,255,255,.12); color:#fff; cursor:pointer; transition:background .12s; }
.ed-bar .share-btn svg{ flex:none; }
.ed-bar .share-btn:hover{ background:rgba(255,255,255,.20); }
.ed-bar .share-btn.on{ background:rgba(61,149,255,.30); }
/* Download: round, asked for in the same breath. The blue is the editor's own accent, not a
   colour named here, so it follows if the accent ever moves. */
.ed-bar .exp-btn{ display:inline-flex; align-items:center; gap:8px; height:40px;
  padding:0 18px; border:none; border-radius:999px; background:var(--accent); color:#fff;
  font-size:14px; font-weight:600; cursor:pointer; transition:background .12s; }
.ed-bar .exp-btn:hover{ background:var(--accent-ink); }
.ed-bar .exp-btn svg{ flex:none; }
/* Both menus hang from under their button now, not from above the panel's edge. */
.ed-bar .exp-menu{ right:0; left:auto; top:calc(100% + 8px); }
.ed-bar .share-menu{ right:0; left:auto; top:calc(100% + 8px); }
/* The account circle sits after them, so the "push everything right" job is the wrapper's
   now and not the circle's — two things claiming it would leave a gap between them. */
.ed-bar .exp-wrap ~ .ed-avwrap{ margin-left:0; }
/* THE ACCOUNT MENU IN THE BAR — 11 September 2026, "similar to home page". It is the same
   markup and the same .tf-avwrap rules, so the circle, the initials and the hover-open list
   need no second set of rules and cannot drift from the header's.
   The one thing it does need is the colours those rules read. They are defined on .tf, which
   scopes the home page, and the editor is not inside it — without these the dropdown would
   come out with no background and no text colour at all. Same values, one place. */
/* margin-left:auto is what puts it at the far right; it is not spacing between things and is
   the only margin allowed in the bar. The 2px it used to carry on its other side was taken
   off on 11 September 2026 — the bar's own padding is what holds it off the edge. */
.ed-avwrap{ margin-left:auto; --card:#FFFFFF; --bg:#F5F5F7; --fg:#16161A;
  --mut2:#55555E; --mut3:#9A9AA4; --bd:#EAEAEF; --acc:var(--brand);
  --float-shadow:0 14px 30px -4px rgba(22,24,45,.10), 0 6px 12px -2px rgba(22,24,45,.08), 0 1px 3px rgba(22,24,45,.10); }
/* Smaller than the header's 38px, and asked down twice more on 11 September 2026: 38 to 32,
   then 32 to 22, then 22 to 15.
   THE LETTERS BREAK THE TYPE SCALE, and it is the one place in the editor that does. The
   scale stops at 10px, and two capitals at 10px are wider than a 15px circle — they would sit
   half outside it. These are not text anybody reads, they are a mark; the alternative was a
   circle with letters spilling out of it. test-editortheme.cjs names this exception rather
   than widening the scale, so nothing else can quietly follow it down. */
.ed-avwrap .tf-avatar{ width:15px; height:15px; font-size:7px; }
/* Above the canvas and the tool rail, or the list would open behind them. */
.ed-avwrap .tf-dd{ z-index:300; }
/* Both full-screen modes take everything else away; the bar goes with them. */
body.previewing .ed-bar,
body.presenting .ed-bar{ display:none !important; }

.ed-body{ flex:1; display:flex; gap:0; padding:0 0 0 var(--rail-w); min-height:0; background:var(--canvas); position:relative; }
.toolbar{ position:absolute; left:0; top:0; bottom:0; z-index:40; width:var(--rail-w); background:var(--panel); border:none; border-right:1px solid var(--line); border-radius:0; box-shadow:none; display:flex; flex-direction:column; align-items:center; gap:4px; padding:11px 3px; overflow:hidden; transition:width .16s ease, box-shadow .16s ease; flex:none; max-height:none; }
/* WHAT EACH TOOL IS FOR, BESIDE THE TOOL — 10 September 2026.
 *
 * It used to be a black pill with two words in it: "Type (T)". That tells somebody who already
 * knows what the tool is that they were right. It tells a person who does not know nothing at
 * all. Mounir sent the Adobe Express version — a small card with a picture of the tool in use,
 * its name, and a sentence saying what it does — and asked for the same at 30% smaller.
 *
 * 185px wide, which is Adobe's card less a third. The words are his, exactly as sent.
 *
 * THE PICTURE IS OPTIONAL AND MISSING BY DEFAULT. Each card looks for a file named after its
 * tool; if there is none, the picture area is taken out and the card is just a name and a
 * sentence. So the pictures can arrive one at a time, by dropping files into a folder, with no
 * code change and nothing broken in between.
 */
.rail-tip{ position:fixed; z-index:300; width:185px; background:#fff; color:#15171C;
  border:1px solid rgba(16,24,40,.10); border-radius:11px;
  overflow:hidden; pointer-events:none; opacity:0; transition:opacity .12s; }
.rail-tip.show{ opacity:1; }
/* The pill sat centred on the tool. A card is taller than a tool button, so it hangs from the
   top of it instead and is nudged up a little — centring a 150px card on a 34px button puts
   half of it off the top of the screen for anything near the head of the rail. */
/* Square, because his pictures are square (200 by 200). A wider frame with these in it
   would cut the top and bottom off every one. */
.rail-tip .rt-art{ display:block; width:100%; aspect-ratio:1 / 1; object-fit:cover; background:#EEF1F6; }
.rail-tip .rt-body{ padding:9px 11px 11px; }
.rail-tip .rt-title{ font-size:13px; font-weight:600; line-height:1.25; }
.rail-tip .rt-desc{ margin-top:3px; font-size:11px; font-weight:400; line-height:1.45; color:#5A616B; }
/* A tool with no sentence written for it yet falls back to the old one-line pill, so nothing
   in the rail is ever left with no label at all. */
.rail-tip.plain{ width:auto; white-space:nowrap; }
.rail-tip.plain .rt-body{ padding:5px 10px; }
.rail-tip.plain .rt-title{ font-size:12px; font-weight:500; }
.rail-toggle{ display:none; }
/* an <a href="/"> since 22 August 2026, so right-click offers "Open in a new tab" — the reset
   below is what a button gave for free and a link does not */
/* The tool rail's own logo is gone — 11 September 2026. It moved into the editor's black bar,
   where there is room for the mark and the word together. Its rules went with it. */
.tlabel{ white-space:nowrap; font-size:13px; font-weight:600; display:none; pointer-events:none; }
.tool-div{ width:20px; height:1px; background:var(--line); margin:4px 0; flex:none; }
.tool-div-push{ margin-top:auto; }
/* tools scroll when the window is short; the colours / Layers / Menu cluster stays pinned to the bottom */
.rail-top{ flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden; display:flex; flex-direction:column; align-items:center; gap:3px; width:100%;
  scrollbar-width:none; -ms-overflow-style:none; }
.rail-top::-webkit-scrollbar{ display:none; }
.rail-bottom{ flex:none; display:flex; flex-direction:column; align-items:center; gap:3px; width:100%; padding-top:2px; }
.toolbar.expanded .rail-top,.toolbar.expanded .rail-bottom{ align-items:stretch; }
/* slim scroll indicator just OUTSIDE the rail (only when the tools overflow) */
.rail-scroll{ position:absolute; left:calc(var(--rail-w) + 1px); width:3px; z-index:41; border-radius:3px; background:var(--muted); opacity:0; transition:opacity .18s, background .12s; pointer-events:none; }
.rail-scroll.show{ opacity:1; pointer-events:auto; cursor:pointer; }
.rail-scroll:hover{ background:var(--ink); }
.tool.stub{ opacity:.38; }
.tool.stub:hover{ opacity:.6; background:transparent; color:var(--muted); }
.swatches{ display:flex; flex-direction:column; align-items:center; gap:2px; margin-top:2px; width:100%; }
.sw-row{ display:flex; align-items:center; justify-content:center; gap:0; width:29px; height:29px; padding:0; border:none; background:transparent; border-radius:9px; cursor:pointer; color:var(--muted); }
.sw-row:hover{ background:var(--hover); color:var(--ink); }

/* ---- fill and outline ------------------------------------------------------------
   Four things one swatch has to say, and they must not look alike:
     a colour   — the colour itself
     none       — set, but switched off: a red bar across it, as Illustrator does
     mixed      — several things selected disagreeing: a split box, not one of the two
     n/a        — this kind of layer has no such slot at all: faded and not clickable
   The last two are the ones normally skipped, and they are why the tool feels broken when
   it is not: without them "no fill" and "images have no fill" look identical. */
#fsSwatches .sw-box{ position:relative; overflow:hidden; }
/* the chequerboard shows through anything see-through, so an alpha fill reads as one */
#fsSwatches .sw-box::before{ content:""; position:absolute; inset:0; z-index:-1;
  background-image:linear-gradient(45deg,#d8d8de 25%,transparent 25%,transparent 75%,#d8d8de 75%),
                   linear-gradient(45deg,#d8d8de 25%,transparent 25%,transparent 75%,#d8d8de 75%);
  background-size:8px 8px; background-position:0 0,4px 4px; background-color:#fff; }
/* none: the diagonal bar */
#fsSwatches .sw-row.empty .sw-box{ background:#fff !important; }
/* the row itself never carries the colour any more — see fsPaint. Its background is for
   hover, and nothing else. */
#fsSwatches .sw-row{ background:transparent; }
#fsSwatches .sw-row:hover{ background:var(--hover); }
#fsSwatches .sw-row.empty .sw-box::after{ content:""; position:absolute; left:-30%; right:-30%; top:50%;
  height:2px; background:var(--danger,#B4443C); transform:rotate(-45deg); }
/* mixed: half one thing, half the other */
#fsSwatches .sw-row.mixed .sw-box::after{ content:""; position:absolute; inset:0;
  background:linear-gradient(135deg,transparent 49%,#fff 49%,#fff 51%,transparent 51%); }
/* not applicable to what is selected */
#fsSwatches .sw-row.off{ opacity:.4; cursor:default; }
#fsSwatches .sw-row.off .sw-box{ background:repeating-linear-gradient(45deg,#e6e6ec,#e6e6ec 3px,#f4f4f8 3px,#f4f4f8 6px) !important; }
/* the chip is the whole of it now, so give it the room the square had */
#fsSwatches .sw-box{ width:24px; height:24px; }
#fsSwatches .sw-row.off:hover{ background:transparent; }
#fsSwatches .sw-row:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }
/* nothing selected: these are the colours the NEXT element gets, which is a different job */
#fsSwatches.nosel .tlabel::after{ content:" (next)"; opacity:.6; }
/* CIRCLES, asked for 26 August 2026 — the fill and outline chips on the left rail and the
   colour swatches in the right panel, all round rather than rounded-square. Every layer that
   draws inside one of them (the chequerboard for transparency, the diagonal for "none", the
   half-and-half for a mixed selection) has to round with it, or a square corner shows through
   the circle. That is why this is more than one line. */
.sw-box{ width:22px; height:22px; border-radius:50%; overflow:hidden; border:1px solid var(--field-bd); flex:none; box-shadow:0 1px 2px rgba(0,0,0,.3); }
.sw-box input[type=color]{ width:150%; height:150%; margin:-25%; padding:0; border:none; cursor:pointer; }
.sw-ic{ width:22px; display:flex; align-items:center; justify-content:center; flex:none; }
.sw-ic svg{ width:14px; height:14px; }
.range-row{ display:flex; align-items:center; gap:8px; }
.range-row input[type=range]{ flex:1; }
.range-row .val{ font-size:12px; color:var(--ink); font-family:'JetBrains Mono',ui-monospace,monospace; width:38px; text-align:right; }
.sec-title{ font-size:11px; font-weight:700; text-transform:none; letter-spacing:0; color:var(--ink); margin:12px 0 6px; padding-top:10px; border-top:1px solid var(--line); }
.chk-row{ display:flex; align-items:center; gap:8px; font-size:13px; margin-bottom:8px; }
.tool{ width:38px; height:38px; border-radius:9px; border:none; background:transparent; color:var(--muted); display:flex; align-items:center; gap:12px; padding:0; justify-content:center; flex:none; }
.tool svg{ flex:none; width:22px; height:22px; }
.tool:hover{ background:var(--hover); color:var(--ink); }
.tool.active{ background:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
/* the solid Move arrow reads black at rest, blue when it's the active tool */
.tool[data-tool="select"]{ color:var(--ink); }
.tool[data-tool="select"].active{ color:var(--sel-fg); }
.ed-mid{ display:flex; align-items:center; gap:6px; margin-left:auto; }
.hgroup{ display:inline-flex; align-items:center; gap:2px; }
.ed-bottom{ flex:none; position:relative; z-index:140; display:flex; align-items:center; justify-content:flex-end; gap:12px; padding:6px 16px; background:var(--panel); border-top:1px solid var(--line); }
.zoombar{ display:flex; align-items:center; gap:8px; }
.zoombar input[type="range"]{ width:150px; cursor:pointer; }
/* ---- Kittl-style thin sliders (all editor ranges) ---- */
input[type=range]{ -webkit-appearance:none; appearance:none; width:100%; height:14px; background:transparent; cursor:pointer; margin:0; }
input[type=range]::-webkit-slider-runnable-track{ height:3px; border-radius:2px; background:linear-gradient(to right, var(--accent) calc(var(--rp,0)*1%), var(--rtrack) calc(var(--rp,0)*1%)); }
input[type=range]::-webkit-slider-thumb{ -webkit-appearance:none; margin-top:-5.5px; width:14px; height:14px; border-radius:50%; background:#fff; border:2px solid var(--accent); box-shadow:0 1px 3px rgba(0,0,0,.28); transition:transform .1s; }
input[type=range]:active::-webkit-slider-thumb{ transform:scale(1.12); }
input[type=range]::-moz-range-track{ height:3px; border-radius:2px; background:var(--rtrack); }
input[type=range]::-moz-range-progress{ height:3px; border-radius:2px; background:var(--accent); }
input[type=range]::-moz-range-thumb{ width:14px; height:14px; border-radius:50%; background:#fff; border:2px solid var(--accent); box-shadow:0 1px 3px rgba(0,0,0,.28); }
input[type=range]:focus{ outline:none; }
input[type=range]:hover,input[type=range]:focus,input[type=range]:active{ background:transparent; box-shadow:none; }
.zoombar .zoom-val{ min-width:48px; text-align:center; }
.icon-btn{ width:32px; height:32px; border:none; background:transparent; border-radius:8px; font-size:16px; line-height:1; color:var(--dim); display:inline-flex; align-items:center; justify-content:center; }
.icon-btn:hover{ background:var(--hover); color:var(--ink); }
.icon-btn:disabled{ opacity:.4; cursor:default; }
.zoom-val{ min-width:52px; height:32px; border:1px solid var(--field-bd); background:var(--field); border-radius:8px; font-size:12px; font-weight:600; color:var(--ink); font-family:'JetBrains Mono',ui-monospace,monospace; }
.zoom-val:hover{ background:var(--hover); }

.stage-wrap{ flex:1; overflow:hidden; background:var(--canvas); display:block; padding:0; position:relative; }
/* THE CANVASES ARE PLACED, NOT STACKED, since 23 August 2026 — each page's name, artboard
   and Add-page bar are positioned by layoutPages, so a canvas can be dragged anywhere by its
   name (Kittl). The artboard's own offsetLeft/offsetTop keep their old meaning because this
   element is still their offset parent and nothing wraps them. */
.stage-outer{ position:relative; transform-origin:top left; display:block; }
.stage-outer.anim{ will-change:transform; }   /* only hint the compositor during animated zoom/pan; idle stays vector-sharp */
.stage-outer.anim{ transition:transform .22s cubic-bezier(.2,.8,.3,1); }
/* artwork that extends past the page stays visible and selectable (Illustrator-style pasteboard);
   exports and thumbnails still crop to the page */
.stage{ position:relative; overflow:visible; background:#fff; box-shadow:0 30px 60px -20px rgba(0,0,0,.6); flex:none; }
/* while a move drag lasts, its page rides above the other canvases — otherwise an object
   dragged toward a later canvas slides underneath it and vanishes mid-drag (one space, 23/8) */
.stage.drag-src{ z-index:6; }
/* placement mode (23 Aug 2026): a press anywhere on the armed canvas carries it — but the
   four-headed mover shows ONLY on the name, asked for the same evening: over the artwork it
   read as an interruption of the content. The name is where the cursor announces the move. */

/* THE CANVAS'S OWN EIGHT HANDLES — 23 August 2026, Kittl's arrangement: squares on the
   corners, pills mid-edge, on a light blue frame. Shown while nothing is selected; the
   chrome divides by the zoom (--inv) so it stays the same size on screen at any scale. */
.cv-frame{ position:absolute; inset:0; pointer-events:none;
  outline:calc(1.5px * var(--inv,1)) solid rgba(93,159,255,.85); }
.cv-frame .cvh{ position:absolute; pointer-events:auto; background:#fff;
  border:calc(1.5px * var(--inv,1)) solid #5D9FFF; border-radius:calc(3px * var(--inv,1));
  width:calc(12px * var(--inv,1)); height:calc(12px * var(--inv,1)); }
.cv-frame .cvh.pill{ border-radius:calc(6px * var(--inv,1)); }
.cv-frame .cvh.t,.cv-frame .cvh.b{ width:calc(34px * var(--inv,1)); height:calc(9px * var(--inv,1)); left:50%; transform:translateX(-50%); cursor:ns-resize; }
.cv-frame .cvh.l,.cv-frame .cvh.r{ height:calc(34px * var(--inv,1)); width:calc(9px * var(--inv,1)); top:50%; transform:translateY(-50%); cursor:ew-resize; }
.cv-frame .cvh.t{ top:calc(-5px * var(--inv,1)); }
.cv-frame .cvh.b{ bottom:calc(-5px * var(--inv,1)); }
.cv-frame .cvh.l{ left:calc(-5px * var(--inv,1)); }
.cv-frame .cvh.r{ right:calc(-5px * var(--inv,1)); }
.cv-frame .cvh.tl{ left:calc(-6px * var(--inv,1)); top:calc(-6px * var(--inv,1)); cursor:nwse-resize; }
.cv-frame .cvh.br{ right:calc(-6px * var(--inv,1)); bottom:calc(-6px * var(--inv,1)); cursor:nwse-resize; }
.cv-frame .cvh.tr{ right:calc(-6px * var(--inv,1)); top:calc(-6px * var(--inv,1)); cursor:nesw-resize; }
.cv-frame .cvh.bl{ left:calc(-6px * var(--inv,1)); bottom:calc(-6px * var(--inv,1)); cursor:nesw-resize; }
.mini{ overflow:hidden; }
.stage.inactive{ cursor:pointer; }
.stage.inactive:hover{ outline:2px solid rgba(61,149,255,.45); }
/* The dashed outline drawn over a shape. It sits on top of the layer's own background and
   must never take a click — the layer under it is what you are selecting and dragging. */
.layer > .stroke-dash{ position:absolute; left:0; top:0; overflow:visible; pointer-events:none; z-index:1; }
.sel-overlay{ position:absolute; left:0; top:0; pointer-events:none; overflow:visible; z-index:60; }
/* page furniture scales with the canvas — it zooms out with the artboard */
/* The strip holding the page name is as wide as the canvas, so nearly all of it is empty space
   to the right of the name. It must not be clickable: a click in that empty space lands on a
   block whose only text sits in an editable child, and the browser answers by putting the caret
   in that child — so the name went into edit mode from a click that was nowhere near it. Making
   the strip itself invisible to the mouse removes the question entirely; the name opts back in,
   and events still bubble from the name up to the strip, so the switch-page handler on it keeps
   working. */
/* NOT SELECTABLE, the same as every object on the canvas — 19 September 2026.
   Reported as "page turns blue when i drag to duplicate them", with a picture. Dragging a page by
   its name left the browser's own sweep-to-highlight switched on, so it highlighted the name and
   the Add-page bar in the system highlight colour and dragged a text selection along behind the
   page. Objects have never done this because .layer has said user-select:none since the beginning;
   the page's own furniture never said it. Said here as well as in the press handler, because a
   selection that began somewhere else can still be dragged across this, and Safari fills whole
   blocks with the highlight where Chrome paints only the words. */
.page-head{ display:flex; align-items:center; margin:0; flex:none; pointer-events:none; user-select:none; -webkit-user-select:none; }
.page-name{ pointer-events:auto; cursor:move; touch-action:none;   /* the four-headed mover, 23 Aug 2026 */ width:max-content; font-size:calc(12px / var(--fit,1)); font-weight:600; color:var(--muted); padding:calc(4px / var(--fit,1)) calc(8px / var(--fit,1)); border-radius:calc(7px / var(--fit,1)); outline:none; min-width:calc(40px / var(--fit,1)); transition:color .12s, background .12s; user-select:none; -webkit-user-select:none; }
.page-name.cur{ color:var(--ink); }
/* while the name is being typed in, the text caret returns — the old always-on cursor:text
   in the rule above is what hid the four-headed mover for a whole evening (23 Aug 2026) —
   and the letters have to be selectable again, or renaming would be typing blind */
.page-name[contenteditable]{ cursor:text; user-select:text; -webkit-user-select:text; }
/* AND THE STRIP AROUND IT WHILE A NAME IS BEING TYPED IN. Not-selectable on a parent is enough, on
   some browsers, to stop the text cursor being placed inside an editable child by a click — which
   is exactly the "let me click between letters" report of 19 September 2026. It is only relaxed
   while a rename is actually happening, so dragging a page still sweeps nothing along with it. */
.page-head:has(.page-name[contenteditable]){ user-select:text; -webkit-user-select:text; }

.page-name:hover{ background:var(--hover); }
.page-name:focus{ background:rgba(61,149,255,.14); color:var(--ink); }
.page-head:has(.page-name:not(.cur)) .page-name{ cursor:pointer; }
.page-bar{ display:flex; align-items:center; gap:calc(10px / var(--fit,1)); margin:0; flex:none; justify-content:center; user-select:none; -webkit-user-select:none; }
.pb-add{ flex:1; max-width:70%; height:calc(30px / var(--fit,1)); border:calc(1.5px / var(--fit,1)) solid var(--field-bd); background:var(--panel); border-radius:calc(9px / var(--fit,1)); color:var(--dim); font-size:calc(13px / var(--fit,1)); font-weight:600; transition:background .12s,color .12s; }
.pb-add:hover{ background:var(--hover); color:var(--ink); border-color:var(--bdh); }
.pb-dup{ width:calc(30px / var(--fit,1)); height:calc(30px / var(--fit,1)); border:calc(1.5px / var(--fit,1)) solid var(--field-bd); background:var(--panel); border-radius:calc(9px / var(--fit,1)); color:var(--dim); display:grid; place-items:center; transition:background .12s,color .12s; }
.pb-dup:hover{ background:var(--hover); color:var(--ink); border-color:var(--bdh); }
.pb-dup svg{ width:calc(15px / var(--fit,1)); height:calc(15px / var(--fit,1)); }
.ag-who{ font-size:12px; color:var(--muted); text-align:center; margin:-4px 0 10px; min-height:1em; }
.ag-who:empty{ display:none; }
.ctx-menu{ position:fixed; z-index:130; min-width:200px; background:var(--panel); border:1px solid var(--line); border-radius:12px; box-shadow:var(--float-shadow); padding:6px; display:flex; flex-direction:column; gap:1px; }
.ctx-menu button{ display:flex; align-items:center; gap:11px; padding:8px 10px; background:transparent; border:none; border-radius:8px; font-size:13px; font-weight:500; color:var(--dim); text-align:left; transition:background .1s,color .1s; }
.ctx-menu button:hover:not(:disabled){ background:var(--hover); color:var(--ink); }
.ctx-menu button:disabled{ opacity:.38; cursor:default; }
.ctx-menu button kbd{ margin-left:auto; font-family:'JetBrains Mono',ui-monospace,monospace; font-size:11px; color:var(--muted); background:var(--field); border:1px solid var(--field-bd); border-radius:5px; padding:2px 6px; }
/* Arrange flyout. The parent is position:relative so the submenu hangs off it; the
   submenu overrides .ctx-menu's position:fixed, which would otherwise pin it to the
   viewport instead of to its parent row. */
.ctx-item-sub{ position:relative; display:flex; }
.ctx-item-sub>button{ flex:1; width:100%; }
.ctx-arrow{ margin-left:auto; display:grid; place-items:center; color:var(--muted); }
.ctx-menu .ctx-sub{ position:absolute; left:100%; top:-6px; margin-left:3px; min-width:172px; display:none; z-index:1; }
.ctx-item-sub:hover>.ctx-sub,.ctx-item-sub:focus-within>.ctx-sub{ display:flex; }
/* near the right edge the flyout opens to the left instead of off-screen */
.ctx-menu.flip-sub .ctx-sub{ left:auto; right:100%; margin-left:0; margin-right:3px; }
.ctx-menu button.danger{ color:#F08A82; }
.ctx-menu button.danger:hover:not(:disabled){ background:rgba(224,72,63,.15); color:#FF9B93; }
.ctx-sep{ height:1px; background:var(--line); margin:4px 6px; }
.page-menu{ position:fixed; z-index:95; width:720px; max-width:92vw; background:var(--panel); border:1px solid var(--line); border-radius:16px; box-shadow:var(--float-shadow); display:flex; flex-direction:column; overflow:hidden; }
.pm-top{ border-bottom:1px solid var(--line); padding:8px; }
.pm-samebar{ width:100%; display:flex; align-items:center; gap:12px; padding:9px 12px; background:transparent; border:none; border-radius:10px; font-size:13px; font-weight:600; color:var(--ink); text-align:left; transition:background .12s; }
.pm-samebar:hover{ background:var(--hover); }
.pm-samebar .csm-ic{ width:32px; height:32px; }
.pm-samelabel{ flex:1; }
.pm-dims{ font-family:'JetBrains Mono',ui-monospace,monospace; font-size:12px; color:var(--muted); }
.pm-body{ display:grid; grid-template-columns:186px 1fr; min-height:0; }
.pm-body .csm-main{ max-height:400px; }
.shape-menu{ position:absolute; left:calc(var(--rail-w) - 5px); top:0; bottom:0; z-index:80; width:206px; display:flex; flex-direction:column; background:var(--panel); border:none; border-right:1px solid var(--line); border-radius:0; box-shadow:14px 0 34px -20px rgba(0,0,0,.5); padding:0; }
.shape-menu.hidden{ display:none; }
.shape-grid{ display:grid; grid-template-columns:repeat(3,58px); gap:4px; justify-content:center; }
.shape-grid button{ width:58px; height:58px; display:grid; place-items:center; padding:0; background:transparent; border:none; border-radius:12px; color:#8a8d92; transition:background .12s,color .12s; }
.shape-grid button svg{ width:40px; height:40px; }
.shape-grid button:hover{ background:var(--hover); color:var(--ink); }
.shape-grid button.active{ background:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
.stage-wrap.tool-pan{ cursor:grab; }
.stage-wrap.tool-pan.panning{ cursor:grabbing; }
/* space-pan: hand cursor wins everywhere, incl. selection handles (inline cursors) — PS behavior */
.stage-wrap.tool-pan *{ cursor:inherit !important; }
.stage-wrap.tool-draw{ cursor:crosshair; }
.stage-wrap.tool-typepath{ cursor:text; }
/* hollow square = the cursor is exactly over a vector anchor point */
.stage-wrap.tool-select.on-anchor,.stage-wrap.tool-select.on-anchor .layer{
  cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M4 3l0 8M4 3l8 0" stroke="white" stroke-width="4" fill="none"/><path d="M4 3l0 8M4 3l8 0" stroke="black" stroke-width="1.6" fill="none"/><rect x="11" y="11" width="8" height="8" fill="white" stroke="black" stroke-width="1.4"/></svg>') 4 3, crosshair; }
/* live X/Y · W/H · angle readout that follows the cursor */
.drag-hud{ position:fixed; z-index:400; pointer-events:none; opacity:0; transition:opacity .08s;
  background:#111; color:#fff; font-size:11px; font-family:'JetBrains Mono',ui-monospace,monospace;
  padding:4px 8px; border-radius:6px; white-space:nowrap; box-shadow:0 4px 14px rgba(0,0,0,.32); }
.drag-hud.show{ opacity:1; }
.drag-hud b{ font-weight:700; }
/* in-place editing of text on a path: invisible field, caret only — the curved text updates live */
.tp-edit{ position:absolute; inset:0; z-index:6; display:flex; align-items:center; justify-content:center;
  color:transparent; background:transparent; outline:none; white-space:nowrap; overflow:hidden;
  font-size:16px; letter-spacing:1px; cursor:text; }
.panel-hint{ font-size:11px; line-height:1.5; color:var(--muted); margin:0 0 10px; }
.stage-wrap.tool-paint,.stage-wrap.tool-paint .layer{ cursor:crosshair; }
/* ERASER. The pointer itself is a plain crosshair; the ring below is what tells you how much
   of the photo the next stroke will take, drawn at the real brush size so what you see is what
   goes. Without it an eraser is guesswork — you find out how big the brush was afterwards. */
.stage-wrap.tool-erase,.stage-wrap.tool-erase .layer{ cursor:crosshair; }
.erase-ring{ position:fixed; z-index:410; pointer-events:none; border-radius:50%;
  border:1px solid rgba(0,0,0,.85); box-shadow:0 0 0 1px rgba(255,255,255,.9) inset, 0 0 0 1px rgba(255,255,255,.9);
  transform:translate(-50%,-50%); }
.erase-ring.restoring{ border-color:var(--accent); border-width:2px; box-shadow:0 0 0 1px rgba(255,255,255,.9) inset, 0 0 0 2px rgba(255,255,255,.9); }
.brush-bar{ display:flex; align-items:center; gap:10px; padding:0 6px; }
.brush-bar label{ font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); }
.brush-bar input[type=range]{ width:110px; }
.bb-dot{ width:18px; height:18px; border-radius:50%; border:2px solid var(--field-bd); background:#15171c; flex:none; }
.bb-val{ font-family:'JetBrains Mono',ui-monospace,monospace; font-size:12px; color:var(--ink); min-width:34px; }
/* AN ARROW OVER EVERYTHING, THE WAY ILLUSTRATOR DOES IT. Asked for on 21 August 2026.
   It used to turn into the four-headed move cursor over an object. That cursor means "this is
   already being moved" — in a drawing program it belongs to dragging a photo around inside its
   crop frame, which is the one place below that still uses it. Over an object you have not
   picked up yet it is a promise the pointer has no business making, and it changes under the
   cursor as it passes over each item, which is the flicker you notice without being able to
   say what moved.
   One arrow, over the artboard and over the artwork alike. The system arrow rather than a
   drawn one: on a Mac that IS a black arrow with a white edge, which is what Illustrator's is,
   and it stays sharp on any screen without a picture to scale. */
.stage-wrap.tool-select,
.stage-wrap.tool-select .layer,
.stage-wrap.tool-select .layer.locked{ cursor:default; }
/* OPTION HELD OVER AN OBJECT: THE DOUBLE ARROW — 26 September 2026 (altcursor.js). "when holding
   option key, the cursor should turn to double cursor, to indicate the duplicate action". Only
   over an object, because that is where an Option-drag makes a copy; the tip is the back arrow's,
   so the point you press is exactly where it was a moment before. */
.stage-wrap.tool-select.alt-dup .layer:not(.locked){ cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26"><path transform="translate(2 2)" d="M0 0V17L5 12.4L12.5 12Z" fill="black" stroke="white" stroke-width="1.3" stroke-linejoin="round"/><path transform="translate(9 5)" d="M0 0V17L5 12.4L12.5 12Z" fill="white" stroke="black" stroke-width="1.7" stroke-linejoin="round"/></svg>') 2 2, copy; }
.stage-wrap.tool-crop{ cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g stroke="white" stroke-width="4.6" fill="none"><path d="M6 1v15a2 2 0 0 0 2 2h15M1 6h15a2 2 0 0 1 2 2v15M23 1l-8.5 8.5M9.5 14.5 1 23"/></g><g stroke="black" stroke-width="2.2" fill="none"><path d="M6 1v15a2 2 0 0 0 2 2h15M1 6h15a2 2 0 0 1 2 2v15M23 1l-8.5 8.5M9.5 14.5 1 23"/></g></svg>') 12 12, crosshair; }
.stage-wrap.tool-text,.stage-wrap.tool-text .layer{ cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g stroke="white" stroke-width="4.4" fill="none" stroke-linecap="round"><path d="M7 3c3 0 5 .8 5 3v12c0 2.2 2 3 5 3M17 3c-3 0-5 .8-5 3M7 21c3 0 5-.8 5-3"/></g><g stroke="black" stroke-width="2" fill="none" stroke-linecap="round"><path d="M7 3c3 0 5 .8 5 3v12c0 2.2 2 3 5 3M17 3c-3 0-5 .8-5 3M7 21c3 0 5-.8 5-3"/></g></svg>') 12 12, text; }
.layer[contenteditable="true"]{ cursor:text !important; }
.stage-wrap.tool-eyedropper{ cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path d="m2 22 1-4 9-9 3 3-9 9z" fill="white" stroke="black" stroke-width="1.4" stroke-linejoin="round"/><path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.9.9a2.1 2.1 0 1 1-3 3l-6-6a2.1 2.1 0 1 1 3-3l.9.9Z" fill="black" stroke="white" stroke-width="1.2" stroke-linejoin="round"/></svg>') 2 20, crosshair; }
.stage-wrap.tool-pan .layer,.stage-wrap.tool-draw .layer,.stage-wrap.tool-crop .layer,.stage-wrap.tool-eyedropper .layer{ cursor:inherit; }
.crop-marquee{ position:absolute; border:1.5px dashed var(--accent); background:rgba(61,149,255,.12); z-index:70; pointer-events:none; }
.text-marquee{ position:absolute; border:1.5px solid #4A9DF8; background:rgba(74,157,248,.06); z-index:70; pointer-events:none; }

.layer{ position:absolute; box-sizing:border-box; user-select:none; -webkit-user-select:none; }
.layer[contenteditable="true"]{ user-select:text; -webkit-user-select:text; }
/* Area Type: wraps at the frame edge and reflows on resize — glyphs are never scaled or stretched */
/* hyphens:manual, not auto — the browser's automatic hyphenation uses a language
   dictionary and splits on syllables, which the export engine cannot reproduce. The
   two would disagree on every hyphenated word. See the note in richLayout. */
/* tab-size is STATED rather than left to the browser. Eight is every browser's default
   today, and the unit is the width of eight spaces in the current font - but a default is
   something that can differ, and the export engine has to match it exactly or anything
   aligned with tabs lines up on screen and nowhere else. */
.layer.text{ white-space:pre-wrap; overflow-wrap:break-word; word-break:normal; hyphens:manual; overflow:visible; tab-size:8; -moz-tab-size:8; }
/* SPACE BETWEEN PARAGRAPHS. The gap sits above every paragraph except the first, which is
   what stops the whole block being pushed down by a gap with nothing above it.
   It has to be a real block to carry a margin: measured in Safari, `br::after` with a
   margin, `br{display:block}` with a margin, and an inline spacer are all ignored. That is
   why the displayed markup is normalised into one <div> per paragraph.
   :first-child is safe here BECAUSE of that normalisation - with a bare text node for the
   first paragraph and divs after it, the first div would match :first-child and lose its
   gap wrongly. Every paragraph being a div is what makes the rule mean what it says. */
.layer.text > div + div{ margin-top:var(--para-gap,0px); }
/* while editing, characters must be selectable (.layer sets user-select:none for dragging) */
.layer[contenteditable="true"]{ cursor:text; user-select:text; -webkit-user-select:text;
  /* THE BROWSER'S OWN FOCUS RING IS TURNED OFF. Reported 28 August 2026: double-click a text
     box and the blue outline is not a straight line but a stepped one. That is TWO rectangles
     seen at once — the editor's own selection frame, drawn around the layer's box, and the
     browser's focus ring, drawn around the ELEMENT. They are not the same rectangle: the ring
     follows the padding box, which on a vertically aligned box includes the padding that
     pushes the words down, and on an auto-height box it follows the words as they are typed
     while the frame follows the height last settled on. Where they differ you see a step.
     The editor draws its own frame, with handles, and has done all along. The ring is a second
     opinion about the same thing, and the one nobody asked for. */
  outline:none; }
.sel-box{ position:absolute; box-sizing:border-box; pointer-events:none; border:calc(1.5px*var(--inv,1)) solid var(--accent); }
.sel-box.thin{ border-width:calc(1px*var(--inv,1)); opacity:.45; }
/* free-transform quad handles live directly in the overlay */
.handle.q{ position:absolute; transform:translate(-50%,-50%); cursor:crosshair; }
/* while a layer is perspective-warped, don't warp its flat UI overlays */
.layer.distorted .img-replace,.layer.distorted .imgfx{ display:none !important; }
/* invisible rotate zones just outside each corner — cursor turns to the curved arrow.
   5 PIXELS PAST THE CORNER HANDLE, NO MORE — 5 October 2026, Mounir: the arrow appeared far from the
   corner (the zone reached 34 pixels out). The handle's outer edge is 6 out, so the zone reaches 11,
   and it stops at the box's edge so it never covers the item itself. */
.rotzone{ position:absolute; width:calc(11px*var(--inv,1)); height:calc(11px*var(--inv,1)); z-index:45; pointer-events:auto; background:transparent;
  cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26"><g fill="none" stroke="white" stroke-width="4" stroke-linecap="round"><path d="M6 10a7.5 7.5 0 0 1 14 1.5"/><path d="M20 5.5V12h-6"/><path d="M20 16a7.5 7.5 0 0 1-14-1.5"/><path d="M6 20.5V14h6"/></g><g fill="none" stroke="black" stroke-width="1.7" stroke-linecap="round"><path d="M6 10a7.5 7.5 0 0 1 14 1.5"/><path d="M20 5.5V12h-6"/><path d="M20 16a7.5 7.5 0 0 1-14-1.5"/><path d="M6 20.5V14h6"/></g></svg>') 13 13, grab; }
.rotzone.tl{ left:calc(-11px*var(--inv,1)); top:calc(-11px*var(--inv,1)); }
.rotzone.tr{ right:calc(-11px*var(--inv,1)); top:calc(-11px*var(--inv,1)); }
.rotzone.bl{ left:calc(-11px*var(--inv,1)); bottom:calc(-11px*var(--inv,1)); }
.rotzone.br{ right:calc(-11px*var(--inv,1)); bottom:calc(-11px*var(--inv,1)); }
/* live-corner widgets sit just inside each corner */
.corner-widget{ position:absolute; width:calc(9px*var(--inv,1)); height:calc(9px*var(--inv,1)); border-radius:50%; background:#fff;
  border:calc(1.5px*var(--inv,1)) solid var(--accent); z-index:52; pointer-events:auto; cursor:nwse-resize; opacity:.9;
  box-shadow:0 1px 3px rgba(0,0,0,.3); }
.corner-widget:hover{ opacity:1; transform:scale(1.15); }
/* THE FOUR EDGES OF THE FRAME AROUND A BOX THAT IS BEING TYPED IN. Drawn only then, and only
   there: a press on the WORDS belongs to the caret, so the strips sit on the frame itself,
   clear of the letters. Scaled by --inv like every other piece of chrome, so they are the same
   width on screen at any zoom. Invisible — the frame is already drawn; these are just the part
   of it you can take hold of. */
.sel-edge{ position:absolute; pointer-events:auto; cursor:move; background:transparent; }
.sel-edge.t { left:0; right:0; top:calc(-5px*var(--inv,1)); height:calc(10px*var(--inv,1)); }
.sel-edge.b { left:0; right:0; bottom:calc(-5px*var(--inv,1)); height:calc(10px*var(--inv,1)); }
.sel-edge.le{ top:0; bottom:0; left:calc(-5px*var(--inv,1)); width:calc(10px*var(--inv,1)); }
.sel-edge.r { top:0; bottom:0; right:calc(-5px*var(--inv,1)); width:calc(10px*var(--inv,1)); }
/* THE TEXT DOES NOT FIT ITS BOX. Sits just under the bottom-left corner, outside the box, so
   it never covers the words it is talking about. Scaled by --inv like every other piece of
   chrome, so it is the same size on screen at any zoom, and turned back by --rot so it stays
   upright on a box that has been rotated — an upside-down sentence is not a warning, it is a
   puzzle. Pointer events are on: unlike the rest of the overlay this one is pressable. */
.txt-over{ position:absolute; left:0; top:100%; margin-top:calc(6px*var(--inv,1));
  display:inline-flex; align-items:center; gap:calc(5px*var(--inv,1));
  padding:calc(4px*var(--inv,1)) calc(9px*var(--inv,1));
  border:0; border-radius:calc(99px*var(--inv,1)); background:var(--brand,#F40628); color:#fff;
  font:600 calc(11px*var(--inv,1))/1 Inter,system-ui,sans-serif; white-space:nowrap;
  cursor:pointer; pointer-events:auto; transform:rotate(calc(var(--rot,0)*-1deg));
  transform-origin:0 0; box-shadow:0 calc(2px*var(--inv,1)) calc(6px*var(--inv,1)) rgba(0,0,0,.22); }
.txt-over svg{ width:calc(13px*var(--inv,1)); height:calc(13px*var(--inv,1)); flex:none; }
.txt-over:hover{ filter:brightness(1.1); }
.corner-widget.tl{ left:calc(11px*var(--inv,1)); top:calc(11px*var(--inv,1)); cursor:nwse-resize; }
.corner-widget.tr{ right:calc(11px*var(--inv,1)); top:calc(11px*var(--inv,1)); cursor:nesw-resize; }
.corner-widget.bl{ left:calc(11px*var(--inv,1)); bottom:calc(11px*var(--inv,1)); cursor:nesw-resize; }
.corner-widget.br{ right:calc(11px*var(--inv,1)); bottom:calc(11px*var(--inv,1)); cursor:nwse-resize; }
.marquee{ position:absolute; box-sizing:border-box; pointer-events:none; z-index:65; background:rgba(61,149,255,.10); border:calc(1px*var(--inv,1)) solid var(--accent); }
.smart-guide{ position:absolute; pointer-events:none; z-index:59; background:#ff2d78; }
.smart-guide.v{ top:0; height:100%; width:calc(1px*var(--inv,1)); }
.smart-guide.h{ left:0; width:100%; height:calc(1px*var(--inv,1)); }
/* EQUAL-SPACING MARKS (23 Aug 2026): a bar laid ON each gap being compared, with the
   measurement written once. Same pink as the alignment guides — they are the same voice. */
.gap-mark{ position:absolute; pointer-events:none; z-index:60; background:rgba(255,45,120,.22); }
.gap-mark.x{ height:calc(6px*var(--inv,1)); transform:translateY(calc(-3px*var(--inv,1))); }
.gap-mark.y{ width:calc(6px*var(--inv,1)); transform:translateX(calc(-3px*var(--inv,1))); }
/* DISTANCE LABELS (23 Aug 2026): a thin line across the gap with the number on it. Quieter
   than the equal-spacing marks — it reports a fact rather than offering a placement. */
/* the construction line drawn at a cardinal angle while rotating (23 Aug 2026) */
.angle-guide{ position:absolute; pointer-events:none; z-index:59;
  height:calc(1px*var(--inv,1)); background:rgba(255,45,120,.75); }
.dist-mark{ position:absolute; pointer-events:none; z-index:60; }
.dist-mark.x{ height:calc(1px*var(--inv,1)); background:rgba(255,45,120,.55); }
.dist-mark.y{ width:calc(1px*var(--inv,1)); background:rgba(255,45,120,.55); }
.dist-mark .dist-num{ position:absolute; left:50%; top:50%;
  transform:translate(-50%,-50%) scale(var(--inv,1)); transform-origin:center;
  background:rgba(255,45,120,.92); color:#fff; font-size:10px; font-weight:600; line-height:1;
  padding:2px 4px; border-radius:3px; white-space:nowrap; }
.gap-mark .gap-num{ position:absolute; left:50%; top:50%;
  transform:translate(-50%,-50%) scale(var(--inv,1)); transform-origin:center;
  background:#ff2d78; color:#fff; font-size:10px; font-weight:700; line-height:1;
  padding:3px 5px; border-radius:4px; white-space:nowrap; }
.layer.cropping{ cursor:move; outline:calc(2px*var(--inv,1)) solid #12b2a6; outline-offset:calc(-2px*var(--inv,1)); }
/* THE PICTURE'S OWN BOX while cropping — dashed, so at a glance it is not the frame. The frame
   is the solid line and its handles cut; this one is the photograph and its handles resize it
   inside the frame. Same colour as the crop outline so both read as parts of one mode. */
.pic-box{ position:absolute; box-sizing:border-box; pointer-events:none; border:calc(1.5px*var(--inv,1)) dashed #12b2a6; }
.pic-box .handle{ transform:rotate(calc(var(--rot,0) * -1deg)); }
.handle.pic{ border-color:#12b2a6; border-radius:50%; }
/* pen node editor */
#nodeEditor .nd-line{ position:absolute; height:calc(1px*var(--inv,1)); background:var(--accent); transform-origin:0 50%; opacity:.7; }
#nodeEditor .nd-ctrl{ position:absolute; width:calc(9px*var(--inv,1)); height:calc(9px*var(--inv,1)); margin-left:calc(-4.5px*var(--inv,1)); margin-top:calc(-4.5px*var(--inv,1)); border-radius:50%; background:#fff; border:calc(1.4px*var(--inv,1)) solid var(--accent); pointer-events:auto; cursor:move; box-shadow:0 1px 3px rgba(0,0,0,.35); }
#nodeEditor .nd-anchor{ position:absolute; width:calc(10px*var(--inv,1)); height:calc(10px*var(--inv,1)); margin-left:calc(-5px*var(--inv,1)); margin-top:calc(-5px*var(--inv,1)); background:#fff; border:calc(1.6px*var(--inv,1)) solid var(--accent); border-radius:calc(2px*var(--inv,1)); pointer-events:auto; cursor:move; box-shadow:0 1px 3px rgba(0,0,0,.4); }
/* The picked anchor is FILLED, the rest stay hollow — Illustrator's convention, and the
   only way to tell at a glance which point a nudge or a delete will act on.
   Only the fill changes: same size, same border, same position. A handle that also grew
   or moved on selection would shift under the cursor at the moment of clicking it. */
#nodeEditor .nd-anchor.sel{ background:var(--accent); }
.swatch-none{ flex:none; width:var(--ctl-h); height:var(--ctl-h); border:1px solid var(--line); border-radius:50%; background:#fff; position:relative; cursor:pointer; padding:0; }
.swatch-none::after{ content:""; position:absolute; inset:0; border-radius:50%; overflow:hidden; background:linear-gradient(to top right, transparent 0, transparent 43%, #e5484d 43%, #e5484d 57%, transparent 57%, transparent 100%); }
.swatch-none:hover{ border-color:var(--muted); }
.swatch-none.on{ outline:2px solid var(--accent); outline-offset:1px; border-radius:50%; }
/* colour swatch button + palette popover */
/* ONE rule, two kinds of swatch. Some colours in the panel are drawn as .cp-swatch buttons
   and some are native <input type="color"> — and every one of them opens the app's own picker
   anyway, because a single click handler routes them all there. So the difference between
   them was never behaviour, only looks, and they looked different: the button has a border,
   the native input had a ring in a different colour and the browser's own inset square inside
   it, which Safari draws smaller than the box it sits in.
   Listing both selectors on one rule is what stops them drifting again. Matching them with
   two rules is how they came to differ in the first place. */
/* An app's colour boxes join the same rule, for the same reason. They were written as rounded
   squares with the browser's own inset chip inside, and looked nothing like the swatches two
   inches away in the panel. The app popup hangs off the page root rather than inside the
   editor, so it does not inherit --ctl-h and --line — it declares its own pair, and a check
   fails if they stop matching the editor's. */
.cp-swatch,
#props input[type=color],
.app-field input[type=color],
.bw-sw{ box-sizing:border-box; flex:none; width:var(--ctl-h); height:var(--ctl-h); border:1px solid var(--line); border-radius:50%; cursor:pointer; padding:0; position:relative; background-clip:padding-box; }
.cp-swatch:hover,
#props input[type=color]:hover,
.app-field input[type=color]:hover{ border-color:var(--muted); }
.bw-sw:hover{ border-color:var(--line-h); }
/* The colour itself has to fill the box. Left alone, WebKit draws the swatch as a small
   rounded square with its own padding and border inside the input — which is the inset look,
   and no amount of styling the input itself removes it. */
#props input[type=color]::-webkit-color-swatch-wrapper,
.app-field input[type=color]::-webkit-color-swatch-wrapper,
.bw-sw::-webkit-color-swatch-wrapper{ padding:0; }
#props input[type=color]::-webkit-color-swatch,
.app-field input[type=color]::-webkit-color-swatch,
.bw-sw::-webkit-color-swatch{ border:none; border-radius:50%; }
#props input[type=color]::-moz-color-swatch,
.app-field input[type=color]::-moz-color-swatch,
.bw-sw::-moz-color-swatch{ border:none; border-radius:50%; }
.cp-swatch.isnone{ background:#fff !important; }
.cp-swatch.isnone::after{ content:""; position:absolute; inset:0; border-radius:50%; overflow:hidden; background:linear-gradient(to top right, transparent 43%, #e5484d 43%, #e5484d 57%, transparent 57%); }
/* The wheel and the swatches are one panel now, stacked, with a rule between them. They used
   to be two tabs — a click to reach either half of something small enough to show whole. */
.cpw-row{ display:flex; gap:12px; padding:2px 2px 12px; align-items:flex-start; }
.cpw-row.cpf{ align-items:stretch; padding:2px 0 14px; }
.cp-sw{ border-top:1px solid var(--line); padding-top:10px; }
.cp-swhead{ display:flex; align-items:center; justify-content:space-between; font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); margin:0 0 8px; }
/* + at the far right of the heading: save the colour the wheel is showing */
/* + is the last swatch in the grid: a circle the same size as every colour (5 October 2026) */
.cp-add{ display:inline-flex; align-items:center; justify-content:center; width:100%; height:auto; aspect-ratio:1/1; padding:0; margin:0;
  background:transparent; border:1px dashed var(--muted); border-radius:50%; color:var(--muted); cursor:pointer;
  font-size:16px; font-weight:400; line-height:1; letter-spacing:0; transition:color .12s, border-color .12s, transform .08s; }
.cp-add:hover{ color:var(--ink); border-color:var(--ink); transform:scale(1.14); }
/* the ones you saved, kept in their own grid above the built-in palette */
.cp-mine{ margin-bottom:9px; }

/* TAKING A SAVED COLOUR OFF THE SHELF — asked for 29 August 2026.
   The cross sits on the swatch it belongs to and appears on hover or keyboard focus, so a panel
   whose job is choosing a colour is not a wall of crosses. `pointer-events` follows the same
   rule: while it is invisible it cannot be clicked either, or the top-right corner of every
   saved colour would be a dead spot that quietly removed things.
   Only colours the person saved have one — the built-in palette below has nothing to remove. */
.cp-mycell{ position:relative; display:inline-flex; }
.cp-mycell .cp-del{ position:absolute; top:-5px; right:-5px; width:15px; height:15px;
  display:grid; place-items:center; padding:0; border:none; border-radius:50%;
  background:#0D0D0C; color:#fff; font-size:11px; line-height:1; cursor:pointer;
  opacity:0; pointer-events:none; transition:opacity .1s; }
.cp-mycell:hover .cp-del,
.cp-mycell .cp-del:focus-visible{ opacity:1; pointer-events:auto; }
/* Close, in its own band across the top of the panel.
   Nudging it upwards was not enough and was never going to be: the panel's padding is only
   10px, so there is nowhere above the first line for it to go. The contents are given a top
   margin instead, which clears a strip for the cross to sit in — so nothing is level with it,
   rather than nearly nothing. .cp-body's padding-top below is what makes that strip, and the
   two numbers are read together by the test. */
.cp-body{ padding-top:18px; }
.cp-x{ position:absolute; top:3px; right:5px; z-index:2; display:inline-flex; align-items:center; justify-content:center;
  width:22px; height:22px; padding:0; background:transparent; border:none; border-radius:6px; color:var(--muted); cursor:pointer;
  transition:color .12s, background .12s; }
.cp-x:hover{ color:var(--ink); background:var(--hover); }
/* THE SQUARE AND TWO STRIPS — 1 November 2026, from a picture of Figma's picker, replacing the
   wheel. The square is the colour from white to its fullest across, and from light to black
   down; the strips are real range sliders drawn as bars, so dragging, clicking and the arrow
   keys all work the way a slider does. --cpf-h is the colour at full strength, --cpf-c the
   colour as chosen; both are set as it moves. */
.cpf{ display:flex; flex-direction:column; align-items:stretch; gap:12px; }
.cpf-sv{ position:relative; width:100%; height:150px; border-radius:10px; cursor:crosshair; touch-action:none;
  background:linear-gradient(to top,#000,rgba(0,0,0,0)),linear-gradient(to right,#fff,var(--cpf-h,red)); }
.cpf-svthumb{ position:absolute; width:16px; height:16px; margin:-8px 0 0 -8px; border-radius:50%; pointer-events:none;
  box-shadow:0 0 0 3px #fff, 0 0 0 4px rgba(0,0,0,.18), 0 2px 6px rgba(0,0,0,.25); }
.cpf-mid{ display:flex; align-items:center; gap:10px; }
.cpf-mid .cpw-pick{ border:none; background:transparent; width:24px; height:24px; }
.cpf-bars{ flex:1; min-width:0; display:flex; flex-direction:column; gap:10px; }
/* The bar is drawn on the slider's own track, and every rule names input[type=range] as well as
   the class, so it outranks the site-wide slider look (a thin 3px line with a blue fill). */
input.cpf-hue[type=range], input.cpf-alpha[type=range]{ -webkit-appearance:none; appearance:none; display:block; width:100%; height:18px; margin:0; background:transparent; cursor:pointer; outline:none; }
input.cpf-hue[type=range]::-webkit-slider-runnable-track{ height:14px; border-radius:7px;
  background:linear-gradient(to right,#f00 0%,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,#f00 100%); }
input.cpf-alpha[type=range]::-webkit-slider-runnable-track{ height:14px; border-radius:7px;
  background:linear-gradient(to right,rgba(0,0,0,0),var(--cpf-c,#000)),repeating-conic-gradient(#d9d9de 0% 25%,#fff 0% 50%) 0 0/8px 8px; }
input.cpf-hue[type=range]::-moz-range-track{ height:14px; border-radius:7px;
  background:linear-gradient(to right,#f00 0%,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,#f00 100%); }
input.cpf-alpha[type=range]::-moz-range-track{ height:14px; border-radius:7px;
  background:linear-gradient(to right,rgba(0,0,0,0),var(--cpf-c,#000)),repeating-conic-gradient(#d9d9de 0% 25%,#fff 0% 50%) 0 0/8px 8px; }
input.cpf-hue[type=range]::-moz-range-progress, input.cpf-alpha[type=range]::-moz-range-progress{ background:transparent; }
input.cpf-hue[type=range]::-webkit-slider-thumb, input.cpf-alpha[type=range]::-webkit-slider-thumb{ -webkit-appearance:none; appearance:none;
  margin-top:-2px; width:18px; height:18px; border:3px solid #fff; border-radius:50%; box-sizing:border-box;
  box-shadow:0 0 0 1px rgba(0,0,0,.18), 0 2px 6px rgba(0,0,0,.25); cursor:grab; }
input.cpf-hue[type=range]::-webkit-slider-thumb{ background:var(--cpf-thumb,red); }
input.cpf-alpha[type=range]::-webkit-slider-thumb{ background:var(--cpf-c,#000); }
input.cpf-hue[type=range]::-moz-range-thumb, input.cpf-alpha[type=range]::-moz-range-thumb{ width:12px; height:12px; border:3px solid #fff; border-radius:50%;
  box-shadow:0 0 0 1px rgba(0,0,0,.18), 0 2px 6px rgba(0,0,0,.25); cursor:grab; }
input.cpf-hue[type=range]::-moz-range-thumb{ background:var(--cpf-thumb,red); }
input.cpf-alpha[type=range]::-moz-range-thumb{ background:var(--cpf-c,#000); }
input.cpf-hue[type=range]:focus-visible::-webkit-slider-thumb, input.cpf-alpha[type=range]:focus-visible::-webkit-slider-thumb{ box-shadow:0 0 0 2px var(--accent,#3b82f6), 0 2px 6px rgba(0,0,0,.25); }
/* the colour code and the opacity figure, in soft filled boxes under the strips */
.cpf-fields{ gap:8px; }
.cpf-fields .cp-hex{ height:34px; padding:0 12px; border:none; border-radius:9px; background:var(--hover); color:var(--ink); font-size:13px; text-transform:uppercase; }
.cpf-fields .cp-hex:focus{ outline:none; box-shadow:0 0 0 2px var(--accent,#3b82f6); }
.cpf-pct{ flex:none; display:inline-flex; align-items:center; justify-content:flex-end; min-width:58px; height:34px; padding:0 12px; border-radius:9px;
  background:var(--hover); color:var(--ink); font-size:13px; font-variant-numeric:tabular-nums; }
.cpw-hexrow{ display:flex; gap:6px; align-items:center; }
/* the eyedropper, beside the hex box: both answer "what colour is that, exactly" */
.cpw-pick{ flex:none; display:inline-flex; align-items:center; justify-content:center; width:28px; height:28px; padding:0;
  background:var(--field-bg,transparent); border:1px solid var(--line); border-radius:var(--ctl-r,7px); color:var(--muted); cursor:pointer; transition:color .12s, border-color .12s; }
.cpw-pick:hover{ color:var(--ink); border-color:var(--muted); }
/* While you are aiming it, the popup gets out of the way of the thing you are aiming at — but
   it stays on the page, so it is still there afterwards showing the colour you picked. */
.cp-pop.picking{ opacity:0; pointer-events:none; }
/* The popup can never be taller than the window. With the swatches under the wheel it grew,
   and anything past the bottom edge of the screen is not just awkward — it is invisible, with
   nothing on screen to say it is there. Capped and scrollable, the swatches are always
   reachable however short the window is. */
/* ABOVE EVERYTHING IT CAN BE OPENED FROM.
   Reported 2 September 2026: "still won't open color selector". It was opening every time — at
   z-index 130, underneath the grid builder at 150, so the panel appeared behind a full-screen
   window and nobody could see it. The press had already cancelled the browser's own colour box
   to make room for this one, so the result was a swatch that did nothing at all.
   This is a popup: it belongs on top of whatever opened it, not at a level that has to be
   checked against every new window. Above the builder, above the app cards, above the font
   popup and the drag readout; below the eyedropper's loupe, which has to stay on top while a
   colour is being picked FROM this panel. */
.cp-pop{ position:fixed; z-index:420; background:var(--panel); border:1px solid var(--line); border-radius:12px; box-shadow:var(--float-shadow); padding:10px; width:auto; min-width:224px; max-width:344px; max-height:calc(100vh - 24px); overflow-y:auto; overscroll-behavior:contain; }
/* 26px cells rather than a share of the width: eight big squares added 230px of height below
   the wheel, which is what pushed the swatches off the bottom in the first place. */
.cp-grid{ display:grid; grid-template-columns:repeat(8,26px); gap:5px; justify-content:space-between; }
/* ROUND, in both grids — your own colours at the top and the palette below — asked for
   26 August 2026, finishing the change made to the swatches in the panels the same day. One
   class serves both grids, so there is nothing here to keep in step. The strike across the
   "no colour" cell needs the same corner or it draws itself a square inside a circle. */
.cp-cell{ width:100%; aspect-ratio:1/1; border-radius:50%; border:1px solid rgba(0,0,0,.12); cursor:pointer; padding:0; transition:transform .08s; }
.cp-cell:hover{ transform:scale(1.14); }
.cp-none{ background:#fff; position:relative; }
.cp-none::after{ content:""; position:absolute; inset:0; border-radius:50%; overflow:hidden; background:linear-gradient(to top right, transparent 42%, #e5484d 42%, #e5484d 58%, transparent 58%); }
.cp-hex{ flex:1; }
/* template organise dialog */
.tpl-dlg-ov{ position:fixed; inset:0; z-index:200; background:rgba(15,15,20,.5); display:flex; align-items:center; justify-content:center; -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px); }
.tpl-dlg{ width:420px; max-width:calc(100vw - 32px); background:#fff; color:#111; border:1px solid var(--ed-line); border-radius:16px; box-shadow:var(--float-shadow); padding:20px; }
.tpl-dlg-h{ font-size:16px; font-weight:700; color:#111; margin-bottom:14px; }
.tpl-f{ display:block; margin-bottom:12px; }
.tpl-f>span{ display:block; font-size:12px; font-weight:600; color:#555; margin-bottom:5px; }
.tpl-f input,.tpl-f select{ width:100%; padding:9px 11px; border:1px solid var(--ed-line); border-radius:9px; background:#fff; color:#111; font-size:14px; }
.tpl-f input::placeholder{ color:#9a9a9a; }
.tpl-f select option{ color:#111; background:#fff; }
.tpl-dlg-btns{ display:flex; justify-content:flex-end; gap:8px; margin-top:18px; }
/* THE MESSAGE A FREE MEMBER GETS ON A PRO TEMPLATE. It borrows the template dialog's box
   rather than inventing a second kind of dialog, because it is the same kind of thing and two
   dialogs that differ slightly look like a mistake rather than a distinction. Wider by a
   little, because it is sentences and not fields. */
/* Roomier and softer than the dialog it borrows from, and the heading is bigger than that
   dialog's. At the shared 16px it was thinner than the buttons under it, which is a card whose
   loudest thing is a button rather than what the card is telling you. */
/* `position:relative` so the cross has something to sit in the corner OF. Without it the
   cross would be placed against the whole page and end up in the corner of the screen. */
/* ---- A TEMPLATE'S OWN PAGE — 22 September 2026 ----------------------------------------
   Picture on the left at whatever shape the design is, its facts on the right, one button.
   The picture is given the room and the panel takes what it needs, because the thing somebody
   came to look at is the design rather than the words beside it. */
.tpv{ position:relative; width:min(1240px,calc(100vw - 48px)); max-height:calc(100vh - 64px); padding:0;
  overflow:hidden; border-radius:20px; }
/* The panel holds still and its INSIDE scrolls, so the close cross stays in the corner rather
   than sliding off the top as soon as somebody looks at the suggestions. */
.tpv-body{ display:grid; grid-template-columns:minmax(0,1.15fr) minmax(300px,1fr); grid-template-rows:auto auto;
  gap:0; max-height:calc(100vh - 64px); overflow:auto; }
/* THE SOFT GREY STAGE STAYS, and it was nearly taken out on 22 September 2026 for looking
   empty. Put on the screen both ways, the opposite is true: the cover pictures are 300 pixels
   across and the panel is now 1240, so there is a lot of room around the picture whatever is
   behind it. On white that room reads as a picture that failed to load. On the grey it reads as
   a stage the picture is standing on. */
.tpv-pic{ grid-column:1; grid-row:1; display:flex; flex-direction:column; align-items:center; justify-content:center;
  padding:40px 44px; min-height:0; background:var(--field,#F6F6F7); }
/* THE PICTURE IS NEVER BLOWN UP. Every cover picture in the shop is 300 pixels across, and a
   300 pixel picture stretched to five hundred is a soft picture in a shop that sells sharpness.
   It is shown at its own size, centred, with room around it. The ceiling is only there for the
   day the covers get bigger. */
.tpv-pic img{ max-width:100%; max-height:min(calc(100vh - 420px),520px); width:auto; height:auto; display:block;
  border-radius:10px; box-shadow:0 18px 40px -18px rgba(0,0,0,.45); }
.tpv-nopic{ width:100%; aspect-ratio:4 / 5; border-radius:10px; background:var(--line,#E3E4E8); }
/* THE PAGES OF A TEMPLATE, one on top of another — 22 September 2026.
   EVERY PAGE IS IN THE STAGE AT ONCE, stacked in the same grid cell, with the ones not showing
   turned down to nothing rather than taken away. That is what keeps the stage the height of the
   TALLEST page, so stepping from page to page does not make the whole panel jump up and down
   under the person doing the stepping. */
.tpv-stage{ position:relative; display:grid; place-items:center; min-width:0; }
.tpv-stage .tpv-pv{ grid-area:1 / 1; opacity:0; pointer-events:none; transition:opacity .18s; }
.tpv-stage .tpv-pv.on{ opacity:1; pointer-events:auto; }
/* THE ARROWS ARE ALWAYS THERE, not only while being pointed at. The cards hide theirs until
   the pointer arrives, which works on a wall of cards and fails here: a phone has no pointer,
   and this panel is where somebody has stopped to look properly. */
.tpv-arr{ position:absolute; top:50%; transform:translateY(-50%); z-index:2;
  width:40px; height:40px; display:grid; place-items:center; padding:0;
  border:1px solid var(--line,#E3E4E8); border-radius:50%; background:#fff; color:#16161A;
  box-shadow:0 2px 10px -2px rgba(0,0,0,.28); cursor:pointer;
  transition:background .14s, transform .12s; }
.tpv-arr svg{ width:20px; height:20px; display:block; }
.tpv-arr-prev{ left:-6px; }
.tpv-arr-next{ right:-6px; }
.tpv-arr:hover{ background:var(--hover); }
.tpv-arr:active{ transform:translateY(-50%) scale(.94); }
.tpv-arr:focus{ outline:none; }
.tpv-arr:focus-visible{ outline:2px solid var(--ld-dark,#16161A); outline-offset:2px; }
/* The dots are a row under the picture. They are the only way through on a touchscreen and the
   only sign the other pages exist, so they are never hidden. */
/* A LONG TEMPLATE STILL GETS A ROW OF DOTS, so it wraps rather than pushing the panel wider.
   Sixteen of them come to about 250 pixels, which fits; this is for the day one does not. */
.tpv-dots{ display:flex; flex-wrap:wrap; justify-content:center; gap:8px; margin-top:18px; max-width:100%; }
/* ONE COLOUR AT THREE STRENGTHS, rather than three greys. The dot is the same near-black the
   rest of the panel uses, faded when it is another page, half up under the pointer, full and a
   little larger when it is the page you are on. Three separate greys would be three things to
   keep in step, and the editor's shared hover grey is a surface tint that would be invisible on
   a mark this small. */
.tpv-dot{ width:8px; height:8px; padding:0; border:none; border-radius:50%;
  background:#16161A; opacity:.26; cursor:pointer; transition:opacity .14s, transform .14s; }
.tpv-dot:hover{ opacity:.55; }
.tpv-dot.on{ opacity:1; transform:scale(1.25); }
.tpv-dot:focus{ outline:none; }
.tpv-dot:focus-visible{ outline:2px solid var(--ld-dark,#16161A); outline-offset:3px; }

.tpv-side{ grid-column:2; grid-row:1; padding:40px 36px; display:flex; flex-direction:column; align-items:flex-start; gap:0; }
.tpv-crown{ width:30px; height:30px; display:grid; place-items:center; border-radius:50%;
  background:#fff; color:var(--gold,#C79A3B); box-shadow:0 1px 4px rgba(0,0,0,.22); margin-bottom:14px; }
.tpv-crown svg{ width:18px; height:18px; display:block; }
.tpv-h{ margin:0; font-size:22px; line-height:1.25; font-weight:700; letter-spacing:-.02em; color:#111; }
.tpv-facts{ margin:10px 0 0; font-size:14px; color:var(--mut3,#8A8A93); }
/* KEEP IT, SAVE IT, SEND IT — one row. The button takes the room and the two small ones sit
   beside it, so the thing almost everybody came to press is still the obvious one. */
.tpv-acts{ margin-top:24px; width:100%; display:flex; align-items:stretch; gap:10px; }
.tf .tpv-use, .tpv-use{ flex:1 1 auto; min-width:0; height:50px; border:none; border-radius:12px;
  background:var(--ld-dark,#16161A); color:#fff; font:inherit; font-size:16px; font-weight:700; cursor:pointer;
  transition:background .14s; }
.tf .tpv-use:hover, .tpv-use:hover{ background:#2A2A2E; }
/* NO BLUE RING WHEN IT IS CLICKED. Asked for on 22 September 2026, with a picture of it. The
   browser draws its own focus ring round a button the moment it is pressed, in its own blue,
   which has nothing to do with this page and looks like an error. It is taken off the PRESS
   and kept for the KEYBOARD — somebody tabbing to this button still has to be able to see
   where they are — and the keyboard ring is drawn in the panel's own black. */
.tpv-acts button:focus{ outline:none; }
.tpv-acts button:focus-visible{ outline:2px solid var(--ld-dark,#16161A); outline-offset:2px; }
.tpv-ico{ flex:0 0 auto; width:50px; height:50px; display:grid; place-items:center; padding:0;
  border:1px solid var(--line,#E3E4E8); border-radius:12px; background:#fff; color:#16161A;
  cursor:pointer; transition:border-color .14s, background .14s, color .14s, transform .12s; }
.tpv-ico:hover{ border-color:rgba(0,0,0,.34); background:var(--hover); }
.tpv-ico:active{ transform:scale(.96); }
.tpv-ico svg{ display:block; }
/* The heart is outlined until it means something, then it fills and turns red — the same
   drawing and the same red as the heart on the cards. */
.tpv-like svg{ fill:none; stroke:currentColor; transition:fill .12s; }
.tpv-like:hover svg{ fill:rgba(244,0,81,.14); }
.tpv-like.on{ color:#F40051; border-color:rgba(244,0,81,.38); }
.tpv-like.on svg{ fill:#F40051; }
/* Copy says so on the button itself, because a message anywhere else is a message somebody
   pressing a small button is not looking at. */
.tpv-share.ok{ color:#0B7A3B; border-color:rgba(11,122,59,.4); background:#F1FAF4; }
.tpv-share.ok svg{ display:none; }
.tpv-share.ok::after{ content:"Copied"; font:inherit; font-size:12px; font-weight:700; }
.tpv-note{ margin:12px 0 0; font-size:13px; color:var(--mut3,#8A8A93); }
/* WHAT YOU GET. Three lines under the button, each with its own mark. The type is the same
   size as the facts above it and the marks are drawn thin, because this is somebody reading
   on before they decide rather than being sold at. */
.tpv-facts-list{ list-style:none; margin:22px 0 0; padding:0; width:100%;
  display:flex; flex-direction:column; gap:14px; }
.tpv-facts-list li{ display:flex; align-items:center; gap:12px;
  font-size:14px; line-height:1.35; color:#3A3A40; }
.tpv-facts-list svg{ flex:0 0 auto; width:20px; height:20px; display:block; color:#16161A; }
/* MORE LIKE THIS — the suggestions across the foot of the panel.
   A GRID RATHER THAN A SLIDING ROW, since 22 September 2026: a row of small pictures that has
   to be scrolled sideways hides most of what it is offering, and the pictures were too small to
   judge a design by. These fill the width and wrap, so everything on offer is simply there.
   Each one keeps its own proportions — a poster stays a poster — and they hang from the top of
   their row rather than being stretched to match their neighbours. */
.tpv-more{ grid-column:1 / -1; grid-row:2; min-width:0; padding:28px 36px 34px; border-top:1px solid var(--line,#E3E4E8); }
.tpv-more-h{ margin:0 0 16px; font-size:18px; font-weight:700; letter-spacing:-.01em; color:#111; }
.tpv-more-row{ display:grid; min-width:0; grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
  align-items:start; gap:18px; }
.tpv-tile{ position:relative; padding:0; border:none; background:none;
  border-radius:12px; cursor:pointer; line-height:0; }
.tpv-tile img{ width:100%; height:auto; display:block; border-radius:12px; background:var(--line,#E3E4E8);
  box-shadow:0 2px 8px -2px rgba(0,0,0,.22); transition:transform .14s, box-shadow .14s; }
.tpv-tile:hover img{ transform:translateY(-3px); box-shadow:0 12px 24px -10px rgba(0,0,0,.4); }
.tpv-tile:focus{ outline:none; }
.tpv-tile:focus-visible{ outline:2px solid var(--ld-dark,#16161A); outline-offset:3px; }
.tpv-tile-crown{ position:absolute; top:9px; right:9px; width:26px; height:26px; display:grid; place-items:center;
  border-radius:50%; background:#fff; color:var(--gold,#C79A3B); box-shadow:0 1px 4px rgba(0,0,0,.22); }
.tpv-tile-crown svg{ width:16px; height:16px; display:block; }
/* On a phone the picture goes on top and the words underneath, because a column of each
   would leave both of them too narrow to be worth looking at. */
@media (max-width:820px){
  .tpv-body{ grid-template-columns:minmax(0,1fr); grid-template-rows:auto auto auto; }
  .tpv-pic{ grid-column:1; grid-row:1; padding:20px; }
  .tpv-pic img{ max-height:42vh; }
  .tpv-side{ grid-column:1; grid-row:2; padding:22px 20px 26px; }
  .tpv-more{ grid-column:1; grid-row:3; padding:20px 20px 24px; }
  .tpv-more-row{ grid-template-columns:repeat(auto-fill,minmax(132px,1fr)); gap:12px; }
  .tpv-h{ font-size:18px; }
}
.pro-wall{ width:460px; text-align:left; padding:26px 28px 28px; border-radius:22px;
  position:relative; }
/* clear of the cross, the same way the other dialog's heading is */
.pro-wall .tpl-dlg-h{ padding-right:30px; }
.pro-wall .tpl-dlg-h{ font-size:21px; line-height:1.25; letter-spacing:-.015em; margin-bottom:12px; }
/* The same crown that is on the card they just clicked, so the message is visibly about THAT
   thing rather than a general notice that happened to appear. Bigger, and on a tinted circle
   rather than a white one: the dialog is already white, and a white circle on white is nothing. */
.pro-wall-badge{ display:grid; place-items:center; margin-bottom:12px;
  width:44px; height:44px; border-radius:50%;
  background:#F4F4F6; color:var(--gold,#C79A3B); }
.pro-wall-badge svg{ width:26px; height:26px; display:block; }
.pro-wall-p{ margin:0 0 10px; font-size:14px; line-height:1.5; color:#444; }
.pro-wall-p:last-of-type{ margin-bottom:0; }
/* TWO FULL-WIDTH PILLS, STACKED, asked for 29 August 2026 against a reference picture. They
   were a pair of small buttons in the bottom right corner, which is the shape of a form you
   are finishing — Cancel and OK. This is not that. It is one offer with a way out under it,
   and the offer has to look like the thing being offered.
   `.tpl-dlg-btns` sends them to the right and lays them in a row; both are undone here rather
   than changed there, because that row is right for the template dialog that owns it.
   BLACK FOR THE OFFER, NOT GOLD. It was gold for an hour. Gold on a white card is a soft
   button, and next to it the grey one looked like the confident choice; black is the plainest
   way to say which of the two is the point. The gold stays where it belongs — on the crown,
   which is the thing being sold. */
.pro-wall .tpl-dlg-btns{ flex-direction:column; justify-content:stretch; gap:10px; margin-top:22px; }
.pro-wall .tpl-dlg-btns .mini-btn{ width:100%; height:54px; border-radius:999px;
  display:flex; align-items:center; justify-content:center;
  font-size:15px; font-weight:700; letter-spacing:-.01em; border:none;
  transition:background .14s, transform .1s; }
.pro-wall .tpl-dlg-btns .mini-btn:active{ transform:scale(.985); }
.pro-wall .tpl-dlg-btns .primary{ background:#0D0D0C; color:#fff; }
.pro-wall .tpl-dlg-btns .primary:hover{ background:#262625; filter:none; }
/* TWO AGAIN — 12 September 2026, later the same day. "Notify me when Pro launches" was taken
   off in the morning and "Browse free templates" wore the black alone for a few hours. Now the
   offer is real: "Get Pro" is the black one on top and goes to Stripe's checkout; "Browse free
   templates" is the grey way out under it, as the picture from 29 August had it. */
/* The text field in the app's own dialog, and the text-style row in the properties panel. */
.ask-input{ margin-top:14px; }
.ask-field{ box-sizing:border-box; width:100%; height:40px; padding:0 12px; border:1px solid var(--ed-line,#e2e2ea);
  border-radius:10px; background:#fff; color:#111; font-family:inherit; font-size:14px; outline:none; }
/* The accent and its tint by name only. Writing the hex here would mean the field kept the
   old colour on the day the accent changed - which is exactly what the theme test forbids,
   and it caught this line before it shipped. */
.ask-field:focus{ border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.ts-row{ display:flex; gap:var(--pp-gap,10px); align-items:center; }
.ts-sel{ flex:1; min-width:0; box-sizing:border-box; height:var(--ctl-h); border:1px solid var(--field-bd);
  background:transparent; border-radius:var(--ctl-r); padding:0 var(--ctl-x); font-family:inherit; font-size:13px;
  color:var(--ink); outline:none; }
.ts-sel:hover{ border-color:var(--bdh); }
.ts-sel:focus{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
.ts-row .mini-btn{ flex:none; height:var(--ctl-h); border-radius:var(--ctl-r); padding:0 11px; font-size:11px; }
/* The line under the picker. Plain when the box matches its style, amber when it has been
   changed since - the same colour the missing-font note uses, and for the same reason:
   nothing is broken, something just is not what it says on the label. */
.ts-note{ margin-top:7px; font-size:11px; line-height:1.5; color:var(--muted); }
.ts-note.off{ color:#8A5A08; background:rgba(224,168,60,.16); border:1px solid rgba(224,168,60,.34);
  border-radius:8px; padding:7px 9px; }
.ts-link{ background:none; border:none; padding:0; font:inherit; color:var(--accent); cursor:pointer; text-decoration:underline; }
.ts-link:hover{ filter:brightness(1.15); }
/* ---- the app's own confirmation ----------------------------------------------------------
   REDRAWN 29 August 2026 against a reference picture Mounir sent, alongside a screenshot of
   the browser's own box it replaces. Centred, roomy, one clear thing to do; a cross top right;
   no icon, asked for in those words.
   THE WORDS ARE UNCHANGED — "keep our message the same". Only the box around them moved. */
.ask-dlg{ width:430px; text-align:center; padding:30px 30px 26px; border-radius:22px;
  position:relative; }
.ask-dlg .tpl-dlg-h{ font-size:19px; line-height:1.3; letter-spacing:-.015em;
  margin:0 0 10px; padding-right:26px; }
.ask-body{ font-size:14px; line-height:1.55; color:#6B6B72; }
.ask-body p{ margin:0 0 10px; }
.ask-list{ margin:0 0 10px; padding-left:18px; text-align:left; display:inline-block; }
.ask-list li{ margin-bottom:3px; }
/* The one line that must not be skimmed past. */
.ask-warn{ font-weight:700; color:var(--danger); margin-bottom:0 !important; }
/* THE CROSS. Black circle, white mark — a third way to say no, and the only one of the three
   that can be seen. Escape and clicking away were the other two and neither is on the screen. */
.ask-dlg .ask-x,
.tpv .ask-x,
.pro-wall .ask-x{ position:absolute; top:14px; right:14px; width:30px; height:30px;
  display:grid; place-items:center; padding:0; border:none; border-radius:50%;
  background:#0D0D0C; color:#fff; cursor:pointer; }
/* THE REST OF THE CROSS IS KEYED ON THE CROSS ITSELF, not on a list of the dialogs that have
   one. Until 22 September 2026 all three of these rules named the dialogs, and the template
   panel had been added to the first list and forgotten in the other two — so it got the black
   circle and the corner, and a cross drawn at whatever size the drawing happened to be, which
   burst out of its own circle. Mounir spotted it and asked for the Pro message's cross. Written
   this way the next dialog to want one cannot get half of it. */
button.ask-x:hover{ background:#333; }
button.ask-x svg{ width:14px; height:14px; display:block; }
button.ask-x:focus{ outline:none; }
button.ask-x:focus-visible{ outline:2px solid #0D0D0C; outline-offset:2px; }
/* Side by side and centred, the way the reference has them, rather than pushed into the
   bottom-right corner like a form you are finishing. */
.ask-dlg .tpl-dlg-btns{ justify-content:center; gap:12px; margin-top:22px; }
.ask-dlg .tpl-dlg-btns .mini-btn{ min-width:132px; height:48px; border-radius:14px;
  display:flex; align-items:center; justify-content:center;
  font-size:15px; font-weight:700; letter-spacing:-.01em; }
/* Cancel: white with a light grey stroke. The safe one, and the one that holds focus. */
.ask-dlg .tpl-dlg-btns .ask-no{ background:#fff; color:#0D0D0C; border:1px solid rgba(0,0,0,.18); }
.ask-dlg .tpl-dlg-btns .ask-no:hover{ background:#fff; border-color:rgba(0,0,0,.34); }
/* BLACK, NOT RED, since 29 August 2026. It was red so the dangerous button could not be
   mistaken for the safe one — which was right when the other button was also coloured. Beside
   a white one, black already reads as the deliberate choice, and it is what the reference
   picture uses. The class name is kept: several callers ask for `danger` and mean "this one
   destroys something", which is still true and may want to look different again one day. */
.ask-dlg .tpl-dlg-btns .ask-danger,
.ask-dlg .tpl-dlg-btns .primary{ background:#0D0D0C; color:#fff; border:1px solid #0D0D0C; }
.ask-dlg .tpl-dlg-btns .ask-danger:hover,
.ask-dlg .tpl-dlg-btns .primary:hover{ background:#262625; filter:none; }
/* The focus ring is black here, not the accent blue. Focus lands on Cancel the moment this
   opens, so on a card that is otherwise black and white the ring is the loudest colour on
   the screen — and it is pointing at the button you are least likely to want highlighted. */
.ask-dlg .mini-btn:focus-visible{ outline:2px solid #0D0D0C; outline-offset:2px; }
.ask-dlg .ask-x:focus-visible{ outline:2px solid #0D0D0C; outline-offset:2px; }
.mini-btn.primary{ background:var(--accent); color:#fff; border-color:var(--accent); }
.mini-btn.primary:hover{ filter:brightness(1.06); }
/* admin template filter bar */
.pj-tfilter{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:-4px 0 14px; }
.pj-tfilter select{ padding:7px 10px; border:1px solid var(--line); border-radius:9px; background:var(--card,#fff); color:var(--ink); font-size:13px; max-width:260px; }
.pj-tfilter select:disabled{ opacity:.5; }
.pj-type{ font-size:11px; color:var(--muted); opacity:.85; margin-top:1px; }
/* gradient stops bar */
.grad-bar{ position:relative; height:22px; border-radius:6px; border:1px solid var(--line); cursor:copy; background-image:linear-gradient(45deg,#ddd 25%,transparent 25%),linear-gradient(-45deg,#ddd 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#ddd 75%),linear-gradient(-45deg,transparent 75%,#ddd 75%); background-size:10px 10px; background-position:0 0,0 5px,5px -5px,-5px 0; }
.grad-stop{ position:absolute; top:50%; width:14px; height:14px; margin-left:-7px; transform:translateY(-50%); border-radius:50%; border:2px solid #fff; box-shadow:0 0 0 1px rgba(0,0,0,.35),0 1px 3px rgba(0,0,0,.4); cursor:grab; }
.grad-stop.sel{ border-color:var(--accent); box-shadow:0 0 0 2px var(--accent),0 1px 3px rgba(0,0,0,.4); }
.stage-wrap.tool-gradient .layer,.stage-wrap.tool-gradient{ cursor:crosshair; }
/* "Replace" pill shown centered on a selected image */
/* ===========================================================================================
   A PICTURE BEING DRAGGED OVER A PICTURE FRAME. 10 September 2026.

   The outline is drawn INSIDE the frame and inherits its corners, so a rounded picture is
   outlined round rather than a square drawn over the top of it. The wash behind it is what
   makes an empty grey frame read as picked out; the outline alone disappears against grey.
   The words are the part that matters: filling an empty frame costs nothing and replacing
   somebody's photograph does, and a coloured glow cannot tell those two apart.
   Neither piece takes the pointer — the drop has to reach the frame underneath.
   =========================================================================================== */
/* WHY PINK AND NOT THE ACCENT. Asked for 10 September 2026: "make all the replace image
   effects, outlines, and bullets, pink, not blue for better visibility". The editor's accent
   is blue and already carries the selection handles, the guides and half the right-hand panel,
   so a blue outline round a picture is one more blue thing on a blue page. Pink is the only
   strong colour the editor does not otherwise use, so it can only mean this one thing.
   It is this pink rather than a brighter one because white has to sit on it: 4.6 to 1 against
   white for the label, and 3.9 against a near-black photograph for the outline.
   All of it reads --drop-accent, declared with the editor's other colours.

   AIMING — the second before it arms. A picture dragged over another answers at once, so
   you know it is a target, and the accent sweeps across it to show how much of the hold is left.
   Pull away and it stops; there is no moment where something happens without warning.
   The duration comes from the app, not from here: one number decides both the clock and the
   sweep, so they can never drift apart. */
.layer.dropaim::before{
  content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none; z-index:6;
  box-shadow:inset 0 0 0 calc(1.2px*var(--pinv,1)) var(--drop-accent);
}
.layer.dropaim::after{
  content:attr(data-dropmsg); position:absolute; left:50%; top:50%;
  transform:translate(-50%,-50%); z-index:7; pointer-events:none; white-space:nowrap;
  background:var(--drop-accent); color:#fff; font-weight:600;
  font-size:calc(8.4px*var(--pinv,1)); line-height:1;
  padding:calc(5.4px*var(--pinv,1)) calc(8.4px*var(--pinv,1));
  border-radius:calc(5.4px*var(--pinv,1));
  box-shadow:0 calc(1.2px*var(--pinv,1)) calc(7.2px*var(--pinv,1)) rgba(16,24,40,.32);
}
/* THE WASH DOES NOT MOVE. It swept across to show the hold running down; taken out on
   10 September 2026 — "remove the animated effect from image replacement, keep everything else
   the same". The hold itself is unchanged; only the moving part is gone, so a frame you are
   aiming at is a lighter wash and the armed one is the stronger. It rides on the clip so it is
   cut to the frame's rounded corners like the picture is, rather than a square creeping out
   over them. */
.layer.dropaim .imgclip::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:var(--drop-accent-wash); opacity:.55;
}
.layer.dropfill::before{
  content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none; z-index:6;
  box-shadow:inset 0 0 0 calc(2.4px*var(--pinv,1)) var(--drop-accent);
  background:var(--drop-accent-wash);
}
/* THE SAME COUNTER-SCALING THE REPLACE PILL USES. Everything on the canvas is drawn at the
   page's zoom, so a label written in plain pixels is a speck at 25% and a banner at 400%.
   --pinv cancels the zoom, which keeps it the same size on the screen at every zoom — the only
   size that matters for something you are reading rather than designing. */
.layer.dropfill::after{
  content:attr(data-dropmsg); position:absolute; left:50%; top:50%;
  transform:translate(-50%,-50%); z-index:7; pointer-events:none; white-space:nowrap;
  background:var(--drop-accent); color:#fff; font-weight:600;
  font-size:calc(8.4px*var(--pinv,1)); line-height:1;
  padding:calc(5.4px*var(--pinv,1)) calc(8.4px*var(--pinv,1));
  border-radius:calc(5.4px*var(--pinv,1));
  box-shadow:0 calc(1.2px*var(--pinv,1)) calc(7.2px*var(--pinv,1)) rgba(16,24,40,.32);
}

.img-replace{ position:absolute; left:calc(8px*var(--pinv,1)); top:calc(8px*var(--pinv,1)); display:inline-flex; align-items:center; gap:calc(3.6px*var(--pinv,1)); padding:calc(4.2px*var(--pinv,1)) calc(7.8px*var(--pinv,1)); font-size:calc(7.8px*var(--pinv,1)); font-weight:600; color:#fff; background:rgba(18,18,22,.82); border-radius:calc(5.4px*var(--pinv,1)); cursor:pointer; opacity:0; pointer-events:none; transition:opacity .12s; z-index:6; white-space:nowrap; box-shadow:0 calc(1.2px*var(--pinv,1)) calc(7.2px*var(--pinv,1)) rgba(0,0,0,.4); -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px); }
.img-replace svg{ width:calc(8.4px*var(--pinv,1)); height:calc(8.4px*var(--pinv,1)); }
/* Shows on any selected image, whatever tool is active. It used to require the Select
   tool, which meant it vanished the moment you picked up anything else — including while
   you were still looking at the image you had just selected. */
.layer.image.selected .img-replace{ opacity:1; pointer-events:auto; }
/* One exception: the eyedropper exists to click pixels, and the pill sits over the corner
   of the image. Left visible so nothing appears to disappear, but it stops intercepting
   the click so sampling still works where it is. */
.stage-wrap.tool-eyedropper .layer.image.selected .img-replace{ opacity:.4; pointer-events:none; }
.img-replace:hover{ background:rgba(18,18,22,.96); }
.layer.image.cropping .img-replace{ display:none !important; }
.stage-wrap.tool-pen .layer,.stage-wrap.tool-pen{ cursor:crosshair; }
/* OVER THE LINE OF A PATH, THE PEN WILL ADD A POINT TO IT RATHER THAN START A NEW ONE, and the
   cursor is the only warning there is. Illustrator draws a "+" beside the nib for exactly this;
   `copy` is the browser's own plus-badged cursor and says the same thing without an asset to
   ship. Without it the rule is invisible, and drawing near an existing line quietly adds to the
   old one — measured on 28 August 2026, and it made the pen look broken. */
.stage-wrap.tool-pen.pen-add .layer,.stage-wrap.tool-pen.pen-add{ cursor:copy; }
.stage-wrap.tool-node .layer,.stage-wrap.tool-node{ cursor:default; }
/* Canva-style floating text island */
/* THE TEXT BAR FOLLOWS THE THEME, since 26 August 2026. It was built always-white with dark
   text written into a dozen rules, so in the dark theme it stayed a white slab floating over
   a dark editor. Every colour here now reads the same variables the rest of the editor does —
   the panel colour, the ink, the control fill — which is what makes it change with the theme
   instead of having to be changed by hand. */
.text-island{ position:fixed; z-index:120; display:flex; align-items:center; gap:2px; background:var(--panel); border:1px solid var(--line); border-radius:12px; box-shadow:var(--float-shadow); padding:5px; color:var(--ink); }
.text-island.hidden{ display:none; }
.text-island .ti-font{ height:29px; border:1px solid transparent; border-radius:9px; background:var(--ctl-bg); padding:0 10px; font-size:13px; max-width:158px; color:var(--ink); cursor:pointer; }
.text-island .ti-fontbtn{ display:flex; align-items:center; justify-content:space-between; gap:7px; height:29px; min-width:104px; max-width:150px; border:1px solid transparent; border-radius:9px; background:var(--ctl-bg); padding:0 10px; font-size:13px; color:var(--ink); cursor:pointer; }
.text-island .ti-fontbtn:hover{ background:var(--hover); }
.text-island .ti-fontbtn span{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.text-island .ti-fontbtn svg{ flex:none; opacity:.5; }
.ti-size{ display:flex; align-items:center; border:1px solid transparent; background:var(--ctl-bg); border-radius:8px; height:29px; overflow:hidden; }
.ti-size button{ width:24px; height:27px; border:none; background:transparent; font-size:16px; line-height:1; cursor:pointer; color:var(--ink); }
.ti-size button:hover{ background:var(--hover); }
.ti-size .ti-szval{ width:32px; text-align:center; border:none; font-size:13px; background:transparent; color:var(--ink); -moz-appearance:textfield; }
.ti-size .ti-szval::-webkit-outer-spin-button,.ti-size .ti-szval::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
.ti-btn{ min-width:29px; height:29px; padding:0 6px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:1px; border:none; background:transparent; border-radius:9px; cursor:pointer; color:var(--ink); font-size:14px; }
.ti-btn:hover{ background:var(--hover); }
.ti-btn.on{ background:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
.ti-btn i{ font-family:Georgia,serif; }
.ti-A{ font-weight:800; line-height:1; }
.ti-cbar{ display:block; width:17px; height:3px; border-radius:2px; box-shadow:0 0 0 .5px var(--field-bd); }
.ti-text{ height:29px; padding:0 11px; border:none; background:transparent; border-radius:9px; font-weight:600; font-size:13px; cursor:pointer; color:var(--ink); }
.ti-text:hover{ background:var(--hover); }
.ti-sep{ width:1px; height:19px; background:var(--line); margin:0 3px; flex:none; }
/* tells you the sliders are acting on the highlighted characters, not the whole box */
.ti-pop-note{ display:inline-block; margin:0 0 10px; padding:3px 8px; border-radius:999px; background:rgba(61,149,255,.10); color:var(--accent,#3E6BE6); font-size:11px; font-weight:600; letter-spacing:.01em; }
.ti-pop-sub{ margin-left:auto; padding-left:10px; font-size:11px; font-weight:500; color:var(--muted,#8C8C8C); white-space:nowrap; }
.ti-pop-chk{ display:flex; align-items:center; gap:7px; margin-top:10px; font-size:12px; font-weight:500; color:var(--ink,#14161c); cursor:pointer; }
.ti-pop-chk input{ width:14px; height:14px; accent-color:var(--accent); }
.ti-btn svg{ width:15px; height:15px; }
.fi-btn svg{ width:14px; height:14px; }
.ti-pop{ position:absolute; background:var(--panel); color:var(--ink); border:1px solid var(--line); border-radius:13px; box-shadow:var(--float-shadow); padding:14px; min-width:220px; }
.ti-pop.hidden{ display:none; }
.ti-pop-h{ font-size:13px; font-weight:600; margin-bottom:8px; color:var(--muted); }
.ti-pop input[type=range]{ width:100%; margin-bottom:14px; }
/* A BUTTON THAT OPENS A MENU, and the menu it opens — 26 August 2026. Every other button in
   this bar stacks its contents in a column, which is why the arrow needs the row laid out again
   rather than inheriting; and the arrow has to beat the blanket 15px icon size above it, which
   two classes do without !important. */
.ti-menu{ flex-direction:row; gap:3px; padding:0 5px; }
.ti-btn .ti-caret{ width:9px; height:9px; flex:none; opacity:.55; }
/* A STRIP OF PICTURES, not a list to read. It was a column with a heading and a word beside
   each icon, which came out three times the height of the bar it hangs from; Mounir asked for
   the row in the example. The popover's own minimum width has to come off with it, or the
   strip sits in the middle of a box twice its size. */
.ti-pop.ti-pop-menu{ min-width:0; padding:6px; }
.ti-pop-opts{ display:flex; flex-direction:row; gap:3px; }
.ti-opt{ display:flex; align-items:center; justify-content:center; width:33px; height:33px; padding:0;
  border:none; border-radius:9px; background:transparent; color:var(--ink); font-size:13px;
  font-family:inherit; cursor:pointer; }
.ti-opt:hover{ background:var(--hover); }
.ti-opt.on{ background:var(--sel-bg); color:var(--sel-fg); }
.ti-opt svg{ width:17px; height:17px; flex:none; }
.ti-pop-row{ display:flex; gap:8px; align-items:center; }
.ti-pop-row input[type=color]{ width:36px; height:32px; border:none; border-radius:50%; background:none; padding:0; cursor:pointer; box-shadow:0 0 0 1px var(--field-bd); }
.ti-pop-row input[type=text]{ flex:1; height:32px; border:1px solid transparent; background:var(--ctl-bg); color:var(--ink); border-radius:8px; padding:0 9px; font-size:13px; }
/* File menu */
.file-wrap{ position:relative; }
.file-btn{ display:inline-flex; align-items:center; gap:5px; }
/* The starting position is only what it looks like for the instant before the script measures
   the File button and puts it underneath. It used to start beside the tool rail, where the
   button was until 11 September 2026. */
.file-menu{ position:fixed; left:12px; top:calc(var(--edbar-h) + 6px); z-index:130; min-width:212px; max-height:calc(100vh - var(--edbar-h) - 24px); overflow-y:auto; background:var(--panel,#fff); border:1px solid var(--line,#ececf2); border-radius:12px; box-shadow:var(--float-shadow); padding:6px; }
.file-menu.hidden{ display:none; }
.fm-item{ display:flex; align-items:center; width:100%; gap:10px; padding:8px 11px; background:transparent; border:none; border-radius:8px; font-size:13px; font-weight:500; color:var(--ink,#14161c); text-align:left; cursor:pointer; }
.fm-item:hover{ background:var(--hover); }
.fm-item.danger{ color:#e0483f; }
/* the admin-only items in the File menu, in blue so they read as a section of their own —
   asked for 26 September 2026: "change the color of this section to blue" */
.fm-item.fm-admin{ color:var(--accent); }
.fm-item .fm-ck{ margin-left:auto; opacity:0; color:var(--accent,#2b3fee); font-weight:700; }
.fm-item.on .fm-ck{ opacity:1; }
.fm-sep{ height:1px; background:var(--line,#ececf2); margin:6px 4px; }
.fm-email{ margin-left:auto; font-size:11px; color:var(--muted,#9aa0a6); font-weight:500; max-width:130px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Rulers */
.ruler{ position:absolute; z-index:58; display:block; background:#f6f7f9; }
.ruler.hidden{ display:none; }
.ruler-top{ border-bottom:1px solid var(--line); cursor:ns-resize; }
.ruler-left{ border-right:1px solid var(--line); cursor:ew-resize; }
.ruler-corner{ position:absolute; z-index:59; background:#f6f7f9; border-right:1px solid var(--line); border-bottom:1px solid var(--line); }
.ruler-corner.hidden{ display:none; }
/* live cursor position indicator on the rulers */
.rcur{ position:absolute; z-index:60; pointer-events:none; display:none; }
.rcur-t{ width:1px; background:var(--accent,#2b3fee); }
.rcur-l{ height:1px; background:var(--accent,#2b3fee); }
.rcur-badge{ position:absolute; font:10px/1.4 ui-monospace,SFMono-Regular,monospace; color:#fff; background:var(--accent,#2b3fee); border-radius:4px; padding:1px 4px; white-space:nowrap; }
.rcur-t .rcur-badge{ left:2px; top:1px; }
.rcur-l .rcur-badge{ left:1px; top:2px; }
/* Guides / margins / bleed */
.guide-layer{ position:absolute; z-index:57; pointer-events:none; overflow:visible; }
.ov-margin{ position:absolute; border:1px dashed rgba(91,91,245,.65); pointer-events:none; }
.ov-bleed{ position:absolute; border:1px dashed rgba(224,72,63,.65); pointer-events:none; }
/* crop marks: solid, thin and black, because that is what a printer cuts to — a dashed
   line would read as another guide rather than an instruction */
.ov-crop{ position:absolute; background:rgba(20,22,28,.8); pointer-events:none; }
/* bleed and crop marks live inside each page, so they follow it anywhere (drawPrintGuides) */
.print-guides{ position:absolute; left:0; top:0; right:0; bottom:0; z-index:57; pointer-events:none; overflow:visible; }
body.clean-view .print-guides,
body.printing-design .print-guides{ display:none !important; }
/* LAYOUT GUIDES DRAWN ON THE PAGE. Task 4 of the guides plan, 3 September 2026.
   They are never in the way: nothing here takes a click, so selecting artwork underneath a
   twelve-column grid works exactly as it does on a bare page.
   A LINE IS ONE PIXEL ON SCREEN AT EVERY ZOOM. --pinv is the inverse of the zoom, which the
   editor already sets on the scaled container this layer sits inside; without it a line would be
   four pixels thick at 400% and gone at 25%. */
/* A CLEAN VIEW: every overlay off at once. Nothing here changes what the editor DOES — the pen
   still draws and Undo still works — it only stops the chrome being drawn over the work. A
   preview that quietly disables half the editor is a mode people get stuck in. */
/* THE FLOATING TEXT BAR IS IN THIS LIST TOO. It hovers over the workspace and it belongs to the
   selection — leaving it up while the selection itself is hidden shows somebody a toolbar for
   something they cannot see. Found in a picture of this, not in the code. The tool rail and the
   properties panel are NOT in it: those are the editor rather than something drawn over the
   work, and hiding the whole editor is what full screen is for. */
body.clean-view #selOverlay,
body.clean-view #guideLayer,
body.clean-view .ruler,
body.clean-view .ruler-corner,
body.clean-view .page-bar,
body.clean-view .page-head,
body.clean-view .text-island,
body.clean-view .cv-frame{ display:none !important; }
/* AND ALWAYS A WAY OUT ON SCREEN, because hiding every overlay hides the things somebody would
   otherwise reach for. A mode you can only leave if you remember how you got in is a trap. */
/* ACROSS THE TOP, NOT THE BOTTOM. At the bottom it landed squarely on the floating island and
   covered the undo and zoom buttons — seen in a picture of it. */
.clean-bar{
  position:fixed; left:50%; transform:translateX(-50%); top:14px; z-index:190;
  display:flex; align-items:center; gap:10px;
  padding:7px 8px 7px 14px; border-radius:12px;
  background:rgba(20,20,24,.88); color:#fff; font-size:12.5px; font-weight:500;
  box-shadow:0 8px 26px rgba(0,0,0,.34);
}
.clean-out{
  height:26px; padding:0 11px; border:none; border-radius:8px;
  background:rgba(255,255,255,.16); color:#fff; font:inherit; font-size:12.5px; font-weight:600;
  cursor:pointer;
}
.clean-out:hover{ background:rgba(255,255,255,.26); }

.ov-gset{ position:absolute; pointer-events:none; }
.ov-gset.line.v{ width:calc(1px*var(--pinv,1)); }
.ov-gset.line.h{ height:calc(1px*var(--pinv,1)); }
/* A MARGIN IS AN OUTLINE, drawn inside its own rectangle so the line sits ON the margin rather
   than a hair outside it. Same one-pixel-on-screen rule as every other guide line. */
.ov-gset.box{
  box-sizing:border-box; background:transparent;
  border:calc(1px*var(--pinv,1)) solid currentColor;
}
.ov-gset.box.dashed{ border-style:dashed; }
/* A DOT SITS ON THE CROSSING, not beside it. Two and a bit screen pixels across, pulled back by
   half its own size so its middle is the point the sum gave rather than its top-left corner. */
.ov-gset.dot{
  width:calc(2.2px*var(--pinv,1)); height:calc(2.2px*var(--pinv,1));
  margin-left:calc(-1.1px*var(--pinv,1)); margin-top:calc(-1.1px*var(--pinv,1));
  border-radius:50%;
}
/* THE STRIP ALONG A MARGIN EDGE THAT CAN BE TAKEN HOLD OF.
   Eleven screen pixels wide at every zoom, centred on the line, because a one-pixel line is not
   something a hand can catch — the ruler guides learnt the same lesson and use nine.
   INVISIBLE UNTIL POINTED AT, then a soft band in the guide's own colour. A permanent thick bar
   would be a second margin line arguing with the first about where the margin is; nothing at all
   would be a control you can only find by accident. */
.ov-gedge{ position:absolute; pointer-events:auto; background:transparent; }
.ov-gedge.v{
  width:calc(11px*var(--pinv,1)); margin-left:calc(-5.5px*var(--pinv,1)); cursor:ew-resize;
}
.ov-gedge.h{
  height:calc(11px*var(--pinv,1)); margin-top:calc(-5.5px*var(--pinv,1)); cursor:ns-resize;
}
.ov-gedge::before{
  content:""; position:absolute; background:currentColor; border-radius:calc(2px*var(--pinv,1));
  opacity:0; transition:opacity .12s ease;
}
.ov-gedge.v::before{
  top:0; bottom:0; left:50%;
  width:calc(3px*var(--pinv,1)); margin-left:calc(-1.5px*var(--pinv,1));
}
.ov-gedge.h::before{
  left:0; right:0; top:50%;
  height:calc(3px*var(--pinv,1)); margin-top:calc(-1.5px*var(--pinv,1));
}
.ov-gedge:hover::before{ opacity:.5; }
/* A SELECTED GUIDE STAYS LIT once it has been pressed, so it is obvious which one Delete is
   about to take. Brighter than the hover, because hovering is "you could" and selected is
   "this one is". */
.ov-gedge.on::before{ opacity:.85; }
/* WHILE ONE IS BEING PULLED THE CURSOR IS THE SAME EVERYWHERE. The strip is redrawn under the
   pointer as the edge moves, and on a fast drag the pointer is briefly over bare canvas, where
   an arrow reappearing for a frame reads as "you have let go of it". */
body.gedge-x, body.gedge-x *{ cursor:ew-resize !important; }
body.gedge-y, body.gedge-y *{ cursor:ns-resize !important; }
body.gedge-x .ov-gedge.v::before, body.gedge-y .ov-gedge.h::before{ opacity:.5; }

.ov-guide{ position:absolute; pointer-events:auto; }
/* THE SELECTED RULER GUIDE, three screen pixels rather than one and in the editor's blue, so
   what Delete is aimed at can be seen from across the room. In screen pixels at every zoom, like
   the line it replaces. */
.ov-guide.on{ background:var(--accent,#3E6BE6); }
.ov-guide.gv.on{ width:calc(3px*var(--pinv,1)); margin-left:calc(-1px*var(--pinv,1)); }
.ov-guide.gh.on{ height:calc(3px*var(--pinv,1)); margin-top:calc(-1px*var(--pinv,1)); }
.ov-guide.gv{ top:0; height:100%; width:1px; background:#12b2a6; cursor:ew-resize; }
.ov-guide.gh{ left:0; width:100%; height:1px; background:#12b2a6; cursor:ns-resize; }
.ov-guide.gv::before{ content:""; position:absolute; left:-4px; top:0; width:9px; height:100%; }
.ov-guide.gh::before{ content:""; position:absolute; top:-4px; left:0; height:9px; width:100%; }
.imgslot{ display:flex; align-items:center; justify-content:center; background:#eef1f6; border:1.5px solid var(--line); color:#9aa6b8; font-size:12px; }
.handle{ position:absolute; width:calc(10px*var(--inv,1)); height:calc(10px*var(--inv,1)); background:#fff; border:calc(1.5px*var(--inv,1)) solid var(--accent); border-radius:calc(2px*var(--inv,1)); z-index:50; box-shadow:0 1px 3px rgba(0,0,0,.35); pointer-events:auto; }
.handle.tl{left:calc(-6px*var(--inv,1));top:calc(-6px*var(--inv,1));cursor:nwse-resize} .handle.tr{right:calc(-6px*var(--inv,1));top:calc(-6px*var(--inv,1));cursor:nesw-resize}
.handle.bl{left:calc(-6px*var(--inv,1));bottom:calc(-6px*var(--inv,1));cursor:nesw-resize} .handle.br{right:calc(-6px*var(--inv,1));bottom:calc(-6px*var(--inv,1));cursor:nwse-resize}
.handle.t{left:calc(50% - 5px*var(--inv,1));top:calc(-6px*var(--inv,1));cursor:ns-resize} .handle.b{left:calc(50% - 5px*var(--inv,1));bottom:calc(-6px*var(--inv,1));cursor:ns-resize}
.handle.l{top:calc(50% - 5px*var(--inv,1));left:calc(-6px*var(--inv,1));cursor:ew-resize} .handle.r{top:calc(50% - 5px*var(--inv,1));right:calc(-6px*var(--inv,1));cursor:ew-resize}
.handle.rot{ left:calc(50% - 12px*var(--inv,1)); top:calc(-34px*var(--inv,1)); width:calc(24px*var(--inv,1)); height:calc(24px*var(--inv,1)); border-radius:50%;
  background:var(--accent) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g stroke="white" stroke-width="2.6" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12a8 8 0 1 0 8-8 8.6 8.6 0 0 0-6 2.5L4 8.5"/><path d="M4 4v4.5h4.5"/></g></svg>') center/60% no-repeat;
  border:calc(1.5px*var(--inv,1)) solid #fff; box-shadow:0 2px 8px rgba(0,0,0,.4);
  cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><g stroke="white" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.7 9.7 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></g><g stroke="black" stroke-width="2.2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.7 9.7 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></g></svg>') 11 11, grab; }
/* ILLUSTRATOR'S RULE: the box turns with the object, the chrome you grab does not.
   A square handle at 45 degrees is a diamond, and the rotate icon upside-down reads as a
   different button. Each one turns back by exactly what the box turned by, about its own
   centre — so it stays where it belongs on the edge and simply does not spin.
   Scoped to .sel-box: the distort handles are appended to the overlay instead, and they carry
   a translate of their own that this would overwrite. */
.sel-box .handle,
.sel-box .corner-widget{ transform:rotate(calc(var(--rot,0) * -1deg)); }
/* the hover grow has to restate the turn, or hovering snaps the handle back to the box angle */
.sel-box .corner-widget:hover{ transform:rotate(calc(var(--rot,0) * -1deg)) scale(1.15); }
/* The stalk from the box's edge up to the rotate button. On the BOX, so it turns with the
   edge it grows out of; on the button it would turn back with the button and point away. */
.rot-stem{ position:absolute; left:calc(50% - 0.75px*var(--inv,1)); top:calc(-11px*var(--inv,1)); width:calc(1.5px*var(--inv,1)); height:calc(12px*var(--inv,1)); background:var(--accent); pointer-events:none; }

.panel{ width:300px; background:var(--panel); border:none; border-left:1px solid var(--line); border-radius:0; box-shadow:none; display:flex; flex-direction:column; align-self:stretch; overflow:hidden; }
.panel-section{ flex:1; overflow:auto; padding:16px; }
.ptabs{ display:flex; border-bottom:1px solid var(--line); flex:none; }
.ptab{ flex:1; padding:12px 8px; background:transparent; border:none; font-size:11px; font-weight:700; color:var(--muted); border-bottom:2px solid transparent; letter-spacing:.12em; text-transform:uppercase; }
.ptab:hover{ color:var(--ink); }
.ptab.active{ color:var(--ink); border-bottom-color:var(--accent); }
.ppane{ display:none; flex:1; min-height:0; }
.ppane.active{ display:flex; flex-direction:column; }
.panel-inner{ flex:1; overflow:auto; padding:12px var(--pp-x); }
/* Floating, draggable panels (Photoshop / Illustrator style) */
.fpanel{ position:fixed; width:208px; max-height:calc(100vh - 150px); background:var(--panel); border:1px solid var(--line); border-radius:12px; box-shadow:var(--float-shadow); display:flex; flex-direction:column; overflow:hidden; z-index:72; }
.fpanel-head{ flex:none; display:flex; align-items:center; gap:6px; padding:10px var(--pp-x); cursor:move; font-size:11px; font-weight:700; letter-spacing:0; text-transform:none; color:var(--ink); border-bottom:1px solid var(--line); user-select:none; touch-action:none; }
.fpanel-head svg{ opacity:.6; }
.fpanel-head:hover{ color:var(--ink); }
.fpanel-close{ margin-left:auto; width:22px; height:22px; display:grid; place-items:center; background:transparent; border:none; border-radius:6px; color:var(--muted); cursor:pointer; }
.fpanel-close:hover{ background:var(--hover); color:var(--ink); }
.fpanel-close svg{ opacity:1; transition:transform .16s ease; }
.fpanel.collapsed{ height:auto !important; max-height:none !important; }
.fpanel.collapsed .ppane{ display:none; }
.fpanel.collapsed .fpanel-head{ border-bottom:none; }
.fpanel.collapsed .fpanel-close svg{ transform:rotate(180deg); }
.fpanel-resize{ flex:none; height:11px; cursor:ns-resize; display:flex; align-items:center; justify-content:center; border-top:1px solid var(--line); }
.fpanel-resize::after{ content:""; width:34px; height:3px; border-radius:2px; background:var(--line); }
.fpanel-resize:hover::after{ background:var(--muted); }
.fpanel-resize.top{ position:absolute; top:0; left:0; right:0; height:8px; border-top:none; border-radius:12px 12px 0 0; }
.fpanel-resize.top::after{ display:none; }
.fpanel.collapsed .fpanel-resize{ display:none; }
/* ---- Kittl-style docking: Properties fixed on right, Layers slide-out on left ---- */
/* 244px + 30%. The properties panel holds two-column rows (Width/Height, colour + hex)
   that were running out of room; the canvas gives up the space instead. */
.fpanel.pdock{ position:relative; flex:0 0 317px; width:317px; max-height:none; align-self:stretch; border:none; border-left:1px solid var(--line); border-radius:0; box-shadow:none; z-index:auto; }
.fpanel.pdock .fpanel-head{ cursor:default; }
.fpanel.pslide{ position:absolute; left:calc(var(--rail-w) - 5px); top:0; bottom:0; width:250px; max-height:none; border:none; border-right:1px solid var(--line); border-radius:0; box-shadow:14px 0 34px -20px rgba(0,0,0,.5); z-index:80; }
.fpanel.pslide .fpanel-head{ cursor:default; }
.fpanel.pslide.hidden{ display:none; }
#layersToggle.on{ background:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
/* Pages panel (right) */
.pages-panel{ flex:none; width:78px; background:var(--panel); border-left:1px solid var(--line); display:flex; flex-direction:column; min-height:0; }
.pages-panel.collapsed{ display:none; }
.pp-head{ flex:none; display:flex; align-items:center; justify-content:space-between; padding:10px 10px; font-size:12px; font-weight:700; letter-spacing:0; text-transform:none; color:var(--ink); border-bottom:1px solid var(--line); }
.pp-collapse{ background:transparent; border:none; color:var(--muted); cursor:pointer; display:grid; place-items:center; width:22px; height:22px; border-radius:6px; }
.pp-collapse:hover{ background:var(--hover); color:var(--ink); }
.pp-list{ flex:1; overflow:auto; padding:12px 10px; display:flex; flex-direction:column; gap:12px; }
.pp-list.grid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
.pp-item{ cursor:pointer; }
.pp-thumbwrap{ border:2px solid var(--line); border-radius:8px; overflow:hidden; background:#fff; box-shadow:0 2px 10px rgba(0,0,0,.12); transition:border-color .12s; }
.pp-item:hover .pp-thumbwrap{ border-color:var(--muted); }
.pp-item.active .pp-thumbwrap{ border-color:var(--accent); box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 30%,transparent),0 2px 10px rgba(0,0,0,.14); }
.pp-thumb{ position:relative; width:100%; }
.pp-num{ display:block; text-align:center; font-size:11px; font-weight:600; color:var(--muted); margin-top:4px; }
.pp-item.active .pp-num{ color:var(--accent); }
.pp-add{ flex:none; margin:0 10px 10px; padding:9px; border:1px dashed var(--line); border-radius:9px; background:transparent; color:var(--ink); font-size:12px; font-weight:600; cursor:pointer; display:flex; align-items:center; justify-content:center; gap:6px; }
.pp-add:hover{ background:var(--hover); border-color:var(--accent); }
.pp-foot{ flex:none; display:flex; gap:4px; justify-content:center; padding:8px; border-top:1px solid var(--line); }
.pp-foot button{ width:30px; height:26px; display:grid; place-items:center; background:transparent; border:none; border-radius:7px; color:var(--muted); cursor:pointer; }
.pp-foot button:hover{ background:var(--hover); color:var(--ink); }
.pp-foot button.on{ background:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
.pp-reopen{ position:fixed; right:0; top:50%; transform:translateY(-50%); z-index:100; width:24px; height:44px; border:1px solid var(--line); border-right:none; background:var(--panel); border-radius:8px 0 0 8px; display:none; place-items:center; cursor:pointer; color:var(--muted); box-shadow:-4px 0 12px -6px rgba(0,0,0,.3); }
.pp-reopen.show{ display:grid; }
.pp-reopen:hover{ color:var(--ink); }
.page-ctx{ position:fixed; }
/* Profile page */
.profile{ min-height:100vh; background:var(--bg); color:var(--ink,#14161c); }
.profile.hidden{ display:none; }
.pr-wrap{ max-width:1080px; margin:0 auto; display:flex; gap:32px; padding:32px 26px 80px; }
.pr-side{ flex:none; width:220px; }
.pr-side-h{ font-size:11px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--muted,#8a9099); padding:0 10px 10px; }
.pr-tab{ display:flex; align-items:center; gap:10px; width:100%; text-align:left; padding:10px 12px; background:transparent; border:none; border-radius:9px; font-size:14px; font-weight:500; color:var(--ink,#14161c); cursor:pointer; }
.pr-tab:hover{ background:var(--hover,#f1f1f6); }
/* THE CHOSEN TAB IS BLACK WITH WHITE ON IT, asked for 26 August 2026 — the same treatment
   the editor's selected buttons already had. It was a pale wash of the accent with accent-
   coloured text, which is a hint rather than a statement: on a page of pale grey panels the
   chosen tab has to be the one thing you cannot mistake. The icon rides on currentColor, so
   it turns white with the word and needs no rule of its own. */
.pr-tab.active{ background:#171717; color:#FFFFFF; }
.pr-main{ flex:1; min-width:0; max-width:560px; }
/* ---- the admin dashboard shell: sidebar + one pane per area ----
   Reuses the profile page's .pr-side / .pr-tab so the two look like the same product.
   It needs its own wrapper because .pr-main caps the width at 560px, which is fine for a
   settings form and far too narrow for a table of users. */
/* EDGE TO EDGE, asked for 26 August 2026. It was capped at 1400px and centred, which on a
   wide screen left the user table squeezed into the middle with empty grey either side while
   the email column was still cutting names off. An admin table wants every pixel it can get;
   there is no line length to protect here, only columns. */
.ad-wrap{ max-width:none; margin:0; display:flex; gap:32px; padding:32px 26px 80px; align-items:flex-start; }
/* BELOW THE BAR, NOT UNDER IT — 27 August 2026. Both this menu and the bar above it stick to
   the window, and this one stuck at 24 pixels from the very top, which is inside the bar. On any
   scroll the word ADMIN and the top of the first tab disappeared behind it. --pjbarh is the
   bar's real measured height and --headh is nought on this page, because the site header is not
   on it. */
/* THE PAGE IS WHITE AND THE MENU KEEPS THE GREY — asked for 28 August 2026.
   The admin area used to be one flat grey sheet with the menu and the panes drawn straight onto
   it. The grey now belongs to the menu alone, so the two read as what they are: a list of places
   on the left, and the thing you are looking at on the right.
   NAMED AS PANEL AND PAGE RATHER THAN AS WHITE AND GREY. --panel is what a card is made of and
   --bg is what a page is made of, and in the dark theme they swap round the same way — so this
   keeps its meaning rather than becoming a white rectangle in a dark room. */
.ad-side{ position:sticky; top:calc(var(--headh, 0px) + var(--pjbarh, 0px) + 24px);
  background:var(--bg,#F4F4F4); border-radius:14px; padding:16px 10px; }
.ad-main{ flex:1; min-width:0; }
.ad-pane{ display:none; }
.ad-pane.active{ display:block; }
.ad-empty{ border:1px dashed var(--line,#ececf2); border-radius:14px; padding:34px 30px; max-width:64ch; }
.ad-empty-t{ font-size:15px; font-weight:700; color:var(--ink,#14161c); margin-bottom:10px; }
.ad-empty p{ font-size:13.5px; line-height:1.65; color:var(--muted,#5f6570); margin:0 0 12px; }
.ad-empty-note{ font-size:12.5px !important; color:var(--muted,#8C8C8C); border-left:2px solid var(--line,#ececf2); padding-left:12px; margin-bottom:0 !important; }
@media (max-width:860px){
  .ad-wrap{ flex-direction:column; gap:18px; }
  .ad-side{ position:static; width:100%; display:flex; gap:6px; flex-wrap:wrap; }
  .ad-side .pr-side-h{ width:100%; padding-bottom:4px; }
  .ad-side .pr-tab{ width:auto; }
}

/* ---- the upload record ----
   Green allowed, red blocked. Amber is the checker itself failing — the file was held
   back but nothing was decided about it, which is neither of the other two.
   Colour is never the only signal: every row also says the word. */
.ct-stats{ display:flex; gap:18px; margin:0 0 16px; font-size:13px; color:var(--muted,#8C8C8C); }
.ct-bar{ display:flex; gap:12px; align-items:center; margin-bottom:16px; flex-wrap:wrap; }
.ct-search{ flex:1; min-width:240px; margin:0; }
.ct-filters{ display:flex; gap:6px; flex-wrap:wrap; }
.ct-f{ height:36px; padding:0 14px; border:1px solid var(--line,#ececf2); border-radius:9px; background:transparent; font-size:13px; font-weight:600; color:var(--muted,#5f6570); cursor:pointer; }
.ct-f:hover{ background:var(--hover,#f4f4f8); color:var(--ink,#14161c); }
.ct-f.active{ background:var(--accent-soft,rgba(62,107,230,.12)); border-color:transparent; color:var(--accent,#3E6BE6); }
/* "Sort by: A–Z ⌄" — the select is transparent and sits on top of the whole control, so
   the label, value and chevron all behave as one clickable target. appearance:none removes
   the platform's own arrow; ours replaces it and is marked aria-hidden so it is not read
   out twice. */
/* Spacing rhythm on this page: the search sits apart from the sort, and the sort sits
   closer to the table it controls than to the search above it. Grouping a control with
   what it affects is what makes the relationship readable without a line or a box.
   The negative left inset lets the words line up with the search box's edge, since the
   control's own padding would otherwise push them in. */
.us-sortbox{ display:inline-flex; align-items:center; gap:6px; position:relative; margin:18px 0 10px -8px; padding:6px 10px; border-radius:8px; cursor:pointer; }
.us-sortbox:hover{ background:var(--hover,#f4f4f8); }
.us-sortbox:focus-within{ box-shadow:0 0 0 2px var(--accent,#3E6BE6); }
.us-sortlabel{ font-size:13px; color:var(--muted,#5f6570); }
.us-sortbox select{
  appearance:none; -webkit-appearance:none;
  background:transparent; border:none; outline:none; cursor:pointer;
  padding:0 20px 0 0; margin:0;
  font-family:inherit; font-size:13px; font-weight:600; color:var(--accent,#3E6BE6);
}
.us-sortchev{ position:absolute; right:8px; color:var(--accent,#3E6BE6); pointer-events:none; }
/* sits on the same line as the sort, but reads as a separate control */
.us-only{ height:30px; padding:0 12px; font-size:12.5px; margin-left:4px; vertical-align:middle; }
/* On, this filter is showing admins, so it is the admin colour — the same orange as the
   Admin badge it is filtering for. The shared .ct-f.active blue is right for the content
   filters, which are about kinds of file, and says nothing here. */
.us-only.active{ background:var(--role-admin-tint); border-color:transparent; color:var(--role-admin); }
.us-only.active:hover{ background:var(--role-admin-tint-h); color:var(--role-admin); }

/* pagination — hidden entirely until there is a second page */
.pager{ display:flex; align-items:center; justify-content:center; gap:14px; margin:16px 0 4px; }
.pager-n{ font-size:13px; color:var(--muted,#5f6570); font-variant-numeric:tabular-nums; min-width:9ch; text-align:center; }
.pager-b{ width:32px; height:32px; display:grid; place-items:center; border:1px solid var(--line,#ececf2); border-radius:8px; background:transparent; color:var(--ink,#14161c); cursor:pointer; }
.pager-b:hover:not(:disabled){ background:var(--hover,#f4f4f8); }
.pager-b:disabled{ opacity:.35; cursor:default; }
.ct-table{ border:1px solid var(--line,#ececf2); border-radius:12px; overflow:hidden; }
.ct-row{ display:grid; grid-template-columns:2fr 1.6fr 1fr 1.6fr .6fr .9fr; gap:14px; align-items:center; padding:11px 15px; border-bottom:1px solid var(--line,#ececf2); font-size:13.5px; }
.ct-row.ct-mini{ grid-template-columns:2fr 1fr 1.6fr .6fr .9fr; }
.ct-row:last-child{ border-bottom:none; }
/* ---- the Errors list -------------------------------------------------------------------
   Its own column widths: the message needs the room, and "people" and "times" are two
   numbers that want to sit next to each other so you can compare them at a glance. */
.er-row{ grid-template-columns:2.6fr 1.2fr .5fr .5fr .9fr 1fr; cursor:pointer; }
.er-row:hover{ background:var(--hover,#f7f7fa); }
.er-row.ct-head{ cursor:default; }
.er-row.ct-head:hover{ background:var(--field,#f6f6f9); }
.er-row.open{ background:var(--hover,#f7f7fa); border-bottom:none; }
.er-detail{ padding:4px 15px 18px; border-bottom:1px solid var(--line,#ececf2); background:var(--hover,#f7f7fa); }
.er-d-msg{ font-size:13.5px; font-weight:600; color:var(--ink,#14161c); margin:0 0 10px; }
.er-d-l{ font-size:12.5px; color:var(--muted,#5f6570); margin:0 0 5px; }
.er-d-l b{ color:var(--ink,#14161c); font-weight:600; margin-right:6px; }
.er-d-pre{ font-family:'JetBrains Mono',ui-monospace,monospace; font-size:11.5px; line-height:1.6;
  color:var(--muted,#5f6570); background:var(--panel,#fff); border:1px solid var(--line,#ececf2);
  border-radius:8px; padding:10px 12px; margin:4px 0 10px; overflow:auto; max-height:220px; white-space:pre-wrap; }
.er-clear{ margin-top:4px; }
/* THE COUNT BESIDE A TAB: how many things are waiting there.
   THE BRAND RED, not the danger red — asked for 28 August 2026, and right for a reason. The
   danger red is the deep brick used by the delete button and the warnings that go with it; it is
   deliberately heavy, because it is the colour of "this cannot be undone". A number saying two
   reports are waiting is not a warning, it is an invitation, and at a glance the two should not
   read as the same thing. #F40628 is Templette's own red and is the brighter of the two. */
.ad-tabcount{ margin-left:auto; min-width:20px; height:18px; padding:0 6px; border-radius:9px;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--brand,#F40628); color:#fff; font-size:11px; font-weight:700;
  letter-spacing:.01em; }
.er-stats{ display:flex; gap:18px; margin:0 0 16px; }
.ct-row[data-uid]{ cursor:pointer; }
.ct-row[data-uid]:hover{ background:var(--hover,#f7f7fa); }
.ct-head{ background:var(--field,#f6f6f9); font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--muted,#8C8C8C); }
.ct-file{ display:flex; flex-direction:column; gap:2px; min-width:0; font-weight:600; color:var(--ink,#14161c); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ct-file em{ font-style:normal; font-size:11.5px; font-weight:500; color:var(--muted,#8C8C8C); }
.ct-em{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--ink,#14161c); }
.ct-score{ font-variant-numeric:tabular-nums; font-weight:700; }
.ct-badge{ display:inline-block; padding:3px 10px; border-radius:999px; font-size:11.5px; font-weight:700; letter-spacing:.02em; white-space:nowrap; }
.ct-ok.ct-badge{ background:rgba(46,140,74,.13); color:#1E7A3C; }
.ct-no.ct-badge{ background:rgba(211,47,47,.13); color:#C62828; }
.ct-err.ct-badge{ background:rgba(198,124,15,.14); color:#9A6206; }
.ct-score.ct-ok{ color:#1E7A3C; }
.ct-score.ct-no{ color:#C62828; }
.ct-score.ct-err{ color:#9A6206; }
.ud-sec-h{ font-size:12px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--muted,#8C8C8C); margin:6px 0 10px; }
.ud-uploads{ margin-bottom:30px; }
@media (max-width:900px){
  .ct-row{ grid-template-columns:2fr 1fr .9fr; }
  .ct-row>span:nth-child(3),.ct-row>span:nth-child(4){ display:none; }
  .ct-row.ct-mini{ grid-template-columns:2fr 1fr .9fr; }
}

/* ---- one user's page in the admin area ---- */
.us-row[data-uid]{ cursor:pointer; }
.us-back{ display:inline-flex; align-items:center; gap:6px; margin-bottom:18px; padding:7px 12px 7px 8px; background:transparent; border:1px solid var(--line,#ececf2); border-radius:9px; font-size:13px; font-weight:600; color:var(--muted,#5f6570); cursor:pointer; }
.us-back:hover{ background:var(--hover,#f4f4f8); color:var(--ink,#14161c); }
.ud-head{ display:flex; align-items:center; gap:16px; margin-bottom:26px; }
.ud-avatar{ width:56px; height:56px; flex:none; border-radius:50%; display:grid; place-items:center; background:var(--accent,#3E6BE6); color:#fff; font-size:19px; font-weight:700; }
.ud-id{ min-width:0; }
.ud-name{ font-size:23px; font-weight:800; margin:0 0 3px; }
.ud-email{ font-size:14px; color:var(--muted,#8C8C8C); word-break:break-all; }
.ud-tags{ margin-left:auto; display:flex; gap:7px; flex-wrap:wrap; }
.ud-tag{ padding:4px 11px; border-radius:999px; background:var(--field,#f4f4f8); color:var(--muted,#5f6570); font-size:11.5px; font-weight:700; letter-spacing:.03em; text-transform:uppercase; }
.ud-tag-admin{ background:var(--admin-soft); color:var(--admin); }
.ud-tag-susp{ background:rgba(224,72,63,.14); color:#C0392B; }
.ud-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:1px; background:var(--line,#ececf2); border:1px solid var(--line,#ececf2); border-radius:12px; overflow:hidden; margin-bottom:30px; }
.ud-cell{ background:var(--panel,#fff); padding:13px 15px; }
.ud-k{ font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--muted,#8C8C8C); margin-bottom:5px; }
.ud-v{ font-size:14px; color:var(--ink,#14161c); word-break:break-all; }
/* actions kept in their own boxed area so they read as deliberate, not incidental */
.ud-danger{ border:1px solid var(--line,#ececf2); border-radius:12px; overflow:hidden; }
.ud-danger-h{ padding:11px 16px; background:var(--field,#f6f6f9); font-size:12px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--muted,#8C8C8C); border-bottom:1px solid var(--line,#ececf2); }
.ud-danger-row{ display:flex; align-items:center; gap:18px; padding:15px 16px; }
.ud-danger-row+.ud-danger-row{ border-top:1px solid var(--line,#ececf2); }
.ud-act-t{ font-size:14px; font-weight:600; color:var(--ink,#14161c); margin-bottom:3px; }
.ud-act-d{ font-size:12.5px; color:var(--muted,#8C8C8C); max-width:62ch; line-height:1.45; }
.ud-danger-row button{ margin-left:auto; flex:none; height:36px; padding:0 16px; border-radius:9px; font-size:13px; font-weight:600; cursor:pointer; }
.ud-danger-row button:disabled{ opacity:.4; cursor:not-allowed; }
.ud-del{ background:#D93B30; border:1px solid #D93B30; color:#fff; }
.ud-del:hover:not(:disabled){ background:#C03228; }
@media (max-width:640px){
  .ud-tags{ margin-left:0; width:100%; }
  .ud-head{ flex-wrap:wrap; }
  .ud-danger-row{ flex-wrap:wrap; }
  .ud-danger-row button{ margin-left:0; width:100%; }
}

.pr-head{ display:flex; align-items:center; gap:11px; margin-bottom:4px; }
.pr-head .pr-title{ margin:0; }
.pr-badge{ padding:3px 10px; border-radius:999px; background:var(--accent-soft,rgba(62,107,230,.12)); color:var(--accent,#3E6BE6); font-size:11.5px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; }
/* First and last name side by side, with the Save button on the same row. They stack on
   a narrow window so neither box gets too small to type in. */
.pr-names{ display:flex; gap:10px; align-items:flex-start; }
.pr-name-col{ flex:1; min-width:0; display:flex; flex-direction:column; gap:5px; }
.pr-names input{ width:100%; height:42px; border:1px solid var(--field-bd,#e2e2ea); border-radius:10px; padding:0 13px; font-size:14.5px; color:var(--ink,#14161c); background:#fff; outline:none; }
.pr-names input:focus{ border-color:var(--accent,#2b3fee); }
.pr-sub{ font-size:11.5px; color:var(--muted,#8C8C8C); padding-left:2px; }
@media (max-width:620px){
  .pr-names{ flex-wrap:wrap; }
  .pr-name-col{ flex:1 1 100%; }
  .pr-names .pr-save{ flex:1 1 100%; }
}
.pr-title{ font-size:24px; font-weight:800; margin:0 0 4px; }
.pr-lead{ font-size:14px; color:var(--muted,#6e7280); margin:0 0 24px; }
.pr-field{ margin-bottom:22px; }
.pr-field label{ display:block; font-size:13px; font-weight:600; color:var(--ink,#14161c); margin-bottom:7px; }
.pr-inline{ display:flex; gap:10px; }
.pr-inline input{ flex:1; height:42px; border:1px solid var(--field-bd,#e2e2ea); border-radius:10px; padding:0 13px; font-size:14.5px; color:var(--ink,#14161c); outline:none; }
.pr-inline input:focus{ border-color:var(--accent,#2b3fee); }
.pr-save{ flex:none; height:42px; padding:0 18px; border:none; border-radius:10px; background:var(--accent,#2b3fee); color:#fff; font-size:14px; font-weight:700; cursor:pointer; }
.pr-save:hover{ background:#2233d8; }
.pr-save:disabled{ opacity:.6; cursor:default; }
.pr-static{ height:42px; display:flex; align-items:center; padding:0 13px; border:1px solid var(--line,#ececf2); border-radius:10px; background:var(--field,#f6f6f9); color:var(--muted,#5f6570); font-size:14.5px; }
.pr-hint{ font-size:12px; color:var(--muted,#9aa0a6); margin-top:6px; }
/* THE PLAN ROW — 12 September 2026, step 12. The word and its button side by side, the same
   shape as the name row above so the page reads as one form. Pro is written in the gold the
   crown wears everywhere else, on the same pale gold the Pro wall's badge sits on, so a person
   who has just paid sees the colour they bought. button.pr-plan-btn, for the reason the delete
   buttons give: a bare class does not outrank the blanket button rule. */
.pr-plan-row{ display:flex; gap:10px; align-items:stretch; }
.pr-plan-row .pr-static{ flex:1; font-weight:700; color:var(--ink,#14161c); }
.pr-plan-row .pr-static.is-pro{ color:#7a5a12; background:color-mix(in srgb,var(--gold,#C79A3B) 14%,#fff); border-color:color-mix(in srgb,var(--gold,#C79A3B) 35%,#fff); }
button.pr-plan-btn{ flex:none; height:42px; padding:0 18px; border:none; border-radius:10px; background:#0D0D0C; color:#fff; font-size:14px; font-weight:700; cursor:pointer; }
button.pr-plan-btn:hover{ background:#262625; }
.pr-pane{ display:none; }
.pr-pane.active{ display:block; }
/* THE PASSWORD TAB — 12 September 2026, to a picture Mounir sent: square-cornered boxes with
   the eye on the right, matching the name boxes above rather than the pill boxes of the reset
   page whose pieces it borrows. Save Settings is the same blue Save as the name row and sits
   greyed out until the boxes are right. */
.pr-cp-field{ margin-bottom:0; }
.pr-cp-field .rp-input{ height:42px; border-radius:10px; border-color:var(--field-bd,#e2e2ea); padding-left:13px; font-size:14.5px; }
.pr-cp-field .rp-input:focus{ border-color:var(--accent,#2b3fee); box-shadow:none; }
#prPanePassword .rp-meter{ margin-top:8px; }
#prPanePassword .rp-hint{ margin-bottom:0; }
#prPanePassword .rp-hint.bad{ color:#c0392b; }
#prPanePassword .rp-msg{ margin-top:12px; font-size:13px; }
#prPanePassword .rp-msg.err{ color:#c0392b; }
#prPanePassword .rp-msg.ok{ color:#1E7A3C; }
.pr-cp-none{ margin:0 0 8px; font-size:14px; color:var(--muted,#5f6570); }
.pr-cp-ts{ margin:0 0 14px; min-height:0; }
.pr-cp-ts:empty{ margin:0; }
/* ===== Deleting the account =====================================================
 * Set apart from the fields above by a rule and a lot of space, because the one thing
 * this must never be is next to something somebody meant to press.
 *
 * Every button here is written as button.something, not .something. A blanket rule near
 * the top of this file styles every button on the site, and a bare class does not outrank
 * it — that is exactly how the share button came out invisible on 23 August 2026.
 *
 * The red is written out rather than taken from a variable. The profile page does not
 * define the editor's colour variables, and a colour that resolves to nothing throws the
 * whole declaration away, leaving a button you cannot see.
 */
.pr-danger{ margin-top:54px; padding-top:26px; border-top:1px solid var(--line,#ececf2); }
.pr-danger-h{ margin:0 0 6px; font-size:15px; font-weight:700; color:#c0392b; }
.pr-danger-p{ margin:0 0 14px; max-width:620px; font-size:13.5px; line-height:1.55; color:var(--muted,#5f6570); }
button.pr-del{ height:40px; padding:0 16px; border:1px solid #e3b4ad; border-radius:10px;
  background:#fff; color:#c0392b; font-size:14px; font-weight:700; cursor:pointer; }
button.pr-del:hover{ background:#fdf3f1; }
/* The confirmation is a panel, not a line of text: it has to look like a stop. */
.pr-confirm{ margin-top:16px; max-width:620px; padding:16px 18px; border:1px solid #e3b4ad;
  border-radius:12px; background:#fdf3f1; }
.pr-confirm.hidden{ display:none; }
.pr-confirm-p{ margin:0 0 8px; font-size:14px; color:#7d2b20; }
.pr-confirm-l{ margin:0 0 14px; padding-left:20px; font-size:13.5px; line-height:1.7; color:#7d2b20; }
.pr-confirm-row{ display:flex; gap:10px; flex-wrap:wrap; }
/* Keeping the account is the plain one and it comes first, so the pointer lands on the
   safe button when somebody presses without reading. */
button.pr-del-no{ height:40px; padding:0 16px; border:1px solid var(--line,#ececf2); border-radius:10px;
  background:#fff; color:var(--ink,#14161c); font-size:14px; font-weight:600; cursor:pointer; }
button.pr-del-no:hover{ background:var(--field,#f6f6f9); }
button.pr-del-yes{ height:40px; padding:0 16px; border:none; border-radius:10px;
  background:#c0392b; color:#fff; font-size:14px; font-weight:700; cursor:pointer; }
button.pr-del-yes:hover{ background:#a93226; }
button.pr-del:disabled,button.pr-del-yes:disabled,button.pr-del-no:disabled{ opacity:.6; cursor:default; }
.pr-del-err{ margin:12px 0 0; font-size:13px; line-height:1.5; color:#7d2b20; word-break:break-word; }
/* ===== Privacy Policy and Terms of Service =====================================
 * Borrowed in shape from Figma, Canva and Kittl: the sections listed down the left so nobody
 * scrolls hunting for the one that matters, a plain-English summary at the top, and the date
 * it last changed where a careful reader looks first.
 *
 * MEASURED FOR READING, not for filling the window. The column stops at 720px because a line
 * of text longer than about ninety characters is measurably harder to follow, and this is the
 * one page on the site somebody has to read carefully rather than skim.
 */
.legal{ min-height:100vh; background:var(--bg,#fff); color:var(--ink,#14161c); }
.legal.hidden{ display:none; }
.lg-wrap{ max-width:1200px; margin:0 auto; padding:26px var(--edge,40px) 90px; display:flex; gap:44px; align-items:flex-start; }
.lg-side{ flex:0 0 232px; position:sticky; top:78px; max-height:calc(100vh - 100px); overflow:auto; }
.lg-side-h{ font-size:11px; font-weight:700; letter-spacing:.09em; text-transform:uppercase; color:var(--muted,#9aa0a6); margin-bottom:12px; }
button.lg-doc{ display:block; width:100%; text-align:left; padding:9px 12px; margin-bottom:4px;
  border:none; border-radius:9px; background:transparent; color:var(--ink,#14161c);
  font-size:14px; font-weight:600; cursor:pointer; }
button.lg-doc:hover{ background:var(--field,#f6f6f9); }
/* BLACK WITH WHITE ON IT, matching the admin and profile tabs — asked for 26 August 2026, the
   same day and for the same reason as those: a pale wash of the accent is a hint, and the
   document you are reading has to be the one thing on the page you cannot mistake. */
button.lg-doc.active{ background:#171717; color:#FFFFFF; }
.lg-toc{ margin-top:18px; padding-top:14px; border-top:1px solid var(--line,#ececf2); display:flex; flex-direction:column; gap:2px; }
.lg-toc a{ padding:5px 12px; border-radius:7px; font-size:12.5px; line-height:1.45;
  color:var(--muted,#5f6570); text-decoration:none; cursor:pointer; }
.lg-toc a:hover{ background:var(--field,#f6f6f9); color:var(--ink,#14161c); }
.lg-main{ flex:1 1 auto; min-width:0; max-width:720px; }
.lg-doc-body.hidden{ display:none; }
.lg-title{ font-size:34px; font-weight:800; letter-spacing:-.02em; margin:0 0 6px; }
.lg-date{ font-size:13px; color:var(--muted,#9aa0a6); margin:0 0 26px; }
/* The summary is not decoration. Most people read this box and nothing else, so it has to be
   true on its own and it has to be findable. */
.lg-sum{ padding:16px 18px; margin:0 0 34px; border:1px solid var(--line,#ececf2); border-radius:12px;
  background:var(--field,#f6f6f9); font-size:14.5px; line-height:1.65; color:var(--ink,#14161c); }
.lg-doc-body h2{ font-size:20px; font-weight:750; letter-spacing:-.01em; margin:38px 0 12px;
  scroll-margin-top:96px; }
.lg-doc-body h3{ font-size:15.5px; font-weight:700; margin:24px 0 8px; }
.lg-doc-body p{ font-size:15px; line-height:1.72; margin:0 0 14px; color:var(--ink,#2a2d35); }
.lg-doc-body ul{ margin:0 0 16px; padding-left:22px; }
.lg-doc-body li{ font-size:15px; line-height:1.72; margin-bottom:8px; color:var(--ink,#2a2d35); }
/* A LIGHTER BLUE THAN THE REST OF THE SITE USES, and it is not a preference. This page is
   dark, and the accent blue on it measures about 3.6 to 1 against the background — under the
   4.5 that ordinary text needs to be read comfortably. These links are how somebody exercises
   the rights the page grants, so they are the last links on the site that should be hard to
   see. Measured, not guessed: see test-legal.cjs, which fails if the contrast drops. */
.lg-doc-body a{ color:#8fa8ff; text-decoration:underline; text-underline-offset:2px; }
.lg-doc-body a:hover{ color:#b3c2ff; }
body.light .lg-doc-body a{ color:var(--accent,#2b3fee); }
.lg-note{ padding:10px 14px; border-left:3px solid var(--accent,#2b3fee); background:var(--field,#f6f6f9);
  border-radius:0 8px 8px 0; font-size:14px !important; }
/* The two paragraphs the law wants in capitals stay in capitals, and are made readable rather
   than shouted: slightly smaller, a little more space between the lines. */
.lg-doc-body p:has(> strong) + p{ margin-top:14px; }
@media (max-width:900px){
  .lg-wrap{ flex-direction:column; gap:22px; padding-bottom:60px; }
  .lg-side{ position:static; flex:1 1 auto; width:100%; max-height:none; }
  .lg-toc{ display:none; }
  .lg-title{ font-size:28px; }
}
/* The line under the sign-up buttons that names what somebody is agreeing to. */
.ag-legal{ margin:14px 0 0; font-size:12px; line-height:1.55; color:var(--muted,#9aa0a6); text-align:center; }
.ag-legal a{ color:inherit; text-decoration:underline; text-underline-offset:2px; }
.ag-legal a:hover{ color:var(--accent,#2b3fee); }

/* My Projects page */
.projects{ min-height:100vh; background:var(--bg); color:var(--ink,#14161c); }
.projects.hidden{ display:none; }
/* 40px each side, the same as the home page nav, so the logo does not shift when you
   move between pages. */
/* Below the site header, for the same reason as the browse sidebar. */
.pj-bar{ display:flex; align-items:center; justify-content:space-between; padding:14px var(--edge); border-bottom:1px solid var(--line,#ececf2); position:sticky; top:var(--headh); background:var(--panel,#fff); z-index:10; }
.pj-logo{ display:flex; align-items:center; gap:9px; font-weight:800; font-size:18px; color:var(--ink,#14161c); cursor:pointer; }
.pj-new{ display:inline-flex; align-items:center; gap:7px; background:var(--accent,#2b3fee); color:#fff; border:none; border-radius:10px; padding:10px 16px; font-size:14px; font-weight:700; cursor:pointer; }
.pj-new:hover{ background:#2233d8; }
.pj-wrap{ max-width:1200px; margin:0 auto; padding:26px 26px 80px; }
/* THE LIKES PAGE IS THE WHOLE WIDTH, like the home page and unlike the projects page.
   The same edge inset as the nav and the templates wall, so the title lines up with the logo
   above it and the cards are the size they are everywhere else — inside the projects page's
   1200px column, seven columns of cards would be seven thin strips. */
.lk-wrap{ padding:26px var(--edge) 80px; }
/* Its own background, asked for on 21 August 2026. The .tf inside carries the home page's
   colour with it, so that is turned off here rather than fought with — one place decides
   what this page looks like. */
#likes{ background:#F4F4F4; }
#likes .tf{ background:transparent; }
/* AND THE WRITING HAS TO COME WITH IT. The projects page this shares its markup with is a
   DARK page, so its heading colour is a near-white — which on this near-white background was
   a title nobody could read. Measured, not guessed: #F2F2F2 on #F4F4F4. The page's own
   near-black and grey instead, the same two the gallery above it uses. */
#likes .pj-title{ color:#16161A; }
#likes .pj-sub,#likes .pj-empty{ color:#55555E; }
.pj-title{ font-size:26px; font-weight:800; margin:6px 0 18px; }
.pj-search{ display:flex; align-items:center; gap:10px; border:1px solid var(--field-bd,#e2e2ea); border-radius:12px; padding:12px 16px; background:var(--field,#f6f6f9); color:var(--muted,#6e7280); }
.pj-search input{ flex:1; border:none; background:transparent; outline:none; font-size:15px; color:var(--ink,#14161c); }
.pj-controls{ display:flex; gap:10px; margin:14px 0 6px; }
.pj-controls select{ height:38px; border:1px solid var(--field-bd,#e2e2ea); border-radius:9px; background:var(--field,#fff); padding:0 12px; font-size:13.5px; color:var(--ink,#14161c); cursor:pointer; }
.pj-h{ font-size:16px; font-weight:700; margin:26px 0 12px; }
.pj-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:16px; }
.pj-card{ position:relative; border-radius:12px; cursor:pointer; }
.pj-thumb{ position:relative; aspect-ratio:4/3; border-radius:10px; overflow:hidden; background:var(--field,#eceef3); border:1px solid var(--line,#ececf2); display:flex; align-items:center; justify-content:center; }
/* ---- admin: users table ---- */
.us-stats{ display:flex; gap:10px; margin:-6px 0 14px; }
.us-stat{ font-size:12.5px; color:var(--mut2,#55555E); background:var(--bg,#F5F5F7); border-radius:8px; padding:5px 11px; }
.us-stat b{ color:var(--fg,#16161A); }
.us-table{ margin-top:16px; border:1px solid var(--bd,#EAEAEF); border-radius:12px; overflow:hidden; background:#fff; }
/* Name, email, role, plan, storage, joined, action. "Last seen" gave way to storage —
   seven columns is already the most this fits without the email being squeezed.
   THE LAST COLUMN IS A FIXED WIDTH, NOT auto. The heading row and each person's row are
   separate grids that only line up because their columns work out the same. With auto, the
   heading's empty last cell measured 0 and a row's buttons measured 200, so every flexible
   column either side came out a different width — and the headings drifted further right
   the further across you looked. Fixed here, both grids agree. */
.us-row{ display:grid; grid-template-columns:1.3fr 1.8fr .6fr .6fr 1.1fr .9fr 76px 88px 30px; gap:12px; align-items:center; padding:11px 16px; border-top:1px solid var(--bd,#EAEAEF); font-size:13px; }

/* Both plans are badged, in the same shape as Admin, so the column reads as one row of
   pills rather than a pill beside a word. Green for Free, pink for Pro.
   The text is a dark version of each background rather than the background colour itself:
   a mid-green on a pale green is the kind of thing that looks fine to whoever picked it and
   is unreadable to everyone else. */
.us-plan{ display:inline-block; font-size:11px; font-weight:700; padding:3px 9px; border-radius:99px;
  color:var(--plan-free); background:var(--plan-free-tint); }
.us-plan.on{ color:var(--plan-pro); background:var(--plan-pro-tint); }
/* The bar is read first and the number second, so the bar comes first and the number sits
   under it — at a glance you are looking for a full bar, not reading megabytes. */
.us-store{ display:flex; flex-direction:column; gap:3px; min-width:0; }
.us-storeb{ display:block; height:5px; border-radius:3px; background:var(--bd,#EAEAEF); overflow:hidden; }
.us-storeb i{ display:block; height:100%; border-radius:3px; background:var(--acc,#3E6BE6); transition:width .2s; }
/* Amber is "worth a look", red is "cannot upload another thing" — the two states that
   need different reactions from you. */
.us-storeb[data-state="near"] i{ background:#E8A33D; }
.us-storeb[data-state="full"] i{ background:#D6453D; }
.us-storen{ font-size:11.5px; color:var(--mut2,#55555E); font-variant-numeric:tabular-nums; }
/* A failure reads as a failure, not as a quiet dash. Muted rather than alarming — the page
   still works, one column of it does not. The reason is in the tooltip. */
.us-storefail{ font-size:11.5px; color:var(--danger); cursor:help; }
.us-row:first-child{ border-top:none; }
.us-head{ background:var(--bg,#F5F5F7); font-size:11px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--mut3,#9A9AA4); }
.us-nm{ font-weight:600; color:var(--fg,#16161A); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.us-nm em{ font-style:normal; color:var(--mut3,#9A9AA4); font-weight:500; }
.us-em{ color:var(--mut2,#55555E); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.us-muted{ color:var(--mut3,#9A9AA4); }
/* Admin is orange — the one status that is about power rather than payment, so it should
   not share a colour with either plan. */
.us-badge.us-owner{ color:#FFF; background:var(--role-admin); }   /* filled, so an owner reads as more than an admin at a glance */
.us-badge{ font-size:11px; font-weight:700; color:var(--role-admin); background:var(--role-admin-tint); padding:3px 9px; border-radius:99px; }
.us-act{ display:flex; gap:6px; justify-content:flex-end; }
/* Faint until you are on the row, then plain. Always visible would be eight identical marks
   competing with the switches; appearing only on hover would leave the row looking like a
   dead end to anyone who never hovers, which is the whole problem it is there to solve. */
.us-more{ display:grid; place-items:center; color:var(--mut3,#9A9AA4); opacity:.45; transition:opacity .12s, color .12s; }
.us-row[data-uid]:hover .us-more{ opacity:1; color:var(--mut2,#55555E); }
/* the switch sits under its heading, which is left-aligned like every other column */
.us-swcell{ display:flex; }

/* The Pro switch. Orange when on, grey when off — the state is the whole point, so the
   colour has to carry it on its own from across the table.
   Sized in one place: the knob and the travel are both worked out from the track, so
   changing --sw-h is the only edit needed to resize it. */
.us-sw{ --sw-h:20px; --sw-w:36px;
  /* which colour this switch turns when it is on — the bright pair, because a switch is a
     solid block of colour and the deep badge shades read as drab at this size. Neither names a
     colour of its own: they are the same two names as the badges, in their solid-block form. */
  --sw-on:var(--plan-pro-vivid, #DB0A72);
  position:relative; flex:none; width:var(--sw-w); height:var(--sw-h); padding:0;
  border:none; border-radius:99px; background:#8E8E9C; cursor:pointer;
  transition:background .16s; }
.us-sw.is-admin{ --sw-on:var(--role-admin-vivid, #E35A00); }
.us-sw:hover{ background:#7D7D8B; }
.us-sw.on{ background:var(--sw-on); }
/* darkened from the same colour rather than a second hex, so a hover cannot drift away
   from the shade it is meant to be a hover of */
.us-sw.on:hover{ background:color-mix(in srgb, var(--sw-on) 86%, #000); }
.us-sw i{ position:absolute; top:2px; left:2px;
  width:calc(var(--sw-h) - 4px); height:calc(var(--sw-h) - 4px);
  border-radius:50%; background:#fff; box-shadow:0 1px 2px rgba(16,18,32,.35);
  transition:transform .16s; }
.us-sw.on i{ transform:translateX(calc(var(--sw-w) - var(--sw-h))); }
/* Keyboard users need to see where they are. The switch has no text, so without this there
   is nothing at all to show it is focused. */
.us-sw:focus-visible{ outline:2px solid var(--acc,#3E6BE6); outline-offset:2px; }
/* while the change is in flight — it stays readable rather than vanishing */
.us-sw:disabled{ opacity:.55; cursor:default; }
.us-act .ghost{ padding:5px 11px; font-size:12px; border-radius:8px; white-space:nowrap; }
@media (max-width:820px){ .us-row{ grid-template-columns:1fr 1fr auto; } .us-row > span:nth-child(4),.us-row > span:nth-child(5){ display:none; } }
/* ---- multi-select on design cards ---- */
.pj-hrow{ display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
.pj-selbar{ display:flex; align-items:center; gap:8px; font-size:13px; font-weight:600; color:var(--mut2,#55555E); }
.pj-selbar.hidden{ display:none; }
.pj-selbar .ghost{ padding:5px 11px; font-size:12.5px; border-radius:8px; }
.pj-selbar .pj-del{ color:#D93B30; }
.pj-selbar .pj-del:hover{ background:rgba(217,59,48,.08); color:#D93B30; }
.pj-check{ position:absolute; top:8px; left:8px; z-index:3; width:22px; height:22px; cursor:pointer; opacity:0; transition:opacity .12s; }
.pj-card:hover .pj-check,.pj-card.sel .pj-check{ opacity:1; }
.pj-check input{ position:absolute; inset:0; opacity:0; margin:0; cursor:pointer; }
.pj-check span{ position:absolute; inset:0; border-radius:6px; background:rgba(255,255,255,.94); box-shadow:0 0 0 1px rgba(20,20,30,.2), 0 1px 3px rgba(0,0,0,.16); transition:background .12s, box-shadow .12s; }
.pj-check input:checked + span{ background:var(--acc,#3E6BE6); box-shadow:0 0 0 1px var(--acc,#3E6BE6); }
.pj-check input:checked + span::after{ content:""; position:absolute; left:7.5px; top:3.5px; width:5px; height:10px; border:solid #fff; border-width:0 2px 2px 0; transform:rotate(45deg); }
.pj-card.sel .pj-thumb{ box-shadow:0 0 0 2px var(--acc,#3E6BE6); }
.pj-mini{ position:absolute; inset:0; overflow:hidden; }
.pj-thumb img{ width:100%; height:100%; object-fit:contain; }
.pj-card.img .pj-thumb img{ object-fit:cover; }
.pj-card:hover .pj-thumb{ border-color:var(--accent,#2b3fee); }
.pj-meta{ padding:8px 2px 0; }
.pj-namewrap{ display:flex; align-items:center; gap:4px; min-width:0; }
.pj-name{ font-size:13.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; outline:none; border-radius:4px; padding:1px 2px; }
.pj-name.editing{ white-space:normal; overflow:visible; text-overflow:clip; background:var(--field,#fff); box-shadow:0 0 0 2px var(--accent,#2b3fee); cursor:text; }
.pj-pen{ flex:none; width:20px; height:20px; display:grid; place-items:center; border:none; background:transparent; color:var(--muted,#8a9099); border-radius:5px; cursor:pointer; opacity:0; transition:opacity .12s; }
.pj-namewrap:hover .pj-pen{ opacity:1; }
.pj-pen:hover{ background:var(--hover,#f1f1f6); color:var(--ink,#14161c); }
.pj-sub{ font-size:12px; color:var(--muted,#8a9099); margin-top:2px; }
.pj-more{ position:absolute; top:8px; right:8px; width:28px; height:28px; border:none; border-radius:8px; background:rgba(255,255,255,.92); color:#333; font-size:18px; line-height:1; cursor:pointer; opacity:0; transition:opacity .12s; box-shadow:0 1px 4px rgba(0,0,0,.2); }
.pj-card:hover .pj-more{ opacity:1; }
/* The preview-images button, beside the ⋯ menu on a template card.
   Unlike ⋯ this one stays VISIBLE when the template already has previews, because the count is
   the only place an admin can see at a glance which templates have had pictures added and which
   are still showing the drawn preview. With nothing added it behaves like ⋯ and appears on hover,
   so a shelf of untouched templates does not sprout a badge on every card. */
.pj-pv{ position:absolute; top:8px; right:40px; height:28px; min-width:28px; padding:0 7px;
  border:none; border-radius:8px; background:rgba(255,255,255,.92); color:#333;
  font-size:12px; font-weight:600; line-height:28px; cursor:pointer; opacity:0;
  transition:opacity .12s; box-shadow:0 1px 4px rgba(0,0,0,.2); }
.pj-card:hover .pj-pv{ opacity:1; }
.pj-pv.has{ opacity:1; }
.pj-pv:hover{ background:#fff; }

/* the preview manager */
.pv-dlg{ width:min(620px,94vw); }
.pv-note{ margin:0 0 10px; font-size:12px; color:var(--muted,#8a9099); line-height:1.5; }
.pv-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(120px,1fr)); gap:10px;
  max-height:46vh; overflow:auto; padding:2px; }
.pv-item{ position:relative; margin:0; aspect-ratio:4/3; border-radius:10px; overflow:hidden;
  border:1px solid var(--line,#ececf2); background:var(--hover,#f1f1f6); }
.pv-item[data-cover]{ border-color:var(--accent,#2b3fee); }
.pv-item img{ width:100%; height:100%; object-fit:contain; display:block; }
.pv-cover{ position:absolute; top:6px; left:6px; padding:2px 6px; border-radius:6px;
  background:var(--accent,#2b3fee); color:#fff; font-size:10px; font-weight:700; letter-spacing:.02em; }
.pv-ops{ position:absolute; left:0; right:0; bottom:0; display:flex; gap:4px; justify-content:center;
  padding:5px; background:linear-gradient(transparent,rgba(0,0,0,.55)); opacity:0; transition:opacity .12s; }
.pv-item:hover .pv-ops{ opacity:1; }
.pv-op{ width:24px; height:24px; border:none; border-radius:6px; cursor:pointer;
  background:rgba(255,255,255,.94); color:#222; font-size:13px; line-height:1; }
.pv-op[disabled]{ opacity:.35; cursor:default; }
/* the same red the delete button in a confirmation uses — removing a preview is meant to look
   like removing something, and it reads the palette rather than naming a colour of its own */
.pv-op.danger:hover{ background:var(--danger-dark); color:#fff; }
.pv-empty{ grid-column:1/-1; margin:0; padding:18px 2px; font-size:13px; color:var(--muted,#8a9099); }
.pv-add{ display:flex; align-items:center; gap:10px; margin-top:12px; }
.pv-status{ font-size:12px; color:var(--muted,#8a9099); }
/* touch has no hover, so the controls have to be there without one */
@media (hover:none){ .pj-pv,.pv-ops{ opacity:1; } }

.pj-empty{ grid-column:1/-1; color:var(--muted,#8a9099); font-size:14px; padding:14px 2px; }
.pj-ctx{ position:fixed; z-index:2100; background:var(--panel,#fff); border:1px solid var(--line,#ececf2); border-radius:10px; box-shadow:var(--float-shadow); padding:5px; min-width:130px; }
.pj-ctx button{ display:block; width:100%; text-align:left; padding:8px 11px; background:transparent; border:none; border-radius:7px; font-size:13px; color:var(--ink,#14161c); cursor:pointer; }
.pj-ctx button:hover{ background:var(--hover,#f1f1f6); }
.pj-ctx button.danger{ color:#e0483f; }
.pj-badge{ position:absolute; top:8px; left:8px; font-size:10.5px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; padding:3px 8px; border-radius:6px; }
.pj-badge.pub{ background:#0f9d58; color:#fff; }
.pj-badge.draft{ background:rgba(0,0,0,.6); color:#fff; }
/* Published-or-draft and Pro-or-free are two separate facts, so they are two separate tags,
   on opposite corners. Stacked in the one corner the first would sit under the second.
   `left:auto` because the rule above sets left, and setting only `right` on top of it pins
   both edges and stretches the tag across the picture. */
/* THE CROWN ALONE HERE, no word beside it. It had "Pro" written next to it and the pair grew
   wide enough to sit on top of "Published" on the opposite corner — seen in a screenshot before
   it shipped, on a card 150px across. Two tags on one narrow card is exactly where a word costs
   more than it is worth, and this one is the same mark the gallery uses, in the same gold, so
   there is nothing to learn from the word. */
.pj-badge.pro{ left:auto; right:8px; display:grid; place-items:center;
  width:24px; height:24px; padding:0; border-radius:50%;
  background:#fff; color:var(--gold,#C79A3B); box-shadow:0 1px 4px rgba(0,0,0,.28); }
.pj-badge.pro svg{ width:14px; height:14px; display:block; }
.pj-adminbadge{ font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--accent,#2b3fee); background:color-mix(in srgb,var(--accent,#2b3fee) 12%,transparent); padding:2px 8px; border-radius:6px; vertical-align:middle; margin-left:6px; }
/* Login gate */
body.locked{ overflow:hidden; }
/* Dim the page behind the dialog instead of covering it with a solid backdrop —
   the user keeps their context, which is what makes it read as a modal. */
/* TWO DIFFERENT THINGS WEARING TWO DIFFERENT DRESSES — settled 26 August 2026.
   SIGNING UP is a page. It is why a new visitor came, so it takes the whole window: edge to
   edge, no dimming, no rounded corner, nothing showing through, and its own address.
   LOGGING IN is a popup. Somebody logging in was already doing something — looking at a
   template, opening a design — and is interrupted for ten seconds. Taking the whole screen
   away from them, and changing the address under them, makes a small errand feel like being
   sent somewhere. So it comes back as a card over the page they were on, exactly as it was.
   The mode is on the gate, so which dress is worn is decided in one place, here. */
#authGate{ position:fixed; inset:0; z-index:2000; display:block;
  padding:0; background:#fff; }
/* Two halves of the window: the form on the left, the pitch on the right. */
#authGate .ag-modal{ position:relative; display:flex; align-items:stretch;
  width:100%; height:100%; max-height:none; background:#fff; border-radius:0; overflow:hidden;
  box-shadow:none; }
/* The form no longer fills its half — it sits in the middle of it at a readable width, with
   the empty space around it doing the work the card's border used to do. */
#authGate .ag-card{ flex:1 1 0; min-width:0; width:auto; max-width:none; overflow-y:auto;
  background:#fff; border:none; border-radius:0; box-shadow:none;
  padding:48px 32px; display:flex; flex-direction:column; justify-content:center; gap:10px;
  font-family:'Inter',system-ui,sans-serif; }
#authGate .ag-card > *{ flex:none; width:100%; max-width:392px; margin-left:auto; margin-right:auto; }

/* ---- the log-in dress: a card over the page you were on ---- */
#authGate[data-mode="login"]{ align-items:center; justify-content:center; padding:24px;
  background:rgba(12,10,16,.62); backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); }
#authGate[data-mode="login"] .ag-modal{ width:min(430px,100%); height:auto;
  max-height:calc(100vh - 48px); border-radius:20px;
  box-shadow:0 40px 100px -30px rgba(0,0,0,.6); }
#authGate[data-mode="login"] .ag-aside{ display:none; }   /* the pitch is for people signing UP */
#authGate[data-mode="login"] .ag-card{ padding:34px 28px; justify-content:flex-start; }
/* SMALLER ON THE CARD. The black circle is right on a full-screen page over a photograph; on
   a 430px card it is a heavy full stop in the corner of a small white box, so it keeps the
   colour and loses the weight. */
#authGate[data-mode="login"] .ag-close{ top:10px; right:10px; left:auto;
  width:32px; height:32px; font-size:18px; }
/* THE BRAND MARK ABOVE THE FORM. Held to its own height rather than the 392px the rest of the
   column is held to, or it would stretch across the whole form. */
#authGate .ag-logo{ width:auto; height:30px; align-self:center; margin:0 auto 18px; display:block; }
#authGate .ag-brand{ font-size:22px; font-weight:800; color:#14161c; text-align:center; }
#authGate .ag-sub{ font-size:13.5px; color:#6e7280; text-align:center; margin-bottom:12px; }
/* FULLY ROUND, asked for 27 August 2026 — every field and every button on this form, so the
   Google button, the two boxes and the main button all share one shape. 99px rather than 50%:
   a percentage bends the corners in proportion to the height AND the width, which on a box
   this wide gives an oval; a large fixed radius is capped at half the height, which is the
   pill that was asked for. */
#authGate .ag-google{ display:flex; align-items:center; justify-content:center; gap:10px; height:44px; border:1px solid #dcdce6; border-radius:99px; background:#fff; font-size:14px; font-weight:600; color:#14161c; cursor:pointer; }
#authGate .ag-google:hover{ background:#f6f7fb; }
#authGate .ag-or{ display:flex; align-items:center; gap:10px; color:#9aa0a6; font-size:12px; margin:6px 0; }
#authGate .ag-or::before,#authGate .ag-or::after{ content:""; flex:1; height:1px; background:#ececf2; }
#authGate .ag-input{ height:44px; border:1px solid #dcdce6; border-radius:99px; padding:0 18px; font-size:14px; color:#14161c; outline:none; }
#authGate .ag-input:focus{ border-color:#5b5bf5; }
/* BLACK, GOING BLUE UNDER THE POINTER — asked for 27 August 2026. The blue is the editor's
   #3B5FE0, written out rather than taken from --accent, which on this form is the brand red.
   The black is #171717, the same one every chosen tab and pill on the site uses. */
#authGate .ag-primary{ height:44px; border:none; border-radius:99px; background:#171717; color:#fff; font-size:14px; font-weight:700; cursor:pointer; margin-top:4px; transition:background .15s; }
#authGate .ag-primary:hover{ background:#3B5FE0; }
#authGate .ag-ghost{ height:40px; width:100%; border:none; background:transparent; color:var(--acc,#F40628); font-size:13.5px; font-weight:600; cursor:pointer; }
#authGate .ag-ghost:hover{ text-decoration:underline; }
/* ONE ADDRESS, ONE ACTION. The gate carries data-mode, and these three rules are the whole
   difference between the sign-up page and the log-in page: the other page's button is not
   there, and "Forgot your password?" is meaningless to somebody who has not got one yet.
   Written here rather than by hiding things in code, so the two pages cannot drift apart. */
#authGate[data-mode="login"] #agSignup{ display:none; }
#authGate[data-mode="signup"] #agLogin{ display:none; }
#authGate[data-mode="signup"] #agForgot{ display:none; }
/* "auto" ON THE SIDES, not 0 — 27 August 2026. Everything in this column is held to 392px and
   centred by `.ag-card > *`, which sets the side margins to auto. This line then set `margin`
   as a shorthand, and a shorthand writes all four sides: the auto margins became nought, and
   this one line sat 138 pixels left of everything above it. Written out here rather than
   shortened, so the next edit cannot undo the centring by accident. */
#authGate .ag-switch{ margin:14px auto 0; font-size:13.5px; color:#6e7280; text-align:center; }
/* THE WAY ACROSS TO THE OTHER FORM, in the editor's blue as of 27 August 2026 — it is the
   quiet second choice on this screen, and the red it was in is the colour the site uses to
   mean "this is the thing to press". */
#authGate .ag-swap{ border:none; background:transparent; padding:0; font:inherit;
  color:#3B5FE0; font-weight:600; cursor:pointer; }
#authGate .ag-swap:hover{ text-decoration:underline; }
#authGate .ag-msg{ min-height:18px; font-size:12.5px; color:#6e7280; text-align:center; }
#authGate .ag-msg.err{ color:#d23f31; }
#authGate .ag-card{ position:relative; }
.ag-close{ position:absolute; top:12px; right:12px; width:30px; height:30px; border:none; background:transparent; color:#8a9099; font-size:22px; line-height:1; border-radius:8px; cursor:pointer; }
.ag-close:hover{ background:var(--hover); color:#14161c; }
/* nav Sign up / Log in */
.tf-authbtns{ display:flex; align-items:center; gap:8px; }
.tf-signup{ background:transparent; border:1px solid rgba(0,0,0,.18); color:var(--tf-ink,#0D0D0C); border-radius:10px; padding:8px 16px; font-size:15.5px; font-weight:600; cursor:pointer; }
.tf-signup:hover{ background:rgba(0,0,0,.04); }
.tf-login{ background:var(--tf-ink,#0D0D0C); color:#fff; border:none; border-radius:10px; padding:9px 18px; font-size:15.5px; font-weight:600; cursor:pointer; }
.tf-login:hover{ opacity:.9; }
/* UPGRADE, sitting to the left of the profile — asked for 29 August 2026. White, with a light
   grey stroke and a gold crown to the left of the word; it was a solid gold button for the
   first hour and was changed on sight.
   THE GOLD IS NOW CARRIED BY THE CROWN ALONE, and that is the whole reason it works: somebody
   sees a gold crown on a template they cannot open, and the way out of that is the same gold
   crown in the corner of the screen. One gold, one meaning. A white button also stops it
   competing with Start creating a few pixels away, which is the loudest thing on the page and
   is meant to be.
   Built to the same measurements as Log in beside it, because two buttons of different heights
   on one row is the first thing anybody sees. The padding is a pixel tighter top and bottom
   than that one to pay for the border, which Log in does not have.
   NOTHING HERE SAYS WHAT `hidden` DOES, and a first version of this wrongly did. There is one
   global rule for that, near the top, carrying !important precisely so no button needs its own;
   a second copy here passed every test I had written and was caught by one I had not — the one
   that counts how many rules in the whole stylesheet mention `hidden`, and insists on one.
   Eight one-off patches had accumulated before that rule existed. This was nearly the ninth.
   `.TF BUTTON.TF-UPGRADE`, AND IT TOOK TWO GOES TO GET THERE. Some hundreds of lines below
   there is a blanket `.tf button{ background:none; border:none; color:inherit; }` which took
   the gold and the black straight back off: measured on the page as a transparent background
   and the page's own ink, while the stylesheet plainly said gold. Spelling it `button.tf-
   upgrade` was not enough — that ties the blanket exactly, and a tie goes to whichever is
   written later, which is the blanket. Naming the section as well wins outright, wherever
   either of them sits in the file. `.tf button.tf-create` above is the same fix for the same
   rule, and is the pattern followed here. */
.tf button.tf-upgrade{ display:inline-flex; align-items:center; gap:7px;
  background:#fff; color:#0D0D0C; border:1px solid rgba(0,0,0,.18); border-radius:10px;
  padding:8px 16px; font-size:14px; font-weight:600; cursor:pointer; white-space:nowrap; }
/* The crown is the only colour on it, which is the point of a white button: the gold is what
   the eye goes to, and it is the same gold as the mark on the templates it is offering. It
   stays exactly that gold on the black version — one gold, one meaning, on any background. */
.tf button.tf-upgrade svg{ flex:none; width:16px; height:16px; color:var(--gold,#C79A3B); }
.tf button.tf-upgrade:hover{ background:#fff; border-color:rgba(0,0,0,.34); }
/* ONE BUTTON, TWO STATES, asked for 29 August 2026. Free: white, "Upgrade", and pressing it
   opens the offer. Pro: black, "Pro", and pressing it does nothing — it has stopped being an
   offer and become a statement of what you already have. Both words live in the page and this
   picks one, so the storage readout that sets `data-plan` never has to know the wording.
   NEITHER WORD SHOWS BEFORE THE PLAN IS KNOWN, because the button is hidden until then. That
   is why there is no rule making one visible by default: a wrong guess here is a Pro member
   being offered an upgrade for half a second. */
.tf button.tf-upgrade .up-free,
.tf button.tf-upgrade .up-pro{ display:none; }
.tf button.tf-upgrade[data-plan="free"] .up-free{ display:inline; }
.tf button.tf-upgrade[data-plan="pro"]  .up-pro { display:inline; }
.tf button.tf-upgrade[data-plan="pro"]{ background:#0D0D0C; color:#fff; border-color:#0D0D0C;
  cursor:default; }
.tf button.tf-upgrade[data-plan="pro"]:hover{ background:#0D0D0C; border-color:#0D0D0C; }
/* layers */
.layers-top{ display:flex; gap:5px; padding:8px 10px; border-bottom:1px solid var(--line); flex:none; }
.layers-top label{ display:block; font-size:10px; font-weight:500; text-transform:none; letter-spacing:0; color:var(--muted); margin-bottom:6px; }
.layers-top .lt-field{ flex:1; min-width:0; }
.layers-top .lt-op{ flex:0 0 84px; }
.layers-top select,.layers-top input{ box-sizing:border-box; width:100%; height:22px; border:1px solid var(--field-bd); background:transparent; border-radius:6px; padding:0 8px; font-size:11px; font-family:inherit; color:var(--ink); outline:none; transition:border-color .15s,box-shadow .15s; }
.layers-top select:hover,.layers-top input:hover{ border-color:var(--bdh); }
.layers-top input{ font-family:inherit; font-size:11px; }
.layers-top select:hover,.layers-top input:hover{ background:var(--hover); }
.layers-top select:focus,.layers-top input:focus{ box-shadow:0 0 0 2px var(--accent) inset; }
.layers-top.disabled select,.layers-top.disabled input{ opacity:.45; }
.layers-actions{ display:flex; gap:3px; padding:6px; border-top:1px solid var(--line); flex:none; }
.layers-actions button{ flex:1; height:22px; display:grid; place-items:center; background:transparent; border:none; border-radius:6px; color:var(--dim); transition:background .12s,color .12s; }
.layers-actions button:hover{ background:var(--hover); color:var(--ink); }
.layers-actions button.on{ background:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
.layers-actions button.danger{ color:#F08A82; }
.layers-actions button.danger:hover{ background:rgba(224,72,63,.15); color:#FF9B93; }
.layers-actions.disabled button{ opacity:.45; }
/* New group works with nothing selected — that is how you make an empty folder */
.layers-actions.disabled #laGroup{ opacity:1; }

/* ---- group (folder) rows in the layers panel ---- */
.lrow.grow{ font-weight:600; }
.lrow.grow .gfolder{ display:grid; place-items:center; width:20px; color:var(--dim); }
.lrow.grow.hid .gfolder,.lrow.grow.hid .gname{ opacity:.4; }
.gtoggle{ width:16px; height:16px; display:grid; place-items:center; padding:0; background:transparent; border:none; border-radius:4px; color:var(--dim); cursor:pointer; }
.gtoggle:hover{ background:var(--hover); color:var(--ink); }
.gchev{ transition:transform .12s; }
.gcount{ font-size:10px; color:var(--muted); font-variant-numeric:tabular-nums; padding:0 2px; }
/* children sit indented under their folder, with a hairline running down the group */
.lrow.lchild{ padding-left:18px; position:relative; }
.lrow.lchild::before{ content:""; position:absolute; left:9px; top:0; bottom:0; width:1px; background:var(--line); }
/* the folder you are about to drop a layer into */
.lrow.grow.drop-in,.lrow.lchild.drop-in{ background:var(--accent-soft); box-shadow:inset 0 0 0 1px var(--accent); }
.llock{ margin-left:auto; color:var(--muted); flex:none; display:grid; place-items:center; }
.lfx{ width:22px; height:20px; border:none; background:transparent; color:var(--dim); font-family:'Instrument Serif',Georgia,serif; font-style:italic; font-weight:700; font-size:12px; display:grid; place-items:center; flex:none; transition:color .12s; }
.lfx:hover{ color:var(--ink); }
/* pale blue was legible on the old dark selection; on a light tint it disappears */
.lrow.sel .lfx{ color:var(--accent); }
.lmore{ width:26px; height:26px; border:none; background:transparent; border-radius:7px; color:var(--muted); display:grid; place-items:center; opacity:0; transition:opacity .12s, background .12s; flex:none; }
.lrow:hover .lmore,.lrow.sel .lmore{ opacity:1; }
.lmore:hover{ background:var(--hover); color:var(--ink); }
.lrow .llock{ margin-left:0; }
.lrow .lname{ flex:1; }
.lt-fx{ flex:0 0 44px; }
.lt-fx label{ display:block; font-size:11px; margin-bottom:6px; }
#fxBtn{ box-sizing:border-box; width:100%; height:22px; border:1px solid var(--field-bd); background:transparent; border-radius:6px; color:var(--dim); font-style:italic; font-weight:700; font-size:11px; font-family:'Instrument Serif',Georgia,serif; transition:all .12s; }
#fxBtn:hover{ border-color:var(--bdh); color:var(--ink); }
#fxBtn:hover{ background:var(--hover); color:var(--ink); }
.fx-dialog{ position:fixed; z-index:120; left:50%; top:50%; transform:translate(-50%,-50%); width:580px; max-width:94vw; background:var(--panel); border:1px solid var(--line); border-radius:14px; box-shadow:var(--float-shadow); display:flex; flex-direction:column; }
.fxd-head{ display:flex; align-items:center; justify-content:space-between; padding:13px 16px; border-bottom:1px solid var(--line); font-weight:700; font-size:13px; color:var(--ink); cursor:move; user-select:none; touch-action:none; }
.fxd-x{ width:28px; height:28px; border:none; background:transparent; border-radius:7px; color:var(--muted); font-size:13px; }
.fxd-x:hover{ background:var(--hover); color:var(--ink); }
.fxd-body{ display:grid; grid-template-columns:195px 1fr; min-height:330px; }
.fxd-list{ border-right:1px solid var(--line); padding:8px; overflow-y:auto; max-height:400px; }
.fxd-row{ display:flex; align-items:center; gap:9px; padding:8px 10px; border-radius:8px; font-size:13px; color:var(--dim); cursor:pointer; user-select:none; transition:background .12s,color .12s; }
.fxd-row:hover{ background:var(--hover); color:var(--ink); }
.fxd-row.cur{ background:var(--accent-soft); color:var(--accent); }
.fxd-row.dis{ opacity:.35; cursor:default; }
.fxd-row.dis:hover{ background:transparent; color:var(--dim); }
.fxd-row input[type=checkbox]{ accent-color:var(--accent); flex:none; }
.fxd-chk{ width:13px; flex:none; }
.fxd-settings{ padding:16px; overflow-y:auto; max-height:400px; }
.fxd-title{ font-size:11px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); margin-bottom:14px; }
.fxd-settings input[type=color]{ width:100%; height:34px; padding:0; border:2px solid var(--field-bd); border-radius:9px; background:none; }
.fxd-off{ font-size:12px; color:var(--muted); margin-top:6px; }
.fxd-foot{ display:flex; justify-content:flex-end; gap:8px; padding:12px 16px; border-top:1px solid var(--line); }
.lhead{ display:flex; gap:6px; margin-bottom:10px; }
.lrow{ display:flex; align-items:center; gap:6px; padding:4px 6px; border-radius:7px; cursor:pointer; border:1px solid transparent; color:var(--dim); }
.lrow:hover{ background:var(--hover); }
/* Selected is a flat tint of the accent and nothing else — no outline. The border stays
   in the box as transparent so a selected row is exactly as tall as an unselected one;
   removing it outright would make every row jump 2px when you click it.
   (This was the old blue written in directly, so it never followed the accent to indigo.) */
.lrow.sel{ background:var(--accent-soft); border-color:transparent; color:var(--accent); }
/* With several selected, this marks the one the Properties panel is describing. Without
   it you cannot tell whose settings you are editing. A deeper pour of the same colour
   rather than an outline, so the row still reads as one flat shape. */
.lrow.sel.sel-main{ background:color-mix(in srgb, var(--accent) 17%, var(--panel)); border-color:transparent; }
.lrow.sel .lname{ color:var(--accent); }
.lrow.hid .lname,.lrow.hid .lthumb{ opacity:.4; }
.lrow.drop-t{ box-shadow:inset 0 2px 0 var(--accent); }   /* drop line above this row */
.lrow.drop-b{ box-shadow:inset 0 -2px 0 var(--accent); }  /* drop line below this row */
.lrow.dragging{ opacity:.5; }
.leye{ width:24px; height:24px; border:none; background:transparent; color:var(--muted); display:flex; align-items:center; justify-content:center; border-radius:6px; flex:none; }
.leye:hover{ background:var(--hover); color:var(--ink); }
.lthumb{ width:23px; height:23px; border-radius:6px; background:var(--field); border:1px solid var(--field-bd); overflow:hidden; display:flex; align-items:center; justify-content:center; flex:none; }
.lthumb img{ width:100%; height:100%; object-fit:cover; }
.lthumb .tt{ font-family:'Instrument Serif',Georgia,serif; font-weight:700; font-size:11px; color:var(--dim); }
.lthumb .sw{ width:13px; height:13px; border-radius:3px; }
.lthumb .ph{ color:var(--muted); font-size:11px; }
.lname{ font-size:11px; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* while renaming: show the whole name, let the caret in, and stop the row's drag
   styling from making the text look inert */
.lname.editing{ overflow:visible; text-overflow:clip; cursor:text; outline:none;
  background:var(--field,#fff); color:var(--ink); border-radius:5px; padding:1px 5px; margin:-1px -2px;
  box-shadow:0 0 0 1.5px var(--accent,#3E6BE6); }
.panel-empty{ color:var(--muted); font-size:11px; line-height:1.5; }
.field{ margin-bottom:16px; }
.field label{ display:block; font-size:11px; font-weight:500; text-transform:none; letter-spacing:0; color:var(--muted); margin-bottom:4px; }
.field input[type=text],.field input[type=number],.field select,.field textarea{
  box-sizing:border-box; width:100%; border:1px solid var(--field-bd); background:transparent; border-radius:var(--ctl-r); padding:0 var(--ctl-x); height:var(--ctl-h); line-height:calc(var(--ctl-h) - 2px); font-size:13px; font-family:inherit; color:var(--ink); outline:none; transition:border-color .15s,box-shadow .15s; }
.field input[type=number]{ font-family:inherit; font-size:13px; }
.field textarea{ height:auto; line-height:1.45; padding:10px var(--ctl-x); }
.field input:hover,.field select:hover,.field textarea:hover{ border-color:var(--bdh); }
.field input:focus,.field select:focus,.field textarea:focus{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }

/* ===== THE RIGHT PANEL: FILLED CONTROLS, NO OUTLINES ==========================
 * Asked for 26 August 2026, with a swatch: every field, every dropdown and every unselected
 * button in the properties panel sits on a pale fill and carries no stroke at all.
 *
 * WHY IT IS ONE BLOCK RATHER THAN AN EDIT TO EACH RULE. The panel's controls were written
 * over months in half a dozen places — fields, the drag-to-change boxes, the alignment
 * buttons, the small pill buttons, the corner-radius popover — and each carried its own
 * "transparent with a one-pixel border". Changed one at a time, the next control added would
 * have arrived with a border again. This says it once, for everything inside #props.
 *
 * THE BORDER IS KEPT AT ZERO WIDTH RATHER THAN REMOVED — border-color:transparent, not
 * border:none — so nothing shifts by two pixels when a control takes focus and the accent
 * ring appears. That is the same reason the layers list keeps a transparent border.
 *
 * --ctl-bg is per theme: the swatch's near-white on the light one, and a step above the
 * panel on the dark one. A near-white fill on a dark panel would be a hole in it. */
#props .field input[type=text],
#props .field input[type=number],
#props .field select,
#props .field textarea,
#props .dsel-btn,
#props .al-btn,
#props .ghost,
#props select.fstyle-sel,
#props .font-btn{
  background:var(--ctl-bg); border-color:transparent; }
/* Hover lifts the fill instead of darkening an edge that is no longer there. */
#props .field input:hover,#props .field select:hover,#props .field textarea:hover,
#props .dsel-btn:hover,#props .al-btn:hover,#props .ghost:hover,
#props select.fstyle-sel:hover,#props .font-btn:hover{
  background:var(--ctl-bg-h); border-color:transparent; }
/* Focus is the one time an edge comes back, because it has to be unmistakable. */
#props .field input:focus,#props .field select:focus,#props .field textarea:focus,
#props select.fstyle-sel:focus{ border-color:var(--accent); }
/* A SELECTED BUTTON IS UNAFFECTED — it has its own fill and must outrank this. */
#props .al-btn.on,#props .al-btn:active{ background:var(--sel-bg); border-color:var(--sel-bg); }
.field textarea{ resize:vertical; min-height:60px; }
.row2{ display:flex; gap:var(--pp-gap); } .row2 > *{ flex:1; }
/* brand logo in the headers */
.brand-logo{ height:21px; width:auto; display:block; }
.brand-logo-sm{ height:15px; }
.pj-logo .brand-logo{ height:18px; }
.pj-logo{ display:inline-flex; align-items:center; }
/* divider between property groups */
/* icon sits beside the field, not inside it (e.g. gradient angle) */
.color-row{ display:flex; align-items:center; gap:var(--pp-gap); }
/* colour field (wide) + its own Opacity % field (narrow) side by side */
.cf-pair{ align-items:flex-start; }
.cf-pair > .field:first-child{ flex:1 1 auto; min-width:0; }
/* Was 84px, the 2.5 : 1 from the reference. The chequer mark inside it takes 26px of that,
   which left "100" and the spinner arrows fighting over what was left. */
.cf-pair > .field:last-child{ flex:0 0 104px; }
/* --- Font preview picker --- */
/* Reads the panel's own size, like every other control in it. It was 22px tall with a 6px
   corner, 8px of padding and 12px text — all set before the panel had shared values. */
.font-btn{ box-sizing:border-box; width:100%; height:var(--ctl-h); display:flex; align-items:center; justify-content:space-between; gap:6px; border:1px solid var(--field-bd); background:transparent; border-radius:var(--ctl-r); padding:0 var(--ctl-x); font-size:13px; color:var(--ink); cursor:pointer; outline:none; transition:border-color .15s,box-shadow .15s; }
.font-btn:hover{ border-color:var(--bdh); }
/* The family and its styles share one line: the name takes the room, the styles take what a
   word like "ExtraBold Italic" needs. Asked for 24 August 2026, in Figma's shape. */
/* TWO EQUAL HALVES, matching the Size and Weight pair directly below them — asked for
   26 August 2026. They were a stretchy name box beside a fixed 118px style box, which came
   out 160 and 118 against the 139 and 139 underneath: three different widths in four boxes,
   in the same column, one under the other. Same flex basis of nothing and the same gap the
   pair below uses, so the two rows line up whatever the panel's width. */
.font-row{ display:grid; grid-template-columns:1fr 1fr; gap:var(--pp-gap); align-items:center; }
.font-row .font-btn{ min-width:0; }
/* IT MATCHES THE FONT BUTTON BESIDE IT, and reads the same variables that button does.
   It was white with black text — asked for on 24 August, when it was the only pale thing in
   a dark panel and stood out for it. Asked for again on 26 August the other way: match the
   dark boxes. Written as var(--field-bd), var(--ink), var(--bdh) rather than as colours, so
   it follows the editor into the light theme without a second rule.
   The dropdown list itself needs its own pair: a browser paints those rows from the panel
   colour, and left alone they come out white text on white. */
/* ONE ARROW, NOT THE SYSTEM'S TWO, and the corner this panel uses rather than the corner the
   operating system likes — asked for 26 August 2026. Taking the appearance over is what makes
   both possible: left in its native dress a dropdown draws its own box and its own double
   chevron and quietly ignores border-radius, so the style box sat beside the font button in a
   different shape with a different arrow. The arrow below is the font button's, character for
   character, and the list is given room on the right so the two never overlap. */
.fstyle-wrap{ position:relative; display:block; min-width:0; }
.fstyle-arrow{ position:absolute; right:var(--ctl-x); top:50%; transform:translateY(-50%);
  pointer-events:none; opacity:.55; color:var(--ink); }
select.fstyle-sel{ -webkit-appearance:none; appearance:none;
  box-sizing:border-box; width:100%; height:var(--ctl-h); min-width:0;
  border:1px solid var(--field-bd); border-radius:var(--ctl-r);
  padding:0 calc(var(--ctl-x) + 18px) 0 var(--ctl-x);
  background:transparent; color:var(--ink); font-size:13px; font-family:inherit; cursor:pointer;
  outline:none; transition:border-color .15s,box-shadow .15s; }
select.fstyle-sel:hover{ border-color:var(--bdh); }
select.fstyle-sel:focus{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
select.fstyle-sel option{ background:var(--panel); color:var(--ink); }
.font-btn span{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.font-btn svg{ flex:none; opacity:.55; }
/* The missing-font line in the properties panel. Amber rather than red: nothing is broken
   and nothing was lost, the design is simply being shown in a substitute - red would say
   "something has gone wrong", which would be a lie. It reads as a note, not an error. */
.font-warn{ margin:-8px 0 14px; padding:8px 10px; border-radius:8px; font-size:11px; line-height:1.5;
  color:#8A5A08; background:rgba(224,168,60,.16); border:1px solid rgba(224,168,60,.34); }
.font-pop{ position:fixed; z-index:400; width:236px; background:var(--panel); border:1px solid var(--line); border-radius:12px; box-shadow:var(--float-shadow); padding:8px; }
/* The Sans Serif / Serif / Script tags under the search box. Deliberately the same pill shape as
   the filters on the home page rather than a new one: one idea should have one appearance. */
.font-tags{ display:flex; flex-wrap:wrap; gap:6px; padding:10px 8px 10px; }
.font-tag{ padding:5px 10px; border-radius:8px; border:1px solid var(--field-bd);
  background:var(--field); color:var(--muted); font:inherit; font-size:11px; font-weight:600;
  cursor:pointer; white-space:nowrap; transition:background .12s,color .12s,border-color .12s; }
.font-tag:hover{ color:var(--ink); }
/* BLACK, NOT THE ACCENT BLUE. Asked for on 22 August. Written out rather than taken from a
   variable: the accent is the blue this was, and every other variable here changes between the
   light and dark themes — black on both is the whole point of the request. */
.font-tag.on{ background:#111318; border-color:#111318; color:#fff; }
.font-search input{ width:100%; height:30px; box-sizing:border-box; border:1px solid var(--field-bd); background:var(--field); border-radius:8px; padding:0 10px; font-size:12px; color:var(--ink); outline:none; font-family:inherit; }
.font-search input:focus{ box-shadow:0 0 0 2px var(--accent) inset; border-color:transparent; }
.font-list{ max-height:340px; overflow-y:auto; margin-top:8px; }
.font-item{ display:block; width:100%; flex:0 0 auto; text-align:left; border:none; background:transparent; border-radius:8px; padding:8px 12px; font-size:14px; line-height:1.5; color:var(--ink); cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.font-item:hover{ background:var(--hover); }
.font-item.sel{ background:var(--accsoft,rgba(62,107,230,.14)); color:var(--accent); }
.font-sec{ font-size:10px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); padding:8px 10px 4px; }
.font-load{ display:block; width:100%; text-align:center; padding:9px 10px; margin-top:4px; background:var(--field); border:1px solid var(--field-bd); border-radius:8px; color:var(--accent); font-size:12px; font-weight:600; }
.font-load:hover{ background:var(--hover); }
.font-empty{ padding:14px 10px; font-size:12px; color:var(--muted); text-align:center; }
.btn-row{ display:flex; gap:6px; flex-wrap:wrap; }
/* A rule above and below a group, matching the effect sections' line so the panel reads as
   one set of divisions rather than two competing ones. The margin is what stops the line
   below sitting hard against the next control. */
.sep-top{ border-top:1px solid var(--line); border-bottom:1px solid var(--line);
  padding-top:14px; padding-bottom:14px; margin-bottom:14px; }
/* an effect section immediately after loses its own top line, so the two share one */
.sep-top + .fxs{ border-top:none; }
.al-row{ display:flex; gap:var(--pp-gap); }
.al-btn.on{ background:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
/* TWO WAYS OF DOING ONE THING, shown as one control with two halves rather than as a tick box.
   "Resize layout" and "Scale everything" are both real answers and neither is the absence of the
   other, which is what a tick box would say. Used where a whole grid is selected and dragging a
   corner has to mean one of them. */
.seg{ display:flex; gap:2px; padding:3px; background:var(--sunk,#F1F2F6); border-radius:11px; }
.seg-btn{
  flex:1 1 0; min-width:0; height:30px; padding:0 8px; border:none; border-radius:8px;
  background:none; color:var(--muted); font:inherit; font-size:12px; font-weight:600;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; cursor:pointer;
}
.seg-btn:hover{ color:var(--ink,#16161A); }
.seg-btn[aria-pressed="true"]{ background:var(--panel,#fff); color:var(--ink,#16161A);
  box-shadow:0 1px 2px rgba(16,18,28,.10); }
/* One line saying what the chosen half actually does, because "Resize layout" is two words and
   the difference between them is the whole point. */
.seg-note{ margin:7px 2px 0; font-size:11px; line-height:1.5; color:var(--muted); }
.sw-mix{ display:flex; align-items:center; gap:10px; }
.sw-mix input[type=range]{ flex:1; }
/* The number beside the stroke-width slider. It was 22px tall with a 6px corner and 11px
   text, from before the panel had shared values. Wide enough for three digits plus the
   spinner arrows the browser draws inside it. */
.sw-mix input[type=number]{ box-sizing:border-box; width:74px; height:var(--ctl-h); border:1px solid var(--field-bd); background:transparent; border-radius:var(--ctl-r); padding:0 var(--ctl-x); color:var(--ink); font-family:inherit; font-size:13px; outline:none; transition:border-color .15s; }
.sw-mix input[type=number]:hover{ border-color:var(--bdh); }
.sw-mix input[type=number]:focus{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
.rad-all{ display:flex; align-items:center; gap:8px; margin-bottom:8px; }
/* The box now carries its own mark, so the thing that stretches is the wrapper around it
   rather than the input itself. */
.rad-all > .in-lead{ flex:1; min-width:0; }
.rad-all input{ width:100%; }
/* Now a button: it opens the four separate corners underneath, the way Figma does.
   Sized like the other controls so it sits level with the field beside it. */
.rad-ic{ width:var(--ctl-h); height:var(--ctl-h); flex:none; display:grid; place-items:center;
  border:1px solid var(--field-bd); border-radius:var(--ctl-r); background:transparent;
  color:var(--dim); cursor:pointer; transition:border-color .12s, background .12s, color .12s; }
.rad-ic:hover{ border-color:var(--bdh); color:var(--ink); }
.rad-ic[aria-expanded="true"],.rad-ic.on{ background:var(--sel-bg); border-color:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
.rad-ic svg{ width:var(--pico); height:var(--pico); }
.rad-grid{ display:grid; grid-template-columns:1fr 44px 1fr; gap:8px; align-items:center; justify-items:stretch; }
/* The link button matches the boxes either side of it. At 22px it sat as a short stub in
   the middle of a row of 36px boxes, which read as a mistake. */
.rad-grid .al-btn{ width:100%; height:var(--ctl-h); }
/* The same height and roundness as a field, so a row of these sitting beside a dropdown
   reads as one line of controls rather than two different-sized things. It was 22px and
   5px, from before the panel had a size to share. */
.al-btn{ box-sizing:border-box; flex:1; height:var(--ctl-h); border:1px solid var(--field-bd); background:transparent; border-radius:var(--ctl-r); display:grid; place-items:center; color:var(--dim); transition:all .12s; }
.al-btn:hover{ border-color:var(--bdh); color:var(--ink); }
.al-btn:active,.al-btn.on{ background:var(--sel-bg); border-color:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
/* Vertical alignment has nothing to move the text within while the box is set to grow with
   its text, so its buttons are disabled there. Dimmed rather than hidden: a control that
   vanishes is one you cannot discover, and the tooltip on each button says which switch to
   flip. The cursor is the normal arrow so it does not invite a click it will ignore. */
.al-row.dim{ opacity:.45; }
/* the Fit pair on a picture: two square buttons on the left, not two halves of the row —
   asked for 26 September 2026: "make the squares not rectangles, and align to left" */
.al-row.fit-row{ justify-content:flex-start; }
.al-row.fit-row .al-btn{ flex:0 0 auto; width:var(--ctl-h); aspect-ratio:1 / 1; }
.al-btn:disabled{ cursor:default; }
.al-btn:disabled:hover{ border-color:var(--field-bd); color:var(--dim); }
.mini-btn{ font-size:10px; font-weight:600; border:none; background:var(--field); border-radius:6px; padding:5px 8px; color:var(--ink); }
.mini-btn:hover{ background:var(--hover); }
.mini-btn.danger{ color:#F08A82; border-color:rgba(224,72,63,.4); }
.layer-name{ font-weight:700; font-size:12px; margin-bottom:2px; color:var(--ink); letter-spacing:-.01em; }
.layer-type{ font-size:10px; color:var(--muted); text-transform:capitalize; letter-spacing:0; margin-bottom:10px; }

.overlay{ position:fixed; inset:0; background:rgba(20,18,14,.86); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:16px; z-index:100; }
.overlay #overlayMsg{ font-size:14px; color:var(--muted); font-weight:600; }
.spinner{ width:38px; height:38px; border:3px solid var(--line); border-top-color:var(--accent); border-radius:50%; animation:spin .8s linear infinite; }
@keyframes spin{ to{ transform:rotate(360deg); } }
.toast{ position:fixed; bottom:22px; left:50%; transform:translateX(-50%); background:#0D0D0D; color:var(--ink); border:1px solid var(--line); padding:11px 18px; border-radius:10px; font-size:13px; z-index:300; /* above every app window (200), or a refusal spoken while one is open is never seen — 5 Sep 2026 */ box-shadow:var(--shadow); max-width:80vw; }
.toast.err{ background:var(--danger); color:#fff; border-color:transparent; }


/* ---------- light editor theme ---------- */
body.light{
  --bg:#F4F4F4; --panel:#FFFFFF; --ink:#1B1B1B; --muted:#8C8C8C; --line:#E6E6E6;
  --soft:#F1F1F1; --field:#EFEFEF; --field-bd:#DADADA; --hover:#E8E8E8; --rtrack:#E4E4EA; --accent-soft:#E8EAFD;
  /* the near-white Mounir sent on 26 August, and a half-step down for hover */
  --ctl-bg:#F7F7F8; --ctl-bg-h:#EFEFF1;
  --dim:#6C6C6C; --bdh:#B6B6B6; --dots:#CFCFCF; --canvas:#F8F9FD;
  /* One line colour for the whole editor — dividers, field outlines, panel edges, menus.
     Declared here rather than on .editor because the template dialog is attached to the
     page root, outside the editor, and still has to match it. */
  --ed-line:#EEEFF3; --ed-line-h:#DCDEE6;
  /* and one hover colour, for every button, menu item, layer row and swatch */
  --ed-hover:#F7F6FC;
  --shadow:0 6px 24px rgba(0,0,0,.12), 0 1px 2px rgba(0,0,0,.05);
  /* Far lighter than the dark theme's — the same shadow reads as dirt on white. But light
     does not mean invisible: the first attempt at this pulled the shadow 10px inside the
     element before blurring it 34px, so what escaped past the edge was about 6% grey on an
     already off-white page, which is nothing. The close 1px layer is what actually makes an
     edge readable; the wide ones only give it depth. */
  /* halved on 26 August 2026, in step with the dark one above */
  --float-shadow:0 7px 15px -2px rgba(22,24,45,.10), 0 3px 6px -1px rgba(22,24,45,.08), 0 1px 2px rgba(22,24,45,.10);
}
body.light .editor{ color-scheme:light; }
body.light .lrow.sel,body.light .lrow.sel .lname{ color:var(--accent,#6C7DF7); }
body.light .toast{ background:#1B1B19; color:#F5F4F1; border-color:transparent; }
body.light .overlay{ background:rgba(244,244,244,.88); }
body.light .stage{ box-shadow:0 24px 50px -18px rgba(30,28,24,.35); }
/* strength guidance inside the sign-in card, shown only once someone tries to sign up */
.ag-hint{ font-size:12px; line-height:1.5; color:#8C8C8C; margin:6px 0 12px; text-align:left; }
.ag-hint.bad{ color:#C62828; }
#agStrength .rp-meter{ margin:4px 0 0; }

/* ===================== Version history panel ===================== */
.fpanel-head{ display:flex; align-items:center; gap:8px; }
.fpanel-head > span{ flex:1; }
.fpanel-ico{ width:24px; height:24px; flex:none; display:grid; place-items:center; padding:0;
  background:transparent; border:none; border-radius:6px; color:var(--dim); cursor:pointer; }
.fpanel-ico:hover{ background:var(--hover); color:var(--ink); }
.ver-cur{ display:flex; align-items:center; gap:9px; padding:11px 2px 13px; font-size:12px; font-weight:600; color:var(--accent); }
.ver-dot{ width:9px; height:9px; border-radius:50%; background:var(--accent); box-shadow:0 0 0 3px var(--accent-soft); }
.ver-row{ display:flex; flex-direction:column; gap:2px; width:100%; text-align:left;
  padding:9px 10px; margin-bottom:2px; background:transparent; border:none; border-radius:8px; cursor:pointer; }
.ver-row:hover{ background:var(--hover); }
.ver-when{ font-size:12px; font-weight:600; color:var(--ink); }
.ver-sub{ font-size:12px; color:var(--muted); }
.ver-note{ margin:14px 2px 0; font-size:12px; line-height:1.5; color:var(--muted); }

/* ===================== Effect sections (Properties panel) =====================
   One block per effect: a title, a + to add it and a − to take it away, and the controls
   only while it is on. The next effect reuses all of this and supplies only its body. */
/* A line above and below every section.
   The second rule drops the top border only when another section sits directly above,
   so the two share one 1px line instead of drawing a 2px one against each other.
   This deliberately does NOT use :first-of-type — that means "first div among its
   siblings", and the "Effects" heading above is also a div, so it never matched and the
   top line never drew at all. */
.fxs{ border-top:1px solid var(--line); border-bottom:1px solid var(--line); }
.fxs + .fxs{ border-top:none; }
.fxs-head{ display:flex; align-items:center; gap:8px; padding:12px 0; }
.fxs-title{ flex:1; font-size:12px; font-weight:600; color:var(--dim); }
.fxs.on .fxs-title{ color:var(--ink); }
.fxs-btn{ width:24px; height:24px; flex:none; display:grid; place-items:center; padding:0;
  background:transparent; border:none; border-radius:6px; color:var(--dim); cursor:pointer; }
.fxs-btn:hover{ background:var(--hover); color:var(--ink); }
.fxs-body{ padding:0 0 14px; }
/* colour: swatch, hex, opacity — one row, as in the reference */
/* THE SHADOW'S COLOUR AND OPACITY ARE NOW THE SAME PAIR OF FIELDS AS THE FILL'S — asked for
   28 August 2026. Seven rules of their own went with the change: a round chip, a borderless
   code box, a 44px number, a per cent sign and their focus colour. Every one of those is
   already said once for the fields the Fill colour uses, and this pair now uses those. Two
   sets of rules describing one thing is exactly how they came to look different enough that
   Mounir could not find the opacity in one of them. */
/* slider rows: label left, value right, track beneath */
.fxs-row{ margin-bottom:13px; }
.fxs-lab{ display:flex; align-items:baseline; justify-content:space-between; margin-bottom:5px; }
.fxs-lab span:first-child{ font-size:12px; font-weight:600; color:var(--ink); }
.fxs-val{ font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
.fxs-range{ width:100%; display:block; }
/* COLOURED TRACKS, LIKE PHOTOSHOP'S — 1 October 2026: "use colorful indicator lines instead of blue".
   Brightness runs dark to light, Contrast flat grey to hard black-and-white, Saturation grey to
   full colour.
   The track shows what the slider does, so it carries no blue fill; the knob goes neutral to match. */
.fxs-range.fxs-brightness{ --fxs-track:linear-gradient(to right,#000,#fff); }
/* contrast: flat grey on the left, splitting into hard black over white on the right, so it
   reads on the dark panel too (a plain grey-to-black ran into the dark panel and vanished) */
.fxs-range.fxs-contrast{ --fxs-track:linear-gradient(to right,#8f8f8f,rgba(143,143,143,0)),linear-gradient(to bottom,#000 50%,#fff 50%); }
.fxs-range.fxs-saturation{ --fxs-track:linear-gradient(to right,#8e8e8e,#6f86b8 35%,#3442e3 58%,#b336de 80%,#ef2b2b); }
/* hue, 1 October 2026: the colour wheel laid flat, as Photoshop draws it, with the original
   colour in the middle where the slider starts */
.fxs-range.fxs-hue{ --fxs-track:linear-gradient(to right,#f00,#ff0 16.7%,#0f0 33.3%,#0ff 50%,#00f 66.7%,#f0f 83.3%,#f00); }
.fxs-range:is(.fxs-brightness,.fxs-contrast,.fxs-saturation,.fxs-hue)::-webkit-slider-runnable-track{ height:4px; background:var(--fxs-track); }
.fxs-range:is(.fxs-brightness,.fxs-contrast,.fxs-saturation,.fxs-hue)::-moz-range-track{ height:4px; background:var(--fxs-track); }
.fxs-range:is(.fxs-brightness,.fxs-contrast,.fxs-saturation,.fxs-hue)::-moz-range-progress{ background:transparent; }
.fxs-range:is(.fxs-brightness,.fxs-contrast,.fxs-saturation,.fxs-hue)::-webkit-slider-thumb{ margin-top:-5px; border-color:var(--bdh); }
.fxs-range:is(.fxs-brightness,.fxs-contrast,.fxs-saturation,.fxs-hue)::-moz-range-thumb{ border-color:var(--bdh); }
.fxs-action{ width:100%; margin-top:2px; }
.fxs-note{ margin:9px 0 0; font-size:12px; line-height:1.5; color:var(--muted); }

/* ===================== RESET PASSWORD PAGE ===================== */
.rp{ position:fixed; inset:0; z-index:200; display:grid; place-items:center; padding:24px;
  background:#F4F4F4; color:#16161A; font-family:'Google Sans Flex','Schibsted Grotesk',system-ui,sans-serif; overflow-y:auto; }
.rp.hidden{ display:none; }
.rp-card{ width:100%; max-width:420px; background:#fff; border-radius:18px; padding:34px 32px 26px;
  box-shadow:0 24px 60px -22px rgba(20,20,30,.28), 0 0 0 1px rgba(20,20,30,.05); }
.rp-logo{ height:26px; width:auto; display:block; margin-bottom:22px; }
.rp-title{ font-size:22px; font-weight:800; margin:0 0 7px; letter-spacing:-.01em; }
.rp-sub{ font-size:13px; line-height:1.55; color:#5f6570; margin:0 0 22px; }
.rp-label{ display:block; font-size:12px; font-weight:600; margin:0 0 6px; }
.rp-field{ position:relative; margin-bottom:12px; }
/* THE SAME SHAPE AS THE SIGN-IN CARD — 1 September 2026.
   Asked for in those words: "the UI style needs to be consistent". This page was built on its
   own and drifted — softly rounded boxes where the sign-in card has pills, its own grey for the
   edges, and a blue button where the card's is black. Somebody arriving here has just come from
   that card, by email, and the two should look like the same product. The numbers below are
   copied from it rather than matched by eye: 44 high, fully round, #dcdce6, and the same blue
   on focus. */
.rp-input{ width:100%; height:44px; padding:0 46px 0 18px; border:1px solid #dcdce6; border-radius:99px;
  font-family:inherit; font-size:14px; color:#14161c; background:#fff; outline:none; box-sizing:border-box; }
.rp-input:focus{ border-color:#5b5bf5; box-shadow:0 0 0 3px rgba(91,91,245,.14); }
/* THE EYE ON THE RESET PAGE — 1 September 2026.
   It was a fixed drawing that never changed, so pressing it revealed the password and the
   button went on looking exactly the same. It now uses the same mechanism as the sign-in
   card: the icon is drawn from the button's own state, so the two cannot disagree. */
.rp-eye{ position:absolute; right:6px; top:50%; transform:translateY(-50%); width:32px; height:32px;
  display:grid; place-items:center; background:transparent; border:none; border-radius:8px; color:#8C8C8C; cursor:pointer;
  --eye:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></svg>'); }
.rp-eye::before{ content:""; width:17px; height:17px; background-color:currentColor;
  -webkit-mask:var(--eye) center/17px 17px no-repeat; mask:var(--eye) center/17px 17px no-repeat; }
.rp-eye[aria-pressed="true"]{ color:#16161A;
  --eye:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.9 17.9A10.4 10.4 0 0 1 12 19c-6.4 0-10-7-10-7a19 19 0 0 1 5.1-5.9m3.2-1A10.4 10.4 0 0 1 12 5c6.4 0 10 7 10 7a19 19 0 0 1-2.2 3.2M9.9 9.9a3 3 0 0 0 4.2 4.2"/><path d="m2 2 20 20"/></svg>'); }
.rp-eye:hover{ background:#f4f4f8; color:#16161A; }
.rp-eye:focus-visible{ outline:2px solid var(--accent,#3765ee); outline-offset:-2px; }
/* four bars, filled as the password gets harder to guess */
.rp-meter{ display:flex; gap:4px; margin:2px 0 8px; }
.rp-meter i{ flex:1; height:3px; border-radius:2px; background:#e8e8ee; transition:background .15s; }
.rp-meter[data-level="1"] i:nth-child(-n+1){ background:#D93B30; }
.rp-meter[data-level="2"] i:nth-child(-n+2){ background:#E08A2E; }
.rp-meter[data-level="3"] i:nth-child(-n+3){ background:#3F9C56; }
.rp-meter[data-level="4"] i{ background:#1E7A3C; }
.rp-hint{ font-size:12px; line-height:1.5; color:#8C8C8C; margin:0 0 18px; }
.rp-hint.bad{ color:#C62828; }
/* White on black, fully round — the sign-in card's button, not this page's own blue one. */
.rp-primary{ width:100%; height:44px; margin-top:6px; border:none; border-radius:99px;
  background:#171717; color:#fff; font-family:inherit; font-size:14px; font-weight:700;
  cursor:pointer; transition:background .15s; }
.rp-primary:hover:not(:disabled){ background:#3B5FE0; }
.rp-primary:disabled{ opacity:.45; cursor:not-allowed; }
.rp-msg{ min-height:18px; margin-top:12px; font-size:13px; line-height:1.5; color:#5f6570; }
.rp-msg.err{ color:#C62828; }
.rp-msg.ok{ color:#1E7A3C; }
.rp-tick{ width:46px; height:46px; border-radius:50%; display:grid; place-items:center;
  background:rgba(46,140,74,.12); color:#1E7A3C; margin-bottom:16px; }
.rp-back{ display:block; margin-top:20px; text-align:center; font-size:13px; color:#8C8C8C; cursor:pointer; text-decoration:none; }
.rp-back:hover{ color:#16161A; }
@media (max-width:460px){ .rp-card{ padding:26px 20px 20px; border-radius:14px; } }

/* ===================== TEMPLIFY HOME (scoped .tf) ===================== */
.tf{ --bg:#F5F5F7; --fg:#16161A; --card:#FFFFFF; --mut2:#55555E; --mut3:#9A9AA4; --bd:#EAEAEF;
  /* the home page is always light, whatever the editor is set to, so it names the light
     value itself rather than inheriting the dark one from the page root */
  --float-shadow:0 14px 30px -4px rgba(22,24,45,.10), 0 6px 12px -2px rgba(22,24,45,.08), 0 1px 3px rgba(22,24,45,.10);
  --acc:var(--brand); --accsoft:var(--brand-soft); --accent:var(--brand); --gold:#C79A3B;
  --serif:'Google Sans Flex','Schibsted Grotesk',system-ui,sans-serif; /* the serif accent was replaced by Google Sans Flex, 25 October 2026 */ --tmono:'JetBrains Mono',ui-monospace,monospace;
  /* THE PAGE ITSELF IS AN OFF-WHITE, not white. The cards on it are white, so a white page
     left them with no edge and the wall read as one large sheet. This is the colour Mounir
     picked, taken from the swatch he sent — lightened on 25 August 2026 from #eeeceb to this,
     from a second swatch. It is a page colour and nothing else: the small surfaces that use
     --bg for their hover are left alone. */
  /* WHITE, asked for 26 August 2026. It was an off-white — first #eeeceb, then #f7f7f6, both
     from swatches Mounir sent — on the reasoning that white cards need an edge to sit on. The
     cards carry their own rounded picture and their own shadow now, so they do not, and he
     asked for white. Everything that fades into the page reads this variable, so the rail's
     fades follow it here rather than in three more places. */
  --page:#FFFFFF;
  /* How far the filter drawer pushes the page. One number, read by the drawer and by the push. */
  --drawerw:288px;
  background:var(--page); color:var(--fg); font-family:'Google Sans Flex','Schibsted Grotesk',system-ui,sans-serif; display:block; min-height:100vh; line-height:1.5; }
/* The site header carries .tf for the palette and the font, but it is a strip and not a
   page: without this it would inherit .tf's min-height:100vh and push everything below it
   off the bottom of the screen. */
/* THE HEADER STICKS, AND IT HAS TO BE THIS ELEMENT THAT DOES IT — 26 August 2026.
   The nav inside was marked sticky and had been since the site was built, and it never stuck:
   a sticky element is held inside its PARENT's box, and its parent is this shell. While the
   "In development" band lived in here the shell was about fifty pixels taller than the nav, so
   the nav clung for fifty pixels and then left with it — enough movement to look like it was
   working. Taking the band off on 26 August left the shell exactly as tall as the nav, which
   is no room at all, and the header scrolled away completely. Sticking the SHELL fixes both:
   its parent is the page, so it has the whole page to stick against. */
.tf-shell{ min-height:0; position:sticky; top:0; z-index:120; }
.tf-shell.hidden{ display:none; }
/* non-editor pages use the brand red; the editor keeps its blue (:root --accent) */
#projects, #authGate{ --accent:var(--brand); --acc:var(--brand); --accsoft:var(--brand-soft); }

/* MY PROFILE IS BLUE, asked for on 25 August 2026. It sits beside the admin area rather than
   beside the gallery — both are places you go to manage the account rather than to make
   something — and the Admin badge on that very page was already blue, so the red Save button
   and the red sidebar were the only red things on a page that had already gone blue.
   It reads the same four variables the admin area does, so the two cannot drift apart. */
#profile{ --accent:var(--admin); --acc:var(--admin); --accsoft:var(--admin-tint);
          --accent-ink:var(--admin-dark); --accent-soft:var(--admin-soft); }

/* The admin area is blue, not orange. Giving it its own colour is not decoration: it is
   the one place where a click can suspend somebody or delete their work, and a visible
   difference between "the product" and "the controls behind it" is worth having.
   Every admin surface reads these variables, so this block is the only place to change it. */
#users{ --accent:var(--admin); --acc:var(--admin); --accsoft:var(--admin-tint);
        --accent-ink:var(--admin-dark); --accent-soft:var(--admin-soft);
        /* The page itself, behind the menu and the panes — see .ad-side.
           NO FALLBACK HEX HERE, deliberately: this block is checked for hexes, because a
           colour written twice is a colour that drifts. --panel is declared by both themes,
           so there is nothing to fall back to. */
        background:var(--panel); }
/* The Admin badge means the same thing wherever it turns up, so it is blue everywhere —
   including on pages that are otherwise orange. This sits after the rules it overrides,
   which is what lets it win without !important. */
.pj-adminbadge, .pr-badge{ color:var(--admin); background:var(--admin-soft); }
.tf *{ box-sizing:border-box; }
.tf button{ font-family:inherit; cursor:pointer; border:none; background:none; color:inherit; }
/* Every page of the site, and only those. .tf covers the header and the home page; the signed-in
   pages are their own sections and have to be named. #editor and #users are deliberately absent:
   the editor has a canvas full of the user's own colours, and the admin area is the one place
   where a click can suspend somebody — neither should look like the rest of the site. */
.tf::selection,       .tf ::selection,
#projects::selection, #projects ::selection,
#profile::selection,  #profile ::selection,
#authGate::selection, #authGate ::selection,
#resetPage::selection,#resetPage ::selection{ background:var(--sel); color:var(--sel-ink); }
.tf em{ font-family:var(--serif); font-style:normal; font-weight:400; color:var(--acc); letter-spacing:-.01em; }
@keyframes tfRise{ from{opacity:0; transform:translateY(16px)} to{opacity:1; transform:none} }
@keyframes tfCardIn{ from{opacity:0; transform:translateY(12px)} to{opacity:1; transform:none} }
@keyframes tfBlink{ 50%{opacity:0} }

/* --- nav --- */
/* Plainly positioned: the shell above it is what sticks now, and two sticky elements one
   inside the other is a thing nobody can reason about later. */
/* THE NAV SITS ABOVE THE ROWS UNDER IT — 27 August 2026. It carries the account menu, which
   hangs down over the search row and the category rail; and because the nav is positioned with
   a z-index it is a stacking context, so EVERYTHING inside it — the menu included — is stacked
   at the nav's level and cannot be lifted above a sibling by a number of its own. At 1 it lost
   to the rail's scroll arrows, which are at 2: the round arrow was drawn on top of the open
   account menu. Anything added to the rows below with a z-index of its own needs a number
   under this one. */
.tf-nav{ position:relative; z-index:5; background:rgba(255,255,255,.85); backdrop-filter:blur(14px); border-bottom:1px solid var(--bd); }
/* Full width, not a centred 1280 column: the logo belongs against the left edge of the
   page and the account menu against the right, level with the headings and cards below.
   A centred column put both of them inside the column instead, which on any screen wider
   than 1280 is a different place. */
.tf-nav-in{ height:var(--navh); display:flex; align-items:center; gap:24px; padding:0 var(--edge); }
/* cursor:pointer because it is the way back to the home page from every page that shows
   this header. Set HERE and not in a second .tf-logo rule further down: a later rule that
   repeats the selector without repeating flex:none reads as the last word on .tf-logo. */
.tf-logo{ display:flex; align-items:center; gap:10px; flex:none; font-weight:800; font-size:21px; letter-spacing:-.02em; cursor:pointer; }
/* 30% up from the 21px every other logo on the site uses. Set here rather than on
   .brand-logo, which is the same image in the sign-in panel, the projects bar, the admin
   bar and the profile page — resizing it there would have moved five things to change one. */
/* and 30% up again on 2 October 2026 ("make logo 30% larger"), for the new script wordmark,
   which reads smaller than the old one at the same height: 27.3 x 1.3 = 35.5 */
.tf-logo .brand-logo{ height:35.5px; }
.tf-mark{ width:30px; height:30px; border-radius:9px; background:var(--fg); position:relative; overflow:hidden; }
.tf-mark::before{ content:""; position:absolute; inset:6px 6px 12px 12px; background:var(--acc); border-radius:3px; }
.tf-mark::after{ content:""; position:absolute; inset:13px 13px 6px 6px; background:var(--gold); border-radius:3px; mix-blend-mode:screen; }
.tf-mark-sm{ width:24px; height:24px; border-radius:7px; }
.tf-brand-sm{ font-size:16px; }
.tf-navsearch{ flex:1; max-width:440px; display:flex; align-items:center; gap:10px; height:42px; padding:0 16px; background:var(--card); border:1px solid var(--bd); border-radius:11px; color:var(--mut3); transition:border-color .2s, box-shadow .2s; }
.tf-navsearch:focus-within{ border-color:var(--acc); box-shadow:0 0 0 3px var(--accsoft); }
.tf-navsearch input{ border:none; outline:none; background:none; width:100%; font-size:14px; color:var(--fg); font-family:inherit; }
.tf-navsearch input::placeholder{ color:var(--mut3); }
.tf-navsearch kbd{ font-family:var(--tmono); font-size:11px; color:var(--mut3); border:1px solid var(--bd); border-radius:5px; padding:2px 6px; background:var(--bg); }
/* THE LINKS SIT NEXT TO THE LOGO since 13 September 2026, the way Mockuuups lays its header
   out — asked for with a picture of theirs beside ours. They used to be pushed to the right
   against the account buttons. The 'auto' that does the pushing moved to .tf-navright. The
   words are the same size, weight and ink as Sign up, and the gaps are wide the way theirs
   are: 56px from the logo, 40px between links. */
.tf-navlinks{ margin-left:56px; display:flex; gap:40px; font-size:15.5px; font-weight:600; color:var(--tf-ink,#0D0D0C); }
/* 15.5px — 30% up from 14px and then 15% back down, both asked for 13 September 2026 with a
   picture of the links and one of Sign up / Log in. All five words move together. */
/* The Start creating button shares the row with the links but stays on the right, next to the
   account buttons — a call-to-action belongs there, and it was not in the picture. */
.tf-navlinks{ flex:1; min-width:0; }
.tf-navlinks button.tf-create{ margin-left:auto; }
.tf-navlinks a{ cursor:pointer; transition:color .15s; }
.tf-navlinks a:hover{ color:var(--fg); }

/* ---- Create: the one thing on the page that moves ----------------------------------
   One flat colour that fades into the next and back round again. No shadow, nothing sliding
   — it simply changes, slowly enough that you notice it only if you look.

   This animates background-color, which does make the browser repaint the button on every
   frame. On one element this size that is nothing; it is only worth avoiding across large
   areas. The colours below are all far enough apart to keep white text readable at every
   point of the fade, which is checked in tools/test-createbtn.cjs — the label passes over
   every shade in turn, so the worst moment is the one that counts. */
/* WRITTEN AS .tf button.tf-create, NOT .tf-create. Two hundred lines above there is a
   blanket `.tf button{ color:inherit; background:none; }`, which is MORE specific than a
   single class — so a plain .tf-create rule lost, and the label came out the nav's grey.
   The background looked right only because an animation beats an ordinary rule whatever
   its specificity; the colour has no animation, so it lost quietly. */
/* The BOX is 30% bigger; the label and the wand inside it are not. Height and side padding
   both grew by the same 1.3, so the button keeps its proportions rather than turning into a
   tall thin thing. The corner radius is left at 5px on purpose — it was set deliberately, and
   scaling it would quietly undo that. */
/* 24 OCTOBER 2026, at Mounir's word: the same height as the Pro button beside it (35px, which is
   what Pro's 8px padding, 14px text and 1px border add up to), no wand icon, and fully round
   corners. The Pro button itself is left exactly as it was. */
.tf button.tf-create{ display:inline-flex; align-items:center; gap:8px; height:40px; padding:0 23px; /* 30% more room round the words, 27 October 2026 (Mounir) */
  border:none; border-radius:999px; cursor:pointer;
  font-size:14px; font-weight:600; letter-spacing:normal; /* same as Pro, asked 25 October 2026 */
  /* LIME, SINCE 13 SEPTEMBER 2026. It was a rainbow that drifted across the button (asked for on
     21 August, and checked at every blend for readable writing). Mounir asked for the avatar's
     lime instead, with the same 14px corner as the lime buttons on the landing page and the app
     pages, so the one colour means "go" everywhere on the site. Dark writing on it measures
     well over the 4.5 to 1 a label needs. */
  color:#fff;
  background:#000; /* BLACK SINCE 25 OCTOBER 2026: lime left the site, buttons are black and white only */
  transition:transform .14s, background .14s; }
.tf button.tf-create:hover{ transform:translateY(-1px); background:#000; }
.tf button.tf-create:active{ transform:translateY(0); }
.tf button.tf-create:focus-visible{ outline:2px solid var(--fg); outline-offset:2px; }
/* The rainbow's lap went with the rainbow (13 September 2026); the lift on hover still goes away
   for anybody who asked their system for less movement. */
@media (prefers-reduced-motion:reduce){
  .tf button.tf-create:hover{ transform:none; }
}

.tf-navright{ margin-left:auto; display:flex; align-items:center; gap:15px; }
/* EVEN SPACING ACROSS THE RIGHT OF THE HEADER — 24 October 2026, Mounir: "make sure spacing is
   balanced". Measured from what you see (the words, the bell, the edges of the two filled buttons),
   every gap is 35px: Start creating to the bell, bell to Sign up, Sign up to Log in, Log in to Pro,
   and, signed in, bell to Pro and Pro to the account picture. Sign up and Log in are words with no box, so their side padding
   counts as space; it is 10px each, and they keep the buttons' 35px height to stay easy to press. */
.tf .tf-authbtns{ gap:15px; }
.tf button.tf-signup, .tf button.tf-login{ height:35px; padding:0 10px; }
.tf-navright > #btnUpgrade{ margin-left:10px; }
.tf-navright > .tf-avwrap{ margin-left:20px; }   /* Pro to the round account picture: 35px too */
.tf-ico{ position:relative; width:38px; height:38px; border-radius:10px; display:grid; place-items:center; color:var(--mut2); transition:background .15s,color .15s; }
.tf-ico:hover{ background:#F2F2F4; color:var(--fg); }
.tf-dot{ position:absolute; top:8px; right:9px; width:7px; height:7px; border-radius:50%; background:var(--acc); border:2px solid var(--bg); }
/* THE RED DOT ON THE BELL. It was decoration until 27 August 2026 and was always on. It is
   hidden by default now and shown only when there is something published that this person has
   not seen — so ".hidden" has to beat the rule above it, which .tf-dot.hidden does by being
   one class more specific. */
.tf-dot.hidden{ display:none; }

/* =========================== THE NEWS PANEL ==========================================
   Asked for 27 August 2026, with Kittl's as the picture: a panel of cards, each a picture,
   a small grey label, a heading and two lines, and the whole card clickable.

   IT HANGS FROM THE HEADER rather than sitting in a page, because the bell is in the header
   and the header is the one thing on screen whatever page you are on. Fixed rather than
   absolute: the header sticks, and an absolutely positioned panel inside a sticky header
   gets clipped the moment the page is scrolled.

   THE WIDTH IS FIXED AND THE HEIGHT IS NOT. It stops short of the bottom of the window and
   scrolls inside itself, so ten items and one item both look deliberate. */
.nw-pop{ position:fixed; top:calc(var(--navh) + 8px); right:var(--edge); z-index:200;
  width:min(420px, calc(100vw - 32px)); max-height:calc(100vh - var(--navh) - 32px);
  display:flex; flex-direction:column; overflow:hidden;
  background:var(--card); border:1px solid var(--bd); border-radius:18px;
  box-shadow:0 18px 50px rgba(20,22,28,.18); }
.nw-pop.hidden{ display:none; }
.nw-pop-head{ display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:16px 18px; border-bottom:1px solid var(--bd); }
.nw-pop-head h2{ margin:0; font-size:17px; font-weight:700; color:var(--fg); }
.tf .nw-x{ width:32px; height:32px; flex:none; display:grid; place-items:center; border-radius:9px;
  background:none; border:none; color:var(--mut2); cursor:pointer; }
.tf .nw-x:hover{ background:var(--bg); color:var(--fg); }
.nw-pop-sub{ display:flex; flex-direction:column; gap:2px; padding:14px 18px 4px; }
.nw-pop-sub strong{ font-size:15px; font-weight:700; color:var(--fg); }
.nw-pop-sub span{ font-size:13px; color:var(--mut2); }
.nw-cards{ padding:10px 14px 16px; overflow-y:auto; display:flex; flex-direction:column; gap:12px; }
.nw-empty{ padding:26px 6px 30px; text-align:center; font-size:14px; color:var(--mut2); }

/* THE CARD IS A BUTTON, not a div with a click on it: one of these is reachable by keyboard
   and readable by a screen reader, and the other only looks that way. Written as
   `.tf .nw-card` because there is a blanket `.tf button{ background:none; border:none }`
   above it, and one class plus one tag beats a bare class — the trap the Edit Template
   button and the About buttons both fell into. */
.tf .nw-card{ display:block; width:100%; text-align:left; padding:0; overflow:hidden;
  background:var(--card); border:1px solid var(--bd); border-radius:14px; cursor:pointer;
  transition:transform .14s ease, box-shadow .14s ease, border-color .14s ease; }
.tf .nw-card:hover{ transform:translateY(-1px); border-color:var(--bdh,#d8d8e0);
  box-shadow:0 8px 22px rgba(20,22,28,.10); }
.tf .nw-card.nw-flat{ cursor:default; }
.tf .nw-card.nw-flat:hover{ transform:none; box-shadow:none; border-color:var(--bd); }
.nw-card-img{ display:block; width:100%; aspect-ratio:16/9; object-fit:cover; background:var(--bg); }
.nw-card-in{ padding:13px 14px 15px; }
.nw-tag{ display:inline-block; margin:0 0 9px; padding:5px 11px; border-radius:99px;
  background:var(--bg); font-size:12px; font-weight:600; color:var(--mut2); }
.nw-card-t{ margin:0 0 5px; font-size:15px; font-weight:700; line-height:1.3; color:var(--fg); }
/* Two lines and then an ellipsis: a card is a headline, not the article. */
.nw-card-b{ margin:0; font-size:13.5px; line-height:1.45; color:var(--mut2);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.nw-card-row{ display:flex; align-items:flex-start; gap:10px; }
.nw-card-row .nw-chev{ flex:none; margin-top:2px; color:var(--mut3); }
.nw-when{ display:block; margin:8px 0 0; font-size:12px; color:var(--mut3); }

@media (max-width:560px){
  .nw-pop{ right:8px; left:8px; width:auto; }
}

/* ---- the maintenance tools, put away behind one line ----
   Quiet on purpose: a link rather than a button, at the bottom rather than the top. Somebody
   coming to the Content tab is looking at uploads; these two are for the day something needs
   measuring or moving again. */
.ad-maint{ margin:28px 0 0; padding:18px 0 0; border-top:1px solid var(--line,#ececf2); }
.ad-maint-go{ display:inline-flex; align-items:center; gap:7px; padding:0; border:none;
  background:none; cursor:pointer; font-size:13px; font-weight:600;
  color:var(--muted,#8a9099); }
.ad-maint-go:hover{ color:var(--ink,#14161c); }
.ad-maint-go::before{ content:"▸"; font-size:11px; transition:transform .15s; }
.ad-maint-go[aria-expanded="true"]::before{ transform:rotate(90deg); }
.ad-maint-body{ margin-top:16px; }
.ad-maint-body.hidden{ display:none; }

/* ---- the delivery check on the Content tab ---- */
.dv-box{ border:1px solid var(--line,#ececf2); border-radius:14px; padding:16px 18px; margin:0 0 16px; }
.dv-box:last-child{ margin-bottom:0; }
.dv-lead{ margin:6px 0 14px; max-width:70ch; }
.dv-out{ margin:14px 0 0; padding:14px 16px; border-radius:11px; background:var(--hover,#f7f7fa);
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12.5px; line-height:1.65;
  color:var(--ink,#14161c); white-space:pre-wrap; word-break:break-word; max-height:60vh; overflow:auto; }
.dv-out.hidden,.dv-box .nw-btn.hidden{ display:none; }
.dv-out b{ font-weight:700; }

/* ---- the News tab on the admin page ---- */
.nw-admin{ display:flex; gap:30px; align-items:flex-start; }
.nw-form{ flex:1 1 0; min-width:0; max-width:560px; display:flex; flex-direction:column; }
.nw-side{ flex:0 0 330px; min-width:0; }
.nw-lab{ display:block; margin:16px 0 6px; font-size:13px; font-weight:600; color:var(--ink,#14161c); }
.nw-lab span{ font-weight:400; color:var(--muted,#8a9099); }
.nw-in{ width:100%; padding:10px 12px; border:1px solid var(--line,#ececf2); border-radius:10px;
  font-family:inherit; font-size:14px; color:var(--ink,#14161c); background:var(--panel,#fff); }
.nw-in:focus{ outline:2px solid #3B5FE0; outline-offset:-1px; }
.nw-area{ resize:vertical; min-height:88px; line-height:1.45; }
.nw-pic{ display:flex; align-items:center; gap:14px; }
.nw-thumb{ flex:none; width:132px; height:75px; border-radius:10px; overflow:hidden;
  border:1px dashed var(--line,#ececf2); display:grid; place-items:center;
  font-size:12px; color:var(--muted,#8a9099); background:var(--hover,#f7f7fa); }
.nw-thumb img{ width:100%; height:100%; object-fit:cover; }
.nw-picbtns{ display:flex; flex-direction:column; align-items:flex-start; gap:8px; }
.nw-btn{ padding:8px 14px; border:1px solid var(--line,#ececf2); border-radius:9px;
  background:var(--panel,#fff); font-size:13px; font-weight:600; color:var(--ink,#14161c); cursor:pointer; }
.nw-btn:hover{ background:var(--hover,#f1f1f6); }
.nw-btn-quiet{ border-color:transparent; color:var(--muted,#8a9099); }
.nw-actions{ display:flex; align-items:center; gap:10px; margin-top:22px; }
.nw-save{ padding:10px 18px; border:1px solid var(--line,#ececf2); border-radius:10px;
  background:var(--panel,#fff); font-size:14px; font-weight:600; color:var(--ink,#14161c); cursor:pointer; }
.nw-save:hover{ background:var(--hover,#f1f1f6); }
.nw-pub{ padding:10px 20px; border:none; border-radius:10px; background:#3B5FE0; color:#fff;
  font-size:14px; font-weight:700; cursor:pointer; }
.nw-pub:hover{ background:#2f4dc0; }
.nw-msg{ margin-top:12px; font-size:13px; color:var(--muted,#8a9099); min-height:19px; }
.nw-msg.err{ color:#C0392B; }
.nw-msg.ok{ color:#1E7B4D; }
.nw-list{ display:flex; flex-direction:column; gap:8px; }
.nw-row{ display:flex; gap:10px; align-items:flex-start; padding:10px;
  border:1px solid var(--line,#ececf2); border-radius:11px; background:var(--panel,#fff); }
.nw-row.on{ border-color:#3B5FE0; }
.nw-row-th{ flex:none; width:52px; height:36px; border-radius:7px; object-fit:cover;
  background:var(--hover,#f7f7fa); }
.nw-row-main{ flex:1; min-width:0; }
.nw-row-t{ font-size:13.5px; font-weight:600; color:var(--ink,#14161c);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.nw-row-s{ display:flex; align-items:center; gap:7px; margin-top:4px;
  font-size:12px; color:var(--muted,#8a9099); white-space:nowrap; }
.nw-state{ display:inline-block; padding:2px 8px; border-radius:99px; font-size:11px; font-weight:700; }
.nw-state.live{ background:#E7F6EE; color:#1E7B4D; }
.nw-state.draft{ background:var(--hover,#f1f1f6); color:var(--muted,#8a9099); }
.nw-row-btns{ display:flex; flex-direction:column; gap:4px; }
.nw-mini{ padding:4px 9px; border:none; border-radius:7px; background:transparent;
  font-size:12px; font-weight:600; color:var(--muted,#8a9099); cursor:pointer; }
.nw-mini:hover{ background:var(--hover,#f1f1f6); color:var(--ink,#14161c); }
.nw-mini.del:hover{ color:#C0392B; }

@media (max-width:900px){ .nw-admin{ flex-direction:column; } .nw-side{ flex:1 1 auto; width:100%; } }

.tf-premium{ display:flex; align-items:center; gap:7px; height:38px; padding:0 16px; border-radius:10px; background:var(--fg); color:var(--bg); font-size:13.5px; font-weight:600; transition:transform .15s, box-shadow .15s; }
.tf-premium:hover{ transform:translateY(-1px); box-shadow:0 6px 16px rgba(25,23,19,.25); }
.tf-premium svg{ color:var(--gold); }
.tf-avwrap{ position:relative; }
.tf-avwrap .tf-avatar{ width:38px; height:38px; border-radius:50%; background:#fff; display:grid; place-items:center; font-weight:700; font-size:14px; color:#000; border:1px solid #000; box-shadow:none; transition:box-shadow .15s; }
.tf-avwrap:hover .tf-avatar{ box-shadow:0 0 0 2px var(--acc); }
.tf-dd{ position:absolute; top:calc(100% + 10px); right:0; width:216px; background:var(--card); border:1px solid var(--bd); border-radius:14px; box-shadow:var(--float-shadow); padding:8px; opacity:0; visibility:hidden; transform:translateY(-6px); transition:all .18s; z-index:60; }
.tf-avwrap:hover .tf-dd{ opacity:1; visibility:visible; transform:translateY(0); }
.tf-dd-head{ padding:10px 12px; border-bottom:1px solid var(--bd); margin-bottom:6px; }
.tf-dd-head strong{ display:block; font-size:14px; }
.tf-dd-head span{ font-size:12px; color:var(--mut3); }
.tf-dd a{ display:block; padding:9px 12px; border-radius:8px; font-size:13.5px; color:var(--mut2); cursor:pointer; transition:background .12s; }
.tf-dd a:hover{ background:var(--bg); color:var(--fg); }

/* --- hero --- */
/* early-access notice above the hero */
/* The site notice: a full-width dark band across the very top, above the nav.
   It runs edge to edge and uses the nav's own side padding, so the tag lines up with the
   logo below it. The page is white, so this is the one dark thing on it — which is the
   point: it reads as a notice about the site rather than as part of the page. */
.tf-notice{ background:#111111; }
.tf-notice-in{ display:flex; align-items:center; gap:12px; padding:9px var(--edge); }
/* the green pill is unchanged — dark text on it, 9.2:1 */
.tf-notice-tag{ flex:none; font-size:11px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:#000; background:#fff; padding:4px 9px; border-radius:99px; }
.tf-notice p{ margin:0; font-size:13.5px; line-height:1.5; color:#FFFFFF; }
/* the email is the pill's green, not the old darker one. #5E8A00 was chosen to sit on a pale
   green card and is 4.60:1 on this band — over the line by a hundredth, which is no margin at
   all. The pill green is 14.4:1 here and ties the two together. */
.tf-notice a{ color:#fff; font-weight:600; text-decoration:underline; text-underline-offset:2px; }
@media (max-width:640px){ .tf-notice-in{ align-items:flex-start; flex-direction:column; gap:8px; } }
.tf-hero{ position:relative; text-align:center; padding:88px 24px 60px; overflow:hidden; }
.tf-hero::before{ display:none; }
.tf-hero-grid{ position:absolute; inset:0; background-image:linear-gradient(var(--bd) 1px,transparent 1px),linear-gradient(90deg,var(--bd) 1px,transparent 1px); background-size:56px 56px; opacity:.35; -webkit-mask-image:radial-gradient(ellipse 70% 80% at 50% 20%,black,transparent); mask-image:radial-gradient(ellipse 70% 80% at 50% 20%,black,transparent); pointer-events:none; }
.tf-hero-badge{ position:relative; display:inline-flex; align-items:center; gap:8px; font-size:13px; font-weight:600; letter-spacing:0; text-transform:none; color:var(--acc); background:var(--accsoft); border:none; border-radius:99px; padding:7px 16px; margin-bottom:26px; animation:tfRise .6s .05s both; }
/* BLACK, SEMI-BOLD, ON TWO LINES, at half the size it was first set to.
   The break is written into the sentence in index.html rather than left to the width of the
   window, so it falls in the same place — after "templates" — on every screen. white-space
   is no longer held to one line, or that break would be ignored. */
.tf-h1{ position:relative; font-size:clamp(22px,3.2vw,50px); line-height:1.04; font-weight:600;
  letter-spacing:-.03em; color:#000; max-width:none; margin:0 auto 26px;
  animation:tfRise .6s .15s both; }
.tf-h1 em{ font-family:inherit; font-style:normal; font-weight:600; color:#000; }
/* A PHONE NEEDS IT SMALLER THAN THE SCALE ALONE GIVES, or "Professional templates" wraps and
   the two lines become three. Measured at 390 pixels wide: 25px does not wrap. This is a floor
   for small screens, not a second opinion about the size — everything wider is still decided by
   the one setting above. */
@media (max-width:560px){ .tf-h1{ font-size:22px; } }
/* THE LINE UNDER THE HEADING, 19 October 2026. Mounir: "add bellow it in smaller text, something
   like: new templates will be added to the website regularly". It sits where the heading's own
   gap was, so the search row below does not move further down. */
.tf-h1:has(+ .tf-hero-note){ margin-bottom:12px; }
.tf-hero-note{ position:relative; font-size:17px; line-height:1.5; color:var(--mut2,#6b6b76); max-width:520px; margin:0 auto 26px; text-wrap:balance; animation:tfRise .6s .25s both; }
@media (max-width:560px){ .tf-hero-note{ font-size:15px; } }
.tf-sub{ position:relative; font-size:18px; color:var(--mut2); max-width:540px; margin:0 auto 38px; animation:tfRise .6s .25s both; }
/* NO OUTLINE. It is a white pill on an off-white page, which is edge enough. The border is
   gone rather than made transparent, so nothing is left holding a pixel and a half of width
   that has to be remembered when the height is next changed. */
/* EIGHT ON THE RIGHT, BECAUSE THE BAR IS 58 AND THE BUTTON IS 42. Sixteen pixels of the bar
   are left over, half above and half below, so eight is the gap the button already has top
   and bottom — and the same eight at the end makes it sit in a corner rather than pushed
   against one side. It was twenty, which read as a hole beside the circle. */
/* ===== THE HEADER'S SECOND ROW =====
   The search bar moved out of the hero and into the header on 26 August 2026, in the shape of
   Yellow Images' bar: the full width of the page, a light grey edge, and stuck to the header
   so it is still there when you are half way down a wall of templates. It is a row of the
   header rather than a thing of its own, so it inherits the header's stickiness — one sticky
   element, not two, which is the whole reason the header worked at all (see .tf-shell).
   It is NOT hidden on the pages that have no templates to search: the header carries it, and
   a control that comes and goes as you move around reads as a fault rather than as tidiness. */
.tf-searchrow{ background:var(--card); border-bottom:1px solid var(--bd); }
.tf-searchrow-in{ padding:12px var(--edge); display:flex; align-items:center; gap:12px; }
.tf-searchrow-in .tf-herosearch{ flex:1; min-width:0; }
/* Shaped like the rail's "All templates" pill, because it is the same kind of thing: a way in,
   not a filter on what is already showing. */
/* 8 October 2026: above the heading, with no outline and no hover change, at Mounir's word */
.tf .tf-filterbtn{ flex:none; display:inline-flex; align-items:center; gap:9px; height:36px;
  padding:0; margin:0 0 14px; border:none; border-radius:0; background:none;
  font-family:inherit; font-size:15px; font-weight:600; letter-spacing:-.01em; color:var(--fg);
  white-space:nowrap; cursor:pointer; }
.tf .tf-filterbtn.hidden{ display:none; }

/* ===== THE FILTER DRAWER =====
   Fixed to the window, starting under the header and running to the bottom. It slides rather
   than appearing, and the page is PUSHED by the same width — see the padding below. */
/* IT CARRIES class="tf" AS WELL, and that is not decoration: it sits outside every page, so
   without it none of the site's colours or its own width variable would reach it — the width
   came out as "whatever the words need", which was ten pixels narrower than the gap the page
   had been pushed by, leaving a stripe of page showing beside it. What .tf brings that it does
   NOT want is a full-screen minimum height, undone on the next line. */
.tf-drawer{ min-height:0;
  position:fixed; left:0; top:var(--headh); bottom:0; width:var(--drawerw); z-index:110;
  background:var(--card); border-right:1px solid var(--bd); overflow-y:auto;
  transform:translateX(-100%); transition:transform .24s ease; }
body.drawer-open .tf-drawer{ transform:translateX(0); }
.tf-drawer-in{ padding:18px 20px 40px; }
/* The heading over the industries. It was the browse sidebar's; that sidebar went on 26 August
   2026 and the drawer is the only place it appears now. */
.tf-filter-h{ font-size:11.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  color:var(--mut3); margin:0 0 8px; }
.tf-drawer-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:6px; }
.tf .tf-drawer-x{ width:30px; height:30px; border:none; border-radius:8px; background:transparent;
  color:var(--mut2); font-size:22px; line-height:1; cursor:pointer; }
.tf .tf-drawer-x:hover{ background:var(--bg); }
/* THE PAGE MOVES, THE HEADER DOES NOT — settled 26 August 2026. It pushed both at first, and
   Mounir asked for the header left alone: the logo, the search bar and the category rail are
   the site's own furniture, and a drawer that filters templates has no business shoving them
   sideways. The drawer starts below the header anyway, so nothing overlaps.
   Padding rather than a transform, so the wall reflows into the narrower space instead of
   sliding its right-hand edge off the screen. */
#home{ transition:padding-left .24s ease; }
body.drawer-open #home{ padding-left:var(--drawerw); }
/* Full width now, not a 640px pill in the middle of a hero. The animation went with the hero:
   a bar that slides in every time the page loads is a bar you wait for. */
/* GREY, AND NO EDGE — settled 26 August 2026, an hour after the grey edge went on. The row it
   sits in is white; a grey fill sets the bar apart from that row without drawing a line round
   it, which is the same trick the editor's own fields use. --bg is the site's pale grey, named
   once at the top of the palette. */
/* THE GAP ROUND THE GREEN BUTTON IS THE SAME ON ALL THREE SIDES — 26 August 2026. The bar is
   52 tall and the button 42, which leaves five above and five below; the bar's right padding
   was eight, so the button sat closer to the top and bottom than to the end of the bar and the
   circle looked pushed in. Five all round now, and the number is written as one variable so
   the three cannot drift apart the next time either height changes. */
.tf-herosearch{ --go-inset:5px;
  position:relative; max-width:none; margin:0; display:flex; align-items:center; height:52px;
  background:var(--bg); border:none; border-radius:99px;
  padding:0 var(--go-inset) 0 6px; color:var(--mut3); box-shadow:none; }
/* NO RING AT ALL WHEN THE BOX HAS THE CURSOR IN IT. It was the site's brand pink, which read
   as an orange halo round a green button; turned green on 25 August 2026, and taken away
   altogether an hour later. The bar is a white pill on an off-white page and the green button
   is already the loudest thing on it — a halo as well was one signal too many.
   The rule is gone rather than set to none, so nothing is left claiming a width that has to
   be remembered next time the bar changes. Keyboard users have not lost anything: the button
   inside the bar has its own outline when it is tabbed to. */
.tf-cat-btn{ display:flex; align-items:center; gap:6px; height:44px; padding:0 12px 0 18px; border-radius:99px; background:none; color:var(--fg); font-size:15px; font-weight:600; white-space:nowrap; cursor:pointer; }
.tf-cat-btn:hover{ background:var(--bg); }
.tf-cat-div{ width:1px; height:28px; background:var(--bd); margin:0 2px; flex:none; }
/* The magnifier that used to sit here was removed on 25 August 2026, along with the word
   "Search" in the box. The green button at the end of the bar says what this is instead. */
.tf-herosearch input{ flex:1; border:none; outline:none; background:none; font-size:16px; color:var(--fg); font-family:inherit; margin-left:12px; }
.tf-herosearch input::placeholder{ color:var(--mut3); }
.tf-cat-menu{ position:fixed; min-width:240px; max-height:70vh; overflow:auto; background:#ffffff; border:1px solid #D8D8E0; border-radius:14px; box-shadow:var(--float-shadow); padding:7px; z-index:200; text-align:left; }
.tf-cat-menu.hidden{ display:none; }
.tf-cat-item{ display:flex; align-items:center; justify-content:space-between; gap:10px; padding:10px 12px; border-radius:9px; font-size:14.5px; font-weight:500; color:#16161A; cursor:pointer; }
.tf-cat-item:hover{ background:#F3F3F6; }
.tf-cat-item .chk{ opacity:0; flex:none; }
/* THE CHOSEN LINE AND ITS TICK ARE BOTH THE EDITOR'S BLUE — 26 August 2026. The tick was the
   brand red and the word beside it stayed plain black, so the one line that was chosen looked
   like a line with a warning on it. Same blue as the industry list and the sort menu: one
   signal for "this is the one", used everywhere. Written out rather than taken from --brand
   or --acc, both of which are the red on this page. */
.tf-cat-item.on{ color:#3B5FE0; }
.tf-cat-item.on .chk{ opacity:1; color:#3B5FE0; }
/* THE BUTTON AT THE END OF THE SEARCH BAR, asked for 25 August 2026 (green until 25 October 2026).
 *
 * Written as button.tf-go, not .tf-go: a blanket rule near the top of this file styles every
 * button on the site, and a bare class does not outrank it — which is how the share button
 * came out invisible on 23 August.
 *
 * The arrow is BLACK on purpose. On a green this light, white would be about 1.5 to 1 against
 * it and effectively invisible; black is over 14 to 1. The colour is written out rather than
 * taken from a variable because it is not part of the theme — it is this one control. */
button.tf-go{ flex:none; width:calc(52px - 2*var(--go-inset)); height:calc(52px - 2*var(--go-inset)); margin-left:8px; display:flex; align-items:center;
  justify-content:center; border:none; border-radius:99px; background:#fff; color:#000; box-shadow:none; outline:none;
  cursor:pointer; transition:transform .12s; }
/* WHITE AND BLACK SINCE 25 OCTOBER 2026 (Mounir: "black arrow, white circle, no outline, animate. when
   hover, rotate to right"). It was a light green, the last of the lime. Under the pointer the arrow
   turns to face right, the way a "go" arrow points. */
button.tf-go svg{ transition:transform .3s cubic-bezier(.4,0,.2,1); }
button.tf-go:hover{ background:#fff; }
button.tf-go:hover svg{ transform:rotate(90deg); }
@media (prefers-reduced-motion:reduce){ button.tf-go svg{ transition:none; } }
button.tf-go:active{ transform:scale(.94); }
button.tf-go:focus-visible{ outline:2px solid #000; outline-offset:2px; }
.tf-searchbtn{ height:44px; padding:0 22px; border-radius:11px; background:var(--acc); color:#fff; font-size:14.5px; font-weight:600; }
/* --mut3 is the palest grey in the set and it is 2.56:1 on this background — fine for the
   tiny category label on a card, which is a whisper by design, and not fine for a line of
   running text. The hero says "Trending:" in it, so the hero uses the darker grey: 6.77:1.
   Changed here rather than in the palette, because the cards want the pale one. */
/* "VIEW ALL CATEGORIES" LIVES HERE NOW, at the end of the Trending line. It used to head the
   row of category tiles, which was taken out on 21 August.
   It is written more specifically than the rule above so it is not underlined like the
   trending words — those are examples you can pick, and this is a way out of the page. The
   arrow keeps it apart from them without needing a colour of its own. */

/* --- discovery / filters --- */
.tf-discovery{ padding:24px 0 0; }
.tf-disc-in{ max-width:1280px; margin:0 auto; padding:0 40px; }
.tf-seclabel{ display:flex; align-items:baseline; gap:14px; margin-bottom:18px; }
.tf-seclabel h2{ font-size:13px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--mut3); margin:0; }
.tf-rule{ flex:1; height:1px; background:var(--bd); }
.tf-chips{ display:flex; gap:10px; flex-wrap:wrap; }
.tf-chip{ display:flex; align-items:center; gap:9px; padding:8px 16px 8px 9px; border-radius:99px; background:var(--card); border:1px solid var(--bd); font-size:14px; font-weight:600; color:var(--mut2); box-shadow:0 1px 2px rgba(20,20,30,.03); transition:all .16s cubic-bezier(.2,.8,.3,1); }
.tf-chip .ico{ width:26px; height:26px; border-radius:50%; display:grid; place-items:center; font-size:13px; color:#191713; flex:none; }
.tf-chip .count{ font-size:12px; font-weight:600; color:var(--mut3); font-family:inherit; }
.tf-chip.active .count{ color:rgba(255,255,255,.7); }
.tf-chip:hover{ border-color:transparent; transform:translateY(-1px); box-shadow:0 6px 16px -8px rgba(20,20,30,.16); }
.tf-chip.active{ background:var(--acc); border-color:var(--acc); color:#fff; box-shadow:0 8px 20px -8px rgba(62,107,230,.5); }
.tf-subfilters{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:16px; padding:14px 16px; background:#fff; border:1px solid var(--bd); border-radius:12px; }
.tf-typelabel{ font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--mut3); margin-right:6px; }
.tf-apill{ padding:7px 15px; border-radius:99px; font-size:13.5px; font-weight:500; color:var(--mut2); background:var(--card); border:1px solid var(--bd); transition:all .15s; }
.tf-apill:hover{ border-color:var(--acc); color:var(--acc); }
.tf-apill.active{ background:var(--acc); border-color:var(--acc); color:#fff; }

/* --- grid --- */
/* FULL WIDTH, like the template rows and the category strip above it. It used to be a
   centred 1280 column, so on any screen wider than that the wall stopped short of the page
   while the nav and the category headings ran to the edges — three different left margins on
   one screen.
   The side padding is --edge, the same variable every bar and row uses, which is what keeps
   the "All Templates" heading lined up with the logo above it. */
.tf-gridwrap{ max-width:none; margin:0; padding:34px var(--edge) 80px; }
/* ===== THE CATEGORY RAIL =====
   One line of pills across the top of the browse page, in the shape of the mockup bar Mounir
   sent on 26 August 2026. It replaces a column of tick boxes down the left.
   The two arrows sit OVER the ends of the rail with a fade under them, so a pill that is half
   off the edge reads as "there is more" rather than as a pill that has been cut in half. */
/* IT STARTS WHERE THE TEMPLATES START, not where the page does — asked for 26 August 2026.
   Running to the left edge put the first pill above the filter column rather than above the
   wall it filters, so the two rows of the page began in two different places. The indent is
   the sidebar plus the gap beside it, read from the same variables the sidebar itself uses. */
/* THE HEADER'S THIRD ROW. Named "catband" rather than the obvious "catrow", because that other
   name belonged to the per-category carousel rows removed on 21 August 2026, and
   tools/test-skeleton.cjs watches the stylesheet to make sure it has not come back — a rule for
   a class nothing has reads as live code. Reusing the name would have turned that check green
   for the wrong reason and hidden the real one. (Which is exactly what happened when this was
   first written: the check went red, correctly.) Its own band, like the search row above it, so the rail sits on
   white and the fades at its ends have something to fade into. */
.tf-catband{ background:var(--card); border-bottom:1px solid var(--bd); }
.tf-catband.hidden{ display:none; }
.tf-catband-in{ padding:10px var(--edge); }
/* NO INDENT. The rail used to start past the browse page's filter column so that it lined up
   with the wall of templates beside it; that column and that page went on 26 August 2026, the
   industries moved into the drawer, and the wall runs the full width. The band's own side
   padding is --edge, the same as the wall's, so they line up with nothing further to do. */
.tf-catbar{ position:relative; display:flex; align-items:center; margin-bottom:0; }
.tf-catbar.hidden{ display:none; }
.tf-cb-rail{ display:flex; gap:10px; overflow-x:auto; scroll-behavior:smooth;
  scrollbar-width:none; -ms-overflow-style:none; padding:2px 0; flex:1; min-width:0; }
.tf-cb-rail::-webkit-scrollbar{ display:none; }   /* the arrows are the control, not a bar */
.tf .tf-pill{ flex:none; display:inline-flex; align-items:center; gap:9px; height:46px;
  padding:0 22px; border:1px solid transparent; border-radius:99px; background:var(--bg);
  font-family:inherit; font-size:15px; font-weight:600; letter-spacing:-.01em; color:var(--fg);
  white-space:nowrap; cursor:pointer; transition:background .12s, color .12s; }
.tf .tf-pill:hover{ background:var(--bd); }
/* The chosen one is filled black with white on it, the same as the admin tabs and the editor's
   selected buttons — one signal for "this is the one", used everywhere. */
.tf .tf-pill.on{ background:#171717; color:#FFFFFF; }
/* "All templates" is not a category; it is the way back out of one, so it keeps a white face
   and an edge whether or not it is the chosen one. */
.tf .tf-pill-all{ background:var(--card); border-color:var(--bd); }
.tf .tf-pill-all:hover{ background:var(--card); border-color:var(--bdh,#d8d8e0); }
.tf .tf-pill-all.on{ background:var(--card); color:var(--fg); border-color:var(--fg); }
.tf-pill svg{ flex:none; }
.tf .tf-cb-arrow{ position:absolute; top:50%; transform:translateY(-50%); z-index:2;
  width:38px; height:38px; display:grid; place-items:center; border-radius:50%;
  border:1px solid var(--bd); background:var(--card); color:var(--fg); cursor:pointer;
  box-shadow:0 2px 10px rgba(22,24,45,.10); }
.tf .tf-cb-arrow:hover{ background:var(--bg); }
.tf-cb-arrow.hidden{ display:none; }
.tf-cb-left{ left:0; }
.tf-cb-right{ right:0; }
/* The fade the arrow sits on. It has to ignore the pointer or it would swallow clicks meant
   for the pill underneath it. */
.tf-catbar::before,.tf-catbar::after{ content:""; position:absolute; top:0; bottom:0; width:78px;
  pointer-events:none; z-index:1; opacity:0; transition:opacity .12s; }
/* The fades read the row's own colour now that the rail sits on white rather than on the page. */
.tf-catbar::before{ left:0; background:linear-gradient(to right,var(--card) 30%,transparent); }
.tf-catbar::after{ right:0; background:linear-gradient(to left,var(--card) 30%,transparent); }
.tf-catbar:has(.tf-cb-left:not(.hidden))::before{ opacity:1; }
.tf-catbar:has(.tf-cb-right:not(.hidden))::after{ opacity:1; }
@media (max-width:820px){
  .tf .tf-pill{ height:40px; padding:0 16px; font-size:14px; }
  .tf .tf-cb-arrow{ width:32px; height:32px; }
}

.tf-gridhead{ display:flex; align-items:center; justify-content:space-between; margin-bottom:22px; }
.tf-h2{ font-size:24px; font-weight:700; letter-spacing:-.02em; margin:0; }
.tf-count{ font-size:13px; color:var(--mut3); margin:4px 0 0; }
/* The sort control and the little menu under it. It sat here as a dead button until
   26 August 2026; the menu is what makes it a control rather than a picture of one. */
.tf-sortwrap{ position:relative; flex:none; }
/* NO EDGE ON EITHER OF THEM — asked for 26 August 2026, and the picture Mounir sent showed a
   plain white card floating on its shadow. The button is a word with an arrow beside it; the
   menu is held up by its shadow, which is what tells you it is above the page. */
.tf .tf-sort{ display:flex; align-items:center; gap:8px; font-size:13.5px; color:var(--mut2);
  border:none; background:transparent; border-radius:9px; padding:8px 14px;
  font-family:inherit; cursor:pointer; }
.tf .tf-sort:hover{ background:var(--bg); }
.tf .tf-sort b{ font-weight:700; color:var(--fg); }
.tf-sort-pop{ position:absolute; right:0; top:calc(100% + 8px); z-index:40; min-width:172px;
  display:flex; flex-direction:column; gap:2px; padding:8px;
  background:var(--card); border:none; border-radius:14px;
  box-shadow:0 14px 34px -8px rgba(22,24,45,.22), 0 2px 6px rgba(22,24,45,.08); }
.tf-sort-pop.hidden{ display:none; }
.tf .tf-sort-opt{ text-align:left; padding:10px 12px; border:none; border-radius:9px;
  background:transparent; font-family:inherit; font-size:15px; font-weight:600; color:var(--fg);
  cursor:pointer; }
.tf .tf-sort-opt:hover{ background:var(--bg); }
/* The chosen line in the editor's blue, the same one the industry list uses — asked for
   26 August 2026. It read var(--acc), which on this page is the brand red. */
.tf .tf-sort-opt.on{ color:#3B5FE0; }
/* PINTEREST WALL. A grid puts the cards in rows, and a row is only as short as its tallest
   card — so a wide banner beside a tall poster leaves a band of empty page under the banner.
   Columns instead, each card going into whichever column is shortest, so they pack together.

   NOT css column-count, which would be one line instead of all this. column-count BALANCES:
   it splits the list into equal chunks, so the card beside the first is the one from the
   MIDDLE of the list, and a sorted gallery reads as shuffled. The graphics drawer hit exactly
   that and was moved off columns for it — see the note by #grGrid.

   --tf-cols is the only place the count is set. The script reads it from here rather than
   measuring the width itself, so the breakpoints below stay the single source of truth. */
.tf-grid{ display:flex; align-items:flex-start; gap:22px; --tf-cols:7; }
.tf-grid > .tf-col{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:26px; }
/* browse-all page: left category filter + results grid */
/* EDGE TO EDGE, 26 August 2026. This carried 40px of side padding of its own ON TOP of the
   --edge the wrapper around it already applies, so the all-categories page had eighty pixels
   of empty page down each side while the heading above it — which only gets --edge — started
   forty pixels further out. Two margins on one screen, and the wall of templates paying for
   both. The side padding comes from the wrapper now, like every other row on the site, so the
   sidebar lines up under the heading and the cards run to the edge. */
/* Sixteen pixels BELOW the header, not sixteen from the top of the window — the header sticks
   as of 26 August 2026, and a sidebar that stops at the top of the window slides underneath it. */
/* NO TICK BOXES, AND THE CHOSEN ONE IN THE EDITOR'S BLUE — asked for 26 August 2026, with a
   picture of Yellow Images' sidebar to match. The box is still in the page and still holds the
   answer, because everything that reads this filter reads the box; it is only taken off the
   screen. So there is no second copy of "which of these is on" to keep in step — the tick and
   the blue are the same fact, told twice, and the browser works out the second from the first.
   The blue is the editor's #3B5FE0, written out rather than taken from --accent: on this page
   --accent is the brand red, and Mounir asked for the editor's blue by name. */
.tf-fchk{ display:flex; align-items:center; gap:0; padding:5px 6px; border-radius:7px;
  font-size:15px; line-height:1.25; color:var(--fg); cursor:pointer; }
.tf-fchk:hover{ background:var(--bg); }
.tf-fchk input{ position:absolute; opacity:0; width:0; height:0; margin:0; pointer-events:none; }
.tf-fchk:has(input:checked){ color:#3B5FE0; }
.tf-fchk:has(input:checked) em{ color:#3B5FE0; }
.tf-fchk span{ flex:1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tf-fchk em{ font-style:normal; color:var(--mut3); font-size:12px; }
.tf-ind-head{ display:flex; align-items:center; gap:2px; }
.tf-ind-head .tf-ind-chk{ flex:1; min-width:0; }
.tf-ind-toggle{ flex:none; width:26px; height:26px; display:grid; place-items:center; border-radius:7px; color:var(--mut2); background:none; cursor:pointer; }
.tf-ind-toggle:hover{ background:var(--bg); }
.tf-ind-toggle svg{ transition:transform .15s; }
.tf-ind-toggle.open svg{ transform:rotate(180deg); }
.tf-subs{ padding-left:26px; margin:0 0 2px; }
.tf-ind{ margin-bottom:1px; }
.tf-subs.hidden{ display:none; }
.tf-subchk{ font-size:13px; color:var(--mut2); }
/* the same wall, with the filter sidebar beside it */
/* One step narrower than the page without the sidebar at each size, because the sidebar
   takes its width off the wall before the columns are worked out. */
/* ---- Kittl-style horizontal category rows ---- */
/* In a row every preview is the same HEIGHT and as wide as its own design needs, so a
   landscape banner and a portrait poster both appear whole rather than cropped to a square.
   The card therefore has no width of its own — it is as wide as the preview inside it. */
/* The card is given the preview's exact width, worked out from the row's height and the
   design's own shape. It is NOT left to size itself: a card that sizes itself takes the
   width of its widest content, and the widest content is the TITLE. "Build Your Strength"
   under a narrow poster made the card wider than the picture, and that extra width showed
   as a blank strip between one preview and the next. Width comes from the artwork here,
   and the words have to fit into it. */
/* so a long title is cut with an ellipsis instead of pushing the card wider */
/* the carousel cards used to square this off — they get the same rounding as the grid */
/* THE SIDEWAYS SCROLL ARROWS ARE GONE TOO. They only ever belonged to the category strip, and
   that went on 21 August — nothing on the page scrolls sideways any more. Kept "in case" they
   would be a set of rules matching nothing, which is how the Edit Template button's styling
   sat in this file describing something that had been taken out of the cards. */
/* Five across on a wide screen, stepping down so a card never gets too narrow to read.
   The count lives here and only here — the packer reads it rather than measuring the width
   itself, so these five lines are the whole story of how wide the wall is. */
/* SEVEN ACROSS, and one fewer at each step down.
 *
 * THE FIRST STEP WAS SET AT 1800 AND THAT WAS TOO HIGH. A window narrower than that dropped
 * straight to six, so asking for seven and getting six was not a stale file or a wrong number
 * — it was this line. Most desktop windows are somewhere between 1400 and 1800 wide, which is
 * exactly the range it was excluding. It now holds seven down to 1450, where a card is still
 * about 180 pixels across.
 *
 * These are window widths, not page widths, which is the other reason a threshold set by eye
 * misleads: the page has margins down both sides, so the wall is always narrower than the
 * number in the bracket.
 *
 * The count is set here and nowhere else — the script reads it from the stylesheet rather than
 * measuring, so these lines are the only place it lives. */
@media (max-width:1450px){ .tf-grid{ --tf-cols:6; } }
@media (max-width:1280px){ .tf-grid{ --tf-cols:5; } }
@media (max-width:1100px){ .tf-grid{ --tf-cols:4; } }
@media (max-width:940px){ .tf-grid{ --tf-cols:3; } }
@media (max-width:760px){ .tf-grid{ --tf-cols:2; } }
@media (max-width:520px){ .tf-grid{ --tf-cols:1; } }
/* No outline. The artwork is the card — a border round it competes with whatever the
   design's own edges are doing, and every template already has its own frame. */
/* THE CARD ITSELF IS NOT PAINTED — the title and category below the preview sit straight on
   the page. Transparent rather than the page's own colour written out again: if the page
   changes, this follows it, and there is no second copy of the value to fall out of step. The
   preview keeps its own white behind it, so a design with see-through parts still reads. */
.tf-card{ display:flex; flex-direction:column; gap:0; cursor:pointer; animation:tfCardIn .45s both; border:none; border-radius:14px; overflow:hidden; background:transparent; }
/* Rounded on all four corners, not just the top two. The card's own overflow:hidden only
   rounds the top, because the title sits below the image and the image's bottom edge is
   in the middle of the card rather than at its edge. */
/* In the GRID the columns set the width, so here the design's shape decides the height —
   the mirror image of the row, and for the same reason: the whole design, never a crop. */
.tf-card-img{ position:relative; aspect-ratio:var(--tf-ar,1); border-radius:14px; overflow:hidden; border:none; background:var(--card); box-shadow:none; }
/* No hover movement on the card itself. The Edit button and the overlay already appear on
   hover, which is acknowledgement enough — the card does not also need to jump. */
.tf-card-img .mini-mount{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; overflow:hidden; }
/* THE BUTTON SITS AT THE BOTTOM OF THE PREVIEW, not in the middle of it. In the middle it
   covers the very thing somebody is trying to look at. It is a hover affordance: the card has
   always been clickable, and this says so out loud and says what will happen. */
.tf-card-overlay{ position:absolute; inset:0; background:transparent; display:grid;
  place-items:end center; padding-bottom:14px; opacity:0; transition:opacity .09s ease-out;
  z-index:2; pointer-events:none; }
.tf-card:hover .tf-card-overlay{ opacity:1; }
/* WRITTEN AS button.tf-edit-btn, NOT .tf-edit-btn. Three hundred lines above there is a
   blanket `.tf button{ background:none; border:none; }`, which is more specific than a bare
   class (one class + one tag beats one class) and therefore won wherever the two disagreed.
   The white fill used to be forced back on with !important on a line of its own; a border and
   a drop shadow written here never appeared at all, and both had a test passing on them,
   because the tests read this rule instead of asking what the button actually looks like.
   Those two declarations are gone rather than forced: the shadow was also cancelled by the
   flat-interface rule near the end of this file, and neither has ever been on screen, so
   removing them changes nothing that anybody has seen. The prefix is what makes the rest of
   this rule land, so keep it. */
/* THIRTY PER CENT SMALLER, 26 August 2026. It was 44px tall with 22px of side padding and
   14.5px type, which on a card the size of a postcard read as a banner laid over the design
   rather than an invitation to open it. Every number here is the old one times 0.7 — the
   height, the padding, the type and the corner together, because shrinking the box alone
   leaves the same words in the same size in a tighter frame, which looks like a mistake. */
button.tf-edit-btn{ display:flex; align-items:center; justify-content:center; height:31px;
  padding:0 15px; border-radius:8px; background:#fff;
  color:var(--fg); font-size:10.2px; font-weight:700; cursor:pointer;
  transform:translateY(4px); transition:transform .12s ease-out; pointer-events:auto; }
/* clear of the preview dots, on the cards that have them */
.tf-card:hover .tf-edit-btn{ transform:translateY(0); }
/* resting state must look identical to the hover state — white pill from the moment it appears */
button.tf-edit-btn:hover{ background:#fff; }
/* THE HEART, top right of a template's picture.
   White and hidden until the card is pointed at, so a wall of templates is a wall of
   templates. Once it is on it is red and STAYS SHOWING whether the card is hovered or not —
   that is the whole job of it: telling you at a glance which ones you kept.
   button.tf-like, not .tf-like: three hundred lines above there is a blanket
   `.tf button{ background:none; border:none; color:inherit; }` which is more specific than a
   bare class and would take the colour straight back off. */
button.tf-like{ position:absolute; top:10px; right:10px; z-index:3;
  width:32px; height:32px; display:grid; place-items:center; border-radius:50%;
  padding:0; border:none; background:transparent; cursor:pointer;
  color:#fff; opacity:0; transform:translateY(-3px);
  transition:opacity .12s ease-out, transform .12s ease-out, color .12s; pointer-events:auto; }
/* NO SHADOW. It had a soft one so a white outline would show on a white photograph; asked for
   plainly on 21 August 2026, and taken off. The rest of this page is flat — the fill on hover
   is what makes the heart readable now. */
/* A thin edge in Templad's grey (the grey of "made simple.") so the white heart still reads on a
   white or pale template — Mounir, 25 October 2026. Once liked, the red needs no edge. */
button.tf-like svg{ fill:#fff; stroke:#9A9AA6; stroke-width:1.5px; transition:fill .15s cubic-bezier(.22,1,.36,1), stroke .15s; }
button.tf-like.on svg{ stroke:#F40051; }
.tf-card:hover button.tf-like{ opacity:1; transform:translateY(0); }
button.tf-like:hover{ transform:scale(1.12); }
/* POINTING AT THE HEART ITSELF FILLS IT IN, white, before anything is decided. An outline
   that only reacts by growing slightly leaves you unsure whether it is a button at all; a
   heart that fills says what pressing it will do. :not(.on) so that a heart already liked
   stays red under the cursor rather than flashing white on the way to being unliked. */
button.tf-like:not(.on):hover svg{ fill:#fff; }
button.tf-like:focus-visible{ opacity:1; outline:2px solid #fff; outline-offset:1px; }
/* liked: red, filled, and never hidden again */
button.tf-like.on{ opacity:1; transform:translateY(0); color:#F40051; }
button.tf-like.on svg{ fill:#F40051; }
.tf-card-badge{ position:absolute; top:12px; left:12px; font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; padding:4px 9px; border-radius:7px; z-index:3; }
.tf-b-featured{ background:var(--fg); color:var(--gold); }
/* THE CROWN, top left of a template's picture. Asked for 29 August 2026: a gold crown on a
   white circle, always visible.
   ALWAYS VISIBLE IS THE POINT, and it is what separates this from the heart on the opposite
   corner. The heart hides until the card is pointed at, so that a wall of templates reads as a
   wall of templates; this must be legible before anybody points at anything, because it is the
   difference between clicking a template and clicking a locked one.
   WHITE CIRCLE RATHER THAN A COLOURED PILL because it has to survive whatever is behind it —
   these sit on photographs, and a gold mark on a gold pizza is not a mark. The shadow is what
   holds the white off a white background.
   GOLD MEANS A PRO TEMPLATE; the pink --plan-pro means a Pro PERSON, in the admin Users list.
   Deliberately not the same: one is a thing you can buy, the other is somebody who has. */
.tf-pro{ position:absolute; top:12px; left:12px; z-index:3;
  width:30px; height:30px; display:grid; place-items:center; border-radius:50%;
  background:#fff; color:var(--gold,#C79A3B); pointer-events:none;
  box-shadow:0 1px 4px rgba(0,0,0,.28); }
.tf-pro svg{ width:18px; height:18px; display:block; }
.tf-card-meta{ display:flex; flex-direction:column; gap:2px; padding:9px 11px 11px; }
.tf-asset{ font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--mut3); margin:0; }
.tf-card-row{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
.tf-card-title{ font-size:14.5px; font-weight:600; letter-spacing:-.01em; margin:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tf-price{ font-size:13px; font-weight:700; font-family:var(--tmono); white-space:nowrap; }
.tf-price.tf-free{ color:#1B7A3D; }
.tf-rating{ display:flex; align-items:center; gap:5px; font-size:12.5px; color:var(--mut3); }
.tf-rating svg{ color:var(--gold); }
.tf-empty{ text-align:center; color:var(--mut3); padding:60px; font-size:15px; }

/* --- editor showcase --- */
.tf-h2b{ font-size:clamp(28px,3.4vw,42px); font-weight:800; letter-spacing:-.03em; line-height:1.1; max-width:520px; margin:0; }
.tf-dark-btn{ display:inline-flex; align-items:center; gap:8px; height:44px; padding:0 20px; border-radius:11px; background:var(--fg); color:var(--bg); font-size:14.5px; font-weight:600; transition:transform .15s, box-shadow .15s; }
.tf-dark-btn:hover{ transform:translateY(-1px); box-shadow:0 8px 20px rgba(25,23,19,.3); }

/* --- footer --- */
.tf-footer{ border-top:1px solid var(--bd); }
.tf-footer-in{ max-width:1280px; margin:0 auto; padding:44px 40px; display:flex; align-items:center; justify-content:space-between; gap:20px; flex-wrap:wrap; color:var(--mut3); font-size:13.5px; }
.tf-flinks{ display:flex; gap:24px; }
.tf-flinks a{ color:var(--mut2); cursor:pointer; }
.tf-flinks a:hover{ color:var(--fg); }
/* SOCIAL LINKS, right side of the footer (24 October 2026, Mounir). Round, the size of a fingertip, quiet until pointed at. */
.tf-fright{ display:flex; align-items:center; gap:22px; flex-wrap:wrap; }
.soc{ display:flex; align-items:center; gap:8px; }
.soc a{ width:38px; height:38px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; color:var(--mut2); background:transparent; border:1px solid var(--bd); transition:color .15s ease, background .15s ease, border-color .15s ease, transform .15s ease; }
.soc a:hover{ color:var(--fg); background:var(--card); border-color:transparent; transform:translateY(-1px); }
.soc a:focus-visible{ outline:2px solid var(--fg); outline-offset:2px; }
@media (max-width:760px){
  .tf-nav-in{ gap:14px; }   /* padding comes from --edge */
  .tf-navlinks{ display:none; }
  .tf-navright{ margin-left:auto; }
  .tf-disc-in{ padding-left:18px; padding-right:18px; }   /* .tf-gridwrap reads --edge, which shrinks on its own */
  /* THE HEADLINE HAS NO RULE OF ITS OWN HERE ANY MORE. It used to be pinned to 24px, which
     held it at its old size on anything narrower than this and quietly overruled the setting
     that decides it everywhere else. One rule for every width is easier to reason about than
     two that disagree. */
}
/* export dropdown */
.exp-wrap{ position:relative; }
.exp-btn{ display:flex; align-items:center; gap:7px; }
.exp-menu{ position:absolute; right:0; top:calc(100% + 6px); background:var(--panel); border:1px solid var(--line); border-radius:12px; box-shadow:var(--float-shadow); padding:6px; min-width:200px; z-index:60; display:flex; flex-direction:column; gap:2px; }
.exp-item{ display:flex; align-items:center; justify-content:space-between; text-align:left; background:transparent; border:none; padding:9px 12px; border-radius:8px; font-size:13px; font-weight:600; color:var(--ink); }
.exp-item:hover{ background:var(--soft); }
/* export size — a row of 1x / 2x / 3x under the three formats */
.exp-sep{ height:1px; margin:6px 4px; background:var(--line); }
.exp-scale{ display:flex; align-items:center; justify-content:space-between; gap:10px; padding:2px 10px 0; }
.exp-scale-l{ font-size:13px; color:var(--ink); }
.exp-scale-btns{ display:flex; gap:3px; }
.exp-sc{ min-width:32px; padding:4px 7px; border:1px solid var(--line); background:transparent;
  border-radius:7px; font-size:12px; font-weight:600; color:var(--muted); cursor:pointer; }
.exp-sc:hover{ background:var(--soft); color:var(--ink); }
.exp-sc.on{ background:var(--sel-bg,#3D95FF); border-color:var(--sel-bg,#3D95FF); color:var(--sel-fg,#fff); box-shadow:var(--sel-edge,none); }
.exp-scale-note{ margin:6px 10px 2px; font-size:11px; line-height:1.45; color:var(--muted);
  font-family:'JetBrains Mono',ui-monospace,monospace; }
.exp-scale-note.warn{ color:var(--danger,#B4443C); }
/* WHAT THE DOWNLOAD IS OF — 3 October 2026 (exportsel.js). The first line of the menu: the page, or
   the selection, so nobody downloads the one believing it is the other. */
.exp-scope{ margin:2px 6px 6px; padding:8px 10px; border-radius:9px; background:var(--field); font-size:12px;
  line-height:1.35; color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:260px; }
.exp-scope b{ color:var(--ink); font-weight:700; }
#expMenu.exp-for-selection .exp-scope{ box-shadow:inset 0 0 0 1.5px var(--accent); }
.exp-sep{ height:1px; background:var(--line); margin:5px 6px; }
.exp-tag{ font-family:'JetBrains Mono',ui-monospace,monospace; font-size:11px; color:var(--muted); font-weight:500; }
/* ---- Properties panel top: GONE, 11 September 2026 ----
   It held Share, Download, the design's name and the saving light, in that order of arrival,
   and all four moved into the editor's black bar across the top. What was left was a padded
   strip with a line under it and nothing inside, so the strip went too. Their rules are up
   beside .ed-bar now. The panel starts at its Properties heading. */
.share-menu{ position:absolute; right:0; top:48px; z-index:88; width:290px; padding:12px;
  background:var(--bg); border:1px solid var(--line); border-radius:12px;
  box-shadow:0 12px 34px rgba(16,16,24,.18); font-size:13px; }
.share-menu.hidden{ display:none; }
.share-menu .sh-h{ font-weight:700; color:var(--ink); margin:0 0 4px; }
.share-menu .sh-note{ margin:0 0 10px; font-size:11.5px; color:var(--muted); line-height:1.45; }
.share-menu .sh-row{ display:flex; gap:6px; align-items:center; }
.share-menu input.sh-link{ flex:1; height:32px; padding:0 8px; border:1px solid var(--line);
  border-radius:8px; background:var(--field,var(--bg)); color:var(--ink); font-size:12px; }
.share-menu button.sh-act{ height:32px; padding:0 12px; border:1px solid var(--line); border-radius:8px;
  background:var(--bg); color:var(--ink); cursor:pointer; font-size:12.5px; font-weight:600; }
/* --accent, NOT --fg: --fg exists only on the home page, so in the editor the browser
   threw the whole declaration away and the button painted transparent on a pale panel —
   invisible. Reported 23 Aug 2026 ("the Create a link button is not visible"), found by
   measuring the button's computed background on the live page. */
.share-menu button.sh-act.primary{ background:var(--accent); color:#fff; border-color:var(--accent); }
.share-menu button.sh-act:hover{ background:var(--hover); }
.share-menu button.sh-act.primary:hover{ opacity:.92; background:var(--accent); }
.share-menu button.sh-stop{ margin-top:10px; width:100%; }

/* PREVIEW: what someone holding a shared link sees. Everything that edits is gone; the
   design is on screen and nothing answers the pointer. Built on the same idea as present
   mode, kept separate because this one keeps the site header and its own bar. */
body.previewing .toolbar,
body.previewing #panelProps,
body.previewing #panelLayers,
body.previewing #panelPages,
body.previewing .float-island,
body.previewing .text-island,
body.previewing #selOverlay,
body.previewing .page-bar,
body.previewing .ed-top{ display:none !important; }
body.previewing .stage-wrap .layer{ pointer-events:none; }
body.previewing .page-name{ cursor:default; pointer-events:none; }
.pv-bar{ position:fixed; left:0; right:0; top:0; height:52px; z-index:70; display:flex;
  align-items:center; gap:12px; padding:0 18px; background:var(--bg); border-bottom:1px solid var(--line); }
.pv-bar .pv-name{ font-weight:700; color:var(--ink); font-size:14px; }
.pv-bar .pv-tag{ font-size:11.5px; color:var(--muted); }
.pv-bar .pv-sp{ flex:1; }
.pv-bar a.pv-cta{ height:34px; display:inline-flex; align-items:center; padding:0 14px; border-radius:8px;
  background:var(--accent); color:#fff; font-size:13px; font-weight:600; text-decoration:none; }
body.previewing .ed-body{ padding-top:52px; }
/* Download's own rules are up beside .ed-bar since 11 September 2026, when it moved out of
   this panel and into the black bar. The proportions came from a reference button Mounir sent
   on 23 August — about three times as wide as it is tall, the width left to the padding and
   the word rather than written down, so a longer label still gets the same breathing room.
   That still holds where it lives now; only the corners changed, to fully round. */
/* The design's name lived here until 11 September 2026, when it moved into the editor's black
   bar along with Share, Download and the saving light. Its rules went with it. */
/* top control bar — canvas size */
.canvasbar{ display:flex; align-items:center; justify-content:center; gap:12px; padding:8px 16px; background:var(--panel); border-bottom:1px solid var(--line); flex:none; }
.cs-wrap{ position:relative; }
.cs-btn{ display:flex; align-items:center; gap:8px; background:var(--field); border:1px solid var(--field-bd); border-radius:10px; padding:8px 12px; font-size:13px; color:var(--ink); }
.cs-btn b{ font-weight:700; }
.cs-btn:hover{ background:var(--hover); }
.cs-dims{ font-size:12px; color:var(--muted); font-family:'JetBrains Mono',ui-monospace,monospace; }
.cs-menu{ position:fixed; background:var(--panel); border:1px solid var(--line); border-radius:16px; box-shadow:var(--float-shadow); z-index:135; width:760px; max-width:92vw; display:flex; flex-direction:column; overflow:hidden; }
.csm-head{ flex:none; display:flex; align-items:center; gap:14px; padding:11px 14px 11px 18px; border-bottom:1px solid var(--line); }
/* Matches the Download button — same 14px. It was being dragged down to 12px by the blanket
   rule further down the file, which is why it read as a caption rather than a heading. Taking
   it out of that list is the fix; setting a size here alone would have lost to it. */
.csm-htitle{ font-size:14px; font-weight:700; color:var(--ink); flex:none; }
/* The box takes the space between the title and the close button, so the header stays one
   line at any panel width instead of the search either wrapping or squashing the title. */
.csm-search{ flex:1; min-width:0; position:relative; display:flex; align-items:center; }
.csm-search > svg{ position:absolute; left:11px; color:var(--muted); pointer-events:none; }
.csm-search input{ width:100%; height:32px; box-sizing:border-box;
  padding:0 12px 0 32px; border:1px solid var(--field-bd); border-radius:9px;
  background:var(--field); color:var(--ink); font:inherit; font-size:13px; outline:none;
  transition:border-color .15s, box-shadow .15s; }
.csm-search input::placeholder{ color:var(--muted); }
.csm-search input:hover{ border-color:var(--bdh); }
.csm-search input:focus{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
/* the browser's own clear cross is a different grey and a different size on every platform */
.csm-search input::-webkit-search-cancel-button{ -webkit-appearance:none; appearance:none; }
.csm-none{ margin:0; color:var(--muted); font-size:13px; line-height:1.5; max-width:46ch; }
.csm-body{ flex:1; min-height:0; display:grid; grid-template-columns:196px 1fr; overflow:hidden; }
.csm-close{ width:30px; height:30px; display:grid; place-items:center; background:transparent; border:none; border-radius:8px; color:var(--dim); cursor:pointer; transition:background .12s,color .12s; }
.csm-close:hover{ background:var(--hover); color:var(--ink); }
.cs-menu.hidden{ display:none; }
.cs-panel-btn{ display:flex; align-items:center; justify-content:space-between; gap:8px; width:100%; background:var(--field,#f4f4f6); border:1px solid var(--field-bd,#e2e2ea); border-radius:var(--ctl-r,4px); padding:9px 11px; font-size:13px; font-weight:600; color:var(--ink,#14161c); cursor:pointer; }
.cs-panel-btn:hover{ background:var(--hover,#eceef3); }
/* See-through black, so it darkens whatever theme it sits on. That is why it works here at
   all — but it is also why the light theme got a plain grey rather than anything of its own. */
.csm-side{ border-right:1px solid var(--line); padding:12px 10px; display:flex; flex-direction:column; gap:3px; background:rgba(0,0,0,.14); }
/* On the light theme it takes the same near-white as the editor canvas. Written as the
   variable, not the hex, so the two cannot drift apart. Dark mode keeps the rule above:
   a near-white block on a dark dialog would read as a hole rather than a sidebar. */
body.light .csm-side{ background:var(--canvas); }
.csm-cat{ display:flex; align-items:center; gap:11px; text-align:left; padding:9px 11px; background:transparent; border:none; border-radius:10px; font-size:13.5px; font-weight:600; color:var(--dim); transition:background .12s,color .12s; }
.csm-cat:hover{ background:var(--hover); color:var(--ink); }
.csm-cat.active{ background:var(--sel-bg); color:var(--sel-fg); box-shadow:var(--sel-edge); }
.csm-cico{ width:24px; height:24px; border-radius:7px; display:grid; place-items:center; flex:none; }
.csm-main{ padding:14px 16px 16px; max-height:520px; overflow-y:auto; }
.csm-h{ font-size:14px; font-weight:700; color:var(--ink); margin:4px 0 12px; }
.csm-chips{ display:flex; gap:6px; flex-wrap:wrap; margin-bottom:14px; }
.csm-chip{ display:flex; align-items:center; gap:7px; padding:6px 13px 6px 8px; border-radius:99px; font-size:12.5px; font-weight:600; color:var(--dim); background:var(--field); border:1px solid var(--field-bd); transition:all .12s; }
.csm-chip:hover{ color:var(--ink); border-color:var(--bdh); }
.csm-chip.active{ border-color:var(--sel-bg); color:var(--sel-fg); background:var(--sel-bg); box-shadow:var(--sel-edge); }
.csm-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:10px; }
/* The card is two bands: a tinted top holding the preview, and a plain bottom holding the
   words. The border is there whether or not the card is picked — it only changes colour —
   so nothing shifts by a pixel when you choose one. position:relative is what the tick in
   the corner is measured against. */
.csm-card{ position:relative; display:flex; flex-direction:column; align-items:stretch;
  gap:2px; text-align:left; padding:0 0 11px; border-radius:14px;
  border:1px solid var(--field-bd); background:var(--panel); overflow:hidden;
  box-shadow:0 1px 2px rgba(16,18,32,.05);
  transition:border-color .12s, box-shadow .12s, background .12s; }
.csm-card:hover{ border-color:var(--bdh); box-shadow:0 4px 14px -4px rgba(16,18,32,.16); }
/* Picked: the accent on the edge and a tick in the corner. Two signals, because the border
   alone is easy to miss at this size and colour alone is no use to anyone who cannot see it. */
.csm-card.active{ border-color:var(--accent); background:var(--panel);
  box-shadow:0 4px 16px -6px color-mix(in srgb, var(--accent) 45%, transparent); }
.csm-card.active .csm-vis{ background:var(--accent-soft); }
.csm-tick{ position:absolute; top:9px; right:9px; width:26px; height:26px; border-radius:50%;
  display:none; place-items:center; background:var(--accent); color:#fff;
  box-shadow:0 2px 6px rgba(16,18,32,.28); }
.csm-card.active .csm-tick{ display:grid; }
/* The tinted band. Its own rounding matches the card's, less the border, or the tint shows
   as a hard corner poking out past the curve. */
.csm-vis{ width:100%; aspect-ratio:128/84; background:var(--soft); border-radius:13px 13px 0 0;
  display:block; margin-bottom:9px; border-bottom:1px solid var(--field-bd);
  transition:background .12s; }
.csm-vis-svg{ width:100%; height:100%; display:block; }
.csm-name{ font-size:12.5px; font-weight:700; color:var(--ink); padding:0 11px; }
.csm-sub{ font-size:11px; color:var(--muted); font-family:'JetBrains Mono',ui-monospace,monospace; padding:0 11px; }
.csm-custom{ display:flex; align-items:flex-end; gap:10px; max-width:420px; }
.csm-custom .field{ flex:1; margin-bottom:0; }
.csm-custom .primary{ height:34px; }
/* text layouts flyout */
/* ---- Templates drawer ---- */
.tpl-menu{ position:absolute; left:41px; top:0; bottom:0; z-index:80; width:330px; display:flex; flex-direction:column; background:var(--panel,#fff); border:none; border-right:1px solid var(--line); box-shadow:14px 0 34px -20px rgba(0,0,0,.5); }
.tpl-menu.hidden{ display:none; }
.tpl-menu .tm-head{ gap:8px; }
.tm-back{ width:22px; height:22px; display:grid; place-items:center; background:transparent; border:none; border-radius:6px; color:var(--muted); cursor:pointer; flex:none; }
.tm-back:hover{ background:var(--hover); color:var(--ink); }
.tm-back.hidden{ display:none; }
.tpl-menu .tm-head span{ flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tpl-sec{ margin-bottom:18px; }
.tpl-sec-head{ display:flex; align-items:center; justify-content:space-between; gap:10px; margin:0 0 8px; }
.tpl-sec-head h4{ margin:0; font-size:12px; font-weight:700; color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tpl-viewall{ flex:none; background:none; border:none; font-size:11.5px; font-weight:600; color:var(--accent); cursor:pointer; }
.tpl-viewall:hover{ text-decoration:underline; }
.tpl-row{ display:flex; gap:8px; overflow-x:auto; padding-bottom:4px; scrollbar-width:none; }
.tpl-row::-webkit-scrollbar{ display:none; }
.tpl-row .tpl-card{ flex:0 0 108px; width:108px; }
.tpl-grid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
.tpl-card{ position:relative; padding:0; border:1px solid var(--field-bd); background:var(--field); border-radius:9px; overflow:hidden; cursor:pointer; aspect-ratio:4/5; display:block; transition:box-shadow .12s, transform .12s; }
.tpl-card:hover{ transform:translateY(-2px); box-shadow:0 0 0 2px var(--accent); }
.tpl-card img{ width:100%; height:100%; object-fit:cover; display:block; }
.tpl-card .mini-mount{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; overflow:hidden; background:#fff; }
/* ---- Photos drawer (shared gallery) ---- */
.photo-menu{ position:absolute; left:41px; top:0; bottom:0; z-index:80; width:340px; display:flex; flex-direction:column; background:var(--panel,#fff); border:none; border-right:1px solid var(--line); box-shadow:14px 0 34px -20px rgba(0,0,0,.5); }
.photo-menu.hidden{ display:none; }
.ph-top{ flex:none; padding:12px 12px 6px; border-bottom:1px solid var(--line); }
.ph-top .tm-addbox{ margin-bottom:8px; }
.ph-admin{ border-style:solid !important; }
.ph-search{ position:relative; margin-bottom:10px; }
.ph-search input{ box-sizing:border-box; width:100%; height:34px; border:1px solid var(--field-bd); background:transparent; border-radius:9px; padding:0 34px 0 12px; font-size:12px; font-family:inherit; color:var(--ink); outline:none; transition:border-color .15s; }
.ph-search input:hover{ border-color:var(--bdh); }
.ph-search input:focus{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
.ph-search svg{ position:absolute; right:11px; top:50%; transform:translateY(-50%); color:var(--muted); pointer-events:none; }
.ph-sec{ font-size:10px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); margin:2px 0 10px; }
.ph-grid{ column-count:2; column-gap:8px; }
/* the photo gallery, newest at the top: two columns the tiles are dealt into in turn (renderGallery) */
#phGrid.ph-cols{ column-count:initial; display:flex; gap:8px; align-items:flex-start; }
#phGrid .ph-col{ flex:1 1 0; min-width:0; }
.ph-item{ position:relative; display:block; width:100%; margin:0 0 8px; padding:0; border:none; background:var(--field); border-radius:10px; overflow:hidden; cursor:pointer; break-inside:avoid; line-height:0; transition:transform .12s, box-shadow .12s; }
.ph-item img{ width:100%; height:auto; display:block; }
.ph-item.ph-svg{ background:#fff; }
/* A TILE THE STORE WOULD NOT GIVE US, after four tries — 15 September 2026. It is left with no
   address at all, so nothing broken is drawn; this says why in the quietest way there is, and
   the tile still works: pressing it fetches the drawing again. */
.ph-item.gr-missing{ min-height:64px; border:1px dashed var(--line,#E3E4E8); }
.ph-item.gr-missing img{ display:none; }   /* the alt text stays for a screen reader, out of sight */
.ph-item.gr-missing::after{ content:"Tap to retry"; position:absolute; inset:0; display:flex;
  align-items:center; justify-content:center; font-size:11px; color:var(--mut2,#6B7280); }
.ph-item.ph-svg img{ height:120px; object-fit:contain; padding:14px; box-sizing:border-box; }
/* WAITING FOR A PICTURE — the photos drawer, 10 September 2026.
   A tile used to have no height at all until its picture arrived, so the newspaper columns
   re-stacked with every one that landed and the wall jumped under the cursor. A tile now
   holds a grey block of roughly the right shape from the start, with a soft light sweeping
   slowly across it, and the block goes the instant the picture is there. Nothing ever sits on
   top of the photo itself. A spinner in each tile was the other option and was turned down:
   twenty of them turning at once reads as an app in trouble, and they are still spinning at
   you when the wall is nearly done. */
.ph-item.ph-load{ aspect-ratio:4 / 3; background:var(--field); }
.ph-item.ph-load::after{ content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background-image:linear-gradient(100deg, transparent 22%, rgba(255,255,255,.5) 50%, transparent 78%);
  background-size:220% 100%; background-repeat:no-repeat; animation:phShimmer 1.5s linear infinite; }
body:not(.light) .ph-item.ph-load::after{ background-image:linear-gradient(100deg, transparent 22%, rgba(255,255,255,.13) 50%, transparent 78%); }
/* The blocks shown while the LIST is still being fetched are not photos yet, so they must not
   look pressable. */
.ph-skel{ cursor:default; }
.ph-skel:hover{ transform:none; box-shadow:none; }
@keyframes phShimmer{ from{ background-position:130% 0; } to{ background-position:-130% 0; } }
@media (prefers-reduced-motion: reduce){ .ph-item.ph-load::after{ animation:none; background-image:none; } }
.ph-item:hover{ transform:translateY(-2px); box-shadow:0 0 0 2px var(--accent); }
/* THE CROWN ON A GALLERY PHOTOGRAPH — 21 September 2026, when the whole gallery became a Pro
   thing. Smaller than the one on a template card because a tile in a drawer is a fraction of the
   size, and the same gold from the same place in the palette so the two read as one badge. */
.ph-pro{ position:absolute; top:6px; left:6px; z-index:3;
  width:22px; height:22px; display:grid; place-items:center; border-radius:50%;
  background:#fff; color:var(--gold,#C79A3B); pointer-events:none;
  box-shadow:0 1px 4px rgba(0,0,0,.28); }
.ph-pro svg{ width:14px; height:14px; display:block; }
/* A tile a free member cannot take. Faded rather than greyed out, so the photograph is still
   worth looking at — the point of showing it is that they want it. */
.ph-item.ph-locked img{ opacity:.55; }
.ph-item.ph-locked:hover img{ opacity:.75; }
.ph-del{ position:absolute; top:6px; right:6px; width:20px; height:20px; display:grid; place-items:center; border-radius:6px; background:rgba(0,0,0,.6); color:#fff; font-size:11px; line-height:1; opacity:0; transition:opacity .12s; }
.ph-item:hover .ph-del{ opacity:1; }
.ph-del:hover{ background:#D93B30; }
.ph-empty{ column-span:all; font-size:12px; color:var(--muted); text-align:center; padding:26px 10px; }
/* Graphics use a real grid rather than the shared .ph-grid newspaper columns.
   With column-count the list fills the whole left column before starting the right one,
   so the item beside the newest graphic is the one from the MIDDLE of the list — a
   correctly sorted gallery read as shuffled. A grid flows left-to-right, top-to-bottom,
   which is the order the list is actually in. Graphics are a fixed 120px tall, so there
   is no masonry to lose here (the photo gallery keeps its columns for that reason). */
#grGrid{ column-count:initial; display:grid; grid-template-columns:repeat(2,1fr); gap:8px; align-content:start; }
#grGrid .ph-item{ margin:0; }
#grGrid .ph-empty{ grid-column:1 / -1; }
/* THE UPLOADS DRAWER — 23 September 2026 (uploads.js). A real grid, for the reason above: the
   newest upload has to be the first tile, top left, and newspaper columns would put the one beside
   it from the middle of the list. Square tiles, so a tall photo and a wide one line up. */
#upGrid{ column-count:initial; display:grid; grid-template-columns:repeat(2,1fr); gap:8px; align-content:start; }
#upGrid .ph-item{ margin:0; aspect-ratio:1 / 1; }
#upGrid .ph-item img{ width:100%; height:100%; object-fit:cover; }
#upGrid .ph-empty{ grid-column:1 / -1; line-height:1.5; }
.up-retry{ margin-top:10px; height:34px; padding:0 16px; border-radius:10px; border:1px solid var(--field-bd);
  background:transparent; color:var(--ink); font:inherit; font-size:12px; font-weight:600; cursor:pointer; }
.up-retry:hover{ border-color:var(--bdh); }
/* The delete button on an upload: shown on hover, and always on a touch screen, which has no hover. */
.up-del{ position:absolute; top:6px; right:6px; z-index:3; width:28px; height:28px; display:grid; place-items:center;
  border-radius:8px; background:rgba(0,0,0,.62); color:#fff; line-height:0; opacity:0; transition:opacity .12s, background .12s; }
.up-item:hover .up-del, .up-del:focus-visible{ opacity:1; }
.up-del:hover{ background:#D93B30; }
@media (hover:none){ .up-del{ opacity:1; } }
.text-menu{ position:absolute; left:41px; top:0; bottom:0; z-index:80; width:326px; display:flex; flex-direction:column; background:var(--panel,#fff); border:none; border-right:1px solid var(--line); border-radius:0; box-shadow:14px 0 34px -20px rgba(0,0,0,.5); padding:0; }
.text-menu.hidden{ display:none; }
.tm-scroll{ flex:1; min-height:0; overflow-y:auto; padding:12px 10px; }
.tm-head{ flex:none; display:flex; align-items:center; justify-content:space-between; padding:7px 10px; font-size:12px; font-weight:700; color:var(--ink); border-bottom:1px solid var(--line); }
.tm-close{ width:22px; height:22px; display:grid; place-items:center; background:transparent; border:none; border-radius:6px; color:var(--muted); cursor:pointer; }
.tm-close:hover{ background:var(--hover); color:var(--ink); }
.tm-addrow{ display:flex; flex-direction:column; gap:8px; margin-bottom:10px; }
.tm-addrow .tm-addbox{ width:100%; margin-bottom:0; }
.tm-addrow .tm-addbox svg{ flex:none; }
.tm-addbox{ display:flex; align-items:center; gap:8px; width:100%; padding:10px; border:1px dashed #E0E0E0; border-radius:10px; background:transparent; font-size:13px; font-weight:600; color:var(--ink,#14161c); margin-bottom:10px; }
.tm-addbox:hover{ background:var(--soft,#f2f2fb); border-color:var(--accent,#5b5bf5); }
/* The admin-only "Save as text style". Solid rather than dashed, because the two above it are
   things to ADD to the page and this one takes something off it — and because a row of three
   identical dashed boxes reads as one control with three labels. */
.tm-addbox.tm-admin{ border-style:solid; border-color:var(--line,#ececf2); }
.tm-addbox.tm-admin:hover{ border-color:var(--accent,#5b5bf5); }
/* The remove cross on a saved style. It sits OVER the preview rather than inside it: drawing a
   preview replaces everything in that box.
   ALWAYS VISIBLE, unlike the one in the picture drawers. Mounir looked for it and could not find
   it: a control that only exists while the pointer is on top of the thing it belongs to is a
   control nobody knows is there. These are rare and deliberate — there is no wall of thumbnails
   here for it to clutter. */
.tm-item .ph-del{ top:10px; right:10px; z-index:2; opacity:1; }
/* the way back after hiding one of the app's own layouts, shown to an admin only */
.tm-restore{ display:block; width:100%; margin:10px 0 2px; padding:8px 10px; border-radius:8px;
  border:1px solid var(--line,#ececf2); background:transparent; color:var(--muted,#6e7280);
  font:inherit; font-size:11.5px; font-weight:600; cursor:pointer; }
.tm-restore:hover{ color:var(--ink,#14161c); border-color:var(--accent,#5b5bf5); }
/* a plain line when the library cannot be reached, shown to an admin only */
.tm-note{ margin:0 0 10px; padding:8px 10px; border-radius:8px; background:#FFF4E5; border:1px solid #F0C987;
  color:#7A4A00; font-size:11.5px; line-height:1.45; }
.tm-sec{ margin-bottom:10px; }
.tm-sec h4{ font-size:11px; text-transform:uppercase; letter-spacing:.6px; color:var(--muted,#6e7280); margin:6px 4px 8px; }
.tm-grid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
.tm-item{ position:relative; display:flex; flex-direction:column; gap:5px; background:transparent; border:none; padding:4px; border-radius:10px; cursor:pointer; }
.tm-item:hover{ background:var(--soft,#f2f2fb); }
.tm-thumb{ position:relative; width:100%; height:84px; border-radius:8px; overflow:hidden; background:#f4f4f6; border:1px solid var(--line,#ececf2); display:flex; align-items:center; justify-content:center; }
.tm-thumb .mini{ position:relative; transform-origin:center; }
.tm-name{ font-size:11.5px; color:var(--muted,#6e7280); text-align:left; padding-left:2px; }
/* text flyout — two-column vertical grid */
#textMenu{ width:326px; }
.tm-grid2{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }

/* ============ Flat interface — for things that sit IN the page ============
   Floating windows are no longer in this list. They used to be, with !important, and that
   is what made a shadow written on a menu simply not appear: this rule is hundreds of lines
   below the rules it was cancelling, and nothing in either place mentions the other. */
.toolbar,.toolbar:hover,.toolbar.expanded,.stage,body.light .stage,.shape-menu,
.pp-thumbwrap,.pp-reopen,.pj-more,
#authGate .ag-card,.text-menu,.toast,.sw-box,.img-replace,
.tf-premium,.tf-premium:hover,.tf-chip.active,.tf-edit-btn,.tf-card:hover .tf-card-img,.tf-dark-btn,
.tf-dark-btn:hover,.tf-avwrap:hover .tf-avatar,
#nodeEditor .nd-ctrl,#nodeEditor .nd-anchor,.handle{ box-shadow:none !important; }
/* keep small functional rings (selection indicators), no drop shadow */
.grad-stop{ box-shadow:0 0 0 1px rgba(0,0,0,.2) !important; }
.grad-stop.sel{ box-shadow:0 0 0 2px var(--accent) !important; }
.pp-item.active .pp-thumbwrap{ box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 40%,transparent) !important; }

/* ============================================================
   Max editor-UI font size = the "Canvas" heading (12px).
   No chrome text larger than that. (Font-preview list and the
   logo mark are graphics/samples, not UI copy, so excluded.)
============================================================ */
.ed-title,.file-btn,.ghost,.primary,.tlabel,.icon-btn,
.ctx-menu button,.pm-samebar,.fm-item,.pp-add,.chk-row,
.fxd-head,.fxd-x,.fxd-row,.fxd-off,.toast,.tpl-dlg-h,
.text-island .ti-font,.text-island .ti-fontbtn,.ti-size button,
.ti-size .ti-szval,.ti-btn,.ti-text,.ti-pop-h,.ti-pop-row input[type=text],
.overlay #overlayMsg,.tpl-f input,.tpl-f select,
.exp-item,.cs-btn,.cs-panel-btn,.csm-cat,.csm-h,.csm-chip,.csm-name,
.tm-head,.tm-addbox,.font-search input{ font-size:12px; }



/* ---------- Pages slide panel (#7, left rail) ---------- */
.pgp-list{ flex:1; overflow-y:auto; padding:10px; display:flex; flex-direction:column; gap:8px; }
.pgp-item{ position:relative; display:flex; align-items:center; gap:10px; padding:6px; border-radius:10px; border:2px solid transparent; cursor:pointer; user-select:none; }
.pgp-item:hover{ background:var(--hover); }
.pgp-item.cur{ border-color:var(--accent); }
.pgp-item.dragging{ opacity:.45; }
.pgp-item.drop-t{ box-shadow:0 -3px 0 0 var(--accent); }
.pgp-item.drop-b{ box-shadow:0 3px 0 0 var(--accent); }
.pgp-thumb{ flex:none; width:64px; height:52px; border-radius:6px; overflow:hidden; background:#fff; box-shadow:0 1px 4px rgba(0,0,0,.35); display:grid; place-items:center; }
.pgp-thumb img{ max-width:64px; max-height:52px; display:block; }
.pgp-ph{ width:64px; height:52px; background:linear-gradient(120deg,var(--field),var(--hover)); display:block; }
.pgp-meta{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.pgp-name{ font-size:12.5px; font-weight:600; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.pgp-item:not(.cur) .pgp-name{ color:var(--dim); }
.pgp-dims{ font-size:10.5px; color:var(--muted); font-family:'JetBrains Mono',ui-monospace,monospace; }
.pgp-more{ position:absolute; top:6px; right:6px; width:20px; height:20px; border:none; border-radius:6px; background:rgba(0,0,0,.45); color:#fff; font-size:12px; line-height:1; display:none; place-items:center; }
.pgp-item:hover .pgp-more{ display:grid; }
.pgp-foot{ flex:none; display:flex; gap:6px; padding:10px; border-top:1px solid var(--line); }
.pgp-foot .mini-btn{ flex:1; display:flex; align-items:center; justify-content:center; gap:6px; }

/* narrow, Adobe-Reader-style Pages panel */
#panelPages{ width:107px; }
#panelPages .pgp-list{ padding:6px; gap:8px; }
#panelPages .pgp-item{ flex-direction:column; align-items:center; gap:4px; padding:5px 5px 3px; }
#panelPages .pgp-thumb{ width:82px; height:64px; }
#panelPages .pgp-thumb img{ max-width:82px; max-height:64px; }
#panelPages .pgp-ph{ width:82px; height:64px; }
#panelPages .pgp-meta{ align-items:center; }
#panelPages .pgp-name{ font-size:10px; max-width:90px; }
#panelPages .pgp-dims{ display:none; }
#panelPages .pgp-foot{ flex-direction:column; padding:6px; }
#panelPages .pgp-foot .mini-btn{ font-size:11px; padding:6px 4px; }
/* contain, not cover. The box is already the design's shape, so the two normally agree —
   but a stored thumbnail made before a template was resized does not, and cover would
   silently crop it. contain shows the whole thing and lets the mismatch be visible. */
.tf-thumbimg{ position:absolute; inset:0; width:100%; height:100%; object-fit:contain; display:block; }
/* a template's moving card (tplanim.js, 20 October 2026): over the still picture, faded in once it plays */
.tf-card-img video.tf-anim{ position:absolute; inset:0; width:100%; height:100%; object-fit:contain; display:block; opacity:0; transition:opacity .25s ease; pointer-events:none; background:transparent; }
.tf-card-img video.tf-anim.on{ opacity:1; }
@media (prefers-reduced-motion: reduce){ .tf-card-img video.tf-anim{ display:none; } }

/* UPLOADED TEMPLATE PREVIEWS — stacked in one place, one visible at a time.
   They are stacked rather than laid in a row because the card's height is set by its aspect
   ratio: a row would need the whole strip to be sized and slid, and any preview a different
   shape from the first would change the card's height as it went past. */
.tf-pv{ opacity:0; transition:opacity .28s ease; pointer-events:none; }
.tf-pv.on{ opacity:1; }
@media (prefers-reduced-motion: reduce){ .tf-pv{ transition:none; } }

/* The dots sit BELOW the card's title since 23 August 2026, by request — a quiet grey row
   on the card itself, not white discs floating over the artwork. Same buttons, same wiring.
   EVERY RULE IS SCOPED `.tf-dots .tf-dot`, and the position is said out loud: the site
   header uses the same `.tf-dot` name for its notification dot, absolutely positioned in
   the top-right corner of an icon — and that rule was catching these, piling all the page
   dots into one navy dot on the card's corner (Mounir's screenshot, 23 August 2026). */
.tf-dots{ display:flex; gap:6px; justify-content:center; padding:0 11px 10px; }
.tf-dots .tf-dot{ position:relative; top:auto; right:auto;
  width:7px; height:7px; padding:0; border-radius:50%; border:0; cursor:pointer;
  background:rgba(0,0,0,.18);
  transition:background .2s ease, transform .2s ease; }
.tf-dots .tf-dot.on{ background:rgba(0,0,0,.62); transform:scale(1.25); }
/* a dot has to be easy to hit on a phone without being a large grey circle on screen */
.tf-dots .tf-dot::after{ content:""; position:absolute; width:22px; height:22px;
  left:50%; top:50%; transform:translate(-50%,-50%); }

/* The carousel arrows — added 23 August 2026 with the five-second slide. Hidden until the
   card is pointed at (a wall of arrows is noise, and a phone never hovers — the dots serve
   there); white discs floating mid-height at each side, the way Canva draws them. */
/* `button.tf-arr`, not `.tf-arr` — the blanket button rule outweighs a bare class and
   would repaint these grey (the suite's specificity guard is what caught it) */
button.tf-arr{ position:absolute; top:50%; transform:translateY(-50%); z-index:3;
  width:28px; height:28px; border-radius:50%; border:0; cursor:pointer;
  background:rgba(255,255,255,.92); color:#111; font-size:18px; line-height:1;
  display:flex; align-items:center; justify-content:center;
  box-shadow:0 1px 4px rgba(0,0,0,.25);
  opacity:0; pointer-events:none; transition:opacity .18s ease; }
button.tf-arr-prev{ left:8px; }
button.tf-arr-next{ right:8px; }
.tf-card:hover button.tf-arr,.tf-card:focus-within button.tf-arr{ opacity:1; pointer-events:auto; }
button.tf-arr:hover{ background:#fff; }
@media (prefers-reduced-motion: reduce){ button.tf-arr{ transition:none; } }

/* A placeholder card. Not clickable, and it does not play the entrance animation — that
   belongs to real content arriving, and a shimmering block that also slides in reads as the
   page being broken rather than as it loading. */
.tf-card.sk{ pointer-events:none; cursor:default; animation:none; }
/* The loading rows mirror the real ones, minus the parts that would be lying: no scroll
   arrows for a track that cannot scroll, and nothing clickable. */
.sk-shimmer,.sk-line{ position:relative; overflow:hidden; background:rgba(127,127,127,.12); border-radius:8px; }
.sk-line{ height:12px; margin-top:6px; }
.sk-shimmer::after,.sk-line::after{ content:""; position:absolute; inset:0; transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.22),transparent); animation:skmove 1.2s infinite; }
@keyframes skmove{ 100%{ transform:translateX(100%); } }

/* Flat by design — but only the things that are PART of the page.
 *
 * This list used to include every floating window as well, with !important, which beat
 * anything those windows declared for themselves. A shadow could be written on a menu, be
 * plainly there in the file, and never appear on screen. Nothing announced the conflict:
 * the rule is 600 lines below the rules it cancels.
 *
 * Docked drawers, cards, thumbnails, handles and pills stay flat: they sit IN the layout,
 * and a shadow on something that is not floating reads as a mistake. Windows that float
 * over the page are the opposite case, and they are no longer listed here. */
.tpl-menu,.photo-menu,.text-menu,.shape-menu,.tpl-card,.tpl-card:hover,
.ti-cbar,.ti-pop-row input[type=color],
.fpanel.pslide,.pp-thumbwrap,.pp-reopen,.pj-more,
#authGate .ag-card,.toast,.sw-box,.img-replace,.drag-hud,.rail-tip,
.ph-item,.ph-item:hover,.pj-check span,.stage,body.light .stage,
.tf-premium,.tf-premium:hover,.tf-edit-btn,.tf-card:hover .tf-card-img,
.tf-dark-btn,.tf-dark-btn:hover,.tf-avwrap:hover .tf-avatar,
#nodeEditor .nd-ctrl,#nodeEditor .nd-anchor,.handle,.tf-notice-in,.tf-chip.active
{ box-shadow:none !important; }

/* ===== Eyedropper loupe — the magnifier that follows the cursor =====
   pointer-events:none is essential: the loupe sits under the cursor, and without it the
   loupe itself would swallow the click meant for the canvas. */
.eye-loupe{ position:fixed; z-index:10000; pointer-events:none; display:flex; flex-direction:column; gap:0;
  background:#fff; border-radius:14px; overflow:hidden; box-shadow:0 0 0 1px rgba(0,0,0,.10), 0 10px 28px -8px rgba(0,0,0,.35); }
.eye-loupe.hidden{ display:none; }
/* the chequerboard shows through wherever the design is transparent */
.eye-loupe .eye-zoom{ display:block; border-radius:12px 12px 0 0;
  background-color:#fff;
  background-image:linear-gradient(45deg,#e9e9ee 25%,transparent 25%,transparent 75%,#e9e9ee 75%),
                   linear-gradient(45deg,#e9e9ee 25%,transparent 25%,transparent 75%,#e9e9ee 75%);
  background-size:12px 12px; background-position:0 0,6px 6px; }
.eye-read{ display:flex; align-items:center; gap:8px; padding:9px 11px; }
.eye-sw{ width:16px; height:16px; border-radius:5px; box-shadow:inset 0 0 0 1px rgba(0,0,0,.14); }
.eye-val{ font-size:12.5px; font-weight:600; letter-spacing:.02em; color:#16161a; font-variant-numeric:tabular-nums; }

/* ===== Error catcher badge — subtle, non-blocking, bottom-left ===== */
.err-badge{ position:fixed; left:14px; bottom:14px; z-index:9999; display:flex; align-items:center; gap:8px;
  padding:8px 10px; border-radius:10px; background:#2A2118; border:1px solid #6B4A16;
  box-shadow:0 10px 30px -12px rgba(0,0,0,.55); color:#FFE2B0; font-size:12.5px; max-width:min(92vw,520px); }
.err-badge.hidden{ display:none; }
.err-ico{ font-size:13px; line-height:1; }
.err-txt{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.err-btn{ flex:none; padding:4px 8px; border-radius:7px; cursor:pointer; font-size:12px;
  background:var(--brand); color:#fff; border:none; }
.err-btn:hover{ filter:brightness(1.08); }
.err-btn.ok{ background:#3E9B45; }
.err-x{ flex:none; background:none; border:none; color:#C9A97A; cursor:pointer; font-size:13px; padding:2px 4px; line-height:1; }
.err-x:hover{ color:#FFE2B0; }

/* auth: forgot-password link + reset-password panel */
.ag-link{ display:block; width:100%; margin-top:10px; background:none; border:none; cursor:pointer;
  font-size:13px; color:#6B7280; text-decoration:underline; text-underline-offset:2px; }
.ag-link:hover{ color:var(--acc,#F40628); }
#agNewPassWrap{ margin-top:12px; padding-top:12px; border-top:1px solid #E5E7EB; }

/* ===== autosave status — quiet when fine, loud only when work is at risk ===== */
.save-status{ display:none; align-items:center; gap:6px; font-size:11.5px; color:#8F8A7D; user-select:none; white-space:nowrap; }
.save-status[data-state="saving"], .save-status[data-state="saved"], .save-status[data-state="error"]{ display:flex; }
.ss-dot{ width:7px; height:7px; border-radius:50%; background:#8F8A7D; flex:none; }

/* HOW FULL A DESIGN IS GETTING — 19 September 2026.
   Shown only once a design passes about seventy percent of its ceiling, and never below, because a
   gauge that is on screen for everybody all the time is noise to the ninety-nine people who will
   never approach it. It sits beside the save mark because that is what it is about, and it is quiet
   on purpose: amber rather than red, because nothing is wrong yet and dressing a heads-up as a
   failure teaches people to ignore the failures. */
.ss-room{ flex:none; border:1px solid rgba(229,169,60,.45); background:rgba(229,169,60,.12);
  color:#B8863A; font:inherit; font-size:11px; line-height:1; padding:4px 8px; border-radius:999px;
  cursor:pointer; white-space:nowrap; transition:background .12s, border-color .12s; }
.ss-room:hover{ background:rgba(229,169,60,.22); border-color:rgba(229,169,60,.7); }
/* IT HAS TO SHOW EVEN WHEN THE SAVE MARK ITSELF IS IDLE, or it would blink away two and a half
   seconds after every save, which is most of the time.
   BY A CLASS RATHER THAN BY THE HIDDEN ATTRIBUTE. There is one global rule for hidden in this
   stylesheet and exactly one on purpose — eight one-off patches had accumulated before it, and a
   check guards against a ninth. This is that ninth if it is written the obvious way. */
.save-status.has-room{ display:flex; }
.save-status[data-state="saving"] .ss-dot{ background:#E5A93C; animation:ssPulse 1s ease-in-out infinite; }
.save-status[data-state="saved"]  .ss-dot{ background:#3E9B45; }
.save-status[data-state="error"]  .ss-dot{ background:#E5484D; animation:none; }
.save-status[data-state="saved"]{ color:#6F8A6B; }
.save-status[data-state="error"]{ color:#E5484D; font-weight:600; }
@keyframes ssPulse{ 0%,100%{ opacity:1 } 50%{ opacity:.35 } }
.ss-retry{ border:none; background:#E5484D; color:#fff; font-size:11px; font-weight:600;
  padding:2px 8px; border-radius:6px; cursor:pointer; margin-left:2px; }
.ss-retry:hover{ filter:brightness(1.1); }

/* Turnstile — collapses to nothing unless a challenge is actually shown */
/* The email fields live in a wrapper so they can be hidden as a group. The card is
   a flex column, so the wrapper has to repeat that layout — otherwise everything
   inside it collapses into one row and the inputs stop filling the width. */
#agEmailBlock{ display:flex; flex-direction:column; gap:10px; width:100%; }
/* THE FORM LAYS OUT LIKE THE BLOCK IT REPLACED — 31 August 2026.
   Wrapping the boxes in a form put a new element between them and the column that was
   arranging them, so they stopped stretching: the email box came out narrower than the
   password box and the Create account button shrank to fit its own text. It reads perfectly
   in the markup and is obvious the moment you look at the page. Same rules, one level down. */
#agForm{ display:flex; flex-direction:column; gap:10px; width:100%; }

/* Turnstile must never push the card wider than it is */
.ag-turnstile{ display:flex; justify-content:center; width:100%; max-width:100%; overflow:hidden; }
.ag-turnstile:empty{ display:none; }
.ag-turnstile iframe{ max-width:100% !important; }

/* ===== Hero banner image (Kittl-style rounded panel behind the search) =====
   The whole thing is opt-in via .tf-hero--img on the section, so removing that
   one class returns the hero to the plain light version. */
.tf-hero--img{ padding:26px 22px 44px; }
/* THE WHOLE PANEL IS OFF FOR NOW. The hero is the plain light one again.
   To turn it back on: put tf-hero--img back on the <section class="tf-hero"> in index.html.
   Everything below is written so that one class is the only switch — including this rule,
   which is scoped to the class rather than to .tf-herobg on its own. Unscoped, the coloured
   block kept drawing after the class came off, because the div is still in the markup.

   The photograph is off too. To put it back:
      background:#9A4A2B url("/assets/hero-bg.jpg") center/cover no-repeat;
   plus the scrim — but read this first. #B45A38 was the colour under the photo, and it gave
   white text 4.71:1: over the 4.5:1 minimum by a hair, and only because the dark scrim was
   carrying part of the load. Without the photo there is no scrim, so the flat colour has to
   hold every piece of text by itself, and #9A4A2B does — white 6.2:1, the peach 4.7:1. The
   lighter colour with no scrim puts the peach at 3.58:1, under the minimum. */
.tf-hero--img .tf-herobg{
  position:absolute; inset:14px 22px auto 22px; height:calc(100% - 40px);
  border-radius:26px; overflow:hidden; z-index:0;
  background:#9A4A2B;
}
.tf-hero--img .tf-h1,
.tf-hero--img .tf-sub{ position:relative; z-index:1; color:#fff; }
.tf-hero--img .tf-h1 em{ color:#FFD9C2; }
.tf-hero--img .tf-herosearch{ z-index:1; border-color:transparent; box-shadow:0 18px 40px -18px rgba(0,0,0,.55); }
.tf-hero--img .tf-hero-grid{ display:none; }
@media (max-width:640px){
  .tf-hero--img{ padding:16px 12px 34px; }
  .tf-hero--img .tf-herobg{ inset:10px 12px auto 12px; height:calc(100% - 28px); border-radius:18px; }
}

/* ===================== ABOUT =====================
   Built 26 August 2026 from Kittl's and Canva's about pages. Both do the same thing: one
   enormous sentence, then bands of alternating colour so the page has a rhythm you can feel
   while scrolling rather than one long column of prose.
   It borrows the home page's variables rather than inventing colours, so the two pages cannot
   drift apart the next time the brand colour moves — which it already has, twice. */
.ab{ padding:0 0 0; }
.ab-hero{ max-width:900px; margin:0 auto; padding:96px var(--edge,40px) 72px; text-align:center; }
/* THE PAGE NAMES ITSELF AGAIN. It was taken off on 26 August 2026 because his copy then did
   not have it; the copy he sent on 22 September opens with ABOUT TEMPLETTE, so it is back.
   Small, spaced out and quiet — a label above the heading, not a second heading. */
.ab-eyebrow{ margin:0 0 18px; font-size:13px; font-weight:600; letter-spacing:.14em;
  color:var(--mut3,#8A8A93); }
.ab-h1{ margin:0 0 26px; font-size:clamp(34px,5.4vw,68px); line-height:1.04; font-weight:600;
  letter-spacing:-.03em; color:var(--fg); }
.ab-lead{ max-width:660px; margin:0 auto 18px; font-size:18px; line-height:1.62; color:var(--mut2); }
.ab-lead:last-of-type{ margin-bottom:0; }
/* The three short sentences under the opening. Set apart from the paragraphs above rather than
   run on from them, because they are the answer to the question the reader is actually asking. */

/* The numbers. Four across on a wide screen, two on a narrow one — never three, which leaves
   one stranded on a line of its own. */
/* FOUR FACTS, TWO OF WHICH HAVE A NUMBER AND TWO OF WHICH DO NOT. They were big-number cards
   until 26 August 2026, when Mounir's own copy replaced them — and two of his four lines have no
   number in them at all. A row of number cards with two blanks in it looks broken, so they are
   one kind of thing now: a ticked line, with the number set large where there is one. */

.ab-fact{ background:var(--card); border:1px solid var(--bd); border-radius:16px;
  padding:28px 28px; font-size:21px; line-height:1.34; font-weight:600;
  letter-spacing:-.015em; color:var(--fg); }
.ab-fact b{ font-weight:600; }

/* A band of colour the full width of the window, with its words held to a column. */
.ab-band{ background:var(--card); border-top:1px solid var(--bd); border-bottom:1px solid var(--bd); }
/* Every other band drops back to the page colour, so seven sections of prose have a rhythm you
   can feel while scrolling instead of running together into one long sheet. */
.ab-band-plain{ background:transparent; border-color:transparent; }
.ab-sub{ margin:-12px 0 22px !important; font-size:18px !important; font-weight:600;
  color:var(--fg) !important; }
.ab-band-in{ max-width:720px; margin:0 auto; padding:84px var(--edge,40px); }
.ab-band-in p{ margin:0 0 18px; font-size:17px; line-height:1.72; color:var(--mut2); }
.ab-band-in p:last-child{ margin-bottom:0; }
.ab-h2{ margin:0 0 24px; font-size:clamp(26px,3.2vw,38px); line-height:1.12; font-weight:600;
  letter-spacing:-.025em; color:var(--fg); }
.ab-h2-c{ text-align:center; }

/* The dark band, so the page is not one shade from top to bottom. Its colours are written out
   rather than taken from the light variables around it — this is the one block that inverts. */
.ab-band-dark{ background:#101014; border-color:#101014; }
.ab-band-dark .ab-h2{ color:#fff; }
.ab-band-dark p{ color:#C9C7C3; }
.ab-band-dark .ab-quiet{ font-size:15px; color:#8D8A85; }
.ab-band-dark a{ color:#fff; text-decoration:underline; text-underline-offset:3px; }

.ab-vals{ max-width:1120px; margin:0 auto; padding:84px var(--edge,40px); }
.ab-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:20px; }
.ab-grid3{ display:grid; grid-template-columns:repeat(3,1fr); gap:20px; align-items:start; }
.ab-card p + p{ margin-top:12px; }
.ab-card{ background:var(--card); border:1px solid var(--bd); border-radius:18px; padding:30px 28px; }
.ab-card h3{ margin:0 0 12px; font-size:19px; font-weight:600; letter-spacing:-.01em; color:var(--fg); }
.ab-card p{ margin:0; font-size:15px; line-height:1.66; color:var(--mut2); }

.ab-cta{ text-align:center; padding:96px var(--edge,40px) 110px; }
.ab-cta-h{ margin:0 0 12px; font-size:clamp(30px,4.4vw,54px); line-height:1.06; font-weight:600;
  letter-spacing:-.03em; color:var(--fg); }
.ab-cta-p{ margin:0 0 30px; font-size:17px; color:var(--mut2); }
/* The line of explanation under the closing heading went with the copy Mounir sent on
   22 September 2026, so the gap it used to provide is put here instead rather than letting
   the button ride up under the heading. */
.ab-cta-row{ display:flex; gap:12px; justify-content:center; flex-wrap:wrap; margin-top:34px; }
/* WRITTEN AS .tf .ab-btn, NOT .ab-btn. There is a blanket `.tf button{ background:none; }`
   further up, and one class plus one tag outranks a bare class — so a plain .ab-btn rule loses
   to it and the red pill comes out as plain text on the page background. That is the same trap
   the Edit Template button fell into, found again here on 26 August 2026 by looking at a
   picture of the page rather than at the stylesheet. Keep the prefix. */
.tf .ab-btn{ display:inline-flex; align-items:center; justify-content:center; height:50px;
  padding:0 28px; border:1px solid transparent; border-radius:99px; background:var(--acc);
  color:#fff; font-family:inherit; font-size:15px; font-weight:600; text-decoration:none;
  cursor:pointer; transition:transform .12s, opacity .12s; }
.tf .ab-btn:hover{ transform:translateY(-1px); opacity:.92; }
.tf .ab-btn-ghost{ background:transparent; color:var(--fg); border-color:var(--bd); }
.tf .ab-btn-ghost:hover{ background:var(--card); }

/* ===================== PRICING — 12 September 2026 =====================================
 * Restyled the same evening to a picture Mounir sent ("use this graphic style, but keeping our
 * pricing and copy"): a small capitals label over the heading; a toggle whose pressed half is
 * a white pill; the Free card plain and the Pro card with a pink-to-violet edge and a "Most
 * popular" tag; black rounded buttons at the FOOT of the cards; grey ring ticks; a one-line
 * strip under the cards; and the questions as one boxed list. The two prices are both in the
 * page and the section's data-pz-term attribute picks one (yearly by default), so script
 * rewrites nothing. Every button is written .tf .pz-btn, for the same reason as .tf .ab-btn
 * above: the blanket `.tf button{ background:none }` outranks a bare class. */
.pz{ --pz-pink:#E0189A; --pz-violet:#7B3FE4; }
.pz-hero{ max-width:900px; margin:0 auto; padding:72px var(--edge,40px) 40px; text-align:center; }
.pz-eyebrow{ margin:0 0 18px; font-size:12px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--mut3); text-align:center; }
.pz-h1{ margin:0; font-size:clamp(36px,5.4vw,64px); line-height:1.02; font-weight:700;
  letter-spacing:-.035em; color:var(--fg); }
.pz-lead{ max-width:640px; margin:0 auto; font-size:19px; line-height:1.55; color:var(--mut2); }
/* THE SWITCH. One grey pill; the pressed half is a white pill with a soft shadow. Buttons with
   aria-pressed rather than a checkbox, so a screen reader says "Yearly, pressed". */
.pz-terms{ display:inline-flex; gap:0; margin-top:34px; padding:4px; border-radius:999px;
  background:#EEEEF2; border:1px solid var(--bd); }
.tf button.pz-term{ display:inline-flex; align-items:center; gap:8px; height:44px; padding:0 24px;
  border-radius:999px; border:none; background:transparent; color:var(--fg); font-family:inherit;
  font-size:15px; font-weight:600; cursor:pointer; transition:background .14s, box-shadow .14s; }
.tf button.pz-term[aria-pressed="true"]{ background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.10), 0 0 0 1px rgba(0,0,0,.04); }
.pz-save{ font-size:14px; font-weight:600; color:var(--pz-pink); }

/* THE TWO CARDS. Equal width; Pro carries the coloured edge, drawn as a gradient border. */
.pz-plans{ max-width:920px; margin:0 auto; padding:0 var(--edge,40px) 18px;
  display:grid; grid-template-columns:1fr 1fr; gap:28px; align-items:stretch; }
.pz-plan{ position:relative; background:#fff; border:1px solid var(--bd); border-radius:18px; padding:30px 28px 28px;
  display:flex; flex-direction:column; }
.pz-plan-pro{ border-color:transparent; background:
  linear-gradient(#fff,#fff) padding-box,
  linear-gradient(135deg, var(--pz-pink), var(--pz-violet)) border-box; }
.pz-popular{ position:absolute; top:22px; right:22px; padding:7px 14px; border-radius:999px;
  font-size:12px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  color:var(--pz-pink); background:color-mix(in srgb, var(--pz-pink) 10%, #fff); }
.pz-plan-name{ display:flex; align-items:center; gap:10px; font-size:26px; font-weight:700;
  letter-spacing:-.02em; color:var(--fg); }
.pz-crown{ display:inline-grid; place-items:center; width:28px; height:28px; border-radius:50%;
  background:#F4F4F6; color:var(--gold,#C79A3B); }
.pz-crown svg{ width:18px; height:18px; display:block; }
.pz-price{ display:flex; align-items:baseline; gap:10px; margin:12px 0 4px; }
.pz-amt{ font-size:60px; line-height:1; font-weight:700; letter-spacing:-.045em; color:var(--fg); }
.pz-per{ font-size:20px; font-weight:600; color:var(--mut2); }
.pz-under{ font-size:15px; color:var(--mut3); }
.pz-plan-p{ margin:22px 0 0; padding-top:22px; border-top:1px solid var(--bd); font-size:15.5px; line-height:1.55; color:var(--fg); }
.pz-list{ list-style:none; margin:18px 0 0; padding:0; display:flex; flex-direction:column; gap:14px; flex:1; }
.pz-list li{ position:relative; padding-left:36px; font-size:15px; line-height:1.5; color:var(--fg); }
/* the tick: a bare check in the editor's lighter blue (#3D95FF, the panel accent), no ring — the swatch Mounir sent — Mounir's word, 12 September 2026 */
.pz-list li{ padding-left:30px; }
.pz-list li::before{ content:""; position:absolute; left:2px; top:5px; width:11px; height:6px;
  border-left:2.5px solid #3D95FF; border-bottom:2.5px solid #3D95FF; transform:rotate(-45deg);
  border-radius:1px; }

/* WHICH PRICE. Both are in the page; the section's attribute picks one. */
#pricing .pz-price-year{ display:none; }
#pricing[data-pz-term="year"] .pz-price.pz-price-year{ display:flex; }
#pricing[data-pz-term="year"] .pz-under.pz-price-year{ display:block; }
#pricing[data-pz-term="year"] .pz-price-month{ display:none; }
#pricing .pz-if-year{ display:none; }
#pricing[data-pz-term="year"] .pz-if-year{ display:inline; }
#pricing[data-pz-term="year"] .pz-if-month{ display:none; }

/* THE BUTTONS sit at the foot of each card, full width, rounded rectangles. */
.tf .pz-btn{ display:inline-flex; align-items:center; justify-content:center; height:54px; padding:0 26px;
  margin-top:28px; width:100%; border:1.5px solid transparent; border-radius:14px; font-family:inherit; font-size:16px; font-weight:700;
  cursor:pointer; transition:transform .12s, background .14s, border-color .14s; }
.tf .pz-btn:active{ transform:scale(.99); }
.tf .pz-btn-ghost{ background:#fff; color:var(--fg); border-color:#C9C9D2; }
.tf .pz-btn-ghost:hover{ border-color:var(--fg); }
.tf .pz-btn-pro{ background:#0D0D0C; color:#fff; }
.tf .pz-btn-pro:hover{ background:#262625; }
.pz-strip{ max-width:920px; margin:0 auto; padding:14px var(--edge,40px) 84px; text-align:center;
  font-size:14px; color:var(--mut2); }
.pz-strip span + span::before{ content:"•"; margin:0 14px; color:var(--mut3); }

/* THE TABLE. Plain, wide, and the group rows are the only thing in bold. */
.pz-table-wrap{ max-width:920px; margin:0 auto; padding:0 var(--edge,40px) 84px; }
.pz-h2{ margin:0 0 26px; font-size:clamp(28px,3.6vw,40px); line-height:1.1; font-weight:700;
  letter-spacing:-.03em; color:var(--fg); text-align:center; }
.pz-table{ width:100%; border-collapse:separate; border-spacing:0; background:#fff;
  border:1px solid var(--bd); border-radius:16px; overflow:hidden; font-size:15px; }
.pz-table th, .pz-table td{ padding:14px 18px; text-align:left; border-bottom:1px solid var(--bd); color:var(--fg); }
.pz-table thead th{ font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--mut2);
  background:#F7F7FA; }
.pz-table thead th:nth-child(n+2), .pz-table td:nth-child(n+2){ text-align:center; width:150px; }
.pz-table tbody tr:last-child td{ border-bottom:none; }
.pz-table .pz-th-group{ padding-top:22px; font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--mut2); }
.pz-table .pz-yes{ color:var(--fg); }
.pz-table .pz-no{ color:var(--mut3); }

/* HOW PAYING WORKS. Three cards. */
.pz-pay{ background:#fff; border-top:1px solid var(--bd); border-bottom:1px solid var(--bd); }
.pz-pay .pz-h2{ padding-top:76px; }
.pz-pay-grid{ max-width:1120px; margin:0 auto; padding:0 var(--edge,40px) 76px;
  display:grid; grid-template-columns:repeat(3,1fr); gap:20px; }
.pz-pay-card{ background:var(--bg); border:1px solid var(--bd); border-radius:16px; padding:28px 26px; }
.pz-pay-card h3{ margin:0 0 10px; font-size:18px; font-weight:700; letter-spacing:-.01em; color:var(--fg); }
.pz-pay-card p{ margin:0; font-size:15px; line-height:1.66; color:var(--mut2); }

/* QUESTIONS. One white box, a row per question, a chevron on the right. Native details. */
.pz-faq{ max-width:920px; margin:0 auto; padding:84px var(--edge,40px) 110px; }
.pz-qs{ background:#fff; border:1px solid var(--bd); border-radius:16px; overflow:hidden; }
.pz-q{ border-bottom:1px solid var(--bd); }
.pz-q:last-child{ border-bottom:none; }
.pz-q summary{ list-style:none; cursor:pointer; padding:22px 60px 22px 24px; position:relative;
  font-size:17px; font-weight:600; color:var(--fg); }
.pz-q summary::-webkit-details-marker{ display:none; }
.pz-q summary::after{ content:""; position:absolute; right:28px; top:50%; width:9px; height:9px;
  border-right:2px solid var(--fg); border-bottom:2px solid var(--fg);
  transform:translateY(-70%) rotate(45deg); transition:transform .16s; }
.pz-q[open] summary::after{ transform:translateY(-30%) rotate(225deg); }
.pz-q p{ margin:-6px 0 22px; padding:0 24px; font-size:15.5px; line-height:1.7; color:var(--mut2); }
.pz-q a{ color:var(--fg); text-decoration:underline; text-underline-offset:3px; }

/* The word in the header, beside Start creating. */
.tf-navlinks{ align-items:center; }
.tf-navlinks a.tf-navlink{ color:var(--tf-ink,#0D0D0C); text-decoration:none; font-weight:600; }
.tf-navlinks a.tf-navlink:hover{ color:var(--mut2); }

@media (max-width:820px){
  .pz-plans{ grid-template-columns:1fr; }
  .pz-pay-grid{ grid-template-columns:1fr; }
  .pz-table thead th:nth-child(n+2), .pz-table td:nth-child(n+2){ width:96px; padding-left:8px; padding-right:8px; }
  .pz-strip span{ display:block; }
  .pz-strip span + span::before{ content:none; }
}

/* =================================================================================================
   THE LANDING PAGE — 13 September 2026
   =================================================================================================
   The front door for visitors, built to the shape of mockuuups.studio at Mounir's word and in the
   site's own type and colours. One grey (--bg) for every card, 32px corners on the big blocks and
   20 on the small, 96px between sections. Motion is quiet and all of it is optional: a soft rise as
   things come into view, two rows of templates gliding past, the app showcase stepping through
   itself, and nothing at all for anybody who has asked their system for less motion. */
/* the search row under the header belongs to the gallery; the landing page has its own way in */
body.on-landing .tf-searchrow, body.on-landing #catBand{ display:none; }
/* AS WIDE AS THE REFERENCE, 13 September 2026 ("make the page elements wider"): mockuuups.studio runs
   its blocks 1368 wide on a 1440 screen, 36 in from each edge. So the blocks here fill the window to
   32px from each side, up to 1600 on a very wide screen, rather than sitting in a 1240 column. */
.ld{ --ld-card:var(--bg,#F5F5F7); --ld-ink:var(--fg,#16161A); --ld-dark:#0D0D0C; --ld-max:1600px; --ld-edge:32px; overflow-x:hidden; }
.ld-sec{ max-width:calc(var(--ld-max) + 2 * var(--ld-edge)); margin:0 auto; padding:96px var(--ld-edge) 0; }
.ld-sec-head{ text-align:center; max-width:760px; margin:0 auto 44px; }
.ld-eyebrow{ margin:0 0 16px; font-size:12px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--mut3); }
.ld-h1{ margin:0; font-size:clamp(40px,6vw,76px); line-height:1; font-weight:700; letter-spacing:-.04em; color:var(--ld-ink); }
.ld-h2{ margin:0; font-size:clamp(28px,3.4vw,44px); line-height:1.1; font-weight:700; letter-spacing:-.03em; color:var(--ld-ink); }
.ld-h3{ margin:0 0 10px; font-size:19px; line-height:1.25; font-weight:700; letter-spacing:-.015em; color:var(--ld-ink); }
.ld-p{ margin:0; font-size:16.5px; line-height:1.6; color:var(--mut2); }
.ld-sec-head .ld-p{ margin-top:16px; font-size:18px; }
.ld-lead{ max-width:720px; margin:24px auto 0; font-size:clamp(17px,1.7vw,22px); line-height:1.5; color:var(--mut2); }

/* ---- the reveal: things rise 18px as they arrive; nothing moves for reduced motion ---- */
.ld-rv{ opacity:0; transform:translateY(18px); transition:opacity .6s ease, transform .6s ease; }
.ld-rv.in{ opacity:1; transform:none; }
@media (prefers-reduced-motion:reduce){ .ld-rv{ opacity:1; transform:none; transition:none; } }

/* ---- hero ---- */
.ld-hero{ max-width:980px; margin:0 auto; padding:72px var(--ld-edge) 40px; text-align:center; }
.ld-cta{ display:flex; justify-content:center; gap:12px; flex-wrap:wrap; margin-top:34px; }
/* the reassurance under the two buttons — 19 September 2026, with the new words */
.ld-note{ margin:16px 0 0; font-size:14px; line-height:1.5; color:var(--mut3); }
.tf button.ld-btn{ display:inline-flex; align-items:center; justify-content:center; height:54px; padding:0 26px; border:none; border-radius:14px;
  font-family:inherit; font-size:16px; font-weight:700; cursor:pointer; transition:transform .12s, background .14s, box-shadow .14s; }
.tf button.ld-btn:active{ transform:scale(.99); }
.tf button.ld-btn-dark{ background:var(--ld-dark); color:#fff; }
.tf button.ld-btn-dark:hover{ background:#262625; }
.tf button.ld-btn-light{ background:#fff; color:var(--ld-ink); box-shadow:inset 0 0 0 1.5px var(--bd); }
.tf button.ld-btn-light:hover{ background:var(--ld-card); }
/* in the dark band the button is lime, like the avatar and the step numbers — Mounir, 13 September 2026 */
.tf .ld-end button.ld-btn-light{ background:#fff; color:#000; box-shadow:none; }
.tf .ld-end button.ld-btn-light:hover{ background:#fff; }
.ld-shot{ max-width:calc(var(--ld-max) + 2 * var(--ld-edge)); margin:0 auto; padding:16px var(--ld-edge) 0; }
.ld-shot-wrap{ background:var(--ld-card); border-radius:32px; padding:56px 56px 0; overflow:hidden; }
.ld-shot-wrap img{ display:block; width:100%; height:auto; border-radius:18px 18px 0 0;
  box-shadow:0 30px 60px -20px rgba(22,24,45,.28), 0 0 0 1px rgba(22,24,45,.06); }

/* ---- templates, moving ---- */
.ld-marq{ display:grid; gap:18px; margin:0 calc(-1 * var(--ld-edge)); padding:4px 0; }
.ld-marq-row{ overflow:hidden; -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent); mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent); }
.ld-marq-track{ display:flex; gap:18px; width:max-content; animation:ld-glide 60s linear infinite; }
.ld-marq-rev .ld-marq-track{ animation-direction:reverse; }
.ld-marq-row:hover .ld-marq-track{ animation-play-state:paused; }
@keyframes ld-glide{ to{ transform:translateX(-50%); } }
@media (prefers-reduced-motion:reduce){ .ld-marq-track{ animation:none; } }
.tf button.ld-tile{ flex:none; width:232px; height:232px; padding:0; border:none; border-radius:18px; overflow:hidden; background:#fff; cursor:pointer; position:relative;
  box-shadow:0 1px 2px rgba(22,24,45,.06), 0 12px 30px -16px rgba(22,24,45,.22); transition:transform .25s ease, box-shadow .25s ease; }
.tf button.ld-tile:hover{ transform:translateY(-4px); box-shadow:0 1px 2px rgba(22,24,45,.06), 0 22px 40px -18px rgba(22,24,45,.3); }
/* cropped from the top, not the middle: a tall template's face and headline are at the top, and a
   square window on its middle showed torsos (Mounir, 13 September 2026) */
.ld-tile img, .ld-tile .mini-mount{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:top center; }
.ld-tile .mini-mount{ background:var(--ld-card); }
.ld-tile-cap{ position:absolute; left:0; right:0; bottom:0; padding:26px 12px 10px; text-align:left; font-size:12.5px; font-weight:600; color:#fff;
  background:linear-gradient(transparent, rgba(0,0,0,.55)); opacity:0; transition:opacity .2s; }
.ld-tile:hover .ld-tile-cap{ opacity:1; }
.ld-wide{ margin-top:28px; }
/* The apps section got one of these on 19 September 2026 and it is a link rather than a button,
   because it goes to a page. Both wear the same rule so the two read as the same thing. */
.tf button.ld-btn-wide, .tf a.ld-btn-wide{ display:flex; align-items:center; justify-content:center;
  width:100%; height:128px; border:none; border-radius:18px; background:var(--ld-dark); color:#fff;
  font:inherit; font-size:17px; font-weight:700; text-decoration:none; cursor:pointer; transition:background .15s; }
.tf button.ld-btn-wide:hover, .tf a.ld-btn-wide:hover{ background:#262625; color:#fff; }

/* ---- three panels ---- */
.ld-three{ display:grid; grid-template-columns:repeat(3,1fr); gap:20px; }
.ld-panel{ background:var(--ld-card); border-radius:24px; padding:36px 32px 40px; transition:transform .25s ease; }
.ld-panel:hover{ transform:translateY(-3px); }
/* the step numbers are black with white numbers since 25 October 2026, when lime left the site. They were lime, and are
   round — Mounir, 13 September 2026: "use this color on steps icons, make them rounds" */
.ld-num{ width:44px; height:44px; border-radius:50%; display:grid; place-items:center; margin-bottom:22px; background:#000; color:#fff; font-size:17px; font-weight:700; }

/* ---- the app showcase ---- */
.ld-show{ background:var(--ld-card); border-radius:32px; padding:28px 28px 40px; }
.ld-show-tabs{ display:flex; gap:6px; flex-wrap:wrap; justify-content:center; margin-bottom:28px; }
.tf button.ld-show-tab{ position:relative; display:inline-flex; align-items:center; gap:9px; height:44px; padding:0 16px 0 12px; border:none; border-radius:12px; overflow:hidden;
  background:transparent; color:var(--mut2); font:inherit; font-size:14px; font-weight:600; cursor:pointer; transition:background .15s, color .15s; }
.tf button.ld-show-tab img{ width:22px; height:22px; border-radius:6px; }
.tf button.ld-show-tab:hover{ color:var(--ld-ink); }
.tf button.ld-show-tab.on{ background:#fff; color:var(--ld-ink); box-shadow:0 1px 2px rgba(22,24,45,.08); }
.ld-show-bar{ position:absolute; left:0; bottom:0; height:2px; width:100%; background:var(--ld-ink); transform:scaleX(0); transform-origin:left; opacity:.7; }
.ld-show-tab.on.run .ld-show-bar{ animation:ld-fill var(--ld-step,6s) linear forwards; }
@keyframes ld-fill{ from{ transform:scaleX(0); } to{ transform:scaleX(1); } }
@media (prefers-reduced-motion:reduce){ .ld-show-tab.on.run .ld-show-bar{ animation:none; transform:scaleX(1); } }
.ld-show-stage{ display:grid; grid-template-columns:1.6fr 1fr; gap:40px; align-items:center; }
.ld-show-pic{ position:relative; aspect-ratio:16/10; border-radius:20px; overflow:hidden; background:#fff;
  box-shadow:0 1px 2px rgba(22,24,45,.06), 0 24px 50px -20px rgba(22,24,45,.28); }
.ld-show-img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; opacity:0; transform:scale(1.02); transition:opacity .45s ease, transform .6s ease; }
.ld-show-img.on{ opacity:1; transform:none; }
.ld-show-copy{ display:none; }
.ld-show-copy.on{ display:block; animation:ld-rise .4s ease; }
@keyframes ld-rise{ from{ opacity:0; transform:translateY(8px); } to{ opacity:1; transform:none; } }
.ld-show-copy .ld-h3{ font-size:clamp(22px,2.2vw,28px); }
.ld-more{ display:inline-flex; align-items:center; gap:6px; margin-top:16px; font-size:15px; font-weight:600; color:var(--ld-ink); text-decoration:none; border-bottom:1.5px solid var(--ld-ink); padding-bottom:1px; }
.ld-more::after{ content:"→"; }

/* ---- features ---- */
.ld-bento{ display:grid; grid-template-columns:repeat(3,1fr); gap:20px; }
/* GREY CARDS, COLOURED ICONS — Mounir, 13 September 2026: the cards were tinted for a moment, then
   "turn card back to grey, and keep icons colorful". Six colours from the editor's own palette and
   the apps' tints, one per card, on the icon only. */
.ld-cell{ background:var(--ld-card); border-radius:24px; padding:32px 30px 34px; transition:transform .25s ease; }
.ld-cell:nth-child(1){ --ld-tint:#3D95FF; }
.ld-cell:nth-child(2){ --ld-tint:#F97316; }
.ld-cell:nth-child(3){ --ld-tint:#0F766E; }
.ld-cell:nth-child(4){ --ld-tint:#C026D3; }
.ld-cell:nth-child(5){ --ld-tint:#3B5FE0; }
.ld-cell:nth-child(6){ --ld-tint:#C79A3B; }
.ld-cell:hover{ transform:translateY(-3px); }

/* ---- pricing ---- */
.ld-plans{ display:grid; grid-template-columns:repeat(2,minmax(0,520px)); justify-content:center; gap:20px; }
.ld-plan{ background:#fff; border:1px solid var(--bd); border-radius:24px; padding:32px; display:flex; flex-direction:column; }
.ld-plan-pro{ background:var(--ld-card); border-color:transparent; }
.ld-plan-name{ display:flex; align-items:center; gap:10px; font-size:15px; font-weight:700; color:var(--mut2); }
.ld-plan-price{ margin:14px 0 0; font-size:52px; line-height:1; font-weight:700; letter-spacing:-.04em; color:var(--ld-ink); }
/* the yearly line under the monthly price, and the sentence that says who each plan is for */
.ld-plan-note{ margin:10px 0 0; font-size:13.5px; line-height:1.5; color:var(--mut3); }
.ld-plan-blurb{ margin:14px 0 22px; font-size:15px; line-height:1.55; color:var(--mut2); }
.ld-plan-per{ font-size:16px; font-weight:600; letter-spacing:0; color:var(--mut3); margin-left:6px; }
.ld-plan-list{ list-style:none; margin:0 0 26px; padding:0; display:grid; gap:10px; }
.ld-plan-list li{ position:relative; padding-left:26px; font-size:15px; line-height:1.5; color:var(--mut2); }
.ld-plan-list li::before{ content:""; position:absolute; left:0; top:6px; width:14px; height:14px; border-radius:50%; background:#3D95FF;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round' d='M4 12.5l5 5L20 6.5'/%3E%3C/svg%3E") center/contain no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round' d='M4 12.5l5 5L20 6.5'/%3E%3C/svg%3E") center/contain no-repeat; }
.ld-plan .ld-btn{ margin-top:auto; width:100%; }

/* ---- the end ---- */
.ld-end{ max-width:var(--ld-max); margin:96px auto 96px; padding:88px var(--ld-edge); text-align:center; background:var(--ld-dark); color:#fff; border-radius:32px; }
.ld-end-h{ margin:0; font-size:clamp(34px,4.6vw,56px); line-height:1.04; font-weight:700; letter-spacing:-.035em; }
.ld-end-p{ max-width:560px; margin:18px auto 0; font-size:18px; line-height:1.55; color:rgba(255,255,255,.72); }
.ld-end .ld-btn{ margin-top:32px; }
/* the band keeps the page's edge until the screen is wider than the band can be, then it centres */
@media (max-width:1664px){ .ld-end{ margin-left:var(--ld-edge); margin-right:var(--ld-edge); } }

@media (max-width:900px){
  .ld{ --ld-edge:18px; }
  .ld-sec{ padding-top:64px; }
  .ld-three, .ld-bento{ grid-template-columns:1fr; }
  .ld-show{ padding:18px 18px 28px; border-radius:24px; }
  .ld-show-stage{ grid-template-columns:1fr; gap:22px; }
  .ld-shot-wrap{ padding:24px 18px 0; border-radius:24px; }
  .ld-plans{ grid-template-columns:1fr; }
  .ld-end{ padding:56px 24px; border-radius:24px; }
  .tf button.ld-tile{ width:180px; height:180px; }
}

/* =================================================================================================
   THE APPS PAGE — 13 September 2026
   =================================================================================================
   Built to the reference Mounir sent (mockuuups.studio/figma), measured rather than eyeballed:
   a 64px headline at -1.5px tracking over a 24px lead; feature rows as light-grey cards with
   32px corners and 88px of padding inside, two equal columns 88px apart, the picture swapping
   sides row by row; cards 24px apart; a dark closing band. The type here is the site's own
   (Schibsted Grotesk over Inter) and the colours are the home page's palette, so it reads as
   Templette and not as a copy of somebody else's site. --ax-card is the one grey; it is the
   page's own --bg, which every other home-page surface already uses. */
.ax{ --ax-card:var(--bg,#F5F5F7); --ax-ink:var(--fg,#16161A); --ax-dark:#0D0D0C; --ax-max:1240px; }

/* ---- the Apps menu in the header — 13 September 2026, to the picture Mounir sent ----
   The word with a chevron; pressed, it takes a soft grey pill and a white card drops from under
   it: one row per app, a 24px picture and the name in 16px medium, rows 56 tall, the card 16px
   round with a soft shadow. */
.tf-navdrop{ position:relative; display:flex; align-items:center; }
.tf-navlinks button.tf-navdrop-btn{ display:inline-flex; align-items:center; gap:6px; height:36px; padding:0 10px 0 12px; margin:0 -12px 0 -12px;
  border:none; border-radius:10px; background:none; font:inherit; font-size:15.5px; font-weight:600; color:var(--tf-ink,#0D0D0C); cursor:pointer; transition:background .15s, color .15s; }
.tf-navlinks button.tf-navdrop-btn:hover, .tf-navlinks button.tf-navdrop-btn.open{ background:var(--bg,#F5F5F7); color:var(--fg); }
.tf-navchev{ transition:transform .18s; }
.tf-navdrop-btn.open .tf-navchev{ transform:rotate(180deg); }
.tf-navmenu{ position:absolute; top:calc(100% + 10px); left:-12px; min-width:236px; padding:8px; z-index:60;
  background:#fff; border:1px solid var(--bd); border-radius:16px;
  box-shadow:0 1px 2px rgba(22,24,45,.06), 0 18px 44px -14px rgba(22,24,45,.22); }
.tf-navitem{ display:flex; align-items:center; gap:14px; height:56px; padding:0 14px; border-radius:10px;
  font-size:16px; font-weight:500; color:var(--fg); text-decoration:none; white-space:nowrap; cursor:pointer; }
.tf-navitem:hover{ background:var(--bg,#F5F5F7); }
.tf-navitem-art{ width:24px; height:24px; border-radius:7px; display:block; flex:none; }

/* ---- the row of apps under the header ---- */
.ax-tabs{ display:flex; align-items:center; justify-content:center; gap:10px; padding:26px var(--edge,40px) 0; }
.ax-tabs-label{ font-size:13px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--mut3); margin-right:6px; }
.ax-tab{ display:inline-flex; align-items:center; gap:9px; height:40px; padding:0 16px 0 12px; border-radius:12px;
  font-size:14px; font-weight:600; color:var(--mut2); text-decoration:none; border:1px solid transparent; transition:background .15s, color .15s; }
.ax-tab:hover{ background:var(--ax-card); color:var(--ax-ink); }
.ax-tab.on{ background:#fff; color:var(--ax-ink); border-color:var(--bd); box-shadow:0 1px 2px rgba(22,24,45,.06); }
.ax-tab-art{ width:20px; height:20px; border-radius:6px; display:block; }

/* ---- the headline ---- */
.ax-hero{ max-width:900px; margin:0 auto; padding:64px var(--edge,40px) 48px; text-align:center; }
.ax-eyebrow{ margin:0 0 18px; font-size:12px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--mut3); }
.ax-h1{ margin:0; font-size:clamp(38px,5.4vw,64px); line-height:1.02; font-weight:700; letter-spacing:-.035em; color:var(--ax-ink); }
.ax-lead{ max-width:680px; margin:22px auto 0; font-size:clamp(17px,1.7vw,22px); line-height:1.5; color:var(--mut2); }
.ax-cta{ display:flex; flex-direction:column; align-items:center; gap:14px; margin-top:34px; }
.ax-cta-note{ font-size:14px; color:var(--mut3); }
/* `.tf button` first, because the home page has a blanket `.tf button{ background:none }` that
   beats a one-class rule — the same fix the pricing page's buttons needed. */
.tf button.ax-btn{ display:inline-flex; align-items:center; justify-content:center; height:54px; padding:0 28px; border:none; border-radius:14px;
  font-family:inherit; font-size:16px; font-weight:700; cursor:pointer; transition:transform .12s, background .14s; }
.tf button.ax-btn:active{ transform:scale(.99); }
/* LIME, both of them — Mounir, 13 September 2026: the Try buttons at the top of every app page and
   the one in the dark band were lime until 25 October 2026; now black on white pages and white in the dark band, like the step numbers on the
   landing page. The class names stay so the two are still told apart in the markup. */
.tf button.ax-btn-dark{ background:#000; color:#fff; }
.tf button.ax-btn-dark:hover{ background:#000; }
.tf button.ax-btn-light{ background:#fff; color:#000; }
.tf button.ax-btn-light:hover{ background:#fff; }

/* ---- the one big picture, in a grey wrapper it bleeds out of at the bottom ---- */
.ax-preview{ max-width:calc(var(--ax-max) + 2 * var(--edge,40px)); margin:0 auto; padding:8px var(--edge,40px) 0; }
.ax-preview.ax-preview-next{ padding-top:24px; }   /* the same gap as between the feature cards below */
.ax-preview-wrap{ background:var(--ax-card); border-radius:32px; padding:64px 64px 0; overflow:hidden; }
.ax-preview-wrap img, .ax-preview-wrap video{ display:block; width:100%; height:auto; border-radius:18px 18px 0 0;
  box-shadow:0 30px 60px -20px rgba(22,24,45,.28), 0 0 0 1px rgba(22,24,45,.06); }

/* ---- the feature rows ---- */
.ax-feats{ max-width:calc(var(--ax-max) + 2 * var(--edge,40px)); margin:0 auto; padding:24px var(--edge,40px) 0; display:grid; gap:24px; }
.ax-feat{ background:var(--ax-card); border-radius:32px; padding:88px; display:grid; grid-template-columns:1fr 1fr; gap:88px; align-items:center; }
/* THE TEN EFFECTS PICTURE, 30% LARGER — 24 September 2026, "make it 30% larger". The picture's column
   takes 1.3 shares and the words 0.7, so the picture is 30% wider than in an even split. Its cards
   sit straight on the grey, so it has no white backing or frame of its own. */
.ax-feat.ax-wide-pic{ grid-template-columns:.7fr 1.3fr; }
.ax-feat-pic.ax-cutout img{ background:none; box-shadow:none; border-radius:0;
  /* "add soft shadow to it, similar to other images" — a drop shadow follows each card's own shape
     through the see-through gaps, where a box shadow would draw one rectangle round all ten */
  filter:drop-shadow(0 1px 1px rgba(22,24,45,.08)) drop-shadow(0 12px 18px rgba(22,24,45,.16)); }
/* the picture swaps sides row by row, and the text keeps its place in the source so a reader
   without the stylesheet still gets heading, words, picture, in that order */
.ax-feat:nth-child(even) .ax-feat-text{ order:2; }
.ax-feat:nth-child(even) .ax-feat-pic{ order:1; }
.ax-feat-text .ax-eyebrow{ margin-bottom:16px; }
.ax-h2{ margin:0; font-size:clamp(28px,3.2vw,42px); line-height:1.12; font-weight:700; letter-spacing:-.025em; color:var(--ax-ink); }
.ax-p{ margin:22px 0 0; font-size:clamp(16px,1.35vw,19px); line-height:1.6; color:var(--mut2); }
.ax-feat-pic{ min-width:0; }
.ax-feat-pic img{ display:block; width:100%; height:auto; border-radius:20px; background:#fff;
  box-shadow:0 1px 2px rgba(22,24,45,.06), 0 18px 40px -18px rgba(22,24,45,.18); }
/* a tall panel is shown at its real size, top-cropped at a sensible height, rather than blown up
   to the column's width and towering over its text */
.ax-feat-pic.ax-tall{ height:520px; overflow:hidden; border-radius:20px; background:#fff; display:flex; justify-content:center; align-items:flex-start;
  box-shadow:0 1px 2px rgba(22,24,45,.06), 0 18px 40px -18px rgba(22,24,45,.18); }
.ax-feat-pic.ax-tall img{ width:min(100%,360px); border-radius:0; box-shadow:none; }
/* a picture shown at 70% of the column's width, centred — asked for 24 September 2026: "make image
   30% smaller", the tall surf poster on the Text Effects page */
.ax-feat-pic.ax-smaller img{ width:70%; margin:0 auto;
  /* "add soft shadow to it similar to other images" — the same shadow the others wear is too faint
     to read round a dark, full-bleed picture, so this one is a little deeper and wider */
  box-shadow:0 2px 6px rgba(22,24,45,.08), 0 28px 60px -22px rgba(22,24,45,.38); }
/* six small headlines, in three columns */
.ax-tiles{ display:grid; grid-template-columns:repeat(3,1fr); gap:14px; }
.ax-tiles img{ border-radius:16px; }
/* four, in two columns — the QR page's colourings */
.ax-tiles.ax-tiles-2{ grid-template-columns:repeat(2,1fr); }
/* a card-shaped picture (the QR app's own card) shown at its real size, centred, not stretched to the column */
.ax-feat-pic.ax-card-pic{ display:flex; justify-content:center; }
.ax-feat-pic.ax-card-pic img{ width:min(100%,468px); }

/* ---- examples, questions ---- */
.ax-sec-head{ text-align:center; max-width:720px; margin:0 auto 40px; }
.ax-examples{ max-width:calc(var(--ax-max) + 2 * var(--edge,40px)); margin:0 auto; padding:96px var(--edge,40px) 0; }
.ax-examples.ax-empty{ display:none; }
.ax-ex-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:20px; }
.ax-ex-grid img{ display:block; width:100%; height:auto; border-radius:18px; background:var(--ax-card);
  box-shadow:0 1px 2px rgba(22,24,45,.06), 0 18px 40px -18px rgba(22,24,45,.16); }
.ax-faq{ max-width:820px; margin:0 auto; padding:96px var(--edge,40px) 0; }
.ax-faq .ax-sec-head{ margin-bottom:28px; }

/* ---- the closing band ---- */
.ax-end{ max-width:var(--ax-max); margin:96px auto 96px; padding:88px var(--edge,40px); text-align:center; background:var(--ax-dark); color:#fff; border-radius:32px; }
.ax-end-h{ margin:0; font-size:clamp(34px,4.6vw,56px); line-height:1.04; font-weight:700; letter-spacing:-.035em; }
.ax-end-p{ max-width:560px; margin:18px auto 0; font-size:18px; line-height:1.55; color:rgba(255,255,255,.72); }
.ax-end .ax-btn{ margin-top:32px; }
/* the band sits inside the page's edge on wide screens and runs edge to edge below 1320 */
@media (max-width:1320px){ .ax-end{ margin-left:var(--edge,40px); margin-right:var(--edge,40px); } }

@media (max-width:900px){
  .ax-feat{ grid-template-columns:1fr; gap:36px; padding:40px 28px; border-radius:24px; }
  .ax-feat.ax-wide-pic{ grid-template-columns:1fr; }
  .ax-feat:nth-child(even) .ax-feat-text{ order:1; }
  .ax-feat:nth-child(even) .ax-feat-pic{ order:2; }
  .ax-preview-wrap{ padding:28px 20px 0; border-radius:24px; }
  .ax-ex-grid{ grid-template-columns:1fr 1fr; }
  .ax-feat-pic.ax-tall{ height:380px; }
  .ax-end{ padding:56px 24px; border-radius:24px; }
}
@media (max-width:560px){
  .ax-ex-grid{ grid-template-columns:1fr; }
  .ax-tabs{ flex-wrap:wrap; }
}

/* One breakpoint, not three: four numbers become two, two cards become one, and the huge type
   is already fluid so it needs nothing here. */
@media (max-width:820px){
  .ab-hero{ padding:64px 20px 48px; }
  .ab-band-in{ padding:56px 20px; }
  .ab-cta{ padding:64px 20px 80px; }
}

/* ===== Auth: the picture on the LEFT, the form on the RIGHT =====
   REDRESSED 27 August 2026, to a picture Mounir gave. What changed and why:

   THE SIDES SWAPPED. The form was on the left; it is on the right now. Done with `order` and
   not by moving the markup: the form has to come FIRST in the page so that it is what a
   screen reader reaches first and what the Tab key lands on first. Which side each one is
   painted on is a matter of appearance, and appearance belongs here.

   THE PITCH WENT. This half held a headline, four selling points and a line of reassurance.
   Somebody who has clicked "create an account" has already been sold to.

   THE PICTURE HAS ROUND CORNERS, which means the panel needs a margin for them to show
   against — a rounded corner flush to the edge of the window is a rounded corner nobody
   sees. The picture fills what is left with object-fit:cover, so it is cropped rather than
   squashed whatever shape the window is. */
#authGate .ag-aside{
  flex:1 1 0; min-width:0; order:-1;            /* -1 = before the form, whatever the markup says */
  display:flex; padding:20px;
  background:#fff;
}
#authGate .ag-photo{
  width:100%; height:100%; min-height:0; object-fit:cover; object-position:50% 30%;
  border-radius:24px; display:block;
}

/* THE WAY OUT: WHITE ON BLACK, TOP RIGHT — asked for 27 August 2026. It was a pale grey cross
   on white at the top LEFT, over the form; with the form now on the right it would have sat on
   the picture, where a grey cross on a photograph is invisible. A black circle reads on
   anything and is the same shape whatever is behind it. */
#authGate .ag-close{ position:absolute; top:18px; right:18px; left:auto; z-index:2;
  display:grid; place-items:center;
  width:44px; height:44px; border:none; border-radius:50%;
  background:#171717; color:#fff; font-size:22px; line-height:1; cursor:pointer;
  transition:background .15s; }
#authGate .ag-close:hover{ background:#3B5FE0; color:#fff; }

/* Below 860px the picture is noise — drop it and show the form alone, still full screen. */
@media (max-width:860px){
  #authGate .ag-modal{ width:100%; }
  #authGate .ag-aside{ display:none; }
  #authGate .ag-card{ flex:1 1 auto; width:100%; padding:64px 24px 40px; }
}

/* Image layers clip their picture on this inner wrapper rather than on the layer
   itself, so the "Replace" pill can sit outside the cropped area instead of being
   silently cut off on small layers or at low zoom. */
.imgclip{ pointer-events:none; }
.imgslot .imgclip{ display:flex; align-items:center; justify-content:center; }

/* ---------- Browse by Category — REMOVED ----------
   WHAT WAS HERE. The "Browse by Category" row — twenty-three tiles you could scroll
   sideways through — was taken out on 21 August 2026, along with its heading and its scroll
   arrows. "View all categories" survived it and now sits at the end of the Trending line under
   the search box.
   The categories themselves are untouched: they are still what the dropdown beside the search
   box filters by, still what the browse-all page lists down its side, and their icons and
   colours are still recorded in taxonomy.js. Only this row of tiles is gone. */

/* The one place the page edge changes. Everything that uses --edge moves together. */
@media (max-width:760px){ :root{ --edge:18px; } }

/* ---------- stroke-style picker ----------
   A dropdown that shows a picture of the line beside each name. A plain <select> cannot do
   that — the browser draws its own list and ignores everything but text — so this is a
   button plus a panel of buttons behaving like one. It reads the panel's control size and
   roundness, so it sits in a row with the fields around it. */
.dsel{ position:relative; }
.dsel-btn{ box-sizing:border-box; width:100%; height:var(--ctl-h); display:flex; align-items:center; gap:8px;
  padding:0 var(--ctl-x); border:1px solid var(--field-bd); border-radius:var(--ctl-r);
  background:transparent; color:var(--ink); font:inherit; font-size:13px; cursor:pointer;
  transition:border-color .15s, box-shadow .15s; }
.dsel-btn:hover{ border-color:var(--bdh); }
/* open looks like focused, because it is */
.dsel-btn[aria-expanded="true"]{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
.dsel-ico{ flex:none; display:grid; place-items:center; }
.dsel-lab{ flex:1; min-width:0; text-align:left; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.dsel-arrow{ flex:none; color:var(--muted); }
/* The open list is dark whatever the page is doing, so it reads as something laid OVER the
   panel rather than another part of it. That is the one place a reversed colour earns its
   keep, and it is what these replaced: the system menus, which we could not style at all.
   The colours are their own variables — a dark surface cannot borrow --panel or --ink,
   those follow the page and would turn the list light again. */
.dsel{ --pop-bg:#1F2024; --pop-ink:#F2F3F5; --pop-dim:#9BA0AA; --pop-hover:#33353C; }
.dsel-pop{ position:absolute; left:0; right:0; top:calc(100% + 6px); z-index:90;
  background:var(--pop-bg); border:1px solid rgba(255,255,255,.10); border-radius:var(--ctl-r);
  box-shadow:0 16px 40px -12px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.30);
  padding:5px; display:flex; flex-direction:column; gap:1px;
  max-height:min(340px,60vh); overflow:auto; }
.dsel-opt{ display:flex; align-items:center; gap:8px; width:100%; height:32px; padding:0 8px;
  border:none; border-radius:7px; background:transparent; color:var(--pop-ink);
  font:inherit; font-size:13px; text-align:left; cursor:pointer; }
.dsel-opt:hover,.dsel-opt:focus{ background:var(--pop-hover); outline:none; }
.dsel-pop .dsel-ico{ color:var(--pop-dim); }
/* the tick marks the current one; it is always in the box so the names do not shift
   sideways when the choice changes */
.dsel-tick{ flex:none; color:var(--pop-ink); opacity:0; }
.dsel-opt.on .dsel-tick{ opacity:1; }
.dsel-opt.on .dsel-lab{ font-weight:600; }
/* a long list gets a scrollbar; the light default one is a bright stripe on a dark panel */
.dsel-pop{ scrollbar-width:thin; scrollbar-color:#4A4D56 transparent; }
.dsel-pop::-webkit-scrollbar{ width:8px; }
.dsel-pop::-webkit-scrollbar-thumb{ background:#4A4D56; border-radius:4px; border:2px solid transparent; background-clip:content-box; }

/* a choice button that carries a picture in front of its name */
.al-btn.al-lbl{ display:flex; align-items:center; justify-content:center; gap:8px; }
.al-btn.al-lbl svg{ flex:none; }

/* a drawing sitting inside a field, in front of the value.
   pointer-events:none matters — without it the left end of the box stops responding to
   clicks, because the drawing is on top of the input rather than inside it. */
.in-lead{ position:relative; display:block; }
/* THE ICON SITS ABOVE THE CONTROL, and the z-index is not decoration. The icon is drawn
   first and the field is drawn over the top of it — which was invisible while every control
   in the panel was transparent, and became visible the moment they were given a fill on
   26 August 2026: the Weight dropdown painted straight over its own icon. Lifting the icon
   one layer puts it back on top, where it always looked as though it was. */
.in-lead > svg{ position:absolute; left:var(--ctl-x); top:50%; transform:translateY(-50%);
  pointer-events:none; color:var(--muted); z-index:1; }
/* two classes beats the one-class-plus-attribute of the shared field rule, so this wins
   without !important */
/* A letter standing in for a drawing. Boxed to the same 19px so X, Y, W, H and the two
   text icons all leave their values starting in the same place. */
.in-lead > .in-lead-t{ position:absolute; z-index:1; left:var(--ctl-x); top:50%; transform:translateY(-50%);
  text-align:center; pointer-events:none; color:var(--muted);
  font-size:13px; font-weight:600; line-height:1; }
/* EVERY kind of control that can sit in a lead-in has to leave room for the mark. Miss one
   and the mark lands on top of the value instead of in front of it — which has now happened
   twice, first for a dropdown in its system dress, then for the one that replaced it. So
   this list is not allowed to drift: tools/test-scrub.cjs reads the controls actually used
   in lead-ins and fails if any of them is missing from here. */
.field .in-lead input,
.field .in-lead select,
.field .in-lead .dsel-btn{ padding-left:calc(var(--ctl-x) + var(--pico) + 7px); }   /* lead-in + gap */
/* Dragging only means something on a number. Anything else — a dropdown, a button — keeps
   its mark as scenery: no resize cursor, and clicks pass straight through to the control.
   Written as "has no number in it" rather than naming each kind, so the next control added
   is covered without anyone remembering to come back here. */
#props .in-lead:not(:has(input[type=number])) > svg{ pointer-events:none; cursor:default; }
/* THE COLOUR IS NOT PART OF THAT. It used to be: the mark on a non-number field was held at
   its resting grey on hover, on the reasoning that colour promises something you can grab.
   Undone 26 August 2026 at Mounir's word — it made the Weight field the one mark in the
   panel that stayed grey while every neighbour lit blue, which reads as a fault rather than
   a rule. The cursor still tells the truth about what can be dragged; the colour just
   answers the pointer, like the rest of them. */
/* Safari ignores padding on a dropdown left in its native dress, which is what put the mark
   on top of the number instead of in front of it. Taking the appearance over is the only way
   to make room — which means drawing the arrow here too, to match the panel's own dropdowns. */
.field .in-lead select{ -webkit-appearance:none; appearance:none;
  padding-right:calc(var(--ctl-x) + 14px); }
.field .in-lead:has(select)::after{ content:""; position:absolute; pointer-events:none;
  right:calc(var(--ctl-x) + 1px); top:calc(50% - 5px); width:7px; height:7px;
  border-right:2px solid var(--muted); border-bottom:2px solid var(--muted);
  border-radius:0 0 2px 0; transform:rotate(45deg); }

/* Every content icon in the properties panel is one size. Written as width AND height
   because an SVG's own width/height attributes win over a rule that sets only one. */
#props .al-btn svg,
#props .dsel-btn > svg:not(.dsel-arrow), #props .dsel-ico svg,
#props .in-lead > svg,
.dsel-pop .dsel-ico svg{ width:var(--pico); height:var(--pico); }
/* the letters standing in for X, Y, W and H are boxed to the same width */
.in-lead > .in-lead-t{ width:var(--pico); }

/* a slider label with its drawing in front of the name */
.fxs-lab .fxs-name{ display:inline-flex; align-items:center; gap:7px; }
.fxs-lab .fxs-name svg{ flex:none; color:var(--muted); }

/* ---------- scrubby number fields ----------
   Drag sideways on the mark at the left of a number box to change it. The up/down arrows
   the browser draws are removed: they are a five-pixel target, and dragging replaces them. */
#props .in-lead input[type=number]{ -moz-appearance:textfield; }
#props .in-lead input[type=number]::-webkit-outer-spin-button,
#props .in-lead input[type=number]::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
/* the mark has to take the mouse now, so it stops letting clicks fall through to the input
   and forwards a plain click by focusing it instead */
#props .in-lead > svg,
#props .in-lead > .in-lead-t{ pointer-events:auto; cursor:ew-resize; touch-action:none; }
/* The mark stays put on hover. Swapping it for a pair of arrows made the box flicker
   between two pictures every time the pointer crossed it, and cost you the one thing that
   says which field you are in. The sideways cursor is the signal instead. */
.in-lead:hover > svg,.in-lead:hover > .in-lead-t{ color:var(--accent); }
.in-lead > svg,.in-lead > .in-lead-t{ transition:color .12s; }
/* while dragging, the whole page shows the resize cursor and stops selecting text */
body.scrubbing{ cursor:ew-resize !important; user-select:none; -webkit-user-select:none; }


/* ===================== FULL SCREEN =====================================================
   Asked for on 22 August 2026: a full-screen button on the right of the island, three
   backdrops bottom-right — grey (the one already used), black and white — and arrows the
   other way to come back. Canva's is the reference.

   IT IS A VIEW, NOT A MODE OF EDITING. Everything that edits is hidden, and the design
   itself stops taking clicks: with no panels there is nothing to edit WITH, and a nudged
   layer nobody meant to touch is the worst thing this could do.
   The backdrop is set on the page root rather than on the canvas element, so the colour
   covers the whole screen including the strip either side of a portrait design. */
body.presenting .toolbar,
body.presenting #panelProps,
body.presenting #panelLayers,
body.presenting #panelPages,
body.presenting .float-island,
body.presenting .text-island,
body.presenting #selOverlay,
body.presenting .site-top,
body.presenting header{ display:none !important; }
body.presenting .stage-wrap{ background:var(--present-bg,#5E5E5E); }
body.presenting .stage-wrap .layer{ pointer-events:none; }
/* the page's name above it and the Add page bar under it are both parts of editing */
body.presenting .page-head,
body.presenting .page-bar{ display:none !important; }
body.presenting .ed-body{ padding:0; }
/* FOUR BACKDROPS. Each one is a colour, not a name that means different things in different
   themes: the swatch below is painted from the same value, so what the dot shows is what
   pressing it gives. Light grey is the editor's own pale backdrop and is the default; dark grey
   is the dark editor's. */
body.presenting.pb-light{ --present-bg:#F8F9FD; }
body.presenting.pb-dark{  --present-bg:#5E5E5E; }
body.presenting.pb-black{ --present-bg:#000000; }
body.presenting.pb-white{ --present-bg:#FFFFFF; }

.present-bar{ position:fixed; right:18px; bottom:18px; z-index:200; display:flex; align-items:center; gap:10px;
  padding:6px 8px; border-radius:14px; background:var(--panel,#1B1D22); border:1px solid var(--line,#2A2D34);
  box-shadow:0 6px 24px rgba(0,0,0,.35); }
.pb-swatches{ display:flex; align-items:center; gap:6px; }
.pb-sw{ width:20px; height:20px; border-radius:50%; border:2px solid transparent; box-shadow:0 0 0 1px rgba(125,125,135,.55) inset; cursor:pointer; padding:0; }
.pb-sw[data-bg="light"]{ background:#F8F9FD; }
.pb-sw[data-bg="dark"]{  background:#5E5E5E; }
.pb-sw[data-bg="black"]{ background:#000000; }
.pb-sw[data-bg="white"]{ background:#FFFFFF; }
.pb-sw:hover{ border-color:var(--accent,#5b5bf5); }
.pb-sw.on{ border-color:var(--accent,#5b5bf5); }
.pb-exit{ width:30px; height:30px; display:grid; place-items:center; border:none; border-radius:9px;
  background:transparent; color:var(--dim,#9aa0aa); cursor:pointer; }
.pb-exit:hover{ background:var(--hover,#25282F); color:var(--ink,#fff); }


/* ===================== REPORT A PROBLEM (23 Aug 2026) =====================================
   A question mark in the bottom-right corner of the editor — the corner Figma and Canva both
   use, so people look there without being told. Quiet until pointed at: it must never compete
   with the design. Hidden while presenting or previewing, which are not moments for chrome. */
/* `--fab-right` is measured from the canvas's own right edge by placeHelpFab(), so the button
   lands in the corner of the CANVAS rather than the corner of the window — clear of the
   properties panel, which it used to sit on top of. The 18px fallback is what it was before,
   and is what applies for the moment before the editor has been laid out. */
/* WHITE IN BOTH THEMES, asked for on 11 September 2026 with a picture of it.
   It used to take the editor's own background, which made it a dark circle in the dark theme.
   It floats over the canvas rather than sitting in a panel, so it is not editor chrome: what
   is behind it is somebody's artwork, and white with a dark mark reads on anything. Same
   reasoning as the clean-view bar and the black top bar — its own colours, on purpose, which
   is why its hover is a step off white rather than the editor's grey. */
button.help-fab{ position:fixed; right:var(--fab-right,18px); bottom:18px; z-index:86;
  width:38px; height:38px; border-radius:50%; border:none;
  background:#FFFFFF; color:#15171C; cursor:pointer;
  font-size:17px; font-weight:700; line-height:1;
  display:flex; align-items:center; justify-content:center;
  box-shadow:0 3px 10px rgba(16,16,24,.14); transition:color .15s, background .15s; }
button.help-fab:hover{ background:#EFEFF2; color:#15171C; }
/* Open. It said so with a blue edge, which went when the edge did — a ring outside the circle
   instead, so the button stays white and still plainly reads as the open one. */
button.help-fab.on{ background:#FFFFFF; color:var(--accent);
  box-shadow:0 3px 10px rgba(16,16,24,.14), 0 0 0 2px rgba(61,149,255,.55); }
body.presenting button.help-fab,
body.previewing button.help-fab{ display:none !important; }

.rep-pop{ position:fixed; right:var(--fab-right,18px); bottom:66px; z-index:87; width:320px; padding:14px;
  background:var(--bg); border:1px solid var(--line); border-radius:12px;
  box-shadow:0 14px 40px rgba(16,16,24,.2); font-size:13px; }
.rep-pop.hidden{ display:none; }
.rep-pop .rp-h{ margin:0 0 4px; font-weight:700; color:var(--ink); font-size:14px; }
.rep-pop .rp-note{ margin:0 0 12px; font-size:11.5px; color:var(--muted); line-height:1.5; }
.rep-pop .rp-f{ display:flex; flex-direction:column; gap:5px; margin-bottom:10px; }
.rep-pop .rp-f>span{ font-size:11px; color:var(--muted); }
.rep-pop select, .rep-pop textarea{ width:100%; padding:8px; border:1px solid var(--line);
  border-radius:8px; background:var(--field,var(--bg)); color:var(--ink);
  font-size:13px; font-family:inherit; }
.rep-pop textarea{ resize:vertical; min-height:70px; }
.rep-pop .rp-other.hidden{ display:none; }
.rep-pop .rp-count{ margin:-6px 0 10px; text-align:right; font-size:11px; color:var(--muted); }
.rep-pop .rp-row{ display:flex; gap:8px; justify-content:flex-end; }
.rep-pop button.rp-act{ height:32px; padding:0 14px; border:1px solid var(--line); border-radius:8px;
  background:var(--bg); color:var(--ink); cursor:pointer; font-size:12.5px; font-weight:600; }
.rep-pop button.rp-act:hover{ background:var(--hover); }
.rep-pop button.rp-act.primary{ background:var(--accent); color:#fff; border-color:var(--accent); }
.rep-pop button.rp-act.primary:disabled{ opacity:.45; cursor:default; }

/* the admin list */
/* ===== Backup: one button, and an honest running commentary ==================
 * The button is written as button.bk-go for the same reason every other one on these pages
 * is: a blanket rule near the top of this file styles every button on the site, and a bare
 * class does not outrank it. Colours are written out rather than read from variables, which
 * the account pages do not all define. */
.bk-lead{ max-width:640px; margin:0 0 10px; font-size:14px; line-height:1.6; color:var(--muted,#5f6570); }
.bk-lead.bk-quiet{ font-size:13px; color:var(--muted,#9aa0a6); margin-bottom:18px; }
button.bk-go{ height:42px; padding:0 20px; border:none; border-radius:10px; background:var(--accent,#2b3fee);
  color:#fff; font-size:14px; font-weight:700; cursor:pointer; }
button.bk-go:hover{ background:#2233d8; }
button.bk-go:disabled{ opacity:.6; cursor:default; }
/* The commentary changes colour only at the end, when there is something to say about it:
   amber when part of it did not come, red when nothing did. A backup that half worked must
   never look like one that worked. */
.bk-status{ max-width:640px; margin-top:16px; padding:12px 14px; border-radius:10px;
  border:1px solid var(--line,#ececf2); background:var(--field,#f6f6f9);
  font-size:13.5px; line-height:1.55; color:var(--ink,#14161c); word-break:break-word; }
.bk-status.hidden{ display:none; }
.bk-status[data-kind="good"]{ border-color:#b7dfc2; background:#f2fbf5; color:#1e6b39; }
.bk-status[data-kind="warn"]{ border-color:#e3d4ad; background:#fdf8ef; color:#7d5a20; }
.bk-status[data-kind="bad"]{ border-color:#e3b4ad; background:#fdf3f1; color:#7d2b20; }
.bk-last{ margin-top:12px; font-size:12.5px; color:var(--muted,#9aa0a6); }
.rp-list{ display:flex; flex-direction:column; gap:10px; }
.rp-item{ border:1px solid var(--line); border-radius:10px; padding:12px; background:var(--panel,var(--bg)); }
.rp-item.solved{ opacity:.6; }
.rp-item .rp-top{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:6px; }
.rp-item .rp-cat{ font-weight:700; color:var(--ink); }
.rp-item .rp-who{ font-size:12px; color:var(--muted); }
.rp-item .rp-plan{ font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
  padding:2px 6px; border-radius:5px; background:var(--hover); color:var(--muted); }
.rp-item .rp-plan.pro{ background:rgba(43,63,238,.14); color:var(--accent); }
.rp-item .rp-when{ margin-left:auto; font-size:11.5px; color:var(--muted); }
.rp-item .rp-msg{ margin:6px 0; font-size:13px; color:var(--ink); line-height:1.5; white-space:pre-wrap; word-break:break-word; }
.rp-item .rp-meta{ font-size:11px; color:var(--muted); margin:4px 0 8px; }
.rp-item .rp-acts{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.rp-item button.rp-b{ height:30px; padding:0 12px; border:1px solid var(--line); border-radius:7px;
  background:var(--bg); color:var(--ink); cursor:pointer; font-size:12px; font-weight:600; }
.rp-item button.rp-b:hover{ background:var(--hover); }
.rp-item input.rp-note{ flex:1; min-width:160px; height:30px; padding:0 8px;
  border:1px solid var(--line); border-radius:7px; background:var(--field,var(--bg));
  color:var(--ink); font-size:12px; }

/* ============================================================================
   RECENTLY DELETED — the one-day bin for uploads (30 August 2026).

   A binned picture has to look like it is on its way out without looking broken:
   faded, but readable, and it brightens when you point at it, because the whole
   reason somebody is in this list is that they want a proper look at something
   before deciding. "Put back" is a real button rather than a menu item — it is
   the thing people come here to press, and burying the safe action two clicks
   deep while the destructive one sits in the open is the wrong way round.
   ============================================================================ */
.pj-binnote{ margin:-6px 0 12px; font-size:13px; color:var(--muted,#6b7280); }
/* ONLY THE PICTURE IS FADED. Fading the whole card also faded the words and the button —
   a button set to solid white came out grey on screen, because everything inside a faded
   element is faded with it. The computed colour said white and the pixels said otherwise,
   which is exactly the kind of thing only a screenshot catches. */
.pj-card.binned .pj-thumb{ opacity:.55; transition:opacity .14s; }
.pj-card.binned:hover .pj-thumb{ opacity:1; }
.pj-card.binned .pj-sub{ color:#b45309; font-weight:600; }
/* IN THE FLOW, NOT ON TOP OF THE NAME. The first version floated this over the bottom-left
   corner of the card and it landed squarely across the file name — which the checks all
   passed, because a button can be present, the right size and perfectly readable while
   covering the one thing that says WHICH picture you are about to put back. Found by taking
   a picture of the page and looking at it. It now sits under the name, full width, always
   visible: nothing to hover for, nothing hidden behind it. */
.pj-restore{
  display:block; width:calc(100% - 20px); margin:0 10px 12px;
  min-height:38px; padding:0 16px;
  border:1px solid rgba(0,0,0,.12); border-radius:999px;
  background:#fff; color:#111; font:inherit; font-size:13px; font-weight:600;
  cursor:pointer; transition:box-shadow .12s, transform .06s;
  box-shadow:0 1px 4px rgba(0,0,0,.16);
}
.pj-restore:hover{ box-shadow:0 2px 10px rgba(0,0,0,.22); }
.pj-restore:active{ transform:translateY(1px); }

/* ============================================================================
   HOUSEKEEPING LINE on the admin page (30 August 2026).

   Three states and they must be told apart at a glance, because the only one
   that matters is the one that almost never happens. Good is quiet — grey text,
   no colour, no icon, present so that its absence would be noticed. Bad is a
   proper warning with a coloured edge. "Cannot check" is amber rather than red:
   something is missing, but nothing is broken.
   ============================================================================ */
.ad-house{
  margin:0 0 14px; padding:11px 14px;
  border-radius:10px; font-size:13px; line-height:1.5;
  border:1px solid transparent;
}
.ad-house.ok{ color:var(--muted,#6b7280); background:rgba(127,127,127,.07); }
.ad-house.unknown{
  color:#92400e; background:rgba(245,158,11,.12); border-color:rgba(245,158,11,.35);
}
.ad-house.bad{
  color:#b91c1c; background:rgba(220,38,38,.10); border-color:rgba(220,38,38,.40);
  font-weight:600;
}

/* ============================================================================
   SHOW THE PASSWORD — 31 August 2026.

   Asked for after the sign-in audit: there was no way to check what you had
   typed, which on the sign-up card is a common reason for not being able to log
   in a minute later.

   The button sits INSIDE the box. Beside it would push the row out of shape and
   break the line of the card; inside keeps the form looking like a form. The
   input gets extra padding on that side so the dots never slide underneath.

   It is 44 pixels square, which is the smallest a finger reliably hits, and it
   is always visible — hiding it until hover would put it out of reach on a
   phone, where checking a fiddly password matters most.
   ============================================================================ */
.ag-passwrap{ position:relative; display:flex; flex-direction:column; }
.ag-passwrap .ag-input{ padding-right:52px; }
.ag-eye{
  position:absolute; right:4px; top:50%; transform:translateY(-50%);
  width:44px; height:44px; padding:0;
  display:flex; align-items:center; justify-content:center;
  border:none; background:none; border-radius:99px;
  color:#8a8a99; cursor:pointer;
  transition:color .12s, background .12s;
}
.ag-eye:hover{ color:#14161c; background:rgba(0,0,0,.05); }
.ag-eye:focus-visible{ outline:2px solid #5b5bf5; outline-offset:-2px; }
/* The two states are drawn in CSS rather than swapped in script, so the button
   cannot end up showing one thing and doing another. */
.ag-eye::before{
  content:""; width:20px; height:20px;
  background-color:currentColor;
  -webkit-mask:var(--eye) center/20px 20px no-repeat;
          mask:var(--eye) center/20px 20px no-repeat;
}
.ag-eye{
  --eye:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></svg>');
}
.ag-eye[aria-pressed="true"]{
  color:#14161c;
  --eye:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.9 17.9A10.4 10.4 0 0 1 12 19c-6.4 0-10-7-10-7a19 19 0 0 1 5.1-5.9m3.2-1A10.4 10.4 0 0 1 12 5c6.4 0 10 7 10 7a19 19 0 0 1-2.2 3.2M9.9 9.9a3 3 0 0 0 4.2 4.2"/><path d="m2 2 20 20"/></svg>');
}

/* ============================================================================
   APPS — the menu on the floating island, and the popup an app opens.
   Added 1 September 2026 with the first app.

   Both follow the editor's own theme variables rather than fixed colours, so an
   app looks right in the light theme and the dark one without each app having
   to think about it. An app that wants its own look sets it inside its own
   card; the frame decides everything outside.
   ============================================================================ */
/* WIDE, WITH A SEARCH BOX ON TOP AND A LINE OF DESCRIPTION UNDER EVERY NAME — asked for on
   2 September 2026 with a picture of Figma's. The width is what makes the descriptions possible:
   at 196 pixels every one of them would have wrapped onto a second line, and a "one-line
   description" that takes two lines is a paragraph. */
/* Two across at 655 pixels from 11 September 2026; since 1 November 2026 a strip of cards from
   the tools on the left to the settings on the right, its width set when it opens (below). */
.apps-menu{
  position:fixed; z-index:80; width:655px; max-width:calc(100vw - 16px); padding:16px;
  background:var(--ed-panel,#fff); border:1px solid var(--ed-line,#e3e3ea);
  border-radius:18px; box-shadow:none;   /* no shadow, asked for 1 November 2026 */
}
/* ONE ROW OF CARDS ACROSS THE EDITOR — 1 November 2026, from Mounir's picture of a "Featured
   apps" strip. The search box on top with the arrows to its right; under it, the cards, light
   grey outlines at rest and light blue when the pointer is on one. The box itself is a very light
   blue, asked for. The blues are our own blue, see-through, so they sit right on light and dark. */
.apps-head{ display:flex; align-items:center; gap:8px; margin-bottom:14px; }
.apps-find{
  flex:1; min-width:0;
  display:flex; align-items:center; gap:8px; height:40px; padding:0 12px;
  border-radius:12px; background:rgba(61,149,255,.07); color:var(--ed-muted,#7a7a88);
  border:1px solid rgba(61,149,255,.16);
}
.apps-search{
  flex:1; min-width:0; height:100%; border:none; background:none; outline:none;
  color:var(--ed-ink,#16161a); font:inherit; font-size:13.5px;
}
.apps-search::placeholder{ color:var(--ed-muted,#7a7a88); }
/* The browser's own clear cross sits at a different size in every browser and is not needed
   here, where the list is one press from being whole again. */
.apps-search::-webkit-search-cancel-button{ display:none; }
.apps-arrow{
  flex:none; width:40px; height:40px; display:flex; align-items:center; justify-content:center;
  border:1px solid var(--ed-line,#e3e3ea); border-radius:12px; background:var(--ed-panel,#fff);
  color:var(--ed-ink,#16161a); cursor:pointer;
}
.apps-arrow:hover:not(:disabled){ background:rgba(61,149,255,.08); border-color:rgba(61,149,255,.45); }
.apps-arrow:disabled{ opacity:.35; cursor:default; }
/* THE STRIP SCROLLS SIDEWAYS, by the arrows or a trackpad, and stops with a card at its left edge.
   Its scroll bar is hidden: the arrows say there is more. */
.apps-view{ overflow-x:auto; overflow-y:hidden; scroll-snap-type:x mandatory; scrollbar-width:none; }
.apps-view::-webkit-scrollbar{ display:none; }
.apps-rows{ display:flex; gap:12px; }
.apps-rows > .apps-item{ min-width:0; }
/* THE PICTURE'S PROPORTIONS: a card a little wider than it is tall. Its width is set by the menu,
   so that the cards that fit share the row evenly. The proportion is the LEAST height: a card whose
   description runs to three lines grows a little rather than cutting the last one, and the rest of
   the row grows with it. */
.apps-item{
  flex:0 0 var(--apps-card-w,160px); width:var(--apps-card-w,160px); aspect-ratio:1.09 / 1;
  display:flex; flex-direction:column; align-items:flex-start; gap:9px;
  padding:14px 14px 14px; border:1px solid var(--ed-line,#e6e7ee); border-radius:14px;
  background:var(--ed-panel,#fff); color:var(--ed-ink,#16161a); font:inherit; font-size:13.5px;
  cursor:pointer; text-align:left; scroll-snap-align:start;
  transition:background .12s ease, border-color .12s ease;
}
.apps-item:hover, .apps-item:focus-visible{ background:rgba(61,149,255,.07); border-color:rgba(61,149,255,.45); outline:none; }
/* EACH APP'S ICON IS ITS OWN COLOUR, set from the app list. The grey here is what an app that
   has not chosen one falls back to. */
.apps-ico{ display:flex; flex:none; color:var(--ed-muted,#7a7a88); }
.apps-words{ display:flex; flex-direction:column; gap:3px; min-width:0; }
.apps-name{ font-weight:600; font-size:14.5px; }
/* THE DESCRIPTIONS WE ALREADY HAVE, up to three lines in a card. */
.apps-blurb{
  color:var(--ed-muted,#7a7a88); font-size:12.5px; line-height:1.4;
  overflow:hidden; display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3;
}
.apps-none{
  padding:14px 10px 16px; color:var(--ed-muted,#7a7a88); font-size:12.5px; text-align:center;
}
/* THE BUTTON IS LIT WHILE AN APP IS OPEN — white on blue, asked for. It has to beat the hover
   colour, or resting the pointer on a lit button would turn it grey again. */
#appsBtn.on, #appsBtn.on:hover{ background:var(--accent); color:#fff; }
/* THE "TESTING" LABEL WAS TAKEN OFF ON 2 SEPTEMBER 2026, asked for. Which apps the public
   cannot see is still decided by the "ready" flag in the app list, not by anything on screen —
   the label only ever said it out loud. */

/* ---- the popup an app opens ---- */
.app-ov{
  position:fixed; inset:0; z-index:90; display:flex; align-items:center; justify-content:center;
  padding:24px; background:rgba(12,12,16,.42); overflow-y:auto;
}
.app-card{
  /* The popup sits at the page root, not inside the editor, so none of the editor's numbers
     reach it. These three are the ones its controls are built from — how tall a control is,
     how round, and the line colour — and they are the editor's own values repeated. A check
     fails if the editor changes its and this is left behind. */
  --ctl-h:36px; --ctl-r:4px; --line:var(--ed-line,#dcdce6);
  position:relative; width:min(420px,100%); padding:26px 24px 22px;
  background:var(--ed-panel,#fff); color:var(--ed-ink,#16161a);
  border-radius:18px; box-shadow:0 24px 60px rgba(0,0,0,.28);
}
.app-x{
  position:absolute; top:12px; right:12px; width:34px; height:34px;
  border:none; border-radius:99px; background:none; color:var(--ed-muted,#7a7a88);
  font-size:22px; line-height:1; cursor:pointer;
}
.app-x:hover{ background:var(--ed-hover,rgba(0,0,0,.06)); color:var(--ed-ink,#16161a); }
.app-title{ margin:0 0 4px; font-size:18px; font-weight:700; }
.app-sub{ margin:0 0 16px; font-size:13px; color:var(--ed-muted,#7a7a88); }
.app-input{
  width:100%; padding:11px 13px; border:1px solid var(--ed-line,#dcdce6); border-radius:12px;
  background:var(--ed-field,#fff); color:var(--ed-ink,#16161a);
  font:inherit; font-size:14px; resize:vertical; outline:none; box-sizing:border-box;
}
.app-input:focus{ border-color:#3D95FF; }
.app-row{ display:flex; gap:10px; margin-top:12px; }
.app-field{ flex:1; display:flex; flex-direction:column; gap:5px; min-width:0; }
/* A round swatch is 36px wide, so the column holding it is too, with its label centred over
   it rather than stranded to one side of an empty 66px box. */
.app-field-sm{ flex:0 0 auto; align-items:center; }
.app-field span{ font-size:11.5px; color:var(--ed-muted,#7a7a88); }
.app-field select{
  /* the same height as a colour swatch beside it, so the row lines up */
  height:var(--ctl-h); padding:0 8px; border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--ed-field,#fff); color:var(--ed-ink,#16161a); font:inherit; font-size:13px;
}
/* A fixed square, so the popup does not jump about as the code changes size while typing. */
.app-preview{
  display:grid; place-items:center; width:100%; height:190px; margin-top:16px;
  border:1px solid var(--ed-line,#e3e3ea); border-radius:12px;
  background:#fff; overflow:hidden;
}
.app-preview svg{ width:170px; height:170px; }
.app-note{ min-height:17px; margin-top:8px; font-size:12px; color:var(--ed-muted,#7a7a88); }
.app-note.bad{ color:#b91c1c; }
/* BLUE, NOT BLACK. It was black and lit up blue on hover until 6 September 2026, when Mounir
   sent a picture of the button he wanted and a second swatch for the hover. The resting colour
   is the editor's own blue rather than the #5593F7 in his picture — a hair lighter — so this
   button matches every other blue in the app; the hover is his swatch exactly. */
.app-go{
  width:100%; height:44px; margin-top:14px; border:none; border-radius:99px;
  background:#3D95FF; color:#fff; font:inherit; font-size:14px; font-weight:700; cursor:pointer;
  transition:background .15s;
}
.app-go:hover:not(:disabled){ background:#497ED4; }
.app-go:disabled{ opacity:.4; cursor:not-allowed; }

/* =================================================================================================
   A WORKSPACE APP — 1 September 2026, task 6 of the bento grid plan.
   Some apps are a small card in the middle of the screen; the grid builder is not one of them. It
   needs a library down one side, a large preview in the middle and the settings down the other,
   which is a room rather than a dialog. So it takes the whole window.

   IT DECLARES ITS OWN CONTROL SIZES, like the card above and for the same reason: it hangs off the
   page root rather than sitting inside the editor, so none of the editor's numbers reach it. A
   check fails if these stop matching the editor's.
   ================================================================================================= */
.bw{
  --ctl-h:36px; --ctl-r:4px; --line:#EEEFF3; --line-h:#DCDEE6;
  --ink:#16161A; --dim:#6C7080; --faint:#9AA0B0;
  --panel:#FFFFFF; --sunk:#F5F6F9; --hover:#F2F3F7; --accent:#3D95FF;
  /* THE EDITOR'S OWN CONTROL COLOURS, copied here by value because this window hangs off the
     page root and none of the editor's numbers reach it. Asked for 2 September 2026: "we need
     to make the app UI cohesive with the editor, fields colors and button colors ... should be
     similar to editor". These four are what the editor's right panel runs on:
       --ctl-bg    the pale fill every field and unselected button sits on
       --ctl-bg-h  a half-step down, for hover
       --sel-bg    the blue a chosen button wears, with white writing on it
       --soft-sel  the pale blue a chosen icon button wears, with a blue icon
     A check compares them against the editor's, so they cannot drift apart quietly. */
  --ctl-bg:#F7F7F8; --ctl-bg-h:#EFEFF1; --sel-bg:#3D95FF; --sel-fg:#FFFFFF;
  /* AND THE DARKER STEP THE FILLED BUTTON TAKES UNDER THE POINTER — the editor's own, written
     out because none of its numbers reach this window. Asked for 6 September 2026: the corners
     were pills where the editor's are barely rounded, and Add to design was black and lit up
     blue, where the editor's Download button is blue and goes darker. */
  --accent-h:#3480DB;
  --soft-sel:#E8EAFD;
  /* ABOVE THE FLOATING ISLAND. Found by taking a picture and looking at it: at the z-index the
     small app card uses, the editor's undo/zoom/theme bar floated on top of this window and its
     buttons were still clickable through it. Below the toast at 200, though — a message about
     something going wrong has to be able to reach the front from anywhere.

     THESE LINES MUST STAY IN THIS RULE, which applies in every theme. On 2 September 2026 the
     dark-theme colours were added and this rule was closed one line too early, so "fixed, full
     screen, on top" ended up in the dark-only block below — and in a light editor the window
     had no position at all and rendered as an ordinary block underneath the page. A check now
     opens the window in both themes and measures where it is. */
  position:fixed; inset:0; z-index:150;
  /* The window holds the focus when it opens so a keyboard is inside it rather than on the page
     behind. Left alone the browser draws a ring round the whole screen for that — visible in the
     picture as a dark line all the way round. The ring belongs on the buttons, which keep
     theirs; a container is not something you press. */
  outline:none;
  display:grid; grid-template-rows:auto 1fr;
  background:var(--panel); color:var(--ink);
  font:inherit; font-size:13px;
}
/* AND IT FOLLOWS THE EDITOR INTO THE DARK. This window was white whatever theme the editor was
   in — open it from a dark editor and a white sheet the size of the screen arrived, which is the
   opposite of cohesive. Every value here is the editor's own dark one, taken from the block at
   the top of this file rather than invented, so the two cannot drift.
   The editor is dark unless the page carries "light", so the DARK set is written as the
   exception to the light one above rather than the other way round — that is how the editor
   itself is written, and doing it the other way round would mean two files disagreeing about
   which theme is the normal one. */
body:not(.light) .bw{
  --line:#333333; --line-h:#454545;
  --ink:#F2F2F2; --dim:#B3B3B3; --faint:#8C8C8C;
  --panel:#232323; --sunk:#1C1C1C; --hover:#303030; --accent:#3D95FF;
  --ctl-bg:#2A2A2A; --ctl-bg-h:#333333; --sel-bg:#3D95FF; --sel-fg:#FFFFFF;
  --soft-sel:rgba(92,130,255,.20);
  /* COLOURS ONLY. Nothing about where the window sits or how it is laid out belongs in a
     theme block — see the note in the rule above. */
}
/* ---- the bar across the top ---- */
/* Three parts, not a row of things pushed apart by springs. The name on the left, the controls
   in the MIDDLE of the window rather than the middle of whatever is left over, and the one real
   action on the right. Sprung apart, the controls sat left of centre and read as an accident —
   found by taking a picture of it and looking. */
.bw-top{
  display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:10px;
  height:60px; padding:0 14px 0 18px;
  border-bottom:1px solid var(--line); background:var(--panel);
}
.bw-mid{ display:flex; align-items:center; gap:8px; justify-self:center; }
.bw-act{ display:flex; align-items:center; gap:8px; justify-self:end; }
.bw-name{ display:flex; align-items:center; gap:9px; font-size:15px; font-weight:700; }
/* DEAL THE CARDS OUT AGAIN. At the left end beside the name, where it was asked for. A word as
   well as the picture: two crossing arrows mean "shuffle" to anybody who has used a music player
   and nothing at all to anybody who has not, and there is room for the word here. */
/* SHUFFLE SITS IN THE SAME PILL AS UNDO AND REDO, so it takes their shape rather than keeping
   the outlined one it had beside the title. It keeps its word, because an icon alone would be
   two crossed arrows nobody has to guess at only once they have already found out. */
.bw-shuffle{ font-size:12.5px; font-weight:600; padding:0 12px 0 10px; }
.bw-shuffle svg{ color:var(--accent); transition:transform .25s cubic-bezier(.2,.7,.3,1); }
.bw-shuffle:hover:not(:disabled) svg{ transform:rotate(180deg); }
@media (prefers-reduced-motion: reduce){
  .bw-shuffle svg{ transition:none; }
  .bw-shuffle:hover svg{ transform:none; }
}
.bw-name svg{ color:var(--accent); }
.bw-set{ display:flex; align-items:center; gap:2px; padding:3px; background:var(--sunk); border-radius:12px; }
.bw-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:7px;
  height:var(--ctl-h); min-width:var(--ctl-h); padding:0 11px;
  border:none; border-radius:var(--ctl-r); background:none; color:var(--ink);
  font:inherit; font-size:13px; cursor:pointer;
}
.bw-btn:hover:not(:disabled){ background:var(--hover); }
.bw-btn:disabled{ opacity:.32; cursor:default; }
.bw-zoom{ min-width:52px; text-align:center; font-variant-numeric:tabular-nums; color:var(--dim); }
/* The one thing this window is for. Everything else in the bar is a way of getting to it. */
.bw-go{
  height:var(--ctl-h); padding:0 20px; border:none; border-radius:var(--ctl-r);
  background:var(--accent); color:#fff; font:inherit; font-size:13.5px; font-weight:700;
  cursor:pointer; transition:background .15s;
}
.bw-go:hover:not(:disabled){ background:var(--accent-h); }
.bw-go:disabled{ opacity:.32; cursor:default; }
.bw-x{
  width:var(--ctl-h); height:var(--ctl-h); border:none; border-radius:var(--ctl-r);
  background:none; color:var(--dim); font-size:22px; line-height:1; cursor:pointer;
}
.bw-x:hover{ background:var(--hover); color:var(--ink); }

/* ---- library · preview · settings ---- */
.bw-body{ display:grid; grid-template-columns:264px 1fr 300px; min-height:0; }
.bw-side{ display:flex; flex-direction:column; min-height:0; background:var(--panel); }
.bw-side.left{ border-right:1px solid var(--line); }
.bw-side.right{ border-left:1px solid var(--line); }
.bw-head{
  display:flex; align-items:center; height:46px; padding:0 18px; flex:none;
  font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--faint);
}
.bw-scroll{ flex:1 1 auto; overflow-y:auto; padding:0 14px 18px; min-height:0; }

/* ---- the layout library (task 8) ----
   Searching and filtering sit OUTSIDE the scrolling part. Scrolled away, a filter that is on
   is a filter you cannot see, and an empty list with no visible reason for it reads as the
   library being broken. */
.bw-find{ flex:none; padding:0 14px 12px; }
.bw-inp{
  width:100%; height:var(--ctl-h); box-sizing:border-box; padding:0 12px;
  border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--panel); color:var(--ink); font:inherit; font-size:13px; outline:none;
}
.bw-inp:focus{ border-color:var(--accent); }
.bw-chips{ display:flex; gap:5px; margin-top:9px; }
.bw-chip{
  flex:1 1 0; height:30px; padding:0 6px; border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--panel); color:var(--dim); font:inherit; font-size:11.5px; cursor:pointer;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.bw-chip:hover{ border-color:var(--line-h); }
.bw-chip[aria-pressed="true"]{ background:var(--ink); border-color:var(--ink); color:#fff; font-weight:600; }
.bw-two{ display:flex; gap:7px; margin-top:7px; }
.bw-sel{
  flex:1 1 0; min-width:0; height:32px; padding:0 8px;
  border:1px solid var(--line); border-radius:9px;
  background:var(--panel); color:var(--ink); font:inherit; font-size:12px;
}
/* Starting from nothing is not one layout among thirty-two — it is the other way of beginning.
   So it sits above the list rather than being something to scroll past and miss. */
.bw-blank{
  display:flex; align-items:center; gap:9px; width:100%; margin-bottom:4px;
  padding:11px 12px; border:1px dashed var(--line-h); border-radius:12px;
  background:var(--panel); color:var(--dim); font:inherit; font-size:12.5px;
  text-align:left; cursor:pointer;
}
.bw-blank:hover{ border-color:var(--accent); color:var(--ink); }
.bw-item{
  display:flex; align-items:center; gap:12px; width:100%; margin-top:3px;
  padding:9px 10px; border:1px solid transparent; border-radius:12px;
  background:none; font:inherit; text-align:left; cursor:pointer;
}
.bw-item:hover{ background:var(--hover); }
.bw-item[aria-pressed="true"]{ background:#EEF2FE; border-color:#C9D6FB; }
.bw-item .bento-thumb{ flex:none; background:var(--sunk); border:1px solid var(--line); border-radius:6px; }
.bw-item[aria-pressed="true"] .bento-thumb{ border-color:#C9D6FB; }
.bw-lab{ min-width:0; }
.bw-lab b{ display:block; font-size:12.5px; font-weight:600; color:var(--ink);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.bw-lab span{ display:block; margin-top:2px; font-size:11px; color:var(--faint); }
.bw-none{ padding:22px 6px; text-align:center; color:var(--faint); font-size:12.5px; line-height:1.6; }
.bw-none button{
  display:inline-block; margin-top:10px; height:32px; padding:0 14px;
  border:1px solid var(--line); border-radius:var(--ctl-r); background:var(--panel);
  color:var(--ink); font:inherit; font-size:12.5px; cursor:pointer;
}
.bw-none button:hover{ background:var(--hover); }
/* A section of the settings panel. */
.bw-sec{ border-top:1px solid var(--line); padding:14px 4px 16px; }
.bw-sec:first-child{ border-top:none; }
.bw-sec h3{ margin:0 0 11px; font-size:12.5px; font-weight:700; }
.bw-sec p{ margin:5px 0 0; font-size:12px; color:var(--faint); }

/* ---- the settings controls (task 9) ---- */
.bw-line{ display:flex; align-items:center; gap:8px; margin-top:9px; }
.bw-line:first-of-type{ margin-top:0; }
.bw-fld{ flex:1 1 0; min-width:0; }
.bw-fld > label{ display:block; margin-bottom:4px; font-size:11px; color:var(--faint); }
/* A number you can type into and step with the arrow keys. The browser's own little spinners
   are removed: at this size they are two three-pixel targets nobody can hit, and they crowd
   out the digits, which are the point. The arrow KEYS still work — those are the ones people
   actually use. */
.bw-num{
  width:100%; height:var(--ctl-h); box-sizing:border-box; padding:0 10px;
  border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--panel); color:var(--ink); font:inherit; font-size:13px;
  font-variant-numeric:tabular-nums; outline:none; -moz-appearance:textfield;
}
.bw-num::-webkit-outer-spin-button, .bw-num::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
.bw-num:focus{ border-color:var(--accent); }
/* Out of range while you are still typing. Red rather than snapping the number back under the
   cursor — a box that corrects you mid-word is a box you cannot type 1200 into. */
.bw-num.bad{ border-color:#D9534F; background:#FFF7F7; }
.bw-lock{
  flex:none; align-self:flex-end; width:var(--ctl-h); height:var(--ctl-h);
  border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--panel); color:var(--faint); cursor:pointer;
  display:flex; align-items:center; justify-content:center;
}
.bw-lock:hover{ border-color:var(--line-h); color:var(--dim); }
.bw-lock[aria-pressed="true"]{ background:var(--ink); border-color:var(--ink); color:#fff; }
.bw-wide{
  width:100%; height:var(--ctl-h); margin-top:9px;
  border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--panel); color:var(--ink); font:inherit; font-size:12.5px; cursor:pointer;
}
.bw-wide:hover{ background:var(--hover); }
.bw-shapes{ display:flex; flex-wrap:wrap; gap:5px; margin-top:2px; }
 /* Two by two rather than four across. "Widescreen" is the longest word in the panel and
    four of these on one line pushed it onto a second by itself, which reads as a mistake. */
.bw-shapes .bw-chip{ flex:1 1 calc(50% - 3px); min-width:0; }
/* The individual row heights and column widths. Small, in a wrapping row, with the number of
   the track above it — a column of full-width fields for a nine-column grid would push
   everything else off the panel. */
.bw-tracks{ display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
.bw-trk{ flex:0 0 62px; }
.bw-trk label{ display:block; margin-bottom:3px; font-size:10px; color:var(--faint); text-align:center; }
.bw-trk input{ height:30px; padding:0 6px; font-size:12px; text-align:center; }
.bw-sub{ margin:10px 0 0; font-size:11px; color:var(--faint); line-height:1.5; }
/* The four sides go four across, not three and a stray one on a second line. They are a set —
   split three and one they read as a mistake, and the odd one out looks like it means something
   different from the others. */
#bwPadFour .bw-trk{ flex:1 1 0; min-width:0; }

/* ---- appearance (task 11) ----
   NO COLOUR AT ALL is a real answer, not a missing one, so it gets a mark of its own: the
   swatch goes white with a red line through it, the same way the editor's own do. A white
   circle on a white panel would read as white paint. */
.bw-sw{ flex:none; }
/* NO COLOUR AT ALL, DRAWN AS NO COLOUR AT ALL.
   Found 2 September 2026. A colour box always paints SOMETHING, and what it paints when it has
   been given nothing is black — so a border set to none showed as a solid black circle. The
   swatch said "black", the grid drew no border, and picking black changed nothing: a control
   that looks set, is not, and reads as broken.

   The editor's own swatches are buttons, and a red line can be drawn over a button. A colour
   input is a replaced element and a browser will not draw anything on top of one, so the line
   goes on a wrapper around it and the input underneath is set to white. */
.bw-swwrap{ position:relative; flex:none; display:inline-flex;
  width:var(--ctl-h); height:var(--ctl-h); }
.bw-swwrap .bw-sw{ width:100%; height:100%; }
/* Inset by the swatch's own one-pixel edge, or the ends of the line sit just outside the circle
   and it reads as a sticker stuck on rather than a mark on the swatch. */
.bw-swwrap.isnone::after{
  content:""; position:absolute; inset:1px; border-radius:50%; pointer-events:none;
  background:linear-gradient(to top right, transparent 43%, #e5484d 43%, #e5484d 57%, transparent 57%);
}
.bw-sw.isnone{ background:#fff !important; position:relative; }
/* A SEE-THROUGH COLOUR shows on the ring, over a chequer, the way the editor shows one — the
   native box behind it can only ever show a solid colour. */
.bw-swwrap.alpha{ border-radius:50%; overflow:hidden;
  background-image:linear-gradient(45deg,#ccc 25%,transparent 25%,transparent 75%,#ccc 75%),
                   linear-gradient(45deg,#ccc 25%,#fff 25%,#fff 75%,#ccc 75%);
  background-size:10px 10px; background-position:0 0,5px 5px; }
.bw-swwrap.alpha::before{ content:""; position:absolute; inset:0; border-radius:50%; background:var(--picked); pointer-events:none; }
.bw-swwrap.alpha .bw-sw{ opacity:0; }
.bw-hex{
  flex:1 1 0; min-width:0; height:var(--ctl-h); box-sizing:border-box; padding:0 10px;
  border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--panel); color:var(--ink); font:inherit; font-size:12.5px;
  font-variant-numeric:tabular-nums; text-transform:lowercase; outline:none;
}
.bw-hex:focus{ border-color:var(--accent); }
.bw-hex:disabled{ color:var(--faint); background:var(--sunk); }
.bw-none-btn{
  flex:none; height:var(--ctl-h); padding:0 11px; border:1px solid var(--line);
  border-radius:var(--ctl-r); background:var(--panel); color:var(--dim);
  font:inherit; font-size:12px; cursor:pointer;
}
.bw-none-btn[aria-pressed="true"]{ background:var(--ink); border-color:var(--ink); color:#fff; }
.bw-sel2{
  flex:1 1 0; min-width:0; height:var(--ctl-h); padding:0 8px;
  border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--panel); color:var(--ink); font:inherit; font-size:12.5px;
}
/* A section that folds away. The whole heading is the button — a small arrow on its own is a
   target you have to aim at, and there is a whole line here doing nothing else. */
.bw-sec > h3{ display:flex; align-items:center; justify-content:space-between; gap:8px;
  width:100%; padding:0; border:none; background:none; font:inherit; font-size:12.5px;
  font-weight:700; color:var(--ink); text-align:left; cursor:pointer; }
.bw-sec > h3 .bw-caret{ flex:none; color:var(--faint); transition:transform .15s; }
.bw-sec.shut > h3{ margin-bottom:0; }
.bw-sec.shut > h3 .bw-caret{ transform:rotate(-90deg); }
.bw-sec.shut > *:not(h3){ display:none; }
/* The dashed guide showing a cell's inner padding went on 2 September 2026, with the words it
   was drawn for. */
/* ---- choosing a cell (task 12) ---- */
.bw-cell{ cursor:pointer; }
/* The chosen cell wears a ring OUTSIDE its own edge, drawn with an outline rather than a
   border: a border would be part of the box and would move everything inside it by two pixels
   the moment you selected something. */
.bw-cell.on{ outline:2px solid var(--accent); outline-offset:2px; }
/* Where the contents of the chosen cell would sit. Shown only on the chosen one — a dot in
   every cell of a sixteen-cell grid is noise, and this is a cell setting. */
/* Two tabs at the top of the settings panel. The grid's settings never go away — they are one
   press from wherever you are. */
.bw-tabs{ display:flex; gap:3px; flex:none; margin:0 14px 10px; padding:3px;
  background:var(--sunk); border-radius:12px; }
.bw-tab{ flex:1 1 0; height:32px; border:none; border-radius:var(--ctl-r); background:none;
  color:var(--dim); font:inherit; font-size:12.5px; font-weight:600; cursor:pointer; }
.bw-tab:hover:not(:disabled){ color:var(--ink); }
.bw-tab[aria-selected="true"]{ background:var(--panel); color:var(--ink);
  box-shadow:0 1px 2px rgba(16,18,28,.10); }
.bw-tab:disabled{ opacity:.45; cursor:default; }
/* WHAT MAKES INHERITED VISIBLE. A row that is following the grid says so in words; a row that
   has been given something of its own gets a dot and a way back. Without this the panel shows
   a colour in every box and there is no way to tell which of them you chose. */
.bw-ovr{ display:flex; align-items:center; justify-content:space-between; gap:8px;
  margin:12px 0 5px; font-size:11px; color:var(--faint); }
.bw-ovr b{ font-weight:600; color:var(--dim); }
.bw-ovr .bw-mark{ display:inline-flex; align-items:center; gap:5px; font-size:10.5px; }
.bw-ovr .bw-dot{ width:6px; height:6px; border-radius:99px; background:var(--accent); }
.bw-ovr button{ height:24px; padding:0 8px; border:1px solid var(--line); border-radius:8px;
  background:var(--panel); color:var(--dim); font:inherit; font-size:10.5px; cursor:pointer; }
.bw-ovr button:hover{ background:var(--hover); color:var(--ink); }
.bw-own > .bw-ovr b{ color:var(--accent); }
.bw-note{ margin:10px 0 0; padding:9px 11px; border-radius:10px; background:var(--sunk);
  color:var(--dim); font-size:11.5px; line-height:1.5; }
.bw-note.bad{ background:#FFF3F3; color:#B4342F; }
.bw-danger{
  width:100%; height:var(--ctl-h); margin-top:14px;
  border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--panel); color:var(--dim); font:inherit; font-size:12.5px; cursor:pointer;
}
.bw-danger:hover:not(:disabled){ background:var(--hover); color:var(--ink); }
.bw-danger:disabled{ opacity:.4; cursor:default; }
.bw-two-btn{ display:flex; gap:7px; margin-top:9px; }
.bw-two-btn .bw-chip{ flex:1 1 0; height:34px; }

/* THE CORNER CONTROL, laid out the way the editor lays out its own (see .rad-all, .rad-ic and
   .rad-grid, which these mirror measure for measure): a square button and one field on the
   first line, then the four corners as the corners of a box with the lock in the middle. */
.bw-corners{ margin-top:6px; }
.bw-rad-all{ display:flex; align-items:center; gap:8px; margin-bottom:8px; }
.bw-rad-all > .bw-lead{ flex:1; min-width:0; }
.bw-rad-ic{
  width:var(--ctl-h); height:var(--ctl-h); flex:none; display:grid; place-items:center;
  border:1px solid transparent; border-radius:var(--ctl-r); background:var(--ctl-bg);
  color:var(--dim); cursor:pointer; transition:background .12s, color .12s;
}
.bw-rad-ic:hover{ background:var(--ctl-bg-h); color:var(--ink); }
.bw-rad-ic[aria-expanded="true"]{ background:var(--sel-bg); border-color:var(--sel-bg); color:var(--sel-fg); }
.bw-rad-grid{ display:grid; grid-template-columns:1fr 44px 1fr; gap:8px; align-items:center; justify-items:stretch; }
.bw-rad-lock{
  width:100%; height:var(--ctl-h); display:grid; place-items:center;
  border:1px solid transparent; border-radius:var(--ctl-r); background:var(--ctl-bg);
  color:var(--dim); cursor:pointer; transition:background .12s, color .12s;
}
.bw-rad-lock:hover{ background:var(--ctl-bg-h); color:var(--ink); }
.bw-rad-lock[aria-pressed="true"]{ background:var(--sel-bg); border-color:var(--sel-bg); color:var(--sel-fg); }
/* A FIELD WITH A MARK IN FRONT OF ITS VALUE — the editor's lead-in, at the editor's measures:
   the mark sits 12px in, is 19px wide, and the value starts 7px after it. The mark is drawn
   above the field so a fill cannot paint over it, which is the fault the editor hit on 26
   August the day its fields were given a fill. */
.bw-lead{ position:relative; display:block; min-width:0; }
.bw-lead > svg{ position:absolute; left:12px; top:50%; transform:translateY(-50%);
  pointer-events:none; color:var(--faint); z-index:1; }
.bw-lead > .bw-num{ padding-left:calc(12px + 19px + 7px); }


/* ---- pictures in cells (task 13) ---- */
/* The picture is clipped by the cell, so it takes the cell's rounded corners for free rather
   than being told them separately and then disagreeing with them. */
/* THE PICTURE IS DRAWN THE WAY THE EDITOR DRAWS ONE: a frame that clips, with the picture
   inside it at an explicit size and offset. The first version used the browser's own fit-and-
   position with a scale on top, which looks the same and is not — a scale grows the picture
   about its middle while the position places it before the scale, so the two disagree the
   moment you zoom AND move. Worse, the editor has no such arrangement, so the picture on the
   page could not have matched the one in the preview. One model, used in both places. */
.bw-picwrap{ position:absolute; inset:0; overflow:hidden; border-radius:inherit;
  pointer-events:none; }
.bw-pic{ position:absolute; display:block; max-width:none; user-select:none; }
.bw-cell.haspic{ overflow:hidden; }
/* A picture that did not load. Said in the cell itself, where the picture should be — a message
   somewhere else is a message about a cell you then have to go and find. */
.bw-broken{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  padding:10px; text-align:center; font-size:11px; line-height:1.4;
  color:#B4342F; background:repeating-linear-gradient(45deg,#FFF3F3,#FFF3F3 8px,#FFE9E9 8px,#FFE9E9 16px); }
/* The chosen cell with a picture in it can be dragged to move the picture inside its frame. */
.bw-cell.movable{ cursor:grab; }
.bw-cell.moving{ cursor:grabbing; }
.bw-thumb{ width:100%; height:96px; object-fit:cover; border-radius:10px;
  border:1px solid var(--line); background:var(--sunk); display:block; }

/* ---- words in cells (task 14) were here; taken out 2 September 2026 — words are put on the
   page with the editor's own text tool now. ---- */

/* NOTHING BEHIND THE GRID IS SHOWN AS A WHITE SHEET — 13 September 2026, at Mounir's word: "bento
   canvas need to be white by default". It was a chequer until then, the mark every editor uses for
   "see-through", and he looked at it and did not want it. The sheet is the page the grid will land
   on, and pages are white; the grid itself still carries no colour of its own, so on a coloured
   page it lands see-through exactly as before. Only what is SHOWN changed. The class stays, so the
   window still knows the difference between no colour and a white one. */
.bw-sheet.clear{ background:#fff; }
/* A label with a control on the far right of it. The linking button lives here rather than
   beside a box, so that pressing it cannot make it move: the boxes below change, it does not. */
.bw-hdr{ display:flex; align-items:center; justify-content:space-between; gap:8px;
  margin:12px 0 5px; font-size:11px; color:var(--faint); }
/* THE TWO HALVES OF APPEARANCE, TOLD APART. Everything under the second heading is a default
   for cells; everything under the first belongs to the grid itself. They read as one list until
   somebody asked for an outline round the grid and found the only "Border" in the panel put one
   round every cell instead. */
.bw-hdr.big{ margin:16px 0 2px; padding-top:12px; border-top:1px solid var(--line);
  font-size:12px; font-weight:700; color:var(--ink); }
.bw-hdr.big:first-child{ margin-top:2px; padding-top:0; border-top:none; }
/* THE GRID'S OWN OUTLINE, drawn ON the sheet rather than as a border of it. A real border would
   move the sheet's inside by its own width, and every cell is positioned against that inside —
   so a two-pixel outline would shift the whole layout and the preview would stop agreeing with
   the page. */
.bw-frame{ position:absolute; inset:0; pointer-events:none; z-index:4; }
.bw-lock.small{ width:28px; height:28px; border-radius:var(--ctl-r); }
.bw-lock.small svg{ width:13px; height:13px; }
/* The middle. Sunk rather than white so a white grid sitting on it has an edge. */
/* THE ROOM AROUND THE PREVIEW IS THE EDITOR'S OWN WORKSPACE COLOUR. Asked for 10 September
   2026: "bento grid, change work space color to same light grey from the editor". It is taken
   as the variable rather than copied as a number, so it follows the editor into dark mode and
   changes with it — the grid you are laying out sits on the same surface as the page it is
   going onto, which is the point.
   It was #E3E2E1, a warm grey sent as a swatch on 2 September 2026 and read off it pixel by
   pixel. That was chosen so a WHITE cell read as a card lying on a surface; cells went grey on
   10 September and a grey cell on a grey room disappeared. */
.bw{ --stage:var(--canvas); }
/* the dark theme keeps its own, which is darker than the editor's workspace there and was not
   part of what was asked; this rule is more specific, so it wins whenever the theme is dark */
body:not(.light) .bw{ --stage:#1C1C1C; }
.bw-stage{
  position:relative; display:flex; align-items:center; justify-content:center;
  min-width:0; min-height:0; padding:34px; overflow:auto; background:var(--stage);
}
/* ---- the live preview ----
   THE GRID IS DRAWN IN ITS OWN PIXELS AND THE WHOLE SHEET IS SCALED, rather than every cell
   being multiplied by the zoom as it is placed. Two reasons, and the second is the important
   one: the browser scales far better than rounding thirty positions by hand does, and — since
   nothing is recalculated to zoom — what is on screen is the arithmetic from task 2 and nothing
   else. A preview that did its own sums at each zoom level would be a second opinion about the
   layout, and a second opinion is a thing that can differ from the canvas. */
.bw-fit{ position:relative; }
.bw-sheet{
  position:relative; transform-origin:0 0;
  background:#fff; box-shadow:0 1px 3px rgba(16,18,28,.10), 0 12px 34px rgba(16,18,28,.10);
}
.bw-cell{ position:absolute; box-sizing:border-box; }
/* Said in one sentence, in the middle of the preview, when the numbers cannot make a grid.
   It replaces the grid rather than sitting beside it: half a grid and a warning reads as the
   warning being about something else. */
.bw-prob{
  max-width:340px; text-align:center; padding:22px 24px; border-radius:14px;
  background:#fff; border:1px solid #F3D6D6; color:#B4342F; font-size:13px; line-height:1.55;
  box-shadow:0 8px 26px rgba(16,18,28,.08);
}
.bw-empty{ text-align:center; color:var(--faint); font-size:13px; max-width:280px; line-height:1.55; }
.bw-empty b{ display:block; color:var(--dim); font-size:14px; margin-bottom:5px; }
/* A panel with a heading and nothing under it reads as broken rather than as not built yet.
   One quiet line costs nothing and says which of the two it is. */
.bw-soon{ padding:4px 4px 0; color:var(--faint); font-size:12.5px; line-height:1.6; }

/* ================================================================================================
   REARRANGING BY HAND (task 19)
   ================================================================================================ */
/* WHERE IT WOULD LAND. Drawn under the pointer while a cell is being dragged or pulled, in the
   accent when it can go there and in red when it cannot — so the answer is where you are looking
   rather than in a panel at the side of the screen. */
.bw-ghost{
  position:absolute; z-index:6; pointer-events:none; border-radius:10px;
  border:2px dashed var(--accent); background:color-mix(in srgb, var(--accent) 14%, transparent);
}
.bw-ghost.bad{ border-color:#D9453D; background:rgba(217,69,61,.13); }
/* While something is being dragged the cells stop reacting to the pointer, or the one under the
   cursor lights up as if it were about to be chosen. */
.bw-sheet.dragging .bw-cell{ pointer-events:none; }
/* The one being carried fades, so it is obvious which cell the rectangle belongs to when the
   two are far apart. */
.bw-cell.lifting{ opacity:.4; }
/* THE CELL A PICTURE IS ABOUT TO LAND IN. A drag from the desktop carries no preview of its own
   worth looking at, so the only thing that says "this one, not the one next to it" is the cell
   itself lighting up. */
.bw-cell.dropping{
  outline:3px dashed var(--accent); outline-offset:-3px;
  background-image:linear-gradient(color-mix(in srgb, var(--accent) 16%, transparent),
                                   color-mix(in srgb, var(--accent) 16%, transparent));
}
/* AND WHILE IT IS BEING FETCHED. A picture is shrunk, checked and uploaded before it appears,
   which can take a second or two on a big photograph — and a cell that does nothing at all for
   two seconds reads as a drop that missed. */
.bw-cell.busy{ position:relative; }
.bw-cell.busy::after{
  content:""; position:absolute; left:50%; top:50%; width:26px; height:26px;
  margin:-13px 0 0 -13px; border-radius:50%; pointer-events:none;
  border:3px solid color-mix(in srgb, var(--accent) 28%, transparent);
  border-top-color:var(--accent);
  animation:bw-spin .7s linear infinite;
}
@keyframes bw-spin{ to{ transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce){
  .bw-cell.busy::after{ animation-duration:2.2s; }
}
/* A CARD ON ITS WAY SOMEWHERE. Its place and its size move, not a transform: two bento cards are
   usually different shapes, and scaling one into the other's box would squash the photograph and
   the corner radii on the way. Above its neighbours while it travels, so it passes over them
   rather than disappearing behind one. */
.bw-cell.sliding{
  z-index:3;
  transition:left .19s cubic-bezier(.2,.7,.3,1), top .19s cubic-bezier(.2,.7,.3,1),
             width .19s cubic-bezier(.2,.7,.3,1), height .19s cubic-bezier(.2,.7,.3,1);
}
/* And the photograph inside it travels with it, worked out for where the card is going. Without
   this the picture stays the size it was and sits pinned in the corner of a growing card for the
   length of the move. */
.bw-cell.sliding .bw-pic{
  transition:left .19s cubic-bezier(.2,.7,.3,1), top .19s cubic-bezier(.2,.7,.3,1),
             width .19s cubic-bezier(.2,.7,.3,1), height .19s cubic-bezier(.2,.7,.3,1);
}
/* Anybody who has asked their computer for less movement gets none. Everything still ends up in
   the right place; it simply arrives at once. */
@media (prefers-reduced-motion: reduce){
  .bw-cell.sliding, .bw-cell.sliding .bw-pic{ transition:none; }
}
/* THE SENTENCE SAYING WHY NOT, over the grid rather than beside it. A refusal shown in a panel on
   the right is a refusal nobody reads: your eyes are on the thing you are dragging. */
.bw-say{
  position:absolute; left:50%; bottom:16px; transform:translateX(-50%); z-index:20;
  max-width:min(460px, 84%); padding:9px 15px; border-radius:99px;
  background:var(--ink); color:#fff; font-size:12.5px; line-height:1.45; text-align:center;
  box-shadow:0 6px 20px rgba(16,18,28,.22); pointer-events:none;
}
.bw-say.bad{ background:#B4342F; }
/* THE EIGHT HANDLES. Big enough to hit — 15 pixels of target for a 9-pixel dot, because a handle
   you have to aim at is a handle you miss. They sit ON the cell's edge, centred on it. */
.bw-grip{
  position:absolute; z-index:8; width:15px; height:15px; margin:-7.5px 0 0 -7.5px;
  border-radius:50%; background:#fff; border:2px solid var(--accent);
  box-shadow:0 1px 4px rgba(16,18,28,.28);
}
.bw-grip.g-n, .bw-grip.g-s{ cursor:ns-resize; }
.bw-grip.g-e, .bw-grip.g-w{ cursor:ew-resize; }
.bw-grip.g-nw, .bw-grip.g-se{ cursor:nwse-resize; }
.bw-grip.g-ne, .bw-grip.g-sw{ cursor:nesw-resize; }
/* The grip for MOVING a cell that has a picture in it, where dragging the middle moves the
   photograph instead. Tucked into the corner, on the cell, not floating beside it. */
.bw-move{
  position:absolute; z-index:9; display:flex; align-items:center; justify-content:center;
  width:26px; height:26px; margin:5px 0 0 5px; border-radius:9px;
  background:var(--accent); color:#fff; cursor:move;
  box-shadow:0 2px 8px rgba(16,18,28,.3);
}
/* The line between two rows or columns. Invisible until you go near it — a grid covered in
   handles is a grid you cannot see. */
.bw-div{ position:absolute; z-index:5; background:transparent; }
.bw-div.col{ cursor:ew-resize; }
.bw-div.row{ cursor:ns-resize; }
.bw-div::after{
  content:""; position:absolute; inset:0; margin:auto; border-radius:99px;
  background:var(--accent); opacity:0; transition:opacity .12s;
}
.bw-div.col::after{ width:3px; height:100%; }
.bw-div.row::after{ width:100%; height:3px; }
.bw-div:hover::after{ opacity:.85; }
.bw.bw-sizing, .bw.bw-sizing *{ cursor:inherit !important; }
/* WHILE A COLOUR IS BEING PICKED OFF AN APP'S WINDOW, the pointer is the EDITOR'S OWN
   eyedropper — asked for 2 September 2026, "use same eyedropper icon from editor". A crosshair
   said "something is about to happen" without saying what. The cursor below is the same
   declaration as .stage-wrap.tool-eyedropper above, character for character, including the
   2 20 hot spot at the tip of the pipette; a check compares the two so they cannot drift.
   It is set on everything inside the window, because a card, a button and a field all carry
   cursors of their own that would otherwise win. */
.app-picking, .app-picking *{ cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path d="m2 22 1-4 9-9 3 3-9 9z" fill="white" stroke="black" stroke-width="1.4" stroke-linejoin="round"/><path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.9.9a2.1 2.1 0 1 1-3 3l-6-6a2.1 2.1 0 1 1 3-3l.9.9Z" fill="black" stroke="white" stroke-width="1.2" stroke-linejoin="round"/></svg>') 2 20, crosshair !important; }
/* PULLING ONE CARD'S OWN EDGES ABOUT, after double-clicking it. A solid band along each edge that
   has something on the other side of it, and a ring round the card so it is obvious which one is
   being worked on. Unlike the round handles, these are bars: the whole edge is the target,
   because the thing you are moving is the whole edge. */
/* THE FOUR IN THE MIDDLE OF THE SIDES STRETCH THE CARD, and they are bars rather than circles so
   that which handle does which job can be seen rather than remembered: a bar lies along the edge
   it moves, a circle sits on the corner it drags. */
.bw-grip.bar{
  border-radius:99px; background:var(--accent); border:0;
  box-shadow:0 1px 5px rgba(16,18,28,.34);
  transition:transform .12s;
}
.bw-grip.g-e.bar, .bw-grip.g-w.bar{ width:7px; height:30px; margin:-15px 0 0 -3.5px; }
.bw-grip.g-n.bar, .bw-grip.g-s.bar{ width:30px; height:7px; margin:-3.5px 0 0 -15px; }
.bw-grip.g-e.bar:hover, .bw-grip.g-w.bar:hover{ transform:scaleX(1.6); }
.bw-grip.g-n.bar:hover, .bw-grip.g-s.bar:hover{ transform:scaleY(1.6); }
@media (prefers-reduced-motion: reduce){
  .bw-grip.bar{ transition:none; }
  .bw-grip.bar:hover{ transform:none; }
}
/* THE RAILS. A row of small buttons above the grid and down its left side: a plus at every line
   to put a column in there, and a minus on every column to take that one away. Faint until the
   pointer is near them, so they are there when wanted and out of the way when not. */
.bw-rail{ position:absolute; z-index:12; }
.bw-rail.across{ top:-30px; left:0; height:24px; }
.bw-rail.down{ left:-30px; top:0; width:24px; }
.bw-plus, .bw-minus{
  position:absolute; display:flex; align-items:center; justify-content:center;
  width:24px; height:24px; padding:0; border-radius:50%;
  border:1px solid var(--line); background:var(--panel); color:var(--dim);
  font:inherit; font-size:15px; line-height:1; cursor:pointer;
  opacity:.5; transition:opacity .12s, background .12s, color .12s, border-color .12s;
}
.bw-rail.across .bw-plus, .bw-rail.across .bw-minus{ top:0; margin-left:-12px; }
.bw-rail.down .bw-plus, .bw-rail.down .bw-minus{ left:0; margin-top:-12px; }
/* Brought up when the pointer is anywhere over the preview, not only when it is on the thin
   strip itself: a control you have to already be touching to see is one nobody finds. */
.bw-stage:hover .bw-plus, .bw-stage:hover .bw-minus{ opacity:.9; }
.bw-plus:hover{ opacity:1 !important; background:var(--accent); border-color:var(--accent); color:#fff; }
.bw-minus:hover{ opacity:1 !important; background:#B4342F; border-color:#B4342F; color:#fff; }
/* A second chosen cell, for merging. Marked differently from the main one — the main one is the
   one the settings are about and the one that survives a merge — but NOT with a dashed line: the
   room-for-contents guide inside every cell is already dashed, and seen in a picture the two read
   as the same thing. A lighter solid ring says "this one as well" without saying "different kind
   of thing". */
.bw-cell.also{ outline:2px solid color-mix(in srgb, var(--accent) 45%, #fff);
  outline-offset:2px; }
/* Duplicate, merge, split, delete. Real buttons in a row that wraps, with the destructive one
   marked and last. */
.bw-ops{ display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
.bw-op{
  flex:1 1 calc(50% - 3px); min-width:0; height:34px; padding:0 10px;
  border:1px solid var(--line); border-radius:var(--ctl-r);
  background:var(--panel); color:var(--ink); font:inherit; font-size:12.5px; cursor:pointer;
}
.bw-op:hover:not(:disabled){ background:var(--hover); border-color:var(--line-h); }
.bw-op:disabled{ opacity:.4; cursor:default; }
.bw-op.danger{ flex:1 1 100%; color:#B4342F; }
.bw-op.danger:hover{ background:#FDF2F1; border-color:#F0C9C6; }

/* ================================================================================================
   SAVED LAYOUTS AND STARS (task 19b)
   ================================================================================================ */
/* A layout in the list is now a row: the layout itself, a star, and — for your own — rename, copy
   and delete. The card stays one big target; the small buttons sit on top of it at the end. */
.bw-row{ position:relative; }
.bw-row .bw-item{ padding-right:34px; }
.bw-row.mine .bw-item{ padding-right:34px; padding-bottom:30px; }
.bw-star{
  position:absolute; top:8px; right:7px; display:flex; align-items:center; justify-content:center;
  width:26px; height:26px; padding:0; border:none; border-radius:50%;
  background:none; color:var(--line-h); cursor:pointer; transition:color .12s, background .12s;
}
.bw-star:hover{ background:var(--hover); color:var(--dim); }
/* A STAR THAT IS ON IS FILLED AND COLOURED; one that is off is an outline. Two shades of the same
   grey would mean looking from one to another to tell which is which. */
.bw-star.on{ color:#E8A317; }
.bw-star.on:hover{ color:#C9880A; }
.bw-star svg{ fill:none; }
.bw-star.on svg path{ fill:currentColor; }
/* Rename, copy and delete. Only on your own layouts, and only once the row is under the pointer
   or has been reached by the keyboard — three buttons on every one of thirty-two cards would be
   a wall of icons. */
.bw-tools{
  position:absolute; left:9px; bottom:7px; display:flex; gap:3px;
  opacity:0; transition:opacity .12s;
}
.bw-row.mine:hover .bw-tools, .bw-tools:focus-within{ opacity:1; }
.bw-tool{
  display:flex; align-items:center; justify-content:center;
  width:26px; height:26px; padding:0; border:1px solid var(--line); border-radius:8px;
  background:var(--panel); color:var(--dim); cursor:pointer;
}
.bw-tool:hover{ background:var(--hover); color:var(--ink); border-color:var(--line-h); }
.bw-tool.danger:hover{ background:#FDF2F1; border-color:#F0C9C6; color:#B4342F; }
/* Save this layout sits under Start from a blank grid and reads as its pair. */
.bw-blank.keep{ margin-top:7px; }
.bw-soon.bad{ color:#B4342F; }

/* THE SMALL PANEL THAT ASKS FOR A NAME, OR FOR A YES. Not the browser's own prompt: that cannot
   say why something was refused, cannot be styled to match anything, and inside a full-screen
   window some browsers will not show it at all. */
.bw-ask-back{
  position:absolute; inset:0; z-index:60; display:flex; align-items:center; justify-content:center;
  padding:24px; background:rgba(16,18,28,.42);
}
.bw-ask{
  width:min(420px, 100%); box-sizing:border-box; padding:22px 22px 18px;
  border-radius:18px; background:var(--panel); color:var(--ink);
  box-shadow:0 18px 50px rgba(16,18,28,.30);
}
.bw-ask h3{ margin:0; font-size:16px; font-weight:700; }
.bw-ask-sub{ margin:8px 0 0; font-size:12.5px; line-height:1.6; color:var(--faint); }
.bw-ask .bw-inp{ margin-top:14px; }
.bw-ask-why{ margin:10px 0 0; font-size:12.5px; line-height:1.55; color:#B4342F; }
.bw-ask-act{ display:flex; justify-content:flex-end; gap:8px; margin-top:18px; }
.bw-ask-act .bw-op{ flex:0 0 auto; min-width:96px; }
.bw-ask-act .bw-go{ min-width:132px; }
.bw-ask-act .bw-go.danger{ background:#B4342F; }
.bw-ask-act .bw-go.danger:hover:not(:disabled){ background:#96291F; }

/* =================================================================================================
   FILLED CONTROLS, NO OUTLINES — the editor's rule, applied to this window.
   Asked for 2 September 2026, with three pictures of the editor's own controls.

   The editor's right panel settled this on 26 August: every field, dropdown and unselected button
   sits on a pale fill and carries no stroke at all; hover lifts the fill rather than darkening an
   edge that is not there; focus is the one time an edge comes back. This window was built after
   that and did the opposite — white boxes with a one-pixel grey line round each — so the two
   looked like two different products, which is what "cohesive" is about.

   AND A CHOSEN BUTTON IS BLUE, NOT BLACK. The editor marks a chosen thing with its accent; this
   window used near-black. Blue is the one colour that means "this one" everywhere else in the
   product, and a second meaning for black is a second thing to learn.

   THE BORDER IS KEPT AT ZERO WIDTH rather than removed — transparent, not none — so nothing
   shifts by two pixels when focus brings the ring back. Same reason as the editor's.
   ================================================================================================= */
.bw-inp, .bw-num, .bw-sel, .bw-sel2, .bw-hex,
.bw-chip, .bw-wide, .bw-danger, .bw-lock, .bw-none-btn, .bw-tool, .bw-op{
  background:var(--ctl-bg); border-color:transparent;
}
.bw-inp:hover, .bw-num:hover, .bw-sel:hover, .bw-sel2:hover, .bw-hex:hover:not(:disabled),
.bw-chip:hover, .bw-wide:hover, .bw-danger:hover:not(:disabled), .bw-lock:hover,
.bw-none-btn:hover, .bw-tool:hover, .bw-op:hover:not(:disabled){
  background:var(--ctl-bg-h); border-color:transparent;
}
.bw-inp:focus, .bw-num:focus, .bw-sel:focus, .bw-sel2:focus, .bw-hex:focus{
  border-color:var(--accent);
}
/* CHOSEN: the accent, with white writing on it. */
.bw-chip[aria-pressed="true"], .bw-none-btn[aria-pressed="true"], .bw-lock[aria-pressed="true"],
.bw-chip[aria-pressed="true"]:hover, .bw-none-btn[aria-pressed="true"]:hover,
.bw-lock[aria-pressed="true"]:hover{
  background:var(--sel-bg); border-color:var(--sel-bg); color:var(--sel-fg);
}
/* A number out of range still goes red, and the red has to beat the fill above. */
.bw-num.bad, .bw-num.bad:hover{ border-color:#D9534F; background:#FFF7F7; }
.bw-hex:disabled{ background:var(--sunk); }
/* The one real action on the bar is the editor's blue, not near-black — it is the same button as
   the editor's Download, in the same place, and they were two different colours. */
.bw-go{ background:var(--sel-bg); }
/* and the darker step under the pointer is the editor's own, since 6 September 2026 — it was
   #2F4FC4, a darker version of the blue this button used to be */
.bw-go:hover:not(:disabled){ background:var(--accent-h); }
/* Taking a cell out is the one destructive thing in the panel, and it says so in the editor's
   own red rather than in grey. */
.bw-tool.danger:hover{ background:#FDF2F1; border-color:transparent; color:#B4342F; }
.bw-op.danger:hover{ background:#FDF2F1; border-color:transparent; }

/* On a narrow window the library folds away rather than squeezing the preview into a strip.
   Asked for in the plan: the settings stay reachable and the preview keeps the room. */
@media (max-width:1100px){
  .bw-body{ grid-template-columns:1fr 288px; }
  .bw-side.left{ display:none; }
}
@media (max-width:820px){
  .bw-body{ grid-template-columns:1fr; }
  .bw-side.right{ display:none; }
}

/* =================================================================================================
   LAYOUT GUIDES — the panel.  Task 3 of the guides plan, 3 September 2026.

   IT IS SMALL AND IT SITS OUT OF THE WAY, which is the whole difference between this and the
   grid builder's window. The thing being judged here is the PAGE — the guides on it and the
   artwork lined up against them — so a panel covering the page would hide the only thing worth
   looking at while the numbers were being changed. It takes the width the editor's own settings
   panel already is, and sits just to the left of it.

   EVERYTHING ABOUT WHERE IT SITS IS IN THIS RULE, WHICH APPLIES IN EVERY THEME. On 2 September
   2026 the grid builder's window was given dark-theme colours and its rule was closed one line
   too early, so "fixed, on top" ended up inside the dark-only block — and in a light editor the
   window had no position at all and rendered as an ordinary block underneath the page. The theme
   block below carries COLOURS AND NOTHING ELSE, and a check opens this panel in both themes and
   measures where it lands.
   ================================================================================================= */
.gw{
  --ctl-h:34px; --ctl-r:4px;
  --line:#EEEFF3; --line-h:#DCDEE6;
  --ink:#16161A; --dim:#6C7080; --faint:#9AA0B0;
  --panel:#FFFFFF; --ctl-bg:#F7F7F8; --ctl-bg-h:#EFEFF1; --accent:#3D95FF;

  /* Above the floating island at 120, below the toast at 200 — a message about something going
     wrong has to be able to reach the front from anywhere, including from behind this. */
  position:fixed; z-index:150;
  /* Clear of the editor's settings panel, which is 300 wide, with the same 16 either side. */
  top:74px; right:316px; width:300px;
  max-height:calc(100vh - 150px);
  display:flex; flex-direction:column; overflow:hidden;
  background:var(--panel); color:var(--ink);
  border:1px solid var(--line);
  /* Real rounding rather than a token 6, and a shadow soft enough to read as floating rather
     than as a box that has been dropped on the page. */
  border-radius:14px;
  box-shadow:0 12px 34px rgba(16,18,28,.16), 0 2px 6px rgba(16,18,28,.06);
  font-size:13px;
  /* The panel holds the focus when it opens so a keyboard is inside it rather than on the page
     behind. Left alone the browser draws a ring round the whole panel; the ring belongs on the
     controls, which keep theirs. */
  outline:none;
}
body:not(.light) .gw{
  --line:#333333; --line-h:#454545;
  --ink:#F2F2F2; --dim:#B3B3B3; --faint:#8C8C8C;
  --panel:#232323; --ctl-bg:#2A2A2A; --ctl-bg-h:#333333; --accent:#3D95FF;
  box-shadow:0 12px 34px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.3);
  /* COLOURS ONLY. Nothing about where the panel sits or how it is laid out belongs in a theme
     block — see the note in the rule above. */
}

/* ---- the row across the top: what this is, a way to add, and the way out ---- */
.gw-top{
  flex:none; display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:9px 9px 9px 14px; border-bottom:1px solid var(--line);
}
.gw-title{ font-size:14px; font-weight:650; letter-spacing:-.01em; }
.gw-topacts{ display:flex; align-items:center; gap:2px; }

/* Generous enough to hit without aiming — 34 square rather than the 20 an X usually gets. */
.gw-x{
  flex:none; width:34px; height:34px; display:flex; align-items:center; justify-content:center;
  border:none; border-radius:9px; background:transparent; color:var(--dim); cursor:pointer;
}
.gw-x:hover{ background:var(--ctl-bg-h); color:var(--ink); }
.gw-x:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }

/* ---- where every kind's settings will go ---- */
.gw-body{ flex:1; min-height:0; overflow:auto; padding:14px 14px 16px; }
.gw-soon{ margin:0; color:var(--dim); font-size:12.5px; line-height:1.55; }
/* WHAT A SET CANNOT DO ON THIS PAGE, said rather than left as a guide that has silently
   stopped appearing. The numbers are never corrected behind anybody's back, so this is the only
   way they find out that a page has been resized past what their guides can manage. */
.gw-warn{
  margin:0 0 10px; padding:8px 10px; border-radius:9px;
  background:rgba(224,72,63,.10); color:#C23A32;
  font-size:12px; line-height:1.45;
}
body:not(.light) .gw-warn{ background:rgba(224,72,63,.16); color:#FF9A93; }
/* and a mark on the row, so the one that cannot draw is found without opening each in turn */
.gw-item.warn .gw-iname::after{
  content:"!"; display:inline-flex; align-items:center; justify-content:center;
  width:14px; height:14px; margin-left:6px; border-radius:50%;
  background:#E0483F; color:#fff; font-size:10px; font-weight:700; vertical-align:1px;
}

/* a line of explanation under a control, for when a setting depends on something that is not
   there yet — said rather than left for somebody to work out from the page */
.gw-note{
  margin:2px 0 6px; padding:0 2px; color:var(--faint); font-size:11.5px; line-height:1.45;
}

/* the two that apply to everything at once, above the list because they belong to none of them */
.gw-all{
  flex:none; display:flex; align-items:center; gap:6px;
  padding:8px 10px 8px 6px; border-bottom:1px solid var(--line);
}
.gw-alleye{
  flex:none; width:28px; height:28px; display:flex; align-items:center; justify-content:center;
  border:none; border-radius:7px; background:transparent; color:var(--dim); cursor:pointer;
}
.gw-alleye:hover{ background:var(--ctl-bg-h); color:var(--ink); }
.gw-alleye.off{ color:var(--faint); }
.gw-alleye:focus-visible{ outline:2px solid var(--accent); outline-offset:-1px; }
.gw-alllab{ flex:1; min-width:0; font-size:12.5px; color:var(--dim); }
.gw-all.off .gw-alllab{ color:var(--faint); text-decoration:line-through; }
.gw-allfade{
  flex:none; display:flex; align-items:center; height:28px; padding:0 2px 0 6px;
  border-radius:8px; background:var(--ctl-bg);
}
.gw-allfade .gw-pct{ width:28px; }

/* ---- the sets on this page ----
   SHOW, HIDE AND LOCK ARE ON THE ROW, one press each, because they are what gets done most.
   Everything else is behind a visible button rather than a double-click or a long press: a
   gesture nobody can see is a feature nobody finds, which is what happened when resizing a cell
   in the grid builder was put behind a double-click and came back reported as broken. */
.gw-list{
  flex:none; max-height:186px; overflow:auto;
  padding:6px; border-bottom:1px solid var(--line);
}
.gw-item{
  display:flex; align-items:center; gap:2px;
  height:34px; padding:0 2px 0 4px; border-radius:9px; cursor:pointer;
}
.gw-item:hover{ background:var(--ctl-bg); }
.gw-item.on{ background:var(--ctl-bg-h); }
.gw-item.on .gw-iname{ font-weight:600; }
/* A hidden set stays in the list, greyed. Hiding is not deleting, and a set that vanished from
   the list when hidden would be a set nobody could bring back. */
.gw-item.off .gw-iname{ color:var(--faint); text-decoration:line-through; }
.gw-iname{
  flex:1; min-width:0; padding:0 4px; font-size:13px; color:var(--ink);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.gw-rename{
  flex:1; min-width:0; height:26px; margin:0 2px; padding:0 7px;
  border:1px solid var(--accent); border-radius:7px;
  background:var(--panel); color:var(--ink); font:inherit; font-size:13px;
}
.gw-rename:focus{ outline:none; }
.gw-eye, .gw-lock, .gw-more{
  flex:none; width:28px; height:28px; display:flex; align-items:center; justify-content:center;
  border:none; border-radius:7px; background:transparent; color:var(--faint); cursor:pointer;
}
.gw-eye{ color:var(--dim); }
.gw-item.off .gw-eye{ color:var(--faint); }
.gw-eye:hover, .gw-lock:hover, .gw-more:hover{ background:var(--panel); color:var(--ink); }
.gw-item.on .gw-eye:hover, .gw-item.on .gw-lock:hover, .gw-item.on .gw-more:hover{ background:var(--panel); }
.gw-lock.on{ color:var(--accent); }
.gw-eye:focus-visible, .gw-lock:focus-visible, .gw-more:focus-visible{
  outline:2px solid var(--accent); outline-offset:-1px;
}

/* one small menu, used both for choosing what to add and for what to do with a set */
.gw-menu{
  position:absolute; z-index:5; min-width:152px; padding:5px;
  border:1px solid var(--line); border-radius:11px; background:var(--panel);
  box-shadow:0 10px 28px rgba(16,18,28,.18), 0 2px 6px rgba(16,18,28,.08);
}
body:not(.light) .gw-menu{ box-shadow:0 10px 28px rgba(0,0,0,.55), 0 2px 6px rgba(0,0,0,.35); }
.gw-mitem{
  display:block; width:100%; height:30px; padding:0 10px; text-align:left;
  border:none; border-radius:7px; background:transparent; color:var(--ink);
  font:inherit; font-size:13px; cursor:pointer;
}
.gw-mitem:hover:not(:disabled){ background:var(--ctl-bg-h); }
.gw-mitem:disabled{ color:var(--faint); cursor:default; }
.gw-mitem.danger:not(:disabled){ color:#E0483F; }
.gw-mline{ margin:5px 8px; border:none; border-top:1px solid var(--line); }

/* A locked set shows its settings and refuses to change them. Greyed rather than hidden: an
   empty panel would read as a set with nothing in it. */
.gw-body.locked{ opacity:.55; }

/* ---- one setting per row: what it is on the left, the control on the right ---- */
.gw-row{ display:flex; align-items:center; gap:10px; min-height:38px; }
.gw-row + .gw-row{ margin-top:2px; }
.gw-lab{ flex:none; width:74px; color:var(--dim); font-size:12.5px; }
.gw-ctl{ flex:1; min-width:0; display:flex; align-items:center; gap:6px; justify-content:flex-end; }
/* Greyed rather than taken away, so the row does not appear and vanish under the pointer when
   the mode changes — and the number that was typed is still there when the mode is put back. */
.gw-row.off .gw-lab{ color:var(--faint); }

.gw-num{
  width:100%; max-width:150px; height:var(--ctl-h); min-width:0;
  padding:0 10px; border-radius:var(--ctl-r); border:1px solid transparent;
  background:var(--ctl-bg); color:var(--ink); font:inherit; font-size:13px; text-align:left;
}
.gw-num:hover:not(:disabled){ background:var(--ctl-bg-h); }
.gw-num:focus{ outline:none; border-color:var(--accent); background:var(--panel); }
.gw-num:disabled{ color:var(--faint); cursor:default; }
/* Out of range WHILE STILL BEING TYPED: said, not corrected. Nothing is ever snapped back
   under the cursor — see the note at the top of the guides app. */
.gw-num.bad, .gw-hex.bad, .gw-pct.bad{ border-color:#E0483F; color:#E0483F; }

.gw-sel{
  appearance:none; -webkit-appearance:none;
  width:100%; max-width:150px; height:var(--ctl-h);
  padding:0 26px 0 10px; border-radius:var(--ctl-r); border:1px solid transparent;
  background:var(--ctl-bg); color:var(--ink); font:inherit; font-size:13px; cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%236C7080' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
  background-repeat:no-repeat; background-position:right 9px center;
}
body:not(.light) .gw-sel{
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%23B3B3B3' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
}
.gw-sel:hover{ background-color:var(--ctl-bg-h); }
.gw-sel:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }

/* the left and right margins, two boxes under one word */
.gw-pair{ display:flex; align-items:center; gap:4px; flex:1; min-width:0; }
.gw-pair .gw-num{ max-width:none; text-align:center; padding:0 4px; }
.gw-mini{ flex:none; color:var(--faint); font-size:11px; width:9px; text-align:center; }

/* the chain that says whether the four sides move together */
.gw-chain{
  flex:none; width:26px; height:26px; display:flex; align-items:center; justify-content:center;
  border:none; border-radius:7px; background:transparent; color:var(--faint); cursor:pointer;
}
.gw-chain:hover{ background:var(--ctl-bg-h); color:var(--ink); }
.gw-chain.on{ color:var(--accent); }
.gw-chain:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }
/* the second half of a pair of rows, which has no word of its own */
.gw-row.cont{ margin-top:4px; }
/* NO RULE HERE FOR [hidden]. The stylesheet already has one global one that beats everything,
   and a check exists specifically to stop a second being added — eight one-off patches had
   accumulated before that rule was written. It caught this the day it was added. */

/* the swatch, the code and the percentage, in the editor's own order */
.gw-col{
  display:flex; align-items:center; gap:0; flex:1; min-width:0; max-width:150px;
  height:var(--ctl-h); padding:0 2px 0 3px;
  border-radius:var(--ctl-r); background:var(--ctl-bg);
}
.gw-sw{
  flex:none; width:24px; height:24px; padding:0; border:none; border-radius:6px;
  background:transparent; cursor:pointer;
}
.gw-sw::-webkit-color-swatch-wrapper{ padding:0; }
.gw-sw::-webkit-color-swatch{ border:1px solid rgba(0,0,0,.14); border-radius:6px; }
.gw-hex{
  flex:1; min-width:0; height:100%; padding:0 4px 0 7px;
  border:none; background:transparent; color:var(--ink);
  font:inherit; font-size:13px; letter-spacing:.02em; text-transform:uppercase;
}
.gw-pct{
  flex:none; width:30px; height:100%; padding:0; text-align:right;
  border:none; background:transparent; color:var(--ink); font:inherit; font-size:13px;
}
.gw-hex:focus, .gw-pct:focus{ outline:none; }
.gw-sign{ flex:none; padding:0 7px 0 2px; color:var(--faint); font-size:12px; }

/* two buttons that are one choice */
.gw-seg{ display:flex; gap:3px; padding:3px; border-radius:11px; background:var(--ctl-bg); }
.gw-segb{
  flex:1; height:26px; padding:0 12px; border:none; border-radius:8px;
  background:transparent; color:var(--dim); font:inherit; font-size:12.5px; font-weight:600;
  cursor:pointer; white-space:nowrap;
}
.gw-segb:hover:not(.on){ color:var(--ink); }
.gw-segb.on{ background:var(--accent); color:#FFFFFF; }
.gw-segb:focus-visible{ outline:2px solid var(--accent); outline-offset:1px; }

/* ---- the library of ready-made guides ----
   IT TAKES THE PANEL OVER RATHER THAN SITTING BESIDE IT. A column beside this one would make the
   whole thing about twice as wide, and the reason the panel is narrow is that the thing being
   judged is the page. A panel that covers the artwork in order to show pictures OF guides has
   its priorities backwards. */
.gw-lib{ flex:1; min-height:0; display:flex; flex-direction:column; }
.gw.libopen{ max-height:calc(100vh - 120px); }
.gw-libhead{
  flex:none; display:flex; align-items:center; justify-content:space-between; gap:8px;
  padding:8px 10px 8px 6px; border-bottom:1px solid var(--line);
}
.gw-back{
  display:flex; align-items:center; gap:3px; height:30px; padding:0 10px 0 6px;
  border:none; border-radius:8px; background:transparent; color:var(--dim);
  font:inherit; font-size:13px; font-weight:500; cursor:pointer;
}
.gw-back:hover{ background:var(--ctl-bg-h); color:var(--ink); }
.gw-savebtn{
  height:30px; padding:0 11px; border:1px solid var(--line-h); border-radius:8px;
  background:transparent; color:var(--ink); font:inherit; font-size:12.5px; font-weight:500;
  cursor:pointer;
}
.gw-savebtn:hover:not(:disabled){ background:var(--ctl-bg-h); }
.gw-savebtn:disabled{ color:var(--faint); border-color:var(--line); cursor:default; }

.gw-libfind{
  flex:none; margin:10px 12px 0; height:32px; padding:0 11px;
  border:1px solid transparent; border-radius:9px;
  background:var(--ctl-bg); color:var(--ink); font:inherit; font-size:13px;
}
.gw-libfind:focus{ outline:none; border-color:var(--accent); background:var(--panel); }
/* THE FILTERS DO NOT SCROLL. Scrolled out of sight, a filter that is on is a filter you cannot
   see, and a library showing two of eleven for no visible reason reads as broken. */
.gw-chips{ flex:none; display:flex; flex-wrap:wrap; gap:5px; padding:9px 12px 10px; }
.gw-chip{
  height:26px; padding:0 10px; border:none; border-radius:13px;
  background:var(--ctl-bg); color:var(--dim); font:inherit; font-size:12px; font-weight:500;
  cursor:pointer; white-space:nowrap;
}
.gw-chip:hover:not(.on){ background:var(--ctl-bg-h); color:var(--ink); }
.gw-chip.on{ background:var(--accent); color:#FFFFFF; }

.gw-cards{ flex:1; min-height:0; overflow:auto; padding:0 12px 14px; }
/* THE PICTURE ON THE LEFT, THE NAME AND THE BUTTONS BESIDE IT. Asked for 3 September 2026,
   straight after the sentence under each name came off: with the description gone the card was a
   square picture with a wide empty column to the right of it and everything stacked underneath,
   which is a lot of scrolling to see nine choices. Side by side, a card is the height of its own
   picture and three of them fit where two did.
   THE PICTURE KEEPS ITS SIZE AND THE REST TAKES WHAT IS LEFT — the thumbnail is drawn at a fixed
   size and squeezing it would make a twelve-column grid unreadable at exactly the moment somebody
   is trying to tell it apart from a six. */
.gw-card{ padding:10px 0; display:flex; align-items:center; gap:12px; }
.gw-card + .gw-card{ border-top:1px solid var(--line); }
.gw-pic{ display:flex; flex:none; }
.gw-thumb{ border-radius:5px; overflow:hidden; display:block; }
.gw-thumbbg{ fill:#FFFFFF; stroke:var(--line-h); stroke-width:1; }
body:not(.light) .gw-thumbbg{ fill:#2E2E2E; }
/* The name and the buttons are one column of their own, filling whatever the picture leaves. */
.gw-cardside{ flex:1; min-width:0; display:flex; flex-direction:column; gap:8px; }
.gw-cardwords{ display:flex; flex-direction:column; gap:1px; }
.gw-cardname{ font-size:13px; font-weight:600; }
/* A LONG NAME IS CUT, NOT WRAPPED. Somebody's saved layout can be called anything; wrapped onto
   three lines it would push the buttons out of line with the picture beside it. */
.gw-cardname{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* ONE LINE, NEVER TWO. The thumbnail beside it is sized so these fit; wrapping is the thing
   that is being avoided, so it is turned off rather than left as a fallback that hides the day
   the sizes stop fitting. */
.gw-cardacts{ display:flex; align-items:center; gap:5px; flex-wrap:nowrap; }
/* …except on one of your own, which has a fourth button and needs the room. */
.gw-card.mine .gw-cardacts{ flex-wrap:wrap; }
.gw-star{
  flex:none; width:28px; height:28px; display:flex; align-items:center; justify-content:center;
  border:none; border-radius:7px; background:transparent; color:var(--faint); cursor:pointer;
}
.gw-star:hover{ background:var(--ctl-bg-h); color:var(--ink); }
.gw-star.on{ color:#E8A33D; }
/* THE SAFE ONE IS THE PLAIN ONE AND IT COMES FIRST. The one that throws work away says so in
   words rather than in an icon, and wears the colour of something to think about. */
.gw-cardadd{
  height:28px; padding:0 13px; border:none; border-radius:8px;
  background:var(--accent); color:#FFFFFF; font:inherit; font-size:12.5px; font-weight:600;
  cursor:pointer;
}
.gw-cardadd:hover{ filter:brightness(1.06); }
.gw-cardrep{
  height:28px; padding:0 11px; border:1px solid rgba(224,72,63,.45); border-radius:8px;
  background:transparent; color:#D2453C; font:inherit; font-size:12.5px; font-weight:500;
  cursor:pointer; white-space:nowrap;
}
.gw-cardrep:hover{ background:rgba(224,72,63,.09); }
body:not(.light) .gw-cardrep{ color:#FF9A93; border-color:rgba(255,154,147,.42); }
.gw-cardacts .gw-more{ margin-left:auto; }
.gw-star:focus-visible, .gw-cardadd:focus-visible, .gw-cardrep:focus-visible,
.gw-chip:focus-visible, .gw-back:focus-visible, .gw-savebtn:focus-visible{
  outline:2px solid var(--accent); outline-offset:1px;
}

/* ---- the way out, and the one real action ----
   ONE FILLED BUTTON. Cancel is a word rather than a button of equal weight: there is one thing
   to do here and one way of backing out, and dressing them the same makes a person stop and read
   both before pressing either. */
.gw-foot{
  flex:none; display:flex; align-items:center; justify-content:flex-end; gap:6px;
  padding:10px 12px; border-top:1px solid var(--line);
}
.gw-cancel{
  height:34px; padding:0 12px; border:none; border-radius:var(--ctl-r);
  background:transparent; color:var(--dim); font:inherit; font-size:13px; font-weight:500;
  cursor:pointer;
}
.gw-cancel:hover{ background:var(--ctl-bg-h); color:var(--ink); }
.gw-go{
  height:34px; padding:0 18px; border:none; border-radius:var(--ctl-r);
  background:var(--accent); color:#FFFFFF; font:inherit; font-size:13px; font-weight:600;
  cursor:pointer;
}
.gw-go:hover{ filter:brightness(1.06); }
.gw-cancel:focus-visible, .gw-go:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

/* On a narrow window there is no room to sit beside the settings panel, so it sits over the
   canvas instead of being pushed off the screen entirely. */
@media (max-width:900px){
  .gw{ right:16px; top:66px; }
}

/* =================================================================================================
   THE TEXT EFFECTS WINDOW
   =================================================================================================
   Task 5, 4 September 2026. Three columns: the effect library, the live page, the settings. It takes
   over the window rather than floating in it — a bent headline is judged by looking at it, so the
   page gets the whole middle.

   THE PURPLE IS DEFINED ONCE, HERE. This editor's accent is blue and the standing rule is to use the
   palette that is already there; this window is the exception, asked for by name, because the design
   it has to match is purple throughout. Everything below reads these three and nothing outside this
   window reads them at all.
   ================================================================================================= */
/* THE PURPLE ITSELF, WRITTEN DOWN ONCE, HERE. It has two readers that are not related to each
   other on the page — the window's own panels, and the handles drawn over the canvas, which live
   inside the stage and cannot inherit anything from the window. Declared on the page itself so
   both can read the same one; a second copy beside the handles is a second copy that drifts, and
   the way anybody would find out is by changing the button and finding the dots still the old
   colour. */
body{ --tfx-blue:#3D95FF; --tfx-blue-h:#3480DB; }
.tfx{
  --tfx-accent:var(--tfx-blue);     /* the filled button, the active tab's underline */
  /* AND THE SHARED SLIDER RULES HAVE TO FIND IT TOO. Found 6 September 2026, from a picture
     Mounir sent of the Wavelength and Phase sliders still royal blue after the rest of this
     window had gone light blue. The sliders are painted by the editor-wide range rules, which
     read --accent — and this window is appended to the page OUTSIDE .editor, so --accent fell
     through to the site's own royal blue rather than the editor's. Setting it here means every
     shared rule that reaches into this window paints in the window's colour. */
  --accent:var(--tfx-blue); accent-color:var(--tfx-blue);
  --tfx-soft:#E8F2FF;          /* the chosen card's fill */
  --tfx-line:#94C5FF;          /* the chosen card's border */
  /* ONE HEIGHT FOR EVERY FIELD IN THIS PANEL. Asked for on 6 September 2026 with a picture:
     the weight menu and the colour row were 42 and the size box was 38, which reads as one of
     them being slightly broken rather than as a size. Written once so the three cannot drift. */
  --tfx-fh:42px;
  --tfx-left-w:300px;
  --tfx-right-w:340px;
  --tfx-paper:#FFFFFF;
  --tfx-ink:#111111;
  --tfx-dim:#6B7280;
  --tfx-edge:#E7E7EA;
  position:fixed; inset:0; z-index:200;
  /* THE MIDDLE IS LEFT ALONE so the real canvas shows through and stays usable. Only the two
     panels take presses; the space between them belongs to the editor. */
  pointer-events:none;
  font-size:13px; color:var(--tfx-ink);
}
.tfx-left, .tfx-right{
  position:absolute; top:var(--tfx-top-h,0px); bottom:0; pointer-events:auto;
  background:var(--tfx-paper); display:flex; flex-direction:column; min-height:0;
}
.tfx-left{ left:0; width:var(--tfx-left-w); border-right:1px solid var(--tfx-edge); }
.tfx-right{ right:0; width:var(--tfx-right-w); border-left:1px solid var(--tfx-edge); }

/* ---- THE BAR ACROSS THE TOP — 13 September 2026 ----
   The same shape as the bento grid's: sixty tall, the app's name at the left, the zoom in the
   middle, a line under it. It spans the whole window, over the editor's own bar, which is put
   away while this is open; so nothing of the editor's shows in the room. The two panels start
   under it, from the same number, so the three cannot overlap or leave a gap. */
.tfx-top{
  position:absolute; left:0; right:0; top:0; height:var(--tfx-top-h,60px); pointer-events:auto;
  display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:10px;
  padding:0 14px 0 18px; background:var(--tfx-paper); border-bottom:1px solid var(--tfx-edge);
}
.tfx-name{ display:flex; align-items:center; gap:9px; font-size:15px; font-weight:700; letter-spacing:-.01em; }
.tfx-name .app-art{ margin-right:0; }
/* Undo and redo in one pill, the zoom in the next, eight pixels apart — the bento grid's bar. */
.tfx-topmid{ display:flex; align-items:center; gap:8px; justify-self:center; }
.tfx-zoomset{
  display:flex; align-items:center; gap:2px; padding:3px; border-radius:12px;
  background:#F5F6F9;
}
.tfx-zbtn:disabled{ opacity:.35; cursor:default; }
.tfx-zbtn:disabled:hover{ background:none; }
.tfx-zbtn, .tfx-zoom{
  appearance:none; border:none; background:none; font:inherit; color:var(--tfx-ink); cursor:pointer;
  height:36px; border-radius:10px; display:inline-flex; align-items:center; justify-content:center;
}
.tfx-zbtn{ min-width:36px; font-size:17px; }
.tfx-zoom{ min-width:56px; padding:0 8px; font-size:13px; color:var(--tfx-dim); font-variant-numeric:tabular-nums; }
.tfx-zbtn:hover, .tfx-zoom:hover{ background:#FFF; color:var(--tfx-ink); }
.tfx-topend{ justify-self:end; }

/* -------------------------------------------------------------------------------------------------
   THE MIDDLE SHOWS THE REAL PAGE — and did again from 18 September 2026
   -------------------------------------------------------------------------------------------------
   For five days this space held the opposite. On 13 September Mounir asked for "a work space and
   canvas, similar to the bento grid app, but canvas needs to be white and not transparent", so four
   rules lived here: the other pages hidden, their names hidden, the page painted white whatever its
   own colour, and every object on it made invisible except the one headline being bent.

   He asked for it back the other way on 18 September 2026 — "i want the real page showing through
   the middle again" — so all four are gone and nothing replaces them. The gap between the two
   panels is simply left alone: the page underneath is the page, with its own colour, its own
   objects, the pages beside it and the names above them, exactly as the editor draws it. The
   window still takes the room, still puts the editor's rail and bars away, still re-centres and
   re-zooms; it just no longer repaints what is in the middle.

   IF IT EVER COMES BACK, it belongs here and nowhere else, and it needs the rule that names the
   one visible headline to come back with it — a rule rather than a mark on the object, because the
   editor rebuilds its objects on every slider move and a class would be gone by the next redraw. */

/* -------------------------------------------------------------------------------------------------
   AN APP BORROWING THE WHOLE WINDOW
   -------------------------------------------------------------------------------------------------
   Not part of the text effects window — the editor's own side of it, so a second app that wants the
   room later gets the same behaviour rather than writing its own. The app says how wide its panels
   are; these two numbers arrive from the editor as the gap to leave.

   HIDDEN BY A CLASS, NOT BY INLINE STYLES, so what disappears is written down in one place and
   comes back by removing one word rather than by unpicking a list. */
body.app-takeover #toolbar,
body.app-takeover .fpanel.pdock,
body.app-takeover .page-bar,
body.app-takeover .float-island,
body.app-takeover .ed-head,
body.app-takeover .ed-bar{ display:none !important; }
body.app-takeover .ed-body{ padding-left:0 !important; }
/* This narrows the workspace, and the editor's own fit then measures what is left — which is why
   the page is re-centred after the class goes on rather than before.
   THE TOP TOO, since 13 September 2026: the editor's own bar across the top is put away with the
   rest, and the app's bar sits in the room it leaves. Before this the black bar with the design's
   name and the Apps button showed between the two panels, and Mounir looked at it and said "I
   don't see what you changed — it should have its own workspace, like bento". */
body.app-takeover .stage-wrap{
  margin-left:var(--takeover-left,0px);
  margin-right:var(--takeover-right,0px);
  margin-top:var(--takeover-top,0px);
}

/* ---- the two side panels scroll on their own ---- */
.tfx-lefttop{ flex:none; padding:18px 18px 0; }
.tfx-grid{
  flex:1; min-height:0; overflow:auto; padding:14px 18px 22px;
  display:grid; grid-template-columns:1fr 1fr; gap:12px; align-content:start;
}
.tfx-scroll{ flex:1; min-height:0; overflow:auto; padding:18px 18px 8px; }

.tfx-h{ margin:0 0 14px; font-size:16px; font-weight:700; letter-spacing:-.01em; }

/* ---- search ---- */
.tfx-search{ position:relative; display:flex; align-items:center; }
.tfx-search svg{ position:absolute; left:12px; color:var(--tfx-dim); pointer-events:none; }
.tfx-search input{
  width:100%; height:40px; padding:0 12px 0 34px; border:1px solid var(--tfx-edge);
  border-radius:10px; background:#FFF; font:inherit; font-size:13px; color:var(--tfx-ink);
}
.tfx-search input::placeholder{ color:var(--tfx-dim); }
.tfx-search input:focus{ outline:none; border-color:var(--tfx-line); box-shadow:0 0 0 3px rgba(61,149,255,.12); }

/* ---- tabs: a purple underline under the one you are on ---- */
.tfx-tabs{ display:flex; gap:22px; margin-top:14px; border-bottom:1px solid var(--tfx-edge); }
.tfx-tab{
  appearance:none; border:none; background:none; font:inherit; font-size:13.5px; cursor:pointer;
  color:var(--tfx-dim); padding:10px 0 11px; position:relative;
}
.tfx-tab.on{ color:var(--tfx-ink); font-weight:600; }
.tfx-tab.on::after{
  content:""; position:absolute; left:0; right:0; bottom:-1px; height:2px;
  background:var(--tfx-accent); border-radius:2px;
}
.tfx-tab:focus-visible{ outline:2px solid var(--tfx-accent); outline-offset:2px; border-radius:4px; }

/* ---- a card: a big picture of the effect, its name underneath ---- */
.tfx-card{
  appearance:none; cursor:pointer; font:inherit; text-align:center;
  display:flex; flex-direction:column; align-items:center; gap:8px;
  padding:14px 8px 10px; border:1px solid var(--tfx-edge); border-radius:12px; background:#FFF;
}
.tfx-card:hover{ border-color:#D6D6DB; }
/* CHOSEN: pale purple fill and a purple border, exactly as the reference shows it. */
.tfx-card.on{ background:var(--tfx-soft); border-color:var(--tfx-line); }
.tfx-card:focus-visible{ outline:2px solid var(--tfx-accent); outline-offset:2px; }
/* THE SPACE IS HELD OPEN AT ITS FINAL SIZE from the start, so the grid does not move as the real
   drawings arrive one at a time.
   THE SHAPE TAKES THE CARD'S OWN INK COLOUR — it is drawn with currentColor — so it reads black on
   white, white on dark, and purple on the chosen card without three copies of the drawing. */
.tfx-cardpic{
  display:block; width:100%; height:74px; padding:0 6px; box-sizing:border-box;
  color:var(--tfx-ink);
}
.tfx-cardpic svg{ display:block; width:100%; height:100%; }
.tfx-card.on .tfx-cardpic{ color:var(--tfx-accent); }
.tfx-cardname{ font-size:12.5px; color:var(--tfx-ink); }
.tfx-card.on .tfx-cardname{ color:var(--tfx-accent); font-weight:600; }
.tfx-none{ grid-column:1 / -1; margin:6px 0; color:var(--tfx-dim); font-size:12.5px; }

/* ---- the settings ---- */
.tfx-sec{ margin:0 0 20px; }
.tfx-sec-last{ margin-bottom:6px; }
.tfx-lab{ display:block; margin:0 0 8px; font-size:12.5px; font-weight:600; color:var(--tfx-ink); }
.tfx-area, .tfx-sel, .tfx-hex{
  width:100%; border:1px solid var(--tfx-edge); border-radius:10px; background:#FFF;
  font:inherit; font-size:13px; color:var(--tfx-ink); padding:10px 12px;
}
.tfx-area{ resize:vertical; min-height:44px; line-height:1.35; }
.tfx-sel{ height:var(--tfx-fh); padding:0 12px; }
.tfx-area:focus, .tfx-sel:focus, .tfx-hex:focus{
  outline:none; border-color:var(--tfx-line); box-shadow:0 0 0 3px rgba(61,149,255,.12);
}
.tfx-row2{ display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:10px; }
.tfx-colour{ display:flex; align-items:center; gap:8px; border:1px solid var(--tfx-edge);
  border-radius:10px; padding:0 10px; height:var(--tfx-fh); background:#FFF;
  /* and the same step below the row above it as that row leaves between its own two halves */
  margin-top:10px; }
/* A GREY OUTLINE, NOT A BLACK ONE. The swatch shows whatever colour the words are, and the
   default is nearly black — outlined in black it vanishes into a dark panel, outlined in grey it
   reads against both themes without the outline itself being visible against a mid colour. */
.tfx-swatch{ flex:none; width:20px; height:20px; border-radius:50%; background:#111111;
  border:1px solid rgba(128,128,128,.4); }
/* IT SITS INSIDE THE COLOUR BOX, so it borrows that box's background rather than carrying one.
   With a white one of its own it stayed white in the dark theme — a white pill with white text
   in it, which is how this was found: by looking at the picture rather than at the rule. */
.tfx-hex{ border:none; padding:0; background:transparent; box-shadow:none !important; }

/* THE ROW SHOWING WHICH EFFECT IS ON. No outline round it since 4 September 2026, asked for by
   name: it is a label, not a thing to press, and drawing a box round a label makes it look like
   one. The picture and the name sit on the panel with nothing else between them. */
.tfx-nowfx{ display:flex; align-items:center; gap:10px; padding:4px 0; }
/* The same drawing again, small, beside the name of the effect that is on. Empty until it has
   one, and then it is a shape rather than a grey block — so the grey is only ever a gap. */
.tfx-nowpic{ flex:none; width:44px; height:26px; border-radius:4px; background:#F3F3F5;
  color:var(--tfx-ink); overflow:hidden; }
.tfx-nowpic:has(svg){ background:none; }
.tfx-nowpic svg{ display:block; width:100%; height:100%; }
.tfx-nowname{ flex:1; font-size:13px; }

.tfx-soon{ color:var(--tfx-dim); font-size:12.5px; line-height:1.5; }
.tfx-hint{ margin:8px 0 0; color:var(--tfx-dim); font-size:12px; line-height:1.5; }

/* ---- a slider and a box, showing one value ---- */
.tfx-pair{ margin-top:12px; }
.tfx-pair:first-of-type{ margin-top:0; }
.tfx-pairlab{ display:block; margin:0 0 6px; font-size:12.5px; color:var(--tfx-dim); }
.tfx-pairrow{ display:flex; align-items:center; gap:10px; }
.tfx-slider{
  flex:1; min-width:0; appearance:none; height:4px; border-radius:2px; background:var(--tfx-edge);
  outline:none; margin:0;
}
.tfx-slider::-webkit-slider-thumb{
  appearance:none; width:16px; height:16px; border-radius:50%; background:var(--tfx-accent);
  border:2px solid var(--tfx-paper); box-shadow:0 1px 3px rgba(0,0,0,.3); cursor:pointer;
}
.tfx-slider::-moz-range-thumb{
  width:16px; height:16px; border-radius:50%; background:var(--tfx-accent);
  border:2px solid var(--tfx-paper); box-shadow:0 1px 3px rgba(0,0,0,.3); cursor:pointer;
}
.tfx-slider:focus-visible{ outline:2px solid var(--tfx-accent); outline-offset:4px; }
/* THE BOX IS A FIXED WIDTH so the sliders beside it all start and end in the same place. A row of
   settings whose sliders each begin somewhere different reads as a mistake. */
.tfx-num{ flex:none; width:78px; display:flex; align-items:center; gap:2px; padding:0 8px;
  height:38px; border:1px solid var(--tfx-edge); border-radius:9px; background:#FFF; }
.tfx-numbox{ width:auto; flex:1; height:var(--tfx-fh); border-radius:10px; }
.tfx-num input{ width:100%; min-width:0; border:none; background:none; font:inherit; font-size:13px;
  color:var(--tfx-ink); padding:0; outline:none; }
/* The browser's own spinner arrows are removed: they are tiny, they steal the width the number
   needs, and there is a slider beside this doing the same job properly. */
.tfx-num input::-webkit-outer-spin-button,
.tfx-num input::-webkit-inner-spin-button{ appearance:none; margin:0; }
.tfx-num input[type=number]{ -moz-appearance:textfield; }
.tfx-num:focus-within{ border-color:var(--tfx-line); box-shadow:0 0 0 3px rgba(61,149,255,.12); }
.tfx-unit{ flex:none; color:var(--tfx-dim); font-size:12px; }

/* ---- two choices, one of them on ---- */
.tfx-seg{ display:flex; gap:6px; padding:4px; border:1px solid var(--tfx-edge); border-radius:11px;
  background:#FAFAFB; }
.tfx-segb{ flex:1; appearance:none; border:none; background:none; font:inherit; font-size:12.5px;
  color:var(--tfx-dim); height:34px; border-radius:8px; cursor:pointer; }
.tfx-segb.on{ background:var(--tfx-accent); color:#FFF; font-weight:600; }
.tfx-segb:focus-visible{ outline:2px solid var(--tfx-accent); outline-offset:2px; }

/* the colour square IS the picker, so the browser's own chrome inside it is taken off */
.tfx-swatch{ appearance:none; padding:0; cursor:pointer; }
.tfx-swatch::-webkit-color-swatch-wrapper{ padding:0; }
.tfx-swatch::-webkit-color-swatch{ border:none; border-radius:50%; }
.tfx-swatch::-moz-color-swatch{ border:none; border-radius:50%; }

/* the switch */
.tfx-toggrow{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:14px; }
.tfx-toglab{ font-size:13px; }
.tfx-tog{ appearance:none; cursor:pointer; flex:none; width:44px; height:26px; border-radius:13px;
  border:none; background:#D8D8DE; position:relative; transition:background .14s ease; }
.tfx-tog i{ position:absolute; top:3px; left:3px; width:20px; height:20px; border-radius:50%;
  background:#FFF; transition:left .14s ease; box-shadow:0 1px 3px rgba(0,0,0,.25); }
.tfx-tog.on{ background:var(--tfx-accent); }
.tfx-tog.on i{ left:21px; }
.tfx-tog:focus-visible{ outline:2px solid var(--tfx-accent); outline-offset:2px; }

.tfx-wide{ appearance:none; width:100%; height:42px; border:1px solid var(--tfx-edge);
  border-radius:10px; background:#FFF; font:inherit; font-size:13px; color:var(--tfx-ink);
  cursor:pointer; }
.tfx-wide:hover{ background:#FAFAFB; }

/* ---- the footer, which does not scroll ---- */
.tfx-foot{
  flex:none; display:flex; align-items:center; justify-content:flex-end; gap:12px;
  padding:14px 18px; border-top:1px solid var(--tfx-edge); background:var(--tfx-paper);
}
.tfx-cancel{ appearance:none; height:44px; padding:0 18px; border:1px solid var(--tfx-edge);
  border-radius:10px; background:#FFF; font:inherit; font-size:13.5px; color:var(--tfx-ink);
  cursor:pointer; }
.tfx-cancel:hover{ background:#FAFAFB; }
.tfx-go{ appearance:none; height:44px; padding:0 22px; border:none; border-radius:10px;
  background:var(--tfx-accent); color:#FFF; font:inherit; font-size:13.5px; font-weight:600;
  cursor:pointer; }
.tfx-go:hover{ filter:brightness(1.06); }
.tfx-cancel:focus-visible, .tfx-go:focus-visible{ outline:2px solid var(--tfx-accent); outline-offset:2px; }

/* ---- IN THE DARK THEME THE PANELS ARE DARK, and the accent stays exactly as it is.
        The reference is a light picture; somebody working at night should not be handed two
        white slabs. The accent does not change with the theme. ---- */
body:not(.light) .tfx{
  --tfx-paper:#232323; --tfx-ink:#F2F2F2; --tfx-dim:#9AA0A6; --tfx-edge:#333333;
  --tfx-soft:rgba(61,149,255,.20);
}
body:not(.light) .tfx-search input,
body:not(.light) .tfx-area, body:not(.light) .tfx-sel,
body:not(.light) .tfx-colour, body:not(.light) .tfx-card,
body:not(.light) .tfx-wide,
body:not(.light) .tfx-num,
body:not(.light) .tfx-cancel{ background:#1A1A1A; }
body:not(.light) .tfx-seg{ background:#1A1A1A; }
body:not(.light) .tfx-zoomset{ background:#1A1A1A; }
body:not(.light) .tfx-zbtn:hover, body:not(.light) .tfx-zoom:hover{ background:#2E2E2E; }
body:not(.light) .tfx-slider{ background:#3D3D3D; }
/* The ring round the slider's knob is the panel it sits on, so it reads as a knob rather than as
   a dot on a line — which means it has to change with the panel. */
body:not(.light) .tfx-slider::-webkit-slider-thumb{ border-color:#232323; }
body:not(.light) .tfx-slider::-moz-range-thumb{ border-color:#232323; }
body:not(.light) .tfx-card:hover{ border-color:#4A4A4A; }
body:not(.light) .tfx-card.on{ background:var(--tfx-soft); border-color:var(--tfx-line); }
body:not(.light) .tfx-card.on .tfx-cardname{ color:#C4B5FD; }
body:not(.light) .tfx-nowpic{ background:#2A2A2A; }
body:not(.light) .tfx-nowpic:has(svg){ background:none; }
body:not(.light) .tfx-tog{ background:#3D3D3D; }
body:not(.light) .tfx-wide:hover, body:not(.light) .tfx-cancel:hover{ background:#242424; }

/* Removing an effect is not the same kind of act as resetting one, so it does not look the same.
   Outlined in the warning colour rather than filled with it: it is a real choice with a real
   consequence, but it is not dangerous — nothing is lost that undo cannot bring back. */
.tfx-danger{ margin-top:8px; color:#C0392B; border-color:rgba(192,57,43,.35); }
.tfx-danger:hover{ background:rgba(192,57,43,.06); }
body:not(.light) .tfx-danger{ color:#FF7B6B; border-color:rgba(255,123,107,.32); }
body:not(.light) .tfx-danger:hover{ background:rgba(255,123,107,.09); }

/* =================================================================================================
   THE HANDLES ON A BENT HEADLINE
   =================================================================================================
   Task 9, 4 September 2026. Sized in PAGE units by the code that draws them, worked out from the
   zoom, so that what appears on screen is always the same number of pixels — a handle is something
   to aim a mouse at, and its size belongs to the screen rather than to the page.

   THE WHOLE LAYER IS SEE-THROUGH TO THE POINTER except the handles themselves. That is what lets
   the workspace's own gate exempt the layer by name without the layer swallowing presses meant for
   the artwork underneath it.
   ================================================================================================= */
.tfxh-layer{
  position:absolute; pointer-events:none;
  --tfxh:var(--tfx-blue);               /* the same blue as the window that drives them */
  /* ABOVE THE SELECTION FRAME, which is at 60. They land in exactly the same places — the
     corners and the middles of the edges — and the first version of this sat underneath, so five
     of the twenty handles could not be pressed at all: an arch's bow is at the top middle, a
     wave's phase at the left middle, and two of a perspective's corners are corners. Where they
     coincide the effect handle wins, because it is the tool for the thing being edited; the
     frame is still reachable everywhere the handle is not.
     THEY USED TO BE PURPLE AND THE FRAME BLUE, which told them apart at a glance. On 6 September
     2026 Mounir asked for this window to take the editor's new blue, and then — the same evening,
     having used it — asked for these back: "i want them to stand out for user to be able to see
     them and differentiate them from the transformation point". So the colour stays the same and
     the FILL is what tells them apart now, which is a stronger signal than a hue anyway:
       a transformation handle  is a WHITE SQUARE with a blue edge — hollow, part of the frame
       an effect handle         is a SOLID BLUE DISC with a white edge — filled, the thing to grab
     Filled against hollow reads at a glance and at any size, and it still reads for somebody who
     cannot tell two blues apart, which two hues never would have. */
  z-index:61;
}
.tfxh-line{ position:absolute; inset:0; overflow:visible; }
.tfxh-line polyline, .tfxh-line polygon{ stroke:var(--tfxh); vector-effect:non-scaling-stroke; }
.tfxh-line polygon{ stroke-dasharray:4 3; opacity:.7; }
.tfxh{
  position:absolute; box-sizing:border-box; pointer-events:auto; cursor:grab;
  border-radius:50%; background:var(--tfxh); border-style:solid; border-color:#FFF;
  /* the shadow is what holds it off a white page, where its white edge has nothing to sit against */
  box-shadow:0 1px 4px rgba(0,0,0,.45);
}
.tfxh.sq{ border-radius:2px; }
/* it is already filled, so the pointer darkens it rather than filling it */
.tfxh:hover{ background:var(--tfx-blue-h); }
.tfxh:active{ cursor:grabbing; background:var(--tfx-blue-h); }

/* AN APP'S OWN PICTURE, in place of the plain outline. Added 4 September 2026 with four of them
   drawn. The corners are rounded here rather than in the pictures, so one of them can be replaced
   without having to match a radius, and so they all round the same amount however they were drawn.
   THEY ARE BIGGER THAN THE OUTLINES WERE. An outline reads at eighteen pixels because it is three
   strokes; a picture with shading and depth in it does not, and shrinking one to eighteen makes
   four different apps look like four grey smudges. */
.apps-ico.art{ width:34px; height:34px; border-radius:9px; overflow:hidden; }
/* In a card the picture is bigger, the size it is in Mounir's picture of the strip. */
.apps-item .apps-ico.art{ width:46px; height:46px; border-radius:12px; }
.apps-ico.art img{ display:block; width:100%; height:100%; object-fit:cover; }

/* AN APP'S PICTURE BESIDE ITS OWN TITLE, at the top of its window. Asked for 4 September 2026,
   the day the pictures went into the menu. Sized to the cap height of the titles it sits beside
   rather than to a round number, so it reads as part of the line rather than as a thing parked
   next to it — the four titles are 14, 15, 16 and 18 pixels, and 22 sits with all of them.
   MIDDLE-ALIGNED rather than nudged by a hand-picked offset: a title that is one day given a
   different size would take the offset with it and the picture would sit a little wrong for ever. */
.app-art{
  display:inline-block; vertical-align:middle; flex:none;
  width:22px; height:22px; border-radius:6px; margin-right:8px;
  object-fit:cover;
}
/* In a flex title the margin is the gap the row already sets, so it is dropped. */
.bw-name .app-art{ margin-right:0; }

/* THE WAY OUT OF THE TEXT EFFECTS WINDOW, in the corner the other apps put theirs. Its own rule
   rather than the shared one because this window's panel is not a card floating in the middle —
   the cross belongs to the right-hand column, and it has to sit above the settings that scroll
   underneath it rather than scrolling away with them. */
.tfx-x{
  position:absolute; top:10px; right:12px; z-index:2;
  width:34px; height:34px; border:none; border-radius:99px;
  background:none; color:var(--tfx-dim); font:inherit; font-size:24px; line-height:1;
  cursor:pointer;
}
.tfx-x:hover{ background:rgba(0,0,0,.06); color:var(--tfx-ink); }
.tfx-x:focus-visible{ outline:2px solid var(--tfx-accent); outline-offset:2px; }
body:not(.light) .tfx-x:hover{ background:rgba(255,255,255,.10); }
/* The heading makes room for it, so a long one can never run underneath the cross. */
.tfx-right .tfx-scroll > .tfx-h{ padding-right:34px; }

/* =================================================================================================
   THE COLOUR PALETTES WINDOW
   =================================================================================================
   Task 3 of the palettes plan, 4 September 2026. Three columns — search and collections, the
   library, the palette you picked — and a footer that stays put across all three.

   IT COVERS THE SCREEN, unlike the text effects window beside it in this stylesheet. That one
   leaves its middle empty so the real page shows through, because a bent headline is judged by
   looking at it while you drag. Nothing here is judged that way: the middle column IS the library.
   So this one takes the whole window and touches nothing belonging to the editor — no panels
   hidden, no page moved, nothing to put back when it closes.

   NO ACCENT COLOUR ANYWHERE. Asked for by name: "light panels, quiet borders — the colours are the
   only loud thing on the screen". Everything in here is paper, ink, a dim grey and a hairline. The
   filled button is near-black rather than the editor's blue, and the chosen card is marked by a
   darker border and a little more weight in its name. A coloured button sitting beside a hundred
   and twenty-six colour strips is one more thing competing with what somebody came to look at.
   ================================================================================================= */
.pal{
  --pal-left-w:272px;
  --pal-right-w:340px;
  --pal-paper:#FFFFFF;      /* the panels */
  --pal-back:#F7F7F8;       /* the middle, a shade down so the cards sit on something */
  --pal-ink:#111113;
  /* THE ONE COLOUR IN THIS WINDOW, and the darker step it takes when the pointer is on it.
     This window was deliberately colourless — the swatches are the only loud thing on the
     screen, and that is still true of everything except the four controls below. On 6 September
     2026 Mounir asked for it to match the rest of the editor: the button you press, the chosen
     way of writing a colour, the four switches and the chosen arrangement. Everything else —
     text, panels, borders, cards — stays grey. */
  --pal-accent:#3D95FF; --pal-accent-h:#497ED4;
  /* and the palest wash of it, for the shelf that is chosen — 6 September 2026 */
  --pal-accent-soft:#E8F2FF;
  --pal-dim:#6E6E73;
  --pal-edge:#E6E6EA;
  --pal-hair:#EFEFF1;       /* the quietest line there is, between rows */
  position:fixed; inset:0; z-index:200;
  display:grid;
  grid-template-columns:var(--pal-left-w) 1fr var(--pal-right-w);
  grid-template-rows:1fr auto;
  background:var(--pal-back);
  font-size:13px; color:var(--pal-ink);
}
.pal-left, .pal-mid, .pal-right{ min-height:0; min-width:0; display:flex; flex-direction:column; }
.pal-left{ background:var(--pal-paper); border-right:1px solid var(--pal-edge); }
.pal-mid{ background:var(--pal-back); }
.pal-right{ background:var(--pal-paper); border-left:1px solid var(--pal-edge); position:relative; }

/* ---- each column scrolls on its own, and the footer never moves ---- */
.pal-lefttop{ flex:none; padding:20px 18px 14px; }
.pal-shelves{ flex:1; min-height:0; overflow:auto; padding:0 12px 18px; }
/* the heading over the library, and the search box across the whole of it */
.pal-midhead{ flex:none; padding:20px 24px 0; }
.pal-midtitle{ margin:0; font-size:21px; font-weight:700; letter-spacing:-.015em; color:var(--pal-ink); }
.pal-midsub{ margin:5px 0 14px; font-size:13px; color:var(--pal-dim); }
.pal-midtop{ flex:none; padding:14px 24px 12px; display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
/* The grid itself no longer lays the cards out — it scrolls, and the rows of cards inside it do
   the laying out. That is what lets an exact-matches heading and a close-matches heading each
   carry their own rows. */
.pal-grid{ flex:1; min-height:0; overflow:auto; padding:0 24px 26px; }
.pal-cards{
  display:grid; grid-template-columns:repeat(auto-fill,minmax(212px,1fr)); gap:18px;
  align-content:start;
}

/* ---- the filter row ---- */
.pal-filters{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; flex:1; }
.pal-filter{ position:relative; display:inline-flex; }
.pal-filter select{
  appearance:none; height:32px; padding:0 26px 0 11px; border:1px solid var(--pal-edge);
  border-radius:8px; background:var(--pal-paper); font:inherit; font-size:12px;
  color:var(--pal-ink); cursor:pointer;
}
.pal-filter::after{
  content:""; position:absolute; right:10px; top:14px; width:6px; height:6px;
  border-right:1.5px solid var(--pal-dim); border-bottom:1.5px solid var(--pal-dim);
  transform:rotate(45deg) translateY(-2px); pointer-events:none;
}
/* A FILTER THAT IS DOING SOMETHING IS MARKED, so a library that looks emptier than expected
   explains itself at a glance rather than after hunting through five dropdowns. */
.pal-filter.on select{ border-color:var(--pal-ink); font-weight:600; }
.pal-filter select:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }
/* WHEN THERE IS SOMETHING TO CLEAR IT LOOKS PRESSABLE, and when there is not it looks like
   nothing. The first version was the same quiet grey either way, and beside four dropdowns with
   dark borders it read as disabled at exactly the moment it was the useful control on the row. */
.pal-clear{
  height:32px; padding:0 12px; border:1px solid var(--pal-edge); border-radius:8px;
  background:var(--pal-paper); font:inherit; font-size:12px; font-weight:600;
  color:var(--pal-ink); cursor:pointer;
}
.pal-clear:hover:not(:disabled){ background:#F2F2F4; }
.pal-clear:disabled{
  border-color:transparent; background:none; color:var(--pal-dim); font-weight:400; cursor:default;
}
.pal-clear:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }

/* ---- a heading over a group of results ---- */
.pal-group{
  display:flex; align-items:center; gap:8px; margin:4px 0 12px;
  font-size:11.5px; font-weight:700; letter-spacing:.07em; text-transform:uppercase;
  color:var(--pal-dim);
}
.pal-group:not(:first-child){ margin-top:26px; }
.pal-group span{ font-weight:500; letter-spacing:0; text-transform:none; }
.pal-note{ margin:0 0 14px; color:var(--pal-dim); font-size:12.5px; }
.pal-detail{ flex:1; min-height:0; overflow:auto; padding:20px 18px 22px; }

.pal-h{
  margin:0 0 16px; font-size:16px; font-weight:700; letter-spacing:-.01em;
  display:flex; align-items:center; gap:9px;
}
.pal-h svg{ flex:none; color:var(--pal-dim); }
.pal-h .app-art{ margin-right:0; }   /* the flex gap is the spacing */

/* ---- the search box ---- */
.pal-search{ position:relative; display:flex; align-items:center; }
.pal-search svg{ position:absolute; left:12px; color:var(--pal-dim); pointer-events:none; }
.pal-search input{
  width:100%; height:44px; padding:0 12px 0 34px; border:1px solid var(--pal-edge);
  border-radius:10px; background:var(--pal-paper); font:inherit; font-size:13px; color:var(--pal-ink);
}
.pal-search input::placeholder{ color:var(--pal-dim); }
.pal-search input:focus{ outline:none; border-color:#B9B9C0; box-shadow:0 0 0 3px rgba(17,17,19,.07); }

/* ---- the shelves: an icon, a name, and how many ---- */
.pal-shelf{
  display:flex; align-items:center; gap:11px;
  width:100%; padding:9px 10px; margin:1px 0; border:none; border-radius:9px;
  background:none; font:inherit; font-size:13px; color:var(--pal-ink);
  cursor:pointer; text-align:left;
}
.pal-ico{ flex:none; color:var(--pal-dim); }
.pal-shname{ flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }
.pal-shn{ flex:none; color:var(--pal-dim); font-size:11.5px; font-variant-numeric:tabular-nums; }
.pal-shelf:hover{ background:#F2F2F4; }
/* THE ONE THAT IS ON WEARS THE PALEST WASH OF THE ACCENT, with its name and its icon in the
   accent itself — asked for on 6 September 2026 with a picture. It was a grey fill and a bolder
   weight; the swatches are still the only STRONG colour on the screen. */
.pal-shelf.on{ background:var(--pal-accent-soft); font-weight:600; color:var(--pal-accent); }
.pal-shelf.on .pal-ico{ color:var(--pal-accent); }
.pal-shelf.on .pal-shn{ color:var(--pal-accent); }
.pal-shelf:focus-visible{ outline:2px solid var(--pal-accent); outline-offset:1px; }

/* the rule under My palettes, and the small heading over the fifteen */
.pal-shline{ height:1px; margin:12px 2px; border:none; background:var(--pal-edge); }
.pal-shhead{
  margin:0 0 4px; padding:0 10px; font-size:11px; font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; color:var(--pal-dim);
}

.pal-count{ color:var(--pal-dim); font-size:12px; letter-spacing:.02em; }

/* ---- a card in the library ---- */
/* NO `overflow` ON THIS BUTTON, and the reason is worth writing down because it cost an hour and
   the picture is the only thing that showed it. The card had overflow:hidden so the colour strip
   would be clipped by the rounded corners. In Chrome a <button> given any overflow other than
   visible stops taking its children's height into account at all: the card measured two pixels —
   its two borders and nothing between them — while the strip inside it correctly measured
   sixty-four. Every rule read perfectly and the library rendered as a hundred and twenty-six
   hairlines. So the clipping is done by the strip itself instead, which is an ordinary element and
   behaves like one. */
/* THE HEART SITS OVER THE CARD, NOT INSIDE IT. A button inside a button is not valid and browsers
   repair it by pulling the inner one out of the card entirely, which leaves the heart somewhere
   nobody put it. So the card and the heart are siblings and this wrapper holds them together. */
.pal-cardwrap{ position:relative; }
.pal-card{
  display:block; width:100%; padding:0; border:1px solid var(--pal-edge); border-radius:12px;
  background:var(--pal-paper); cursor:pointer; text-align:left; font:inherit;
}
.pal-card:hover{ border-color:#CFCFD6; }
.pal-cardwrap.on .pal-card{ border-color:var(--pal-ink); box-shadow:0 0 0 1px var(--pal-ink); }
.pal-card:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:2px; }
.pal-cfoot{ display:block; }

/* ---- the heart ---- */
.pal-heart{
  position:absolute; top:8px; right:8px; width:28px; height:28px; padding:0;
  display:flex; align-items:center; justify-content:center;
  border:none; border-radius:99px; cursor:pointer;
  background:rgba(255,255,255,.82); color:#3A3A3E;
  /* THE SWATCHES UNDERNEATH ARE ANY COLOUR AT ALL, so the heart cannot rely on contrast with them.
     It carries its own pale disc, which is the only way an outline is legible on both a cream and
     a near-black without changing colour and looking like two different controls. */
  opacity:0; transition:opacity .12s ease;
}
.pal-cardwrap:hover .pal-heart,
.pal-heart:focus-visible,
.pal-heart.on{ opacity:1; }
.pal-heart:hover{ background:#FFF; }
.pal-heart.on{ color:#111113; }
.pal-heart.on svg path{ fill:currentColor; }
.pal-heart:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }
/* one pixel less than the card's radius, because it sits inside the card's own border */
.pal-strip{ display:flex; height:64px; border-radius:11px 11px 0 0; overflow:hidden; }
.pal-strip i{ flex:1; display:block; }
.pal-cname{ display:block; padding:10px 12px 0; font-size:13px; font-weight:600; color:var(--pal-ink); }
.pal-cardwrap.on .pal-cname{ font-weight:700; }
.pal-cmeta{ display:block; padding:2px 12px 11px; font-size:11.5px; color:var(--pal-dim); }
.pal-near{ font-style:normal; color:var(--pal-ink); }

/* ---- nothing matches ---- */
.pal-nothing{ padding:40px 0 0; max-width:420px; }
.pal-nothing-h{ margin:0 0 6px; font-size:15px; font-weight:700; }
.pal-nothing-p{ margin:0 0 16px; color:var(--pal-dim); line-height:1.5; }
.pal-clear-big{
  height:38px; padding:0 16px; border:1px solid var(--pal-edge); border-radius:9px;
  background:var(--pal-paper); font:inherit; font-size:13px; font-weight:600;
  color:var(--pal-ink); cursor:pointer;
}
.pal-clear-big:hover{ background:#F4F4F6; }
.pal-clear-big:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:2px; }

/* ---- the palette you picked ---- */
.pal-dhead{ display:flex; align-items:flex-start; gap:10px; margin:0 34px 14px 0; }
.pal-dtitle{ flex:1; min-width:0; }
.pal-dname{ margin:0 0 3px; font-size:17px; font-weight:700; letter-spacing:-.01em; }
.pal-dsub{ margin:0; font-size:11.5px; color:var(--pal-dim); line-height:1.4; }
/* The heart here is always visible — there is one palette in this column and no strip of colour
   behind it, so it needs no disc and no hiding until hover the way the ones on the cards do. */
.pal-dheart{
  flex:none; width:30px; height:30px; padding:0; display:flex; align-items:center;
  justify-content:center; border:none; border-radius:99px; background:none;
  color:var(--pal-dim); cursor:pointer;
}
.pal-dheart:hover{ background:#F2F2F4; color:var(--pal-ink); }
.pal-dheart.on{ color:var(--pal-ink); }
.pal-dheart.on svg path{ fill:currentColor; }
.pal-dheart:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }

.pal-big{ display:flex; height:88px; border-radius:10px; overflow:hidden; border:1px solid var(--pal-edge); }
.pal-big i{ flex:1; display:block; }

/* ---- the format switch, and copy all ---- */
/* ---- HOW TO SHOW THE COLOURS. One track holding three, the chosen one a filled pill sitting
   inside it rather than three boxes wedged together. 5 September 2026, to Mounir's drawing.
   Black rather than the purple in his picture, because black is what this window already uses
   for the thing you press. ---- */
.pal-fmts{
  display:grid; grid-template-columns:repeat(3,1fr); gap:2px; margin-top:14px;
  padding:4px; border:1px solid var(--pal-edge); border-radius:12px; background:#F4F4F6;
}
.pal-fmt{
  height:34px; padding:0 10px; border:none; border-radius:9px; background:transparent;
  font:inherit; font-size:13px; color:var(--pal-dim); cursor:pointer;
  transition:background .12s ease, color .12s ease;
}
.pal-fmt:hover{ color:var(--pal-ink); }
.pal-fmt.on{ background:var(--pal-accent); color:#FFF; font-weight:600; }
.pal-fmt:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:-2px; }

/* the whole width, because it does something to the whole list */
.pal-copyall{
  display:flex; align-items:center; justify-content:center; gap:7px; width:100%;
  height:40px; margin-top:14px; padding:0 12px;
  border:1px solid var(--pal-edge); border-radius:10px; background:var(--pal-paper);
  font:inherit; font-size:13px; font-weight:600; color:var(--pal-ink); cursor:pointer;
}
.pal-copyall:hover{ background:#F2F2F4; }
.pal-copyall.done{ border-color:var(--pal-ink); }
.pal-copyall:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }

/* ---- a row per colour ---- */
.pal-rows{ margin-top:14px; }
.pal-row{
  display:flex; align-items:center; gap:6px; padding:8px 2px;
  border-bottom:1px solid var(--pal-hair); border-radius:6px;
}
.pal-row:last-child{ border-bottom:none; }
.pal-row.copied{ background:#F2F2F4; }
.pal-sw{ width:26px; height:26px; border-radius:7px; border:1px solid var(--pal-edge); flex:none; }
.pal-rname{
  flex:1; min-width:0; font-size:12.5px; color:var(--pal-ink);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; text-transform:capitalize;
}

/* =================================================================================================
   EDITING A PALETTE
   =================================================================================================
   Task 6, 4 September 2026. The row grew a grip to drag it by, a colour picker sitting under the
   swatch, a name you can type over and a code you can type into.

   EVERY BOX IN A ROW LOOKS LIKE TEXT UNTIL YOU TOUCH IT. Five inputs to a row, five to twelve rows,
   is a wall of boxes; borders on all of them would turn a palette into a form. So they are plain
   until hovered or focused, which is when knowing they are editable is actually useful. */
.pal-grip{
  flex:none; width:18px; height:26px; padding:0; display:flex; align-items:center;
  justify-content:center; border:none; border-radius:5px; background:none;
  color:var(--pal-dim); cursor:grab; opacity:.5;
}
.pal-row:hover .pal-grip, .pal-grip:focus-visible{ opacity:1; }
.pal-grip:active{ cursor:grabbing; }
.pal-grip:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }

/* THE PICKER IS THE SWATCH. An <input type="color"> cannot be styled into a 26-pixel rounded
   square in every browser, so the swatch is drawn and the real control laid invisibly on top of
   it — the same trick the editor's own colour rows have used since long before this. */
.pal-swwrap{ position:relative; flex:none; width:26px; height:26px; }
.pal-swwrap .pal-sw{ position:absolute; inset:0; }
.pal-pick{
  position:absolute; inset:0; width:100%; height:100%; opacity:0; cursor:pointer;
  border:none; padding:0; background:none;
}
.pal-pick:focus-visible + .pal-sw, .pal-swwrap:focus-within .pal-sw{
  outline:2px solid var(--pal-ink); outline-offset:2px;
}

.pal-rname-in, .pal-rcode-in, .pal-dname-in{
  border:1px solid transparent; border-radius:6px; background:none; font:inherit;
  color:var(--pal-ink); min-width:0;
}
.pal-rname-in{ flex:1; height:26px; padding:0 6px; font-size:12.5px; text-transform:capitalize; }
.pal-rcode-in{
  flex:none; width:106px; height:26px; padding:0 6px; color:var(--pal-dim);
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:11px;
  letter-spacing:.02em;
}
.pal-rname-in:hover, .pal-rcode-in:hover{ border-color:var(--pal-edge); }
.pal-rname-in:focus, .pal-rcode-in:focus, .pal-dname-in:focus{
  outline:none; border-color:var(--pal-ink); background:var(--pal-paper); color:var(--pal-ink);
}
.pal-dname-in{
  width:100%; height:30px; margin:0 0 2px -6px; padding:0 6px;
  font-size:17px; font-weight:700; letter-spacing:-.01em;
}
.pal-dname-in:hover{ border-color:var(--pal-edge); }

.pal-rowbtn{
  flex:none; width:24px; height:24px; padding:0; display:flex; align-items:center;
  justify-content:center; border:none; border-radius:6px; background:none;
  color:var(--pal-dim); cursor:pointer; opacity:.42; transition:opacity .1s ease;
}
.pal-row:hover .pal-rowbtn, .pal-rowbtn:focus-visible{ opacity:1; }
.pal-rowbtn:hover:not(:disabled){ background:#F2F2F4; color:var(--pal-ink); }
.pal-rowbtn:disabled{ opacity:.18; cursor:default; }
.pal-rowbtn:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }

/* ---- while a row is being dragged ---- */
.pal-row.dragging{ opacity:.4; }
.pal-row.droptarget{ box-shadow:inset 0 2px 0 var(--pal-ink); }

/* ---- add, reverse, and how many there are ---- */
.pal-editrow{ display:flex; align-items:center; gap:8px; margin-top:14px; }
.pal-editbtn{
  display:inline-flex; align-items:center; gap:5px; height:30px; padding:0 11px;
  border:1px solid var(--pal-edge); border-radius:8px; background:var(--pal-paper);
  font:inherit; font-size:12px; font-weight:600; color:var(--pal-ink); cursor:pointer;
}
.pal-editbtn:hover:not(:disabled){ background:#F2F2F4; }
.pal-editbtn:disabled{ opacity:.4; cursor:default; }
.pal-editbtn:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }
.pal-count-small{ margin-left:auto; font-size:11px; color:var(--pal-dim); font-variant-numeric:tabular-nums; }

/* ---- where a palette came from ---- */
.pal-yours{ color:var(--pal-ink); font-weight:600; }

/* ---- starting one from nothing ---- */
.pal-new{
  display:flex; align-items:center; justify-content:center; gap:7px; width:100%; height:36px;
  margin-top:10px; border:1px solid var(--pal-edge); border-radius:9px; background:var(--pal-paper);
  font:inherit; font-size:12.5px; font-weight:600; color:var(--pal-ink); cursor:pointer;
}
.pal-new:hover{ background:#F2F2F4; }
.pal-new:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }
/* THE CODE IS SET IN A FIXED-WIDTH FACE and its digits do not shift about, because a column of
   codes is read down rather than across and proportional digits make it ragged. */
.pal-rcode{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:11.5px;
  letter-spacing:.02em; color:var(--pal-dim); white-space:nowrap;
}
.pal-copy{
  flex:none; width:26px; height:26px; padding:0; display:flex; align-items:center;
  justify-content:center; border:none; border-radius:6px; background:none;
  color:var(--pal-dim); cursor:pointer;
  /* QUIET BUT ALWAYS THERE. It was hidden until the row was hovered, which is tidy and means the
     control the task asks for is invisible until you happen to move over it. A button nobody can
     see is not a button. */
  opacity:.42; transition:opacity .1s ease;
}
.pal-row:hover .pal-copy, .pal-copy:focus-visible, .pal-copy.done{ opacity:1; }
.pal-copy:hover{ background:#F2F2F4; color:var(--pal-ink); }
.pal-copy.done{ color:var(--pal-ink); }
.pal-copy:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:1px; }
.pal-empty{
  display:flex; flex-direction:column; height:100%; align-items:stretch; justify-content:center;
  gap:16px; padding:0 20px;
}
.pal-empty p{ margin:0; color:var(--pal-dim); text-align:center; line-height:1.5; }
.pal-emptybtn{
  width:100%; height:44px; padding:0 16px; border-radius:10px;
  font:inherit; font-size:13px; font-weight:600; cursor:pointer;
}
/* its colours are Add to Canvas's colours, taken from the same rule below rather than written
   again here, so the two can never drift apart */

/* ---- the footer, across all three columns, never scrolls ---- */
.pal-foot{
  grid-column:1 / -1;
  display:flex; justify-content:flex-end; align-items:center; gap:10px;
  padding:14px 20px; background:var(--pal-paper); border-top:1px solid var(--pal-edge);
}
.pal-go, .pal-cancel{
  height:40px; padding:0 20px; border-radius:10px; font:inherit; font-size:13px;
  font-weight:600; cursor:pointer;
}
.pal-cancel{ border:1px solid var(--pal-edge); background:var(--pal-paper); color:var(--pal-ink); }
.pal-cancel:hover{ background:#F4F4F6; }
.pal-go, .pal-emptybtn{ border:1px solid var(--pal-accent); background:var(--pal-accent); color:#FFF; }
.pal-go:hover, .pal-emptybtn:hover{ background:var(--pal-accent-h); border-color:var(--pal-accent-h); }
.pal-go:active:not(:disabled){ background:var(--pal-accent-h); transform:scale(.97); }
.pal-go:disabled{ opacity:.38; cursor:not-allowed; }
.pal-go:disabled:hover{ background:var(--pal-accent); border-color:var(--pal-accent); }
.pal-go:focus-visible, .pal-cancel:focus-visible, .pal-emptybtn:focus-visible{ outline:2px solid var(--pal-accent); outline-offset:2px; }

/* ---- the way out, in the corner the other apps put theirs ---- */
.pal-x{
  position:absolute; top:10px; right:12px; z-index:2;
  width:34px; height:34px; border:none; border-radius:99px;
  background:none; color:var(--pal-dim); font:inherit; font-size:24px; line-height:1;
  cursor:pointer;
}
.pal-x:hover{ background:rgba(0,0,0,.06); color:var(--pal-ink); }
.pal-x:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:2px; }

/* -------------------------------------------------------------------------------------------------
   THE DARK THEME
   -------------------------------------------------------------------------------------------------
   Nine values swapped and nothing else, because everything above is written in terms of them. The
   swatches themselves are untouched — a palette is the same palette whichever theme somebody has
   the editor set to, and dimming it to suit the panels would be showing a colour that is not the
   one you get. */
body:not(.light) .pal{
  --pal-paper:#161617;
  --pal-back:#0E0E0F;
  --pal-ink:#F2F2F3;
  --pal-dim:#9A9AA2;
  --pal-edge:#2C2C30;
  --pal-hair:#232326;
}
body:not(.light) .pal-search input{ background:#0E0E0F; }
body:not(.light) .pal-search input:focus{ border-color:#4A4A52; box-shadow:0 0 0 3px rgba(255,255,255,.06); }
body:not(.light) .pal-shelf:hover{ background:#202024; }
/* a wash rather than a flat tint, so it sits on the dark panel instead of glowing off it */
body:not(.light) .pal{ --pal-accent-soft:rgba(61,149,255,.16); }
body:not(.light) .pal-shelf.on{ background:var(--pal-accent-soft); }
body:not(.light) .pal-card:hover{ border-color:#414146; }
body:not(.light) .pal-cardwrap.on .pal-card{ border-color:#F2F2F3; box-shadow:0 0 0 1px #F2F2F3; }
body:not(.light) .pal-filter select{ background:#0E0E0F; }
body:not(.light) .pal-clear:hover:not(:disabled){ background:#202024; }
body:not(.light) .pal-clear-big:hover{ background:#202024; }
/* the heart keeps its pale disc in both themes, because what is under it is a colour rather than
   a panel and the theme says nothing about that */
body:not(.light) .pal-heart{ background:rgba(20,20,22,.78); color:#E8E8EA; }
body:not(.light) .pal-heart:hover{ background:#141416; }
body:not(.light) .pal-heart.on{ color:#FFF; }
body:not(.light) .pal-dheart:hover{ background:#202024; }
body:not(.light) .pal-fmts{ background:#161617; }
body:not(.light) .pal-fmt{ background:transparent; }
body:not(.light) .pal-fmt:hover{ background:#202024; }
/* the dark theme paints .pal-fmt transparent, and that outranks .pal-fmt.on — so the chosen one
   has to say so again here or it comes out with no fill at all. Found by reading it back off the
   screen after the accent went in, 6 September 2026. */
body:not(.light) .pal-fmt.on{ background:var(--pal-accent); color:#FFF; }
body:not(.light) .pal-copyall:hover{ background:#202024; }
body:not(.light) .pal-copy:hover{ background:#202024; }
body:not(.light) .pal-row.copied{ background:#202024; }
body:not(.light) .pal-rowbtn:hover:not(:disabled){ background:#202024; }
body:not(.light) .pal-editbtn:hover:not(:disabled){ background:#202024; }
body:not(.light) .pal-new:hover{ background:#202024; }
body:not(.light) .pal-cancel:hover{ background:#202024; }
/* The filled button inverts with the theme rather than staying black on black. */
body:not(.light) .pal-x:hover{ background:rgba(255,255,255,.10); }

/* ---- where a palette is kept, said under the search box ---- */
.pal-kept{
  margin:10px 2px 0; font-size:11px; line-height:1.45; color:var(--pal-dim);
}
/* KEPT IN THIS BROWSER IS WORTH NOTICING, so it is not the same quiet grey as everything else.
   Not a warning — nothing is wrong — but a person who has just built a palette should read this
   rather than skim past it. */
.pal-kept.here{ color:var(--pal-ink); }
.pal-kept.bad{ color:#C0392B; }
body:not(.light) .pal-kept.bad{ color:#FF7B6B; }
.pal-danger{ margin-left:auto; color:#C0392B; border-color:rgba(192,57,43,.32); }
.pal-danger:hover:not(:disabled){ background:rgba(192,57,43,.06); }
body:not(.light) .pal-danger{ color:#FF7B6B; border-color:rgba(255,123,107,.3); }
body:not(.light) .pal-danger:hover:not(:disabled){ background:rgba(255,123,107,.09); }
/* ---- the collections group in the left column ---- */

/* =================================================================================================
   CHOOSING HOW A PALETTE WILL LOOK ON THE PAGE
   =================================================================================================
   Task 8, 4 September 2026. The right-hand column has two halves behind two tabs: the colours, and
   the arrangement. Everything drawn here comes from the layout engine — the five little pictures
   are the real thing at a small size, not five illustrations of it. */
.pal-tabs{ display:flex; gap:2px; margin:0 0 14px; border-bottom:1px solid var(--pal-edge); }
.pal-tab{
  padding:8px 12px 9px; border:none; background:none; font:inherit; font-size:12.5px;
  color:var(--pal-dim); cursor:pointer; position:relative;
}
.pal-tab.on{ color:var(--pal-ink); font-weight:600; }
.pal-tab.on::after{
  content:""; position:absolute; left:8px; right:8px; bottom:-1px; height:2px;
  background:var(--pal-ink); border-radius:2px;
}
.pal-tab:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:-2px; border-radius:4px; }

/* THE BIG PICTURE SITS ON A CHEQUER, so a palette with no background of its own reads as having
   none rather than as having a white one. The same convention every image editor uses. */
/* PLAIN, since 5 September 2026 at Mounir's word: no checkerboard behind the preview and no
   border around it. It sits on the panel like everything else. */
.pal-bigprev{
  display:flex; align-items:center; justify-content:center; min-height:120px; padding:12px 0;
  overflow:hidden;
}
.pal-prev{ display:block; max-width:100%; height:auto; }
.pal-prevsize{ margin:7px 2px 14px; font-size:11px; color:var(--pal-dim); font-variant-numeric:tabular-nums; }

/* ---- the five ---- */
.pal-lays{ display:grid; grid-template-columns:repeat(5,1fr); gap:6px; margin-bottom:16px; }
.pal-lay{
  display:flex; flex-direction:column; align-items:center; gap:5px; padding:7px 3px 6px;
  border:1px solid var(--pal-edge); border-radius:9px; background:var(--pal-paper);
  font:inherit; cursor:pointer;
}
.pal-lay:hover{ border-color:#CFCFD6; }
.pal-lay.on{ border-color:var(--pal-accent); box-shadow:0 0 0 1px var(--pal-accent); }
.pal-lay:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:2px; }
.pal-laypic{ display:flex; align-items:center; justify-content:center; width:100%; height:44px; }
.pal-laypic svg{ max-width:100%; max-height:100%; }
.pal-layname{ font-size:10px; color:var(--pal-dim); }
.pal-lay.on .pal-layname{ color:var(--pal-accent); font-weight:600; }

/* ---- the settings ---- */
.pal-sets{ display:grid; grid-template-columns:1fr 1fr; gap:8px 10px; }
.pal-set{ display:flex; flex-direction:column; gap:4px; min-width:0; }
.pal-set > span{ font-size:11px; color:var(--pal-dim); }
.pal-set input[type=number], .pal-set select{
  height:30px; padding:0 8px; border:1px solid var(--pal-edge); border-radius:7px;
  background:var(--pal-paper); font:inherit; font-size:12px; color:var(--pal-ink); min-width:0;
}
.pal-set select{ appearance:none; padding-right:22px; cursor:pointer; }
.pal-set input:focus, .pal-set select:focus{ outline:none; border-color:var(--pal-ink); }
.pal-bgrow{ display:flex; align-items:center; gap:9px; height:30px; }
.pal-swwrap.off .pal-sw{ opacity:.25; }
.pal-ticks{ margin-top:14px; }
.pal-tick{ display:inline-flex; align-items:center; gap:6px; font-size:12px; cursor:pointer; }
.pal-tick input{ width:14px; height:14px; accent-color:var(--pal-ink); cursor:pointer; }

/* ---- THE FOUR SWITCHES, one to a row: what it shows on the left, the switch on the right.
   5 September 2026. A row of small tick boxes reading "Title  Colour names  Codes" looked like a
   heading; these say what they do and are big enough to hit. ---- */
.pal-switch{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  padding:9px 0; font-size:13px; cursor:pointer;
}
.pal-switch + .pal-switch{ border-top:1px solid var(--pal-edge); }
.pal-sws{
  appearance:none; -webkit-appearance:none; margin:0; flex:none; position:relative;
  width:40px; height:24px; border-radius:12px; background:#D8D8DE; cursor:pointer;
  transition:background .15s ease;
}
.pal-sws::after{
  content:""; position:absolute; top:2px; left:2px; width:20px; height:20px; border-radius:50%;
  background:#FFF; box-shadow:0 1px 3px rgba(0,0,0,.28); transition:transform .15s ease;
}
.pal-sws:checked{ background:var(--pal-accent); }
.pal-sws:checked::after{ transform:translateX(16px); }
.pal-sws:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:2px; }

body:not(.light) .pal-set input[type=number], body:not(.light) .pal-set select{ background:#0E0E0F; }
body:not(.light) .pal-sws{ background:#3A3A42; }
body:not(.light) .pal-sws:checked{ background:var(--pal-accent); }
body:not(.light) .pal-sws::after{ background:#F2F2F3; }
/* on a dark screen the "on" track is near-white, so the knob has to go dark or it disappears
   into it — a switch you cannot see the state of is worse than no switch */
body:not(.light) .pal-lay:hover{ border-color:#404046; }

/* ---- USING A COLOUR ON WORK YOU ALREADY HAVE, task 11, 5 September 2026 -------------------
   Sits under the big strip, between looking at a palette and doing something with it. Quiet
   until it is needed: the only loud thing in it is the row of colours, which is the point. */
.pal-use{
  margin:0 0 16px; padding:12px 12px 11px; border:1px solid var(--pal-edge); border-radius:10px;
  background:var(--pal-paper);
}
.pal-use-none p{ margin:0; font-size:12px; line-height:1.45; color:var(--pal-dim); }
.pal-usehead{ margin:0 0 9px; font-size:12px; color:var(--pal-dim); }
.pal-usehead b{ color:var(--pal-ink); font-weight:600; }
.pal-uselock{ margin:-4px 0 9px; font-size:11.5px; line-height:1.4; color:#C0392B; }
.pal-usetabs{ display:flex; gap:6px; margin-bottom:9px; }
.pal-usetab{
  padding:5px 11px 6px; border:1px solid var(--pal-edge); border-radius:999px;
  background:none; font:inherit; font-size:11.5px; color:var(--pal-dim); cursor:pointer;
}
.pal-usetab.on{ border-color:var(--pal-ink); color:var(--pal-ink); font-weight:600; }
.pal-usetab:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:2px; }
/* GENEROUS ENOUGH TO PRESS. These are the one thing in this block anybody aims at, and a row of
   14px squares is a row of misses. */
.pal-usestrip{ display:flex; flex-wrap:wrap; gap:6px; }
.pal-usecol{
  width:34px; height:34px; border:1px solid rgba(0,0,0,.14); border-radius:8px;
  padding:0; cursor:pointer;
}
.pal-usecol:hover{ transform:translateY(-1px); }
.pal-usecol:focus-visible{ outline:2px solid var(--pal-ink); outline-offset:2px; }
.pal-usenote{ margin:9px 0 0; font-size:11px; line-height:1.45; color:var(--pal-dim); }

body:not(.light) .pal-usecol{ border-color:rgba(255,255,255,.16); }
body:not(.light) .pal-uselock{ color:#FF7B6B; }
/* which build of the palettes plugin this is — tiny, grey, for reading off a screenshot */
.pal-build{ margin:-8px 0 12px; font-size:10.5px; color:var(--pal-dim); font-variant-numeric:tabular-nums; }

/* ===========================================================================================
   LAYOUTS — the app that puts a ready-made arrangement on the page. 8 September 2026.

   THE THUMBNAILS CARRY THE WHOLE THING, so they get the room. Everything round them is kept
   quiet on purpose: a card with a name, a note when the layout does not suit the page, and
   nothing else. The interface is not the interesting part and should not look like it is.
   =========================================================================================== */
.app-card-wide{ width:min(940px,100%); }

.lay-grid{
  display:grid; gap:14px; margin-top:12px;
  /* three across on a roomy window, two when it narrows, one on a phone — by asking for a
     minimum readable width rather than by counting screens */
  grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
  max-height:min(58vh,520px); overflow:auto; padding:2px;
}

/* THE KINDS RUN DOWN THE LEFT. Asked for on 9 September 2026: "this need to be in left side
   menu, not top". Across the top they wrapped onto a second line and pushed the thumbnails down;
   down the side they are one steady column and the thumbnails start at the top of the window.
   The search box sits above the thumbnails, over the thumbnails, where what it filters is. */
.lay-body{ display:flex; align-items:flex-start; gap:18px; margin-top:14px; }
.lay-main{ flex:1 1 auto; min-width:0; }
.lay-search{ margin-top:0; }
.lay-chips{ flex:0 0 172px; display:flex; flex-direction:column; gap:3px; }
.lay-chip{
  display:flex; align-items:center; gap:7px; width:100%; text-align:left;
  padding:8px 12px; border:1px solid transparent; border-radius:9px;
  background:transparent; color:var(--ed-muted,#7a7a88);
  font:inherit; font-size:13px; cursor:pointer; white-space:nowrap;
  transition:background .12s ease, color .12s ease, border-color .12s ease;
}
.lay-chip:hover{ background:rgba(127,127,127,.09); color:var(--ed-ink,#16161a); }
.lay-chip.on{ background:#3D95FF; border-color:#3D95FF; color:#fff; }
.lay-chip:focus-visible{ outline:2px solid #3D95FF; outline-offset:2px; }
.lay-chip svg{ fill:currentColor; }
/* On a phone there is no room for a column beside the thumbnails, so the kinds go back to a
   row that wraps — above the search box, still before what they narrow. */
@media (max-width:640px){
  .lay-body{ display:block; }
  .lay-chips{ flex-direction:row; flex-wrap:wrap; gap:6px; margin-bottom:12px; }
  .lay-chip{ width:auto; border-radius:999px; border-color:var(--line); padding:6px 12px; }
}
.lay-empty{ grid-column:1/-1; margin:18px 2px; font-size:13px; color:var(--ed-muted,#7a7a88); }

/* THE HEART SITS OVER THE CARD RATHER THAN INSIDE IT — a button inside a button is not allowed,
   and somebody aiming for the heart must never accidentally drop a layout on their page. */
.lay-item{ position:relative; }
.lay-fav{
  position:absolute; top:16px; right:16px; width:26px; height:26px; padding:0;
  display:grid; place-items:center; border:0; border-radius:999px; cursor:pointer;
  background:rgba(255,255,255,.86); color:#9aa1ad; fill:none; stroke:currentColor; stroke-width:1.8;
  opacity:0; transition:opacity .12s ease, color .12s ease;
}
.lay-item:hover .lay-fav, .lay-fav:focus-visible, .lay-fav.on{ opacity:1; }
.lay-fav:hover{ color:#E8384F; }
/* a favourite stays lit whether or not the pointer is anywhere near it — that is the point */
.lay-fav.on{ color:#E8384F; fill:currentColor; stroke:none; }
.lay-fav:focus-visible{ outline:2px solid #3D95FF; outline-offset:2px; }

/* THROWING A SAVED ARRANGEMENT AWAY. Top LEFT, opposite the heart, so the two are never confused
   and a hand going for one cannot land on the other. Only a saved one has it: the library is not
   the person's to delete, and an invented one is not stored anywhere to be deleted from. */
.lay-bin{
  position:absolute; top:16px; left:16px; width:26px; height:26px; padding:0;
  display:grid; place-items:center; border:0; border-radius:999px; cursor:pointer;
  background:rgba(255,255,255,.86); color:#9aa1ad;
  opacity:0; transition:opacity .12s ease, color .12s ease;
}
.lay-item:hover .lay-bin, .lay-bin:focus-visible{ opacity:1; }
.lay-bin:hover{ color:#b91c1c; }
.lay-bin:focus-visible{ outline:2px solid #3D95FF; outline-offset:2px; }

/* KEEPING ONE. The button sits under the chips because it is about the page rather than about the
   list, and it says what it will keep rather than just "Save" — a person who has four things
   selected should be able to read that before pressing, not after. */
.lay-keep{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-top:12px; }
.lay-keep-btn{
  flex:0 0 auto; padding:8px 15px; border:1px solid var(--line); border-radius:999px;
  background:var(--ed-field,#fff); color:var(--ed-ink,#16161a);
  font:inherit; font-size:12.5px; font-weight:600; cursor:pointer;
}
.lay-keep-btn:hover:not(:disabled){ border-color:#3D95FF; color:#3D95FF; }
.lay-keep-btn:focus-visible{ outline:2px solid #3D95FF; outline-offset:2px; }
.lay-keep-btn:disabled{ opacity:.5; cursor:default; }
.lay-keep-why{ flex:1 1 200px; font-size:12px; color:var(--ed-muted,#7a7a88); }

.lay-nameit{
  display:grid; grid-template-columns:1fr auto auto; gap:8px; align-items:center;
  margin-top:10px; padding:12px 14px; border-radius:12px;
  background:rgba(127,127,127,.08); border:1px solid var(--line);
}
.lay-nameit-in{ grid-column:1; }
.lay-nameit-go, .lay-nameit-no{
  padding:10px 16px; border-radius:12px; border:1px solid var(--line);
  font:inherit; font-size:13px; font-weight:600; cursor:pointer;
  background:var(--ed-field,#fff); color:var(--ed-ink,#16161a);
}
.lay-nameit-go{ background:#3D95FF; border-color:#3D95FF; color:#fff; }
.lay-nameit-go:hover{ background:#2C7FE0; border-color:#2C7FE0; }
.lay-nameit-no:hover{ border-color:#3D95FF; color:#3D95FF; }
.lay-nameit-go:focus-visible, .lay-nameit-no:focus-visible{
  outline:2px solid #3D95FF; outline-offset:2px;
}
/* WHERE IT GOES, SAID BEFORE IT IS KEPT rather than in a note somewhere else. Somebody about to
   spend a minute naming an arrangement should know it lives in this browser and nowhere else. */
.lay-nameit-warn{
  grid-column:1/-1; margin:2px 0 0; font-size:11.5px; color:var(--ed-muted,#7a7a88);
}

.lay-card{
  display:flex; flex-direction:column; gap:8px; padding:10px; width:100%;
  background:var(--ed-panel,#fff); border:1px solid var(--line); border-radius:14px;
  text-align:left; cursor:pointer; font:inherit; color:inherit;
  transition:border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
.lay-card:hover{ border-color:#3D95FF; box-shadow:0 6px 18px rgba(61,149,255,.16); }
.lay-card:active{ transform:translateY(1px); }
.lay-card:focus-visible{ outline:2px solid #3D95FF; outline-offset:2px; }

/* The thumbnail keeps the page's own shape rather than being squashed into a fixed box —
   the whole point of drawing it at the canvas size is that it tells you the truth about what
   you are going to get. */
/* EVERY CARD IS THE SAME HEIGHT, and each drawing keeps its own shape inside it. The drawings are
   no longer all the same proportion — a wide card, a square post and a tall poster are drawn as
   what they are — so left to size themselves the cards would step up and down the grid like a
   broken fence. A fixed box with the drawing centred in it gives a tidy wall AND an honest
   picture, which is the whole point of showing the real proportions. */
.lay-thumb{
  display:block; height:152px; background:#F4F5F7; border:1px solid var(--line);
  border-radius:9px; overflow:hidden;
}
.lay-thumb svg{ display:block; width:100%; height:100%; }
.lay-name{ font-size:13px; font-weight:600; }
.lay-note{ font-size:11px; color:var(--ed-muted,#7a7a88); min-height:0; }
.lay-off .lay-thumb{ opacity:.72; }

/* ===================== PRINTING THE DESIGN ITSELF ========================================
 * Steps 1 to 4 of the "real text and real shapes in the PDF" plan, 15 September 2026.
 *
 * Print used to print a PICTURE of the design, drawn a second time by Render X, so a PDF made
 * this way contained no text — every word in it was a photograph of a word. Now the browser
 * prints the real design off the screen, which is how live text and live vectors get into the
 * file. These rules are what clears everything else off the sheet.
 *
 * THE DESIGN IS MOVED ONTO A SHEET WHILE THIS IS ON, and put back afterwards. Hiding the editor
 * around it was tried first and the design came out on two identical sheets: the editor is a
 * screen-sized thing with pieces hanging past its edges, and a browser counts sheets by how far
 * the document reaches rather than by what can be seen. Taking the page out of the editor leaves
 * nothing to reach past anything.
 *
 * THE SHEET HAS THREE PARTS. The paper, sized to the design plus whatever a printer needs round
 * it; a box that carries the artwork and cuts it off where the bleed ends; and the crop marks on
 * top, drawn as real lines. With no bleed switched on the box is exactly the design, so artwork
 * hanging over the edge is cut off there — which is what the downloads do.
 *
 * ON SCREEN, while the print box is open, the sheet is simply not shown. It would otherwise
 * appear below the editor for as long as the box is up.
 * ======================================================================================== */
@media screen{
  body.printing-design > .print-sheet{ display:none !important; }
}
@media print{
  html, body{ margin:0 !important; padding:0 !important; background:#fff !important;
              height:auto !important; min-height:0 !important; overflow:visible !important; }

  body.printing-design > *:not(.print-sheet){ display:none !important; }

  body.printing-design > .print-sheet{
    display:block !important; margin:0 !important; overflow:visible !important; }

  /* ONE SHEET PER PAGE, AND NO BLANK ONE ON THE END. The break goes BEFORE every sheet after
     the first rather than after every sheet: a break after the last one asks the browser for a
     sheet with nothing on it, which is what a six-page document coming out as seven means. */
  body.printing-design > .print-sheet + .print-sheet{ break-before:page !important; }

  /* the design carries the zoom it was being looked at at, and nothing else may shift it */
  body.printing-design .print-sheet .stage{
    transform:none !important; margin:0 !important;
    box-shadow:none !important; border-radius:0 !important; outline:none !important; }

  /* the marks sit over the artwork, in the margin outside the bleed */
  body.printing-design .print-marks{
    position:absolute !important; left:0 !important; top:0 !important; pointer-events:none; }

  /* without this a browser is free to drop background colours and photographs to save ink,
     and a poster printed without its background is not the poster */
  body.printing-design .print-sheet, body.printing-design .print-sheet *{
    -webkit-print-color-adjust:exact !important; print-color-adjust:exact !important; }
}

/* THE LETTER CASE MENU — 23 September 2026 (textcase.js). Four choices, each written in its own
   case so the word shows what it does, and a tick on the one in force. */
.tc-menu{ position:fixed; z-index:400; min-width:190px; padding:6px; background:var(--panel,#fff); color:var(--ink);
  border:1px solid var(--line); border-radius:14px; box-shadow:var(--float-shadow, 0 12px 32px rgba(0,0,0,.18)); }
.tc-opt{ display:flex; align-items:center; justify-content:space-between; gap:12px; width:100%; height:40px; padding:0 12px;
  border:0; border-radius:10px; background:transparent; color:inherit; font:inherit; font-size:14px; text-align:left; cursor:pointer; }
.tc-opt:hover{ background:var(--hover); }
.tc-opt.on{ font-weight:600; }
.tc-tick{ width:16px; height:16px; flex:none; color:var(--accent,#3D95FF); }

/* THE ZOOM TOOL — 26 September 2026 (zoomtool.js). While Z is on, this clear sheet lies over the
   page and takes the mouse, so a zoom drag can never pick anything up; it is removed the moment the
   tool is put down. The pointer is a magnifying glass, plus to zoom in, minus while Option is held,
   with its point at the centre of the lens. Holding Space lets the mouse through, so Space still pans. */
.zt-sheet{ position:absolute; inset:0; z-index:70; background:transparent;
  cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" stroke="white" stroke-linecap="round"><circle cx="10" cy="10" r="6.6" stroke-width="4.6"/><path d="M15 15L21 21" stroke-width="5.6"/></g><circle cx="10" cy="10" r="6.6" fill="white" stroke="black" stroke-width="2.2"/><path d="M15 15L21 21" stroke="black" stroke-width="3.2" stroke-linecap="round"/><path d="M6.8 10H13.2M10 6.8V13.2" stroke="black" stroke-width="1.9" stroke-linecap="round"/></svg>') 10 10, zoom-in; }
.zt-sheet.zt-out{ cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" stroke="white" stroke-linecap="round"><circle cx="10" cy="10" r="6.6" stroke-width="4.6"/><path d="M15 15L21 21" stroke-width="5.6"/></g><circle cx="10" cy="10" r="6.6" fill="white" stroke="black" stroke-width="2.2"/><path d="M15 15L21 21" stroke="black" stroke-width="3.2" stroke-linecap="round"/><path d="M6.8 10H13.2" stroke="black" stroke-width="1.9" stroke-linecap="round"/></svg>') 10 10, zoom-out; }
.zt-sheet.zt-pass{ pointer-events:none; }

/* ===================== THE TEMPLATES MENU AND THE INDUSTRY PAGES — 7 October 2026 (catpages.js) =====
   Shaped like Envato's: an "All templates" line on top, then every industry in three columns. The
   same white card, border and shadow as the Apps menu beside it, so the two read as one header. */
.tf-megawrap{ position:relative; }
.tf-navlinks a.tf-megalink{ display:inline-flex; align-items:center; gap:6px; }
.tf-megalink .tf-navchev{ transition:transform .15s; }
.tf-megalink.open .tf-navchev{ transform:rotate(180deg); }
.tf-mega{ position:absolute; top:calc(100% + 10px); left:-24px; z-index:61; width:min(760px, calc(100vw - 48px));
  background:#fff; border:1px solid var(--bd); border-radius:18px; padding:22px 24px 18px;
  box-shadow:0 1px 2px rgba(22,24,45,.06), 0 24px 56px -16px rgba(22,24,45,.24); }
/* the gap between the word and the card is part of the menu, or the pointer falls through it */
.tf-mega::before{ content:""; position:absolute; left:0; right:0; top:-12px; height:12px; }
.tf-mega-all{ display:inline-flex; align-items:center; gap:8px; font-size:18px; font-weight:700; color:var(--fg);
  text-decoration:none; padding:6px 10px; margin:0 -10px 10px; border-radius:10px; }
.tf-mega-all:hover{ background:var(--bg,#F5F5F7); }
.tf-mega-h{ font-size:12px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--mut3,#8a8f98); margin:4px 0 8px; }
.tf-mega-cols{ display:grid; grid-template-columns:repeat(3,1fr); gap:2px 18px; }
.tf-mega-col{ display:flex; flex-direction:column; gap:2px; }
.tf-mega-link{ display:flex; align-items:center; justify-content:space-between; gap:10px; min-height:36px; padding:0 10px;
  margin:0 -10px; border-radius:10px; font-size:14px; font-weight:400; color:var(--fg); text-decoration:none; }   /* regular, not bold, a size down — 7 October 2026 */
.tf-mega-link:hover, .tf-mega-link:focus-visible{ background:var(--bg,#F5F5F7); outline:none; }
.tf-mega-link em{ font-style:normal; font-size:12px; color:var(--mut3,#8a8f98); }
/* the introduction under a category page's heading */
.tf-catintro{ font-size:15px; line-height:1.5; color:var(--mut2,#555); margin:6px 0 2px; max-width:640px; }
/* the type list in the filter drawer, on a category page */
.tf-ftype{ display:flex; align-items:center; gap:10px; }
.tf-ftype em{ margin-left:auto; font-style:normal; font-size:12px; color:var(--mut3,#8a8f98); }
.tf-ftype-none{ font-size:14px; color:var(--mut3,#8a8f98); margin:6px 0; }

/* ===================== THE HEADER IN ONE ROW, WHEN SCROLLED — 8 October 2026 (headcompact.js) =====
   Logo, search bar, and the right-hand buttons on one line, like Envato's. The links fade out; the
   search bar arrives in the space they left, with even room either side. */
.tf-navslot{ display:none; }   /* not .tf-navsearch: an older, unused rule already has that name */
body.head-compact .tf-navslot{ display:flex; flex:1; min-width:0; margin:0 clamp(16px, 3vw, 44px); }
body.head-compact .tf-navslot .tf-herosearch{ flex:1; height:48px; }
body.head-compact .tf-navlinks{ flex:none; margin-left:0; gap:0; }
body.head-compact .tf-navright{ margin-left:0; }
/* while it is in one row: the links, the search row and the category row are not shown. The change
   happens while the header is slid up out of sight (headcompact.js), so nothing needs to fade. */
body.head-compact .tf-navlinks > :not(#navEditor){ display:none; }
body.head-compact .tf-searchrow, body.head-compact #catBand{ display:none; }
/* no will-change on the header: it would make the fixed-position menus inside it (All Items, News)
   measure from the header instead of the window */

/* THE ROW OF CATEGORY PILLS UNDER THE SEARCH BAR IS GONE — 8 October 2026, Mounir, with a picture
   of it: "remove". Hidden here rather than taken out of the page, so everything that fills it and
   the Filter button it switches on keep working exactly as before; the header is simply one row
   shorter. The industries are still in the Templates menu and the filter drawer. */
#catBand{ display:none !important; }
/* White, not the page's grey, behind the header while it slides up and back down (headcompact). */
body.head-moving{ background:#fff; }

/* ===================== THE MENU ON TABLETS AND PHONES — 9 October 2026 (mobilenav.js) =====================
 * Envato's way, asked for by Mounir with five pictures. Three stages:
 *   wider than 1200  — the full header, untouched (1100 until Graphics was added, 10 October 2026).
 *   761 to 1200      — logo, Start creating, the menu button. Templates, Apps, Pricing, the bell and the
 *                      account buttons go into the menu, because at 900 and 768 they ran into each other.
 *   760 and below    — logo and the menu button only (Start creating is in the menu).
 * The rules use !important only where the app sets a display from script (the avatar, Upgrade). */
.tf-burger{ display:none; align-items:center; justify-content:center; width:46px; height:46px; margin-left:4px;
  border:none; border-radius:14px; background:transparent; color:var(--tf-ink,#0D0D0C); cursor:pointer; }
.tf-burger:hover{ background:var(--bg,#F5F5F7); }
.tf-burger:focus-visible{ outline:2px solid var(--tf-ink,#0D0D0C); outline-offset:2px; }
@media (max-width:1200px){   /* 1100 until 10 October 2026, when Graphics joined the header */
  .tf-burger{ display:inline-flex; }
  .tf-navlinks{ margin-left:0; justify-content:flex-end; }
  .tf-navlinks > :not(#navEditor){ display:none !important; }
  .tf-navright{ margin-left:8px; gap:6px; }
  .tf-navright > #newsBell, .tf-navright > #authBtns, .tf-navright > #btnUpgrade,
  .tf-navright > .tf-avwrap{ display:none !important; }
  .tf button.tf-create{ height:40px; padding:0 23px; font-size:14px; white-space:nowrap; }
}
@media (max-width:760px){
  .tf-navlinks{ display:none !important; }
  .tf-navright{ margin-left:auto; }
}
/* the search bar's own field never forces the bar wider than the phone. A text box keeps a width of
   its own unless told otherwise, and at 390 it pushed the green search button 19 pixels off the side */
.tf-herosearch input{ min-width:0; }
@media (max-width:480px){ .tf-cat-btn{ padding:0 8px 0 14px; } .tf-herosearch input{ margin-left:8px; } }
/* the footer's links wrap onto more lines instead of pushing the page wider than the phone — at 390
   pixels they made the whole page 658 wide, so every page could be dragged sideways */
@media (max-width:760px){
  .tf-flinks{ flex-wrap:wrap; gap:10px 20px; }
  .tf-footer-in{ padding:32px var(--edge); }
}

.mn-veil{ position:fixed; inset:0; z-index:900; background:rgba(13,13,12,.42); opacity:0; transition:opacity .25s ease; }
.mn-drawer{ position:fixed; top:0; right:0; bottom:0; z-index:901; width:min(440px, calc(100vw - 56px));
  display:flex; flex-direction:column; background:#fff; color:var(--tf-ink,#0D0D0C);
  box-shadow:-18px 0 48px rgba(0,0,0,.16); transform:translateX(100%); transition:transform .26s cubic-bezier(.22,.8,.24,1);
  font-family:inherit; }
html.mn-open .mn-veil{ opacity:1; }
html.mn-open .mn-drawer{ transform:none; }
html.mn-open, html.mn-open body{ overflow:hidden; }
.mn-head{ display:flex; align-items:center; justify-content:space-between; gap:12px; padding:18px 20px 10px 28px; flex:none; }
.mn-logo img{ height:34px; width:auto; display:block; }
.mn-x{ width:46px; height:46px; border:none; border-radius:14px; background:transparent; color:inherit; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; }
.mn-x:hover{ background:var(--bg,#F5F5F7); }
.mn-body{ position:relative; flex:1; min-height:0; overflow:hidden; }
.mn-pane{ position:absolute; inset:0; display:flex; flex-direction:column; overflow-y:auto; -webkit-overflow-scrolling:touch;
  padding:6px 0 20px; transition:transform .26s cubic-bezier(.22,.8,.24,1); }
.mn-sub{ transform:translateX(100%); }
.mn-drawer.sub-open .mn-main{ transform:translateX(-30%); visibility:hidden; transition:transform .26s cubic-bezier(.22,.8,.24,1), visibility 0s .26s; }
.mn-drawer.sub-open .mn-sub{ transform:none; }
.mn-list{ display:flex; flex-direction:column; padding:0 16px; }
.mn-row{ display:flex; align-items:center; justify-content:space-between; gap:12px; width:100%; min-height:54px;
  padding:0 12px; border:none; border-radius:14px; background:transparent; color:inherit; text-align:left;
  font:inherit; font-size:19px; font-weight:600; letter-spacing:-.01em; text-decoration:none; cursor:pointer; }
.mn-row:hover, .mn-row:focus-visible{ background:var(--bg,#F5F5F7); outline:none; }
.mn-row svg{ flex:none; color:var(--mut2,#55555E); }
.mn-soft{ font-size:17px; font-weight:500; min-height:48px; }
.mn-strong{ font-weight:700; }
.mn-app{ justify-content:flex-start; font-size:17px; }
.mn-app img{ width:28px; height:28px; border-radius:7px; flex:none; }
.mn-h{ padding:18px 12px 6px; font-size:12.5px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--mut3,#9A9AA4); }
.mn-sub .mn-row:not(.mn-strong){ font-size:16.5px; font-weight:500; min-height:46px; }
.mn-line{ height:1px; margin:12px 12px; background:var(--bd,#EAEAEF); }
.mn-dot{ width:9px; height:9px; border-radius:50%; background:#E0483F; flex:none; }
.mn-back{ display:flex; align-items:center; gap:8px; margin:0 16px 4px; min-height:50px; padding:0 12px; border:none;
  border-radius:14px; background:transparent; color:inherit; font:inherit; font-size:19px; font-weight:700; cursor:pointer; text-align:left; }
.mn-back:hover, .mn-back:focus-visible{ background:var(--bg,#F5F5F7); outline:none; }
.mn-foot{ margin-top:auto; display:flex; flex-direction:column; gap:10px; padding:20px 28px calc(20px + env(safe-area-inset-bottom)); }
.mn-btn{ height:54px; border-radius:14px; font:inherit; font-size:17px; font-weight:650; cursor:pointer; }
.mn-outline{ background:#fff; color:var(--tf-ink,#0D0D0C); border:1.5px solid var(--tf-ink,#0D0D0C); }
.mn-outline:hover{ background:var(--bg,#F5F5F7); }
.mn-primary{ background:#000; color:#fff; border:none; }
.mn-primary:hover{ filter:brightness(.96); }
@media (max-width:480px){
  .mn-drawer{ width:calc(100vw - 44px); }
  .mn-head{ padding-left:22px; }
  .mn-list{ padding:0 10px; }
  .mn-foot{ padding-left:22px; padding-right:22px; }
}
@media (prefers-reduced-motion:reduce){ .mn-drawer, .mn-veil, .mn-pane{ transition:none; } }

/* ===================== THE FILTERS ON A PHONE — 9 October 2026 (filtersheet.js) =====================
 * Below 760 pixels the filter panel rises from the bottom, Envato's way, instead of sliding in from
 * the left and squeezing the templates into a strip beside it. Above 760 nothing here applies. */
.fs-head, .fs-foot, .fs-veil{ display:none; }
@media (max-width:760px){
  .tf-drawer{ top:auto; left:0; right:0; bottom:0; width:auto; max-height:min(86vh, 760px); z-index:905;
    display:flex; flex-direction:column; overflow:hidden; border:none; border-radius:22px 22px 0 0;
    box-shadow:0 -18px 48px rgba(0,0,0,.18); transform:translateY(105%);
    transition:transform .28s cubic-bezier(.22,.8,.24,1); }
  body.drawer-open .tf-drawer{ transform:none; }
  body.drawer-open #home{ padding-left:0; }
  .fs-veil{ display:block; position:fixed; inset:0; z-index:904; background:rgba(13,13,12,.42);
    opacity:0; pointer-events:none; transition:opacity .25s ease; }
  body.drawer-open .fs-veil{ opacity:1; pointer-events:auto; }
  body.drawer-open{ overflow:hidden; }
  .fs-head{ display:flex; align-items:center; justify-content:space-between; gap:12px; flex:none;
    padding:22px 18px 14px 24px; border-bottom:1px solid var(--bd,#EAEAEF); }
  .fs-title{ margin:0; font-size:24px; font-weight:750; letter-spacing:-.02em; color:var(--fg); }
  .tf .fs-x{ width:46px; height:46px; border:none; border-radius:14px; background:transparent; color:var(--fg);
    display:inline-flex; align-items:center; justify-content:center; cursor:pointer; }
  .tf .fs-x:hover{ background:var(--bg); }
  .tf-drawer .tf-drawer-x{ display:none; }          /* the big close button above replaces it */
  .tf-drawer-in{ flex:1; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch; padding:16px 24px 20px; }
  .fs-foot{ display:block; flex:none; padding:14px 24px calc(16px + env(safe-area-inset-bottom));
    border-top:1px solid var(--bd,#EAEAEF); background:var(--card,#fff); }
  .tf .fs-show{ width:100%; height:54px; border:none; border-radius:14px; background:#000; color:#fff;
    font-family:inherit; font-size:17px; font-weight:650; cursor:pointer; }
  .tf .fs-show:hover{ filter:brightness(.96); }
}
@media (max-width:760px) and (prefers-reduced-motion:reduce){ .tf-drawer, .fs-veil{ transition:none; } }

/* the Graphics menu has no pictures beside its words, so its rows are a little shorter than Apps' */
.tf-gfxmenu{ min-width:200px; }
.tf-gfxmenu .tf-navitem{ height:46px; font-weight:500; }

/* A template's details, written into its own page for Google (13 October 2026). It sits above the
   gallery under the popup, and goes when the popup closes. */
.tf-tplseo{ padding:28px var(--edge, 32px) 6px; }
.tps-card{ display:flex; gap:28px; align-items:center; max-width:980px; padding:22px; border:1px solid var(--bd,#EAEAEF);
  border-radius:22px; background:var(--card,#fff); }
.tps-pic{ width:220px; max-height:260px; object-fit:contain; border-radius:14px; flex:none; background:#F4F4F6; }
.tps-text{ min-width:0; }
.tps-h{ margin:0; font-size:28px; font-weight:750; letter-spacing:-.02em; color:var(--tf-ink,#0D0D0C); }
.tps-facts{ margin:6px 0 0; font-size:15px; color:var(--mut2,#555); }
.tps-note{ margin:10px 0 0; font-size:14.5px; font-weight:600; color:var(--tf-ink,#0D0D0C); }
.tps-list{ margin:14px 0 0; padding-left:20px; font-size:15px; line-height:1.7; color:var(--tf-ink,#0D0D0C); }
.tps-more-h{ margin:26px 0 12px; font-size:18px; font-weight:700; color:var(--tf-ink,#0D0D0C); }
.tps-more{ display:flex; gap:14px; overflow-x:auto; margin:0; padding:0 0 6px; list-style:none; }
.tps-more li{ flex:none; width:140px; }
.tps-more a{ display:block; color:var(--tf-ink,#0D0D0C); text-decoration:none; font-size:13.5px; font-weight:600; }
.tps-more img{ display:block; width:140px; height:140px; object-fit:cover; border-radius:12px; background:#F4F4F6; margin-bottom:6px; }
.tps-more span{ display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
@media (max-width:640px){
  .tps-card{ flex-direction:column; align-items:flex-start; gap:16px; padding:18px; }
  .tps-pic{ width:100%; max-height:320px; }
  .tps-h{ font-size:24px; }
}

/* The Support page (13 October 2026) uses the About page's styles; only its email link is its own. */
#support .ab-hero{ padding-bottom:110px; }
#support .sp-mail{ color:var(--fg); font-weight:600; text-decoration:underline; text-underline-offset:3px; }
#support .ab-cta-row{ margin-top:30px; }

/* ===================== ANIMATOR — the drawer's contents (14 October 2026) ======================
   animator-panel.js draws it. The editor's own colours, so it is right in the light and the dark
   theme alike. */
#anBody{ padding:2px 2px 10px; color:var(--ink); }
.anm-tabs{ display:grid; grid-template-columns:1fr 1fr; gap:4px; padding:4px; border-radius:12px; background:var(--soft,#1A1A1A); border:1px solid var(--line); }
body.light .editor .anm-tabs{ background:#F2F3F6; }
.anm-tabs button{ height:36px; border:none; border-radius:9px; background:transparent; color:var(--muted); font:inherit; font-size:13px; font-weight:600;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; padding:0 8px; cursor:pointer; }
.anm-tabs button.on{ background:var(--panel); color:var(--ink); box-shadow:0 1px 4px rgba(0,0,0,.18); }
.anm-tabs button:disabled{ opacity:.45; cursor:default; }
.anm-hint{ margin:12px 0 0; padding:10px 12px; border-radius:10px; background:var(--soft,#1A1A1A); color:var(--muted); font-size:12.5px; line-height:1.5; }
body.light .editor .anm-hint{ background:#F4F5F8; }
.anm-lab{ display:flex; justify-content:space-between; align-items:center; margin:16px 0 8px; font-size:12px; font-weight:600; color:var(--muted); }
.anm-lab span{ font-weight:500; }
.anm-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:8px; }
.anm-tile{ display:flex; flex-direction:column; align-items:center; gap:6px; padding:0 0 8px; border:1px solid var(--line); border-radius:12px;
  background:transparent; color:var(--ink); font:inherit; font-size:12px; font-weight:600; cursor:pointer; }
.anm-tile:hover{ border-color:var(--bdh,#555); }
.anm-tile.on{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent) inset; }
.anm-pv{ width:100%; height:62px; display:grid; place-items:center; overflow:hidden; border-radius:11px 11px 0 0; }
.anm-pv i{ display:block; width:24px; height:24px; border-radius:7px; background:var(--accent); }
.anm-tile.none .anm-pv i{ background:transparent; border:2px dashed var(--muted); }
.anm-seg{ display:grid; grid-template-columns:repeat(3,1fr); gap:6px; }
.anm-seg4{ grid-template-columns:repeat(4,1fr); }
.anm-seg button{ height:36px; border:1px solid var(--line); border-radius:9px; background:transparent; color:var(--ink); font:inherit; font-size:12.5px; font-weight:600; cursor:pointer; }
.anm-seg button svg{ width:16px; height:16px; display:block; margin:auto; }
.anm-seg button.on{ background:var(--accent); border-color:var(--accent); color:#fff; }
.anm-row{ display:flex; align-items:center; gap:10px; }
.anm-row input[type=range]{ flex:1; accent-color:var(--accent); }
.anm-val{ min-width:50px; height:32px; border-radius:8px; display:grid; place-items:center; border:1px solid var(--line); font-size:12.5px; font-weight:600; font-variant-numeric:tabular-nums; }
.anm-switch{ display:flex; align-items:center; justify-content:space-between; margin-top:14px; padding-top:12px; border-top:1px solid var(--line); font-size:13px; font-weight:600; }
.anm-tog{ width:42px; height:26px; border-radius:99px; border:none; background:var(--line); position:relative; cursor:pointer; transition:background .15s; }
.anm-tog::after{ content:""; position:absolute; top:3px; left:3px; width:20px; height:20px; border-radius:50%; background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.25); transition:left .15s; }
.anm-tog.on{ background:var(--accent); }
.anm-tog.on::after{ left:19px; }
.anm-remove{ width:100%; height:40px; margin-top:16px; border:1px solid var(--line); border-radius:10px; background:transparent; color:var(--danger,#E0483F); font:inherit; font-size:13px; font-weight:600; cursor:pointer; }
/* the play badge on an animated object, kept the same size on screen at any zoom (the size is set by the script) */
.anm-badge{ position:absolute; z-index:5; pointer-events:none; border-radius:50%; background:var(--accent,#3E6BE6); color:#fff; display:grid; place-items:center; box-shadow:0 1px 4px rgba(0,0,0,.3); }
.anm-badge svg{ width:58%; height:58%; }
/* the tiles' little previews, playing while the pointer is over them */
@keyframes anm-fade{ 0%,15%{opacity:0} 60%,100%{opacity:1} }
@keyframes anm-rise{ 0%,15%{opacity:0; transform:translateY(18px)} 60%,100%{opacity:1; transform:none} }
@keyframes anm-slide{ 0%,15%{opacity:0; transform:translateX(-30px)} 60%,100%{opacity:1; transform:none} }
@keyframes anm-pop{ 0%,15%{opacity:0; transform:scale(.3)} 45%{opacity:1; transform:scale(1.15)} 60%,100%{transform:none} }
@keyframes anm-zoom{ 0%,15%{opacity:0; transform:scale(1.8)} 60%,100%{opacity:1; transform:none} }
@keyframes anm-wipe{ 0%,15%{clip-path:inset(0 100% 0 0)} 60%,100%{clip-path:inset(0 0 0 0)} }
@keyframes anm-blur{ 0%,15%{opacity:0; filter:blur(6px)} 60%,100%{opacity:1; filter:none} }
@keyframes anm-spin{ 0%,15%{opacity:0; transform:rotate(-180deg) scale(.4)} 60%,100%{opacity:1; transform:none} }
@keyframes anm-bounce{ 0%,10%{opacity:0; transform:translateY(-24px)} 35%{opacity:1; transform:none} 45%{transform:translateY(-8px)} 55%,100%{transform:none} }
.anm-tile:hover .anm-k-fade{ animation:anm-fade 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-rise{ animation:anm-rise 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-slide{ animation:anm-slide 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-pop{ animation:anm-pop 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-zoom{ animation:anm-zoom 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-wipe{ animation:anm-wipe 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-blur{ animation:anm-blur 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-spin{ animation:anm-spin 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-bounce{ animation:anm-bounce 1.6s ease-in-out infinite; }
@media (prefers-reduced-motion:reduce){ .anm-tile:hover .anm-pv i{ animation:none !important; } }
/* the Animator's player bar (step 5), above the zoom bar, only while the drawer is open */
.anm-player{ position:fixed; left:50%; bottom:74px; transform:translateX(-50%); z-index:121; display:flex; align-items:center; gap:12px;
  padding:7px 14px 7px 7px; background:var(--panel); border:1px solid var(--line); border-radius:14px; box-shadow:var(--float-shadow); color:var(--ink); }
.anm-player.hidden{ display:none; }
.anm-play{ width:40px; height:40px; border:none; border-radius:10px; background:var(--accent); color:#fff; display:grid; place-items:center; cursor:pointer; }
.anm-play svg{ width:18px; height:18px; }
.anm-track{ width:260px; height:6px; border-radius:99px; background:var(--line); position:relative; overflow:hidden; }
.anm-track b{ position:absolute; left:0; top:0; bottom:0; width:0; border-radius:99px; background:var(--accent); }
.anm-time{ min-width:84px; font-size:12.5px; font-weight:600; color:var(--muted); font-variant-numeric:tabular-nums; }
/* while the page plays: nothing on it can be picked up, and the badges step aside */
body.anm-playing #stageWrap{ pointer-events:none; }
body.anm-playing .anm-badge{ display:none !important; }
/* Animator step 6: the text tiles show letters, and the "while on the page" tiles keep moving on hover */
.anm-pv em{ font-style:normal; font-size:20px; font-weight:800; letter-spacing:-.01em; color:var(--accent); white-space:nowrap; }
.anm-pv em b{ font-weight:inherit; }
@keyframes anm-type{ 0%{opacity:0} 100%{opacity:1} }
@keyframes anm-pulse{ 0%,100%{transform:none} 50%{transform:scale(1.18)} }
@keyframes anm-sway{ 0%,100%{transform:rotate(0)} 25%{transform:rotate(-12deg)} 75%{transform:rotate(12deg)} }
@keyframes anm-float{ 0%,100%{transform:translateY(0)} 50%{transform:translateY(-8px)} }
.anm-tile:hover .anm-k-type b{ opacity:0; animation:anm-type 1.6s steps(1) infinite; }
.anm-tile:hover .anm-k-type b:nth-child(2){ animation-delay:.25s; }
.anm-tile:hover .anm-k-type b:nth-child(3){ animation-delay:.5s; }
.anm-tile:hover .anm-k-words{ animation:anm-rise 1.6s ease-in-out infinite; display:inline-block; }
.anm-tile:hover .anm-k-letters{ animation:anm-bounce 1.6s ease-in-out infinite; display:inline-block; }
.anm-tile:hover .anm-k-pulse{ animation:anm-pulse 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-sway{ animation:anm-sway 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-k-float{ animation:anm-float 1.6s ease-in-out infinite; }
/* Animator step 8: the card shown while Google Export makes a video */
.anm-wait-ov{ position:fixed; inset:0; z-index:300; display:grid; place-items:center; background:rgba(15,15,20,.45); -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px); }
.anm-wait{ width:360px; max-width:calc(100vw - 32px); padding:28px 28px 24px; border-radius:22px; background:#fff; color:#16161A; text-align:center; box-shadow:0 24px 60px -20px rgba(0,0,0,.45); }
.anm-spin{ display:block; width:40px; height:40px; margin:0 auto 16px; border-radius:50%; border:4px solid #E8EAF2; border-top-color:#3E6BE6; animation:anm-spin-r .9s linear infinite; }
@keyframes anm-spin-r{ to{ transform:rotate(360deg); } }
.anm-wait-h{ margin:0 0 8px; font-size:19px; font-weight:700; letter-spacing:-.01em; }
.anm-wait-p{ margin:0; font-size:14px; line-height:1.5; color:#55555E; }
.anm-wait-t{ margin:14px 0 0; font-size:13px; font-weight:600; color:#8A8A93; font-variant-numeric:tabular-nums; }
@media (prefers-reduced-motion:reduce){ .anm-spin{ animation-duration:3s; } }
/* Animator's tile drawings (animator-icons.js, approved 14 October 2026): colours by class, so one
   drawing is right on both themes */
#animatorMenu{ --ai-a:#4C93FF; --ai-b:#7FAEFF; --ai-p:#3A5A8C; --ai-g:#8C8C8C; }
body.light .editor #animatorMenu{ --ai-a:#3D8BFF; --ai-b:#8BB8FF; --ai-p:#CFE1FF; --ai-g:#9A9AA4; }
.anm-pv .anm-ic{ width:56px; height:56px; display:block; overflow:visible; }
.ai-fa{ fill:var(--ai-a); } .ai-fb{ fill:var(--ai-b); } .ai-fp{ fill:var(--ai-p); }
.ai-sa{ stroke:var(--ai-a); } .ai-sb{ stroke:var(--ai-b); } .ai-sp{ stroke:var(--ai-p); } .ai-sg{ stroke:var(--ai-g); }
.ai-stop{ stop-color:var(--ai-a); }
.anm-tile:hover .anm-ic.anm-k-type{ animation:anm-fade 1.6s ease-in-out infinite; }
.anm-tile:hover .anm-ic.anm-k-none{ animation:none; }
/* Animator tiles, 15 October 2026 (Mounir, with a picture: "title outside the frame, and enlarge
   and center icon"): the frame is a square holding only the drawing, larger and centred; the name
   sits under the frame. The selected one is marked on the frame. */
.anm-tile{ border:none !important; box-shadow:none !important; background:transparent !important; padding:0; gap:7px; }
.anm-tile .anm-pv{ width:100%; height:auto; aspect-ratio:1; display:grid; place-items:center; margin:0;
  border:1px solid var(--line); border-radius:14px; background:transparent; transition:border-color .12s; }
.anm-tile:hover .anm-pv{ border-color:var(--bdh,#555); }
.anm-tile.on .anm-pv{ border:2px solid var(--accent); }
.anm-pv .anm-ic{ width:68%; height:68%; }

/* EVERY CALL TO ACTION IS BLACK AND WHITE — 25 October 2026, the same rule as the homepage's (see the
   end of the homepage stylesheet). The Try buttons on the app pages, the pricing page and the About
   page. Black with white words on white, white with black words on the dark band, the second button
   of a pair white with a black edge. Regular weight, fully round, 56 tall, 40 either side. */
.tf button.ax-btn, .tf .pz-btn, .tf .ab-btn{ height:56px; padding:0 40px; border-radius:999px; border:1px solid #000;
  font-size:17px; font-weight:400; letter-spacing:normal; box-shadow:none; opacity:1;
  background:#000; color:#fff; transition:transform .14s; }
.tf button.ax-btn-dark, .tf button.ax-btn-dark:hover, .tf .pz-btn-pro, .tf .pz-btn-pro:hover, .tf .ab-btn:hover{ background:#000; color:#fff; border-color:#000; opacity:1; }
.tf button.ax-btn-light, .tf button.ax-btn-light:hover{ background:#fff; color:#000; border-color:#fff; }
.tf .pz-btn-ghost, .tf .pz-btn-ghost:hover, .tf .ab-btn-ghost, .tf .ab-btn-ghost:hover{ background:#fff; color:#000; border-color:#000; }
.tf button.ax-btn:hover, .tf .pz-btn:hover, .tf .ab-btn:hover{ transform:translateY(-1px); }
.tf button.ax-btn:focus-visible, .tf .pz-btn:focus-visible, .tf .ab-btn:focus-visible{ outline:2px solid currentColor; outline-offset:3px; }

/* EVERY HEADING IS SEMIBOLD — 25 October 2026. Mounir: "make all heading semibold and not bold in all
   pages". Every page a visitor or customer sees, the header and the sign-in box included; the editor
   and the admin panel are left as they are. Marked important because the headings on each page were
   set heavier by rules more specific than this one, and this has to win over all of them. */
:is(#siteTop,#authGate,#landing,#home,#pricing,#apps,#about,#support,#legal,#profile,#resetPage,#likes,#projects,.tf-foot,.tr-foot)
  :is(h1,h2,h3,h4,h5,h6,.tr-h1,.tr-h2,.tr-h3,.tr-h4,.tr-end-h,.tr-app-h,.tr-feat-h,.lg-title,.pz-h1,.pz-h2,.ax-h1,.ax-h2,.ax-end-h,.tf-h1,.tf-h2){ font-weight:400 !important; /* Regular since 28 October 2026 (Mounir): every page except the admin panel and the editor */ }
/* and their letter spacing eased to -2% (25 October 2026): the tighter settings were made for Schibsted
   Grotesk, and in Google Sans Flex they pushed the letters into each other on the big headings. */
:is(#siteTop,#authGate,#landing,#home,#pricing,#apps,#about,#support,#legal,#profile,#resetPage,#likes,#projects)
  :is(h1,h2,.tr-h1,.tr-h2,.tr-h3,.tr-end-h,.pz-h1,.pz-h2,.ax-h1,.ax-h2,.ax-end-h,.tf-h1){ letter-spacing:-.02em !important; }

/* EVERY FIELD AND BUTTON IN THE EDITOR HAS A 4PX CORNER — 25 October 2026. Mounir: "reduce corners
   radius in all editors field and buttons to XS · 4". The properties panel, the dialogs and the grid
   builder read one number for this (the control roundness, set to 4 above). The buttons below had their
   own roundness written into them, so they are listed here; they were found by measuring every visible
   button and field in the open editor. Round things stay round: colour swatches, the help button,
   slider handles. */
#editor :is(.ed-btn,.ed-ibtn,.share-btn,.primary,.tool,.sw-row,.ti-fontbtn,.ti-btn,.ti-text,.pb-add,.pb-dup,.fpanel-ico,.fxs-btn,.fi-btn){ border-radius:4px !important; }

/* THE HEART, 25 October 2026 (Mounir): solid white until liked, then the like red (#F40051) with a
   squash-and-spring and eight dots bursting out. The pop moves the button, never the drawing inside
   it, because a moved drawing comes out blurry on sharp screens. The dots are added by the like
   itself and taken away when they land. */
@keyframes like-pop{ 0%{ transform:scale(1); } 30%{ transform:scale(.82); } 100%{ transform:scale(1); } }
button.tf-like.like-pop, .tpv-like.like-pop{ animation:like-pop 350ms cubic-bezier(.34,1.96,.64,1); }
.like-burst{ position:absolute; left:50%; top:50%; width:0; height:0; pointer-events:none; color:#F40051; }
.like-burst i{ position:absolute; width:calc(2.5px * var(--ps,1)); height:calc(2.5px * var(--ps,1));
  left:calc(-1.25px * var(--ps,1)); top:calc(-1.25px * var(--ps,1)); border-radius:50%; background:currentColor; opacity:0;
  animation:like-dot var(--pd,600ms) ease-out var(--pw,0ms) forwards; }
@keyframes like-dot{
  0%{ opacity:0; transform:translate(0,0) scale(.4); }
  20%{ opacity:1; transform:translate(calc(var(--px) * .25),calc(var(--py) * .25)) scale(1); }
  100%{ opacity:0; transform:translate(var(--px),var(--py)) scale(.6); } }
.tpv-like{ position:relative; }
@media (prefers-reduced-motion:reduce){ button.tf-like.like-pop, .tpv-like.like-pop, .like-burst i{ animation:none !important; } }

/* THE HEADER MENUS MOVE AS ONE PANEL — 25 October 2026 (navmorph.js). Mounir sent shadcn's navigation
   menu for its fluid feel. One white panel sits behind whichever of Templates, Graphics and Apps is
   open, reshapes itself when you move between them, and the new contents slide in from that side.
   The menus keep their own size and contents; only their white box is handed to the panel. */
#siteTop .tf-nav-in{ position:relative; }
/* timings measured on shadcn's own page, 25 October 2026: 0.35s with one soft ease-out for every
   movement, 0.15s to close, growing from and shrinking to 90% */
.tf-navsurface{ --nm-ease:cubic-bezier(.22,1,.36,1);
  position:absolute; z-index:59; pointer-events:none; left:0; top:0; width:0; height:0; overflow:hidden;
  background:#fff; border:1px solid var(--bd); border-radius:16px; font-size:15.5px; font-weight:600; color:var(--tf-ink,#0D0D0C);
  box-shadow:0 1px 2px rgba(22,24,45,.06), 0 24px 56px -16px rgba(22,24,45,.24);
  opacity:0; transform:scale(.9); transform-origin:50% 0;
  transition:left .35s var(--nm-ease), top .35s var(--nm-ease), width .35s var(--nm-ease), height .35s var(--nm-ease),
    border-radius .35s var(--nm-ease), opacity .15s ease, transform .15s ease; }
.tf-navsurface.on{ opacity:1; transform:none;
  transition:left .35s var(--nm-ease), top .35s var(--nm-ease), width .35s var(--nm-ease), height .35s var(--nm-ease),
    border-radius .35s var(--nm-ease), opacity .35s var(--nm-ease), transform .35s var(--nm-ease); }
.tf-navsurface.nm-instant{ transition:opacity .35s var(--nm-ease), transform .35s var(--nm-ease); }
html.nav-morph #siteTop :is(.tf-navmenu,.tf-mega){ background:transparent; border-color:transparent; box-shadow:none; }
#siteTop .tf-navmenu{ --nm-r:16px; }
#siteTop .tf-mega{ --nm-r:18px; }
/* the real menu: hidden while the copies move, and growing with the panel when it first opens */
#siteTop .nm-wait{ visibility:hidden; }
@keyframes nm-pop{ from{ opacity:0; transform:scale(.9); } to{ opacity:1; transform:none; } }
#siteTop .nm-pop{ transform-origin:50% 0; animation:nm-pop .35s cubic-bezier(.22,1,.36,1); }
/* the moving copies inside the panel */
.tf-navsurface .nm-ghost{ pointer-events:none; }
@keyframes nm-out-l{ to{ opacity:0; transform:translateX(-50%); } }
@keyframes nm-out-r{ to{ opacity:0; transform:translateX(50%); } }
@keyframes nm-from-l{ from{ opacity:0; transform:translateX(-50%); } }
@keyframes nm-from-r{ from{ opacity:0; transform:translateX(50%); } }
.tf-navsurface .nm-out-l{ animation:nm-out-l .35s cubic-bezier(.22,1,.36,1) forwards; }
.tf-navsurface .nm-out-r{ animation:nm-out-r .35s cubic-bezier(.22,1,.36,1) forwards; }
.tf-navsurface .nm-from-l{ animation:nm-from-l .35s cubic-bezier(.22,1,.36,1); }
.tf-navsurface .nm-from-r{ animation:nm-from-r .35s cubic-bezier(.22,1,.36,1); }
@media (prefers-reduced-motion:reduce){ .tf-navsurface, .tf-navsurface.on, .tf-navsurface.nm-instant{ transition:none; }
  #siteTop .nm-pop{ animation:none; } }

/* "PROFESSIONAL TEMPLATES FOR ___" — the changing last word (tfrotate.js), 26 October 2026. Mounir sent
   a chromatic text reveal: each word is uncovered left to right by a narrow colourful band, black
   behind it, nothing ahead of it. The band is one wide gradient slid across the letters. The box the
   word sits in eases to the next word's width, so the line re-centres smoothly. */
.tf-rot{ position:relative; display:inline-block; vertical-align:bottom; white-space:nowrap; text-align:left;
  transition:width .45s cubic-bezier(.22,1,.36,1); }
.tf-rot-w{ display:inline-block; white-space:nowrap; padding-bottom:.08em; margin-bottom:-.08em; }
.tf-rot-meas{ position:absolute; left:0; top:0; visibility:hidden; pointer-events:none; }
.tf-rot-w.in{
  color:transparent; -webkit-text-fill-color:transparent;
  background-image:linear-gradient(90deg, #000 0%, #000 33%, #7B5CFF 38%, #3D95FF 41%, #18C2A0 44%, #FFC24D 47%, #FF5C8A 50%, transparent 55%, transparent 100%);
  background-size:300% 100%; background-repeat:no-repeat;
  -webkit-background-clip:text; background-clip:text;
  animation:tfRotIn 1.1s cubic-bezier(.4,0,.2,1) both; }
@keyframes tfRotIn{ from{ background-position:100% 0; } to{ background-position:0 0; } }
.tf-rot-w.out{ animation:tfRotOut .26s ease-in forwards; }
@keyframes tfRotOut{ to{ opacity:0; transform:translateY(-.12em); filter:blur(3px); } }
@media (prefers-reduced-motion:reduce){ .tf-rot{ transition:none; } .tf-rot-w.in, .tf-rot-w.out{ animation:none; color:inherit; -webkit-text-fill-color:currentColor; background:none; } }
/* One line, at Mounir's word (26 October 2026: "use one line not 2"). A phone cannot fit the whole
   sentence at the heading's size, and short words then fitted while long ones wrapped, so the
   heading jumped between one line and two. On a phone the changing word always takes the second line. */
@media (max-width:560px){ #home .tf-h1 .tf-rot{ display:block; margin:0 auto; } }

/* THE HEADER IS FROSTED GLASS — 27 October 2026, Mounir: "apply a similar blurry transparent effect
   to menu" (savee.com). Measured there: white at 80% over a 24px blur, with a thin line underneath.
   Ours already had a lighter version on the menu bar, but the strip holding it was solid white, so
   nothing behind it could ever show through. The strip is clear now; the menu bar and the search row
   under it carry the glass. Safari needs its own spelling of the blur, so both are written. */
#siteTop.tf-shell{ background:transparent; }
#siteTop .tf-nav, #siteTop .tf-searchrow{ background:rgba(255,255,255,.8);
  -webkit-backdrop-filter:blur(24px); backdrop-filter:blur(24px); }

/* The Pro diamond replaced the gold crown on 27 October 2026. Each badge's drawing is now an even size
   inside an even circle, so it sits on whole pixels and draws symmetric; the gold tint behind the big
   and pricing badges became a plain light grey, because gold behind a pink-and-blue gem clashed. */
/* and the titles that are not marked as headings: the plan names on the pricing page (28 October 2026) */
#pricing .pz-plan-name{ font-weight:400; }
/* THE APP PAGES ARE LIGHT GREY — 28 October 2026, Mounir: "use the light grey color for apps page background
   instead of white". The homepage's light grey; the panels that were that grey on white turn white, so
   they still stand apart from the page. */
#apps.ax{ background:#F2F2F4; --ax-card:#FFFFFF; }

/* NO GREY BEHIND A SLIDER WHEN IT IS POINTED AT — 29 October 2026, Mounir: "remove this hover grey effect from
   editor slider, just the grey hover effect only". The panel's rule that greys a text box under the pointer
   (#props .field input:hover) caught the sliders too; they keep their track and handle and lose only that. */
#props .field input[type=range]:hover, #editor input[type=range]:hover{ background:transparent; }

/* "CHECK YOUR EMAIL" AFTER SIGNING UP — 30 October 2026 (signupsent.js). The form makes way for one
   screen; the robot check stays underneath, unseen, because sending again needs a fresh one. */
#authGate.ag-sent :is(#agTitle,#agSub,#authWho,#agGoogle,.ag-or,.ag-switch,#agMsg,.ag-legal),
#authGate.ag-sent #agForm > :not(#agTurnstile){ display:none !important; }
#authGate .ag-sentbox{ display:flex; flex-direction:column; align-items:center; text-align:center; gap:0; padding:8px 0 4px; }
#authGate .ag-senticon{ width:64px; height:64px; border-radius:50%; background:#F2F2F4; color:#14161c; display:grid; place-items:center; margin:6px 0 18px; }
#authGate .ag-sentttl{ font-size:24px; font-weight:400; letter-spacing:-.01em; color:#14161c; }
#authGate .ag-senttxt{ font-size:15px; line-height:1.5; color:#14161c; margin:12px 0 0; max-width:340px; overflow-wrap:anywhere; }
#authGate .ag-senttxt b{ font-weight:600; }
#authGate .ag-senthint{ font-size:13.5px; line-height:1.5; color:#6e7280; margin:8px 0 26px; max-width:340px; }
#authGate .ag-sentagain{ width:100%; height:48px; border:1px solid #dcdce6; border-radius:99px; background:#fff; color:#14161c; font:inherit; font-size:14px; cursor:pointer; transition:background .15s, color .15s; }
#authGate .ag-sentagain:hover:not(:disabled){ background:#F2F2F4; }
#authGate .ag-sentagain:disabled{ color:#9A9AA6; cursor:default; }
#authGate .ag-sentnote{ min-height:20px; font-size:13px; color:#6e7280; margin:10px 0 2px; }
#authGate .ag-sentnote.bad{ color:#c0392b; }
#authGate .ag-sentback{ margin-top:4px; }

/* PATTERNS — 31 October 2026 (apps/patterns.js). Built on the text effects window's panels so the two
   look and behave alike; only what is new lives here. The middle is a preview of the page, not the
   editor, so it covers the room between the panels. */
.pat .pat-mid{ position:absolute; top:var(--tfx-top-h,60px); bottom:0; left:var(--tfx-left-w); right:var(--tfx-right-w);
  pointer-events:auto; background:#F2F2F4; display:flex; align-items:center; justify-content:center; }
.pat .pat-page{ background:#fff; border-radius:6px; box-shadow:0 0 0 1px rgba(0,0,0,.05); overflow:hidden; }   /* the drop shadow taken off, asked for 1 November 2026; the hairline edge stays */
.pat .pat-page svg{ display:block; width:100%; height:100%; }
.pat .pat-grid{ grid-template-columns:1fr 1fr; }
.pat .pat-card{ padding:8px 8px 10px; gap:8px; }
.pat .pat-thumb{ display:block; width:100%; aspect-ratio:4/3; border-radius:8px; overflow:hidden; background:#fff; border:1px solid var(--tfx-edge); }
.pat .pat-thumb svg{ display:block; width:100%; height:100%; }
.pat .pat-card.on .pat-thumb{ border-color:var(--tfx-line); }
.pat .pat-x{ appearance:none; width:40px; height:40px; border:none; border-radius:10px; background:none; font-size:24px; line-height:1; color:var(--tfx-dim); cursor:pointer; }
.pat .pat-x:hover{ background:#F5F6F9; color:var(--tfx-ink); }
.pat .pat-pair.off{ opacity:.4; }
.pat .pat-colour{ margin-top:0; }
.pat .pat-sw{ appearance:none; flex:none; width:22px; height:22px; border-radius:50%; padding:0; cursor:pointer;
  border:1px solid rgba(128,128,128,.4); background:#3D95FF; }
.pat .pat-sw.none{ background:linear-gradient(135deg,transparent 45%,#E5484D 45%,#E5484D 55%,transparent 55%),#fff; }
body:not(.light) .pat .pat-mid{ background:#161616; }
body:not(.light) .pat .pat-x:hover{ background:#2E2E2E; }
body:not(.light) .pat .pat-card.on .tfx-cardname{ color:var(--tfx-accent); }

/* ============================================================================
   ADMIN — USERS TAB IN THE STAGESHOW LOOK — 1 November 2026, asked for: "apply the benji UI
   skill to the admin section", one tab first. Its rules, borrowed for a real working page:
   quiet greys and one blue for anything that is on, sentence case, room to breathe, hairline
   edges instead of grey bands, and the heaviest marks only on what matters (the names and the
   two switches). Every word, number and control is the same as before; only the look changed.
   Scoped to the Users tab, so the other tabs are untouched until he has seen this one.
   ============================================================================ */
#adPaneUsers .pj-title{ font-size:24px; font-weight:700; letter-spacing:-.01em; margin:6px 0 6px; }
/* the two counts as one quiet line under the title, not two grey pills */
#adPaneUsers .us-stats{ gap:16px; margin:0 0 20px; }
#adPaneUsers .us-stat{ background:none; padding:0; border-radius:0; font-size:13px; color:#7a7a80; }
#adPaneUsers .us-stat b{ color:#1a1a1d; font-weight:600; }
/* the bin check: a line with a status dot, not a grey box, while all is well */
#adPaneUsers .ad-house.ok{ background:none; border:none; padding:0 0 0 16px; position:relative; color:#7a7a80; font-size:12.5px; margin:0 0 18px; }
#adPaneUsers .ad-house.ok::before{ content:""; position:absolute; left:0; top:50%; width:7px; height:7px; margin-top:-3.5px; border-radius:50%; background:#22c55e; }
/* the search box: white with a hairline, the way the rest of the strip reads */
#adPaneUsers .pj-search{ background:#fff; border:1px solid #e6e6ea; border-radius:12px; padding:11px 14px; }
#adPaneUsers .pj-search:focus-within{ border-color:#3D95FF; box-shadow:0 0 0 3px rgba(61,149,255,.15); }
#adPaneUsers .pj-search input{ font-size:14px; }
/* the table: a hairline card, sentence-case headings on white, rows with room */
#adPaneUsers .us-table{ margin-top:12px; border:1px solid #ececef; border-radius:14px; background:#fff; padding:4px 8px; }
#adPaneUsers .us-row{ padding:12px 12px; border-top:1px solid #f1f1f3; border-radius:0; }
#adPaneUsers .us-row:first-child, #adPaneUsers .us-row.us-head + .us-row{ border-top:none; }
#adPaneUsers .us-head{ background:none; text-transform:none; letter-spacing:0; font-size:12px; font-weight:500; color:#9a9aa0; padding-top:12px; padding-bottom:10px; border-bottom:1px solid #ececef; }
/* hover: an inset rounded fill, never a band edge to edge */
#adPaneUsers .us-row[data-uid]:hover{ background:#f6f7f9; border-radius:10px; border-top-color:transparent; }
#adPaneUsers .us-row[data-uid]:hover + .us-row{ border-top-color:transparent; }
#adPaneUsers .us-nm{ font-weight:600; color:#1a1a1d; }
#adPaneUsers .us-em, #adPaneUsers .us-muted{ color:#8a8a90; }
/* Free is the ordinary case, so it is grey; Pro keeps its colour because it means something */
#adPaneUsers .us-plan{ color:#6b6b72; background:#f1f1f3; font-weight:600; }
#adPaneUsers .us-plan.on{ color:var(--plan-pro); background:var(--plan-pro-tint); }
#adPaneUsers .us-badge{ font-weight:600; }
/* the storage bar thinner, the number under it quieter */
#adPaneUsers .us-storeb{ height:4px; background:#efeff2; }
#adPaneUsers .us-storen{ color:#8a8a90; }
/* both switches the one blue when on, grey when off: one accent for "this is on" */
#adPaneUsers .us-sw, #adPaneUsers .us-sw.is-admin{ --sw-on:#3D95FF; background:#d4d4da; }
#adPaneUsers .us-sw:hover{ background:#c4c4cb; }
#adPaneUsers .us-sw.on{ background:var(--sw-on); }
#adPaneUsers .us-sw.on:hover{ background:#2f86ef; }

/* ============================================================================
   THE ADMIN MENU AS A DARK ICON RAIL — 1 November 2026, from a picture Mounir sent of an
   expanding side menu. Narrow and dark, the Templad mark on top, one icon per area. It widens
   over the page while the pointer is on it, showing the names, and the mark keeps it open.
   The place it takes in the page stays 64 pixels, so widening never shoves the table sideways;
   kept open, the page makes room for it.
   ============================================================================ */
#users .ad-wrap{ gap:28px; padding:24px 26px 80px 20px; }
#users .ad-side{ z-index:30; width:64px; flex:none; padding:0; background:none; border-radius:0;
  height:calc(100vh - var(--headh, 0px) - var(--pjbarh, 0px) - 48px); transition:width .18s ease; }
/* WIDENING PUSHES THE PAGE ALONG, it never covers it — 1 November 2026, asked for: "menu
   should push not hide page". The menu's own place in the page grows, so the page moves over. */
#users .ad-side.pinned, #users .ad-side:hover, #profile .pr-side.pinned, #profile .pr-side:hover{ width:224px; }
#users .ad-rail, #profile .ad-rail{ position:absolute; left:0; top:0; bottom:0; z-index:30; width:100%; overflow:hidden;
  display:flex; flex-direction:column; gap:4px; padding:12px 10px; box-sizing:border-box;
  background:#121216; border-radius:18px; transition:width .18s ease, box-shadow .18s ease; }
/* the mark: the same red T as the site's icon, in a rounded square */
#users .ad-railmark, #profile .ad-railmark{ flex:none; width:44px; height:44px; padding:0; border:none; border-radius:12px; background:none; cursor:pointer; display:grid; place-items:center; margin-bottom:8px; }
#users .ad-railmark img, #profile .ad-railmark img{ width:36px; height:36px; display:block; border-radius:10px; }
#users .ad-railmark:focus-visible, #profile .ad-railmark:focus-visible{ outline:2px solid #3D95FF; outline-offset:2px; }
/* the word Admin appears only once there is room for it */
/* beside the mark, so it never pushes the icons down as the rail widens */
#users .ad-rail .pr-side-h, #profile .ad-rail .pr-side-h{ position:absolute; left:66px; top:27px; padding:0; color:#8b8b93; white-space:nowrap; opacity:0; transition:opacity .12s; }
#users .ad-rail:hover .pr-side-h, #users .ad-side.pinned .pr-side-h, #profile .pr-side.pinned .pr-side-h, #profile .ad-rail:hover .pr-side-h{ opacity:1; }
/* each area: an icon at a fixed spot, its name beside it, cut off while the rail is narrow */
#users .ad-rail .pr-tab, #profile .ad-rail .pr-tab{ position:relative; flex:none; width:100%; height:44px; padding:0 12px; gap:14px; overflow:hidden; white-space:nowrap;
  border-radius:12px; color:#8b8b93; font-size:13.5px; font-weight:500; background:none; }
#users .ad-rail .pr-tab svg, #profile .ad-rail .pr-tab svg{ flex:none; width:20px; height:20px; }
#users .ad-rail .pr-tab:hover, #profile .ad-rail .pr-tab:hover{ background:rgba(255,255,255,.06); color:#ececef; }
#users .ad-rail .pr-tab.active, #profile .ad-rail .pr-tab.active{ background:rgba(255,255,255,.1); color:#fff; }
#users .ad-rail .pr-tab:focus-visible, #profile .ad-rail .pr-tab:focus-visible{ outline:2px solid #3D95FF; outline-offset:-2px; }
/* a count sits on its icon's corner, so it shows when the rail is narrow too */
#users .ad-rail .ad-tabcount, #profile .ad-rail .ad-tabcount{ position:absolute; left:30px; top:6px; margin:0; min-width:16px; height:16px; padding:0 4px; font-size:10px; border-radius:8px; box-shadow:0 0 0 2px #121216; }
/* once the names show, the count moves to the end of its row, so it never sits on a word */
#users .ad-side:hover .ad-tabcount, #users .ad-side.pinned .ad-tabcount{ position:static; margin-left:auto; box-shadow:none; }
@media (max-width:860px){
#users .ad-side, #users .ad-side.pinned{ width:100%; height:auto; }
#users .ad-rail, #users .ad-rail:hover, #profile .ad-rail, #profile .ad-rail:hover{ position:static; width:100%; flex-direction:row; flex-wrap:wrap; box-shadow:none; }
#users .ad-rail .pr-tab, #profile .ad-rail .pr-tab{ width:auto; }
#users .ad-rail .pr-side-h, #users .ad-railmark, #profile .ad-rail .pr-side-h, #profile .ad-railmark{ display:none; }
}

/* ============================================================================
   THE WHOLE ADMIN IN THE STAGESHOW LOOK — 1 November 2026, asked for: "apply benji UI style
   to the whole admin". The Users tab's look, carried to every tab: one weight of title, quiet
   counts as a line, white search boxes with a hairline, hairline cards, headings in normal
   letters on white instead of capitals on a grey band, and rounded hover strips. Words,
   numbers and buttons are all as they were. The Design system tab is its own reference page
   and is left alone.
   ============================================================================ */
#users .ad-main .pj-title{ font-size:24px; font-weight:700; letter-spacing:-.01em; margin:6px 0 6px; }
#users .ad-main .ct-stats, #users .ad-main .er-stats{ gap:16px; margin:0 0 20px; }
#users .ad-main .us-stat{ background:none; padding:0; border-radius:0; font-size:13px; color:#7a7a80; }
#users .ad-main .us-stat b{ color:#1a1a1d; font-weight:600; }
#users .ad-main .pj-search{ background:#fff; border:1px solid #e6e6ea; border-radius:12px; padding:11px 14px; }
#users .ad-main .pj-search:focus-within{ border-color:#3D95FF; box-shadow:0 0 0 3px rgba(61,149,255,.15); }
#users .ad-main .pj-search input{ font-size:14px; }
#users .ad-main .ct-f{ height:40px; border-radius:12px; border-color:#e6e6ea; font-weight:500; color:#5f5f66; }
#users .ad-main .ct-f.active{ background:rgba(61,149,255,.1); color:#2f7fe6; border-color:transparent; }
/* tables: a hairline card, headings in normal letters, rows with room, a rounded hover strip */
#users .ad-main .ct-table{ border:1px solid #ececef; border-radius:14px; background:#fff; padding:4px 8px; }
#users .ad-main .ct-row{ padding:12px 12px; border-bottom:1px solid #f1f1f3; }
#users .ad-main .ct-row:last-child{ border-bottom:none; }
#users .ad-main .ct-head{ background:none; text-transform:none; letter-spacing:0; font-size:12px; font-weight:500; color:#9a9aa0; padding-top:12px; padding-bottom:10px; border-bottom:1px solid #ececef; }
#users .ad-main .ct-row[data-uid]:hover{ background:#f6f7f9; border-radius:10px; }
/* reports: hairline cards with room, the category as a quiet label */
#users .ad-main .rp-item{ border:1px solid #ececef; border-radius:14px; padding:16px 18px; background:#fff; }
#users .ad-main .rp-item .rp-cat{ text-transform:capitalize; font-weight:600; font-size:14px; }
#users .ad-main .rp-item .rp-who, #users .ad-main .rp-item .rp-when, #users .ad-main .rp-item .rp-meta{ color:#8a8a90; }
#users .ad-main .rp-item .rp-msg{ font-size:14px; margin:8px 0; }
#users .ad-main .rp-item input.rp-note{ height:36px; padding:0 12px; border:1px solid #e6e6ea; border-radius:10px; background:#fff; }
#users .ad-main .rp-item button.rp-b{ height:36px; padding:0 14px; border-radius:10px; border-color:#e6e6ea; background:#fff; font-weight:500; }
/* news: white fields, the list as hairline cards, its heading in normal letters */
#users .ad-main .nw-in{ background:#fff; border-color:#e6e6ea; border-radius:12px; }
#users .ad-main .nw-in:focus{ outline:none; border-color:#3D95FF; box-shadow:0 0 0 3px rgba(61,149,255,.15); }
#users .ad-main .tf-filter-h{ text-transform:none; letter-spacing:0; font-size:13px; font-weight:600; color:#1a1a1d; }
#users .ad-main .nw-row{ border-color:#ececef; border-radius:14px; padding:12px; background:#fff; }
#users .ad-main .nw-row.on{ border-color:#3D95FF; }

/* ---- one person's page, laid out the Stageshow way (see admin.js, openUserDetail) ---- */
#usDetail .ud-head{ gap:16px; margin-bottom:24px; }
#usDetail .ud-avatar{ width:52px; height:52px; font-size:18px; font-weight:600; background:#3D95FF; }
#usDetail .ud-name{ font-size:22px; font-weight:700; letter-spacing:-.01em; margin:0 0 2px; word-break:break-all; }
#usDetail .ud-email{ font-size:13.5px; color:#8a8a90; }
#usDetail .ud-tag{ text-transform:none; letter-spacing:0; font-weight:600; font-size:12px; padding:4px 10px; background:#f1f1f3; color:#5f5f66; }
#usDetail .ud-tag-admin{ background:var(--admin-soft); color:var(--admin); }
#usDetail .ud-tag-susp{ background:rgba(224,72,63,.12); color:#C0392B; }
/* the grid is no longer one block of cells: it holds the three tiles and the list of facts */
#usDetail .ud-grid{ display:block; background:none; border:none; border-radius:0; overflow:visible; margin-bottom:28px; }
#usDetail .ud-sum{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px; margin-bottom:12px; }
#usDetail .ud-tile{ border:1px solid #ececef; border-radius:14px; background:#fff; padding:16px 18px; }
#usDetail .ud-k{ text-transform:none; letter-spacing:0; font-size:12.5px; font-weight:500; color:#8a8a90; margin-bottom:6px; }
#usDetail .ud-tile .ud-v{ font-size:18px; font-weight:600; color:#1a1a1d; word-break:normal; }
#usDetail .ud-bar{ margin-top:10px; height:4px; background:#efeff2; }
/* the facts: label on the left, value on the right, two columns, hairlines between */
#usDetail .ud-facts{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); column-gap:32px; border:1px solid #ececef; border-radius:14px; background:#fff; padding:4px 18px; }
#usDetail .ud-facts .ud-cell{ display:flex; align-items:baseline; justify-content:space-between; gap:16px; padding:13px 0; border-bottom:1px solid #f1f1f3; background:none; }
#usDetail .ud-facts .ud-cell:nth-last-child(-n+1){ border-bottom:none; }
#usDetail .ud-facts .ud-cell:nth-last-child(2):nth-child(odd){ border-bottom:none; }
#usDetail .ud-facts .ud-k{ margin:0; flex:none; }
#usDetail .ud-facts .ud-v{ font-size:13.5px; color:#1a1a1d; text-align:right; word-break:normal; overflow-wrap:anywhere; }
#usDetail .ud-facts .ud-id{ grid-column:1 / -1; }
#usDetail .ud-facts .ud-id .ud-v{ font-family:ui-monospace, "SF Mono", Menlo, monospace; font-size:12.5px; color:#5f5f66; }
#usDetail .ud-sec-h{ text-transform:none; letter-spacing:0; font-size:14px; font-weight:600; color:#1a1a1d; margin:0 0 10px; }
#usDetail .ud-uploads{ margin-bottom:28px; }
#usDetail .ud-uploads .pj-empty{ padding:16px 18px; color:#8a8a90; }
/* account actions: a hairline card, its heading in normal letters on white */
#usDetail .ud-danger{ border:1px solid #ececef; border-radius:14px; background:#fff; }
#usDetail .ud-danger-h{ background:none; text-transform:none; letter-spacing:0; font-size:14px; font-weight:600; color:#1a1a1d; padding:16px 18px 6px; border-bottom:none; }
#usDetail .ud-danger-row{ padding:14px 18px; }
#usDetail .ud-danger-row button{ height:38px; border-radius:10px; }
#usDetail .ud-danger-row .ghost{ background:#fff; border:1px solid #e6e6ea; color:#1a1a1d; }
@media (max-width:860px){
  #usDetail .ud-sum{ grid-template-columns:1fr; }
  #usDetail .ud-facts{ grid-template-columns:1fr; }
}
/* the role and status tags sit beside the name, not stranded at the far right */
#usDetail .ud-tags{ margin-left:4px; align-self:flex-start; margin-top:6px; }

/* THE ADMIN'S REFRESH BUTTON IN THE STAGESHOW COLOUR AND MOTION — 1 November 2026, asked for.
   No icon at rest. Pressed, it squeezes a little; while the list loads a thin arc turns beside
   the word, the way Stageshow's spinner does, and the button keeps its colour throughout. */
#users .ad-refresh{ background:#3b82f6; font-weight:600; border-radius:12px; padding:10px 18px; gap:0;
  transition:background .15s ease, transform .12s ease; }
#users .ad-refresh:hover{ background:#2f74e6; }
#users .ad-refresh:active{ transform:scale(.965); }
#users .ad-refresh:focus-visible{ outline:2px solid #3b82f6; outline-offset:2px; }
#users .ad-refresh .ad-spin{ display:inline-grid; place-items:center; width:0; overflow:hidden; opacity:0;
  transition:width .18s ease, opacity .18s ease, margin .18s ease; }
#users .ad-refresh.busy .ad-spin{ width:15px; opacity:1; margin-right:8px; }
#users .ad-refresh.busy .ad-spin svg{ animation:ad-spin .8s linear infinite; }
#users .ad-refresh.busy{ cursor:default; }
@keyframes ad-spin{ to{ transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce){ #users .ad-refresh.busy .ad-spin svg{ animation:none; } }

/* ============================================================================
   THE PROFILE PAGES WITH THE SAME RAIL AND THE STAGESHOW LOOK — 1 November 2026, asked for:
   "apply same side menu style and benji UI to profile pages". The rail is the admin's (see
   above), placed the same way: at the left edge, sticking below the header, widening on hover
   and pushing the page along. The page itself: one weight of title, white fields with a
   hairline, the Stageshow blue on the buttons with a small squeeze when pressed, and the
   delete area as its own hairline card. Every field, button and word is as it was.
   ============================================================================ */
#profile .pr-wrap{ max-width:none; margin:0; gap:28px; padding:24px 26px 80px 20px; align-items:flex-start; }
#profile .pr-side{ position:sticky; top:calc(var(--headh, 0px) + 24px); z-index:30; width:64px; flex:none; padding:0;
  height:calc(100vh - var(--headh, 0px) - 48px); transition:width .18s ease; }
#profile .pr-main{ flex:1; max-width:600px; margin:0 auto; padding-top:8px; }
@media (max-width:860px){
  #profile .pr-wrap{ flex-direction:column; gap:18px; }
  #profile .pr-side, #profile .pr-side.pinned{ position:static; width:100%; height:auto; }
  #profile .pr-main{ margin:0; }
}
/* the title keeps the Regular weight every customer page uses (28 October 2026) */
#profile .pr-title{ font-size:24px; }
/* the page white, like the admin area, so the rail and the fields sit on the same ground */
#profile.profile{ background:var(--panel,#fff); }
#profile .pr-lead{ color:#8a8a90; }
#profile .pr-field label{ font-weight:500; color:#5f5f66; }
#profile .pr-names input, #profile .pr-inline input, #profile .pr-cp-field .rp-input{ background:#fff; border:1px solid #e6e6ea; border-radius:12px; }
#profile .pr-names input:focus, #profile .pr-inline input:focus, #profile .pr-cp-field .rp-input:focus{ border-color:#3b82f6; box-shadow:0 0 0 3px rgba(59,130,246,.15); outline:none; }
/* what cannot be typed into reads as a value, not as a greyed-out box */
#profile .pr-static{ background:#f6f7f9; border-color:transparent; border-radius:12px; color:#1a1a1d; }
#profile .pr-save{ background:#3b82f6; border-radius:12px; font-weight:600; transition:background .15s ease, transform .12s ease; }
#profile .pr-save:hover:not(:disabled){ background:#2f74e6; }
#profile .pr-save:active:not(:disabled){ transform:scale(.965); }
#profile .pr-badge{ background:rgba(59,130,246,.12); color:#2f74e6; font-weight:600; }
/* the delete area: its own hairline card at the foot, the red kept for the words that mean it */
#profile .pr-danger{ margin-top:44px; padding:20px 22px; border:1px solid #ececef; border-top:1px solid #ececef; border-radius:14px; background:#fff; }
#profile .pr-danger-h{ font-weight:600; }
#profile .pr-del{ border-radius:12px; }
/* a Pro plan keeps its gold */
#profile .pr-plan-row .pr-static.is-pro{ color:#7a5a12; background:color-mix(in srgb,var(--gold,#C79A3B) 14%,#fff); border-color:color-mix(in srgb,var(--gold,#C79A3B) 35%,#fff); }

/* ============================================================================
   MY PROJECTS IN THE STAGESHOW LOOK — 1 November 2026, asked for: "apply benji skill to my
   projects page". A white page, a white search box and white drop-downs with a hairline, the
   pictures as the subject: softly rounded, on a pale ground, rising a little with a soft shadow
   when the pointer is on one. Headings keep the Regular weight every customer page uses.
   Every card, menu, checkbox and word is as it was.
   ============================================================================ */
#projects.projects{ background:var(--panel,#fff); }
#projects .pj-wrap{ padding-top:30px; }
#projects .pj-title{ font-size:24px; margin:4px 0 16px; }
#projects .pj-search{ background:#fff; border:1px solid #e6e6ea; border-radius:12px; padding:11px 14px; }
#projects .pj-search:focus-within{ border-color:#3b82f6; box-shadow:0 0 0 3px rgba(59,130,246,.15); }
#projects .pj-search input{ font-size:14px; }
#projects .pj-controls{ margin:12px 0 4px; }
#projects .pj-controls select{ height:40px; border:1px solid #e6e6ea; border-radius:12px; background-color:#fff; padding:0 14px; color:#1a1a1d; cursor:pointer; }
#projects .pj-controls select:hover{ border-color:#d4d4da; }
#projects .pj-controls select:focus{ outline:none; border-color:#3b82f6; box-shadow:0 0 0 3px rgba(59,130,246,.15); }
#projects .pj-h{ font-size:16px; margin:30px 0 12px; color:#1a1a1d; }
#projects .pj-grid{ gap:20px; }
/* the picture is the card: a pale ground, a hairline, rounded; on hover it rises with a soft shadow */
#projects .pj-thumb{ border-radius:14px; background:#f6f7f9; border:1px solid #ececef;
  transition:transform .18s ease, box-shadow .18s ease, border-color .18s ease; }
/* no rise and no shadow on hover — taken off 1 November 2026, asked for */
#projects .pj-card.sel .pj-thumb{ border-color:#3b82f6; box-shadow:0 0 0 2px #3b82f6; }
#projects .pj-meta{ padding:10px 2px 0; }
#projects .pj-name{ font-size:13.5px; font-weight:600; color:#1a1a1d; }
#projects .pj-sub{ font-size:12px; color:#8a8a90; }
#projects .pj-more{ border-radius:10px; background:rgba(255,255,255,.95); box-shadow:0 0 0 1px rgba(0,0,0,.06); }
#projects .pj-check input:checked + span{ background:#3b82f6; box-shadow:0 0 0 1px #3b82f6; }
#projects .pj-empty{ color:#8a8a90; }
/* RENAMING A DESIGN, the Stageshow way — 1 November 2026, asked for: the name becomes a field,
   white with our blue edge and a soft blue halo, rounded. (Selected words take the site's one selection colour.) */
#projects .pj-name.editing{ background:#fff; border-radius:8px; padding:3px 8px; margin:-3px -6px;
  box-shadow:0 0 0 1px #3b82f6, 0 0 0 4px rgba(59,130,246,.15); color:#1a1a1d; }

/* YOUR LIKES — room under "21 templates" before the cards start. 1 November 2026, asked for. */
#likes #likesCount{ margin:0 0 22px; }

/* PICKING SEVERAL DESIGNS ON MY PROJECTS, the Stageshow way — 1 November 2026, asked for. The
   count and its three actions sit together in one pale rounded strip instead of three outlined
   buttons in a row; the buttons are quiet text that lights up on hover, and Delete keeps its red. */
#projects .pj-selbar{ gap:2px; padding:4px; border-radius:12px; background:#f3f4f6; font-weight:500; }
#projects .pj-selbar #pjSelCount{ padding:0 10px 0 8px; color:#1a1a1d; font-weight:600; font-size:13px; }
#projects .pj-selbar .ghost{ height:30px; padding:0 12px; border:none; border-radius:9px; background:transparent; color:#5f5f66; font-size:13px; font-weight:500; transition:background .12s ease, color .12s ease; }
#projects .pj-selbar .ghost:hover{ background:#fff; color:#1a1a1d; box-shadow:0 0 0 1px #e6e6ea; }
#projects .pj-selbar .pj-del{ color:#d93b30; }
#projects .pj-selbar .pj-del:hover{ background:#fff; color:#c03228; box-shadow:0 0 0 1px rgba(217,59,48,.25); }
/* the selection strip sits at the right-hand end of the Designs heading row — 1 November 2026, asked for */
#projects .pj-selbar{ margin-left:auto; }
