/* ============================================================
   Local departures from the imported design.

   qatar-ink.css is a verbatim copy of the design project's sheet, so that a
   fresh export can replace it without losing work. Everything this site changes
   or corrects lives here instead, and is linked after it.
   ============================================================ */

/* ---- WHITE GROUND ----
   The imported design's light theme is a sage paper, #F6F8EF. This site wants
   plain white, so the ground and everything derived from it step up: the
   surfaces go to near-white greys rather than tinted ones, and the rules cool
   off to match, or a warm hairline on a white ground reads as a stain.

   The chart hues are deliberately left alone -- ash, tangerine and red were
   picked to sit on paper and they hold on white. Only the neutrals move.

   --bg-rgb has to move with --bg: several rules build translucent panels with
   rgba(var(--bg-rgb),...), and leaving it sage would tint them against a white
   page. */
:root[data-theme="light"] {
  --bg:#FFFFFF; --bg2:#F7F7F5; --surface:#F1F1EE; --surface2:#E7E7E3; --grid:#F4F4F1;
  --rule:#B9B9B2; --rule2:#E2E2DD; --rule3:#6F6F68;
  --fg:#1C1C1B; --fg2:#4E4E4A; --fg3:#6F6F68; --fg4:#B9B9B2;
  --ac:#1C1C1B; --ac-hi:#000000;
  --r0:#FFFFFF;
  --bg-rgb:255,255,255; --fg-rgb:28,28,27; --fg2-rgb:78,78,74; --ac-rgb:28,28,27;
  --tip-fg2:#B9B9B2;
}

/* ---- MEDIA GRADE ----
   The imported sheet prints every image and video at grayscale(1). It suits the
   plates and the charts, but the story runs on screenshots of the ads
   themselves, and their colour is evidence: the blue of a Facebook chrome, a
   flag, the red of a campaign's own branding. Fully desaturated they stop
   reading as captures of real posts.

   So: a wash rather than a conversion. Most of the chroma comes out, a warm
   cast goes in to sit the media in the same family as the tangerine and sage of
   the charts, and saturate() brings back enough that strong colours survive as
   colour. contrast() replaces the local contrast the desaturation costs.

   Set twice. The first pass left about half the original chroma with a clear
   warm cast, which read as a grade applied to the page rather than as
   photographs that happen to be muted; the second overcorrected. This sits
   between them -- a little under 40% of the original chroma, with the sepia low
   enough to warm the neutrals without tinting anything already coloured. */
.moment-media img, .moment-media video,
.moment-media-grid img, .moment-media-grid video,
.hero-bg, .intro-bg, .moment-bg, .moment-overlay {
  filter:grayscale(.68) sepia(.08) saturate(1.18) contrast(1.04);
}
/* The hero wall sits behind the headline, so it stays a step further back:
   less chroma again, and the imported sheet's brightness and opacity kept.

   Applied to the scrim's backdrop rather than to the wall itself. The two look
   identical -- same values, same result everywhere the scrim is painted -- but
   the scrim is the layer the drifting phrases cut holes in, and a filter set on
   the wall would survive the cut: the holes revealed a wall that was already
   77% grey, which is not a reveal of anything. Moved here, the grade is part of
   what the letterforms remove, so the reels run in full colour inside them.

   brightness started at .95 x the wall's own opacity:.6 -- .57, the two being
   equivalent over a near-black ground, and the opacity had to move here for the
   same reason the grade did. It is .46 now, a step darker again: the headline's
   word is set as an outline with the wall showing through it, so the wall is
   doing two jobs at once, and at .57 the ground was bright enough that the
   counters of the letters did not separate from it. Only the ground moves --
   the holes cut the filter with the scrim, so what shows inside the letterforms
   is unchanged.

   This is the hero in both themes, not only in dark: the section pins the dark
   tokens in light mode as well (index.html), so there is one scrim to set.

   The backdrop root is .hero-section, which already sets isolation:isolate, so
   nothing below the section is filtered. */
.hero-scrim {
  -webkit-backdrop-filter:grayscale(.77) sepia(.07) saturate(1.12) contrast(1.03) brightness(.46);
  backdrop-filter:grayscale(.77) sepia(.07) saturate(1.12) contrast(1.03) brightness(.46);
}
/* And the imported sheet greys the wall at source as well -- grayscale(1) and
   opacity:.5, qatar-ink.css -- which has to be undone rather than re-tinted.
   Anything left on the wall itself is underneath the scrim, so it survives the
   holes cut in it and the reels come through grey again. */
