/* =============================================================================
 *  assets/css/phone.css — THE PHONE SHELL. One file, six surfaces, loaded LAST.
 *
 *  ── WHY THIS FILE EXISTS AND WHY IT IS LAST ────────────────────────────────
 *  The site's mobile rules had been written three times, in three files, and two
 *  of those three could not reach the pages they were written for:
 *
 *    · site.css carries a @media (max-width: 760px) block — but the home page
 *      never loads site.css (it is a React export and loads spa.css), and the
 *      AAC door loads neither. So the two surfaces people arrive on got the
 *      DESKTOP rules at 390px and the four hand-written pages got the mobile
 *      ones. Same markup, same intention, two answers.
 *    · footer.css is loaded after site.css on every page and restates the same
 *      properties, so a mobile floor written in site.css for anything inside the
 *      footer was dead on arrival — written, reviewed, and never applied. (That
 *      was found and fixed on 2026-09-30 by moving the floor into footer.css.)
 *    · `.dock`, the bottom navigation, is STYLED in site.css and MARKED UP in
 *      four of the six pages. So the two surfaces that most needed a thumb-level
 *      navigation — the home page and the sign-in door — had neither its markup
 *      nor its styling, and a phone's only way to change page was the header.
 *
 *  Those are not three bugs. They are one fault with three faces: the phone
 *  layer had no single place to live, so every page got whichever parts of it
 *  happened to be audible from there.
 *
 *  So the phone layer lives HERE, and this file is linked LAST on all six
 *  surfaces — after site.css / spa.css / aac.css AND after footer.css. It
 *  therefore wins on source order against every one of them and needs no
 *  `!important` to do it. The rule to keep: anything that is about a PHONE goes
 *  in this file. Anything that is about the house's look goes where it already
 *  lives, and this file does not restate it.
 *
 *  ── WHAT IT MAY AND MAY NOT DECIDE ─────────────────────────────────────────
 *  POSTURE and GEOMETRY: what is on screen, how big it is, where a thumb finds
 *  it. Never a literal colour — every value below resolves from
 *  assets/css/tokens.css, so the shell follows the theme like everything else,
 *  in both grounds, with no per-theme rule of its own.
 *
 *  ── THE BREAKPOINTS, AND WHAT EACH ONE IS FOR ──────────────────────────────
 *    ≤ 760px    a phone (and a small tablet in portrait). The dock appears, the
 *               header slims, the type floor comes up. This matches the width
 *               site.css and spa.css already use, so nothing lands half-way.
 *    ≤ 400px    a small phone (SE, mini). Only the tightest things move again.
 *    coarse     a touch pointer at ANY width. Tap floors key off this, because
 *               44px is a fact about fingers and not about screens.
 *    landscape  a phone on its side has ~360px of height to spend; the dock
 *               gives some of it back.
 *
 *  ── THE JS HALF ────────────────────────────────────────────────────────────
 *  assets/js/phone.js plants the dock on the surfaces whose markup has none,
 *  marks the current page in it, and drives the header's retract on scroll. Both
 *  halves degrade: with the script blocked, the four pages that ship the dock
 *  inline still get a styled, working dock, and the header simply never
 *  retracts. Nothing below depends on the script having run except the rules
 *  explicitly scoped to `html[data-ph]`.
 * ========================================================================== */


/* =============================================================================
 *  0 · THE SHELL'S METRICS
 *
 *  Published as tokens because three different files need to agree with them:
 *  the body's bottom clearance here, the footer's last row in footer.css, and
 *  the scroll-into-view offset in phone.js. A dock whose height is a literal in
 *  one of those three is a footer with its final line under the dock.
 *
 *  --ph-dock-h is the dock's own box. --ph-dock-safe adds the home-gesture bar,
 *  which is what content actually has to clear. They are separate because the
 *  dock paints over the gesture area deliberately (its blur should reach the
 *  screen edge) while content must not.
 * ========================================================================== */
