/* ============================================================
   social-dark.css — THE DESK AFTER DARK. The Social suite's single
   fixed theme: the house Night palette re-declared as this sheet's
   own ground (spec 2026-07-03 §3), plus the invariant cream masthead.
   ONE COLOUR HERE IS THE SUITE'S OWN, and it is --mark. Every other value
   is still the house Night palette. See the note on the token itself.
   Shared by social.html (landing)
   and social/workbench.html (the light table / bench).
   The banned-forever list for Social surfaces is spec 2026-07-03 3
   (guarded by tests/test_social_dark.py, which scans this file).
   ============================================================ */
/* One request, not two. Axis tags sorted, Schibsted as a range, Fraunces
   removed. See the note at the head of editorial-frame.css. 2026-07-27. */
/* The font request moved to a <link> in each page that loads this sheet.
   An @import cannot start until this stylesheet has itself downloaded and
   parsed, so the faces arrived about half a second late and every visit
   showed a flash of fallback type (design review, finding 7). */

:root{
  /* NINE TEXT SIZES for the whole product, replacing fifteen. Eight applied
     2026-08-10 from the styling preview, the ninth 2026-08-12 on Ritu's ruling:
     the scale used to run 40px straight to 68px, a jump of 1.7 where every
     other step is 1.2 to 1.45. --t-8 held 68 and nothing used it, so 48 took
     that name. The headline keeps its clamp and only the ends of it read
     tokens; a headline frozen to a fixed step is what wraps.
     social.html loads THIS SHEET ALONE. */
  --t-1:12px; --t-2:14px; --t-3:16px; --t-4:19px;
  --t-5:23px; --t-6:28px; --t-7:40px; --t-8:48px; --t-9:68px;
  /* SEVEN SPACING STEPS for the whole product, replacing thirteen. Applied
     2026-08-10 from the styling preview. Values under 4px are left alone, and
     INLINE padding and margin are only ever reduced: raising them narrows the
     column the text sits in, which is how a paragraph gains a line.
     social.html loads THIS SHEET ALONE. */
  --s-1:4px; --s-2:8px; --s-3:16px; --s-4:24px;
  --s-5:40px; --s-6:64px; --s-7:104px;
  /* The x-height match for the mono-to-sans move. MEASURED: JetBrains Mono
     renders an x-height of 0.55 em, Schibsted Grotesk 0.527, so the sans is
     scaled up about 4.3% and the two faces sit on one x-height. See
     preview/x-height.json. social.html loads THIS SHEET ALONE. */
  --x-adjust:0.55;
  /* TWO TRACKING VALUES for the whole product, replacing ten. Applied
     2026-08-10 from the styling preview. See the note in editorial-frame.css.
     social.html loads THIS SHEET ALONE, so the tokens are declared here. */
  --track-tight:-0.01em;
  --track-caps:0.16em;
  /* ONE TRACKING VALUE FOR EVERY HEADLINE. Applied 2026-08-12.

     The product carried four: -0.03em on the tool pages, -0.03em on editorial
     and corporate, -0.025em on the front door, -0.02em on social. This is the
     TIGHTEST of them, and tightest is the safe direction: loosening
     DeskTriage's and FieldReportage's headline from -0.03em to -0.01em wrapped
     both onto two lines when the tracking step was measured on 2026-08-10.
     Tightening cannot add a line.

     It is separate from --track-tight because that is the body's value,
     -0.01em, and a 68px headline and a 16px label do not want the same one. */
  --track-hero:  -0.03em;
  /* ONE RULE WEIGHT for the whole product: every divider in the five sheets
     reads this token instead of carrying its own number.

     THE VALUE IS 1px, AND 0.5px WAS TRIED FIRST AND MEASURED. In Chrome a
     0.5px border occupies exactly the same space as a 1px one and reports as
     1px, at BOTH display densities: a 40px-tall box with 0.5px borders and one
     with 1px borders both measure 42px. Ritu's ruling of 2026-08-10 is that
     Chrome is the only engine in scope, so a half-pixel here is a number that
     looks like a decision and paints like nothing. It would also halve every
     divider on the day some future Chrome starts honouring it, with nobody
     deciding that.

     The point of the token is that the next person changes ONE number, not 141.
     Guard: tests/test_css_architecture.py::HairlineTokenTest. */
  --hairline:    1px;
  /* Night ground (values = editorial-frame.css [data-theme="night"]) */
  --paper:       #16171A;
  --paper-deep:  #1F2024;
  --paper-warm:  #1F2024;
  --ink:         #ECE7DC;
  --ink-soft:    #C3BDB0;
  --quiet:       #989185;
  --rule:        #34353A;
  --rule-soft:   #2A2B2F;
  --accent:      #56B6BF;
  --accent-deep: #1E4F80;
  /* SOCIAL'S OWN COLOUR, and the only one in this sheet that is. Ritu,
     2026-09-09: "I want the yellow (amber!) replaced in the Social landing
     page and HookCraft's hero. Suggest something unique and certainly not
     slop." He chose this riso pink from four rendered on the real pages.

     MEASURED, not assumed: 7.62:1 against the ground #16171A, and the same
     7.62:1 for the ground reading back off it, so HookCraft's hero block
     works both ways round. The gold it replaces read 7.50:1.

     The rest of the product keeps the house gold #CBA15A. editorial-frame.css,
     corporate.css, front-door.css and byline-kit.css are untouched, because he
     named the Social landing and HookCraft and nothing else. */
  --mark:        #FF7FB2;
  --good:        #86A86E;
  --warn:        #D98C53;
  --brand-rule:  #3576B5;
  --cta-fill:    #56B6BF;
  --cta-ink:     #16171A;
  --overlay-bg:  rgba(0,0,0,.62);
  --value-mark:  rgba(255,127,178,.20);
  /* Citation slips carry daylight (Day tokens, the one polarity inversion) */
  --day-paper:   #FBFAF6;
  /* Masthead strip matches the suite-wide cool-grey palette (de-tell), NOT the
     warm --day-paper daylight above (which stays for the citation slips). */
  --mast-paper:  #E9EDED;
  --day-ink:     #1A1717;
  --day-quiet:   #6B6259;
  --day-mark:    #8A5E12;
  /* Brand chrome */
  --navy:        #082A4C;
  /* Structure */
  --font-display:"Schibsted Grotesk",system-ui,-apple-system,sans-serif;
  --font-body:"Newsreader",Georgia,serif;
  --font-mono:"JetBrains Mono",ui-monospace,Menlo,monospace;
  /* Unified with the other root sheets 2026-08-03. The wider 4vw/2.75rem gutter
     put the Social wordmark 4-5px further in than every other surface across
     820-1280 (measured), so "equidistant across pages" failed in that band even
     with a shared frame. Then widened again, in all three sheets together: the
     old cap of 45px meant the margin stopped growing below 1386px, so a 14in
     office laptop at 1280 got 44px where a MacBook at 1512 got 100px. See the
     long note in editorial-frame.css. */
  --gutter:clamp(1.5rem, 5vw, 5rem);
  /* One frame for the whole product (spec 2026-08-03 §5A). Ritu's ruling: the
     front door is the standard, so this is the front door's column, and every
     masthead inset and every primary content column derives from it. Declared
     identically in editorial-frame.css and suite.css because no stylesheet is
     loaded by every page; tests/frame-token.test.js keeps the three in step. */
  --frame:min(72rem, 100% - var(--gutter) * 2);
  /* Prose cap inside the wide frame. Deliberately NOT --measure, which is 62ch
     and caps the rewrite output column in editorial-frame.css. See the note
     there. */
  --prose:min(38rem, 100%);
  --col:72rem;
  /* Finding 8. The artifact is the PRODUCT — the thing the page is selling — and
     it measured 1.10:1 against the ground while the citation slip beside it
     measured 17.16:1. The eye terminated on the working and never reached the
     result. Lifted to 1.60:1 on the sheet's own cool axis; --ink still reads
     9.06:1 on it and --ink-soft 5.97:1. The slip keeps its daylight: it is the
     one deliberate polarity inversion here and the best thing on the site.
     --edge is a component boundary at 3.21:1, clearing WCAG 1.4.11's 3:1. The
     shared --rule (1.46:1) was doing that job and is a hairline, not a boundary. */
  --artifact:    #3A3B40;
  --edge:        #66686D;
  --touch-min:44px;
  /* Ritu's ruling 2026-07-28: 44px is the floor for CONTROLS; inline and footer
     text links take the AA target size instead. Mirrors suite.css. */
  --touch-link:24px;
  --pill:999px;          /* capsule radius — buttons (2026-07-12 pill pivot; Social converged) */
  --box:16px;            /* input + output boxes (Ritu, 2026-08-07) */

  /* SECTION RHYTHM — added 2026-07-28.

     The review's finding 18 was that Social has no fluid TYPE scale. It has no
     fluid SPACE scale either, and nobody checked: every section padding on this
     sheet was a fixed rem value (.hero 4-7.5rem, .desks 5rem, .table 4rem,
     .voices 5rem, .foot 4.5rem) that rendered identically at 1440 and at 390.
     80px between sections is correct on a desktop and is a third of the screen
     on a phone. Measured at 390px: the hero's 48px bottom padding plus .desks'
     80px top gave 128px of continuous emptiness between the lede and "Pick your
     platform" — Ritu, 2026-07-28: "a yawning gap".

     Both ramps are tuned to land on the OLD value at 1440 so the desktop
     composition is untouched, and to fall away on a phone. --section hits
     79.9px at 1440 (was 5rem/80px) and 40px at 390. */
  /* Floors lowered 2026-07-28 (second pass). The first pass made the section
     rhythm fluid but left the hero's INTERNAL margins as fixed rems, so at 390px
     the eyebrow still held 33px under it, the lede 36px above it, and the run
     from the lede to "Pick your platform" was 73px of nothing. Ritu flagged both
     gaps again. Each ramp still lands on its old value at 1440, so the desktop
     composition is untouched; only the phone floor moves. */
  --section:    clamp(1.5rem, 5.55vw, 5rem);
  --section-sm: clamp(1.25rem, 4.44vw, 4rem);

  /* THE PRESS, Social's own copy of the values editorial-frame.css declares
     and explains at length (see its :root, "THE PRESS"). Ritu's ruling reads
     "Do it for the entire project", and social.html and framework.html load
     THIS SHEET ALONE, so an unresolvable var() here would void every rule
     that reads it with no error. Two depths, one curve: --press-deep for the
     one loud action on a page, --press-light for everything else that
     answers a tap. Not one of the two authorities test_css_architecture.py
     pins (editorial-frame.css, suite.css); kept identical by hand, the same
     way this sheet already re-declares the Night palette as its own ground. */
  --press-deep:  0.955;
  --press-light: 0.98;
  --press-in:    85ms;
  --press-out:   200ms;
  --press-ease:  cubic-bezier(0.23, 1, 0.32, 1);
}
@media (prefers-reduced-motion: reduce) {
  :root { --press-in: 0s; --press-out: 0s; }
}
/* The resize grip's two hairlines, removed on Ritu's 2026-08-07 ruling.
   Paint only: the drag stays. Mirrors editorial-frame.css. */