.hero-strips { filter:none; opacity:1; }
/* A viewer who has asked for reduced motion is usually also asking for a
   quieter page; the grade is left alone, but nothing here should animate. */

/* ---- THE STIPPLED WORD ----
   One word in the headline is screened rather than set solid: red dots over the
   page's own ink, matching the treatment on the plate numbers and page titles.
   It began as a 45-degree hatch and was converted to the screen so the story's
   headline reads as part of the same system.

   background-clip:text needs a transparent fill to show through, so it is
   guarded -- without the @supports the word would be invisible rather than
   merely unhatched wherever the property is missing. The text itself is real
   text: selectable, searchable, and read out as normal. */
@supports ((-webkit-background-clip:text) or (background-clip:text)) {
  .hero-copy h1 .hatched {
    /* Red dots on ink, not red-and-ink stripes -- the same screen the plate
       numbers and page titles use, at the density display type needs. The ink
       layer underneath matters: with the dots alone the gaps would be
       transparent and the moving media wall would show through the letterforms.
       Two dot layers on a half-step offset, so the screen is a lattice rather
       than a plain grid. */
    background-image:
      radial-gradient(circle at center, var(--red) 1.45px, transparent 1.55px),
      radial-gradient(circle at center, var(--red) 1.45px, transparent 1.55px),
      linear-gradient(var(--fg), var(--fg));
    background-size:4.2px 4.2px, 4.2px 4.2px, 100% 100%;
    background-position:0 0, 2.1px 2.1px, 0 0;
    -webkit-background-clip:text; background-clip:text;
    color:transparent; -webkit-text-fill-color:transparent;
  }
}

/* ---- and the same word as a window ----
   The screen above is what the word is painted with, and the ink layer under it
   is what keeps the moving wall out of the letterforms. Both stay as the
   fallback: where the scrim cannot be masked the word has to be printed, or it
   would be a hole onto the dimmed wall and read as nothing.

   Where the scrim can be masked -- .holes, which the hero script sets once the
   cuts are in place -- the word is drawn as an outline and nothing else. The
   script cuts the letterforms out of the scrim, so the reels run inside them at
   full strength; the rule around them is the only ink, and it is what keeps the
   word legible whatever is passing behind it.

   The rule is --fg, the headline's own ink, not a flat white: the line above it
   is cream, and a pure white outline beside it reads as a mistake rather than as
   emphasis. It is set in em so it holds its weight against the clamp -- .02em
   is about three pixels at the desktop size and still a hairline at the floor.

   -webkit-text-stroke is centred on the letterform, which is the reason the cut
   needs no inset: the hole's edge is the glyph's edge, so half the rule covers
   the scrim and half the window, and neither side bleeds past it. The @supports
   guard matters more than usual here -- without the stroke a transparent fill is
   an invisible word, so where it is missing the screened fallback above stands
   instead. */
@supports (-webkit-text-stroke: 1px currentColor) {
  .hero-section.holes .hero-copy h1 .hatched {
    background-image:none;
    color:transparent; -webkit-text-fill-color:transparent;
    -webkit-text-stroke:.02em var(--fg);
  }
}

/* ---- PLATE NUMBERS ----
   The story's numerals are set as a diagram would set them: screened and hatched
   rather than filled, the way a plate number and its key are printed. Same
   background-clip:text mechanism and the same @supports guard as the hatched
   word above -- without the guard a browser missing the property would render
   them invisible rather than plain.

   The moment numbers take a dot screen. They are enormous and sit under the
   copy, so the imported sheet holds them at 9% -- but a stipple at 9% is nothing
   at all, so they lift to 20% and the texture, not the tone, does the work of
   keeping them behind the text. The grid is 5px, which at 48-96px type reads as
   a screen rather than as dots.

   The phase numerals take the hatch, and the pitch encodes the phase the way
   the density bands on the stats page do: mid for the first, wide for the
   second, fine for the third, and the fourth -- the phase still running -- is
   the only one that takes the red. */
