
:root{--surface:#faf4ec;--surface-alt:#f1e6d8;--surface-deep:#004952;--surface-deepest:#002f36;--on-surface:#213d42;--soft:#38555a;--muted:#506d73;--on-surface-deep:#f8eddd;--line:#e8dac7;--line-strong:#70898e;--primary:#127c8b;--primary-text:#007382;--on-primary:#ffffff;--primary-hover:#006674;--accent:#73c3ff;--accent-text:#006ea7;--accent-tint:#d5dee1;--on-accent-tint:#006b79;--on-accent:#131313;--danger:#aa3333;--danger-text:#b63f3e;--on-danger:#ffffff;--field:#fffdfb;--font-display:'Poppins', system-ui, sans-serif;--font-body:'Figtree', system-ui, sans-serif;--weight-display:700;--weight-body:400;--weight-strong:600;--display-scale:1;--text-eyebrow:0.72rem;--text-small:0.82rem;--text-detail:0.9rem;--text-body:1rem;--text-lead:1.12rem;--text-title:1.12rem;--text-section:calc(1.9rem * var(--display-scale, 1));--text-display:calc(clamp(2.6rem, 9vw, 4.2rem) * var(--display-scale, 1));--space-2xs:0.25rem;--space-xs:0.5rem;--space-sm:0.75rem;--space-md:1rem;--space-lg:1.25rem;--space-xl:1.75rem;--space-2xl:2.5rem;--space-section:3.25rem;--space-sectionWide:4.375rem;--radius-xs:3px;--radius-sm:6px;--radius-md:8px;--radius-lg:12px;--radius-button:14px;--radius-card:16px;--radius-panel:18px;--radius-pill:999px;--radius-round:50%;--radius-arch:60px 60px 6px 6px;--radius-archLarge:170px 170px 8px 8px;--shadow-none:none;--shadow-card:0 10px 30px rgba(12, 90, 102, 0.12);--shadow-warm:0 18px 50px rgba(90, 60, 40, 0.10);--shadow-bar:0 -6px 20px rgba(0, 0, 0, 0.08);--leading-body:1.55;--leading-tight:1.15;--leading-display:1.02;--tracking-eyebrow:0.14em;--tracking-display:-0.01em;--measure-lead:34ch;--measure-prose:60ch;--target-size:44px;--target-min:24px;--header-h:calc(44px + 2 * 0.5rem);--container-page:1080px;--container-action:26rem;--border-hairline:1px;--border-rule:2px;
 --wt-ink:var(--on-surface);--wt-ink-soft:var(--soft);
 --wt-paper:var(--surface-alt);--wt-card:var(--surface);
 --wt-line:var(--line-strong);--wt-line-soft:var(--line);
 --wt-primary:var(--primary);--wt-primary-text:var(--primary-text);
 --wt-on-primary:var(--on-primary);
 --wt-primary-soft:color-mix(in srgb, var(--primary) 10%, var(--surface));
 --wt-warn:var(--danger-text);--wt-warn-fill:var(--danger);
 --wt-on-warn:var(--on-danger);
 --wt-field:var(--field);
 --wt-deep:var(--surface-deep);--wt-on-deep:var(--on-surface-deep);
 --wt-amber:var(--accent);--wt-on-amber:var(--on-accent);
 --wt-amber-text:var(--accent-text);}
/* b-0556 — the brand faces, self-hosted from this app's own public/, same three
   files and same reasoning as apps/signup: a copy, because a reach into another
   app's public/ fails silently the day a build context narrows. Poppins 600 for
   headings only — a dashboard is a tool, and 700 display weight is the front
   door's register, not this one's. font-display:swap: the words render before
   the files arrive, on the coastal 4G this surface is built for. */
@font-face{font-family:Poppins;src:url(/fonts/poppins-latin-600-normal.woff2) format('woff2');
 font-weight:var(--weight-strong);font-style:normal;font-display:swap}
@font-face{font-family:Figtree;src:url(/fonts/figtree-latin-400-normal.woff2) format('woff2');
 font-weight:var(--weight-body);font-style:normal;font-display:swap}
@font-face{font-family:Figtree;src:url(/fonts/figtree-latin-600-normal.woff2) format('woff2');
 font-weight:var(--weight-strong);font-style:normal;font-display:swap}
*{box-sizing:border-box}
body{margin:0;background:var(--wt-paper);color:var(--wt-ink);
 font:16px/1.55 Figtree,system-ui,-apple-system,"Segoe UI",sans-serif}
/* 🚨 b-0680 — EVERY link, because the two that had no rule were on every page.
   The footer link and the ones inside a wt-note had no colour rule at all, so
   they inherited the browser's own default link blue and its visited purple:
   measured in chromium 27 Aug 2026, not read off this sheet, which is why it
   survived — a raw-values budget counts hexes that are WRITTEN DOWN, and this
   one was written nowhere.
   ⚠️ The two exact values live in every-link-has-a-brand-colour.test.ts and
   deliberately NOT here: writing them into this comment put apps/owner over its
   raw-values budget, which is that guard working correctly on the file.
   ⚠️ The footer is printed by layout(), so it was on every screen behind the
   login, /privacidad included — the Art. 13 notice, the page a regulator reads.
   ⚠️ A blanket rule rather than two selectors, deliberately: naming the two is
   the fix that leaves the next unstyled link looking exactly like this one.
   An author rule beats the UA sheet regardless of specificity, so this settles
   :visited too, and the four rules that mean something else — wt-brand, the top
   nav, wt-site and the section pills — still win on specificity below.
   🚨 NO BACKTICKS AND NO DOLLAR-BRACE IN A COMMENT HERE. This sheet is a
   template literal: the first draft of this comment quoted the selectors in
   backticks and esbuild parsed them as the end of the string. Same trap as the
   angle-bracket note further down, one character class over. */
a{color:var(--wt-primary-text)}
/* The flow. A block's gap is its own margin-bottom; nothing on this surface
   has a top margin, the user agent's included. */
p,ul,ol,dl,figure,blockquote,pre,table{margin:0 0 var(--spacing-small)}
/* ── The design system (Kyle, 29 Sep 2026) ───────────────────────────────
   One place for the spacing matrix, the type primitives and the shared
   components. src/ui.ts renders the markup for each, and field() in this file
   the labelled control. The spacing utilities (.mb-*) are the LAST rules in
   this sheet, after every component, so a utility on an element always wins
   over a component's own margin.

   The spacing matrix. Every vertical gap on the dashboard is one of these:
     --spacing-2xs      0.25rem  a label to its control, a line under a line
     --spacing-xs       0.5rem   tight pairs: a hint under a list, chips
     --spacing-stack    0.75rem  boxes of one list: history, messages, bills
     --spacing-small    1rem     paragraph flow, field to field, a row of buttons
     --spacing-med      1.25rem  one block to the next: the lede, a notice, a card
     --spacing-large    1.75rem  before an unboxed group, after a list that closes one
     --spacing-section  1.75rem, 2.5rem from 1024px: before a section or a big title
     --pad-card         1rem, 1.25rem from 1024px: inside cards and notices
     --gutter           0.9375rem, 2.5rem from 768px, 3.75rem from 1280px: the
                        side padding of everything that sits at the page edge
   Breakpoints are the front door's (FARONDA_DOOR_BREAKPOINT, Tailwind's
   steps): sm 640px, md 768px, lg 1024px, xl 1280px. Mobile-first, so every
   query is a min-width and the phone is the unqueried base.
   Titles space themselves in em (.mb-1em by default, down to .mb-0), so the
   gap under a heading scales with the heading. Top margins are never used
   for spacing: the element above owns the gap, and the last child of a
   padded block carries none, because the padding is the gap. */
:root{--spacing-2xs:var(--space-2xs);--spacing-xs:var(--space-xs);--spacing-stack:var(--space-sm);
 --spacing-small:var(--space-md);--spacing-med:var(--space-lg);--spacing-large:var(--space-xl);
 --spacing-section:var(--space-xl);--pad-card:var(--space-md);
 --gutter:0.9375rem;--title-font:Poppins,system-ui,sans-serif}
@media (min-width:768px){:root{--gutter:2.5rem}}
@media (min-width:1024px){:root{--spacing-section:var(--space-2xl);--pad-card:var(--space-lg)}}
@media (min-width:1280px){:root{--gutter:3.75rem}}

/* Titles, named by size; the tag is the caller's choice. Four tiers:
   huge 1.5rem the page title, big 1.1rem a section, med 1rem a card, and the
   eyebrow's small caps over a list. The bottom margin is a .mb-* class the
   primitive always adds, so the base carries none. */
.huge-title,.big-title,.med-title,.eyebrow{font-family:var(--title-font);
 font-weight:var(--weight-strong);letter-spacing:-.01em;margin:0}
.huge-title{font-size:1.5rem}
.big-title{font-size:1.1rem}
.med-title{font-size:var(--text-body)}
.eyebrow{font-size:var(--text-small);letter-spacing:.08em;text-transform:uppercase;
 color:var(--wt-primary-text)}
/* The floor for a heading no primitive drew: the shared fact-chip sheet's
   group titles and the tenant markup the preview embeds. Zero specificity,
   so any component rule wins. */
:where(h1,h2,h3,h4){font-family:var(--title-font);font-weight:var(--weight-strong);
 letter-spacing:-.01em;margin:0 0 .5em}
:where(h1){font-size:1.5rem}
:where(h2){font-size:1.1rem}

/* Body text. The lede is the step above the hint; both are the soft ink. */
.wt-lede{color:var(--wt-ink-soft);max-width:44rem}
.wt-hint{font-size:.85rem;color:var(--wt-ink-soft)}

/* Fields. One .wt-field per label, hint, error and control; the block owns
   the gap below it and the parts inside it sit a 2xs apart. */
.wt-field{margin:0 0 var(--spacing-small)}
.wt-field > *{margin-bottom:var(--spacing-2xs)}
.wt-field > .wt-file{margin-bottom:var(--spacing-2xs)}
label{display:block;margin:0 0 var(--spacing-2xs);font-weight:var(--weight-strong);font-size:.92rem}
input,textarea,select{width:100%;padding:.6rem;font:inherit;color:inherit;margin:0;
 background:var(--wt-field);border:1px solid var(--wt-line);border-radius:var(--radius-sm)}
textarea{min-height:6.5rem;resize:vertical}
input[aria-invalid="true"],textarea[aria-invalid="true"]{border:2px solid var(--wt-warn)}
.wt-field-error{color:var(--wt-warn);font-weight:var(--weight-strong);font-size:.9rem}
/* A checkbox with its words beside it: the 44px label is the target. */
label.wt-check{display:flex;align-items:center;gap:.6rem;font-weight:var(--weight-strong);
 min-height:44px;cursor:pointer;margin:0 0 var(--spacing-xs)}

/* Buttons. The teal fill saves; .wt-secondary is the outline for a second
   action; .wt-danger (below, by the confirmation) deletes; .wt-go is amber.
   No margin: the row they sit in places them. */
button{font:inherit;padding:.65rem 1.1rem;border:0;border-radius:var(--radius-sm);
 background:var(--wt-primary);color:var(--on-primary);cursor:pointer;margin:0}
button.wt-secondary{background:transparent;color:var(--wt-ink);border:1px solid var(--wt-line)}
/* 🚨 b-0681 — AMBER, and it is the only amber control behind the login. Sunlight
   fill with the ink label measures 9.36:1, which is AAA; the visual pass
   estimated 5.5 and was pessimistic, and brand-palette-contrast.test.ts pins
   the real figure so neither number can drift. The rule is one selector on
   purpose: a modifier class is greppable, and one-amber-per-screen.test.ts
   counts it. */
