/* ============================================================================
   header + navigation — the single source of truth
   ----------------------------------------------------------------------------
   These rules used to live in three places: css/style.css (app, faq, privacy)
   and inline <style> blocks in index.html and about.html. The three copies
   drifted, which is why the nav rows never quite lined up:

       desktop   tabs 14.4px apart, account row 11.2px apart
       mobile    tabs 12px apart,   account row 16px apart  (inverted!)
       mobile    the gap between the two rows was 13px on the home page
                 and 18.6px on about and app
       fonts     the account row rendered at 13.76 / 13.12 / 13.6px
                 depending on which page you were looking at

   Everything below is driven by four custom properties, so a row cannot drift
   from its neighbour again: change the token, every page moves together.

   Markup varies by page and the selectors cover all of it:
       logo      .site-header h1        index, app, faq, privacy
                 .site-header .logo     about
       tabs      button.tab / a.tab     index, app
                 .tabs a                about
       account   #account               index, app   (.ghost-btn, #account-name)
                 #acct-links            about        (a, .who)
   ========================================================================== */

/* Always reserve the scrollbar's width, on every page, even ones short enough
   not to need one. Without this the viewport changes width by ~15px whenever a
   page or tab is tall enough to scroll and the next one is not, and the header
   — justify-content: space-between, so pinned to the right edge — jumps
   sideways by exactly that much. Measured: a 15px viewport change moves the
   tabs 15px.

   This lives in header.css and not style.css because index.html and about.html
   link only this file. Putting it in style.css fixed /app, /faq and /privacy
   and left the landing page and About still jumping, which is precisely the
   pair of routes that were reported: home -> gallery, and Tastemaker -> About.

   Headless browsers use overlay scrollbars and never reproduce any of this. */
html {
  scrollbar-gutter: stable;
}
@supports not (scrollbar-gutter: stable) {
  html { overflow-y: scroll; }
}

:root {
  --nav-gap: 0.9rem;        /* space between items inside one row */
  --nav-group-gap: 1.8rem;  /* 2x --nav-gap: enough to read as a deliberate break */
  --nav-pad-x: 0rem;        /* horizontal padding on each item */
  --nav-pad-y: 0.15rem;
  --nav-font: 0.86rem;
  --nav-item-h: 1.6rem;     /* one box height for every item in either row */
  --nav-row-pad: 0.5rem;    /* mobile only: space either side of the divider */
}

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.8rem;
  padding: 1.1rem 2rem;
}