:root {
  --ph-dock-h: 0px;
  --ph-dock-safe: 0px;
  /*  The header's height on a phone, published for phone.js: it is the offset a
   *  scroll-to-anchor has to leave clear, and the script should not carry its own
   *  copy of a number this file decides. */
  --ph-hdr-h: 56px;
  /*  The dock's own surfaces, named so the two themes resolve them without this
   *  file knowing which one is on. 92% rather than solid: the content scrolling
   *  under a translucent bar is what tells you the page continues. */
  --ph-bar: color-mix(in srgb, var(--obsidian) 92%, transparent);
  --ph-blur: saturate(150%) blur(18px);
}


/* =============================================================================
 *  1 · THE VIEWPORT IS THE DEVICE'S, NOT THE WIDEST CHILD'S
 *
 *  `overflow-x: clip` on <html> alone does NOT stop a phone zooming out. It
 *  stops sideways SCROLLING; the document's content box stays as wide as its
 *  widest descendant, and that width is what the mobile viewport measures and
 *  then shrinks the page to fit.
 *
 *  That is what made the sign-in door report a 421px viewport on a 360px phone —
 *  every word on the card rendered ~15% smaller than designed, which reads as
 *  type that is simply too small and sends the search in the wrong direction.
 *  The escapee was a 320px panel hidden with `visibility` (so its lazy seal
 *  still loads), which keeps its box and still counts toward scroll width.
 *
 *  It needs BOTH boxes, on every surface, which is why it is stated here rather
 *  than in the three files that each got it half-right. `clip` and not `hidden`:
 *  `hidden` makes a scroll container, which breaks `position: sticky` inside it.
 * ========================================================================== */
html { overflow-x: clip; }
body { overflow-x: clip; }


/* =============================================================================
 *  2 · THE DOCK
 *
 *  Five destinations under the thumb, on every surface. Written outside a media
 *  query and hidden by default, so the markup can ship in the HTML a server
 *  sends (no flash, no JS dependency) and only the POSTURE is conditional.
 *
 *  ── WHY FIVE, AND WHY THESE FIVE ───────────────────────────────────────────
 *  The site has exactly four destinations — home, the two divisions, the door —
 *  and one thing people arrive wanting to do, which is get in touch. That fifth
 *  action had been reachable only by scrolling the whole page to the footer,
 *  which on the home page is 6,200px down. CONTACT is therefore a dock slot that
 *  scrolls to the form and focuses it (see phone.js), not a sixth page.
 *
 *  Five 78px cells fit a 390px screen with 0 to spare, and a 320px screen at
 *  64px each. That is the ceiling: a sixth slot would put the labels below the
 *  readable floor, and the labels are what make a dock navigable rather than a
 *  row of guesses.
 * ========================================================================== */
.dock {
  position: fixed; inset: auto 0 0 0; z-index: 120;
  display: none;
  background: var(--ph-bar);
  backdrop-filter: var(--ph-blur);
  -webkit-backdrop-filter: var(--ph-blur);
  border-top: 1px solid var(--line-soft);
  /*  The gesture bar is PAINTED OVER (the blur should reach the screen edge) and
   *  the cells are padded away from it, rather than the bar stopping short of
   *  it and leaving a strip of raw page showing through. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
  /*  A dock is chrome: it must not be draggable, selectable, or flash grey under
   *  a finger, and it must never be what the page scrolls. */
  -webkit-tap-highlight-color: transparent;
  user-select: none; -webkit-user-select: none;
  overscroll-behavior: contain;
}

.dock ul {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: stretch;
}
.dock li { display: flex; }

/*  THE CELL IS THE TARGET. Every other small control on this site is grown to
 *  44px with a transparent pseudo-element, because the visible box is smaller
 *  than a fingertip and the design wants it that way. A dock cell is the
 *  opposite case: it SHOULD be as large as it looks, so the whole 56×64 box is
 *  the hit area and there is no pseudo-element to go wrong. */