button.wt-go{background:var(--wt-amber);color:var(--wt-on-amber);font-weight:var(--weight-strong)}
.wt-actions{display:flex;gap:.6rem;flex-wrap:wrap;margin:0 0 var(--spacing-small)}
/* 🚨 b-1271 — ONE affordance per decision. "Add to my site" was a solid button
   and "Open the editor" a bare link, in the same slot, for the same decision.
   Same shape, same 44px target; the anchor keeps being an anchor because it
   navigates rather than submits. */
.wt-link-button{display:inline-flex;align-items:center;justify-content:center;
 min-height:var(--target-min);padding:.65rem 1.1rem;border-radius:var(--radius-sm);
 background:var(--wt-primary);color:var(--wt-on-primary);text-decoration:none;
 font-weight:var(--weight-strong)}
.wt-link-button:hover{text-decoration:underline}
/* The same target, outlined, where the screen already has its one fill. */
.wt-link-button.wt-link-button--outline{background:transparent;color:var(--wt-primary-text);
 border:var(--border-rule) solid var(--wt-primary)}

/* Messages. .wt-note is the boxed status line; .wt-error the warning tone and
   .wt-done the settled one. The error summary above a form is .wt-problems. */
.wt-note{padding:var(--space-sm) var(--pad-card);border-radius:var(--radius-sm);border:1px solid var(--wt-line);
 background:var(--wt-card);margin:0 0 var(--spacing-med)}
.wt-note.wt-error{border-color:var(--wt-warn);color:var(--wt-warn)}
.wt-note.wt-done{border-color:var(--wt-primary)}
/* The error summary: first thing in main, impossible to scroll past. */
.wt-problems{border:3px solid var(--wt-warn);border-radius:var(--radius-sm);background:var(--wt-card);
 padding:.9rem var(--pad-card);margin:0 0 var(--spacing-med)}
.wt-problems h2{color:var(--wt-warn)}
.wt-problems ul{padding-left:1.2rem}
.wt-problems li{margin:0 0 var(--spacing-2xs)}
.wt-problems a{color:var(--wt-warn);font-weight:var(--weight-strong)}

/* Disclosure. A native details element; the marker is drawn, so the summary
   reads the same in every browser, and the target is 24px at least. */
details.wt-disclosure>summary{display:flex;align-items:center;min-height:var(--target-min);
 cursor:pointer;font-weight:var(--weight-strong);list-style:none}
details.wt-disclosure>summary::-webkit-details-marker{display:none}
details.wt-disclosure>summary::before{content:'▸ ';white-space:pre}
details.wt-disclosure[open]>summary::before{content:'▾ '}

/* Cards. A bordered box on the card ground; stacked cards of one list sit a
   stack apart. The rule was only ever written for a form. */
form.wt-card{border:1px solid var(--wt-line);border-radius:var(--radius-md);background:var(--wt-card);
 padding:var(--pad-card);margin:0 0 var(--spacing-stack)}
/* b-0681 — header.wt-top is gone; the bar is header.wt-bar further down. The
   rule is deleted rather than left orphaned: an unused header rule is the next
   reader's confusion about which one is live. */
/* b-0374 — the two lists on "que te encuentren". Plain, because the page is
   read by somebody who is already annoyed and wants the facts. */
.wt-vis{list-style:none;padding:0;margin:0 0 var(--spacing-med)}
.wt-vis li{padding:.7rem 0;border-bottom:1px solid var(--wt-line-soft)}
.wt-vis--todo li{border-left:3px solid var(--wt-primary);padding-left:.7rem}
.wt-cal{list-style:none;padding:0;margin:0 0 var(--spacing-med)}
.wt-cal__item{padding:.8rem 0;border-bottom:1px solid var(--wt-line-soft)}
.wt-cal__item--overdue{border-left:3px solid var(--wt-primary);padding-left:.7rem}
.wt-cal__item > p{margin:0 0 var(--spacing-2xs)}
.wt-cal__edit{margin:0 0 var(--spacing-xs)}
.wt-preview-mail{white-space:pre-wrap;font:inherit;background:var(--wt-card);border:1px solid var(--wt-line);padding:.8rem;border-radius:var(--radius-sm)}
.wt-cal__form{display:grid;gap:.35rem;max-width:22rem}
.wt-cal__form{margin:0 0 var(--spacing-xs)}
.wt-cal__inline{display:inline-block;margin:0 var(--spacing-xs) var(--spacing-xs) 0}
/* b-0740 — the two openings at the publish gate. Each option is a whole tappable
   row rather than a radio with a label beside it: the phone-first target rule is
   24px and a bare radio is not, and an owner reaching for the words rather than
   the dot must still hit the control. */
/* b-0737 — what the register saw. Plain, because it asks for nothing and a
   panel that looks like a warning would read as "your website has problems" on
   the screen that hands it over. */
.wt-findings{margin:0 0 var(--spacing-med)}
.wt-findings ul{margin:0 0 var(--spacing-2xs);padding-left:1.1rem}
.wt-findings li{padding:.15rem 0}
.wt-choice{border:1px solid var(--wt-line-soft);border-radius:var(--radius-sm);padding:var(--pad-card)}
.wt-choice{margin:0 0 var(--spacing-med)}
.wt-choice legend{padding:0 .4rem;font-weight:var(--weight-strong)}
.wt-choice__option{display:flex;gap:.7rem;align-items:flex-start;
 padding:.7rem .2rem;min-height:44px;cursor:pointer}
.wt-choice__option+.wt-choice__option{border-top:1px solid var(--wt-line-soft)}
/* b-1597 -- the box is the AA figure, as label.wt-check since b-0681: the 44px label was
   already the target, and a browser gate cannot tell that from an 18px control. */
.wt-choice__option input{margin-top:0;width:var(--target-min);height:var(--target-min);flex:none}
.wt-choice__option:has(+ p){margin-bottom:var(--spacing-xs)}
/* b-0591 — «Enséñasela a alguien». Zero JavaScript by ruling: an anchor and a
   read-only field, so there is no clipboard permission prompt and nothing to
   fail with scripts off. The field is full width because a hostname that wraps
   mid-word is a hostname an owner mistypes when they read it aloud. */
.wt-share{border:1px solid var(--wt-line-soft);border-radius:var(--radius-sm);padding:var(--pad-card)}
.wt-share{margin:0 0 var(--spacing-med)}
.wt-share__title{font-size:var(--text-body);margin:0 0 var(--spacing-xs)}
.wt-share__url{width:100%;font-family:ui-monospace,monospace;
 padding:.6rem;min-height:44px;border:1px solid var(--wt-line-soft);
 border-radius:var(--radius-sm);background:var(--wt-field)}
/* 🚨 SC 2.5.8's 24px, and the small variant broke it before it was measured:
   at .9rem the anchor computed to 22px on /panel. The target is sized here
   rather than inherited, so shrinking the type cannot shrink it again. */
.wt-share .wt-actions a{display:inline-block;min-height:44px;line-height:44px}
.wt-share .wt-actions{margin-bottom:var(--spacing-xs)}
/* Actually smaller, and it was not until it was looked at. The small variant
   started as a padding change, which at 390px is invisible: the repeat read as
   loud as the publish screen's, on the page an owner opens to EDIT. The type
   scale is what carries it. */
.wt-share--small{padding:.7rem .8rem;font-size:.9rem}
.wt-share--small .wt-share__title{font-size:.95rem;margin:0 0 var(--spacing-2xs)}
.wt-share--small .wt-share__url{padding:.45rem;font-size:.85rem}
/* The publishing state strip, under the header on every screen, is p.wt-publish
   further down; its first rule here was overridden in every property and went. */
.wt-publish .wt-hint{display:inline;margin:0}
/* The link out to the real website, and the honest state when there is none. */
/* 🚨 WCAG 2.1 SC 1.4.10 (Reflow) — b-0689. white-space:nowrap made this link
   unbreakable, and at a 32px root "Ver tu web (se abre en otra pestaña)" is
   429px: EVERY dashboard screen scrolled sideways, at 320px AND at 390px, the
   same 429, because an unbreakable box does not care how much room it has.
   The header already wraps; a nowrap child defeats that, which is why the
   defect survived a wrapping row.
   ⚠️ nowrap was there for a reason worth keeping — the parenthetical belongs on
   the same line as the link text at ordinary sizes — so it is CONDITIONAL now
   rather than deleted: hold the line while there is room, break when there is
   not. max-width caps the box; overflow-wrap is the last resort for a single
   long word, which the tenant's own domain can be.
   ⚠️ Found because b-0689's fourth criterion said to check this surface after
   fixing the front door. It was not obvious: this header wraps and its brand is
   text, so reasoning from the CSS said it was fine. Measuring said otherwise. */
/* b-0681 — the site link sits on the DEEP band now, so it inherits the bar's
   cream (8.75:1) rather than carrying the teal it needed on a light header. */
.wt-site{margin-left:auto;font-weight:var(--weight-strong);text-decoration:none;
 max-width:100%;overflow-wrap:anywhere;display:inline-flex;align-items:center;
 min-height:var(--target-size)}
.wt-site:hover{text-decoration:underline}
.wt-site--none{font-weight:var(--weight-body);opacity:.85}
/* b-1269 — it is a link now, so it carries a link's weight and no dimming. */
.wt-site--publish{font-weight:var(--weight-strong);opacity:1}
/*
 * b-1269 — zone 0. The one human gate in the business, above the row list.
 * The live line wraps anywhere because it carries a hostname the owner chose
 * and we do not control its length (b-0689's 429px lesson).
 */
/* Design 1.1 (Kyle, 29 Sep 2026) - the preview card. One centred box: the
   state as a badge, the site as the one filled link, the address under it,
   what that address means, then the way to publish. Both states wear it; the
   live one drops the last two. */
.wt-golive{padding:var(--spacing-large) var(--pad-card);border-radius:var(--radius-md);
 background:var(--wt-card);border:1px solid var(--wt-line);text-align:center;overflow-wrap:anywhere}
.wt-golive{margin:0 0 var(--spacing-med)}
.wt-golive p{margin:0 0 var(--spacing-stack)}
.wt-golive .wt-actions{justify-content:center;margin:0 0 var(--spacing-xs)}
.wt-golive .wt-golive__note{max-width:36rem;margin-inline:auto}
/* The state, as a badge. It is the card's heading, so the card is labelled by
   it; the dot is the strip's own, in the strip's two meanings. */
.wt-badge{display:inline-flex;align-items:center;gap:var(--spacing-xs);
 padding:var(--spacing-2xs) var(--spacing-small);border:1px solid var(--wt-ink);
 border-radius:var(--radius-pill);background:var(--wt-paper);color:var(--wt-ink);
 font-size:var(--text-small);letter-spacing:0}
.wt-badge--draft .wt-dot{background:var(--wt-ink-soft)}
/* Outlined, not filled, and NOT amber: the fill would need an on-primary
   foreground that is not a --wt-* token (every-link-has-a-brand-colour), and
   amber is one control per screen by b-0681, counted by
   one-amber-per-screen.test.ts. A bordered target at the link colour is
   prominent without borrowing either. */
.wt-golive__go{display:inline-flex;align-items:center;min-height:var(--target-min);
 padding:.65rem 1.1rem;border-radius:var(--radius-sm);color:var(--wt-primary-text);
 border:var(--border-rule) solid var(--wt-primary);background:transparent;
 font-weight:var(--weight-strong);text-decoration:none}
