/* ============================================================================
   THE TOOL PAGE'S SCALE — one size for 92 hand-authored pdf and image tools
   ----------------------------------------------------------------------------
   Two sections: the drop zone, and the type scale around it. It was called
   drop-zone.css for its first hour, until measuring the 92 pages showed the
   oversized headings were the same request wearing a different hat.
   ----------------------------------------------------------------------------
   Owner brief, 2026-09-01, with two screenshots of the app: "make the drop area
   for image tools and pdf tools more premium and medium size so user gets a
   premium application feel."

   MEASURED FIRST, IN BRAVE AT 390x844, BEFORE ANY NUMBER HERE WAS CHOSEN:

     page                    zone h   % of viewport   title   icon   button
     pdf-to-jpg               347px        41%         24px   64px   155x42
     merge-pdf                378px        45%          --    48px   184x46
     jpg-to-webp              313px        37%         24px    --    121x39
     heic-to-pdf              252px        30%         24px    --    152x42
     image-compressor         224px        27%          --    48px     --
     compress-pdf             210px        25%        17.6px  46px     --

   The spread is the defect. The same control is 210px on one page and 378px on
   another, and the two that read as "an app" in the screenshots are the two at
   the bottom of that table. So the target is not invented: it is compress-pdf's
   proportions applied to the other 91 pages -- ~200px, a 17px title, a 40px
   glyph. "Medium size" turned out to be a number the site already had.

   WHAT THIS FILE MAY AND MAY NOT DO
   ---------------------------------
   A site-wide cosmetic layer over .tool-scope was built and reverted inside one
   day on 2026-08-03: "you made the whole area blue", "ugly design", "dropdowns
   are invisible". memory/traps/glass-design-layer.md is the record, and the
   cause it names is that 45 tools have 45 designs, so one skin averages to
   something worse than any of them.

   That failure was about COLOUR, and this file therefore sets none. No
   background, no border-color, no box-shadow colour, no text colour. Every
   surface here keeps whatever the page already painted; what changes is how big
   it is. The four colour tokens the zone needs are read from the page itself --
   --card-border, --accent-cyan, --text-muted, --text-main -- and all four are
   defined on all 44 pdf-tools and all 48 image-tools pages, verified by grep,
   so the zone stays that page's colour and cannot average into a blue wash.

   WHY THE DECLARATIONS ARE !important, WHICH IS NOT A SHORTCUT
   -----------------------------------------------------------
   Measured across the 92 pages: 264 of the 347 rules that select a drop zone
   are two-class selectors and 71 are three-class. Eight are single-class. A
   stylesheet linked last in <head> ties and wins only against those eight; it
   loses to the other 339 on specificity no matter where it sits.

   The alternatives were to add a markup hook to 92 hand-authored pages, or to
   chain ancestors this file cannot know. Both are worse. Templates/partials/
   dev-glass.css already resolves the identical problem the identical way.

   The rule that keeps it honest: !important appears ONLY on geometry and type
   -- size, spacing, radius, weight. If you are about to mark a colour important
   here, you are writing the 2026-08-03 layer again.

   LOADED BY: a <link> inserted last in <head> on every pdf-tools and image-tools
   page by scripts/apply-tool-scale.mjs. Re-run that script after adding a page.
   ========================================================================= */