textarea::-webkit-resizer { display: none; }

/* HOVER IS GUARDED, 2026-07-28.

   Every :hover on this sheet is now inside @media (hover: hover). On a touch
   screen there is no pointer to move away, so a tapped element keeps its hover
   state until something else is tapped. Ritu, on the light table: "at first
   glance, 'Put the next print...' is white. But once I click through all three
   pictures, it retains the blue colour." That is .print-swap:hover sticking —
   the button is not broken, it is permanently hovered.

   All eight rules were unguarded, so the same trap sat on the masthead pill,
   the desk cards and both desk CTAs. Guarding the lot rather than the one Ritu
   happened to tap. Desktop behaviour is unchanged: a mouse reports hover:hover.
*/
*{box-sizing:border-box;}

/* THE BROWSER'S OWN FLASH IS SILENCED, EVERYWHERE, 2026-09-10. Same finding
   and the same fix as editorial-frame.css: a tap paints the browser's own
   grey or blue box over the element regardless of any CSS on the page,
   measured at rgba(0, 0, 0, 0.18), so a control that already presses
   answered one tap with two things at once. `touch-action: manipulation`
   drops the engine's wait for a possible second tap. By element and role,
   not by class, so a control built later inherits it unasked. */
button, select, summary, [role="button"], [role="tab"], a {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* Finding 4. This sheet had ZERO authored :focus rules, so keyboard focus fell
   to the UA default, which measured 2.99:1 on --paper and 2.72:1 on
   --paper-deep against a 3:1 requirement. The ring uses --accent, not
   --accent-deep: the navy is a light-theme colour and would vanish on this
   ground. Teal on --paper measures 7.54:1. */
:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;}

/* First tab stop on the page. Off-screen until focused. */
.skip-link{position:absolute;left:-9999px;top:0;z-index:100;
  background:var(--accent);color:var(--paper);
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-2);letter-spacing: var(--track-caps);
  text-transform:uppercase;text-decoration:none;
  padding: var(--s-3) var(--s-3);border-radius:var(--pill);}
.skip-link:focus{left:var(--gutter);top:.6rem;}
/* Fluid root. Social had no root ramp at all, so every rem-based size on the
   page was frozen: at 1440 and at 390 the lede measured 17.60px, the eyebrow
   11.20px and .d-cta 10.56px, identically. Matches suite.css's ramp so the two
   families scale the same way. Design review 2026-07-27, finding 18. */
/* scrollbar-gutter: the scrollbar is part of the frame — see the note in
   editorial-frame.css. A short page has no scrollbar, so its viewport is ~15px
   wider, so a viewport-derived masthead inset puts the wordmark 7.5px off every
   page that does scroll. Ritu ruled 2026-08-03 that it holds the space
   everywhere. */
html{scroll-behavior:smooth;font-size:clamp(16px, 0.6vw + 14px, 18px);scrollbar-gutter:stable;}
/* clip (not hidden): still no sideways scroll, but keeps html/body from
   becoming scroll containers — hidden would force overflow-y:auto and break
   the light table's position:sticky print. */
html,body{overflow-x:clip;}
body{margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--font-body);font-size: var(--t-4);line-height: calc(var(--s-1) * 8);
  -webkit-font-smoothing:antialiased;}

/* THE LEADING FOLLOWS THE TYPE DOWN. Added 2026-09-16 by the leading gate on
   its first run over the suite: fifteen findings on social.html, every one of
   them this line. --t-4 is 19px on a desktop and 16px on a phone, and a fixed
   32px against both reads 1.68 up there and 2.00 down here. It reached
   .voices-note, .band-line and four list items at 320, 358 and 412, because
   they all inherit it.

   A ratio, not a step, and deliberately so. This is the DEFAULT every element
   on the page inherits unless it says otherwise, so a number that tracks the
   type is the only kind that can be right for all of them at once. The desktop
   is untouched: the rule above still paints there. */
@media (max-width: 430px) {
  body { line-height: 1.65; }
}

/* ---- masthead: BRAND INVARIANT. Cream strip, 2px navy rule. ---- */
/* Full-bleed chrome, contents on the product frame. This rule derived from
   --col and then ADDED a gutter on top, which put the Social wordmark a whole
   gutter further inward than every other surface. Fixed 2026-08-03: one shared
   --frame, no addition (spec §5A). The earlier note is still the reason the
   inset is derived at all: a fixed --gutter does not centre with the column, so
   the mark drifted 0px at 1280, 65px at 1440, 145px at 1600, 245px at 1800. */
/* TWO SHAPES ON THIS SHEET, AND ONLY TWO PAGES LOAD IT. social.html is the
   suite HOME and keeps `.masthead`. framework.html is a DESK and carries
   `.tool-masthead` like every other desk in the product (rebuilt 2026-09-22,
   audit A1). Both are the same cream strip over the same dark page, so the
   paint is shared and only the things a desk does differently are split out
   below. */
.masthead{display:flex;align-items:center;justify-content:space-between;gap: var(--s-4);
  padding:1.5rem calc((100% - var(--frame)) / 2) 1.05rem;background:var(--mast-paper);
  border-bottom:2px solid var(--navy);}

/* A DESK'S MASTHEAD, WRITTEN OUT IN FULL RATHER THAN SHARED WITH THE LINE
   ABOVE, and tests/frame-token.test.js is the reason: it reads each sheet's
   masthead rule by name and checks the inline padding is derived from --frame.
   A selector list hides one of the two shapes from it. Two rules, two checks.

   IT HOLDS STILL AT EVERY WIDTH. Ritu, 2026-09-22: "The masthead moves too."
   Every other desk got that on the same day from editorial-frame.css, which
   this page does not load, so it is written here for the one surface that
   needs it. The suite home above is a landing page and keeps its own
   behaviour: it pins on a phone, one query below, and scrolls on the desktop
   as it always has. z-index 30 is this sheet's number for the bar, under the
   app's tab bar at 40.

   AND IT STANDS THE SAME HEIGHT AS EVERY OTHER DESK. MEASURED 2026-09-22 at
   1512: this bar was 96px against 99 on the eight desks that use
   editorial-frame.css, because that sheet pairs 1.6rem with 1.1rem and this
   one pairs 1.5rem with 1.05rem. The desk shape takes the desk's numbers, in
   the shorthand, because the spacing generator reads longhand padding and
   rewrites it to a token: two loose declarations failed that gate. */
.tool-masthead{display:flex;align-items:center;justify-content:space-between;gap: var(--s-4);
  padding:1.6rem calc((100% - var(--frame)) / 2) 1.1rem;background:var(--mast-paper);
  border-bottom:2px solid var(--navy);
  position:sticky;top:0;z-index:30;}
/* Optical alignment, 2026-07-28. The nib artwork carries ~1px of internal
   padding at phone scale, so although the image BOX starts on the gutter
   like everything else, the mark's first ink landed 1px inside it. Measured
   off the rendered pixels on DeskCopyPro at 360px: nib 21.0px, the theme
   capsule / eyebrow / h1 all 20.0px, and the delta held at every sampling
   threshold from 40 down to 2, so it is the artwork and not anti-aliasing.
   Ritu: "the wordmark logo sticks out a tad". Corrected in CSS; the SVG is
   a fixed asset and is never edited. */