.wt-golive__go:hover{text-decoration:underline}
.wt-bar .wt-hint{opacity:.85}
/* b-0165 — the second link only takes the auto-margin once. */
.wt-site--edit{margin-left:var(--space-md)}
.wt-brand{font-family:var(--title-font);font-weight:var(--weight-strong);
 text-decoration:none;font-size:1.15rem;letter-spacing:-.01em;
 display:inline-flex;align-items:center;min-height:var(--target-min)}
/* b-0617 — WCAG 2.2 SC 2.5.8. These were 23px tall, measured at 390px, on the
   one control set that appears on every screen of a phone-first product for
   owners in their fifties and sixties. inline-flex, because min-height does
   nothing to an inline box. 24px is the AA figure; 44px is AAA and is a design
   decision rather than this fix. */
/* b-0617 — WCAG 2.2 SC 2.5.8 still applies: inline-flex, because min-height
   does nothing to an inline box. The bar carries it on its own links below. */
/* b-1684 — the footer sits on main's column; it had no padding and hugged x=0.
   The gutter since 30 Sep 2026, so it stays on main's edge at every width. */
footer.wt-foot{padding:0 var(--gutter)}
footer.wt-foot a{display:inline-flex;align-items:center;min-height:var(--target-min)}
/* The language switcher is INLINE in a sentence, so 2.5.8 exempts it — and a
   19x20px tap target is still a bad one for the audience this is built for.
   Padded rather than grown, so the line it sits in does not change height. */
.wt-locales a,.wt-locales strong{display:inline-flex;align-items:center;min-height:var(--target-min);
 padding:0 .2rem}

/* The shell: one reading column, every screen, every width. It was a two-track
   grid whose first track held the seventeen section pills (b-0681). */
.wt-shell{display:grid;grid-template-columns:1fr;gap:0;max-width:44rem;margin:0 auto}
main{padding:var(--spacing-med) var(--gutter);min-width:0;max-width:100%}
/* 🚨 WCAG 2.1 SC 1.4.10 (Reflow) — /privacidad scrolled the whole page sideways
   by 107px at 320px, and had done for as long as the page existed. Found by
   b-0529's gate on its first run; pa11y reads the DOCUMENT, not the layout, and
   had passed this page every time.
   THREE causes, and the first masked the second:
   1. main is a grid item sized by its content. min-width:0 was already set and
      is not enough — max-width:100% is what caps it to the track.
   2. .wt-scroll had NO CSS AT ALL. The markup has declared a scroll wrapper
      around the notice table since it was written, and nothing ever made it
      scroll, so a wide table pushed the page instead of itself.
   3. A 328px unbreakable filename in a code element. The Art. 13 notice points
      at internal documents by name on purpose — one list, not two — so the
      filenames stay and the wrapping gives.
   ⚠️ The table scrolls in its own box rather than being squeezed: SC 1.4.10
   exempts content that needs two-dimensional layout, and a legal basis table
   is that. What it does not permit is the PAGE scrolling. */
.wt-scroll{overflow-x:auto;max-width:100%}
/* ⚠️ NOT scoped to main any more (b-0689). /vista embeds the tenant's own
   preview markup as a sibling of main, so its hero h1 was outside this rule:
   at a 32px root "Servicios" alone is 327px and the page scrolled sideways by
   15px. A text-wrapping rule is safe document-wide, and scoping it to main was
   what made a whole page's worth of headings exempt without anyone choosing
   that. ⚠️ The PREVIEW's markup belongs to the tenant site; what is fixed here
   is how this dashboard presents it at dashboard width, which is ours. */
/* ⚠️ label, dt and summary are here because apps/signup's equivalent rule has
   always listed them and this one did not — /confirmar overflowed by 22px on a
   label (b-0689). The two surfaces carrying different lists was the bug. */
:is(p,li,dd,dt,h1,h2,h3,td,th,label,summary){overflow-wrap:break-word}
main code,main a{overflow-wrap:anywhere}
/* min(), because the more specific selector was silently undoing the cap on
   main above: (0,2,1) beats (0,0,1), so max-width went back to 44rem and the
   grid item was sized by its content again. 44rem is still the reading measure;
   100% is the floor that stops it exceeding the track. */
/* 🚨 width:100% is what makes the COLUMN stable, and without it the reading
   measure varied page to page. margin:auto on a grid item is shrink-to-fit, so
   a screen whose content happened to be narrow got a narrower column: measured
   at 1280px, /panel/textos was 704px wide and /panel/fotos 539px. Before b-0681
   this rule was scoped to the plain shell and the panel screens were the other
   track of a two-column grid, so they never took it.
   The visual pass asks for "the same screens on the same column"; a column that
   changes width between screens is not that. max-width still caps it and
   min-width:0 above still lets it give way, so nothing about reflow moves. */
.wt-shell main{width:100%;max-width:min(44rem,100%);margin:0 auto}

/* b-0681 — .wt-sections is GONE, and so is the two-column shell that held it.
   Seventeen pills above the content of every editing screen; the home is the
   section list and always was. The one page that still needs the list renders
   it as ul.wt-jump in its own body -- see sectionList. */
/* The back link. 44px, its own row, and the only thing above the h1 inside
   main. Arrow first, so the shape is the affordance before the words are read. */
.wt-back{margin:0 0 var(--spacing-xs)}
.wt-back a{display:inline-flex;align-items:center;gap:.35rem;min-height:var(--target-size);
 font-weight:var(--weight-strong);text-decoration:none}
.wt-back a:hover{text-decoration:underline}

/* b-0587 — what a chip that saved (or would not) says, on the one screen that
   saves without reloading. Both elements are created by FACT_SAVE_JS, so with
   scripts off neither exists and neither takes any space. The error sits inside
   the chips' flex row, so it needs a full basis or it lands beside the chip it
   is about. ⚠️ Never scope either to .wt-chips — chipSurfaceProblems refuses a
   selector starting .wt-chip*, which is how the shared sheet stays shared. */
/* b-0588 — what the browser made of the photograph the owner chose. Bounded
   the same way every other image here is; the note beneath it is a status
   line, not a hint, so it keeps the hint's size and loses its opacity. */
.wt-shrink__preview{display:block;width:100%;max-height:11rem;object-fit:cover;
 margin:0 0 var(--spacing-2xs);border-radius:var(--radius-sm);border:1px solid var(--wt-line)}
.wt-shrink{opacity:1;font-weight:var(--weight-strong)}
.wt-factsaid,.wt-factoops{margin:0 0 var(--spacing-xs);padding:var(--space-xs) var(--space-sm);font-size:.92rem}
.wt-factoops{flex:1 1 100%}
/* b-0424 — the sentence that says a rewrite REPLACES what is there. It is the
   one line on this page an owner must not skim, so it is coloured rather than
   set as ordinary body text. */
p.wt-warn{color:var(--wt-warn)}
/*
 * b-1193 - DE-EMPHASIS BY COLOUR, NEVER BY OPACITY, ON ANYTHING CONTAINING A LINK.
 *
 * axe, 10 Sep 2026, on /panel/textos, /panel/color, /panel/aviso and /recuperar:
 * #3c9098 on #f1e6d8 measured 3.02:1 where 4.5:1 is required. The link's OWN
 * colour is #007383 - 4.51:1 on this ground, and it PASSES. The opacity:.75 this
 * rule used to carry composited it down to 3.03:1.
 *
 * 🚨 A CHILD CANNOT UNDO A PARENT'S OPACITY. The first repair was
 * .wt-hint a{opacity:1} and it changed nothing: opacity is not an inherited
 * property, it composites the element and its whole subtree as one layer. The
 * only fix is not to dim the container.
 *
 * --wt-ink-soft is 5.30:1 on this ground and is what .wt-lede and
 * --fact-chip-label already use, so the hint keeps its quieter look and the
 * link keeps the graded colour a{} gives it.
 *
 * packages/ui's 6,144-pair sweep passed throughout and was right to: it grades
 * design TOKENS, axe measures RENDERED pixels, and an opacity lives between the
 * two. That gap is why color-contrast now gates - GATED_RULES in run-pa11y.ts.
 */
/* b-0365 — the row that was just saved, marked on the page itself. */
.wt-row--saved > form.wt-card{border-color:var(--wt-primary)}
.wt-row__saved{margin:0 0 var(--spacing-2xs);font-size:.85rem;font-weight:var(--weight-strong);color:var(--wt-primary-text)}
/* b-0564 — a third-party mark sits small beside its name, never as a hero. */
.wt-row-logo__preview{max-height:56px;width:auto;object-fit:contain;background:var(--wt-card)}
/* b-0176 — an empty language, on the line where the owner picks one. */
.wt-lang-empty{font-weight:var(--weight-body);opacity:.75;font-size:.85rem}
/* b-0177 — an empty section says what it is for, in the home's own words. */
.wt-empty{margin:0 0 var(--spacing-small)}
.wt-empty__what{margin:0 0 var(--spacing-2xs);font-weight:var(--weight-strong)}
.wt-empty .wt-hint{margin:0 0 var(--spacing-xs)}
ol.wt-history{list-style:none;padding:0;margin:0}
/* /papeles' documents and uploads: a plain stack, no bullets. */
ul.wt-vault{list-style:none;padding:0;margin:0}
ul.wt-vault:where(:not(:last-child)){margin-bottom:var(--spacing-med)}
ul.wt-vault > li{padding-block:var(--spacing-xs);border-bottom:var(--border-hairline) solid var(--wt-line-soft)}
ul.wt-vault > li:last-child{border-bottom:0}
.wt-locales a{color:var(--wt-primary-text)}
.wt-locales strong{border-bottom:2px solid var(--wt-primary)}
ol.wt-history li{border:1px solid var(--wt-line);border-radius:var(--radius-sm);background:var(--wt-card);
 padding:var(--space-sm) var(--pad-card);margin-bottom:var(--spacing-stack);display:flex;justify-content:space-between;
 align-items:center;gap:var(--space-md);flex-wrap:wrap}
form.wt-inline{display:inline}
/* b-1236 -- an author display beats the UA's [hidden], so the first row's
   «Antes» and the last row's «Después» were drawn despite the attribute. */
form.wt-inline[hidden]{display:none}
/* A form with a confirmation straight under it (an enquiry's "answered", a
   paper's expiry date, a team photo's upload) sat flush on it: the form owns
   the gap. An inline one becomes the block it already draws as, or the margin
   would not apply. */
form:has(+ details.wt-confirm){margin-bottom:var(--spacing-xs)}
form.wt-inline:has(+ details.wt-confirm){display:block}
ol.wt-leads{list-style:none;padding:0;margin:0}
ol.wt-leads li{border:1px solid var(--wt-line);border-radius:var(--radius-sm);background:var(--wt-card);
 padding:.85rem var(--pad-card);margin-bottom:var(--spacing-stack)}
ol.wt-leads .wt-lead__who{font-weight:var(--weight-strong)}
ol.wt-leads .wt-lead__msg{white-space:pre-wrap;margin:0 0 var(--spacing-stack)}
.wt-visits{font-weight:var(--weight-strong);border-left:3px solid var(--wt-primary);padding:.4rem .8rem}
.wt-visits{margin:0 0 var(--spacing-small)}
ol.wt-leads .wt-lead__actions{display:flex;flex-wrap:wrap;gap:.6rem;margin:0 0 var(--spacing-stack)}
ol.wt-leads .wt-lead__act{min-height:var(--target-min);display:inline-flex;align-items:center;padding:0 .9rem;
 border:1px solid var(--wt-line);border-radius:var(--radius-sm);text-decoration:none;font-weight:var(--weight-strong)}
/* b-1417 — the reservation book, read at a bar in the middle of a service: the
   day's requests first and framed, walk-in sizes as a grid of large buttons, one
   line per table with the time, party and name before any control. */