.dock a {
  flex: 1 1 auto;
  display: grid; justify-items: center; align-content: center; gap: 5px;
  padding: 9px 2px 10px;
  min-height: 56px;
  font-size: 9.5px; letter-spacing: .1em; line-height: 1;
  color: var(--faint);
  position: relative;
  transition: color .3s var(--ease);
}
.dock svg {
  width: 21px; height: 21px;
  stroke: currentColor; fill: none;
  stroke-width: 1.2; stroke-linecap: round; stroke-linejoin: round;
  transition: transform .3s var(--ease);
}

/*  THE CURRENT PAGE, said twice over — gold ink AND a rule across the top of the
 *  cell. Colour alone is not enough: at 9.5px on a translucent bar the
 *  difference between --faint and --gold-lt is a judgement call, and on the one
 *  control whose job is to tell you where you are it should not be. The rule is
 *  the house's own hairline language (see .hdr__nav a::after in site.css), run
 *  along the top edge so it reads as a tab marker rather than an underline. */
.dock a[aria-current="page"] { color: var(--gold-lt); }
.dock a[aria-current="page"]::before {
  content: "";
  position: absolute; left: 50%; top: -1px;
  width: 26px; height: 2px; transform: translateX(-50%);
  background: linear-gradient(90deg, var(--gold-deep), var(--gold-pale), var(--gold-deep));
}
/*  Press feedback. A dock with no answer to a tap feels broken on a touch
 *  screen in a way it never does under a mouse — there is no hover to have
 *  told you the control was live. */
.dock a:active { color: var(--gold-hi); }
.dock a:active svg { transform: scale(.9); }

/*  The AAC slot is the one that leaves the public site for the management
 *  surface, and it is the only dock item that is a different KIND of
 *  destination. It carries the house colour at rest to say so. */
.dock a[href^="/aac"] { color: color-mix(in srgb, var(--gold) 72%, var(--faint)); }


/* =============================================================================
 *  3 · PHONE POSTURE
 * ========================================================================== */
@media (max-width: 760px) {

  :root {
    /*  57, not 56: the cell's own 56px PLUS the hairline above it. The bar's box
     *  is what content has to clear, and measuring the cell alone leaves the
     *  footer's last line one pixel under the rule — which is exactly the kind of
     *  off-by-a-border a token is supposed to stop happening twice. */
    --ph-dock-h: 57px;
    --ph-dock-safe: calc(57px + env(safe-area-inset-bottom, 0px));
    /*  --dock-h is site.css's own token for this, consumed by its
     *  `body { padding-bottom }`. Kept in step rather than replaced, so a page
     *  that loads site.css and this file does not get two different answers. */
    --dock-h: var(--ph-dock-safe);
  }

  .dock { display: block; }

  /*  BOTTOM CLEARANCE, ON EVERY SURFACE.
   *
   *  site.css gives `body` this padding and spa.css / aac.css do not, which is
   *  the other half of why the dock could not simply be planted on the home page
   *  and the door: it would have sat on top of the footer's last two rows. Set
   *  on `body` here so all six surfaces get it from one place.
   *
   *  The dock itself needs no reservation beyond this: it is fixed, so on a page
   *  shorter than the viewport (the 404, the door) it floats at the bottom of the
   *  screen over the body's own background, which propagates to the canvas. */
  body { padding-bottom: var(--ph-dock-safe); }

  /*  THE SKIP LINK clears the retracting header rather than sitting under it. */
  .skip { top: calc(8px + env(safe-area-inset-top, 0px)); }
}