.wordmark img{margin-left:-1px;display:block;height:clamp(36px,4vw,48px);width:auto;}
.mast-right,
.masthead-utility{display:flex;align-items:center;gap: var(--s-3);}
/* THE ACCOUNT CAPSULE ON THE CREAM STRIP, added 2026-09-20 with account.css.
   The masthead here is a light island in a dark page, so the capsule takes the
   --day-* family that .mast-nav-link and .suite-mark above already take, not
   the page's own ink. MEASURED at 390 before this rule: --ink is #ECE7DC and
   --mast-paper is #E9EDED, which painted the reader's own name at 1.0:1.
   With it: #1A1717 on #E9EDED, 14.0:1. The menu is deliberately untouched; it
   drops below the strip onto the page and keeps the page's dark panel. */
.masthead .account {
  --acct-ink:      var(--day-ink);
  --acct-edge:     var(--day-quiet);
  --acct-edge-out: var(--day-ink);
  --acct-hover-bg: var(--day-paper);
}

/* AND THE BAR HAS TO HOLD IT. MEASURED at 390 the moment the capsule got its
   real shape: this masthead carries four things (wordmark 110, "All tools" 85,
   the gear 44, the capsule 87) plus three 16px gaps, which is 374px of content
   in a 341px box. The masthead does not scroll, so the capsule was clipped at
   the screen edge instead. At 358 it ran 40px over.

   The unstyled control hid this. It measured 55px because it had no padding and
   no border, so the bar looked like it fitted while showing the reader's name in
   Arial. Styling it correctly is what made the real width visible.

   Two existing mechanisms close the 33px, and no control is removed: the gap
   goes to the 8px step, and the capsule falls back to the initial that
   account.css already draws for a narrow screen. That is 24 + 27 = 51px. "All
   tools" stays, because on a phone it is the only way from this desk to the
   others (audit finding U7). */
/* The narrow-width rules for this bar live at the FOOT of this sheet, after
   the 699.98px block that sets the masthead gap. Same specificity, later wins,
   and putting them here instead cost a measured 5px at 320. */
.mast-nav-link{display:inline-flex;align-items:center;min-height:var(--touch-min);
  background:var(--navy);color:var(--day-paper);border-radius:var(--pill);
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);text-transform:uppercase;
  padding: var(--s-2) var(--s-3);text-decoration:none;}
@media (hover: hover){.mast-nav-link:hover{background:var(--day-ink);}}
.suite-mark{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--day-quiet);}
/* Ritu, 2026-09-12, as a non-negotiable: "In every tool on the app, the
   masthead and the main settings must be static when the user scrolls down."
   Two of the app's six surfaces run on this sheet, so the pin has to be
   declared here as well as in editorial-frame.css; the two sheets are separate
   systems by design and neither reads the other.

   699.98px is --phone-max, the same literal the editorial phone block and
   phone-places.js both use. The 640px query below is this sheet's own older
   masthead breakpoint and is left where it is: it decides what the row SHOWS,
   which is a different question from whether the row stays.

   No background or rule is added: .masthead above already paints
   var(--mast-paper) and draws the navy rule along its foot, so content passes
   under an opaque bar. z-index 30 matches the editorial side, under the tab
   bar's 40 and far under any overlay. This sheet already sets overflow-x:clip
   rather than hidden on html and body (line 253), so nothing between this bar
   and the viewport is a scroll container and the pin holds. */
@media (max-width:699.98px){
  /* The DESK is pinned at every width by the rule near the top of this sheet.
     This is the suite home, which pins on a phone only. */
  .masthead{position:sticky;top:0;z-index:30;}

  /* The landing mark, on the same word. Scoped AWAY from the tool mastheads by
     :not(:has(.suite-mark-name)): a masthead naming a tool is an app surface
     and holds the app's 30px, one rule below. */
  /* 40px, one number across the whole product. Ritu, 2026-09-12, holding up a
     photograph of the editorial landing masthead on his phone: "The wordmark on
     an app should be at least this size". Measured on live prod at 360, what he
     photographed renders 36px, so that is the floor. Measured the ceiling on the
     tool bars: at 46px the brand column squeezes the story control from 92 to
     80px and the story name truncates; 40 is the largest that fits. Full table
     in editorial-frame.css. */
  .masthead .wordmark img{height:40px;width:auto;}

  /* Ritu, 2026-09-12: "Ensure the masthead panels are of the same size across
     all the tools on the app."

     MEASURED before this, at 360 / 390 / 412: six of the app's seven surfaces
     stand at exactly 54px, because .tool-masthead pairs var(--s-1) of vertical
     padding with a 44px control and a 2px brand rule. FrameWork stood at
     91.2 / 91.7 / 92, because this sheet's .masthead pairs 1.5rem and 1.05rem
     with a nav pill that measures 48. It is the only surface that disagreed,
     and the only one whose masthead grew with the viewport, since those
     paddings are set in rem while the pill's is not.

     Two numbers close it. The vertical padding becomes var(--s-1), the same
     step .tool-masthead uses. And the nav pill gives up its own vertical
     padding, which was pushing it to 48 against its declared 44px floor:
     min-height holds the floor, so height is max(44, content) and the pill
     lands on 44 exactly, the way every control in the editorial masthead
     does. 44 + 4 + 4 + the 2px rule is 54.

     :has(.suite-mark-name) and not a page name. A masthead carrying
     .suite-mark-name is one that names a TOOL; social.html's names the suite
     and is a landing page, not one of the app's surfaces, so it keeps the
     taller chrome it was designed with. Any social tool added later inherits
     the tool height by carrying the tool's own mark, which is the behaviour
     worth having. */
  /* 60px, not 54, from 2026-09-12: the mark went to 40px across the product and
     the bar grew by six to carry it without crowding. The number is written in
     .tool-masthead (editorial-frame.css) and matched here, because his ruling is
     that the app's surfaces hold ONE masthead size and this is the one surface
     that does not use that sheet. If one of the two moves, the parity check in
     scripts/box_tools_align_check.mjs fails. */
  /* WAS `.masthead:has(.suite-mark-name)`, a guess at "this masthead names a
     tool". The page now says so itself. */
  .tool-masthead{ padding-top: var(--s-1); padding-bottom: var(--s-1); min-height: 60px; }
  /* The same 30px the editorial mastheads hold, so the app's seven surfaces
     show one identity at one size. This one was never brought down with the
     rest and sat alone at 36 in a 54px bar, 8px of air. */
  /* 40px, the product's one mark size. See the note above: this bar carries
     the tool's name beside the mark, which is why it was held at 30, but the
     mark is the identity and it goes to the same size as everywhere else. */
  .tool-masthead .wordmark img{height:40px;width:auto;}
  .tool-masthead .mast-nav-link{ padding-block: 0; }
}

@media (max-width:640px){
  .masthead .suite-mark,
  .tool-masthead .suite-mark{display:none;}
  /* Pill fills its 44px capsule on phones (fuller label, tighter padding). */
  .mast-nav-link{font-size: var(--t-2);padding: var(--s-2) var(--s-2);}
}

/* On the product frame (spec §5A). Was max-width:--col PLUS a --gutter of inline
   padding, which inset the body a whole gutter further than the masthead and put
   the Social headline 46px right of its own wordmark. --frame is already
   gutter-bound below its cap, so the padding is not just unnecessary, it was the
   bug. 2026-08-03. */
.shell{width:var(--frame);margin:0 auto;padding:0;}

/* ---- hero ---- */
/* The top pad took its measure from viewport HEIGHT alone, and a phone is short
   on width but not on height: 10vh on a 844px-tall phone is 84px, so the hero
   opened with a near-empty screen. Ritu, 2026-07-28: "acres of space". Taking
   the lesser of the height and a width measure lets the desktop keep its 90px
   at 1440x900 while the phone falls to the 3rem floor. */
.hero{padding:clamp(2rem,min(10vh,6.25vw),7.5rem) 0 var(--section-sm);}
.eyebrow{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--quiet);margin:0 0 clamp(1rem,2.5vw,2rem);}
h1{font-family:var(--font-display);font-weight:400;
  font-size: clamp(var(--t-7),5vw, var(--t-9));line-height:1.08;letter-spacing: var(--track-hero);
  margin:0;max-width:28ch;text-wrap:balance;
  /* The rotating hero reserves the tallest pair. The value is measured by
     social.html at load and on resize and written into --hero-reserve; the
     2.2em here is only the JS-off fallback, where nothing rotates and the
     first pair's natural height is all that is ever needed. */
  min-height:var(--hero-reserve, 2.2em);}
/* input + voice flow as one sentence (inline) so the line break falls where it
   balances the two lines, not forced at the input/voice seam — a long half over
   a stubby half read lopsided. Opacity-only fade (inline boxes can't transform). */
.loop{display:inline;transition:opacity .45s ease;}

.loop.voice{font-style:italic;font-weight:420;color:var(--mark);}
.loop.out{opacity:0;}
.strap{display:inline-block;margin-top: var(--s-5);font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);letter-spacing: var(--track-caps);text-transform:uppercase;
  color:var(--quiet);text-decoration:none;}