.wt-book__days{display:flex;justify-content:space-between;gap:var(--space-xs);flex-wrap:wrap;margin:0 0 var(--spacing-2xs)}
.wt-book__days a{display:inline-flex;align-items:center;min-height:2.75rem;font-weight:var(--weight-strong)}
/* The one title off the four tiers, and on purpose: the day is what a waiter
   reads at arm's length, so it sits between the page and section sizes. */
.big-title.wt-book__day{font-size:1.3rem}
.wt-book__day::first-letter{text-transform:uppercase}
.wt-book__today{font-size:.8rem;vertical-align:middle;padding:.1rem .5rem;border-radius:var(--radius-sm);
 background:var(--wt-primary-soft);color:var(--wt-primary-text);text-transform:none}
.wt-book__total{margin:0 0 var(--spacing-small);font-size:1.05rem}
.wt-book__total a{font-weight:var(--weight-strong);color:var(--wt-warn)}
.wt-book__count{display:inline-block;min-width:1.6rem;text-align:center;padding:.05rem .45rem;border-radius:var(--radius-sm);
 background:var(--wt-warn-fill);color:var(--wt-on-warn);font-size:var(--text-detail)}
.wt-book__elsewhere{border:1px solid var(--wt-line);border-radius:var(--radius-sm);padding:var(--space-sm) var(--pad-card) var(--space-xs)}
.wt-book__elsewhere{margin:0 0 var(--spacing-small)}
.wt-book__elsewhere p{margin:0 0 var(--spacing-2xs)}
.wt-book__elsewhere ul{margin:0;padding:0;list-style:none}
.wt-book__elsewhere li{display:flex;align-items:center;justify-content:space-between;gap:var(--space-xs);min-height:2.75rem}
.wt-book__pending{border:2px solid var(--wt-warn);border-radius:var(--radius-md);padding:var(--space-sm) var(--space-sm) var(--space-xs)}
.wt-book__pending{margin:0 0 var(--spacing-med)}
.wt-book__pending ol.wt-leads li{border-color:var(--wt-line)}
/* b-1451 — the callbacks block: beside the book, never inside it. */
.wt-book__callbacks{border:1px solid var(--wt-line);border-radius:var(--radius-md);padding:var(--space-sm) var(--space-sm) var(--space-xs)}
.wt-book__callbacks{margin:0 0 var(--spacing-med)}
.wt-book__callbacks .wt-hint{margin:0 0 var(--spacing-xs)}
/* b-1423 — «mesa libre ahora»: one big thumb target, the first thing on the screen. */
.wt-tablefree{margin:0 0 var(--spacing-med)}
.wt-tablefree button{width:100%;min-height:3.5rem;font-size:1.15rem;font-weight:var(--weight-strong);
 margin:0 0 var(--spacing-xs)}
.wt-book__sizes{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-xs)}
.wt-book__sizes form{margin:0}
.wt-book__sizes button{width:100%;min-height:3.5rem;font-size:1.5rem;font-weight:var(--weight-strong);padding:0}
.wt-book__slots{list-style:none;padding:0;margin:0 0 var(--spacing-large)}
.wt-book__slots > li{margin:0 0 var(--spacing-med)}
.wt-book__slot{display:flex;justify-content:space-between;align-items:baseline;gap:var(--space-xs);
 padding:0 0 .3rem;border-bottom:2px solid var(--wt-line)}
.wt-book__slot span:last-child{font-size:.95rem;color:var(--wt-ink-soft)}
.wt-book__slot--full span:last-child{color:var(--wt-warn)}
/* A booking's lines sit one xs apart; the row's own padding closes it. */
.wt-book__line{display:flex;align-items:baseline;gap:.35rem .75rem;flex-wrap:wrap}
.wt-book__line,.wt-book__tags,.wt-book__suggested,.wt-book__memory,.wt-book__group,
 .wt-book__offered,.wt-book__controls{margin:0 0 var(--spacing-xs)}
.wt-book__time{font-size:1.25rem;font-weight:var(--weight-strong);font-variant-numeric:tabular-nums}
.wt-book__party{font-size:1.1rem;font-weight:var(--weight-strong)}
.wt-book__name{font-size:1.05rem}
.wt-book__tags{display:flex;flex-wrap:wrap;gap:.35rem}
.wt-book__tag{font-size:var(--text-small);padding:.1rem .5rem;border-radius:var(--radius-sm);border:1px solid var(--wt-line)}
.wt-book__tag--confirmed{background:var(--wt-primary-soft);color:var(--wt-primary-text);border-color:transparent}
.wt-book__tag--requested{color:var(--wt-warn);border-color:var(--wt-warn)}
.wt-book__tag--deposit-received,.wt-book__tag--deposit-waived{color:var(--wt-ink-soft)}
.wt-book__tag--deposit-asked{color:var(--wt-warn);border-color:var(--wt-warn)}
.wt-book__suggested{font-weight:var(--weight-strong)}
.wt-book__memory{padding:var(--space-xs) var(--space-sm);background:var(--wt-card);border:1px solid var(--wt-line);border-radius:var(--radius-sm)}
.wt-book__memory p{margin:0}
li.wt-book__declined{opacity:.7}
li.wt-book__declined .wt-book__name{text-decoration:line-through}
.wt-book__controls{display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--space-xs)}
form.wt-book__deposit{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2xs) var(--space-xs);margin:0;flex:1 1 100%;min-width:0}
form.wt-book__deposit label{margin:0;font-size:var(--text-detail);flex:1 0 100%}
form.wt-book__deposit select{flex:1 1 0;min-width:0;width:auto;min-height:2.75rem}
form.wt-book__deposit button,details.wt-book__move button{min-height:2.75rem}
details.wt-book__move{flex:1 1 100%}
/* The disclosure primitive, drawn as an outlined control. */
details.wt-book__move>summary{display:inline-flex;min-height:2.75rem;padding:0 .9rem;
 border:1px solid var(--wt-line);border-radius:var(--radius-sm);margin:0 0 var(--spacing-xs)}
.wt-book__pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-xs)}
.wt-book__pair label{margin:0 0 var(--spacing-xs)}
.wt-book__pending .wt-actions{margin-bottom:var(--spacing-xs)}
.wt-book__pending .wt-actions button{min-height:2.75rem;min-width:7rem}
/* b-1404 — table bills: the table and the total read at arm's length, Pagado the
   larger of the two buttons. The QR sheet prints one code per cell, the chrome hidden. */
.wt-bill__bizum{font-weight:var(--weight-strong);margin:0 0 var(--spacing-small)}
form.wt-bill__new textarea{min-height:6rem}
ol.wt-bills{list-style:none;padding:0;margin:0}
li.wt-bill{background:var(--wt-card);border:1px solid var(--wt-line);border-radius:var(--radius-md);padding:var(--space-sm) var(--pad-card)}
.wt-bill{margin:0 0 var(--spacing-stack)}
.wt-bill__head{display:flex;justify-content:space-between;align-items:baseline;gap:var(--space-xs);flex-wrap:wrap}
.wt-bill__head{margin:0 0 var(--spacing-2xs)}
.wt-bill__table{font-size:1.25rem;font-weight:var(--weight-strong)}
.wt-bill__total{font-size:1.5rem;font-weight:var(--weight-strong);font-variant-numeric:tabular-nums}
li.wt-bill .wt-hint{margin:0 0 var(--spacing-xs)}
ul.wt-bill__items{margin:0 0 var(--spacing-xs);padding-left:1.1rem}
.wt-bill__actions{display:grid;grid-template-columns:2fr 1fr;gap:var(--space-xs)}
.wt-bill__actions form{margin:0}
.wt-bill__actions button{width:100%;min-height:3rem}
ul.wt-bills__closed{list-style:none;padding:0;margin:0}
li.wt-bill__closed{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2xs) var(--space-sm);padding:var(--space-xs) 0;border-bottom:1px solid var(--wt-line-soft)}
li.wt-bill__closed .wt-bill__table{font-size:var(--text-body)}
li.wt-bill__closed--cancelled{opacity:.7}
ol.wt-qr{list-style:none;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(9.5rem,1fr));gap:var(--space-sm)}
.wt-qr{margin:0 0 var(--spacing-med)}
li.wt-qr__cell{background:var(--wt-card);border:1px dashed var(--wt-line);border-radius:var(--radius-sm);padding:var(--space-xs);text-align:center;break-inside:avoid;page-break-inside:avoid}
li.wt-qr__cell svg{display:block;width:100%;height:auto;max-width:12rem;margin:0 auto var(--spacing-2xs)}
.wt-qr__table{font-size:1.35rem;font-weight:var(--weight-strong);margin:0 0 var(--spacing-2xs)}
.wt-qr__scan{font-size:.8rem;line-height:1.3;margin:0 0 var(--spacing-2xs)}
@media print{
 body:has(ol.wt-qr){background:none}
 body:has(ol.wt-qr)>*:not(.wt-shell),body:has(ol.wt-qr) main>*:not(ol.wt-qr),body:has(ol.wt-qr) .wt-foot{display:none}
 body:has(ol.wt-qr) .wt-shell,body:has(ol.wt-qr) main{max-width:none;padding:0}
 ol.wt-qr{grid-template-columns:repeat(3,1fr);gap:0;margin:0}
 li.wt-qr__cell{background:none;border-radius:0;padding:.4rem .4rem .6rem}
 li.wt-qr__cell svg{max-width:5cm}
}
/*
  b-1452 — the reading translation. It is a SCREEN and never a file, so the one
  way it leaves the dashboard is the browser's own print. These rules are the
  watermark the criterion asks for: the three warnings repeat at the head of
  every printed page: position:fixed in paged media repeats per page, and the
  document under them is indented clear of it.
*/
.wt-reading__warn{border:2px solid var(--wt-warn);border-radius:var(--radius-sm);padding:.85rem var(--pad-card) var(--space-xs);color:var(--wt-warn)}
.wt-reading__warn{margin:0 0 var(--spacing-small)}
.wt-reading__warn p{margin:0 0 var(--spacing-2xs)}
.wt-reading{margin:0 0 var(--spacing-large)}
.wt-reading dt{font-weight:var(--weight-strong)}
.wt-reading dd{margin:0 0 var(--spacing-xs)}
/* The results screen's counts: each label-and-number pair a tight step from
   the next, so four pairs do not read as eight unbroken lines. */
