/* ============================================================================
   brand.css — After Image's skin.

   This file is one shop's colour and one shop's typeface, and nothing else.
   The structure it hangs on — type scale, spacing, radii, shadows, motion —
   is in theme.css beside it and is the same for every site we build. See the
   note at the top of that file for which token belongs where.

   To dress a different shop, copy skin-template.css, not this. This one is
   full of measurements taken off After Image's actual sign and none of them
   transfer.

   Everything below is derived from a photograph of that signage
   (brand/logo.png), not invented. Measured off that photo:

       sign black      #13110c    76% of the plaque
       sign lettering  #dfd5c7    13%, and 13.0:1 against the black

   The lettering samples warm (R−B = 24) partly because it was photographed
   under tungsten shop lighting rather than because the paint is that yellow, so
   the cream used below is pulled back towards neutral while keeping the warmth
   that makes it read as cream rather than grey. The black is used as measured:
   it is very slightly brown, and that is what stops the palette feeling like a
   stock dark theme.

   The shop's identity is a black plaque with cream lettering, so the branded
   surfaces (header, hero, footer) are dark and the working surfaces (the
   booking form, the calendar) are light. Booking a haircut on your phone in a
   queue is a reading task, and a dark form is a worse reading task.
   ========================================================================== */

/* The shop's face, shipped with the shop's site.

   Playfair Display: a high-contrast serif in the same family of shapes as the
   plaque over the door — the thin-to-heavy stroke that makes AFTER IMAGE read
   the way it does, and the caps that carry MEN'S HAIRDRESSING AND GROOMING
   underneath it. Chosen because it holds that contrast at 16px on a phone;
   a true Didone loses its hairlines there and the headings turn to lace.

   One file, 38kB, latin only, variable across the weights the site uses, and
   served from this domain. swap rather than block: the page is readable in the
   fallback serif from the first paint and re-renders when the font lands,
   which is the right trade for somebody on a phone outside the shop.

   OFL 1.1 — see playfair-display.LICENCE.txt beside it. */
@font-face {
  font-family: 'Playfair Display';
  src: url('/brand/playfair-display.woff2') format('woff2');
  /* The range the file actually carries — it is one variable font sliced to
     the weights this site uses, so claiming 400 here would have the browser
     clamp silently rather than tell anybody. */
  font-weight: 500 700;
  font-style: normal;
  font-display: swap;
}