/* ═══════════════════════════════════════════════════════════════════════════
   THIS SECTION APPLIES BELOW 900px ONLY.

   ROUND TWO ADDED TWO MORE SECTIONS AND THIS HEADING USED TO SAY "EVERYTHING IN
   THIS FILE". It no longer does, and the correction matters because the reason
   below is still exactly right for SIZE and was never right for surface:

     below 900px  — the phone scale (this block).      Round one.
     at 900px+    — the desktop geometry band.         Round two, and it exists
                    because "uneven" was a separate complaint from "too big".
     every width  — the surface (fill, rim, guide, plate, CTA). Round two. A
                    gradient and an inset highlight are proportional; a 188px
                    min-height is not, which is the whole reason the two are not
                    in the same block.

   The complaint was about a phone, every number in it was measured on a
   390x844 phone, and the desktop column was laid out by hand at its own scale.
   Rendered at 1280px the mobile scale reads as undersized -- a 17px drop title
   in a 1050px-wide card -- which would be answering a question nobody asked
   while putting all 92 pages at risk on the width Google actually renders.

   THE FIRST ATTEMPT DID THIS THE OTHER WAY ROUND AND THE MECHANISM WAS WRONG.
   It applied the scale at every width and undid it above 900px with
   `font-size: revert`. That does not restore the page's own value: revert rolls
   a property back to the previous CASCADE ORIGIN, which is the user-agent
   stylesheet, so every h2 on desktop would have gone to the browser default
   1.5em rather than back to what its page designed. An author-level value that
   has been overridden is not reachable that way at all. Not applying the rule
   is the only correct form.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 899px) {
  /* ── THE ZONE ITSELF ───────────────────────────────────────────────────────
     min-height rather than height, because several tools swap the zone's contents
     for a file list in place and a fixed height would clip it. max-height is
     deliberately absent for the same reason. */
  /* ROUND TWO EXTENDED THIS LIST, AND THE REASON IS A MEASUREMENT.
     The six classes on the first line were round one's ENTIRE list. Enumerating
     every class attribute across the 92 files afterwards found eleven more one-off
     containers and six zones addressed only by id, so EIGHTEEN PAGES never got the
     phone scale at all -- and nothing in this file said so. Reading the CSS could
     not have told you; only counting the pages could.

     `#drop-zone` is not a typo for `.drop-zone`. extract-pdf-pages writes
     `<label id="drop-zone">` and long-screenshot-to-pdf `<div id="drop-zone">`, and
     a class selector never matches an id, which is why both measured as having no
     zone at all.

     scripts/lib/drop-zones.mjs is the source of truth and
     tests/tool-scale/registry.verify.mjs fails if this file stops mentioning
     anything listed there. ADD THE SELECTOR THERE FIRST. */
  .drop-zone,
  .upload-zone,
  .upload-area,
  .cp-drop,
  .wp-drop,
  .massive-drop-zone,
  .cr-drop,
  .p2w-drop,
  .pdm-drop,
  .mt-drop,
  .rw-drop,
  .ts-dropzone,
  .upload-section,
  .upload-view,
  .upload-container,
  .initial-upload-view,
  .drop-zone-container,
  .upload-dropzone,
  .drop-area,
  .drop-target,
  #drop-zone,
  #upload-area,
  #upload-view,
  #ps-upload,
  #initial-upload-zone {
    min-height: 188px !important;
    /* ROUND ONE REFUSED A max-height HERE AND SAID SO IN THE COMMENT ABOVE.
       That refusal was right at the time and is now reversed, deliberately.

       Its reason -- "several tools swap the zone's contents for a file list in
       place and a fixed height would clip it" -- is a real risk and the honest
       answer to it is evidence, not abstention. tests/tool-scale/no-clip.verify.mjs
       asserts scrollHeight <= clientHeight on every zone at three widths, so a cap
       that would clip a tool fails the build with that tool's name on it.

       Without the cap, extending the selector list left four newly-reached pages
       far outside the band on a phone -- pdf-dark-mode 390px, edit-pdf-metadata
       345, webp-to-pdf 327, pdf-to-word 323 -- which is 46% of an 844px viewport
       and exactly the complaint round one was answering for everyone else. */
    max-height: 260px !important;
    padding: 22px 18px !important;
    border-radius: 18px !important;
    border-width: 1.5px !important;
    gap: 10px !important;
  }

  /* A ZONE THAT CONTAINS ANOTHER ZONE IS A WRAPPER, NOT A TARGET.
     Five pages nest one match inside another -- split-pdf-using-size's #uploadView
     holds #dropZone, png-to-pdf's #initialUploadView holds #initialDropZone,
     image-to-base64's .upload-section holds #dropZone, tiff-to-jpg's
     .drop-zone-container holds #dropZone. Styling both draws the surface twice,
     one box inside an identical box, which is precisely the "multiple containers"
     the owner asked to remove.

     :has() is the only way to say this in CSS, and it is already used in this file
     (the label rule below), so the support floor is unchanged. */
  .upload-section:has(.drop-zone),
  .upload-view:has(.drop-zone),
  .upload-container:has(.drop-zone),
  .initial-upload-view:has(.massive-drop-zone),
  .initial-upload-view:has(.drop-zone),
  .drop-zone-container:has(.drop-zone),
  .drop-area:has(.drop-zone),
  .drop-target:has(.drop-zone) {
    min-height: 0 !important;
    max-height: none !important;
    padding: 0 !important;
    border: 0 !important;
    background: none !important;
    box-shadow: none !important;
  }

  /* TWO EXISTING PATTERNS THIS FILE MUST NOT FIGHT, both found by measuring all
     92 pages rather than by reading them.

     .compact-upload -- 48 pages. It is the zone's LOADED state: once a file is
     in, the full-size drop target is dead weight but still has to stay reachable
     for adding another, so the page collapses it to a strip. Its own rule is
     `.tool-scope .compact-upload{min-height:0!important}`, which outranks the
     block above today purely because it carries one more class. Relying on that
     is relying on an accident, so the exemption is written down.

     AND WRITING IT DOWN AS CLASSES WAS NOT ENOUGH -- owner report, 2026-09-02,
     with a screenshot of image-converter: a 188px dashed box holding one lone
     Browse Files button, sitting above the settings it should have got out of
     the way of. The block above matches `#upload-area`, an ID, and an ID beats
     any number of classes even when both carry !important. So on the five zones
     addressed by id the collapse never collapsed: min-height stayed 188px and
     the padding stayed 22px, measured. The exemption now names them too.

     The lesson is the one this file keeps re-learning: a selector list that
     mixes classes and IDs cannot be exempted by a class-only rule. Any future
     opt-out from the base block has to repeat the IDs.

     position:absolute zones -- image-rotator's .upload-zone is inset:0 over the
     preview stage, so it takes its height from the stage (measured: 420px) and
     nothing here applies. Left alone deliberately: forcing that one smaller would
     uncover the canvas behind it. */
  .compact-upload,
  .drop-zone.compact-upload,
  .upload-zone.compact-upload,
  #drop-zone.compact-upload,
  #upload-area.compact-upload,
  #upload-view.compact-upload,
  #ps-upload.compact-upload,
  #initial-upload-zone.compact-upload {
    min-height: 0 !important;
    /* ROUND TWO ADDED A max-height TO THE BLOCK ABOVE, and the collapsed state has
       to opt out of it as explicitly as it opts out of the minimum. Six pages ship
       .compact-upload as their INITIAL state (autobrightnesscontrast 60px,
       image-resizer 22px, gif-to-webp 112, heic-to-jpg 114, heic-viewer 79,
       svg-to-png 65), so this is not a rare branch. */
    max-height: none !important;
    padding: 10px 12px !important;
  }

  /* ── THE TITLE — the single biggest win in the file ────────────────────────
     `h2.drop-zone-title` occurs 52 times across the 92 pages, at 24px. It is the
     "Drag & Drop PDF here" in the owner's screenshot and it is the reason the
     control reads as a landing page rather than as an app. 17px is compress-pdf's
     measured 17.6 rounded to the scale the rest of this file uses. */
  .drop-zone-title,
  .upload-title,
  .cp-drop-title,
  /* The heading by position as well as by name. webp-to-pdf puts a bare <h2>
     inside .wp-drop with no class at all, and it was the one page the audit
     caught still sitting above the band at 18.4px. Listing every zone class here
     is what stops the next hand-authored page slipping through the same way. */
  .drop-zone h2, .drop-zone h3,
  .upload-zone h2, .upload-zone h3,
  .upload-area h2, .upload-area h3,
  .cp-drop h2, .cp-drop h3,
  .wp-drop h2, .wp-drop h3,
  .massive-drop-zone h2, .massive-drop-zone h3 {
    font-size: 17px !important;
    font-weight: 650 !important;
    line-height: 1.3 !important;
    letter-spacing: -0.01em !important;
    margin: 0 !important;
  }

  /* ── THE SUB-LINE ─────────────────────────────────────────────────────────
     "or click to browse files". Support text, so it steps down rather than
     sitting one notch under the title as it does at 16px today. */
  .drop-zone-text,
  .drop-zone-desc,
  .drop-zone-sub,
  .upload-text,
  .upload-sub,
  .upload-subtext,
  .upload-p,
  .cp-drop-sub,
  .wp-drop-hint,
  /* And by position, for the pages that write the sub-line as a bare <p> with no
     class -- image-compressor's "or click here to browse files" was still 15px
     after the named selectors above had done their work. A drop target has no
     long-form prose in it, so catching every paragraph inside one is safe in a
     way that catching every paragraph on the page would not be. */
  .drop-zone > p,
  .upload-zone > p,
  .upload-area > p,
  .cp-drop > p,
  .wp-drop > p,
  .massive-drop-zone > p,
  /* ROUND TWO: the same eighteen containers. Extending the ZONE list without
     extending this one is what made two pages clip -- pdf-dark-mode's sub-line was
     still 15px and ran to 113px over five lines inside a 258px box, and the cap
     took the blame for a type rule that had never reached the page. A selector list
     copied for one rule and not the others is worse than one that was never
     extended, because the zone looks handled. */
  .cr-drop > p,
  .p2w-drop > p,
  .pdm-drop > p,
  .mt-drop > p,
  .rw-drop > p,
  .ts-dropzone > p,
  .upload-section > p,
  .upload-view > p,
  .upload-container > p,
  .initial-upload-view > p,
  .drop-zone-container > p,
  .upload-dropzone > p,
  .drop-area > p,
  .drop-target > p,
  #drop-zone > p,
  #upload-area > p,
  #upload-view > p,
  #ps-upload > p,
  #initial-upload-zone > p,
  .pdm-drop-hint,
  .p2w-drop-hint,
  .cr-drop-sub {
    font-size: 13px !important;
    line-height: 1.45 !important;
    margin: 0 !important;
  }

  /* ── THE GLYPH ────────────────────────────────────────────────────────────
     64px on pdf-to-jpg, 48 on merge-pdf, 46 on compress-pdf. At 64 it is the
     loudest thing on the screen and it is decoration -- the control's meaning is
     in the title. 40px puts it a step below the title's optical weight. */
  .drop-zone > svg,
  .upload-zone > svg,
  .upload-area > svg,
  .cp-drop > svg,
  .wp-drop > svg,
  /* ROUND TWO: the same eighteen. pdf-to-word's mark measured 54px because
     `.p2w-drop > svg` was in no list. */
  .cr-drop > svg,
  .p2w-drop > svg,
  .pdm-drop > svg,
  .mt-drop > svg,
  .rw-drop > svg,
  .ts-dropzone > svg,
  .upload-section > svg,
  .upload-view > svg,
  .upload-container > svg,
  .initial-upload-view > svg,
  .drop-zone-container > svg,
  .upload-dropzone > svg,
  .drop-area > svg,
  .drop-target > svg,
  #drop-zone > svg,
  #upload-area > svg,
  #upload-view > svg,
  #ps-upload > svg,
  #initial-upload-zone > svg,
  .drop-icon,
  .upload-icon,
  .drop-zone-icon,
  .wp-drop-icon,
  .cp-drop-orb {
    width: 40px !important;
    height: 40px !important;
    flex: 0 0 auto !important;
  }

  /* SEVEN OF THE 92 DRAW THE GLYPH AS AN EMOJI, NOT AN SVG, and width/height
     cannot touch a text node. Measured after the first pass of this file:
     convert-multiple-images-into-a-single-pdf and gif-converter ship a 48px
     folder, extract-text-from-pdf a 64px page, rotate-pdf a 56px page,
     split-pdf-using-size a 56px page, avif-to-png a 48px cloud, jpg-to-png a
     48px folder, photo-enhancer a 48px camera. Every one of them is set with
     `font-size: 3rem` or similar, so it needs the type property and not the box.

     34px rather than 40: an emoji's glyph does not fill its em box the way a
     40px SVG viewBox does, so matching the numbers would leave the emoji pages
     with a visibly larger mark than the SVG ones. */
  .drop-icon,
  .upload-icon,
  .drop-zone-icon,
  .wp-drop-icon,
  .cp-drop-orb {
    font-size: 34px !important;
    line-height: 1 !important;
    margin-bottom: 6px !important;
  }

  /* CHILD MARGINS INSIDE A ZONE ARE DOUBLE SPACING, AND THEY WERE COSTING 45px.
     Every zone here is a flex column with a `gap`, so a child's own margin-bottom is
     added on top of the gap rather than instead of it. Round one already set
     `margin: 0` on the title and the sub-line for this reason; pdf-to-word's mark and
     heading carried 13.6px and 9.6px that nothing had reached. Stated as its own rule
     so the next container added to the lists above inherits it. */
  .drop-zone > *,
  .upload-zone > *,
  .upload-area > *,
  .cp-drop > *,
  .wp-drop > *,
  .massive-drop-zone > *,
  .cr-drop > *,
  .p2w-drop > *,
  .pdm-drop > *,
  .mt-drop > *,
  .rw-drop > *,
  .ts-dropzone > *,
  .upload-section > *,
  .upload-view > *,
  .upload-container > *,
  .initial-upload-view > *,
  .drop-zone-container > *,
  .upload-dropzone > *,
  .drop-area > *,
  .drop-target > *,
  #drop-zone > *,
  #upload-area > *,
  #upload-view > *,
  #ps-upload > *,
  #initial-upload-zone > * {
    margin-top: 0 !important;
    margin-bottom: 0 !important;
  }

  /* ── THE CALL TO ACTION ───────────────────────────────────────────────────
     A full-bleed 42-46px slab is a form's submit button. An app's primary action
     inside a card is a pill that does not span the card, so width goes to auto
     and the type comes down with everything else. Colour is untouched: whatever
     cyan, violet or emerald the page chose, it keeps. */
  .btn-upload,
  .upload-btn,
  .drop-zone button,
  .drop-zone .btn,
  .upload-zone button,
  .upload-area button,
  .cp-drop button,
  .wp-drop button {
    font-size: 14px !important;
    font-weight: 600 !important;
    min-height: 38px !important;
    padding: 9px 22px !important;
    border-radius: 999px !important;
    width: auto !important;
  }

  /* ── THE ONE THING THAT IS NOT ALLOWED TO SHRINK ──────────────────────────
     A control under 44px is not reachable on a phone, and the button rule above
     sets 38px of content box. The zone's own tap target is the whole card, so
     this only has to hold the button, and it does it with the padding rather than
     by raising the font back up. Matches Template-pdf-tools' own .tool-scope
     floor -- see the 44px minimum it sets on button and a[download]. */
  .btn-upload,
  .upload-btn,
  .drop-zone button,
  .upload-zone button,
  .upload-area button {
    min-width: 44px !important;
  }

  /* ── NARROW PHONES ────────────────────────────────────────────────────────
     At 360px and below the title wraps to three lines on the longest label
     ("Drag & Drop HEIC files here"), which pushes the zone back over 240px and
     undoes the point of the file. */
  @media (max-width: 380px) {
    .drop-zone,
    .upload-zone,
    .upload-area,
    .cp-drop,
    .wp-drop,
    .massive-drop-zone { padding: 18px 14px !important; min-height: 176px !important; }
    .drop-zone-title,
    .upload-title,
    .cp-drop-title { font-size: 16px !important; }
  }

  /* ── REDUCED MOTION IS THE PAGE'S BUSINESS, NOT THIS FILE'S ───────────────
     Nothing here animates, deliberately. The zones' hover and dragover
     transitions belong to each page and several are the only feedback that a drag
     registered. */


  /* ═══════════════════════════════════════════════════════════════════════════
     SECTION 2 — THE TYPE SCALE
     ═══════════════════════════════════════════════════════════════════════════

     Owner brief, 2026-09-01: "In the tools text fonts are too large. Make them
     small in size to look premium same for the elements of the pages and bttons."

     MEASURED ACROSS ALL 92 PAGES IN BRAVE AT 390px BEFORE WRITING ANY OF THIS,
     and the measurement narrowed the job considerably. Visible text elements
     inside .tool-scope, by computed size:

         28px      1     h2  "Global Optimization Settings"
         24px      3     h2  "Drag & Drop PDF Here"
       22.4px      1     h2  "Drag & Drop Images Here"
         20px      7     h2  "Batch Processing Workflow"
       19.2px      1     h2  "Conversion Options"
       18.7px      3     h3  "Generating PDF..."
       18.4px      9     h2  "Convert from PDF"
       17.6px     21     p   "or click to upload (JPG, PNG...)"
         17px     54     h2  (the drop titles this file already set)
         16px     54
       15.7px     17

     THE BODY TEXT IS NOT THE PROBLEM. It is already 15.7-16px on the great
     majority of these pages, which is a normal reading size on a phone and not
     something to shrink. What is oversized is a tail of roughly 25 SECTION
     HEADINGS between 18.4 and 28px -- a desktop heading scale on a 390px column.
     So this section is a heading scale plus one step down for paragraphs, and
     not the blanket shrink the brief could have been read as asking for.

     THE THREE THINGS IT DELIBERATELY DOES NOT TOUCH
     -----------------------------------------------
     1. COLOUR. Same reason as section 1: the 2026-08-03 layer was reverted for
        colour, not for size. memory/traps/glass-design-layer.md.

     2. input, select and textarea. Raising or lowering a form control changes
        its box, its baseline and every layout built around it, on 92 pages that
        were laid out by hand. Separately: 128 controls across these pages
        ALREADY compute under 16px -- some at 12px -- which means Safari zooms
        the viewport when they take focus. That is a real pre-existing defect and
        it is a fix, not a scale change, so it does not belong in this file.

     3. Anything inside a canvas-driven preview or a results grid. Those size
        themselves from their content.
     ═══════════════════════════════════════════════════════════════════════════ */
  /* ── SECTION HEADINGS ─────────────────────────────────────────────────────
     17px is the same step the drop title takes in section 1, which is what makes
     a panel heading and the drop target read as one system rather than as two
     scales that happen to share a page. */
  .tool-scope h2 {
    font-size: 17px !important;
    line-height: 1.3 !important;
    letter-spacing: -0.01em !important;
  }
  .tool-scope h3 {
    font-size: 15.5px !important;
    line-height: 1.35 !important;
  }
  /* h4 and below are already at or under the body size everywhere measured, so
     they are left alone rather than pinned for symmetry. */

  /* ── PARAGRAPHS ───────────────────────────────────────────────────────────
     One step, 16/17.6 -> 15px. Not smaller: this is the text that explains what
     the tool does and what formats it takes, and a phone is where it is least
     comfortable to read. `li` comes with it or a tool's own instructions end up
     larger than the prose introducing them. */
  .tool-scope p,
  .tool-scope li {
    font-size: 15px !important;
    line-height: 1.5 !important;
  }

  /* ── THE LABELS ABOVE CONTROLS ────────────────────────────────────────────
     "COMPRESSION QUALITY", "OUTPUT FORMAT". They are small-caps eyebrows doing a
     label's job, and at 16px they compete with the value they are labelling.

     THE :not(:has(...)) IS NOT DECORATION -- IT IS A DEFECT THIS RULE ALREADY
     CAUSED ONCE. Note 2 above says this file does not touch form controls. The
     first version of this rule broke that promise without naming a control: a
     label that WRAPS its input is that input's parent, font-size inherits, and
     re-auditing the 92 pages showed compare-pdf's inputs had moved from 12px to
     13.5px. Nothing selected them; they inherited.

     So the rule applies only to labels that stand above a control rather than
     containing one. A wrapping label keeps whatever it had, and the 128
     controls already under the 16px floor stay exactly where they were --
     which is a separate defect, to be fixed as a defect and not drifted into
     from a type scale. */
  .tool-scope label:not(:has(input)):not(:has(select)):not(:has(textarea)) {
    font-size: 13.5px !important;
    letter-spacing: 0.02em !important;
  }

  /* ── BUTTONS ──────────────────────────────────────────────────────────────
     14px matches the drop zone's CTA in section 1. The 44px tap floor the
     template sets on .tool-scope button is NOT overridden here -- only the type
     comes down, and the padding does the rest. */
  .tool-scope button,
  .tool-scope .btn,
  .tool-scope a.btn,
  .tool-scope a[download] {
    font-size: 14px !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   DESKTOP — ROUND TWO. THE GEOMETRY ROUND ONE DELIBERATELY DID NOT SET.

   Round one stopped at 899px and that was right for SIZE: a 17px drop title in a
   1050px-wide card is answering a question nobody asked, on the width Google
   actually renders.

   It was not right for EVENNESS, which is what the owner actually filed --
   "the dropdown areas are uneven", with a screenshot of rearrange-pdf-pages.
   Measured across all 90 zones at 1280 and 1920 on 2026-09-01:

     height   149 - 734px   (4.9x spread; mobile before round one was 1.8x)
     width    240 - 1141px
     radius   9 values:  0  8  12  13  13.6  14.4  16  20  24
     border   5 treatments: dashed 1px / 2px / 3px, solid 1px, none
     title    8 sizes, 17 - 29px

   THE TALL ONES ARE EMPTY, NOT FULL, AND THAT IS WHAT MAKES A CAP SAFE.
   Children's height measured against the box:

     photo-enhancer      734px around 105px of content  (32px padding, flex stretch)
     heic-viewer         683px around 217px
     svg-to-png          600px around 134px  (min-height:300px + 48px padding)
     pdf-to-grayscale    400px around 111px  (min-height:400px + 96px padding)
     pdf-viewer          400px around 159px  (min-height:400px + 64px padding)

   70-85% of a tall zone is padding and an explicit min-height. Capping both clips
   nothing -- and tests/tool-scale/no-clip.verify.mjs proves that per page rather
   than trusting this paragraph.

   WIDTH IS DELIBERATELY NOT UNIFIED, and this is the note that stops the next
   session "finishing the job". 240-1141px is the largest spread in the data, but
   several narrow zones are load-bearing: image-frame sits at 270px BESIDE a
   controls column, and forcing a width would break that layout rather than even
   it. Height and surface are shared; width belongs to the page.

   THE EMPTY SPACE BELOW A ZONE IS NOT THIS FILE'S PROBLEM EITHER. It looked like
   dead card at first and it is not: image-frame's 548px is control groups,
   blur-image's 514px is a 300px control card, tiff-to-webp's 475px is settings
   panels. Only rearrange-pdf-pages has real dead air and it is 73px of card
   padding. Nothing here changes .tool-scope's height.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 900px) {
  .drop-zone,
  .upload-zone,
  .upload-area,
  .cp-drop,
  .wp-drop,
  .massive-drop-zone,
  .cr-drop,
  .p2w-drop,
  .pdm-drop,
  .mt-drop,
  .rw-drop,
  .ts-dropzone,
  .upload-section,
  .upload-view,
  .upload-container,
  .initial-upload-view,
  .drop-zone-container,
  .upload-dropzone,
  .drop-area,
  .drop-target,
  #drop-zone,
  #upload-area,
  #upload-view,
  #ps-upload,
  #initial-upload-zone {
    min-height: 240px !important;               /* p25 was 223; the model pages are 206 and 210 */
    /* THE CAP IS THE ONE RULE HERE THAT CAN BREAK A TOOL, so it does not ship on
       the argument above. tests/tool-scale/no-clip.verify.mjs asserts
       scrollHeight <= clientHeight on every zone at 390, 1280 and 1920, and a page
       that would clip goes into HEIGHT_EXEMPT with its measurement rather than the
       band being widened to make the failure go away.

       overflow is deliberately NOT set alongside it. A zone that outgrows the cap
       should fail the gate and be exempted, not silently scrolled -- a scrollbar
       inside a drop target is a worse outcome than a tall drop target. */
    max-height: 300px !important;
    padding: clamp(20px, 3vw, 34px) !important; /* replaces 32 / 48 / 64 / 96px */
    border-radius: 20px !important;             /* replaces nine values */
    border-width: 1px !important;               /* replaces five treatments */
    gap: 12px !important;
  }

  /* The collapsed state and the wrappers opt out here too -- same reasons as the
     phone block, and repeated rather than shared because a reader of one media
     query should not have to hold the other one in their head. */
  .compact-upload,
  .drop-zone.compact-upload,
  .upload-zone.compact-upload,
  #drop-zone.compact-upload,
  #upload-area.compact-upload,
  #upload-view.compact-upload,
  #ps-upload.compact-upload,
  #initial-upload-zone.compact-upload {
    min-height: 0 !important;
    max-height: none !important;
  }
  .upload-section:has(.drop-zone),
  .upload-view:has(.drop-zone),
  .upload-container:has(.drop-zone),
  .initial-upload-view:has(.massive-drop-zone),
  .initial-upload-view:has(.drop-zone),
  .drop-zone-container:has(.drop-zone),
  .drop-area:has(.drop-zone),
  .drop-target:has(.drop-zone) {
    min-height: 0 !important;
    max-height: none !important;
    padding: 0 !important;
    border: 0 !important;
    background: none !important;
    box-shadow: none !important;
  }


  /* THE TITLE. 24px on 43 pages, 29px on three, 17px on one -- eight sizes for a
     single control. 20px sits one step above the desktop body scale without
     competing with the page h1 above it.

     Listed by container AND by name, the way round one learned to: webp-to-pdf
     puts a bare <h2> inside .wp-drop with no class at all, and it was the one page
     the first audit caught still sitting above the band. */
  .drop-zone h2, .drop-zone h3,
  .upload-zone h2, .upload-zone h3,
  .upload-area h2, .upload-area h3,
  .cp-drop h2, .cp-drop h3,
  .wp-drop h2, .wp-drop h3,
  .massive-drop-zone h2, .massive-drop-zone h3,
  .cr-drop h2, .cr-drop h3,
  .p2w-drop h2, .p2w-drop h3,
  .pdm-drop h2, .pdm-drop h3,
  .mt-drop h2, .mt-drop h3,
  .rw-drop h2, .rw-drop h3,
  .ts-dropzone h2, .ts-dropzone h3,
  .upload-section h2, .upload-section h3,
  .upload-view h2, .upload-view h3,
  .upload-container h2, .upload-container h3,
  .initial-upload-view h2, .initial-upload-view h3,
  .drop-zone-container h2, .drop-zone-container h3,
  .upload-dropzone h2, .upload-dropzone h3,
  .drop-area h2, .drop-area h3,
  .drop-target h2, .drop-target h3,
  #drop-zone h2, #drop-zone h3,
  #upload-area h2, #upload-area h3,
  #upload-view h2, #upload-view h3,
  #ps-upload h2, #ps-upload h3,
  #initial-upload-zone h2, #initial-upload-zone h3,
  .drop-zone-title,
  .upload-title,
  .cp-drop-title,
  .cr-drop-title,
  .drop-title,
  .upload-text {
    font-size: 20px !important;
    font-weight: 650 !important;
    line-height: 1.3 !important;
    letter-spacing: -0.01em !important;
    margin: 0 !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE SURFACE — ROUND TWO, AND THE COLOUR ROUND ONE DELIBERATELY DID NOT SET.

   This file's own header says: "That failure was about COLOUR, and this file
   therefore sets none." That guard is ALSO what left the zone looking flat, and
   the measurement is one line:

     .tool-scope   background: var(--bg-panel)                   -> rgba(15,23,42,.6)
     .drop-zone    background: var(--bg-card) -> var(--bg-panel) -> rgba(15,23,42,.6)

   The pane is painted the SAME VALUE as the card behind it. Two 60% layers
   composite to ~84% opaque, so the zone reads as a hole cut in the card rather
   than an object sitting on it -- and the page's own .zen-orb drift, running on
   all 92 pages the whole time, never reaches the eye.

   "Glassmorphism is not applied" and "the background animation is not present"
   were ONE defect with ONE fix, and nothing new animates here: the orbs were
   always there, behind two opaque layers.

   WHY THIS IS NOT THE 2026-08-03 LAYER. Every colour below is one of two kinds:

     1. a white or black ALPHA -- a value, not a hue. It lifts any surface without
        tinting it, so a page whose palette is emerald stays emerald.
     2. a color-mix of the PAGE'S OWN --accent-cyan / --accent-violet /
        --card-border, all verified present on all 44 pdf-tools and all 48
        image-tools pages.

   NO LITERAL COLOUR IS WRITTEN DOWN, and tests/tool-scale/no-colour.verify.mjs
   fails the build if one ever is. THAT GATE IS THE WHOLE GUARD. If you are here
   because it went red and you are about to silence it, you are rewriting the layer
   that was deleted in a day.

   AT EVERY WIDTH, unlike the geometry above. A gradient and an inset highlight are
   proportional; a 188px min-height is not. Round one's 899px ceiling was right for
   size and would be wrong here.

   NO SECOND backdrop-filter. .tool-scope already blurs on all 92 pages. Nesting
   another creates a containing block -- a trap this project has already paid for on
   31 pages -- and forces the compositor to re-read a full viewport every frame for
   an effect nobody can see through an already-blurred parent.
   ═══════════════════════════════════════════════════════════════════════════ */
.drop-zone,
.upload-zone,
.upload-area,
.cp-drop,
.wp-drop,
.massive-drop-zone,
.cr-drop,
.p2w-drop,
.pdm-drop,
.mt-drop,
.rw-drop,
.ts-dropzone,
.upload-section,
.upload-view,
.upload-container,
.initial-upload-view,
.drop-zone-container,
.upload-dropzone,
.drop-area,
.drop-target,
#drop-zone,
#upload-area,
#upload-view,
#ps-upload,
#initial-upload-zone {
  position: relative;
  background:
    radial-gradient(120% 90% at 12% 0%,
      color-mix(in srgb, var(--accent-cyan) 12%, transparent) 0%, transparent 58%),
    radial-gradient(120% 90% at 88% 100%,
      color-mix(in srgb, var(--accent-violet) 13%, transparent) 0%, transparent 58%),
    linear-gradient(180deg, rgba(255,255,255,.065), rgba(255,255,255,.015))
    !important;
  /* border-style is set here and not only border-width, because three pages ship
     `border: none` and a width on a style:none border computes to 0px. The evenness
     gate caught exactly that: "2 border widths: 1px x81, 0px x3". */
  border-style: solid !important;
  border-color: color-mix(in srgb, var(--accent-cyan) 22%, var(--card-border)) !important;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.10),
    0 12px 28px -18px rgba(0,0,0,.75) !important;
  transition: border-color .22s ease, box-shadow .22s ease !important;
}