dl.wt-outcomes > div{margin:0 0 var(--spacing-xs)}
@media print{
 body:has(.wt-reading) .wt-reading__warn{position:fixed;top:0;left:0;right:0;background:#fff;border:0;border-bottom:2px solid #000;color:#000;font-weight:var(--weight-strong);margin:0;padding:.25cm 0}
 body:has(.wt-reading) .wt-reading{margin-top:3cm}
 body:has(.wt-reading) .wt-foot,body:has(.wt-reading) nav{display:none}
 @page{margin:2.5cm 1.5cm 1.5cm}
}
form.wt-book__jump{display:flex;align-items:flex-end;gap:var(--space-xs);flex-wrap:wrap}
form.wt-book__jump label{flex:1 0 100%;margin:0}
form.wt-book__jump input{flex:1 1 10rem;width:auto}
form.wt-book__jump button{min-height:2.75rem}
.wt-book__exports{flex-direction:column;align-items:flex-start}
.wt-book__exports a{display:inline-flex;align-items:center;min-height:2.75rem}
/* b-1597 — the add-on screens' standalone links, found under 24px by their first sweep. */
.wt-downloads a,a.wt-card-link{display:inline-flex;align-items:center;min-height:2.75rem}
/* b-1425 — a group request: the occasion and budget under the line, the offer framed as its own step. */
.wt-book__group{font-weight:var(--weight-strong)}
.wt-book__offered{color:var(--wt-ink-soft)}
.wt-book__offer{border:1px solid var(--wt-line);border-radius:var(--radius-sm);padding:var(--space-sm) var(--space-sm) .6rem}
.wt-book__offer{margin:0 0 var(--spacing-xs)}
.wt-book__offer select,.wt-book__offer input{min-height:2.75rem}
.wt-book__offer--missing p{margin:0 0 var(--spacing-xs)}
.wt-book__offer--missing a{display:inline-flex;align-items:center;min-height:2.75rem;font-weight:var(--weight-strong)}
fieldset.wt-groupmenu{min-width:0}
.wt-groupmenu{margin:0 0 var(--spacing-small)}
fieldset.wt-groupmenu legend{font-weight:var(--weight-strong);padding:0 var(--space-2xs)}
details.wt-allergens>summary{padding:.55rem 0}
details.wt-allergens>.wt-hint{font-size:inherit;color:inherit;margin:0 0 var(--spacing-xs)}
/* b-0541, b-0571 — the facts are CHIPS and the whole chip is the button, and
   since b-0571 the sheet that draws them is packages/content/src/fact-chips.ts,
   shared verbatim with the sign-up. A row of label + Quitar/Añadir was ~44px of
   vertical per fact — 2,433px of screen at 51 facts — and the old collapse rule
   folded a group with no tick to «18 cosas», a price rather than a preview.
   ⚠️ GEOMETRY arrives with the sheet; the seven below map its colour to this
   surface. Do not restyle .wt-chip here — fact-chip-parity.test.ts fails, and
   the point is that the owner meets one screen twice, not two screens once. */
:root{--fact-chip-surface:var(--wt-card);--fact-chip-ink:var(--wt-ink);
 --fact-chip-line:var(--wt-line);--fact-chip-on:var(--wt-primary);
 --fact-chip-on-ink:var(--on-primary);--fact-chip-on-weight:var(--weight-strong);
 --fact-chip-more:var(--wt-primary-text);
 --fact-chip-label:var(--wt-ink-soft)}

.wt-chips{display:flex;flex-wrap:wrap;gap:.5rem}
.wt-chipform{display:inline-flex;margin:0}
.wt-chip{margin:0;min-height:44px;padding:.55rem .95rem;border-radius:999px;
 background:var(--fact-chip-surface);color:var(--fact-chip-ink);
 border:1px solid var(--fact-chip-line);font:inherit;font-size:.92rem;line-height:1.25;
 text-align:left;overflow-wrap:anywhere;cursor:pointer;display:inline-flex;
 flex-wrap:wrap;align-items:flex-start;align-content:center;width:auto;max-width:100%}
.wt-chip--on,.wt-chipbox:checked+.wt-chip{background:var(--fact-chip-on);
 color:var(--fact-chip-on-ink);border-color:var(--fact-chip-on);
 font-weight:var(--fact-chip-on-weight)}
.wt-chip:disabled{opacity:.45;cursor:not-allowed}
.wt-chip-mark{margin-right:.4rem;display:none}
.wt-chip--on .wt-chip-mark,.wt-chipbox:checked+.wt-chip .wt-chip-mark{display:inline}
.wt-chipbox{position:absolute;width:1px;height:1px;margin:0;padding:0;overflow:hidden;
 clip:rect(0 0 0 0);white-space:nowrap;border:0}
.wt-chipbox:focus-visible+.wt-chip{outline:2px solid var(--fact-chip-on);outline-offset:2px}
.wt-chip-because{flex:1 0 100%;font-size:.82rem;font-weight:400}
.wt-chip:has(.wt-chip-because){display:inline-block}
.wt-chip:has(.wt-chip-because) .wt-chip-because{display:block}
details.wt-chipmore{margin:.6rem 0 0}
details.wt-chipmore>summary{min-height:44px;display:inline-flex;align-items:center;
 cursor:pointer;color:var(--fact-chip-more);font-weight:var(--fact-chip-on-weight)}
details.wt-chipmore[open]{display:none}
.wt-extra{display:contents}
.wt-factgroup:has(> details.wt-chipmore:not([open])) .wt-extra{display:none}
.wt-factsearch{display:flex;flex-wrap:wrap;gap:.5rem;align-items:end;margin:.75rem 0 0}
.wt-factsearch label{flex:1 1 100%;margin:0 0 .1rem;font-size:.86rem;color:var(--fact-chip-label)}
.wt-factsearch input{flex:1 1 12rem;min-height:44px;margin:0;-webkit-appearance:none;appearance:none}
.wt-factsearch input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}
.wt-factsearch button{margin:0}
.wt-factgroup{margin:0 0 2rem;padding:0}
.wt-factgroup>:is(h2,h3){margin:0 0 .6rem;font-size:1rem;color:var(--fact-chip-ink)}
.wt-factresultlabel{margin:.6rem 0 .35rem;font-size:.86rem;color:var(--fact-chip-label)}
.wt-chips.wt-factresults{margin-top:0}
.wt-chips.wt-factlive{margin-top:.6rem}
.wt-factlive[hidden],.wt-factmiss[hidden]{display:none}
.wt-factmiss{margin:.25rem 0 0;color:var(--fact-chip-more)}

/* b-0170 — the per-day row: the box, then the two buttons that rewrite it.
   The buttons stay 44px tall on a phone; this is the most-tapped form here. */
/* b-0318 — the read-from-a-photo carta, before it is anybody's content. Each
   dish is one row and one form; the price sits beside the name because a menu
   photographed two summers ago has the right dishes at the wrong prices, and
   that is the thing the owner is being asked to catch. */
ul.wt-proposedlist{list-style:none;padding:0}
.wt-proposedlist{margin:0 0 var(--spacing-small)}
li.wt-proposed{border-bottom:1px solid var(--wt-line);padding:.6rem 0;display:flex;
 flex-wrap:wrap;align-items:baseline;gap:.4rem .75rem}
li.wt-proposed:last-child{border-bottom:0}
.wt-proposed__dish{font-weight:var(--weight-strong)}
.wt-proposed__price{font-variant-numeric:tabular-nums}
.wt-proposed__group{color:var(--wt-primary-text);font-size:.85rem}
/* b-1271 — what the page says once this is in. Quiet: it is evidence for the
   decision, never a second thing to read. */
.wt-preview{margin:0 0 var(--spacing-xs);padding:.6rem .8rem;border-left:3px solid var(--wt-line);
 background:var(--wt-primary-soft);border-radius:var(--radius-sm)}
.wt-preview__what{margin:0 0 var(--spacing-2xs);font-size:.85rem;color:var(--wt-ink-soft)}
.wt-preview__where{margin:0 0 var(--spacing-2xs);font-weight:var(--weight-strong);font-size:var(--text-detail)}
.wt-preview__rows{margin:0;padding-left:1.1rem}
.wt-declined{margin:0 0 var(--spacing-small);border-top:1px solid var(--wt-line-soft);padding-top:.6rem}
.wt-declined summary{min-height:var(--target-min);display:flex;align-items:center;cursor:pointer}
.wt-proposed__what{flex:1 1 100%;color:var(--wt-ink);font-size:var(--text-detail)}
li.wt-proposed form{margin-left:auto}
li.wt-proposed button{margin:0;min-height:44px;font-size:var(--text-detail);padding:.4rem .8rem}
.wt-hours-day{border-bottom:1px solid var(--wt-line);padding-bottom:var(--space-xs);margin-bottom:var(--spacing-xs)}
.wt-hours-day:last-of-type{border-bottom:0}
.wt-kitchen{border:0;border-top:1px solid var(--wt-line);padding:var(--space-sm) 0 0;min-width:0}
.wt-kitchen{margin:0 0 var(--spacing-small)}
.wt-kitchen>legend{padding:0;float:left;width:100%}
.wt-kitchen>legend+*{clear:both}
.wt-kitchen textarea{min-height:0;height:auto}
.wt-kitchen>.wt-hint{margin-bottom:var(--spacing-xs)}
.wt-hours-actions{display:flex;gap:var(--space-xs);flex-wrap:wrap;align-items:flex-start;margin:0}
.wt-hours-day .wt-field{margin-bottom:var(--spacing-xs)}
p.wt-hours-actions button{min-height:44px;font-size:var(--text-detail);padding:.4rem .8rem}
/* The echo. Deliberately a LIST rather than a table: one column of labels and
   one of values reads the same on a 320px screen and needs no header row. */
ul.wt-hours-echo{list-style:none;padding:0}
.wt-hours-echo{margin:0 0 var(--spacing-small)}
ul.wt-hours-echo li{display:flex;justify-content:space-between;gap:var(--space-md);
 padding:.35rem 0;border-bottom:1px solid var(--wt-line)}
ul.wt-hours-echo li:last-child{border-bottom:0}
.wt-hours-value{font-variant-numeric:tabular-nums;text-align:right}
form.wt-add{border-style:dashed;background:transparent}
/* b-0681 — the BOX is 24px now, not 22. The wrapping label is 44px and is the
   real target, so SC 2.5.8 was satisfied either way; what 22 cost was a browser
   gate that could not tell a compliant control from a non-compliant one without
   knowing about label wrapping. The token is the AA figure. */
label.wt-check input{width:auto;min-width:var(--target-min);min-height:var(--target-min);flex:0 0 auto}
/* b-1242 -- native radios and checkboxes take the palette, not the browser's blue. */
input[type=radio],input[type=checkbox]{accent-color:var(--wt-primary)}
/* 🚨 b-0582 — the consent sentence NAMES THE DOMAIN, and a domain is one
   unbreakable token. servicioscosta.net measured 244px at a 32px root and
   pushed /dominio/nuevo 53px sideways at 320px (SC 1.4.10, Reflow). The label
   is a flex row, so the text child needs its own min-width:0 before
   overflow-wrap can act on it. Scoped to the buy form: nothing else on this
   surface puts a hostname inside a sentence. */
form.wt-domain__buy label.wt-check{align-items:flex-start}
form.wt-domain__buy label.wt-check span{min-width:0;overflow-wrap:anywhere}
/* Fourteen allergen boxes on a phone (b-0428). Two columns where there is room,
   one where there is not — and each row keeps its own 44px target either way. */
/* 🚨 A fieldset's default min-width is min-content, and it OVERRIDES width and
   max-width — the long-standing UA quirk. At a 32px root the allergen labels
   made /panel/carta 333px wide in a 320px viewport (b-0689). min-width:0 is the
   whole fix and it is safe: nothing here wants a fieldset wider than its box. */
fieldset{min-width:0}
fieldset.wt-checkgroup{border:1px solid var(--wt-line-soft);border-radius:var(--radius-md);
 margin:0 0 var(--spacing-2xs);padding:.4rem .8rem .8rem}
