/* touch.css — tap-target repairs for phones and tablets.

   WHY A SEPARATE FILE. design.css is generated from design-source/ and is
   overwritten by `npm run convert:design`. responsive.css is Lovable's layer
   and is being actively edited there; keeping these rules apart avoids two
   agents writing the same file. Fold this into responsive.css once ownership
   of that file is settled — the rules below are not in conflict with it, they
   sit on top of its min-height:44px for buttons.

   THE PROBLEM. A sweep of 30 pages at 390px found eight inline links between
   16px and 20px tall — among them the school's phone number (17px) and its
   email address (18px). Those two are the primary action on a phone, and a
   17px target is roughly a third of what a thumb reliably hits (Apple and
   Google both put the floor around 44px / 48dp).

   The selector deliberately skips links that already carry their own padding
   or a pill radius -- those are buttons and responsive.css has already given
   them 44px; adding to them would double the box.

   THE APPROACH. NOT `min-height:44px` on every inline link: these sit inside
   sentences, and giving each one a 44px box would open gaps in the paragraph
   and break the line rhythm. Instead the HIT AREA is grown while the layout
   box stays put — vertical padding expands the target, an equal negative
   margin cancels the effect on flow. The text does not move; the area a
   thumb can land on roughly doubles.

   WCAG 2.5.8 exempts links inline in a sentence from the minimum target size.
   This goes past the exemption on purpose: "exempt" describes what passes an
   audit, not what a parent can hit one-handed at nine at night. */

/* 1400 rather than 1150: an iPad in landscape is 1024-1366 logical pixels
   wide, a finger all the same. The first version stopped at 1150 and a
   landscape iPad Air (1180) got no tap-target help at all -- the sweep found
   17 sub-32px targets on /programs at that size. */