.site-header h1,
.site-header .brand-wrap,
.site-header .logo {
  font-family: 'Romla', Georgia, serif;
  font-size: 4.75rem;
  font-weight: normal;
  letter-spacing: 0.02em;
  line-height: 1;
  color: var(--logo-blue, var(--blue, #0000f6));
  text-decoration: none;
  animation: fade-in 1.1s ease 0.1s both;
}

.header-right {
  display: flex;
  align-items: center;
  gap: var(--nav-group-gap);
  opacity: 1;
  transition: opacity 0.2s;
}

.site-header:hover .header-right,
.header-right:focus-within { opacity: 1; }

/* Both rows are the same kind of thing, so they get the same rule. The gap
   subtracts the items' own horizontal padding, which means --nav-gap is the
   space you actually see between two words no matter how fat the tap targets
   get. That padding is what made the account row look 4px looser than the
   tabs on a phone. */
.tabs,
#account,
#acct-links {
  display: flex;
  align-items: center;
  gap: calc(var(--nav-gap) - 2 * var(--nav-pad-x));
}

/* every item in either row: one size, one padding, one colour */
/* Every item is the same box: same height, contents centred. A button, an
   anchor, a bare span and a gear glyph all sit on different natural baselines,
   which is why the gear floated 2px above the words next to it and the account
   name was 9px shorter than its neighbours on a phone. Giving them one height
   makes align-items:center actually line them up. */
.tab,
.tabs a,
#account > *,
#acct-links > * {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--nav-item-h);
  border: none;
  background: none;
  font: inherit;
  font-size: var(--nav-font);
  line-height: 1;
  /* Navigation follows the wordmark instead of fading into the surrounding
     wall. In a storefront --room-logo is the same contrast-aware ink used by
     the Ajiwai mark; everywhere else this falls back to the product cobalt. */
  color: var(--room-logo, var(--logo-blue, var(--blue, #0000f6)));
  text-decoration: none;
  cursor: pointer;
  padding: var(--nav-pad-y) var(--nav-pad-x);
}

/* The bell. Grey while there is nothing, blue the moment there is — the colour
   is the whole signal, with a small dot behind it for the glance that does not
   register hue. Sized off --nav-font like the gear, and with no min-width: a
   box wider than its glyph leaves dead space that reads as a wider gap, which
   is exactly what the settings gear used to do to this row. */
#notif-btn {
  position: relative;
  padding-block: 0;
  color: var(--muted);
  transition: color 0.2s;
}
#notif-btn .bell {
  width: 1.25em;
  height: 1.25em;
  display: block;
}
#notif-btn .bell-dot { display: none; }

#notif-btn.has-unread { color: var(--logo-blue, var(--blue, #0000f6)); }
#notif-btn.has-unread .bell-dot {
  display: block;
  position: absolute;
  top: 0.1em;
  right: -0.1em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--logo-blue, var(--blue, #0000f6));
}
/* one small nudge when something arrives, then it settles */
#notif-btn.has-unread .bell { animation: bell-tilt 0.9s ease both; transform-origin: 50% 15%; }
@keyframes bell-tilt {
  0%, 100% { transform: rotate(0); }
  15% { transform: rotate(-11deg); }
  32% { transform: rotate(9deg); }
  50% { transform: rotate(-5deg); }
  68% { transform: rotate(3deg); }
}
@media (prefers-reduced-motion: reduce) {
  #notif-btn.has-unread .bell { animation: none; }
}

/* An author `display` rule outranks the UA's `[hidden] { display: none }`, so
   the shared box above would have un-hidden every signed-out button in the
   header. These pages toggle visibility with the hidden property alone, never
   style.display, so overriding it back is safe. */
.site-header [hidden] { display: none !important; }

.tab:hover,
.tabs a:hover,
#account > *:hover,
#acct-links > *:hover {
  color: var(--room-logo, var(--logo-blue, var(--blue, #0000f6)));
  text-decoration: underline;
  text-underline-offset: 0.28em;
}

/* the account row is the quieter of the two */
#account .ghost-btn,
#acct-links a { font-style: italic; }

/* The active mark is an underline on the text, not a border on the box. As a
   border it sat at the bottom of a 38px tap target on a phone, a good 10px
   adrift of the word it was meant to underline, and it made the active tab 1px
   taller than its neighbours. This tracks the text at any box height. */
.tab.active,
.tabs a.active {
  color: var(--room-logo, var(--logo-blue, var(--blue, #0000f6)));
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.45em;
}

#account-name,
#acct-links .who { color: var(--ink); font-style: normal; }

/* The gear used to carry an inline font-size and a 40px min-width, which made
   it a head taller than the row and left a gap beside it three times wider
   than every other. It is an icon, so it may run a little large, but it is
   sized off the row's own scale and padded like everything else. */
/* No min-width: padding it out to a wider box left dead space inside the box,
   which reads as a wider gap either side of the gear and breaks the rhythm.
   The row's own height is the tap target, and the glyph runs large to help. */
#settings-btn {
  font-size: 1.4em;
  /* the bigger glyph would otherwise push the box past the shared row height
     and lift the gear a fraction above the words either side of it */
  padding-block: 0;
  color: var(--logo-blue, var(--blue, #0000f6));
}

/* Sign in is the one blue word in the header, on every page. about.html used
   to render it as an ordinary grey link because its nav is built in JS and
   never got the id the other two pages style. */
/* #account .ghost-btn outranks a bare #signin-btn, so the id alone lost the
   font-style fight and sign in came out italic like its neighbours. */
#account #signin-btn,
#signin-btn,
.signin-link,
#acct-links .signin-link {
  color: var(--logo-blue, var(--blue, #0000f6));
  font-style: normal;
}
#account #signin-btn:hover,
#signin-btn:hover,
.signin-link:hover,
#acct-links .signin-link:hover {
  color: var(--logo-blue, var(--blue, #0000f6));
  text-decoration: underline;
}

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

/* ---- phone: logo on top, tabs, divider, account ------------------------- */
@media (max-width: 820px) {
  :root {
    --nav-gap: 0.75rem;
    --nav-pad-x: 0.2rem;   /* a bigger tap target, invisible in the spacing */
    --nav-pad-y: 0.3rem;
    --nav-font: 0.82rem;
    --nav-item-h: 2.375rem;  /* 38px: the row is the tap target */
  }

  .site-header {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
    padding: 0.7rem 1rem 0.4rem;
  }
  .site-header h1,
  .site-header .brand-wrap,
  .site-header .logo { font-size: 2.6rem; line-height: 1; text-align: center; }

  /* The rows carry the spacing around the divider themselves, so it is even
     on both sides. When the flex gap did part of the job the space below the
     rule was more than twice the space above it. */
  .header-right { flex-direction: column; align-items: stretch; gap: 0; opacity: 1; }

  /* about.html marks its account row up as <nav class="tabs" id="acct-links">,
     so without the :not() it drew a second divider under itself and carried
     the tab row's bottom padding on top of its own top padding — 9px taller
     than the same row on every other page. */
  .tabs:not(#acct-links) {
    justify-content: center;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    padding-bottom: var(--nav-row-pad);
    border-bottom: 1px solid var(--line, #e1e1e1);
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tab, .tabs a { white-space: nowrap; }

  #account,
  #acct-links {
    /* reserve one row even while empty (first visit, state unknown), so the
       page below does not drop when the account row fades in */
    min-height: calc(var(--nav-item-h) + var(--nav-row-pad));
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 0.35rem;
    padding-top: var(--nav-row-pad);
    border-left: none;   /* about.html drew a divider the other pages never had */
    padding-left: 0;
  }

  /* Sign out gets the second line to itself. It landed there by luck of the
     wrap for an admin, and jumped back up beside the account name for anyone
     with fewer buttons or a shorter name; a full-width basis pins it. */
  #signout-btn { flex: 0 0 100%; }

  /* Sign out sits on its own line, deliberately rather than by accident. It
     already wrapped there on a phone, but only because the six links above it
     happened to fill the row: add or remove one link, or sign in on a wider
     phone, and it would jump back up. */
  #signout-btn { flex-basis: 100%; }
}

/* Keep touch navigation explicitly fully opaque as well; it has no hover state
   to lean on for additional contrast. */
@media (hover: none) {
  .header-right { opacity: 1; }
}

/* ---- the account row is known before first paint, or the nav waits ---------
   js/header.js paints the row from the last known state before the browser's
   first paint, so the tabs never take two positions. On a first-ever visit
   there is no known state: the nav holds at opacity 0 and fades in ONCE, at
   its final position, when /api/me lands (or at header.js's 900ms floor).
   The base .header-right already carries transition: opacity 0.2s, so adding
   hdr-ready is itself the fade. Specificity (0,3,1) beats the hover and
   mobile opacity rules regardless of source order. */
html:not(.hdr-ready) .site-header .header-right { opacity: 0; }

/* ---- the Beta chip, and the reporter behind it ---------------------------- */
/* Sasen's brief: small, superscript, still blue, still Romla. It inherits the
   wordmark's face rather than restating it, so a future change to the logo
   font carries the chip along instead of leaving it behind. */
#beta-chip {
  font-family: 'Romla', Georgia, serif;
  color: var(--logo-blue, #0000f6);
  font-size: 0.9rem;
  line-height: 1;
  margin-left: 0.28rem;
  vertical-align: super;
  cursor: pointer;
  opacity: 0.72;
  transition: opacity 0.15s;
  user-select: none;
}
#beta-chip:hover,
#beta-chip:focus-visible,
#beta-chip[aria-expanded='true'] { opacity: 1; }
#beta-chip:focus-visible { outline: 2px solid var(--logo-blue, #0000f6); outline-offset: 3px; border-radius: 3px; }
/* the room turns the chrome white; the chip is part of the wordmark, so it goes too */
body.room-full #beta-chip { color: var(--room-ink); }

#beta-pop {
  position: absolute;
  z-index: 80;
  width: min(340px, calc(100vw - 24px));
  background: var(--card, #fff);
  color: var(--ink, #1a1a1a);
  border: 1px solid var(--line, #e1e1e1);
  border-radius: 14px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.16);
  padding: 0.95rem 1rem 0.85rem;
  animation: bp-in 0.16s ease-out;
}
@keyframes bp-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { #beta-pop { animation: none; } }

#beta-pop .bp-head { font-size: 0.98rem; font-weight: 600; }
#beta-pop .bp-sub { margin: 0.2rem 0 0.6rem; font-size: 0.78rem; color: var(--muted, #7f7f7f); line-height: 1.45; }
#beta-pop textarea {
  width: 100%; box-sizing: border-box; resize: vertical;
  font-family: inherit; font-size: 0.88rem; line-height: 1.5;
  padding: 0.55rem 0.7rem; border: 1px solid var(--line, #e1e1e1);
  border-radius: 10px; background: var(--bg, #f9f9f9); color: inherit;
}
#beta-pop textarea:focus { outline: none; border-color: var(--accent-ink, #505050); }
#beta-pop .bp-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; margin-top: 0.6rem; }
#beta-pop .bp-msg { font-size: 0.76rem; color: var(--muted, #7f7f7f); font-style: italic; min-height: 1em; }
#beta-pop .bp-send {
  border: 1px solid var(--ink, #1a1a1a); background: var(--ink, #1a1a1a); color: var(--bg, #f9f9f9);
  font: inherit; font-size: 0.82rem; padding: 0.34rem 0.85rem; border-radius: 999px; cursor: pointer;
}
#beta-pop .bp-send:disabled { opacity: 0.5; cursor: default; }

/* On a phone the panel is the width of the screen and anchoring it to a chip
   that sits under a centred wordmark reads as an accident, so it centres. */
@media (max-width: 560px) {
  #beta-pop { left: 12px !important; right: 12px; width: auto; }
}


/* ==== the demo nudges =====================================================
   Two of these: "try a demo sign-in" in the header (#signin-hint, on / and
   /app) and "try demo photo" over the Match My Room dropzone (#demo-hint, /app
   only). They are the same offer in two places, so they are one look: an oval
   with a few sparkles over it.

   This lives in header.css because it is the only stylesheet all five pages
   load. index.html does not load style.css, and the previous arrangement kept
   a byte-identical copy of these rules inline in BOTH index.html and app.html
   plus a third partial copy in style.css. Three copies of a look is three
   chances for it to drift, and it had already drifted: the pages hid the nudge
   at different breakpoints (820px and 640px). One copy, here.
   ========================================================================= */

.nudge-wrap { position: relative; display: inline-block; }

/* The oval. pointer-events is set back to auto because the containers that
   hold these are click-through — #signin-hint sits over the header and must
   not swallow anything — but the pill itself looks like a button and so has to
   behave like one. */
.nudge-pill {
  display: inline-block;
  font: inherit; font-size: 0.88rem; font-style: italic; white-space: nowrap;
  color: #3a3a3a;
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 999px;
  padding: 0.3rem 0.85rem;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
  cursor: pointer;
  pointer-events: auto;
  transition: box-shadow 0.15s, border-color 0.15s, color 0.15s;
}
.nudge-pill:hover,
.nudge-pill:focus-visible {
  color: var(--logo-blue, #0000f6);
  border-color: rgba(0, 0, 0, 0.16);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
}
.nudge-pill:disabled { opacity: 0.6; cursor: default; }

/* The sparkles sit OUTSIDE the pill, on its corners, so .nudge-wrap must not
   clip and the pill must not be what they are positioned against. */
.nudge-wrap .sparkle {
  position: absolute; line-height: 1; pointer-events: none;
  color: var(--logo-blue, #0000f6);
  opacity: 0; animation: nudge-twinkle 2.6s ease-in-out infinite;
}
.nudge-wrap .s1 { top: -7px; left: 10px; font-size: 0.58rem; animation-delay: 0.2s; }
.nudge-wrap .s2 { top: -6px; right: 9px; font-size: 0.7rem; animation-delay: 1.1s; }
.nudge-wrap .s3 { bottom: -8px; left: 46%; font-size: 0.5rem; animation-delay: 1.8s; }
@keyframes nudge-twinkle {
  0%, 100% { opacity: 0; transform: scale(0.5); }
  50% { opacity: 0.5; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .nudge-wrap .sparkle { animation: none; opacity: 0.32; }
}

/* ---- the sign-in nudge, in the header ---------------------------------- */
#signin-hint {
  position: fixed; top: 60px; right: 38px; z-index: 60;
  display: flex; align-items: flex-end; gap: 0.5rem;
  pointer-events: none;             /* the header underneath stays clickable */
  opacity: 0; transform: translateY(7px);
  transition: opacity 0.8s ease, transform 0.8s ease;
}
#signin-hint.show { opacity: 1; transform: translateY(0); }
#signin-hint .sh-textwrap { margin-bottom: 6px; }
#signin-hint .sh-arrow { width: 108px; height: 58px; overflow: visible; }
#signin-hint .sh-arrow path {
  fill: none; stroke: #bcbcbc; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ---- the same nudge on a phone ----------------------------------------- */
/* The arrow is drawn for a fixed desktop header position, and a phone header
   wraps and centres, so it points at nothing and is dropped. The pill stays
   exactly as it is; only the POINTING changes, to a caret on whichever edge
   faces the button, placed at runtime by placeSigninHint() in js/header.js. */
@media (max-width: 820px) {
  #signin-hint:not(.sh-mobile) { display: none; }
  #signin-hint .sh-arrow { display: none; }
}
#signin-hint.sh-mobile {
  position: fixed; right: auto;      /* placeSigninHint writes top/left */
  align-items: center; gap: 0;
}
/* the button it points at has scrolled off; see placeSigninHint */
#signin-hint.sh-mobile.sh-off { display: none; }
#signin-hint.sh-mobile .sh-arrow { display: none; }
#signin-hint.sh-mobile .sh-textwrap { margin-bottom: 0; display: flex; align-items: center; }
#signin-hint.sh-mobile.sh-at-below { flex-direction: column; }
#signin-hint.sh-mobile.sh-at-below .sh-textwrap { flex-direction: column; }
#signin-hint.sh-mobile .sh-text { font-size: 0.8rem; padding: 0.24rem 0.7rem; }

/* The caret always sits on the edge nearest the button. Which edge that is
   depends on where placeSigninHint found room, so there is one triangle per
   side rather than one rotated triangle: a rotated border-triangle carries its
   own bounding box and the gap stops matching. */
#signin-hint.sh-mobile .sh-textwrap::before {
  content: ''; display: block; width: 0; height: 0; flex: none;
  border: 6px solid transparent;
}
#signin-hint.sh-mobile.sh-at-below .sh-textwrap::before {
  margin: 0 auto 4px; border-top-width: 0;
  border-bottom: 7px solid rgba(0, 0, 0, 0.22);
}
/* pill on the LEFT of the button: the caret points right, at the button */
#signin-hint.sh-mobile.sh-at-left .sh-textwrap { flex-direction: row-reverse; }
#signin-hint.sh-mobile.sh-at-left .sh-textwrap::before {
  margin-left: 4px; border-right-width: 0;
  border-left: 7px solid rgba(0, 0, 0, 0.22);
}
/* pill on the RIGHT of the button: the caret points left */
#signin-hint.sh-mobile.sh-at-right .sh-textwrap::before {
  margin-right: 4px; border-left-width: 0;
  border-right: 7px solid rgba(0, 0, 0, 0.22);
}

/* on a dressed room the chrome inverts; the nudge follows it */
body.room-full .nudge-pill {
  background: rgba(0, 0, 0, 0.42); color: #fff; border-color: rgba(255, 255, 255, 0.25);
}
body.room-full .nudge-wrap .sparkle { color: var(--room-ink); }
body.room-full #signin-hint .sh-arrow path { stroke: var(--room-muted); }
body.room-full #signin-hint.sh-mobile.sh-at-below .sh-textwrap::before {
  border-bottom-color: rgba(255, 255, 255, 0.55);
}
body.room-full #signin-hint.sh-mobile.sh-at-left .sh-textwrap::before {
  border-left-color: rgba(255, 255, 255, 0.55);
}
body.room-full #signin-hint.sh-mobile.sh-at-right .sh-textwrap::before {
  border-right-color: rgba(255, 255, 255, 0.55);
}

/* about.html's logo is a bare anchor; js/header.js wraps it with the Beta chip
   so the pair is one flex item and the chip can be a true inline superscript.
   The wrapper is listed with the wordmark in the two rules above, and must stay
   listed in both: `vertical-align: super` raises the chip by a fraction of its
   PARENT's font size, so a wrapper left at body size drops the chip to the
   baseline at that width while every other page keeps it aloft. */
.brand-wrap { display: inline-block; white-space: nowrap; }