fieldset.wt-checkgroup legend{font-weight:var(--weight-strong);padding:0 .3rem}
fieldset.wt-checkgroup label.wt-check{font-weight:var(--weight-body)}
@media (min-width:640px){
 fieldset.wt-checkgroup{display:grid;grid-template-columns:1fr 1fr;column-gap:var(--space-md)}
 fieldset.wt-checkgroup legend{grid-column:1 / -1}
 /* b-1318 — a 1–7 scale reads down, never in two columns. */
 fieldset.wt-checkgroup.wt-scale{display:block}
}
/* b-1319 — the one line after a refusal: quiet, below everything else. */
form.wt-ask{margin:0 0 var(--spacing-large);max-width:36rem}
/* b-1332 — a prepared change: what it is, now, and after. */
dl.wt-change{display:grid;grid-template-columns:auto 1fr;gap:.35rem 1rem}
.wt-change{margin:0 0 var(--spacing-small)}
dl.wt-change dt{font-weight:var(--weight-strong)}
dl.wt-change dd{margin:0}
dl.wt-change del{color:var(--wt-ink-soft)}
dl.wt-change ins{text-decoration:none;font-weight:var(--weight-strong)}
/* b-1374 — the owner's own section, before and after, side by side at phone width. */
.wt-shotpair{margin:0 0 var(--spacing-large)}
.wt-shotpair__frames{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-sm);align-items:start}
.wt-shotpair__frame{margin:0;min-width:0}
.wt-shotpair__frame figcaption{font-weight:var(--weight-strong);margin-bottom:var(--spacing-2xs)}
.wt-shotpair__frame a{display:block}
.wt-shotpair__frame img{width:100%;height:auto;border:1px solid var(--wt-line);border-radius:var(--radius-sm);background:var(--wt-card)}
/* b-1577 — the chat. A list of turns, the owner's aligned with the page and the
   assistant's on a card, so a phone reader can tell them apart without colour. */
.wt-chat{margin:0 0 var(--spacing-large)}
ol.wt-chat__turns{list-style:none;padding:0;margin:0;display:grid;gap:var(--spacing-stack)}
.wt-chat__turn{margin:0;padding:var(--space-sm);border-radius:var(--radius-sm);border:1px solid var(--wt-line)}
.wt-chat__turn p{margin:0 0 var(--spacing-2xs)}
.wt-chat__shot{display:block;max-width:100%;height:auto;border:1px solid var(--wt-line);border-radius:var(--radius-sm)}
.wt-chat__turn p:last-child{margin-bottom:0}
.wt-chat__turn--answer{background:var(--wt-card)}
/* b-1579 — the three questions, above the box. One form each, so a tap is a submit. */
.wt-asks{margin:0 0 var(--spacing-med)}
.wt-asks__one{margin:0 0 var(--spacing-xs)}
.wt-asks__one button{width:100%;text-align:left}
/* b-1246 — from 1024px the phone column stops being the whole screen. The
   home's rows and a collection's cards take two tracks; everything else keeps
   its column. Below the breakpoint nothing here applies, so the 390px page the
   design pass measured is untouched (owner-browser-facts reads it at 390). */
@media (min-width:1024px){
 .wt-shell{max-width:72rem}
 .wt-shell main{max-width:min(72rem,100%)}
 ol.wt-home{grid-template-columns:1fr 1fr}
 main :is(section, .wt-rows):has(> .wt-row){display:grid;grid-template-columns:1fr 1fr;gap:0 1.5rem;align-items:start}
 main :is(section, .wt-rows):has(> .wt-row) > :not(.wt-row){grid-column:1 / -1}
}
ul.wt-jump{list-style:none;display:flex;flex-wrap:wrap;gap:.5rem .9rem;padding:0}
.wt-jump{margin:0 0 var(--spacing-small)}
ul.wt-jump a{color:var(--wt-primary-text);display:inline-flex;align-items:center;min-height:24px}
/* 🚨 min(), because a minmax FLOOR larger than the container overflows it —
   9rem is 288px at a 32px root and the column then cannot fit a 320px screen
   (b-0689, measured 328px). min(9rem,100%) keeps the intended column width and
   lets it give way when there is less room than that. Standard idiom, and the
   defect it fixes is invisible at the default text size. */
/* b-0713 — the style gallery.
   ⚠️ aspect-ratio, with the intrinsic width/height on the img, reserves the
   box BEFORE the lazy image arrives, so a cell of 54 cards does not reflow the
   page as they load. Without it, lazy loading trades page weight for layout
   shift and an owner taps the wrong card. */
.wt-style-gallery{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(10rem,100%),1fr));
 gap:var(--space-sm);margin:0 0 var(--spacing-small)}
.wt-style-card{margin:0;border:var(--border-rule) solid;border-radius:var(--radius-md);
 overflow:hidden;display:flex;flex-direction:column}
.wt-style-card img{width:100%;height:auto;aspect-ratio:390/650;object-fit:cover;
 object-position:top center;display:block;background:var(--surface-alt)}
.wt-style-card .wt-actions{margin:0;padding:var(--space-xs)}
.wt-style-card button{width:100%}
/* The one they are wearing reads as chosen rather than as an option. */
.wt-style-card--current{border-width:2px;border-color:var(--wt-primary,currentColor)}
.wt-style-card__current{margin:0;padding:var(--space-xs);text-align:center;font-weight:var(--weight-strong)}
.wt-swatches{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));
 gap:.6rem;margin:0 0 var(--spacing-small)}
form.wt-swatch{margin:0}
form.wt-swatch button{width:100%;max-width:100%;min-width:0;min-height:5rem;display:flex;flex-direction:column;
 align-items:flex-start;justify-content:space-between;gap:var(--space-xs);padding:.7rem .8rem;
 border:var(--border-rule) solid;border-radius:var(--radius-md);text-align:left}
form.wt-swatch button[aria-current="true"]{outline:3px solid var(--wt-ink);outline-offset:2px}
.wt-swatch__marks{display:flex;gap:.3rem}
.wt-swatch__marks i{width:1.1rem;height:1.1rem;border-radius:50%;display:block}
.wt-swatch__label{font-size:.8rem;font-weight:var(--weight-strong)}
/* b-1270 - the state under the name. Not a second label: it is one line of the
   same span, so the button's accessible name reads "Mostaza, el que tienes
   ahora" and the six buttons stay distinguishable by ear. */
.wt-swatch__note{display:block;font-weight:var(--weight-body);font-size:var(--text-eyebrow);opacity:.85}
/* b-0673 — the logo chooser reuses the swatch button wholesale and adds only a
   preview slot, because the two options ARE swatches: one tap, one save, the
   live answer marked with aria-current. object-fit:contain in a fixed box is
   what stops an acquired logo of any aspect ratio deciding the row height —
   b-0670 measured one at 1255x776. */
.wt-logo-option__preview{display:flex;align-items:center;justify-content:flex-start;
 width:100%;height:3rem;overflow:hidden}
.wt-logo-option__image{max-width:100%;max-height:100%;object-fit:contain;object-position:left}
.wt-logo-option__wordmark{font-family:var(--font-display,inherit);font-size:1.1rem;
 font-weight:var(--weight-strong);line-height:1.2;overflow-wrap:anywhere}
input[type="color"]{height:3rem;padding:.2rem}

/* ===== THE HOME (b-0682) ===================================================
   Sixteen visually identical bordered cards, each ending in the same verb,
   became list rows under three cadence bands. A card says "this is a thing on
   its own"; sixteen of them say nothing is more important than anything else,
   which is the opposite of what cadence.ts knows.
   The row is the link and the CHEVRON is the affordance.
   The verb is not named in this comment: until b-1633 this sheet was inlined
   into every page as text, and naming it failed home.test.ts on its own
   explanation. */
/* The cadence bands are eyebrows; each list closes one med step above the next. */
/* Design 1.1 (Kyle, 29 Sep 2026) - each row is a light card, a stack step
   from the next, still grouped under the cadence bands; the bands are what
   keep sixteen of them from reading as equal weight. The whole card is the
   link, the min-height is the design's 56px, and the list items stretch so
   two cards side by side match heights. */
ol.wt-home{list-style:none;padding:0;display:grid;gap:var(--spacing-stack)}
.wt-home{margin:0 0 var(--spacing-med)}
ol.wt-home li{display:flex;min-width:0}
ol.wt-home li > *{flex:1 1 auto;min-width:0}
ol.wt-home a{display:flex;flex-wrap:wrap;align-items:baseline;gap:.15rem .7rem;
 padding:var(--spacing-stack) var(--pad-card);min-height:56px;text-decoration:none;color:var(--wt-ink);
 background:var(--wt-card);border:1px solid var(--wt-line-soft);border-radius:var(--radius-md)}
ol.wt-home a:hover{border-color:var(--wt-line)}
/* The chevron replaces sixteen repetitions of one word. aria-hidden in the
   markup: it is the shape of "there is more here", and a screen reader already
   has the link. */
.wt-home__go{margin-left:auto;color:var(--wt-primary-text);font-size:1.2rem;line-height:1}
.wt-home__label{flex:1 1 100%;font-weight:var(--weight-strong)}
/* overflow-wrap, so a pasted delivery URL cannot push a row wider than a phone. */
.wt-home__value{flex:1 1 12rem;min-width:0;color:var(--wt-ink-soft);overflow-wrap:anywhere}
/* The empty-state verb, and ONLY the empty state. A filled row says nothing
   here -- that repetition is the defect. An empty one says «Añade tus fotos»,
   which is information and is b-0159's completeness signal falling out of the
   content rather than being a meter. */
.wt-home__do{margin-left:auto;color:var(--wt-primary-text);font-weight:var(--weight-strong);
 white-space:nowrap;font-size:var(--text-detail)}
/* 🚨 Zone 2 -- what needs you, and it is ABSENT most days. Never a checklist:
   if this ever grows a percentage or a progress bar it has become the thing the
   12 Aug review rejected. Card-weight on purpose -- it is the one thing on the
   page that is an interruption rather than a list. */
section.wt-needs{padding:var(--pad-card) var(--pad-card) var(--space-xs);
 border:1px solid var(--wt-line);border-radius:var(--radius-md);background:var(--wt-card)}
.wt-needs{margin:0 0 var(--spacing-med)}
section.wt-needs ul{list-style:none;margin:0;padding:0}
section.wt-needs li+li{border-top:1px solid var(--wt-line-soft)}
/* b-1240 -- a grid, so the two text lines stack in the first column and the
   section rows' chevron sits beside both. It was the one link on the page
   with no affordance, and it is the one that asks for action. */
section.wt-needs a{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:.7rem;
 row-gap:.1rem;padding:.6rem 0;min-height:var(--target-size);align-content:center;
 text-decoration:none;color:var(--wt-ink)}
section.wt-needs .wt-home__go{grid-column:2;grid-row:1/3;align-self:center;margin:0}
.wt-needs__what{font-weight:var(--weight-strong)}
.wt-needs__more{font-size:.85rem;color:var(--wt-ink-soft)}
/* Zone 4 -- one line, and it is the front door to /historial. */
p.wt-lastchange{font-size:var(--text-detail);color:var(--wt-ink-soft)}
/* b-0610 — the gallery row of the home, as photographs. The sentence they
   replace is still in the markup and still read aloud; clip rather than
   display:none, which would take it out of the accessibility tree too. */
/* b-0689 — a row of fixed-rem thumbnails is as unbreakable as a nowrap link:
   at a 32px root it overran a 320px screen by 3px. Wrapping costs nothing here
   (they are a summary, not a sequence) and removes the whole class. */
.wt-home__thumbs{display:inline-flex;flex-wrap:wrap;gap:.3rem;vertical-align:-.6rem;max-width:100%}
.wt-home__thumb{width:2.4rem;height:2.4rem;object-fit:cover;border-radius:var(--radius-sm);
 border:1px solid var(--wt-line)}
/* b-0681 — the same clip, unscoped, because a second caller arrived: the bar's
   "(se abre en otra pestaña)". One rule and two names rather than two rules --
   wt-home__said keeps its name because b-0610's comment above explains it. */
/* b-1242 — the file control's native input joins the clip once a script runs. */
.wt-home__said,.wt-said,.wt-file--js input[type="file"]{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
 white-space:nowrap;border:0}
.wt-photo__actions{margin:0 0 var(--spacing-small);padding:0}
/* ===== THE PHOTO INDEX (b-0683) ============================================
   3,748px, 40 inputs and 17 buttons became a list of rows. The row is a link
   into the photograph's own screen; the two reorder forms stay beside it,
   because reordering is a comparison and needs both photographs on screen. */