@supports ((-webkit-background-clip:text) or (background-clip:text)) {
  .moment-number, .phase-divider .phase-num {
    -webkit-background-clip:text; background-clip:text;
    color:transparent; -webkit-text-fill-color:transparent;
  }
  .moment-number {
    opacity:.2;
    /* A step denser than the first pass: dots up from 1.15 to 1.25px and the
       lattice in from 5 to 4.6px, which takes coverage from about 17% to 23%.
       Still well short of the page titles' screen, so the plate number stays
       behind the headline rather than competing with it. */
    background-image:radial-gradient(circle at center, var(--fg) 1.25px, transparent 1.35px);
    background-size:4.6px 4.6px;
  }
  .phase-divider .phase-num {
    background-image:repeating-linear-gradient(45deg,
      var(--fg) 0 2.4px, transparent 2.4px 6px);
  }
  [data-phase="offline"] .phase-num {
    background-image:repeating-linear-gradient(-45deg,
      var(--fg) 0 2.2px, transparent 2.2px 7.5px);
  }
  [data-phase="investigation"] .phase-num {
    background-image:repeating-linear-gradient(45deg,
      var(--fg) 0 2.6px, transparent 2.6px 4.6px);
  }
  [data-phase="resurgence"] .phase-num {
    background-image:repeating-linear-gradient(45deg,
      var(--red) 0 2.6px, transparent 2.6px 5.4px);
  }
}

/* ---- PAGE TITLES ----
   The big page title on each section page -- THE NUMBERS, 34 MONTHS OF
   OPERATION, WHERE THE MONEY WENT -- printed as a dot screen rather than solid
   ink, the same device as the plate numbers but far denser: a 3.8px lattice with
   the dots on a half-step offset, so it reads as a tone at arm's length and only
   resolves into dots up close. Loose enough and display type turns to lace.

   Guarded, because background-clip:text needs a transparent fill to show
   through: without the @supports a browser missing the property renders the
   title invisible rather than merely solid.

   Not applied to the story's own headline (.hero-section h1) -- that one already
   carries the red hatched word and sits over moving media, where it needs solid
   weight -- nor to campaigns / campaign-runs, whose titles are set in sentence
   case at paragraph weight rather than as display type. */
/* campaigns and campaign-runs have no .hero block -- their title sits straight
   in the page wrapper. Marked .page-title and given the same display spec the
   .hero h1 rules carry on every other page, so all nine now match. */
.page-title {
  font-family:Archivo,sans-serif; font-weight:900;
  font-size:clamp(36px,5vw,72px); line-height:.88;
  letter-spacing:-.05em; text-transform:uppercase;
}

@supports ((-webkit-background-clip:text) or (background-clip:text)) {
  .hero h1, #hero h1, .page-title {
    background-image:
      radial-gradient(circle at center, var(--fg) 1.35px, transparent 1.5px),
      radial-gradient(circle at center, var(--fg) 1.35px, transparent 1.5px);
    background-size:3.8px 3.8px, 3.8px 3.8px;
    background-position:0 0, 1.9px 1.9px;
    -webkit-background-clip:text; background-clip:text;
    color:transparent; -webkit-text-fill-color:transparent;
  }
}

/* ---- STAT FIGURES ----
   qatar-ink.css sizes every hero figure at up to 72px and kills the inline
   sizes, deliberately -- "two sizes only". That holds at the ~920px the design
   was drawn at, where a narrow hero is ~224px wide. The stats grid reaches five
   columns on a desktop, and '859.8M' then wants 267px in a 205px cell, which is
   the clipped headline the restyle was meant to fix. Neither rule adds a third
   size; they stop the larger one overflowing. */
.stat-hero.narrow .stat-val,
.stat-hero.narrow .stat-val[style] { font-size:clamp(34px,3.6vw,54px) !important; }
/* The sheet asks long values to wrap instead of shrinking, but the spend tiles
   carry an inline white-space:nowrap, and inline beats a stylesheet. */
.stat-card .stat-val[style] { white-space:normal !important; }

/* ---- PHASE RULES ----
   The rules between the story's phases were a solid 3px ink line running the
   full width, which met the targeting panel head on. They are a green dot screen
   now, and masked so the screen thins out and is gone before it reaches the
   panel's left edge -- roughly 68% across at desktop widths, so the fade runs
   out by 72%.

   The border itself is kept at 3px and made transparent rather than removed, so
   the divider's box metrics and the spacing below it do not shift; the screen is
   painted over where the line used to be.

   Green because it is the one hue in the palette not already carrying meaning
   here: red marks the running phase and the projection, tangerine the TikTok
   series. Ash green is structural, which is what a rule is. */