/* =============================================================================
 *  4 · THE HEADER RETRACTS
 *
 *  `.hdr` is `position: fixed` and 56px tall on a phone, which is 7% of a
 *  390×844 screen held permanently against pages that are 4,700–6,200px long.
 *  On a desk that is the right trade — the navigation is always there. On a
 *  phone the navigation is now in the dock, under the thumb, and the header's
 *  remaining job (whose site this is, and the language) is one a person needs
 *  on arrival and when they stop, not continuously.
 *
 *  So it retracts on the way DOWN and comes back the moment the scroll
 *  reverses — the gesture that means "I want the chrome" on every mobile OS.
 *  phone.js owns the decision and writes `data-ph-hdr` on <html>; this file
 *  owns only what that looks like.
 *
 *  Two details that matter:
 *   · it comes back on scroll UP, not at the top of the page. A header you have
 *     to scroll 6,000px to recover is a header you have lost.
 *   · `transform`, not `top`. The header carries a backdrop-filter, and
 *     animating its position re-rasterises the blurred backdrop every frame.
 *   · `html[data-ph-hdr]` — never a bare class — so with the script blocked the
 *     header is simply always visible, which is today's behaviour.
 * ========================================================================== */
@media (max-width: 760px) {
  .hdr, header.fixed {
    transition: transform .28s var(--ease), background-color .5s var(--ease),
                border-color .5s var(--ease);
    will-change: transform;
  }
  html[data-ph-hdr="off"] .hdr,
  html[data-ph-hdr="off"] header.fixed { transform: translateY(-100%); }

  /*  A retracted header must not take the focus ring with it. Anything focused
   *  inside it pins it back on screen — otherwise tabbing into the language
   *  button scrolls focus to somewhere invisible. */
  html[data-ph-hdr="off"] .hdr:focus-within,
  html[data-ph-hdr="off"] header.fixed:focus-within { transform: none; }

  @media (prefers-reduced-motion: reduce) {
    .hdr, header.fixed { transition: none; }
  }

  /*  THE DIVISION LABEL GETS ITS OWN ROW.
   *
   *  spa.js writes the division you are scrolled into — APPARELS, JEWELLERY —
   *  into the home page's header, and spa.css centres it on the VIEWPORT
   *  (`left: 50%`), which is right on a desk where that row is mostly empty.
   *  On a phone the midpoint is already taken. Measured at 375px with the
   *  header pinned open: the label spans 146→229, the ENG button sits 159→184
   *  and AAC SIGNIN starts at 211 — so the word was printed straight across
   *  ENG (25px of overlap) and 18px into AAC SIGNIN. The same at 360, 390 and
   *  414; ENG was fully inside the label's box at every one of them.
   *
   *  It cannot simply be re-centred between its neighbours: wordmark + ENG +
   *  AAC SIGNIN leave about 19px of clear space in that row, and the label is
   *  83px wide. So it moves DOWN, into the strip the header already carries
   *  below its controls, and owns the full width there with nothing beside it.
   *
   *  Anchored to the header's BOTTOM edge rather than given a top offset, so
   *  it stays correct if the header's height changes — and the header's height
   *  is React's, not ours. Nothing here changes that height: the label fades
   *  in and out as you scroll past a division, and paying for it in header
   *  height would shift the whole page every time it did.
   */
  html body .aa-secnav {
    left: 0; right: 0;
    top: auto; bottom: 3px;
    transform: none;
    justify-content: center;
  }
  /*  Smaller and tighter than the desktop label: it is now a caption under the
   *  row rather than a peer of the controls beside it. */
  html body .aa-secnav__link { font-size: 10px; letter-spacing: .2em; }
}