@media (hover: hover){.strap:hover{color:var(--accent);}}
/* A RATIO, NOT 36 PIXELS. Ritu, 2026-09-13, with FrameWork and the Scroll Desk
   side by side on his phone: "Look at the difference in the gap between
   sentences in the description on either... I've been reiterating that symmetry
   is the essence of this project."

   line-height was calc(var(--s-1) * 9), a flat 36px, while the font-size on
   this element is not flat at all: 23px on the landing, 19px under 620, and
   16.34px where FrameWork's own sheet sets it. So the leading read 1.57, then
   1.89, then 2.20, and the same class of text was loose in proportion to how
   small it got. The Scroll Desk's equivalent, .hook-sub, has always been a
   ratio at 1.5, which is why the two pages disagreed.

   1.5 on both. On the landing that is 34.5px against the 36 it had, which is a
   pixel and a half nobody can see; on FrameWork's phone it is 24.5px against
   36, which is the whole complaint.

   The margin is the second half of it: the gap between a hero and the sentence
   under it is now 1rem on both pages, where it was 26px here and 10px there. */
.lede{max-width:52ch;font-size: var(--t-5);line-height: calc(var(--s-1) * 9);
  color:var(--ink-soft);margin: var(--s-3) 0 0;}
.lede em{color:var(--ink);font-style:normal;}
.cta-row{display:flex;align-items:center;gap: var(--s-4);flex-wrap:wrap;margin-top: var(--s-5);}
.cta{display:inline-flex;align-items:center;gap: var(--s-2);min-height:var(--touch-min);
  background:var(--accent);color:var(--paper);--ico-color:currentColor;border:1px solid var(--accent);border-radius:var(--pill);
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-2);letter-spacing: var(--track-caps);text-transform:uppercase;
  padding: var(--s-3) var(--s-4);text-decoration:none;
  transition: background 0.18s ease, border-color 0.18s ease, transform var(--press-out) var(--press-ease);}
@media (hover: hover){.cta:hover{background:var(--ink);border-color:var(--ink);}}
/* The system's one loud action (editorial-frame.css names it the same way at
   its own .cta) takes --press-deep. A dead one, framework.html's Write it
   before there is a name to work with, must not answer: :not(:disabled). */
.cta:active:not(:disabled) {
  transform: scale(var(--press-deep));
  transition: background 0.18s ease, border-color 0.18s ease, transform var(--press-in) var(--press-ease);
}
.cta-quiet{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--quiet);text-decoration:none;
  border-bottom:1px solid var(--rule);padding: var(--s-2) 0;min-height:0;}
@media (hover: hover){.cta-quiet:hover{color:var(--accent);border-color:var(--accent);}}

/* ---- the light table (signature) ---- */
.table{padding:var(--section-sm) 0 2rem;}
.demo[hidden]{display:none;}
.demo-single{max-width:44rem;margin:0 auto;}
.demo-single .print-steps{gap: var(--s-5);}
/* No side rule at all since 2026-10-01: Ritu ruled every side stripe out as
   AI slop. The quote marks and the softer ink set the example apart. */
.demo-input{padding: .15rem 0;
  color:var(--ink-soft);font-size: var(--t-4);line-height: calc(var(--s-1) * 7);margin: var(--s-4) 0 var(--s-5);}
.rough-note{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--mark);margin: var(--s-2) 0 0;}
/* Section labels: full capitals, bold, and a step LARGER than they were.

   Two corrections in one day, both Ritu's. First the light table was promoted to
   a display heading (wrong: it is a label, not a headline). Then the whole set
   was set in small capitals to get "lower case except the first letter" — also
   wrong, and worse: small caps draw at roughly x-height, so 0.7rem of small caps
   is visually far smaller than 0.7rem of capitals. Ritu: "The point size became
   too small. Can barely read. Both Pick Your Platform and The Light Table. They
   need to be bolder and all-cap."

   So: uppercase restored, weight to 700, size up from .7rem to .8rem. That is
   13.1px at 390 and 14.4px at 1440, against the 11.4/12.6px these labels have
   carried until now — bigger than the original, not merely a revert. Tracking
   eases a touch because bold capitals need less air than regular ones to stay
   separated. */
.table-head{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-2);font-weight:700;
  letter-spacing: var(--track-caps);text-transform:uppercase;color:var(--quiet);
  border-top: var(--hairline) solid var(--rule-soft);padding-top: var(--s-4);margin: 0 0 var(--s-5);}

.table-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(2rem,4vw,4rem);align-items:start;}
.print{position:sticky;top:var(--print-top,2rem);margin:0;}
.print img{display:block;max-width:100%;width:auto;height:auto;
  max-height:min(78vh,44rem);margin:0 auto;border: var(--hairline) solid var(--rule-soft);}
.print img[hidden]{display:none;}
.print figcaption{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: normal;
  color:var(--quiet);margin-top: var(--s-3);line-height: calc(var(--s-1) * 5);}
.print-swap{display:inline-flex;align-items:center;min-height:var(--touch-min);
  margin-top: var(--s-3);background:transparent;border: var(--hairline) solid var(--edge);border-radius:var(--pill);
  color:var(--ink-soft);font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);
  letter-spacing: var(--track-caps);text-transform:uppercase;padding: var(--s-2) var(--s-3);cursor:pointer;}
@media (hover: hover){.print-swap:hover{border-color:var(--accent);color:var(--accent);}}
.print-swap[hidden]{display:none;}
/* The swap is a JS affordance: with no script it would be a button that does
   nothing, so it is hidden without JS. The content it swaps to is NOT hidden --
   every print and step-set is on the page in that case. */
html:not(.js) .print-swap{display:none;}

/* ---- hold the rotation (WCAG 2.2.2) ---- */
/* The hero rotated forever and could only be stopped with a mouse. Hover is not
   a mechanism: it fails for keyboard users and for every touch device, which is
   why the page rotated on a phone with no way to hold it. The section carried
   focusin/focusout for exactly this, but the hero has ZERO focusable children,
   so they could never fire.
   Same grammar as .print-swap above -- an outline pill, never filled, at the
   44px floor. --edge and not --rule: --rule is #34353A here, 1.46:1 on this
   ground, so the control would have been invisible (see the demo rail note). */
/* Held to the lede's own measure, not the hero's full width. The h1 box runs the
   whole column, so right-aligning to the hero left the control stranded 432px
   out in empty space with nothing near it. On the lede's right edge it reads as
   belonging to the text block it sits under. */
/* font-size is carried here ONLY so that 52ch resolves to the same pixel width
   it does on .lede — ch is relative to the element's own font, so without it the
   same 52ch came out 541px against the lede's 675px and the control sat 134px
   short of the edge it is meant to line up with. The button sets its own size,
   so nothing visible inherits this. */
.hero-hold{display:flex;justify-content:flex-end;max-width:52ch;
  font-size: var(--t-5);margin: var(--s-5) 0 0;}
.rot-hold{display:inline-flex;align-items:center;min-height:var(--touch-min);
  background:transparent;border:1px solid var(--edge);border-radius:var(--pill);
  color:var(--ink-soft);font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);
  letter-spacing: var(--track-caps);text-transform:uppercase;padding: var(--s-2) var(--s-3);cursor:pointer;}
@media (hover:hover){.rot-hold:hover{border-color:var(--accent);color:var(--accent);}}
.rot-hold[hidden]{display:none;}
html:not(.js) .rot-hold{display:none;}
.steps{display:flex;flex-direction:column;gap: var(--s-6);padding-bottom: var(--s-6);}
.print-steps{display:flex;flex-direction:column;gap: var(--s-6);}
.print-steps[hidden]{display:none;}
/* Steps hide-then-reveal ONLY when JS is present (html.js set by script). */
.js .step{opacity:0;transform:translateY(18px);transition:opacity .7s ease,transform .7s ease;}
.js .step.on{opacity:1;transform:none;}
.step-no{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--accent);margin: 0 0 var(--s-2);}
.step h2{font-family:var(--font-display);font-weight:420;font-size: var(--t-7);
  line-height: calc(var(--s-1) * 12);margin: 0 0 var(--s-3);}
.step p{color:var(--ink-soft);margin: 0 0 var(--s-4);max-width:44ch;}

.chips{display:flex;flex-wrap:wrap;gap: var(--s-2);margin: 0 0 var(--s-3);padding:0;list-style:none;}
.chips li{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: normal;
  color:var(--ink);border:1px solid var(--rule);background:var(--paper-deep);
  border-radius:var(--pill);padding: var(--s-2) var(--s-2);}
.gaps{margin:0;padding:0;list-style:none;}
.gaps li{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);color:var(--quiet);
  padding: var(--s-1) 0 var(--s-1) var(--s-3);position:relative;}
.gaps li::before{content:'—';position:absolute;left:0;color:var(--mark);}
/* Scoped under .step so the 1.4rem top margin beats .step p (which is more
   specific than a bare .gaps-label and was silently zeroing it out). */
.step .gaps-label{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--mark);margin: var(--s-4) 0 var(--s-2);}
/* Given by the photographer — a third lane between shown (chips) and unknown
   (gaps): supplied on trust, marked so it is never read as the desk's finding. */
.step .given-label{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--accent);margin: var(--s-4) 0 var(--s-2);}
.given{margin:0;padding:0;list-style:none;}
.given li{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);color:var(--ink);
  padding: var(--s-1) 0 var(--s-1) var(--s-3);position:relative;}
.given li::before{content:'+';position:absolute;left:0;color:var(--accent);font-weight:600;}

.slips{display:flex;flex-direction:column;gap: var(--s-3);}
.slip{background:var(--day-paper);color:var(--day-ink);border: var(--hairline) solid var(--rule-soft);
  padding: var(--s-3) var(--s-3);}