:root {
  /* --- the two colours that are actually theirs --------------------------- */
  --sign:       #13110c;   /* measured off the plaque */
  --sign-cream: #f2ece2;   /* the lettering, neutralised out of tungsten light */
  /* The same black as three numbers, for anything that needs it at an alpha:
     the gradients that fade a photograph into the plaque, and the shadow
     tokens over in theme.css. CSS cannot vary the alpha of a hex custom
     property, and the two fades that marry the hero and the shop photographs
     to the dark bands used to hard-code 19,17,12 — so they silently stopped
     matching the moment the black moved for dark mode. Change --sign, change
     this, and they follow. */
  --sign-rgb:   19, 17, 12;
  /* And the cream as three numbers, for the same reason. Every hairline and
     every hover wash on a dark band is the lettering colour at an alpha —
     the circles round the two social icons, the underline under the phone
     number, the rule above the sign-off. Those were written as
     rgba(242,236,226,…) in app.css, which meant a shop whose lettering is not
     this cream got After Image's cream in eight places nobody would think to
     look. Change --sign-cream, change this. */
  --sign-cream-rgb: 242, 236, 226;
  /* The edge where a dark band meets the page. Nothing in daylight: black on
     cream is its own edge. In dark mode the two are both nearly black and the
     hairline is what stops the hero and the footer from bleeding into the
     page — see the dark block below. */
  --band-edge:  transparent;

  /* --- surfaces ---------------------------------------------------------- */
  --paper:      #ffffff;
  --cream:      #faf8f4;   /* page background: warm, so it sits with the sign */
  --cream-deep: #f0ece4;   /* hover, striped rows, the day you're looking at */
  --brand:      #13110c;
  --brand-deep: #0a0906;
  --on-brand:   #f2ece2;

  /* --- text -------------------------------------------------------------- */
  --ink:        #13110c;   /* 17.6:1 on --cream */
  --ink-soft:   #5c5548;   /*  7.3:1 on --cream — captions, meta, help text */
  --ink-faint:  #8a8175;   /*  3.6:1 — decorative only, never body copy */
  --line:       #e4ded3;

  /* --- text on the dark bands ---------------------------------------------
     The header, the hero and the footer are painted in --sign in both themes,
     so the text on them is its own ladder and does NOT follow --ink, which
     inverts. These five were hard-coded warm greys in app.css: on a shop with
     a blue or green sign they were beige text on blue, at a ratio nobody had
     checked. Ratios below are against --sign in the light theme; every one is
     a shade better against the darker --sign that dark mode swaps in. */
  --band-ink:       #b9b1a4;   /* 8.9:1 — body copy in the footer */
  --band-ink-soft:  #8f887c;   /* 5.4:1 — column headings, the sign-off row */
  --band-ink-faint: #847d71;   /* 4.6:1 — the build credit, and no dimmer */
  --band-link:      #a39b8d;   /* 6.9:1 — a link at the faint level */
  /* Text sitting directly on a photograph rather than on flat --sign: the
     address under the hero. Brighter than --band-ink on purpose, because a
     photograph is not a flat colour and the text-shadow under it is doing
     half the work. */
  --photo-ink:      #cbc3b6;   /* 10.8:1 against the darkest part of the fade */

  /* --- state ------------------------------------------------------------- */
  /* Restrained on purpose. The shop has no third colour, so these earn their
     place by meaning something: free, taken, gone wrong. Each is checked
     against --paper in the contrast table at the bottom of this file. */
  --free:       #2c7a51;   /* an available slot */
  --free-bg:    #eaf3ed;
  --taken:      #7e7569;   /* booked — deliberately quiet, it is not an error */
  /* A day or a time you can tap, on the public booking page, drawn as a ring
     rather than a fill — see the calendar block in app.css.
     Blue and not the green above, because the two were saying the same word in
     different places: green means "this is free", and on a page where every
     offered day and every offered time is by definition free it stopped
     meaning anything and just tinted the screen. This one is chrome for a
     control — the colour of something you press, like a link. Not a shop
     colour and not pretending to be one; the shop's two colours stay on the
     sign, the header and the chosen day. */
  --pick:       #1c5c9e;   /* 6.8:1 on --paper */
  --warn:       #8a5a1b;   /* inside the cancellation window */
  --warn-bg:    #f7f0e4;
  --danger:     #a33c31;   /* no-show, failed charge */
  --danger-bg:  #f9edeb;

  /* --- ink on a filled block ----------------------------------------------
     Two of them, and the difference is the whole point.

     --on-accent goes on a state colour that is FILLED rather than outlined:
     the day under your thumb on the calendar, the time you are hovering, the
     status chips in the admin. Those backgrounds invert between themes — in
     daylight --pick is a deep blue and in dark mode it is a pale one — so the
     ink on them has to invert too. It was #fff in both, which is how white on
     --pick ended up at 2.1:1 in dark mode, on the hovered day and the hovered
     time, in the middle of the booking flow.

     --on-dark goes on a surface that is dark in BOTH themes: the wash behind
     a hovered social icon, and the disc a barber's initials sit on when there
     is no photograph. Those never invert, so this one must not either. */
  --on-accent:  #ffffff;
  --on-dark:    #ffffff;

  /* --- form ------------------------------------------------------------- */
  --field:      #ffffff;
  /* 3.0:1 against the field, which WCAG 1.4.11 requires for the boundary of a
     control. The prettier #cfc7b9 managed 1.7:1 — invisible to a lot of people,
     on the one part of the page where "where do I type?" has to be obvious. */
  --field-line: #9c9486;
  --focus:      #13110c;

  /* --- the display face ---------------------------------------------------
     The sign is a high-contrast serif — thin AFTER, heavy IMAGE, with MEN'S
     HAIRDRESSING AND GROOMING spaced out in caps beneath it — so the display
     face is Playfair Display, shipped with the site (see the @font-face above).

     It used to be a stack that asked for Didot, then Bodoni MT, then whatever
     the device had. That reads beautifully on a Mac, where Didot exists, and
     nowhere else: Windows fell through to Bodoni MT only if Office was
     installed, and an Android phone — which is most of this shop's customers —
     landed on Georgia or plain Times. The headings were a different typeface on
     nearly every device, and none of them was the shop's.

     Still no third-party server involved. The file is 38kB, served from this
     domain beside the photographs, so nothing on the page waits on
     gstatic.com being up at 8am on a Saturday. */
  --display:    "Playfair Display", "Didot", "Bodoni MT", "Iowan Old Style",
                "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
}