/* LIGHT THEME IS NOT A SECOND PALETTE, IT IS THE SAME RULES OVER A LIGHT GROUND.
   A white-on-white lift reads as nothing, so only the top layer flips to an opaque
   white wash; the two accent washes are the page's own tokens, already darker in
   light mode, and stay exactly as they are.

   THE THEME SIGNAL ON THIS SITE IS THE PRESENCE OR ABSENCE OF `.dark` ON <html>.
   Nothing ever adds a `light` class -- memory/traps/glass-design-layer.md -- so
   `html:not(.dark)` is the only correct way to write this, and `html.light` would
   silently never match. */
html:not(.dark) .drop-zone,
html:not(.dark) .upload-zone,
html:not(.dark) .upload-area,
html:not(.dark) .cp-drop,
html:not(.dark) .wp-drop,
html:not(.dark) .massive-drop-zone,
html:not(.dark) .cr-drop,
html:not(.dark) .p2w-drop,
html:not(.dark) .pdm-drop,
html:not(.dark) .mt-drop,
html:not(.dark) .rw-drop,
html:not(.dark) .ts-dropzone,
html:not(.dark) .upload-section,
html:not(.dark) .upload-view,
html:not(.dark) .upload-container,
html:not(.dark) .initial-upload-view,
html:not(.dark) .drop-zone-container,
html:not(.dark) .upload-dropzone,
html:not(.dark) .drop-area,
html:not(.dark) .drop-target,
html:not(.dark) #drop-zone,
html:not(.dark) #upload-area,
html:not(.dark) #upload-view,
html:not(.dark) #ps-upload,
html:not(.dark) #initial-upload-zone {
  background:
    radial-gradient(120% 90% at 12% 0%,
      color-mix(in srgb, var(--accent-cyan) 9%, transparent) 0%, transparent 58%),
    radial-gradient(120% 90% at 88% 100%,
      color-mix(in srgb, var(--accent-violet) 9%, transparent) 0%, transparent 58%),
    linear-gradient(180deg, rgba(255,255,255,.90), rgba(255,255,255,.50))
    !important;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.90),
    0 10px 22px -18px rgba(0,0,0,.30) !important;
}