@media (max-width: 1400px) and (pointer: coarse), (max-width: 900px) {

  /* Inline links inside running text. Scoped to the page body so the header,
     the rail and the admin chrome keep their own metrics. */
  main.page a:not([style*="border-radius:999px"]):not([style*="padding"]):not(.rail-link):not([data-page]) {
    padding-block: 10px;
    margin-block: -10px;
    /* The larger box must not clip against the line above or below. */
    position: relative;
    z-index: 1;
    /* A tapped link that wraps across two lines should highlight as one. */
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
  }

  /* Phone and email are actions, not prose, and get a real target plus the
     visual affordance to match. */
  main.page a[href^="tel:"],
  main.page a[href^="mailto:"] {
    display: inline-block;
    min-height: 44px;
    line-height: 44px;
    padding-inline: 2px;
    margin-block: 0;
    font-weight: 700;
    text-decoration: underline;
    text-underline-offset: 3px;
  }

  /* Anything that is genuinely a standalone link-as-button — sitting on its
     own line rather than inside a sentence — takes the full target. */
  main.page > section > a,
  main.page > section > div > a[style*="border-radius:999px"] {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* A mouse does not need the padding, and on a hybrid device the coarse-pointer
   query above already covers touch. Nothing here applies to desktop. */

/* ── Body text that is too small to read on a phone ───────────────────────
   A sweep of ten pages at 390px found 24 runs of real prose (paragraphs and
   list items over 40 characters) set at 13px or 14px. iOS body text is 17px
   by default and 16px is the usual web floor; 13px is a squint.

   Raised NARROWLY rather than by a blanket rule. body.html carries 253 inline
   `font-size:13px|14px` declarations and most are correct — badges, pills,
   eyebrow labels, stamp text — where 13px is a deliberate typographic choice
   and enlarging it would wrap pills and break the layout. So:

     - only `p` and `li`, which is where prose lives; `span` is where the
       micro-labels live and is untouched
     - not uppercase letter-spaced labels, which are eyebrows rather than
       reading text and would wrap if grown

   The sizes land at 15px and 15.5px, not 16px: the pack's own body scale
   steps 15/16/17/19, and jumping two steps changes the rhythm of a paragraph
   rather than just its legibility. */
/* Also on any touch device up to a landscape iPad: 14.5px prose is as hard
   to read at arm's length on a tablet as on a phone, and the sweep found 20
   such runs on the homepage at 1180x820. */
@media (max-width: 900px), (max-width: 1400px) and (pointer: coarse) {
  /* The pack sets these inline at half-pixel steps -- 14.5, 13.5, 12.5 -- so
     the selector has to name each literal. A first pass matched only "13px"
     and "14px" and changed nothing: every offending paragraph on the homepage
     was 14.5px, and the one true 14px was the uppercase eyebrow this rule is
     right to skip. Measure, do not assume the round numbers. */
  main.page p[style*="font-size:14.5px"]:not([style*="text-transform:uppercase"]),
  main.page li[style*="font-size:14.5px"]:not([style*="text-transform:uppercase"]),
  main.page p[style*="font-size:14px"]:not([style*="text-transform:uppercase"]),
  main.page li[style*="font-size:14px"]:not([style*="text-transform:uppercase"]) {
    font-size: 15.5px !important;
    line-height: 1.55;
  }
  main.page p[style*="font-size:13.5px"]:not([style*="text-transform:uppercase"]),
  main.page li[style*="font-size:13.5px"]:not([style*="text-transform:uppercase"]),
  main.page p[style*="font-size:13px"]:not([style*="text-transform:uppercase"]),
  main.page li[style*="font-size:13px"]:not([style*="text-transform:uppercase"]),
  main.page p[style*="font-size:12.5px"]:not([style*="text-transform:uppercase"]),
  main.page li[style*="font-size:12.5px"]:not([style*="text-transform:uppercase"]),
  main.page p[style*="font-size:12px"]:not([style*="text-transform:uppercase"]),
  main.page li[style*="font-size:12px"]:not([style*="text-transform:uppercase"]) {
    font-size: 15px !important;
    line-height: 1.55;
  }
}

/* ── Virtual tour hotspots ───────────────────────────────────────────────
   The pack draws each hotspot as a 30px pulsing dot. That is the right
   VISUAL size -- a 44px dot over a photograph is a sticker -- but it is
   two-thirds of a reliable thumb target. So the hit area grows through a
   transparent pseudo-element and the dot itself does not change: 30px seen,
   48px tapped. */
@media (pointer: coarse) {
  #page-virtual-tour button[aria-label^="Hotspot"] { position: absolute; }
  #page-virtual-tour button[aria-label^="Hotspot"]::after {
    content: ""; position: absolute; inset: -9px; border-radius: 50%;
  }
}

/* ── Form controls: no focus-zoom, and a finger's worth of height ─────────
   iOS Safari zooms the whole page when a focused field's text is under 16px,
   and the zoom does not reverse on blur — the visitor is left on a sideways
   page mid-form. responsive.css already fixed this under 640px; tablets and
   landscape phones were still shipping 13.3px tour-form inputs and a 14.5px
   handbook search. Same media arm as the prose floor above: every touch
   device up to a landscape iPad, plus every narrow window.

   16px is a browser threshold, not a design decision — it is the smallest
   text Safari will leave alone. 48px is the field height that goes with it. */
@media (max-width: 900px), (max-width: 1400px) and (pointer: coarse) {
  main.page :is(input[type="text"], input[type="search"], input[type="email"],
    input[type="tel"], input[type="date"], input[type="number"], input:not([type]),
    select, textarea):not([aria-hidden="true"]):not([tabindex="-1"]) {
    font-size: 16px !important;
    min-height: 48px;
    box-sizing: border-box;
  }

  /* A range slider's track is thin by design; the hit area should not be. */
  main.page input[type="range"] {
    min-height: 44px;
  }
}

/* ── G2 · The label tier: one readable floor for kickers, chips and captions ─
   MOBILE_TABLET_AUDIT.md § G2. Phase 2, 2026-09-15.

   The prose floor above deliberately raises only `p` and `li`, and skips
   anything uppercase — because an uppercase letterspaced kicker is a label,
   not reading text, and a blanket rule would have wrapped every pill on the
   site. That left the label tier untouched, and measured across 32 pages at
   390px there were **218 runs of real text below 13px**: 10.5px column headers
   on /campus, 11px section kickers, 11.5px timeline captions, 12px document
   chips, 12.5px on roughly twenty pages. Letterspaced caps are the hardest
   thing on a page to read at arm's length, and these were the smallest.

   13px is the floor. The LETTERSPACING comes down as the size goes up —
   .16em–.24em is what makes a label look small and cramped, and holding it
   while growing the type is what makes a kicker wrap. At 13px, .14em is the
   same optical weight the label had at 11px with .2em.

   WHY LITERAL SIZES RATHER THAN A CLEVER SELECTOR. The audit's draft used
   `font-size: max(13px, 1em)`, which is wrong here: `1em` is the PARENT's
   size, so a 12px kicker inside a 19px paragraph would have jumped to 19px.
   The pack writes its sizes inline at half-pixel steps, so the five literals
   below are named the same way the prose floor above names its own — blunt,
   but it matches exactly what is there and nothing else.

   TWO DELIBERATE EXCLUSIONS, both on the homepage:
     · #home-hero — the two chips and the place line are 8.6px and 10.9px ON
       PURPOSE (home-mobile.css §1). The school asked for them on one line at
       the top of the first screen, and 13px puts them back onto three rows.
     · #home-built-here — the postcard's "THE WINTER PARK SCHOOL · AN
       INNOVATION ADVANTAGE SCHOOL™" band and its stamp lettering are drawn
       artwork, not labels. Real postcards have small print.

   Both are excluded with `:not(#id *)` ON the selector. The first attempt
   used a separate rule setting `font-size: revert-layer`, and that was wrong
   in a way worth recording: with no cascade layers declared, `revert-layer`
   behaves like `revert` and rolls the property back past the whole AUTHOR
   origin — including the element's own inline style. The postcard's 7.5px
   "WPS · 32789" stamp came back at the user-agent default of 16px and burst
   out of a 62px stamp. Measured, not noticed by eye. */
@media (max-width: 900px), (max-width: 1400px) and (pointer: coarse) {

  main.page :is(p, span, a, li, dt, th, div, label)[style*="text-transform:uppercase"]:is(
      [style*="font-size:10.5px"], [style*="font-size:11px"], [style*="font-size:11.5px"],
      [style*="font-size:12px"], [style*="font-size:12.5px"]
    ):not(#home-hero *):not(#home-built-here *) {
    font-size: 13px !important;
    letter-spacing: .14em !important;
  }

  /* The same tier where it comes from a class in design.css rather than from
     an inline style — the handbook's kickers and chapter chips, and the
     programme-hero age switcher. */
  main.page :is(.hb-kicker, .hb-qa__go, .hb-chapcard__c, .agesw-title, .agesw-all, .fd-kicker) {
    font-size: 13px !important;
    letter-spacing: .14em !important;
  }

  /* Non-uppercase small text that is still real reading: the portfolio's
     50-odd tag pills, the "1–2 years" age notes on /how-we-learn, and the
     calendar legend. `span` is outside the prose floor's reach by design, so
     each is named. */
  main.page #families-portfolio span[style*="font-size:12.5px"],
  main.page #how-we-learn-same-challenge span[style*="font-size:11.5px"],
  main.page #home-practical [data-fun-box] span,
  main.page [data-fun-box][style*="font-size:12px"] span,
  main.page [data-fun-box] > div[style*="font-size:12px"] span {
    font-size: 13px !important;
  }

  /* The age ladder's range line — "6 weeks – 12 months" under each age name,
     on all seven programme pages and the picker. 48 of them, and at 12.5px
     with opacity .8 it was the least legible text on those pages. The two
     attributes together are unique to this element. */
  main.page span[style*="font-size:12.5px"][style*="opacity:.8"] {
    font-size: 13px !important;
  }

  /* The C → A → P → E chip that heads the method section. Its inline size is
     `clamp(12px,1.3vw,16px)`, so it lands on the 12px floor of the clamp on a
     phone and the literal-size selectors above cannot see it. The tracking
     comes down further than the rest of the tier because this one sits in a
     pill that already wraps to two lines at 390px. */
  main.page span[data-cape-word] {
    font-size: 13px !important;
    letter-spacing: .1em !important;
  }

}

/* ── G8 · The tap targets still under 44px ────────────────────────────────
   MOBILE_TABLET_AUDIT.md § G8. Phase 2, 2026-09-15.

   Padding, never font-size: the label keeps its size and its place, and the
   surface a thumb can land on grows underneath it.

   The harness's remaining TAP entries after this are all known false
   positives, listed in §0 of the audit: the CSS-only tab radios (`#stg-*`,
   `#fd-*`, which are 1px and deliberately invisible — their <label>s are the
   target), the virtual-tour hotspots (30px dots with a 48px `::after` the
   harness cannot measure), the handbook's visually-hidden search label, and
   the 8px admin door in the footer. */
@media (max-width: 1400px) and (pointer: coarse), (max-width: 900px) {

  /* Standalone links that sit on their own line and act as buttons: the
     programme-hero breadcrumb ("Programs ·"), "See all ages →", the Code of
     Ethics contents list, the calendar's "add to Google / Outlook / .ics"
     row, the FAQ's Clear button, and the letter's "open on its own page". */
  main.page .agesw-all,
  main.page .fd-more,
  main.page .prog-eyebrow > a,
  main.page #first-day-hero .prog-eyebrow > a,
  main.page #calendar-dates button,
  main.page #ethics-document a,
  main.page #families-faq-questions button,
  main.page #ia-letter-on-ai a {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    box-sizing: border-box;
    /* `display:inline-flex` on a grid or flex ITEM is blockified, and a
       blockified item stretches to its track. The FAQ's "Clear" pill went from
       71px wide to 313px — a full-width dark slab under the search field,
       which is not a secondary action any more. These grow DOWN to 44px and
       keep their own width. */
    width: fit-content;
    justify-self: start;
  }

  /* The homepage carousel dots are 11px, and 11px is the right VISUAL size —
     a 44px dot is a button, not a dot. So the dot stays and the hit area is
     a transparent pseudo-element over it, the same pattern the virtual-tour
     hotspots already use. */
  main.page #home-what-children-build button[aria-label^="Card"] { position: relative; }
  main.page #home-what-children-build button[aria-label^="Card"]::after {
    content: ""; position: absolute; inset: -17px; border-radius: 50%;
  }

  /* Captions that label a field: tapping one focuses the input, so it is a
     target, but giving it 44px of box would push the field down the page.
     Hit area only — padding out, an equal margin back. */
  main.page label[for]:not(.hb-search__lbl) {
    display: inline-block;
    padding-block: 16px;
    margin-block: -16px;
    position: relative;
  }

  /* Links that are ALONE in their paragraph rather than inside a sentence:
     the programme and first-day hero breadcrumbs, and the "school calendar"
     line on /contact. The inline-link rule at the top of this file already
     gives them 10px of padding, which on 16px text is a 36px target — better
     than the 16px they started at, and still short of 44. They are not in
     running prose, so there is no line rhythm to protect and no neighbouring
     link to overlap: they take the full 14px and land exactly on 44.

     Prose links keep 10px deliberately. At 14px each one would be a 44px box
     inside a 25px line, so two links on consecutive lines would overlap and
     the wrong one would take the tap. */
  main.page section[id$="-hero"] p > a,
  main.page #contact-reach-us p > a {
    /* !important, and for a measured reason. The generic inline-link rule at
       the top of this file carries four :not() arguments — two attribute
       selectors, a class and another attribute — which put it at (0,5,2). This
       one is (0,2,4): more element names, fewer classes, and the class column
       decides. Without !important the 10px simply wins and these stay at 36px,
       which is exactly what the first version of this rule did. */
    padding-block: 14px !important;
    margin-block: -14px !important;
    position: relative;
  }
}