.slip .claim{font-family:var(--font-body);font-weight:500;font-size: var(--t-4);line-height: calc(var(--s-1) * 7);margin: 0 0 var(--s-2);color:var(--day-ink);}
.slip .passage{font-style:italic;color:var(--day-quiet);font-size: var(--t-3);line-height: calc(var(--s-1) * 6);
  margin: 0 0 var(--s-3);}
.slip .src{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: normal;
  color:var(--day-quiet);margin:0;word-break:break-all;}

.artifact{background:var(--artifact);border: var(--hairline) solid var(--edge);padding: var(--s-4) var(--s-4);}
.artifact .overlay{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-weight:700;font-size: var(--t-2);
  letter-spacing: normal;color:var(--mark);margin: 0 0 var(--s-3);}
.artifact .body{font-family:var(--font-body);color:var(--ink);font-size: var(--t-4);
  line-height: calc(var(--s-1) * 8);margin:0;white-space:pre-line;}
.artifact .close{font-family:var(--font-display);font-style:italic;font-weight:420;
  font-size: var(--t-4);line-height: calc(var(--s-1) * 7);color:var(--ink);display:block;margin-top: var(--s-3);}
.verdict{display:flex;align-items:center;gap: var(--s-2);flex-wrap:wrap;margin-top: var(--s-4);}
.badge{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);font-weight:700;letter-spacing: normal;
  background:var(--good);color:var(--paper);padding: var(--s-1) var(--s-2);}
.voice-tag{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--quiet);}

/* ---- the desks (platform map) ---- */
.desks{padding:var(--section) 0 0;}
.desks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:1px;
  background:var(--rule);border: var(--hairline) solid var(--rule-soft);margin-top: var(--s-5);}
.desk{display:flex;flex-direction:column;gap: var(--s-2);background:var(--paper);
  padding: var(--s-4) var(--s-4) var(--s-4);text-decoration:none;color:inherit;}
@media (hover: hover){.desk:hover{background:var(--paper-deep);}}
.d-status{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;margin:0;}
.d-status.open{color:var(--good);}
.d-status.soon{color:var(--mark);}
.desk h3{font-family:var(--font-display);font-weight:420;font-size: var(--t-6);margin:0;line-height: calc(var(--s-1) * 8);}
/* .d-sub was inside the <h3>, so the heading's accessible name serialised with
   no separator: "FrameWorkThe Instagram desk". It is a label for the card, not
   part of the heading, so it now sits beside the heading and reads separately. */
.d-sub{display:block;font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--quiet);margin: var(--s-2) 0 0;}
.desk p{color:var(--quiet);font-size: var(--t-3);line-height: calc(var(--s-1) * 6);margin:0;flex:1;}
/* Finding 9. .d-cta is the conversion goal and rendered as a 309x17px text link
   sitting four pixels under .d-watch, a 117x44px control, at the same 10.6px
   type — the aside outranked the action. The pill and the touch floor move to
   the primary; .d-watch drops to plain text below. Each card now reads as one
   action plus one aside. */
.d-cta{display:inline-flex;align-items:center;align-self:flex-start;
  min-height:var(--touch-min);background:var(--accent);color:var(--paper);
  border:1px solid var(--accent);border-radius:var(--pill);
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;padding: var(--s-3) var(--s-3);text-decoration:none;
  transition:background .18s ease,border-color .18s ease;}
@media (hover: hover){.d-cta:hover,.desk:hover .d-cta{background:var(--ink);border-color:var(--ink);color:var(--paper);}}
.desk-actions{display:flex;flex-direction:column;gap: var(--s-2);margin-top: var(--s-2);}
.d-watch{align-self:flex-start;font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);
  letter-spacing: var(--track-caps);text-transform:uppercase;color:var(--quiet);
  background:none;border:0;padding:0;cursor:pointer;
  text-decoration:underline;text-underline-offset:3px;
  min-height:var(--touch-min);display:inline-flex;align-items:center;}
@media (hover: hover){.d-watch:hover{color:var(--accent);}}

/* ---- voices ---- */
.voices{padding:var(--section) 0 1rem;}
.voices-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:1px;
  background:var(--rule);border: var(--hairline) solid var(--rule-soft);margin-top: var(--s-5);}
.voices-grid .voice{background:var(--paper);padding: var(--s-4) var(--s-4) var(--s-4);}
.voices-grid .voice .fam{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--accent);margin: 0 0 var(--s-2);}
.voices-grid .voice h3{font-family:var(--font-display);font-weight:420;font-size: var(--t-6);margin: 0 0 var(--s-2);line-height: calc(var(--s-1) * 8);}
.voices-grid .voice p{color:var(--quiet);font-size: var(--t-3);line-height: calc(var(--s-1) * 6);margin:0;}
.voices-note{color:var(--ink-soft);max-width:52ch;margin: var(--s-5) 0 0;}

/* ---- the desk's memory (#teach anchor band) ---- */
/* The 3px teal left rail is retired suite-wide (DESIGN-SYSTEM.md:89). Carried
   as a rule line above instead, which is the house's own structural idiom. */
.suite-band{margin: var(--s-6) 0 0;border: var(--hairline) solid var(--rule-soft);border-top: var(--hairline) solid var(--accent);
  padding: var(--s-5) var(--s-4) var(--s-5);background:var(--paper-deep);}
.band-eyebrow{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--accent);margin: 0 0 var(--s-3);}
.band-line{color:var(--ink-soft);max-width:56ch;margin:0;}

/* ---- floor ---- */
.floor{margin:var(--section) 0 0;border: var(--hairline) solid var(--rule-soft);padding: var(--s-5) var(--s-4) var(--s-5);}
.floor-label{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--quiet);margin: 0 0 var(--s-4);}
.floor ul{margin:0;padding:0;list-style:none;max-width:56ch;}
.floor li{padding: var(--s-2) 0;border-bottom: var(--hairline) solid var(--rule-soft);color:var(--ink-soft);}
.floor li:last-child{border-bottom:none;}
.floor .bar{font-family:var(--font-display);font-style:italic;font-weight:420;
  font-size: var(--t-5);color:var(--ink);display:block;margin-top: var(--s-4);}

/* ---- footer ---- */
/* Full-bleed chrome, contents on the content column — the same rule the
   masthead follows, and the same fix it took on 2026-07-28. The footer sits
   OUTSIDE .shell so it can carry its rule edge to edge, but it was also
   carrying its text there: measured at 1710px the copyright line started at
   x=0 and "Powered by T.U.L.U. Logic" ended at x=1695, both jammed against the
   window with no gutter at all, while every other line on the page began at
   x=249. Ritu, 2026-07-30: "the footer on either side is getting cut."
   Moved onto the shared --frame 2026-08-03, which collapses to the gutter below
   its cap on its own, so the max(0px, ...) + gutter construction is gone. */
.foot{margin-top:var(--section-sm);
  padding:1.8rem calc((100% - var(--frame)) / 2) 2.6rem;
  border-top: var(--hairline) solid var(--rule-soft);
  display:flex;justify-content:space-between;align-items:center;gap: var(--s-3);flex-wrap:wrap;
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: normal;color:var(--quiet);}
/* Ritu, 2026-09-12: "Footers should be on the right. The copyright mark ...
   should always be on the right." This sheet's footer is a space-between row
   that wraps on a phone, so both lines landed left at 25 while the editorial
   tools' footers end right at 365. Below --phone-max it stacks to the right
   like theirs, using the gutter this rule already computes. The desktop row is
   untouched. */
@media (max-width:699.98px){
  .foot{flex-direction:column;align-items:flex-end;text-align:right;gap: var(--s-1);}
  .foot .copy{justify-content:flex-end;}

}

/* Copyright wordmark, miniature — the real SVG, lightened for the dark ground
   the same way the live tools do in Night (brightness/invert to a muted grey). */
.foot .copy img{height:18px;width:auto;vertical-align:middle;position:relative;top:-1px;
  filter:brightness(0) invert(1);opacity:.55;}

/* ============================================================
   THE WORKBENCH, same skin. Loaded after editorial-frame.css on
   social/workbench.html; the :root re-declarations above put the
   bench on the dark ground. Below: darkroom objects for the wb-*
   vocabulary the inline style leaves bare, and the cream research
   slips (the landing's citation object, same polarity inversion).
   ============================================================ */
.wb-bench-eyebrow{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);letter-spacing: var(--track-caps);
  text-transform:uppercase;color:var(--quiet);margin: 0 0 var(--s-2);}
.wb-text{color:var(--ink);}
.wb-caption{white-space:pre-line;line-height:1.6;}
.wb-hashtags{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-2);color:var(--accent);}
.wb-gaps ul{margin: var(--s-1) 0 0;padding:0;list-style:none;}
.wb-gaps li{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);color:var(--quiet);
  padding: var(--s-1) 0 var(--s-1) var(--s-3);position:relative;}
.wb-gaps li::before{content:'—';position:absolute;left:0;color:var(--mark);}
.wb-fixed{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);color:var(--quiet);}
.wb-audit ul{margin: var(--s-1) 0 0;padding-left: var(--s-3);font-size: var(--t-3);color:var(--warn);}
.wb-audit-ok{font-family: var(--font-display); font-size-adjust: var(--x-adjust);font-size: var(--t-1);color:var(--good);}
/* Research citations as daylight slips (scoped by id so the inline
   style's generic .wb-row keeps governing the SEE list). */
