/* ===========================================================================
   SWC in-context photo editor — chrome
   ---------------------------------------------------------------------------
   This is TOOL chrome, not site chrome. It never renders for a visitor, and it
   has to look right on the /v2 pages (where the estate tokens are defined) and
   on the legacy and Core30 pages (where some of them are not).

   So the tokens are resolved ONCE, here, into local --swcE-* properties with a
   neutral fallback. Everything below this block uses only --swcE-*. That is the
   one place a literal color is allowed to appear, and it exists so the tool
   degrades on a page with no brand stylesheet rather than rendering invisible.
   Nothing in here is copied into a public component.
   ======================================================================== */

/* SURFACES ARE FIXED ON PURPOSE. The first cut bound them to --reverse1 /
   --color1 / --color4, which works on /v2 and falls apart everywhere else:
   those tokens mean different things per template, and on the ops template
   --reverse1 resolved transparent — a see-through dialog with pale green text
   on a photograph. A tool has to be readable on a page whose palette it has
   never met, so paper/ink/line do not consult the page at all.

   Only the ACCENT and the TYPEFACE are borrowed from the brand, both with a
   fallback, because those two cannot make anything illegible. */
:root {
  --swcE-paper: #fbf9f5;
  --swcE-ink:   #23211e;
  --swcE-quiet: #6b6760;
  --swcE-line:  rgba(35, 33, 30, .18);
  --swcE-gold:  var(--colorGold, #a98b4b);
  /* GO. The same green every other TLC tool uses for "this is the button that does
     the thing" — measured off the Review Board's refresh panel, which is
     rgba(31,150,74,.82) over a dark scrim; opaque here because this one sits on
     paper. Craig, 2026-08-18: "big green save button below like all other sites
     TLC PBS." */
  --swcE-go:     #1f964a;
  --swcE-goDark: #187a3c;
  --swcE-font:  var(--font1Family, "Helvetica Neue", Arial, sans-serif);
  --swcE-shadow: 0 24px 60px -24px rgba(0, 0, 0, .45);
  /* NOT --RadiusBrand. The estate language is square by design and that token is
     0px here, so binding to it renders a hard-cornered panel. This is tool
     chrome, which the block above already exempts from the page's palette; the
     same exemption is what lets it carry a radius the site itself does not use.
     Craig, 2026-08-18: "round the corners of the popup modals slightly." */
  --swcE-radius: 6px;
}

/* ------------------------------------------------------------------ switch
   THERE IS NO PILL HERE ANY MORE. The editor's on/off control is a button in the
   review bar's sticky footer (id="expReviewPhotosBtn"), styled with the rest of
   that bar in 00_resources/estate-build/src/review-nav-source.html. Two fixed
   controls in the same bottom corner is what this deletion fixes — do not add a
   second one back to this file without deleting the first.
   Craig, 2026-08-17: "I'd like the 'Edit Photos' button to be in the sticky
   footer too." */

/* ----------------------------------------------------------------- badges
   The badge sits OUTSIDE the top-right corner of the photograph, so it never
   covers any part of the picture the client is trying to judge. It is nearly
   invisible until the photograph is hovered. */

.swcE-layer {
  position: fixed; inset: 0; z-index: 2147482000; pointer-events: none;
}
/* While a dialog is open the badges behind it are still live, so the client can
   start a second edit on top of the one they have not finished, and the pencils
   crowding the panel read as an invitation to edit the editor. One class, set by
   both editors, retires the whole layer for as long as a panel is up. */
html.swcE-dialog .swcE-layer { display: none; }
.swcE-badge {
  position: absolute; top: 0; left: 0; margin: 0;
  width: 26px; height: 26px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid var(--swcE-line); border-radius: 50%;
  background: var(--swcE-paper); color: var(--swcE-quiet);
  pointer-events: auto; cursor: pointer;
  opacity: 0; transform-origin: 50% 50%;
  transition: opacity .32s ease, color .32s ease, border-color .32s ease;
  box-shadow: 0 4px 14px -8px rgba(0, 0, 0, .5);
}
.swcE-badge svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.swcE-badge:hover, .swcE-badge:focus-visible {
  opacity: 1; color: var(--swcE-gold); border-color: var(--swcE-gold);
}
html.swcE-on .swcE-badge { opacity: .28; }
html.swcE-on .swcE-badge:hover { opacity: 1; }
@media (hover: none) { html.swcE-on .swcE-badge { opacity: .85; } }

/* ----------------------------------------------------------------- dialog */

.swcE-dlg {
  position: fixed; inset: 0; z-index: 2147483600;
  font-family: var(--swcE-font); color-scheme: light;
}
.swcE-scrim {
  position: absolute; inset: 0;
  background: rgba(18, 16, 14, .74); backdrop-filter: blur(3px);
}
.swcE-panel {
  position: relative; margin: 4vh auto; max-width: 1000px; width: calc(100% - 40px);
  max-height: 92vh; overflow: auto;
  background: var(--swcE-paper); color: var(--swcE-ink);
  border: 1px solid var(--swcE-line); box-shadow: var(--swcE-shadow);
  border-radius: var(--swcE-radius);
}
/* The panel sits inside whatever page the client happens to be on, so every
   piece of text states its own color. Inheriting one on-dark color from a
   template is how the whole dialog goes unreadable at once. */
.swcE-panel, .swcE-panel p, .swcE-panel span, .swcE-panel label,
.swcE-panel h1, .swcE-panel h2, .swcE-panel h3 { color: var(--swcE-ink); }

/* …then hand the secondary text back its quieter color. The blanket rule
   above is one class + one element, so a bare .swcE-hint loses to it and the
   whole panel flattens to a single weight with no hierarchy left. */
.swcE-panel .swcE-eyebrow, .swcE-panel .swcE-hint, .swcE-panel .swcE-note,
.swcE-panel .swcE-meta, .swcE-panel .swcE-label, .swcE-panel .swcE-status,
.swcE-panel .swcE-dropSub, .swcE-panel .swcE-revert { color: var(--swcE-quiet); }
.swcE-panel .swcE-status.is-bad { color: #9b2c2c; }
.swcE-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 22px; border-bottom: 1px solid var(--swcE-line);
}
.swcE-eyebrow {
  margin: 0; font-size: .7rem; text-transform: uppercase; letter-spacing: .18em;
  color: var(--swcE-quiet);
}
.swcE-x {
  border: 0; background: none; font-size: 1.6rem; line-height: 1;
  color: var(--swcE-quiet); cursor: pointer; padding: 0 4px;
}
.swcE-x:hover { color: var(--swcE-ink); }

.swcE-body { display: grid; grid-template-columns: 1.15fr 1fr; gap: 26px; padding: 22px; }
@media (max-width: 860px) { .swcE-body { grid-template-columns: 1fr; } }

.swcE-stage { width: 100%; background: rgba(0, 0, 0, .06); overflow: hidden; }
.swcE-prev {
  width: 100%; height: 100%; object-fit: cover; display: block;
  cursor: grab; touch-action: none; user-select: none; -webkit-user-drag: none;
}
.swcE-prev.is-drag { cursor: grabbing; }
.swcE-hint, .swcE-note, .swcE-meta {
  margin: 8px 0 0; font-size: .78rem; color: var(--swcE-quiet);
}
.swcE-meta { min-height: 1.2em; font-variant-numeric: tabular-nums; }

.swcE-drop {
  display: grid; place-items: center; gap: 4px; text-align: center;
  padding: 26px 18px; cursor: pointer;
  border: 1px dashed var(--swcE-line); background: rgba(35, 33, 30, .03);
  color: var(--swcE-ink);
  transition: border-color .3s ease, background .3s ease;
}
.swcE-drop:hover, .swcE-drop:focus-visible, .swcE-drop.is-over {
  border-color: var(--swcE-gold); background: rgba(0, 0, 0, .03);
}
.swcE-dropTitle { font-size: .95rem; }
.swcE-dropSub { font-size: .78rem; color: var(--swcE-quiet); }

.swcE-label {
  display: block; margin: 18px 0 6px;
  font-size: .7rem; text-transform: uppercase; letter-spacing: .14em;
  color: var(--swcE-quiet);
}

/* Inputs and buttons are the site's components by class, but a template that
   styles them for a dark band would hand the client an invisible field and an
   invisible Save. Restate the four properties that decide legibility and leave
   the rest — shape, spacing, letter-spacing — to the site. */
.swcE-dlg .swcE-right .form-control {
  width: 100%; box-sizing: border-box;
  background: #fff; color: var(--swcE-ink);
  border: 1px solid var(--swcE-line); opacity: 1;
}
.swcE-dlg .form-control::placeholder { color: var(--swcE-quiet); opacity: 1; }
.swcE-dlg .form-control:focus-visible { outline: 2px solid var(--swcE-gold); outline-offset: 1px; }

.swcE-actions { display: flex; gap: 10px; margin-top: 20px; flex-wrap: wrap; }

/* Save and Cancel deliberately keep the site's own .btn-primary / .btn-outline-
   secondary colors. An earlier pass forced them to charcoal and lost anyway —
   `t_tlc-styles.css` sets .btn-primary with !important, and beating that would
   have meant out-shouting the brand pipeline from a tool stylesheet. Style
   guide is law: the button is the site's button, and it follows the brand
   tokens wherever they land. Only the disabled state is restated, because a
   disabled Save has to read as unavailable rather than merely pale. */
.swcE-dlg .swcE-actions .swcE-save[disabled] {
  opacity: .34; cursor: default; filter: grayscale(.6);
}
.swcE-dlg .swcE-actions .swcE-cancel { background: transparent; }
.swcE-revert {
  display: block; margin-top: 14px; padding: 0;
  border: 0; background: none; cursor: pointer;
  font-size: .78rem; color: var(--swcE-quiet);
  border-bottom: 1px solid var(--swcE-line);
}
.swcE-revert:hover { color: var(--swcE-ink); border-bottom-color: var(--swcE-gold); }
.swcE-revert[disabled] { opacity: .5; cursor: default; }

/* Sits under the undo, close enough to read as a caption on it (6px) rather than
   as the next thing in the panel. It is the only place the client is told the
   old photograph does not last for ever. */
.swcE-revertNote {
  margin: 6px 0 0; font-size: .72rem; line-height: 1.45;
  color: var(--swcE-quiet);
}
.swcE-revertNote[hidden] { display: none; }

.swcE-status { margin: 14px 0 0; min-height: 1.2em; font-size: .82rem; color: var(--swcE-quiet); }
.swcE-status.is-bad { color: #9b2c2c; }

@media (prefers-reduced-motion: reduce) {
  .swcE-badge, .swcE-drop { transition: none; }
}

/* ------------------------------------------------------------------- zoom
   Pan and zoom are non-destructive: the file uploaded is always the full
   frame, and these are two numbers stored beside it. A client who zooms too
   far can Reset, and a future change of heart costs nothing. */
.swcE-zoomRow { display: flex; align-items: center; gap: 12px; margin-top: 10px; }
.swcE-zoomLbl {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .14em;
  color: var(--swcE-quiet);
}
.swcE-dlg .swcE-zoom {
  flex: 1; height: 2px; padding: 0; margin: 0; border: 0;
  -webkit-appearance: none; appearance: none;
  background: var(--swcE-line); cursor: pointer;
}
.swcE-dlg .swcE-zoom::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--swcE-gold); border: 0; cursor: grab;
}
.swcE-dlg .swcE-zoom::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--swcE-gold); border: 0; cursor: grab;
}
.swcE-zoomReset {
  border: 0; background: none; padding: 0; cursor: pointer;
  font-size: .72rem; text-transform: uppercase; letter-spacing: .12em;
  color: var(--swcE-quiet); border-bottom: 1px solid transparent;
}
.swcE-zoomReset:hover { color: var(--swcE-ink); border-bottom-color: var(--swcE-gold); }
.swcE-stage { touch-action: none; }