ol.wt-photos{list-style:none;padding:0}
.wt-photos{margin:0 0 var(--spacing-small)}
ol.wt-photos li{border-bottom:1px solid var(--wt-line-soft);display:flex;
 align-items:center;gap:var(--space-xs);flex-wrap:wrap}
ol.wt-photos li:last-child{border-bottom:0}
.wt-photos__row{flex:1 1 12rem;display:flex;align-items:center;gap:var(--space-sm);
 min-height:64px;padding:var(--space-xs) var(--space-2xs);text-decoration:none;color:var(--wt-ink)}
.wt-photos__row:hover{background:var(--wt-card)}
.wt-photos__thumb{flex:0 0 auto;width:3.5rem;height:3.5rem;object-fit:cover;display:block;
 border-radius:var(--radius-sm);border:1px solid var(--wt-line);background:var(--wt-paper)}
.wt-photos__text{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:.1rem}
/* The badge is the main photo's row, said in words -- never a colour alone. */
.wt-photos__badge{font-size:.75rem;font-weight:var(--weight-strong);letter-spacing:.06em;
 text-transform:uppercase;color:var(--wt-primary-text)}
.wt-photos__said{overflow-wrap:anywhere}
/* «(sin descripción)» is the actionable state on this screen, so it is quiet
   and italic rather than absent -- an empty cell reads as a layout fault. */
.wt-photos__said--none{color:var(--wt-ink-soft);font-style:italic}
/* b-1236 -- wrap, because «Después» at 200% text is wider than «Bajar» was. */
.wt-photos__move{display:flex;flex-wrap:wrap;gap:.4rem;padding:0 var(--space-2xs)}
.wt-photos__move button{min-height:var(--target-size);min-width:var(--target-size);
 padding:.4rem .7rem;font-size:var(--text-detail)}
/* The photograph, on its own screen. Bounded like every other image here. */
.wt-photo-detail__image{display:block;width:100%;max-height:20rem;object-fit:cover;
 margin:0 0 var(--spacing-small);border-radius:var(--radius-md);border:1px solid var(--wt-line);
 background:var(--wt-paper)}
/* 🚨 The native file input SURVIVES -- no-js-floor.test.ts requires it, and a
   scripted replacement is the thing ADR 0079's floor forbids. Only the browser's
   own button is styled, and where a browser ignores the pseudo-element the
   fallback is the native control, which is ugly and works. */
input[type="file"]{padding:.4rem;background:var(--wt-field)}
input[type="file"]::file-selector-button{font:inherit;font-weight:var(--weight-strong);
 margin-right:.6rem;padding:.5rem .9rem;min-height:var(--target-min);cursor:pointer;
 color:var(--wt-ink);background:transparent;
 border:var(--border-rule) solid var(--wt-primary);border-radius:var(--radius-sm)}
/* b-1242 -- where a script runs, the native control is clipped (still focusable)
   and the label beside it is the button, in the owner's language; the chosen
   filename is echoed after it. With scripts off none of this applies and the
   native control above is what the owner uses. */
.wt-file{position:relative;display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-xs) var(--space-sm);margin:0 0 var(--spacing-stack)}
.wt-file__button{display:none}
.wt-file--js .wt-file__button{display:inline-flex;align-items:center;min-height:var(--target-min);
 padding:.5rem .9rem;font-weight:var(--weight-strong);cursor:pointer;color:var(--wt-ink);
 border:var(--border-rule) solid var(--wt-primary);border-radius:var(--radius-sm);background:transparent}
.wt-file--js input[type="file"]{padding:0}
.wt-file--js input[type="file"]:focus-visible+.wt-file__button{outline:var(--border-rule) solid var(--wt-primary);outline-offset:2px}
.wt-file__name{display:none;color:var(--wt-ink-soft);font-size:var(--text-detail);overflow-wrap:anywhere}
.wt-file--js .wt-file__name{display:inline}
.wt-file .wt-shrink,.wt-file .wt-shrink__preview{flex:1 1 100%;order:3;margin:0}
/* b-0625 — the hours form's save is first in the DOM because the first submit
   button is what Enter presses. b-1237 — it is the ONLY save now, and sticky,
   so it stays in reach through seven days of boxes instead of being repeated
   at the bottom. Card-coloured so the day beneath does not show through. */
.wt-hours-save--top{position:sticky;top:0;z-index:2;justify-content:flex-end;margin:0 0 var(--spacing-stack);
 padding:var(--space-xs) 0 var(--space-sm);background:var(--wt-card);border-bottom:1px solid var(--wt-line-soft)}
/* b-1237 -- the copy disclosure overwrites six boxes and is not a deletion, so
   it borrows the confirm shape at rule weight rather than the warning colour. */
.wt-copy .wt-confirm__open{border-color:var(--wt-line);color:var(--wt-ink)}
.wt-copy .wt-confirm__body{border-color:var(--wt-line)}
/* b-0618 — a paragraph may hold phrasing content only, and every control in the
   photo row is its own form element. The browser was closing the paragraph
   early and re-parenting them, so the class rule replaces the p.wt-actions
   selector for that one container.
   Until b-1633 this sheet was inlined into every page, and a tag named in this
   comment was parsed by no-js-floor.test.ts as a real form (b-0618). It is
   served at its own URL now; the element is still named in words. */
/* b-0610 — the dashboard had NO img rule of any kind, on a phone-first surface.
   Every photograph rendered at its intrinsic size, so one 4000px phone snap
   would have pushed the page wider than the screen. The floor comes first; the
   three below are what each photograph is FOR. */
img{max-width:100%;height:auto}
.wt-row-photo__preview{border-radius:var(--radius-sm);border:1px solid var(--wt-line)}
/* Tall enough to recognise a room, short enough that twenty rows are still a
   list you can scroll. object-fit, so a portrait photo is not letterboxed into
   a strip nobody can identify. */
.wt-gallery__preview{width:100%;max-height:14rem;object-fit:cover;display:block;
 margin:0 0 var(--spacing-stack);background:var(--wt-paper)}
.wt-hero-photo__preview{width:100%;max-height:16rem;object-fit:cover;display:block;
 margin:0 0 var(--spacing-xs);border-radius:var(--radius-sm);border:1px solid var(--wt-line)}
/* b-0684 — the login page's recovery form. Its summary is a heading in weight,
   because it replaced an h2 and is the second thing on the screen: the big
   title drawn inside it, so it is the section size without a copy of it. */
.wt-login__resend summary{cursor:pointer;margin:0 0 var(--spacing-xs);
 min-height:var(--target-size);display:flex;align-items:center}
.wt-login__resend[open] summary{margin-bottom:var(--spacing-2xs)}
/* b-0684 — the two verbs the check did NOT recommend. Quiet, and still one tap
   away: the owner who knows better than the recommendation is not blocked. */
details.wt-byod__other{border-top:1px solid var(--wt-line-soft);margin:0}
details.wt-byod__other summary{cursor:pointer;min-height:var(--target-size);
 display:flex;align-items:center;font-weight:var(--weight-strong);color:var(--wt-primary-text)}
details.wt-byod__other[open]{padding-bottom:var(--space-sm)}
.wt-byod__option--first{border:1px solid var(--wt-line);border-radius:var(--radius-md);
 background:var(--wt-card);padding:var(--pad-card)}
.wt-byod__option--first{margin:0 0 var(--spacing-small)}
/* The reason this verb and not the other two. A recommendation with no reason
   reads as the only option. */
.wt-byod__why{color:var(--wt-ink-soft);font-size:.92rem;margin:0 0 var(--spacing-stack)}
.wt-byod__mail{margin:0 0 var(--spacing-small)}
/* b-1070 — the three answers to the who-has-the-access question are POSTs,
   so they are buttons drawn as the home's rows rather than links. */
.wt-answers form{margin:0}
.wt-answers button.wt-answer{display:flex;flex-wrap:wrap;align-items:baseline;gap:.15rem .7rem;
 width:100%;text-align:left;background:var(--wt-card);color:var(--wt-ink);
 padding:var(--spacing-stack) var(--pad-card);border:1px solid var(--wt-line-soft);
 border-radius:var(--radius-md);min-height:var(--target-size)}
.wt-answers button.wt-answer:hover{border-color:var(--wt-line)}
.wt-answers button.wt-answer .wt-home__label{color:var(--wt-primary-text)}
.wt-send__to{margin:0 0 var(--spacing-2xs)}
.wt-send textarea{width:100%}
.wt-copy{margin:0}
/* The copy button a script adds straight under a textarea: the textarea owns
   the gap, which was none where the textarea sits in the page's own flow. */
textarea:has(+ .wt-copy){margin-bottom:var(--spacing-xs)}
.wt-procedure dl{margin:0 0 var(--spacing-xs)}
.wt-procedure dt{font-weight:var(--weight-strong);margin:0 0 var(--spacing-2xs)}
.wt-procedure dd{margin:0 0 var(--spacing-xs)}
.wt-procedure ol,.wt-procedure ul{margin:0 0 var(--spacing-2xs);padding-left:1.25rem}
.wt-giveup{padding-top:var(--space-md);border-top:1px solid var(--wt-line-soft)}
.wt-held{border:1px solid var(--wt-line);border-radius:var(--radius-md);background:var(--wt-card);
 padding:var(--pad-card)}
.wt-held{margin:0 0 var(--spacing-large)}
/* b-0618 — the confirmation. Closed, it is one control among four and reads as
   the hazard it is; open, it takes the whole row so the photograph and the
   sentence sit directly above the button that acts. */
/* No margin of its own: in a row the row's gap places it; alone in the page's
   flow the rhythm block below does, as it does any other disclosure. */
details.wt-confirm{flex:0 1 auto}
details.wt-confirm[open]{flex:1 1 100%}
.wt-confirm__open{min-height:44px;display:inline-flex;align-items:center;gap:.35rem;
 padding:.65rem 1.1rem;border:1px solid var(--wt-warn);border-radius:var(--radius-sm);
 color:var(--wt-warn);font-weight:var(--weight-strong);cursor:pointer}
.wt-confirm[open] > .wt-confirm__open{margin-bottom:var(--spacing-xs)}
.wt-confirm__body{margin:0;padding:.85rem var(--pad-card);border:1px solid var(--wt-warn);
 border-radius:var(--radius-sm);background:var(--wt-card)}
.wt-confirm__body p{margin:0 0 var(--spacing-xs)}
/* The photograph is REPEATED here, small, because the row's own copy is a
   screen away by the time the owner has scrolled to this button — and a warning
   that arrives after the moment is worse than none (copy-mechanics 8b). Small,
   because it is an identity check and not a second gallery. */
.wt-confirm__what{display:flex;gap:var(--space-sm);align-items:flex-start;margin:0 0 var(--spacing-stack)}
.wt-confirm__thumb{flex:0 0 auto;width:4.5rem;height:4.5rem;object-fit:cover;display:block;
 border-radius:var(--radius-sm)}
/* b-0686 — a FILL takes the fill role and its own label colour. It used to take
   --wt-warn (the TEXT red) with the card ground on top, which is the same
   fill-used-as-text confusion the palette's primary/primaryText split exists to
   make impossible. */
button.wt-danger{background:var(--wt-warn-fill);color:var(--wt-on-warn)}
.wt-home__dot{display:inline-block;width:.9rem;height:.9rem;border-radius:50%;
 margin-right:.4rem;vertical-align:-1px;border:1px solid var(--wt-line)}