/* The wrappers and the collapsed strip must not be painted either. Repeated from
   the geometry blocks because a surface reset and a size reset are different
   statements and the next reader should not have to infer one from the other. */
.compact-upload,
.upload-section:has(.drop-zone),
.upload-view:has(.drop-zone),
.upload-container:has(.drop-zone),
.initial-upload-view:has(.massive-drop-zone),
.initial-upload-view:has(.drop-zone),
.drop-zone-container:has(.drop-zone),
.drop-area:has(.drop-zone),
.drop-target:has(.drop-zone) {
  background: none !important;
  box-shadow: none !important;
}

/* THE INNER DASHED GUIDE, AS ::after AND NEVER ::before.
   ::before is TAKEN -- .upload-area::before and .upload-zone::before both exist on
   real pages -- and overwriting a page's own pseudo-element is how a decoration
   becomes a bug on exactly the page nobody screenshotted. ::after is free on every
   zone class measured except .cp-drop, which uses it for its drag state and is left
   out of the list below entirely rather than fought: one page keeping its own drag
   affordance is a better outcome than a specificity war that ends with that
   affordance invisible.

   pointer-events:none is LOAD-BEARING, not hygiene. This box covers the whole drop
   target; without it the control silently stops accepting clicks. */
.drop-zone::after,
.upload-zone::after,
.upload-area::after,
.wp-drop::after,
.massive-drop-zone::after,
.cr-drop::after,
.p2w-drop::after,
.pdm-drop::after,
.mt-drop::after,
.rw-drop::after,
.ts-dropzone::after,
.upload-section::after,
.upload-view::after,
.upload-container::after,
.initial-upload-view::after,
.drop-zone-container::after,
.upload-dropzone::after,
.drop-area::after,
.drop-target::after,
#drop-zone::after,
#upload-area::after,
#upload-view::after,
#ps-upload::after,
#initial-upload-zone::after {
  content: '' !important;
  position: absolute !important;
  inset: 9px !important;
  border-radius: 13px !important;
  border: 1px dashed color-mix(in srgb, var(--accent-cyan) 26%, transparent) !important;
  pointer-events: none !important;
}

@media (min-width: 900px) {
  .drop-zone::after,
  .upload-zone::after,
  .upload-area::after,
  .wp-drop::after,
  .massive-drop-zone::after,
  .cr-drop::after,
  .p2w-drop::after,
  .pdm-drop::after,
  .mt-drop::after,
  .rw-drop::after,
  .ts-dropzone::after,
  .upload-section::after,
  .upload-view::after,
  .upload-container::after,
  .initial-upload-view::after,
  .drop-zone-container::after,
  .upload-dropzone::after,
  .drop-area::after,
  .drop-target::after,
  #drop-zone::after,
  #upload-area::after,
  #upload-view::after,
  #ps-upload::after,
  #initial-upload-zone::after {
    inset: 13px !important;
    border-radius: 15px !important;
  }
}

/* The collapsed strip and the wrappers get no guide -- a dashed ring inside a 22px
   strip is a line, and a ring around a wrapper draws the second container the owner
   asked to remove. */
.compact-upload::after,
.upload-section:has(.drop-zone)::after,
.upload-view:has(.drop-zone)::after,
.upload-container:has(.drop-zone)::after,
.initial-upload-view:has(.massive-drop-zone)::after,
.initial-upload-view:has(.drop-zone)::after,
.drop-zone-container:has(.drop-zone)::after,
.drop-area:has(.drop-zone)::after,
.drop-target:has(.drop-zone)::after {
  content: none !important;
}

/* THE ICON PLATE, ON THE GLYPH THE PAGE ALREADY DRAWS.
   Round one sized these to 40px, and 34px for the seven pages that draw the mark as
   an emoji rather than an SVG (an emoji's glyph does not fill its em box the way a
   40px viewBox does, so matching the numbers would leave those pages visibly
   larger). Round two puts each mark in a circular plate instead of leaving it
   floating: the reference the owner sent has a plate, and it is what makes the mark
   read as part of a control rather than a decoration dropped on top of one.

   box-sizing is stated explicitly because several pages set content-box locally, and
   a 54px plate holding a 40px padded glyph is not a circle. */
.drop-icon,
.upload-icon,
.drop-zone-icon,
.wp-drop-icon,
.cp-drop-orb,
.cr-drop-orb,
.p2w-drop-icon,
.pdm-drop-icon {
  box-sizing: border-box !important;
  /* display:grid makes this a BLOCK-LEVEL box; in a block-flow zone that pins it
     hard left. The composition rule above centres it, and this centres it on any
     page that rule cannot reach. Measured: 16 icons off-centre, worst -419px. */
  margin-inline: auto !important;
  width: 54px !important;
  height: 54px !important;
  /* 7px, NOT 12px, AND THE 12px WAS A MEASURED BUG.
     A 54px plate with a 1px border and 12px padding leaves a 28px hole. The
     glyphs these pages actually draw are a 48px SVG and a 34px emoji, so every
     plated icon was either overflowing it or being shrunk below round one's
     40px. Measured on webp-to-gif: box 54x54, scrollHeight 60, child 48x48
     sitting dx=10 dy=10 off centre -- place-items:center cannot centre what
     does not fit, it aligns and then overflows toward one edge.
     7px leaves 38px, which is round one's glyph size back within 2px. */
  padding: 7px !important;
  border-radius: 50% !important;
  display: grid !important;
  place-items: center !important;
  background: linear-gradient(155deg, rgba(255,255,255,.09), rgba(255,255,255,.02)) !important;
  border: 1px solid color-mix(in srgb, var(--accent-cyan) 20%, var(--card-border)) !important;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.12) !important;
}
html:not(.dark) .drop-icon,
html:not(.dark) .upload-icon,
html:not(.dark) .drop-zone-icon,
html:not(.dark) .wp-drop-icon,
html:not(.dark) .cp-drop-orb,
html:not(.dark) .cr-drop-orb,
html:not(.dark) .p2w-drop-icon,
html:not(.dark) .pdm-drop-icon {
  background: linear-gradient(155deg, rgba(255,255,255,.95), rgba(255,255,255,.55)) !important;
}
@media (min-width: 900px) {
  .drop-icon,
  .upload-icon,
  .drop-zone-icon,
  .wp-drop-icon,
  .cp-drop-orb,
  .cr-drop-orb,
  .p2w-drop-icon,
  .pdm-drop-icon {
    width: 62px !important;
    height: 62px !important;
    padding: 8px !important;   /* 44px of glyph, same reasoning as the 7px above */
  }
}

/* THE GLYPH CONFORMS TO THE PLATE, RATHER THAN THE PLATE HOPING IT FITS.
   Six of the eighteen plated icons carry an SVG with intrinsic width/height
   attributes -- 48px on webp-to-avif and webp-to-gif -- which no padding change
   can talk down. Sizing the child is the only rule that holds for a glyph the
   page authored at any size, so it is stated rather than assumed.

   preserveAspectRatio defaults to `meet`, so a non-square viewBox letterboxes
   inside the square plate instead of stretching. */
.drop-icon > svg,
.drop-icon > img,
.upload-icon > svg,
.upload-icon > img,
.drop-zone-icon > svg,
.drop-zone-icon > img,
.wp-drop-icon > svg,
.wp-drop-icon > img,
.cp-drop-orb > svg,
.cp-drop-orb > img,
.cr-drop-orb > svg,
.cr-drop-orb > img,
.p2w-drop-icon > svg,
.p2w-drop-icon > img,
.pdm-drop-icon > svg,
.pdm-drop-icon > img {
  width: 100% !important;
  height: 100% !important;
  display: block !important;
}

/* AN EMOJI IS TEXT ON A BASELINE, NOT A BOX, and that is the whole of the
   remaining offset. Twelve of the eighteen plated icons are a bare emoji.
   place-items:center centres its LINE BOX; the half-leading above and below the
   glyph then pushes the visible ink low, and round one's 34px in a 28px hole
   overflowed on top of that. Giving it the whole plate and killing the leading
   puts the ink where the circle's centre is.

   :has() is already load-bearing in this file (the wrapper resets above), so
   this introduces no new support requirement. */
.drop-icon:not(:has(svg)):not(:has(img)),
.upload-icon:not(:has(svg)):not(:has(img)),
.drop-zone-icon:not(:has(svg)):not(:has(img)),
.wp-drop-icon:not(:has(svg)):not(:has(img)),
.cp-drop-orb:not(:has(svg)):not(:has(img)),
.cr-drop-orb:not(:has(svg)):not(:has(img)),
.p2w-drop-icon:not(:has(svg)):not(:has(img)),
.pdm-drop-icon:not(:has(svg)):not(:has(img)) {
  padding: 0 !important;
  line-height: 1 !important;
}

/* THE CALL TO ACTION. Round one made it a pill instead of a full-bleed slab and
   left its colour entirely alone. Round two gives it the reference's treatment -- a
   rim over a low-alpha gradient rather than a solid fill -- because it is the last
   thing inside the zone still reading as a form's submit button.

   COLOUR IS STILL THE PAGE'S. Both gradient stops and the rim are color-mix of the
   page's own tokens, so nothing here decides what colour a tool's primary action is.

   TEXT COLOUR IS SET HERE, AND THE FIRST DRAFT OF THIS FILE REFUSED TO.
   The refusal was reasoned -- dev-glass.css forces `color: var(--text-main)
   !important` on every button inside .tool-scope, and fighting it silently once
   cost a whole session (memory/traps/glass-design-layer.md) -- and it was wrong on
   two counts, both of which a screenshot settled in seconds.

   First, dev-glass ships on FOUR categories and pdf-tools and image-tools are not
   among them: grep finds it on 2 of these 92 pages, not 92.

   Second and decisive: --btn-text is `#ffffff` on 40 pages and `#0f172a` on the
   other 40, because it is the CONTRAST colour for a SOLID accent fill. Replacing
   that fill with a translucent gradient inverts its meaning. Not setting the colour
   did not leave the page's choice intact -- it left dark text on a dark pill in
   dark mode and white text on a pale pill in light mode, invisible in BOTH. The
   label had to follow the fill.

   var(--accent-cyan) is still the page's own token, so no hue is imposed: #22d3ee
   on a dark ground, #0e7490 on a light one, and whatever a page with a different
   palette chose. This is what the owner's reference image does too.

   44px MINIMUM, AND IT IS A CHANGE. Round one set min-height:38px and made the rest
   up with padding, which is correct but is not the same statement: a page with its
   own tighter padding lands under the reachable floor and only a rendered
   measurement says so. The gate asserts the rendered box. */