/* ------------------------------------------------------------- text editor
   Shares the badge and dialog chrome with the photo editor — one pill, one
   overlay, one visual language. The text badge is smaller and squarer so the
   two are distinguishable at a glance without a legend. */
.swcE-badge-t { width: 22px; height: 22px; border-radius: 3px; }
.swcE-badge-t svg { width: 11px; height: 11px; }

.swcE-panel-t { max-width: 660px; }
.swcE-tbody { padding: 22px; }
.swcE-was { margin: 0 0 4px; font-size: .8rem; color: var(--swcE-quiet); }
.swcE-panel .swcE-was { color: var(--swcE-quiet); }

.swcE-dlg .swcE-ta {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 96px;
  background: #fff; color: var(--swcE-ink);
  border: 1px solid var(--swcE-line); opacity: 1;
  font: inherit; font-size: .95rem; line-height: 1.5;
}
.swcE-dlg .swcE-ta:focus-visible { outline: 2px solid var(--swcE-gold); outline-offset: 1px; }
.swcE-dlg .swcE-tbody .form-control {
  width: 100%; box-sizing: border-box;
  background: #fff; color: var(--swcE-ink);
  border: 1px solid var(--swcE-line); opacity: 1;
}
.swcE-count {
  margin: 6px 0 0; font-size: .76rem; color: var(--swcE-quiet);
  font-variant-numeric: tabular-nums;
}
.swcE-panel .swcE-count.is-warn { color: #8a6d1f; }

/* ------------------------------------------------------- what is editable
   Treepl's own In-Context editor outlines its editable regions, and it is the
   best thing about that tool: you can see at a glance what you are allowed to
   touch instead of hunting for a pencil. Same idea here, quieter — hairline,
   offset off the content so it never crowds a headline, and only while edit
   mode is on. A visitor never gets this class; a signed-in editor who has the
   pill switched off does not either. */
html.swcE-on .swcE-hot {
  outline: 1px dashed color-mix(in srgb, var(--swcE-gold) 55%, transparent);
  outline-offset: 4px;
  /* BOTH properties named in ONE shorthand on purpose. A second `transition`
     rule further down the sheet would reset transition-property to whatever it
     listed and silently kill the other one — that is how a card's transform
     animation was lost to an LQIP rule on this site. */
  transition: outline-color .3s ease, background-color .9s ease;
}
html.swcE-on .swcE-hot:hover {
  outline-color: var(--swcE-gold);
}
@media (prefers-reduced-motion: reduce) {
  html.swcE-on .swcE-hot { transition: none; }
}

/* ------------------------------------------------------- editing in place
   The words themselves are the input. There is no field to draw, so the whole
   affordance is four states on the block: editable (the dashed hairline above),
   focused, changed-and-unsaved, and empty.

   Craig, 2026-08-18: "I want the user to be able to just click and edit the
   text directly in the browser window." */

html.swcE-on [data-swct-edit] { cursor: text; }

/* Focused. Solid, not dashed — the hairline stops being a hint and starts being
   a boundary. The wash is warm rather than pale so it reads as a field on a
   dark hero photograph AND on a white section; a paper tint disappears on one
   and a gray tint muddies the other. */
html.swcE-on [data-swct-edit]:focus,
html.swcE-on [data-swct-edit]:focus-visible {
  outline: 1px solid var(--swcE-gold);
  outline-offset: 4px;
  background-color: color-mix(in srgb, var(--swcE-gold) 10%, transparent);
}

/* Changed, not yet saved. Same solid line held after the caret leaves, so a
   client can see at a glance which blocks the bar's count is talking about. */
html.swcE-on [data-swct-edit].swcE-dirty {
  outline: 1px solid color-mix(in srgb, var(--swcE-gold) 80%, transparent);
  outline-offset: 4px;
}

/* Emptied. It cannot be saved — the bake refuses an empty replacement — and it
   has also collapsed to nothing on screen, so the min-height is what keeps it
   clickable enough to put the words back into. */
html.swcE-on [data-swct-edit].swcE-bad {
  outline: 1px solid #9b2c2c;
  outline-offset: 4px;
  min-height: 1.2em;
}

/* Saved. One gesture, the same one the site uses everywhere: a fade. The class
   is added and then removed, and the .swcE-hot transition above carries it
   back down over .9s. */
html.swcE-on [data-swct-edit].swcE-saved {
  background-color: color-mix(in srgb, var(--swcE-gold) 22%, transparent);
}

/* THE WHOLE-CARD LINK COVERS THE CARD'S WORDS. estate.js drops a real
   <a class="card-hit"> at inset:0 / z-index:3 over every .card-ed so that
   middle-click and cmd-click behave — which also means a click on a card's
   heading lands on the anchor and never reaches the text. Every card on /v2
   would be uneditable, and the failure looks like the editor ignoring you.
   Only while editing: the overlay is the visitor's feature, not theirs. */
html.swcE-on .card-hit { pointer-events: none; }

/* --------------------------------------------------------------- save bar
   Craig, 2026-08-18: "Upon clicking in the text field, I'd like a floating
   'SAVE EDIT' modal to appear near the bottom of the browser window to save
   any/all changes that are made."

   Bottom is already occupied on every /v2 page by the review bar, so this one
   is seated on that bar's MEASURED height by text-edit.js (seatBar) — the
   review bar re-syncs its own height on load and is shorter on a phone, so any
   number written here would be wrong on one of them. `bottom` below is only
   what governs the frame before that runs. */

/* VERTICAL, AND A FIXED WIDTH — the fixed width is a BUG FIX, not a taste.
   -------------------------------------------------------------------------
   Craig, 2026-08-18: "clicking the SAVE button jumped the box bigger and moved it
   but didn't save anything. Then, clicking it again finally did the SAVE."

   Measured: pressing Save blurs the block, `focusout` repaints the note from
   "h1 · designed around 44 characters, now 54" to the shortcut hint, the panel was
   `width:max-content` centered by `translateX(-50%)`, so it grew 534px -> 585px and
   the button slid 26px RIGHT between mousedown and mouseup. A click only fires when
   both halves land on the same element, so the press was thrown away. Press near the
   button's right edge and it misses by more than its own width.

   Two things make that impossible now rather than merely unlikely: the panel's width
   never depends on its text, and the note has a reserved height so a one-line to
   two-line swap cannot move the button vertically either. The button is also full
   width, so there is no narrow target left to slide off.

   And it is a vertical stack — message, then a big green action, then the quiet
   secondary — which is the shape every other TLC tool uses for the same job.
   Craig: "i want a vertical modal with message and big green save button below like
   all other sites TLC PBS." */
.swcE-bar {
  /* 14px above the DOCK — the height of whatever sticky footer the site puts at the
     bottom of the window, measured and published as --tlc-dock by text-edit.js. The
     72px fallback is the estate reviewer bar's resting height, so the very first
     paint is right on this site before JS has measured; on a site with no footer the
     variable resolves 0 and the panel simply sits lower. seatBar() overwrites this
     inline once it has a real number. */
  position: fixed; left: 50%; bottom: calc(var(--tlc-dock, 72px) + 14px);
  z-index: 2147482500;                 /* over the review bar, under the dialog */
  /* 20px, and the ladder it belongs to, measured: 7px title->note (one thought),
     10px between the two actions (siblings, and the Project Board's confirm footer
     gap), 20px here because the action is a different KIND of thing from the status
     above it. At 14px that boundary read as another line of text. */
  display: flex; flex-direction: column; align-items: stretch; gap: 20px;
  width: 340px; max-width: calc(100vw - 28px);
  padding: 18px 20px 20px;
  font-family: var(--swcE-font);
  color: var(--swcE-ink); background: var(--swcE-paper);
  border: 1px solid var(--swcE-line); border-radius: var(--swcE-radius);
  box-shadow: var(--swcE-shadow);
  opacity: 0; transform: translate(-50%, 10px);
  transition: opacity .32s ease, transform .32s ease;
}
.swcE-bar.is-in { opacity: 1; transform: translate(-50%, 0); }
/* [hidden] LIVES IN THE UA STYLESHEET AND LOSES TO ANY AUTHOR display:. Without
   this line `bar.hidden = true` is a no-op against the display:flex above, and
   the bar sits there empty for ever — measured twice on MongooseSports before
   anyone believed it. */
.swcE-bar[hidden] { display: none; }

/* Centered, on Craig's say-so (2026-08-19: "Center the text above SAVE BUTTON"). The
   button and the Discard beneath it are both centered, so a left-ragged status line
   above them was the only thing in the panel off its axis. */
.swcE-barMain { min-width: 0; text-align: center; }
/* 20px title / 1.25 is the Project Board's confirm title
   (`.pb2-confirm-modal .pb2-modal-title`), so the two tools say it at the same size. */
.swcE-bar .swcE-barCount {
  margin: 0; font-size: 20px; line-height: 1.25; color: var(--swcE-ink);
  font-variant-numeric: tabular-nums;
}
/* CRAIG'S VALUES, 2026-08-19, pasted verbatim: 8px / 1.2 / min-height 1em. The count
   line above is the message; this is a footnote to it, and at 8px it reads as one.
   The reserved height is therefore ONE line, not two — which on its own would let a
   two-line note grow the panel again, so the thing that actually protects the click
   is the pointerdown->pointerup note lock in text-edit.js, not this number. Do not
   raise it back to 3em thinking that is the fix; measure a press first. */
.swcE-bar .swcE-barNote {
  margin: 7px 0 0;
  font-size: 8px;
  line-height: 1.2;
  color: var(--swcE-quiet);
  min-height: 1em;
}
.swcE-bar .swcE-barNote.is-bad { color: #9b2c2c; }
.swcE-bar .swcE-barNote.is-warn { color: #8a6d1f; }

.swcE-barBtns { display: flex; flex-direction: column; align-items: stretch; gap: 10px; }
/* THE BIG GREEN ONE. Green because it is the same green every other TLC tool uses to
   mean "this is the go button" — the Review Board's refresh panel, measured, is
   rgba(31,150,74,.82) — and because this panel now also owns the DISCARD, so the two
   must not look alike. It is editor chrome, which is why the value is allowed here:
   the whole --swcE-* block at the top of this file is a deliberately local palette
   for a tool that has to render on a page whose brand tokens may not have resolved.
   It is not a page component and never travels into one. */
.swcE-bar .swcE-barSave {
  width: 100%; justify-content: center; white-space: nowrap;
  padding: 16px 22px;
  font-family: var(--swcE-font); font-size: 15px; letter-spacing: .14em;
  text-transform: uppercase;
  color: #fff; background: var(--swcE-go); border: 1px solid var(--swcE-go);
  border-radius: var(--swcE-radius); cursor: pointer;
  transition: background .2s ease, border-color .2s ease;
}
.swcE-bar .swcE-barSave:hover { background: var(--swcE-goDark); border-color: var(--swcE-goDark); }
.swcE-bar .swcE-barSave:focus-visible { outline: 2px solid var(--swcE-ink); outline-offset: 2px; }
.swcE-bar .swcE-barSave[disabled] { opacity: .34; cursor: default; filter: grayscale(.6); }
.swcE-bar .swcE-barUndo {
  border: 0; background: none; padding: 4px 0 0; cursor: pointer;
  font-size: 14px; color: var(--swcE-quiet); text-align: center;
}
.swcE-bar .swcE-barUndo span { border-bottom: 1px solid var(--swcE-line); }
.swcE-bar .swcE-barUndo:hover { color: var(--swcE-ink); }
.swcE-bar .swcE-barUndo:hover span { border-bottom-color: var(--swcE-gold); }
.swcE-bar .swcE-barUndo[disabled] { opacity: .5; cursor: default; }

/* The dialog owns the screen while it is up, and so does the bake chip's
   corner — one fixed control per corner. */
html.swcE-dialog .swcE-bar { display: none; }
/* AND THE VEIL OWNS IT WHILE IT IS UP. Craig, 2026-08-19, of the panel still sitting
   there reading "No unsaved changes / Saved. It is live." behind the green one: "this
   window isn't necessary. once the green modal pops up and begins saving, this window
   can fade away." Fades rather than `display:none` so it leaves the way it arrived,
   and `pointer-events:none` so a stray click during the save cannot reach a button
   that is on its way out. */
html.tlc-veil-up .swcE-bar { opacity: 0; pointer-events: none; }

/* AFTER the desktop rules, never before: a @media block adds no specificity, so
   an equal-specificity rule later in the sheet beats it at every width —
   including inside the query. */
@media (max-width: 600px) {
  .swcE-bar {
    left: 14px; right: 14px; width: auto; max-width: none;
    transform: translateY(10px);
  }
  .swcE-bar.is-in { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .swcE-bar { transition: none; }
}

/* ================================================================= TLC VEIL
   THE NAME, because Craig asked for one: "What do we call this sequence of buttons.
   i want to reuse this look and feel in action in other sites for saving and danger
   and warnings."

     .tlc-veil            the full-screen scrim + one centered card
       .is-go             green — working, saving, refreshing (the default)
       .is-danger         red   — it did not work, here is why
       .is-warn           amber — it worked, but read this
     .tlc-commit          the bottom panel that raises it (here: .swcE-bar)

   Together: raise the commit panel, press its one big action, the veil covers the
   window and reports. Called with window.TLCVeil.go() / .danger() / .warn() / .hide()
   from text-edit.js, so a page only has to say what happened, never how to draw it.

   IT IS WRITTEN TO BE LIFTED. Nothing in this block reads a --swcE-* token or an
   estate class; the only inherited thing is the type family. Moving it to
   turnerlee.com/shared/core/v1/tlc-veil.css and including it is the whole port — do
   that rather than copying it into the next site, or the two drift the way the
   Review Board's copy and this one nearly did.

   Craig, 2026-08-18: "i want the SAVING EDITS modal to look just like the Refresh
   modal pixel perfect, centered on the view window while it's saving."

   That modal is MongooseSports `/reviews/board` `.mg-over` / `.mg-over-box`, and
   every number below is copied from it rather than eyeballed from the screenshot:
   the scrim, the blur, the .22s fade, `width:min(350px,100%)`, `padding:18px 5px`,
   the 18px radius, `rgba(31,150,74,.82)` on a `rgba(255,255,255,.30)` hairline, the
   `0 26px 70px rgba(0,0,0,.42)` shadow, the .965 -> 1 scale, the --mgo type ladder,
   and Craig's own "big active crazy" spinner at 3.4rem / .225rem stroke.

   Two of its hard-won details come with it and must not be tidied away:
   `width:min(350px,100%)` NOT 94vw — the box is grid-centered inside a padded fixed
   layer, and an item wider than its area overflows instead of centring, which is
   exactly the "not centered on mobile" bug he reported there on 2026-08-16; and the
   14px phone padding sits AFTER the unmediated rule, because a media query adds no
   specificity and would otherwise lose to it. */
.tlc-veil {
  position: fixed; inset: 0; z-index: 2147483650;   /* above the dialog layer */
  display: grid; place-items: center; padding: 24px;
  opacity: 0; transition: opacity .22s cubic-bezier(.22, .61, .36, 1);
  background: rgba(10, 12, 14, .42);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  --mgo-size: clamp(20px, min(3.6vw, 2.5vh), 22px);
  --mgo-bump: 0px;
  /* One pair per state: the fill, and the ink that is legible on it. GO is the
     Review Board's measured green. DANGER and WARN are Bootstrap 5.3's own $danger
     and $warning — which /v2 already loads, so they are this page's existing
     vocabulary for the two ideas rather than two colors somebody picked — carried at
     the same .82 as the green so all three read as one family. Amber takes dark ink,
     exactly as `.btn-warning` does (measured on the live page: ink rgb(0,0,0)). */
  --veil-fill: rgba(31, 150, 74, .82);
  --veil-ink:  #fff;
  --veil-ink2: rgba(255, 255, 255, .92);
}
.tlc-veil.is-go     { --veil-fill: rgba(31, 150, 74, .82); }
.tlc-veil.is-danger { --veil-fill: rgba(220, 53, 69, .82); }
.tlc-veil.is-warn {
  --veil-fill: rgba(255, 193, 7, .88);
  --veil-ink:  #23211e; --veil-ink2: rgba(35, 33, 30, .86);
}
.tlc-veil.is-in { opacity: 1; }
.tlc-veil[hidden] { display: none; }
.tlc-veil-box {
  display: flex; flex-direction: column; align-items: center; gap: 0;
  width: min(350px, 100%); padding: 18px 5px;
  border-radius: 18px; text-align: center;
  background: var(--veil-fill); border: 1px solid rgba(255, 255, 255, .30);
  box-shadow: 0 26px 70px rgba(0, 0, 0, .42);
  transform: scale(.965); transition: transform .22s cubic-bezier(.22, .61, .36, 1);
}
.tlc-veil.is-warn .tlc-veil-box { border-color: rgba(35, 33, 30, .22); }
.tlc-veil.is-in .tlc-veil-box { transform: scale(1); }
@media (max-width: 560px) { .tlc-veil { padding: 14px; } }
.tlc-veil h2 {
  margin: clamp(15px, 2.2vh, 24px) 0 0; color: var(--veil-ink);
  font-family: var(--swcE-font); font-weight: 900;
  font-size: calc(var(--mgo-size) + var(--mgo-bump)); line-height: 1.15;
  letter-spacing: .04em; text-transform: uppercase;
}
.tlc-veil p {
  margin: 14px 0 0; color: var(--veil-ink2);
  font-family: var(--swcE-font); font-weight: 300;
  font-size: var(--mgo-size); line-height: 1.15;
}
/* A HELD VEIL NEEDS A WAY OUT, and it needs a VISIBLE one. Bad news must not fade on
   its own — so the panel that carries it does not auto-hide, and the way to dismiss
   it cannot be a click somewhere the client has to guess at. Same shape as the commit
   panel's action: one wide button under the message. Only rendered when held, so the
   saving state stays the four words and a spinner. */
/* EVERY DESCENDANT OF EVERY PANEL, ONCE — not the one element that just bit us.
   `[hidden]{display:none}` lives in the UA stylesheet, and an author sheet beats the UA
   sheet at ANY specificity, so `el.hidden = true` is a silent no-op on anything this
   file gives a `display:`. That has now cost three separate bugs across two sites
   (twice on MongooseSports /reviews, once here), so it is answered as a blanket over
   the chrome subtrees instead of one selector per victim — a new panel part is covered
   before it is written.

   Here it was the spinner: the red "EDIT NOT SAVED" panel sat there with a spinner
   still turning on it, and the TEST PASSED, because the test asserted the attribute was
   set. It was set. Assert computed `display` for this, never `.hidden`. */
/* `!important` DELIBERATELY, and this is the one place in this file it is right. The
   blanket is competing with its own siblings, not with somebody else's sheet: the
   spinner's `display` rule below is `.tlc-veil .preloader-wrapper`, exactly as specific
   as `.tlc-veil [hidden]`, and it comes LATER — so the first version of this rule was
   silently beaten and the red panel kept its spinner. Ordering would fix it today and
   break again the next time a part is added below; `hidden` meaning hidden is a policy,
   so it is written as one. */
.tlc-veil [hidden], .swcE-bar [hidden], .swcE-dlg [hidden], .swcE-layer [hidden] {
  display: none !important;
}
/* THE ACTION ROW. 26px above, against 14px from headline to message: the headline and
   the message are the same kind of thing and belong together, the buttons are a
   different kind and have to read as separate. Measured — at 18px the three gaps were
   20 / 14 / 18, near enough equal to read as three stacked lines rather than a message
   and an action. The 10px BETWEEN the buttons is deliberately tighter than that 26px:
   they are siblings, and equal spacing would make the pair read as two unrelated
   controls. Wraps on a narrow phone rather than shrinking either one below its tap
   target. */
.tlc-veil-acts {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: stretch;
  gap: 10px; margin: 26px 0 4px;
}
/* EQUAL SHARES OF THE ROW, so the label can change without the row re-laying out.
   NOTIFY US becomes "Reported" the moment it is pressed, and on a content-sized row
   that shrank the pair, wrapped OK onto a second line and grew the whole panel under
   the client's cursor — the same class of bug as the save bar that moved between
   mousedown and mouseup. Width comes from the row here, never from the text. */
.tlc-veil-acts > * { flex: 1 1 0; min-width: 0; }
/* No side margins on either button: the row's `gap` is the only thing that spaces
   them, so the measured gap is the number written above it. Leaving the old 5px
   margins on made a 10px gap render as 20px. */
.tlc-veil-ok {
  margin: 0; padding: 12px 26px;
  min-width: 140px;
  font-family: var(--swcE-font); font-size: 14px; letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--veil-ink); background: rgba(255, 255, 255, .16);
  border: 1px solid rgba(255, 255, 255, .55); border-radius: var(--swcE-radius, 6px);
  cursor: pointer; transition: background .2s ease;
}
.tlc-veil-ok:hover { background: rgba(255, 255, 255, .28); }
.tlc-veil-ok:focus-visible { outline: 2px solid var(--veil-ink); outline-offset: 2px; }
.tlc-veil.is-warn .tlc-veil-ok {
  background: rgba(35, 33, 30, .10); border-color: rgba(35, 33, 30, .45);
}
.tlc-veil.is-warn .tlc-veil-ok:hover { background: rgba(35, 33, 30, .18); }

/* NOTIFY US — the second half of the loop. Craig, 2026-08-19: "we'll then make sure to
   include the error log on the Ops Dashboard so we can ID and fix any problems if they
   arise."

   Quieter than OK on purpose: dismissing is what the client came to do, telling us is
   the favour. Same size and the same tap target though — a 10px ghost link beside a
   14px button reads as a footnote, and this is the press we actually want. */
.tlc-veil-tell {
  margin: 0; padding: 12px 22px;
  min-width: 140px;
  font-family: var(--swcE-font); font-size: 14px; letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--veil-ink2); background: transparent;
  border: 1px solid rgba(255, 255, 255, .30); border-radius: var(--swcE-radius, 6px);
  cursor: pointer; transition: background .2s ease, border-color .2s ease, color .2s ease;
}
.tlc-veil-tell:hover { background: rgba(255, 255, 255, .14); border-color: rgba(255, 255, 255, .55); }
.tlc-veil-tell:focus-visible { outline: 2px solid var(--veil-ink); outline-offset: 2px; }
.tlc-veil.is-warn .tlc-veil-tell { border-color: rgba(35, 33, 30, .30); }
.tlc-veil.is-warn .tlc-veil-tell:hover { background: rgba(35, 33, 30, .10); border-color: rgba(35, 33, 30, .45); }
/* Sent: still legible, obviously spent. Not `disabled`-gray — the client should be able
   to read what they just did, and a control that vanishes on success leaves them
   wondering whether it worked. */
/* Spent, not competing. Once it is sent, OK is the only thing left to do, so the
   reported state goes QUIETER than its resting state rather than filling in and
   reading as a second equal button. The tick says done without a word. */
.tlc-veil-tell.is-sent {
  color: var(--veil-ink2); border-color: rgba(255, 255, 255, .18);
  background: transparent; cursor: default; letter-spacing: .10em;
}
/* No tick glyph here: it widens the label's min-content past its equal share of the
   row, which re-splits the pair and moves OK by 4px. The word is the signal. */
.tlc-veil-tell.is-failed { border-color: rgba(255, 255, 255, .70); color: var(--veil-ink); }

/* The spinner, carried over by hand exactly as the Review Board carries it — same
   markup, same 1333ms arc, same "crazy" 333ms wrapper — because neither page loads
   MDB and neither should start for one spinner. Prefixed keyframes so they cannot
   collide with anything on /v2. */
/* 54.4px AND 3.6px, NOT 3.4rem AND .225rem — the same spinner, stated in the unit
   that survives the move. The reference page's root font-size is 16px, so its rem
   values render 54.4 / 3.6; this site sets `html{font-size:102%}` and the harness
   measured a 337px-wide viewport at 20.6px root, which blew the same declaration up
   to a 70px spinner. Pixel-perfect means the pixels, so the pixels are what is
   written. */
.tlc-veil .preloader-wrapper {
  position: relative; display: inline-block; width: 54.4px; height: 54.4px;
  font-size: 0; animation: swcEo-rotate 1568ms linear infinite;
}
.tlc-veil .preloader-wrapper.crazy { animation-duration: 333ms; }
.tlc-veil .spinner-layer {
  position: absolute; width: 100%; height: 100%; opacity: 1; border-color: #fff;
  animation: swcEo-fill 5332ms cubic-bezier(.4, 0, .2, 1) infinite both;
}
.tlc-veil .gap-patch {
  position: absolute; top: 0; left: 45%; width: 10%; height: 100%;
  overflow: hidden; border-color: inherit;
}
.tlc-veil .gap-patch .circle { left: -450%; width: 1000%; }
.tlc-veil .circle-clipper {
  position: relative; display: inline-block; width: 50%; height: 100%;
  overflow: hidden; border-color: inherit;
}
.tlc-veil .circle-clipper .circle {
  position: absolute; top: 0; right: 0; bottom: 0; width: 200%; height: 100%;
  border-color: inherit; border-style: solid; border-width: 3.6px;
  border-bottom-color: transparent; border-radius: 50%;
}
.tlc-veil .circle-clipper.left .circle {
  left: 0; border-right-color: transparent;
  transform: rotate(129deg); animation: swcEo-left 1333ms cubic-bezier(.4, 0, .2, 1) infinite both;
}
.tlc-veil .circle-clipper.right .circle {
  left: -100%; border-left-color: transparent;
  transform: rotate(-129deg); animation: swcEo-right 1333ms cubic-bezier(.4, 0, .2, 1) infinite both;
}
@keyframes swcEo-rotate { to { transform: rotate(360deg); } }
@keyframes swcEo-fill {
  12.5% { transform: rotate(135deg); } 25% { transform: rotate(270deg); }
  37.5% { transform: rotate(405deg); } 50% { transform: rotate(540deg); }
  62.5% { transform: rotate(675deg); } 75% { transform: rotate(810deg); }
  87.5% { transform: rotate(945deg); } to { transform: rotate(1080deg); }
}
@keyframes swcEo-left {
  from { transform: rotate(130deg); } 50% { transform: rotate(-5deg); } to { transform: rotate(130deg); }
}
@keyframes swcEo-right {
  from { transform: rotate(-130deg); } 50% { transform: rotate(5deg); } to { transform: rotate(-130deg); }
}
@media (prefers-reduced-motion: reduce) {
  .tlc-veil, .tlc-veil-box { transition: none; }
  .tlc-veil .preloader-wrapper, .tlc-veil .spinner-layer,
  .tlc-veil .circle-clipper .circle { animation-duration: 4s; }
}

/* ============================================================ TLC SIGNALS: TOAST
   The third surface of the set, after the commit panel and the veil. Craig,
   2026-08-19: "it's not quite like a modal saving window, because it's not actively
   doing something, that's why it's down in the bottom left. It's just letting you
   know something is complete."

   So the rules that make it a toast and not a small veil: it never covers the page,
   it never takes focus, it never blocks, and it is not centered. What it shares with
   the veil is the COLOR GRAMMAR — the same green at the same .82, the same white
   ink — so that the two read as one system rather than two tools that both happen to
   be green.

   Transparent over the page, not over a scrim, which is the one real difference from
   the veil box: the veil floats on a blurred dark layer that guarantees contrast,
   and this floats on whatever is behind it — a photograph on one page and near-white
   paper on the next. The backdrop-filter is therefore load-bearing, not decoration.
   It is what turns .82 into glass instead of into a washed-out green on paper. */
/* THE CORNER IS A STACK, NOT A SLOT. The bake chip is a persistent button and a
   "Baked 1 edit" report is a passing status line, and both belong bottom-left — so
   the corner owns the position ONCE and the toasts inside it are ordinary flow items.
   The alternative is every toast doing its own bottom math against every other
   toast's height, which is the same arithmetic that put the chip on top of the NEXT
   button in the first place.

   column-reverse + append = the FIRST thing added sits nearest the corner and later
   ones stack above it. That is the right way round, and it is not the obvious one:
   the bake chip is added at boot and is a persistent BUTTON, so a toast arriving
   later must never displace it. A stack that pushed the chip up would move a control
   under the cursor — the same class of bug as the save panel that grew between
   mousedown and mouseup. Transient news gives way to a standing control, not the
   reverse.

   pointer-events:none on the container so the empty column never eats a click on the
   page behind it; the children take it back. */
.tlc-toaster {
  position: fixed; left: 16px; z-index: 2147483000;
  /* CLEAR OF THE DOCK. Craig: "It also cannot lay on top of the back button... we'll
     be utilizing the sticky footer on all of our sites in Ops." 0px fallback, not
     72px like the panel: the panel is ours and only ever renders on a page with the
     reviewer bar, whereas a toast is the piece most likely to be lifted to a site
     with no footer at all, and there it should sit at 16px rather than float. */
  bottom: calc(var(--tlc-dock, 0px) + 16px);
  display: flex; flex-direction: column-reverse; align-items: flex-start; gap: 10px;
  pointer-events: none;
}
.tlc-toaster > * { pointer-events: auto; }

.tlc-toast {
  padding: 9px 15px;
  /* px, not rem: this site sets html{font-size:102%} and the harness measures a
     337px viewport at 20.6px root, which renders a "12px" chip at 15.5px. */
  font-family: var(--swcE-font); font-size: 12px; line-height: 1.2;
  letter-spacing: .10em; text-transform: uppercase;
  color: #fff;
  background: var(--tlc-toast-fill, rgba(31, 150, 74, .82));
  backdrop-filter: blur(8px) saturate(1.15);
  -webkit-backdrop-filter: blur(8px) saturate(1.15);
  border: 1px solid rgba(255, 255, 255, .30);
  /* 12px against the veil box's 18px. Same language, scaled: the veil is a 350px
     panel and this is a 40px chip, so carrying 18px across would read as a pill. */
  border-radius: 12px;
  box-shadow: 0 14px 34px -12px rgba(0, 0, 0, .45);
  transition: background .2s ease, border-color .2s ease;
}
/* CURSOR BELONGS TO THE BUTTON, NOT TO THE LOOK. On the base rule it gave a
   `role="status"` line a pointer, which is the cursor promising a click that no
   handler will answer — the same lie as a hover state on a status message. */
button.tlc-toast { cursor: pointer; }
/* A toast that is NOT in a toaster still has to land somewhere sane, so that lifting
   the class to another site without the container is a worse layout rather than a
   broken one. */
body > .tlc-toast {
  position: fixed; left: 16px; bottom: calc(var(--tlc-dock, 0px) + 16px);
  z-index: 2147483000;
}
/* Only a BUTTON gets a hover state. A status line that lightens under the cursor is
   claiming to be clickable. */
button.tlc-toast:hover { background: rgba(31, 150, 74, .94); border-color: rgba(255, 255, 255, .55); }
button.tlc-toast.is-warn:hover { background: rgba(255, 193, 7, .96); }
.tlc-toast:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.tlc-toast[hidden] { display: none; }
/* The site's one gesture, reused: a short rise and a fade. Applied by removing this
   class a frame after the node is in the DOM, so there is a from-state to start at.
   NOT on the base rule — the bake chip is a .tlc-toast that must paint immediately,
   and an opacity:0 default would make it invisible until something thought to
   animate it. */
.tlc-toast.is-ghost { opacity: 0; transform: translateY(6px); }
.tlc-toast.is-live { transition: opacity .28s ease, transform .28s ease; }
/* The same two off-green states the veil carries, so `warn` and `danger` mean the
   one thing across all three surfaces. Amber takes dark ink here too. */
.tlc-toast.is-warn {
  --tlc-toast-fill: rgba(255, 193, 7, .88);
  color: #23211e; border-color: rgba(35, 33, 30, .22);
}
.tlc-toast.is-danger { --tlc-toast-fill: rgba(220, 53, 69, .82); }

/* THE COMMIT PANEL AND A TOAST ONLY COLLIDE ON A NARROW SCREEN, so only hide one
   there. The panel is 340px wide and bottom-CENTER; the toaster is bottom-LEFT.
   At 1440 they sit side by side and both should show — hiding at every width was
   a one-line change that made "the panel and the toast read the same dock number"
   untestable, because a display:none element measures as a zero rect 760px from
   the footer rather than as a toast in the wrong place.

   Deliberately AFTER the rules above: a @media block adds no specificity, so an
   equal-specificity rule later in the sheet would beat it at every width,
   including inside the query. */
@media (max-width: 760px) {
  html.swcE-bar-up .tlc-toast { display: none; }
}

/* --------------------------------------------------------------------- bake
   There is no bake CHIP any more — leftovers sweep themselves on load and report
   the result as an ordinary toast, because Craig, 2026-08-19: "they shouldn't have
   to click a confirmation button after they've already saved it". What is left
   here is the review dialog, which is a diagnostic he opens deliberately via
   SWCBake.review(), never something the page offers him.

   The dialog owns the screen while it is open, and the badge layer is already
   hidden by html.swcE-dialog — every toast goes with it. */
html.swcE-dialog .tlc-toast { display: none; }

.swcE-bakelist {
  margin: 4px 0 18px; max-height: 46vh; overflow-y: auto;
  border-top: 1px solid var(--swcE-line);
}
.swcE-bakerow {
  display: flex; gap: 10px; align-items: baseline;
  padding: 9px 2px; border-bottom: 1px solid var(--swcE-line);
  font-size: .82rem; line-height: 1.45;
}
.swcE-bakemark { flex: 0 0 auto; width: 1em; font-weight: 700; }
.swcE-bakerow.is-ok .swcE-bakemark { color: var(--swcE-gold); }
.swcE-bakerow.is-no .swcE-bakemark { color: #9b2c2c; }
/* A refusal is the interesting row — it is the one that keeps its override and
   needs Craig to look at it, so it does not get to be quiet. */
.swcE-bakerow.is-no .swcE-bakewords { color: #9b2c2c; }
.swcE-bakewords { color: var(--swcE-ink); overflow-wrap: anywhere; }