#researchList .wb-row{display:block;background:var(--day-paper);color:var(--day-ink);
  border: var(--hairline) solid var(--rule-soft);padding: var(--s-3) var(--s-3);margin: 0 0 var(--s-3);}
#researchList .wb-row .wb-text{color:var(--day-ink);}
#researchList .wb-row a{color:var(--navy);}
#researchList .wb-row .wb-passage{display:block;font-style:italic;color:var(--day-quiet);
  margin-top: var(--s-2);}

@media (max-width:52rem){
  .table-grid{grid-template-columns:1fr;}
  .print{position:static;}
  .steps,.print-steps{gap: var(--s-5);}
  /* This used to assert that every looping phrase wraps to exactly two lines at
     mobile widths and reserve two. Measured 2026-07-27, that is false below
     376px: at 360px one pair takes three lines and at 320px three of five do,
     producing a 22.7px jump every four seconds. The reserve is now measured
     from the real phrases in social.html; only the JS-off fallback is stated
     here, and it no longer claims anything it has not measured. */
  h1{min-height:var(--hero-reserve, 2.3em); font-size: clamp(var(--t-5), 5vw, var(--t-7));}
}
@media (prefers-reduced-motion:reduce){
  .js .step{opacity:1;transform:none;transition:none;}
  .loop{transition:none;}
  html{scroll-behavior:auto;}
}

/* Every select on a page loading this sheet alone (framework.html's Voice
   and Web research) carried no press of its own, measured 2026-09-10.
   --press-light: a select is a secondary control here, never the one loud
   action on the page. No rule in this sheet sets `transform` on `select`
   elsewhere, so the resting "out" transition below is never shadowed. */
select {
  transition: transform var(--press-out) var(--press-ease);
}
select:active:not(:disabled) {
  transform: scale(var(--press-light));
  transition: transform var(--press-in) var(--press-ease);
}

/* ============================================================
   HOUSE-STYLED DROPDOWN MENUS — de-tell, 2026-07-31
   Same move as editorial-frame.css (see its block for the full note): the
   open menu of a native <select> is the browser's — system font, blue bar —
   and reads as the AI-page tell. `appearance: base-select` (Chromium 135+)
   hands it to this sheet; Safari/Firefox keep the system menu, unharmed.
   Desk-after-dark voice: the menu is an elevated panel (--paper-deep), the
   highlight is the teal on dark ink, never the system blue.
   ============================================================ */
@supports (appearance: base-select) {
  /* Tool-page styles (framework.html .fw-field select) pin `appearance:
     none` for the fallback path; the opt-in must win by weight. */
  select { appearance: base-select !important; }
  ::picker(select) { appearance: base-select; }
  select::picker-icon { display: none; } /* pages draw their own chevron */

  ::picker(select) {
    background: var(--paper-deep);
    color: var(--ink);
    border: 1px solid var(--rule);
    border-radius: 0;
    box-shadow: 0 12px 32px -12px var(--overlay-bg);
    padding: var(--s-1) 0;
    scrollbar-width: thin;
    scrollbar-color: var(--rule) transparent;
    transform-origin: top;
    transition: opacity 150ms cubic-bezier(0.23, 1, 0.32, 1),
                transform 150ms cubic-bezier(0.23, 1, 0.32, 1),
                display 150ms allow-discrete, overlay 150ms allow-discrete;
  }
  @starting-style {
    ::picker(select) { opacity: 0; transform: translateY(-3px) scale(0.98); }
  }

  select option {
    padding: var(--s-2) var(--s-3);
    cursor: pointer;
  }
  select option::checkmark { display: none; }
  select option:checked {
    text-decoration: underline;
    text-underline-offset: 0.22em;
    text-decoration-thickness: 1px;
    text-decoration-color: var(--accent);
  }
  select option:focus-visible { outline: none; }
  select option:focus-visible,
  select option:active {
    background: var(--accent);
    color: var(--paper);
  }
  @media (hover: hover) and (pointer: fine) {
    select option:hover { background: var(--accent); color: var(--paper); }
  }
  @media (max-width: 520px) {
    select option { min-height: var(--touch-min); display: flex; align-items: center; }
  }
  @media (prefers-reduced-motion: reduce) {
    ::picker(select) { transition: opacity 120ms ease; }
    @starting-style {
      ::picker(select) { opacity: 0; transform: none; }
    }
  }
}


/* ============================================================
   HOUSE-STYLED FILE CONTROL — de-tell 2026-07-31, Ritu's ruling.
   The workbench's visible file input rendered the browser's raw
   "Choose File" button. The button becomes the house quiet control
   (mono small-caps pill, hairline rule); the filename line reads
   as the bench's own annotation.
   ============================================================ */
input[type="file"] {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-2);
  letter-spacing: normal;
  color: var(--quiet);
  max-width: 100%;
}
input[type="file"]::file-selector-button {
  font-family: var(--font-display); font-size-adjust: var(--x-adjust);
  font-size: var(--t-1);
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--ink);
  background: var(--paper-deep);
  border: 1px solid var(--rule);
  border-radius: var(--pill);
  padding: var(--s-2) var(--s-3);
  margin-right: var(--s-2);
  min-height: var(--touch-min);
  cursor: pointer;
  transition: border-color .2s ease, color .2s ease;
}
@media (hover: hover) {
  input[type="file"]::file-selector-button:hover {
    border-color: var(--accent);
    color: var(--accent);
  }
}
input[type="file"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ============================================================
   HOUSE-STYLED CHECKS — same de-tell sweep as the dropdown block
   above (2026-07-31); full note in editorial-frame.css. Hairline
   box, accent fill, glyph in the page's own paper; --check-accent
   / --check-glyph let a page recolour a lane without falling back
   to the native control.
   ============================================================ */
input[type="checkbox"], input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px; height: 16px; flex: none;
  display: inline-grid; place-content: center;
  background: transparent;
  border: 1px solid var(--quiet);
  border-radius: 0;
  vertical-align: -3px;
  margin: 0;
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 120ms ease-out;
}
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:checked, input[type="radio"]:checked {
  background-color: var(--check-accent, var(--accent));
  border-color: var(--check-accent, var(--accent));
}
input[type="checkbox"]::after {
  content: ""; width: 4px; height: 8px; margin-top: -2px;
  border-right: 2px solid var(--check-glyph, var(--paper));
  border-bottom: 2px solid var(--check-glyph, var(--paper));
  transform: rotate(45deg) scale(0.6);
  opacity: 0;
  transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), opacity 120ms ease-out;
}
input[type="checkbox"]:checked::after { transform: rotate(45deg) scale(1); opacity: 1; }
input[type="radio"]::after {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--check-glyph, var(--paper));
  transform: scale(0.6);
  opacity: 0;
  transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), opacity 120ms ease-out;
}
input[type="radio"]:checked::after { transform: scale(1); opacity: 1; }
@media (hover: hover) and (pointer: fine) {
  input[type="checkbox"]:hover, input[type="radio"]:hover { border-color: var(--accent); }
}
input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  input[type="checkbox"]::after, input[type="radio"]::after { transition: opacity 120ms ease; }
}


/* Per-band text sizes, appended by preview/build-precision.mjs
   --apply-type. The product's root font is a clamp, so one rem is a
   different number of pixels at each width and a single mapped value
   would be wrong everywhere except the width it was mapped at. These
   carry identical specificity to the rule above and come later, which is
   how the narrower band wins. */
@media (max-width: 620px) {
.lede {
  font-size: var(--t-4);
  /* THE LEADING COMES DOWN WITH THE TYPE. This block dropped the font from
     23px to 19px and left line-height at nine steps of the baseline grid, so
     the same sentence read at 1.57 on a desktop and 1.89 here; FrameWork's own
     sheet takes the font to 16.34px and it reached 2.20. Ritu, 2026-09-13,
     with the two social tools side by side: "Look at the difference in the gap
     between sentences in the description on either", and "I've been reiterating
     that symmetry is the essence of this project."

     Seven steps, 28px, is 1.47 against this font: the grid is kept, which is
     the point of writing leading as a multiple of it, and the ratio lands where
     .hook-sub has always been. A font-size step in a media query has to carry
     its leading with it or the two drift apart at exactly the width where the
     text is hardest to read. */
  line-height: calc(var(--s-1) * 7);
}
}
@media (max-width: 620px) {
.demo-input {
  font-size: var(--t-3);
}
}
@media (max-width: 620px) {
.hero-hold {
  font-size: var(--t-4);
}
}
@media (max-width: 620px) {
.step h2 {
  font-size: var(--t-6);
}
}
@media (max-width: 620px) {
.slip .claim {
  font-size: var(--t-3);
}
}
@media (max-width: 620px) {
.artifact .body {
  font-size: var(--t-3);
}
}
@media (max-width: 620px) {
.desk h3 {
  font-size: var(--t-5);
}
}
@media (max-width: 620px) {
.voices-grid .voice h3 {
  font-size: var(--t-5);
}
}
@media (max-width: 620px) {
.wb-audit ul {
  font-size: var(--t-2);
}
}
@media (max-width: 430px) {
.skip-link {
  font-size: var(--t-1);
}
}
@media (max-width: 430px) {
body {
  font-size: var(--t-3);
}
}
@media (max-width:640px) {
@media (max-width: 430px) {
.mast-nav-link {
  font-size: var(--t-1);
}
}
}
@media (max-width: 430px) {
.cta {
  font-size: var(--t-1);
}
}
@media (max-width: 430px) {
.slip .passage {
  font-size: var(--t-2);
}
}
@media (max-width: 430px) {
.desk p {
  font-size: var(--t-2);
}
}
@media (max-width: 430px) {
.voices-grid .voice p {
  font-size: var(--t-2);
}
}
@media (max-width: 430px) {
.wb-hashtags {
  font-size: var(--t-1);
}
}
@media (max-width: 430px) {
input[type="file"] {
  font-size: var(--t-1);
}
}