.drop-zone button,
.drop-zone .btn,
.upload-zone button,
.upload-area button,
.cp-drop button,
.wp-drop button,
.massive-drop-zone button,
.cr-drop button,
.p2w-drop button,
.pdm-drop button,
.mt-drop button,
.rw-drop button,
.ts-dropzone button,
.upload-section button,
.upload-view button,
.upload-container button,
.initial-upload-view button,
.drop-zone-container button,
.upload-dropzone button,
.drop-area button,
.drop-target button,
#drop-zone button,
#upload-area button,
#upload-view button,
#ps-upload button,
#initial-upload-zone button,
.btn-upload,
.upload-btn {
  min-height: 44px !important;
  padding: 11px 22px !important;
  border-radius: 999px !important;
  font-size: 14px !important;
  font-weight: 600 !important;
  width: auto !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 8px !important;
  border: 1px solid color-mix(in srgb, var(--accent-cyan) 42%, transparent) !important;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent-cyan) 18%, transparent),
    color-mix(in srgb, var(--accent-violet) 13%, transparent)) !important;
  color: var(--accent-cyan) !important;
  transition: border-color .2s ease, background .2s ease, color .2s ease,
              box-shadow .2s ease, transform .2s ease !important;
}

/* circle-image-cropper and passport-size-photo are the two pages of 92 that DO
   inline dev-glass.css, whose button rule is roughly (0,3,1) and beats the (0,1,1)
   above on specificity even though both carry !important. Losing to !important
   raises nothing and looks like a build failure -- that is the trap's own wording.
   An id cannot be used (these buttons have none), so the ancestor chain is spelled
   out to clear it. */
html:root .tool-scope .drop-zone button,
html:root .tool-scope .upload-zone button,
html:root .tool-scope .upload-area button,
html:root .tool-scope #upload-area button,
html:root .tool-scope #ps-upload button,
html:root .tool-scope .btn-upload,
html:root .tool-scope .upload-btn {
  color: var(--accent-cyan) !important;
}

@media (min-width: 900px) {
  .drop-zone button,
  .drop-zone .btn,
  .upload-zone button,
  .upload-area button,
  .cp-drop button,
  .wp-drop button,
  .massive-drop-zone button,
  .cr-drop button,
  .p2w-drop button,
  .pdm-drop button,
  .mt-drop button,
  .rw-drop button,
  .ts-dropzone button,
  .upload-section button,
  .upload-view button,
  .upload-container button,
  .initial-upload-view button,
  .drop-zone-container button,
  .upload-dropzone button,
  .drop-area button,
  .drop-target button,
  #drop-zone button,
  #upload-area button,
  #upload-view button,
  #ps-upload button,
  #initial-upload-zone button,
  .btn-upload,
  .upload-btn {
    min-height: 46px !important;
    padding: 12px 26px !important;
    font-size: 15px !important;
  }
}

/* THE ONE STACKED CONTROL PAIR ON THE SITE, AND WHY THIS IS A PAGE RULE.
   Round one declares, above:  <zone> > * { margin-top:0; margin-bottom:0 }
   Its reason is real -- 34 of the 84 zones are a flex or grid column with a
   row-gap, and a child's own margin double-counts against that gap. Controls
   were collateral.

   heic-to-jpg is the ONLY zone on the site with two adjacent sibling controls
   in NORMAL FLOW, measured across all 84 pages, so it is the only page where
   nothing else supplies the spacing: its .paste-btn's own `margin-top: 1rem`
   was being zeroed and the two pills rendered touching at a 0.0px gap.

   IT IS WRITTEN AS A PAGE RULE RATHER THAN `button + button` DELIBERATELY. The
   only other zone with adjacent controls, image-cropper, lays them out SIDE BY
   SIDE in a flex row; a sibling-combinator margin would have pushed that one
   10px out of line to fix this one. One page is broken, so one page is fixed.

   margin-left goes with it: .paste-btn carries `margin-left: .5rem` for a
   side-by-side layout it no longer has, and 8px of it put the two pills'
   centres 4px apart -- the misalignment in the owner's screenshot. */
.drop-zone > .paste-btn {
  margin-top: 12px !important;
  margin-left: 0 !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE COMPOSITION INSIDE THE BOX. Round two set the box and never said where
   the content sits in it, and a min-height without a composition rule is what
   produced the owner's "alignment and spacing issues".

   MEASURED, all 84 zones, before this rule was written:

     390px    15 of 82 zones top-heavy by more than 40px
     1280px   26 of 84   -- txt-to-pdf above=35 below=162,
                            rearrange-pdf-pages above=35 below=133

   Every one of them was `display:block`, where a taller box simply leaves the
   slack at the bottom. compress-pdf -- the page the owner sent as the model --
   was already a centred column, which is exactly why it read as finished.

   THE PLATE PROVES THE SAME POINT MORE SHARPLY. `display:grid` on the icon made
   it a BLOCK-LEVEL box, so in a block zone it pinned hard left: 16 plated icons
   measured off-centre, pdf-to-jpg by -403px and rotate-pdf by -419px. A flex
   column with align-items:center fixes the cause rather than the symptom, and
   the margin-inline below is the belt to that braces.

   THE CENSUS THAT MADE THIS SAFE: block 50, flex/column 32, flex/row 1, grid 1.
   The flex/row zone (png-to-pdf) has a single child, so a column is identical
   for it. The grid zone is exempted by name below.
   ═══════════════════════════════════════════════════════════════════════════ */
.drop-zone,
.upload-zone,
.upload-area,
.cp-drop,
.wp-drop,
.massive-drop-zone,
.cr-drop,
.p2w-drop,
.pdm-drop,
.mt-drop,
.rw-drop,
.ts-dropzone,
.upload-section,
.upload-view,
.upload-container,
.initial-upload-view,
.drop-zone-container,
.upload-dropzone,
.drop-area,
.drop-target,
#drop-zone,
#upload-area,
#upload-view,
#ps-upload,
#initial-upload-zone {
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  justify-content: center !important;
  text-align: center !important;
}

/* A PAGE THAT HIDES ITS OWN ZONE HAS TO BE ABLE TO.
   Owner report, 2026-09-02. image-converter's script ends handleFiles() with
   `uploadArea.style.display = 'none'` -- it has the files, the target is spent,
   put it away. That line had never done anything: an inline style loses to a
   declaration carrying !important, so the column rule above kept the zone laid
   out and the page showed a dead 188px drop target above its own settings.

   Measured, not reasoned: setting style.display='none' on #upload-area at 390px
   left getComputedStyle().display reading `flex`.

   SEVEN of the eleven id-addressed pages end their upload handler this way, so
   the stylesheet was fighting all seven. Only a rule of equal weight and greater
   specificity can hand the decision back, which is what the attribute selector
   buys. Blink serialises style.display='none' as `display: none;` with the
   space, but a hand-written attribute may not, so both spellings are matched and
   [hidden] with them.

   THE SELECTOR LIST IS BUILT FROM scripts/lib/drop-zones.mjs, not scraped from
   this file. The first attempt scraped it and swallowed the comment banner above
   the rule, which put prose inside the selector list -- and ONE invalid selector
   voids the WHOLE comma list, so the block parsed away to nothing and measured
   as if it were never written. It looked right in the diff.

   PREFER A CLASS over an inline style in new code: .compact-upload collapses the
   zone to a strip and keeps it reachable for a second file, which is nearly
   always better than removing it. This rule exists so the pages that already
   chose to hide are not overruled -- not to bless the choice. */
.drop-zone[style*="display: none"],
.drop-zone[style*="display:none"],
.drop-zone[hidden],
.upload-zone[style*="display: none"],
.upload-zone[style*="display:none"],
.upload-zone[hidden],
.upload-area[style*="display: none"],
.upload-area[style*="display:none"],
.upload-area[hidden],
.cp-drop[style*="display: none"],
.cp-drop[style*="display:none"],
.cp-drop[hidden],
.wp-drop[style*="display: none"],
.wp-drop[style*="display:none"],
.wp-drop[hidden],
.massive-drop-zone[style*="display: none"],
.massive-drop-zone[style*="display:none"],
.massive-drop-zone[hidden],
.cr-drop[style*="display: none"],
.cr-drop[style*="display:none"],
.cr-drop[hidden],
.p2w-drop[style*="display: none"],
.p2w-drop[style*="display:none"],
.p2w-drop[hidden],
.pdm-drop[style*="display: none"],
.pdm-drop[style*="display:none"],
.pdm-drop[hidden],
.mt-drop[style*="display: none"],
.mt-drop[style*="display:none"],
.mt-drop[hidden],
.rw-drop[style*="display: none"],
.rw-drop[style*="display:none"],
.rw-drop[hidden],
.ts-dropzone[style*="display: none"],
.ts-dropzone[style*="display:none"],
.ts-dropzone[hidden],
.upload-section[style*="display: none"],
.upload-section[style*="display:none"],
.upload-section[hidden],
.upload-view[style*="display: none"],
.upload-view[style*="display:none"],
.upload-view[hidden],
.upload-container[style*="display: none"],
.upload-container[style*="display:none"],
.upload-container[hidden],
.initial-upload-view[style*="display: none"],
.initial-upload-view[style*="display:none"],
.initial-upload-view[hidden],
.drop-zone-container[style*="display: none"],
.drop-zone-container[style*="display:none"],
.drop-zone-container[hidden],
.upload-dropzone[style*="display: none"],
.upload-dropzone[style*="display:none"],
.upload-dropzone[hidden],
.drop-area[style*="display: none"],
.drop-area[style*="display:none"],
.drop-area[hidden],
.drop-target[style*="display: none"],
.drop-target[style*="display:none"],
.drop-target[hidden],
#drop-zone[style*="display: none"],
#drop-zone[style*="display:none"],
#drop-zone[hidden],
#upload-area[style*="display: none"],
#upload-area[style*="display:none"],
#upload-area[hidden],
#upload-view[style*="display: none"],
#upload-view[style*="display:none"],
#upload-view[hidden],
#ps-upload[style*="display: none"],
#ps-upload[style*="display:none"],
#ps-upload[hidden],
#initial-upload-zone[style*="display: none"],
#initial-upload-zone[style*="display:none"],
#initial-upload-zone[hidden] {
  display: none !important;
}

/* remove-watermark-from-pdf lays its zone out as a 4-cell 802px grid. It is the
   one zone a column would rebuild rather than centre, so it keeps its own. */
.rw-drop { display: grid !important; }

/* THE FOUR CHILDREN THAT RELY ON THE BLOCK-FLOW STRETCH, measured rather than
   guessed: three are a real .drop-zone nested inside a wrapper that is itself in
   the registry, and one is a full-width text block. align-items:center would
   shrink-wrap them and undo a layout the page needs. */