/* ----------------------------------------------------------------- dark ----
   Same approach as the bakery site: re-declare the custom properties and let
   everything downstream follow.

   Two ways in, and the order matters:

     :root[data-theme="dark"]   an explicit choice, stamped on <html> by the
                                server from a cookie. The admin has a control
                                for it — a shop iPad on a bright windowsill and
                                the same iPad at eight on a January morning are
                                not the same reading problem, and the operating
                                system setting is shared by six people.
     prefers-color-scheme       everybody else, including the whole public site,
                                which has no control and needs none.

   The media query is gated on :root:not([data-theme]) so that an explicit
   choice always beats the operating system rather than losing to whichever
   rule happens to come last. There is no localStorage and no inline script:
   the value arrives with the HTML, so there is no flash of the wrong theme.

   The two blocks are duplicated on purpose. Plain CSS has no way to name a set
   of declarations and apply it to two selectors, and the alternatives — a
   preprocessor, or a second layer of indirection through more custom
   properties — cost more than twenty-six repeated lines do. Change one, change
   both; bin/contrast.js reads the FIRST block, so keep that one authoritative.
   -------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    /* The plaque goes DARKER in dark mode, which sounds wrong until you look
       at it. The page here is #1a1712 (L* 7.9) and the sign's own #13110c is
       L* 5.1 — under three points apart, which is not a surface, it is a
       rounding error. The hero band, the photograph strip and the footer, the
       three things this whole design is built on, dissolved into the page.

       #070605 is L* 1.7, so the step is 6.2 rather than 2.8. Be honest about
       what that buys: at this end of the scale no two blacks are far apart,
       and the contrast ratio only moves from 1.06 to 1.13. The tone step stops
       the two surfaces reading as one; --band-edge below is what actually
       draws the line. Both are needed, and neither is enough alone. */
    --sign:       #070605;
    --sign-rgb:   7, 6, 5;
    --band-edge:  rgba(var(--sign-cream-rgb), .10);

    --paper:      #221e18;
    --cream:      #1a1712;
    --cream-deep: #2b261f;
    --brand:      #f2ece2;   /* inverts: on dark, the "brand surface" is the light one */
    --brand-deep: #ffffff;
    --on-brand:   #13110c;

    --ink:        #f2ece2;   /* 14.8:1 on --cream */
    --ink-soft:   #b3aa9c;   /*  7.4:1 */
    --ink-faint:  #7d7568;   /*  3.5:1 */
    --line:       #3b352c;

    --free:       #6cc094;
    --free-bg:    #1c2f25;
    --taken:      #8f877a;
    --pick:       #7fb6e8;   /* 7.7:1 on --paper */
    --warn:       #d9a45c;
    --warn-bg:    #33281a;
    --danger:     #e88a7d;
    --danger-bg:  #331f1c;

    /* The fills went pale, so the ink on them goes dark. See --on-accent in
       the light block: this is the line that fixes white-on-#7fb6e8. The band
       inks above are NOT re-declared — the bands stay dark in both themes, so
       the same five greys are correct here and read a shade better against
       #070605 than they do against #13110c. */
    --on-accent:  #13110c;

    --field:      #2b261f;
    --field-line: #777065;   /* 3.1:1, same reasoning as the light theme */
    --focus:      #f2ece2;

    /* Pure black rather than the sign-tinted default in theme.css. A warm
       shadow needs a lighter surface underneath it to read as warm; on these
       near-blacks it just looks like the shadow has failed. */
    --shadow:     0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.5);
    --shadow-lg:  0 2px 4px rgba(0,0,0,.45), 0 20px 50px rgba(0,0,0,.6);
  }
}