/* ── The clip and the mic, on a Social box (2026-09-09) ────────────────────
   Ritu, 2026-09-08: "I want this functionality where every box will accept a
   word, PDF, and audio", and 2026-09-09: "connect Social to the voice
   functionality in Editorial and Corporate."

   .box-tools / .box-tool / .box-tools-msg are editorial-frame.css's classes,
   and that sheet is not loaded by framework.html or social.html: this sheet is
   the Social suite's whole theme (see the file header). So the rules are
   restated here in Social's own tokens, exactly as corporate.css restated them
   on 2026-09-08, rather than pulling in the newsroom frame whole for one pair
   of marks. scroll-desk.html needed none of this: it already loads
   editorial-frame.css, so its marker uses the original rules.

   PORTED, NOT REINVENTED. Every number below is editorial-frame.css's, and
   each one is a measured difference rather than a preference. Re-measure with
   scripts/box_tools_align_check.mjs before changing any of them; that gate
   finds this page by scanning for the marker, so it checks these rules
   without being told they exist.

   (1) margin-right pulls the row back by the 14px of empty tap target that
       sits outside a 16px glyph centred in a 44px button, so the MARK lines up
       with the content edge rather than the button doing so. Ritu, 2026-08-20:
       "the alignments are awry."
   (2) margin-left:auto puts the pair on the box's right edge, so a shared
       affordance sits at the same x on every surface in the product.
   (3) The ::after is the part that answers the pointer, and it stops short of
       the button's bottom edge. Without it the lower 6px of the button lies
       over the top of the textarea and a click in the box's top right corner
       opens the file picker instead of putting the caret in the field. */
.box-tools {
  display: flex; align-items: center; gap: 2px;
  width: fit-content; margin-left: auto; flex: none;
  margin-right: calc((var(--touch-min) - var(--t-3)) / -2);
  position: relative;
  /* top: 0, where editorial-frame.css carries top: 2px on a label row. That
     2px is a correction for a mark centred on a one-line row; the anchoring
     below is flex-end, so the correction is not just unnecessary, it is wrong
     by exactly 2px and the gate said 6 where it wanted 8. */
  top: 0;
  pointer-events: none;
  /* (4) ON A LABEL ROW THE PAIR TAKES NO HEIGHT AT ALL, and this is the rule
     I left out of the first port. The gate caught it at once: 27px above the
     box where every other mark in the product sits at 8. A 44px control on an
     11px label row is the tallest thing in it, so the row grew to the control
     and pushed the box down. Zero height, and the buttons overflow the row
     equally in both directions into margins that are empty in both. The 44px
     tap area is untouched: an overflowing child is still clickable everywhere
     it is drawn. Straight from editorial-frame.css:1302. */
  height: 0;
  /* flex-end, NOT the centre editorial uses. Editorial's label rows are one
     line by construction; FrameWork's label wraps to two below 430px, and a
     centred zero-height marker then drifts with the label's height: measured
     11px above the box at seven widths and 27px at 390 and 360. The mark's
     relationship is to the BOX under it, not to the words beside it, so it
     anchors to the row's bottom edge and the gap stops depending on how many
     lines the label takes. */
  align-self: flex-end;
}
.box-tools .box-tool { margin: calc(var(--touch-min) / -2) 0; }
.box-tool {
  appearance: none; background: none; border: 0; padding: 0;
  width: var(--touch-min); height: var(--touch-min);
  margin: calc(var(--s-2) * -1) 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--pill); cursor: pointer;
  font-size: var(--t-3); line-height: 0; flex: none;
  position: relative; pointer-events: none;
  transition: color 160ms cubic-bezier(0.23, 1, 0.32, 1),
              background-color 160ms cubic-bezier(0.23, 1, 0.32, 1),
              transform var(--press-out) var(--press-ease);
}
.box-tool::after {
  content: ''; position: absolute;
  inset: 0 0 calc((var(--touch-min) - var(--t-3)) / 2 - var(--s-2) + 1px) 0;
  pointer-events: auto;
}
@media (hover: hover) { .box-tool:hover { --ico-color: var(--ink); } }
.box-tool:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Attach and Dictate carried no press at all before this (measured 2026-09-10
   against a mic and a clip on this exact page). --press-light, the same
   depth editorial-frame.css gives this class: pointer-events sit on the
   ::after above, not on the button itself, but :active still reads on the
   host element the hit resolves to. */
.box-tool:active:not(:disabled) {
  transform: scale(var(--press-light));
  transition: color 160ms cubic-bezier(0.23, 1, 0.32, 1),
              background-color 160ms cubic-bezier(0.23, 1, 0.32, 1),
              transform var(--press-in) var(--press-ease);
}
/* Recording. The fill says so; the movement is the second half of saying it. */
.box-tool[aria-pressed="true"] {
  background: var(--accent); --ico-color: var(--paper);
  animation: box-tool-breath 1.6s cubic-bezier(0.37, 0, 0.63, 1) infinite;
}
@keyframes box-tool-breath {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.09); }
}
@media (prefers-reduced-motion: reduce) {
  .box-tool[aria-pressed="true"] { animation: none; }
}
/* A file that would not read, or a beat of progress worth naming. Silence on
   failure would leave the reporter waiting for words that are never coming. */
.box-tools-msg {
  display: none; font-family: var(--font-body); font-size: var(--t-2);
  font-style: italic; letter-spacing: 0; text-transform: none;
  color: var(--warn); margin: 0 0 var(--s-2);
}
.box-tools-msg.is-shown { display: block; }
.box-tools-msg.is-progress { color: var(--quiet); }
/* The offer to send a lost stretch of recording again. A word in a sentence,
   not a mark on a row, so it takes the line's own type and underlines itself. */
.box-tools-again {
  appearance: none; background: none; border: 0; padding: 0;
  margin-left: var(--s-2); font: inherit; color: inherit;
  text-decoration: underline; cursor: pointer;
}
.box-tools-again:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.box-tools-again:disabled { cursor: default; opacity: 0.5; }

/* ============================================================
   THE PHONE LAYER on this sheet (Task 14 fix round 2,
   2026-09-11). The first @media (max-width: 699.98px) block in
   this file. 699.98px is --phone-max, the one edge every phone
   rule on this branch reads; the literal is written out because
   a media query cannot read a custom property, and declaring
   the token here unused would be a declaration that overrides
   nothing. Nothing from editorial-frame.css is referenced: the
   two systems stay apart, and this rule sets geometry only.
   ============================================================ */
@media (max-width: 699.98px) {
  /* The masthead's measured height at phone width, read by the pinned hero on
     the desks this sheet serves and by nothing else. The same value and the
     same name as editorial-frame.css, for the same reason --gutter is declared
     three times: no stylesheet is loaded by every page. */
  :root {
    --phone-mast-h: 60px;
    --phone-hero-h: 62px;
  }
  /* FRAMEWORK'S WRITE IT HOLDS THE COLUMN ITS OWN FIELDS HOLD.
     Ritu, 2026-09-11: "The alignment of the buttons is all over the place."
     Reported by scripts/phone_balance_check.mjs once finding 5 could
     anchor a column on a page with no .main: FrameWork puts its column on
     <main class="shell">. Measured at 390: the six fields above this
     button (What, Where, When, the box, Voice and Web research) each hold
     24.4..365.6, and Write it held 24.4..163.1, 202.23px short of the
     edge every one of them keeps. 173.05px short at 360, 223.84px at 412.

     justify-content as well as width, for the same reason recorded on
     .phone-lead-action and .feed .cta in editorial-frame.css: widening a
     shrink-to-fit button leaves its words at the left of a box they used
     to fill. .cta's own padding puts them 24px in, which reads as a label
     stuck to one end of a 341px bar.

     .fw-write-row scopes this to FrameWork. That class is declared in
     framework.html's own <style> and appears nowhere else; social.html
     loads this sheet too and carries no such row. */
  .fw-write-row .cta {
    width: 100%;
    justify-content: center;
  }
}

/* THE NARROWEST SCREEN, 2026-09-13. The same step editorial-frame.css carries,
   and it must be the same in all three root sheets or the wordmark's own inset
   drifts between surfaces: declared in that sheet alone, wordmark_frame_check
   reported the left edge 8px apart across surfaces at 320. The reasoning is
   written out once, at the foot of assets/css/editorial-frame.css.
   tests/frame-token.test.js keeps these three in step. */