.upload-view > .drop-zone,
.upload-section > .drop-zone,
.upload-container > .drop-zone,
.drop-zone-container > .drop-zone,
.drop-zone-container > .file-list,
.initial-upload-view > .drop-zone,
.drop-area > .drop-zone,
.drop-target > .drop-zone,
.upload-dropzone > .drop-zone,
.upload-text {
  align-self: stretch !important;
  width: 100% !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE HOVER STATE, WHICH ROUND TWO INTENDED AND NEVER WROTE.

   The surface block declares `transition: border-color .22s, box-shadow .22s`
   and then defines no state to transition TO -- a transition with no hover is
   the fingerprint of a rule that was planned and dropped. Owner report: "on
   hover areas should get highlighted of the dropdown but they are not."

   Colour stays the page's own, same as the resting surface: a color-mix of
   --accent-cyan, never a literal, so tests/tool-scale/no-colour.verify.mjs
   still holds.
   ═══════════════════════════════════════════════════════════════════════════ */
.drop-zone:hover,
.upload-zone:hover,
.upload-area:hover,
.cp-drop:hover,
.wp-drop:hover,
.massive-drop-zone:hover,
.cr-drop:hover,
.p2w-drop:hover,
.pdm-drop:hover,
.mt-drop:hover,
.rw-drop:hover,
.ts-dropzone:hover,
.upload-section:hover,
.upload-view:hover,
.upload-container:hover,
.initial-upload-view:hover,
.drop-zone-container:hover,
.upload-dropzone:hover,
.drop-area:hover,
.drop-target:hover,
#drop-zone:hover,
#upload-area:hover,
#upload-view:hover,
#ps-upload:hover,
#initial-upload-zone:hover {
  border-color: color-mix(in srgb, var(--accent-cyan) 46%, var(--card-border)) !important;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.16),
    0 16px 34px -18px rgba(0,0,0,.8),
    0 0 0 3px color-mix(in srgb, var(--accent-cyan) 12%, transparent) !important;
}
.drop-zone:hover::after,
.upload-zone:hover::after,
.upload-area:hover::after,
.cp-drop:hover::after,
.wp-drop:hover::after,
.massive-drop-zone:hover::after,
.cr-drop:hover::after,
.p2w-drop:hover::after,
.pdm-drop:hover::after,
.mt-drop:hover::after,
.rw-drop:hover::after,
.ts-dropzone:hover::after,
.upload-section:hover::after,
.upload-view:hover::after,
.upload-container:hover::after,
.initial-upload-view:hover::after,
.drop-zone-container:hover::after,
.upload-dropzone:hover::after,
.drop-area:hover::after,
.drop-target:hover::after,
#drop-zone:hover::after,
#upload-area:hover::after,
#upload-view:hover::after,
#ps-upload:hover::after,
#initial-upload-zone:hover::after {
  border-color: color-mix(in srgb, var(--accent-cyan) 52%, transparent) !important;
}
html:not(.dark) .drop-zone:hover,
html:not(.dark) .upload-zone:hover,
html:not(.dark) .upload-area:hover,
html:not(.dark) .cp-drop:hover,
html:not(.dark) .wp-drop:hover,
html:not(.dark) .massive-drop-zone:hover,
html:not(.dark) .cr-drop:hover,
html:not(.dark) .p2w-drop:hover,
html:not(.dark) .pdm-drop:hover,
html:not(.dark) .mt-drop:hover,
html:not(.dark) .rw-drop:hover,
html:not(.dark) .ts-dropzone:hover,
html:not(.dark) .upload-section:hover,
html:not(.dark) .upload-view:hover,
html:not(.dark) .upload-container:hover,
html:not(.dark) .initial-upload-view:hover,
html:not(.dark) .drop-zone-container:hover,
html:not(.dark) .upload-dropzone:hover,
html:not(.dark) .drop-area:hover,
html:not(.dark) .drop-target:hover,
html:not(.dark) #drop-zone:hover,
html:not(.dark) #upload-area:hover,
html:not(.dark) #upload-view:hover,
html:not(.dark) #ps-upload:hover,
html:not(.dark) #initial-upload-zone:hover {
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.95),
    0 14px 28px -18px rgba(0,0,0,.34),
    0 0 0 3px color-mix(in srgb, var(--accent-cyan) 10%, transparent) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE CTA LABEL, AT A SPECIFICITY THE PAGES CANNOT OUTRANK.

   The rule above sets `color: var(--accent-cyan)` at (0,1,1). Measured across
   all 84 pages in BOTH themes, exactly one page beat it and the result was the
   owner's "theme issues" report: pdf-to-grayscale rendered "Select PDF File" in
   rgb(15,23,42) on rgb(30,41,59) -- 1.22:1, invisible in dark mode, while the
   same page reads correctly in light. Its own rule sets --btn-text, the contrast
   colour for a SOLID accent fill, and round two replaced that fill with a
   translucent gradient.

   One page failing is not an argument for a page rule: it is an argument for the
   shared rule winning everywhere, because the next page copied from that one
   fails the same way and nothing says so. The gate is tests/tool-scale, and the
   measurement is the CTA contrast sweep.

   ALSO REDUCES THE GLOW AND SOFTENS NOTHING ELSE. Owner, 2026-09-01: "reduce the
   glow from the button". Several pages carry a coloured halo sized for a solid
   fill; over a translucent pill it reads as a smudge. One restrained shadow
   replaces them, and the radius stays the 999px pill the round already set.
   ═══════════════════════════════════════════════════════════════════════════ */
html:root .tool-scope .drop-zone button,
html:root .tool-scope .drop-zone .btn-upload,
html:root .tool-scope .drop-zone .upload-btn,
html:root .tool-scope .upload-zone button,
html:root .tool-scope .upload-zone .btn-upload,
html:root .tool-scope .upload-zone .upload-btn,
html:root .tool-scope .upload-area button,
html:root .tool-scope .upload-area .btn-upload,
html:root .tool-scope .upload-area .upload-btn,
html:root .tool-scope .cp-drop button,
html:root .tool-scope .cp-drop .btn-upload,
html:root .tool-scope .cp-drop .upload-btn,
html:root .tool-scope .wp-drop button,
html:root .tool-scope .wp-drop .btn-upload,
html:root .tool-scope .wp-drop .upload-btn,
html:root .tool-scope .massive-drop-zone button,
html:root .tool-scope .massive-drop-zone .btn-upload,
html:root .tool-scope .massive-drop-zone .upload-btn,
html:root .tool-scope .cr-drop button,
html:root .tool-scope .cr-drop .btn-upload,
html:root .tool-scope .cr-drop .upload-btn,
html:root .tool-scope .p2w-drop button,
html:root .tool-scope .p2w-drop .btn-upload,
html:root .tool-scope .p2w-drop .upload-btn,
html:root .tool-scope .pdm-drop button,
html:root .tool-scope .pdm-drop .btn-upload,
html:root .tool-scope .pdm-drop .upload-btn,
html:root .tool-scope .mt-drop button,
html:root .tool-scope .mt-drop .btn-upload,
html:root .tool-scope .mt-drop .upload-btn,
html:root .tool-scope .rw-drop button,
html:root .tool-scope .rw-drop .btn-upload,
html:root .tool-scope .rw-drop .upload-btn,
html:root .tool-scope .ts-dropzone button,
html:root .tool-scope .ts-dropzone .btn-upload,
html:root .tool-scope .ts-dropzone .upload-btn,
html:root .tool-scope .upload-section button,
html:root .tool-scope .upload-section .btn-upload,
html:root .tool-scope .upload-section .upload-btn,
html:root .tool-scope .upload-view button,
html:root .tool-scope .upload-view .btn-upload,
html:root .tool-scope .upload-view .upload-btn,
html:root .tool-scope .upload-container button,
html:root .tool-scope .upload-container .btn-upload,
html:root .tool-scope .upload-container .upload-btn,
html:root .tool-scope .initial-upload-view button,
html:root .tool-scope .initial-upload-view .btn-upload,
html:root .tool-scope .initial-upload-view .upload-btn,
html:root .tool-scope .drop-zone-container button,
html:root .tool-scope .drop-zone-container .btn-upload,
html:root .tool-scope .drop-zone-container .upload-btn,
html:root .tool-scope .upload-dropzone button,
html:root .tool-scope .upload-dropzone .btn-upload,
html:root .tool-scope .upload-dropzone .upload-btn,
html:root .tool-scope .drop-area button,
html:root .tool-scope .drop-area .btn-upload,
html:root .tool-scope .drop-area .upload-btn,
html:root .tool-scope .drop-target button,
html:root .tool-scope .drop-target .btn-upload,
html:root .tool-scope .drop-target .upload-btn,
html:root .tool-scope #drop-zone button,
html:root .tool-scope #drop-zone .btn-upload,
html:root .tool-scope #drop-zone .upload-btn,
html:root .tool-scope #upload-area button,
html:root .tool-scope #upload-area .btn-upload,
html:root .tool-scope #upload-area .upload-btn,
html:root .tool-scope #upload-view button,
html:root .tool-scope #upload-view .btn-upload,
html:root .tool-scope #upload-view .upload-btn,
html:root .tool-scope #ps-upload button,
html:root .tool-scope #ps-upload .btn-upload,
html:root .tool-scope #ps-upload .upload-btn,
html:root .tool-scope #initial-upload-zone button,
html:root .tool-scope #initial-upload-zone .btn-upload,
html:root .tool-scope #initial-upload-zone .upload-btn {
  color: var(--accent-cyan) !important;
  border-radius: 999px !important;
  box-shadow: 0 2px 10px -6px rgba(0,0,0,.55) !important;
}

/* THE PLATED GLYPH TAKES THE PAGE'S ACCENT. Owner, 2026-09-01: "change the icon
   color". Most of these SVGs are authored with stroke="currentColor" or
   fill="currentColor", so setting `color` on the plate tints them without
   touching a glyph that states its own fill. */