/* b-1270 - the colour row shows the SURFACE the site is built from, not the
   seed. The seed is an input to derivePalette and the page never paints it;
   printing it beside the name was the fourth of the review's four answers. The
   two dots inside are the primary and the accent on that surface, which is the
   same three-value summary the swatches on /panel/color already give. */
.wt-home__paint{display:inline-flex;align-items:center;gap:.25rem;
 width:2.6rem;height:1.3rem;padding:0 .3rem;margin-right:.45rem;vertical-align:-.3rem;
 border-radius:var(--radius-sm);border:1px solid var(--wt-line)}
.wt-home__paint i{width:.5rem;height:.5rem;border-radius:50%;display:block}

/* ===== BARRA + HOJA DE MENU + LINEA DE ESTADO (b-0681) =====================
   The 27 Aug design pass, sections 1-3 of its component sheet. The eight-item
   nav, the two site links and the seventeen section pills came to 657px above
   the h1 at 390px -- 78% of the viewport, on every editing screen, measured.
   The door does the same job in 158px.
   The menu is a details element, so this needs no script and cannot need one.
   b-0617 is kept: inline-flex plus min-height on every bar link, because
   min-height does nothing to an inline box and 2.5.8 wants 24px.
   NO BACKTICKS OR DOLLAR-BRACE IN THIS COMMENT -- the sheet is a template
   literal and esbuild reads them as the end of the string. */
header.wt-bar{background:var(--wt-deep);color:var(--wt-on-deep)}
header.wt-bar a{color:var(--wt-on-deep)}
/* 🚨 WCAG 2.1 SC 1.4.10 (Reflow) — flex-wrap is LOAD-BEARING, not tidiness.
   At 320px with the reader's text at 200% (a 32px root) the wordmark alone is
   148px and the menu 79px; with 40px of padding each side they cannot share a
   line, and a non-wrapping row does not care how much room it has -- which is
   b-0689's own sentence, arriving on the bar that replaced the header it was
   written about. Measured: 56px of sideways scroll on EVERY screen behind the
   login, and run-pa11y's reflow pass named the menu element on all of them.
   ⚠️ The old header wrapped. This one dropped that property while nothing at
   the default text size could see it -- the browser-facts gate measures at
   390px and 16px, and was green over all of it.
   🚨 AND THE FIRST DRAFT OF THIS COMMENT QUOTED THE SELECTOR IN BACKTICKS,
   which esbuild read as the end of this template literal. The rule is two
   screens up and it was still made. NO BACKTICKS AND NO DOLLAR-BRACE HERE. */
.wt-bar__row{display:flex;align-items:center;flex-wrap:wrap;gap:var(--space-xs) var(--space-md);
 min-height:56px;padding:.35rem var(--gutter);position:relative}
.wt-menu{position:static;margin-left:var(--space-xs)}
.wt-menu summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;
 gap:var(--space-2xs);min-height:var(--target-size);padding:0 var(--space-2xs);font-weight:var(--weight-strong)}
.wt-menu summary::-webkit-details-marker{display:none}
.wt-menu[open] summary{text-decoration:underline;text-underline-offset:2px}
.wt-menu__sheet{position:absolute;left:0;right:0;top:100%;z-index:9;
 background:var(--wt-card);color:var(--wt-ink);
 border-bottom:1px solid var(--wt-line-soft);box-shadow:var(--shadow-card);
 padding:var(--space-xs) 0}
/* 🚨 b-0729 -- THE BAR PREFIX IS LOAD-BEARING AND THE SHEET WAS INVISIBLE
   WITHOUT IT. Measured 29 Aug 2026 against a signed-in dashboard, computed
   styles at 390px: seven of the ten rows rendered rgb(248,237,221) on
   rgb(250,244,236) -- a contrast ratio of 1.06. Near-white on near-white, on
   the primary navigation.
   The cause is the cascade, not a token. header.wt-bar a is (0,1,2) -- one
   class, two elements -- and the unprefixed .wt-menu__sheet a was (0,1,1), so
   the BAR won and painted every sheet row in the on-deep colour. The sheet is a
   popover that paints its own card ground; on-deep is for text on the deep bar.
   🚨 AND THIS IS WHY THE REPORT LOOKED BACKWARDS. Barry saw the bottom three
   dark and the group above them pale, which is the opposite of what the CSS
   appears to say -- a previous session read the quiet class and correctly ruled
   it out. The quiet rows are the three carrying .wt-menu__quiet, which is
   (0,2,1): two classes beat two elements, so those three were the only ones
   ESCAPING the bug. The class that looked like the cause was the cure.
   ⚠️ Prefixing BOTH rules keeps their order intact: (0,2,2) for the base and
   (0,3,2) for the quiet one, so quiet still wins over its own base and both
   still beat the bar. */
header.wt-bar .wt-menu__sheet a{display:flex;align-items:center;min-height:48px;padding:0 var(--gutter);
 color:var(--wt-ink);text-decoration:none;font-weight:var(--weight-strong)}
/* box-shadow rather than a border, so the row does not need a calc() padding to
   stay aligned with its siblings -- and calc(1.25rem - 3px) is a px SPACING
   value the raw-values guard counts. An inset shadow paints inside the box. */
.wt-menu__sheet a[aria-current="page"]{box-shadow:inset var(--border-rule) 0 0 0 var(--wt-primary)}
/* The rule's room is below it and on the row above it: no top margin. */
.wt-menu__sheet hr{border:0;border-top:1px solid var(--wt-line-soft);margin:0;padding-bottom:var(--space-xs)}
header.wt-bar .wt-menu__sheet a:has(+ hr){margin-bottom:var(--spacing-xs)}
header.wt-bar .wt-menu__sheet a.wt-menu__quiet{font-weight:var(--weight-body);color:var(--wt-ink-soft);
 min-height:var(--target-min)}
/* From md the sheet is a popover under the menu, not the page's width, so
   its rows keep a card's inset rather than the page gutter. */
@media (min-width:768px){
 .wt-menu{position:relative;margin-left:0}
 .wt-menu__sheet{left:auto;right:0;min-width:16rem;border:1px solid var(--wt-line-soft);
  border-radius:0 0 var(--radius-md) var(--radius-md)}
 header.wt-bar .wt-menu__sheet a{padding:0 var(--space-lg)}
}
/* The status line. Its container and class are what b-0609's watcher swaps --
   renaming or splitting this element breaks the poll. */
/* 🚨 SC 1.4.10 again, and the same shape as the bar above: a flex row that
   cannot wrap, with a nowrap child. The right-aligned time is one unbreakable
   token -- "el 13/7/2026" is 136px at a 32px root -- so at 320px with the
   reader's text doubled it pushed the document 56px sideways on EVERY screen
   that has a publish state. Wrapping drops it to its own line and keeps the
   date itself unbroken, which is what nowrap is for.
   ⚠️ The nowrap is NOT the defect and must stay: a date split across two lines
   is worse than a date on a line of its own. */
p.wt-publish{display:flex;align-items:center;flex-wrap:wrap;gap:var(--space-2xs) var(--space-xs);margin:0;
 padding:var(--space-xs) var(--gutter);font-size:var(--text-detail);color:var(--wt-ink-soft);
 border-bottom:1px solid var(--wt-line-soft);background:var(--wt-paper)}
p.wt-publish strong{color:var(--wt-ink);font-weight:var(--weight-strong)}
.wt-dot{flex:0 0 auto;width:.7rem;height:.7rem;border-radius:50%;background:var(--wt-primary)}
.wt-publish--updating .wt-dot{background:var(--wt-ink-soft)}
.wt-publish--problem{color:var(--wt-warn);background:var(--wt-card)}
.wt-publish--problem .wt-dot{background:var(--wt-warn)}
.wt-publish--problem a{color:var(--wt-warn);font-weight:var(--weight-strong)}
.wt-publish a{min-height:var(--target-min);display:inline-flex;align-items:center}
.wt-publish__when{margin-left:auto;white-space:nowrap;color:var(--wt-ink-soft)}

.wt-replies{list-style:none;padding:0;margin:0 0 var(--spacing-small)}
.wt-reply{border:1px solid var(--wt-line);border-radius:var(--radius-md);background:var(--wt-card);padding:.85rem var(--space-md) var(--space-md);margin:0 0 var(--spacing-stack)}
.wt-reply--shown{border-left:4px solid var(--wt-primary)}
.wt-reply__text{margin:0 0 var(--spacing-xs);font-style:italic}
.wt-reply__text p{margin:0}
.wt-reply__why{font-weight:var(--weight-strong)}
.wt-replies__state p{margin:0 0 var(--spacing-xs)}
.wt-replies__state form.wt-inline{display:block}
.wt-speed{margin:0 0 var(--spacing-large)}
.wt-speed__median{margin:0 0 var(--spacing-stack)}
.wt-speed__track{position:relative;height:.5rem;border-radius:var(--radius-sm);background:var(--wt-line-soft);margin:0 0 var(--spacing-2xs)}
.wt-speed__goal{position:absolute;top:-.35rem;width:.125rem;height:1.2rem;background:var(--wt-ink)}
.wt-speed__you{position:absolute;top:-.2rem;width:.9rem;height:.9rem;margin-left:-.45rem;border-radius:50%;background:var(--wt-warn)}
.wt-speed__you.is-fast{background:var(--wt-primary)}

/* ── The rhythm (see the design system block) ─────────────────────────────
   The page's own blocks: a wrapper with no margin of its own (an unclassed
   form, a div, a disclosure) is one small step from the next, as a paragraph
   is. Zero weight, so any component's margin wins over it. */
:where(main, main section) > :where(form, div, details){margin-bottom:var(--spacing-small)}
/* The page's opening lede, one med step from what it introduces. */
.wt-lede{margin-bottom:var(--spacing-med)}
/* A new group, a section or a section-size title in the middle of a flow,
   opens a section gap below whatever comes before it, and that element owns
   the gap: nothing here has a top margin. One class of weight and after the
   components, so it wins over their own margins; the utilities come later
   still and win over it. An unboxed group (a div.wt-card, never drawn as a
   box) gets the large step, and that one rule is heavier than a utility. */
section{margin:0 0 var(--spacing-section)}
:where(main) :has(+ section, + .big-title){margin-bottom:var(--spacing-section)}
:where(main) :has(+ div.wt-card){margin-bottom:var(--spacing-large)}
/* The last thing in a padded block carries no margin: the padding is the gap. */
:where(main,.wt-card,.wt-note,.wt-problems,.wt-field,fieldset,.wt-golive,.wt-needs,.wt-held,
 .wt-share,.wt-choice,.wt-book__pending,.wt-book__callbacks,.wt-book__elsewhere,.wt-book__offer,
 .wt-book__memory,.wt-bill,.wt-leads > li,.wt-history > li,.wt-reading__warn,.wt-confirm__body,
 .wt-preview,.wt-byod__option--first,.wt-kitchen,.wt-reply) > :last-child{margin-bottom:0}

/* ── Spacing utilities. LAST, so they win (see the design system block). ── */
.mb-0{margin-bottom:0}
.mb-02em{margin-bottom:0.2em}.mb-03em{margin-bottom:0.3em}.mb-05em{margin-bottom:0.5em}
.mb-08em{margin-bottom:0.8em}.mb-1em{margin-bottom:1em}
.mb-2xs{margin-bottom:var(--spacing-2xs)}.mb-xs{margin-bottom:var(--spacing-xs)}
.mb-stack{margin-bottom:var(--spacing-stack)}.mb-small{margin-bottom:var(--spacing-small)}
.mb-med{margin-bottom:var(--spacing-med)}.mb-large{margin-bottom:var(--spacing-large)}
.mb-section{margin-bottom:var(--spacing-section)}