@media (max-width: 349.98px) {
  :root { --gutter: 1rem; }

  /* AND THE LAST SIXTEEN PIXELS AT THE NARROWEST SCREEN, 2026-09-15.
     The gap gave three at 358 and the row holds one line from there up. At 320
     it does not: the gutter step above leaves 288 of usable row, the wordmark
     takes 110.4, the gap 16 and the gear 44 with its own 16 gap, so the pill is
     left 101.6 where it needs 117.6 and wraps to two lines, taking the masthead
     from 60px to 74.

     THE SIXTEEN COME FROM QUANTITIES, NOT FROM THE DESIGN. It is the same
     answer the Build Blueprint pill was given at this width on 2026-09-13, and
     for the same reason: his words stay, the pill stays, and the shortfall is
     taken from the tracking and the padding, which are numbers rather than
     parts of the design. The group's own gap halves as well, which costs
     nothing at a width where two controls sit alone in the row.

     Measured after: 44px pill, 60px masthead, one line, at 320. */
  .mast-right,
  .masthead-utility { gap: var(--s-2); }
  .mast-right .mast-nav-link,
  .masthead-utility .mast-nav-link { padding-inline: var(--s-1); letter-spacing: 0.08em; }
}

/* ============================================================
   THE SETTINGS DRAWER ON THE SOCIAL DESKS, 2026-09-15.

   Ritu, of the Scroll Desk: "How about having that panel we pushed down as a
   SETTINGS option, as in the editorial tools?", and then, of FrameWork: "Same
   for FrameWork's Voice and Web Research options."

   WHY THE RULES ARE HERE AND NOT BORROWED. .settings-panel, .settings-backdrop
   and .settings-toggle are declared in assets/css/editorial-frame.css and
   nowhere else. The Scroll Desk loads that sheet and takes them from it.
   FrameWork does not load it, and the first build of this showed exactly what
   that means: the drawer painted as a block of unstyled text at the top of the
   page. The two ways out of that were to pull an editorial sheet into a social
   page, which crosses the line this product keeps between the two suites, or to
   declare the drawer in the social system's own sheet. This is the second.

   THE DECLARATIONS ARE THE EDITORIAL ONES, copied, not redesigned: a reader who
   has opened settings on DeskCopy Pro must find the same object here. Every
   value is a token this sheet already declares, so the drawer comes out in the
   social palette without a single colour being written twice.

   THE DESKTOP DOES NOT HAVE ONE. He was speaking of the phone, and on a desktop
   these controls sit in the right-hand column of a two-column desk where they
   read correctly. So the wrapper is display: contents above the breakpoint,
   which takes its box away and hands the fields back to the column they were in.
   ============================================================ */
.settings-panel { display: contents; }
.settings-panel > h2 { display: none; }
.settings-backdrop { display: none; }
/* TWO CLASSES, AND THE REASON IS A MEASURED LOSS. `.settings-toggle` alone is
   one class, and icons.css carries `button:has(> .ico) { display: inline-flex }`
   at one element plus one class, which outranks it and loads after this sheet.
   The first build of this showed the gear on the 1440 desktop, where the drawer
   is display: contents and the button therefore opens nothing: the exact defect
   the 2026-08-17 cull removed from the Scroll Desk. */
.mast-right .settings-toggle,
.masthead-utility .settings-toggle { display: none; }

@media (max-width: 699.98px) {
  .mast-right .settings-toggle,
  .masthead-utility .settings-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--touch-min);
    min-height: var(--touch-min);
    padding: 0;
    background: transparent;
    border: 1px solid var(--rule);
    border-radius: var(--pill);
    color: var(--ink-soft);
    cursor: pointer;
    transition: border-color 0.18s ease, color 0.18s ease,
                transform var(--press-out) var(--press-ease);
  }
  .settings-toggle.active { border-color: var(--accent); color: var(--accent); }
  .settings-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .settings-toggle:active:not(:disabled) {
    transform: scale(var(--press-light));
    transition: border-color 0.18s ease, color 0.18s ease,
                transform var(--press-in) var(--press-ease);
  }
  /* The word goes, the mark stays. Ritu's rule of 2026-08-20: a mark everyone
     reads ships with no word and no pill, keeping its title and aria-label and
     the 44px floor. editorial-frame.css does the same to this button at this
     width, so the gear is the same object on all eight desks. */
  .settings-toggle .btn-label {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }

  /* AND THE SAME TO "ALL TOOLS", 2026-09-20. Ritu's ruling, from three rendered
     versions of this bar at 358: the word goes and the mark stays, matching the
     gear beside it.

     WHY IT HAD TO CHANGE AT ALL. The account capsule above it was rendering
     unstyled, in Arial, at 55px, which made the bar LOOK like it fitted. Drawn
     correctly it is 62px, and the four controls then needed 374px of a 341px
     box at 390. MEASURED before: 24px over at 358, 33px over at 320, and the
     capsule was clipped at the screen edge on his own phone. MEASURED after:
     fits at 320, 358, 360, 390 and 412.

     The gap note below already refused to spend his words to make room, and was
     right to: that was three pixels and this is thirty-three. The title and the
     aria-label carry the words now, and the 44px floor is kept, which is his
     rule of 2026-08-20 for a mark that ships without one.

     The mark is `tools` in shell/icons.json, drawn in that set's own language
     (16px box, no fill, currentColor at 1.5, round caps) rather than pasted in
     from anywhere. */
  .mast-right .mast-nav-link {
    width: var(--touch-min);
    min-width: var(--touch-min);
    min-height: var(--touch-min);
    padding: 0;
    justify-content: center;
  }
  .mast-right .mast-nav-link .btn-label {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }

  /* AND THE ROOM THE GEAR TAKES COMES FROM THE GAP, NOT FROM HIS WORDS.
     MEASURED at 358 with the gear in the row: the masthead went from 60px tall
     to 74 because "All tools" wrapped to two lines inside its pill, which grew
     from 44px tall to 64. The row is 358 wide, its own padding takes 48.4, the
     wordmark 110.4 and this gap 24, leaving 175.2 for the right-hand group;
     the gear takes 44 and the group's own gap 16, so the pill was left 115.2
     and needed 117.6, which is what it measures at 390 where it holds one line.

     THREE PIXELS. So the gap gives them rather than the label: --s-3 instead of
     --s-4 below 700px, which is 16 against 24, and the pill gets 123.2. The
     words are his and they stay. Measured after: 44px pill, 60px masthead, one
     line, at 320, 358, 390 and 412. */
  .masthead { gap: var(--s-3); }

  .settings-backdrop { position: fixed; inset: 0; background: var(--overlay-bg); z-index: 998; }
  .settings-backdrop.visible { display: block; }

  .settings-panel {
    display: none;
    position: fixed;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: min(92vw, 820px);
    max-height: 85vh;
    overflow-y: auto;
    z-index: 999;
    background: var(--paper);
    border: var(--hairline) solid var(--rule-soft);
    border-top: 3px solid var(--brand-rule);
    /* var(--s-3), 16px, NOT var(--s-5)'s 40. MEASURED at 320 and 358 with the
       drawer open: 40px each side left the Voice menu 209.2 and 244.1px of
       text, and "Brand desk. Documentary house style" needs 255.5, so the
       longest voice was sliced on both. 16px gives 273.2 and 308.1. The
       editorial drawers open on a phone at the page's own gutter for the same
       reason: a panel that is nearly the whole screen does not also need a
       desk's margins. */
    padding: var(--s-3);
    box-sizing: border-box;
  }
  .settings-panel.visible { display: block; }
  .settings-panel > h2 {
    display: block;
    font-family: var(--font-display);
    font-size: var(--t-6);
    letter-spacing: var(--track-tight);
    color: var(--ink);
    margin: 0 0 var(--s-3);
  }
}


/* ── THE SOCIAL MASTHEAD ON A NARROW SCREEN ─────────────────────────────────
   AT THE FOOT DELIBERATELY. These two blocks have the same specificity as the
   @media (max-width: 699.98px) rule above that sets .masthead { gap: --s-3 }.
   Written earlier in the sheet they lost to it, and 320 stayed 5px over while
   the numbers said it should fit. Source order is the whole mechanism here.

   MEASURED 2026-09-20, framework.html, after "All tools" became a mark:
     width   room   needs  (wordmark 110 + 3 gaps + mark 44 + gear 44 + capsule)
     320     288    284    with the 8px gap and the initial
     358     310    308    with the 16px gap and the initial
     390     341    333    with the 16px gap and the full name
     412     362    333    the same
   Every one fits. Before this work the capsule was clipped at 320, 358 and 360. */

/* THE NAME BECOMES ITS INITIAL BELOW 390, and the threshold is measured, not
   chosen. The capsule is 87px with a name and 62px with a letter. At 390 the
   bar has 341px and needs 333 with the name, so the name stays. At 360 it has
   312 and needs 333, so it cannot. 390 is where that crosses.
   account.css draws both; this only says which one this bar can afford. */
@media (max-width: 389.98px) {
  .masthead .account .account__full { display: none; }
  .masthead .account .account__initial { display: inline; }
}

/* AND THE LAST FIVE PIXELS AT 320. Only here: 358 keeps the 16px gap it was
   rendered and approved with, so the bar he ruled on is the bar that ships. */
@media (max-width: 340px) {
  .masthead { gap: var(--s-2); }
  .masthead .mast-right,
  .tool-masthead .masthead-utility { gap: var(--s-2); }
}