.phase-divider-inner {
  border-top-color:transparent;
  position:relative;
}
.phase-divider-inner::before {
  /* Two rows of dots, not one. A 4px strip on a 3.4px lattice rendered a single
     row and read as a dotted hairline -- lighter than the 3px solid line it
     replaced, which made the phase break stop registering as a break. */
  content:''; position:absolute; left:0; right:0; top:-4px; height:7px;
  pointer-events:none;
  background-image:radial-gradient(circle at center, var(--ash) 1.2px, transparent 1.3px);
  background-size:3.3px 3.3px;
  -webkit-mask-image:linear-gradient(to right, #000 0 48%, transparent 72%);
  mask-image:linear-gradient(to right, #000 0 48%, transparent 72%);
}

/* ---- SECTION AND MOMENT HEADINGS ----
   Every heading below the page title takes the screen: the phase names that
   open each section, and the moment headings within them. Ink, at the same
   density as the page titles, so the whole hierarchy is screened and only the
   body copy is set solid.

   Briefly tried in red. Red is doing specific work on this site -- the running
   phase, the projected figure, the one word in the story's headline -- and
   spending it on every heading would have cost it that meaning.

   color is set !important because the imported sheet forces the phase names to
   ink. The fill is what actually lets the screen show through, but the colour
   has to give way too, or a browser without -webkit-text-fill-color paints
   solid over it. */
@supports ((-webkit-background-clip:text) or (background-clip:text)) {
  .phase-divider .phase-name, .moment-title {
    background-image:
      radial-gradient(circle at center, var(--fg) 1.35px, transparent 1.5px),
      radial-gradient(circle at center, var(--fg) 1.35px, transparent 1.5px);
    background-size:3.8px 3.8px, 3.8px 3.8px;
    background-position:0 0, 1.9px 1.9px;
    -webkit-background-clip:text; background-clip:text;
    color:transparent !important; -webkit-text-fill-color:transparent;
  }
  /* The plate numeral sits inside the heading and keeps its own, lighter screen
     rather than inheriting the heading's. */
  .moment-title .moment-number {
    background-image:radial-gradient(circle at center, var(--fg) 1.25px, transparent 1.35px);
    background-size:4.6px 4.6px;
    background-position:0 0;
  }
}

/* ---- PROVENANCE STAMP ----
   Every media block in the story carries a stamp saying what it is. These are
   the operation's own advertisements, and once they are graded, cropped and set
   in a designed page they start to look like the site's own illustrations. A
   reader who screenshots one, or meets it out of context, should still be able
   to tell where it came from.

   One stamp per block rather than one per tile: a grid of four gets a single
   mark across it, which reads as a stamp on a sheet rather than a watermark
   tiled over each frame.

   It sits above the media but below nothing else -- pointer-events are off, so
   the video controls and the audio toggle underneath still take clicks.

   Top right rather than across the middle: centred, it sat on whatever the
   picture was of, which on an advertisement is the face or the headline it was
   built around. The corner keeps the frame readable and still travels with any
   screenshot of it. Top right specifically because the audio toggle holds the
   bottom right, and because it is where a reader's eye lands last on a frame
   they have just finished looking at.

   Smaller than it was, for the same reason -- a corner mark does not have to
   carry the whole box -- but still bordered and still in red, the one hue the
   site reserves for the operation itself. The angle is eased from 11 degrees to
   6: it rotates about its own top right corner, so the free end swings down
   into the frame and cannot clip against the edge, and a steeper tilt in a
   corner reads as a sticker rather than a stamp. Deliberately not stippled: at
   this size a screen would cost the legibility the stamp exists for. */
.moment-media, .moment-media-grid { position:relative; }
.moment-media::after, .moment-media-grid::after {
  content:'This content is from an influence operation';
  position:absolute; top:12px; right:12px; left:auto; z-index:2;
  transform:rotate(-6deg); transform-origin:100% 0;
  max-width:min(52%, 280px);
  padding:6px 11px;
  border:2px solid var(--red);
  font-family:Archivo,sans-serif; font-weight:900;
  font-size:clamp(9px,.95vw,13px); line-height:1.15;
  letter-spacing:.05em; text-transform:uppercase; text-align:center;
  color:var(--red); opacity:.72;
  pointer-events:none;
}
/* Not on our own figures. The stamp is a claim about the picture -- that it
   came from the operation -- and a chart this investigation drew is the one
   kind of story media that claim is false of. build_story.py marks those. */
.moment-media.own-work::after,
.moment-media-grid.own-work::after { content:none; }

/* The stamp arrives with the block it sits on rather than ahead of it. */
.moment-media::after, .moment-media-grid::after { transition:opacity .9s ease .35s; }
.moment-inner:not(.visible) .moment-media::after,
.moment-inner:not(.visible) .moment-media-grid::after { opacity:0; }

/* ---- TITLE GAP ----
   The imported sheet opens every .hero with 84px of padding, and flowchart's
   own block pushes its title further still. campaigns and campaign-runs sit at
   44px because they never had a .hero to inherit it, and that is the tighter
   reading -- the title should follow the nav, not float away from it.

   Only the top is touched: the sheet's 40px side gutters and the section rhythm
   below the title are left as they are.

   index.html is deliberately not included. Its hero is the full-bleed opening of
   the story, not a page title, and the space there is doing different work. */
.hero { padding-top:44px; }
#hero { padding-top:44px; }

/* ---- STORY COLUMN INSET ----
   The vertical accent bar down the left of each moment is gone. It was a flex
   item, so it and the 48px gap after it were also what held the copy column in
   from the page edge -- removing it dropped every moment 52px left, to 36px,
   which is further left than the nav's own text starts and reads as a slip
   rather than a decision. The inset is kept as padding so the column stays
   exactly where it was and only the line disappears.

   Dropped on narrow screens, where the section's own 20px padding is the whole
   margin and 52px more would leave the copy in a gutter. */
.moment-inner { padding-left:52px; }
@media (max-width:900px) { .moment-inner { padding-left:0; } }


/* ---- LIGHT THEME: THE MASTHEAD STAYS BLACK ----
   On white the nav was a cream bar separated from the page by a rule, which
   left the masthead doing the same job as the page it sits on. Reversed out it
   reads as chrome rather than as the top of the article, and the bar is the
   same object in both themes.

   Done by re-pointing the tokens on the bar itself rather than by restating
   each colour, the way the hero and intro sections already borrow the dark
   palette. Everything inside -- the links, the active item's underline, the
   date, the theme button and its border and hover -- resolves against these and
   needs no rule of its own. --ac goes back to the dark theme's tangerine
   because the light theme's accent is the page's own ink, which is invisible
   here.

   Last in the file, so it wins over the ink layer at the end of qatar-ink.css
   (which sets the nav's own colours from --fg). */
:root[data-theme="light"] .nav {
  --bg:#0A0A0B; --bg-rgb:10,10,11; --bg2:#0F0E11;
  --fg:#FFFFFF; --fg-rgb:255,255,255; --fg2:#C8C8C4; --fg3:#9B9B96; --fg4:#4A4A47;
  --rule:#3A3A38; --rule2:#2C2C2A; --rule3:#6A6A66;
  --ac:#FF4A1C; --ac-hi:#FF6A44; --ac-rgb:255,74,28;
  background:#0A0A0B;
  color:#FFFFFF;
  /* The double rule is kept, inverted: the bar's own edge, a white gap the
     width of the page, then the hairline. Restated rather than inherited
     because both halves are built from --bg and --fg, and re-pointing those
     above would have drawn the second rule in white on a white page. */
  border-bottom:2px solid #0A0A0B;
  box-shadow:0 3px 0 -1px #FFFFFF, 0 4px 0 -1px #1C1C1B;
}
/* The progress bar is fixed above the nav, and in light mode it is drawn in the
   page's ink -- black on black for the height of the bar. White over the
   masthead, which is the only place the first few pixels of it are ever seen. */
:root[data-theme="light"] #scroll-progress { background:#FFFFFF; }

/* The nav no longer carries a date, which used to push the theme toggle to
   the right edge; the toggle takes that space itself. */
.theme-toggle { margin-left:auto; }