/* The same values, chosen rather than inherited. See the note above. */
:root[data-theme="dark"] {
  --sign:       #070605;
  --sign-rgb:   7, 6, 5;
  --band-edge:  rgba(var(--sign-cream-rgb), .10);

  --paper:      #221e18;
  --cream:      #1a1712;
  --cream-deep: #2b261f;
  --brand:      #f2ece2;
  --brand-deep: #ffffff;
  --on-brand:   #13110c;

  --ink:        #f2ece2;
  --ink-soft:   #b3aa9c;
  --ink-faint:  #7d7568;
  --line:       #3b352c;

  --free:       #6cc094;
  --free-bg:    #1c2f25;
  --taken:      #8f877a;
  --pick:       #7fb6e8;
  --warn:       #d9a45c;
  --warn-bg:    #33281a;
  --danger:     #e88a7d;
  --danger-bg:  #331f1c;

  --on-accent:  #13110c;

  --field:      #2b261f;
  --field-line: #777065;
  --focus:      #f2ece2;

  --shadow:     0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.5);
  --shadow-lg:  0 2px 4px rgba(0,0,0,.45), 0 20px 50px rgba(0,0,0,.6);
}

/* data-theme="light" needs no block of its own: the light values are the ones
   at the top of the file, and the media query above no longer fires once the
   attribute is present. It exists purely as a way to say "not the system". */

/* ============================================================================
   Measured contrast, light theme. Do not hand-edit these numbers — run
   `node bin/contrast.js`, which recomputes every pairing from the values above
   in both themes. Written by hand first time round, and three of the six were
   wrong, which is the entire argument for the script.

     --ink             on --cream      17.8:1   AAA
     --ink-soft        on --cream       7.0:1   AAA
     --ink-faint       on --cream       3.6:1   decorative only
     --on-brand        on --brand      16.1:1   AAA  (the real sign pairing)
     --free            on --free-bg     4.6:1   AA
     --danger          on --danger-bg   5.6:1   AA
     --field-line      on --field       3.0:1   meets WCAG 1.4.11 for borders

   The dark bands, which are read against --sign rather than --cream:

     --band-ink        on --sign        8.9:1   AAA  (footer body copy)
     --band-ink-soft   on --sign        5.4:1   AA   (headings, sign-off)
     --band-ink-faint  on --sign        4.6:1   AA   (the build credit)
     --band-link       on --sign        6.9:1   AA
     --photo-ink       on --sign       10.8:1   AAA  (on the hero photograph)
     --sign-cream      on --sign       16.1:1   AAA  (the focus ring)

   And the ink on a filled state colour, which is the pairing that inverts.
   The dark-theme figures are the ones worth knowing, because that is where
   this was broken: --on-accent was #fff in both themes, so the hovered day
   and the hovered time sat at 2.1:1 on a pale --pick.

     --on-accent       on --pick        6.8:1 light   8.8:1 dark
     --on-accent       on --free        5.2:1 light   8.6:1 dark
     --on-accent       on --taken       4.5:1 light   5.3:1 dark
     --on-dark         on --sign       18.9:1 light  20.2:1 dark

   `node bin/contrast.js --check` exits non-zero on any regression, so it can go
   in a pre-commit hook once there's something to hook it to. The script takes a
   path, so a new shop's skin can be checked before it ever goes live:

     node bin/contrast.js brand/skin-yourshop.css --check
   ========================================================================== */