/* =============================================================================
 *  5 · A FLOOR UNDER THE READING
 *
 *  The house voice is small, widely-tracked uppercase, and it stays — it is the
 *  design, and the 2026-09-30 pass deliberately left the 11px eyebrows and the
 *  header's 10px controls alone for exactly that reason.
 *
 *  Body COPY is a different question. `body` is
 *  `clamp(13px, .36vw + 11.6px, 15px)`, whose middle term only reaches the 15px
 *  cap on a wide screen — so at 390px every paragraph on the site renders at its
 *  13px floor, in caps, at .07em tracking, held at arm's length. 13px uppercase
 *  is not 13px sentence case: the capital height is the full type size, so it
 *  LOOKS larger and reads slower, and a 60-word paragraph of it is work.
 *
 *  Raised on the prose containers only, never on `body`. Dozens of rules across
 *  site.css, spa.css and footer.css are px-sized siblings of these, and moving
 *  the root would move the few em-sized ones out of step with all of them.
 * ========================================================================== */
@media (max-width: 760px) {
  .lede { font-size: 14.5px; }
  .prose p, .prose li,
  .facts p, .div-panel__body p, .sect p,
  .chain__d { font-size: 14px; line-height: 1.85; }
  /*  Tracking yields before size does. .07em of a 14px cap is ~1px per letter,
   *  which at this measure costs a word a line; the voice survives .045em and
   *  the paragraph gets its line back. */
  .prose p, .prose li, .facts p, .div-panel__body p, .sect p { letter-spacing: .045em; }
}


/* =============================================================================
 *  6 · THE DOCK ON A SMALL PHONE, AND ON ITS SIDE
 * ========================================================================== */
@media (max-width: 400px) {
  /*  320–400px: five labels still fit, but only without their tracking. At
   *  .1em, "JEWELLERY" is 9 characters and 8px of letter-spacing inside a 64px
   *  cell — the tracking is what overflows, not the word. */
  .dock a { font-size: 9px; letter-spacing: .04em; padding-inline: 0; }
  .dock svg { width: 19px; height: 19px; }
}

/* -----------------------------------------------------------------------------
 *  6b · SHORT VIEWPORTS — THE DOCK GOES WITH THE HEADER
 *
 *  Measured against the Play device catalogue shipped beside this site
 *  (devices.csv, 19,159 devices, Display Ratio in column I):
 *
 *      16:9 phones                      2,104 devices   17.1% of all phones
 *      phones <= 660dp of viewport      2,890 devices   23.5% of all phones
 *      chrome as a share of those       17.7% median, 30.9% worst (a 366dp screen)
 *
 *  113dp of header + dock is 14.4% of a median phone and nearly a third of the
 *  shortest ones. On a tall 20:9 screen that is a fair price for always-there
 *  navigation. On a 360x640 16:9 screen — and 640dp is the single commonest
 *  phone height in the catalogue, 1,969 devices — it is not.
 *
 *  So on a SHORT screen the dock retracts with the header rather than only the
 *  header doing it, and both come back on the first upward scroll. The page
 *  gets its whole height while you are reading and its navigation back the
 *  moment you look for it.
 *
 *  Keyed on HEIGHT, not on orientation: a 16:9 phone held upright is short, and
 *  `orientation: landscape` would miss every one of them. 700px catches the
 *  533 / 569 / 640dp clusters and stops below the 720dp one.
 * -------------------------------------------------------------------------- */