.drop-icon > svg,
.upload-icon > svg,
.drop-zone-icon > svg,
.wp-drop-icon > svg,
.cp-drop-orb > svg,
.cr-drop-orb > svg,
.p2w-drop-icon > svg,
.pdm-drop-icon > svg {
  color: var(--accent-cyan) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE BUTTON'S HOVER IS VIOLET, NOT MORE CYAN. Owner, 2026-09-01: "on button
   hover add some light purple color instead of blue neon and should apply for
   all."

   The resting pill is a cyan-to-violet gradient with a cyan rim, so hovering it
   into MORE cyan read as a neon flare rather than a state change. Moving the
   hover to the violet end uses a colour the pill already contains, which is why
   it reads as the same control lighting up instead of a different one.

   Still the page's own token -- var(--accent-violet), verified present on all 44
   pdf-tools and all 48 image-tools pages -- so no hue is imposed and
   tests/tool-scale/no-colour.verify.mjs still holds.

   THE SPECIFICITY IS (0,4,1) ON PURPOSE. pdf-to-grayscale ships
   `:root.dark .tool-scope .upload-btn { color: ... !important }` at (0,4,0),
   which beat the previous `html .tool-scope ...` at (0,3,1) and left the label at
   1.22:1 in dark mode. `html:root` adds the class-level weight that clears it.
   ═══════════════════════════════════════════════════════════════════════════ */
html:root .tool-scope .drop-zone button:hover,
html:root .tool-scope .drop-zone .btn-upload:hover,
html:root .tool-scope .drop-zone .upload-btn:hover,
html:root .tool-scope .upload-zone button:hover,
html:root .tool-scope .upload-zone .btn-upload:hover,
html:root .tool-scope .upload-zone .upload-btn:hover,
html:root .tool-scope .upload-area button:hover,
html:root .tool-scope .upload-area .btn-upload:hover,
html:root .tool-scope .upload-area .upload-btn:hover,
html:root .tool-scope .cp-drop button:hover,
html:root .tool-scope .cp-drop .btn-upload:hover,
html:root .tool-scope .cp-drop .upload-btn:hover,
html:root .tool-scope .wp-drop button:hover,
html:root .tool-scope .wp-drop .btn-upload:hover,
html:root .tool-scope .wp-drop .upload-btn:hover,
html:root .tool-scope .massive-drop-zone button:hover,
html:root .tool-scope .massive-drop-zone .btn-upload:hover,
html:root .tool-scope .massive-drop-zone .upload-btn:hover,
html:root .tool-scope .cr-drop button:hover,
html:root .tool-scope .cr-drop .btn-upload:hover,
html:root .tool-scope .cr-drop .upload-btn:hover,
html:root .tool-scope .p2w-drop button:hover,
html:root .tool-scope .p2w-drop .btn-upload:hover,
html:root .tool-scope .p2w-drop .upload-btn:hover,
html:root .tool-scope .pdm-drop button:hover,
html:root .tool-scope .pdm-drop .btn-upload:hover,
html:root .tool-scope .pdm-drop .upload-btn:hover,
html:root .tool-scope .mt-drop button:hover,
html:root .tool-scope .mt-drop .btn-upload:hover,
html:root .tool-scope .mt-drop .upload-btn:hover,
html:root .tool-scope .rw-drop button:hover,
html:root .tool-scope .rw-drop .btn-upload:hover,
html:root .tool-scope .rw-drop .upload-btn:hover,
html:root .tool-scope .ts-dropzone button:hover,
html:root .tool-scope .ts-dropzone .btn-upload:hover,
html:root .tool-scope .ts-dropzone .upload-btn:hover,
html:root .tool-scope .upload-section button:hover,
html:root .tool-scope .upload-section .btn-upload:hover,
html:root .tool-scope .upload-section .upload-btn:hover,
html:root .tool-scope .upload-view button:hover,
html:root .tool-scope .upload-view .btn-upload:hover,
html:root .tool-scope .upload-view .upload-btn:hover,
html:root .tool-scope .upload-container button:hover,
html:root .tool-scope .upload-container .btn-upload:hover,
html:root .tool-scope .upload-container .upload-btn:hover,
html:root .tool-scope .initial-upload-view button:hover,
html:root .tool-scope .initial-upload-view .btn-upload:hover,
html:root .tool-scope .initial-upload-view .upload-btn:hover,
html:root .tool-scope .drop-zone-container button:hover,
html:root .tool-scope .drop-zone-container .btn-upload:hover,
html:root .tool-scope .drop-zone-container .upload-btn:hover,
html:root .tool-scope .upload-dropzone button:hover,
html:root .tool-scope .upload-dropzone .btn-upload:hover,
html:root .tool-scope .upload-dropzone .upload-btn:hover,
html:root .tool-scope .drop-area button:hover,
html:root .tool-scope .drop-area .btn-upload:hover,
html:root .tool-scope .drop-area .upload-btn:hover,
html:root .tool-scope .drop-target button:hover,
html:root .tool-scope .drop-target .btn-upload:hover,
html:root .tool-scope .drop-target .upload-btn:hover,
html:root .tool-scope #drop-zone button:hover,
html:root .tool-scope #drop-zone .btn-upload:hover,
html:root .tool-scope #drop-zone .upload-btn:hover,
html:root .tool-scope #upload-area button:hover,
html:root .tool-scope #upload-area .btn-upload:hover,
html:root .tool-scope #upload-area .upload-btn:hover,
html:root .tool-scope #upload-view button:hover,
html:root .tool-scope #upload-view .btn-upload:hover,
html:root .tool-scope #upload-view .upload-btn:hover,
html:root .tool-scope #ps-upload button:hover,
html:root .tool-scope #ps-upload .btn-upload:hover,
html:root .tool-scope #ps-upload .upload-btn:hover,
html:root .tool-scope #initial-upload-zone button:hover,
html:root .tool-scope #initial-upload-zone .btn-upload:hover,
html:root .tool-scope .btn-upload:hover,
html:root .tool-scope .upload-btn:hover {
  color: color-mix(in srgb, var(--accent-violet) 92%, var(--text-main)) !important;
  border-color: color-mix(in srgb, var(--accent-violet) 50%, transparent) !important;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent-violet) 34%, rgba(255,255,255,1)),
    color-mix(in srgb, var(--accent-violet) 22%, rgba(255,255,255,1))) !important;
  box-shadow:
    0 6px 18px -10px color-mix(in srgb, var(--accent-violet) 70%, transparent),
    0 0 0 4px color-mix(in srgb, var(--accent-violet) 12%, transparent) !important;
  transform: translateY(-1px) !important;
}

/* ── THE SAME WASH, DARK THEME ─────────────────────────────────────────────
   Mixing toward opaque white is what makes the fill READ as light purple on a
   white page. On a slate ground it would paint a milky slab, so dark keeps the
   violet translucent and lifts the LABEL toward white instead. Same token, same
   gesture, opposite direction -- which is the whole reason this is two rules and
   not one.

   (0,5,1) beats the base rule's (0,4,1), and both clear pdf-to-grayscale's
   own (0,4,0) that caused the 1.22:1 label. */
html:root.dark .tool-scope .drop-zone button:hover,
html:root.dark .tool-scope .drop-zone .btn-upload:hover,
html:root.dark .tool-scope .drop-zone .upload-btn:hover,
html:root.dark .tool-scope .upload-zone button:hover,
html:root.dark .tool-scope .upload-zone .btn-upload:hover,
html:root.dark .tool-scope .upload-zone .upload-btn:hover,
html:root.dark .tool-scope .upload-area button:hover,
html:root.dark .tool-scope .upload-area .btn-upload:hover,
html:root.dark .tool-scope .upload-area .upload-btn:hover,
html:root.dark .tool-scope .cp-drop button:hover,
html:root.dark .tool-scope .cp-drop .btn-upload:hover,
html:root.dark .tool-scope .cp-drop .upload-btn:hover,
html:root.dark .tool-scope .wp-drop button:hover,
html:root.dark .tool-scope .wp-drop .btn-upload:hover,
html:root.dark .tool-scope .wp-drop .upload-btn:hover,
html:root.dark .tool-scope .massive-drop-zone button:hover,
html:root.dark .tool-scope .massive-drop-zone .btn-upload:hover,
html:root.dark .tool-scope .massive-drop-zone .upload-btn:hover,
html:root.dark .tool-scope .cr-drop button:hover,
html:root.dark .tool-scope .cr-drop .btn-upload:hover,
html:root.dark .tool-scope .cr-drop .upload-btn:hover,
html:root.dark .tool-scope .p2w-drop button:hover,
html:root.dark .tool-scope .p2w-drop .btn-upload:hover,
html:root.dark .tool-scope .p2w-drop .upload-btn:hover,
html:root.dark .tool-scope .pdm-drop button:hover,
html:root.dark .tool-scope .pdm-drop .btn-upload:hover,
html:root.dark .tool-scope .pdm-drop .upload-btn:hover,
html:root.dark .tool-scope .mt-drop button:hover,
html:root.dark .tool-scope .mt-drop .btn-upload:hover,
html:root.dark .tool-scope .mt-drop .upload-btn:hover,
html:root.dark .tool-scope .rw-drop button:hover,
html:root.dark .tool-scope .rw-drop .btn-upload:hover,
html:root.dark .tool-scope .rw-drop .upload-btn:hover,
html:root.dark .tool-scope .ts-dropzone button:hover,
html:root.dark .tool-scope .ts-dropzone .btn-upload:hover,
html:root.dark .tool-scope .ts-dropzone .upload-btn:hover,
html:root.dark .tool-scope .upload-section button:hover,
html:root.dark .tool-scope .upload-section .btn-upload:hover,
html:root.dark .tool-scope .upload-section .upload-btn:hover,
html:root.dark .tool-scope .upload-view button:hover,
html:root.dark .tool-scope .upload-view .btn-upload:hover,
html:root.dark .tool-scope .upload-view .upload-btn:hover,
html:root.dark .tool-scope .upload-container button:hover,
html:root.dark .tool-scope .upload-container .btn-upload:hover,
html:root.dark .tool-scope .upload-container .upload-btn:hover,
html:root.dark .tool-scope .initial-upload-view button:hover,
html:root.dark .tool-scope .initial-upload-view .btn-upload:hover,
html:root.dark .tool-scope .initial-upload-view .upload-btn:hover,
html:root.dark .tool-scope .drop-zone-container button:hover,
html:root.dark .tool-scope .drop-zone-container .btn-upload:hover,
html:root.dark .tool-scope .drop-zone-container .upload-btn:hover,
html:root.dark .tool-scope .upload-dropzone button:hover,
html:root.dark .tool-scope .upload-dropzone .btn-upload:hover,
html:root.dark .tool-scope .upload-dropzone .upload-btn:hover,
html:root.dark .tool-scope .drop-area button:hover,
html:root.dark .tool-scope .drop-area .btn-upload:hover,
html:root.dark .tool-scope .drop-area .upload-btn:hover,
html:root.dark .tool-scope .drop-target button:hover,
html:root.dark .tool-scope .drop-target .btn-upload:hover,
html:root.dark .tool-scope .drop-target .upload-btn:hover,
html:root.dark .tool-scope #drop-zone button:hover,
html:root.dark .tool-scope #drop-zone .btn-upload:hover,
html:root.dark .tool-scope #drop-zone .upload-btn:hover,
html:root.dark .tool-scope #upload-area button:hover,
html:root.dark .tool-scope #upload-area .btn-upload:hover,
html:root.dark .tool-scope #upload-area .upload-btn:hover,
html:root.dark .tool-scope #upload-view button:hover,
html:root.dark .tool-scope #upload-view .btn-upload:hover,
html:root.dark .tool-scope #upload-view .upload-btn:hover,
html:root.dark .tool-scope #ps-upload button:hover,
html:root.dark .tool-scope #ps-upload .btn-upload:hover,
html:root.dark .tool-scope #ps-upload .upload-btn:hover,
html:root.dark .tool-scope #initial-upload-zone button:hover,
html:root.dark .tool-scope #initial-upload-zone .btn-upload:hover,
html:root.dark .tool-scope .btn-upload:hover,
html:root.dark .tool-scope .upload-btn:hover {
  color: color-mix(in srgb, var(--accent-violet) 30%, rgba(255,255,255,1)) !important;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent-violet) 30%, transparent),
    color-mix(in srgb, var(--accent-violet) 18%, transparent)) !important;
}

/* The lift is decoration; the colour is the state. Reduced motion keeps the
   colour and drops the movement. */
@media (prefers-reduced-motion: reduce) {
  html:root .tool-scope .drop-zone button:hover,
  html:root .tool-scope .drop-zone .btn-upload:hover,
  html:root .tool-scope .drop-zone .upload-btn:hover,
  html:root .tool-scope .upload-zone button:hover,
  html:root .tool-scope .upload-zone .btn-upload:hover,
  html:root .tool-scope .upload-zone .upload-btn:hover,
  html:root .tool-scope .upload-area button:hover,
  html:root .tool-scope .upload-area .btn-upload:hover,
  html:root .tool-scope .upload-area .upload-btn:hover,
  html:root .tool-scope .cp-drop button:hover,
  html:root .tool-scope .cp-drop .btn-upload:hover,
  html:root .tool-scope .cp-drop .upload-btn:hover,
  html:root .tool-scope .wp-drop button:hover,
  html:root .tool-scope .wp-drop .btn-upload:hover,
  html:root .tool-scope .wp-drop .upload-btn:hover,
  html:root .tool-scope .massive-drop-zone button:hover,
  html:root .tool-scope .massive-drop-zone .btn-upload:hover,
  html:root .tool-scope .massive-drop-zone .upload-btn:hover,
  html:root .tool-scope .cr-drop button:hover,
  html:root .tool-scope .cr-drop .btn-upload:hover,
  html:root .tool-scope .cr-drop .upload-btn:hover,
  html:root .tool-scope .p2w-drop button:hover,
  html:root .tool-scope .p2w-drop .btn-upload:hover,
  html:root .tool-scope .p2w-drop .upload-btn:hover,
  html:root .tool-scope .pdm-drop button:hover,
  html:root .tool-scope .pdm-drop .btn-upload:hover,
  html:root .tool-scope .pdm-drop .upload-btn:hover,
  html:root .tool-scope .mt-drop button:hover,
  html:root .tool-scope .mt-drop .btn-upload:hover,
  html:root .tool-scope .mt-drop .upload-btn:hover,
  html:root .tool-scope .rw-drop button:hover,
  html:root .tool-scope .rw-drop .btn-upload:hover,
  html:root .tool-scope .rw-drop .upload-btn:hover,
  html:root .tool-scope .ts-dropzone button:hover,
  html:root .tool-scope .ts-dropzone .btn-upload:hover,
  html:root .tool-scope .ts-dropzone .upload-btn:hover,
  html:root .tool-scope .upload-section button:hover,
  html:root .tool-scope .upload-section .btn-upload:hover,
  html:root .tool-scope .upload-section .upload-btn:hover,
  html:root .tool-scope .upload-view button:hover,
  html:root .tool-scope .upload-view .btn-upload:hover,
  html:root .tool-scope .upload-view .upload-btn:hover,
  html:root .tool-scope .upload-container button:hover,
  html:root .tool-scope .upload-container .btn-upload:hover,
  html:root .tool-scope .upload-container .upload-btn:hover,
  html:root .tool-scope .initial-upload-view button:hover,
  html:root .tool-scope .initial-upload-view .btn-upload:hover,
  html:root .tool-scope .initial-upload-view .upload-btn:hover,
  html:root .tool-scope .drop-zone-container button:hover,
  html:root .tool-scope .drop-zone-container .btn-upload:hover,
  html:root .tool-scope .drop-zone-container .upload-btn:hover,
  html:root .tool-scope .upload-dropzone button:hover,
  html:root .tool-scope .upload-dropzone .btn-upload:hover,
  html:root .tool-scope .upload-dropzone .upload-btn:hover,
  html:root .tool-scope .drop-area button:hover,
  html:root .tool-scope .drop-area .btn-upload:hover,
  html:root .tool-scope .drop-area .upload-btn:hover,
  html:root .tool-scope .drop-target button:hover,
  html:root .tool-scope .drop-target .btn-upload:hover,
  html:root .tool-scope .drop-target .upload-btn:hover,
  html:root .tool-scope #drop-zone button:hover,
  html:root .tool-scope #drop-zone .btn-upload:hover,
  html:root .tool-scope #drop-zone .upload-btn:hover,
  html:root .tool-scope #upload-area button:hover,
  html:root .tool-scope #upload-area .btn-upload:hover,
  html:root .tool-scope #upload-area .upload-btn:hover,
  html:root .tool-scope #upload-view button:hover,
  html:root .tool-scope #upload-view .btn-upload:hover,
  html:root .tool-scope #upload-view .upload-btn:hover,
  html:root .tool-scope #ps-upload button:hover,
  html:root .tool-scope #ps-upload .btn-upload:hover,
  html:root .tool-scope #ps-upload .upload-btn:hover,
  html:root .tool-scope #initial-upload-zone button:hover,
  html:root .tool-scope #initial-upload-zone .btn-upload:hover,
  html:root .tool-scope .btn-upload:hover,
  html:root .tool-scope .upload-btn:hover {
    transform: none !important;
  }
}

/* THE CONSTRAINTS FOOTNOTE. Deliberately the smallest type in the zone: it is a
   reference, read once, not a message.

   WRITTEN AS `p.drop-constraints`, AND THE BARE CLASS DID NOT WORK. This file's own
   type scale carries `.tool-scope p, .tool-scope li { font-size: 15px !important }`,
   which is (0,2,1); a bare `.drop-constraints` is (0,1,0) and loses on specificity
   even though both are !important -- the footnote rendered at 15px on every page
   that got it, and the only thing that said so was a measurement. Adding the element
   ties at (0,2,1) and wins on source order, since this block is further down the
   same stylesheet.

   The bare selector stays alongside it for any zone that sits outside .tool-scope. */
p.drop-constraints,
.tool-scope p.drop-constraints,
.drop-constraints {
  font-size: 11.5px !important;
  line-height: 1.4 !important;
  margin: 4px 0 0 !important;
  opacity: .85 !important;
}
@media (min-width: 900px) {
  p.drop-constraints,
  .tool-scope p.drop-constraints,
  .drop-constraints { font-size: 12.5px !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE SUB-LINE RULE HAS NEVER WORKED ON A BARE <p>, AND THIS FIXES IT.

   Found while chasing two pages that clipped under round two's phone cap. The cap
   was not the cause; this was.

   Round one declares, both inside @media (max-width: 899px):

     .drop-zone > p, .upload-zone > p, ...   font-size: 13px !important   (0,1,1)
     .tool-scope p, .tool-scope li           font-size: 15px !important   (0,2,1)

   Both carry !important, so specificity decides and the general rule wins on every
   page whose sub-line is an unclassed <p>. The 13px only ever applied where the
   paragraph also had a class from the first list (.drop-zone-text and friends).
   pdf-dark-mode's sub-line measured 15px and ran to 113px over five lines inside a
   258px box; that is 38px of a 34px overflow.

   NOTHING ON SCREEN SAID SO. The paragraph looked like body copy because it WAS
   body copy, at the size the page would have used anyway. Only a
   document.styleSheets walk naming the winning rule found it, which is the same
   method and the same lesson as the 2026-08-12 entry in
   memory/traps/glass-design-layer.md: losing to !important raises nothing.

   Written as a separate block at the END of the file rather than by editing the
   original: `.tool-scope <zone> > p` is (0,2,1), which TIES the general rule, and a
   tie is broken by source order. Being last is the whole mechanism. Do not move
   this block above the type scale.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 899px) {
  .tool-scope .drop-zone > p,
  .tool-scope .upload-zone > p,
  .tool-scope .upload-area > p,
  .tool-scope .cp-drop > p,
  .tool-scope .wp-drop > p,
  .tool-scope .massive-drop-zone > p,
  .tool-scope .cr-drop > p,
  .tool-scope .p2w-drop > p,
  .tool-scope .pdm-drop > p,
  .tool-scope .mt-drop > p,
  .tool-scope .rw-drop > p,
  .tool-scope .ts-dropzone > p,
  .tool-scope .upload-section > p,
  .tool-scope .upload-view > p,
  .tool-scope .upload-container > p,
  .tool-scope .initial-upload-view > p,
  .tool-scope .drop-zone-container > p,
  .tool-scope .upload-dropzone > p,
  .tool-scope .drop-area > p,
  .tool-scope .drop-target > p,
  .tool-scope #drop-zone > p,
  .tool-scope #upload-area > p,
  .tool-scope #upload-view > p,
  .tool-scope #ps-upload > p,
  .tool-scope #initial-upload-zone > p,
  /* The named hint classes need the same treatment for the same reason: they are
     (0,1,0) on their own and lose to `.tool-scope p` just as the bare <p> does. */
  .tool-scope .pdm-drop-hint,
  .tool-scope .p2w-drop-hint,
  .tool-scope .wp-drop-hint,
  .tool-scope .cr-drop-sub,
  .tool-scope .cp-drop-sub,
  .tool-scope .drop-zone-text,
  .tool-scope .drop-zone-desc,
  .tool-scope .drop-zone-sub,
  .tool-scope .upload-text,
  .tool-scope .upload-sub,
  .tool-scope .upload-subtext,
  .tool-scope .upload-p {
    font-size: 13px !important;
    line-height: 1.45 !important;
    margin: 0 !important;
  }
}

/* THE ONE PAGE THE PHONE CAP DOES NOT APPLY TO, AND WHY IT IS ONE AND NOT FIVE.
   tests/tool-scale/no-clip.verify.mjs found five zones overflowing the 260px cap.
   FOUR OF THEM WERE A BUG IN THIS FILE, not pages needing an exemption: the 13px
   sub-line rule was losing to `.tool-scope p` on specificity (see the block above),
   so their paragraphs were rendering at 15px. Fixing that dropped pdf-to-word from
   300px to 225 and cleared webp-to-pdf, gif-converter and image-converter outright.

   THE ORDER MATTERS AND IT IS THE POINT: an exemption written before the cause was
   found would have pardoned four real defects permanently, and every one of them
   would still be shipping a 15px paragraph today.

   pdf-dark-mode is the genuine one. Its .pdm-drop is the only zone on the site
   carrying two prose blocks -- a 4-line paragraph AND a 2-line hint -- beside a
   glyph, a 2-line heading and a button. 270px measured at 390px wide, of which the
   last 12px is text. Recorded in scripts/lib/drop-zones.mjs HEIGHT_EXEMPT with the
   same numbers; the gate asserts an exempt page really does carry max-height:none,
   so this rule and that entry cannot drift apart. */
.pdm-drop {
  max-height: none !important;
}

/* image-rotator's zone is inset:0 over the canvas stage, so its height comes from
   the stage: MEASURED 598px of content in a 450px box, and a cap clips the canvas.
   Round one exempted it from the phone scale for the same reason.

   WRITTEN AT EVERY WIDTH, AND THE FIRST VERSION WAS NOT. It sat inside
   @media (min-width: 900px), so the phone kept the 260px cap and the gate caught it
   -- "is height-exempt but still has max-height 260px". An exemption that only
   applies at one breakpoint is not an exemption, and the assertion that an exempt
   page really carries max-height:none is what turned that into a message rather
   than a silently clipped canvas. */
#imageCanvasWrapper .upload-zone,
.canvas-wrapper .upload-zone,
#uploadOverlay.upload-zone {
  min-height: 0 !important;
  max-height: none !important;
}

/* THE TITLE, AT PHONE WIDTH, FOR THE CONTAINERS ROUND ONE DID NOT KNOW.
   `.upload-text` is in round one's SUB-LINE list, and image-cropper uses it on its
   <h2>: `<h2 class="upload-text">Drag & Drop Image Here</h2>`. So the sub-line block
   above -- which had to be raised to (0,2,0) to beat `.tool-scope p` -- started
   winning against that heading and rendered the page's drop title at 13px. The
   evenness gate reported it as "2 title sizes: 17x73, 13x1", which is one page in
   ninety and exactly the kind of thing no one sees by looking.

   A class name is not a role. `.upload-text` means "text in the upload area" on 88
   pages and "the heading of the upload area" on one, and no amount of care with the
   class lists resolves that. Selecting the ELEMENT inside the container does:
   `.tool-scope #upload-view h2` is (0,2,1) and beats `.tool-scope .upload-text` at
   (0,2,0) on specificity, wherever either sits in the file.

   The desktop equivalent of this block already exists in the min-width:900px section
   above; this is the phone half, which round one had only for its own six
   containers. */
@media (max-width: 899px) {
  .tool-scope .cr-drop h2, .tool-scope .cr-drop h3,
  .tool-scope .p2w-drop h2, .tool-scope .p2w-drop h3,
  .tool-scope .pdm-drop h2, .tool-scope .pdm-drop h3,
  .tool-scope .mt-drop h2, .tool-scope .mt-drop h3,
  .tool-scope .rw-drop h2, .tool-scope .rw-drop h3,
  .tool-scope .ts-dropzone h2, .tool-scope .ts-dropzone h3,
  .tool-scope .upload-section h2, .tool-scope .upload-section h3,
  .tool-scope .upload-view h2, .tool-scope .upload-view h3,
  .tool-scope .upload-container h2, .tool-scope .upload-container h3,
  .tool-scope .initial-upload-view h2, .tool-scope .initial-upload-view h3,
  .tool-scope .drop-zone-container h2, .tool-scope .drop-zone-container h3,
  .tool-scope .upload-dropzone h2, .tool-scope .upload-dropzone h3,
  .tool-scope .drop-area h2, .tool-scope .drop-area h3,
  .tool-scope .drop-target h2, .tool-scope .drop-target h3,
  .tool-scope #drop-zone h2, .tool-scope #drop-zone h3,
  .tool-scope #upload-area h2, .tool-scope #upload-area h3,
  .tool-scope #upload-view h2, .tool-scope #upload-view h3,
  .tool-scope #ps-upload h2, .tool-scope #ps-upload h3,
  .tool-scope #initial-upload-zone h2, .tool-scope #initial-upload-zone h3,
  .tool-scope .drop-zone h2, .tool-scope .drop-zone h3,
  .tool-scope .upload-zone h2, .tool-scope .upload-zone h3,
  .tool-scope .upload-area h2, .tool-scope .upload-area h3,
  .tool-scope .cp-drop h2, .tool-scope .cp-drop h3,
  .tool-scope .wp-drop h2, .tool-scope .wp-drop h3,
  .tool-scope .massive-drop-zone h2, .tool-scope .massive-drop-zone h3 {
    font-size: 17px !important;
    font-weight: 650 !important;
    line-height: 1.3 !important;
    letter-spacing: -0.01em !important;
    margin: 0 !important;
  }
}