@media (max-width: 760px) and (max-height: 700px) {
  .dock {
    transition: transform .28s var(--ease);
    will-change: transform;
  }
  html[data-ph-hdr="off"] .dock { transform: translateY(100%); }
  /*  Never take the focus ring off screen with it — the same guard §4 puts on
   *  the header, for the same reason. */
  html[data-ph-hdr="off"] .dock:focus-within { transform: none; }

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

@media (max-height: 460px) and (orientation: landscape) {
  /*  A phone on its side has ~360px of height. 56px of dock is 15% of it for a
   *  navigation nobody turned the phone to use — so the icons go and the labels
   *  stay. The label is the half that identifies the destination; the icon is
   *  the half that only decorates it. */
  :root { --ph-dock-h: 39px; --ph-dock-safe: calc(39px + env(safe-area-inset-bottom, 0px)); }
  .dock a { min-height: 38px; padding: 7px 2px; gap: 0; }
  .dock svg { display: none; }
}


/* =============================================================================
 *  7 · INSIDE THE APP
 *
 *  ALOFTAURA ACHIEVES ships these same files and serves them at its own secure
 *  origin, so every page below is also an app screen. Two things are true there
 *  and nowhere else, and phone.js sets `html.in-app` when the shell's bridge is
 *  present (window.AloftShell):
 *
 *   · THERE IS NO BROWSER CHROME. No address bar means no retracting toolbar to
 *     borrow space from and nothing above the header but the status bar, so the
 *     header must clear the notch itself.
 *   · THE DOCK IS THE ONLY NAVIGATION. In a browser the back gesture and the
 *     address bar are always there; in the app the dock is it, so it does not
 *     retract and it is never the thing a layout decides to drop.
 * ========================================================================== */
html.in-app .hdr,
html.in-app header.fixed { padding-top: calc(16px + env(safe-area-inset-top, 0px)); }

/*  The app draws its own splash and its own offline notice; the site's "skip to
 *  content" link is a keyboard affordance for a surface that has a keyboard. */
html.in-app .skip { display: none; }


/* =============================================================================
 *  8 · TOUCH
 *
 *  site.css already grows the header's controls and the footer's links to 44px
 *  with a transparent pseudo-element, and the 2026-09-30 hit-test pass confirmed
 *  those pads genuinely work — measured by probing outward with
 *  elementFromPoint rather than by reading getBoundingClientRect(), which
 *  reports the visible box and is why dozens of false positives were filed
 *  against controls that were already correct.
 *
 *  So this section adds the ones that file could not reach: the footer's send
 *  button and the language picker's own menu rows, both of which live in
 *  footer.css's half of the cascade and neither of which had a pad.
 * ========================================================================== */
@media (pointer: coarse) {
  .aa-contact__send, .aa-send { min-height: 44px; }
  .i18n-menu button, .theme-pick__b { min-height: 44px; }

  /*  THE HEADER'S DIVISION LINKS — the one control a tablet exposes that no
   *  existing coarse rule reaches.
   *
   *  site.css's coarse block names .hdr__lang, .hdr__aac, .ftr__links, .ftr__mark
   *  and .dock a, and that list was complete for a PHONE: below 760px .hdr__nav
   *  is `display: none`, so its links are not a target because they are not
   *  there. Between 761 and 1024 with a finger — an iPad in portrait, a Surface,
   *  a phone in landscape — they ARE there, at 22px, and nothing had ever grown
   *  them. Measured 48×22 on apparel/jewellery at 768 by hit test.
   *
   *  `::before`, never `::after`: on these links ::after is the gold hairline
   *  that grows on hover, and a pad written there is what turned that hairline
   *  into a 77×44 gold slab over the footer's AAC link on every touch device
   *  (found 2026-09-30). The same pseudo-element cannot be both. */
  .hdr__nav a, .aa-secnav__link { position: relative; }
  .hdr__nav a::before, .aa-secnav__link::before {
    /*  -14, not -12. The home page's secondary-nav link is a 16px line, and
     *  16 + 24 is 40 — close enough to look right in the rule and four pixels
     *  short in the hit test, which is where it was caught. */
    content: ""; position: absolute; inset: -14px -8px; z-index: 0;
  }

  /*  The footer's theme and integrity triggers are padded in footer.css, whose
   *  block is already ungated by width — verified at 390 and 768: 52×23 plus a
   *  -14/-10 inset is 51px of finger. Nothing to add. (A hit test reports them
   *  at 38px because the pads of ADJACENT links overlap and win the point, which
   *  is a probe artefact, not a target that is too small.) */

  /*  Momentum scrolling inside the two places this site scrolls a box rather
   *  than the page. Without it a drag inside them does nothing on iOS. */
  .intg__body, .prose { -webkit-overflow-scrolling: touch; }
}
